vdr
1.7.27
|
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 }