/*#*
*#* $Log$
+ *#* Revision 1.6 2006/01/23 23:13:39 bernie
+ *#* gfx_blit(): New function, but dog slow for now.
+ *#*
+ *#* Revision 1.5 2006/01/17 22:59:23 bernie
+ *#* Implement correct line clipping algorithm.
+ *#*
*#* Revision 1.4 2006/01/17 02:31:29 bernie
*#* Add bitmap format support; Improve some comments.
*#*
#endif /* CONFIG_BITMAP_FMT */
/*!
- * Plot a point in bitmap \a bm.
+ * Plot a pixel in bitmap \a bm.
*
* \note bm is evaluated twice.
* \see BM_CLEAR BM_DRAWPIXEL
( *BM_ADDR(bm, x, y) |= BM_MASK(bm, x, y) )
/*!
- * Clear a point in bitmap \a bm.
+ * Clear a pixel in bitmap \a bm.
*
* \note bm is evaluated twice.
* \see BM_PLOT BM_DRAWPIXEL
( *BM_ADDR(bm, x, y) &= ~BM_MASK(bm, x, y) )
/*!
- * Set a point in bitmap \a bm to the specified color.
+ * Set a pixel in bitmap \a bm to the specified color.
*
* \note bm is evaluated twice.
* \note This macro is somewhat slower than BM_PLOT and BM_CLEAR.
*p = (*p & ~mask) | ((fg_pen) ? mask : 0); \
} while (0)
+/*!
+ * Get the value of the pixel in bitmap \a bm.
+ *
+ * \return The returned value is either 0 or 1.
+ *
+ * \note bm is evaluated twice.
+ * \see BM_DRAWPIXEL
+ */
+#define BM_READPIXEL(bm, x, y) \
+ ( *BM_ADDR(bm, x, y) & BM_MASK(bm, x, y) ? 1 : 0 )
/*!
* Initialize a Bitmap structure with the provided parameters.
bm->penX = 0;
bm->penY = 0;
+#if CONFIG_GFX_CLIPPING
bm->cr.xmin = 0;
bm->cr.ymin = 0;
bm->cr.xmax = w;
bm->cr.ymax = h;
+#endif /* CONFIG_GFX_CLIPPING */
}
/*!
* Clear the whole bitmap surface to the background color.
*
- * \note This function does \b not update the current pen position
+ * \note This function does \b not update the current pen position.
*/
void gfx_bitmapClear(Bitmap *bm)
{
- memset(bm->raster, 0, (bm->width * bm->height) / 8);
+ memset(bm->raster, 0, RASTER_SIZE(bm->width, bm->height));
}
}
#endif /* CPU_HARVARD */
-
-/*!
- * Draw a line on the bitmap \a bm using the specified start and end
- * coordinates.
+/**
+ * Copy a rectangular area of a bitmap on another bitmap.
*
- * \note This function does \b not update the current pen position.
+ * Blitting is a common copy operation involving two bitmaps.
+ * A rectangular area of the source bitmap is copied bit-wise
+ * to a different position in the destination bitmap.
+ *
+ * \note Using the same bitmap for \a src and \a dst is unsupported.
+ *
+ * \param dst Bitmap where the operation writes
*
- * \todo Optimize for vertical and horiziontal lines.
*/
-void gfx_line(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
+void gfx_blit(Bitmap *dst, Rect *rect, Bitmap *src, coord_t srcx, coord_t srcy)
{
- int x, y, e, len, adx, ady, signx, signy;
+ coord_t dxmin, dymin, dxmax, dymax;
+ coord_t dx, dy, sx, sy;
-
-#if CONFIG_GFX_CLIPPING
- /* FIXME: broken */
-
- #define XMIN (bm->cr.xmin)
- #define YMIN (bm->cr.ymin)
- #define XMAX (bm->cr.xmax - 1)
- #define YMAX (bm->cr.ymax - 1)
-
- /* Clipping */
- if (x1 < XMIN)
- {
- if (x2 != x1)
- y1 = y2 - ((x2 - XMIN) * (y2 - y1)) / (x2 - x1);
- x1 = XMIN;
- }
- if (y1 < YMIN)
- {
- if (y2 != y1)
- x1 = x2 - ((y2 - YMIN) * (x2 - x1)) / (y2 - y1);
- y1 = YMIN;
- }
- if (x2 < XMIN)
- {
- if (x2 != x1)
- y2 = y2 - ((XMIN - x1) * (y2 - y1)) / (x2 - x1);
- x2 = XMIN;
- }
- if (y2 < YMIN)
- {
- if (y2 != y1)
- x2 = x2 - ((YMIN - y1) * (x2 - x1)) / (y2 - y1);
- y2 = YMIN;
- }
-
- if (x1 > XMAX)
- {
- if (x2 != x1)
- y1 = ((x2 - XMAX) * (y2 - y1)) / (x2 - x1);
- x1 = XMAX;
- }
- if (y1 > YMAX)
- {
- if (y2 != y1)
- x1 = ((y2 - YMAX) * (x2 - x1)) / (y2 - y1);
- y1 = YMAX;
- }
- if (x2 > XMAX)
+ /*
+ * Clip coordinates inside dst->cr and src->width/height.
+ */
+ dxmin = rect->xmin;
+ if (dxmin < dst->cr.xmin)
{
- if (x2 != x1)
- y2 = ((XMAX - x1) * (y2 - y1)) / (x2 - x1);
- x2 = XMAX;
+ srcx += dst->cr.xmin - dxmin;
+ dxmin = dst->cr.xmin;
}
- if (y2 > YMAX)
+ dymin = rect->ymin;
+ if (dymin < dst->cr.ymin)
{
- if (y2 != y1)
- x2 = ((YMAX - y1) * (x2 - x1)) / (y2 - y1);
- y2 = YMAX;
+ srcy += dst->cr.ymin - dymin;
+ dymin = dst->cr.ymin;
}
+ dxmax = MIN(MIN(rect->xmax, rect->xmin + src->width), dst->cr.xmax);
+ dymax = MIN(MIN(rect->ymax, rect->ymin + src->height), dst->cr.ymax);
- #undef XMIN
- #undef YMIN
- #undef XMAX
- #undef YMAX
-
-#endif /* CONFIG_GFX_CLIPPING */
+ /* TODO: make it not as dog slow as this */
+ for (dx = dxmin, sx = srcx; dx < dxmax; ++dx, ++sx)
+ for (dy = dymin, sy = srcy; dy < dymax; ++dy, ++sy)
+ BM_DRAWPIXEL(dst, dx, dy, BM_READPIXEL(src, sx, sy));
+}
+/*!
+ * Draw a sloped line without performing clipping.
+ *
+ * Parameters are the same of gfx_line().
+ * This routine is based on the Bresenham Line-Drawing Algorithm.
+ *
+ * \note Passing coordinates outside the bitmap boundaries will
+ * result in memory trashing.
+ *
+ * \todo Optimize for vertical and horiziontal lines.
+ *
+ * \sa gfx_line()
+ */
+static void gfx_lineUnclipped(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
+{
+ int x, y, e, len, adx, ady, signx, signy;
if (x2 > x1)
{
while (len--)
{
/* Sanity check */
- if ((x >= 0) && (x < bm->width) && (y >= 0) && (y < bm->height))
- BM_PLOT(bm, x, y);
+ ASSERT((x >= 0) && (x < bm->width) && (y >= 0) && (y < bm->height));
+ BM_PLOT(bm, x, y);
x += signx;
e += ady;
if (e >= 0)
while (len--)
{
/* Sanity check */
- if ((x >= 0) && (x < bm->width) && (y >= 0) && (y < bm->height))
- BM_PLOT(bm, x, y);
+ ASSERT ((x >= 0) && (x < bm->width) && (y >= 0) && (y < bm->height));
+ BM_PLOT(bm, x, y);
y += signy;
e += adx;
if (e >= 0)
}
}
+//! Helper routine for gfx_line().
+static int gfx_findRegion(int x, int y, Rect *cr)
+{
+ int code = 0;
+
+ if (y >= cr->ymax)
+ code |= 1; /* below */
+ else if (y < cr->ymin)
+ code |= 2; /* above */
+
+ if (x >= cr->xmax)
+ code |= 4; /* right */
+ else if (x < cr->xmin)
+ code |= 8; /* left */
+
+ return code;
+}
+
+/**
+ * Draw a sloped line segment.
+ *
+ * Draw a sloped line segment identified by the provided
+ * start and end coordinates on the bitmap \a bm.
+ *
+ * The line endpoints are clipped inside the current bitmap
+ * clipping rectangle using the Cohen-Sutherland algorithm,
+ * which is very fast.
+ *
+ * \note The point at coordinates \a x2 \a y2 is not drawn.
+ *
+ * \note This function does \b not update the current pen position.
+ *
+ * \todo Compute updated Bresenham error term.
+ */
+void gfx_line(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
+{
+#if CONFIG_GFX_CLIPPING
+ int clip1 = gfx_findRegion(x1, y1, &bm->cr);
+ int clip2 = gfx_findRegion(x2, y2, &bm->cr);
+
+ /* Loop while there is at least one point outside */
+ while (clip1 | clip2)
+ {
+ /* Check for line totally outside */
+ if (clip1 & clip2)
+ return;
+
+ int c = clip1 ? clip1 : clip2;
+ int x, y;
+
+ if (c & 1) /* Below */
+ {
+ x = x1 + (x2 - x1) * (bm->cr.ymax - y1) / (y2 - y1);
+ y = bm->cr.ymax - 1;
+ }
+ else if (c & 2) /* Above */
+ {
+ x = x1 + (x2 - x1) * (bm->cr.ymin - y1) / (y2 - y1);
+ y = bm->cr.ymin;
+ }
+ else if (c & 4) /* Right */
+ {
+ y = y1 + (y2 - y1) * (bm->cr.xmax - x1) / (x2 - x1);
+ x = bm->cr.xmax - 1;
+ }
+ else /* Left */
+ {
+ y = y1 + (y2 - y1) * (bm->cr.xmin - x1) / (x2 - x1);
+ x = bm->cr.xmin;
+ }
+
+ if (c == clip1) /* First endpoint was clipped */
+ {
+ // TODO: adjust Bresenham error term
+ //coord_t clipdx = ABS(x - x1);
+ //coord_t clipdy = ABS(y - y1);
+ //e += (clipdy * e2) + ((clipdx - clipdy) * e1);
+
+ x1 = x;
+ y1 = y;
+ clip1 = gfx_findRegion(x1, y1, &bm->cr);
+ }
+ else /* Second endpoint was clipped */
+ {
+ x2 = x;
+ y2 = y;
+ clip2 = gfx_findRegion(x2, y2, &bm->cr);
+ }
+ }
+#endif /* CONFIG_GFX_CLIPPING */
+
+ gfx_lineUnclipped(bm, x1, y1, x2, y2);
+}
/*!
* Move the current pen position to the specified coordinates.
+ *
+ * The pen position is used for drawing operations such as
+ * gfx_lineTo(), which can be used to draw polygons.
*/
void gfx_moveTo(Bitmap *bm, coord_t x, coord_t y)
{
*
* \note This function moves the current pen position to the
* new coordinates.
+ *
+ * \sa gfx_line()
*/
void gfx_lineTo(Bitmap *bm, coord_t x, coord_t y)
{
/*!
- * Draw an the outline of an hollow rectangle.
+ * Draw the perimeter of an hollow rectangle.
*
* \note The bottom-right corner of the rectangle is drawn at (x2-1;y2-1).
- * \note This function does \b not update the current pen position
+ * \note This function does \b not update the current pen position.
*/
void gfx_rectDraw(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2)
{
if (x1 > x2) SWAP(x1, x2);
if (y1 > y2) SWAP(y1, y2);
- /* Clip rect to bitmap bounds */
- if (x1 < 0) x1 = 0;
- if (x2 < 0) x2 = 0;
- if (x1 > bm->width) x1 = bm->width;
- if (x2 > bm->width) x2 = bm->width;
- if (y1 < 0) y1 = 0;
- if (y2 < 0) y2 = 0;
- if (y1 > bm->width) y1 = bm->width;
- if (y2 > bm->width) y2 = bm->width;
-
- /*
- * Draw rectangle
- * NOTE: Code paths are duplicated for efficiency
- */
+#if CONFIG_GFX_CLIPPING
+ /* Clip rect to bitmap clip region */
+ if (x1 < bm->cr.xmin) x1 = bm->cr.xmin;
+ if (x2 < bm->cr.xmin) x2 = bm->cr.xmin;
+ if (x1 > bm->cr.xmax) x1 = bm->cr.xmax;
+ if (x2 > bm->cr.xmax) x2 = bm->cr.xmax;
+ if (y1 < bm->cr.ymin) y1 = bm->cr.ymin;
+ if (y2 < bm->cr.ymin) y2 = bm->cr.ymin;
+ if (y1 > bm->cr.ymax) y1 = bm->cr.ymax;
+ if (y2 > bm->cr.ymax) y2 = bm->cr.ymax;
+#endif
+
+ /* NOTE: Code paths are duplicated for efficiency */
if (color) /* fill */
{
for (x = x1; x < x2; x++)
/*!
- * Imposta un rettangolo di clipping per il disegno nella bitmap.
+ * Set the bitmap clipping rectangle to the specified coordinates.
+ *
+ * All drawing performed on the bitmap will be clipped inside this
+ * rectangle.
+ *
+ * \note Following the convention used in all other operations, the
+ * top-left pixels of the rectangle are included, while the
+ * bottom-right pixels are considered outside the clipping region.
*/
void gfx_setClipRect(Bitmap *bm, coord_t minx, coord_t miny, coord_t maxx, coord_t maxy)
{