From 9573e7d6ccb69acb5f7643e2caf05973a3810675 Mon Sep 17 00:00:00 2001 From: Emil Ong Date: Sun, 24 Aug 2014 14:22:30 -0700 Subject: [PATCH] Fix clipping issue for circleFill when top is offscreen. Replaced circleFill algorithm with a scanning algorithm. Also fixes problem with XOR draw mode for filled circles. --- MicroView.cpp | 65 +++++++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/MicroView.cpp b/MicroView.cpp index e97b2fa..cc3db1c 100644 --- a/MicroView.cpp +++ b/MicroView.cpp @@ -591,7 +591,6 @@ void MicroView::circle(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t color, ui pixel(x0 - y, y0 + x, color, mode); pixel(x0 + y, y0 - x, color, mode); pixel(x0 - y, y0 - x, color, mode); - } } @@ -608,38 +607,44 @@ void MicroView::circleFill(uint8_t x0, uint8_t y0, uint8_t radius) { Draw filled circle with radius using color and mode at x,y of the screen buffer. */ void MicroView::circleFill(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t color, uint8_t mode) { - // TODO - - find a way to check for no overlapping of pixels so that XOR draw mode will work perfectly - int8_t f = 1 - radius; - int8_t ddF_x = 1; - int8_t ddF_y = -2 * radius; - int8_t x = 0; - int8_t y = radius; - - // Temporary disable fill circle for XOR mode. - if (mode==XOR) return; - - for (uint8_t i=y0-radius; i<=y0+radius; i++) { - pixel(x0, i, color, mode); + // Don't bother trying to draw something totally offscreen + if (x0 - radius >= LCDWIDTH || y0 - radius >= LCDHEIGHT) { + return; } - while (x= 0) { - y--; - ddF_y += 2; - f += ddF_y; - } - x++; - ddF_x += 2; - f += ddF_x; + // High-level algorithm overview: + // Scan horizontally from left to right, then top to bottom, checking if each pixel + // is within the circle. We use uint16_t's because even the small screen squares beyond 8 bits. + uint16_t radiusSq = radius * radius; - for (uint8_t i=y0-y; i<=y0+y; i++) { - pixel(x0+x, i, color, mode); - pixel(x0-x, i, color, mode); - } - for (uint8_t i=y0-x; i<=y0+x; i++) { - pixel(x0+y, i, color, mode); - pixel(x0-y, i, color, mode); - } + // Optimization: define the start and end onscreen + uint16_t xStart = max(0, x0-radius); + uint16_t xEnd = min(LCDWIDTH-1, x0+radius); + uint16_t yStart = max(0, y0-radius); + uint16_t yEnd = min(LCDHEIGHT-1, y0+radius); + + // scan horizontally... + for (uint16_t x = xStart; x <= xEnd; ++x) { + // Optimization: Record where if we have intersected the circle on this vertical + // scan. Once we have intersected, then don't intersect anymore, don't bother + // drawing; we've exited the circle. + bool intersected = false; + + // Optimization: relative x squared only changes with the outer loop/the horizontal scan. + int16_t rx2 = (x-x0) * (x-x0); + + // Scan vertically... + for (uint16_t y = yStart; y <= yEnd; ++y) { + int16_t ry2 = (y-y0) * (y-y0); + if (rx2 + ry2 <= radiusSq) { + pixel(x, y, color, mode); + intersected = true; + } + else if (intersected) { + // We've left the circle. Move on to the next horizontal scan line. + break; + } + } } }