vdr  1.7.27
osd.c
Go to the documentation of this file.
00001 /*
00002  * osd.c: Abstract On Screen Display layer
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: osd.c 2.27 2012/03/05 10:28:01 kls Exp $
00008  */
00009 
00010 #include "osd.h"
00011 #include <math.h>
00012 #include <stdlib.h>
00013 #include <sys/ioctl.h>
00014 #include <sys/stat.h>
00015 #include <sys/unistd.h>
00016 #include "device.h"
00017 #include "tools.h"
00018 
00019 tColor HsvToColor(double H, double S, double V)
00020 {
00021   if (S > 0) {
00022      H /= 60;
00023      int i = floor(H);
00024      double f = H - i;
00025      double p = V * (1 - S);
00026      double q = V * (1 - S * f);
00027      double t = V * (1 - S * (1 - f));
00028      switch (i) {
00029        case 0:  return RgbToColor(V, t, p);
00030        case 1:  return RgbToColor(q, V, p);
00031        case 2:  return RgbToColor(p, V, t);
00032        case 3:  return RgbToColor(p, q, V);
00033        case 4:  return RgbToColor(t, p, V);
00034        default: return RgbToColor(V, p, q);
00035        }
00036      }
00037   else { // greyscale
00038      uint8_t n = V * 0xFF;
00039      return RgbToColor(n, n, n);
00040      }
00041 }
00042 
00043 #define USE_ALPHA_LUT
00044 #ifdef USE_ALPHA_LUT
00045 // Alpha blending with lookup table (by Reinhard Nissl <rnissl@gmx.de>)
00046 // A little slower (138 %) on fast machines than the implementation below and faster
00047 // on slow machines (79 %), but requires some 318KB of RAM for the lookup table.
00048 static uint16_t AlphaLutFactors[255][256][2];
00049 static uint8_t AlphaLutAlpha[255][256];
00050 
00051 class cInitAlphaLut {
00052 public:
00053   cInitAlphaLut(void)
00054   {
00055     for (int alphaA = 0; alphaA < 255; alphaA++) {
00056         int range = (alphaA == 255 ? 255 : 254);
00057         for (int alphaB = 0; alphaB < 256; alphaB++) {
00058             int alphaO_x_range = 255 * alphaA + alphaB * (range - alphaA);
00059             if (!alphaO_x_range)
00060                alphaO_x_range++;
00061             int factorA = (256 * 255 * alphaA + alphaO_x_range / 2) / alphaO_x_range;
00062             int factorB = (256 * alphaB * (range - alphaA) + alphaO_x_range / 2) / alphaO_x_range;
00063             AlphaLutFactors[alphaA][alphaB][0] = factorA;
00064             AlphaLutFactors[alphaA][alphaB][1] = factorB;
00065             AlphaLutAlpha[alphaA][alphaB] = alphaO_x_range / range;
00066             }
00067         }
00068   }
00069   } InitAlphaLut;
00070 
00071 tColor AlphaBlend(tColor ColorFg, tColor ColorBg, uint8_t AlphaLayer)
00072 {
00073   tColor Alpha = (ColorFg & 0xFF000000) >> 24;
00074   Alpha *= AlphaLayer;
00075   Alpha >>= 8;
00076   uint16_t *lut = &AlphaLutFactors[Alpha][(ColorBg & 0xFF000000) >> 24][0];
00077   return (tColor)((AlphaLutAlpha[Alpha][(ColorBg & 0xFF000000) >> 24] << 24)
00078     | (((((ColorFg & 0x00FF00FF) * lut[0] + (ColorBg & 0x00FF00FF) * lut[1])) & 0xFF00FF00)
00079     |  ((((ColorFg & 0x0000FF00) * lut[0] + (ColorBg & 0x0000FF00) * lut[1])) & 0x00FF0000)) >> 8);
00080 }
00081 #else
00082 // Alpha blending without lookup table.
00083 // Also works fast, but doesn't return the theoretically correct result.
00084 // It's "good enough", though.
00085 static tColor Multiply(tColor Color, uint8_t Alpha)
00086 {
00087   tColor RB = (Color & 0x00FF00FF) * Alpha;
00088   RB = ((RB + ((RB >> 8) & 0x00FF00FF) + 0x00800080) >> 8) & 0x00FF00FF;
00089   tColor AG = ((Color >> 8) & 0x00FF00FF) * Alpha;
00090   AG = ((AG + ((AG >> 8) & 0x00FF00FF) + 0x00800080)) & 0xFF00FF00;
00091   return AG | RB;
00092 }
00093 
00094 tColor AlphaBlend(tColor ColorFg, tColor ColorBg, uint8_t AlphaLayer)
00095 {
00096   tColor Alpha = (ColorFg & 0xFF000000) >> 24;
00097   if (AlphaLayer < ALPHA_OPAQUE) {
00098      Alpha *= AlphaLayer;
00099      Alpha = ((Alpha + ((Alpha >> 8) & 0x000000FF) + 0x00000080) >> 8) & 0x000000FF;
00100      }
00101   return Multiply(ColorFg, Alpha) + Multiply(ColorBg, 255 - Alpha);
00102 }
00103 #endif
00104 
00105 // --- cPalette --------------------------------------------------------------
00106 
00107 cPalette::cPalette(int Bpp)
00108 {
00109   SetBpp(Bpp);
00110   SetAntiAliasGranularity(10, 10);
00111 }
00112 
00113 cPalette::~cPalette()
00114 {
00115 }
00116 
00117 void cPalette::SetAntiAliasGranularity(uint FixedColors, uint BlendColors)
00118 {
00119   if (FixedColors >= MAXNUMCOLORS || BlendColors == 0)
00120      antiAliasGranularity = MAXNUMCOLORS - 1;
00121   else {
00122      int ColorsForBlending = MAXNUMCOLORS - FixedColors;
00123      int ColorsPerBlend = ColorsForBlending / BlendColors + 2; // +2 = the full foreground and background colors, which are amoung the fixed colors
00124      antiAliasGranularity = double(MAXNUMCOLORS - 1) / (ColorsPerBlend - 1);
00125      }
00126 }
00127 
00128 void cPalette::Reset(void)
00129 {
00130   numColors = 0;
00131   modified = false;
00132 }
00133 
00134 int cPalette::Index(tColor Color)
00135 {
00136   // Check if color is already defined:
00137   for (int i = 0; i < numColors; i++) {
00138       if (color[i] == Color)
00139          return i;
00140       }
00141   // No exact color, try a close one:
00142   int i = ClosestColor(Color, 4);
00143   if (i >= 0)
00144      return i;
00145   // No close one, try to define a new one:
00146   if (numColors < maxColors) {
00147      color[numColors++] = Color;
00148      modified = true;
00149      return numColors - 1;
00150      }
00151   // Out of colors, so any close color must do:
00152   return ClosestColor(Color);
00153 }
00154 
00155 void cPalette::SetBpp(int Bpp)
00156 {
00157   bpp = Bpp;
00158   maxColors = 1 << bpp;
00159   Reset();
00160 }
00161 
00162 void cPalette::SetColor(int Index, tColor Color)
00163 {
00164   if (Index < maxColors) {
00165      if (numColors <= Index) {
00166         numColors = Index + 1;
00167         modified = true;
00168         }
00169      else
00170         modified |= color[Index] != Color;
00171      color[Index] = Color;
00172      }
00173 }
00174 
00175 const tColor *cPalette::Colors(int &NumColors) const
00176 {
00177   NumColors = numColors;
00178   return numColors ? color : NULL;
00179 }
00180 
00181 void cPalette::Take(const cPalette &Palette, tIndexes *Indexes, tColor ColorFg, tColor ColorBg)
00182 {
00183   for (int i = 0; i < Palette.numColors; i++) {
00184       tColor Color = Palette.color[i];
00185       if (ColorFg || ColorBg) {
00186          switch (i) {
00187            case 0: Color = ColorBg; break;
00188            case 1: Color = ColorFg; break;
00189            default: ;
00190            }
00191          }
00192       int n = Index(Color);
00193       if (Indexes)
00194          (*Indexes)[i] = n;
00195       }
00196 }
00197 
00198 void cPalette::Replace(const cPalette &Palette)
00199 {
00200   for (int i = 0; i < Palette.numColors; i++)
00201       SetColor(i, Palette.color[i]);
00202   numColors = Palette.numColors;
00203   antiAliasGranularity = Palette.antiAliasGranularity;
00204 }
00205 
00206 tColor cPalette::Blend(tColor ColorFg, tColor ColorBg, uint8_t Level) const
00207 {
00208   if (antiAliasGranularity > 0)
00209      Level = uint8_t(int(Level / antiAliasGranularity + 0.5) * antiAliasGranularity);
00210   int Af = (ColorFg & 0xFF000000) >> 24;
00211   int Rf = (ColorFg & 0x00FF0000) >> 16;
00212   int Gf = (ColorFg & 0x0000FF00) >>  8;
00213   int Bf = (ColorFg & 0x000000FF);
00214   int Ab = (ColorBg & 0xFF000000) >> 24;
00215   int Rb = (ColorBg & 0x00FF0000) >> 16;
00216   int Gb = (ColorBg & 0x0000FF00) >>  8;
00217   int Bb = (ColorBg & 0x000000FF);
00218   int A = (Ab + (Af - Ab) * Level / 0xFF) & 0xFF;
00219   int R = (Rb + (Rf - Rb) * Level / 0xFF) & 0xFF;
00220   int G = (Gb + (Gf - Gb) * Level / 0xFF) & 0xFF;
00221   int B = (Bb + (Bf - Bb) * Level / 0xFF) & 0xFF;
00222   return (A << 24) | (R << 16) | (G << 8) | B;
00223 }
00224 
00225 int cPalette::ClosestColor(tColor Color, int MaxDiff) const
00226 {
00227   int n = 0;
00228   int d = INT_MAX;
00229   int A1 = (Color & 0xFF000000) >> 24;
00230   int R1 = (Color & 0x00FF0000) >> 16;
00231   int G1 = (Color & 0x0000FF00) >>  8;
00232   int B1 = (Color & 0x000000FF);
00233   for (int i = 0; i < numColors && d > 0; i++) {
00234       int A2 = (color[i] & 0xFF000000) >> 24;
00235       int R2 = (color[i] & 0x00FF0000) >> 16;
00236       int G2 = (color[i] & 0x0000FF00) >>  8;
00237       int B2 = (color[i] & 0x000000FF);
00238       int diff = 0;
00239       if (A1 || A2) // fully transparent colors are considered equal
00240          diff = (abs(A1 - A2) << 1) + (abs(R1 - R2) << 1) + (abs(G1 - G2) << 1) + (abs(B1 - B2) << 1);
00241       if (diff < d) {
00242          d = diff;
00243          n = i;
00244          }
00245       }
00246   return d <= MaxDiff ? n : -1;
00247 }
00248 
00249 // --- cBitmap ---------------------------------------------------------------
00250 
00251 cBitmap::cBitmap(int Width, int Height, int Bpp, int X0, int Y0)
00252 :cPalette(Bpp)
00253 {
00254   bitmap = NULL;
00255   x0 = X0;
00256   y0 = Y0;
00257   width = height = 0;
00258   SetSize(Width, Height);
00259 }
00260 
00261 cBitmap::cBitmap(const char *FileName)
00262 {
00263   bitmap = NULL;
00264   x0 = 0;
00265   y0 = 0;
00266   width = height = 0;
00267   LoadXpm(FileName);
00268 }
00269 
00270 cBitmap::cBitmap(const char *const Xpm[])
00271 {
00272   bitmap = NULL;
00273   x0 = 0;
00274   y0 = 0;
00275   width = height = 0;
00276   SetXpm(Xpm);
00277 }
00278 
00279 cBitmap::~cBitmap()
00280 {
00281   free(bitmap);
00282 }
00283 
00284 void cBitmap::SetSize(int Width, int Height)
00285 {
00286   if (bitmap && Width == width && Height == height)
00287      return;
00288   width = Width;
00289   height = Height;
00290   free(bitmap);
00291   bitmap = NULL;
00292   dirtyX1 = 0;
00293   dirtyY1 = 0;
00294   dirtyX2 = width - 1;
00295   dirtyY2 = height - 1;
00296   if (width > 0 && height > 0) {
00297      bitmap = MALLOC(tIndex, width * height);
00298      if (bitmap)
00299         memset(bitmap, 0x00, width * height);
00300      else
00301         esyslog("ERROR: can't allocate bitmap!");
00302      }
00303   else
00304      esyslog("ERROR: invalid bitmap parameters (%d, %d)!", width, height);
00305 }
00306 
00307 bool cBitmap::Contains(int x, int y) const
00308 {
00309   x -= x0;
00310   y -= y0;
00311   return 0 <= x && x < width && 0 <= y && y < height;
00312 }
00313 
00314 bool cBitmap::Covers(int x1, int y1, int x2, int y2) const
00315 {
00316   x1 -= x0;
00317   y1 -= y0;
00318   x2 -= x0;
00319   y2 -= y0;
00320   return x1 <= 0 && y1 <= 0 && x2 >= width - 1 && y2 >= height - 1;
00321 }
00322 
00323 bool cBitmap::Intersects(int x1, int y1, int x2, int y2) const
00324 {
00325   x1 -= x0;
00326   y1 -= y0;
00327   x2 -= x0;
00328   y2 -= y0;
00329   return !(x2 < 0 || x1 >= width || y2 < 0 || y1 >= height);
00330 }
00331 
00332 bool cBitmap::Dirty(int &x1, int &y1, int &x2, int &y2)
00333 {
00334   if (dirtyX2 >= 0) {
00335      x1 = dirtyX1;
00336      y1 = dirtyY1;
00337      x2 = dirtyX2;
00338      y2 = dirtyY2;
00339      return true;
00340      }
00341   return false;
00342 }
00343 
00344 void cBitmap::Clean(void)
00345 {
00346   dirtyX1 = width;
00347   dirtyY1 = height;
00348   dirtyX2 = -1;
00349   dirtyY2 = -1;
00350 }
00351 
00352 bool cBitmap::LoadXpm(const char *FileName)
00353 {
00354   bool Result = false;
00355   FILE *f = fopen(FileName, "r");
00356   if (f) {
00357      char **Xpm = NULL;
00358      bool isXpm = false;
00359      int lines = 0;
00360      int index = 0;
00361      char *s;
00362      cReadLine ReadLine;
00363      while ((s = ReadLine.Read(f)) != NULL) {
00364            s = skipspace(s);
00365            if (!isXpm) {
00366               if (strcmp(s, "/* XPM */") != 0) {
00367                  esyslog("ERROR: invalid header in XPM file '%s'", FileName);
00368                  break;
00369                  }
00370               isXpm = true;
00371               }
00372            else if (*s++ == '"') {
00373               if (!lines) {
00374                  int w, h, n, c;
00375                  if (4 != sscanf(s, "%d %d %d %d", &w, &h, &n, &c)) {
00376                     esyslog("ERROR: faulty 'values' line in XPM file '%s'", FileName);
00377                     isXpm = false;
00378                     break;
00379                     }
00380                  lines = h + n + 1;
00381                  Xpm = MALLOC(char *, lines);
00382                  memset(Xpm, 0, lines * sizeof(char*));
00383                  }
00384               char *q = strchr(s, '"');
00385               if (!q) {
00386                  esyslog("ERROR: missing quotes in XPM file '%s'", FileName);
00387                  isXpm = false;
00388                  break;
00389                  }
00390               *q = 0;
00391               if (index < lines)
00392                  Xpm[index++] = strdup(s);
00393               else {
00394                  esyslog("ERROR: too many lines in XPM file '%s'", FileName);
00395                  isXpm = false;
00396                  break;
00397                  }
00398               }
00399            }
00400      if (isXpm) {
00401         if (index == lines)
00402            Result = SetXpm(Xpm);
00403         else
00404            esyslog("ERROR: too few lines in XPM file '%s'", FileName);
00405         }
00406      if (Xpm) {
00407         for (int i = 0; i < index; i++)
00408             free(Xpm[i]);
00409         }
00410      free(Xpm);
00411      fclose(f);
00412      }
00413   else
00414      esyslog("ERROR: can't open XPM file '%s'", FileName);
00415   return Result;
00416 }
00417 
00418 bool cBitmap::SetXpm(const char *const Xpm[], bool IgnoreNone)
00419 {
00420   if (!Xpm)
00421      return false;
00422   const char *const *p = Xpm;
00423   int w, h, n, c;
00424   if (4 != sscanf(*p, "%d %d %d %d", &w, &h, &n, &c)) {
00425      esyslog("ERROR: faulty 'values' line in XPM: '%s'", *p);
00426      return false;
00427      }
00428   if (n > MAXNUMCOLORS) {
00429      esyslog("ERROR: too many colors in XPM: %d", n);
00430      return false;
00431      }
00432   int b = 0;
00433   while (1 << (1 << b) < (IgnoreNone ? n - 1 : n))
00434         b++;
00435   SetBpp(1 << b);
00436   SetSize(w, h);
00437   int NoneColorIndex = MAXNUMCOLORS;
00438   for (int i = 0; i < n; i++) {
00439       const char *s = *++p;
00440       if (int(strlen(s)) < c) {
00441          esyslog("ERROR: faulty 'colors' line in XPM: '%s'", s);
00442          return false;
00443          }
00444       s = skipspace(s + c);
00445       if (*s != 'c') {
00446          esyslog("ERROR: unknown color key in XPM: '%c'", *s);
00447          return false;
00448          }
00449       s = skipspace(s + 1);
00450       if (strcasecmp(s, "none") == 0) {
00451          NoneColorIndex = i;
00452          if (!IgnoreNone)
00453             SetColor(i, clrTransparent);
00454          continue;
00455          }
00456       if (*s != '#') {
00457          esyslog("ERROR: unknown color code in XPM: '%c'", *s);
00458          return false;
00459          }
00460       tColor color = strtoul(++s, NULL, 16) | 0xFF000000;
00461       SetColor((IgnoreNone && i > NoneColorIndex) ? i - 1 : i, color);
00462       }
00463   for (int y = 0; y < h; y++) {
00464       const char *s = *++p;
00465       if (int(strlen(s)) != w * c) {
00466          esyslog("ERROR: faulty pixel line in XPM: %d '%s'", y, s);
00467          return false;
00468          }
00469       for (int x = 0; x < w; x++) {
00470           for (int i = 0; i <= n; i++) {
00471               if (i == n) {
00472                  esyslog("ERROR: undefined pixel color in XPM: %d %d '%s'", x, y, s);
00473                  return false;
00474                  }
00475               if (strncmp(Xpm[i + 1], s, c) == 0) {
00476                  if (i == NoneColorIndex)
00477                     NoneColorIndex = MAXNUMCOLORS;
00478                  SetIndex(x, y, (IgnoreNone && i > NoneColorIndex) ? i - 1 : i);
00479                  break;
00480                  }
00481               }
00482           s += c;
00483           }
00484       }
00485   if (NoneColorIndex < MAXNUMCOLORS && !IgnoreNone)
00486      return SetXpm(Xpm, true);
00487   return true;
00488 }
00489 
00490 void cBitmap::SetIndex(int x, int y, tIndex Index)
00491 {
00492   if (bitmap) {
00493      if (0 <= x && x < width && 0 <= y && y < height) {
00494         if (bitmap[width * y + x] != Index) {
00495            bitmap[width * y + x] = Index;
00496            if (dirtyX1 > x)  dirtyX1 = x;
00497            if (dirtyY1 > y)  dirtyY1 = y;
00498            if (dirtyX2 < x)  dirtyX2 = x;
00499            if (dirtyY2 < y)  dirtyY2 = y;
00500            }
00501         }
00502      }
00503 }
00504 
00505 void cBitmap::DrawPixel(int x, int y, tColor Color)
00506 {
00507   x -= x0;
00508   y -= y0;
00509   SetIndex(x, y, Index(Color));
00510 }
00511 
00512 void cBitmap::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
00513 {
00514   if (bitmap && Bitmap.bitmap && Intersects(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) {
00515      if (Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1))
00516         Reset();
00517      x -= x0;
00518      y -= y0;
00519      if (ReplacePalette && Covers(x + x0, y + y0, x + x0 + Bitmap.Width() - 1, y + y0 + Bitmap.Height() - 1)) {
00520         Replace(Bitmap);
00521         for (int ix = 0; ix < Bitmap.width; ix++) {
00522             for (int iy = 0; iy < Bitmap.height; iy++) {
00523                 if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
00524                    SetIndex(x + ix, y + iy, Bitmap.bitmap[Bitmap.width * iy + ix]);
00525                 }
00526             }
00527         }
00528      else {
00529         tIndexes Indexes;
00530         Take(Bitmap, &Indexes, ColorFg, ColorBg);
00531         for (int ix = 0; ix < Bitmap.width; ix++) {
00532             for (int iy = 0; iy < Bitmap.height; iy++) {
00533                 if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
00534                    SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]);
00535                 }
00536             }
00537         }
00538      }
00539 }
00540 
00541 void cBitmap::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
00542 {
00543   if (bitmap) {
00544      int w = Font->Width(s);
00545      int h = Font->Height();
00546      int limit = 0;
00547      int cw = Width ? Width : w;
00548      int ch = Height ? Height : h;
00549      if (!Intersects(x, y, x + cw - 1, y + ch - 1))
00550         return;
00551      if (ColorBg != clrTransparent)
00552         DrawRectangle(x, y, x + cw - 1, y + ch - 1, ColorBg);
00553      if (Width || Height) {
00554         limit = x + cw - x0;
00555         if (Width) {
00556            if ((Alignment & taLeft) != 0)
00557               ;
00558            else if ((Alignment & taRight) != 0) {
00559               if (w < Width)
00560                  x += Width - w;
00561               }
00562            else { // taCentered
00563               if (w < Width)
00564                  x += (Width - w) / 2;
00565               }
00566            }
00567         if (Height) {
00568            if ((Alignment & taTop) != 0)
00569               ;
00570            else if ((Alignment & taBottom) != 0) {
00571               if (h < Height)
00572                  y += Height - h;
00573               }
00574            else { // taCentered
00575               if (h < Height)
00576                  y += (Height - h) / 2;
00577               }
00578            }
00579         }
00580      x -= x0;
00581      y -= y0;
00582      Font->DrawText(this, x, y, s, ColorFg, ColorBg, limit);
00583      }
00584 }
00585 
00586 void cBitmap::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
00587 {
00588   if (bitmap && Intersects(x1, y1, x2, y2)) {
00589      if (Covers(x1, y1, x2, y2))
00590         Reset();
00591      x1 -= x0;
00592      y1 -= y0;
00593      x2 -= x0;
00594      y2 -= y0;
00595      x1 = max(x1, 0);
00596      y1 = max(y1, 0);
00597      x2 = min(x2, width - 1);
00598      y2 = min(y2, height - 1);
00599      tIndex c = Index(Color);
00600      for (int y = y1; y <= y2; y++) {
00601          for (int x = x1; x <= x2; x++)
00602              SetIndex(x, y, c);
00603          }
00604      }
00605 }
00606 
00607 void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
00608 {
00609   if (!Intersects(x1, y1, x2, y2))
00610      return;
00611   // Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF
00612   int rx = x2 - x1;
00613   int ry = y2 - y1;
00614   int cx = (x1 + x2) / 2;
00615   int cy = (y1 + y2) / 2;
00616   switch (abs(Quadrants)) {
00617     case 0: rx /= 2; ry /= 2; break;
00618     case 1: cx = x1; cy = y2; break;
00619     case 2: cx = x2; cy = y2; break;
00620     case 3: cx = x2; cy = y1; break;
00621     case 4: cx = x1; cy = y1; break;
00622     case 5: cx = x1;          ry /= 2; break;
00623     case 6:          cy = y2; rx /= 2; break;
00624     case 7: cx = x2;          ry /= 2; break;
00625     case 8:          cy = y1; rx /= 2; break;
00626     default: ;
00627     }
00628   int TwoASquare = 2 * rx * rx;
00629   int TwoBSquare = 2 * ry * ry;
00630   int x = rx;
00631   int y = 0;
00632   int XChange = ry * ry * (1 - 2 * rx);
00633   int YChange = rx * rx;
00634   int EllipseError = 0;
00635   int StoppingX = TwoBSquare * rx;
00636   int StoppingY = 0;
00637   while (StoppingX >= StoppingY) {
00638         switch (Quadrants) {
00639           case  5: DrawRectangle(cx,     cy + y, cx + x, cy + y, Color); // no break
00640           case  1: DrawRectangle(cx,     cy - y, cx + x, cy - y, Color); break;
00641           case  7: DrawRectangle(cx - x, cy + y, cx,     cy + y, Color); // no break
00642           case  2: DrawRectangle(cx - x, cy - y, cx,     cy - y, Color); break;
00643           case  3: DrawRectangle(cx - x, cy + y, cx,     cy + y, Color); break;
00644           case  4: DrawRectangle(cx,     cy + y, cx + x, cy + y, Color); break;
00645           case  0:
00646           case  6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break;
00647           case  8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break;
00648           case -1: DrawRectangle(cx + x, cy - y, x2,     cy - y, Color); break;
00649           case -2: DrawRectangle(x1,     cy - y, cx - x, cy - y, Color); break;
00650           case -3: DrawRectangle(x1,     cy + y, cx - x, cy + y, Color); break;
00651           case -4: DrawRectangle(cx + x, cy + y, x2,     cy + y, Color); break;
00652           default: ;
00653           }
00654         y++;
00655         StoppingY += TwoASquare;
00656         EllipseError += YChange;
00657         YChange += TwoASquare;
00658         if (2 * EllipseError + XChange > 0) {
00659            x--;
00660            StoppingX -= TwoBSquare;
00661            EllipseError += XChange;
00662            XChange += TwoBSquare;
00663            }
00664         }
00665   x = 0;
00666   y = ry;
00667   XChange = ry * ry;
00668   YChange = rx * rx * (1 - 2 * ry);
00669   EllipseError = 0;
00670   StoppingX = 0;
00671   StoppingY = TwoASquare * ry;
00672   while (StoppingX <= StoppingY) {
00673         switch (Quadrants) {
00674           case  5: DrawRectangle(cx,     cy + y, cx + x, cy + y, Color); // no break
00675           case  1: DrawRectangle(cx,     cy - y, cx + x, cy - y, Color); break;
00676           case  7: DrawRectangle(cx - x, cy + y, cx,     cy + y, Color); // no break
00677           case  2: DrawRectangle(cx - x, cy - y, cx,     cy - y, Color); break;
00678           case  3: DrawRectangle(cx - x, cy + y, cx,     cy + y, Color); break;
00679           case  4: DrawRectangle(cx,     cy + y, cx + x, cy + y, Color); break;
00680           case  0:
00681           case  6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break;
00682           case  8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break;
00683           case -1: DrawRectangle(cx + x, cy - y, x2,     cy - y, Color); break;
00684           case -2: DrawRectangle(x1,     cy - y, cx - x, cy - y, Color); break;
00685           case -3: DrawRectangle(x1,     cy + y, cx - x, cy + y, Color); break;
00686           case -4: DrawRectangle(cx + x, cy + y, x2,     cy + y, Color); break;
00687           default: ;
00688           }
00689         x++;
00690         StoppingX += TwoBSquare;
00691         EllipseError += XChange;
00692         XChange += TwoBSquare;
00693         if (2 * EllipseError + YChange > 0) {
00694            y--;
00695            StoppingY -= TwoASquare;
00696            EllipseError += YChange;
00697            YChange += TwoASquare;
00698            }
00699         }
00700 }
00701 
00702 void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
00703 {
00704   if (!Intersects(x1, y1, x2, y2))
00705      return;
00706   bool upper    = Type & 0x01;
00707   bool falling  = Type & 0x02;
00708   bool vertical = Type & 0x04;
00709   if (vertical) {
00710      for (int y = y1; y <= y2; y++) {
00711          double c = cos((y - y1) * M_PI / (y2 - y1 + 1));
00712          if (falling)
00713             c = -c;
00714          int x = int((x2 - x1 + 1) * c / 2);
00715          if (upper && !falling || !upper && falling)
00716             DrawRectangle(x1, y, (x1 + x2) / 2 + x, y, Color);
00717          else
00718             DrawRectangle((x1 + x2) / 2 + x, y, x2, y, Color);
00719          }
00720      }
00721   else {
00722      for (int x = x1; x <= x2; x++) {
00723          double c = cos((x - x1) * M_PI / (x2 - x1 + 1));
00724          if (falling)
00725             c = -c;
00726          int y = int((y2 - y1 + 1) * c / 2);
00727          if (upper)
00728             DrawRectangle(x, y1, x, (y1 + y2) / 2 + y, Color);
00729          else
00730             DrawRectangle(x, (y1 + y2) / 2 + y, x, y2, Color);
00731          }
00732      }
00733 }
00734 
00735 const tIndex *cBitmap::Data(int x, int y) const
00736 {
00737   return &bitmap[y * width + x];
00738 }
00739 
00740 void cBitmap::ReduceBpp(const cPalette &Palette)
00741 {
00742   int NewBpp = Palette.Bpp();
00743   if (Bpp() == 4 && NewBpp == 2) {
00744      for (int i = width * height; i--; ) {
00745          tIndex p = bitmap[i];
00746          bitmap[i] = (p >> 2) | ((p & 0x03) != 0);
00747          }
00748      }
00749   else if (Bpp() == 8) {
00750      if (NewBpp == 2) {
00751         for (int i = width * height; i--; ) {
00752             tIndex p = bitmap[i];
00753             bitmap[i] = (p >> 6) | ((p & 0x30) != 0);
00754             }
00755         }
00756      else if (NewBpp == 4) {
00757         for (int i = width * height; i--; ) {
00758             tIndex p = bitmap[i];
00759             bitmap[i] = p >> 4;
00760             }
00761         }
00762      else
00763         return;
00764      }
00765   else
00766      return;
00767   SetBpp(NewBpp);
00768   Replace(Palette);
00769 }
00770 
00771 void cBitmap::ShrinkBpp(int NewBpp)
00772 {
00773   int NumOldColors;
00774   const tColor *Colors = this->Colors(NumOldColors);
00775   if (Colors) {
00776      // Find the most frequently used colors and create a map table:
00777      int Used[MAXNUMCOLORS] = { 0 };
00778      int Map[MAXNUMCOLORS] = { 0 };
00779      for (int i = width * height; i--; )
00780          Used[bitmap[i]]++;
00781      int MaxNewColors = (NewBpp == 4) ? 16 : 4;
00782      cPalette NewPalette(NewBpp);
00783      for (int i = 0; i < MaxNewColors; i++) {
00784          int Max = 0;
00785          int Index = -1;
00786          for (int n = 0; n < NumOldColors; n++) {
00787              if (Used[n] > Max) {
00788                 Max = Used[n];
00789                 Index = n;
00790                 }
00791              }
00792          if (Index >= 0) {
00793             Used[Index] = 0;
00794             Map[Index] = i;
00795             NewPalette.SetColor(i, Colors[Index]);
00796             }
00797          else
00798             break;
00799          }
00800      // Complete the map table for all other colors (will be set to closest match):
00801      for (int n = 0; n < NumOldColors; n++) {
00802          if (Used[n])
00803             Map[n] = NewPalette.Index(Colors[n]);
00804          }
00805      // Do the actual index mapping:
00806      for (int i = width * height; i--; )
00807          bitmap[i] = Map[bitmap[i]];
00808      SetBpp(NewBpp);
00809      Replace(NewPalette);
00810      }
00811 }
00812 
00813 cBitmap *cBitmap::Scaled(double FactorX, double FactorY, bool AntiAlias)
00814 {
00815   // Fixed point scaling code based on www.inversereality.org/files/bitmapscaling.pdf
00816   // by deltener@mindtremors.com
00817   cBitmap *b = new cBitmap(int(round(Width() * FactorX)), int(round(Height() * FactorY)), Bpp(), X0(), Y0());
00818   int RatioX = (Width() << 16) / b->Width();
00819   int RatioY = (Height() << 16) / b->Height();
00820   if (!AntiAlias || FactorX <= 1.0 && FactorY <= 1.0) {
00821      // Downscaling - no anti-aliasing:
00822      b->Replace(*this); // copy palette
00823      tIndex *DestRow = b->bitmap;
00824      int SourceY = 0;
00825      for (int y = 0; y < b->Height(); y++) {
00826          int SourceX = 0;
00827          tIndex *SourceRow = bitmap + (SourceY >> 16) * Width();
00828          tIndex *Dest = DestRow;
00829          for (int x = 0; x < b->Width(); x++) {
00830              *Dest++ = SourceRow[SourceX >> 16];
00831              SourceX += RatioX;
00832              }
00833          SourceY += RatioY;
00834          DestRow += b->Width();
00835          }
00836      }
00837   else {
00838      // Upscaling - anti-aliasing:
00839      b->SetBpp(8);
00840      b->Replace(*this); // copy palette (must be done *after* SetBpp()!)
00841      int SourceY = 0;
00842      for (int y = 0; y < b->Height() - 1; y++) {
00843          int SourceX = 0;
00844          int sy = SourceY >> 16;
00845          uint8_t BlendY = 0xFF - ((SourceY >> 8) & 0xFF);
00846          for (int x = 0; x < b->Width() - 1; x++) {
00847              int sx = SourceX >> 16;
00848              uint8_t BlendX = 0xFF - ((SourceX >> 8) & 0xFF);
00849              tColor c1 = b->Blend(GetColor(sx, sy),     GetColor(sx + 1, sy),     BlendX);
00850              tColor c2 = b->Blend(GetColor(sx, sy + 1), GetColor(sx + 1, sy + 1), BlendX);
00851              tColor c3 = b->Blend(c1, c2, BlendY);
00852              b->DrawPixel(x + X0(), y + Y0(), c3);
00853              SourceX += RatioX;
00854              }
00855          SourceY += RatioY;
00856          }
00857      }
00858   return b;
00859 }
00860 
00861 // --- cRect -----------------------------------------------------------------
00862 
00863 const cRect cRect::Null;
00864 
00865 void cRect::Grow(int Dx, int Dy)
00866 {
00867   point.Shift(-Dx, -Dy);
00868   size.Grow(Dx, Dy);
00869 }
00870 
00871 bool cRect::Contains(const cPoint &Point) const
00872 {
00873   return Left() <= Point.X() &&
00874          Top() <= Point.Y() &&
00875          Right() >= Point.X() &&
00876          Bottom() >= Point.Y();
00877 }
00878 
00879 bool cRect::Contains(const cRect &Rect) const
00880 {
00881   return Left() <= Rect.Left() &&
00882          Top() <= Rect.Top() &&
00883          Right() >= Rect.Right() &&
00884          Bottom() >= Rect.Bottom();
00885 }
00886 
00887 bool cRect::Intersects(const cRect &Rect) const
00888 {
00889   return !(Left() > Rect.Right() ||
00890            Top() > Rect.Bottom() ||
00891            Right() < Rect.Left() ||
00892            Bottom() < Rect.Top());
00893 }
00894 
00895 cRect cRect::Intersected(const cRect &Rect) const
00896 {
00897   cRect r;
00898   if (!IsEmpty() && !Rect.IsEmpty()) {
00899      r.SetLeft(max(Left(), Rect.Left()));
00900      r.SetTop(max(Top(), Rect.Top()));
00901      r.SetRight(min(Right(), Rect.Right()));
00902      r.SetBottom(min(Bottom(), Rect.Bottom()));
00903      }
00904   return r;
00905 }
00906 
00907 void cRect::Combine(const cRect &Rect)
00908 {
00909   if (IsEmpty())
00910      *this = Rect;
00911   if (Rect.IsEmpty())
00912      return;
00913   // must set right/bottom *before* top/left!
00914   SetRight(max(Right(), Rect.Right()));
00915   SetBottom(max(Bottom(), Rect.Bottom()));
00916   SetLeft(min(Left(), Rect.Left()));
00917   SetTop(min(Top(), Rect.Top()));
00918 }
00919 
00920 void cRect::Combine(const cPoint &Point)
00921 {
00922   if (IsEmpty())
00923      Set(Point.X(), Point.Y(), 1, 1);
00924   // must set right/bottom *before* top/left!
00925   SetRight(max(Right(), Point.X()));
00926   SetBottom(max(Bottom(), Point.Y()));
00927   SetLeft(min(Left(), Point.X()));
00928   SetTop(min(Top(), Point.Y()));
00929 }
00930 
00931 // --- cPixmap ---------------------------------------------------------------
00932 
00933 cMutex cPixmap::mutex;
00934 
00935 cPixmap::cPixmap(void)
00936 {
00937   layer = -1;
00938   alpha = ALPHA_OPAQUE;
00939   tile = false;
00940 }
00941 
00942 cPixmap::cPixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort)
00943 {
00944   layer = Layer;
00945   if (layer >= MAXPIXMAPLAYERS) {
00946      layer = MAXPIXMAPLAYERS - 1;
00947      esyslog("ERROR: pixmap layer %d limited to %d", Layer, layer);
00948      }
00949   viewPort = ViewPort;
00950   if (!DrawPort.IsEmpty())
00951      drawPort = DrawPort;
00952   else {
00953      drawPort = viewPort;
00954      drawPort.SetPoint(0, 0);
00955      }
00956   alpha = ALPHA_OPAQUE;
00957   tile = false;
00958 }
00959 
00960 void cPixmap::MarkViewPortDirty(const cRect &Rect)
00961 {
00962   dirtyViewPort.Combine(Rect.Intersected(viewPort));
00963 }
00964 
00965 void cPixmap::MarkViewPortDirty(const cPoint &Point)
00966 {
00967   if (viewPort.Contains(Point))
00968      dirtyViewPort.Combine(Point);
00969 }
00970 
00971 void cPixmap::MarkDrawPortDirty(const cRect &Rect)
00972 {
00973   dirtyDrawPort.Combine(Rect.Intersected(drawPort));
00974   if (tile)
00975      MarkViewPortDirty(viewPort);
00976   else
00977      MarkViewPortDirty(Rect.Shifted(viewPort.Point()));
00978 }
00979 
00980 void cPixmap::MarkDrawPortDirty(const cPoint &Point)
00981 {
00982   if (drawPort.Contains(Point)) {
00983      dirtyDrawPort.Combine(Point);
00984      if (tile)
00985         MarkViewPortDirty(viewPort);
00986      else
00987         MarkViewPortDirty(Point.Shifted(viewPort.Point()));
00988      }
00989 }
00990 
00991 void cPixmap::SetClean(void)
00992 {
00993   dirtyViewPort = dirtyDrawPort = cRect();
00994 }
00995 
00996 void cPixmap::SetLayer(int Layer)
00997 {
00998   Lock();
00999   if (Layer >= MAXPIXMAPLAYERS) {
01000      esyslog("ERROR: pixmap layer %d limited to %d", Layer, MAXPIXMAPLAYERS - 1);
01001      Layer = MAXPIXMAPLAYERS - 1;
01002      }
01003   if (Layer != layer) {
01004      if (Layer > 0 || layer > 0)
01005         MarkViewPortDirty(viewPort);
01006      layer = Layer;
01007      }
01008   Unlock();
01009 }
01010 
01011 void cPixmap::SetAlpha(int Alpha)
01012 {
01013   Lock();
01014   Alpha = constrain(Alpha, ALPHA_TRANSPARENT, ALPHA_OPAQUE);
01015   if (Alpha != alpha) {
01016      MarkViewPortDirty(viewPort);
01017      alpha = Alpha;
01018      }
01019   Unlock();
01020 }
01021 
01022 void cPixmap::SetTile(bool Tile)
01023 {
01024   Lock();
01025   if (Tile != tile) {
01026      if (drawPort.Point() != cPoint(0, 0) || drawPort.Width() < viewPort.Width() || drawPort.Height() < viewPort.Height())
01027         MarkViewPortDirty(viewPort);
01028      tile = Tile;
01029      }
01030   Unlock();
01031 }
01032 
01033 void cPixmap::SetViewPort(const cRect &Rect)
01034 {
01035   Lock();
01036   if (Rect != viewPort) {
01037      if (tile)
01038         MarkViewPortDirty(viewPort);
01039      else
01040         MarkViewPortDirty(drawPort.Shifted(viewPort.Point()));
01041      viewPort = Rect;
01042      if (tile)
01043         MarkViewPortDirty(viewPort);
01044      else
01045         MarkViewPortDirty(drawPort.Shifted(viewPort.Point()));
01046      }
01047   Unlock();
01048 }
01049 
01050 void cPixmap::SetDrawPortPoint(const cPoint &Point, bool Dirty)
01051 {
01052   Lock();
01053   if (Point != drawPort.Point()) {
01054      if (Dirty) {
01055         if (tile)
01056            MarkViewPortDirty(viewPort);
01057         else
01058            MarkViewPortDirty(drawPort.Shifted(viewPort.Point()));
01059         }
01060      drawPort.SetPoint(Point);
01061      if (Dirty && !tile)
01062         MarkViewPortDirty(drawPort.Shifted(viewPort.Point()));
01063      }
01064   Unlock();
01065 }
01066 
01067 // --- cImage ----------------------------------------------------------------
01068 
01069 cImage::cImage(void)
01070 {
01071   data = NULL;
01072 }
01073 
01074 cImage::cImage(const cImage &Image)
01075 {
01076   size = Image.Size();
01077   int l = size.Width() * size.Height() * sizeof(tColor);
01078   data = MALLOC(tColor, l);
01079   memcpy(data, Image.Data(), l);
01080 }
01081 
01082 cImage::cImage(const cSize &Size, const tColor *Data)
01083 {
01084   size = Size;
01085   int l = size.Width() * size.Height() * sizeof(tColor);
01086   data = MALLOC(tColor, l);
01087   if (Data)
01088      memcpy(data, Data, l);
01089 }
01090 
01091 cImage::~cImage()
01092 {
01093   free(data);
01094 }
01095 
01096 void cImage::Clear(void)
01097 {
01098   memset(data, 0x00, Width() * Height() * sizeof(tColor));
01099 }
01100 
01101 void cImage::Fill(tColor Color)
01102 {
01103   for (int i = Width() * Height() - 1; i >= 0; i--)
01104       data[i] = Color;
01105 }
01106 
01107 // --- cPixmapMemory ---------------------------------------------------------
01108 
01109 cPixmapMemory::cPixmapMemory(void)
01110 {
01111   data = NULL;
01112   panning = false;
01113 }
01114 
01115 cPixmapMemory::cPixmapMemory(int Layer, const cRect &ViewPort, const cRect &DrawPort)
01116 :cPixmap(Layer, ViewPort, DrawPort)
01117 {
01118   data = MALLOC(tColor, this->DrawPort().Width() * this->DrawPort().Height());
01119 }
01120 
01121 cPixmapMemory::~cPixmapMemory()
01122 {
01123   free(data);
01124 }
01125 
01126 void cPixmapMemory::Clear(void)
01127 {
01128   Lock();
01129   memset(data, 0x00, DrawPort().Width() * DrawPort().Height() * sizeof(tColor));
01130   MarkDrawPortDirty(DrawPort());
01131   Unlock();
01132 }
01133 
01134 void cPixmapMemory::Fill(tColor Color)
01135 {
01136   Lock();
01137   for (int i = DrawPort().Width() * DrawPort().Height() - 1; i >= 0; i--)
01138       data[i] = Color;
01139   MarkDrawPortDirty(DrawPort());
01140   Unlock();
01141 }
01142 
01143 void cPixmap::DrawPixmap(const cPixmap *Pixmap, const cRect &Dirty)
01144 {
01145   if (Pixmap->Tile() && (Pixmap->DrawPort().Point() != cPoint(0, 0) || Pixmap->DrawPort().Size() < Pixmap->ViewPort().Size())) {
01146      cPoint t0 = Pixmap->DrawPort().Point().Shifted(Pixmap->ViewPort().Point()); // the origin of the draw port in absolute OSD coordinates
01147      // Find the top/leftmost location where the draw port touches the view port:
01148      while (t0.X() > Pixmap->ViewPort().Left())
01149            t0.Shift(-Pixmap->DrawPort().Width(), 0);
01150      while (t0.Y() > Pixmap->ViewPort().Top())
01151            t0.Shift(0, -Pixmap->DrawPort().Height());
01152      cPoint t = t0;;
01153      while (t.Y() <= Pixmap->ViewPort().Bottom()) {
01154            while (t.X() <= Pixmap->ViewPort().Right()) {
01155                  cRect Source = Pixmap->DrawPort(); // assume the entire pixmap needs to be rendered
01156                  Source.Shift(Pixmap->ViewPort().Point()); // Source is now in absolute OSD coordinates
01157                  cPoint Delta = Source.Point() - t;
01158                  Source.SetPoint(t); // Source is now where the pixmap's data shall be drawn
01159                  Source = Source.Intersected(Pixmap->ViewPort()); // Source is now limited to the pixmap's view port
01160                  Source = Source.Intersected(Dirty); // Source is now limited to the actual dirty rectangle
01161                  if (!Source.IsEmpty()) {
01162                     cPoint Dest = Source.Point().Shifted(-ViewPort().Point()); // remember the destination point
01163                     Source.Shift(Delta); // Source is now back at the pixmap's draw port location, still in absolute OSD coordinates
01164                     Source.Shift(-Pixmap->ViewPort().Point()); // Source is now relative to the pixmap's view port again
01165                     Source.Shift(-Pixmap->DrawPort().Point()); // Source is now relative to the pixmap's data
01166                     if (Pixmap->Layer() == 0)
01167                        Copy(Pixmap, Source, Dest); // this is the "background" pixmap
01168                     else
01169                        Render(Pixmap, Source, Dest); // all others are alpha blended over the background
01170                     }
01171                  t.Shift(Pixmap->DrawPort().Width(), 0); // increase one draw port width to the right
01172                  }
01173            t.SetX(t0.X()); // go back to the leftmost position
01174            t.Shift(0, Pixmap->DrawPort().Height()); // increase one draw port height down
01175            }
01176      }
01177   else {
01178      cRect Source = Pixmap->DrawPort(); // assume the entire pixmap needs to be rendered
01179      Source.Shift(Pixmap->ViewPort().Point()); // Source is now in absolute OSD coordinates
01180      Source = Source.Intersected(Pixmap->ViewPort()); // Source is now limited to the pixmap's view port
01181      Source = Source.Intersected(Dirty); // Source is now limited to the actual dirty rectangle
01182      if (!Source.IsEmpty()) {
01183         cPoint Dest = Source.Point().Shifted(-ViewPort().Point()); // remember the destination point
01184         Source.Shift(-Pixmap->ViewPort().Point()); // Source is now relative to the pixmap's draw port again
01185         Source.Shift(-Pixmap->DrawPort().Point()); // Source is now relative to the pixmap's data
01186         if (Pixmap->Layer() == 0)
01187            Copy(Pixmap, Source, Dest); // this is the "background" pixmap
01188         else
01189            Render(Pixmap, Source, Dest); // all others are alpha blended over the background
01190         }
01191      }
01192 }
01193 
01194 void cPixmapMemory::DrawImage(const cPoint &Point, const cImage &Image)
01195 {
01196   Lock();
01197   cRect r = cRect(Point, Image.Size()).Intersected(DrawPort().Size());
01198   if (!r.IsEmpty()) {
01199      int ws = Image.Size().Width();
01200      int wd = DrawPort().Width();
01201      int w = r.Width() * sizeof(tColor);
01202      const tColor *ps = Image.Data();
01203      if (Point.Y() < 0)
01204         ps -= Point.Y() * ws;
01205      if (Point.X() < 0)
01206         ps -= Point.X();
01207      tColor *pd = data + wd * r.Top() + r.Left();
01208      for (int y = r.Height(); y-- > 0; ) {
01209          memcpy(pd, ps, w);
01210          ps += ws;
01211          pd += wd;
01212          }
01213      MarkDrawPortDirty(r);
01214      }
01215   Unlock();
01216 }
01217 
01218 void cPixmapMemory::DrawImage(const cPoint &Point, int ImageHandle)
01219 {
01220   Lock();
01221   if (const cImage *Image = cOsdProvider::GetImageData(ImageHandle))
01222      DrawImage(Point, *Image);
01223   Unlock();
01224 }
01225 
01226 void cPixmapMemory::DrawPixel(const cPoint &Point, tColor Color)
01227 {
01228   Lock();
01229   if (DrawPort().Size().Contains(Point)) {
01230      int p = Point.Y() * DrawPort().Width() + Point.X();
01231      if (Layer() == 0 && !IS_OPAQUE(Color))
01232         data[p] = AlphaBlend(Color, data[p]);
01233      else
01234         data[p] = Color;
01235      MarkDrawPortDirty(Point);
01236      }
01237   Unlock();
01238 }
01239 
01240 void cPixmapMemory::DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool Overlay)
01241 {
01242   Lock();
01243   cRect r = cRect(Point, cSize(Bitmap.Width(), Bitmap.Height())).Intersected(DrawPort().Size());
01244   if (!r.IsEmpty()) {
01245      bool UseColors = ColorFg || ColorBg;
01246      int wd = DrawPort().Width();
01247      tColor *pd = data + wd * r.Top() + r.Left();
01248      for (int y = r.Top(); y <= r.Bottom(); y++) {
01249          tColor *cd = pd;
01250          for (int x = r.Left(); x <= r.Right(); x++) {
01251              tIndex Index = *Bitmap.Data(x - Point.X(), y - Point.Y());
01252              if (Index || !Overlay) {
01253                 if (UseColors)
01254                    *cd = Index ? ColorFg : ColorBg;
01255                 else
01256                    *cd = Bitmap.Color(Index);
01257                 }
01258              cd++;
01259              }
01260          pd += wd;
01261          }
01262      MarkDrawPortDirty(r);
01263      }
01264   Unlock();
01265 }
01266 
01267 void cPixmapMemory::DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
01268 {
01269   Lock();
01270   int x = Point.X();
01271   int y = Point.Y();
01272   int w = Font->Width(s);
01273   int h = Font->Height();
01274   int limit = 0;
01275   int cw = Width ? Width : w;
01276   int ch = Height ? Height : h;
01277   cRect r(x, y, cw, ch);
01278   if (ColorBg != clrTransparent)
01279      DrawRectangle(r, ColorBg);
01280   if (Width || Height) {
01281      limit = x + cw;
01282      if (Width) {
01283         if ((Alignment & taLeft) != 0)
01284            ;
01285         else if ((Alignment & taRight) != 0) {
01286            if (w < Width)
01287               x += Width - w;
01288            }
01289         else { // taCentered
01290            if (w < Width)
01291               x += (Width - w) / 2;
01292            }
01293         }
01294      if (Height) {
01295         if ((Alignment & taTop) != 0)
01296            ;
01297         else if ((Alignment & taBottom) != 0) {
01298            if (h < Height)
01299               y += Height - h;
01300            }
01301         else { // taCentered
01302            if (h < Height)
01303               y += (Height - h) / 2;
01304            }
01305         }
01306      }
01307   Font->DrawText(this, x, y, s, ColorFg, ColorBg, limit);
01308   MarkDrawPortDirty(r);
01309   Unlock();
01310 }
01311 
01312 void cPixmapMemory::DrawRectangle(const cRect &Rect, tColor Color)
01313 {
01314   Lock();
01315   cRect r = Rect.Intersected(DrawPort().Size());
01316   if (!r.IsEmpty()) {
01317      int wd = DrawPort().Width();
01318      int w = r.Width() * sizeof(tColor);
01319      tColor *ps = NULL;
01320      tColor *pd = data + wd * r.Top() + r.Left();
01321      for (int y = r.Height(); y-- > 0; ) {
01322          if (ps)
01323             memcpy(pd, ps, w); // all other lines are copied fast from the first one
01324          else {
01325             // explicitly fill the first line:
01326             tColor *cd = ps = pd;
01327             for (int x = r.Width(); x-- > 0; ) {
01328                 *cd = Color;
01329                 cd++;
01330                 }
01331             }
01332          pd += wd;
01333          }
01334      MarkDrawPortDirty(r);
01335      }
01336   Unlock();
01337 }
01338 
01339 void cPixmapMemory::DrawEllipse(const cRect &Rect, tColor Color, int Quadrants)
01340 {
01341 //TODO use anti-aliasing?
01342 //TODO fix alignment
01343   Lock();
01344   // Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF
01345   int x1 = Rect.Left();
01346   int y1 = Rect.Top();
01347   int x2 = Rect.Right();
01348   int y2 = Rect.Bottom();
01349   int rx = x2 - x1;
01350   int ry = y2 - y1;
01351   int cx = (x1 + x2) / 2;
01352   int cy = (y1 + y2) / 2;
01353   switch (abs(Quadrants)) {
01354     case 0: rx /= 2; ry /= 2; break;
01355     case 1: cx = x1; cy = y2; break;
01356     case 2: cx = x2; cy = y2; break;
01357     case 3: cx = x2; cy = y1; break;
01358     case 4: cx = x1; cy = y1; break;
01359     case 5: cx = x1;          ry /= 2; break;
01360     case 6:          cy = y2; rx /= 2; break;
01361     case 7: cx = x2;          ry /= 2; break;
01362     case 8:          cy = y1; rx /= 2; break;
01363     default: ;
01364     }
01365   int TwoASquare = 2 * rx * rx;
01366   int TwoBSquare = 2 * ry * ry;
01367   int x = rx;
01368   int y = 0;
01369   int XChange = ry * ry * (1 - 2 * rx);
01370   int YChange = rx * rx;
01371   int EllipseError = 0;
01372   int StoppingX = TwoBSquare * rx;
01373   int StoppingY = 0;
01374   while (StoppingX >= StoppingY) {
01375         switch (Quadrants) {
01376           case  5: DrawRectangle(cRect(cx,     cy + y, x + 1, 1), Color); // no break
01377           case  1: DrawRectangle(cRect(cx,     cy - y, x + 1, 1), Color); break;
01378           case  7: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); // no break
01379           case  2: DrawRectangle(cRect(cx - x, cy - y, x + 1, 1), Color); break;
01380           case  3: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); break;
01381           case  4: DrawRectangle(cRect(cx,     cy + y, x + 1, 1), Color); break;
01382           case  0:
01383           case  6: DrawRectangle(cRect(cx - x, cy - y, 2 * x + 1,       1), Color); if (Quadrants == 6) break;
01384           case  8: DrawRectangle(cRect(cx - x, cy + y, 2 * x + 1,       1), Color); break;
01385           case -1: DrawRectangle(cRect(cx + x, cy - y, x2 - x + 1,      1), Color); break;
01386           case -2: DrawRectangle(cRect(x1,     cy - y, cx - x - x1 + 1, 1), Color); break;
01387           case -3: DrawRectangle(cRect(x1,     cy + y, cx - x - x1 + 1, 1), Color); break;
01388           case -4: DrawRectangle(cRect(cx + x, cy + y, x2 - x + 1,      1), Color); break;
01389           default: ;
01390           }
01391         y++;
01392         StoppingY += TwoASquare;
01393         EllipseError += YChange;
01394         YChange += TwoASquare;
01395         if (2 * EllipseError + XChange > 0) {
01396            x--;
01397            StoppingX -= TwoBSquare;
01398            EllipseError += XChange;
01399            XChange += TwoBSquare;
01400            }
01401         }
01402   x = 0;
01403   y = ry;
01404   XChange = ry * ry;
01405   YChange = rx * rx * (1 - 2 * ry);
01406   EllipseError = 0;
01407   StoppingX = 0;
01408   StoppingY = TwoASquare * ry;
01409   while (StoppingX <= StoppingY) {
01410         switch (Quadrants) {
01411           case  5: DrawRectangle(cRect(cx,     cy + y, x + 1, 1), Color); // no break
01412           case  1: DrawRectangle(cRect(cx,     cy - y, x + 1, 1), Color); break;
01413           case  7: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); // no break
01414           case  2: DrawRectangle(cRect(cx - x, cy - y, x + 1, 1), Color); break;
01415           case  3: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); break;
01416           case  4: DrawRectangle(cRect(cx,     cy + y, x + 1, 1), Color); break;
01417           case  0:
01418           case  6: DrawRectangle(cRect(cx - x, cy - y, 2 * x + 1,       1), Color); if (Quadrants == 6) break;
01419           case  8: DrawRectangle(cRect(cx - x, cy + y, 2 * x + 1,       1), Color); break;
01420           case -1: DrawRectangle(cRect(cx + x, cy - y, x2 - x + 1,      1), Color); break;
01421           case -2: DrawRectangle(cRect(x1,     cy - y, cx - x - x1 + 1, 1), Color); break;
01422           case -3: DrawRectangle(cRect(x1,     cy + y, cx - x - x1 + 1, 1), Color); break;
01423           case -4: DrawRectangle(cRect(cx + x, cy + y, x2 - x + 1,      1), Color); break;
01424           default: ;
01425           }
01426         x++;
01427         StoppingX += TwoBSquare;
01428         EllipseError += XChange;
01429         XChange += TwoBSquare;
01430         if (2 * EllipseError + YChange > 0) {
01431            y--;
01432            StoppingY -= TwoASquare;
01433            EllipseError += YChange;
01434            YChange += TwoASquare;
01435            }
01436         }
01437   MarkDrawPortDirty(Rect);
01438   Unlock();
01439 }
01440 
01441 void cPixmapMemory::DrawSlope(const cRect &Rect, tColor Color, int Type)
01442 {
01443   //TODO anti-aliasing?
01444   //TODO also simplify cBitmap::DrawSlope()
01445   Lock();
01446   bool upper    = Type & 0x01;
01447   bool falling  = Type & 0x02;
01448   bool vertical = Type & 0x04;
01449   int x1 = Rect.Left();
01450   int y1 = Rect.Top();
01451   int x2 = Rect.Right();
01452   int y2 = Rect.Bottom();
01453   int w  = Rect.Width();
01454   int h  = Rect.Height();
01455   if (vertical) {
01456      for (int y = y1; y <= y2; y++) {
01457          double c = cos((y - y1) * M_PI / h);
01458          if (falling)
01459             c = -c;
01460          int x = (x1 + x2) / 2 + int(w * c / 2);
01461          if (upper && !falling || !upper && falling)
01462             DrawRectangle(cRect(x1, y, x - x1 + 1, 1), Color);
01463          else
01464             DrawRectangle(cRect(x, y, x2 - x + 1, 1), Color);
01465          }
01466      }
01467   else {
01468      for (int x = x1; x <= x2; x++) {
01469          double c = cos((x - x1) * M_PI / w);
01470          if (falling)
01471             c = -c;
01472          int y = (y1 + y2) / 2 + int(h * c / 2);
01473          if (upper)
01474             DrawRectangle(cRect(x, y1, 1, y - y1 + 1), Color);
01475          else
01476             DrawRectangle(cRect(x, y, 1, y2 - y + 1), Color);
01477          }
01478      }
01479   MarkDrawPortDirty(Rect);
01480   Unlock();
01481 }
01482 
01483 void cPixmapMemory::Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)
01484 {
01485   Lock();
01486   if (Pixmap->Alpha() != ALPHA_TRANSPARENT) {
01487      if (const cPixmapMemory *pm = dynamic_cast<const cPixmapMemory *>(Pixmap)) {
01488         cRect s = Source.Intersected(Pixmap->DrawPort().Size());
01489         if (!s.IsEmpty()) {
01490            cPoint v = Dest - Source.Point();
01491            cRect d = s.Shifted(v).Intersected(DrawPort().Size());
01492            if (!d.IsEmpty()) {
01493               s = d.Shifted(-v);
01494               int a = pm->Alpha();
01495               int ws = pm->DrawPort().Width();
01496               int wd = DrawPort().Width();
01497               const tColor *ps = pm->data + ws * s.Top() + s.Left();
01498               tColor *pd = data + wd * d.Top() + d.Left();
01499               for (int y = d.Height(); y-- > 0; ) {
01500                   const tColor *cs = ps;
01501                   tColor *cd = pd;
01502                   for (int x = d.Width(); x-- > 0; ) {
01503                       *cd = AlphaBlend(*cs, *cd, a);
01504                       cs++;
01505                       cd++;
01506                       }
01507                   ps += ws;
01508                   pd += wd;
01509                   }
01510               MarkDrawPortDirty(d);
01511               }
01512            }
01513         }
01514      }
01515   Unlock();
01516 }
01517 
01518 void cPixmapMemory::Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)
01519 {
01520   Lock();
01521   if (const cPixmapMemory *pm = dynamic_cast<const cPixmapMemory *>(Pixmap)) {
01522      cRect s = Source.Intersected(pm->DrawPort().Size());
01523      if (!s.IsEmpty()) {
01524         cPoint v = Dest - Source.Point();
01525         cRect d = s.Shifted(v).Intersected(DrawPort().Size());
01526         if (!d.IsEmpty()) {
01527            s = d.Shifted(-v);
01528            int ws = pm->DrawPort().Width();
01529            int wd = DrawPort().Width();
01530            int w = d.Width() * sizeof(tColor);
01531            const tColor *ps = pm->data + ws * s.Top() + s.Left();
01532            tColor *pd = data + wd * d.Top() + d.Left();
01533            for (int y = d.Height(); y-- > 0; ) {
01534                memcpy(pd, ps, w);
01535                ps += ws;
01536                pd += wd;
01537                }
01538            MarkDrawPortDirty(d);
01539            }
01540         }
01541      }
01542   Unlock();
01543 }
01544 
01545 void cPixmapMemory::Scroll(const cPoint &Dest, const cRect &Source)
01546 {
01547   Lock();
01548   cRect s;
01549   if (&Source == &cRect::Null)
01550      s = DrawPort().Shifted(-DrawPort().Point());
01551   else
01552      s = Source.Intersected(DrawPort().Size());
01553   if (!s.IsEmpty()) {
01554      cPoint v = Dest - Source.Point();
01555      cRect d = s.Shifted(v).Intersected(DrawPort().Size());
01556      if (!d.IsEmpty()) {
01557         s = d.Shifted(-v);
01558         if (d.Point() != s.Point()) {
01559            int ws = DrawPort().Width();
01560            int wd = ws;
01561            int w = d.Width() * sizeof(tColor);
01562            const tColor *ps = data + ws * s.Top() + s.Left();
01563            tColor *pd = data + wd * d.Top() + d.Left();
01564            for (int y = d.Height(); y-- > 0; ) {
01565                memmove(pd, ps, w); // source and destination might overlap!
01566                ps += ws;
01567                pd += wd;
01568                }
01569            if (panning)
01570               SetDrawPortPoint(DrawPort().Point().Shifted(s.Point() - d.Point()), false);
01571            else
01572               MarkDrawPortDirty(d);
01573            }
01574         }
01575      }
01576   Unlock();
01577 }
01578 
01579 void cPixmapMemory::Pan(const cPoint &Dest, const cRect &Source)
01580 {
01581   Lock();
01582   panning = true;
01583   Scroll(Dest, Source);
01584   panning = false;
01585   Unlock();
01586 }
01587 
01588 // --- cOsd ------------------------------------------------------------------
01589 
01590 static const char *OsdErrorTexts[] = {
01591   "ok",
01592   "too many areas",
01593   "too many colors",
01594   "bpp not supported",
01595   "areas overlap",
01596   "wrong alignment",
01597   "out of memory",
01598   "wrong area size",
01599   "unknown",
01600   };
01601 
01602 int cOsd::osdLeft = 0;
01603 int cOsd::osdTop = 0;
01604 int cOsd::osdWidth = 0;
01605 int cOsd::osdHeight = 0;
01606 cVector<cOsd *> cOsd::Osds;
01607 cMutex cOsd::mutex;
01608 
01609 cOsd::cOsd(int Left, int Top, uint Level)
01610 {
01611   cMutexLock MutexLock(&mutex);
01612   isTrueColor = false;
01613   savedBitmap = NULL;
01614   numBitmaps = 0;
01615   savedPixmap = NULL;
01616   numPixmaps = 0;
01617   left = Left;
01618   top = Top;
01619   width = height = 0;
01620   level = Level;
01621   active = false;
01622   for (int i = 0; i < Osds.Size(); i++) {
01623       if (Osds[i]->level > level) {
01624          Osds.Insert(this, i);
01625          return;
01626          }
01627       }
01628   Osds.Append(this);
01629 }
01630 
01631 cOsd::~cOsd()
01632 {
01633   cMutexLock MutexLock(&mutex);
01634   for (int i = 0; i < numBitmaps; i++)
01635       delete bitmaps[i];
01636   delete savedBitmap;
01637   delete savedPixmap;
01638   for (int i = 0; i < numPixmaps; i++)
01639       delete pixmaps[i];
01640   for (int i = 0; i < Osds.Size(); i++) {
01641       if (Osds[i] == this) {
01642          Osds.Remove(i);
01643          if (Osds.Size())
01644             Osds[0]->SetActive(true);
01645          break;
01646          }
01647       }
01648 }
01649 
01650 void cOsd::SetOsdPosition(int Left, int Top, int Width, int Height)
01651 {
01652   osdLeft = Left;
01653   osdTop = Top;
01654   osdWidth = constrain(Width, MINOSDWIDTH, MAXOSDWIDTH);
01655   osdHeight = constrain(Height, MINOSDHEIGHT, MAXOSDHEIGHT);
01656 }
01657 
01658 void cOsd::SetAntiAliasGranularity(uint FixedColors, uint BlendColors)
01659 {
01660   if (isTrueColor)
01661      return;
01662   for (int i = 0; i < numBitmaps; i++)
01663       bitmaps[i]->SetAntiAliasGranularity(FixedColors, BlendColors);
01664 }
01665 
01666 cBitmap *cOsd::GetBitmap(int Area)
01667 {
01668   if (isTrueColor)
01669      Area = 0; // returns the dummy bitmap
01670   return Area < numBitmaps ? bitmaps[Area] : NULL;
01671 }
01672 
01673 cPixmap *cOsd::CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort)
01674 {
01675   if (isTrueColor) {
01676      LOCK_PIXMAPS;
01677      cPixmap *Pixmap = new cPixmapMemory(Layer, ViewPort, DrawPort);
01678      if (AddPixmap(Pixmap))
01679         return Pixmap;
01680      delete Pixmap;
01681      }
01682   return NULL;
01683 }
01684 
01685 void cOsd::DestroyPixmap(cPixmap *Pixmap)
01686 {
01687   if (isTrueColor) {
01688      LOCK_PIXMAPS;
01689      for (int i = 1; i < numPixmaps; i++) { // begin at 1 - don't let the background pixmap be destroyed!
01690          if (pixmaps[i] == Pixmap) {
01691             pixmaps[0]->MarkViewPortDirty(Pixmap->ViewPort());
01692             delete Pixmap;
01693             while (i < numPixmaps - 1) {
01694                   pixmaps[i] = pixmaps[i + 1];
01695                   i++;
01696                   }
01697             numPixmaps--;
01698             return;
01699             }
01700          }
01701      esyslog("ERROR: attempt to destroy an unregistered pixmap");
01702      }
01703 }
01704 
01705 cPixmap *cOsd::AddPixmap(cPixmap *Pixmap)
01706 {
01707   if (Pixmap) {
01708      LOCK_PIXMAPS;
01709      if (numPixmaps < MAXOSDPIXMAPS)
01710         return pixmaps[numPixmaps++] = Pixmap;
01711      else
01712         esyslog("ERROR: too many OSD pixmaps requested (maximum is %d)", MAXOSDPIXMAPS);
01713      }
01714   return NULL;
01715 }
01716 
01717 cPixmapMemory *cOsd::RenderPixmaps(void)
01718 {
01719   cPixmapMemory *Pixmap = NULL;
01720   if (isTrueColor) {
01721      LOCK_PIXMAPS;
01722      // Collect overlapping dirty rectangles:
01723      cRect d;
01724      for (int i = 0; i < numPixmaps; i++) {
01725          cPixmap *pm = pixmaps[i];
01726          if (!pm->DirtyViewPort().IsEmpty()) {
01727             if (d.IsEmpty() || d.Intersects(pm->DirtyViewPort())) {
01728                d.Combine(pm->DirtyViewPort());
01729                pm->SetClean();
01730                }
01731             }
01732          }
01733      if (!d.IsEmpty()) {
01734 //#define DebugDirty
01735 #ifdef DebugDirty
01736         static cRect OldDirty;
01737         cRect NewDirty = d;
01738         d.Combine(OldDirty);
01739         OldDirty = NewDirty;
01740 #endif
01741         Pixmap = new cPixmapMemory(0, d);
01742         Pixmap->Clear();
01743         // Render the individual pixmaps into the resulting pixmap:
01744         for (int Layer = 0; Layer < MAXPIXMAPLAYERS; Layer++) {
01745             for (int i = 0; i < numPixmaps; i++) {
01746                 cPixmap *pm = pixmaps[i];
01747                 if (pm->Layer() == Layer)
01748                    Pixmap->DrawPixmap(pm, d);
01749                 }
01750             }
01751 #ifdef DebugDirty
01752         cPixmapMemory DirtyIndicator(7, NewDirty);
01753         static tColor DirtyIndicatorColors[] = { 0x7FFFFF00, 0x7F00FFFF };
01754         static int DirtyIndicatorIndex = 0;
01755         DirtyIndicator.Fill(DirtyIndicatorColors[DirtyIndicatorIndex]);
01756         DirtyIndicatorIndex = 1 - DirtyIndicatorIndex;
01757         Pixmap->Render(&DirtyIndicator, DirtyIndicator.DrawPort(), DirtyIndicator.ViewPort().Point().Shifted(-Pixmap->ViewPort().Point()));
01758 #endif
01759         }
01760      }
01761   return Pixmap;
01762 }
01763 
01764 eOsdError cOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
01765 {
01766   if (NumAreas > MAXOSDAREAS)
01767      return oeTooManyAreas;
01768   eOsdError Result = oeOk;
01769   for (int i = 0; i < NumAreas; i++) {
01770       if (Areas[i].x1 > Areas[i].x2 || Areas[i].y1 > Areas[i].y2 || Areas[i].x1 < 0 || Areas[i].y1 < 0)
01771          return oeWrongAlignment;
01772       for (int j = i + 1; j < NumAreas; j++) {
01773           if (Areas[i].Intersects(Areas[j])) {
01774              Result = oeAreasOverlap;
01775              break;
01776              }
01777           }
01778       if (Areas[i].bpp == 32) {
01779          if (NumAreas > 1)
01780             return oeTooManyAreas;
01781          }
01782       }
01783   return Result;
01784 }
01785 
01786 eOsdError cOsd::SetAreas(const tArea *Areas, int NumAreas)
01787 {
01788   eOsdError Result = CanHandleAreas(Areas, NumAreas);
01789   if (Result == oeOk) {
01790      while (numBitmaps)
01791            delete bitmaps[--numBitmaps];
01792      width = height = 0;
01793      isTrueColor = NumAreas == 1 && Areas[0].bpp == 32;
01794      if (isTrueColor) {
01795         width = Areas[0].x2 - Areas[0].x1 + 1;
01796         height = Areas[0].y2 - Areas[0].y1 + 1;
01797         cPixmap *Pixmap = CreatePixmap(0, cRect(Areas[0].x1, Areas[0].y1, width, height));
01798         Pixmap->Clear();
01799         bitmaps[numBitmaps++] = new cBitmap(10, 10, 8); // dummy bitmap for GetBitmap()
01800         }
01801      else {
01802         for (int i = 0; i < NumAreas; i++) {
01803             bitmaps[numBitmaps++] = new cBitmap(Areas[i].Width(), Areas[i].Height(), Areas[i].bpp, Areas[i].x1, Areas[i].y1);
01804             width = max(width, Areas[i].x2 + 1);
01805             height = max(height, Areas[i].y2 + 1);
01806             }
01807         }
01808      }
01809   else
01810      esyslog("ERROR: cOsd::SetAreas returned %d (%s)", Result, Result < oeUnknown ? OsdErrorTexts[Result] : OsdErrorTexts[oeUnknown]);
01811   return Result;
01812 }
01813 
01814 void cOsd::SaveRegion(int x1, int y1, int x2, int y2)
01815 {
01816   if (isTrueColor) {
01817      delete savedPixmap;
01818      cRect r(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
01819      savedPixmap = new cPixmapMemory(0, r);
01820      savedPixmap->Copy(pixmaps[0], r, cPoint(0, 0));
01821      }
01822   else {
01823      delete savedBitmap;
01824      savedBitmap = new cBitmap(x2 - x1 + 1, y2 - y1 + 1, 8, x1, y1);
01825      for (int i = 0; i < numBitmaps; i++)
01826          savedBitmap->DrawBitmap(bitmaps[i]->X0(), bitmaps[i]->Y0(), *bitmaps[i]);
01827      }
01828 }
01829 
01830 void cOsd::RestoreRegion(void)
01831 {
01832   if (isTrueColor) {
01833      if (savedPixmap) {
01834         pixmaps[0]->Copy(savedPixmap, savedPixmap->DrawPort(), savedPixmap->ViewPort().Point());
01835         delete savedPixmap;
01836         savedPixmap = NULL;
01837         }
01838      }
01839   else {
01840      if (savedBitmap) {
01841         DrawBitmap(savedBitmap->X0(), savedBitmap->Y0(), *savedBitmap);
01842         delete savedBitmap;
01843         savedBitmap = NULL;
01844         }
01845      }
01846 }
01847 
01848 eOsdError cOsd::SetPalette(const cPalette &Palette, int Area)
01849 {
01850   if (isTrueColor)
01851      return oeOk;
01852   if (Area < numBitmaps) {
01853      bitmaps[Area]->Take(Palette);
01854      return oeOk;
01855      }
01856   return oeUnknown;
01857 }
01858 
01859 void cOsd::DrawImage(const cPoint &Point, const cImage &Image)
01860 {
01861   if (isTrueColor)
01862      pixmaps[0]->DrawImage(Point, Image);
01863 }
01864 
01865 void cOsd::DrawImage(const cPoint &Point, int ImageHandle)
01866 {
01867   if (isTrueColor)
01868      pixmaps[0]->DrawImage(Point, ImageHandle);
01869 }
01870 
01871 void cOsd::DrawPixel(int x, int y, tColor Color)
01872 {
01873   if (isTrueColor)
01874      pixmaps[0]->DrawPixel(cPoint(x, y), Color);
01875   else {
01876      for (int i = 0; i < numBitmaps; i++)
01877          bitmaps[i]->DrawPixel(x, y, Color);
01878      }
01879 }
01880 
01881 void cOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
01882 {
01883   if (isTrueColor)
01884      pixmaps[0]->DrawBitmap(cPoint(x, y), Bitmap, ColorFg, ColorBg, Overlay);
01885   else {
01886      for (int i = 0; i < numBitmaps; i++)
01887          bitmaps[i]->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg, ReplacePalette, Overlay);
01888      }
01889 }
01890 
01891 void cOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
01892 {
01893   if (isTrueColor)
01894      pixmaps[0]->DrawText(cPoint(x, y), s, ColorFg, ColorBg, Font, Width, Height, Alignment);
01895   else {
01896      for (int i = 0; i < numBitmaps; i++)
01897          bitmaps[i]->DrawText(x, y, s, ColorFg, ColorBg, Font, Width, Height, Alignment);
01898      }
01899 }
01900 
01901 void cOsd::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
01902 {
01903   if (isTrueColor)
01904      pixmaps[0]->DrawRectangle(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color);
01905   else {
01906      for (int i = 0; i < numBitmaps; i++)
01907          bitmaps[i]->DrawRectangle(x1, y1, x2, y2, Color);
01908      }
01909 }
01910 
01911 void cOsd::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
01912 {
01913   if (isTrueColor)
01914      pixmaps[0]->DrawEllipse(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color, Quadrants);
01915   else {
01916      for (int i = 0; i < numBitmaps; i++)
01917          bitmaps[i]->DrawEllipse(x1, y1, x2, y2, Color, Quadrants);
01918      }
01919 }
01920 
01921 void cOsd::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
01922 {
01923   if (isTrueColor)
01924      pixmaps[0]->DrawSlope(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color, Type);
01925   else {
01926      for (int i = 0; i < numBitmaps; i++)
01927          bitmaps[i]->DrawSlope(x1, y1, x2, y2, Color, Type);
01928      }
01929 }
01930 
01931 void cOsd::Flush(void)
01932 {
01933 }
01934 
01935 // --- cOsdProvider ----------------------------------------------------------
01936 
01937 cOsdProvider *cOsdProvider::osdProvider = NULL;
01938 int cOsdProvider::oldWidth = 0;
01939 int cOsdProvider::oldHeight = 0;
01940 double cOsdProvider::oldAspect = 1.0;
01941 cImage *cOsdProvider::images[MAXOSDIMAGES] = { NULL };
01942 
01943 cOsdProvider::cOsdProvider(void)
01944 {
01945   delete osdProvider;
01946   osdProvider = this;
01947 }
01948 
01949 cOsdProvider::~cOsdProvider()
01950 {
01951   osdProvider = NULL;
01952 }
01953 
01954 cOsd *cOsdProvider::NewOsd(int Left, int Top, uint Level)
01955 {
01956   cMutexLock MutexLock(&cOsd::mutex);
01957   if (Level == OSD_LEVEL_DEFAULT && cOsd::IsOpen())
01958      esyslog("ERROR: attempt to open OSD while it is already open - using dummy OSD!");
01959   else if (osdProvider) {
01960      cOsd *ActiveOsd = cOsd::Osds.Size() ? cOsd::Osds[0] : NULL;
01961      cOsd *Osd = osdProvider->CreateOsd(Left, Top, Level);
01962      if (Osd == cOsd::Osds[0]) {
01963         if (ActiveOsd)
01964            ActiveOsd->SetActive(false);
01965         Osd->SetActive(true);
01966         }
01967      return Osd;
01968      }
01969   else
01970      esyslog("ERROR: no OSD provider available - using dummy OSD!");
01971   return new cOsd(Left, Top, 999); // create a dummy cOsd, so that access won't result in a segfault
01972 }
01973 
01974 void cOsdProvider::UpdateOsdSize(bool Force)
01975 {
01976   int Width;
01977   int Height;
01978   double Aspect;
01979   cDevice::PrimaryDevice()->GetOsdSize(Width, Height, Aspect);
01980   if (Width != oldWidth || Height != oldHeight || !DoubleEqual(Aspect, oldAspect) || Force) {
01981      Setup.OSDLeft = int(round(Width * Setup.OSDLeftP));
01982      Setup.OSDTop = int(round(Height * Setup.OSDTopP));
01983      Setup.OSDWidth = int(round(Width * Setup.OSDWidthP)) & ~0x07; // OSD width must be a multiple of 8
01984      Setup.OSDHeight = int(round(Height * Setup.OSDHeightP));
01985      Setup.OSDAspect = Aspect;
01986      Setup.FontOsdSize = int(round(Height * Setup.FontOsdSizeP));
01987      Setup.FontFixSize = int(round(Height * Setup.FontFixSizeP));
01988      Setup.FontSmlSize = int(round(Height * Setup.FontSmlSizeP));
01989      cFont::SetFont(fontOsd, Setup.FontOsd, Setup.FontOsdSize);
01990      cFont::SetFont(fontFix, Setup.FontFix, Setup.FontFixSize);
01991      cFont::SetFont(fontSml, Setup.FontSml, Setup.FontSmlSize);
01992      oldWidth = Width;
01993      oldHeight = Height;
01994      oldAspect = Aspect;
01995      dsyslog("OSD size changed to %dx%d @ %g", Width, Height, Aspect);
01996      }
01997 }
01998 
01999 bool cOsdProvider::SupportsTrueColor(void)
02000 {
02001   if (osdProvider) {
02002      return osdProvider->ProvidesTrueColor();
02003      }
02004   else
02005      esyslog("ERROR: no OSD provider available in call to SupportsTrueColor()");
02006   return false;
02007 }
02008 
02009 int cOsdProvider::StoreImageData(const cImage &Image)
02010 {
02011   LOCK_PIXMAPS;
02012   for (int i = 1; i < MAXOSDIMAGES; i++) {
02013       if (!images[i]) {
02014          images[i] = new cImage(Image);
02015          return i;
02016          }
02017       }
02018   return 0;
02019 }
02020 
02021 void cOsdProvider::DropImageData(int ImageHandle)
02022 {
02023   LOCK_PIXMAPS;
02024   if (0 < ImageHandle && ImageHandle < MAXOSDIMAGES) {
02025      delete images[ImageHandle];
02026      images[ImageHandle] = NULL;
02027      }
02028 }
02029 
02030 const cImage *cOsdProvider::GetImageData(int ImageHandle)
02031 {
02032   LOCK_PIXMAPS;
02033   if (0 < ImageHandle && ImageHandle < MAXOSDIMAGES)
02034      return images[ImageHandle];
02035   return NULL;
02036 }
02037 
02038 int cOsdProvider::StoreImage(const cImage &Image)
02039 {
02040   if (osdProvider)
02041      return osdProvider->StoreImageData(Image);
02042   return -1;
02043 }
02044 
02045 void cOsdProvider::DropImage(int ImageHandle)
02046 {
02047   if (osdProvider)
02048      osdProvider->DropImageData(ImageHandle);
02049 }
02050 
02051 void cOsdProvider::Shutdown(void)
02052 {
02053   delete osdProvider;
02054   osdProvider = NULL;
02055 }
02056 
02057 // --- cTextScroller ---------------------------------------------------------
02058 
02059 cTextScroller::cTextScroller(void)
02060 {
02061   osd = NULL;
02062   left = top = width = height = 0;
02063   font = NULL;
02064   colorFg = 0;
02065   colorBg = 0;
02066   offset = 0;
02067   shown = 0;
02068 }
02069 
02070 cTextScroller::cTextScroller(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg)
02071 {
02072   Set(Osd, Left, Top, Width, Height, Text, Font, ColorFg, ColorBg);
02073 }
02074 
02075 void cTextScroller::Set(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg)
02076 {
02077   osd = Osd;
02078   left = Left;
02079   top = Top;
02080   width = Width;
02081   height = Height;
02082   font = Font;
02083   colorFg = ColorFg;
02084   colorBg = ColorBg;
02085   offset = 0;
02086   textWrapper.Set(Text, Font, Width);
02087   shown = min(Total(), height / font->Height());
02088   height = shown * font->Height(); // sets height to the actually used height, which may be less than Height
02089   DrawText();
02090 }
02091 
02092 void cTextScroller::Reset(void)
02093 {
02094   osd = NULL; // just makes sure it won't draw anything
02095 }
02096 
02097 void cTextScroller::DrawText(void)
02098 {
02099   if (osd) {
02100      for (int i = 0; i < shown; i++)
02101          osd->DrawText(left, top + i * font->Height(), textWrapper.GetLine(offset + i), colorFg, colorBg, font, width);
02102      }
02103 }
02104 
02105 void cTextScroller::Scroll(bool Up, bool Page)
02106 {
02107   if (Up) {
02108      if (CanScrollUp()) {
02109         offset -= Page ? shown : 1;
02110         if (offset < 0)
02111            offset = 0;
02112         DrawText();
02113         }
02114      }
02115   else {
02116      if (CanScrollDown()) {
02117         offset += Page ? shown : 1;
02118         if (offset + shown > Total())
02119            offset = Total() - shown;
02120         DrawText();
02121         }
02122      }
02123 }