vdr  1.7.27
dvbsdffosd.c
Go to the documentation of this file.
00001 /*
00002  * dvbsdffosd.c: Implementation of the DVB SD Full Featured On Screen Display
00003  *
00004  * See the README file for copyright information and how to reach the author.
00005  *
00006  * $Id: dvbsdffosd.c 2.3 2011/04/17 12:55:09 kls Exp $
00007  */
00008 
00009 #include "dvbsdffosd.h"
00010 #include <linux/dvb/osd.h>
00011 #include <signal.h>
00012 #include <sys/ioctl.h>
00013 #include <sys/unistd.h>
00014 #include <vdr/tools.h>
00015 
00016 // --- cDvbSdFfOsd -----------------------------------------------------------
00017 
00018 #define MAXNUMWINDOWS 7 // OSD windows are counted 1...7
00019 #define MAXOSDMEMORY  92000 // number of bytes available to the OSD (for unmodified DVB cards)
00020 
00021 class cDvbSdFfOsd : public cOsd {
00022 private:
00023   int osdDev;
00024   int osdMem;
00025   bool shown;
00026   void Cmd(OSD_Command cmd, int color = 0, int x0 = 0, int y0 = 0, int x1 = 0, int y1 = 0, const void *data = NULL);
00027 protected:
00028   virtual void SetActive(bool On);
00029 public:
00030   cDvbSdFfOsd(int Left, int Top, int OsdDev, uint Level);
00031   virtual ~cDvbSdFfOsd();
00032   virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas);
00033   virtual eOsdError SetAreas(const tArea *Areas, int NumAreas);
00034   virtual void Flush(void);
00035   };
00036 
00037 cDvbSdFfOsd::cDvbSdFfOsd(int Left, int Top, int OsdDev, uint Level)
00038 :cOsd(Left, Top, Level)
00039 {
00040   osdDev = OsdDev;
00041   shown = false;
00042   if (osdDev < 0)
00043      esyslog("ERROR: invalid OSD device handle (%d)!", osdDev);
00044   else {
00045      osdMem = MAXOSDMEMORY;
00046 #ifdef OSD_CAP_MEMSIZE
00047      // modified DVB cards may have more OSD memory:
00048      osd_cap_t cap;
00049      cap.cmd = OSD_CAP_MEMSIZE;
00050      if (ioctl(osdDev, OSD_GET_CAPABILITY, &cap) == 0)
00051         osdMem = cap.val;
00052 #endif
00053      }
00054 }
00055 
00056 cDvbSdFfOsd::~cDvbSdFfOsd()
00057 {
00058   SetActive(false);
00059 }
00060 
00061 void cDvbSdFfOsd::SetActive(bool On)
00062 {
00063   if (On != Active()) {
00064      cOsd::SetActive(On);
00065      if (On) {
00066         // must clear all windows here to avoid flashing effects - doesn't work if done
00067         // in Flush() only for the windows that are actually used...
00068         for (int i = 0; i < MAXNUMWINDOWS; i++) {
00069             Cmd(OSD_SetWindow, 0, i + 1);
00070             Cmd(OSD_Clear);
00071             }
00072         if (GetBitmap(0)) // only flush here if there are already bitmaps
00073            Flush();
00074         }
00075      else if (shown) {
00076         for (int i = 0; GetBitmap(i); i++) {
00077             Cmd(OSD_SetWindow, 0, i + 1);
00078             Cmd(OSD_Close);
00079             }
00080         shown = false;
00081         }
00082      }
00083 }
00084 
00085 eOsdError cDvbSdFfOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
00086 {
00087   eOsdError Result = cOsd::CanHandleAreas(Areas, NumAreas);
00088   if (Result == oeOk) {
00089      if (NumAreas > MAXNUMWINDOWS)
00090         return oeTooManyAreas;
00091      int TotalMemory = 0;
00092      for (int i = 0; i < NumAreas; i++) {
00093          if (Areas[i].bpp != 1 && Areas[i].bpp != 2 && Areas[i].bpp != 4 && Areas[i].bpp != 8)
00094             return oeBppNotSupported;
00095          if ((Areas[i].Width() & (8 / Areas[i].bpp - 1)) != 0)
00096             return oeWrongAlignment;
00097          if (Areas[i].Width() < 1 || Areas[i].Height() < 1 || Areas[i].Width() > 720 || Areas[i].Height() > 576)
00098             return oeWrongAreaSize;
00099          TotalMemory += Areas[i].Width() * Areas[i].Height() / (8 / Areas[i].bpp);
00100          }
00101      if (TotalMemory > osdMem)
00102         return oeOutOfMemory;
00103      }
00104   return Result;
00105 }
00106 
00107 eOsdError cDvbSdFfOsd::SetAreas(const tArea *Areas, int NumAreas)
00108 {
00109   if (shown) {
00110      for (int i = 0; GetBitmap(i); i++) {
00111          Cmd(OSD_SetWindow, 0, i + 1);
00112          Cmd(OSD_Close);
00113          }
00114      shown = false;
00115      }
00116   return cOsd::SetAreas(Areas, NumAreas);
00117 }
00118 
00119 void cDvbSdFfOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, const void *data)
00120 {
00121   if (osdDev >= 0) {
00122      osd_cmd_t dc;
00123      dc.cmd   = cmd;
00124      dc.color = color;
00125      dc.x0    = x0;
00126      dc.y0    = y0;
00127      dc.x1    = x1;
00128      dc.y1    = y1;
00129      dc.data  = (void *)data;
00130      ioctl(osdDev, OSD_SEND_CMD, &dc);
00131      }
00132 }
00133 
00134 void cDvbSdFfOsd::Flush(void)
00135 {
00136   if (!Active())
00137      return;
00138   cBitmap *Bitmap;
00139   for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) {
00140       Cmd(OSD_SetWindow, 0, i + 1);
00141       if (!shown)
00142          Cmd(OSD_Open, Bitmap->Bpp(), Left() + Bitmap->X0(), Top() + Bitmap->Y0(), Left() + Bitmap->X0() + Bitmap->Width() - 1, Top() + Bitmap->Y0() + Bitmap->Height() - 1, (void *)1); // initially hidden!
00143       int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
00144       if (!shown || Bitmap->Dirty(x1, y1, x2, y2)) {
00145          if (!shown) {
00146             x1 = y1 = 0;
00147             x2 = Bitmap->Width() - 1;
00148             y2 = Bitmap->Height() - 1;
00149             }
00150          //TODO Workaround: apparently the bitmap sent to the driver always has to be a multiple
00151          //TODO of 8 bits wide, and (dx * dy) also has to be a multiple of 8.
00152          //TODO Fix driver (should be able to handle any size bitmaps!)
00153          while ((x1 > 0 || x2 < Bitmap->Width() - 1) && ((x2 - x1) & 7) != 7) {
00154                if (x2 < Bitmap->Width() - 1)
00155                   x2++;
00156                else if (x1 > 0)
00157                   x1--;
00158                }
00159          //TODO "... / 2" <==> Bpp???
00160          while ((y1 > 0 || y2 < Bitmap->Height() - 1) && (((x2 - x1 + 1) * (y2 - y1 + 1) / 2) & 7) != 0) {
00161                if (y2 < Bitmap->Height() - 1)
00162                   y2++;
00163                else if (y1 > 0)
00164                   y1--;
00165                }
00166          while ((x1 > 0 || x2 < Bitmap->Width() - 1) && (((x2 - x1 + 1) * (y2 - y1 + 1) / 2) & 7) != 0) {
00167                if (x2 < Bitmap->Width() - 1)
00168                   x2++;
00169                else if (x1 > 0)
00170                   x1--;
00171                }
00172          // commit colors:
00173          int NumColors;
00174          const tColor *Colors = Bitmap->Colors(NumColors);
00175          if (Colors) {
00176             //TODO this should be fixed in the driver!
00177             tColor colors[NumColors];
00178             for (int i = 0; i < NumColors; i++) {
00179                 // convert AARRGGBB to AABBGGRR (the driver expects the colors the wrong way):
00180                 colors[i] = (Colors[i] & 0xFF000000) | ((Colors[i] & 0x0000FF) << 16) | (Colors[i] & 0x00FF00) | ((Colors[i] & 0xFF0000) >> 16);
00181                 }
00182             Colors = colors;
00183             //TODO end of stuff that should be fixed in the driver
00184             Cmd(OSD_SetPalette, 0, NumColors - 1, 0, 0, 0, Colors);
00185             }
00186          // commit modified data:
00187          Cmd(OSD_SetBlock, Bitmap->Width(), x1, y1, x2, y2, Bitmap->Data(x1, y1));
00188          }
00189       Bitmap->Clean();
00190       }
00191   if (!shown) {
00192      // Showing the windows in a separate loop to avoid seeing them come up one after another
00193      for (int i = 0; (Bitmap = GetBitmap(i)) != NULL; i++) {
00194          Cmd(OSD_SetWindow, 0, i + 1);
00195          Cmd(OSD_MoveWindow, 0, Left() + Bitmap->X0(), Top() + Bitmap->Y0());
00196          }
00197      shown = true;
00198      }
00199 }
00200 
00201 // --- cDvbOsdProvider -------------------------------------------------------
00202 
00203 cDvbOsdProvider::cDvbOsdProvider(int OsdDev)
00204 {
00205   osdDev = OsdDev;
00206 }
00207 
00208 cOsd *cDvbOsdProvider::CreateOsd(int Left, int Top, uint Level)
00209 {
00210   return new cDvbSdFfOsd(Left, Top, osdDev, Level);
00211 }