vdr  1.7.31
osd.c
Go to the documentation of this file.
1 /*
2  * osd.c: Abstract On Screen Display layer
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: osd.c 2.32 2012/06/09 14:37:24 kls Exp $
8  */
9 
10 #include "osd.h"
11 #include <math.h>
12 #include <stdlib.h>
13 #include <sys/ioctl.h>
14 #include <sys/stat.h>
15 #include <sys/unistd.h>
16 #include "device.h"
17 #include "tools.h"
18 
19 tColor HsvToColor(double H, double S, double V)
20 {
21  if (S > 0) {
22  H /= 60;
23  int i = floor(H);
24  double f = H - i;
25  double p = V * (1 - S);
26  double q = V * (1 - S * f);
27  double t = V * (1 - S * (1 - f));
28  switch (i) {
29  case 0: return RgbToColor(V, t, p);
30  case 1: return RgbToColor(q, V, p);
31  case 2: return RgbToColor(p, V, t);
32  case 3: return RgbToColor(p, q, V);
33  case 4: return RgbToColor(t, p, V);
34  default: return RgbToColor(V, p, q);
35  }
36  }
37  else { // greyscale
38  uint8_t n = V * 0xFF;
39  return RgbToColor(n, n, n);
40  }
41 }
42 
43 tColor RgbShade(tColor Color, double Factor)
44 {
45  double f = fabs(constrain(Factor, -1.0, 1.0));
46  double w = Factor > 0 ? f * 0xFF : 0;
47  return (Color & 0xFF000000) |
48  (min(0xFF, int((1 - f) * ((Color >> 16) & 0xFF) + w + 0.5)) << 16) |
49  (min(0xFF, int((1 - f) * ((Color >> 8) & 0xFF) + w + 0.5)) << 8) |
50  (min(0xFF, int((1 - f) * ( Color & 0xFF) + w + 0.5)) );
51 }
52 
53 #define USE_ALPHA_LUT
54 #ifdef USE_ALPHA_LUT
55 // Alpha blending with lookup table (by Reinhard Nissl <rnissl@gmx.de>)
56 // A little slower (138 %) on fast machines than the implementation below and faster
57 // on slow machines (79 %), but requires some 318KB of RAM for the lookup table.
58 static uint16_t AlphaLutFactors[255][256][2];
59 static uint8_t AlphaLutAlpha[255][256];
60 
62 public:
64  {
65  for (int alphaA = 0; alphaA < 255; alphaA++) {
66  int range = (alphaA == 255 ? 255 : 254);
67  for (int alphaB = 0; alphaB < 256; alphaB++) {
68  int alphaO_x_range = 255 * alphaA + alphaB * (range - alphaA);
69  if (!alphaO_x_range)
70  alphaO_x_range++;
71  int factorA = (256 * 255 * alphaA + alphaO_x_range / 2) / alphaO_x_range;
72  int factorB = (256 * alphaB * (range - alphaA) + alphaO_x_range / 2) / alphaO_x_range;
73  AlphaLutFactors[alphaA][alphaB][0] = factorA;
74  AlphaLutFactors[alphaA][alphaB][1] = factorB;
75  AlphaLutAlpha[alphaA][alphaB] = alphaO_x_range / range;
76  }
77  }
78  }
79  } InitAlphaLut;
80 
81 tColor AlphaBlend(tColor ColorFg, tColor ColorBg, uint8_t AlphaLayer)
82 {
83  tColor Alpha = (ColorFg & 0xFF000000) >> 24;
84  Alpha *= AlphaLayer;
85  Alpha >>= 8;
86  uint16_t *lut = &AlphaLutFactors[Alpha][(ColorBg & 0xFF000000) >> 24][0];
87  return (tColor)((AlphaLutAlpha[Alpha][(ColorBg & 0xFF000000) >> 24] << 24)
88  | (((((ColorFg & 0x00FF00FF) * lut[0] + (ColorBg & 0x00FF00FF) * lut[1])) & 0xFF00FF00)
89  | ((((ColorFg & 0x0000FF00) * lut[0] + (ColorBg & 0x0000FF00) * lut[1])) & 0x00FF0000)) >> 8);
90 }
91 #else
92 // Alpha blending without lookup table.
93 // Also works fast, but doesn't return the theoretically correct result.
94 // It's "good enough", though.
95 static tColor Multiply(tColor Color, uint8_t Alpha)
96 {
97  tColor RB = (Color & 0x00FF00FF) * Alpha;
98  RB = ((RB + ((RB >> 8) & 0x00FF00FF) + 0x00800080) >> 8) & 0x00FF00FF;
99  tColor AG = ((Color >> 8) & 0x00FF00FF) * Alpha;
100  AG = ((AG + ((AG >> 8) & 0x00FF00FF) + 0x00800080)) & 0xFF00FF00;
101  return AG | RB;
102 }
103 
104 tColor AlphaBlend(tColor ColorFg, tColor ColorBg, uint8_t AlphaLayer)
105 {
106  tColor Alpha = (ColorFg & 0xFF000000) >> 24;
107  if (AlphaLayer < ALPHA_OPAQUE) {
108  Alpha *= AlphaLayer;
109  Alpha = ((Alpha + ((Alpha >> 8) & 0x000000FF) + 0x00000080) >> 8) & 0x000000FF;
110  }
111  return Multiply(ColorFg, Alpha) + Multiply(ColorBg, 255 - Alpha);
112 }
113 #endif
114 
115 // --- cPalette --------------------------------------------------------------
116 
118 {
119  SetBpp(Bpp);
120  SetAntiAliasGranularity(10, 10);
121 }
122 
124 {
125 }
126 
127 void cPalette::SetAntiAliasGranularity(uint FixedColors, uint BlendColors)
128 {
129  if (FixedColors >= MAXNUMCOLORS || BlendColors == 0)
131  else {
132  int ColorsForBlending = MAXNUMCOLORS - FixedColors;
133  int ColorsPerBlend = ColorsForBlending / BlendColors + 2; // +2 = the full foreground and background colors, which are among the fixed colors
134  antiAliasGranularity = double(MAXNUMCOLORS - 1) / (ColorsPerBlend - 1);
135  }
136 }
137 
138 void cPalette::Reset(void)
139 {
140  numColors = 0;
141  modified = false;
142 }
143 
145 {
146  // Check if color is already defined:
147  for (int i = 0; i < numColors; i++) {
148  if (color[i] == Color)
149  return i;
150  }
151  // No exact color, try a close one:
152  int i = ClosestColor(Color, 4);
153  if (i >= 0)
154  return i;
155  // No close one, try to define a new one:
156  if (numColors < maxColors) {
157  color[numColors++] = Color;
158  modified = true;
159  return numColors - 1;
160  }
161  // Out of colors, so any close color must do:
162  return ClosestColor(Color);
163 }
164 
165 void cPalette::SetBpp(int Bpp)
166 {
167  bpp = Bpp;
168  maxColors = 1 << bpp;
169  Reset();
170 }
171 
172 void cPalette::SetColor(int Index, tColor Color)
173 {
174  if (Index < maxColors) {
175  if (numColors <= Index) {
176  numColors = Index + 1;
177  modified = true;
178  }
179  else
180  modified |= color[Index] != Color;
181  color[Index] = Color;
182  }
183 }
184 
185 const tColor *cPalette::Colors(int &NumColors) const
186 {
187  NumColors = numColors;
188  return numColors ? color : NULL;
189 }
190 
191 void cPalette::Take(const cPalette &Palette, tIndexes *Indexes, tColor ColorFg, tColor ColorBg)
192 {
193  for (int i = 0; i < Palette.numColors; i++) {
194  tColor Color = Palette.color[i];
195  if (ColorFg || ColorBg) {
196  switch (i) {
197  case 0: Color = ColorBg; break;
198  case 1: Color = ColorFg; break;
199  default: ;
200  }
201  }
202  int n = Index(Color);
203  if (Indexes)
204  (*Indexes)[i] = n;
205  }
206 }
207 
208 void cPalette::Replace(const cPalette &Palette)
209 {
210  for (int i = 0; i < Palette.numColors; i++)
211  SetColor(i, Palette.color[i]);
212  numColors = Palette.numColors;
214 }
215 
216 tColor cPalette::Blend(tColor ColorFg, tColor ColorBg, uint8_t Level) const
217 {
218  if (antiAliasGranularity > 0)
219  Level = uint8_t(int(Level / antiAliasGranularity + 0.5) * antiAliasGranularity);
220  int Af = (ColorFg & 0xFF000000) >> 24;
221  int Rf = (ColorFg & 0x00FF0000) >> 16;
222  int Gf = (ColorFg & 0x0000FF00) >> 8;
223  int Bf = (ColorFg & 0x000000FF);
224  int Ab = (ColorBg & 0xFF000000) >> 24;
225  int Rb = (ColorBg & 0x00FF0000) >> 16;
226  int Gb = (ColorBg & 0x0000FF00) >> 8;
227  int Bb = (ColorBg & 0x000000FF);
228  int A = (Ab + (Af - Ab) * Level / 0xFF) & 0xFF;
229  int R = (Rb + (Rf - Rb) * Level / 0xFF) & 0xFF;
230  int G = (Gb + (Gf - Gb) * Level / 0xFF) & 0xFF;
231  int B = (Bb + (Bf - Bb) * Level / 0xFF) & 0xFF;
232  return (A << 24) | (R << 16) | (G << 8) | B;
233 }
234 
235 int cPalette::ClosestColor(tColor Color, int MaxDiff) const
236 {
237  int n = 0;
238  int d = INT_MAX;
239  int A1 = (Color & 0xFF000000) >> 24;
240  int R1 = (Color & 0x00FF0000) >> 16;
241  int G1 = (Color & 0x0000FF00) >> 8;
242  int B1 = (Color & 0x000000FF);
243  for (int i = 0; i < numColors && d > 0; i++) {
244  int A2 = (color[i] & 0xFF000000) >> 24;
245  int R2 = (color[i] & 0x00FF0000) >> 16;
246  int G2 = (color[i] & 0x0000FF00) >> 8;
247  int B2 = (color[i] & 0x000000FF);
248  int diff = 0;
249  if (A1 || A2) // fully transparent colors are considered equal
250  diff = (abs(A1 - A2) << 1) + (abs(R1 - R2) << 1) + (abs(G1 - G2) << 1) + (abs(B1 - B2) << 1);
251  if (diff < d) {
252  d = diff;
253  n = i;
254  }
255  }
256  return d <= MaxDiff ? n : -1;
257 }
258 
259 // --- cBitmap ---------------------------------------------------------------
260 
261 cBitmap::cBitmap(int Width, int Height, int Bpp, int X0, int Y0)
262 :cPalette(Bpp)
263 {
264  bitmap = NULL;
265  x0 = X0;
266  y0 = Y0;
267  width = height = 0;
268  SetSize(Width, Height);
269 }
270 
271 cBitmap::cBitmap(const char *FileName)
272 {
273  bitmap = NULL;
274  x0 = 0;
275  y0 = 0;
276  width = height = 0;
277  LoadXpm(FileName);
278 }
279 
280 cBitmap::cBitmap(const char *const Xpm[])
281 {
282  bitmap = NULL;
283  x0 = 0;
284  y0 = 0;
285  width = height = 0;
286  SetXpm(Xpm);
287 }
288 
290 {
291  free(bitmap);
292 }
293 
294 void cBitmap::SetSize(int Width, int Height)
295 {
296  if (bitmap && Width == width && Height == height)
297  return;
298  width = Width;
299  height = Height;
300  free(bitmap);
301  bitmap = NULL;
302  dirtyX1 = 0;
303  dirtyY1 = 0;
304  dirtyX2 = width - 1;
305  dirtyY2 = height - 1;
306  if (width > 0 && height > 0) {
308  if (bitmap)
309  memset(bitmap, 0x00, width * height);
310  else
311  esyslog("ERROR: can't allocate bitmap!");
312  }
313  else
314  esyslog("ERROR: invalid bitmap parameters (%d, %d)!", width, height);
315 }
316 
317 bool cBitmap::Contains(int x, int y) const
318 {
319  x -= x0;
320  y -= y0;
321  return 0 <= x && x < width && 0 <= y && y < height;
322 }
323 
324 bool cBitmap::Covers(int x1, int y1, int x2, int y2) const
325 {
326  x1 -= x0;
327  y1 -= y0;
328  x2 -= x0;
329  y2 -= y0;
330  return x1 <= 0 && y1 <= 0 && x2 >= width - 1 && y2 >= height - 1;
331 }
332 
333 bool cBitmap::Intersects(int x1, int y1, int x2, int y2) const
334 {
335  x1 -= x0;
336  y1 -= y0;
337  x2 -= x0;
338  y2 -= y0;
339  return !(x2 < 0 || x1 >= width || y2 < 0 || y1 >= height);
340 }
341 
342 bool cBitmap::Dirty(int &x1, int &y1, int &x2, int &y2)
343 {
344  if (dirtyX2 >= 0) {
345  x1 = dirtyX1;
346  y1 = dirtyY1;
347  x2 = dirtyX2;
348  y2 = dirtyY2;
349  return true;
350  }
351  return false;
352 }
353 
354 void cBitmap::Clean(void)
355 {
356  dirtyX1 = width;
357  dirtyY1 = height;
358  dirtyX2 = -1;
359  dirtyY2 = -1;
360 }
361 
362 bool cBitmap::LoadXpm(const char *FileName)
363 {
364  bool Result = false;
365  FILE *f = fopen(FileName, "r");
366  if (f) {
367  char **Xpm = NULL;
368  bool isXpm = false;
369  int lines = 0;
370  int index = 0;
371  char *s;
372  cReadLine ReadLine;
373  while ((s = ReadLine.Read(f)) != NULL) {
374  s = skipspace(s);
375  if (!isXpm) {
376  if (strcmp(s, "/* XPM */") != 0) {
377  esyslog("ERROR: invalid header in XPM file '%s'", FileName);
378  break;
379  }
380  isXpm = true;
381  }
382  else if (*s++ == '"') {
383  if (!lines) {
384  int w, h, n, c;
385  if (4 != sscanf(s, "%d %d %d %d", &w, &h, &n, &c)) {
386  esyslog("ERROR: faulty 'values' line in XPM file '%s'", FileName);
387  isXpm = false;
388  break;
389  }
390  lines = h + n + 1;
391  Xpm = MALLOC(char *, lines);
392  memset(Xpm, 0, lines * sizeof(char*));
393  }
394  char *q = strchr(s, '"');
395  if (!q) {
396  esyslog("ERROR: missing quotes in XPM file '%s'", FileName);
397  isXpm = false;
398  break;
399  }
400  *q = 0;
401  if (index < lines)
402  Xpm[index++] = strdup(s);
403  else {
404  esyslog("ERROR: too many lines in XPM file '%s'", FileName);
405  isXpm = false;
406  break;
407  }
408  }
409  }
410  if (isXpm) {
411  if (index == lines)
412  Result = SetXpm(Xpm);
413  else
414  esyslog("ERROR: too few lines in XPM file '%s'", FileName);
415  }
416  if (Xpm) {
417  for (int i = 0; i < index; i++)
418  free(Xpm[i]);
419  }
420  free(Xpm);
421  fclose(f);
422  }
423  else
424  esyslog("ERROR: can't open XPM file '%s'", FileName);
425  return Result;
426 }
427 
428 bool cBitmap::SetXpm(const char *const Xpm[], bool IgnoreNone)
429 {
430  if (!Xpm)
431  return false;
432  const char *const *p = Xpm;
433  int w, h, n, c;
434  if (4 != sscanf(*p, "%d %d %d %d", &w, &h, &n, &c)) {
435  esyslog("ERROR: faulty 'values' line in XPM: '%s'", *p);
436  return false;
437  }
438  if (n > MAXNUMCOLORS) {
439  esyslog("ERROR: too many colors in XPM: %d", n);
440  return false;
441  }
442  int b = 0;
443  while (1 << (1 << b) < (IgnoreNone ? n - 1 : n))
444  b++;
445  SetBpp(1 << b);
446  SetSize(w, h);
447  int NoneColorIndex = MAXNUMCOLORS;
448  for (int i = 0; i < n; i++) {
449  const char *s = *++p;
450  if (int(strlen(s)) < c) {
451  esyslog("ERROR: faulty 'colors' line in XPM: '%s'", s);
452  return false;
453  }
454  s = skipspace(s + c);
455  if (*s != 'c') {
456  esyslog("ERROR: unknown color key in XPM: '%c'", *s);
457  return false;
458  }
459  s = skipspace(s + 1);
460  if (strcasecmp(s, "none") == 0) {
461  NoneColorIndex = i;
462  if (!IgnoreNone)
464  continue;
465  }
466  if (*s != '#') {
467  esyslog("ERROR: unknown color code in XPM: '%c'", *s);
468  return false;
469  }
470  tColor color = strtoul(++s, NULL, 16) | 0xFF000000;
471  SetColor((IgnoreNone && i > NoneColorIndex) ? i - 1 : i, color);
472  }
473  for (int y = 0; y < h; y++) {
474  const char *s = *++p;
475  if (int(strlen(s)) != w * c) {
476  esyslog("ERROR: faulty pixel line in XPM: %d '%s'", y, s);
477  return false;
478  }
479  for (int x = 0; x < w; x++) {
480  for (int i = 0; i <= n; i++) {
481  if (i == n) {
482  esyslog("ERROR: undefined pixel color in XPM: %d %d '%s'", x, y, s);
483  return false;
484  }
485  if (strncmp(Xpm[i + 1], s, c) == 0) {
486  if (i == NoneColorIndex)
487  NoneColorIndex = MAXNUMCOLORS;
488  SetIndex(x, y, (IgnoreNone && i > NoneColorIndex) ? i - 1 : i);
489  break;
490  }
491  }
492  s += c;
493  }
494  }
495  if (NoneColorIndex < MAXNUMCOLORS && !IgnoreNone)
496  return SetXpm(Xpm, true);
497  return true;
498 }
499 
500 void cBitmap::SetIndex(int x, int y, tIndex Index)
501 {
502  if (bitmap) {
503  if (0 <= x && x < width && 0 <= y && y < height) {
504  if (bitmap[width * y + x] != Index) {
505  bitmap[width * y + x] = Index;
506  if (dirtyX1 > x) dirtyX1 = x;
507  if (dirtyY1 > y) dirtyY1 = y;
508  if (dirtyX2 < x) dirtyX2 = x;
509  if (dirtyY2 < y) dirtyY2 = y;
510  }
511  }
512  }
513 }
514 
515 void cBitmap::DrawPixel(int x, int y, tColor Color)
516 {
517  x -= x0;
518  y -= y0;
519  SetIndex(x, y, Index(Color));
520 }
521 
522 void cBitmap::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
523 {
524  if (bitmap && Bitmap.bitmap && Intersects(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) {
525  if (Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1))
526  Reset();
527  x -= x0;
528  y -= y0;
529  if (ReplacePalette && Covers(x + x0, y + y0, x + x0 + Bitmap.Width() - 1, y + y0 + Bitmap.Height() - 1)) {
530  Replace(Bitmap);
531  for (int ix = 0; ix < Bitmap.width; ix++) {
532  for (int iy = 0; iy < Bitmap.height; iy++) {
533  if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
534  SetIndex(x + ix, y + iy, Bitmap.bitmap[Bitmap.width * iy + ix]);
535  }
536  }
537  }
538  else {
539  tIndexes Indexes;
540  Take(Bitmap, &Indexes, ColorFg, ColorBg);
541  for (int ix = 0; ix < Bitmap.width; ix++) {
542  for (int iy = 0; iy < Bitmap.height; iy++) {
543  if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
544  SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]);
545  }
546  }
547  }
548  }
549 }
550 
551 void cBitmap::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
552 {
553  if (bitmap) {
554  int w = Font->Width(s);
555  int h = Font->Height();
556  int limit = 0;
557  int cw = Width ? Width : w;
558  int ch = Height ? Height : h;
559  if (!Intersects(x, y, x + cw - 1, y + ch - 1))
560  return;
561  if (ColorBg != clrTransparent)
562  DrawRectangle(x, y, x + cw - 1, y + ch - 1, ColorBg);
563  if (Width || Height) {
564  limit = x + cw - x0;
565  if (Width) {
566  if ((Alignment & taLeft) != 0) {
567  if ((Alignment & taBorder) != 0)
568  x += max(h / TEXT_ALIGN_BORDER, 1);
569  }
570  else if ((Alignment & taRight) != 0) {
571  if (w < Width)
572  x += Width - w;
573  if ((Alignment & taBorder) != 0)
574  x -= max(h / TEXT_ALIGN_BORDER, 1);
575  }
576  else { // taCentered
577  if (w < Width)
578  x += (Width - w) / 2;
579  }
580  }
581  if (Height) {
582  if ((Alignment & taTop) != 0)
583  ;
584  else if ((Alignment & taBottom) != 0) {
585  if (h < Height)
586  y += Height - h;
587  }
588  else { // taCentered
589  if (h < Height)
590  y += (Height - h) / 2;
591  }
592  }
593  }
594  x -= x0;
595  y -= y0;
596  Font->DrawText(this, x, y, s, ColorFg, ColorBg, limit);
597  }
598 }
599 
600 void cBitmap::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
601 {
602  if (bitmap && Intersects(x1, y1, x2, y2)) {
603  if (Covers(x1, y1, x2, y2))
604  Reset();
605  x1 -= x0;
606  y1 -= y0;
607  x2 -= x0;
608  y2 -= y0;
609  x1 = max(x1, 0);
610  y1 = max(y1, 0);
611  x2 = min(x2, width - 1);
612  y2 = min(y2, height - 1);
613  tIndex c = Index(Color);
614  for (int y = y1; y <= y2; y++) {
615  for (int x = x1; x <= x2; x++)
616  SetIndex(x, y, c);
617  }
618  }
619 }
620 
621 void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
622 {
623  if (!Intersects(x1, y1, x2, y2))
624  return;
625  // Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF
626  int rx = x2 - x1;
627  int ry = y2 - y1;
628  int cx = (x1 + x2) / 2;
629  int cy = (y1 + y2) / 2;
630  switch (abs(Quadrants)) {
631  case 0: rx /= 2; ry /= 2; break;
632  case 1: cx = x1; cy = y2; break;
633  case 2: cx = x2; cy = y2; break;
634  case 3: cx = x2; cy = y1; break;
635  case 4: cx = x1; cy = y1; break;
636  case 5: cx = x1; ry /= 2; break;
637  case 6: cy = y2; rx /= 2; break;
638  case 7: cx = x2; ry /= 2; break;
639  case 8: cy = y1; rx /= 2; break;
640  default: ;
641  }
642  int TwoASquare = 2 * rx * rx;
643  int TwoBSquare = 2 * ry * ry;
644  int x = rx;
645  int y = 0;
646  int XChange = ry * ry * (1 - 2 * rx);
647  int YChange = rx * rx;
648  int EllipseError = 0;
649  int StoppingX = TwoBSquare * rx;
650  int StoppingY = 0;
651  while (StoppingX >= StoppingY) {
652  switch (Quadrants) {
653  case 5: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); // no break
654  case 1: DrawRectangle(cx, cy - y, cx + x, cy - y, Color); break;
655  case 7: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); // no break
656  case 2: DrawRectangle(cx - x, cy - y, cx, cy - y, Color); break;
657  case 3: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); break;
658  case 4: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); break;
659  case 0:
660  case 6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break;
661  case 8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break;
662  case -1: DrawRectangle(cx + x, cy - y, x2, cy - y, Color); break;
663  case -2: DrawRectangle(x1, cy - y, cx - x, cy - y, Color); break;
664  case -3: DrawRectangle(x1, cy + y, cx - x, cy + y, Color); break;
665  case -4: DrawRectangle(cx + x, cy + y, x2, cy + y, Color); break;
666  default: ;
667  }
668  y++;
669  StoppingY += TwoASquare;
670  EllipseError += YChange;
671  YChange += TwoASquare;
672  if (2 * EllipseError + XChange > 0) {
673  x--;
674  StoppingX -= TwoBSquare;
675  EllipseError += XChange;
676  XChange += TwoBSquare;
677  }
678  }
679  x = 0;
680  y = ry;
681  XChange = ry * ry;
682  YChange = rx * rx * (1 - 2 * ry);
683  EllipseError = 0;
684  StoppingX = 0;
685  StoppingY = TwoASquare * ry;
686  while (StoppingX <= StoppingY) {
687  switch (Quadrants) {
688  case 5: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); // no break
689  case 1: DrawRectangle(cx, cy - y, cx + x, cy - y, Color); break;
690  case 7: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); // no break
691  case 2: DrawRectangle(cx - x, cy - y, cx, cy - y, Color); break;
692  case 3: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); break;
693  case 4: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); break;
694  case 0:
695  case 6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break;
696  case 8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break;
697  case -1: DrawRectangle(cx + x, cy - y, x2, cy - y, Color); break;
698  case -2: DrawRectangle(x1, cy - y, cx - x, cy - y, Color); break;
699  case -3: DrawRectangle(x1, cy + y, cx - x, cy + y, Color); break;
700  case -4: DrawRectangle(cx + x, cy + y, x2, cy + y, Color); break;
701  default: ;
702  }
703  x++;
704  StoppingX += TwoBSquare;
705  EllipseError += XChange;
706  XChange += TwoBSquare;
707  if (2 * EllipseError + YChange > 0) {
708  y--;
709  StoppingY -= TwoASquare;
710  EllipseError += YChange;
711  YChange += TwoASquare;
712  }
713  }
714 }
715 
716 void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
717 {
718  if (!Intersects(x1, y1, x2, y2))
719  return;
720  bool upper = Type & 0x01;
721  bool falling = Type & 0x02;
722  bool vertical = Type & 0x04;
723  if (vertical) {
724  for (int y = y1; y <= y2; y++) {
725  double c = cos((y - y1) * M_PI / (y2 - y1 + 1));
726  if (falling)
727  c = -c;
728  int x = int((x2 - x1 + 1) * c / 2);
729  if (upper && !falling || !upper && falling)
730  DrawRectangle(x1, y, (x1 + x2) / 2 + x, y, Color);
731  else
732  DrawRectangle((x1 + x2) / 2 + x, y, x2, y, Color);
733  }
734  }
735  else {
736  for (int x = x1; x <= x2; x++) {
737  double c = cos((x - x1) * M_PI / (x2 - x1 + 1));
738  if (falling)
739  c = -c;
740  int y = int((y2 - y1 + 1) * c / 2);
741  if (upper)
742  DrawRectangle(x, y1, x, (y1 + y2) / 2 + y, Color);
743  else
744  DrawRectangle(x, (y1 + y2) / 2 + y, x, y2, Color);
745  }
746  }
747 }
748 
749 const tIndex *cBitmap::Data(int x, int y) const
750 {
751  return &bitmap[y * width + x];
752 }
753 
754 void cBitmap::ReduceBpp(const cPalette &Palette)
755 {
756  int NewBpp = Palette.Bpp();
757  if (Bpp() == 4 && NewBpp == 2) {
758  for (int i = width * height; i--; ) {
759  tIndex p = bitmap[i];
760  bitmap[i] = (p >> 2) | ((p & 0x03) != 0);
761  }
762  }
763  else if (Bpp() == 8) {
764  if (NewBpp == 2) {
765  for (int i = width * height; i--; ) {
766  tIndex p = bitmap[i];
767  bitmap[i] = (p >> 6) | ((p & 0x30) != 0);
768  }
769  }
770  else if (NewBpp == 4) {
771  for (int i = width * height; i--; ) {
772  tIndex p = bitmap[i];
773  bitmap[i] = p >> 4;
774  }
775  }
776  else
777  return;
778  }
779  else
780  return;
781  SetBpp(NewBpp);
782  Replace(Palette);
783 }
784 
785 void cBitmap::ShrinkBpp(int NewBpp)
786 {
787  int NumOldColors;
788  const tColor *Colors = this->Colors(NumOldColors);
789  if (Colors) {
790  // Find the most frequently used colors and create a map table:
791  int Used[MAXNUMCOLORS] = { 0 };
792  int Map[MAXNUMCOLORS] = { 0 };
793  for (int i = width * height; i--; )
794  Used[bitmap[i]]++;
795  int MaxNewColors = (NewBpp == 4) ? 16 : 4;
796  cPalette NewPalette(NewBpp);
797  for (int i = 0; i < MaxNewColors; i++) {
798  int Max = 0;
799  int Index = -1;
800  for (int n = 0; n < NumOldColors; n++) {
801  if (Used[n] > Max) {
802  Max = Used[n];
803  Index = n;
804  }
805  }
806  if (Index >= 0) {
807  Used[Index] = 0;
808  Map[Index] = i;
809  NewPalette.SetColor(i, Colors[Index]);
810  }
811  else
812  break;
813  }
814  // Complete the map table for all other colors (will be set to closest match):
815  for (int n = 0; n < NumOldColors; n++) {
816  if (Used[n])
817  Map[n] = NewPalette.Index(Colors[n]);
818  }
819  // Do the actual index mapping:
820  for (int i = width * height; i--; )
821  bitmap[i] = Map[bitmap[i]];
822  SetBpp(NewBpp);
823  Replace(NewPalette);
824  }
825 }
826 
827 cBitmap *cBitmap::Scaled(double FactorX, double FactorY, bool AntiAlias)
828 {
829  // Fixed point scaling code based on www.inversereality.org/files/bitmapscaling.pdf
830  // by deltener@mindtremors.com
831  cBitmap *b = new cBitmap(int(round(Width() * FactorX)), int(round(Height() * FactorY)), Bpp(), X0(), Y0());
832  int RatioX = (Width() << 16) / b->Width();
833  int RatioY = (Height() << 16) / b->Height();
834  if (!AntiAlias || FactorX <= 1.0 && FactorY <= 1.0) {
835  // Downscaling - no anti-aliasing:
836  b->Replace(*this); // copy palette
837  tIndex *DestRow = b->bitmap;
838  int SourceY = 0;
839  for (int y = 0; y < b->Height(); y++) {
840  int SourceX = 0;
841  tIndex *SourceRow = bitmap + (SourceY >> 16) * Width();
842  tIndex *Dest = DestRow;
843  for (int x = 0; x < b->Width(); x++) {
844  *Dest++ = SourceRow[SourceX >> 16];
845  SourceX += RatioX;
846  }
847  SourceY += RatioY;
848  DestRow += b->Width();
849  }
850  }
851  else {
852  // Upscaling - anti-aliasing:
853  b->SetBpp(8);
854  b->Replace(*this); // copy palette (must be done *after* SetBpp()!)
855  int SourceY = 0;
856  for (int y = 0; y < b->Height() - 1; y++) {
857  int SourceX = 0;
858  int sy = SourceY >> 16;
859  uint8_t BlendY = 0xFF - ((SourceY >> 8) & 0xFF);
860  for (int x = 0; x < b->Width() - 1; x++) {
861  int sx = SourceX >> 16;
862  uint8_t BlendX = 0xFF - ((SourceX >> 8) & 0xFF);
863  tColor c1 = b->Blend(GetColor(sx, sy), GetColor(sx + 1, sy), BlendX);
864  tColor c2 = b->Blend(GetColor(sx, sy + 1), GetColor(sx + 1, sy + 1), BlendX);
865  tColor c3 = b->Blend(c1, c2, BlendY);
866  b->DrawPixel(x + X0(), y + Y0(), c3);
867  SourceX += RatioX;
868  }
869  SourceY += RatioY;
870  }
871  }
872  return b;
873 }
874 
875 // --- cRect -----------------------------------------------------------------
876 
877 const cRect cRect::Null;
878 
879 void cRect::Grow(int Dx, int Dy)
880 {
881  point.Shift(-Dx, -Dy);
882  size.Grow(Dx, Dy);
883 }
884 
885 bool cRect::Contains(const cPoint &Point) const
886 {
887  return Left() <= Point.X() &&
888  Top() <= Point.Y() &&
889  Right() >= Point.X() &&
890  Bottom() >= Point.Y();
891 }
892 
893 bool cRect::Contains(const cRect &Rect) const
894 {
895  return Left() <= Rect.Left() &&
896  Top() <= Rect.Top() &&
897  Right() >= Rect.Right() &&
898  Bottom() >= Rect.Bottom();
899 }
900 
901 bool cRect::Intersects(const cRect &Rect) const
902 {
903  return !(Left() > Rect.Right() ||
904  Top() > Rect.Bottom() ||
905  Right() < Rect.Left() ||
906  Bottom() < Rect.Top());
907 }
908 
909 cRect cRect::Intersected(const cRect &Rect) const
910 {
911  cRect r;
912  if (!IsEmpty() && !Rect.IsEmpty()) {
913  r.SetLeft(max(Left(), Rect.Left()));
914  r.SetTop(max(Top(), Rect.Top()));
915  r.SetRight(min(Right(), Rect.Right()));
916  r.SetBottom(min(Bottom(), Rect.Bottom()));
917  }
918  return r;
919 }
920 
921 void cRect::Combine(const cRect &Rect)
922 {
923  if (IsEmpty())
924  *this = Rect;
925  if (Rect.IsEmpty())
926  return;
927  // must set right/bottom *before* top/left!
928  SetRight(max(Right(), Rect.Right()));
929  SetBottom(max(Bottom(), Rect.Bottom()));
930  SetLeft(min(Left(), Rect.Left()));
931  SetTop(min(Top(), Rect.Top()));
932 }
933 
934 void cRect::Combine(const cPoint &Point)
935 {
936  if (IsEmpty())
937  Set(Point.X(), Point.Y(), 1, 1);
938  // must set right/bottom *before* top/left!
939  SetRight(max(Right(), Point.X()));
940  SetBottom(max(Bottom(), Point.Y()));
941  SetLeft(min(Left(), Point.X()));
942  SetTop(min(Top(), Point.Y()));
943 }
944 
945 // --- cPixmap ---------------------------------------------------------------
946 
948 
950 {
951  layer = -1;
953  tile = false;
954 }
955 
956 cPixmap::cPixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort)
957 {
958  layer = Layer;
959  if (layer >= MAXPIXMAPLAYERS) {
960  layer = MAXPIXMAPLAYERS - 1;
961  esyslog("ERROR: pixmap layer %d limited to %d", Layer, layer);
962  }
963  viewPort = ViewPort;
964  if (!DrawPort.IsEmpty())
965  drawPort = DrawPort;
966  else {
967  drawPort = viewPort;
968  drawPort.SetPoint(0, 0);
969  }
971  tile = false;
972 }
973 
975 {
977 }
978 
980 {
981  if (viewPort.Contains(Point))
982  dirtyViewPort.Combine(Point);
983 }
984 
986 {
988  if (tile)
990  else
992 }
993 
995 {
996  if (drawPort.Contains(Point)) {
997  dirtyDrawPort.Combine(Point);
998  if (tile)
1000  else
1002  }
1003 }
1004 
1006 {
1008 }
1009 
1010 void cPixmap::SetLayer(int Layer)
1011 {
1012  Lock();
1013  if (Layer >= MAXPIXMAPLAYERS) {
1014  esyslog("ERROR: pixmap layer %d limited to %d", Layer, MAXPIXMAPLAYERS - 1);
1015  Layer = MAXPIXMAPLAYERS - 1;
1016  }
1017  if (Layer != layer) {
1018  if (Layer > 0 || layer > 0)
1020  layer = Layer;
1021  }
1022  Unlock();
1023 }
1024 
1025 void cPixmap::SetAlpha(int Alpha)
1026 {
1027  Lock();
1028  Alpha = constrain(Alpha, ALPHA_TRANSPARENT, ALPHA_OPAQUE);
1029  if (Alpha != alpha) {
1031  alpha = Alpha;
1032  }
1033  Unlock();
1034 }
1035 
1036 void cPixmap::SetTile(bool Tile)
1037 {
1038  Lock();
1039  if (Tile != tile) {
1040  if (drawPort.Point() != cPoint(0, 0) || drawPort.Width() < viewPort.Width() || drawPort.Height() < viewPort.Height())
1042  tile = Tile;
1043  }
1044  Unlock();
1045 }
1046 
1047 void cPixmap::SetViewPort(const cRect &Rect)
1048 {
1049  Lock();
1050  if (Rect != viewPort) {
1051  if (tile)
1053  else
1055  viewPort = Rect;
1056  if (tile)
1058  else
1060  }
1061  Unlock();
1062 }
1063 
1064 void cPixmap::SetDrawPortPoint(const cPoint &Point, bool Dirty)
1065 {
1066  Lock();
1067  if (Point != drawPort.Point()) {
1068  if (Dirty) {
1069  if (tile)
1071  else
1073  }
1074  drawPort.SetPoint(Point);
1075  if (Dirty && !tile)
1077  }
1078  Unlock();
1079 }
1080 
1081 // --- cImage ----------------------------------------------------------------
1082 
1084 {
1085  data = NULL;
1086 }
1087 
1088 cImage::cImage(const cImage &Image)
1089 {
1090  size = Image.Size();
1091  int l = size.Width() * size.Height() * sizeof(tColor);
1092  data = MALLOC(tColor, l);
1093  memcpy(data, Image.Data(), l);
1094 }
1095 
1096 cImage::cImage(const cSize &Size, const tColor *Data)
1097 {
1098  size = Size;
1099  int l = size.Width() * size.Height() * sizeof(tColor);
1100  data = MALLOC(tColor, l);
1101  if (Data)
1102  memcpy(data, Data, l);
1103 }
1104 
1106 {
1107  free(data);
1108 }
1109 
1110 void cImage::Clear(void)
1111 {
1112  memset(data, 0x00, Width() * Height() * sizeof(tColor));
1113 }
1114 
1116 {
1117  for (int i = Width() * Height() - 1; i >= 0; i--)
1118  data[i] = Color;
1119 }
1120 
1121 // --- cPixmapMemory ---------------------------------------------------------
1122 
1124 {
1125  data = NULL;
1126  panning = false;
1127 }
1128 
1129 cPixmapMemory::cPixmapMemory(int Layer, const cRect &ViewPort, const cRect &DrawPort)
1130 :cPixmap(Layer, ViewPort, DrawPort)
1131 {
1132  data = MALLOC(tColor, this->DrawPort().Width() * this->DrawPort().Height());
1133 }
1134 
1136 {
1137  free(data);
1138 }
1139 
1141 {
1142  Lock();
1143  memset(data, 0x00, DrawPort().Width() * DrawPort().Height() * sizeof(tColor));
1145  Unlock();
1146 }
1147 
1149 {
1150  Lock();
1151  for (int i = DrawPort().Width() * DrawPort().Height() - 1; i >= 0; i--)
1152  data[i] = Color;
1154  Unlock();
1155 }
1156 
1157 void cPixmap::DrawPixmap(const cPixmap *Pixmap, const cRect &Dirty)
1158 {
1159  if (Pixmap->Tile() && (Pixmap->DrawPort().Point() != cPoint(0, 0) || Pixmap->DrawPort().Size() < Pixmap->ViewPort().Size())) {
1160  cPoint t0 = Pixmap->DrawPort().Point().Shifted(Pixmap->ViewPort().Point()); // the origin of the draw port in absolute OSD coordinates
1161  // Find the top/leftmost location where the draw port touches the view port:
1162  while (t0.X() > Pixmap->ViewPort().Left())
1163  t0.Shift(-Pixmap->DrawPort().Width(), 0);
1164  while (t0.Y() > Pixmap->ViewPort().Top())
1165  t0.Shift(0, -Pixmap->DrawPort().Height());
1166  cPoint t = t0;;
1167  while (t.Y() <= Pixmap->ViewPort().Bottom()) {
1168  while (t.X() <= Pixmap->ViewPort().Right()) {
1169  cRect Source = Pixmap->DrawPort(); // assume the entire pixmap needs to be rendered
1170  Source.Shift(Pixmap->ViewPort().Point()); // Source is now in absolute OSD coordinates
1171  cPoint Delta = Source.Point() - t;
1172  Source.SetPoint(t); // Source is now where the pixmap's data shall be drawn
1173  Source = Source.Intersected(Pixmap->ViewPort()); // Source is now limited to the pixmap's view port
1174  Source = Source.Intersected(Dirty); // Source is now limited to the actual dirty rectangle
1175  if (!Source.IsEmpty()) {
1176  cPoint Dest = Source.Point().Shifted(-ViewPort().Point()); // remember the destination point
1177  Source.Shift(Delta); // Source is now back at the pixmap's draw port location, still in absolute OSD coordinates
1178  Source.Shift(-Pixmap->ViewPort().Point()); // Source is now relative to the pixmap's view port again
1179  Source.Shift(-Pixmap->DrawPort().Point()); // Source is now relative to the pixmap's data
1180  if (Pixmap->Layer() == 0)
1181  Copy(Pixmap, Source, Dest); // this is the "background" pixmap
1182  else
1183  Render(Pixmap, Source, Dest); // all others are alpha blended over the background
1184  }
1185  t.Shift(Pixmap->DrawPort().Width(), 0); // increase one draw port width to the right
1186  }
1187  t.SetX(t0.X()); // go back to the leftmost position
1188  t.Shift(0, Pixmap->DrawPort().Height()); // increase one draw port height down
1189  }
1190  }
1191  else {
1192  cRect Source = Pixmap->DrawPort(); // assume the entire pixmap needs to be rendered
1193  Source.Shift(Pixmap->ViewPort().Point()); // Source is now in absolute OSD coordinates
1194  Source = Source.Intersected(Pixmap->ViewPort()); // Source is now limited to the pixmap's view port
1195  Source = Source.Intersected(Dirty); // Source is now limited to the actual dirty rectangle
1196  if (!Source.IsEmpty()) {
1197  cPoint Dest = Source.Point().Shifted(-ViewPort().Point()); // remember the destination point
1198  Source.Shift(-Pixmap->ViewPort().Point()); // Source is now relative to the pixmap's draw port again
1199  Source.Shift(-Pixmap->DrawPort().Point()); // Source is now relative to the pixmap's data
1200  if (Pixmap->Layer() == 0)
1201  Copy(Pixmap, Source, Dest); // this is the "background" pixmap
1202  else
1203  Render(Pixmap, Source, Dest); // all others are alpha blended over the background
1204  }
1205  }
1206 }
1207 
1208 void cPixmapMemory::DrawImage(const cPoint &Point, const cImage &Image)
1209 {
1210  Lock();
1211  cRect r = cRect(Point, Image.Size()).Intersected(DrawPort().Size());
1212  if (!r.IsEmpty()) {
1213  int ws = Image.Size().Width();
1214  int wd = DrawPort().Width();
1215  int w = r.Width() * sizeof(tColor);
1216  const tColor *ps = Image.Data();
1217  if (Point.Y() < 0)
1218  ps -= Point.Y() * ws;
1219  if (Point.X() < 0)
1220  ps -= Point.X();
1221  tColor *pd = data + wd * r.Top() + r.Left();
1222  for (int y = r.Height(); y-- > 0; ) {
1223  memcpy(pd, ps, w);
1224  ps += ws;
1225  pd += wd;
1226  }
1227  MarkDrawPortDirty(r);
1228  }
1229  Unlock();
1230 }
1231 
1232 void cPixmapMemory::DrawImage(const cPoint &Point, int ImageHandle)
1233 {
1234  Lock();
1235  if (const cImage *Image = cOsdProvider::GetImageData(ImageHandle))
1236  DrawImage(Point, *Image);
1237  Unlock();
1238 }
1239 
1240 void cPixmapMemory::DrawPixel(const cPoint &Point, tColor Color)
1241 {
1242  Lock();
1243  if (DrawPort().Size().Contains(Point)) {
1244  int p = Point.Y() * DrawPort().Width() + Point.X();
1245  if (Layer() == 0 && !IS_OPAQUE(Color))
1246  data[p] = AlphaBlend(Color, data[p]);
1247  else
1248  data[p] = Color;
1249  MarkDrawPortDirty(Point);
1250  }
1251  Unlock();
1252 }
1253 
1254 void cPixmapMemory::DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool Overlay)
1255 {
1256  Lock();
1257  cRect r = cRect(Point, cSize(Bitmap.Width(), Bitmap.Height())).Intersected(DrawPort().Size());
1258  if (!r.IsEmpty()) {
1259  bool UseColors = ColorFg || ColorBg;
1260  int wd = DrawPort().Width();
1261  tColor *pd = data + wd * r.Top() + r.Left();
1262  for (int y = r.Top(); y <= r.Bottom(); y++) {
1263  tColor *cd = pd;
1264  for (int x = r.Left(); x <= r.Right(); x++) {
1265  tIndex Index = *Bitmap.Data(x - Point.X(), y - Point.Y());
1266  if (Index || !Overlay) {
1267  if (UseColors)
1268  *cd = Index ? ColorFg : ColorBg;
1269  else
1270  *cd = Bitmap.Color(Index);
1271  }
1272  cd++;
1273  }
1274  pd += wd;
1275  }
1276  MarkDrawPortDirty(r);
1277  }
1278  Unlock();
1279 }
1280 
1281 void cPixmapMemory::DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
1282 {
1283  Lock();
1284  int x = Point.X();
1285  int y = Point.Y();
1286  int w = Font->Width(s);
1287  int h = Font->Height();
1288  int limit = 0;
1289  int cw = Width ? Width : w;
1290  int ch = Height ? Height : h;
1291  cRect r(x, y, cw, ch);
1292  if (ColorBg != clrTransparent)
1293  DrawRectangle(r, ColorBg);
1294  if (Width || Height) {
1295  limit = x + cw;
1296  if (Width) {
1297  if ((Alignment & taLeft) != 0) {
1298  if ((Alignment & taBorder) != 0)
1299  x += max(h / TEXT_ALIGN_BORDER, 1);
1300  }
1301  else if ((Alignment & taRight) != 0) {
1302  if (w < Width)
1303  x += Width - w;
1304  if ((Alignment & taBorder) != 0)
1305  x -= max(h / TEXT_ALIGN_BORDER, 1);
1306  }
1307  else { // taCentered
1308  if (w < Width)
1309  x += (Width - w) / 2;
1310  }
1311  }
1312  if (Height) {
1313  if ((Alignment & taTop) != 0)
1314  ;
1315  else if ((Alignment & taBottom) != 0) {
1316  if (h < Height)
1317  y += Height - h;
1318  }
1319  else { // taCentered
1320  if (h < Height)
1321  y += (Height - h) / 2;
1322  }
1323  }
1324  }
1325  Font->DrawText(this, x, y, s, ColorFg, ColorBg, limit);
1326  MarkDrawPortDirty(r);
1327  Unlock();
1328 }
1329 
1331 {
1332  Lock();
1333  cRect r = Rect.Intersected(DrawPort().Size());
1334  if (!r.IsEmpty()) {
1335  int wd = DrawPort().Width();
1336  int w = r.Width() * sizeof(tColor);
1337  tColor *ps = NULL;
1338  tColor *pd = data + wd * r.Top() + r.Left();
1339  for (int y = r.Height(); y-- > 0; ) {
1340  if (ps)
1341  memcpy(pd, ps, w); // all other lines are copied fast from the first one
1342  else {
1343  // explicitly fill the first line:
1344  tColor *cd = ps = pd;
1345  for (int x = r.Width(); x-- > 0; ) {
1346  *cd = Color;
1347  cd++;
1348  }
1349  }
1350  pd += wd;
1351  }
1352  MarkDrawPortDirty(r);
1353  }
1354  Unlock();
1355 }
1356 
1357 void cPixmapMemory::DrawEllipse(const cRect &Rect, tColor Color, int Quadrants)
1358 {
1359 //TODO use anti-aliasing?
1360 //TODO fix alignment
1361  Lock();
1362  // Algorithm based on http://homepage.smc.edu/kennedy_john/BELIPSE.PDF
1363  int x1 = Rect.Left();
1364  int y1 = Rect.Top();
1365  int x2 = Rect.Right();
1366  int y2 = Rect.Bottom();
1367  int rx = x2 - x1;
1368  int ry = y2 - y1;
1369  int cx = (x1 + x2) / 2;
1370  int cy = (y1 + y2) / 2;
1371  switch (abs(Quadrants)) {
1372  case 0: rx /= 2; ry /= 2; break;
1373  case 1: cx = x1; cy = y2; break;
1374  case 2: cx = x2; cy = y2; break;
1375  case 3: cx = x2; cy = y1; break;
1376  case 4: cx = x1; cy = y1; break;
1377  case 5: cx = x1; ry /= 2; break;
1378  case 6: cy = y2; rx /= 2; break;
1379  case 7: cx = x2; ry /= 2; break;
1380  case 8: cy = y1; rx /= 2; break;
1381  default: ;
1382  }
1383  int TwoASquare = 2 * rx * rx;
1384  int TwoBSquare = 2 * ry * ry;
1385  int x = rx;
1386  int y = 0;
1387  int XChange = ry * ry * (1 - 2 * rx);
1388  int YChange = rx * rx;
1389  int EllipseError = 0;
1390  int StoppingX = TwoBSquare * rx;
1391  int StoppingY = 0;
1392  while (StoppingX >= StoppingY) {
1393  switch (Quadrants) {
1394  case 5: DrawRectangle(cRect(cx, cy + y, x + 1, 1), Color); // no break
1395  case 1: DrawRectangle(cRect(cx, cy - y, x + 1, 1), Color); break;
1396  case 7: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); // no break
1397  case 2: DrawRectangle(cRect(cx - x, cy - y, x + 1, 1), Color); break;
1398  case 3: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); break;
1399  case 4: DrawRectangle(cRect(cx, cy + y, x + 1, 1), Color); break;
1400  case 0:
1401  case 6: DrawRectangle(cRect(cx - x, cy - y, 2 * x + 1, 1), Color); if (Quadrants == 6) break;
1402  case 8: DrawRectangle(cRect(cx - x, cy + y, 2 * x + 1, 1), Color); break;
1403  case -1: DrawRectangle(cRect(cx + x, cy - y, rx - x + 1, 1), Color); break;
1404  case -2: DrawRectangle(cRect(x1, cy - y, cx - x - x1 + 1, 1), Color); break;
1405  case -3: DrawRectangle(cRect(x1, cy + y, cx - x - x1 + 1, 1), Color); break;
1406  case -4: DrawRectangle(cRect(cx + x, cy + y, rx - x + 1, 1), Color); break;
1407  default: ;
1408  }
1409  y++;
1410  StoppingY += TwoASquare;
1411  EllipseError += YChange;
1412  YChange += TwoASquare;
1413  if (2 * EllipseError + XChange > 0) {
1414  x--;
1415  StoppingX -= TwoBSquare;
1416  EllipseError += XChange;
1417  XChange += TwoBSquare;
1418  }
1419  }
1420  x = 0;
1421  y = ry;
1422  XChange = ry * ry;
1423  YChange = rx * rx * (1 - 2 * ry);
1424  EllipseError = 0;
1425  StoppingX = 0;
1426  StoppingY = TwoASquare * ry;
1427  while (StoppingX <= StoppingY) {
1428  switch (Quadrants) {
1429  case 5: DrawRectangle(cRect(cx, cy + y, x + 1, 1), Color); // no break
1430  case 1: DrawRectangle(cRect(cx, cy - y, x + 1, 1), Color); break;
1431  case 7: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); // no break
1432  case 2: DrawRectangle(cRect(cx - x, cy - y, x + 1, 1), Color); break;
1433  case 3: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); break;
1434  case 4: DrawRectangle(cRect(cx, cy + y, x + 1, 1), Color); break;
1435  case 0:
1436  case 6: DrawRectangle(cRect(cx - x, cy - y, 2 * x + 1, 1), Color); if (Quadrants == 6) break;
1437  case 8: DrawRectangle(cRect(cx - x, cy + y, 2 * x + 1, 1), Color); break;
1438  case -1: DrawRectangle(cRect(cx + x, cy - y, rx - x + 1, 1), Color); break;
1439  case -2: DrawRectangle(cRect(x1, cy - y, cx - x - x1 + 1, 1), Color); break;
1440  case -3: DrawRectangle(cRect(x1, cy + y, cx - x - x1 + 1, 1), Color); break;
1441  case -4: DrawRectangle(cRect(cx + x, cy + y, rx - x + 1, 1), Color); break;
1442  default: ;
1443  }
1444  x++;
1445  StoppingX += TwoBSquare;
1446  EllipseError += XChange;
1447  XChange += TwoBSquare;
1448  if (2 * EllipseError + YChange > 0) {
1449  y--;
1450  StoppingY -= TwoASquare;
1451  EllipseError += YChange;
1452  YChange += TwoASquare;
1453  }
1454  }
1455  MarkDrawPortDirty(Rect);
1456  Unlock();
1457 }
1458 
1459 void cPixmapMemory::DrawSlope(const cRect &Rect, tColor Color, int Type)
1460 {
1461  //TODO anti-aliasing?
1462  //TODO also simplify cBitmap::DrawSlope()
1463  Lock();
1464  bool upper = Type & 0x01;
1465  bool falling = Type & 0x02;
1466  bool vertical = Type & 0x04;
1467  int x1 = Rect.Left();
1468  int y1 = Rect.Top();
1469  int x2 = Rect.Right();
1470  int y2 = Rect.Bottom();
1471  int w = Rect.Width();
1472  int h = Rect.Height();
1473  if (vertical) {
1474  for (int y = y1; y <= y2; y++) {
1475  double c = cos((y - y1) * M_PI / h);
1476  if (falling)
1477  c = -c;
1478  int x = (x1 + x2) / 2 + int(w * c / 2);
1479  if (upper && !falling || !upper && falling)
1480  DrawRectangle(cRect(x1, y, x - x1 + 1, 1), Color);
1481  else
1482  DrawRectangle(cRect(x, y, x2 - x + 1, 1), Color);
1483  }
1484  }
1485  else {
1486  for (int x = x1; x <= x2; x++) {
1487  double c = cos((x - x1) * M_PI / w);
1488  if (falling)
1489  c = -c;
1490  int y = (y1 + y2) / 2 + int(h * c / 2);
1491  if (upper)
1492  DrawRectangle(cRect(x, y1, 1, y - y1 + 1), Color);
1493  else
1494  DrawRectangle(cRect(x, y, 1, y2 - y + 1), Color);
1495  }
1496  }
1497  MarkDrawPortDirty(Rect);
1498  Unlock();
1499 }
1500 
1501 void cPixmapMemory::Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)
1502 {
1503  Lock();
1504  if (Pixmap->Alpha() != ALPHA_TRANSPARENT) {
1505  if (const cPixmapMemory *pm = dynamic_cast<const cPixmapMemory *>(Pixmap)) {
1506  cRect s = Source.Intersected(Pixmap->DrawPort().Size());
1507  if (!s.IsEmpty()) {
1508  cPoint v = Dest - Source.Point();
1509  cRect d = s.Shifted(v).Intersected(DrawPort().Size());
1510  if (!d.IsEmpty()) {
1511  s = d.Shifted(-v);
1512  int a = pm->Alpha();
1513  int ws = pm->DrawPort().Width();
1514  int wd = DrawPort().Width();
1515  const tColor *ps = pm->data + ws * s.Top() + s.Left();
1516  tColor *pd = data + wd * d.Top() + d.Left();
1517  for (int y = d.Height(); y-- > 0; ) {
1518  const tColor *cs = ps;
1519  tColor *cd = pd;
1520  for (int x = d.Width(); x-- > 0; ) {
1521  *cd = AlphaBlend(*cs, *cd, a);
1522  cs++;
1523  cd++;
1524  }
1525  ps += ws;
1526  pd += wd;
1527  }
1528  MarkDrawPortDirty(d);
1529  }
1530  }
1531  }
1532  }
1533  Unlock();
1534 }
1535 
1536 void cPixmapMemory::Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)
1537 {
1538  Lock();
1539  if (const cPixmapMemory *pm = dynamic_cast<const cPixmapMemory *>(Pixmap)) {
1540  cRect s = Source.Intersected(pm->DrawPort().Size());
1541  if (!s.IsEmpty()) {
1542  cPoint v = Dest - Source.Point();
1543  cRect d = s.Shifted(v).Intersected(DrawPort().Size());
1544  if (!d.IsEmpty()) {
1545  s = d.Shifted(-v);
1546  int ws = pm->DrawPort().Width();
1547  int wd = DrawPort().Width();
1548  int w = d.Width() * sizeof(tColor);
1549  const tColor *ps = pm->data + ws * s.Top() + s.Left();
1550  tColor *pd = data + wd * d.Top() + d.Left();
1551  for (int y = d.Height(); y-- > 0; ) {
1552  memcpy(pd, ps, w);
1553  ps += ws;
1554  pd += wd;
1555  }
1556  MarkDrawPortDirty(d);
1557  }
1558  }
1559  }
1560  Unlock();
1561 }
1562 
1563 void cPixmapMemory::Scroll(const cPoint &Dest, const cRect &Source)
1564 {
1565  Lock();
1566  cRect s;
1567  if (&Source == &cRect::Null)
1568  s = DrawPort().Shifted(-DrawPort().Point());
1569  else
1570  s = Source.Intersected(DrawPort().Size());
1571  if (!s.IsEmpty()) {
1572  cPoint v = Dest - Source.Point();
1573  cRect d = s.Shifted(v).Intersected(DrawPort().Size());
1574  if (!d.IsEmpty()) {
1575  s = d.Shifted(-v);
1576  if (d.Point() != s.Point()) {
1577  int ws = DrawPort().Width();
1578  int wd = ws;
1579  int w = d.Width() * sizeof(tColor);
1580  const tColor *ps = data + ws * s.Top() + s.Left();
1581  tColor *pd = data + wd * d.Top() + d.Left();
1582  for (int y = d.Height(); y-- > 0; ) {
1583  memmove(pd, ps, w); // source and destination might overlap!
1584  ps += ws;
1585  pd += wd;
1586  }
1587  if (panning)
1588  SetDrawPortPoint(DrawPort().Point().Shifted(s.Point() - d.Point()), false);
1589  else
1590  MarkDrawPortDirty(d);
1591  }
1592  }
1593  }
1594  Unlock();
1595 }
1596 
1597 void cPixmapMemory::Pan(const cPoint &Dest, const cRect &Source)
1598 {
1599  Lock();
1600  panning = true;
1601  Scroll(Dest, Source);
1602  panning = false;
1603  Unlock();
1604 }
1605 
1606 // --- cOsd ------------------------------------------------------------------
1607 
1608 static const char *OsdErrorTexts[] = {
1609  "ok",
1610  "too many areas",
1611  "too many colors",
1612  "bpp not supported",
1613  "areas overlap",
1614  "wrong alignment",
1615  "out of memory",
1616  "wrong area size",
1617  "unknown",
1618  };
1619 
1620 int cOsd::osdLeft = 0;
1621 int cOsd::osdTop = 0;
1622 int cOsd::osdWidth = 0;
1623 int cOsd::osdHeight = 0;
1626 
1627 cOsd::cOsd(int Left, int Top, uint Level)
1628 {
1629  cMutexLock MutexLock(&mutex);
1630  isTrueColor = false;
1631  savedBitmap = NULL;
1632  numBitmaps = 0;
1633  savedPixmap = NULL;
1634  numPixmaps = 0;
1635  left = Left;
1636  top = Top;
1637  width = height = 0;
1638  level = Level;
1639  active = false;
1640  for (int i = 0; i < Osds.Size(); i++) {
1641  if (Osds[i]->level > level) {
1642  Osds.Insert(this, i);
1643  return;
1644  }
1645  }
1646  Osds.Append(this);
1647 }
1648 
1650 {
1651  cMutexLock MutexLock(&mutex);
1652  for (int i = 0; i < numBitmaps; i++)
1653  delete bitmaps[i];
1654  delete savedBitmap;
1655  delete savedPixmap;
1656  for (int i = 0; i < numPixmaps; i++)
1657  delete pixmaps[i];
1658  for (int i = 0; i < Osds.Size(); i++) {
1659  if (Osds[i] == this) {
1660  Osds.Remove(i);
1661  if (Osds.Size())
1662  Osds[0]->SetActive(true);
1663  break;
1664  }
1665  }
1666 }
1667 
1668 void cOsd::SetOsdPosition(int Left, int Top, int Width, int Height)
1669 {
1670  osdLeft = Left;
1671  osdTop = Top;
1674 }
1675 
1676 void cOsd::SetAntiAliasGranularity(uint FixedColors, uint BlendColors)
1677 {
1678  if (isTrueColor)
1679  return;
1680  for (int i = 0; i < numBitmaps; i++)
1681  bitmaps[i]->SetAntiAliasGranularity(FixedColors, BlendColors);
1682 }
1683 
1685 {
1686  if (isTrueColor)
1687  Area = 0; // returns the dummy bitmap
1688  return Area < numBitmaps ? bitmaps[Area] : NULL;
1689 }
1690 
1691 cPixmap *cOsd::CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort)
1692 {
1693  if (isTrueColor) {
1694  LOCK_PIXMAPS;
1695  cPixmap *Pixmap = new cPixmapMemory(Layer, ViewPort, DrawPort);
1696  if (AddPixmap(Pixmap))
1697  return Pixmap;
1698  delete Pixmap;
1699  }
1700  return NULL;
1701 }
1702 
1704 {
1705  if (isTrueColor) {
1706  LOCK_PIXMAPS;
1707  for (int i = 1; i < numPixmaps; i++) { // begin at 1 - don't let the background pixmap be destroyed!
1708  if (pixmaps[i] == Pixmap) {
1709  pixmaps[0]->MarkViewPortDirty(Pixmap->ViewPort());
1710  delete Pixmap;
1711  while (i < numPixmaps - 1) {
1712  pixmaps[i] = pixmaps[i + 1];
1713  i++;
1714  }
1715  numPixmaps--;
1716  return;
1717  }
1718  }
1719  esyslog("ERROR: attempt to destroy an unregistered pixmap");
1720  }
1721 }
1722 
1724 {
1725  if (Pixmap) {
1726  LOCK_PIXMAPS;
1727  if (numPixmaps < MAXOSDPIXMAPS)
1728  return pixmaps[numPixmaps++] = Pixmap;
1729  else
1730  esyslog("ERROR: too many OSD pixmaps requested (maximum is %d)", MAXOSDPIXMAPS);
1731  }
1732  return NULL;
1733 }
1734 
1736 {
1737  cPixmapMemory *Pixmap = NULL;
1738  if (isTrueColor) {
1739  LOCK_PIXMAPS;
1740  // Collect overlapping dirty rectangles:
1741  cRect d;
1742  for (int i = 0; i < numPixmaps; i++) {
1743  cPixmap *pm = pixmaps[i];
1744  if (!pm->DirtyViewPort().IsEmpty()) {
1745  if (d.IsEmpty() || d.Intersects(pm->DirtyViewPort())) {
1746  d.Combine(pm->DirtyViewPort());
1747  pm->SetClean();
1748  }
1749  }
1750  }
1751  if (!d.IsEmpty()) {
1752 //#define DebugDirty
1753 #ifdef DebugDirty
1754  static cRect OldDirty;
1755  cRect NewDirty = d;
1756  d.Combine(OldDirty);
1757  OldDirty = NewDirty;
1758 #endif
1759  Pixmap = new cPixmapMemory(0, d);
1760  Pixmap->Clear();
1761  // Render the individual pixmaps into the resulting pixmap:
1762  for (int Layer = 0; Layer < MAXPIXMAPLAYERS; Layer++) {
1763  for (int i = 0; i < numPixmaps; i++) {
1764  cPixmap *pm = pixmaps[i];
1765  if (pm->Layer() == Layer)
1766  Pixmap->DrawPixmap(pm, d);
1767  }
1768  }
1769 #ifdef DebugDirty
1770  cPixmapMemory DirtyIndicator(7, NewDirty);
1771  static tColor DirtyIndicatorColors[] = { 0x7FFFFF00, 0x7F00FFFF };
1772  static int DirtyIndicatorIndex = 0;
1773  DirtyIndicator.Fill(DirtyIndicatorColors[DirtyIndicatorIndex]);
1774  DirtyIndicatorIndex = 1 - DirtyIndicatorIndex;
1775  Pixmap->Render(&DirtyIndicator, DirtyIndicator.DrawPort(), DirtyIndicator.ViewPort().Point().Shifted(-Pixmap->ViewPort().Point()));
1776 #endif
1777  }
1778  }
1779  return Pixmap;
1780 }
1781 
1782 eOsdError cOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
1783 {
1784  if (NumAreas > MAXOSDAREAS)
1785  return oeTooManyAreas;
1786  eOsdError Result = oeOk;
1787  for (int i = 0; i < NumAreas; i++) {
1788  if (Areas[i].x1 > Areas[i].x2 || Areas[i].y1 > Areas[i].y2 || Areas[i].x1 < 0 || Areas[i].y1 < 0)
1789  return oeWrongAlignment;
1790  for (int j = i + 1; j < NumAreas; j++) {
1791  if (Areas[i].Intersects(Areas[j])) {
1792  Result = oeAreasOverlap;
1793  break;
1794  }
1795  }
1796  if (Areas[i].bpp == 32) {
1797  if (NumAreas > 1)
1798  return oeTooManyAreas;
1799  }
1800  }
1801  return Result;
1802 }
1803 
1804 eOsdError cOsd::SetAreas(const tArea *Areas, int NumAreas)
1805 {
1806  eOsdError Result = CanHandleAreas(Areas, NumAreas);
1807  if (Result == oeOk) {
1808  while (numBitmaps)
1809  delete bitmaps[--numBitmaps];
1810  width = height = 0;
1811  isTrueColor = NumAreas == 1 && Areas[0].bpp == 32;
1812  if (isTrueColor) {
1813  width = Areas[0].x2 - Areas[0].x1 + 1;
1814  height = Areas[0].y2 - Areas[0].y1 + 1;
1815  cPixmap *Pixmap = CreatePixmap(0, cRect(Areas[0].x1, Areas[0].y1, width, height));
1816  Pixmap->Clear();
1817  bitmaps[numBitmaps++] = new cBitmap(10, 10, 8); // dummy bitmap for GetBitmap()
1818  }
1819  else {
1820  for (int i = 0; i < NumAreas; i++) {
1821  bitmaps[numBitmaps++] = new cBitmap(Areas[i].Width(), Areas[i].Height(), Areas[i].bpp, Areas[i].x1, Areas[i].y1);
1822  width = max(width, Areas[i].x2 + 1);
1823  height = max(height, Areas[i].y2 + 1);
1824  }
1825  }
1826  }
1827  else
1828  esyslog("ERROR: cOsd::SetAreas returned %d (%s)", Result, Result < oeUnknown ? OsdErrorTexts[Result] : OsdErrorTexts[oeUnknown]);
1829  return Result;
1830 }
1831 
1832 void cOsd::SaveRegion(int x1, int y1, int x2, int y2)
1833 {
1834  if (isTrueColor) {
1835  delete savedPixmap;
1836  cRect r(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
1837  savedPixmap = new cPixmapMemory(0, r);
1838  savedPixmap->Copy(pixmaps[0], r, cPoint(0, 0));
1839  }
1840  else {
1841  delete savedBitmap;
1842  savedBitmap = new cBitmap(x2 - x1 + 1, y2 - y1 + 1, 8, x1, y1);
1843  for (int i = 0; i < numBitmaps; i++)
1844  savedBitmap->DrawBitmap(bitmaps[i]->X0(), bitmaps[i]->Y0(), *bitmaps[i]);
1845  }
1846 }
1847 
1849 {
1850  if (isTrueColor) {
1851  if (savedPixmap) {
1853  delete savedPixmap;
1854  savedPixmap = NULL;
1855  }
1856  }
1857  else {
1858  if (savedBitmap) {
1860  delete savedBitmap;
1861  savedBitmap = NULL;
1862  }
1863  }
1864 }
1865 
1866 eOsdError cOsd::SetPalette(const cPalette &Palette, int Area)
1867 {
1868  if (isTrueColor)
1869  return oeOk;
1870  if (Area < numBitmaps) {
1871  bitmaps[Area]->Take(Palette);
1872  return oeOk;
1873  }
1874  return oeUnknown;
1875 }
1876 
1877 void cOsd::DrawImage(const cPoint &Point, const cImage &Image)
1878 {
1879  if (isTrueColor)
1880  pixmaps[0]->DrawImage(Point, Image);
1881 }
1882 
1883 void cOsd::DrawImage(const cPoint &Point, int ImageHandle)
1884 {
1885  if (isTrueColor)
1886  pixmaps[0]->DrawImage(Point, ImageHandle);
1887 }
1888 
1889 void cOsd::DrawPixel(int x, int y, tColor Color)
1890 {
1891  if (isTrueColor)
1892  pixmaps[0]->DrawPixel(cPoint(x, y), Color);
1893  else {
1894  for (int i = 0; i < numBitmaps; i++)
1895  bitmaps[i]->DrawPixel(x, y, Color);
1896  }
1897 }
1898 
1899 void cOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
1900 {
1901  if (isTrueColor)
1902  pixmaps[0]->DrawBitmap(cPoint(x, y), Bitmap, ColorFg, ColorBg, Overlay);
1903  else {
1904  for (int i = 0; i < numBitmaps; i++)
1905  bitmaps[i]->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg, ReplacePalette, Overlay);
1906  }
1907 }
1908 
1909 void cOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
1910 {
1911  if (isTrueColor)
1912  pixmaps[0]->DrawText(cPoint(x, y), s, ColorFg, ColorBg, Font, Width, Height, Alignment);
1913  else {
1914  for (int i = 0; i < numBitmaps; i++)
1915  bitmaps[i]->DrawText(x, y, s, ColorFg, ColorBg, Font, Width, Height, Alignment);
1916  }
1917 }
1918 
1919 void cOsd::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
1920 {
1921  if (isTrueColor)
1922  pixmaps[0]->DrawRectangle(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color);
1923  else {
1924  for (int i = 0; i < numBitmaps; i++)
1925  bitmaps[i]->DrawRectangle(x1, y1, x2, y2, Color);
1926  }
1927 }
1928 
1929 void cOsd::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
1930 {
1931  if (isTrueColor)
1932  pixmaps[0]->DrawEllipse(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color, Quadrants);
1933  else {
1934  for (int i = 0; i < numBitmaps; i++)
1935  bitmaps[i]->DrawEllipse(x1, y1, x2, y2, Color, Quadrants);
1936  }
1937 }
1938 
1939 void cOsd::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
1940 {
1941  if (isTrueColor)
1942  pixmaps[0]->DrawSlope(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color, Type);
1943  else {
1944  for (int i = 0; i < numBitmaps; i++)
1945  bitmaps[i]->DrawSlope(x1, y1, x2, y2, Color, Type);
1946  }
1947 }
1948 
1949 void cOsd::Flush(void)
1950 {
1951 }
1952 
1953 // --- cOsdProvider ----------------------------------------------------------
1954 
1956 int cOsdProvider::oldWidth = 0;
1957 int cOsdProvider::oldHeight = 0;
1958 double cOsdProvider::oldAspect = 1.0;
1960 
1962 {
1963  delete osdProvider;
1964  osdProvider = this;
1965 }
1966 
1968 {
1969  osdProvider = NULL;
1970 }
1971 
1972 cOsd *cOsdProvider::NewOsd(int Left, int Top, uint Level)
1973 {
1974  cMutexLock MutexLock(&cOsd::mutex);
1975  if (Level == OSD_LEVEL_DEFAULT && cOsd::IsOpen())
1976  esyslog("ERROR: attempt to open OSD while it is already open - using dummy OSD!");
1977  else if (osdProvider) {
1978  cOsd *ActiveOsd = cOsd::Osds.Size() ? cOsd::Osds[0] : NULL;
1979  cOsd *Osd = osdProvider->CreateOsd(Left, Top, Level);
1980  if (Osd == cOsd::Osds[0]) {
1981  if (ActiveOsd)
1982  ActiveOsd->SetActive(false);
1983  Osd->SetActive(true);
1984  }
1985  return Osd;
1986  }
1987  else
1988  esyslog("ERROR: no OSD provider available - using dummy OSD!");
1989  return new cOsd(Left, Top, 999); // create a dummy cOsd, so that access won't result in a segfault
1990 }
1991 
1993 {
1994  int Width;
1995  int Height;
1996  double Aspect;
1997  cDevice::PrimaryDevice()->GetOsdSize(Width, Height, Aspect);
1998  if (Width != oldWidth || Height != oldHeight || !DoubleEqual(Aspect, oldAspect) || Force) {
1999  Setup.OSDLeft = int(round(Width * Setup.OSDLeftP));
2000  Setup.OSDTop = int(round(Height * Setup.OSDTopP));
2001  Setup.OSDWidth = int(round(Width * Setup.OSDWidthP)) & ~0x07; // OSD width must be a multiple of 8
2002  Setup.OSDHeight = int(round(Height * Setup.OSDHeightP));
2003  Setup.OSDAspect = Aspect;
2004  Setup.FontOsdSize = int(round(Height * Setup.FontOsdSizeP));
2005  Setup.FontFixSize = int(round(Height * Setup.FontFixSizeP));
2006  Setup.FontSmlSize = int(round(Height * Setup.FontSmlSizeP));
2010  oldWidth = Width;
2011  oldHeight = Height;
2012  oldAspect = Aspect;
2013  dsyslog("OSD size changed to %dx%d @ %g", Width, Height, Aspect);
2014  }
2015 }
2016 
2018 {
2019  if (osdProvider) {
2020  return osdProvider->ProvidesTrueColor();
2021  }
2022  else
2023  esyslog("ERROR: no OSD provider available in call to SupportsTrueColor()");
2024  return false;
2025 }
2026 
2028 {
2029  LOCK_PIXMAPS;
2030  for (int i = 1; i < MAXOSDIMAGES; i++) {
2031  if (!images[i]) {
2032  images[i] = new cImage(Image);
2033  return i;
2034  }
2035  }
2036  return 0;
2037 }
2038 
2039 void cOsdProvider::DropImageData(int ImageHandle)
2040 {
2041  LOCK_PIXMAPS;
2042  if (0 < ImageHandle && ImageHandle < MAXOSDIMAGES) {
2043  delete images[ImageHandle];
2044  images[ImageHandle] = NULL;
2045  }
2046 }
2047 
2048 const cImage *cOsdProvider::GetImageData(int ImageHandle)
2049 {
2050  LOCK_PIXMAPS;
2051  if (0 < ImageHandle && ImageHandle < MAXOSDIMAGES)
2052  return images[ImageHandle];
2053  return NULL;
2054 }
2055 
2057 {
2058  if (osdProvider)
2059  return osdProvider->StoreImageData(Image);
2060  return -1;
2061 }
2062 
2063 void cOsdProvider::DropImage(int ImageHandle)
2064 {
2065  if (osdProvider)
2066  osdProvider->DropImageData(ImageHandle);
2067 }
2068 
2070 {
2071  delete osdProvider;
2072  osdProvider = NULL;
2073 }
2074 
2075 // --- cTextScroller ---------------------------------------------------------
2076 
2078 {
2079  osd = NULL;
2080  left = top = width = height = 0;
2081  font = NULL;
2082  colorFg = 0;
2083  colorBg = 0;
2084  offset = 0;
2085  shown = 0;
2086 }
2087 
2088 cTextScroller::cTextScroller(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg)
2089 {
2090  Set(Osd, Left, Top, Width, Height, Text, Font, ColorFg, ColorBg);
2091 }
2092 
2093 void cTextScroller::Set(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg)
2094 {
2095  osd = Osd;
2096  left = Left;
2097  top = Top;
2098  width = Width;
2099  height = Height;
2100  font = Font;
2101  colorFg = ColorFg;
2102  colorBg = ColorBg;
2103  offset = 0;
2104  textWrapper.Set(Text, Font, Width);
2105  shown = min(Total(), height / font->Height());
2106  height = shown * font->Height(); // sets height to the actually used height, which may be less than Height
2107  DrawText();
2108 }
2109 
2111 {
2112  osd = NULL; // just makes sure it won't draw anything
2113 }
2114 
2116 {
2117  if (osd) {
2118  for (int i = 0; i < shown; i++)
2120  }
2121 }
2122 
2123 void cTextScroller::Scroll(bool Up, bool Page)
2124 {
2125  if (Up) {
2126  if (CanScrollUp()) {
2127  offset -= Page ? shown : 1;
2128  if (offset < 0)
2129  offset = 0;
2130  DrawText();
2131  }
2132  }
2133  else {
2134  if (CanScrollDown()) {
2135  offset += Page ? shown : 1;
2136  if (offset + shown > Total())
2137  offset = Total() - shown;
2138  DrawText();
2139  }
2140  }
2141 }