[ANNOUNCE] VDR-1.5.12: some "micro" speed improvements

Message ID 47757833.6020701@gmx.de
State New
Headers

Commit Message

Reinhard Nissl Dec. 28, 2007, 10:26 p.m. UTC
  Hi,

the first patch optimizes cNitFilter::Process() by looping only over the
relevant channels. It introduces a further hash in cChannels and
implements an iterator for hiding this implementation detail.

The channel_display patch avoids to call DrawText() when the "date" text
has not changed in the channel display. As DrawText() calls
DrawRectangle() when a non transparent background color was specified,
drawing the same text will always yield a dirty area although there is
no difference when looking at the bitmap before and after the call to
DrawText().

And finally, the formerly released osd_draw patch has been updated a
little bit. DrawBitmap() and DrawRectangle() use now memcpy() when possible.

Bye.
  

Patch

--- ../vdr-1.5.12-orig/osd.h	2007-10-12 16:28:44.000000000 +0200
+++ osd.h	2007-12-26 22:49:13.000000000 +0100
@@ -135,6 +135,7 @@  private:
   int x0, y0;
   int width, height;
   int dirtyX1, dirtyY1, dirtyX2, dirtyY2;
+  void SetIndexInternal(int x, int y, tIndex Index);
 public:
   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).
