summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Reufer <thomas@reufer.ch>2015-03-06 19:44:28 +0100
committerThomas Reufer <thomas@reufer.ch>2015-03-06 19:44:28 +0100
commita730cf32754a7fda6286ba8b1ff6ccaa3124ef4e (patch)
tree99f157507607d22a96aa913bc846d35104b6a57c
parenta30995b5241d469f88c4dd16ed4635f45a57876c (diff)
downloadvdr-plugin-rpihddevice-a730cf32754a7fda6286ba8b1ff6ccaa3124ef4e.tar.gz
vdr-plugin-rpihddevice-a730cf32754a7fda6286ba8b1ff6ccaa3124ef4e.tar.bz2
added support for GPU accelerated pixmaps and font kerning
-rw-r--r--HISTORY3
-rw-r--r--ovgosd.c1785
-rw-r--r--ovgosd.h4
3 files changed, 1447 insertions, 345 deletions
diff --git a/HISTORY b/HISTORY
index 657e8da..e8d9ae3 100644
--- a/HISTORY
+++ b/HISTORY
@@ -1,6 +1,9 @@
VDR Plugin 'rpihddevice' Revision History
-----------------------------------------
+- new:
+ - added font kerning
+ - support for GPU accelerated pixmaps
- fixed:
- improved video frame rate detection to be more tolerant to inaccurate values
- adapted cOvgRawOsd::Flush() to new cOsd::RenderPixmaps() of vdr-2.1.10
diff --git a/ovgosd.c b/ovgosd.c
index 4d9cc30..69c8e2e 100644
--- a/ovgosd.c
+++ b/ovgosd.c
@@ -22,6 +22,68 @@
#include "setup.h"
#include "tools.h"
+/* ------------------------------------------------------------------------- */
+
+// glyphs containing kerning cache, based on VDR's implementation
+
+class cOvgGlyph : public cListObject
+{
+public:
+
+ cOvgGlyph(uint charCode, VGfloat advanceX, VGfloat advanceY) :
+ m_charCode(charCode), m_advanceX(advanceX), m_advanceY(advanceY) { }
+
+ virtual ~cOvgGlyph() { }
+
+ uint CharCode(void) { return m_charCode; }
+ VGfloat AdvanceX(void) { return m_advanceX; }
+ VGfloat AdvanceY(void) { return m_advanceY; }
+
+ bool GetKerningCache(uint prevSym, VGfloat &kerning)
+ {
+ for (int i = m_kerningCache.Size(); --i > 0; )
+ if (m_kerningCache[i].m_prevSym == prevSym)
+ {
+ kerning = m_kerningCache[i].m_kerning;
+ return true;
+ }
+
+ return false;
+ }
+
+ void SetKerningCache(uint prevSym, VGfloat kerning)
+ {
+ m_kerningCache.Append(tKerning(prevSym, kerning));
+ }
+
+private:
+
+ struct tKerning
+ {
+ public:
+
+ tKerning(uint prevSym, VGfloat kerning = 0.0f)
+ {
+ m_prevSym = prevSym;
+ m_kerning = kerning;
+ }
+
+ uint m_prevSym;
+ VGfloat m_kerning;
+ };
+
+ uint m_charCode;
+
+ VGfloat m_advanceX;
+ VGfloat m_advanceY;
+
+ cVector<tKerning> m_kerningCache;
+};
+
+/* ------------------------------------------------------------------------- */
+
+#define CHAR_HEIGHT (1 << 14)
+
class cOvgFont : public cListObject
{
public:
@@ -48,7 +110,7 @@ public:
s_fonts->Clear();
if (!retry)
{
- ELOG("OVG out of memory - failed to load font!");
+ ELOG("[OpenVG] out of memory - failed to load font!");
font = new cOvgFont();
break;
}
@@ -68,42 +130,47 @@ public:
ELOG("failed to deinitialize FreeType library!");
}
- VGuint GlyphIndex(uint character)
+ cOvgGlyph* Glyph(uint charCode) const
{
- std::vector<FT_ULong>::iterator it =
- std::find(m_characters.begin(), m_characters.end(), character);
-
- return it == m_characters.end() ? 0 : it - m_characters.begin();
- }
+ for (cOvgGlyph *g = m_glyphs.First(); g; g = m_glyphs.Next(g))
+ if (g->CharCode() == charCode)
+ return g;
- VGfloat Escapement(VGuint index)
- {
- return index < m_escapements.size() ? m_escapements[index] : 0;
+ return 0;
}
- VGfloat Height(void)
+ VGfloat Kerning(cOvgGlyph *glyph, uint prevSym) const
{
- return m_height;
- }
+ VGfloat kerning = 0.0f;
+ if (glyph && prevSym)
+ {
+ if (!glyph->GetKerningCache(prevSym, kerning))
+ {
+ FT_Vector delta;
+ FT_UInt cur = FT_Get_Char_Index(m_face, glyph->CharCode());
+ FT_UInt prev = FT_Get_Char_Index(m_face, prevSym);
+ FT_Get_Kerning(m_face, prev, cur, FT_KERNING_DEFAULT, &delta);
- VGFont Font(void)
- {
- return m_font;
+ kerning = (VGfloat)delta.x / CHAR_HEIGHT;
+ glyph->SetKerningCache(prevSym, kerning);
+ }
+ }
+ return kerning;
}
- const char* Name(void)
- {
- return *m_name;
- }
+ VGfloat Height(void) { return m_height; }
+ VGfloat Descender(void) { return m_descender; }
+ VGFont Font(void) { return m_font; }
+ const char* Name(void) { return *m_name; }
private:
- #define CHAR_HEIGHT (1 << 14)
-
cOvgFont(void) :
m_font(VG_INVALID_HANDLE),
m_name(""),
- m_height(0)
+ m_height(0.0f),
+ m_descender(0.0f),
+ m_face(0)
{ }
cOvgFont(FT_Library lib, const char *name) :
@@ -111,56 +178,53 @@ private:
{
ILOG("loading %s ...", *m_name);
- FT_Face face;
- if (FT_New_Face(lib, name, 0, &face))
+ if (FT_New_Face(lib, name, 0, &m_face))
ELOG("failed to open %s!", name);
- m_font = vgCreateFont(face->num_glyphs);
+ m_font = vgCreateFont(m_face->num_glyphs);
if (m_font == VG_INVALID_HANDLE)
{
- FT_Done_Face(face);
ELOG("failed to allocate new OVG font!");
return;
}
- FT_Set_Char_Size(face, 0, CHAR_HEIGHT, 0, 0);
- m_height = (VGfloat)(face->size->metrics.ascender -
- face->size->metrics.descender + 63) / (VGfloat)CHAR_HEIGHT;
+ FT_Set_Char_Size(m_face, 0, CHAR_HEIGHT, 0, 0);
+ m_height = (VGfloat)(m_face->size->metrics.height) / CHAR_HEIGHT;
+ m_descender = (VGfloat)(abs(m_face->size->metrics.descender)) /
+ CHAR_HEIGHT;
- int glyphId = 0;
FT_UInt glyphIndex;
- FT_ULong ch = FT_Get_First_Char(face, &glyphIndex);
+ FT_ULong ch = FT_Get_First_Char(m_face, &glyphIndex);
while (ch != 0)
{
- if (FT_Load_Glyph(face, glyphIndex, FT_LOAD_DEFAULT))
+ if (FT_Load_Glyph(m_face, glyphIndex, FT_LOAD_DEFAULT))
break;
- FT_Outline *ot = &face->glyph->outline;
+ FT_Outline *ot = &m_face->glyph->outline;
VGPath path = ConvertOutline(ot);
VGfloat origin[] = { 0.0f, 0.0f };
VGfloat esc[] = {
- (VGfloat)(face->glyph->advance.x) / (VGfloat)CHAR_HEIGHT,
- (VGfloat)(face->glyph->advance.y) / (VGfloat)CHAR_HEIGHT
+ (VGfloat)(m_face->glyph->advance.x) / CHAR_HEIGHT,
+ (VGfloat)(m_face->glyph->advance.y) / CHAR_HEIGHT
};
- vgSetGlyphToPath(m_font, glyphId++, path, VG_FALSE, origin, esc);
+ vgSetGlyphToPath(m_font, ch, path, VG_FALSE, origin, esc);
- m_characters.push_back(ch);
- m_escapements.push_back(esc[0]);
+ m_glyphs.Add(new cOvgGlyph(ch, esc[0], esc[1]));
if (path != VG_INVALID_HANDLE)
vgDestroyPath(path);
- ch = FT_Get_Next_Char(face, ch, &glyphIndex);
+ ch = FT_Get_Next_Char(m_face, ch, &glyphIndex);
}
- FT_Done_Face(face);
}
~cOvgFont()
{
vgDestroyFont(m_font);
+ FT_Done_Face(m_face);
}
static void Init(void)
@@ -171,7 +235,7 @@ private:
}
// convert freetype outline to OpenVG path,
- // based on Raspberry's vgfont library
+ // based on Raspberry Pi's vgfont library
VGPath ConvertOutline(FT_Outline *outline)
{
@@ -245,19 +309,23 @@ private:
}
VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
- VG_PATH_DATATYPE_S_16, 1.0f / (VGfloat)CHAR_HEIGHT, 0.0f, 0, 0,
- VG_PATH_CAPABILITY_APPEND_TO);
+ VG_PATH_DATATYPE_S_16, 1.0f / (VGfloat)CHAR_HEIGHT, 0.0f,
+ segments.size(), coord.size(), VG_PATH_CAPABILITY_APPEND_TO);
+
+ if (path != VG_INVALID_HANDLE)
+ vgAppendPathData(path, segments.size(), &segments[0], &coord[0]);
- vgAppendPathData(path, segments.size(), &segments[0], &coord[0]);
return path;
}
VGFont m_font;
cString m_name;
VGfloat m_height;
+ VGfloat m_descender;
- std::vector<FT_ULong> m_characters;
- std::vector<VGfloat> m_escapements;
+ mutable cList<cOvgGlyph> m_glyphs;
+
+ FT_Face m_face;
static FT_Library s_ftLib;
static cList<cOvgFont> *s_fonts;
@@ -273,14 +341,23 @@ class cOvgString
public:
cOvgString(const unsigned int *symbols, cOvgFont *font) :
- m_width(0.0f), m_height(font->Height()), m_font(font)
+ m_width(0.0f), m_height(font->Height()), m_descender(font->Descender()),
+ m_font(font)
{
+ uint prevSym = 0;
for (int i = 0; symbols[i]; i++)
- {
- VGuint glyphId = font->GlyphIndex(symbols[i]);
- m_glyphIds.push_back(glyphId);
- m_width += font->Escapement(glyphId);
- }
+ if (cOvgGlyph *g = font->Glyph(symbols[i]))
+ {
+ VGfloat kerning = 0.0f;
+ if (prevSym)
+ {
+ kerning = m_font->Kerning(g, prevSym);
+ m_kerning.push_back(kerning);
+ }
+ m_width += g->AdvanceX() + kerning;
+ m_glyphIds.push_back(symbols[i]);
+ prevSym = symbols[i];
+ }
}
~cOvgString() { }
@@ -289,13 +366,18 @@ public:
VGint Length(void) { return m_glyphIds.size(); }
VGfloat Width(void) { return m_width; }
VGfloat Height(void) { return m_height; }
+ VGfloat Descender(void) { return m_descender; }
const VGuint *GlyphIds(void) { return &m_glyphIds[0]; }
+ const VGfloat *Kerning(void) { return &m_kerning[0]; }
private:
std::vector<VGuint> m_glyphIds;
+ std::vector<VGfloat> m_kerning;
+
VGfloat m_width;
VGfloat m_height;
+ VGfloat m_descender;
cOvgFont *m_font;
};
@@ -305,17 +387,15 @@ class cOvgPaintBox
{
public:
- static void Draw(VGPath path, tColor color)
+ static void Draw(VGPath path)
{
- SetPaint(color);
vgDrawPath(path, VG_FILL_PATH);
}
- static void Draw(cOvgString *string, tColor color)
+ static void Draw(cOvgString *string)
{
- SetPaint(color);
vgDrawGlyphs(string->Font(), string->Length(), string->GlyphIds(),
- NULL, NULL, VG_FILL_PATH, VG_TRUE);
+ string->Kerning(), NULL, VG_FILL_PATH, VG_TRUE);
}
static VGPath Rect(void)
@@ -353,16 +433,52 @@ public:
s_initialized = false;
}
-private:
-
- static void SetPaint(tColor color)
+ static void SetColor(tColor color)
{
if (!s_initialized)
SetUp();
+ vgSetParameteri(s_paint, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR);
vgSetColor(s_paint, (color << 8) + (color >> 24));
+ vgSetPaint(s_paint, VG_FILL_PATH);
}
+ static void SetAlpha(int alpha)
+ {
+ if (!s_initialized)
+ SetUp();
+
+ alpha = constrain(alpha, ALPHA_TRANSPARENT, ALPHA_OPAQUE);
+ VGfloat values[] = {
+ 1.0f, 1.0f, 1.0f, alpha / 255.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+
+ vgSetfv(VG_COLOR_TRANSFORM_VALUES, 8, values);
+ vgSeti(VG_COLOR_TRANSFORM, alpha == ALPHA_OPAQUE ? VG_FALSE : VG_TRUE);
+ }
+
+ static void SetPattern(VGImage image = VG_INVALID_HANDLE)
+ {
+ if (!s_initialized)
+ SetUp();
+
+ vgPaintPattern(s_paint, image);
+ if (image == VG_INVALID_HANDLE)
+ return;
+
+ vgSetParameteri(s_paint, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
+ vgSetParameteri(s_paint, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_REPEAT);
+ vgSetPaint(s_paint, VG_FILL_PATH);
+ }
+
+ static void SetScissoring(int x = 0, int y = 0, int w = 0, int h = 0)
+ {
+ VGint cropArea[4] = { x, y, w, h };
+ vgSetiv(VG_SCISSOR_RECTS, 4, cropArea);
+ vgSeti(VG_SCISSORING, w && h ? VG_TRUE : VG_FALSE);
+ }
+
+private:
+
static void SetUp(void)
{
// paint
@@ -379,30 +495,30 @@ private:
s_ellipse[i] = vgCreatePath(VG_PATH_FORMAT_STANDARD,
VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
- vguArc(s_ellipse[0], 0.0f, 0.0f, 2.0f, 2.0f, 0, 90, VGU_ARC_OPEN);
- vguArc(s_ellipse[1], 1.0f, 0.0f, 2.0f, 2.0f, 90, 90, VGU_ARC_OPEN);
- vguArc(s_ellipse[2], 1.0f, 1.0f, 2.0f, 2.0f, 180, 90, VGU_ARC_OPEN);
- vguArc(s_ellipse[3], 0.0f, 1.0f, 2.0f, 2.0f, 270, 90, VGU_ARC_OPEN);
+ vguArc(s_ellipse[0], 0.0f, 1.0f, 2.0f, 2.0f, 270, 90, VGU_ARC_OPEN);
+ vguArc(s_ellipse[1], 1.0f, 1.0f, 2.0f, 2.0f, 180, 90, VGU_ARC_OPEN);
+ vguArc(s_ellipse[2], 1.0f, 0.0f, 2.0f, 2.0f, 90, 90, VGU_ARC_OPEN);
+ vguArc(s_ellipse[3], 0.0f, 0.0f, 2.0f, 2.0f, 0, 90, VGU_ARC_OPEN);
// close path via corner opposed of center of arc for inverted arcs
VGubyte cornerSeg[] = { VG_LINE_TO_ABS, VG_CLOSE_PATH };
VGfloat cornerData[][2] = {
- { 1.0f, 1.0f }, { 0.0f, 1.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f }
+ { 1.0f, 0.0f }, { 0.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f }
};
for (int i = 0; i < 4; i++)
vgAppendPathData(s_ellipse[i], 2, cornerSeg, cornerData[i]);
vguEllipse(s_ellipse[4], 0.5f, 0.5f, 1.0f, 1.0f);
- vguArc(s_ellipse[5], 0.0f, 1.0f, 2.0f, 2.0f, 270, 90, VGU_ARC_PIE);
- vguArc(s_ellipse[6], 1.0f, 1.0f, 2.0f, 2.0f, 180, 90, VGU_ARC_PIE);
- vguArc(s_ellipse[7], 1.0f, 0.0f, 2.0f, 2.0f, 90, 90, VGU_ARC_PIE);
- vguArc(s_ellipse[8], 0.0f, 0.0f, 2.0f, 2.0f, 0, 90, VGU_ARC_PIE);
+ vguArc(s_ellipse[5], 0.0f, 0.0f, 2.0f, 2.0f, 0, 90, VGU_ARC_PIE);
+ vguArc(s_ellipse[6], 1.0f, 0.0f, 2.0f, 2.0f, 90, 90, VGU_ARC_PIE);
+ vguArc(s_ellipse[7], 1.0f, 1.0f, 2.0f, 2.0f, 180, 90, VGU_ARC_PIE);
+ vguArc(s_ellipse[8], 0.0f, 1.0f, 2.0f, 2.0f, 270, 90, VGU_ARC_PIE);
vguArc(s_ellipse[9], 0.0f, 0.5f, 2.0f, 1.0f, 270, 180, VGU_ARC_PIE);
- vguArc(s_ellipse[10], 0.5f, 1.0f, 1.0f, 2.0f, 180, 180, VGU_ARC_PIE);
+ vguArc(s_ellipse[10], 0.5f, 0.0f, 1.0f, 2.0f, 0, 180, VGU_ARC_PIE);
vguArc(s_ellipse[11], 1.0f, 0.5f, 2.0f, 1.0f, 90, 180, VGU_ARC_PIE);
- vguArc(s_ellipse[12], 0.5f, 0.0f, 1.0f, 2.0f, 0, 180, VGU_ARC_PIE);
+ vguArc(s_ellipse[12], 0.5f, 1.0f, 1.0f, 2.0f, 180, 180, VGU_ARC_PIE);
// slopes
VGubyte slopeSeg[] = {
@@ -411,21 +527,22 @@ private:
// gradient of the slope: VDR uses 0.5 but 0.6 looks nicer...
const VGfloat s = 0.6f;
VGfloat slopeData[] = {
- 1.0f, 1.0f, 1.0f, 0.0f, 1.0f - s, 0.0f, s, 1.0f, 0.0f, 1.0f
+ 1.0f, 0.0f, 1.0f, 1.0f, 1.0f - s, 1.0f, s, 0.0f, 0.0f, 0.0f
};
VGfloat slopeScale[][2] = {
{ -1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, -1.0f },
- { 1.0f, -1.0f }, { -1.0f, 1.0f }, { 1.0f, 1.0f },
- { -1.0f, -1.0f }
+ { -1.0f, 1.0f }, { 1.0f, -1.0f }, {-1.0f, -1.0f },
+ { 1.0f, 1.0f }
};
VGfloat slopeTrans[][2] = {
- { -1.0f, -1.0f }, { -1.0f, 0.0f }, { 0.0f, -1.0f },
- { 0.0f, 0.0f }, { -1.0f, -1.0f }, { 0.0f, -1.0f },
- { -1.0f, 0.0f }
+ { -1.0f, -1.0f }, { -1.0f, 0.0f }, { 0.0f, -1.0f },
+ { -1.0f, -1.0f }, { 0.0f, 0.0f }, { -1.0f, 0.0f },
+ { 0.0f, -1.0f }
};
VGfloat slopeRot[] = { 0.0f, 0.0f, 0.0f, 90.0f, 90.0f, 90.0f, 90.0f };
VGfloat backupMatrix[9];
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
vgGetMatrix(backupMatrix);
for (int i = 0; i < 8; i++)
@@ -471,27 +588,125 @@ bool cOvgPaintBox::s_initialized = false;
/* ------------------------------------------------------------------------- */
+class cEgl
+{
+public:
+
+ EGLDisplay display;
+ EGLContext context;
+ EGLConfig config;
+ EGLint nConfig;
+ EGLSurface surface;
+ EGLSurface currentSurface;
+ EGL_DISPMANX_WINDOW_T window;
+
+ static const char* errStr(EGLint error)
+ {
+ return error == EGL_SUCCESS ? "success" :
+ error == EGL_NOT_INITIALIZED ? "not initialized" :
+ error == EGL_BAD_ACCESS ? "bad access" :
+ error == EGL_BAD_ALLOC ? "bad alloc" :
+ error == EGL_BAD_ATTRIBUTE ? "bad attribute" :
+ error == EGL_BAD_CONTEXT ? "bad context" :
+ error == EGL_BAD_CONFIG ? "bad config" :
+ error == EGL_BAD_CURRENT_SURFACE ? "bad current surface" :
+ error == EGL_BAD_DISPLAY ? "bad display" :
+ error == EGL_BAD_SURFACE ? "bad surface" :
+ error == EGL_BAD_MATCH ? "bad match" :
+ error == EGL_BAD_PARAMETER ? "bad parameter" :
+ error == EGL_BAD_NATIVE_PIXMAP ? "bad native pixmap" :
+ error == EGL_BAD_NATIVE_WINDOW ? "bad native window" :
+ error == EGL_CONTEXT_LOST ? "context lost" :
+ "unknown error";
+ }
+};
+
+/* ------------------------------------------------------------------------- */
+
+class cOvgRenderTarget
+{
+public:
+
+ cOvgRenderTarget(int _width = 0, int _height = 0) :
+ surface(EGL_NO_SURFACE),
+ image(VG_INVALID_HANDLE),
+ width(_width),
+ height(_height) { }
+
+ virtual ~cOvgRenderTarget() { }
+
+ virtual bool MakeCurrent(cEgl *egl)
+ {
+ // if this is a window surface, check for an update after OSD reset
+ if (image == VG_INVALID_HANDLE && surface != egl->surface)
+ {
+ surface = egl->surface;
+ width = egl->window.width;
+ height = egl->window.height;
+ }
+
+ if (egl->currentSurface == surface)
+ return true;
+
+ if (eglMakeCurrent(egl->display, surface, surface, egl->context) ==
+ EGL_FALSE)
+ {
+ ELOG("[EGL] failed to connect context to surface: %s!",
+ cEgl::errStr(eglGetError()));
+ return false;
+ }
+ egl->currentSurface = surface;
+ return true;
+ }
+
+ EGLSurface surface;
+ VGImage image;
+ int width;
+ int height;
+
+private:
+
+ cOvgRenderTarget(const cOvgRenderTarget&);
+ cOvgRenderTarget& operator= (const cOvgRenderTarget&);
+};
+
+/* ------------------------------------------------------------------------- */
+
class cOvgCmd
{
public:
- cOvgCmd() { }
+ cOvgCmd(cOvgRenderTarget *target) : m_target(target) { }
virtual ~cOvgCmd() { }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height) = 0;
+ virtual bool Execute(cEgl *egl) = 0;
+ virtual const char* Description(void) = 0;
+
+protected:
+
+ cOvgRenderTarget *m_target;
+
+private:
+
+ cOvgCmd(const cOvgCmd&);
+ cOvgCmd& operator= (const cOvgCmd&);
};
class cOvgCmdFlush : public cOvgCmd
{
public:
- cOvgCmdFlush() : cOvgCmd() { }
+ cOvgCmdFlush(cOvgRenderTarget *target) :
+ cOvgCmd(target) { }
+
+ virtual const char* Description(void) { return "Flush"; }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual bool Execute(cEgl *egl)
{
- eglSwapBuffers(display, surface);
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
+ eglSwapBuffers(egl->display, m_target->surface);
return true;
}
};
@@ -500,10 +715,12 @@ class cOvgCmdReset : public cOvgCmd
{
public:
- cOvgCmdReset(bool cleanup = false) : cOvgCmd(), m_cleanup(cleanup) { }
+ cOvgCmdReset(bool cleanup = false) :
+ cOvgCmd(0), m_cleanup(cleanup) { }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual const char* Description(void) { return "Reset"; }
+
+ virtual bool Execute(cEgl *egl)
{
if (m_cleanup)
{
@@ -518,21 +735,115 @@ private:
bool m_cleanup;
};
+class cOvgCmdCreatePixelBuffer : public cOvgCmd
+{
+public:
+
+ cOvgCmdCreatePixelBuffer(cOvgRenderTarget *target) :
+ cOvgCmd(target) { }
+
+ virtual const char* Description(void) { return "CreatePixelBuffer"; }
+
+ virtual bool Execute(cEgl *egl)
+ {
+ int origW = m_target->width;
+ int origH = m_target->height;
+
+ m_target->width = min(m_target->width, vgGeti(VG_MAX_IMAGE_WIDTH));
+ m_target->height = min(m_target->height, vgGeti(VG_MAX_IMAGE_HEIGHT));
+
+ if (origW != m_target->width || origH != m_target->height)
+ ELOG("[OpenVG] cannot allocate pixmap of %dpx x %dpx, "
+ "clipped to %dpx x %dpx!", origW, origH,
+ m_target->width, m_target->height);
+
+ m_target->image = vgCreateImage(VG_sARGB_8888, m_target->width,
+ m_target->height, VG_IMAGE_QUALITY_BETTER);
+
+ if (m_target->image == VG_INVALID_HANDLE)
+ {
+ ELOG("[OpenVG] failed to allocate %dx%d px pixel buffer!",
+ m_target->width, m_target->height);
+ return false;
+ }
+
+ m_target->surface = eglCreatePbufferFromClientBuffer(egl->display,
+ EGL_OPENVG_IMAGE, (EGLClientBuffer)m_target->image,
+ egl->config, NULL);
+
+ if (m_target->surface == EGL_NO_SURFACE)
+ {
+ ELOG("[EGL] failed to create pixel buffer surface: %s!",
+ cEgl::errStr(eglGetError()));
+ vgDestroyImage(m_target->image);
+ return false;
+ }
+
+ if (eglSurfaceAttrib(egl->display, m_target->surface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED) == EGL_FALSE)
+ {
+ ELOG("[EGL] failed to set surface attributes!");
+ eglDestroySurface(egl->display, m_target->surface);
+ vgDestroyImage(m_target->image);
+ return false;
+ }
+ return true;
+ }
+};
+
+class cOvgCmdDestroySurface : public cOvgCmd
+{
+public:
+
+ cOvgCmdDestroySurface(cOvgRenderTarget *target) : cOvgCmd(target) { }
+
+ virtual const char* Description(void) { return "DestroySurface"; }
+
+ virtual bool Execute(cEgl *egl)
+ {
+ // only destroy pixel buffer surfaces
+ if (m_target->image != VG_INVALID_HANDLE)
+ {
+ if (eglDestroySurface(egl->display, m_target->surface) == EGL_FALSE)
+ ELOG("[EGL] failed to destroy surface: %s!",
+ cEgl::errStr(eglGetError()));
+
+ vgDestroyImage(m_target->image);
+ }
+ delete m_target;
+ return true;
+ }
+};
+
class cOvgCmdClear : public cOvgCmd
{
public:
- cOvgCmdClear() : cOvgCmd() { }
+ cOvgCmdClear(cOvgRenderTarget *target, tColor color = clrTransparent) :
+ cOvgCmd(target), m_color(color) { }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual const char* Description(void) { return "Clear"; }
+
+ virtual bool Execute(cEgl *egl)
{
- float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
+ VGfloat color[4] = {
+ (m_color >> 16 & 0xff) / 255.0f,
+ (m_color >> 8 & 0xff) / 255.0f,
+ (m_color & 0xff) / 255.0f,
+ (m_color >> 24 & 0xff) / 255.0f
+ };
+
vgSetfv(VG_CLEAR_COLOR, 4, color);
- vgClear(0, 0, width, height);
- eglSwapBuffers(display, surface);
+ vgClear(0, 0, m_target->width, m_target->height);
return true;
}
+
+private:
+
+ tColor m_color;
};
class cOvgCmdSaveRegion : public cOvgCmd
@@ -540,10 +851,11 @@ class cOvgCmdSaveRegion : public cOvgCmd
public:
cOvgCmdSaveRegion(cRect *rect, VGImage *image) :
- cOvgCmd(), m_rect(rect), m_image(image) { }
+ cOvgCmd(0), m_rect(rect), m_image(image) { }
+
+ virtual const char* Description(void) { return "SaveRegion"; }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual bool Execute(cEgl *egl)
{
if (m_image && m_rect)
{
@@ -553,8 +865,15 @@ public:
*m_image = vgCreateImage(VG_sARGB_8888,
m_rect->Width(), m_rect->Height(), VG_IMAGE_QUALITY_BETTER);
- vgGetPixels(*m_image, 0, 0, m_rect->X(), height - m_rect->Bottom()
- - 1, m_rect->Width(), m_rect->Height());
+ if (*m_image == VG_INVALID_HANDLE)
+ {
+ ELOG("failed to allocate image!");
+ return false;
+ }
+
+ vgGetPixels(*m_image, 0, 0, m_rect->X(),
+ egl->window.height - m_rect->Bottom() - 1,
+ m_rect->Width(), m_rect->Height());
}
return true;
}
@@ -570,14 +889,15 @@ class cOvgCmdRestoreRegion : public cOvgCmd
public:
cOvgCmdRestoreRegion(cRect *rect, VGImage *image) :
- cOvgCmd(), m_rect(rect), m_image(image) { }
+ cOvgCmd(0), m_rect(rect), m_image(image) { }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual const char* Description(void) { return "RestoreRegion"; }
+
+ virtual bool Execute(cEgl *egl)
{
if (m_image && m_rect)
- vgSetPixels(m_rect->X(), height - m_rect->Bottom() - 1, *m_image,
- 0, 0, m_rect->Width(), m_rect->Height());
+ vgSetPixels(m_rect->X(), egl->window.height - m_rect->Bottom() - 1,
+ *m_image, 0, 0, m_rect->Width(), m_rect->Height());
return true;
}
@@ -591,10 +911,12 @@ class cOvgCmdDropRegion : public cOvgCmd
{
public:
- cOvgCmdDropRegion(VGImage *image) : cOvgCmd(), m_image(image) { }
+ cOvgCmdDropRegion(VGImage *image) :
+ cOvgCmd(0), m_image(image) { }
+
+ virtual const char* Description(void) { return "DropRegion"; }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual bool Execute(cEgl *egl)
{
if (m_image && *m_image)
vgDestroyImage(*m_image);
@@ -610,13 +932,28 @@ class cOvgCmdDrawPixel : public cOvgCmd
{
public:
- cOvgCmdDrawPixel(int x, int y, tColor color) :
- cOvgCmd(), m_x(x), m_y(y), m_color(color) { }
+ cOvgCmdDrawPixel(cOvgRenderTarget *target, int x, int y, tColor color,
+ bool alphablend) :
+ cOvgCmd(target), m_x(x), m_y(y), m_color(color),
+ m_alphablend(alphablend) { }
+
+ virtual const char* Description(void) { return "DrawPixel"; }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual bool Execute(cEgl *egl)
{
- vgWritePixels(&m_color, 0, VG_sARGB_8888, m_x, height - m_y - 1, 1, 1);
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
+ if (m_alphablend)
+ {
+ tColor dstPixel;
+ vgReadPixels(&dstPixel, 0, VG_sARGB_8888,
+ m_x, m_target->height - 1 - m_y, 1, 1);
+
+ m_color = AlphaBlend(m_color, dstPixel);
+ }
+ vgWritePixels(&m_color, 0, VG_sARGB_8888,
+ m_x, m_target->height - 1 - m_y, 1, 1);
return true;
}
@@ -625,27 +962,33 @@ private:
int m_x;
int m_y;
tColor m_color;
+ bool m_alphablend;
};
class cOvgCmdDrawRectangle : public cOvgCmd
{
public:
- cOvgCmdDrawRectangle(int x, int y, int w, int h, tColor color) :
- cOvgCmd(), m_x(x), m_y(y), m_w(w), m_h(h), m_color(color) { }
+ cOvgCmdDrawRectangle(cOvgRenderTarget *target,
+ int x, int y, int w, int h, tColor color) :
+ cOvgCmd(target), m_x(x), m_y(y), m_w(w), m_h(h), m_color(color) { }
+
+ virtual const char* Description(void) { return "DrawRectangle"; }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual bool Execute(cEgl *egl)
{
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
vgLoadIdentity();
- vgScale(1.0f, -1.0f);
- vgTranslate(m_x, m_y - height);
+ vgTranslate(m_x, m_target->height - m_h - m_y);
vgScale(m_w, m_h);
- cOvgPaintBox::Draw(cOvgPaintBox::Rect(), m_color);
+ cOvgPaintBox::SetColor(m_color);
+ cOvgPaintBox::Draw(cOvgPaintBox::Rect());
return true;
}
@@ -662,22 +1005,27 @@ class cOvgCmdDrawEllipse : public cOvgCmd
{
public:
- cOvgCmdDrawEllipse(int x, int y, int w, int h, tColor color, int quadrants) :
- cOvgCmd(), m_x(x), m_y(y), m_w(w), m_h(h), m_quadrants(quadrants),
+ cOvgCmdDrawEllipse(cOvgRenderTarget *target,
+ int x, int y, int w, int h, tColor color, int quadrants) :
+ cOvgCmd(target), m_x(x), m_y(y), m_w(w), m_h(h), m_quadrants(quadrants),
m_color(color) { }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual const char* Description(void) { return "DrawEllipse"; }
+
+ virtual bool Execute(cEgl *egl)
{
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
vgLoadIdentity();
- vgScale(1.0f, -1.0f);
- vgTranslate(m_x, m_y - height);
+ vgTranslate(m_x, m_target->height - m_h - m_y);
vgScale(m_w, m_h);
- cOvgPaintBox::Draw(cOvgPaintBox::Ellipse(m_quadrants), m_color);
+ cOvgPaintBox::SetColor(m_color);
+ cOvgPaintBox::Draw(cOvgPaintBox::Ellipse(m_quadrants));
return true;
}
@@ -695,22 +1043,27 @@ class cOvgCmdDrawSlope : public cOvgCmd
{
public:
- cOvgCmdDrawSlope(int x, int y, int w, int h, tColor color, int type) :
- cOvgCmd(), m_x(x), m_y(y), m_w(w), m_h(h), m_type(type),
+ cOvgCmdDrawSlope(cOvgRenderTarget *target,
+ int x, int y, int w, int h, tColor color, int type) :
+ cOvgCmd(target), m_x(x), m_y(y), m_w(w), m_h(h), m_type(type),
m_color(color) { }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual const char* Description(void) { return "DrawSlope"; }
+
+ virtual bool Execute(cEgl *egl)
{
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
vgLoadIdentity();
- vgScale(1.0f, -1.0f);
- vgTranslate(m_x, m_y - height);
+ vgTranslate(m_x, m_target->height - m_h - m_y);
vgScale(m_w, m_h);
- cOvgPaintBox::Draw(cOvgPaintBox::Slope(m_type), m_color);
+ cOvgPaintBox::SetColor(m_color);
+ cOvgPaintBox::Draw(cOvgPaintBox::Slope(m_type));
return true;
}
@@ -728,11 +1081,13 @@ class cOvgCmdDrawText : public cOvgCmd
{
public:
- cOvgCmdDrawText(int x, int y, unsigned int *symbols, cString *fontName,
- int fontSize, tColor color, int width, int height, int alignment) :
- cOvgCmd(), m_x(x), m_y(y), m_symbols(symbols), m_fontName(fontName),
- m_fontSize(fontSize), m_color(color), m_width(width), m_height(height),
- m_alignment(alignment) { }
+ cOvgCmdDrawText(cOvgRenderTarget *target,
+ int x, int y, unsigned int *symbols, cString *fontName,
+ int fontSize, tColor colorFg, tColor colorBg, int w, int h,
+ int alignment) :
+ cOvgCmd(target), m_x(x), m_y(y), m_w(w), m_h(h),
+ m_symbols(symbols), m_fontName(fontName), m_fontSize(fontSize),
+ m_colorFg(colorFg), m_colorBg(colorBg), m_alignment(alignment) { }
virtual ~cOvgCmdDrawText()
{
@@ -740,72 +1095,100 @@ public:
delete m_fontName;
}
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual const char* Description(void) { return "DrawText"; }
+
+ virtual bool Execute(cEgl *egl)
{
- vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
- vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
+ if (!m_target->MakeCurrent(egl))
+ return false;
cOvgFont *font = cOvgFont::Get(*m_fontName);
+ if (!font)
+ return false;
+
cOvgString *string = new cOvgString(m_symbols, font);
- VGfloat strWidth = string->Width() * (VGfloat)m_fontSize;
- VGfloat strHeight = string->Height() * (VGfloat)m_fontSize;
VGfloat offsetX = 0;
VGfloat offsetY = 0;
+ VGfloat width = string->Width() * (VGfloat)m_fontSize;
+ VGfloat height = string->Height() * (VGfloat)m_fontSize;
+ VGfloat descender = string->Descender() * (VGfloat)m_fontSize;
- if (m_width)
+ if (m_w)
{
if (m_alignment & taLeft)
{
if (m_alignment & taBorder)
- offsetX += max(strHeight / TEXT_ALIGN_BORDER, 1.0f);
+ offsetX += max(height / TEXT_ALIGN_BORDER, 1.0f);
}
else if (m_alignment & taRight)
{
- if (strWidth < (VGfloat)m_width)
- offsetX += m_width - strWidth;
+ if (width < m_w)
+ offsetX += m_w - width;
if (m_alignment & taBorder)
- offsetX -= max(strHeight / TEXT_ALIGN_BORDER, 1.0f);
+ offsetX -= max(height / TEXT_ALIGN_BORDER, 1.0f);
}
else
{
- if (strWidth < (VGfloat)m_width)
- offsetX += (m_width - strWidth) / 2;
+ if (width < m_w)
+ offsetX += (m_w - width) / 2;
}
}
- if (m_height)
+ if (m_h)
{
if (m_alignment & taTop) { }
else if (m_alignment & taBottom)
{
- if (strHeight < (VGfloat)m_height)
- offsetY += m_height - strHeight;
+ if (height < m_h)
+ offsetY += m_h - height;
}
else
{
- if (strHeight < (VGfloat)m_height)
- offsetY += (m_height - strHeight) / 2;
+ if (height < m_h)
+ offsetY += (m_h - height) / 2;
}
}
+ vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_GLYPH_USER_TO_SURFACE);
+
+ // some magic offset to conform with VDR's text rendering
+ offsetY -= 0.06f * m_fontSize;
+
vgLoadIdentity();
- vgTranslate(m_x + offsetX, height - m_y - m_fontSize - offsetY + 2);
+ vgTranslate(m_x + offsetX,
+ m_target->height - m_y - m_fontSize - offsetY + 1);
vgScale(m_fontSize, m_fontSize);
VGfloat origin[2] = { 0.0f, 0.0f };
vgSetfv(VG_GLYPH_ORIGIN, 2, origin);
- VGint cropArea[4] = {
- m_width ? m_x : 0, m_height ? height - m_y - m_height - 1 : 0,
- m_width ? m_width : width - 1, m_height ? m_height : height - 1
- };
- vgSetiv(VG_SCISSOR_RECTS, 4, cropArea);
- vgSeti(VG_SCISSORING, VG_TRUE);
+ cOvgPaintBox::SetScissoring(
+ m_w ? m_x : m_x + floor(offsetX),
+ m_h ? m_target->height - m_y - m_h : m_target->height - m_y -
+ m_fontSize - floor(descender) + 1,
+ m_w ? m_w : floor(width) + 1,
+ m_h ? m_h : m_fontSize + floor(descender) - 1);
+
+ if (m_colorBg != clrTransparent)
+ {
+ VGfloat color[4] = {
+ (m_colorBg >> 16 & 0xff) / 255.0f,
+ (m_colorBg >> 8 & 0xff) / 255.0f,
+ (m_colorBg & 0xff) / 255.0f,
+ (m_colorBg >> 24 & 0xff) / 255.0f
+ };
+ vgSetfv(VG_CLEAR_COLOR, 4, color);
+ vgClear(0, 0, m_target->width, m_target->height);
+ }
- cOvgPaintBox::Draw(string, m_color);
+ if (string->Length())
+ {
+ cOvgPaintBox::SetColor(m_colorFg);
+ cOvgPaintBox::Draw(string);
+ }
- vgSeti(VG_SCISSORING, VG_FALSE);
+ cOvgPaintBox::SetScissoring();
delete string;
return true;
}
@@ -814,107 +1197,365 @@ private:
int m_x;
int m_y;
+ int m_w;
+ int m_h;
unsigned int *m_symbols;
cString *m_fontName;
int m_fontSize;
- tColor m_color;
- int m_width;
- int m_height;
+ tColor m_colorFg;
+ tColor m_colorBg;
int m_alignment;
};
-class cOvgCmdDrawImage : public cOvgCmd
+class cOvgCmdRenderPixels : public cOvgCmd
{
public:
- cOvgCmdDrawImage(int x, int y, int w, int h, const tColor *argb,
- bool overlay, double scaleX, double scaleY) :
- cOvgCmd(), m_x(x), m_y(y), m_w(w), m_h(h), m_argb(argb),
- m_overlay(overlay), m_scaleX(scaleX), m_scaleY(scaleY) { }
+ cOvgCmdRenderPixels(cOvgRenderTarget *target, cOvgRenderTarget *source,
+ int dx, int dy, int sx, int sy, int w, int h, int alpha) :
+ cOvgCmd(target), m_source(source), m_dx(dx), m_dy(dy),
+ m_sx(sx), m_sy(sy), m_w(w), m_h(h), m_alpha(alpha) { }
- virtual bool Execute(EGLDisplay display, EGLSurface surface,
- int width, int height)
+ virtual const char* Description(void) { return "RenderPixels"; }
+
+ virtual bool Execute(cEgl *egl)
{
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
+ cOvgPaintBox::SetAlpha(m_alpha);
+ cOvgPaintBox::SetScissoring(m_dx,
+ m_target->height - m_dy - m_h, m_w, m_h);
+
+ vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);
vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
vgSeti(VG_IMAGE_QUALITY, VG_IMAGE_QUALITY_BETTER);
- vgSeti(VG_BLEND_MODE, m_overlay ? VG_BLEND_SRC_OVER : VG_BLEND_SRC);
vgLoadIdentity();
- vgScale(1.0f, -1.0f);
- vgTranslate(m_x, m_y - height);
- vgScale(m_scaleX, m_scaleY);
+ vgTranslate(m_dx - m_sx,
+ m_target->height - m_source->height - m_dy + m_sy);
- VGImage image = vgCreateImage(VG_sARGB_8888, m_w, m_h,
+ vgDrawImage(m_source->image);
+
+ cOvgPaintBox::SetAlpha(255);
+ cOvgPaintBox::SetScissoring();
+ return true;
+ }
+
+private:
+
+ cOvgRenderTarget *m_source;
+ int m_dx;
+ int m_dy;
+ int m_sx;
+ int m_sy;
+ int m_w;
+ int m_h;
+ int m_alpha;
+};
+
+class cOvgCmdRenderPattern : public cOvgCmd
+{
+public:
+
+ cOvgCmdRenderPattern(cOvgRenderTarget *target, cOvgRenderTarget *source,
+ int dx, int dy, int sx, int sy, int w, int h, int alpha) :
+ cOvgCmd(target), m_source(source), m_dx(dx), m_dy(dy),
+ m_sx(sx), m_sy(sy), m_w(w), m_h(h), m_alpha(alpha) { }
+
+ virtual const char* Description(void) { return "RenderPattern"; }
+
+ virtual bool Execute(cEgl *egl)
+ {
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
+ int sx = m_dx - m_sx;
+ int sy = m_target->height - m_dy - m_source->height + m_sy;
+ int dy = m_target->height - m_dy - m_h;
+
+ while (sx > m_dx)
+ sx -= m_source->width;
+
+ while (sy > dy)
+ sy -= m_source->height;
+
+ int nx = (m_dx + m_w - sx) / m_source->width;
+ if ((m_dx + m_w - sx) % m_source->width) nx++;
+
+ int ny = (dy + m_h - sy) / m_source->height;
+ if ((dy + m_h - sy) % m_source->height) ny++;
+
+ cOvgPaintBox::SetAlpha(m_alpha);
+ cOvgPaintBox::SetScissoring(m_dx, dy, m_w, m_h);
+
+ VGPath path = vgCreatePath(VG_PATH_FORMAT_STANDARD,
+ VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0,
+ VG_PATH_CAPABILITY_TRANSFORM_TO);
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+ vgLoadIdentity();
+ vgScale(m_source->width * nx, m_source->height * ny);
+ vgTransformPath(path, cOvgPaintBox::Rect());
+
+ cOvgPaintBox::SetPattern(m_source->image);
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
+ vgLoadIdentity();
+ vgTranslate(sx, sy);
+
+ cOvgPaintBox::Draw(path);
+
+ cOvgPaintBox::SetAlpha(255);
+ cOvgPaintBox::SetScissoring();
+ cOvgPaintBox::SetPattern();
+ vgDestroyPath(path);
+ return true;
+ }
+
+private:
+
+ cOvgRenderTarget *m_source;
+ int m_dx;
+ int m_dy;
+ int m_sx;
+ int m_sy;
+ int m_w;
+ int m_h;
+ int m_alpha;
+};
+
+class cOvgCmdCopyPixels : public cOvgCmd
+{
+public:
+
+ cOvgCmdCopyPixels(cOvgRenderTarget *target, cOvgRenderTarget *source,
+ int dx, int dy, int sx, int sy, int w, int h) :
+ cOvgCmd(target), m_source(source),
+ m_dx(dx), m_dy(dy), m_sx(sx), m_sy(sy), m_w(w), m_h(h) { }
+
+ virtual const char* Description(void) { return "CopyPixels"; }
+
+ virtual bool Execute(cEgl *egl)
+ {
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
+ vgSetPixels(m_dx, m_target->height - m_h - m_dy, m_source->image,
+ m_sx, m_source->height - m_h - m_sy, m_w, m_h);
+ return true;
+ }
+
+private:
+
+ cOvgRenderTarget *m_source;
+ int m_dx;
+ int m_dy;
+ int m_sx;
+ int m_sy;
+ int m_w;
+ int m_h;
+};
+
+class cOvgCmdMovePixels : public cOvgCmd
+{
+public:
+
+ cOvgCmdMovePixels(cOvgRenderTarget *target,
+ int dx, int dy, int sx, int sy, int w, int h) :
+ cOvgCmd(target),
+ m_dx(dx), m_dy(dy), m_sx(sx), m_sy(sy), m_w(w), m_h(h) { }
+
+ virtual const char* Description(void) { return "MovePixels"; }
+
+ virtual bool Execute(cEgl *egl)
+ {
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
+ vgCopyPixels(m_dx, m_target->height - m_h - m_dy,
+ m_sx, m_target->height - m_h - m_sy, m_w, m_h);
+ return true;
+ }
+
+private:
+
+ int m_dx;
+ int m_dy;
+ int m_sx;
+ int m_sy;
+ int m_w;
+ int m_h;
+};
+
+class cOvgCmdStoreImage : public cOvgCmd
+{
+public:
+
+ cOvgCmdStoreImage(VGImage *image, int w, int h, tColor *argb) :
+ cOvgCmd(0), m_image(image), m_w(w), m_h(h), m_argb(argb) { }
+
+ virtual ~cOvgCmdStoreImage()
+ {
+ free(m_argb);
+ }
+
+ virtual const char* Description(void) { return "StoreImage"; }
+
+ virtual bool Execute(cEgl *egl)
+ {
+ *m_image = vgCreateImage(VG_sARGB_8888, m_w, m_h,
VG_IMAGE_QUALITY_BETTER);
- vgImageSubData(image, m_argb, m_w * sizeof(tColor),
- VG_sARGB_8888, 0, 0, m_w, m_h);
- vgDrawImage(image);
+ if (*m_image == VG_INVALID_HANDLE)
+ return false;
- vgDestroyImage(image);
+ vgImageSubData(*m_image, m_argb, m_w * sizeof(tColor),
+ VG_sARGB_8888, 0, 0, m_w, m_h);
return true;
}
-protected:
+private:
- int m_x;
- int m_y;
+ VGImage *m_image;
int m_w;
int m_h;
- const tColor* m_argb;
- bool m_overlay;
- double m_scaleX;
- double m_scaleY;
+ tColor *m_argb;
};
-class cOvgCmdDrawBitmap : public cOvgCmdDrawImage
+class cOvgCmdDropImage : public cOvgCmd
{
public:
- cOvgCmdDrawBitmap(int x, int y, int w, int h, tColor *bitmap,
- bool overlay = false, double scaleX = 1.0f, double scaleY = 1.0f) :
- cOvgCmdDrawImage(x, y, w, h, bitmap, overlay, scaleX, scaleY),
- m_bitmap(bitmap) { }
+ cOvgCmdDropImage(VGImage *image) :
+ cOvgCmd(0), m_image(image) { }
- virtual ~cOvgCmdDrawBitmap()
+ virtual const char* Description(void) { return "DropImage"; }
+
+ virtual bool Execute(cEgl *egl)
+ {
+ if (*m_image != VG_INVALID_HANDLE)
+ vgDestroyImage(*m_image);
+
+ *m_image = VG_INVALID_HANDLE;
+ return true;
+ }
+
+private:
+
+ VGImage *m_image;
+};
+
+class cOvgCmdDrawImage : public cOvgCmd
+{
+public:
+
+ cOvgCmdDrawImage(cOvgRenderTarget *target, VGImage *image, int x, int y) :
+ cOvgCmd(target), m_image(image), m_x(x), m_y(y) { }
+
+ virtual const char* Description(void) { return "DrawImage"; }
+
+ virtual bool Execute(cEgl *egl)
{
- free(m_bitmap);
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
+ VGint height = vgGetParameteri(*m_image, VG_IMAGE_WIDTH);
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
+ vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
+ vgSeti(VG_IMAGE_QUALITY, VG_IMAGE_QUALITY_BETTER);
+ vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
+
+ vgLoadIdentity();
+ vgTranslate(m_x, m_target->height - height - m_y);
+
+ vgDrawImage(*m_image);
+ return true;
}
protected:
- tColor *m_bitmap;
+ VGImage *m_image;
+ int m_x;
+ int m_y;
};
-class cOvgCmdDrawPixmap : public cOvgCmdDrawImage
+class cOvgCmdDrawBitmap : public cOvgCmd
{
public:
- cOvgCmdDrawPixmap(int x, int y, cPixmapMemory* pixmap) :
- cOvgCmdDrawImage(x + pixmap->ViewPort().X(), y + pixmap->ViewPort().Y(),
- pixmap->ViewPort().Width(), pixmap->ViewPort().Height(),
- (tColor*)pixmap->Data(), false, 1.0f, 1.0f),
- m_pixmap(pixmap) { }
+ cOvgCmdDrawBitmap(cOvgRenderTarget *target,
+ int x, int y, int w, int h, tColor *argb,
+ bool overlay = false, double scaleX = 1.0f, double scaleY = 1.0f) :
+ cOvgCmd(target), m_x(x), m_y(y), m_w(w), m_h(h), m_argb(argb),
+ m_overlay(overlay), m_scaleX(scaleX), m_scaleY(scaleY) { }
- virtual ~cOvgCmdDrawPixmap()
+ virtual ~cOvgCmdDrawBitmap()
{
- delete m_pixmap;
+ free(m_argb);
+ }
+
+ virtual const char* Description(void) { return "DrawBitmap"; }
+
+ virtual bool Execute(cEgl *egl)
+ {
+ if (!m_target->MakeCurrent(egl))
+ return false;
+
+ vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
+ vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
+ vgSeti(VG_IMAGE_QUALITY, VG_IMAGE_QUALITY_BETTER);
+ vgSeti(VG_BLEND_MODE, m_overlay ? VG_BLEND_SRC_OVER : VG_BLEND_SRC);
+
+ vgLoadIdentity();
+ vgScale(1.0f, -1.0f);
+ vgTranslate(m_x, m_y - m_target->height);
+ vgScale(m_scaleX, m_scaleY);
+
+ VGImage image = vgCreateImage(VG_sARGB_8888, m_w, m_h,
+ VG_IMAGE_QUALITY_BETTER);
+
+ if (image == VG_INVALID_HANDLE)
+ {
+ ELOG("failed to allocate image!");
+ return false;
+ }
+
+ vgImageSubData(image, m_argb, m_w * sizeof(tColor),
+ VG_sARGB_8888, 0, 0, m_w, m_h);
+ vgDrawImage(image);
+
+ vgDestroyImage(image);
+ return true;
}
protected:
- cPixmapMemory* m_pixmap;
+ int m_x;
+ int m_y;
+ int m_w;
+ int m_h;
+ tColor *m_argb;
+ bool m_overlay;
+ double m_scaleX;
+ double m_scaleY;
};
/* ------------------------------------------------------------------------- */
+#define MAXOSDIMAGES_OVG 256
+
class cOvgThread : public cThread
{
public:
cOvgThread() : cThread("ovgthread"), m_wait(new cCondWait())
{
+ for (int i = 0; i < MAXOSDIMAGES_OVG; i++)
+ m_images[i] = VG_INVALID_HANDLE;
+
Start();
}
@@ -939,18 +1580,65 @@ public:
m_wait->Signal();
}
+ VGImage *GetImageRef(int imageHandle)
+ {
+ if (0 <= imageHandle && imageHandle < MAXOSDIMAGES_OVG)
+ return &m_images[imageHandle];
+
+ return VG_INVALID_HANDLE;
+ }
+
+ virtual int StoreImageData(const cImage &image)
+ {
+ Lock();
+ int imageId = -1;
+ for (int i = 0; i < MAXOSDIMAGES_OVG && imageId < 0; i++)
+ if (m_images[i] == VG_INVALID_HANDLE)
+ {
+ tColor *argb = MALLOC(tColor, image.Width() * image.Height());
+ if (argb)
+ {
+ // mark as used - will be overwritten by OVG thread
+ m_images[i]++;
+ imageId = i;
+
+ memcpy(argb, image.Data(),
+ sizeof(tColor) * image.Width() * image.Height());
+
+ DoCmd(new cOvgCmdStoreImage(&m_images[i], image.Width(),
+ image.Height(), argb));
+ }
+ }
+
+ Unlock();
+ if (imageId < 0)
+ ELOG("failed to store OSD image!");
+
+ return imageId;
+ }
+
+ virtual void DropImageData(int imageHandle)
+ {
+ Lock();
+ if (0 <= imageHandle && imageHandle < MAXOSDIMAGES_OVG)
+ DoCmd(new cOvgCmdDropImage(&m_images[imageHandle]));
+ Unlock();
+ }
+
protected:
virtual void Action(void)
{
DLOG("cOvgThread() thread started");
- EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- if (display == EGL_NO_DISPLAY)
- ELOG("failed to get EGL display connection!");
+ cEgl egl;
+ egl.display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+ if (egl.display == EGL_NO_DISPLAY)
+ ELOG("[EGL] failed to get display connection!");
- if (eglInitialize(display, NULL, NULL) == EGL_FALSE)
- ELOG("failed to init EGL display connection!");
+ if (eglInitialize(egl.display, NULL, NULL) == EGL_FALSE)
+ ELOG("[EGL] failed to init display connection!");
eglBindAPI(EGL_OPENVG_API);
@@ -964,67 +1652,67 @@ protected:
EGL_NONE
};
- EGLConfig config;
- EGLint nConfig;
-
// get an appropriate EGL frame buffer configuration
- if (eglChooseConfig(display, attr, &config, 1, &nConfig)
+ if (eglChooseConfig(egl.display, attr, &egl.config, 1, &egl.nConfig)
== EGL_FALSE)
- ELOG("failed to get EGL frame buffer config!");
+ ELOG("[EGL] failed to get frame buffer config!");
// create an EGL rendering context
- EGLContext context = eglCreateContext(display, config, NULL, NULL);
- if (context == EGL_NO_CONTEXT)
- ELOG("failed to create EGL rendering context!");
+ egl.context = eglCreateContext(egl.display, egl.config, NULL, NULL);
+ if (egl.context == EGL_NO_CONTEXT)
+ ELOG("[EGL] failed to create rendering context!");
while (Running())
{
- bool reset = false;
-
- DISPMANX_DISPLAY_HANDLE_T dDisplay = vc_dispmanx_display_open(
+ DISPMANX_DISPLAY_HANDLE_T display = vc_dispmanx_display_open(
cRpiDisplay::GetVideoPort() == cRpiVideoPort::eHDMI ? 0 : 1);
DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(0);
- int width, height;
- cRpiDisplay::GetSize(width, height);
+ cRpiDisplay::GetSize(egl.window.width, egl.window.height);
- VC_RECT_T dstRect = { 0, 0, width, height };
- VC_RECT_T srcRect = { 0, 0, width << 16, height << 16 };
+ VC_RECT_T srcRect = { 0, 0,
+ egl.window.width << 16, egl.window.height << 16 };
+ VC_RECT_T dstRect = { 0, 0, egl.window.width, egl.window.height };
- DISPMANX_ELEMENT_HANDLE_T element = vc_dispmanx_element_add(
- update, dDisplay, 2 /*layer*/, &dstRect, 0, &srcRect,
+ egl.window.element = vc_dispmanx_element_add(
+ update, display, 2 /*layer*/, &dstRect, 0, &srcRect,
DISPMANX_PROTECTION_NONE, 0, 0, (DISPMANX_TRANSFORM_T)0);
- vc_dispmanx_update_submit_sync(update);
- EGL_DISPMANX_WINDOW_T nativewindow;
- nativewindow.element = element;
- nativewindow.width = width;
- nativewindow.height = height;
+ vc_dispmanx_update_submit_sync(update);
- const EGLint windowAttr[] = {
- EGL_RENDER_BUFFER, EGL_SINGLE_BUFFER,
+ // create background surface
+ const EGLint attr[] = {
+ EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
EGL_NONE
};
- EGLSurface surface = eglCreateWindowSurface(display, config,
- &nativewindow, windowAttr);
- if (surface == EGL_NO_SURFACE)
- ELOG("failed to create EGL window surface!");
+ egl.surface = eglCreateWindowSurface(egl.display, egl.config,
+ &egl.window, attr);
+ if (egl.surface == EGL_NO_SURFACE)
+ ELOG("[EGL] failed to create window surface: %s!",
+ cEgl::errStr(eglGetError()));
- // connect the context to the surface
- if (eglMakeCurrent(display, surface, surface, context)
- == EGL_FALSE)
- ELOG("failed to connect context to surface!");
+ if (eglSurfaceAttrib(egl.display, egl.surface,
+ EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED) == EGL_FALSE)
+ ELOG("[EGL] failed to set surface attributes: %s!",
+ cEgl::errStr(eglGetError()));
+
+ if (eglMakeCurrent(egl.display, egl.surface, egl.surface,
+ egl.context) == EGL_FALSE)
+ ELOG("failed to connect context to surface: %s!",
+ cEgl::errStr(eglGetError()));
+
+ egl.currentSurface = egl.surface;
float color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
- vgSetfv(VG_CLEAR_COLOR, 4, color);
- vgClear(0, 0, width, height);
- eglSwapBuffers(display, surface);
+ vgSetfv(VG_CLEAR_COLOR, 4, color);
+ vgClear(0, 0, egl.window.width, egl.window.height);
+ bool reset = false;
while (!reset)
{
if (m_commands.empty())
- m_wait->Wait(100);
+ m_wait->Wait(20);
else
{
Lock();
@@ -1032,100 +1720,173 @@ protected:
m_commands.pop();
Unlock();
- if (cmd)
- {
- if (!cmd->Execute(display, surface, width, height))
- reset = true;
+ reset = cmd ? !cmd->Execute(&egl) : true;
- delete cmd;
- }
+ VGErrorCode err = vgGetError();
+ if (cmd && err != VG_NO_ERROR)
+ ELOG("[OpenVG] %s error: %s",
+ cmd->Description(), errStr(err));
+
+ //ELOG("[OpenVG] %s", cmd->Description());
+ delete cmd;
}
}
- // Release OpenGL resources
- eglMakeCurrent(display,
- EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- eglDestroySurface(display, surface);
+ if (eglDestroySurface(egl.display, egl.surface) == EGL_FALSE)
+ ELOG("[EGL] failed to destroy surface: %s!",
+ cEgl::errStr(eglGetError()));
+
+ vc_dispmanx_element_remove(update, egl.window.element);
+ vc_dispmanx_display_close(display);
- vc_dispmanx_element_remove(update, element);
- vc_dispmanx_display_close(dDisplay);
+ DLOG("cOvgThread() thread reset");
}
+ for (int i = 0; i < MAXOSDIMAGES_OVG; i++)
+ if (m_images[i] != VG_INVALID_HANDLE)
+ vgDestroyImage(m_images[i]);
+
cOvgFont::CleanUp();
cOvgPaintBox::CleanUp();
+ vgFinish();
- eglDestroyContext(display, context);
- eglTerminate(display);
+ eglDestroyContext(egl.display, egl.context);
+ eglTerminate(egl.display);
DLOG("cOvgThread() thread ended");
}
private:
+ static const char* errStr(VGErrorCode error)
+ {
+ return
+ error == VG_NO_ERROR ? "no error" :
+ error == VG_BAD_HANDLE_ERROR ? "bad handle" :
+ error == VG_ILLEGAL_ARGUMENT_ERROR ? "illegal argument" :
+ error == VG_OUT_OF_MEMORY_ERROR ? "out of memory" :
+ error == VG_PATH_CAPABILITY_ERROR ? "path capability" :
+ error == VG_UNSUPPORTED_IMAGE_FORMAT_ERROR ? "unsup. image format" :
+ error == VG_UNSUPPORTED_PATH_FORMAT_ERROR ? "unsup. path format" :
+ error == VG_IMAGE_IN_USE_ERROR ? "image in use" :
+ error == VG_NO_CONTEXT_ERROR ? "no context" :
+ "unknown error";
+ }
+
std::queue<cOvgCmd*> m_commands;
cCondWait *m_wait;
+ VGImage m_images[MAXOSDIMAGES_OVG];
};
/* ------------------------------------------------------------------------- */
-class cOvgOsd : public cOsd
+class cOvgPixmap : public cPixmap
{
public:
- cOvgOsd(int Left, int Top, uint Level, cOvgThread *ovg) :
- cOsd(Left, Top, Level),
- m_ovg(ovg), m_savedImage(0)
- { }
+ cOvgPixmap(int Layer, cOvgThread *ovg, const cRect &ViewPort,
+ const cRect &DrawPort = cRect::Null) :
+ cPixmap(Layer, ViewPort, DrawPort),
+ m_ovg(ovg),
+ m_buffer(0),
+ m_dirty(false)
+ {
+ m_buffer = new cOvgRenderTarget(
+ this->DrawPort().Width(), this->DrawPort().Height());
- virtual ~cOvgOsd()
+ m_ovg->DoCmd(new cOvgCmdCreatePixelBuffer(m_buffer));
+ }
+
+ virtual ~cOvgPixmap()
{
- SetActive(false);
- if (m_savedImage)
- m_ovg->DoCmd(new cOvgCmdDropRegion(&m_savedImage));
+ m_ovg->DoCmd(new cOvgCmdDestroySurface(m_buffer));
}
- virtual eOsdError SetAreas(const tArea *Areas, int NumAreas)
+ virtual void SetAlpha(int Alpha)
{
- if (Active())
- m_ovg->DoCmd(new cOvgCmdClear());
+ Alpha = constrain(Alpha, ALPHA_TRANSPARENT, ALPHA_OPAQUE);
+ if (Alpha != cPixmap::Alpha())
+ {
+ cPixmap::SetAlpha(Alpha);
+ SetDirty();
+ }
+ }
- return cOsd::SetAreas(Areas, NumAreas);
+ virtual void SetTile(bool Tile)
+ {
+ cPixmap::SetTile(Tile);
+ SetDirty();
}
- virtual void SaveRegion(int x1, int y1, int x2, int y2)
+ virtual void SetViewPort(const cRect &Rect)
{
- if (!Active())
- return;
+ cPixmap::SetViewPort(Rect);
+ SetDirty();
+ }
- m_savedRect = cRect(x1 + Left(), y1 + Top(), x2 - x1 + 1, y2 - y1 + 1);
- m_ovg->DoCmd(new cOvgCmdSaveRegion(&m_savedRect, &m_savedImage));
+ virtual void SetDrawPortPoint(const cPoint &Point, bool Dirty = true)
+ {
+ cPixmap::SetDrawPortPoint(Point, Dirty);
+ if (Dirty)
+ SetDirty();
}
- virtual void RestoreRegion(void)
+ virtual void Clear(void)
{
- if (!Active())
- return;
+ LOCK_PIXMAPS;
+ m_ovg->DoCmd(new cOvgCmdClear(m_buffer));
+ SetDirty();
+ MarkDrawPortDirty(DrawPort());
+ }
- if (m_savedImage)
- m_ovg->DoCmd(new cOvgCmdRestoreRegion(&m_savedRect, &m_savedImage));
+ virtual void Fill(tColor Color)
+ {
+ LOCK_PIXMAPS;
+ m_ovg->DoCmd(new cOvgCmdClear(m_buffer, Color));
+ SetDirty();
+ MarkDrawPortDirty(DrawPort());
}
- virtual void DrawPixel(int x, int y, tColor Color)
+ virtual void DrawImage(const cPoint &Point, const cImage &Image)
{
- if (!Active())
+ LOCK_PIXMAPS;
+ tColor *argb = MALLOC(tColor, Image.Width() * Image.Height());
+ if (!argb)
return;
- m_ovg->DoCmd(new cOvgCmdDrawPixel(x + Left(), y + Top(), Color));
+ memcpy(argb, Image.Data(),
+ sizeof(tColor) * Image.Width() * Image.Height());
+
+ m_ovg->DoCmd(new cOvgCmdDrawBitmap(m_buffer, Point.X(), Point.Y(),
+ Image.Width(), Image.Height(), argb, false));
+
+ SetDirty();
+ MarkDrawPortDirty(cRect(Point, cSize(Image.Width(),
+ Image.Height())).Intersected(DrawPort().Size()));
}
- virtual void DrawBitmap(int x, int y, const cBitmap &Bitmap,
- tColor ColorFg = 0, tColor ColorBg = 0,
- bool ReplacePalette = false, bool Overlay = false)
+ virtual void DrawImage(const cPoint &Point, int ImageHandle)
{
- if (!Active())
- return;
+ m_ovg->DoCmd(new cOvgCmdDrawImage(m_buffer,
+ m_ovg->GetImageRef(ImageHandle), Point.X(), Point.Y()));
+ SetDirty();
+ }
+
+ virtual void DrawPixel(const cPoint &Point, tColor Color)
+ {
+ LOCK_PIXMAPS;
+ m_ovg->DoCmd(new cOvgCmdDrawPixel(m_buffer, Point.X(), Point.Y(),
+ Color, Layer() == 0 && !IS_OPAQUE(Color)));
+ SetDirty();
+ MarkDrawPortDirty(Point);
+ }
+
+ virtual void DrawBitmap(const cPoint &Point, const cBitmap &Bitmap,
+ tColor ColorFg = 0, tColor ColorBg = 0, bool Overlay = false)
+ {
+ LOCK_PIXMAPS;
bool specialColors = ColorFg || ColorBg;
tColor *argb = MALLOC(tColor, Bitmap.Width() * Bitmap.Height());
if (!argb)
@@ -1137,20 +1898,22 @@ public:
{
tIndex index = *Bitmap.Data(px, py);
*p++ = (!index && Overlay) ? clrTransparent : (specialColors ?
- (index == 0 ? ColorBg : index ==1 ? ColorFg :
+ (index == 0 ? ColorBg : index == 1 ? ColorFg :
Bitmap.Color(index)) : Bitmap.Color(index));
}
- m_ovg->DoCmd(new cOvgCmdDrawBitmap(Left() + x, Top() + y,
+ m_ovg->DoCmd(new cOvgCmdDrawBitmap(m_buffer, Point.X(), Point.Y(),
Bitmap.Width(), Bitmap.Height(), argb, Overlay));
+
+ SetDirty();
+ MarkDrawPortDirty(cRect(Point, cSize(Bitmap.Width(),
+ Bitmap.Height())).Intersected(DrawPort().Size()));
}
- virtual void DrawScaledBitmap(int x, int y, const cBitmap &Bitmap,
+ virtual void DrawScaledBitmap(const cPoint &Point, const cBitmap &Bitmap,
double FactorX, double FactorY, bool AntiAlias = false)
{
- if (!Active())
- return;
-
+ LOCK_PIXMAPS;
tColor *argb = MALLOC(tColor, Bitmap.Width() * Bitmap.Height());
if (!argb)
return;
@@ -1160,72 +1923,381 @@ public:
for (int px = 0; px < Bitmap.Width(); px++)
*p++ = Bitmap.Color(*Bitmap.Data(px, py));
- m_ovg->DoCmd(new cOvgCmdDrawBitmap(Left() + x, Top() + y, Bitmap.Width(),
- Bitmap.Height(), argb, false, FactorX, FactorY));
+ m_ovg->DoCmd(new cOvgCmdDrawBitmap(m_buffer, Point.X(), Point.Y(),
+ Bitmap.Width(), Bitmap.Height(), argb, false,
+ FactorX, FactorY));
+
+ SetDirty();
+ MarkDrawPortDirty(cRect(Point, cSize(
+ int(round(Bitmap.Width() * FactorX)) + 1,
+ int(round(Bitmap.Height() * FactorY)) + 1)).Intersected(
+ DrawPort().Size()));
+ }
+
+ virtual void DrawText(const cPoint &Point, const char *s, tColor ColorFg,
+ tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0,
+ int Alignment = taDefault)
+ {
+ 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;
+
+ m_ovg->DoCmd(new cOvgCmdDrawText(m_buffer, Point.X(), Point.Y(),
+ symbols, new cString(Font->FontName()), Font->Size(),
+ ColorFg, ColorBg, Width, Height, Alignment));
+
+ SetDirty();
+ MarkDrawPortDirty(cRect(Point.X(), Point.Y(),
+ Width ? Width : DrawPort().Width() - Point.X(),
+ Height ? Height : DrawPort().Height() - Point.Y()));
+ }
+
+ virtual void DrawRectangle(const cRect &Rect, tColor Color)
+ {
+ LOCK_PIXMAPS;
+ m_ovg->DoCmd(new cOvgCmdDrawRectangle(m_buffer,
+ Rect.X(), Rect.Y(), Rect.Width(), Rect.Height(), Color));
+
+ SetDirty();
+ MarkDrawPortDirty(Rect);
}
- virtual void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
+ virtual void DrawEllipse(const cRect &Rect, tColor Color, int Quadrants = 0)
+ {
+ LOCK_PIXMAPS;
+ m_ovg->DoCmd(new cOvgCmdDrawEllipse(m_buffer,
+ Rect.X(), Rect.Y(), Rect.Width(), Rect.Height(),
+ Color, Quadrants));
+
+ SetDirty();
+ MarkDrawPortDirty(Rect);
+ }
+
+ virtual void DrawSlope(const cRect &Rect, tColor Color, int Type)
+ {
+ LOCK_PIXMAPS;
+ m_ovg->DoCmd(new cOvgCmdDrawSlope(m_buffer,
+ Rect.X(), Rect.Y(), Rect.Width(), Rect.Height(), Color, Type));
+
+ SetDirty();
+ MarkDrawPortDirty(Rect);
+ }
+
+ virtual void Render(const cPixmap *Pixmap, const cRect &Source,
+ const cPoint &Dest)
+ {
+ LOCK_PIXMAPS;
+ if (Pixmap->Alpha() == ALPHA_TRANSPARENT)
+ return;
+
+ if (const cOvgPixmap *pm = dynamic_cast<const cOvgPixmap *>(Pixmap))
+ {
+ m_ovg->DoCmd(new cOvgCmdRenderPixels(m_buffer, pm->m_buffer,
+ Dest.X(), Dest.Y(), Source.X(), Source.Y(),
+ Source.Width(), Source.Height(), pm->Alpha()));
+
+ SetDirty();
+ MarkDrawPortDirty(DrawPort());
+ }
+ }
+
+ virtual void Copy(const cPixmap *Pixmap, const cRect &Source,
+ const cPoint &Dest)
+ {
+ LOCK_PIXMAPS;
+ if (const cOvgPixmap *pm = dynamic_cast<const cOvgPixmap *>(Pixmap))
+ {
+ m_ovg->DoCmd(new cOvgCmdCopyPixels(m_buffer, pm->m_buffer,
+ Dest.X(), Dest.Y(), Source.X(), Source.Y(),
+ Source.Width(), Source.Height()));
+
+ SetDirty();
+ MarkDrawPortDirty(DrawPort());
+ }
+ }
+
+ virtual void Scroll(const cPoint &Dest, const cRect &Source, bool pan)
+ {
+ LOCK_PIXMAPS;
+ cRect s;
+ if (&Source == &cRect::Null)
+ s = DrawPort().Shifted(-DrawPort().Point());
+ else
+ s = Source.Intersected(DrawPort().Size());
+
+ if (Dest != s.Point())
+ {
+ m_ovg->DoCmd(new cOvgCmdMovePixels(m_buffer, Dest.X(), Dest.Y(),
+ s.X(), s.Y(), s.Width(), s.Height()));
+
+ if (pan)
+ SetDrawPortPoint(DrawPort().Point().Shifted(s.Point() - Dest),
+ false);
+ else
+ MarkDrawPortDirty(Dest);
+ SetDirty();
+ }
+ }
+
+ virtual void Scroll(const cPoint &Dest, const cRect &Source = cRect::Null)
+ {
+ Scroll(Dest, Source, false);
+ }
+
+ virtual void Pan(const cPoint &Dest, const cRect &Source = cRect::Null)
+ {
+ Scroll(Dest, Source, true);
+ }
+
+ virtual void CopyToTarget(cOvgRenderTarget *target, int left, int top)
+ {
+ LOCK_PIXMAPS;
+ cRect d = ViewPort().Shifted(left, top);
+ cPoint s = -DrawPort().Point();
+
+ m_ovg->DoCmd(new cOvgCmdCopyPixels(target, m_buffer,
+ d.X(), d.Y(), s.X(), s.Y(), d.Width(), d.Height()));
+
+ SetDirty(false);
+ }
+
+ virtual void RenderToTarget(cOvgRenderTarget *target, int left, int top)
+ {
+ LOCK_PIXMAPS;
+ cRect d = ViewPort().Shifted(left, top);
+ cPoint s = -DrawPort().Point();
+
+ if (Tile())
+ m_ovg->DoCmd(new cOvgCmdRenderPattern(target, m_buffer,
+ d.X(), d.Y(), s.X(), s.Y(), d.Width(), d.Height(),
+ Alpha()));
+ else
+ m_ovg->DoCmd(new cOvgCmdRenderPixels(target, m_buffer,
+ d.X(), d.Y(), s.X(), s.Y(), d.Width(), d.Height(),
+ Alpha()));
+
+ SetDirty(false);
+ }
+
+ virtual bool IsDirty(void) { return m_dirty; }
+ virtual void SetDirty(bool dirty = true) { m_dirty = dirty; }
+
+private:
+
+ cOvgPixmap(const cOvgPixmap&);
+ cOvgPixmap& operator= (const cOvgPixmap&);
+
+ cOvgThread *m_ovg;
+ cOvgRenderTarget *m_buffer;
+
+ bool m_dirty;
+};
+
+/* ------------------------------------------------------------------------- */
+
+class cOvgOsd : public cOsd
+{
+public:
+
+ cOvgOsd(int Left, int Top, uint Level, cOvgThread *ovg) :
+ cOsd(Left, Top, Level),
+ m_ovg(ovg),
+ m_surface(new cOvgRenderTarget()),
+ m_savedImage(0)
+ { }
+
+ virtual ~cOvgOsd()
+ {
+ SetActive(false);
+ if (m_savedImage)
+ m_ovg->DoCmd(new cOvgCmdDropRegion(&m_savedImage));
+
+ m_ovg->DoCmd(new cOvgCmdDestroySurface(m_surface));
+ }
+
+ virtual eOsdError SetAreas(const tArea *Areas, int NumAreas)
+ {
+ cRect r;
+ for (int i = 0; i < NumAreas; i++)
+ r.Combine(cRect(Areas[0].x1, Areas[0].y1,
+ Areas[0].x2 - Areas[0].x1 + 1,
+ Areas[0].y2 - Areas[0].y1 + 1));
+
+ tArea area = { r.Left(), r.Top(), r.Right(), r.Bottom(), 32 };
+
+ for (int i = 0; i < m_pixmaps.Size(); i++)
+ m_pixmaps[i] = NULL;
+
+ return cOsd::SetAreas(&area, 1);
+ }
+
+ cPixmap *CreatePixmap(int Layer, const cRect &ViewPort,
+ const cRect &DrawPort)
+ {
+ LOCK_PIXMAPS;
+ cOvgPixmap *pm = new cOvgPixmap(Layer, m_ovg, ViewPort, DrawPort);
+
+ if (cOsd::AddPixmap(pm))
+ {
+ for (int i = 0; i < m_pixmaps.Size(); i++)
+ if (!m_pixmaps[i])
+ return m_pixmaps[i] = pm;
+
+ m_pixmaps.Append(pm);
+ return pm;
+ }
+
+ delete pm;
+ return NULL;
+ }
+
+ virtual void DestroyPixmap(cPixmap *Pixmap)
+ {
+ if (Pixmap)
+ {
+ LOCK_PIXMAPS;
+ for (int i = 1; i < m_pixmaps.Size(); i++)
+ if (m_pixmaps[i] == Pixmap)
+ {
+ if (Pixmap->Layer() >= 0)
+ m_pixmaps[0]->SetDirty();
+
+ m_pixmaps[i] = NULL;
+ cOsd::DestroyPixmap(Pixmap);
+ return;
+ }
+ }
+ }
+
+ virtual void SaveRegion(int x1, int y1, int x2, int y2)
{
if (!Active())
return;
- m_ovg->DoCmd(new cOvgCmdDrawRectangle(x1 + Left(), y1 + Top(),
- x2 - x1 + 1, y2 - y1 + 1, Color));
+ m_savedRect = cRect(x1 + Left(), y1 + Top(), x2 - x1 + 1, y2 - y1 + 1);
+ m_ovg->DoCmd(new cOvgCmdSaveRegion(&m_savedRect, &m_savedImage));
+ }
+
+ virtual void RestoreRegion(void)
+ {
+ if (!Active())
+ return;
+
+ if (m_savedImage)
+ m_ovg->DoCmd(new cOvgCmdRestoreRegion(&m_savedRect, &m_savedImage));
+ }
+
+ virtual void DrawPixel(int x, int y, tColor Color)
+ {
+ if (!m_pixmaps[0])
+ return;
+
+ m_pixmaps[0]->DrawPixel(
+ cPoint(x, y) - m_pixmaps[0]->ViewPort().Point(), Color);
+ }
+
+ virtual void DrawBitmap(int x, int y, const cBitmap &Bitmap,
+ tColor ColorFg = 0, tColor ColorBg = 0,
+ bool ReplacePalette = false, bool Overlay = false)
+ {
+ if (!m_pixmaps[0])
+ return;
+
+ m_pixmaps[0]->DrawBitmap(
+ cPoint(x, y) - m_pixmaps[0]->ViewPort().Point(),
+ Bitmap, ColorFg, ColorBg, Overlay);
+ }
+
+ virtual void DrawScaledBitmap(int x, int y, const cBitmap &Bitmap,
+ double FactorX, double FactorY, bool AntiAlias = false)
+ {
+ if (!m_pixmaps[0])
+ return;
+
+ m_pixmaps[0]->DrawScaledBitmap(
+ cPoint(x, y) - m_pixmaps[0]->ViewPort().Point(),
+ Bitmap, FactorX, FactorY);
}
virtual void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color,
int Quadrants = 0)
{
- if (!Active())
+ if (!m_pixmaps[0])
return;
- m_ovg->DoCmd(new cOvgCmdDrawEllipse(x1 + Left(), y1 + Top(),
- x2 - x1 + 1, y2 - y1 + 1, Color, Quadrants));
+ m_pixmaps[0]->DrawEllipse(
+ cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1).Shifted(
+ - m_pixmaps[0]->ViewPort().Point()), Color, Quadrants);
}
virtual void DrawSlope(int x1, int y1, int x2, int y2, tColor Color,
int Type)
{
- if (!Active())
+ if (!m_pixmaps[0])
return;
- m_ovg->DoCmd(new cOvgCmdDrawSlope(x1 + Left(), y1 + Top(),
- x2 - x1 + 1, y2 - y1 + 1, Color, Type));
+ m_pixmaps[0]->DrawSlope(
+ cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1).Shifted(
+ - m_pixmaps[0]->ViewPort().Point()), Color, Type);
}
virtual void DrawText(int x, int y, const char *s, tColor ColorFg,
tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0,
int Alignment = taDefault)
{
- if (!Active())
+ if (!m_pixmaps[0])
return;
- if (Width && ColorBg != clrTransparent)
- m_ovg->DoCmd(new cOvgCmdDrawRectangle(x + Left(), y + Top(),
- Width, Height ? Height : Font->Height(), ColorBg));
- if (!s)
+ m_pixmaps[0]->DrawText(cPoint(x, y) - m_pixmaps[0]->ViewPort().Point(),
+ s, ColorFg, ColorBg, Font, Width, Height, Alignment);
+ }
+
+ virtual void Flush(void)
+ {
+ if (!Active())
return;
- int len = Utf8StrLen(s);
- if (len)
+ LOCK_PIXMAPS;
+
+//#define USE_VDRS_RENDERING
+#ifdef USE_VDRS_RENDERING
+ while (cOvgPixmap *pm = dynamic_cast<cOvgPixmap *>(RenderPixmaps()))
{
- unsigned int *symbols = MALLOC(unsigned int, len + 1);
- if (!symbols)
- return;
+ pm->CopyToTarget(m_surface, Left(), Top());
- Utf8ToArray(s, symbols, len + 1);
- m_ovg->DoCmd(new cOvgCmdDrawText(x + Left(), y + Top(), symbols,
- new cString(Font->FontName()), Font->Size(), ColorFg,
- Width, Height, Alignment));
+#if APIVERSNUM >= 20110
+ DestroyPixmap(pm);
+#else
+ delete pm;
+#endif
}
- }
+#else
+ bool dirty = false;
+ for (int i = 0; i < m_pixmaps.Size() && !dirty; i++)
+ if (m_pixmaps[i] &&
+ m_pixmaps[i]->Layer() >= 0 && m_pixmaps[i]->IsDirty())
+ dirty = true;
- virtual void Flush(void)
- {
- if (!Active())
+ if (!dirty)
return;
- m_ovg->DoCmd(new cOvgCmdFlush(), true);
+ m_ovg->DoCmd(new cOvgCmdClear(m_surface));
+
+ for (int layer = 0; layer < MAXPIXMAPLAYERS; layer++)
+ for (int i = 0; i < m_pixmaps.Size(); i++)
+ if (m_pixmaps[i])
+ if (m_pixmaps[i]->Layer() == layer)
+ m_pixmaps[i]->RenderToTarget(m_surface, Left(), Top());
+#endif
+ m_ovg->DoCmd(new cOvgCmdFlush(m_surface), true);
+ return;
}
protected:
@@ -1236,19 +2308,26 @@ protected:
{
cOsd::SetActive(On);
if (!On)
- m_ovg->DoCmd(new cOvgCmdClear());
+ Clear();
else
if (GetBitmap(0))
Flush();
}
}
-private:
+ virtual void Clear(void)
+ {
+ m_ovg->DoCmd(new cOvgCmdClear(m_surface));
+ m_ovg->DoCmd(new cOvgCmdFlush(m_surface));
+ }
- cOvgThread *m_ovg;
- cRect m_savedRect;
- VGImage m_savedImage;
+private:
+ cOvgThread *m_ovg;
+ cOvgRenderTarget *m_surface;
+ cVector<cOvgPixmap *> m_pixmaps;
+ cRect m_savedRect;
+ VGImage m_savedImage;
};
/* ------------------------------------------------------------------------- */
@@ -1259,12 +2338,14 @@ public:
cOvgRawOsd(int Left, int Top, uint Level, cOvgThread *ovg) :
cOsd(Left, Top, Level),
- m_ovg(ovg)
+ m_ovg(ovg),
+ m_surface(new cOvgRenderTarget())
{ }
virtual ~cOvgRawOsd()
{
SetActive(false);
+ m_ovg->DoCmd(new cOvgCmdDestroySurface(m_surface));
}
virtual void Flush(void)
@@ -1284,7 +2365,7 @@ public:
memcpy(argb, pm->Data(), sizeof(tColor) *
pm->DrawPort().Width() * pm->DrawPort().Height());
- m_ovg->DoCmd(new cOvgCmdDrawBitmap(
+ m_ovg->DoCmd(new cOvgCmdDrawBitmap(m_surface,
Left() + pm->ViewPort().Left(),
Top() + pm->ViewPort().Top(),
pm->DrawPort().Width(), pm->DrawPort().Height(),
@@ -1315,14 +2396,15 @@ public:
for (int x = x1; x <= x2; ++x)
*p++ = bitmap->GetColor(x, y);
- m_ovg->DoCmd(new cOvgCmdDrawBitmap(Left() + bitmap->X0()
- + x1, Top() + bitmap->Y0() + y1, w, h, argb));
+ m_ovg->DoCmd(new cOvgCmdDrawBitmap(m_surface,
+ Left() + bitmap->X0() + x1,
+ Top() + bitmap->Y0() + y1, w, h, argb));
bitmap->Clean();
}
}
}
- m_ovg->DoCmd(new cOvgCmdFlush(), true);
+ m_ovg->DoCmd(new cOvgCmdFlush(m_surface), true);
}
virtual eOsdError SetAreas(const tArea *Areas, int NumAreas)
@@ -1331,7 +2413,7 @@ public:
cBitmap *bitmap;
if (Active())
- m_ovg->DoCmd(new cOvgCmdClear());
+ Clear();
error = cOsd::SetAreas(Areas, NumAreas);
@@ -1349,16 +2431,23 @@ protected:
{
cOsd::SetActive(On);
if (!On)
- m_ovg->DoCmd(new cOvgCmdClear());
+ Clear();
else
if (GetBitmap(0))
Flush();
}
}
+ virtual void Clear(void)
+ {
+ m_ovg->DoCmd(new cOvgCmdClear(m_surface));
+ m_ovg->DoCmd(new cOvgCmdFlush(m_surface));
+ }
+
private:
- cOvgThread *m_ovg;
+ cOvgThread *m_ovg;
+ cOvgRenderTarget *m_surface;
};
@@ -1390,6 +2479,16 @@ cOsd *cRpiOsdProvider::CreateOsd(int Left, int Top, uint Level)
return new cOvgRawOsd(Left, Top, Level, m_ovg);
}
+int cRpiOsdProvider::StoreImageData(const cImage &Image)
+{
+ return m_ovg->StoreImageData(Image);
+}
+
+void cRpiOsdProvider::DropImageData(int ImageHandle)
+{
+ m_ovg->DropImageData(ImageHandle);
+}
+
void cRpiOsdProvider::ResetOsd(bool cleanup)
{
if (s_instance)
diff --git a/ovgosd.h b/ovgosd.h
index 2e35ae4..7c88821 100644
--- a/ovgosd.h
+++ b/ovgosd.h
@@ -25,8 +25,8 @@ protected:
virtual cOsd *CreateOsd(int Left, int Top, uint Level);
virtual bool ProvidesTrueColor(void) { return true; }
- virtual int StoreImageData(const cImage &Image) { return 0; }
- virtual void DropImageData(int ImageHandle) { }
+ virtual int StoreImageData(const cImage &Image);
+ virtual void DropImageData(int ImageHandle);
private: