vdr-plugin-softhdcuvid/openglosd.cpp

2156 lines
63 KiB
C++

#define __STL_CONFIG_H
#include "openglosd.h"
#include <algorithm>
/****************************************************************************************
* Helpers
****************************************************************************************/
void ConvertColor(const GLint &colARGB, glm::vec4 &col) {
col.a = ((colARGB & 0xFF000000) >> 24) / 255.0;
col.r = ((colARGB & 0x00FF0000) >> 16) / 255.0;
col.g = ((colARGB & 0x0000FF00) >> 8) / 255.0;
col.b = ((colARGB & 0x000000FF)) / 255.0;
}
/****************************************************************************************
* cShader
****************************************************************************************/
#ifdef CUVID
const char *glversion = "#version 330 core ";
#else
#ifdef RASPI
const char *glversion = "#version 300 es";
#else
const char *glversion = "#version 300 es ";
#endif
#endif
const char *rectVertexShader = "%s\n \
\
layout (location = 0) in vec2 position; \
out vec4 rectCol; \
uniform vec4 inColor; \
uniform mat4 projection; \
\
void main() \
{ \
gl_Position = projection * vec4(position.x, position.y, 0.0, 1.0); \
rectCol = inColor; \
} \
";
const char *rectFragmentShader = "%s\n \
\
precision mediump float; \
in vec4 rectCol; \
out vec4 color; \
\
void main() \
{ \
color = rectCol; \
} \
";
const char *textureVertexShader = "%s\n \
\
layout (location = 0) in vec2 position; \
layout (location = 1) in vec2 texCoords; \
\
out vec2 TexCoords; \
out vec4 alphaValue;\
\
uniform mat4 projection; \
uniform vec4 alpha; \
\
void main() \
{ \
gl_Position = projection * vec4(position.x, position.y, 0.0, 1.0); \
TexCoords = texCoords; \
alphaValue = alpha; \
} \
";
const char *textureFragmentShader = "%s\n \
precision mediump float; \
in vec2 TexCoords; \
in vec4 alphaValue; \
out vec4 color; \
\
uniform sampler2D screenTexture; \
\
void main() \
{ \
color = texture(screenTexture, TexCoords) * alphaValue; \
} \
";
const char *textVertexShader = "%s\n \
\
layout (location = 0) in vec2 position; \
layout (location = 1) in vec2 texCoords; \
\
out vec2 TexCoords; \
out vec4 textColor; \
\
uniform mat4 projection; \
uniform vec4 inColor; \
\
void main() \
{ \
gl_Position = projection * vec4(position.x, position.y, 0.0, 1.0); \
TexCoords = texCoords; \
textColor = inColor; \
} \
";
const char *textFragmentShader = "%s\n \
precision mediump float; \
in vec2 TexCoords; \
in vec4 textColor; \
\
out vec4 color; \
\
uniform sampler2D glyphTexture; \
\
void main() \
{ \
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(glyphTexture, TexCoords).r); \
color = textColor * sampled; \
} \
";
///
/// GLX check error.
///
#define GlxCheck(void) \
{ \
GLenum err; \
\
if ((err = glGetError()) != GL_NO_ERROR) { \
esyslog("video/glx: error %s:%d %d '%s'\n", __FILE__, __LINE__, err, gluErrorString(err)); \
} \
}
static cShader *Shaders[stCount];
void cShader::Use(void) { glUseProgram(id); }
bool cShader::Load(eShaderType type) {
this->type = type;
const char *vertexCode = NULL;
const char *fragmentCode = NULL;
switch (type) {
case stRect:
vertexCode = rectVertexShader;
fragmentCode = rectFragmentShader;
break;
case stTexture:
vertexCode = textureVertexShader;
fragmentCode = textureFragmentShader;
break;
case stText:
vertexCode = textVertexShader;
fragmentCode = textFragmentShader;
break;
default:
esyslog("[softhddev]unknown shader type\n");
break;
}
if (vertexCode == NULL || fragmentCode == NULL) {
esyslog("[softhddev]ERROR reading shader\n");
return false;
}
if (!Compile(vertexCode, fragmentCode)) {
esyslog("[softhddev]ERROR compiling shader\n");
return false;
}
return true;
}
void cShader::SetFloat(const GLchar *name, GLfloat value) { glUniform1f(glGetUniformLocation(id, name), value); }
void cShader::SetInteger(const GLchar *name, GLint value) { glUniform1i(glGetUniformLocation(id, name), value); }
void cShader::SetVector2f(const GLchar *name, GLfloat x, GLfloat y) {
glUniform2f(glGetUniformLocation(id, name), x, y);
}
void cShader::SetVector3f(const GLchar *name, GLfloat x, GLfloat y, GLfloat z) {
glUniform3f(glGetUniformLocation(id, name), x, y, z);
}
void cShader::SetVector4f(const GLchar *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) {
glUniform4f(glGetUniformLocation(id, name), x, y, z, w);
}
void cShader::SetMatrix4(const GLchar *name, const glm::mat4 &matrix) {
glUniformMatrix4fv(glGetUniformLocation(id, name), 1, GL_FALSE, glm::value_ptr(matrix));
}
bool cShader::Compile(const char *vertexCode, const char *fragmentCode) {
GLuint sVertex, sFragment;
char *buffer = (char *)malloc(1000);
// Vertex Shader
sVertex = glCreateShader(GL_VERTEX_SHADER);
sprintf(buffer, vertexCode, glversion);
glShaderSource(sVertex, 1, (const GLchar **)&buffer, NULL);
glCompileShader(sVertex);
// esyslog("[softhddev]:SHADER:VERTEX %s\n",vertexCode);
if (!CheckCompileErrors(sVertex)) {
free(buffer);
return false;
}
// Fragment Shader
sFragment = glCreateShader(GL_FRAGMENT_SHADER);
sprintf(buffer, fragmentCode, glversion);
glShaderSource(sFragment, 1, (const GLchar **)&buffer, NULL);
glCompileShader(sFragment);
// esyslog("[softhddev]:SHADER:FRAGMENT %s\n",fragmentCode);
if (!CheckCompileErrors(sFragment)) {
free(buffer);
return false;
}
// link Program
id = glCreateProgram();
glAttachShader(id, sVertex);
glAttachShader(id, sFragment);
glLinkProgram(id);
if (!CheckCompileErrors(id, true))
return false;
// Delete the shaders as they're linked into our program now and no longer
// necessery
glDeleteShader(sVertex);
glDeleteShader(sFragment);
free(buffer);
return true;
}
bool cShader::CheckCompileErrors(GLuint object, bool program) {
GLint success;
GLchar infoLog[1024];
if (!program) {
glGetShaderiv(object, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(object, 1024, NULL, infoLog);
esyslog("\n[softhddev]:SHADER: Compile-time error: Type: %d - \n>%s<\n", type, infoLog);
return false;
}
} else {
glGetProgramiv(object, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(object, 1024, NULL, infoLog);
esyslog("[softhddev]:SHADER: Link-time error: Type: %d - \n>%s<\n", type, infoLog);
return false;
}
}
return true;
}
#define KERNING_UNKNOWN (-10000)
/****************************************************************************************
* cOglGlyph
****************************************************************************************/
cOglGlyph::cOglGlyph(FT_ULong charCode, FT_BitmapGlyph ftGlyph) {
this->charCode = charCode;
bearingLeft = ftGlyph->left;
bearingTop = ftGlyph->top;
width = ftGlyph->bitmap.width;
height = ftGlyph->bitmap.rows;
advanceX = ftGlyph->root.advance.x >> 16; // value in 1/2^16 pixel
LoadTexture(ftGlyph);
}
cOglGlyph::~cOglGlyph(void) {}
int cOglGlyph::GetKerningCache(FT_ULong prevSym) {
for (int i = kerningCache.Size(); --i > 0;) {
if (kerningCache[i].prevSym == prevSym)
return kerningCache[i].kerning;
}
return KERNING_UNKNOWN;
}
void cOglGlyph::SetKerningCache(FT_ULong prevSym, int kerning) { kerningCache.Append(tKerning(prevSym, kerning)); }
void cOglGlyph::BindTexture(void) { glBindTexture(GL_TEXTURE_2D, texture); }
void cOglGlyph::LoadTexture(FT_BitmapGlyph ftGlyph) {
// Disable byte-alignment restriction
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, ftGlyph->bitmap.width, ftGlyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE,
ftGlyph->bitmap.buffer);
// Set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
}
extern "C" void GlxInitopengl();
extern "C" void GlxDrawopengl();
extern "C" void GlxDestroy();
extern "C" void makejpg(uint8_t *data, int width, int height);
/****************************************************************************************
* cOglFont
****************************************************************************************/
FT_Library cOglFont::ftLib = 0;
cList<cOglFont> *cOglFont::fonts = 0;
bool cOglFont::initiated = false;
cOglFont::cOglFont(const char *fontName, int charHeight) : name(fontName) {
size = charHeight;
height = 0;
bottom = 0;
int error = FT_New_Face(ftLib, fontName, 0, &face);
if (error)
esyslog("[softhddev]ERROR: failed to open %s!", *name);
FT_Set_Char_Size(face, 0, charHeight * 64, 0, 0);
height = (face->size->metrics.ascender - face->size->metrics.descender + 63) / 64;
bottom = abs((face->size->metrics.descender - 63) / 64);
}
cOglFont::~cOglFont(void) { FT_Done_Face(face); }
cOglFont *cOglFont::Get(const char *name, int charHeight) {
if (!fonts)
Init();
cOglFont *font;
for (font = fonts->First(); font; font = fonts->Next(font))
if (!strcmp(font->Name(), name) && charHeight == font->Size()) {
return font;
}
font = new cOglFont(name, charHeight);
fonts->Add(font);
return font;
}
void cOglFont::Init(void) {
if (FT_Init_FreeType(&ftLib)) {
esyslog("[softhddev]failed to initialize FreeType library!");
return;
}
fonts = new cList<cOglFont>;
initiated = true;
}
void cOglFont::Cleanup(void) {
if (!initiated)
return;
delete fonts;
fonts = 0;
if (ftLib) {
if (FT_Done_FreeType(ftLib))
esyslog("failed to deinitialize FreeType library!");
}
ftLib = 0;
}
cOglGlyph *cOglFont::Glyph(FT_ULong charCode) const {
// Non-breaking space:
if (charCode == 0xA0)
charCode = 0x20;
// Lookup in cache:
for (cOglGlyph *g = glyphCache.First(); g; g = glyphCache.Next(g)) {
if (g->CharCode() == charCode) {
return g;
}
}
FT_UInt glyph_index = FT_Get_Char_Index(face, charCode);
FT_Int32 loadFlags = FT_LOAD_NO_BITMAP;
// Load glyph image into the slot (erase previous one):
int error = FT_Load_Glyph(face, glyph_index, loadFlags);
if (error) {
esyslog("[softhddev]FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message);
return NULL;
}
FT_Glyph ftGlyph;
FT_Stroker stroker;
error = FT_Stroker_New(ftLib, &stroker);
if (error) {
esyslog("[softhddev]FT_Stroker_New FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message);
return NULL;
}
float outlineWidth = 0.25f;
FT_Stroker_Set(stroker, (int)(outlineWidth * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
error = FT_Get_Glyph(face->glyph, &ftGlyph);
if (error) {
esyslog("[softhddev]FT_Get_Glyph FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message);
return NULL;
}
error = FT_Glyph_StrokeBorder(&ftGlyph, stroker, 0, 1);
if (error) {
esyslog("[softhddev]FT_Glyph_StrokeBorder FT_Error (0x%02x) : %s\n", FT_Errors[error].code,
FT_Errors[error].message);
return NULL;
}
FT_Stroker_Done(stroker);
error = FT_Glyph_To_Bitmap(&ftGlyph, FT_RENDER_MODE_NORMAL, 0, 1);
if (error) {
esyslog("[softhddev]FT_Glyph_To_Bitmap FT_Error (0x%02x) : %s\n", FT_Errors[error].code,
FT_Errors[error].message);
return NULL;
}
cOglGlyph *Glyph = new cOglGlyph(charCode, (FT_BitmapGlyph)ftGlyph);
glyphCache.Add(Glyph);
FT_Done_Glyph(ftGlyph);
return Glyph;
}
int cOglFont::Kerning(cOglGlyph *glyph, FT_ULong prevSym) const {
int kerning = 0;
if (glyph && prevSym) {
kerning = glyph->GetKerningCache(prevSym);
if (kerning == KERNING_UNKNOWN) {
FT_Vector delta;
FT_UInt glyph_index = FT_Get_Char_Index(face, glyph->CharCode());
FT_UInt glyph_index_prev = FT_Get_Char_Index(face, prevSym);
FT_Get_Kerning(face, glyph_index_prev, glyph_index, FT_KERNING_DEFAULT, &delta);
kerning = delta.x / 64;
glyph->SetKerningCache(prevSym, kerning);
}
}
return kerning;
}
/****************************************************************************************
* cOglFb
****************************************************************************************/
cOglFb::cOglFb(GLint width, GLint height, GLint viewPortWidth, GLint viewPortHeight) {
initiated = false;
fb = 0;
texture = 0;
this->width = width;
this->height = height;
this->viewPortWidth = viewPortWidth;
this->viewPortHeight = viewPortHeight;
if (width != viewPortWidth || height != viewPortHeight)
scrollable = true;
else
scrollable = false;
}
cOglFb::~cOglFb(void) {
if (texture)
glDeleteTextures(1, &texture);
if (fb)
glDeleteFramebuffers(1, &fb);
}
bool cOglFb::Init(void) {
initiated = true;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
esyslog("[softhddev]ERROR: %d Framebuffer is not complete!\n", __LINE__);
return false;
}
return true;
}
void cOglFb::Bind(void) {
if (!initiated)
Init();
glViewport(0, 0, width, height);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
}
void cOglFb::BindRead(void) { glBindFramebuffer(GL_READ_FRAMEBUFFER, fb); }
void cOglFb::BindWrite(void) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb); }
void cOglFb::Unbind(void) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, 0);
}
bool cOglFb::BindTexture(void) {
if (!initiated)
return false;
glBindTexture(GL_TEXTURE_2D, texture);
return true;
}
void cOglFb::Blit(GLint destX1, GLint destY1, GLint destX2, GLint destY2) {
glBlitFramebuffer(0, 0, width, height, destX1, destY1, destX2, destY2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glFlush();
}
/****************************************************************************************
* cOglOutputFb
****************************************************************************************/
cOglOutputFb::cOglOutputFb(GLint width, GLint height) : cOglFb(width, height, width, height) {
// surface = 0;
initiated = false;
fb = 0;
texture = 0;
}
cOglOutputFb::~cOglOutputFb(void) {
// glVDPAUUnregisterSurfaceNV(surface);
glDeleteTextures(1, &texture);
glDeleteFramebuffers(1, &fb);
}
bool cOglOutputFb::Init(void) {
initiated = true;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glGenFramebuffers(1, &fb);
glBindFramebuffer(GL_FRAMEBUFFER, fb);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
esyslog("[softhddev]ERROR::cOglOutputFb: Framebuffer is not complete!");
return false;
}
return true;
}
void cOglOutputFb::BindWrite(void) {
if (!initiated)
Init();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb);
}
void cOglOutputFb::Unbind(void) { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
/****************************************************************************************
* cOglVb
****************************************************************************************/
static cOglVb *VertexBuffers[vbCount];
cOglVb::cOglVb(int type) {
this->type = (eVertexBufferType)type;
vao = 0;
vbo = 0;
sizeVertex1 = 0;
sizeVertex2 = 0;
numVertices = 0;
drawMode = 0;
}
cOglVb::~cOglVb(void) {}
bool cOglVb::Init(void) {
if (type == vbTexture) {
// Texture VBO definition
sizeVertex1 = 2;
sizeVertex2 = 2;
numVertices = 6;
drawMode = GL_TRIANGLES;
shader = stTexture;
} else if (type == vbRect) {
// Rectangle VBO definition
sizeVertex1 = 2;
sizeVertex2 = 0;
numVertices = 4;
drawMode = GL_TRIANGLE_FAN;
shader = stRect;
} else if (type == vbEllipse) {
// Ellipse VBO definition
sizeVertex1 = 2;
sizeVertex2 = 0;
numVertices = 182;
drawMode = GL_TRIANGLE_FAN;
shader = stRect;
} else if (type == vbSlope) {
// Slope VBO definition
sizeVertex1 = 2;
sizeVertex2 = 0;
numVertices = 102;
drawMode = GL_TRIANGLE_FAN;
shader = stRect;
} else if (type == vbText) {
// Text VBO definition
sizeVertex1 = 2;
sizeVertex2 = 2;
numVertices = 6;
drawMode = GL_TRIANGLES;
shader = stText;
}
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * (sizeVertex1 + sizeVertex2) * numVertices, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, sizeVertex1, GL_FLOAT, GL_FALSE, (sizeVertex1 + sizeVertex2) * sizeof(GLfloat),
(GLvoid *)0);
if (sizeVertex2 > 0) {
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, sizeVertex2, GL_FLOAT, GL_FALSE, (sizeVertex1 + sizeVertex2) * sizeof(GLfloat),
(GLvoid *)(sizeVertex1 * sizeof(GLfloat)));
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
return true;
}
void cOglVb::Bind(void) { glBindVertexArray(vao); }
void cOglVb::Unbind(void) { glBindVertexArray(0); }
void cOglVb::ActivateShader(void) { Shaders[shader]->Use(); }
void cOglVb::EnableBlending(void) {
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
void cOglVb::DisableBlending(void) { glDisable(GL_BLEND); }
void cOglVb::SetShaderColor(GLint color) {
glm::vec4 col;
ConvertColor(color, col);
Shaders[shader]->SetVector4f("inColor", col.r, col.g, col.b, col.a);
}
void cOglVb::SetShaderAlpha(GLint alpha) {
Shaders[shader]->SetVector4f("alpha", 1.0f, 1.0f, 1.0f, (GLfloat)(alpha) / 255.0f);
}
void cOglVb::SetShaderProjectionMatrix(GLint width, GLint height) {
glm::mat4 projection = glm::ortho(0.0f, (GLfloat)width, (GLfloat)height, 0.0f, -1.0f, 1.0f);
Shaders[shader]->SetMatrix4("projection", projection);
}
void cOglVb::SetVertexData(GLfloat *vertices, int count) {
if (count == 0)
count = numVertices;
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * (sizeVertex1 + sizeVertex2) * count, vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void cOglVb::DrawArrays(int count) {
if (count == 0)
count = numVertices;
glDrawArrays(drawMode, 0, count);
glFlush();
}
/****************************************************************************************
* cOpenGLCmd
****************************************************************************************/
//------------------ cOglCmdInitOutputFb --------------------
cOglCmdInitOutputFb::cOglCmdInitOutputFb(cOglOutputFb *oFb) : cOglCmd(NULL) { this->oFb = oFb; }
bool cOglCmdInitOutputFb::Execute(void) {
bool ok = oFb->Init();
oFb->Unbind();
return ok;
}
//------------------ cOglCmdInitFb --------------------
cOglCmdInitFb::cOglCmdInitFb(cOglFb *fb, cCondWait *wait) : cOglCmd(fb) { this->wait = wait; }
bool cOglCmdInitFb::Execute(void) {
bool ok = fb->Init();
fb->Unbind();
if (wait)
wait->Signal();
return ok;
}
//------------------ cOglCmdDeleteFb --------------------
cOglCmdDeleteFb::cOglCmdDeleteFb(cOglFb *fb) : cOglCmd(fb) {}
bool cOglCmdDeleteFb::Execute(void) {
if (fb)
delete fb;
return true;
}
//------------------ cOglCmdRenderFbToBufferFb --------------------
cOglCmdRenderFbToBufferFb::cOglCmdRenderFbToBufferFb(cOglFb *fb, cOglFb *buffer, GLint x, GLint y, GLint transparency,
GLint drawPortX, GLint drawPortY)
: cOglCmd(fb) {
this->buffer = buffer;
this->x = (GLfloat)x;
this->y = (GLfloat)y;
this->drawPortX = (GLfloat)drawPortX;
this->drawPortY = (GLfloat)drawPortY;
this->transparency = transparency;
}
bool cOglCmdRenderFbToBufferFb::Execute(void) {
GLfloat x2 = x + fb->ViewportWidth(); // right
GLfloat y2 = y + fb->ViewportHeight(); // bottom
GLfloat texX1 = drawPortX / (GLfloat)fb->Width();
GLfloat texY1 = drawPortY / (GLfloat)fb->Height();
GLfloat texX2 = texX1 + 1.0f;
GLfloat texY2 = texY1 + 1.0f;
if (fb->Scrollable()) {
GLfloat pageHeight = (GLfloat)fb->ViewportHeight() / (GLfloat)fb->Height();
texX1 = abs(drawPortX) / (GLfloat)fb->Width();
texY1 = 1.0f - pageHeight - abs(drawPortY) / (GLfloat)fb->Height();
texX2 = texX1 + (GLfloat)fb->ViewportWidth() / (GLfloat)fb->Width();
// x2 = x + fb->Width();
texY2 = texY1 + pageHeight;
}
GLfloat quadVertices[] = {
// Pos // TexCoords
x, y, texX1, texY2, // left top
x, y2, texX1, texY1, // left bottom
x2, y2, texX2, texY1, // right bottom
x, y, texX1, texY2, // left top
x2, y2, texX2, texY1, // right bottom
x2, y, texX2, texY2 // right top
};
VertexBuffers[vbTexture]->ActivateShader();
VertexBuffers[vbTexture]->SetShaderAlpha(transparency);
VertexBuffers[vbTexture]->SetShaderProjectionMatrix(buffer->Width(), buffer->Height());
buffer->Bind();
if (!fb->BindTexture())
return false;
VertexBuffers[vbTexture]->Bind();
VertexBuffers[vbTexture]->SetVertexData(quadVertices);
VertexBuffers[vbTexture]->DrawArrays();
VertexBuffers[vbTexture]->Unbind();
buffer->Unbind();
return true;
}
//------------------ cOglCmdCopyBufferToOutputFb --------------------
cOglCmdCopyBufferToOutputFb::cOglCmdCopyBufferToOutputFb(cOglFb *fb, cOglOutputFb *oFb, GLint x, GLint y)
: cOglCmd(fb) {
this->oFb = oFb;
this->x = x;
this->y = y;
}
extern unsigned char *posd;
bool cOglCmdCopyBufferToOutputFb::Execute(void) {
pthread_mutex_lock(&OSDMutex);
fb->BindRead();
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
if (posd)
glReadPixels(0, 0, fb->Width(), fb->Height(), GL_RGBA, GL_UNSIGNED_BYTE, posd);
glFlush();
pthread_mutex_unlock(&OSDMutex);
ActivateOsd(oFb->texture, x, y, fb->Width(), fb->Height());
return true;
}
//------------------ cOglCmdFill --------------------
cOglCmdFill::cOglCmdFill(cOglFb *fb, GLint color) : cOglCmd(fb) { this->color = color; }
bool cOglCmdFill::Execute(void) {
glm::vec4 col;
ConvertColor(color, col);
fb->Bind();
glClearColor(col.r, col.g, col.b, col.a);
glClear(GL_COLOR_BUFFER_BIT);
fb->Unbind();
return true;
}
//------------------ cOglCmdDrawRectangle --------------------
cOglCmdDrawRectangle::cOglCmdDrawRectangle(cOglFb *fb, GLint x, GLint y, GLint width, GLint height, GLint color)
: cOglCmd(fb) {
this->x = x;
this->y = y;
this->width = width;
this->height = height;
this->color = color;
}
bool cOglCmdDrawRectangle::Execute(void) {
GLfloat x1 = x;
GLfloat y1 = y;
GLfloat x2 = x + width;
GLfloat y2 = y + height;
GLfloat vertices[] = {
x1, y1, // left top
x2, y1, // right top
x2, y2, // right bottom
x1, y2 // left bottom
};
VertexBuffers[vbRect]->ActivateShader();
VertexBuffers[vbRect]->SetShaderColor(color);
VertexBuffers[vbRect]->SetShaderProjectionMatrix(fb->Width(), fb->Height());
fb->Bind();
VertexBuffers[vbRect]->DisableBlending();
VertexBuffers[vbRect]->Bind();
VertexBuffers[vbRect]->SetVertexData(vertices);
VertexBuffers[vbRect]->DrawArrays();
VertexBuffers[vbRect]->Unbind();
VertexBuffers[vbRect]->EnableBlending();
fb->Unbind();
return true;
}
//------------------ cOglCmdDrawEllipse --------------------
/// quadrants:
///< 0 draws the entire ellipse
///< 1..4 draws only the first, second, third or fourth quadrant,
///< respectively 5..8 draws the right, top, left or bottom half,
///< respectively -1..-4 draws the inverted part of the given quadrant
cOglCmdDrawEllipse::cOglCmdDrawEllipse(cOglFb *fb, GLint x, GLint y, GLint width, GLint height, GLint color,
GLint quadrants)
: cOglCmd(fb) {
this->x = x;
this->y = y;
this->width = width;
this->height = height;
this->color = color;
this->quadrants = quadrants;
}
bool cOglCmdDrawEllipse::Execute(void) {
int numVertices = 0;
GLfloat *vertices = NULL;
switch (quadrants) {
case 0:
vertices = CreateVerticesFull(numVertices);
break;
case 1:
case 2:
case 3:
case 4:
case -1:
case -2:
case -3:
case -4:
vertices = CreateVerticesQuadrant(numVertices);
break;
case 5:
case 6:
case 7:
case 8:
vertices = CreateVerticesHalf(numVertices);
break;
default:
break;
}
VertexBuffers[vbEllipse]->ActivateShader();
VertexBuffers[vbEllipse]->SetShaderColor(color);
VertexBuffers[vbEllipse]->SetShaderProjectionMatrix(fb->Width(), fb->Height());
// not antialiased
fb->Bind();
VertexBuffers[vbEllipse]->DisableBlending();
VertexBuffers[vbEllipse]->Bind();
VertexBuffers[vbEllipse]->SetVertexData(vertices, numVertices);
VertexBuffers[vbEllipse]->DrawArrays(numVertices);
VertexBuffers[vbEllipse]->Unbind();
VertexBuffers[vbEllipse]->EnableBlending();
fb->Unbind();
delete[] vertices;
return true;
}
GLfloat *cOglCmdDrawEllipse::CreateVerticesFull(int &numVertices) {
int size = 364;
numVertices = size / 2;
GLfloat radiusX = (GLfloat)width / 2;
GLfloat radiusY = (GLfloat)height / 2;
GLfloat *vertices = new GLfloat[size];
vertices[0] = x + radiusX;
vertices[1] = y + radiusY;
for (int i = 0; i <= 180; i++) {
vertices[2 * i + 2] = x + radiusX + (GLfloat)cos(2 * i * M_PI / 180.0f) * radiusX;
vertices[2 * i + 3] = y + radiusY - (GLfloat)sin(2 * i * M_PI / 180.0f) * radiusY;
}
return vertices;
}
GLfloat *cOglCmdDrawEllipse::CreateVerticesQuadrant(int &numVertices) {
int size = 94;
numVertices = size / 2;
GLfloat radiusX = (GLfloat)width;
GLfloat radiusY = (GLfloat)height;
GLint transX = 0;
GLint transY = 0;
GLint startAngle = 0;
GLfloat *vertices = new GLfloat[size];
switch (quadrants) {
case 1:
vertices[0] = x;
vertices[1] = y + height;
transY = radiusY;
break;
case 2:
vertices[0] = x + width;
vertices[1] = y + height;
transX = radiusX;
transY = radiusY;
startAngle = 90;
break;
case 3:
vertices[0] = x + width;
vertices[1] = y;
transX = radiusX;
startAngle = 180;
break;
case 4:
vertices[0] = x;
vertices[1] = y;
startAngle = 270;
break;
case -1:
vertices[0] = x + width;
vertices[1] = y;
transY = radiusY;
break;
case -2:
vertices[0] = x;
vertices[1] = y;
transX = radiusX;
transY = radiusY;
startAngle = 90;
break;
case -3:
vertices[0] = x;
vertices[1] = y + height;
transX = radiusX;
startAngle = 180;
break;
case -4:
vertices[0] = x + width;
vertices[1] = y + height;
startAngle = 270;
break;
default:
break;
}
for (int i = 0; i <= 45; i++) {
vertices[2 * i + 2] = x + transX + (GLfloat)cos((2 * i + startAngle) * M_PI / 180.0f) * radiusX;
vertices[2 * i + 3] = y + transY - (GLfloat)sin((2 * i + startAngle) * M_PI / 180.0f) * radiusY;
}
return vertices;
}
GLfloat *cOglCmdDrawEllipse::CreateVerticesHalf(int &numVertices) {
int size = 184;
numVertices = size / 2;
GLfloat radiusX = 0.0f;
GLfloat radiusY = 0.0f;
GLint transX = 0;
GLint transY = 0;
GLint startAngle = 0;
GLfloat *vertices = new GLfloat[size];
switch (quadrants) {
case 5:
radiusX = (GLfloat)width;
radiusY = (GLfloat)height / 2;
vertices[0] = x;
vertices[1] = y + radiusY;
startAngle = 270;
transY = radiusY;
break;
case 6:
radiusX = (GLfloat)width / 2;
radiusY = (GLfloat)height;
vertices[0] = x + radiusX;
vertices[1] = y + radiusY;
startAngle = 0;
transX = radiusX;
transY = radiusY;
break;
case 7:
radiusX = (GLfloat)width;
radiusY = (GLfloat)height / 2;
vertices[0] = x + radiusX;
vertices[1] = y + radiusY;
startAngle = 90;
transX = radiusX;
transY = radiusY;
break;
case 8:
radiusX = (GLfloat)width / 2;
radiusY = (GLfloat)height;
vertices[0] = x + radiusX;
vertices[1] = y;
startAngle = 180;
transX = radiusX;
break;
default:
break;
}
for (int i = 0; i <= 90; i++) {
vertices[2 * i + 2] = x + transX + (GLfloat)cos((2 * i + startAngle) * M_PI / 180.0f) * radiusX;
vertices[2 * i + 3] = y + transY - (GLfloat)sin((2 * i + startAngle) * M_PI / 180.0f) * radiusY;
}
return vertices;
}
//------------------ cOglCmdDrawSlope --------------------
/// type:
///< 0: horizontal, rising, lower
///< 1: horizontal, rising, upper
///< 2: horizontal, falling, lower
///< 3: horizontal, falling, upper
///< 4: vertical, rising, lower
///< 5: vertical, rising, upper
///< 6: vertical, falling, lower
///< 7: vertical, falling, upper
cOglCmdDrawSlope::cOglCmdDrawSlope(cOglFb *fb, GLint x, GLint y, GLint width, GLint height, GLint color, GLint type)
: cOglCmd(fb) {
this->x = x;
this->y = y;
this->width = width;
this->height = height;
this->color = color;
this->type = type;
}
bool cOglCmdDrawSlope::Execute(void) {
bool falling = type & 0x02;
bool vertical = type & 0x04;
int steps = 100;
if (width < 100)
steps = 25;
int numVertices = steps + 2;
GLfloat *vertices = new GLfloat[numVertices * 2];
switch (type) {
case 0:
case 4:
vertices[0] = (GLfloat)(x + width);
vertices[1] = (GLfloat)(y + height);
break;
case 1:
case 5:
vertices[0] = (GLfloat)x;
vertices[1] = (GLfloat)y;
break;
case 2:
case 6:
vertices[0] = (GLfloat)x;
vertices[1] = (GLfloat)(y + height);
break;
case 3:
case 7:
vertices[0] = (GLfloat)(x + width);
vertices[1] = (GLfloat)y;
break;
default:
vertices[0] = (GLfloat)(x);
vertices[1] = (GLfloat)(y);
break;
}
for (int i = 0; i <= steps; i++) {
GLfloat c = cos(i * M_PI / steps);
if (falling)
c = -c;
if (vertical) {
vertices[2 * i + 2] = (GLfloat)x + (GLfloat)width / 2.0f + (GLfloat)width * c / 2.0f;
vertices[2 * i + 3] = (GLfloat)y + (GLfloat)i * ((GLfloat)height) / steps;
} else {
vertices[2 * i + 2] = (GLfloat)x + (GLfloat)i * ((GLfloat)width) / steps;
vertices[2 * i + 3] = (GLfloat)y + (GLfloat)height / 2.0f + (GLfloat)height * c / 2.0f;
}
}
VertexBuffers[vbSlope]->ActivateShader();
VertexBuffers[vbSlope]->SetShaderColor(color);
VertexBuffers[vbSlope]->SetShaderProjectionMatrix(fb->Width(), fb->Height());
// not antialiased
fb->Bind();
VertexBuffers[vbSlope]->DisableBlending();
VertexBuffers[vbSlope]->Bind();
VertexBuffers[vbSlope]->SetVertexData(vertices, numVertices);
VertexBuffers[vbSlope]->DrawArrays(numVertices);
VertexBuffers[vbSlope]->Unbind();
VertexBuffers[vbSlope]->EnableBlending();
fb->Unbind();
delete[] vertices;
return true;
}
//------------------ cOglCmdDrawText --------------------
cOglCmdDrawText::cOglCmdDrawText(cOglFb *fb, GLint x, GLint y, unsigned int *symbols, GLint limitX, const char *name,
int fontSize, tColor colorText)
: cOglCmd(fb), fontName(name) {
this->x = x;
this->y = y;
this->limitX = limitX;
this->colorText = colorText;
this->fontSize = fontSize;
this->symbols = symbols;
this->fontName = name;
}
cOglCmdDrawText::~cOglCmdDrawText(void) { free(symbols); }
bool cOglCmdDrawText::Execute(void) {
cOglFont *f = cOglFont::Get(*fontName, fontSize);
if (!f)
return false;
VertexBuffers[vbText]->ActivateShader();
VertexBuffers[vbText]->SetShaderColor(colorText);
VertexBuffers[vbText]->SetShaderProjectionMatrix(fb->Width(), fb->Height());
fb->Bind();
VertexBuffers[vbText]->Bind();
int xGlyph = x;
int fontHeight = f->Height();
int bottom = f->Bottom();
FT_ULong sym = 0;
FT_ULong prevSym = 0;
int kerning = 0;
for (int i = 0; symbols[i]; i++) {
sym = symbols[i];
cOglGlyph *g = f->Glyph(sym);
if (!g) {
esyslog("[softhddev]ERROR: could not load glyph %lx", sym);
}
if (limitX && xGlyph + g->AdvanceX() > limitX) {
break;
}
kerning = f->Kerning(g, prevSym);
prevSym = sym;
GLfloat x1 = xGlyph + kerning + g->BearingLeft(); // left
GLfloat y1 = y + (fontHeight - bottom - g->BearingTop()); // top
GLfloat x2 = x1 + g->Width(); // right
GLfloat y2 = y1 + g->Height(); // bottom
GLfloat vertices[] = {
x1, y2, 0.0, 1.0, // left bottom
x1, y1, 0.0, 0.0, // left top
x2, y1, 1.0, 0.0, // right top
x1, y2, 0.0, 1.0, // left bottom
x2, y1, 1.0, 0.0, // right top
x2, y2, 1.0, 1.0 // right bottom
};
g->BindTexture();
VertexBuffers[vbText]->SetVertexData(vertices);
VertexBuffers[vbText]->DrawArrays();
xGlyph += kerning + g->AdvanceX();
if (xGlyph > fb->Width() - 1)
break;
}
glBindTexture(GL_TEXTURE_2D, 0);
VertexBuffers[vbText]->Unbind();
fb->Unbind();
return true;
}
//------------------ cOglCmdDrawImage --------------------
cOglCmdDrawImage::cOglCmdDrawImage(cOglFb *fb, tColor *argb, GLint width, GLint height, GLint x, GLint y, bool overlay,
double scaleX, double scaleY)
: cOglCmd(fb) {
this->argb = argb;
this->x = x;
this->y = y;
this->width = width;
this->height = height;
this->overlay = overlay;
this->scaleX = scaleX;
this->scaleY = scaleY;
}
cOglCmdDrawImage::~cOglCmdDrawImage(void) { free(argb); }
bool cOglCmdDrawImage::Execute(void) {
GLuint texture;
#ifdef USE_DRM
// pthread_mutex_lock(&OSDMutex);
GlxDrawopengl(); // here we need the Shared Context for upload
GlxCheck();
#endif
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, argb);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glFlush();
#ifdef USE_DRM
GlxInitopengl(); // Reset Context
GlxCheck();
// pthread_mutex_unlock(&OSDMutex);
#endif
GLfloat x1 = x; // left
GLfloat y1 = y; // top
GLfloat x2 = x + width; // right
GLfloat y2 = y + height; // bottom
GLfloat quadVertices[] = {
x1, y2, 0.0, 1.0, // left bottom
x1, y1, 0.0, 0.0, // left top
x2, y1, 1.0, 0.0, // right top
x1, y2, 0.0, 1.0, // left bottom
x2, y1, 1.0, 0.0, // right top
x2, y2, 1.0, 1.0 // right bottom
};
VertexBuffers[vbTexture]->ActivateShader();
VertexBuffers[vbTexture]->SetShaderAlpha(255);
VertexBuffers[vbTexture]->SetShaderProjectionMatrix(fb->Width(), fb->Height());
fb->Bind();
glBindTexture(GL_TEXTURE_2D, texture);
if (overlay)
VertexBuffers[vbTexture]->DisableBlending();
VertexBuffers[vbTexture]->Bind();
VertexBuffers[vbTexture]->SetVertexData(quadVertices);
VertexBuffers[vbTexture]->DrawArrays();
VertexBuffers[vbTexture]->Unbind();
if (overlay)
VertexBuffers[vbTexture]->EnableBlending();
fb->Unbind();
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(1, &texture);
return true;
}
//------------------ cOglCmdDrawTexture --------------------
cOglCmdDrawTexture::cOglCmdDrawTexture(cOglFb *fb, sOglImage *imageRef, GLint x, GLint y) : cOglCmd(fb) {
this->imageRef = imageRef;
this->x = x;
this->y = y;
}
bool cOglCmdDrawTexture::Execute(void) {
GLfloat x1 = x; // top
GLfloat y1 = y; // left
GLfloat x2 = x + imageRef->width; // right
GLfloat y2 = y + imageRef->height; // bottom
GLfloat quadVertices[] = {
// Pos // TexCoords
x1, y1, 0.0f, 0.0f, // left bottom
x1, y2, 0.0f, 1.0f, // left top
x2, y2, 1.0f, 1.0f, // right top
x1, y1, 0.0f, 0.0f, // left bottom
x2, y2, 1.0f, 1.0f, // right top
x2, y1, 1.0f, 0.0f // right bottom
};
VertexBuffers[vbTexture]->ActivateShader();
VertexBuffers[vbTexture]->SetShaderAlpha(255);
VertexBuffers[vbTexture]->SetShaderProjectionMatrix(fb->Width(), fb->Height());
fb->Bind();
glBindTexture(GL_TEXTURE_2D, imageRef->texture);
VertexBuffers[vbTexture]->Bind();
VertexBuffers[vbTexture]->SetVertexData(quadVertices);
VertexBuffers[vbTexture]->DrawArrays();
VertexBuffers[vbTexture]->Unbind();
fb->Unbind();
return true;
}
//------------------ cOglCmdStoreImage --------------------
cOglCmdStoreImage::cOglCmdStoreImage(sOglImage *imageRef, tColor *argb) : cOglCmd(NULL) {
this->imageRef = imageRef;
data = argb;
}
cOglCmdStoreImage::~cOglCmdStoreImage(void) { free(data); }
bool cOglCmdStoreImage::Execute(void) {
#ifdef USE_DRM
// pthread_mutex_lock(&OSDMutex);
GlxDrawopengl(); // here we need the Shared Context for upload
GlxCheck();
#endif
glGenTextures(1, &imageRef->texture);
glBindTexture(GL_TEXTURE_2D, imageRef->texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, imageRef->width, imageRef->height, 0, GL_BGRA,
GL_UNSIGNED_INT_8_8_8_8_REV, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
glFlush();
#ifdef USE_DRM
GlxInitopengl(); // Reset Context
GlxCheck();
// pthread_mutex_lock(&OSDMutex);
#endif
return true;
}
//------------------ cOglCmdDropImage --------------------
cOglCmdDropImage::cOglCmdDropImage(sOglImage *imageRef, cCondWait *wait) : cOglCmd(NULL) {
this->imageRef = imageRef;
this->wait = wait;
}
bool cOglCmdDropImage::Execute(void) {
if (imageRef->texture != GL_NONE)
glDeleteTextures(1, &imageRef->texture);
wait->Signal();
return true;
}
/******************************************************************************
* cOglThread
******************************************************************************/
cOglThread::cOglThread(cCondWait *startWait, int maxCacheSize) : cThread("oglThread") {
stalled = false;
memCached = 0;
this->maxCacheSize = maxCacheSize * 1024 * 1024;
this->startWait = startWait;
wait = new cCondWait();
maxTextureSize = 0;
for (int i = 0; i < OGL_MAX_OSDIMAGES; i++) {
imageCache[i].used = false;
imageCache[i].texture = GL_NONE;
imageCache[i].width = 0;
imageCache[i].height = 0;
}
Start();
}
cOglThread::~cOglThread() {
delete wait;
wait = NULL;
}
void cOglThread::Stop(void) {
for (int i = 0; i < OGL_MAX_OSDIMAGES; i++) {
if (imageCache[i].used) {
DropImageData(i);
}
}
Cancel(2);
stalled = false;
}
void cOglThread::DoCmd(cOglCmd *cmd) {
while (stalled)
cCondWait::SleepMs(10);
bool doSignal = false;
Lock();
if (commands.size() == 0)
doSignal = true;
commands.push(cmd);
Unlock();
if (commands.size() > OGL_CMDQUEUE_SIZE) {
stalled = true;
}
if (doSignal || stalled)
wait->Signal();
}
int cOglThread::StoreImage(const cImage &image) {
if (!maxCacheSize) {
return 0;
}
if (image.Width() > maxTextureSize || image.Height() > maxTextureSize) {
esyslog("[softhddev] cannot store image of %dpx x %dpx "
"(maximum size is %dpx x %dpx) - falling back to "
"cOsdProvider::StoreImageData()",
image.Width(), image.Height(), maxTextureSize, maxTextureSize);
return 0;
}
int imgSize = image.Width() * image.Height();
int newMemUsed = imgSize * sizeof(tColor) + memCached;
if (newMemUsed > maxCacheSize) {
float cachedMB = memCached / 1024.0f / 1024.0f;
float maxMB = maxCacheSize / 1024.0f / 1024.0f;
esyslog("[softhddev]Maximum size for GPU cache reached. Used: %.2fMB Max: "
"%.2fMB",
cachedMB, maxMB);
return 0;
}
int slot = GetFreeSlot();
if (!slot)
return 0;
tColor *argb = MALLOC(tColor, imgSize);
if (!argb) {
esyslog("[softhddev]memory allocation of %ld kb for OSD image failed", (imgSize * sizeof(tColor)) / 1024);
ClearSlot(slot);
slot = 0;
return 0;
}
memcpy(argb, image.Data(), sizeof(tColor) * imgSize);
sOglImage *imageRef = GetImageRef(slot);
imageRef->width = image.Width();
imageRef->height = image.Height();
DoCmd(new cOglCmdStoreImage(imageRef, argb));
cTimeMs timer(5000);
while (imageRef->used && imageRef->texture == 0 && !timer.TimedOut())
cCondWait::SleepMs(2);
if (imageRef->texture == GL_NONE) {
esyslog("[softhddev]failed to store OSD image texture! (%s)",
timer.TimedOut() ? "timed out" : "allocation failed");
DropImageData(slot);
slot = 0;
}
memCached += imgSize * sizeof(tColor);
return slot;
}
int cOglThread::GetFreeSlot(void) {
Lock();
int slot = 0;
for (int i = 0; i < OGL_MAX_OSDIMAGES && !slot; i++) {
if (!imageCache[i].used) {
imageCache[i].used = true;
slot = -i - 1;
}
}
Unlock();
return slot;
}
void cOglThread::ClearSlot(int slot) {
int i = -slot - 1;
if (i >= 0 && i < OGL_MAX_OSDIMAGES) {
Lock();
imageCache[i].used = false;
imageCache[i].texture = GL_NONE;
imageCache[i].width = 0;
imageCache[i].height = 0;
Unlock();
}
}
sOglImage *cOglThread::GetImageRef(int slot) {
int i = -slot - 1;
if (0 <= i && i < OGL_MAX_OSDIMAGES)
return &imageCache[i];
return 0;
}
void cOglThread::DropImageData(int imageHandle) {
sOglImage *imageRef = GetImageRef(imageHandle);
if (!imageRef)
return;
int imgSize = imageRef->width * imageRef->height * sizeof(tColor);
memCached -= imgSize;
cCondWait dropWait;
DoCmd(new cOglCmdDropImage(imageRef, &dropWait));
dropWait.Wait();
ClearSlot(imageHandle);
}
void cOglThread::Action(void) {
if (!InitOpenGL()) {
esyslog("[softhddev]Could not initiate OpenGL Context");
Cleanup();
startWait->Signal();
return;
}
dsyslog("[softhddev]OpenGL Context initialized");
if (!InitShaders()) {
esyslog("[softhddev]Could not initiate Shaders");
Cleanup();
startWait->Signal();
return;
}
dsyslog("[softhddev]Shaders initialized");
if (!InitVdpauInterop()) {
esyslog("[softhddev]: vdpau interop NOT initialized");
Cleanup();
startWait->Signal();
return;
}
dsyslog("[softhddev]vdpau interop initialized");
if (!InitVertexBuffers()) {
esyslog("[softhddev]: Vertex Buffers NOT initialized");
Cleanup();
startWait->Signal();
return;
}
dsyslog("[softhddev]Vertex buffers initialized");
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
dsyslog("[softhddev]Maximum Pixmap size: %dx%dpx", maxTextureSize, maxTextureSize);
// now Thread is ready to do his job
startWait->Signal();
stalled = false;
while (Running()) {
if (commands.empty()) {
wait->Wait(20);
continue;
}
Lock();
cOglCmd *cmd = commands.front();
commands.pop();
Unlock();
// uint64_t start = cTimeMs::Now();
cmd->Execute();
// esyslog("[softhddev]\"%s\", %dms, %d commands left, time %" PRIu64 "",
// cmd->Description(), (int)(cTimeMs::Now() - start), commands.size(),
// cTimeMs::Now());
delete cmd;
if (stalled && commands.size() < OGL_CMDQUEUE_SIZE / 2)
stalled = false;
}
dsyslog("[softhddev]Cleaning up OpenGL stuff");
Cleanup();
dsyslog("[softhddev]OpenGL Worker Thread Ended");
}
bool cOglThread::InitOpenGL(void) {
#ifdef USE_DRM
GlxInitopengl();
#else
const char *displayName = X11DisplayName;
if (!displayName) {
displayName = getenv("DISPLAY");
if (!displayName) {
displayName = ":0.0";
}
}
dsyslog("[softhddev]OpenGL using display %s", displayName);
int argc = 3;
char *buffer[3];
buffer[0] = strdup("openglosd");
buffer[1] = strdup("-display");
buffer[2] = strdup(displayName);
char **argv = buffer;
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_ALPHA);
glutInitWindowSize(1, 1);
glutInitWindowPosition(0, 0);
glutCreateWindow("openglosd");
glutHideWindow();
free(buffer[0]);
free(buffer[1]);
free(buffer[2]);
GLenum err = glewInit();
if (err != GLEW_OK) {
esyslog("[softhddev]glewInit failed: %s Using Version: %s\n", glewGetErrorString(err),
glewGetString(GLEW_VERSION));
// return false;
}
#endif
VertexBuffers[vbText]->EnableBlending();
glDisable(GL_DEPTH_TEST);
return true;
}
bool cOglThread::InitShaders(void) {
for (int i = 0; i < stCount; i++) {
cShader *shader = new cShader();
if (!shader->Load((eShaderType)i))
return false;
Shaders[i] = shader;
}
return true;
}
void cOglThread::DeleteShaders(void) {
for (int i = 0; i < stCount; i++)
delete Shaders[i];
}
bool cOglThread::InitVdpauInterop(void) {
#if 0
void *vdpDevice = GetVDPAUDevice();
void *procAdress = GetVDPAUProcAdress();
while (glGetError() != GL_NO_ERROR) ;
glVDPAUInitNV(vdpDevice, procAdress);
if (glGetError() != GL_NO_ERROR)
return false;
#endif
return true;
}
bool cOglThread::InitVertexBuffers(void) {
for (int i = 0; i < vbCount; i++) {
cOglVb *vb = new cOglVb(i);
if (!vb->Init())
return false;
VertexBuffers[i] = vb;
}
return true;
}
void cOglThread::DeleteVertexBuffers(void) {
for (int i = 0; i < vbCount; i++) {
delete VertexBuffers[i];
}
}
void cOglThread::Cleanup(void) {
esyslog("[softhddev]OglThread cleanup\n");
pthread_mutex_lock(&OSDMutex);
OsdClose();
DeleteVertexBuffers();
delete cOglOsd::oFb;
cOglOsd::oFb = NULL;
DeleteShaders();
// glVDPAUFiniNV();
cOglFont::Cleanup();
#ifndef USE_DRM
glutExit();
#else
GlxDestroy();
#endif
pthread_mutex_unlock(&OSDMutex);
}
/****************************************************************************************
* cOglPixmap
****************************************************************************************/
cOglPixmap::cOglPixmap(std::shared_ptr<cOglThread> oglThread, int Layer, const cRect &ViewPort, const cRect &DrawPort)
: cPixmap(Layer, ViewPort, DrawPort) {
this->oglThread = oglThread;
int width = DrawPort.IsEmpty() ? ViewPort.Width() : DrawPort.Width();
int height = DrawPort.IsEmpty() ? ViewPort.Height() : DrawPort.Height();
fb = new cOglFb(width, height, ViewPort.Width(), ViewPort.Height());
dirty = true;
}
cOglPixmap::~cOglPixmap(void) {
if (!oglThread->Active())
return;
oglThread->DoCmd(new cOglCmdDeleteFb(fb));
}
void cOglPixmap::SetAlpha(int Alpha) {
Alpha = constrain(Alpha, ALPHA_TRANSPARENT, ALPHA_OPAQUE);
if (Alpha != cPixmap::Alpha()) {
cPixmap::SetAlpha(Alpha);
SetDirty();
}
}
void cOglPixmap::SetTile(bool Tile) {
cPixmap::SetTile(Tile);
SetDirty();
}
void cOglPixmap::SetViewPort(const cRect &Rect) {
cPixmap::SetViewPort(Rect);
SetDirty();
}
void cOglPixmap::SetDrawPortPoint(const cPoint &Point, bool Dirty) {
cPixmap::SetDrawPortPoint(Point, Dirty);
if (Dirty)
SetDirty();
}
void cOglPixmap::Clear(void) {
if (!oglThread->Active())
return;
LOCK_PIXMAPS;
oglThread->DoCmd(new cOglCmdFill(fb, clrTransparent));
SetDirty();
MarkDrawPortDirty(DrawPort());
}
void cOglPixmap::Fill(tColor Color) {
if (!oglThread->Active())
return;
LOCK_PIXMAPS;
oglThread->DoCmd(new cOglCmdFill(fb, Color));
SetDirty();
MarkDrawPortDirty(DrawPort());
}
void cOglPixmap::DrawImage(const cPoint &Point, const cImage &Image) {
if (!oglThread->Active())
return;
tColor *argb = MALLOC(tColor, Image.Width() * Image.Height());
if (!argb)
return;
memcpy(argb, Image.Data(), sizeof(tColor) * Image.Width() * Image.Height());
oglThread->DoCmd(new cOglCmdDrawImage(fb, argb, Image.Width(), Image.Height(), Point.X(), Point.Y()));
SetDirty();
MarkDrawPortDirty(cRect(Point, cSize(Image.Width(), Image.Height())).Intersected(DrawPort().Size()));
}
void cOglPixmap::DrawImage(const cPoint &Point, int ImageHandle) {
if (!oglThread->Active())
return;
if (ImageHandle < 0 && oglThread->GetImageRef(ImageHandle)) {
sOglImage *img = oglThread->GetImageRef(ImageHandle);
oglThread->DoCmd(new cOglCmdDrawTexture(fb, img, Point.X(), Point.Y()));
}
/*
Fallback to VDR implementation, needs to separate cSoftOsdProvider from
softhddevice.cpp else { if (cSoftOsdProvider::GetImageData(ImageHandle))
DrawImage(Point, *cSoftOsdProvider::GetImageData(ImageHandle));
}
*/
SetDirty();
MarkDrawPortDirty(DrawPort());
}
void cOglPixmap::DrawPixel(const cPoint &Point, tColor Color) {
cRect r(Point.X(), Point.Y(), 1, 1);
oglThread->DoCmd(new cOglCmdDrawRectangle(fb, r.X(), r.Y(), r.Width(), r.Height(), Color));
SetDirty();
MarkDrawPortDirty(r);
}
void cOglPixmap::DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool Overlay) {
if (!oglThread->Active())
return;
LOCK_PIXMAPS;
bool specialColors = ColorFg || ColorBg;
tColor *argb = MALLOC(tColor, Bitmap.Width() * Bitmap.Height());
if (!argb)
return;
tColor *p = argb;
for (int py = 0; py < Bitmap.Height(); py++)
for (int px = 0; px < Bitmap.Width(); px++) {
tIndex index = *Bitmap.Data(px, py);
*p++ = (!index && Overlay) ? clrTransparent
: (specialColors ? (index == 0 ? ColorBg
: index == 1 ? ColorFg
: Bitmap.Color(index))
: Bitmap.Color(index));
}
oglThread->DoCmd(new cOglCmdDrawImage(fb, argb, Bitmap.Width(), Bitmap.Height(), Point.X(), Point.Y(), true));
SetDirty();
MarkDrawPortDirty(cRect(Point, cSize(Bitmap.Width(), Bitmap.Height())).Intersected(DrawPort().Size()));
}
void cOglPixmap::DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font,
int Width, int Height, int Alignment) {
if (!oglThread->Active())
return;
LOCK_PIXMAPS;
int len = s ? Utf8StrLen(s) : 0;
unsigned int *symbols = MALLOC(unsigned int, len + 1);
if (!symbols)
return;
if (len)
Utf8ToArray(s, symbols, len + 1);
else
symbols[0] = 0;
int x = Point.X();
int y = Point.Y();
int w = Font->Width(s);
int h = Font->Height();
int limitX = 0;
int cw = Width ? Width : w;
int ch = Height ? Height : h;
cRect r(x, y, cw, ch);
if (ColorBg != clrTransparent)
oglThread->DoCmd(new cOglCmdDrawRectangle(fb, r.X(), r.Y(), r.Width(), r.Height(), ColorBg));
if (Width || Height) {
limitX = x + cw;
if (Width) {
if ((Alignment & taLeft) != 0) {
if ((Alignment & taBorder) != 0)
x += std::max(h / TEXT_ALIGN_BORDER, 1);
} else if ((Alignment & taRight) != 0) {
if (w < Width)
x += Width - w;
if ((Alignment & taBorder) != 0)
x -= std::max(h / TEXT_ALIGN_BORDER, 1);
} else { // taCentered
if (w < Width)
x += (Width - w) / 2;
}
}
if (Height) {
if ((Alignment & taTop) != 0)
;
else if ((Alignment & taBottom) != 0) {
if (h < Height)
y += Height - h;
} else { // taCentered
if (h < Height)
y += (Height - h) / 2;
}
}
}
oglThread->DoCmd(new cOglCmdDrawText(fb, x, y, symbols, limitX, Font->FontName(), Font->Size(), ColorFg));
SetDirty();
MarkDrawPortDirty(r);
}
void cOglPixmap::DrawRectangle(const cRect &Rect, tColor Color) {
if (!oglThread->Active())
return;
LOCK_PIXMAPS;
oglThread->DoCmd(new cOglCmdDrawRectangle(fb, Rect.X(), Rect.Y(), Rect.Width(), Rect.Height(), Color));
SetDirty();
MarkDrawPortDirty(Rect);
}
void cOglPixmap::DrawEllipse(const cRect &Rect, tColor Color, int Quadrants) {
if (!oglThread->Active())
return;
LOCK_PIXMAPS;
oglThread->DoCmd(new cOglCmdDrawEllipse(fb, Rect.X(), Rect.Y(), Rect.Width(), Rect.Height(), Color, Quadrants));
SetDirty();
MarkDrawPortDirty(Rect);
}
void cOglPixmap::DrawSlope(const cRect &Rect, tColor Color, int Type) {
if (!oglThread->Active())
return;
LOCK_PIXMAPS;
oglThread->DoCmd(new cOglCmdDrawSlope(fb, Rect.X(), Rect.Y(), Rect.Width(), Rect.Height(), Color, Type));
SetDirty();
MarkDrawPortDirty(Rect);
}
void cOglPixmap::Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest) {
esyslog("[softhddev] Render %d %d %d not implemented in OpenGl OSD", Pixmap->ViewPort().X(), Source.X(), Dest.X());
}
void cOglPixmap::Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest) {
esyslog("[softhddev] Copy %d %d %d not implemented in OpenGl OSD", Pixmap->ViewPort().X(), Source.X(), Dest.X());
}
void cOglPixmap::Scroll(const cPoint &Dest, const cRect &Source) {
esyslog("[softhddev] Scroll %d %d not implemented in OpenGl OSD", Source.X(), Dest.X());
}
void cOglPixmap::Pan(const cPoint &Dest, const cRect &Source) {
esyslog("[softhddev] Pan %d %d not implemented in OpenGl OSD", Source.X(), Dest.X());
}
/******************************************************************************
* cOglOsd
******************************************************************************/
cOglOutputFb *cOglOsd::oFb = NULL;
cOglOsd::cOglOsd(int Left, int Top, uint Level, std::shared_ptr<cOglThread> oglThread) : cOsd(Left, Top, Level) {
this->oglThread = oglThread;
bFb = NULL;
isSubtitleOsd = false;
int osdWidth = 0;
int osdHeight = 0;
// pthread_mutex_lock(&OSDMutex);
VideoGetOsdSize(&osdWidth, &osdHeight);
// osdWidth = 1920;
// osdHeight = 1080;
dsyslog("[softhddev]cOglOsd osdLeft %d osdTop %d screenWidth %d screenHeight %d", Left, Top, osdWidth, osdHeight);
#if 0
if (posd)
free(posd);
posd = (unsigned char *)calloc(osdWidth * osdHeight * 4, 1);
#endif
// create output framebuffer
if (!oFb) {
oFb = new cOglOutputFb(osdWidth, osdHeight);
oglThread->DoCmd(new cOglCmdInitOutputFb(oFb));
}
// pthread_mutex_unlock(&OSDMutex);
}
cOglOsd::~cOglOsd() {
OsdClose();
SetActive(false);
oglThread->DoCmd(new cOglCmdDeleteFb(bFb));
}
eOsdError cOglOsd::SetAreas(const tArea *Areas, int NumAreas) {
cRect r;
if (NumAreas > 1)
isSubtitleOsd = true;
for (int i = 0; i < NumAreas; i++)
r.Combine(cRect(Areas[i].x1, Areas[i].y1, Areas[i].Width(), Areas[i].Height()));
tArea area = {r.Left(), r.Top(), r.Right(), r.Bottom(), 32};
// now we know the actuaL osd size, create double buffer frame buffer
if (bFb) {
oglThread->DoCmd(new cOglCmdDeleteFb(bFb));
DestroyPixmap(oglPixmaps[0]);
}
bFb = new cOglFb(r.Width(), r.Height(), r.Width(), r.Height());
cCondWait initiated;
oglThread->DoCmd(new cOglCmdInitFb(bFb, &initiated));
initiated.Wait();
return cOsd::SetAreas(&area, 1);
}
cPixmap *cOglOsd::CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort) {
if (!oglThread->Active())
return NULL;
LOCK_PIXMAPS;
int width = DrawPort.IsEmpty() ? ViewPort.Width() : DrawPort.Width();
int height = DrawPort.IsEmpty() ? ViewPort.Height() : DrawPort.Height();
if (width > oglThread->MaxTextureSize() || height > oglThread->MaxTextureSize()) {
esyslog("[softhddev] cannot allocate pixmap of %dpx x %dpx, clipped to "
"%dpx x %dpx!",
width, height, std::min(width, oglThread->MaxTextureSize()),
std::min(height, oglThread->MaxTextureSize()));
width = std::min(width, oglThread->MaxTextureSize());
height = std::min(height, oglThread->MaxTextureSize());
}
cOglPixmap *p = new cOglPixmap(oglThread, Layer, ViewPort, DrawPort);
if (cOsd::AddPixmap(p)) {
// find free slot
for (int i = 0; i < oglPixmaps.Size(); i++)
if (!oglPixmaps[i])
return oglPixmaps[i] = p;
// append at end
oglPixmaps.Append(p);
return p;
}
delete p;
return NULL;
}
void cOglOsd::DestroyPixmap(cPixmap *Pixmap) {
if (!oglThread->Active())
return;
if (!Pixmap)
return;
LOCK_PIXMAPS;
int start = 1;
if (isSubtitleOsd)
start = 0;
for (int i = start; i < oglPixmaps.Size(); i++) {
if (oglPixmaps[i] == Pixmap) {
if (Pixmap->Layer() >= 0)
oglPixmaps[0]->SetDirty();
oglPixmaps[i] = NULL;
cOsd::DestroyPixmap(Pixmap);
return;
}
}
}
void cOglOsd::Flush(void) {
if (!oglThread->Active())
return;
LOCK_PIXMAPS;
// check if any pixmap is dirty
bool dirty = false;
for (int i = 0; i < oglPixmaps.Size() && !dirty; i++)
if (oglPixmaps[i] && oglPixmaps[i]->Layer() >= 0 && oglPixmaps[i]->IsDirty())
dirty = true;
if (!dirty)
return;
// clear buffer
// uint64_t start = cTimeMs::Now();
// dsyslog("[softhddev]Start Flush at %" PRIu64 "", cTimeMs::Now());
oglThread->DoCmd(new cOglCmdFill(bFb, clrTransparent));
// render pixmap textures blended to buffer
for (int layer = 0; layer < MAXPIXMAPLAYERS; layer++) {
for (int i = 0; i < oglPixmaps.Size(); i++) {
if (oglPixmaps[i]) {
if (oglPixmaps[i]->Layer() == layer) {
oglThread->DoCmd(new cOglCmdRenderFbToBufferFb(
oglPixmaps[i]->Fb(), bFb, oglPixmaps[i]->ViewPort().X(),
(!isSubtitleOsd) ? oglPixmaps[i]->ViewPort().Y() : 0, oglPixmaps[i]->Alpha(),
oglPixmaps[i]->DrawPort().X(), oglPixmaps[i]->DrawPort().Y()));
oglPixmaps[i]->SetDirty(false);
}
}
}
}
oglThread->DoCmd(new cOglCmdCopyBufferToOutputFb(bFb, oFb, Left(), Top()));
// dsyslog("[softhddev]End Flush at %" PRIu64 ", duration %d", cTimeMs::Now(),
// (int)(cTimeMs::Now()-start));
}
void cOglOsd::DrawScaledBitmap(int x, int y, const cBitmap &Bitmap, double FactorX, double FactorY, bool AntiAlias) {
(void)FactorX;
(void)FactorY;
(void)AntiAlias;
int yNew = y - oglPixmaps[0]->ViewPort().Y();
oglPixmaps[0]->DrawBitmap(cPoint(x, yNew), Bitmap);
}