qp_draw_circle.c (6054B)
1 // Copyright 2021 Paul Cotter (@gr1mr3aver) 2 // Copyright 2021 Nick Brassel (@tzarc) 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 #include "qp.h" 6 #include "qp_internal.h" 7 #include "qp_comms.h" 8 #include "qp_draw.h" 9 10 // Utilize 8-way symmetry to draw circles 11 static bool qp_circle_helper_impl(painter_device_t device, uint16_t centerx, uint16_t centery, uint16_t offsetx, uint16_t offsety, bool filled) { 12 /* 13 Circles have the property of 8-way symmetry, so eight pixels can be drawn 14 for each computed [offsetx,offsety] given the center coordinates 15 represented by [centerx,centery]. 16 17 For filled circles, we can draw horizontal lines between each pair of 18 pixels with the same final value of y. 19 20 Two special cases exist and have been optimized: 21 1) offsetx == offsety (the final point), makes half the coordinates 22 equivalent, so we can omit them (and the corresponding fill lines) 23 2) offsetx == 0 (the starting point) means that some horizontal lines 24 would be a single pixel in length, so we write individual pixels instead. 25 This also makes half the symmetrical points identical to their twins, 26 so we only need four points or two points and one line 27 */ 28 29 int16_t xpx = ((int16_t)centerx) + ((int16_t)offsetx); 30 int16_t xmx = ((int16_t)centerx) - ((int16_t)offsetx); 31 int16_t xpy = ((int16_t)centerx) + ((int16_t)offsety); 32 int16_t xmy = ((int16_t)centerx) - ((int16_t)offsety); 33 int16_t ypx = ((int16_t)centery) + ((int16_t)offsetx); 34 int16_t ymx = ((int16_t)centery) - ((int16_t)offsetx); 35 int16_t ypy = ((int16_t)centery) + ((int16_t)offsety); 36 int16_t ymy = ((int16_t)centery) - ((int16_t)offsety); 37 38 if (offsetx == 0) { 39 if (!qp_internal_setpixel_impl(device, centerx, ypy)) { 40 return false; 41 } 42 if (!qp_internal_setpixel_impl(device, centerx, ymy)) { 43 return false; 44 } 45 if (filled) { 46 if (!qp_internal_fillrect_helper_impl(device, xpy, centery, xmy, centery)) { 47 return false; 48 } 49 } else { 50 if (!qp_internal_setpixel_impl(device, xpy, centery)) { 51 return false; 52 } 53 if (!qp_internal_setpixel_impl(device, xmy, centery)) { 54 return false; 55 } 56 } 57 } else if (offsetx == offsety) { 58 if (filled) { 59 if (!qp_internal_fillrect_helper_impl(device, xpy, ypy, xmy, ypy)) { 60 return false; 61 } 62 if (!qp_internal_fillrect_helper_impl(device, xpy, ymy, xmy, ymy)) { 63 return false; 64 } 65 } else { 66 if (!qp_internal_setpixel_impl(device, xpy, ypy)) { 67 return false; 68 } 69 if (!qp_internal_setpixel_impl(device, xmy, ypy)) { 70 return false; 71 } 72 if (!qp_internal_setpixel_impl(device, xpy, ymy)) { 73 return false; 74 } 75 if (!qp_internal_setpixel_impl(device, xmy, ymy)) { 76 return false; 77 } 78 } 79 80 } else { 81 if (filled) { 82 if (!qp_internal_fillrect_helper_impl(device, xpx, ypy, xmx, ypy)) { 83 return false; 84 } 85 if (!qp_internal_fillrect_helper_impl(device, xpx, ymy, xmx, ymy)) { 86 return false; 87 } 88 if (!qp_internal_fillrect_helper_impl(device, xpy, ypx, xmy, ypx)) { 89 return false; 90 } 91 if (!qp_internal_fillrect_helper_impl(device, xpy, ymx, xmy, ymx)) { 92 return false; 93 } 94 } else { 95 if (!qp_internal_setpixel_impl(device, xpx, ypy)) { 96 return false; 97 } 98 if (!qp_internal_setpixel_impl(device, xmx, ypy)) { 99 return false; 100 } 101 if (!qp_internal_setpixel_impl(device, xpx, ymy)) { 102 return false; 103 } 104 if (!qp_internal_setpixel_impl(device, xmx, ymy)) { 105 return false; 106 } 107 if (!qp_internal_setpixel_impl(device, xpy, ypx)) { 108 return false; 109 } 110 if (!qp_internal_setpixel_impl(device, xmy, ypx)) { 111 return false; 112 } 113 if (!qp_internal_setpixel_impl(device, xpy, ymx)) { 114 return false; 115 } 116 if (!qp_internal_setpixel_impl(device, xmy, ymx)) { 117 return false; 118 } 119 } 120 } 121 122 return true; 123 } 124 125 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 126 // Quantum Painter External API: qp_circle 127 128 bool qp_circle(painter_device_t device, uint16_t x, uint16_t y, uint16_t radius, uint8_t hue, uint8_t sat, uint8_t val, bool filled) { 129 qp_dprintf("qp_circle: entry\n"); 130 painter_driver_t *driver = (painter_driver_t *)device; 131 if (!driver || !driver->validate_ok) { 132 qp_dprintf("qp_circle: fail (validation_ok == false)\n"); 133 return false; 134 } 135 136 // plot the initial set of points for x, y and r 137 int16_t xcalc = 0; 138 int16_t ycalc = (int16_t)radius; 139 int16_t err = ((5 - (radius >> 2)) >> 2); 140 141 qp_internal_fill_pixdata(device, (radius * 2) + 1, hue, sat, val); 142 143 if (!qp_comms_start(device)) { 144 qp_dprintf("qp_circle: fail (could not start comms)\n"); 145 return false; 146 } 147 148 bool ret = true; 149 if (!qp_circle_helper_impl(device, x, y, xcalc, ycalc, filled)) { 150 ret = false; 151 } 152 153 if (ret) { 154 while (xcalc < ycalc) { 155 xcalc++; 156 if (err < 0) { 157 err += (xcalc << 1) + 1; 158 } else { 159 ycalc--; 160 err += ((xcalc - ycalc) << 1) + 1; 161 } 162 if (!qp_circle_helper_impl(device, x, y, xcalc, ycalc, filled)) { 163 ret = false; 164 break; 165 } 166 } 167 } 168 169 qp_dprintf("qp_circle: %s\n", ret ? "ok" : "fail"); 170 qp_comms_stop(device); 171 return ret; 172 }