vdr  2.4.7
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 4.11 2020/12/18 23:02:47 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  }
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 
516 {
517  if (bitmap) {
518  memset(bitmap, Index, width * height);
519  dirtyX1 = 0;
520  dirtyY1 = 0;
521  dirtyX2 = width - 1;
522  dirtyY2 = height - 1;
523  }
524 }
525 
526 void cBitmap::DrawPixel(int x, int y, tColor Color)
527 {
528  x -= x0;
529  y -= y0;
530  SetIndex(x, y, Index(Color));
531 }
532 
533 void cBitmap::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
534 {
535  if (bitmap && Bitmap.bitmap && Intersects(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) {
536  if (Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1))
537  Reset();
538  x -= x0;
539  y -= y0;
540  if (ReplacePalette && Covers(x + x0, y + y0, x + x0 + Bitmap.Width() - 1, y + y0 + Bitmap.Height() - 1)) {
541  Replace(Bitmap);
542  for (int ix = 0; ix < Bitmap.width; ix++) {
543  for (int iy = 0; iy < Bitmap.height; iy++) {
544  if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
545  SetIndex(x + ix, y + iy, Bitmap.bitmap[Bitmap.width * iy + ix]);
546  }
547  }
548  }
549  else {
550  tIndexes Indexes;
551  Take(Bitmap, &Indexes, ColorFg, ColorBg);
552  for (int ix = 0; ix < Bitmap.width; ix++) {
553  for (int iy = 0; iy < Bitmap.height; iy++) {
554  if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
555  SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]);
556  }
557  }
558  }
559  }
560 }
561 
562 void cBitmap::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
563 {
564  if (bitmap) {
565  int w = Font->Width(s);
566  int h = Font->Height();
567  int limit = 0;
568  int cw = Width ? Width : w;
569  int ch = Height ? Height : h;
570  if (!Intersects(x, y, x + cw - 1, y + ch - 1))
571  return;
572  if (ColorBg != clrTransparent)
573  DrawRectangle(x, y, x + cw - 1, y + ch - 1, ColorBg);
574  if (Width || Height) {
575  limit = x + cw - x0;
576  if (Width) {
577  if ((Alignment & taLeft) != 0) {
578  if ((Alignment & taBorder) != 0)
579  x += max(h / TEXT_ALIGN_BORDER, 1);
580  }
581  else if ((Alignment & taRight) != 0) {
582  if (w < Width)
583  x += Width - w;
584  if ((Alignment & taBorder) != 0)
585  x -= max(h / TEXT_ALIGN_BORDER, 1);
586  }
587  else { // taCentered
588  if (w < Width)
589  x += (Width - w) / 2;
590  }
591  }
592  if (Height) {
593  if ((Alignment & taTop) != 0)
594  ;
595  else if ((Alignment & taBottom) != 0) {
596  if (h < Height)
597  y += Height - h;
598  }
599  else { // taCentered
600  if (h < Height)
601  y += (Height - h) / 2;
602  }
603  }
604  }
605  x -= x0;
606  y -= y0;
607  Font->DrawText(this, x, y, s, ColorFg, ColorBg, limit);
608  }
609 }
610 
611 void cBitmap::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
612 {
613  if (bitmap && Intersects(x1, y1, x2, y2)) {
614  if (Covers(x1, y1, x2, y2))
615  Reset();
616  x1 -= x0;
617  y1 -= y0;
618  x2 -= x0;
619  y2 -= y0;
620  x1 = max(x1, 0);
621  y1 = max(y1, 0);
622  x2 = min(x2, width - 1);
623  y2 = min(y2, height - 1);
624  tIndex c = Index(Color);
625  for (int y = y1; y <= y2; y++) {
626  for (int x = x1; x <= x2; x++)
627  SetIndex(x, y, c);
628  }
629  }
630 }
631 
632 void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
633 {
634  if (!Intersects(x1, y1, x2, y2))
635  return;
636  // Algorithm based on https://dai.fmph.uniba.sk/upload/0/01/Ellipse.pdf
637  int rx = x2 - x1;
638  int ry = y2 - y1;
639  int cx = (x1 + x2) / 2;
640  int cy = (y1 + y2) / 2;
641  switch (abs(Quadrants)) {
642  case 0: rx /= 2; ry /= 2; break;
643  case 1: cx = x1; cy = y2; break;
644  case 2: cx = x2; cy = y2; break;
645  case 3: cx = x2; cy = y1; break;
646  case 4: cx = x1; cy = y1; break;
647  case 5: cx = x1; ry /= 2; break;
648  case 6: cy = y2; rx /= 2; break;
649  case 7: cx = x2; ry /= 2; break;
650  case 8: cy = y1; rx /= 2; break;
651  default: ;
652  }
653  int TwoASquare = max(1, 2 * rx * rx);
654  int TwoBSquare = max(1, 2 * ry * ry);
655  int x = rx;
656  int y = 0;
657  int XChange = ry * ry * (1 - 2 * rx);
658  int YChange = rx * rx;
659  int EllipseError = 0;
660  int StoppingX = TwoBSquare * rx;
661  int StoppingY = 0;
662  while (StoppingX >= StoppingY) {
663  switch (Quadrants) {
664  case 5: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); // no break
665  case 1: DrawRectangle(cx, cy - y, cx + x, cy - y, Color); break;
666  case 7: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); // no break
667  case 2: DrawRectangle(cx - x, cy - y, cx, cy - y, Color); break;
668  case 3: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); break;
669  case 4: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); break;
670  case 0:
671  case 6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break;
672  case 8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break;
673  case -1: DrawRectangle(cx + x, cy - y, x2, cy - y, Color); break;
674  case -2: DrawRectangle(x1, cy - y, cx - x, cy - y, Color); break;
675  case -3: DrawRectangle(x1, cy + y, cx - x, cy + y, Color); break;
676  case -4: DrawRectangle(cx + x, cy + y, x2, cy + y, Color); break;
677  default: ;
678  }
679  y++;
680  StoppingY += TwoASquare;
681  EllipseError += YChange;
682  YChange += TwoASquare;
683  if (2 * EllipseError + XChange > 0) {
684  x--;
685  StoppingX -= TwoBSquare;
686  EllipseError += XChange;
687  XChange += TwoBSquare;
688  }
689  }
690  x = 0;
691  y = ry;
692  XChange = ry * ry;
693  YChange = rx * rx * (1 - 2 * ry);
694  EllipseError = 0;
695  StoppingX = 0;
696  StoppingY = TwoASquare * ry;
697  while (StoppingX <= StoppingY) {
698  switch (Quadrants) {
699  case 5: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); // no break
700  case 1: DrawRectangle(cx, cy - y, cx + x, cy - y, Color); break;
701  case 7: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); // no break
702  case 2: DrawRectangle(cx - x, cy - y, cx, cy - y, Color); break;
703  case 3: DrawRectangle(cx - x, cy + y, cx, cy + y, Color); break;
704  case 4: DrawRectangle(cx, cy + y, cx + x, cy + y, Color); break;
705  case 0:
706  case 6: DrawRectangle(cx - x, cy - y, cx + x, cy - y, Color); if (Quadrants == 6) break;
707  case 8: DrawRectangle(cx - x, cy + y, cx + x, cy + y, Color); break;
708  case -1: DrawRectangle(cx + x, cy - y, x2, cy - y, Color); break;
709  case -2: DrawRectangle(x1, cy - y, cx - x, cy - y, Color); break;
710  case -3: DrawRectangle(x1, cy + y, cx - x, cy + y, Color); break;
711  case -4: DrawRectangle(cx + x, cy + y, x2, cy + y, Color); break;
712  default: ;
713  }
714  x++;
715  StoppingX += TwoBSquare;
716  EllipseError += XChange;
717  XChange += TwoBSquare;
718  if (2 * EllipseError + YChange > 0) {
719  y--;
720  StoppingY -= TwoASquare;
721  EllipseError += YChange;
722  YChange += TwoASquare;
723  }
724  }
725 }
726 
727 void cBitmap::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
728 {
729  if (!Intersects(x1, y1, x2, y2))
730  return;
731  bool upper = Type & 0x01;
732  bool falling = Type & 0x02;
733  bool vertical = Type & 0x04;
734  if (vertical) {
735  for (int y = y1; y <= y2; y++) {
736  double c = cos((y - y1) * M_PI / (y2 - y1 + 1));
737  if (falling)
738  c = -c;
739  int x = int((x2 - x1 + 1) * c / 2);
740  if (upper && !falling || !upper && falling)
741  DrawRectangle(x1, y, (x1 + x2) / 2 + x, y, Color);
742  else
743  DrawRectangle((x1 + x2) / 2 + x, y, x2, y, Color);
744  }
745  }
746  else {
747  for (int x = x1; x <= x2; x++) {
748  double c = cos((x - x1) * M_PI / (x2 - x1 + 1));
749  if (falling)
750  c = -c;
751  int y = int((y2 - y1 + 1) * c / 2);
752  if (upper)
753  DrawRectangle(x, y1, x, (y1 + y2) / 2 + y, Color);
754  else
755  DrawRectangle(x, (y1 + y2) / 2 + y, x, y2, Color);
756  }
757  }
758 }
759 
760 const tIndex *cBitmap::Data(int x, int y) const
761 {
762  return &bitmap[y * width + x];
763 }
764 
765 void cBitmap::ReduceBpp(const cPalette &Palette)
766 {
767  int NewBpp = Palette.Bpp();
768  if (Bpp() == 4 && NewBpp == 2) {
769  for (int i = width * height; i--; ) {
770  tIndex p = bitmap[i];
771  bitmap[i] = (p >> 2) | ((p & 0x03) != 0);
772  }
773  }
774  else if (Bpp() == 8) {
775  if (NewBpp == 2) {
776  for (int i = width * height; i--; ) {
777  tIndex p = bitmap[i];
778  bitmap[i] = (p >> 6) | ((p & 0x30) != 0);
779  }
780  }
781  else if (NewBpp == 4) {
782  for (int i = width * height; i--; ) {
783  tIndex p = bitmap[i];
784  bitmap[i] = p >> 4;
785  }
786  }
787  else
788  return;
789  }
790  else
791  return;
792  SetBpp(NewBpp);
793  Replace(Palette);
794 }
795 
796 void cBitmap::ShrinkBpp(int NewBpp)
797 {
798  int NumOldColors;
799  const tColor *Colors = this->Colors(NumOldColors);
800  if (Colors) {
801  // Find the most frequently used colors and create a map table:
802  int Used[MAXNUMCOLORS] = { 0 };
803  int Map[MAXNUMCOLORS] = { 0 };
804  for (int i = width * height; i--; )
805  Used[bitmap[i]]++;
806  int MaxNewColors = (NewBpp == 4) ? 16 : 4;
807  cPalette NewPalette(NewBpp);
808  for (int i = 0; i < MaxNewColors; i++) {
809  int Max = 0;
810  int Index = -1;
811  for (int n = 0; n < NumOldColors; n++) {
812  if (Used[n] > Max) {
813  Max = Used[n];
814  Index = n;
815  }
816  }
817  if (Index >= 0) {
818  Used[Index] = 0;
819  Map[Index] = i;
820  NewPalette.SetColor(i, Colors[Index]);
821  }
822  else
823  break;
824  }
825  // Complete the map table for all other colors (will be set to closest match):
826  for (int n = 0; n < NumOldColors; n++) {
827  if (Used[n])
828  Map[n] = NewPalette.Index(Colors[n]);
829  }
830  // Do the actual index mapping:
831  for (int i = width * height; i--; )
832  bitmap[i] = Map[bitmap[i]];
833  SetBpp(NewBpp);
834  Replace(NewPalette);
835  }
836 }
837 
838 cBitmap *cBitmap::Scaled(double FactorX, double FactorY, bool AntiAlias) const
839 {
840  // Fixed point scaling code based on www.inversereality.org/files/bitmapscaling.pdf
841  // by deltener@mindtremors.com
842  int w = max(1, int(round(Width() * FactorX)));
843  int h = max(1, int(round(Height() * FactorY)));
844  cBitmap *b = new cBitmap(w, h, Bpp(), X0(), Y0());
845  int RatioX = (Width() << 16) / b->Width();
846  int RatioY = (Height() << 16) / b->Height();
847  if (!AntiAlias || FactorX <= 1.0 && FactorY <= 1.0) {
848  // Downscaling - no anti-aliasing:
849  b->Replace(*this); // copy palette
850  tIndex *DestRow = b->bitmap;
851  int SourceY = 0;
852  for (int y = 0; y < b->Height(); y++) {
853  int SourceX = 0;
854  tIndex *SourceRow = bitmap + (SourceY >> 16) * Width();
855  tIndex *Dest = DestRow;
856  for (int x = 0; x < b->Width(); x++) {
857  *Dest++ = SourceRow[SourceX >> 16];
858  SourceX += RatioX;
859  }
860  SourceY += RatioY;
861  DestRow += b->Width();
862  }
863  }
864  else {
865  // Upscaling - anti-aliasing:
866  b->SetBpp(8);
867  b->Replace(*this); // copy palette (must be done *after* SetBpp()!)
868  int SourceY = 0;
869  for (int y = 0; y < b->Height(); y++) {
870  int SourceX = 0;
871  int sy = min(SourceY >> 16, Height() - 2);
872  uint8_t BlendY = 0xFF - ((SourceY >> 8) & 0xFF);
873  for (int x = 0; x < b->Width(); x++) {
874  int sx = min(SourceX >> 16, Width() - 2);
875  uint8_t BlendX = 0xFF - ((SourceX >> 8) & 0xFF);
876  tColor c1 = b->Blend(GetColor(sx, sy), GetColor(sx + 1, sy), BlendX);
877  tColor c2 = b->Blend(GetColor(sx, sy + 1), GetColor(sx + 1, sy + 1), BlendX);
878  tColor c3 = b->Blend(c1, c2, BlendY);
879  b->DrawPixel(x + X0(), y + Y0(), c3);
880  SourceX += RatioX;
881  }
882  SourceY += RatioY;
883  }
884  }
885  return b;
886 }
887 
888 // --- cRect -----------------------------------------------------------------
889 
890 const cRect cRect::Null;
891 
892 void cRect::Grow(int Dx, int Dy)
893 {
894  point.Shift(-Dx, -Dy);
895  size.Grow(Dx, Dy);
896 }
897 
898 bool cRect::Contains(const cPoint &Point) const
899 {
900  return Left() <= Point.X() &&
901  Top() <= Point.Y() &&
902  Right() >= Point.X() &&
903  Bottom() >= Point.Y();
904 }
905 
906 bool cRect::Contains(const cRect &Rect) const
907 {
908  return Left() <= Rect.Left() &&
909  Top() <= Rect.Top() &&
910  Right() >= Rect.Right() &&
911  Bottom() >= Rect.Bottom();
912 }
913 
914 bool cRect::Intersects(const cRect &Rect) const
915 {
916  return !(Left() > Rect.Right() ||
917  Top() > Rect.Bottom() ||
918  Right() < Rect.Left() ||
919  Bottom() < Rect.Top());
920 }
921 
922 cRect cRect::Intersected(const cRect &Rect) const
923 {
924  cRect r;
925  if (!IsEmpty() && !Rect.IsEmpty()) {
926  r.SetLeft(max(Left(), Rect.Left()));
927  r.SetTop(max(Top(), Rect.Top()));
928  r.SetRight(min(Right(), Rect.Right()));
929  r.SetBottom(min(Bottom(), Rect.Bottom()));
930  }
931  return r;
932 }
933 
934 void cRect::Combine(const cRect &Rect)
935 {
936  if (IsEmpty())
937  *this = Rect;
938  if (Rect.IsEmpty())
939  return;
940  // must set right/bottom *before* top/left!
941  SetRight(max(Right(), Rect.Right()));
942  SetBottom(max(Bottom(), Rect.Bottom()));
943  SetLeft(min(Left(), Rect.Left()));
944  SetTop(min(Top(), Rect.Top()));
945 }
946 
947 void cRect::Combine(const cPoint &Point)
948 {
949  if (IsEmpty())
950  Set(Point.X(), Point.Y(), 1, 1);
951  // must set right/bottom *before* top/left!
952  SetRight(max(Right(), Point.X()));
953  SetBottom(max(Bottom(), Point.Y()));
954  SetLeft(min(Left(), Point.X()));
955  SetTop(min(Top(), Point.Y()));
956 }
957 
958 // --- cPixmap ---------------------------------------------------------------
959 
961 
963 {
964  layer = -1;
966  tile = false;
967 }
968 
969 cPixmap::cPixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort)
970 {
971  layer = Layer;
972  if (layer >= MAXPIXMAPLAYERS) {
973  layer = MAXPIXMAPLAYERS - 1;
974  esyslog("ERROR: pixmap layer %d limited to %d", Layer, layer);
975  }
976  viewPort = ViewPort;
977  if (!DrawPort.IsEmpty())
978  drawPort = DrawPort;
979  else {
980  drawPort = viewPort;
981  drawPort.SetPoint(0, 0);
982  }
984  tile = false;
985 }
986 
988 {
989  if (layer >= 0)
991 }
992 
994 {
995  if (layer >= 0 && viewPort.Contains(Point))
996  dirtyViewPort.Combine(Point);
997 }
998 
1000 {
1002  if (tile)
1004  else
1006 }
1007 
1009 {
1010  if (drawPort.Contains(Point)) {
1011  dirtyDrawPort.Combine(Point);
1012  if (tile)
1014  else
1016  }
1017 }
1018 
1020 {
1022 }
1023 
1024 void cPixmap::SetLayer(int Layer)
1025 {
1026  Lock();
1027  if (Layer >= MAXPIXMAPLAYERS) {
1028  esyslog("ERROR: pixmap layer %d limited to %d", Layer, MAXPIXMAPLAYERS - 1);
1029  Layer = MAXPIXMAPLAYERS - 1;
1030  }
1031  // The sequence here is important, because the view port is only marked as dirty
1032  // if the layer is >= 0:
1033  if (layer >= 0) {
1034  MarkViewPortDirty(viewPort); // the pixmap is visible and may or may not become invisible
1035  layer = Layer;
1036  }
1037  else if (Layer >= 0) {
1038  layer = Layer;
1039  MarkViewPortDirty(viewPort); // the pixmap was invisible and has become visible
1040  }
1041  else
1042  layer = Layer; // the pixmap was invisible and remains so
1043  Unlock();
1044 }
1045 
1046 void cPixmap::SetAlpha(int Alpha)
1047 {
1048  Lock();
1050  if (Alpha != alpha) {
1052  alpha = Alpha;
1053  }
1054  Unlock();
1055 }
1056 
1057 void cPixmap::SetTile(bool Tile)
1058 {
1059  Lock();
1060  if (Tile != tile) {
1061  if (drawPort.Point() != cPoint(0, 0) || drawPort.Width() < viewPort.Width() || drawPort.Height() < viewPort.Height())
1063  tile = Tile;
1064  }
1065  Unlock();
1066 }
1067 
1068 void cPixmap::SetViewPort(const cRect &Rect)
1069 {
1070  Lock();
1071  if (Rect != viewPort) {
1072  if (tile)
1074  else
1076  viewPort = Rect;
1077  if (tile)
1079  else
1081  }
1082  Unlock();
1083 }
1084 
1085 void cPixmap::SetDrawPortPoint(const cPoint &Point, bool Dirty)
1086 {
1087  Lock();
1088  if (Point != drawPort.Point()) {
1089  if (Dirty) {
1090  if (tile)
1092  else
1094  }
1095  drawPort.SetPoint(Point);
1096  if (Dirty && !tile)
1098  }
1099  Unlock();
1100 }
1101 
1102 // --- cImage ----------------------------------------------------------------
1103 
1105 {
1106  data = NULL;
1107 }
1108 
1109 cImage::cImage(const cImage &Image)
1110 {
1111  size = Image.Size();
1112  int l = size.Width() * size.Height();
1113  data = MALLOC(tColor, l);
1114  memcpy(data, Image.Data(), l * sizeof(tColor));
1115 }
1116 
1117 cImage::cImage(const cSize &Size, const tColor *Data)
1118 {
1119  size = Size;
1120  int l = size.Width() * size.Height();
1121  data = MALLOC(tColor, l);
1122  if (Data)
1123  memcpy(data, Data, l * sizeof(tColor));
1124 }
1125 
1127 {
1128  free(data);
1129 }
1130 
1131 void cImage::Clear(void)
1132 {
1133  memset(data, 0x00, Width() * Height() * sizeof(tColor));
1134 }
1135 
1137 {
1138  for (int i = Width() * Height() - 1; i >= 0; i--)
1139  data[i] = Color;
1140 }
1141 
1142 // --- cPixmapMemory ---------------------------------------------------------
1143 
1145 {
1146  data = NULL;
1147  panning = false;
1148 }
1149 
1150 cPixmapMemory::cPixmapMemory(int Layer, const cRect &ViewPort, const cRect &DrawPort)
1151 :cPixmap(Layer, ViewPort, DrawPort)
1152 {
1153  data = MALLOC(tColor, this->DrawPort().Width() * this->DrawPort().Height());
1154  panning = false;
1155 }
1156 
1158 {
1159  free(data);
1160 }
1161 
1163 {
1164  Lock();
1165  memset(data, 0x00, DrawPort().Width() * DrawPort().Height() * sizeof(tColor));
1167  Unlock();
1168 }
1169 
1171 {
1172  Lock();
1173  for (int i = DrawPort().Width() * DrawPort().Height() - 1; i >= 0; i--)
1174  data[i] = Color;
1176  Unlock();
1177 }
1178 
1179 void cPixmap::DrawPixmap(const cPixmap *Pixmap, const cRect &Dirty)
1180 {
1181  if (Pixmap->Tile() && (Pixmap->DrawPort().Point() != cPoint(0, 0) || Pixmap->DrawPort().Size() < Pixmap->ViewPort().Size())) {
1182  cPoint t0 = Pixmap->DrawPort().Point().Shifted(Pixmap->ViewPort().Point()); // the origin of the draw port in absolute OSD coordinates
1183  // Find the top/leftmost location where the draw port touches the view port:
1184  while (t0.X() > Pixmap->ViewPort().Left())
1185  t0.Shift(-Pixmap->DrawPort().Width(), 0);
1186  while (t0.Y() > Pixmap->ViewPort().Top())
1187  t0.Shift(0, -Pixmap->DrawPort().Height());
1188  cPoint t = t0;
1189  while (t.Y() <= Pixmap->ViewPort().Bottom()) {
1190  while (t.X() <= Pixmap->ViewPort().Right()) {
1191  cRect Source = Pixmap->DrawPort(); // assume the entire pixmap needs to be rendered
1192  Source.Shift(Pixmap->ViewPort().Point()); // Source is now in absolute OSD coordinates
1193  cPoint Delta = Source.Point() - t;
1194  Source.SetPoint(t); // Source is now where the pixmap's data shall be drawn
1195  Source = Source.Intersected(Pixmap->ViewPort()); // Source is now limited to the pixmap's view port
1196  Source = Source.Intersected(Dirty); // Source is now limited to the actual dirty rectangle
1197  if (!Source.IsEmpty()) {
1198  cPoint Dest = Source.Point().Shifted(-ViewPort().Point()); // remember the destination point
1199  Source.Shift(Delta); // Source is now back at the pixmap's draw port location, still in absolute OSD coordinates
1200  Source.Shift(-Pixmap->ViewPort().Point()); // Source is now relative to the pixmap's view port again
1201  Source.Shift(-Pixmap->DrawPort().Point()); // Source is now relative to the pixmap's data
1202  if (Pixmap->Layer() == 0)
1203  Copy(Pixmap, Source, Dest); // this is the "background" pixmap
1204  else
1205  Render(Pixmap, Source, Dest); // all others are alpha blended over the background
1206  }
1207  t.Shift(Pixmap->DrawPort().Width(), 0); // increase one draw port width to the right
1208  }
1209  t.SetX(t0.X()); // go back to the leftmost position
1210  t.Shift(0, Pixmap->DrawPort().Height()); // increase one draw port height down
1211  }
1212  }
1213  else {
1214  cRect Source = Pixmap->DrawPort(); // assume the entire pixmap needs to be rendered
1215  Source.Shift(Pixmap->ViewPort().Point()); // Source is now in absolute OSD coordinates
1216  Source = Source.Intersected(Pixmap->ViewPort()); // Source is now limited to the pixmap's view port
1217  Source = Source.Intersected(Dirty); // Source is now limited to the actual dirty rectangle
1218  if (!Source.IsEmpty()) {
1219  cPoint Dest = Source.Point().Shifted(-ViewPort().Point()); // remember the destination point
1220  Source.Shift(-Pixmap->ViewPort().Point()); // Source is now relative to the pixmap's draw port again
1221  Source.Shift(-Pixmap->DrawPort().Point()); // Source is now relative to the pixmap's data
1222  if (Pixmap->Layer() == 0)
1223  Copy(Pixmap, Source, Dest); // this is the "background" pixmap
1224  else
1225  Render(Pixmap, Source, Dest); // all others are alpha blended over the background
1226  }
1227  }
1228 }
1229 
1230 void cPixmapMemory::DrawImage(const cPoint &Point, const cImage &Image)
1231 {
1232  Lock();
1233  cRect r = cRect(Point, Image.Size()).Intersected(DrawPort().Size());
1234  if (!r.IsEmpty()) {
1235  int ws = Image.Size().Width();
1236  int wd = DrawPort().Width();
1237  int w = r.Width() * sizeof(tColor);
1238  const tColor *ps = Image.Data();
1239  if (Point.Y() < 0)
1240  ps -= Point.Y() * ws;
1241  if (Point.X() < 0)
1242  ps -= Point.X();
1243  tColor *pd = data + wd * r.Top() + r.Left();
1244  for (int y = r.Height(); y-- > 0; ) {
1245  memcpy(pd, ps, w);
1246  ps += ws;
1247  pd += wd;
1248  }
1249  MarkDrawPortDirty(r);
1250  }
1251  Unlock();
1252 }
1253 
1254 void cPixmapMemory::DrawImage(const cPoint &Point, int ImageHandle)
1255 {
1256  Lock();
1257  if (const cImage *Image = cOsdProvider::GetImageData(ImageHandle))
1258  DrawImage(Point, *Image);
1259  Unlock();
1260 }
1261 
1262 void cPixmapMemory::DrawPixel(const cPoint &Point, tColor Color)
1263 {
1264  Lock();
1265  if (DrawPort().Size().Contains(Point)) {
1266  int p = Point.Y() * DrawPort().Width() + Point.X();
1267  if (Layer() == 0 && !IS_OPAQUE(Color))
1268  data[p] = AlphaBlend(Color, data[p]);
1269  else
1270  data[p] = Color;
1271  MarkDrawPortDirty(Point);
1272  }
1273  Unlock();
1274 }
1275 
1276 void cPixmapMemory::DrawBlendedPixel(const cPoint &Point, tColor Color, uint8_t Alpha)
1277 {
1278  Lock();
1279  if (DrawPort().Size().Contains(Point)) {
1280  int p = Point.Y() * DrawPort().Width() + Point.X();
1281  if (Alpha != ALPHA_OPAQUE) {
1282  if (Color == clrTransparent)
1283  data[p] = (data[p] & 0x00FFFFFF) | ((((data[p] >> 24) * (255 - Alpha)) << 16) & 0xFF000000);
1284  else
1285  data[p] = AlphaBlend(Color, data[p], Alpha);
1286  }
1287  else
1288  data[p] = Color;
1289  MarkDrawPortDirty(Point);
1290  }
1291  Unlock();
1292 }
1293 
1294 void cPixmapMemory::DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool Overlay)
1295 {
1296  Lock();
1297  cRect r = cRect(Point, cSize(Bitmap.Width(), Bitmap.Height())).Intersected(DrawPort().Size());
1298  if (!r.IsEmpty()) {
1299  bool UseColors = ColorFg || ColorBg;
1300  int wd = DrawPort().Width();
1301  tColor *pd = data + wd * r.Top() + r.Left();
1302  for (int y = r.Top(); y <= r.Bottom(); y++) {
1303  tColor *cd = pd;
1304  for (int x = r.Left(); x <= r.Right(); x++) {
1305  tIndex Index = *Bitmap.Data(x - Point.X(), y - Point.Y());
1306  if (Index || !Overlay) {
1307  if (UseColors)
1308  *cd = Index ? ColorFg : ColorBg;
1309  else
1310  *cd = Bitmap.Color(Index);
1311  }
1312  cd++;
1313  }
1314  pd += wd;
1315  }
1316  MarkDrawPortDirty(r);
1317  }
1318  Unlock();
1319 }
1320 
1321 void cPixmapMemory::DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
1322 {
1323  Lock();
1324  int x = Point.X();
1325  int y = Point.Y();
1326  int w = Font->Width(s);
1327  int h = Font->Height();
1328  int limit = 0;
1329  int cw = Width ? Width : w;
1330  int ch = Height ? Height : h;
1331  cRect r(x, y, cw, ch);
1332  if (ColorBg != clrTransparent)
1333  DrawRectangle(r, ColorBg);
1334  if (Width || Height) {
1335  limit = x + cw;
1336  if (Width) {
1337  if ((Alignment & taLeft) != 0) {
1338  if ((Alignment & taBorder) != 0)
1339  x += max(h / TEXT_ALIGN_BORDER, 1);
1340  }
1341  else if ((Alignment & taRight) != 0) {
1342  if (w < Width)
1343  x += Width - w;
1344  if ((Alignment & taBorder) != 0)
1345  x -= max(h / TEXT_ALIGN_BORDER, 1);
1346  }
1347  else { // taCentered
1348  if (w < Width)
1349  x += (Width - w) / 2;
1350  }
1351  }
1352  if (Height) {
1353  if ((Alignment & taTop) != 0)
1354  ;
1355  else if ((Alignment & taBottom) != 0) {
1356  if (h < Height)
1357  y += Height - h;
1358  }
1359  else { // taCentered
1360  if (h < Height)
1361  y += (Height - h) / 2;
1362  }
1363  }
1364  }
1365  Font->DrawText(this, x, y, s, ColorFg, ColorBg, limit);
1366  MarkDrawPortDirty(r);
1367  Unlock();
1368 }
1369 
1371 {
1372  Lock();
1373  cRect r = Rect.Intersected(DrawPort().Size());
1374  if (!r.IsEmpty()) {
1375  int wd = DrawPort().Width();
1376  int w = r.Width() * sizeof(tColor);
1377  tColor *ps = NULL;
1378  tColor *pd = data + wd * r.Top() + r.Left();
1379  for (int y = r.Height(); y-- > 0; ) {
1380  if (ps)
1381  memcpy(pd, ps, w); // all other lines are copied fast from the first one
1382  else {
1383  // explicitly fill the first line:
1384  tColor *cd = ps = pd;
1385  for (int x = r.Width(); x-- > 0; ) {
1386  *cd = Color;
1387  cd++;
1388  }
1389  }
1390  pd += wd;
1391  }
1392  MarkDrawPortDirty(r);
1393  }
1394  Unlock();
1395 }
1396 
1397 void cPixmapMemory::DrawEllipse(const cRect &Rect, tColor Color, int Quadrants)
1398 {
1399  Lock();
1400  // Algorithm based on https://dai.fmph.uniba.sk/upload/0/01/Ellipse.pdf
1401  int x1 = Rect.Left();
1402  int y1 = Rect.Top();
1403  int x2 = Rect.Right();
1404  int y2 = Rect.Bottom();
1405  int rx = x2 - x1;
1406  int ry = y2 - y1;
1407  int ax = rx & 0x01; // alignment to make semi-circles match rectangles of same size
1408  int ay = ry & 0x01;
1409  int cx = (x1 + x2) / 2;
1410  int cy = (y1 + y2) / 2;
1411  switch (abs(Quadrants)) {
1412  case 0: rx /= 2; ry /= 2; break;
1413  case 1: cx = x1; cy = y2; break;
1414  case 2: cx = x2; cy = y2; break;
1415  case 3: cx = x2; cy = y1; break;
1416  case 4: cx = x1; cy = y1; break;
1417  case 5: cx = x1; ry /= 2; break;
1418  case 6: cy = y2; rx /= 2; break;
1419  case 7: cx = x2; ry /= 2; break;
1420  case 8: cy = y1; rx /= 2; break;
1421  default: ;
1422  }
1423  int TwoASquare = max(1, 2 * rx * rx);
1424  int TwoBSquare = max(1, 2 * ry * ry);
1425  int x = rx;
1426  int y = 0;
1427  int XChange = ry * ry * (1 - 2 * rx);
1428  int YChange = rx * rx;
1429  int EllipseError = 0;
1430  int StoppingX = TwoBSquare * rx;
1431  int StoppingY = 0;
1432  int Delta = 0;
1433  bool AntiAliased = Setup.AntiAlias;
1434  while (StoppingX >= StoppingY) {
1435  if (!AntiAliased) {
1436  switch (Quadrants) {
1437  case 5: DrawRectangle(cRect(cx, cy + y + ay, x + 1, 1), Color); // no break
1438  case 1: DrawRectangle(cRect(cx, cy - y, x + 1, 1), Color); break;
1439  case 7: DrawRectangle(cRect(cx - x, cy + y + ay, x + 1, 1), Color); // no break
1440  case 2: DrawRectangle(cRect(cx - x, cy - y, x + 1, 1), Color); break;
1441  case 3: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); break;
1442  case 4: DrawRectangle(cRect(cx, cy + y, x + 1, 1), Color); break;
1443  case 0:
1444  case 6: DrawRectangle(cRect(cx - x, cy - y, 2 * x + ax + 1, 1), Color); if (Quadrants == 6) break;
1445  case 8: DrawRectangle(cRect(cx - x, cy + y, 2 * x + ax + 1, 1), Color); break;
1446  case -1: DrawRectangle(cRect(cx + x, cy - y, rx - x + 1, 1), Color); break;
1447  case -2: DrawRectangle(cRect(x1, cy - y, cx - x - x1 + 1, 1), Color); break;
1448  case -3: DrawRectangle(cRect(x1, cy + y, cx - x - x1 + 1, 1), Color); break;
1449  case -4: DrawRectangle(cRect(cx + x, cy + y, rx - x + 1, 1), Color); break;
1450  default: ;
1451  }
1452  }
1453  else {
1454  uint8_t intensity = abs(255 * (long int)EllipseError / XChange);
1455  if (EllipseError >= 0) {
1456  intensity = 255 - intensity;
1457  Delta = 0;
1458  }
1459  else
1460  Delta = 1;
1461  switch (Quadrants) {
1462  case 5: DrawRectangle( cRect( cx, cy + y + ay, x + Delta, 1), Color);
1463  DrawBlendedPixel(cPoint(cx + x + Delta, cy + y + ay), Color, intensity);
1464  // no break
1465  case 1: DrawRectangle( cRect( cx, cy - y, x + Delta, 1), Color);
1466  DrawBlendedPixel(cPoint(cx + x + Delta, cy - y), Color, intensity);
1467  break;
1468  case 7: DrawRectangle( cRect( cx - x + 1 - Delta, cy + ay + y, x + Delta, 1), Color);
1469  DrawBlendedPixel(cPoint(cx - x - Delta, cy + ay + y), Color, intensity);
1470  // no break
1471  case 2: DrawRectangle( cRect( cx - x + 1 - Delta, cy - y, x + Delta, 1), Color);
1472  DrawBlendedPixel(cPoint(cx - x - Delta, cy - y), Color, intensity);
1473  break;
1474  case 3: DrawRectangle( cRect( cx - x + 1 - Delta, cy + y, x + Delta, 1), Color);
1475  DrawBlendedPixel(cPoint(cx - x - Delta, cy + y), Color, intensity);
1476  break;
1477  case 4: DrawRectangle( cRect( cx, cy + y, x + Delta, 1), Color);
1478  DrawBlendedPixel(cPoint(cx + x + Delta, cy + y), Color, intensity);
1479  break;
1480  case 0:
1481  case 6: DrawRectangle( cRect( cx - x - Delta + 1, cy - y, 2 * (x + Delta) + ax - 1, 1), Color);
1482  DrawBlendedPixel(cPoint(cx - x - Delta, cy - y), Color, intensity);
1483  DrawBlendedPixel(cPoint(cx + x + Delta + ax, cy - y), Color, intensity);
1484  if (Quadrants == 6)
1485  break;
1486  case 8: DrawRectangle( cRect( cx - x - Delta + 1, cy + y, 2 * (x + Delta) + ax - 1 , 1), Color);
1487  DrawBlendedPixel(cPoint(cx - x - Delta, cy + y), Color, intensity);
1488  DrawBlendedPixel(cPoint(cx + x + Delta + ax, cy + y), Color, intensity);
1489  break;
1490  case -1: DrawRectangle( cRect( cx + x + 1 + Delta, cy - y, rx - (x + Delta), 1), Color);
1491  DrawBlendedPixel(cPoint(cx + x + Delta, cy - y), Color, 255-intensity);
1492  break;
1493  case -2: DrawRectangle( cRect( x1, cy - y, rx - x - Delta, 1), Color);
1494  DrawBlendedPixel(cPoint(cx - x - Delta, cy - y), Color, 255-intensity);
1495  break;
1496  case -3: DrawRectangle( cRect( x1, cy + y, rx - x - Delta, 1), Color);
1497  DrawBlendedPixel(cPoint(cx - x - Delta, cy + y), Color, 255-intensity);
1498  break;
1499  case -4: DrawRectangle( cRect( cx + x + 1 + Delta, cy + y, rx - x - Delta, 1), Color);
1500  DrawBlendedPixel(cPoint(cx + x + Delta, cy + y), Color, 255-intensity);
1501  break;
1502  default: ;
1503  }
1504  }
1505  y++;
1506  StoppingY += TwoASquare;
1507  EllipseError += YChange;
1508  YChange += TwoASquare;
1509  if (2 * EllipseError + XChange > 0) {
1510  x--;
1511  StoppingX -= TwoBSquare;
1512  EllipseError += XChange;
1513  XChange += TwoBSquare;
1514  }
1515  }
1516  int ymax = y - 1;
1517  x = 0;
1518  y = ry;
1519  XChange = ry * ry;
1520  YChange = rx * rx * (1 - 2 * ry);
1521  EllipseError = 0;
1522  StoppingX = 0;
1523  StoppingY = TwoASquare * ry;
1524  while (StoppingX <= StoppingY) {
1525  if (!AntiAliased) {
1526  switch (Quadrants) {
1527  case 5: DrawRectangle(cRect(cx, cy + y + ay, x + 1, 1), Color); // no break
1528  case 1: DrawRectangle(cRect(cx, cy - y, x + 1, 1), Color); break;
1529  case 7: DrawRectangle(cRect(cx - x, cy + y + ay, x + 1, 1), Color); // no break
1530  case 2: DrawRectangle(cRect(cx - x, cy - y, x + 1, 1), Color); break;
1531  case 3: DrawRectangle(cRect(cx - x, cy + y, x + 1, 1), Color); break;
1532  case 4: DrawRectangle(cRect(cx, cy + y, x + 1, 1), Color); break;
1533  case 0:
1534  case 6: DrawRectangle(cRect(cx - x, cy - y, 2 * x + ax + 1, 1), Color); if (Quadrants == 6) break;
1535  case 8: DrawRectangle(cRect(cx - x, cy + y, 2 * x + ax + 1, 1), Color); break;
1536  case -1: DrawRectangle(cRect(cx + x, cy - y, rx - x + 1, 1), Color); break;
1537  case -2: DrawRectangle(cRect(x1, cy - y, cx - x - x1 + 1, 1), Color); break;
1538  case -3: DrawRectangle(cRect(x1, cy + y, cx - x - x1 + 1, 1), Color); break;
1539  case -4: DrawRectangle(cRect(cx + x, cy + y, rx - x + 1, 1), Color); break;
1540  default: ;
1541  }
1542  }
1543  else {
1544  uint8_t intensity = abs(255 * (long int)EllipseError / YChange);
1545  if (EllipseError >= 0) {
1546  intensity = 255 - intensity;
1547  Delta = 1;
1548  }
1549  else
1550  Delta = 0;
1551  switch (Quadrants) {
1552  case 5: DrawRectangle( cRect( cx + x, cy + ay + 1 + ymax, 1, y - ymax - Delta), Color);
1553  DrawBlendedPixel(cPoint(cx + x, cy + ay + y + 1 - Delta), Color, intensity);
1554  // no break
1555  case 1: DrawRectangle( cRect( cx + x, cy - y + Delta, 1, y - ymax - Delta), Color);
1556  DrawBlendedPixel(cPoint(cx + x, cy - y - 1 + Delta), Color, intensity);
1557  break;
1558  case 7: DrawRectangle( cRect( cx - x, cy + ay + 1 + ymax, 1, y - ymax - Delta), Color);
1559  DrawBlendedPixel(cPoint(cx - x, cy + ay + y + 1 - Delta), Color, intensity);
1560  // no break
1561  case 2: DrawRectangle( cRect( cx - x, cy - y + Delta, 1, y - ymax - Delta), Color);
1562  DrawBlendedPixel(cPoint(cx - x, cy - y - 1 + Delta), Color, intensity);
1563  break;
1564  case 3: DrawRectangle( cRect( cx - x, cy + 1 + ymax, 1, y - ymax - Delta), Color);
1565  DrawBlendedPixel(cPoint(cx - x, cy + y + 1 - Delta), Color, intensity);
1566  break;
1567  case 4: DrawRectangle( cRect( cx + x, cy + 1 + ymax, 1, y - ymax - Delta), Color);
1568  DrawBlendedPixel(cPoint(cx + x, cy + y + 1 - Delta), Color, intensity);
1569  break;
1570  case 0:
1571  case 6: DrawRectangle( cRect( cx + x + ax, cy - y + Delta, 1, y - ymax - Delta), Color);
1572  DrawRectangle( cRect( cx - x, cy - y + Delta, 1, y - ymax - Delta), Color);
1573  DrawBlendedPixel(cPoint(cx - x, cy - y + Delta - 1), Color, intensity);
1574  DrawBlendedPixel(cPoint(cx + x + ax, cy - y + Delta - 1), Color, intensity);
1575  if (Quadrants == 6)
1576  break;
1577  case 8: DrawRectangle( cRect( cx - x, cy + 1 + ymax, 1, y - ymax - Delta), Color);
1578  DrawRectangle( cRect( cx + x + ax, cy + 1 + ymax, 1, y - ymax - Delta), Color);
1579  DrawBlendedPixel(cPoint(cx - x, cy + y + 1 - Delta), Color, intensity);
1580  DrawBlendedPixel(cPoint(cx + x + ax, cy + y + 1 - Delta), Color, intensity);
1581  break;
1582  case -1: DrawRectangle( cRect( cx + x, cy - ry, 1, ry - y - 1 + Delta), Color);
1583  DrawBlendedPixel(cPoint(cx + x, cy - y - 1 + Delta), Color, 255-intensity);
1584  break;
1585  case -2: DrawRectangle( cRect( cx - x, cy - ry, 1, ry - y - 1 + Delta), Color);
1586  DrawBlendedPixel(cPoint(cx - x, cy - y - 1 + Delta), Color, 255-intensity);
1587  break;
1588  case -3: DrawRectangle( cRect( cx - x, cy + y + 2 - Delta, 1, ry - y - 1 + Delta), Color);
1589  DrawBlendedPixel(cPoint(cx - x, cy + y + 1 - Delta), Color, 255-intensity);
1590  break;
1591  case -4: DrawRectangle( cRect( cx + x, cy + y + 2 - Delta, 1, ry - y - 1 + Delta), Color);
1592  DrawBlendedPixel(cPoint(cx + x, cy + y + 1 - Delta), Color, 255-intensity);
1593  break;
1594  default: ;
1595  }
1596  }
1597  x++;
1598  StoppingX += TwoBSquare;
1599  EllipseError += XChange;
1600  XChange += TwoBSquare;
1601  if (2 * EllipseError + YChange > 0) {
1602  y--;
1603  StoppingY -= TwoASquare;
1604  EllipseError += YChange;
1605  YChange += TwoASquare;
1606  }
1607  }
1608  if (AntiAliased && Quadrants < 0 ) {
1609  switch (Quadrants) {
1610  case -1: DrawRectangle(cRect(cx + x, cy - ry, rx - x + 1, ry - y), Color); break;
1611  case -2: DrawRectangle(cRect(x1, cy - ry, rx - x + 1, ry - y), Color); break;
1612  case -3: DrawRectangle(cRect(x1, cy + y + 1, rx - x + 1, ry - y), Color); break;
1613  case -4: DrawRectangle(cRect(cx + x, cy + y + 1, rx - x + 1, ry - y), Color); break;
1614  default: ;
1615  }
1616  }
1617  MarkDrawPortDirty(Rect);
1618  Unlock();
1619 }
1620 
1621 void cPixmapMemory::DrawSlope(const cRect &Rect, tColor Color, int Type)
1622 {
1623  //TODO also simplify cBitmap::DrawSlope()
1624  Lock();
1625  bool upper = Type & 0x01;
1626  bool falling = Type & 0x02;
1627  bool vertical = Type & 0x04;
1628  int x1 = Rect.Left();
1629  int y1 = Rect.Top();
1630  int x2 = Rect.Right();
1631  int y2 = Rect.Bottom();
1632  int w = Rect.Width();
1633  int h = Rect.Height();
1634  bool AntiAliased = Setup.AntiAlias;
1635  uint8_t intensity = 0;
1636 
1637  if (vertical) {
1638  if (upper)
1639  DrawRectangle(cRect(x1, y1, w, 1), Color);
1640  else
1641  DrawRectangle(cRect(x1, y2, w, 1), Color);
1642  for (int y = 1; y <= (y2 - y1) / 2; y++) {
1643  double c = cos(y * M_PI / (y2 - y1));
1644  if (AntiAliased) {
1645  double wc = (w * c + (w & 1)) / 2;
1646  intensity = 255 * fabs(wc - floor(wc));
1647  }
1648  if (falling)
1649  c = -c;
1650  int x = (x1 + x2 + w * c + 1) / 2;
1651  if (upper && !falling || !upper && falling) {
1652  if (AntiAliased) {
1653  DrawRectangle(cRect(x1, y1 + y, x - x1, 1), Color);
1654  DrawBlendedPixel(cPoint(x, y1 + y), Color, upper ? intensity : 255 - intensity);
1655  DrawRectangle(cRect(x1, y2 - y, x2 - x, 1), Color);
1656  DrawBlendedPixel(cPoint(x1 + x2 - x, y2 - y), Color, upper ? 255 - intensity : intensity);
1657  }
1658  else {
1659  DrawRectangle(cRect(x1, y1 + y, x - x1 + 1, 1), Color);
1660  DrawRectangle(cRect(x1, y2 - y, x2 - x + 1, 1), Color);
1661  }
1662  }
1663  else {
1664  if (AntiAliased) {
1665  DrawRectangle(cRect(x + 1, y1 + y, x2 - x, 1), Color);
1666  DrawBlendedPixel(cPoint(x, y1 + y), Color, falling ? intensity : 255 - intensity);
1667  DrawRectangle(cRect(x1 + x2 - x + 1, y2 - y, x - x1, 1), Color);
1668  DrawBlendedPixel(cPoint(x1 + x2 - x, y2 - y), Color, falling ? 255 - intensity : intensity);
1669  }
1670  else {
1671  DrawRectangle(cRect(x, y1 + y, x2 - x + 1, 1), Color);
1672  DrawRectangle(cRect(x1 + x2 - x, y2 - y, x - x1 + 1, 1), Color);
1673  }
1674  }
1675  }
1676  }
1677  else {
1678  if ((upper && !falling) || (!upper && falling))
1679  DrawRectangle(cRect(x1, y1, 1, h), Color);
1680  else
1681  DrawRectangle(cRect(x2, y1, 1, h), Color);
1682  for (int x = 1; x <= (x2 - x1) / 2; x++) {
1683  double c = cos(x * M_PI / (x2 - x1));
1684  if (AntiAliased) {
1685  double hc = (h * c + (h & 1)) / 2;
1686  intensity = 255 * fabs(hc - floor(hc));
1687  }
1688  if (falling)
1689  c = -c;
1690  int y = (y1 + y2 + h * c + 1) / 2;
1691  if (upper) {
1692  if (AntiAliased) {
1693  DrawRectangle(cRect(x1 + x, y1, 1, y - y1), Color);
1694  DrawBlendedPixel(cPoint(x1 + x, y), Color, falling ? 255 - intensity : intensity);
1695  DrawRectangle(cRect(x2 - x, y1, 1, y2 - y), Color);
1696  DrawBlendedPixel(cPoint(x2 - x, y1 + y2 - y), Color, falling ? intensity : 255 - intensity);
1697  }
1698  else {
1699  DrawRectangle(cRect(x1 + x, y1, 1, y - y1 + 1), Color);
1700  DrawRectangle(cRect(x2 - x, y1, 1, y2 - y + 1), Color);
1701  }
1702  }
1703  else {
1704  if (AntiAliased) {
1705  DrawRectangle(cRect(x1 + x, y + 1, 1, y2 - y), Color);
1706  DrawBlendedPixel(cPoint(x1 + x, y), Color, falling ? intensity : 255 - intensity);
1707  DrawRectangle(cRect(x2 - x, y1 + y2 - y + 1, 1, y - y1), Color);
1708  DrawBlendedPixel(cPoint(x2 - x, y1 + y2 - y), Color, falling ? 255 - intensity : intensity);
1709  }
1710  else {
1711  DrawRectangle(cRect(x1 + x, y, 1, y2 - y + 1), Color);
1712  DrawRectangle(cRect(x2 - x, y1 + y2 - y, 1, y - y1 + 1), Color);
1713  }
1714  }
1715  }
1716  }
1717  MarkDrawPortDirty(Rect);
1718  Unlock();
1719 }
1720 
1721 void cPixmapMemory::Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)
1722 {
1723  Lock();
1724  if (Pixmap->Alpha() != ALPHA_TRANSPARENT) {
1725  if (const cPixmapMemory *pm = dynamic_cast<const cPixmapMemory *>(Pixmap)) {
1726  cRect s = Source.Intersected(Pixmap->DrawPort().Size());
1727  if (!s.IsEmpty()) {
1728  cPoint v = Dest - Source.Point();
1729  cRect d = s.Shifted(v).Intersected(DrawPort().Size());
1730  if (!d.IsEmpty()) {
1731  s = d.Shifted(-v);
1732  int a = pm->Alpha();
1733  int ws = pm->DrawPort().Width();
1734  int wd = DrawPort().Width();
1735  const tColor *ps = pm->data + ws * s.Top() + s.Left();
1736  tColor *pd = data + wd * d.Top() + d.Left();
1737  for (int y = d.Height(); y-- > 0; ) {
1738  const tColor *cs = ps;
1739  tColor *cd = pd;
1740  for (int x = d.Width(); x-- > 0; ) {
1741  *cd = AlphaBlend(*cs, *cd, a);
1742  cs++;
1743  cd++;
1744  }
1745  ps += ws;
1746  pd += wd;
1747  }
1748  MarkDrawPortDirty(d);
1749  }
1750  }
1751  }
1752  }
1753  Unlock();
1754 }
1755 
1756 void cPixmapMemory::Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)
1757 {
1758  Lock();
1759  if (const cPixmapMemory *pm = dynamic_cast<const cPixmapMemory *>(Pixmap)) {
1760  cRect s = Source.Intersected(pm->DrawPort().Size());
1761  if (!s.IsEmpty()) {
1762  cPoint v = Dest - Source.Point();
1763  cRect d = s.Shifted(v).Intersected(DrawPort().Size());
1764  if (!d.IsEmpty()) {
1765  s = d.Shifted(-v);
1766  int ws = pm->DrawPort().Width();
1767  int wd = DrawPort().Width();
1768  int w = d.Width() * sizeof(tColor);
1769  const tColor *ps = pm->data + ws * s.Top() + s.Left();
1770  tColor *pd = data + wd * d.Top() + d.Left();
1771  for (int y = d.Height(); y-- > 0; ) {
1772  memcpy(pd, ps, w);
1773  ps += ws;
1774  pd += wd;
1775  }
1776  MarkDrawPortDirty(d);
1777  }
1778  }
1779  }
1780  Unlock();
1781 }
1782 
1783 void cPixmapMemory::Scroll(const cPoint &Dest, const cRect &Source)
1784 {
1785  Lock();
1786  cRect s;
1787  if (&Source == &cRect::Null)
1788  s = DrawPort().Shifted(-DrawPort().Point());
1789  else
1790  s = Source.Intersected(DrawPort().Size());
1791  if (!s.IsEmpty()) {
1792  cPoint v = Dest - Source.Point();
1793  cRect d = s.Shifted(v).Intersected(DrawPort().Size());
1794  if (!d.IsEmpty()) {
1795  s = d.Shifted(-v);
1796  if (d.Point() != s.Point()) {
1797  int ws = DrawPort().Width();
1798  int wd = ws;
1799  int w = d.Width() * sizeof(tColor);
1800  const tColor *ps = data + ws * s.Top() + s.Left();
1801  tColor *pd = data + wd * d.Top() + d.Left();
1802  for (int y = d.Height(); y-- > 0; ) {
1803  memmove(pd, ps, w); // source and destination might overlap!
1804  ps += ws;
1805  pd += wd;
1806  }
1807  if (panning)
1808  SetDrawPortPoint(DrawPort().Point().Shifted(s.Point() - d.Point()), false);
1809  else
1810  MarkDrawPortDirty(d);
1811  }
1812  }
1813  }
1814  Unlock();
1815 }
1816 
1817 void cPixmapMemory::Pan(const cPoint &Dest, const cRect &Source)
1818 {
1819  Lock();
1820  panning = true;
1821  Scroll(Dest, Source);
1822  panning = false;
1823  Unlock();
1824 }
1825 
1826 // --- cOsd ------------------------------------------------------------------
1827 
1828 static const char *OsdErrorTexts[] = {
1829  "ok",
1830  "too many areas",
1831  "too many colors",
1832  "bpp not supported",
1833  "areas overlap",
1834  "wrong alignment",
1835  "out of memory",
1836  "wrong area size",
1837  "unknown",
1838  };
1839 
1840 int cOsd::osdLeft = 0;
1841 int cOsd::osdTop = 0;
1842 int cOsd::osdWidth = 0;
1843 int cOsd::osdHeight = 0;
1844 cSize cOsd::maxPixmapSize(INT_MAX, INT_MAX);
1847 
1848 cOsd::cOsd(int Left, int Top, uint Level)
1849 {
1850  cMutexLock MutexLock(&mutex);
1851  isTrueColor = false;
1852  savedBitmap = NULL;
1853  numBitmaps = 0;
1854  savedPixmap = NULL;
1855  left = Left;
1856  top = Top;
1857  width = height = 0;
1858  level = Level;
1859  active = false;
1860  for (int i = 0; i < Osds.Size(); i++) {
1861  if (Osds[i]->level > level) {
1862  Osds.Insert(this, i);
1863  return;
1864  }
1865  }
1866  Osds.Append(this);
1867 }
1868 
1870 {
1871  cMutexLock MutexLock(&mutex);
1872  for (int i = 0; i < numBitmaps; i++)
1873  delete bitmaps[i];
1874  delete savedBitmap;
1875  delete savedPixmap;
1876  for (int i = 0; i < pixmaps.Size(); i++)
1877  delete pixmaps[i];
1878  for (int i = 0; i < Osds.Size(); i++) {
1879  if (Osds[i] == this) {
1880  Osds.Remove(i);
1881  if (Osds.Size())
1882  Osds[0]->SetActive(true);
1883  break;
1884  }
1885  }
1886 }
1887 
1888 void cOsd::SetOsdPosition(int Left, int Top, int Width, int Height)
1889 {
1890  osdLeft = Left;
1891  osdTop = Top;
1894 }
1895 
1896 void cOsd::SetAntiAliasGranularity(uint FixedColors, uint BlendColors)
1897 {
1898  if (isTrueColor)
1899  return;
1900  for (int i = 0; i < numBitmaps; i++)
1901  bitmaps[i]->SetAntiAliasGranularity(FixedColors, BlendColors);
1902 }
1903 
1905 {
1906  return Area < numBitmaps ? (isTrueColor ? bitmaps[0] : bitmaps[Area]) : NULL;
1907 }
1908 
1909 const cSize &cOsd::MaxPixmapSize(void) const
1910 {
1911  return maxPixmapSize;
1912 }
1913 
1914 cPixmap *cOsd::CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort)
1915 {
1916  if (isTrueColor) {
1917  LOCK_PIXMAPS;
1918  cPixmap *Pixmap = new cPixmapMemory(Layer, ViewPort, DrawPort);
1919  if (AddPixmap(Pixmap))
1920  return Pixmap;
1921  delete Pixmap;
1922  }
1923  return NULL;
1924 }
1925 
1927 {
1928  if (Pixmap) {
1929  LOCK_PIXMAPS;
1930  for (int i = 1; i < pixmaps.Size(); i++) { // begin at 1 - don't let the background pixmap be destroyed!
1931  if (pixmaps[i] == Pixmap) {
1932  if (Pixmap->Layer() >= 0)
1933  pixmaps[0]->MarkViewPortDirty(Pixmap->ViewPort());
1934  delete Pixmap;
1935  pixmaps[i] = NULL;
1936  return;
1937  }
1938  }
1939  esyslog("ERROR: attempt to destroy an unregistered pixmap");
1940  }
1941 }
1942 
1944 {
1945  if (Pixmap) {
1946  LOCK_PIXMAPS;
1947  for (int i = 0; i < pixmaps.Size(); i++) {
1948  if (!pixmaps[i])
1949  return pixmaps[i] = Pixmap;
1950  }
1951  pixmaps.Append(Pixmap);
1952  }
1953  return Pixmap;
1954 }
1955 
1957 {
1958  cPixmap *Pixmap = NULL;
1959  if (isTrueColor) {
1960  LOCK_PIXMAPS;
1961  // Collect overlapping dirty rectangles:
1962  cRect d;
1963  for (int i = 0; i < pixmaps.Size(); i++) {
1964  if (cPixmap *pm = pixmaps[i]) {
1965  if (!pm->DirtyViewPort().IsEmpty()) {
1966  if (d.IsEmpty() || d.Intersects(pm->DirtyViewPort())) {
1967  d.Combine(pm->DirtyViewPort());
1968  pm->SetClean();
1969  }
1970  }
1971  }
1972  }
1973  if (!d.IsEmpty()) {
1974 //#define DebugDirty
1975 #ifdef DebugDirty
1976  static cRect OldDirty;
1977  cRect NewDirty = d;
1978  d.Combine(OldDirty);
1979  OldDirty = NewDirty;
1980 #endif
1981  Pixmap = CreatePixmap(-1, d);
1982  if (Pixmap) {
1983  Pixmap->Clear();
1984  // Render the individual pixmaps into the resulting pixmap:
1985  for (int Layer = 0; Layer < MAXPIXMAPLAYERS; Layer++) {
1986  for (int i = 0; i < pixmaps.Size(); i++) {
1987  if (cPixmap *pm = pixmaps[i]) {
1988  if (pm->Layer() == Layer)
1989  Pixmap->DrawPixmap(pm, d);
1990  }
1991  }
1992  }
1993 #ifdef DebugDirty
1994  cPixmapMemory DirtyIndicator(7, NewDirty);
1995  static tColor DirtyIndicatorColors[] = { 0x7FFFFF00, 0x7F00FFFF };
1996  static int DirtyIndicatorIndex = 0;
1997  DirtyIndicator.Fill(DirtyIndicatorColors[DirtyIndicatorIndex]);
1998  DirtyIndicatorIndex = 1 - DirtyIndicatorIndex;
1999  Pixmap->Render(&DirtyIndicator, DirtyIndicator.DrawPort(), DirtyIndicator.ViewPort().Point().Shifted(-Pixmap->ViewPort().Point()));
2000 #endif
2001  }
2002  }
2003  }
2004  return Pixmap;
2005 }
2006 
2007 eOsdError cOsd::CanHandleAreas(const tArea *Areas, int NumAreas)
2008 {
2009  if (NumAreas > MAXOSDAREAS)
2010  return oeTooManyAreas;
2011  eOsdError Result = oeOk;
2012  for (int i = 0; i < NumAreas; i++) {
2013  if (Areas[i].x1 > Areas[i].x2 || Areas[i].y1 > Areas[i].y2 || Areas[i].x1 < 0 || Areas[i].y1 < 0)
2014  return oeWrongAlignment;
2015  for (int j = i + 1; j < NumAreas; j++) {
2016  if (Areas[i].Intersects(Areas[j])) {
2017  Result = oeAreasOverlap;
2018  break;
2019  }
2020  }
2021  if (Areas[i].bpp == 32) {
2022  if (NumAreas > 1)
2023  return oeTooManyAreas;
2024  }
2025  }
2026  return Result;
2027 }
2028 
2029 eOsdError cOsd::SetAreas(const tArea *Areas, int NumAreas)
2030 {
2031  eOsdError Result = CanHandleAreas(Areas, NumAreas);
2032  if (Result == oeOk) {
2033  while (numBitmaps)
2034  delete bitmaps[--numBitmaps];
2035  for (int i = 0; i < pixmaps.Size(); i++) {
2036  delete pixmaps[i];
2037  pixmaps[i] = NULL;
2038  }
2039  width = height = 0;
2040  isTrueColor = NumAreas == 1 && Areas[0].bpp == 32;
2041  if (isTrueColor) {
2042  width = Areas[0].x2 - Areas[0].x1 + 1;
2043  height = Areas[0].y2 - Areas[0].y1 + 1;
2044  cPixmap *Pixmap = CreatePixmap(0, cRect(Areas[0].x1, Areas[0].y1, width, height));
2045  if (Pixmap)
2046  Pixmap->Clear();
2047  else
2048  Result = oeOutOfMemory;
2049  bitmaps[numBitmaps++] = new cBitmap(10, 10, 8); // dummy bitmap for GetBitmap()
2050  }
2051  else {
2052  for (int i = 0; i < NumAreas; i++) {
2053  bitmaps[numBitmaps++] = new cBitmap(Areas[i].Width(), Areas[i].Height(), Areas[i].bpp, Areas[i].x1, Areas[i].y1);
2054  width = max(width, Areas[i].x2 + 1);
2055  height = max(height, Areas[i].y2 + 1);
2056  }
2057  }
2058  }
2059  else
2060  esyslog("ERROR: cOsd::SetAreas returned %d (%s)", Result, Result < oeUnknown ? OsdErrorTexts[Result] : OsdErrorTexts[oeUnknown]);
2061  return Result;
2062 }
2063 
2064 void cOsd::SaveRegion(int x1, int y1, int x2, int y2)
2065 {
2066  if (isTrueColor) {
2067  delete savedPixmap;
2068  cRect r(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
2069  savedPixmap = new cPixmapMemory(0, r);
2070  savedPixmap->Copy(pixmaps[0], r, cPoint(0, 0));
2071  }
2072  else {
2073  delete savedBitmap;
2074  savedBitmap = new cBitmap(x2 - x1 + 1, y2 - y1 + 1, 8, x1, y1);
2075  for (int i = 0; i < numBitmaps; i++)
2076  savedBitmap->DrawBitmap(bitmaps[i]->X0(), bitmaps[i]->Y0(), *bitmaps[i]);
2077  }
2078 }
2079 
2081 {
2082  if (isTrueColor) {
2083  if (savedPixmap) {
2085  delete savedPixmap;
2086  savedPixmap = NULL;
2087  }
2088  }
2089  else {
2090  if (savedBitmap) {
2092  delete savedBitmap;
2093  savedBitmap = NULL;
2094  }
2095  }
2096 }
2097 
2098 eOsdError cOsd::SetPalette(const cPalette &Palette, int Area)
2099 {
2100  if (isTrueColor)
2101  return oeOk;
2102  if (Area < numBitmaps) {
2103  bitmaps[Area]->Take(Palette);
2104  return oeOk;
2105  }
2106  return oeUnknown;
2107 }
2108 
2109 void cOsd::DrawImage(const cPoint &Point, const cImage &Image)
2110 {
2111  if (isTrueColor)
2112  pixmaps[0]->DrawImage(Point, Image);
2113 }
2114 
2115 void cOsd::DrawImage(const cPoint &Point, int ImageHandle)
2116 {
2117  if (isTrueColor)
2118  pixmaps[0]->DrawImage(Point, ImageHandle);
2119 }
2120 
2121 void cOsd::DrawPixel(int x, int y, tColor Color)
2122 {
2123  if (isTrueColor)
2124  pixmaps[0]->DrawPixel(cPoint(x, y), Color);
2125  else {
2126  for (int i = 0; i < numBitmaps; i++)
2127  bitmaps[i]->DrawPixel(x, y, Color);
2128  }
2129 }
2130 
2131 void cOsd::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
2132 {
2133  if (isTrueColor)
2134  pixmaps[0]->DrawBitmap(cPoint(x, y), Bitmap, ColorFg, ColorBg, Overlay);
2135  else {
2136  for (int i = 0; i < numBitmaps; i++)
2137  bitmaps[i]->DrawBitmap(x, y, Bitmap, ColorFg, ColorBg, ReplacePalette, Overlay);
2138  }
2139 }
2140 
2141 void cOsd::DrawScaledBitmap(int x, int y, const cBitmap &Bitmap, double FactorX, double FactorY, bool AntiAlias)
2142 {
2143  const cBitmap *b = &Bitmap;
2144  if (!DoubleEqual(FactorX, 1.0) || !DoubleEqual(FactorY, 1.0))
2145  b = b->Scaled(FactorX, FactorY, AntiAlias);
2146  DrawBitmap(x, y, *b);
2147  if (b != &Bitmap)
2148  delete b;
2149 }
2150 
2151 void cOsd::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
2152 {
2153  if (isTrueColor)
2154  pixmaps[0]->DrawText(cPoint(x, y), s, ColorFg, ColorBg, Font, Width, Height, Alignment);
2155  else {
2156  for (int i = 0; i < numBitmaps; i++)
2157  bitmaps[i]->DrawText(x, y, s, ColorFg, ColorBg, Font, Width, Height, Alignment);
2158  }
2159 }
2160 
2161 void cOsd::DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
2162 {
2163  if (isTrueColor)
2164  pixmaps[0]->DrawRectangle(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color);
2165  else {
2166  for (int i = 0; i < numBitmaps; i++)
2167  bitmaps[i]->DrawRectangle(x1, y1, x2, y2, Color);
2168  }
2169 }
2170 
2171 void cOsd::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)
2172 {
2173  if (isTrueColor)
2174  pixmaps[0]->DrawEllipse(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color, Quadrants);
2175  else {
2176  for (int i = 0; i < numBitmaps; i++)
2177  bitmaps[i]->DrawEllipse(x1, y1, x2, y2, Color, Quadrants);
2178  }
2179 }
2180 
2181 void cOsd::DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
2182 {
2183  if (isTrueColor)
2184  pixmaps[0]->DrawSlope(cRect(x1, y1, x2 - x1 + 1, y2 - y1 + 1), Color, Type);
2185  else {
2186  for (int i = 0; i < numBitmaps; i++)
2187  bitmaps[i]->DrawSlope(x1, y1, x2, y2, Color, Type);
2188  }
2189 }
2190 
2191 void cOsd::Flush(void)
2192 {
2193 }
2194 
2195 // --- cOsdProvider ----------------------------------------------------------
2196 
2198 int cOsdProvider::oldWidth = 0;
2199 int cOsdProvider::oldHeight = 0;
2200 double cOsdProvider::oldAspect = 1.0;
2202 int cOsdProvider::osdState = 0;
2203 
2205 {
2206  delete osdProvider;
2207  osdProvider = this;
2208 }
2209 
2211 {
2212  osdProvider = NULL;
2213 }
2214 
2215 cOsd *cOsdProvider::NewOsd(int Left, int Top, uint Level)
2216 {
2217  cMutexLock MutexLock(&cOsd::mutex);
2218  if (Level == OSD_LEVEL_DEFAULT && cOsd::IsOpen())
2219  esyslog("ERROR: attempt to open OSD while it is already open - using dummy OSD!");
2220  else if (osdProvider) {
2221  cOsd *ActiveOsd = cOsd::Osds.Size() ? cOsd::Osds[0] : NULL;
2222  cOsd *Osd = osdProvider->CreateOsd(Left, Top, Level);
2223  if (Osd == cOsd::Osds[0]) {
2224  if (ActiveOsd)
2225  ActiveOsd->SetActive(false);
2226  Osd->SetActive(true);
2227  }
2228  return Osd;
2229  }
2230  else
2231  esyslog("ERROR: no OSD provider available - using dummy OSD!");
2232  return new cOsd(Left, Top, 999); // create a dummy cOsd, so that access won't result in a segfault
2233 }
2234 
2236 {
2237  int Width;
2238  int Height;
2239  double Aspect;
2240  cMutexLock MutexLock(&cOsd::mutex);
2241  cDevice::PrimaryDevice()->GetOsdSize(Width, Height, Aspect);
2242  if (Width != oldWidth || Height != oldHeight || !DoubleEqual(Aspect, oldAspect) || Force) {
2243  Setup.OSDLeft = int(round(Width * Setup.OSDLeftP));
2244  Setup.OSDTop = int(round(Height * Setup.OSDTopP));
2245  Setup.OSDWidth = min(Width - Setup.OSDLeft, int(round(Width * Setup.OSDWidthP))) & ~0x07; // OSD width must be a multiple of 8
2246  Setup.OSDHeight = min(Height - Setup.OSDTop, int(round(Height * Setup.OSDHeightP)));
2247  Setup.OSDAspect = Aspect;
2248  Setup.FontOsdSize = int(round(Height * Setup.FontOsdSizeP));
2249  Setup.FontFixSize = int(round(Height * Setup.FontFixSizeP));
2250  Setup.FontSmlSize = int(round(Height * Setup.FontSmlSizeP));
2254  oldWidth = Width;
2255  oldHeight = Height;
2256  oldAspect = Aspect;
2257  dsyslog("OSD size changed to %dx%d @ %g", Width, Height, Aspect);
2258  osdState++;
2259  }
2260 }
2261 
2263 {
2264  cMutexLock MutexLock(&cOsd::mutex);
2265  bool Result = osdState != State;
2266  State = osdState;
2267  return Result;
2268 }
2269 
2271 {
2272  if (osdProvider) {
2273  return osdProvider->ProvidesTrueColor();
2274  }
2275  else
2276  esyslog("ERROR: no OSD provider available in call to SupportsTrueColor()");
2277  return false;
2278 }
2279 
2281 {
2282  LOCK_PIXMAPS;
2283  for (int i = 1; i < MAXOSDIMAGES; i++) {
2284  if (!images[i]) {
2285  images[i] = new cImage(Image);
2286  return i;
2287  }
2288  }
2289  return 0;
2290 }
2291 
2292 void cOsdProvider::DropImageData(int ImageHandle)
2293 {
2294  LOCK_PIXMAPS;
2295  if (0 < ImageHandle && ImageHandle < MAXOSDIMAGES) {
2296  delete images[ImageHandle];
2297  images[ImageHandle] = NULL;
2298  }
2299 }
2300 
2301 const cImage *cOsdProvider::GetImageData(int ImageHandle)
2302 {
2303  LOCK_PIXMAPS;
2304  if (0 < ImageHandle && ImageHandle < MAXOSDIMAGES)
2305  return images[ImageHandle];
2306  return NULL;
2307 }
2308 
2310 {
2311  if (osdProvider)
2312  return osdProvider->StoreImageData(Image);
2313  return 0;
2314 }
2315 
2316 void cOsdProvider::DropImage(int ImageHandle)
2317 {
2318  if (osdProvider)
2319  osdProvider->DropImageData(ImageHandle);
2320 }
2321 
2323 {
2324  delete osdProvider;
2325  osdProvider = NULL;
2326 }
2327 
2328 // --- cTextScroller ---------------------------------------------------------
2329 
2331 {
2332  osd = NULL;
2333  left = top = width = height = 0;
2334  font = NULL;
2335  colorFg = 0;
2336  colorBg = 0;
2337  offset = 0;
2338  shown = 0;
2339 }
2340 
2341 cTextScroller::cTextScroller(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg)
2342 {
2343  Set(Osd, Left, Top, Width, Height, Text, Font, ColorFg, ColorBg);
2344 }
2345 
2346 void cTextScroller::Set(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg)
2347 {
2348  osd = Osd;
2349  left = Left;
2350  top = Top;
2351  width = Width;
2352  height = Height;
2353  font = Font;
2354  colorFg = ColorFg;
2355  colorBg = ColorBg;
2356  offset = 0;
2357  textWrapper.Set(Text, Font, Width);
2358  shown = min(Total(), height / font->Height());
2359  height = shown * font->Height(); // sets height to the actually used height, which may be less than Height
2360  DrawText();
2361 }
2362 
2364 {
2365  osd = NULL; // just makes sure it won't draw anything
2366 }
2367 
2369 {
2370  if (osd) {
2371  for (int i = 0; i < shown; i++)
2373  }
2374 }
2375 
2376 void cTextScroller::Scroll(bool Up, bool Page)
2377 {
2378  if (Up) {
2379  if (CanScrollUp()) {
2380  offset -= Page ? shown : 1;
2381  if (offset < 0)
2382  offset = 0;
2383  DrawText();
2384  }
2385  }
2386  else {
2387  if (CanScrollDown()) {
2388  offset += Page ? shown : 1;
2389  if (offset + shown > Total())
2390  offset = Total() - shown;
2391  DrawText();
2392  }
2393  }
2394 }
Definition: osd.h:169
int y0
Definition: osd.h:172
void ShrinkBpp(int NewBpp)
Shrinks the color depth of the bitmap to NewBpp by keeping only the 2^NewBpp most frequently used col...
Definition: osd.c:796
int dirtyY2
Definition: osd.h:174
int width
Definition: osd.h:173
void ReduceBpp(const cPalette &Palette)
Reduces the color depth of the bitmap to that of the given Palette.
Definition: osd.c:765
int Height(void) const
Definition: osd.h:189
int height
Definition: osd.h:173
bool Dirty(int &x1, int &y1, int &x2, int &y2)
Tells whether there is a dirty area and returns the bounding rectangle of that area (relative to the ...
Definition: osd.c:342
cBitmap * Scaled(double FactorX, double FactorY, bool AntiAlias=false) const
Creates a copy of this bitmap, scaled by the given factors.
Definition: osd.c:838
int X0(void) const
Definition: osd.h:186
void DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
Draws a "slope" into the rectangle defined by the upper left (x1, y1) and lower right (x2,...
Definition: osd.c:727
tColor GetColor(int x, int y) const
Returns the color at the given coordinates.
Definition: osd.h:277
bool Contains(int x, int y) const
Returns true if this bitmap contains the point (x, y).
Definition: osd.c:317
void SetIndex(int x, int y, tIndex Index)
Sets the index at the given coordinates to Index.
Definition: osd.c:500
void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
Draws a filled rectangle defined by the upper left (x1, y1) and lower right (x2, y2) corners with the...
Definition: osd.c:611
void DrawPixel(int x, int y, tColor Color)
Sets the pixel at the given coordinates to the given Color, which is a full 32 bit ARGB value.
Definition: osd.c:526
bool Covers(int x1, int y1, int x2, int y2) const
Returns true if the rectangle defined by the given coordinates completely covers this bitmap.
Definition: osd.c:324
void Clean(void)
Marks the dirty area as clean.
Definition: osd.c:354
int dirtyX1
Definition: osd.h:174
tIndex * bitmap
Definition: osd.h:171
void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg=0, tColor ColorBg=0, bool ReplacePalette=false, bool Overlay=false)
Sets the pixels in this bitmap with the data from the given Bitmap, putting the upper left corner of ...
Definition: osd.c:533
void SetSize(int Width, int Height)
Sets the size of this bitmap to the given values.
Definition: osd.c:294
void Fill(tIndex Index)
Fills the bitmap data with the given Index.
Definition: osd.c:515
int dirtyX2
Definition: osd.h:174
int dirtyY1
Definition: osd.h:174
void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width=0, int Height=0, int Alignment=taDefault)
Draws the given string at coordinates (x, y) with the given foreground and background color and font.
Definition: osd.c:562
bool SetXpm(const char *const Xpm[], bool IgnoreNone=false)
Sets this bitmap to the given XPM data.
Definition: osd.c:428
void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants=0)
Draws a filled ellipse defined by the upper left (x1, y1) and lower right (x2, y2) corners with the g...
Definition: osd.c:632
bool LoadXpm(const char *FileName)
Calls SetXpm() with the data from the file FileName.
Definition: osd.c:362
virtual ~cBitmap()
Definition: osd.c:289
cBitmap(int Width, int Height, int Bpp, int X0=0, int Y0=0)
Creates a bitmap with the given Width, Height and color depth (Bpp).
Definition: osd.c:261
bool Intersects(int x1, int y1, int x2, int y2) const
Returns true if the rectangle defined by the given coordinates intersects with this bitmap.
Definition: osd.c:333
int Y0(void) const
Definition: osd.h:187
int x0
Definition: osd.h:172
int Width(void) const
Definition: osd.h:188
const tIndex * Data(int x, int y) const
Returns the address of the index byte at the given coordinates.
Definition: osd.c:760
virtual int Width(void) const
Returns the original character width as requested when the font was created, or 0 if the default widt...
Definition: skincurses.c:23
virtual int Height(void) const
Returns the height of this font in pixel (all characters have the same height).
Definition: skincurses.c:26
virtual void DrawText(cBitmap *Bitmap, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, int Width) const
Draws the given text into the Bitmap at position (x, y) with the given colors.
Definition: skincurses.c:27
static cDevice * PrimaryDevice(void)
Returns the primary device.
Definition: device.h:148
virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect)
Returns the Width, Height and PixelAspect ratio the OSD should use to best fit the resolution of the ...
Definition: device.c:526
Definition: font.h:37
virtual int Height(void) const =0
Returns the height of this font in pixel (all characters have the same height).
static void SetFont(eDvbFont Font, const char *Name, int CharHeight)
< Draws the given text into the Pixmap at position (x, y) with the given colors.
Definition: font.c:405
Definition: osd.h:419
int Height(void) const
Definition: osd.h:436
const cSize & Size(void) const
Definition: osd.h:434
int Width(void) const
Definition: osd.h:435
cImage(void)
Definition: osd.c:1104
tColor * data
Definition: osd.h:422
virtual ~cImage()
Definition: osd.c:1126
cSize size
Definition: osd.h:421
const tColor * Data(void) const
Definition: osd.h:437
void Clear(void)
Clears the image data by setting all pixels to be fully transparent.
Definition: osd.c:1131
void Fill(tColor Color)
Fills the image data with the given Color.
Definition: osd.c:1136
cInitAlphaLut(void)
Definition: osd.c:63
Definition: thread.h:67
static int oldHeight
Definition: osd.h:975
static int oldWidth
Definition: osd.h:974
virtual bool ProvidesTrueColor(void)
Returns true if this OSD provider is able to handle a true color OSD.
Definition: osd.h:983
static cImage * images[MAXOSDIMAGES]
Definition: osd.h:977
static double oldAspect
Definition: osd.h:976
static bool OsdSizeChanged(int &State)
Checks if the OSD size has changed and a currently displayed OSD needs to be redrawn.
Definition: osd.c:2262
static void DropImage(int ImageHandle)
Drops the image referenced by the given ImageHandle.
Definition: osd.c:2316
virtual ~cOsdProvider()
Definition: osd.c:2210
virtual int StoreImageData(const cImage &Image)
Copies the given Image and returns a handle for later reference.
Definition: osd.c:2280
virtual void DropImageData(int ImageHandle)
Drops the image data referenced by ImageHandle.
Definition: osd.c:2292
static cOsdProvider * osdProvider
Definition: osd.h:973
static int osdState
Definition: osd.h:978
static int StoreImage(const cImage &Image)
Stores the given Image for later use with DrawImage() on an OSD or pixmap.
Definition: osd.c:2309
cOsdProvider(void)
Definition: osd.c:2204
static void Shutdown(void)
Shuts down the OSD provider facility by deleting the current OSD provider.
Definition: osd.c:2322
static void UpdateOsdSize(bool Force=false)
Inquires the actual size of the video display and adjusts the OSD and font sizes accordingly.
Definition: osd.c:2235
virtual cOsd * CreateOsd(int Left, int Top, uint Level)=0
Returns a pointer to a newly created cOsd object, which will be located at the given coordinates.
static const cImage * GetImageData(int ImageHandle)
Gets the image data referenced by ImageHandle.
Definition: osd.c:2301
static bool SupportsTrueColor(void)
Returns true if the current OSD provider is able to handle a true color OSD.
Definition: osd.c:2270
static cOsd * NewOsd(int Left, int Top, uint Level=OSD_LEVEL_DEFAULT)
Returns a pointer to a newly created cOsd object, which will be located at the given coordinates.
Definition: osd.c:2215
The cOsd class is the interface to the "On Screen Display".
Definition: osd.h:729
int numBitmaps
Definition: osd.h:739
cOsd(int Left, int Top, uint Level)
Initializes the OSD with the given coordinates.
Definition: osd.c:1848
virtual eOsdError SetPalette(const cPalette &Palette, int Area)
Sets the Palette for the given Area (the first area is numbered 0).
Definition: osd.c:2098
uint level
Definition: osd.h:743
virtual const cSize & MaxPixmapSize(void) const
Returns the maximum possible size of a pixmap this OSD can create.
Definition: osd.c:1909
int top
Definition: osd.h:742
virtual void DrawImage(const cPoint &Point, const cImage &Image)
Draws the given Image on this OSD at the given Point.
Definition: osd.c:2109
static int osdHeight
Definition: osd.h:732
virtual void SetActive(bool On)
Sets this OSD to be the active one.
Definition: osd.h:767
int Width(void)
Definition: osd.h:826
bool isTrueColor
Definition: osd.h:736
cVector< cPixmap * > pixmaps
Definition: osd.h:741
virtual eOsdError SetAreas(const tArea *Areas, int NumAreas)
Sets the sub-areas to the given areas.
Definition: osd.c:2029
virtual void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg=0, tColor ColorBg=0, bool ReplacePalette=false, bool Overlay=false)
Sets the pixels in the OSD with the data from the given Bitmap, putting the upper left corner of the ...
Definition: osd.c:2131
virtual void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants=0)
Draws a filled ellipse defined by the upper left (x1, y1) and lower right (x2, y2) corners with the g...
Definition: osd.c:2171
cBitmap * GetBitmap(int Area)
Returns a pointer to the bitmap for the given Area, or NULL if no such bitmap exists.
Definition: osd.c:1904
virtual void DestroyPixmap(cPixmap *Pixmap)
Destroys the given Pixmap, which has previously been created by a call to CreatePixmap().
Definition: osd.c:1926
void SetAntiAliasGranularity(uint FixedColors, uint BlendColors)
Allows the system to optimize utilization of the limited color palette entries when generating blende...
Definition: osd.c:1896
virtual void DrawPixel(int x, int y, tColor Color)
Sets the pixel at the given coordinates to the given Color, which is a full 32 bit ARGB value.
Definition: osd.c:2121
bool active
Definition: osd.h:744
virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas)
Checks whether the OSD can display the given set of sub-areas.
Definition: osd.c:2007
static int osdWidth
Definition: osd.h:732
cPixmap * AddPixmap(cPixmap *Pixmap)
Adds the given Pixmap to the list of currently active pixmaps in this OSD.
Definition: osd.c:1943
cPixmap * RenderPixmaps(void)
Renders the dirty part of all pixmaps into a resulting pixmap that shall be displayed on the OSD.
Definition: osd.c:1956
static int osdLeft
Definition: osd.h:732
virtual cPixmap * CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort=cRect::Null)
Creates a new true color pixmap on this OSD (see cPixmap for details).
Definition: osd.c:1914
static void SetOsdPosition(int Left, int Top, int Width, int Height)
Sets the position and size of the OSD to the given values.
Definition: osd.c:1888
virtual void SaveRegion(int x1, int y1, int x2, int y2)
Saves the region defined by the given coordinates for later restoration through RestoreRegion().
Definition: osd.c:2064
virtual void DrawScaledBitmap(int x, int y, const cBitmap &Bitmap, double FactorX, double FactorY, bool AntiAlias=false)
Sets the pixels in the OSD with the data from the given Bitmap, putting the upper left corner of the ...
Definition: osd.c:2141
virtual void Flush(void)
Actually commits all data to the OSD hardware.
Definition: osd.c:2191
virtual void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color)
Draws a filled rectangle defined by the upper left (x1, y1) and lower right (x2, y2) corners with the...
Definition: osd.c:2161
int Left(void)
Definition: osd.h:824
cBitmap * bitmaps[MAXOSDAREAS]
Definition: osd.h:738
virtual void DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type)
Draws a "slope" into the rectangle defined by the upper left (x1, y1) and lower right (x2,...
Definition: osd.c:2181
static cMutex mutex
Definition: osd.h:735
int left
Definition: osd.h:742
cPixmapMemory * savedPixmap
Definition: osd.h:740
int height
Definition: osd.h:742
static int osdTop
Definition: osd.h:732
int Top(void)
Definition: osd.h:825
virtual void RestoreRegion(void)
Restores the region previously saved by a call to SaveRegion().
Definition: osd.c:2080
static cVector< cOsd * > Osds
Definition: osd.h:733
cBitmap * savedBitmap
Definition: osd.h:737
static cSize maxPixmapSize
Definition: osd.h:734
virtual ~cOsd()
Shuts down the OSD.
Definition: osd.c:1869
int width
Definition: osd.h:742
int Height(void)
Definition: osd.h:827
static int IsOpen(void)
Returns true if there is currently a level 0 OSD open.
Definition: osd.h:819
virtual void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width=0, int Height=0, int Alignment=taDefault)
Draws the given string at coordinates (x, y) with the given foreground and background color and font.
Definition: osd.c:2151
Definition: osd.h:88
tColor Color(int Index) const
Returns the color at the given Index.
Definition: osd.h:119
tColor Blend(tColor ColorFg, tColor ColorBg, uint8_t Level) const
Determines a color that consists of a linear blend between ColorFg and ColorBg.
Definition: osd.c:216
void Reset(void)
Resets the palette, making it contain no colors.
Definition: osd.c:138
void Replace(const cPalette &Palette)
Replaces the colors of this palette with the colors from the given palette.
Definition: osd.c:208
const tColor * Colors(int &NumColors) const
Returns a pointer to the complete color table and stores the number of valid entries in NumColors.
Definition: osd.c:185
int maxColors
Definition: osd.h:92
virtual ~cPalette()
Definition: osd.c:123
bool modified
Definition: osd.h:93
tIndex tIndexes[MAXNUMCOLORS]
Definition: osd.h:96
int bpp
Definition: osd.h:91
void SetBpp(int Bpp)
Sets the color depth of this palette to the given value.
Definition: osd.c:165
void SetColor(int Index, tColor Color)
Sets the palette entry at Index to Color.
Definition: osd.c:172
tColor color[MAXNUMCOLORS]
Definition: osd.h:90
void SetAntiAliasGranularity(uint FixedColors, uint BlendColors)
Allows the system to optimize utilization of the limited color palette entries when generating blende...
Definition: osd.c:127
int Index(tColor Color)
Returns the index of the given Color (the first color has index 0).
Definition: osd.c:144
int ClosestColor(tColor Color, int MaxDiff=INT_MAX) const
Returns the index of a color in this palette that is closest to the given Color.
Definition: osd.c:235
int numColors
Definition: osd.h:92
cPalette(int Bpp=8)
Initializes the palette with the given color depth.
Definition: osd.c:117
double antiAliasGranularity
Definition: osd.h:94
int Bpp(void) const
Definition: osd.h:111
void Take(const cPalette &Palette, tIndexes *Indexes=NULL, tColor ColorFg=0, tColor ColorBg=0)
Takes the colors from the given Palette and adds them to this palette, using existing entries if poss...
Definition: osd.c:191
virtual void Pan(const cPoint &Dest, const cRect &Source=cRect::Null)
Does the same as Scroll(), but also shifts the draw port accordingly, so that the view port doesn't g...
Definition: osd.c:1817
virtual void Scroll(const cPoint &Dest, const cRect &Source=cRect::Null)
Scrolls the data in the pixmap's draw port to the given Dest point.
Definition: osd.c:1783
virtual void Clear(void)
Clears the pixmap's draw port by setting all pixels to be fully transparent.
Definition: osd.c:1162
bool panning
Definition: osd.h:693
virtual void Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)
Copies the part of the given Pixmap covered by Source into this pixmap at location Dest.
Definition: osd.c:1756
cPixmapMemory(void)
Definition: osd.c:1144
virtual ~cPixmapMemory()
Definition: osd.c:1157
virtual void Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)
Renders the part of the given Pixmap covered by Source into this pixmap at location Dest.
Definition: osd.c:1721
virtual void DrawBlendedPixel(const cPoint &Point, tColor Color, uint8_t AlphaLayer=ALPHA_OPAQUE)
Like DrawPixel(), but with an additional AlphaLayer, and works on any pixmap, not only the background...
Definition: osd.c:1276
virtual void DrawSlope(const cRect &Rect, tColor Color, int Type)
Draws a "slope" with the given Color into the given rectangle.
Definition: osd.c:1621
virtual void DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg=0, tColor ColorBg=0, bool Overlay=false)
Sets the pixels in the OSD with the data from the given Bitmap, putting the upper left corner of the ...
Definition: osd.c:1294
virtual void DrawImage(const cPoint &Point, const cImage &Image)
Draws the given Image into this pixmap at the given Point.
Definition: osd.c:1230
virtual void DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width=0, int Height=0, int Alignment=taDefault)
Draws the given string at Point with the given foreground and background color and font.
Definition: osd.c:1321
virtual void Fill(tColor Color)
Fills the pixmap's draw port with the given Color.
Definition: osd.c:1170
virtual void DrawEllipse(const cRect &Rect, tColor Color, int Quadrants=0)
Draws a filled ellipse with the given Color that fits into the given rectangle.
Definition: osd.c:1397
tColor * data
Definition: osd.h:692
virtual void DrawPixel(const cPoint &Point, tColor Color)
Sets the pixel at the given Point to the given Color, which is a full 32 bit ARGB value.
Definition: osd.c:1262
virtual void DrawRectangle(const cRect &Rect, tColor Color)
Draws a filled rectangle with the given Color.
Definition: osd.c:1370
Definition: osd.h:454
virtual void DrawPixmap(const cPixmap *Pixmap, const cRect &Dirty)
Draws the Dirty part of the given Pixmap into this pixmap.
Definition: osd.c:1179
const cRect & DrawPort(void) const
Returns the pixmap's draw port, which is relative to the view port.
Definition: osd.h:543
bool Tile(void) const
Definition: osd.h:538
int alpha
Definition: osd.h:460
virtual void SetViewPort(const cRect &Rect)
Sets the pixmap's view port to the given Rect.
Definition: osd.c:1068
cPixmap(void)
Definition: osd.c:962
static void Unlock(void)
Definition: osd.h:535
virtual void Clear(void)=0
Clears the pixmap's draw port by setting all pixels to be fully transparent.
void MarkViewPortDirty(const cRect &Rect)
Marks the given rectangle of the view port of this pixmap as dirty.
Definition: osd.c:987
cRect dirtyDrawPort
Definition: osd.h:465
cRect viewPort
Definition: osd.h:462
int Alpha(void) const
Definition: osd.h:537
virtual void SetDrawPortPoint(const cPoint &Point, bool Dirty=true)
Sets the pixmap's draw port to the given Point.
Definition: osd.c:1085
static void Lock(void)
All public member functions of cPixmap set locks as necessary to make sure they are thread-safe (unle...
Definition: osd.h:529
static cMutex mutex
Definition: osd.h:458
virtual void SetLayer(int Layer)
Sets the layer of this pixmap to the given value.
Definition: osd.c:1024
const cRect & ViewPort(void) const
Returns the pixmap's view port, which is relative to the OSD's origin.
Definition: osd.h:539
void MarkDrawPortDirty(const cRect &Rect)
Marks the given rectangle of the draw port of this pixmap as dirty.
Definition: osd.c:999
void SetClean(void)
Resets the "dirty" rectangles of this pixmap.
Definition: osd.c:1019
virtual void Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)=0
Copies the part of the given Pixmap covered by Source into this pixmap at location Dest.
bool tile
Definition: osd.h:461
cRect dirtyViewPort
Definition: osd.h:464
virtual void Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest)=0
Renders the part of the given Pixmap covered by Source into this pixmap at location Dest.
virtual void SetAlpha(int Alpha)
Sets the alpha value of this pixmap to the given value.
Definition: osd.c:1046
int layer
Definition: osd.h:459
cRect drawPort
Definition: osd.h:463
virtual void SetTile(bool Tile)
Sets the tile property of this pixmap to the given value.
Definition: osd.c:1057
int Layer(void) const
Definition: osd.h:536
Definition: osd.h:306
int Y(void) const
Definition: osd.h:319
int X(void) const
Definition: osd.h:318
void SetX(int X)
Definition: osd.h:320
cPoint Shifted(int Dx, int Dy) const
Definition: osd.h:326
void Shift(int Dx, int Dy)
Definition: osd.h:324
char * Read(FILE *f)
Definition: tools.c:1465
Definition: osd.h:352
static const cRect Null
Definition: osd.h:357
void Combine(const cRect &Rect)
Combines this rectangle with the given Rect.
Definition: osd.c:934
int Top(void) const
Definition: osd.h:370
cSize size
Definition: osd.h:355
bool Intersects(const cRect &Rect) const
Returns true if this rectangle intersects with Rect.
Definition: osd.c:914
bool Contains(const cPoint &Point) const
Returns true if this rectangle contains Point.
Definition: osd.c:898
void SetPoint(int X, int Y)
Definition: osd.h:377
cRect Intersected(const cRect &Rect) const
Returns the intersection of this rectangle and the given Rect.
Definition: osd.c:922
int Height(void) const
Definition: osd.h:368
int Left(void) const
Definition: osd.h:369
int Bottom(void) const
Definition: osd.h:372
void SetRight(int Right)
Definition: osd.h:387
const cSize & Size(void) const
Definition: osd.h:374
void Grow(int Dx, int Dy)
Grows the rectangle by the given number of pixels in either direction.
Definition: osd.c:892
int Right(void) const
Definition: osd.h:371
void SetTop(int Top)
Definition: osd.h:386
void SetLeft(int Left)
Definition: osd.h:385
cRect Shifted(int Dx, int Dy) const
Definition: osd.h:391
void SetBottom(int Bottom)
Definition: osd.h:388
cPoint point
Definition: osd.h:354
void Shift(int Dx, int Dy)
Definition: osd.h:389
int Width(void) const
Definition: osd.h:367
const cPoint & Point(void) const
Definition: osd.h:373
bool IsEmpty(void) const
Returns true if this rectangle is empty.
Definition: osd.h:415
void Set(int X, int Y, int Width, int Height)
Definition: osd.h:375
int AntiAlias
Definition: config.h:327
int FontFixSize
Definition: config.h:336
int OSDHeight
Definition: config.h:323
double OSDAspect
Definition: config.h:324
double OSDWidthP
Definition: config.h:322
double OSDHeightP
Definition: config.h:322
double FontOsdSizeP
Definition: config.h:331
int FontOsdSize
Definition: config.h:334
int FontSmlSize
Definition: config.h:335
int OSDTop
Definition: config.h:323
double FontFixSizeP
Definition: config.h:333
double OSDLeftP
Definition: config.h:322
double FontSmlSizeP
Definition: config.h:332
int OSDLeft
Definition: config.h:323
char FontOsd[MAXFONTNAME]
Definition: config.h:328
int OSDWidth
Definition: config.h:323
char FontSml[MAXFONTNAME]
Definition: config.h:329
double OSDTopP
Definition: config.h:322
char FontFix[MAXFONTNAME]
Definition: config.h:330
Definition: osd.h:330
void Grow(int Dw, int Dh)
Definition: osd.h:348
int Height(void) const
Definition: osd.h:342
int Width(void) const
Definition: osd.h:341
int Height(void)
Definition: osd.h:1057
tColor colorBg
Definition: osd.h:1045
int Left(void)
Definition: osd.h:1054
void DrawText(void)
Definition: osd.c:2368
int shown
Definition: osd.h:1046
int Total(void)
Definition: osd.h:1058
int height
Definition: osd.h:1043
cTextWrapper textWrapper
Definition: osd.h:1047
const cFont * font
Definition: osd.h:1044
cTextScroller(void)
Definition: osd.c:2330
void Scroll(bool Up, bool Page)
Definition: osd.c:2376
int Top(void)
Definition: osd.h:1055
void Set(cOsd *Osd, int Left, int Top, int Width, int Height, const char *Text, const cFont *Font, tColor ColorFg, tColor ColorBg)
Definition: osd.c:2346
void Reset(void)
Definition: osd.c:2363
tColor colorFg
Definition: osd.h:1045
int left
Definition: osd.h:1043
int width
Definition: osd.h:1043
cOsd * osd
Definition: osd.h:1042
int top
Definition: osd.h:1043
bool CanScrollDown(void)
Definition: osd.h:1063
int offset
Definition: osd.h:1046
int Width(void)
Definition: osd.h:1056
bool CanScrollUp(void)
Definition: osd.h:1062
const char * GetLine(int Line)
Returns the given Line. The first line is numbered 0.
Definition: font.c:635
void Set(const char *Text, const cFont *Font, int Width)
Wraps the Text to make it fit into the area defined by the given Width when displayed with the given ...
Definition: font.c:563
int Size(void) const
Definition: tools.h:721
virtual void Insert(T Data, int Before=0)
Definition: tools.h:722
virtual void Append(T Data)
Definition: tools.h:741
virtual void Remove(int Index)
Definition: tools.h:755
cSetup Setup
Definition: config.c:372
#define MINOSDHEIGHT
Definition: config.h:56
#define MINOSDWIDTH
Definition: config.h:54
#define MAXOSDWIDTH
Definition: config.h:55
#define MAXOSDHEIGHT
Definition: config.h:57
@ fontOsd
Definition: font.h:22
@ fontFix
Definition: font.h:23
uint32_t tColor
Definition: font.h:29
uint8_t tIndex
Definition: font.h:31
static uint16_t AlphaLutFactors[255][256][2]
Definition: osd.c:58
tColor AlphaBlend(tColor ColorFg, tColor ColorBg, uint8_t AlphaLayer)
Definition: osd.c:81
tColor RgbShade(tColor Color, double Factor)
Returns a brighter (Factor > 0) or darker (Factor < 0) version of the given Color.
Definition: osd.c:43
class cInitAlphaLut InitAlphaLut
static uint8_t AlphaLutAlpha[255][256]
Definition: osd.c:59
static const char * OsdErrorTexts[]
Definition: osd.c:1828
tColor HsvToColor(double H, double S, double V)
Converts the given Hue (0..360), Saturation (0..1) and Value (0..1) to an RGB tColor value.
Definition: osd.c:19
#define ALPHA_TRANSPARENT
Definition: osd.h:25
#define ALPHA_OPAQUE
Definition: osd.h:26
@ taBorder
Definition: osd.h:163
@ taTop
Definition: osd.h:161
@ taBottom
Definition: osd.h:162
@ taRight
Definition: osd.h:160
@ taLeft
Definition: osd.h:159
#define MAXNUMCOLORS
Definition: osd.h:24
#define MAXPIXMAPLAYERS
Definition: osd.h:452
eOsdError
Definition: osd.h:44
@ oeUnknown
Definition: osd.h:52
@ oeTooManyAreas
Definition: osd.h:45
@ oeAreasOverlap
Definition: osd.h:48
@ oeOutOfMemory
Definition: osd.h:50
@ oeWrongAlignment
Definition: osd.h:49
@ oeOk
Definition: osd.h:44
tColor RgbToColor(uint8_t R, uint8_t G, uint8_t B)
Definition: osd.h:63
#define OSD_LEVEL_DEFAULT
Definition: osd.h:21
@ clrTransparent
Definition: osd.h:32
#define LOCK_PIXMAPS
Definition: osd.h:685
#define MAXOSDAREAS
Definition: osd.h:716
#define TEXT_ALIGN_BORDER
Definition: osd.h:28
#define MAXOSDIMAGES
Definition: osd.h:968
#define IS_OPAQUE(c)
Definition: osd.h:27
static const cCursesFont Font
Definition: skincurses.c:31
Definition: osd.h:298
int bpp
Definition: osd.h:300
int x2
Definition: osd.h:299
int y1
Definition: osd.h:299
int x1
Definition: osd.h:299
int y2
Definition: osd.h:299
T constrain(T v, T l, T h)
Definition: tools.h:66
char * skipspace(const char *s)
Definition: tools.h:207
#define dsyslog(a...)
Definition: tools.h:37
#define MALLOC(type, size)
Definition: tools.h:47
bool DoubleEqual(double a, double b)
Definition: tools.h:93
T min(T a, T b)
Definition: tools.h:58
T max(T a, T b)
Definition: tools.h:59
#define esyslog(a...)
Definition: tools.h:35