vdr-plugin-softhdcuvid/openglosd.cpp

2314 lines
62 KiB
C++

#define __STL_CONFIG_H
#include <algorithm>
#include "openglosd.h"
/****************************************************************************************
* 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, aborting\n");
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(), Overlay));
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);
}