diff --git a/MicroView.cpp b/MicroView.cpp index cffada8..8856d4a 100644 --- a/MicroView.cpp +++ b/MicroView.cpp @@ -604,47 +604,62 @@ void MicroView::circleFill(uint8_t x0, uint8_t y0, uint8_t radius) { /** \brief Draw filled circle with color and mode. - Draw filled circle with radius using color and mode at x,y of the screen buffer. + Draw filled circle with radius using color and mode at x,y of the screen + buffer. Uses the Bresenham circle algorithm with a few modifications to + paint the circle without overlapping draw operations. */ -void MicroView::circleFill(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t color, uint8_t mode) { - // Don't bother trying to draw something totally offscreen - if (x0 - radius >= LCDWIDTH || y0 - radius >= LCDHEIGHT) { - return; - } - - // 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; - - // 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. - uint16_t rx2 = (x-x0) * (x-x0); - - // Scan vertically... - for (uint16_t y = yStart; y <= yEnd; ++y) { - uint16_t ry2 = (y-y0) * (y-y0); - if (rx2 + ry2 <= radiusSq) { - pixel(x, y, color, mode); - intersected = true; +void MicroView::circleFill(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t +color, uint8_t mode) { + int8_t x = radius; + int8_t y = 0; + int8_t radiusError = 1 - x; + int8_t y_last = y0 + x; + int8_t y_low; + int8_t x_alt; + int8_t x_alt_max; + while (x >= y) { + pixel(x + x0, y + y0, color, mode); + x_alt = (x0 - x); + x_alt_max = x0 + x; + while (x_alt < x_alt_max) { + pixel(x_alt, y + y0, color, mode); + x_alt++; + } + if (y != x) { + pixel(y + x0, x + y0, color, mode); + pixel(y + x0, y0 - x, color, mode); + } + if (y != 0) { + pixel(x + x0, y0 - y, color, mode); + x_alt = (x0 - x); + x_alt_max = x0 + x; + while (x_alt < x_alt_max) { + pixel(x_alt, y0 - y, color, mode); + x_alt++; } - else if (intersected) { - // We've left the circle. Move on to the next horizontal scan line. - break; + if (y != x) { + pixel(x0 - y, x + y0, color, mode); + pixel(x0 - y, y0 - x, color, mode); + if (y_last > y0 + x) { + x_alt = x0 - y + 1; + x_alt_max = x0 + y; + y_last = y0 + x; + y_low = y0 - x; + while (x_alt < x_alt_max) { + pixel(x_alt, y_last, color, mode); + pixel(x_alt, y_low, color, mode); + x_alt++; + } + } } } + y++; + if (radiusError<0) { + radiusError += 2 * y + 1; + } else { + x--; + radiusError += 2 * (y - x + 1); + } } }