X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=gfx%2Fgfx.c;h=f4234e9161448e938efbb60edb8edd32292431ea;hb=7af0cce24e5a3eaddb972962a13f2b71bcf8c404;hp=d634c5d67bdae46ad66b45a2475e611c2a495e42;hpb=888d1c49ed7cea0f272f520a74be35a32032bd6e;p=bertos.git diff --git a/gfx/gfx.c b/gfx/gfx.c index d634c5d6..f4234e91 100755 --- a/gfx/gfx.c +++ b/gfx/gfx.c @@ -16,6 +16,15 @@ /*#* *#* $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. + *#* *#* Revision 1.3 2005/11/27 23:33:40 bernie *#* Use appconfig.h instead of cfg/config.h. *#* @@ -43,37 +52,71 @@ #include +/** + * \name Known pixel formats for bitmap representation. + * \{ + */ +#define BITMAP_FMT_PLANAR_H_MSB 1 /**< Planar pixels, horizontal bytes, MSB left. */ +#define BITMAP_FMT_PLANAR_V_LSB 2 /**< Planar pixels, vertical bytes, LSB top. */ +/* \} */ + +#if CONFIG_BITMAP_FMT == BITMAP_FMT_PLANAR_H_MSB + + #define BM_ADDR(bm, x, y) ((bm)->raster + (y) * (bm)->stride + ((x) / 8)) + #define BM_MASK(bm, x, y) (1 << (7 - (x) % 8)) + +#elif CONFIG_BITMAP_FMT == BITMAP_FMT_PLANAR_V_LSB + + #define BM_ADDR(bm, x, y) ((bm)->raster + ((y) / 8) * (bm)->stride + (x)) + #define BM_MASK(bm, x, y) (1 << ((y) % 8)) + +#else + #error Unknown value of CONFIG_BITMAP_FMT +#endif /* CONFIG_BITMAP_FMT */ /*! - * Plot a point in bitmap \a bm. + * Plot a pixel in bitmap \a bm. * - * \note bm is evaluated twice + * \note bm is evaluated twice. + * \see BM_CLEAR BM_DRAWPIXEL */ #define BM_PLOT(bm, x, y) \ - ( *((bm)->raster + ((y) / 8) * (bm)->width + (x)) |= 1 << ((y) % 8) ) + ( *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 + * \note bm is evaluated twice. + * \see BM_PLOT BM_DRAWPIXEL */ #define BM_CLEAR(bm, x, y) \ - ( *((bm)->raster + ((y) / 8) * (bm)->width + (x)) &= ~(1 << ((y) % 8)) ) + ( *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 bm is evaluated twice. * \note This macro is somewhat slower than BM_PLOT and BM_CLEAR. * \see BM_PLOT BM_CLEAR */ #define BM_DRAWPIXEL(bm, x, y, fg_pen) \ do { \ - uint8_t *p = (bm)->raster + ((y) / 8) * (bm)->width + (x); \ - uint8_t mask = 1 << ((y) % 8); \ + uint8_t *p = BM_ADDR(bm, x, y); \ + uint8_t mask = BM_MASK(bm, x, y); \ *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. * @@ -84,19 +127,33 @@ void gfx_bitmapInit(Bitmap *bm, uint8_t *raster, coord_t w, coord_t h) bm->raster = raster; bm->width = w; bm->height = h; + #if (CONFIG_BITMAP_FMT == BITMAP_FMT_PLANAR_H_MSB) + bm->stride = (w + 7) / 8; + #elif CONFIG_BITMAP_FMT == BITMAP_FMT_PLANAR_V_LSB + bm->stride = w; + #else + #error Unknown value of CONFIG_BITMAP_FMT + #endif /* CONFIG_BITMAP_FMT */ 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)); } @@ -116,78 +173,63 @@ void gfx_blit_P(Bitmap *bm, const pgm_uint8_t *raster) } #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; - - -#if CONFIG_GFX_CLIPPING - /* FIXME: broken */ - - #define XMIN 0 - #define YMIN 0 - #define XMAX (bm->width - 1) - #define YMAX (bm->height - 1) + coord_t dxmin, dymin, dxmax, dymax; + coord_t dx, dy, sx, sy; - /* Clipping */ - if (x1 < XMIN) - { - y1 = y2 - ((x2 - XMIN) * (y2 - y1)) / (x2 - x1); - x1 = XMIN; - } - if (y1 < YMIN) - { - x1 = x2 - ((y2 - YMIN) * (x2 - x1)) / (y2 - y1); - y1 = YMIN; - } - if (x2 < XMIN) - { - y2 = y2 - ((XMIN - x1) * (y2 - y1)) / (x2 - x1); - x2 = XMIN; - } - if (y2 < YMIN) - { - x2 = x2 - ((YMIN - y1) * (x2 - x1)) / (y2 - y1); - y2 = YMIN; - } - - if (x1 > XMAX) - { - y1 = ((x2 - XMAX) * (y2 - y1)) / (x2 - x1); - x1 = XMAX; - } - if (y1 > YMAX) - { - 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) { - 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) { - 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) { @@ -227,8 +269,8 @@ void gfx_line(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2) 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) @@ -247,8 +289,8 @@ void gfx_line(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2) 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) @@ -260,9 +302,105 @@ void gfx_line(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2) } } +//! 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) { @@ -275,6 +413,8 @@ 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) { @@ -284,10 +424,10 @@ 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 border of the rectangle is not drawn. - * \note This function does \b not update the current pen position + * \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. */ void gfx_rectDraw(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2) { @@ -308,7 +448,7 @@ void gfx_rectDraw(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2) * * \note The bottom-right border of the rectangle is not drawn. * - * \note This function does \b not update the current pen position + * \note This function does \b not update the current pen position. */ void gfx_rectFillC(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t color) { @@ -318,20 +458,19 @@ void gfx_rectFillC(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2, u 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++) @@ -352,7 +491,7 @@ void gfx_rectFillC(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2, u * * \note The bottom-right border of the rectangle is not drawn. * - * \note This function does \b not update the current pen position + * \note This function does \b not update the current pen position. */ void gfx_rectFill(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2) { @@ -363,9 +502,9 @@ void gfx_rectFill(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2) /*! * Clear a rectangular area. * - * \note The bottom-right border of the rectangle is not drawn. + * \note The bottom-right border of the rectangle is not cleared. * - * \note This function does \b not update the current pen position + * \note This function does \b not update the current pen position. */ void gfx_rectClear(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2) { @@ -374,7 +513,14 @@ void gfx_rectClear(Bitmap *bm, coord_t x1, coord_t y1, coord_t x2, coord_t y2) /*! - * 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) {