--- ../vdr-1.5.12-orig/osd.c	2007-10-12 14:38:36.000000000 +0200
+++ osd.c	2007-12-28 10:21:35.000000000 +0100
@@ -394,15 +394,20 @@  bool cBitmap::SetXpm(const char *const X
 void cBitmap::SetIndex(int x, int y, tIndex Index)
 {
   if (bitmap) {
-     if (0 <= x && x < width && 0 <= y && y < height) {
-        if (bitmap[width * y + x] != Index) {
-           bitmap[width * y + x] = Index;
-           if (dirtyX1 > x)  dirtyX1 = x;
-           if (dirtyY1 > y)  dirtyY1 = y;
-           if (dirtyX2 < x)  dirtyX2 = x;
-           if (dirtyY2 < y)  dirtyY2 = y;
-           }
-        }
+     if (0 <= x && x < width && 0 <= y && y < height)
+        SetIndexInternal(x, y, Index);
+     }
+}
+
+void cBitmap::SetIndexInternal(int x, int y, tIndex Index)
+{
+  // this function relies on existing bitmap and valid coordinates
+  if (bitmap[width * y + x] != Index) {
+     bitmap[width * y + x] = Index;
+     if (dirtyX1 > x)  dirtyX1 = x;
+     if (dirtyY1 > y)  dirtyY1 = y;
+     if (dirtyX2 < x)  dirtyX2 = x;
+     if (dirtyY2 < y)  dirtyY2 = y;
      }
 }
 
@@ -410,37 +415,147 @@  void cBitmap::DrawPixel(int x, int y, tC
 {
   x -= x0;
   y -= y0;
-  if (0 <= x && x < width && 0 <= y && y < height)
-     SetIndex(x, y, Index(Color));
+  if (bitmap && 0 <= x && x < width && 0 <= y && y < height)
+     SetIndexInternal(x, y, Index(Color));
 }
 
 void cBitmap::DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool ReplacePalette, bool Overlay)
 {
   if (bitmap && Bitmap.bitmap && Intersects(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1)) {
-     if (Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1))
+     bool Covered = Covers(x, y, x + Bitmap.Width() - 1, y + Bitmap.Height() - 1);
+     if (Covered)
         Reset();
      x -= x0;
      y -= y0;
-     if (ReplacePalette && Covers(x + x0, y + y0, x + x0 + Bitmap.Width() - 1, y + y0 + Bitmap.Height() - 1)) {
+     // determine valid destination area [x1,x2]x[y1,y2] to avoid range checks inside the loops
+     int x1 = max(0, x), x2 = min(0 + width , x + Bitmap.width)  - 1;
+     int y1 = max(0, y), y2 = min(0 + height, y + Bitmap.height) - 1;
+
+#define FOR_Y_LOOP0                                                                                              \
+        tIndex *pRowSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x1 - x)];                                    \
+        tIndex *pRowDst = &bitmap[width * y1 + x1];                                                              \
+        for (int &yy = y1, ye = min(y2, dirtyY1 - 1); yy <= ye; yy++, pRowDst += width, pRowSrc += Bitmap.width)
+
+#define FOR_Y_LOOP1                                                                                              \
+        tIndex *pRowSrc = &Bitmap.bitmap[Bitmap.width * (y2 - y) + (x1 - x)];                                    \
+        tIndex *pRowDst = &bitmap[width * y2 + x1];                                                              \
+        for (int &yy = y2, ye = max(y1, dirtyY2 + 1); yy >= ye; yy--, pRowDst -= width, pRowSrc -= Bitmap.width)
+
+#define DETECT_DIRTY_AREA_Y(Reverse, TransferCondition, TransferOperation) \
+     do {                                                                  \
+        FOR_Y_LOOP##Reverse {                                              \
+            tIndex *pSrc = pRowSrc;                                        \
+            tIndex *pDst = pRowDst;                                        \
+            bool GotDirty = false;                                         \
+            for (int xx = x1; xx <= x2; xx++) {                            \
+                if (TransferCondition) {                                   \
+                   if (*pDst != TransferOperation) {                       \
+                      GotDirty = true;                                     \
+                      if (dirtyX1 > xx)  dirtyX1 = xx;                     \
+                      if (dirtyX2 < xx)  dirtyX2 = xx;                     \
+                      }                                                    \
+                   }                                                       \
+                pSrc++;                                                    \
+                pDst++;                                                    \
+                }                                                          \
+            if (GotDirty) {                                                \
+               if (dirtyY1 > yy)  dirtyY1 = yy;                            \
+               if (dirtyY2 < yy)  dirtyY2 = yy;                            \
+               break;                                                      \
+               }                                                           \
+            }                                                              \
+        }                                                                  \
+     while (false)
+
+#define FOR_X_LOOP0                                                                         \
+        tIndex *pColSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x1 - x)];               \
+        tIndex *pColDst = &bitmap[width * y1 + x1];                                         \
+        for (int &xx = x1, xe = min(x2, dirtyX1 - 1); xx <= xe; xx++, pColDst++, pColSrc++)
+
+#define FOR_X_LOOP1                                                                         \
+        tIndex *pColSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x2 - x)];               \
+        tIndex *pColDst = &bitmap[width * y1 + x2];                                         \
+        for (int &xx = x2, xe = max(x1, dirtyX2 + 1); xx >= xe; xx--, pColDst--, pColSrc--)
+
+#define DETECT_DIRTY_AREA_X(Reverse, TransferCondition, TransferOperation) \
+     do {                                                                  \
+        FOR_X_LOOP##Reverse {                                              \
+            tIndex *pSrc = pColSrc;                                        \
+            tIndex *pDst = pColDst;                                        \
+            bool GotDirty = false;                                         \
+            for (int yy = y1; yy <= y2; yy++) {                            \
+                if (TransferCondition) {                                   \
+                   if (*pDst != TransferOperation) {                       \
+                      GotDirty = true;                                     \
+                      if (dirtyX1 > xx)  dirtyX1 = xx;                     \
+                      if (dirtyX2 < xx)  dirtyX2 = xx;                     \
+                      break;                                               \
+                      }                                                    \
+                   }                                                       \
+                pSrc += Bitmap.width;                                      \
+                pDst += width;                                             \
+                }                                                          \
+            if (GotDirty)                                                  \
+               break;                                                      \
+            }                                                              \
+        }                                                                  \
+     while (false)
+
+#define DRAW_BITMAP(TransferCondition, TransferOperation, CanUseMemCpy)           \
+     do {                                                                         \
+        DETECT_DIRTY_AREA_Y(0, TransferCondition, TransferOperation); /* above */ \
+        DETECT_DIRTY_AREA_Y(1, TransferCondition, TransferOperation); /* below */ \
+        if (y2 < y1) /* nothing dirty */                                          \
+           return;                                                                \
+        DETECT_DIRTY_AREA_X(0, TransferCondition, TransferOperation); /* left  */ \
+        DETECT_DIRTY_AREA_X(1, TransferCondition, TransferOperation); /* right */ \
+        /* process dirty area now */                                              \
+        tIndex *pRowSrc = &Bitmap.bitmap[Bitmap.width * (y1 - y) + (x1 - x)];     \
+        tIndex *pRowDst = &bitmap[width * y1 + x1];                               \
+        int n = sizeof(tIndex) * (x2 - x1 + 1);                                   \
+        for (int yy = y1; yy <= y2; yy++) {                                       \
+            tIndex *pSrc = pRowSrc;                                               \
+            tIndex *pDst = pRowDst;                                               \
+            if (CanUseMemCpy)                                                     \
+               memcpy(pDst, pSrc, n);                                             \
+            else {                                                                \
+               for (int xx = x1; xx <= x2; xx++) {                                \
+                   if (TransferCondition)                                         \
+                      *pDst = TransferOperation;                                  \
+                   pSrc++;                                                        \
+                   pDst++;                                                        \
+                   }                                                              \
+               }                                                                  \
+            pRowSrc += Bitmap.width;                                              \
+            pRowDst += width;                                                     \
+            }                                                                     \
+        }                                                                         \
+     while (false)
+
+     if (ReplacePalette && Covered) {
         Replace(Bitmap);
-        for (int ix = 0; ix < Bitmap.width; ix++) {
-            for (int iy = 0; iy < Bitmap.height; iy++) {
-                if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
-                   SetIndex(x + ix, y + iy, Bitmap.bitmap[Bitmap.width * iy + ix]);
-                }
-            }
+        if (Overlay)
+           DRAW_BITMAP(*pSrc != 0, *pSrc, false);
+        else
+           DRAW_BITMAP(true, *pSrc, true);
         }
      else {
         tIndexes Indexes;
         Take(Bitmap, &Indexes, ColorFg, ColorBg);
-        for (int ix = 0; ix < Bitmap.width; ix++) {
-            for (int iy = 0; iy < Bitmap.height; iy++) {
-                if (!Overlay || Bitmap.bitmap[Bitmap.width * iy + ix] != 0)
-                   SetIndex(x + ix, y + iy, Indexes[int(Bitmap.bitmap[Bitmap.width * iy + ix])]);
-                }
-            }
+        if (Overlay)
+           DRAW_BITMAP(*pSrc != 0, Indexes[(int)*pSrc], false);
+        else
+           DRAW_BITMAP(true, Indexes[(int)*pSrc], false);
         }
      }
