@@ -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).
@@ -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)