summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS2
-rw-r--r--HISTORY16
-rw-r--r--PLUGINS/src/osddemo/HISTORY4
-rw-r--r--PLUGINS/src/osddemo/README7
-rw-r--r--PLUGINS/src/osddemo/osddemo.c367
-rw-r--r--PLUGINS/src/skincurses/skincurses.c3
-rw-r--r--font.c60
-rw-r--r--font.h8
-rw-r--r--osd.c1098
-rw-r--r--osd.h534
-rw-r--r--skinsttng.c121
11 files changed, 2135 insertions, 85 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index fd06cd2a..ff821cc9 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1260,6 +1260,8 @@ Reinhard Nissl <rnissl@gmx.de>
for implementing cDeviceHook
for implementing cDevice::GetCurrentlyTunedTransponder()
for fixing DDS detection for HD resolution subtitles
+ for some valuable input during development of the TrueColor OSD, help with
+ debugging, and an implementation of the AlphaBlend() function.
Richard Robson <richard_robson@beeb.net>
for reporting freezing replay if a timer starts while in Transfer Mode from the
diff --git a/HISTORY b/HISTORY
index ff9f7d49..7cbe0fa7 100644
--- a/HISTORY
+++ b/HISTORY
@@ -6481,7 +6481,7 @@ Video Disk Recorder Revision History
from Osama Alrawab). See INSTALL for information on how to turn this on.
- Added Arabian language texts (thanks to Osama Alrawab).
-2010-12-29: Version 1.7.17
+2011-02-20: Version 1.7.17
- Updated the Estonian OSD texts (thanks to Arthur Konovalov).
- Fixed following symbolic links in RemoveFileOrDir() (cont'd) (thanks to
@@ -6519,3 +6519,17 @@ Video Disk Recorder Revision History
recording is started that has a frame rate other than the default.
- The include path to the freetype2 header files is now retrieved via a call to
'pkg-config --cflags freetype2' (suggested by Andreas Oberritter).
+- The OSD now has full TrueColor support. There can be several "pixmaps" that can
+ be overlayed with alpha blending. All existing skins should work out of the box
+ with the TrueColor OSD - the only exception being cOsd::GetBitmap(). Since the
+ TrueColor OSD doesn't use bitmaps, this function will return a dummy bitmap, which
+ may not be what the plugin expects. As long as this bitmap is only used for setting
+ the palette, there is no problem. However, any other operations on this bitmap will
+ have no effect. See the description of the cPixmap functions in osd.h for details
+ about the new functionalities.
+ The "ST:TNG Panels" skin has been enhanced to automatically use the TrueColor OSD
+ if available.
+ The "osddemo" plugin has been extended to show some of the possibilities of the
+ TrueColor OSD if it is run on a system that actually provides TrueColor support.
+ Thanks to Reinhard Nissl for some valuable input, help with debugging, and an
+ implementation of the AlphaBlend() function.
diff --git a/PLUGINS/src/osddemo/HISTORY b/PLUGINS/src/osddemo/HISTORY
index 4217cb1e..1270fbf6 100644
--- a/PLUGINS/src/osddemo/HISTORY
+++ b/PLUGINS/src/osddemo/HISTORY
@@ -21,3 +21,7 @@ VDR Plugin 'osddemo' Revision History
2008-04-13: Version 0.1.3
- Fixed setting the OSD level (thanks to Wolfgang Rohdewald).
+
+2011-02-20: Version 0.2.0
+
+- Added support for TrueColor OSD.
diff --git a/PLUGINS/src/osddemo/README b/PLUGINS/src/osddemo/README
index 50d36d8c..90ae2952 100644
--- a/PLUGINS/src/osddemo/README
+++ b/PLUGINS/src/osddemo/README
@@ -19,4 +19,11 @@ Demonstration of how a plugin can have its very own OSD setup.
It's a very primitive game that opens a small window in which the
user can draw lines with the Up, Down, Left and Right buttons.
The color buttons are used to switch color.
+
+On a VDR system with TrueColor support it displays some of the
+possibilities available with the TrueColor OSD. Once the "Animation"
+pixmap is displayed, it can be moved around with the Up, Down, Left
+and Right buttons. The Red button turns off the "Tiled Pixmaps"
+display, and the Green button toggles the color display.
+
Press Ok to close the window.
diff --git a/PLUGINS/src/osddemo/osddemo.c b/PLUGINS/src/osddemo/osddemo.c
index 2a9e09a6..87d94638 100644
--- a/PLUGINS/src/osddemo/osddemo.c
+++ b/PLUGINS/src/osddemo/osddemo.c
@@ -3,12 +3,13 @@
*
* See the README file for copyright information and how to reach the author.
*
- * $Id: osddemo.c 2.1 2008/04/13 12:59:57 kls Exp $
+ * $Id: osddemo.c 2.2 2011/02/20 15:06:18 kls Exp $
*/
+#include <vdr/osd.h>
#include <vdr/plugin.h>
-static const char *VERSION = "0.1.3";
+static const char *VERSION = "0.2.0";
static const char *DESCRIPTION = "Demo of arbitrary OSD setup";
static const char *MAINMENUENTRY = "Osd Demo";
@@ -22,7 +23,7 @@ private:
tColor color;
public:
cLineGame(void);
- ~cLineGame();
+ virtual ~cLineGame();
virtual void Show(void);
virtual eOSState ProcessKey(eKeys Key);
};
@@ -73,6 +74,364 @@ eOSState cLineGame::ProcessKey(eKeys Key)
return state;
}
+// --- cTrueColorDemo --------------------------------------------------------
+
+class cTrueColorDemo : public cOsdObject, public cThread {
+private:
+ cOsd *osd;
+ cPoint cursor;
+ cRect cursorLimits;
+ bool clockwise;
+ cPixmap *destroyablePixmap;
+ cPixmap *toggleablePixmap;
+ virtual void Action(void);
+ cPixmap *CreateTextPixmap(const char *s, int Line, int Layer, tColor ColorFg, tColor ColorBg, const cFont *Font = NULL);
+public:
+ cTrueColorDemo(void);
+ virtual ~cTrueColorDemo();
+ virtual void Show(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+cTrueColorDemo::cTrueColorDemo(void)
+{
+ osd = NULL;
+ clockwise = true;
+ destroyablePixmap = NULL;
+ toggleablePixmap = NULL;
+}
+
+cTrueColorDemo::~cTrueColorDemo()
+{
+ delete osd;
+}
+
+cPixmap *cTrueColorDemo::CreateTextPixmap(const char *s, int Line, int Layer, tColor ColorFg, tColor ColorBg, const cFont *Font)
+{
+ if (!Font)
+ Font = cFont::GetFont(fontOsd);
+ const int h = Font->Height(s);
+ int w = Font->Width(s);
+ cPixmap *Pixmap = osd->CreatePixmap(Layer, cRect((osd->Width() - w) / 2, Line, w, h));
+ if (Pixmap) {
+ Pixmap->Clear();
+ Pixmap->SetAlpha(0);
+ Pixmap->DrawText(cPoint(0, 0), s, ColorFg, ColorBg, Font);
+ }
+ return Pixmap;
+}
+
+void cTrueColorDemo::Action(void)
+{
+ cPixmap *FadeInPixmap = NULL;
+ cPixmap *FadeOutPixmap = NULL;
+ cPixmap *MovePixmap = NULL;
+ cPixmap *NextPixmap = NULL;
+ cPixmap *TilePixmap = NULL;
+ cPixmap *ScrollPixmap = NULL;
+ cPixmap *AnimPixmap = NULL;
+ int FrameTime = 40; // ms
+ int FadeTime = 1000; // ms
+ int MoveTime = 4000; // ms
+ int TileTime = 6000; // ms
+ int ScrollWaitTime = 1000; // ms
+ int ScrollLineTime = 200; // ms
+ int ScrollTotalTime = 8000; // ms
+ uint64_t Start = 0;
+ uint64_t ScrollStartTime = 0;
+ int ScrollLineNumber = 0;
+ cPoint MoveStart, MoveEnd;
+ cPoint TileStart, TileEnd;
+ cPoint ScrollStart, ScrollEnd;
+ int Line = osd->Height() / 20;
+ int StartLine = Line;
+ cPoint OldCursor;
+ int State = 0;
+ while (Running()) {
+ cPixmap::Lock();
+ bool Animated = false;
+ uint64_t Now = cTimeMs::Now();
+ if (FadeInPixmap) {
+ double t = min(double(Now - Start) / FadeTime, 1.0);
+ int Alpha = t * ALPHA_OPAQUE;
+ FadeInPixmap->SetAlpha(Alpha);
+ if (t >= 1)
+ FadeInPixmap = NULL;
+ Animated = true;
+ }
+ if (FadeOutPixmap) {
+ double t = min(double(Now - Start) / FadeTime, 1.0);
+ int Alpha = ALPHA_OPAQUE - t * ALPHA_OPAQUE;
+ FadeOutPixmap->SetAlpha(Alpha);
+ if (t >= 1)
+ FadeOutPixmap = NULL;
+ Animated = true;
+ }
+ if (MovePixmap) {
+ double t = min(double(Now - Start) / MoveTime, 1.0);
+ int x = MoveStart.X() + t * (MoveEnd.X() - MoveStart.X());
+ int y = MoveStart.Y() + t * (MoveEnd.Y() - MoveStart.Y());
+ cRect r = MovePixmap->ViewPort();
+ r.SetPoint(x, y);
+ MovePixmap->SetViewPort(r);
+ if (t >= 1)
+ MovePixmap = NULL;
+ Animated = true;
+ }
+ if (TilePixmap) {
+ double t = min(double(Now - Start) / TileTime, 1.0);
+ int x = TileStart.X() + t * (TileEnd.X() - TileStart.X());
+ int y = TileStart.Y() + t * (TileEnd.Y() - TileStart.Y());
+ TilePixmap->SetDrawPortPoint(cPoint(x, y));
+ if (t >= 1) {
+ destroyablePixmap = TilePixmap;
+ TilePixmap = NULL;
+ }
+ Animated = true;
+ }
+ if (ScrollPixmap) {
+ if (int(Now - Start) > ScrollWaitTime) {
+ if (ScrollStartTime) {
+ double t = min(double(Now - ScrollStartTime) / ScrollLineTime, 1.0);
+ int x = ScrollStart.X() + t * (ScrollEnd.X() - ScrollStart.X());
+ int y = ScrollStart.Y() + t * (ScrollEnd.Y() - ScrollStart.Y());
+ ScrollPixmap->SetDrawPortPoint(cPoint(x, y));
+ if (t >= 1) {
+ if (int(Now - Start) < ScrollTotalTime) {
+ cRect r = ScrollPixmap->DrawPort();
+ r.SetPoint(-r.X(), -r.Y());
+ ScrollPixmap->Pan(cPoint(0, 0), r);
+ const cFont *Font = cFont::GetFont(fontOsd);
+ cString s = cString::sprintf("Line %d", ++ScrollLineNumber);
+ ScrollPixmap->DrawRectangle(cRect(0, ScrollPixmap->ViewPort().Height(), ScrollPixmap->DrawPort().Width(), ScrollPixmap->DrawPort().Height()), clrTransparent);
+ ScrollPixmap->DrawText(cPoint(0, ScrollPixmap->ViewPort().Height()), s, clrYellow, clrTransparent, Font);
+ ScrollStartTime = Now;
+ }
+ else {
+ FadeOutPixmap = ScrollPixmap;
+ ScrollPixmap = NULL;
+ Start = cTimeMs::Now();
+ }
+ }
+ }
+ else
+ ScrollStartTime = Now;
+ }
+ Animated = true;
+ }
+ if (AnimPixmap) {
+ int d = AnimPixmap->ViewPort().Height();
+ if (clockwise)
+ d = -d;
+ cPoint p = AnimPixmap->DrawPort().Point().Shifted(0, d);
+ if (clockwise && p.Y() <= -AnimPixmap->DrawPort().Height())
+ p.SetY(0);
+ else if (!clockwise && p.Y() > 0)
+ p.SetY(-(AnimPixmap->DrawPort().Height() - AnimPixmap->ViewPort().Height()));
+ AnimPixmap->SetDrawPortPoint(p);
+ }
+ if (!Animated) {
+ switch (State) {
+ case 0: {
+ if (cFont *Font = cFont::CreateFont(DefaultFontOsd, osd->Height() / 10)) {
+ FadeInPixmap = CreateTextPixmap("VDR", Line, 1, clrYellow, clrTransparent, Font);
+ if (FadeInPixmap)
+ Line += FadeInPixmap->DrawPort().Height();
+ delete Font;
+ Start = cTimeMs::Now();
+ }
+ State++;
+ }
+ break;
+ case 1: {
+ FadeInPixmap = CreateTextPixmap("Video Disk Recorder", Line, 3, clrYellow, clrTransparent);
+ if (FadeInPixmap)
+ Line += FadeInPixmap->DrawPort().Height();
+ Start = cTimeMs::Now();
+ State++;
+ }
+ break;
+ case 2: {
+ FadeInPixmap = CreateTextPixmap("True Color OSD Demo", Line, 1, clrYellow, clrTransparent);
+ if (FadeInPixmap)
+ Line += FadeInPixmap->DrawPort().Height();
+ Start = cTimeMs::Now();
+ State++;
+ }
+ break;
+ case 3: {
+ if (cFont *Font = cFont::CreateFont(DefaultFontOsd, osd->Height() / 10)) {
+ NextPixmap = CreateTextPixmap("Millions of colors", Line, 1, clrYellow, clrTransparent, Font);
+ if (NextPixmap) {
+ FadeInPixmap = NextPixmap;
+ }
+ Start = cTimeMs::Now();
+ StartLine = Line;
+ Line += NextPixmap->DrawPort().Height();
+ }
+ State++;
+ }
+ break;
+ case 4: {
+ Line += osd->Height() / 10;
+ int w = osd->Width() / 2;
+ int h = osd->Height() - Line - osd->Height() / 10;
+ cImage Image(cSize(w, h));
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++)
+ Image.SetPixel(cPoint(x, y), HsvToColor(360 * double(x) / w, 1 - double(y) / h, 1) | 0xDF000000);
+ }
+ if (cPixmap *Pixmap = osd->CreatePixmap(2, cRect((osd->Width() - w) / 2, Line, w, h))) {
+ Pixmap->DrawImage(cPoint(0, 0), Image);
+ toggleablePixmap = Pixmap;
+ }
+ State++;
+ }
+ break;
+ case 5: {
+ if (NextPixmap) {
+ MovePixmap = NextPixmap;
+ MoveStart = MovePixmap->ViewPort().Point();
+ MoveEnd.Set(osd->Width() - MovePixmap->ViewPort().Width(), osd->Height() - MovePixmap->ViewPort().Height());
+ Start = cTimeMs::Now();
+ }
+ State++;
+ }
+ break;
+ case 6: {
+ TilePixmap = CreateTextPixmap("Tiled Pixmaps", StartLine, 1, clrRed, clrWhite);
+ if (TilePixmap) {
+ TilePixmap->SetViewPort(TilePixmap->ViewPort().Grown(TilePixmap->DrawPort().Width(), TilePixmap->DrawPort().Height()));
+ TilePixmap->SetAlpha(200);
+ TilePixmap->SetTile(true);
+ TileStart = TilePixmap->DrawPort().Point();
+ TileEnd = TileStart.Shifted(TilePixmap->ViewPort().Width(), TilePixmap->ViewPort().Height());
+ MovePixmap = TilePixmap;
+ MoveStart = MovePixmap->ViewPort().Point();
+ MoveEnd.Set(10, osd->Height() - MovePixmap->ViewPort().Height() - 10);
+ Start = cTimeMs::Now();
+ }
+ State++;
+ }
+ break;
+ case 7: {
+ const cFont *Font = cFont::GetFont(fontOsd);
+ const char *Text = "Scrolling Pixmaps";
+ int w = Font->Width(Text);
+ int h = Font->Height();
+ if (cPixmap *Pixmap = osd->CreatePixmap(2, cRect((osd->Width() - w) / 2, StartLine, w, 2 * h), cRect(0, 0, w, 3 * h))) {
+ Pixmap->Clear();
+ Pixmap->DrawText(cPoint(0, 0), Text, clrYellow, clrTransparent, Font);
+ cString s = cString::sprintf("Line %d", ++ScrollLineNumber);
+ Pixmap->DrawText(cPoint(0, Pixmap->ViewPort().Height()), s, clrYellow, clrTransparent, Font);
+ ScrollPixmap = Pixmap;
+ ScrollStart.Set(0, 0);
+ ScrollEnd.Set(0, -h);
+ Start = cTimeMs::Now();
+ }
+ State++;
+ }
+ break;
+ case 8: {
+ const cFont *Font = cFont::GetFont(fontSml);
+ const char *Text = "Animation";
+ const int Size = Font->Width(Text) + 10;
+ const int NumDots = 12;
+ const int AnimFrames = NumDots;
+ AnimPixmap = osd->CreatePixmap(3, cRect((osd->Width() - Size) / 2, StartLine, Size, Size), cRect(0, 0, Size, Size * AnimFrames));
+ if (AnimPixmap) {
+ AnimPixmap->SetAlpha(0);
+ AnimPixmap->Clear();
+ const int Diameter = Size / 5;
+ int xc = Size / 2 - Diameter / 2;
+ for (int Frame = 0; Frame < AnimFrames; Frame++) {
+ AnimPixmap->DrawEllipse(cRect(0, Frame * Size, Size, Size), 0xDDFFFFFF);
+ int yc = Frame * Size + Size / 2 - Diameter / 2;
+ int Color = 0xFF;
+ int Delta = Color / NumDots / 3;
+ for (int a = 0; a < NumDots; a++) {
+ double t = 2 * M_PI * (Frame + a) / NumDots;
+ int x = xc + ((Size - Diameter) / 2 - 5) * cos(t);
+ int y = yc + ((Size - Diameter) / 2 - 5) * sin(t);
+ AnimPixmap->DrawEllipse(cRect(x, y, Diameter, Diameter), ArgbToColor(0xFF, Color, Color, Color));
+ Color -= Delta;
+ }
+ AnimPixmap->DrawText(cPoint(0, Frame * Size), "Animation", clrBlack, clrTransparent, cFont::GetFont(fontSml), Size, Size, taCenter);
+ }
+ FadeInPixmap = AnimPixmap;
+ LOCK_THREAD;
+ OldCursor = cursor = AnimPixmap->ViewPort().Point();
+ cursorLimits.Set(0, 0, osd->Width(), osd->Height());
+ cursorLimits.SetRight(cursorLimits.Right() - Size);
+ cursorLimits.SetBottom(cursorLimits.Bottom() - Size);
+ cursorLimits.Grow(-10, -10);
+ Start = cTimeMs::Now();
+ }
+ State++;
+ }
+ break;
+ case 9: {
+ LOCK_THREAD;
+ if (cursor != OldCursor) {
+ MovePixmap = AnimPixmap;
+ MoveStart = MovePixmap->ViewPort().Point();
+ MoveEnd = OldCursor = cursor;
+ MoveTime = 500;
+ Start = cTimeMs::Now();
+ }
+ }
+ break;
+ }
+ }
+ osd->Flush();
+ cPixmap::Unlock();
+ int Delta = cTimeMs::Now() - Now;
+ if (Delta < FrameTime)
+ cCondWait::SleepMs(FrameTime - Delta);
+ }
+}
+
+void cTrueColorDemo::Show(void)
+{
+ osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop(), 50);
+ if (osd) {
+ tArea Area = { 0, 0, cOsd::OsdWidth() - 1, cOsd::OsdHeight() - 1, 32 };
+ if (osd->SetAreas(&Area, 1) == oeOk) {
+ osd->DrawRectangle(0, 0, osd->Width() -1 , osd->Height() - 1, clrGray50);
+ osd->Flush();
+ Start();
+ }
+ }
+}
+
+eOSState cTrueColorDemo::ProcessKey(eKeys Key)
+{
+ eOSState state = cOsdObject::ProcessKey(Key);
+ if (state == osUnknown) {
+ LOCK_PIXMAPS;
+ LOCK_THREAD;
+ const int d = 80;
+ switch (Key & ~k_Repeat) {
+ case kUp: cursor.SetY(max(cursorLimits.Top(), cursor.Y() - d)); clockwise = false; break;
+ case kDown: cursor.SetY(min(cursorLimits.Bottom(), cursor.Y() + d)); clockwise = true; break;
+ case kLeft: cursor.SetX(max(cursorLimits.Left(), cursor.X() - d)); clockwise = false; break;
+ case kRight: cursor.SetX(min(cursorLimits.Right(), cursor.X() + d)); clockwise = true; break;
+ case kRed: if (destroyablePixmap) {
+ osd->DestroyPixmap(destroyablePixmap);
+ destroyablePixmap = NULL;
+ }
+ break;
+ case kGreen: if (toggleablePixmap)
+ toggleablePixmap->SetLayer(-toggleablePixmap->Layer());
+ break;
+ case kOk: return osEnd;
+ default: return state;
+ }
+ state = osContinue;
+ }
+ return state;
+}
+
// --- cPluginOsddemo --------------------------------------------------------
class cPluginOsddemo : public cPlugin {
@@ -131,6 +490,8 @@ void cPluginOsddemo::Housekeeping(void)
cOsdObject *cPluginOsddemo::MainMenuAction(void)
{
// Perform the action when selected from the main VDR menu.
+ if (cOsdProvider::SupportsTrueColor())
+ return new cTrueColorDemo;
return new cLineGame;
}
diff --git a/PLUGINS/src/skincurses/skincurses.c b/PLUGINS/src/skincurses/skincurses.c
index 4abb8634..84076cd4 100644
--- a/PLUGINS/src/skincurses/skincurses.c
+++ b/PLUGINS/src/skincurses/skincurses.c
@@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
- * $Id: skincurses.c 2.4 2010/02/28 12:50:13 kls Exp $
+ * $Id: skincurses.c 2.5 2011/01/04 08:52:03 kls Exp $
*/
#include <ncurses.h>
@@ -23,6 +23,7 @@ public:
virtual int Width(const char *s) const { return s ? Utf8StrLen(s) : 0; }
virtual int Height(void) const { return 1; }
virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {}
+ virtual void DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {}
};
static const cCursesFont Font;
diff --git a/font.c b/font.c
index 4ff4017d..227dd63f 100644
--- a/font.c
+++ b/font.c
@@ -6,7 +6,7 @@
*
* BiDi support by Osama Alrawab <alrawab@hotmail.com> @2008 Tripoli-Libya.
*
- * $Id: font.c 2.5 2010/09/19 11:49:19 kls Exp $
+ * $Id: font.c 2.6 2011/02/20 14:15:38 kls Exp $
*/
#include "font.h"
@@ -118,6 +118,7 @@ public:
virtual int Width(const char *s) const;
virtual int Height(void) const { return height; }
virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const;
+ virtual void DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const;
};
cFreetypeFont::cFreetypeFont(const char *Name, int CharHeight, int CharWidth)
@@ -329,6 +330,62 @@ void cFreetypeFont::DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColo
}
}
+void cFreetypeFont::DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const
+{
+ if (s && height) { // checking height to make sure we actually have a valid font
+#ifdef BIDI
+ cString bs = Bidi(s);
+ s = bs;
+#endif
+ bool AntiAliased = Setup.AntiAlias;
+ bool TransparentBackground = ColorBg == clrTransparent;
+ uint prevSym = 0;
+ while (*s) {
+ int sl = Utf8CharLen(s);
+ uint sym = Utf8CharGet(s, sl);
+ s += sl;
+ cGlyph *g = Glyph(sym, AntiAliased);
+ if (!g)
+ continue;
+ int kerning = Kerning(g, prevSym);
+ prevSym = sym;
+ uchar *buffer = g->Bitmap();
+ int symWidth = g->Width();
+ if (Width && x + symWidth + g->Left() + kerning - 1 > Width)
+ break; // we don't draw partial characters
+ if (x + symWidth + g->Left() + kerning > 0) {
+ for (int row = 0; row < g->Rows(); row++) {
+ for (int pitch = 0; pitch < g->Pitch(); pitch++) {
+ uchar bt = *(buffer + (row * g->Pitch() + pitch));
+ if (AntiAliased) {
+ if (bt > 0x00) {
+ tColor bg;
+ if (bt == 0xFF || TransparentBackground)
+ bg = ColorFg;
+ else {
+ bg = AlphaBlend(ColorFg, ColorBg, bt);
+ bt = ALPHA_OPAQUE;
+ }
+ Pixmap->DrawPixel(cPoint(x + pitch + g->Left() + kerning, y + row + (height - Bottom() - g->Top())), bg, bt);
+ }
+ }
+ else { //monochrome rendering
+ for (int col = 0; col < 8 && col + pitch * 8 <= symWidth; col++) {
+ if (bt & 0x80)
+ Pixmap->DrawPixel(cPoint(x + col + pitch * 8 + g->Left() + kerning, y + row + (height - Bottom() - g->Top())), ColorFg);
+ bt <<= 1;
+ }
+ }
+ }
+ }
+ }
+ x += g->AdvanceX() + kerning;
+ if (x > Pixmap->DrawPort().Width() - 1)
+ break;
+ }
+ }
+}
+
// --- cDummyFont ------------------------------------------------------------
// A dummy font, in case there are no fonts installed:
@@ -339,6 +396,7 @@ public:
virtual int Width(const char *s) const { return 50; }
virtual int Height(void) const { return 20; }
virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {}
+ virtual void DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {};
};
// --- cFont -----------------------------------------------------------------
diff --git a/font.h b/font.h
index cb76671a..dbca88ef 100644
--- a/font.h
+++ b/font.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: font.h 2.4 2010/09/19 11:48:37 kls Exp $
+ * $Id: font.h 2.5 2011/01/14 16:22:03 kls Exp $
*/
#ifndef __FONT_H
@@ -26,6 +26,7 @@ enum eDvbFont {
};
class cBitmap;
+class cPixmap;
typedef uint32_t tColor; // see also osd.h
typedef uint8_t tIndex;
@@ -54,6 +55,9 @@ public:
virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const = 0;
///< Draws the given text into the Bitmap at position (x, y) with the given colors.
///< The text will not exceed the given Width (if > 0), and will end with a complete character.
+ virtual void DrawText(cPixmap *Pixmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const {}; // not "pure", so that existing implementations still compile
+ ///< Draws the given text into the Pixmap at position (x, y) with the given colors.
+ ///< The text will not exceed the given Width (if > 0), and will end with a complete character.
static void SetFont(eDvbFont Font, const char *Name, int CharHeight);
///< Sets the given Font to use the font data according to Name (see CreateFont())
///< and make its characters CharHeight pixels high.
@@ -81,7 +85,7 @@ public:
///< of the actual font file.
///< Returns true if any font names were found.
static cString GetFontFileName(const char *FontName);
- ///< Retruns the actual font file name for the given FontName.
+ ///< Returns the actual font file name for the given FontName.
#ifdef BIDI
static cString Bidi(const char *Ltr);
///< Converts any "right-to-left" parts in the "left-to-right" string Ltr
diff --git a/osd.c b/osd.c
index 7a2b76ae..f2b5e8ab 100644
--- a/osd.c
+++ b/osd.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.c 2.11 2010/12/12 23:16:19 kls Exp $
+ * $Id: osd.c 2.12 2011/02/20 14:48:01 kls Exp $
*/
#include "osd.h"
@@ -16,6 +16,96 @@
#include "device.h"
#include "tools.h"
+tColor HsvToColor(double H, double S, double V)
+{
+ if (S > 0) {
+ H /= 60;
+ int i = floor(H);
+ double f = H - i;
+ double p = V * (1 - S);
+ double q = V * (1 - S * f);
+ double t = V * (1 - S * (1 - f));
+ switch (i) {
+ case 0: return RgbToColor(V, t, p);
+ case 1: return RgbToColor(q, V, p);
+ case 2: return RgbToColor(p, V, t);
+ case 3: return RgbToColor(p, q, V);
+ case 4: return RgbToColor(t, p, V);
+ default: return RgbToColor(V, p, q);
+ }
+ }
+ else { // greyscale
+ uint8_t n = V * 0xFF;
+ return RgbToColor(n, n, n);
+ }
+}
+
+#define USE_ALPHA_LUT
+#ifdef USE_ALPHA_LUT
+// Alpha blending with lookup table (by Reinhard Nissl <rnissl@gmx.de>)
+// A little faster than the implementation below, but requires some 500KB
+// of RAM for the lookup table.
+static uint16_t AlphaLut[256][256][4];
+
+class cInitAlphaLut {
+public:
+ cInitAlphaLut(void)
+ {
+ for (int alphaA = 0; alphaA < 256; alphaA++) {
+ for (int alphaB = 0; alphaB < 256; alphaB++) {
+ int alphaO_255 = 255 * alphaA + alphaB * (255 - alphaA);
+ if (!alphaO_255)
+ alphaO_255++;
+ int factorA = 256 * 255 * alphaA / alphaO_255;
+ int factorB = 256 * alphaB * (255 - alphaA) / alphaO_255;
+ AlphaLut[alphaA][alphaB][0] = factorA;
+ AlphaLut[alphaA][alphaB][1] = factorB;
+ // index 2 is never used
+ AlphaLut[alphaA][alphaB][3] = alphaO_255 / 255;
+ }
+ }
+ }
+ };
+
+cInitAlphaLut initAlphaLut;
+
+tColor AlphaBlend(tColor ColorFg, tColor ColorBg, tColor AlphaLayer)
+{
+ tColor Alpha = (ColorFg & 0xFF000000) >> 24;
+ if (AlphaLayer < ALPHA_OPAQUE) {
+ Alpha *= AlphaLayer;
+ Alpha = ((Alpha + ((Alpha >> 8) & 0x000000FF) + 0x00000080) >> 8) & 0x000000FF;
+ }
+ uint16_t *lut = &AlphaLut[Alpha][(ColorBg & 0xFF000000) >> 24][0];
+ return (tColor)((lut[3] << 24)
+ | (((((ColorFg & 0x00FF0000) >> 16) * lut[0] + ((ColorBg & 0x00FF0000) >> 16) * lut[1]) << 8) & 0x00FF0000)
+ | (((((ColorFg & 0x0000FF00) >> 8) * lut[0] + ((ColorBg & 0x0000FF00) >> 8) * lut[1]) ) & 0x0000FF00)
+ | (((((ColorFg & 0x000000FF) ) * lut[0] + ((ColorBg & 0x000000FF) ) * lut[1]) >> 8) & 0x000000FF));
+}
+#else
+// Alpha blending without lookup table.
+// Also works fast, but doesn't return the theoretically correct result.
+// It's "good enough", though.
+static tColor Multiply(tColor Color, tColor Alpha)
+{
+ tColor RB = (Color & 0x00FF00FF) * Alpha;
+ RB = ((RB + ((RB >> 8) & 0x00FF00FF) + 0x00800080) >> 8) & 0x00FF00FF;
+ tColor AG = ((Color >> 8) & 0x00FF00FF) * Alpha;
+ AG = ((AG + ((AG >> 8) & 0x00FF00FF) + 0x00800080)) & 0xFF00FF00;
+ return AG | RB;
+}
+
+tColor AlphaBlend(tColor ColorFg, tColor ColorBg, tColor AlphaLayer)
+{
+ tColor Alpha = (ColorFg & 0xFF000000) >> 24;
+ if (AlphaLayer < ALPHA_OPAQUE) {
+ Alpha *= AlphaLayer;
+ Alpha = ((Alpha + ((Alpha >> 8) & 0x000000FF) + 0x00000080) >> 8) & 0x000000FF;
+ }
+ return Multiply(ColorFg, Alpha) + Multiply(ColorBg, 255 - Alpha);
+}
+#endif
+
// --- cPalette --------------------------------------------------------------
cPalette::cPalette(int Bpp)
@@ -612,8 +702,6 @@ void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quad
void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
{
- // TODO This is just a quick and dirty implementation of a slope drawing
- // machanism. If somebody can come up with a better solution, let's have it!
if (!Intersects(x1, y1, x2, y2))
return;
bool upper = Type & 0x01;
@@ -723,6 +811,729 @@ void cBitmap::ShrinkBpp(int NewBpp)
}
}
+// --- cRect -----------------------------------------------------------------
+
+const cRect cRect::Null;
+
+void cRect::Grow(int Dx, int Dy)
+{
+ point.Shift(-Dx, -Dy);
+ size.Grow(Dx, Dy);
+}
+
+bool cRect::Contains(const cPoint &Point) const
+{
+ return Left() <= Point.X() &&
+ Top() <= Point.Y() &&
+ Right() >= Point.X() &&
+ Bottom() >= Point.Y();
+}
+
+bool cRect::Contains(const cRect &Rect) const
+{
+ return Left() <= Rect.Left() &&
+ Top() <= Rect.Top() &&
+ Right() >= Rect.Right() &&
+ Bottom() >= Rect.Bottom();
+}
+
+bool cRect::Intersects(const cRect &Rect) const
+{
+ return !(Left() > Rect.Right() ||
+ Top() > Rect.Bottom() ||
+ Right() < Rect.Left() ||
+ Bottom() < Rect.Top());
+}
+
+cRect cRect::Intersected(const cRect &Rect) const
+{
+ cRect r;
+ if (!IsEmpty() && !Rect.IsEmpty()) {
+ r.SetLeft(max(Left(), Rect.Left()));
+ r.SetTop(max(Top(), Rect.Top()));
+ r.SetRight(min(Right(), Rect.Right()));
+ r.SetBottom(min(Bottom(), Rect.Bottom()));
+ }
+ return r;
+}
+
+void cRect::Combine(const cRect &Rect)
+{
+ if (IsEmpty())
+ *this = Rect;
+ if (Rect.IsEmpty())
+ return;
+ // must set right/bottom *before* top/left!
+ SetRight(max(Right(), Rect.Right()));
+ SetBottom(max(Bottom(), Rect.Bottom()));
+ SetLeft(min(Left(), Rect.Left()));
+ SetTop(min(Top(), Rect.Top()));
+}
+
+void cRect::Combine(const cPoint &Point)
+{
+ if (IsEmpty())
+ Set(Point.X(), Point.Y(), 1, 1);
+ // must set right/bottom *before* top/left!
+ SetRight(max(Right(), Point.X()));
+ SetBottom(max(Bottom(), Point.Y()));
+ SetLeft(min(Left(), Point.X()));
+ SetTop(min(Top(), Point.Y()));
+}
+
+// --- cPixmap ---------------------------------------------------------------
+
+cMutex cPixmap::mutex;
+
+cPixmap::cPixmap(void)
+{
+ layer = -1;
+ alpha = ALPHA_OPAQUE;
+ tile = false;
+}
+
+cPixmap::cPixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort)
+{
+ layer = Layer;
+ if (layer >= MAXPIXMAPLAYERS) {
+ layer = MAXPIXMAPLAYERS - 1;
+ esyslog("ERROR: pixmap layer %d limited to %d", Layer, layer);
+ }
+ viewPort = ViewPort;
+ if (!DrawPort.IsEmpty())
+ drawPort = DrawPort;
+ else {
+ drawPort = viewPort;
+ drawPort.SetPoint(0, 0);
+ }
+ alpha = ALPHA_OPAQUE;
+ tile = false;
+}
+
+void cPixmap::MarkViewPortDirty(const cRect &Rect)
+{
+ dirtyViewPort.Combine(Rect.Intersected(viewPort));
+}
+
+void cPixmap::MarkViewPortDirty(const cPoint &Point)
+{
+ if (viewPort.Contains(Point))
+ dirtyViewPort.Combine(Point);
+}
+
+void cPixmap::MarkDrawPortDirty(const cRect &Rect)
+{
+ dirtyDrawPort.Combine(Rect.Intersected(drawPort));
+ if (tile)
+ MarkViewPortDirty(viewPort);
+ else
+ MarkViewPortDirty(Rect.Shifted(viewPort.Point()));
+}
+
+void cPixmap::MarkDrawPortDirty(const cPoint &Point)
+{
+ if (drawPort.Contains(Point)) {
+ dirtyDrawPort.Combine(Point);
+ if (tile)
+ MarkViewPortDirty(viewPort);
+ else
+ MarkViewPortDirty(Point.Shifted(viewPort.Point()));
+ }
+}
+
+void cPixmap::SetClean(void)
+{
+ dirtyViewPort = dirtyDrawPort = cRect();
+}
+
+void cPixmap::SetLayer(int Layer)
+{
+ Lock();
+ if (Layer >= MAXPIXMAPLAYERS) {
+ esyslog("ERROR: pixmap layer %d limited to %d", Layer, MAXPIXMAPLAYERS - 1);
+ Layer = MAXPIXMAPLAYERS - 1;
+ }
+ if (Layer && Layer != layer) {
+ if (Layer > 0 || layer > 0)
+ MarkViewPortDirty(viewPort);
+ layer = Layer;
+ }
+ Unlock();
+}
+
+void cPixmap::SetAlpha(int Alpha)
+{
+ Lock();
+ Alpha = min(max(Alpha, ALPHA_TRANSPARENT), ALPHA_OPAQUE);
+ if (Alpha != alpha) {
+ MarkViewPortDirty(viewPort);
+ alpha = Alpha;
+ }
+ Unlock();
+}
+
+void cPixmap::SetTile(bool Tile)
+{
+ Lock();
+ if (Tile != tile) {
+ if (drawPort.Point() != cPoint(0, 0) || drawPort.Width() < viewPort.Width() || drawPort.Height() < viewPort.Height())
+ MarkViewPortDirty(viewPort);
+ tile = Tile;
+ }
+ Unlock();
+}
+
+void cPixmap::SetViewPort(const cRect &Rect)
+{
+ Lock();
+ if (Rect != viewPort) {
+ if (tile)
+ MarkViewPortDirty(viewPort);
+ else
+ MarkViewPortDirty(drawPort.Shifted(viewPort.Point()));
+ viewPort = Rect;
+ if (tile)
+ MarkViewPortDirty(viewPort);
+ else
+ MarkViewPortDirty(drawPort.Shifted(viewPort.Point()));
+ }
+ Unlock();
+}
+
+void cPixmap::SetDrawPortPoint(const cPoint &Point, bool Dirty)
+{
+ Lock();
+ if (Point != drawPort.Point()) {
+ if (Dirty) {
+ if (tile)
+ MarkViewPortDirty(viewPort);
+ else
+ MarkViewPortDirty(drawPort.Shifted(viewPort.Point()));
+ }
+ drawPort.SetPoint(Point);
+ if (Dirty && !tile)
+ MarkViewPortDirty(drawPort.Shifted(viewPort.Point()));
+ }
+ Unlock();
+}
+
+// --- cImage ----------------------------------------------------------------
+
+cImage::cImage(void)
+{
+ data = NULL;
+}
+
+cImage::cImage(const cImage &Image)
+{
+ size = Image.Size();
+ int l = size.Width() * size.Height() * sizeof(tColor);
+ data = MALLOC(tColor, l);
+ memcpy(data, Image.Data(), l);
+}
+
+cImage::cImage(const cSize &Size, const tColor *Data)
+{
+ size = Size;
+ int l = size.Width() * size.Height() * sizeof(tColor);
+ data = MALLOC(tColor, l);
+ if (Data)
+ memcpy(data, Data, l);
+}
+
+cImage::~cImage()
+{
+ free(data);
+}
+
+void cImage::Clear(void)
+{
+ memset(data, 0x00, Width() * Height() * sizeof(tColor));
+}
+
+void cImage::Fill(tColor Color)
+{
+ for (int i = Width() * Height() - 1; i >= 0; i--)
+ data[i] = Color;
+}
+
+// --- cPixmapMemory ---------------------------------------------------------
+
+cPixmapMemory::cPixmapMemory(void)
+{
+ data = NULL;
+ panning = false;
+}
+
+cPixmapMemory::cPixmapMemory(int Layer, const cRect &ViewPort, const cRect &DrawPort)
+:cPixmap(Layer, ViewPort, DrawPort)
+{
+ data = MALLOC(tColor, this->DrawPort().Width() * this->DrawPort().Height());
+}
+
+cPixmapMemory::~cPixmapMemory()
+{
+ free(data);
+}
+
+void cPixmapMemory::Clear(void)
+{
+ Lock();
+ memset(data, 0x00, DrawPort().Width() * DrawPort().Height() * sizeof(tColor));
+ MarkDrawPortDirty(DrawPort());
+ Unlock();
+}
+
+void cPixmapMemory::Fill(tColor Color)
+{
+ Lock();
+ for (int i = DrawPort().Width() * DrawPort().Height() - 1; i >= 0; i--)
+ data[i] = Color;
+ MarkDrawPortDirty(DrawPort());
+ Unlock();
+}
+
+void cPixmap::DrawPixmap(const cPixmap *Pixmap, const cRect &Dirty, bool Opaque)
+{
+ if (Pixmap->Tile() && (Pixmap->DrawPort().Point() != cPoint(0, 0) || Pixmap->DrawPort().Size() < Pixmap->ViewPort().Size())) {
+ cPoint t0 = Pixmap->DrawPort().Point().Shifted(Pixmap->ViewPort().Point()); // the origin of the draw port in absolute OSD coordinates
+ // Find the top/leftmost location where the draw port touches the view port:
+ while (t0.X() > Pixmap->ViewPort().Left())
+ t0.Shift(-Pixmap->DrawPort().Width(), 0);
+ while (t0.Y() > Pixmap->ViewPort().Top())
+ t0.Shift(0, -Pixmap->DrawPort().Height());
+ cPoint t = t0;;
+ while (t.Y() <= Pixmap->ViewPort().Bottom()) {
+ while (t.X() <= Pixmap->ViewPort().Right()) {
+ cRect Source = Pixmap->DrawPort(); // assume the entire pixmap needs to be rendered
+ Source.Shift(Pixmap->ViewPort().Point()); // Source is now in absolute OSD coordinates
+ cPoint Delta = Source.Point() - t;
+ Source.SetPoint(t); // Source is now where the pixmap's data shall be drawn
+ Source = Source.Intersected(Pixmap->ViewPort()); // Source is now limited to the pixmap's view port
+ Source = Source.Intersected(Dirty); // Source is now limited to the actual dirty rectangle
+ if (!Source.IsEmpty()) {
+ cPoint Dest = Source.Point().Shifted(-ViewPort().Point()); // remember the destination point
+ Source.Shift(Delta); // Source is now back at the pixmap's draw port location, still in absolute OSD coordinates
+ Source.Shift(-Pixmap->ViewPort().Point()); // Source is now relative to the pixmap's view port again
+ Source.Shift(-Pixmap->DrawPort().Point()); // Source is now relative to the pixmap's data
+ if (Opaque)
+ Copy(Pixmap, Source, Dest); // this is the "background" pixmap
+ else
+ Render(Pixmap, Source, Dest); // all others are alpha blended over the background
+ }
+ t.Shift(Pixmap->DrawPort().Width(), 0); // increase one draw port width to the right
+ }
+ t.SetX(t0.X()); // go back to the leftmost position
+ t.Shift(0, Pixmap->DrawPort().Height()); // increase one draw port height down
+ }
+ }
+ else {
+ cRect Source = Pixmap->DrawPort(); // assume the entire pixmap needs to be rendered
+ Source.Shift(Pixmap->ViewPort().Point()); // Source is now in absolute OSD coordinates
+ Source = Source.Intersected(Pixmap->ViewPort()); // Source is now limited to the pixmap's view port
+ Source = Source.Intersected(Dirty); // Source is now limited to the actual dirty rectangle
+ if (!Source.IsEmpty()) {
+ cPoint Dest = Source.Point().Shifted(-ViewPort().Point()); // remember the destination point
+ Source.Shift(-Pixmap->ViewPort().Point()); // Source is now relative to the pixmap's draw port again
+ Source.Shift(-Pixmap->DrawPort().Point()); // Source is now relative to the pixmap's data
+ if (Opaque)
+ Copy(Pixmap, Source, Dest); // this is the "background" pixmap
+ else
+ Render(Pixmap, Source, Dest); // all others are alpha blended over the background
+ }
+ }
+}
+
+void cPixmapMemory::DrawImage(const cPoint &Point, const cImage &Image)
+{
+ Lock();
+ cRect r = cRect(Point, Image.Size()).Intersected(DrawPort().Size());
+ if (!r.IsEmpty()) {
+ int ws = Image.Size().Width();
+ int wd = DrawPort().Width();
+ int w = r.Width() * sizeof(tColor);
+ const tColor *ps = Image.Data();
+ if (Point.Y() < 0)
+ ps -= Point.Y() * ws;
+ if (Point.X() < 0)
+ ps -= Point.X();
+ tColor *pd = data + wd * r.Top() + r.Left();
+ for (int y = r.Height(); y-- > 0; ) {
+ memcpy(pd, ps, w);
+ ps += ws;
+ pd += wd;
+ }
+ MarkDrawPortDirty(r);
+ }
+ Unlock();
+}
+
+void cPixmapMemory::DrawImage(const cPoint &Point, int ImageHandle)
+{
+ Lock();
+ if (const cImage *Image = cOsdProvider::GetImageData(ImageHandle))
+ DrawImage(Point, *Image);
+ Unlock();
+}
+
+void cPixmapMemory::DrawPixel(const cPoint &Point, tColor Color, tColor Alpha)
+{
+ Lock();
+ if (DrawPort().Size().Contains(Point)) {
+ int p = Point.Y() * DrawPort().Width() + Point.X();
+ data[p] = AlphaBlend(Color, data[p], Alpha);
+ MarkDrawPortDirty(Point);
+ }
+ Unlock();
+}
+
+void cPixmapMemory::DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool Overlay)
+{
+ Lock();
+ cRect r = cRect(Point, cSize(Bitmap.Width(), Bitmap.Height())).Intersected(DrawPort().Size());
+ if (!r.IsEmpty()) {
+ bool UseColors = ColorFg || ColorBg;
+ int wd = DrawPort().Width();
+ tColor *pd = data + wd * r.Top() + r.Left();
+ for (int y = r.Top(); y <= r.Bottom(); y++) {
+ tColor *cd = pd;
+ for (int x = r.Left(); x <= r.Right(); x++) {
+ tIndex Index = *Bitmap.Data(x - Point.X(), y - Point.Y());
+ if (Index || !Overlay) {
+ if (UseColors)
+ *cd = Index ? ColorFg : ColorBg;
+ else
+ *cd = Bitmap.Color(Index);
+ }
+ cd++;
+ }
+ pd += wd;
+ }
+ MarkDrawPortDirty(r);
+ }
+ Unlock();
+}
+
+void cPixmapMemory::DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
+{
+ Lock();
+ int x = Point.X();
+ int y = Point.Y();
+ int w = Font->Width(s);
+ int h = Font->Height();
+ int limit = 0;
+ int cw = Width ? Width : w;
+ int ch = Height ? Height : h;
+ cRect r(x, y, cw, ch);
+ if (ColorBg != clrTransparent)
+ DrawRectangle(r, ColorBg);
+ if (Width || Height) {
+ limit = x + cw;
+ if (Width) {
+ if ((Alignment & taLeft) != 0)
+ ;
+ else if ((Alignment & taRight) != 0) {
+ if (w < Width)
+ x += Width - w;
+ }
+ 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;
+ }
+ }
+ }
+ Font->DrawText(this, x, y, s, ColorFg, ColorBg, limit);
+ MarkDrawPortDirty(r);
+ Unlock();
+}
+
+void cPixmapMemory::DrawRectangle(const cRect &Rect, tColor Color)
+{
+ Lock();
+ cRect r = Rect.Intersected(DrawPort().Size());
+ if (!r.IsEmpty()) {
+ int wd = DrawPort().Width();
+ int w = r.Width() * sizeof(tColor);
+ tColor *ps = NULL;
+ tColor *pd = data + wd * r.Top() + r.Left();
+ for (int y = r.Height(); y-- > 0; ) {
+ if (ps)
+ memcpy(pd, ps, w); // all other lines are copied fast from the first one
+ else {
+ // explicitly fill the first line:
+ tColor *cd = ps = pd;
+ for (int x = r.Width(); x-- > 0; ) {
+ *cd = Color;
+ cd++;
+ }
+ }
+ pd += wd;
+ }
+ MarkDrawPortDirty(r);
+ }
+ Unlock();
+}
+
+void cPixmapMemory::DrawEllipse(const cRect &Rect, tColor Color, int Quadrants)
+{
+//TODO use anti-aliasing?
+//TODO fix alignment
+ Lock();
+ // Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF
+ int x1 = Rect.Left();
+ int y1 = Rect.Top();
+ int x2 = Rect.Right();
+ int y2 = Rect.Bottom();
+ int rx = x2 - x1;
+ int ry = y2 - y1;
+ int cx = (x1 + x2) / 2;
+ int cy = (y1 + y2) / 2;
+ switch (abs(Quadrants)) {
+ case 0: rx /= 2; ry /= 2; break;
+ case 1: cx = x1; cy = y2; break;
+ case 2: cx = x2; cy = y2; break;
+ case 3: cx = x2; cy = y1; break;
+ case 4: cx = x1; cy = y1; break;
+ case 5: cx = x1; ry /= 2; break;
+ case 6: cy = y2; rx /= 2; break;
+ case 7: cx = x2; ry /= 2; break;
+ case 8: cy = y1; rx /= 2; break;
+ default: ;
+ }
+ int TwoASquare = 2 * rx * rx;
+ int TwoBSquare = 2 * ry * ry;
+ int x = rx;
+ int y = 0;
+ int XChange = ry * ry * (1 - 2 * rx);
+ int YChange = rx * rx;
+ int EllipseError = 0;
+ int StoppingX = TwoBSquare * rx;
+ int StoppingY = 0;
+ while (StoppingX >= StoppingY) {
+ switch (Quadrants) {
+ case 5: DrawRectangle(cRect(cx, cy + y, x + 1, 1), Color); // no break
+ case 1: DrawRectangle(cRect(cx, cy - y, x + 1, 1), Color); break;
+ case 7: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); // no break
+ case 2: DrawRectangle(cRect(cx - x, cy - y, x + 1, 1), Color); break;
+ case 3: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); break;
+ case 4: DrawRectangle(cRect(cx, cy + y, x + 1, 1), Color); break;
+ case 0:
+ case 6: DrawRectangle(cRect(cx - x, cy - y, 2 * x + 1, 1), Color); if (Quadrants == 6) break;
+ case 8: DrawRectangle(cRect(cx - x, cy + y, 2 * x + 1, 1), Color); break;
+ case -1: DrawRectangle(cRect(cx + x, cy - y, x2 - x + 1, 1), Color); break;
+ case -2: DrawRectangle(cRect(x1, cy - y, cx - x - x1 + 1, 1), Color); break;
+ case -3: DrawRectangle(cRect(x1, cy + y, cx - x - x1 + 1, 1), Color); break;
+ case -4: DrawRectangle(cRect(cx + x, cy + y, x2 - x + 1, 1), Color); break;
+ default: ;
+ }
+ y++;
+ StoppingY += TwoASquare;
+ EllipseError += YChange;
+ YChange += TwoASquare;
+ if (2 * EllipseError + XChange > 0) {
+ x--;
+ StoppingX -= TwoBSquare;
+ EllipseError += XChange;
+ XChange += TwoBSquare;
+ }
+ }
+ x = 0;
+ y = ry;
+ XChange = ry * ry;
+ YChange = rx * rx * (1 - 2 * ry);
+ EllipseError = 0;
+ StoppingX = 0;
+ StoppingY = TwoASquare * ry;
+ while (StoppingX <= StoppingY) {
+ switch (Quadrants) {
+ case 5: DrawRectangle(cRect(cx, cy + y, x + 1, 1), Color); // no break
+ case 1: DrawRectangle(cRect(cx, cy - y, x + 1, 1), Color); break;
+ case 7: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); // no break
+ case 2: DrawRectangle(cRect(cx - x, cy - y, x + 1, 1), Color); break;
+ case 3: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); break;
+ case 4: DrawRectangle(cRect(cx, cy + y, x + 1, 1), Color); break;
+ case 0:
+ case 6: DrawRectangle(cRect(cx - x, cy - y, 2 * x + 1, 1), Color); if (Quadrants == 6) break;
+ case 8: DrawRectangle(cRect(cx - x, cy + y, 2 * x + 1, 1), Color); break;
+ case -1: DrawRectangle(cRect(cx + x, cy - y, x2 - x + 1, 1), Color); break;
+ case -2: DrawRectangle(cRect(x1, cy - y, cx - x - x1 + 1, 1), Color); break;
+ case -3: DrawRectangle(cRect(x1, cy + y, cx - x - x1 + 1, 1), Color); break;
+ case -4: DrawRectangle(cRect(cx + x, cy + y, x2 - x + 1, 1), Color); break;
+ }
+ x++;
+ StoppingX += TwoBSquare;
+ EllipseError += XChange;
+ XChange += TwoBSquare;
+ if (2 * EllipseError + YChange > 0) {
+ y--;
+ StoppingY -= TwoASquare;
+ EllipseError += YChange;
+ YChange += TwoASquare;
+ }
+ }
+ MarkDrawPortDirty(Rect);
+ Unlock();
+}
+
+void cPixmapMemory::DrawSlope(const cRect &Rect, tColor Color, int Type)
+{
+ //TODO anti-aliasing?
+ //TODO also simplify cBitmap::DrawSlope()
+ Lock();
+ bool upper = Type & 0x01;
+ bool falling = Type & 0x02;
+ bool vertical = Type & 0x04;
+ int x1 = Rect.Left();
+ int y1 = Rect.Top();
+ int x2 = Rect.Right();
+ int y2 = Rect.Bottom();
+ int w = Rect.Width();
+ int h = Rect.Height();
+ if (vertical) {
+ for (int y = y1; y <= y2; y++) {
+ double c = cos((y - y1) * M_PI / h);
+ if (falling)
+ c = -c;
+ int x = (x1 + x2) / 2 + int(w * c / 2);
+ if (upper && !falling || !upper && falling)
+ DrawRectangle(cRect(x1, y, x - x1 + 1, 1), Color);
+ else
+ DrawRectangle(cRect(x, y, x2 - x + 1, 1), Color);
+ }
+ }
+ else {
+ for (int x = x1; x <= x2; x++) {
+ double c = cos((x - x1) * M_PI / w);
+ if (falling)
+ c = -c;
+ int y = (y1 + y2) / 2 + int(h * c / 2);
+ if (upper)
+ DrawRectangle(cRect(x, y1, 1, y - y1 + 1), Color);
+ else
+ DrawRectangle(cRect(x, y, 1, y2 - y + 1), Color);
+ }
+ }
+ MarkDrawPortDirty(Rect);
+ Unlock();
+}
+
+void cPixmapMemory::Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)
+{
+ Lock();
+ if (Pixmap->Alpha() != ALPHA_TRANSPARENT) {
+ if (const cPixmapMemory *pm = dynamic_cast<const cPixmapMemory *>(Pixmap)) {
+ cRect s = Source.Intersected(Pixmap->DrawPort().Size());
+ if (!s.IsEmpty()) {
+ cPoint v = Dest - Source.Point();
+ cRect d = s.Shifted(v).Intersected(DrawPort().Size());
+ if (!d.IsEmpty()) {
+ s = d.Shifted(-v);
+ int a = pm->Alpha();
+ int ws = pm->DrawPort().Width();
+ int wd = DrawPort().Width();
+ const tColor *ps = pm->data + ws * s.Top() + s.Left();
+ tColor *pd = data + wd * d.Top() + d.Left();
+ for (int y = d.Height(); y-- > 0; ) {
+ const tColor *cs = ps;
+ tColor *cd = pd;
+ for (int x = d.Width(); x-- > 0; ) {
+ *cd = AlphaBlend(*cs, *cd, a);
+ cs++;
+ cd++;
+ }
+ ps += ws;
+ pd += wd;
+ }
+ MarkDrawPortDirty(d);
+ }
+ }
+ }
+ }
+ Unlock();
+}
+
+void cPixmapMemory::Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)
+{
+ Lock();
+ if (const cPixmapMemory *pm = dynamic_cast<const cPixmapMemory *>(Pixmap)) {
+ cRect s = Source.Intersected(pm->DrawPort().Size());
+ if (!s.IsEmpty()) {
+ cPoint v = Dest - Source.Point();
+ cRect d = s.Shifted(v).Intersected(DrawPort().Size());
+ if (!d.IsEmpty()) {
+ s = d.Shifted(-v);
+ int ws = pm->DrawPort().Width();
+ int wd = DrawPort().Width();
+ int w = d.Width() * sizeof(tColor);
+ const tColor *ps = pm->data + ws * s.Top() + s.Left();
+ tColor *pd = data + wd * d.Top() + d.Left();
+ for (int y = d.Height(); y-- > 0; ) {
+ memcpy(pd, ps, w);
+ ps += ws;
+ pd += wd;
+ }
+ MarkDrawPortDirty(d);
+ }
+ }
+ }
+ Unlock();
+}
+
+void cPixmapMemory::Scroll(const cPoint &Dest, const cRect &Source)
+{
+ Lock();
+ cRect s;
+ if (&Source == &cRect::Null)
+ s = DrawPort().Shifted(-DrawPort().Point());
+ else
+ s = Source.Intersected(DrawPort().Size());
+ if (!s.IsEmpty()) {
+ cPoint v = Dest - Source.Point();
+ cRect d = s.Shifted(v).Intersected(DrawPort().Size());
+ if (!d.IsEmpty()) {
+ s = d.Shifted(-v);
+ if (d.Point() != s.Point()) {
+ int ws = DrawPort().Width();
+ int wd = ws;
+ int w = d.Width() * sizeof(tColor);
+ const tColor *ps = data + ws * s.Top() + s.Left();
+ tColor *pd = data + wd * d.Top() + d.Left();
+ for (int y = d.Height(); y-- > 0; ) {
+ memmove(pd, ps, w); // source and destination might overlap!
+ ps += ws;
+ pd += wd;
+ }
+ if (panning)
+ SetDrawPortPoint(DrawPort().Point().Shifted(s.Point() - d.Point()), false);
+ else
+ MarkDrawPortDirty(d);
+ }
+ }
+ }
+ Unlock();
+}
+
+void cPixmapMemory::Pan(const cPoint &Dest, const cRect &Source)
+{
+ Lock();
+ panning = true;
+ Scroll(Dest, Source);
+ panning = false;
+ Unlock();
+}
+
// --- cOsd ------------------------------------------------------------------
static const char *OsdErrorTexts[] = {
@@ -745,8 +1556,11 @@ cVector<cOsd *> cOsd::Osds;
cOsd::cOsd(int Left, int Top, uint Level)
{
- savedRegion = NULL;
+ isTrueColor = false;
+ savedBitmap = NULL;
numBitmaps = 0;
+ savedPixmap = NULL;
+ numPixmaps = 0;
left = Left;
top = Top;
width = height = 0;
@@ -765,7 +1579,10 @@ cOsd::~cOsd()
{
for (int i = 0; i < numBitmaps; i++)
delete bitmaps[i];
- delete savedRegion;
+ delete savedBitmap;
+ delete savedPixmap;
+ for (int i = 0; i < numPixmaps; i++)
+ delete pixmaps[i];
for (int i = 0; i < Osds.Size(); i++) {
if (Osds[i] == this) {
Osds.Remove(i);
@@ -786,15 +1603,110 @@ void cOsd::SetOsdPosition(int Left, int Top, int Width, int Height)
void cOsd::SetAntiAliasGranularity(uint FixedColors, uint BlendColors)
{
+ if (isTrueColor)
+ return;
for (int i = 0; i < numBitmaps; i++)
bitmaps[i]->SetAntiAliasGranularity(FixedColors, BlendColors);
}
cBitmap *cOsd::GetBitmap(int Area)
{
+ if (isTrueColor)
+ Area = 0; // returns the dummy bitmap
return Area < numBitmaps ? bitmaps[Area] : NULL;
}
+cPixmap *cOsd::CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort)
+{
+ if (isTrueColor) {
+ LOCK_PIXMAPS;
+ cPixmap *Pixmap = new cPixmapMemory(Layer, ViewPort, DrawPort);
+ if (AddPixmap(Pixmap))
+ return Pixmap;
+ delete Pixmap;
+ }
+ return NULL;
+}
+
+void cOsd::DestroyPixmap(cPixmap *Pixmap)
+{
+ if (isTrueColor) {
+ LOCK_PIXMAPS;
+ for (int i = 1; i < numPixmaps; i++) { // begin at 1 - don't let the background pixmap be destroyed!
+ if (pixmaps[i] == Pixmap) {
+ pixmaps[0]->MarkViewPortDirty(Pixmap->ViewPort());
+ delete Pixmap;
+ while (i < numPixmaps - 1) {
+ pixmaps[i] = pixmaps[i + 1];
+ i++;
+ }
+ numPixmaps--;
+ return;
+ }
+ }
+ esyslog("ERROR: attempt to destroy an unregistered pixmap");
+ }
+}
+
+cPixmap *cOsd::AddPixmap(cPixmap *Pixmap)
+{
+ LOCK_PIXMAPS;
+ if (numPixmaps < MAXOSDPIXMAPS)
+ return pixmaps[numPixmaps++] = Pixmap;
+ else
+ esyslog("ERROR: too many OSD pixmaps requested (maximum is %d)", MAXOSDPIXMAPS);
+ return NULL;
+}
+
+cPixmapMemory *cOsd::RenderPixmaps(void)
+{
+ cPixmapMemory *Pixmap = NULL;
+ if (isTrueColor) {
+ LOCK_PIXMAPS;
+ for (;;) {
+ // Collect overlapping dirty rectangles:
+ cRect d;
+ for (int i = 0; i < numPixmaps; i++) {
+ cPixmap *pm = pixmaps[i];
+ if (!pm->DirtyViewPort().IsEmpty()) {
+ if (d.IsEmpty() || d.Intersects(pm->DirtyViewPort())) {
+ d.Combine(pm->DirtyViewPort());
+ pm->SetClean();
+ }
+ }
+ }
+ if (d.IsEmpty())
+ break;
+//#define DebugDirty
+#ifdef DebugDirty
+ static cRect OldDirty;
+ cRect NewDirty = d;
+ d.Combine(OldDirty);
+ OldDirty = NewDirty;
+#endif
+ Pixmap = new cPixmapMemory(0, d);
+ Pixmap->Clear();
+ // Render the individual pixmaps into the resulting pixmap:
+ for (int Layer = 0; Layer < MAXPIXMAPLAYERS; Layer++) {
+ for (int i = 0; i < numPixmaps; i++) {
+ cPixmap *pm = pixmaps[i];
+ if (pm->Layer() == Layer)
+ Pixmap->DrawPixmap(pm, d, i == 0);
+ }
+ }
+#ifdef DebugDirty
+ cPixmapMemory DirtyIndicator(7, NewDirty);
+ static tColor DirtyIndicatorColors[] = { 0x7FFFFF00, 0x7F00FFFF };
+ static int DirtyIndicatorIndex = 0;
+ DirtyIndicator.Fill(DirtyIndicatorColors[DirtyIndicatorIndex]);
+ DirtyIndicatorIndex = 1 - DirtyIndicatorIndex;
+ Pixmap->Render(&DirtyIndicator, DirtyIndicator.DrawPort(), DirtyIndicator.ViewPort().Point().Shifted(-Pixmap->ViewPort().Point()));
+#endif
+ }
+ }
+ return Pixmap;
+}
+
eOsdError cOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
{
if (NumAreas > MAXOSDAREAS)
@@ -809,6 +1721,10 @@ eOsdError cOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
break;
}
}
+ if (Areas[i].bpp == 32) {
+ if (NumAreas > 1)
+ return oeTooManyAreas;
+ }
}
return Result;
}
@@ -820,11 +1736,21 @@ eOsdError cOsd::SetAreas(const tArea *Areas, int NumAreas)
while (numBitmaps)
delete bitmaps[--numBitmaps];
width = height = 0;
- for (int i = 0; i < NumAreas; i++) {
- bitmaps[numBitmaps++] = new cBitmap(Areas[i].Width(), Areas[i].Height(), Areas[i].bpp, Areas[i].x1, Areas[i].y1);
- width = max(width, Areas[i].x2 + 1);
- height = max(height, Areas[i].y2 + 1);
- }
+ isTrueColor = NumAreas == 1 && Areas[0].bpp == 32;
+ if (isTrueColor) {
+ width = Areas[0].x2 - Areas[0].x1 + 1;
+ height = Areas[0].y2 - Areas[0].y1 + 1;
+ cPixmap *Pixmap = CreatePixmap(0, cRect(Areas[0].x1, Areas[0].y1, width, height));
+ Pixmap->Clear();
+ bitmaps[numBitmaps++] = new cBitmap(10, 10, 8); // dummy bitmap for GetBitmap()
+ }
+ else {
+ for (int i = 0; i < NumAreas; i++) {
+ bitmaps[numBitmaps++] = new cBitmap(Areas[i].Width(), Areas[i].Height(), Areas[i].bpp, Areas[i].x1, Areas[i].y1);
+ width = max(width, Areas[i].x2 + 1);
+ height = max(height, Areas[i].y2 + 1);
+ }
+ }
}
else
esyslog("ERROR: cOsd::SetAreas returned %d (%s)", Result, Result < oeUnknown ? OsdErrorTexts[Result] : OsdErrorTexts[oeUnknown]);
@@ -833,62 +1759,119 @@ eOsdError cOsd::SetAreas(const tArea *Areas, int NumAreas)
void cOsd::SaveRegion(int x1, int y1, int x2, int y2)
{
- delete savedRegion;
- savedRegion = new cBitmap(x2 - x1 + 1, y2 - y1 + 1, 8, x1, y1);
- for (int i = 0; i < numBitmaps; i++)
- savedRegion->DrawBitmap(bitmaps[i]->X0(), bitmaps[i]->Y0(), *bitmaps[i]);
+ if (isTrueColor) {
+ delete savedPixmap;
+ cRect r(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ savedPixmap = new cPixmapMemory(0, r);
+ savedPixmap->Copy(pixmaps[0], r, cPoint(0, 0));
+ }
+ else {
+ delete savedBitmap;
+ savedBitmap = new cBitmap(x2 - x1 + 1, y2 - y1 + 1, 8, x1, y1);
+ for (int i = 0; i < numBitmaps; i++)
+ savedBitmap->DrawBitmap(bitmaps[i]->X0(), bitmaps[i]->Y0(), *bitmaps[i]);
+ }
}
void cOsd::RestoreRegion(void)
{
- if (savedRegion) {
- DrawBitmap(savedRegion->X0(), savedRegion->Y0(), *savedRegion);
- delete savedRegion;
- savedRegion = NULL;
+ if (isTrueColor) {
+ if (savedPixmap) {
+ pixmaps[0]->Copy(savedPixmap, savedPixmap->DrawPort(), savedPixmap->ViewPort().Point());
+ delete savedPixmap;
+ savedPixmap = NULL;
+ }
+ }
+ else {
+ if (savedBitmap) {
+ DrawBitmap(savedBitmap->X0(), savedBitmap->Y0(), *savedBitmap);
+ delete savedBitmap;
+ savedBitmap = NULL;
+ }
}
}
eOsdError cOsd::SetPalette(const cPalette &Palette, int Area)
{
- if (Area < numBitmaps)
+ if (isTrueColor)
+ return oeOk;
+ if (Area < numBitmaps) {
bitmaps[Area]->Take(Palette);
+ return oeOk;
+ }
return oeUnknown;
}
+void cOsd::DrawImage(const cPoint &Point, const cImage &Image)
+{
+ if (isTrueColor)
+ pixmaps[0]->DrawImage(Point, Image);
+}
+
+void cOsd::DrawImage(const cPoint &Point, int ImageHandle)
+{
+ if (isTrueColor)
+ pixmaps[0]->DrawImage(Point, ImageHandle);
+}
+
void cOsd::DrawPixel(int x, int y, tColor Color)
{
- for (int i = 0; i < numBitmaps; i++)
- bitmaps[i]->DrawPixel(x, y, Color);
+ if (isTrueColor)
+ pixmaps[0]->DrawPixel(cPoint(x, y), Color);
+ else {
+ for (int i = 0; i < numBitmaps; i++)
+ bitmaps[i]->DrawPixel(x, y, Color);
+ }
}
void cOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
{
- for (int i = 0; i < numBitmaps; i++)
- bitmaps[i]->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg, ReplacePalette, Overlay);
+ if (isTrueColor)
+ pixmaps[0]->DrawBitmap(cPoint(x, y), Bitmap, ColorFg, ColorBg, Overlay);
+ else {
+ for (int i = 0; i < numBitmaps; i++)
+ bitmaps[i]->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg, ReplacePalette, Overlay);
+ }
}
void cOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
{
- for (int i = 0; i < numBitmaps; i++)
- bitmaps[i]->DrawText(x, y, s, ColorFg, ColorBg, Font, Width, Height, Alignment);
+ if (isTrueColor)
+ pixmaps[0]->DrawText(cPoint(x, y), s, ColorFg, ColorBg, Font, Width, Height, Alignment);
+ else {
+ for (int i = 0; i < numBitmaps; i++)
+ bitmaps[i]->DrawText(x, y, s, ColorFg, ColorBg, Font, Width, Height, Alignment);
+ }
}
void cOsd::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
{
- for (int i = 0; i < numBitmaps; i++)
- bitmaps[i]->DrawRectangle(x1, y1, x2, y2, Color);
+ if (isTrueColor)
+ pixmaps[0]->DrawRectangle(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color);
+ else {
+ for (int i = 0; i < numBitmaps; i++)
+ bitmaps[i]->DrawRectangle(x1, y1, x2, y2, Color);
+ }
}
void cOsd::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
{
- for (int i = 0; i < numBitmaps; i++)
- bitmaps[i]->DrawEllipse(x1, y1, x2, y2, Color, Quadrants);
+ if (isTrueColor)
+ pixmaps[0]->DrawEllipse(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color, Quadrants);
+ else {
+ for (int i = 0; i < numBitmaps; i++)
+ bitmaps[i]->DrawEllipse(x1, y1, x2, y2, Color, Quadrants);
+ }
}
void cOsd::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
{
- for (int i = 0; i < numBitmaps; i++)
- bitmaps[i]->DrawSlope(x1, y1, x2, y2, Color, Type);
+ if (isTrueColor)
+ pixmaps[0]->DrawSlope(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color, Type);
+ else {
+ for (int i = 0; i < numBitmaps; i++)
+ bitmaps[i]->DrawSlope(x1, y1, x2, y2, Color, Type);
+ }
}
void cOsd::Flush(void)
@@ -901,6 +1884,7 @@ cOsdProvider *cOsdProvider::osdProvider = NULL;
int cOsdProvider::oldWidth = 0;
int cOsdProvider::oldHeight = 0;
double cOsdProvider::oldAspect = 1.0;
+cImage *cOsdProvider::images[MAXOSDIMAGES] = { NULL };
cOsdProvider::cOsdProvider(void)
{
@@ -957,6 +1941,58 @@ void cOsdProvider::UpdateOsdSize(bool Force)
}
}
+bool cOsdProvider::SupportsTrueColor(void)
+{
+ if (osdProvider) {
+ return osdProvider->ProvidesTrueColor();
+ }
+ else
+ esyslog("ERROR: no OSD provider available in call to SupportsTrueColor()");
+ return false;
+}
+
+int cOsdProvider::StoreImageData(const cImage &Image)
+{
+ LOCK_PIXMAPS;
+ for (int i = 1; i < MAXOSDIMAGES; i++) {
+ if (!images[i]) {
+ images[i] = new cImage(Image);
+ return i;
+ }
+ }
+ return 0;
+}
+
+void cOsdProvider::DropImageData(int ImageHandle)
+{
+ LOCK_PIXMAPS;
+ if (0 < ImageHandle && ImageHandle < MAXOSDIMAGES) {
+ delete images[ImageHandle];
+ images[ImageHandle] = NULL;
+ }
+}
+
+const cImage *cOsdProvider::GetImageData(int ImageHandle)
+{
+ LOCK_PIXMAPS;
+ if (0 < ImageHandle && ImageHandle < MAXOSDIMAGES)
+ return images[ImageHandle];
+ return NULL;
+}
+
+int cOsdProvider::StoreImage(const cImage &Image)
+{
+ if (osdProvider)
+ return osdProvider->StoreImageData(Image);
+ return -1;
+}
+
+void cOsdProvider::DropImage(int ImageHandle)
+{
+ if (osdProvider)
+ osdProvider->DropImageData(ImageHandle);
+}
+
void cOsdProvider::Shutdown(void)
{
delete osdProvider;
diff --git a/osd.h b/osd.h
index aa8a35ab..2bb6e0a0 100644
--- a/osd.h
+++ b/osd.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.h 2.5 2010/01/17 13:23:50 kls Exp $
+ * $Id: osd.h 2.6 2011/02/20 14:52:17 kls Exp $
*/
#ifndef __OSD_H
@@ -15,12 +15,15 @@
#include <stdint.h>
#include "config.h"
#include "font.h"
+#include "thread.h"
#include "tools.h"
#define OSD_LEVEL_DEFAULT 0
#define OSD_LEVEL_SUBTITLES 10
#define MAXNUMCOLORS 256
+#define ALPHA_TRANSPARENT 0x00
+#define ALPHA_OPAQUE 0xFF
enum {
//AARRGGBB
@@ -50,6 +53,28 @@ enum eOsdError { oeOk, // see also OsdErrorTexts in osd.c
typedef uint32_t tColor; // see also font.h
typedef uint8_t tIndex;
+inline tColor ArgbToColor(uint8_t A, uint8_t R, uint8_t G, uint8_t B)
+{
+ return (tColor(A) << 24) | (tColor(R) << 16) | (tColor(G) << 8) | B;
+}
+
+inline tColor RgbToColor(uint8_t R, uint8_t G, uint8_t B)
+{
+ return (tColor(R) << 16) | (tColor(G) << 8) | B;
+}
+
+inline tColor RgbToColor(double R, double G, double B)
+{
+ return RgbToColor(uint8_t(0xFF * R), uint8_t(0xFF * G), uint8_t(0xFF * B));
+}
+
+tColor HsvToColor(double H, double S, double V);
+ ///< Converts the given Hue (0..360), Saturation (0..1) and Value (0..1)
+ ///< to an RGB tColor value. The alpha value of the result is 0x00, so
+ ///< the caller may need to set it accordingly.
+
+tColor AlphaBlend(tColor ColorFg, tColor ColorBg, tColor AlphaLayer = ALPHA_OPAQUE);
+
class cPalette {
private:
tColor color[MAXNUMCOLORS];
@@ -257,16 +282,424 @@ struct tArea {
bool Intersects(const tArea &Area) const { return !(x2 < Area.x1 || x1 > Area.x2 || y2 < Area.y1 || y1 > Area.y2); }
};
+class cPoint {
+private:
+ int x;
+ int y;
+public:
+ cPoint(void) { x = y = 0; }
+ cPoint(int X, int Y) { x = X; y = Y; }
+ cPoint(const cPoint &Point) { x = Point.X(); y = Point.Y(); }
+ bool operator==(const cPoint &Point) const { return x == Point.X() && y == Point.Y(); }
+ bool operator!=(const cPoint &Point) const { return !(*this == Point); }
+ cPoint operator-(void) const { return cPoint(-x, -y); }
+ cPoint operator-(const cPoint &Point) const { return cPoint(x - Point.X(), y - Point.Y()); }
+ int X(void) const { return x; }
+ int Y(void) const { return y; }
+ void SetX(int X) { x = X; }
+ void SetY(int Y) { y = Y; }
+ void Set(int X, int Y) { x = X; y = Y; }
+ void Set(const cPoint &Point) { x = Point.X(); y = Point.Y(); }
+ void Shift(int Dx, int Dy) { x += Dx; y += Dy; }
+ void Shift(const cPoint &Dp) { x += Dp.X(); y += Dp.Y(); }
+ cPoint Shifted(int Dx, int Dy) const { cPoint p(*this); p.Shift(Dx, Dy); return p; }
+ cPoint Shifted(const cPoint &Dp) const { cPoint p(*this); p.Shift(Dp); return p; }
+ };
+
+class cSize {
+private:
+ int width;
+ int height;
+public:
+ cSize(void) { width = height = 0; }
+ cSize(int Width, int Height) { width = Width; height = Height; }
+ cSize(const cSize &Size) { width = Size.Width(); height = Size.Height(); }
+ bool operator==(const cSize &Size) const { return width == Size.Width() && height == Size.Height(); }
+ bool operator!=(const cSize &Size) const { return !(*this == Size); }
+ bool operator<(const cSize &Size) const { return width < Size.Width() && height < Size.Height(); }
+ int Width(void) const { return width; }
+ int Height(void) const { return height; }
+ void SetWidth(int Width) { width = Width; }
+ void SetHeight(int Height) { height = Height; }
+ void Set(int Width, int Height) { width = Width; height = Height; }
+ void Set(const cSize &Size) { width = Size.Width(); height = Size.Height(); }
+ bool Contains(const cPoint &Point) const { return 0 <= Point.X() && 0 <= Point.Y() && Point.X() < width && Point.Y() < height; }
+ void Grow(int Dw, int Dh) { width += 2 * Dw; height += 2 * Dh; }
+ cSize Grown(int Dw, int Dh) const { cSize s(*this); s.Grow(Dw, Dh); return s; }
+ };
+
+class cRect {
+private:
+ cPoint point;
+ cSize size;
+public:
+ static const cRect Null;
+ cRect(void): point(0, 0), size(0, 0) {}
+ cRect(int X, int Y, int Width, int Height): point(X, Y), size(Width, Height) {}
+ cRect(const cPoint &Point, const cSize &Size): point(Point), size(Size) {}
+ cRect(const cSize &Size): point(0, 0), size(Size) {}
+ cRect(const cRect &Rect): point(Rect.Point()), size(Rect.Size()) {}
+ bool operator==(const cRect &Rect) const { return point == Rect.Point() && size == Rect.Size(); }
+ bool operator!=(const cRect &Rect) const { return !(*this == Rect); }
+ int X(void) const { return point.X(); }
+ int Y(void) const { return point.Y(); }
+ int Width(void) const { return size.Width(); }
+ int Height(void) const { return size.Height(); }
+ int Left(void) const { return X(); }
+ int Top(void) const { return Y(); }
+ int Right(void) const { return X() + Width() - 1; }
+ int Bottom(void) const { return Y() + Height() - 1; }
+ const cPoint &Point(void) const { return point; }
+ const cSize &Size(void) const { return size; }
+ void Set(int X, int Y, int Width, int Height) { point.Set(X, Y); size.Set(Width, Height); }
+ void Set(cPoint Point, cSize Size) { point.Set(Point); size.Set(Size); }
+ void SetPoint(int X, int Y) { point.Set(X, Y); }
+ void SetPoint(const cPoint &Point) { point.Set(Point); }
+ void SetSize(int Width, int Height) { size.Set(Width, Height); }
+ void SetSize(const cSize &Size) { size.Set(Size); }
+ void SetX(int X) { point.SetX(X); }
+ void SetY(int Y) { point.SetY(Y); }
+ void SetWidth(int Width) { size.SetWidth(Width); }
+ void SetHeight(int Height) { size.SetHeight(Height); }
+ void SetLeft(int Left) { SetWidth(Width() + X() - Left); SetX(Left); }
+ void SetTop(int Top) { SetHeight(Height() + Y() - Top); SetY(Top); }
+ void SetRight(int Right) { SetWidth(Right - X() + 1); }
+ void SetBottom(int Bottom) { SetHeight(Bottom - Y() + 1); }
+ void Shift(int Dx, int Dy) { point.Shift(Dx, Dy); }
+ void Shift(const cPoint &Dp) { point.Shift(Dp); }
+ cRect Shifted(int Dx, int Dy) const { cRect r(*this); r.Shift(Dx, Dy); return r; }
+ cRect Shifted(const cPoint &Dp) const { cRect r(*this); r.Shift(Dp); return r; }
+ void Grow(int Dx, int Dy);
+ ///< Grows the rectangle by the given number of pixels in either direction.
+ ///< A negative value will shrink the rectangle.
+ cRect Grown(int Dw, int Dh) const { cRect r(*this); r.Grow(Dw, Dh); return r; }
+ bool Contains(const cPoint &Point) const;
+ ///< Returns true if this rectangle contains Point.
+ bool Contains(const cRect &Rect) const;
+ ///< Returns true if this rectangle completely contains Rect.
+ bool Intersects(const cRect &Rect) const;
+ ///< Returns true if this rectangle intersects with Rect.
+ cRect Intersected(const cRect &Rect) const;
+ ///< Returns the intersection of this rectangle and the given Rect.
+ void Combine(const cRect &Rect);
+ ///< Combines this rectangle with the given Rect.
+ cRect Combined(const cRect &Rect) const { cRect r(*this); r.Combine(Rect); return r; }
+ ///< Returns the surrounding rectangle that contains this rectangle and the
+ ///< given Rect.
+ void Combine(const cPoint &Point);
+ ///< Combines this rectangle with the given Point.
+ cRect Combined(const cPoint &Point) const { cRect r(*this); r.Combine(Point); return r; }
+ ///< Returns the surrounding rectangle that contains this rectangle and the
+ ///< given Point.
+ bool IsEmpty(void) const { return Width() <= 0 || Height() <= 0; }
+ ///< Returns true if this rectangle is empty.
+ };
+
+class cImage {
+private:
+ cSize size;
+ tColor *data;
+public:
+ cImage(void);
+ cImage(const cImage &Image);
+ cImage(const cSize &Size, const tColor *Data = NULL);
+ ///< Creates an image with the given Size and allocates the necessary memory
+ ///< to copy the pixels pointed to by Data, which is a sequence of
+ ///< (Size.Width() * Size.Height()) tColor values.
+ ///< If Data is NULL, the allocated memory is not initialized.
+ ///< The alpha value of the Image's pixels is taken into account, so it has to be
+ ///< greater than 0 for the image to be visible.
+ virtual ~cImage();
+ const cSize &Size(void) const { return size; }
+ int Width(void) const { return size.Width(); }
+ int Height(void) const { return size.Height(); }
+ const tColor *Data(void) const { return data; }
+ tColor GetPixel(const cPoint &Point) const { return data[size.Width() * Point.Y() + Point.X()]; }
+ ///< Returns the pixel value at the given Point.
+ ///< For performance reasons there is no range check here, so the caller
+ ///< must make sure that the Point is within the images size.
+ void SetPixel(const cPoint &Point, tColor Color) { data[size.Width() * Point.Y() + Point.X()] = Color; }
+ ///< Sets the pixel at the given Point to Color.
+ ///< For performance reasons there is no range check here, so the caller
+ ///< must make sure that the Point is within the images size.
+ void Clear(void);
+ ///< Clears the image data by setting all pixels to be fully transparent.
+ void Fill(tColor Color);
+ ///< Fills the image data with the given Color.
+ };
+
+#define MAXPIXMAPLAYERS 8
+
+class cPixmap {
+ friend class cOsd;
+ friend class cPixmapMutexLock;
+private:
+ static cMutex mutex;
+ int layer;
+ int alpha;
+ bool tile;
+ cRect viewPort;
+ cRect drawPort;
+ cRect dirtyViewPort;
+ cRect dirtyDrawPort;
+protected:
+ virtual ~cPixmap() {}
+ void MarkViewPortDirty(const cRect &Rect);
+ ///< Marks the given rectangle of the view port of this pixmap as dirty.
+ ///< Rect is combined with the existing dirtyViewPort rectangle.
+ ///< The coordinates of Rect are given in absolute OSD values.
+ void MarkViewPortDirty(const cPoint &Point);
+ ///< Marks the given point of the view port of this pixmap as dirty.
+ ///< Point is combined with the existing dirtyViewPort rectangle.
+ ///< The coordinates of Point are given in absolute OSD values.
+ void MarkDrawPortDirty(const cRect &Rect);
+ ///< Marks the given rectangle of the draw port of this pixmap as dirty.
+ ///< Rect is combined with the existing dirtyDrawPort rectangle.
+ ///< The coordinates of Rect are relative to the pixmap's draw port.
+ ///< If Rect extends into the currently visible view port of this pixmap,
+ ///< MarkViewPortDirty() is called with the appropriate value.
+ void MarkDrawPortDirty(const cPoint &Point);
+ ///< Marks the given point of the draw port of this pixmap as dirty.
+ ///< Point is combined with the existing dirtyDrawPort rectangle.
+ ///< The coordinates of Point are relative to the pixmap's draw port.
+ ///< If Point is within the currently visible view port of this pixmap,
+ ///< MarkViewPortDirty() is called with the appropriate value.
+ virtual void DrawPixmap(const cPixmap *Pixmap, const cRect &Dirty, bool Opaque);
+ ///< Draws the Dirty part of the given Pixmap into this pixmap. If Opaque
+ ///< is true, the Pixmap is copied, otherwise it is rendered into this
+ ///< pixmap. This function is used only to implement the tile handling
+ ///< in the final rendering to the OSD.
+public:
+ cPixmap(void);
+ cPixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null);
+ ///< Creates a pixmap in the given Layer. When rendering the final OSD, pixmaps
+ ///< are handled in ascending order of their individual layer. This is
+ ///< important if pixmaps overlap each other. The one with the highest layer is
+ ///< rendered last. The actual value of Layer doesn't matter, it is only used
+ ///< for defining the rendering sequence. If Layer is less than zero, this
+ ///< pixmap will not be rendered into the final OSD (it can be activated by a
+ ///< later call to SetLayer()). The value 0 is reserved for the background
+ ///< pixmap and shall not be used otherwise. If there are several pixmaps with
+ ///< the same value of Layer, their rendering sequence within that layer is
+ ///< undefined.
+ ///< I order to allow devices that can handle only a limited number of layers,
+ ///< the Layer parameters must be less than 8 (MAXPIXMAPLAYERS).
+ ///< ViewPort defines the rectangle in which this pixmap will be rendered on
+ ///< the OSD. If no DrawPort ist given, it defaults to the same size as the
+ ///< ViewPort, with its upper left corner set to (0, 0).
+ ///< All drawing operations will be executed relative to the origin of the
+ ///< DrawPort rectangle, and will be clipped to the size of this rectangle.
+ ///< The DrawPort may have a different size than the ViewPort. If it is smaller
+ ///< than the ViewPort, the rest of the ViewPort is treated as fully transparent
+ ///< (unless this is a tiled pixmap, in which case the DrawPort is repeated
+ ///< horizontally and vertically to fill the entire ViewPort). If the DrawPort
+ ///< is larger than the ViewPort, only that portion of the DrawPort that
+ ///< intersects with the ViewPort will be visible on the OSD.
+ ///< The drawing area of a newly created cPixmap is not initialized and may
+ ///< contain random data.
+ static void Lock(void) { mutex.Lock(); }
+ ///< All member functions of cPixmap set locks as necessary to make sure
+ ///< they are thread-safe. If several cPixmap member functions need to be
+ ///< called in a row, the caller must surround these calls with proper
+ ///< Lock()/Unlock() calls. See the LOCK_PIXMAPS macro for a convenient
+ ///< way of doing this.
+ static void Unlock(void) { mutex.Unlock(); }
+ int Layer(void) const { return layer; }
+ int Alpha(void) const { return alpha; }
+ bool Tile(void) const { return tile; }
+ const cRect &ViewPort(void) const { return viewPort; }
+ ///< Returns the pixmap's view port, which is relative to the OSD's origin.
+ const cRect &DrawPort(void) const { return drawPort; }
+ ///< Returns the pixmap's draw port, which is relative to the view port.
+ const cRect &DirtyViewPort(void) const { return dirtyViewPort; }
+ ///< Returns the "dirty" rectangle this pixmap causes on the OSD. This is the
+ ///< surrounding rectangle around all pixels that have been modified since the
+ ///< last time this pixmap has been rendered to the OSD. The rectangle is
+ ///< relative to the OSD's origin.
+ const cRect &DirtyDrawPort(void) const { return dirtyDrawPort; }
+ ///< Returns the "dirty" rectangle in the draw port of this this pixmap. This is
+ ///< the surrounding rectangle around all pixels that have been modified since the
+ ///< last time this pixmap has been rendered to the OSD. The rectangle is
+ ///< relative to the draw port's origin.
+ void SetClean(void);
+ ///< Resets the "dirty" rectangles of this pixmap.
+ virtual void SetLayer(int Layer);
+ ///< Sets the layer of this pixmap to the given value.
+ ///< If the new layer is greater than zero, the pixmap will be visible.
+ ///< If it is less than zero, it will be invisible.
+ ///< A value of 0 will be silently ignored.
+ ///< If a derived class reimplements this function, it needs to call the base
+ ///< class function.
+ virtual void SetAlpha(int Alpha);
+ ///< Sets the alpha value of this pixmap to the given value.
+ ///< Alpha is limited to the range 0 (fully transparent) to 255 (fully opaque).
+ ///< If a derived class reimplements this function, it needs to call the base
+ ///< class function.
+ virtual void SetTile(bool Tile);
+ ///< Sets the tile property of this pixmap to the given value. If Tile is true,
+ ///< the pixmaps data will be repeated horizontally and vertically if necessary
+ ///< to fill the entire view port.
+ ///< If a derived class reimplements this function, it needs to call the base
+ ///< class function.
+ virtual void SetViewPort(const cRect &Rect);
+ ///< Sets the pixmap's view port to the given Rect.
+ ///< If a derived class reimplements this function, it needs to call the base
+ ///< class function.
+ virtual void SetDrawPortPoint(const cPoint &Point, bool Dirty = true);
+ ///< Sets the pixmap's draw port to the given Point.
+ ///< Only the origin point of the draw port can be modified, its size is fixed.
+ ///< By default, setting a new draw port point results in marking the relevant
+ ///< part of the view port as "drity". If Dirty is set to false, the view port
+ ///< will not be marked as dirty. This is mainly used to implement the Pan()
+ ///< function.
+ ///< If a derived class reimplements this function, it needs to call the base
+ ///< class function.
+ virtual void Clear(void) = 0;
+ ///< Clears the pixmap's draw port by setting all pixels to be fully transparent.
+ ///< A derived class must call Lock()/Unlock().
+ virtual void Fill(tColor Color) = 0;
+ ///< Fills the pixmap's draw port with the given Color.
+ ///< A derived class must call Lock()/Unlock().
+ virtual void DrawImage(const cPoint &Point, const cImage &Image) = 0;
+ ///< Draws the given Image into this pixmap at the given Point.
+ virtual void DrawImage(const cPoint &Point, int ImageHandle) = 0;
+ ///< Draws the image referenced by the given ImageHandle into this pixmap at
+ ///< the given Point. ImageHandle must be a value that has previously been
+ ///< returned by a call to cOsdProvider::StoreImage(). If ImageHandle
+ ///< has an invalid value, nothing happens.
+ virtual void DrawPixel(const cPoint &Point, tColor Color, tColor Alpha = ALPHA_OPAQUE) = 0;
+ ///< Sets the pixel at the given Point to the given Color, which is
+ ///< a full 32 bit ARGB value. If the alpha value of Color is not 0xFF
+ ///< (fully opaque), the pixel is alpha blended with the existing color
+ ///< at the given position in this pixmap. If Alpha is less than
+ ///< ALPHA_OPAQUE, the alpha value of Color will be reduced accordingly.
+ virtual void DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool Overlay = false) = 0;
+ ///< Sets the pixels in the OSD with the data from the given
+ ///< Bitmap, putting the upper left corner of the Bitmap at Point.
+ ///< If ColorFg or ColorBg is given, the first palette entry of the Bitmap
+ ///< will be mapped to ColorBg and the second palette entry will be mapped to
+ ///< ColorFg (palette indexes are defined so that 0 is the background and
+ ///< 1 is the foreground color).
+ ///< If Overlay is true, any pixel in Bitmap that has color index 0 will
+ ///< not overwrite the corresponding pixel in the target area.
+ ///< This function is mainly for compatibility with skins or plugins that
+ ///< draw bitmaps onto the OSD.
+ 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) = 0;
+ ///< Draws the given string at Point with the given foreground
+ ///< and background color and font. If Width and Height are given, the text
+ ///< will be drawn into a rectangle with the given size and the given
+ ///< Alignment (default is top-left). If ColorBg is clrTransparent, no
+ ///< background pixels will be drawn, which allows drawing "transparent" text.
+ virtual void DrawRectangle(const cRect &Rect, tColor Color) = 0;
+ ///< Draws a filled rectangle with the given Color.
+ virtual void DrawEllipse(const cRect &Rect, tColor Color, int Quadrants = 0) = 0;
+ ///< Draws a filled ellipse with the given Color that fits into the given
+ ///< rectangle. Quadrants controls which parts of the ellipse are actually drawn:
+ ///< 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..-8 draws the inverted part of the given quadrant(s)
+ ///< If Quadrants is not 0, the coordinates are those of the actual area, not
+ ///< the full circle!
+ virtual void DrawSlope(const cRect &Rect, tColor Color, int Type) = 0;
+ ///< Draws a "slope" with the given Color into the given rectangle.
+ ///< Type controls the direction of the slope and which side of it will be drawn:
+ ///< 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
+ virtual void Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest) = 0;
+ ///< Renders the part of the given Pixmap covered by Source into this pixmap at
+ ///< location Dest. The Source rectangle is relative to the given Pixmap's draw port.
+ ///< The Pixmap's alpha value is to be used when rendering.
+ virtual void Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest) = 0;
+ ///< Copies the part of the given Pixmap covered by Source into this pixmap at
+ ///< location Dest. The Source rectangle is relative to the given Pixmap's draw port.
+ ///< The data from Pixmap is copied as is, no alpha handling of any kind takes
+ ///< place.
+ virtual void Scroll(const cPoint &Dest, const cRect &Source = cRect::Null) = 0;
+ ///< Scrolls the data in the pixmap's draw port to the given Dest point.
+ ///< If Source is given, only the data within that rectangle is scrolled.
+ ///< Source and Dest are relative to this pixmap's draw port.
+ virtual void Pan(const cPoint &Dest, const cRect &Source = cRect::Null) = 0;
+ ///< Does the same as Scroll(), but also shifts the draw port accordingly,
+ ///< so that the view port doesn't get dirty if the scrolled rectangle
+ ///< covers the entire view port. This may be of advantage if, e.g.,
+ ///< there is a draw port that holds, say, 11 lines of text, while the
+ ///< view port displays only 10 lines. By Pan()'ing the draw port up one
+ ///< line, an new bottom line can be written into the draw port (without
+ ///< being seen through the view port), and later the draw port can be
+ ///< shifted smoothly, resulting in a smooth scrolling.
+ ///< It is the caller's responsibility to make sure that Source and Dest
+ ///< are given in such a way that the view port will not get dirty. No
+ ///< check is done whether this condition actually holds true.
+ };
+
+class cPixmapMutexLock : public cMutexLock {
+public:
+ cPixmapMutexLock(void): cMutexLock(&cPixmap::mutex) {}
+ };
+
+#define LOCK_PIXMAPS cPixmapMutexLock PixmapMutexLock
+
+// cPixmapMemory is an implementation of cPixmap that uses an array of tColor
+// values to store the pixmap.
+
+class cPixmapMemory : public cPixmap {
+private:
+ tColor *data;
+ bool panning;
+public:
+ cPixmapMemory(void);
+ cPixmapMemory(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null);
+ virtual ~cPixmapMemory();
+ const uint8_t *Data(void) { return (uint8_t *)data; }
+ virtual void Clear(void);
+ virtual void Fill(tColor Color);
+ virtual void DrawImage(const cPoint &Point, const cImage &Image);
+ virtual void DrawImage(const cPoint &Point, int ImageHandle);
+ virtual void DrawPixel(const cPoint &Point, tColor Color, tColor Alpha = ALPHA_OPAQUE);
+ virtual void DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool Overlay = false);
+ 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);
+ virtual void DrawRectangle(const cRect &Rect, tColor Color);
+ virtual void DrawEllipse(const cRect &Rect, tColor Color, int Quadrants = 0);
+ virtual void DrawSlope(const cRect &Rect, tColor Color, int Type);
+ virtual void Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest);
+ virtual void Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest);
+ virtual void Scroll(const cPoint &Dest, const cRect &Source = cRect::Null);
+ virtual void Pan(const cPoint &Dest, const cRect &Source = cRect::Null);
+ };
+
#define MAXOSDAREAS 16
+#define MAXOSDPIXMAPS 64
+
+/// The cOsd class is the interface to the "On Screen Display".
+/// An actual output device needs to derive from this class and implement
+/// the functionality necessary to display the OSD on the TV screen.
+/// If the actual OSD supports "True Color", it can either let VDR do
+/// all the rendering by calling RenderPixmaps() ("raw mode"), or it can
+/// reimplement all necessary cPixmap functions and do the rendering
+/// itself ("high level mode").
+/// If an OSD provides a "high level mode", it shall also provide a "raw mode"
+/// in order to verify proper operation. The plugin that impements the OSD
+/// shall offer a configuration switch in its setup.
class cOsd {
friend class cOsdProvider;
private:
static int osdLeft, osdTop, osdWidth, osdHeight;
static cVector<cOsd *> Osds;
- cBitmap *savedRegion;
+ bool isTrueColor;
+ cBitmap *savedBitmap;
cBitmap *bitmaps[MAXOSDAREAS];
int numBitmaps;
+ cPixmapMemory *savedPixmap;
+ cPixmap *pixmaps[MAXOSDPIXMAPS];
+ int numPixmaps;
int left, top, width, height;
uint level;
bool active;
@@ -295,6 +728,32 @@ protected:
virtual void SetActive(bool On) { active = On; }
///< Sets this OSD to be the active one.
///< A derived class must call cOsd::SetActive(On).
+ const cPixmap * const *Pixmaps(void) { return pixmaps; }
+ ///< Returns the list of currently active pixmaps in this OSD.
+ int NumPixmaps(void) { return numPixmaps; }
+ ///< Returns the number of currently active pixmaps in this OSD.
+ cPixmap *AddPixmap(cPixmap *Pixmap);
+ ///< Adds the given Pixmap to the list of currently active pixmaps in this OSD.
+ ///< Returns Pixmap if the operation was successful, or NULL if the maximum
+ ///< number of pixmaps has been exceeded.
+ ///< A derived class that implements its own cPixmap class must call AddPixmap()
+ ///< in order to add a newly created pixmap to the OSD's list of pixmaps.
+ cPixmapMemory *RenderPixmaps(void);
+ ///< Renders the dirty part of all pixmaps into a resulting pixmap that
+ ///< shall be displayed on the OSD. The returned pixmap's view port is
+ ///< set to the location of the rectangle on the OSD that needs to be
+ ///< refreshed; its draw port's origin is at (0, 0), and it has the same
+ ///< size as the view port.
+ ///< If there are several non-overlapping dirty rectangles from different pixmaps,
+ ///< they are returned separately in order to avoid re-rendering large parts
+ ///< of the OSD that haven't changed at all. The caller must therefore call
+ ///< RenderPixmaps() repeatedly until it returns NULL, and display the returned
+ ///< parts of the OSD at their appropriate locations. During this entire
+ ///< operation the caller must hold a lock on the cPixmap mutex (for instance
+ ///< by putting a LOCK_PIXMAPS into the scope of the operation).
+ ///< If there are no dirty pixmaps, or if this is not a true color OSD,
+ ///< this function returns NULL.
+ ///< The caller must delete the returned pixmap after use.
public:
virtual ~cOsd();
///< Shuts down the OSD.
@@ -309,6 +768,9 @@ public:
///< screen.
static int IsOpen(void) { return Osds.Size() && Osds[0]->level == OSD_LEVEL_DEFAULT; }
///< Returns true if there is currently a level 0 OSD open.
+ bool IsTrueColor(void) const { return isTrueColor; }
+ ///< Returns 'true' if this is a true color OSD (providing full 32 bit color
+ ///< depth).
int Left(void) { return left; }
int Top(void) { return top; }
int Width(void) { return width; }
@@ -323,9 +785,33 @@ public:
///< a single color combination, and may not be able to serve all
///< requested colors. By default the palette assumes there will be
///< 10 fixed colors and 10 color combinations.
+ ///< If this is a true color OSD, this function does nothing.
cBitmap *GetBitmap(int Area);
///< Returns a pointer to the bitmap for the given Area, or NULL if no
///< such bitmap exists.
+ ///< If this is a true color OSD, a pointer to a dummy bitmap with 8bpp
+ ///< is returned. This is done so that skins that call this function
+ ///< in order to preset the bitmap's palette won't crash.
+ virtual cPixmap *CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null);
+ ///< Creates a new true color pixmap on this OSD (see cPixmap for details).
+ ///< The caller must not delete the returned object, it will be deleted when
+ ///< the OSD is deleted. DestroyPixmap() can be called if a pixmap shall be
+ ///< destroyed before the OSD is deleted.
+ ///< If this is not a true color OSD, this function returns NULL.
+ virtual void DestroyPixmap(cPixmap *Pixmap);
+ ///< Destroys the given Pixmap, which has previously been created by a call to
+ ///< CreatePixmap(). When the OSD is deleted, all pixmaps are destroyed
+ ///< automatically. So this function only needs to be used if a pixmap shall
+ ///< be destroyed while the OSD is still being used.
+ virtual void DrawImage(const cPoint &Point, const cImage &Image);
+ ///< Draws the given Image on this OSD at the given Point.
+ ///< If this is not a true color OSD, this function does nothing.
+ virtual void DrawImage(const cPoint &Point, int ImageHandle);
+ ///< Draws the image referenced by the given ImageHandle on this OSD at
+ ///< the given Point. ImageHandle must be a value that has previously been
+ ///< returned by a call to cOsdProvider::StoreImage(). If ImageHandle
+ ///< has an invalid value, nothing happens.
+ ///< If this is not a true color OSD, this function does nothing.
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas);
///< Checks whether the OSD can display the given set of sub-areas.
///< The return value indicates whether a call to SetAreas() with this
@@ -343,6 +829,9 @@ public:
///< are part of the rectangle that surrounds a given drawing operation
///< will be drawn into, with the proper offsets.
///< A new call overwrites any previous settings
+ ///< To set up a true color OSD, exactly one area must be requested, with
+ ///< its coordinates set to the full area the OSD shall cover, and the
+ ///< bpp value set to 32.
virtual void SaveRegion(int x1, int y1, int x2, int y2);
///< Saves the region defined by the given coordinates for later restoration
///< through RestoreRegion(). Only one saved region can be active at any
@@ -352,6 +841,7 @@ public:
///< If SaveRegion() has not been called before, nothing will happen.
virtual eOsdError SetPalette(const cPalette &Palette, int Area);
///< Sets the Palette for the given Area (the first area is numbered 0).
+ ///< If this is a true color OSD, nothing happens and oeOk is returned.
virtual void DrawPixel(int x, int y, tColor Color);
///< Sets the pixel at the given coordinates to the given Color, which is
///< a full 32 bit ARGB value.
@@ -368,6 +858,7 @@ public:
///< area shall have its palette replaced with the one from Bitmap.
///< If Overlay is true, any pixel in Bitmap that has color index 0 will
///< not overwrite the corresponding pixel in the target area.
+ ///< If this is a true color OSD, ReplacePalette has no meaning.
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);
///< Draws the given string at coordinates (x, y) with the given foreground
///< and background color and font. If Width and Height are given, the text
@@ -401,18 +892,40 @@ public:
///< 7: vertical, falling, upper
virtual void Flush(void);
///< Actually commits all data to the OSD hardware.
+ ///< Flush() should return as soon as possible.
};
+#define MAXOSDIMAGES 64
+
class cOsdProvider {
+ friend class cPixmapMemory;
private:
static cOsdProvider *osdProvider;
static int oldWidth;
static int oldHeight;
static double oldAspect;
+ static cImage *images[MAXOSDIMAGES];
protected:
virtual cOsd *CreateOsd(int Left, int Top, uint Level) = 0;
///< Returns a pointer to a newly created cOsd object, which will be located
///< at the given coordinates.
+ virtual bool ProvidesTrueColor(void) { return false; }
+ ///< Returns true if this OSD provider is able to handle a true color OSD.
+ virtual int StoreImageData(const cImage &Image);
+ ///< Copies the given Image and returns a handle for later reference.
+ ///< A derived class can implement its own image storing mechanism by
+ ///< reimplementing this function as well as DropImageData().
+ ///< The base class implementation simply copies the image data to allow
+ ///< plugins to always use this interface, no matter if the actual device
+ ///< provides support for storing image data or not. The handles returned
+ ///< by the default implementation are positive integers. A derived class
+ ///< might want to use negative integers as handles, so that it can fall
+ ///< back to using the base class image storing mechanism if, e.g., it runs
+ ///< out of memory.
+ virtual void DropImageData(int ImageHandle);
+ ///< Drops the image data referenced by ImageHandle.
+ static const cImage *GetImageData(int ImageHandle);
+ ///< Gets the image data referenced by ImageHandle.
public:
cOsdProvider(void);
//XXX maybe parameter to make this one "sticky"??? (frame-buffer etc.)
@@ -427,7 +940,22 @@ public:
///< Inquires the actual size of the video display and adjusts the OSD and
///< font sizes accordingly. If Force is true, all settings are recalculated,
///< even if the video resolution hasn't changed since the last call to
- ///< this funtion.
+ ///< this function.
+ static bool SupportsTrueColor(void);
+ ///< Returns true if the current OSD provider is able to handle a true color OSD.
+ static int StoreImage(const cImage &Image);
+ ///< Stores the given Image for later use with DrawImage() on an OSD or
+ ///< pixmap. The returned number is a handle that must be used when
+ ///< referencing this image in a call to DrawImage() or DropImage().
+ ///< The image data is copied, so any later changes to Image will have
+ ///< no effect on the stored image.
+ ///< A derived class may be able to copy frequently used images to some
+ ///< space where they can be retrieved faster than using a cImage in each call.
+ ///< If this is not a true color OSD, or if the image data can't be stored for
+ ///< any reason, this function returns 0 and nothing is stored.
+ static void DropImage(int ImageHandle);
+ ///< Drops the image referenced by the given ImageHandle. If ImageHandle
+ ///< has an invalid value, nothing happens.
static void Shutdown(void);
///< Shuts down the OSD provider facility by deleting the current OSD provider.
};
diff --git a/skinsttng.c b/skinsttng.c
index c39ecce0..9bd1b15c 100644
--- a/skinsttng.c
+++ b/skinsttng.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: skinsttng.c 2.6 2010/11/07 15:10:08 kls Exp $
+ * $Id: skinsttng.c 2.7 2011/02/20 13:02:49 kls Exp $
*/
// Star Trek: The Next Generation® is a registered trademark of Paramount Pictures
@@ -182,12 +182,17 @@ cSkinSTTNGDisplayChannel::cSkinSTTNGDisplayChannel(bool WithInfo)
int yt = (y0 + y1) / 2;
int yb = (y6 + y7) / 2;
osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop() + (Setup.ChannelInfoPos ? 0 : cOsd::OsdHeight() - y7));
- tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 8 } };
- if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 32 } }; // TrueColor
+ if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
else {
- tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 4 } };
- osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 8 } }; // 256 colors
+ if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ else {
+ tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 4 } }; // 16 colors
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ }
}
osd->DrawRectangle(x0, y0, x7 - 1, y7 - 1, Theme.Color(clrBackground));
osd->DrawRectangle(x0, y0, x1 - 1, y1 - 1, clrTransparent);
@@ -220,12 +225,17 @@ cSkinSTTNGDisplayChannel::cSkinSTTNGDisplayChannel(bool WithInfo)
y0 = 0;
y1 = lineHeight;
osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop() + (Setup.ChannelInfoPos ? 0 : cOsd::OsdHeight() - y1));
- tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 8 } };
- if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 32 } }; // TrueColor
+ if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
else {
- tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 4 } };
- osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 8 } }; // 256 colors
+ if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ else {
+ tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 4 } }; // 16 colors
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ }
}
osd->DrawRectangle(x0, y0, x7 - 1, y1 - 1, clrTransparent);
osd->DrawEllipse (x0, y0, x1 - 1, y1 - 1, frameColor, 7);
@@ -397,21 +407,26 @@ cSkinSTTNGDisplayMenu::cSkinSTTNGDisplayMenu(void)
int yt = (y0 + y1) / 2;
int yb = (y6 + y7) / 2;
osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop());
- tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 8 } };
- if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 32 } }; // TrueColor
+ if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
else {
- tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 4 } };
- if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 8 } }; // 256 colors
+ if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
else {
- tArea Areas[] = { { x0, y0, x7 - 1, y3 - 1, 2 },
- { x0, y3, x3 - 1, y4 - 1, 1 },
- { x3, y3, x4 - 1, y4 - 1, 2 },
- { x4, y3, x7 - 1, y4 - 1, 2 },
- { x0, y4, x7 - 1, y7 - 1, 4 }
- };
- osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 4 } }; // 16 colors
+ if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ else {
+ tArea Areas[] = { { x0, y0, x7 - 1, y3 - 1, 2 }, // 2..16 colors
+ { x0, y3, x3 - 1, y4 - 1, 1 },
+ { x3, y3, x4 - 1, y4 - 1, 2 },
+ { x4, y3, x7 - 1, y4 - 1, 2 },
+ { x0, y4, x7 - 1, y7 - 1, 4 }
+ };
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ }
}
}
osd->DrawRectangle(x0, y0, x7 - 1, y7 - 1, Theme.Color(clrBackground));
@@ -754,12 +769,17 @@ cSkinSTTNGDisplayReplay::cSkinSTTNGDisplayReplay(bool ModeOnly)
int yt = (y0 + y1) / 2;
int yb = (y6 + y7) / 2;
osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop() + cOsd::OsdHeight() - y7);
- tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 8 } };
- if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 32 } }; // TrueColor
+ if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
else {
- tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 4 } };
- osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 8 } }; // 256 colors
+ if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ else {
+ tArea Areas[] = { { 0, 0, x7 - 1, y7 - 1, 4 } }; // 16 colors
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ }
}
osd->DrawRectangle(x0, y0, x7 - 1, y7 - 1, ModeOnly ? clrTransparent : Theme.Color(clrBackground));
if (!ModeOnly) {
@@ -888,12 +908,17 @@ cSkinSTTNGDisplayVolume::cSkinSTTNGDisplayVolume(void)
y0 = 0;
y1 = lineHeight;
osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop() + cOsd::OsdHeight() - y1);
- tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 8 } };
- if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 32 } }; // TrueColor
+ if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
else {
- tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 4 } };
- osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 8 } }; // 256 colors
+ if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ else {
+ tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 4 } }; // 16 colors
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ }
}
osd->DrawRectangle(x0, y0, x7 - 1, y1 - 1, clrTransparent);
osd->DrawEllipse (x0, y0, x1 - 1, y1 - 1, frameColor, 7);
@@ -1003,21 +1028,26 @@ cSkinSTTNGDisplayTracks::cSkinSTTNGDisplayTracks(const char *Title, int NumTrack
int yt = (y0 + y1) / 2;
int yb = (y6 + y7) / 2;
osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop() + cOsd::OsdHeight() - y7);
- tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 8 } };
- if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 32 } }; // TrueColor
+ if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
else {
- tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 4 } };
- if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 8 } }; // 256 colors
+ if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
else {
- tArea Areas[] = { { x0, y0, x7 - 1, y3 - 1, 2 },
- { x0, y3, x3 - 1, y4 - 1, 1 },
- { x3, y3, x4 - 1, y4 - 1, 2 },
- { x4, y3, x7 - 1, y4 - 1, 2 },
- { x0, y4, x7 - 1, y7 - 1, 4 }
- };
- osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ tArea Areas[] = { { x0, y0, x7 - 1, y7 - 1, 4 } }; // 16 colors
+ if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ else {
+ tArea Areas[] = { { x0, y0, x7 - 1, y3 - 1, 2 }, // 2..16 colors
+ { x0, y3, x3 - 1, y4 - 1, 1 },
+ { x3, y3, x4 - 1, y4 - 1, 2 },
+ { x4, y3, x7 - 1, y4 - 1, 2 },
+ { x0, y4, x7 - 1, y7 - 1, 4 }
+ };
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ }
}
}
osd->DrawRectangle(x0, y0, x7 - 1, y7 - 1, Theme.Color(clrBackground));
@@ -1131,12 +1161,17 @@ cSkinSTTNGDisplayMessage::cSkinSTTNGDisplayMessage(void)
y0 = 0;
y1 = lineHeight;
osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop() + cOsd::OsdHeight() - y1);
- tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 8 } };
- if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 32 } }; // TrueColor
+ if (osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
else {
- tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 2 } };
- osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 8 } }; // 256 colors
+ if (Setup.AntiAlias && osd->CanHandleAreas(Areas, sizeof(Areas) / sizeof(tArea)) == oeOk)
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ else {
+ tArea Areas[] = { { x0, y0, x7 - 1, y1 - 1, 2 } }; // 4 colors
+ osd->SetAreas(Areas, sizeof(Areas) / sizeof(tArea));
+ }
}
osd->DrawRectangle(x0, y0, x7 - 1, y1 - 1, clrTransparent);
osd->DrawEllipse (x0, y0, x1 - 1, y1 - 1, frameColor, 7);