/* * osddemo.c: A plugin for the Video Disk Recorder * * See the README file for copyright information and how to reach the author. * * $Id: osddemo.c 3.1 2014/01/01 13:29:54 kls Exp $ */ #include #include static const char *VERSION = "2.1.1"; static const char *DESCRIPTION = "Demo of arbitrary OSD setup"; static const char *MAINMENUENTRY = "Osd Demo"; // --- DrawEllipses ---------------------------------------------------------- void DrawEllipse(cOsd *Osd, int x1, int y1, int x2, int y2, int Quadrants) { Osd->DrawRectangle(x1 + 2, y1 + 2, x2 - 2, y2 - 2, clrGreen); Osd->DrawEllipse(x1 + 3, y1 + 3, x2 - 3, y2 - 3, clrRed, Quadrants); } void DrawEllipses(cOsd *Osd) { int xa = 0; int ya = 0; int xb = Osd->Width() - 1; int yb = Osd->Height() - 1; int x0 = xa; int x5 = xb; int x1 = x0 + (xb - xa) / 5; int x2 = x0 + (xb - xa) * 2 / 5; int x3 = x0 + (xb - xa) * 3 / 5; int x4 = x0 + (xb - xa) * 4 / 5; int y0 = ya; int y4 = yb; int y2 = (y0 + y4) / 2; int y1 = (y0 + y2) / 2; int y3 = (y2 + y4) / 2; Osd->DrawRectangle(xa, ya, xb, yb, clrGray50); DrawEllipse(Osd, x4, y0, x5, y4, 0); DrawEllipse(Osd, x2, y1, x3, y2, 1); DrawEllipse(Osd, x1, y1, x2, y2, 2); DrawEllipse(Osd, x1, y2, x2, y3, 3); DrawEllipse(Osd, x2, y2, x3, y3, 4); DrawEllipse(Osd, x3, y1, x4, y3, 5); DrawEllipse(Osd, x1, y0, x3, y1, 6); DrawEllipse(Osd, x0, y1, x1, y3, 7); DrawEllipse(Osd, x1, y3, x3, y4, 8); DrawEllipse(Osd, x3, y0, x4, y1, -1); DrawEllipse(Osd, x0, y0, x1, y1, -2); DrawEllipse(Osd, x0, y3, x1, y4, -3); DrawEllipse(Osd, x3, y3, x4, y4, -4); Osd->Flush(); } // --- DrawSlopes ------------------------------------------------------------ void DrawSlope(cOsd *Osd, int x1, int y1, int x2, int y2, int Type) { Osd->DrawRectangle(x1 + 2, y1 + 2, x2 - 2, y2 - 2, clrGreen); Osd->DrawSlope(x1 + 3, y1 + 3, x2 - 3, y2 - 3, clrRed, Type); } void DrawSlopes(cOsd *Osd) { int xa = 0; int ya = 0; int xb = Osd->Width() - 1; int yb = Osd->Height() - 1; int x0 = xa; int x4 = xb; int x2 = (x0 + x4) / 2; int x1 = (x0 + x2) / 2; int x3 = (x2 + x4) / 2; int y0 = ya; int y3 = yb; int y2 = (y0 + y3) / 2; int y1 = (y0 + y2) / 2; Osd->DrawRectangle(xa, ya, xb, yb, clrGray50); DrawSlope(Osd, x0, y0, x2, y1, 0); DrawSlope(Osd, x2, y0, x4, y1, 1); DrawSlope(Osd, x0, y1, x2, y2, 2); DrawSlope(Osd, x2, y1, x4, y2, 3); DrawSlope(Osd, x0, y2, x1, y3, 4); DrawSlope(Osd, x1, y2, x2, y3, 5); DrawSlope(Osd, x2, y2, x3, y3, 6); DrawSlope(Osd, x3, y2, x4, y3, 7); Osd->Flush(); } // --- cLineGame ------------------------------------------------------------- class cLineGame : public cOsdObject { private: cOsd *osd; int x; int y; tColor color; public: cLineGame(void); virtual ~cLineGame(); virtual void Show(void); virtual eOSState ProcessKey(eKeys Key); }; cLineGame::cLineGame(void) { osd = NULL; x = y = 0; color = clrRed; } cLineGame::~cLineGame() { delete osd; } void cLineGame::Show(void) { osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop(), 50); if (osd) { int x1 = cOsd::OsdWidth() - 1; int y1 = cOsd::OsdHeight() - 1; while (x1 > 0 && y1 > 0) { tArea Area = { 0, 0, x1, y1, 4 }; if (osd->CanHandleAreas(&Area, 1) == oeOk) { osd->SetAreas(&Area, 1); osd->DrawRectangle(0, 0, osd->Width() - 1, osd->Height() - 1, clrGray50); osd->Flush(); x = osd->Width() / 2; y = osd->Height() / 2; break; } x1 = x1 * 9 / 10; y1 = y1 * 9 / 10; } } } eOSState cLineGame::ProcessKey(eKeys Key) { eOSState state = cOsdObject::ProcessKey(Key); if (state == osUnknown) { const int d = 4; switch (Key & ~k_Repeat) { case kUp: y = max(0, y - d); break; case kDown: y = min(osd->Height() - d, y + d); break; case kLeft: x = max(0, x - d); break; case kRight: x = min(osd->Width() - d, x + d); break; case kRed: color = clrRed; break; case kGreen: color = clrGreen; break; case kYellow: color = clrYellow; break; case kBlue: color = clrBlue; break; case k1: DrawEllipses(osd); return osContinue; case k2: DrawSlopes(osd); return osContinue; case kBack: case kOk: return osEnd; default: return state; } osd->DrawRectangle(x, y, x + d - 1, y + d - 1, color); osd->Flush(); state = osContinue; } return state; } // --- cTrueColorDemo -------------------------------------------------------- class cTrueColorDemo : public cOsdObject, public cThread { private: cOsd *osd; cPoint cursor; cRect cursorLimits; bool clockwise; cPixmap *destroyablePixmap; cPixmap *toggleablePixmap; bool SetArea(void); virtual void Action(void); cPixmap *CreateTextPixmap(const char *s, int Line, int Layer, tColor ColorFg, tColor ColorBg, const cFont *Font); 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() { Cancel(3); delete osd; } cPixmap *cTrueColorDemo::CreateTextPixmap(const char *s, int Line, int Layer, tColor ColorFg, tColor ColorBg, const cFont *Font) { 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; cFont *OsdFont = cFont::CreateFont(Setup.FontOsd, Setup.FontOsdSize); cFont *SmlFont = cFont::CreateFont(Setup.FontSml, Setup.FontSmlSize); cFont *LrgFont = cFont::CreateFont(Setup.FontOsd, osd->Height() / 10); 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); 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, OsdFont); 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: { FadeInPixmap = CreateTextPixmap("VDR", Line, 1, clrYellow, clrTransparent, LrgFont); if (FadeInPixmap) Line += FadeInPixmap->DrawPort().Height(); Start = cTimeMs::Now(); State++; } break; case 1: { FadeInPixmap = CreateTextPixmap("Video Disk Recorder", Line, 3, clrYellow, clrTransparent, OsdFont); if (FadeInPixmap) Line += FadeInPixmap->DrawPort().Height(); Start = cTimeMs::Now(); State++; } break; case 2: { FadeInPixmap = CreateTextPixmap("True Color OSD Demo", Line, 1, clrYellow, clrTransparent, OsdFont); if (FadeInPixmap) Line += FadeInPixmap->DrawPort().Height(); Start = cTimeMs::Now(); State++; } break; case 3: { NextPixmap = CreateTextPixmap("Millions of colors", Line, 1, clrYellow, clrTransparent, LrgFont); 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, OsdFont); 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 char *Text = "Scrolling Pixmaps"; int w = OsdFont->Width(Text); int h = OsdFont->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, OsdFont); cString s = cString::sprintf("Line %d", ++ScrollLineNumber); Pixmap->DrawText(cPoint(0, Pixmap->ViewPort().Height()), s, clrYellow, clrTransparent, OsdFont); ScrollPixmap = Pixmap; ScrollStart.Set(0, 0); ScrollEnd.Set(0, -h); Start = cTimeMs::Now(); } State++; } break; case 8: { const char *Text = "Animation"; const int Size = SmlFont->Width(Text) + 10; const int NumDots = 12; const int AnimFrames = NumDots; // Temporarily using pixmap layer 0 to have the text alpha blended: AnimPixmap = osd->CreatePixmap(0, 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), Text, clrBlack, clrTransparent, SmlFont, Size, Size, taCenter); } AnimPixmap->SetLayer(3); // now setting the actual pixmap layer 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); } delete OsdFont; delete SmlFont; delete LrgFont; } bool cTrueColorDemo::SetArea(void) { if (osd) { tArea Area = { 0, 0, cOsd::OsdWidth() - 1, cOsd::OsdHeight() - 1, 32 }; return osd->SetAreas(&Area, 1) == oeOk; } return false; } void cTrueColorDemo::Show(void) { osd = cOsdProvider::NewOsd(cOsd::OsdLeft(), cOsd::OsdTop(), 50); if (osd) { if (SetArea()) { 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 k1: Cancel(3); SetArea(); DrawEllipses(osd); break; case k2: Cancel(3); SetArea(); DrawSlopes(osd); break; case kBack: case kOk: return osEnd; default: return state; } state = osContinue; } return state; } // --- cPluginOsddemo -------------------------------------------------------- class cPluginOsddemo : public cPlugin { private: // Add any member variables or functions you may need here. public: cPluginOsddemo(void); virtual ~cPluginOsddemo(); virtual const char *Version(void) { return VERSION; } virtual const char *Description(void) { return DESCRIPTION; } virtual const char *CommandLineHelp(void); virtual bool ProcessArgs(int argc, char *argv[]); virtual bool Start(void); virtual void Housekeeping(void); virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; } virtual cOsdObject *MainMenuAction(void); virtual cMenuSetupPage *SetupMenu(void); virtual bool SetupParse(const char *Name, const char *Value); }; cPluginOsddemo::cPluginOsddemo(void) { // Initialize any member variables here. // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT! } cPluginOsddemo::~cPluginOsddemo() { // Clean up after yourself! } const char *cPluginOsddemo::CommandLineHelp(void) { // Return a string that describes all known command line options. return NULL; } bool cPluginOsddemo::ProcessArgs(int argc, char *argv[]) { // Implement command line argument processing here if applicable. return true; } bool cPluginOsddemo::Start(void) { // Start any background activities the plugin shall perform. return true; } void cPluginOsddemo::Housekeeping(void) { // Perform any cleanup or other regular tasks. } cOsdObject *cPluginOsddemo::MainMenuAction(void) { // Perform the action when selected from the main VDR menu. if (cOsdProvider::SupportsTrueColor()) return new cTrueColorDemo; return new cLineGame; } cMenuSetupPage *cPluginOsddemo::SetupMenu(void) { // Return a setup menu in case the plugin supports one. return NULL; } bool cPluginOsddemo::SetupParse(const char *Name, const char *Value) { // Parse your own setup parameters and store their values. return false; } VDRPLUGINCREATOR(cPluginOsddemo); // Don't touch this!