Merge pull request #21 from ben-zen/circle_drawing

Improved circleFill() drawing
This commit is contained in:
jpliew
2014-10-04 13:52:12 +10:00

View File

@@ -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);
}
}
}