+
+#undef DRAW_BITMAP
+#undef DETECT_DIRTY_AREA_Y
+#undef FOR_Y_LOOP0
+#undef FOR_Y_LOOP1
+#undef DETECT_DIRTY_AREA_X
+#undef FOR_X_LOOP0
+#undef FOR_X_LOOP1
 }
 
 void cBitmap::DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment)
@@ -502,10 +617,91 @@  void cBitmap::DrawRectangle(int x1, int 
      x2 = min(x2, width - 1);
      y2 = min(y2, height - 1);
      tIndex c = Index(Color);
-     for (int y = y1; y <= y2; y++)
-         for (int x = x1; x <= x2; x++)
-             SetIndex(x, y, c);
+
+#define FOR_Y_LOOP0                                                                     \
+        tIndex *pRowDst = &bitmap[width * y1 + x1];                                     \
+        for (int &yy = y1, ye = min(y2, dirtyY1 - 1); yy <= ye; yy++, pRowDst += width)
+
+#define FOR_Y_LOOP1                                                                     \
+        tIndex *pRowDst = &bitmap[width * y2 + x1];                                     \
+        for (int &yy = y2, ye = max(y1, dirtyY2 + 1); yy >= ye; yy--, pRowDst -= width)
+
+#define DETECT_DIRTY_AREA_Y(Reverse)                \
+     do {                                           \
+        FOR_Y_LOOP##Reverse {                       \
+            tIndex *pDst = pRowDst;                 \
+            bool GotDirty = false;                  \
+            for (int xx = x1; xx <= x2; xx++) {     \
+                if (*pDst != c) {                   \
+                   GotDirty = true;                 \
+                   if (dirtyX1 > xx)  dirtyX1 = xx; \
+                   if (dirtyX2 < xx)  dirtyX2 = xx; \
+                   }                                \
+                pDst++;                             \
+                }                                   \
+            if (GotDirty) {                         \
+               if (dirtyY1 > yy)  dirtyY1 = yy;     \
+               if (dirtyY2 < yy)  dirtyY2 = yy;     \
+               break;                               \
+               }                                    \
+            }                                       \
+        }                                           \
+     while (false)
+
+#define FOR_X_LOOP0                                                              \
+        tIndex *pColDst = &bitmap[width * y1 + x1];                              \
+        for (int &xx = x1, xe = min(x2, dirtyX1 - 1); xx <= xe; xx++, pColDst++)
+
+#define FOR_X_LOOP1                                                              \
+        tIndex *pColDst = &bitmap[width * y1 + x2];                              \
+        for (int &xx = x2, xe = max(x1, dirtyX2 + 1); xx >= xe; xx--, pColDst--)
+
+#define DETECT_DIRTY_AREA_X(Reverse)                \
+     do {                                           \
+        FOR_X_LOOP##Reverse {                       \
+            tIndex *pDst = pColDst;                 \
+            bool GotDirty = false;                  \
+            for (int yy = y1; yy <= y2; yy++) {     \
+                if (*pDst != c) {                   \
+                   GotDirty = true;                 \
+                   if (dirtyX1 > xx)  dirtyX1 = xx; \
+                   if (dirtyX2 < xx)  dirtyX2 = xx; \
+                   break;                           \
+                   }                                \
+                pDst += width;                      \
+                }                                   \
+            if (GotDirty)                           \
+               break;                               \
+            }                                       \
+        }                                           \
+     while (false)
+
+     DETECT_DIRTY_AREA_Y(0); /* above */
+     DETECT_DIRTY_AREA_Y(1); /* below */
+     if (y2 < y1) /* nothing dirty */
+        return;
+     DETECT_DIRTY_AREA_X(0); /* left  */
+     DETECT_DIRTY_AREA_X(1); /* right */
+     // now fill only dirty area of rectangle
+     tIndex *pRowDst = &bitmap[width * y1 + x1];
+     tIndex *pDst = pRowDst;
+     for (int x = x1; x <= x2; x++)
+         *pDst++ = c;
+     // copy the single line above to all other lines
+     tIndex *pRowSrc = pRowDst;
+     int n = sizeof(tIndex) * (x2 - x1 + 1);
+     for (int y = y1 + 1; y <= y2; y++) {
+         pRowDst += width;
+         memcpy(pRowDst, pRowSrc, n);
+         }
      }
+
+#undef DETECT_DIRTY_AREA_Y
+#undef FOR_Y_LOOP0
+#undef FOR_Y_LOOP1
+#undef DETECT_DIRTY_AREA_X
+#undef FOR_X_LOOP0
+#undef FOR_X_LOOP1
 }
 
 void cBitmap::DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants)