1 /**************************************************************************/
2 /* */
3 /* Copyright (c) Microsoft Corporation. All rights reserved. */
4 /* */
5 /* This software is licensed under the Microsoft Software License */
6 /* Terms for Microsoft Azure RTOS. Full text of the license can be */
7 /* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
8 /* and in the root directory of this software. */
9 /* */
10 /**************************************************************************/
11
12
13 /**************************************************************************/
14 /**************************************************************************/
15 /** */
16 /** GUIX Component */
17 /** */
18 /** Display Management (Display) */
19 /** */
20 /**************************************************************************/
21
22 #define GX_SOURCE_CODE
23
24
25 /* Include necessary system files. */
26
27 #include "gx_api.h"
28 #include "gx_utility.h"
29 #include "gx_display.h"
30
31 /**************************************************************************/
32 /* */
33 /* FUNCTION RELEASE */
34 /* */
35 /* _gx_display_driver_generic_ellipse_draw PORTABLE C */
36 /* 6.1.6 */
37 /* AUTHOR */
38 /* */
39 /* Kenneth Maxwell, Microsoft Corporation */
40 /* */
41 /* DESCRIPTION */
42 /* */
43 /* Display driver to draw ellipse. */
44 /* */
45 /* INPUT */
46 /* */
47 /* context Drawing context */
48 /* xcenter x-coord of center of ellipse */
49 /* ycenter y-coord of center of ellipse */
50 /* a Length of the Semi-major Axis */
51 /* b Length of the Semi-minor Axis */
52 /* */
53 /* OUTPUT */
54 /* */
55 /* None */
56 /* */
57 /* CALLS */
58 /* */
59 /* [gx_display_driver_pixel_blend] Basic display driver pixel */
60 /* blend function */
61 /* [gx_display_driver_pixel_write] Basic display driver pixel */
62 /* write function */
63 /* _gx_utility_rectangle_point_detect Detect whether a pixel is */
64 /* inside rectangle */
65 /* */
66 /* CALLED BY */
67 /* */
68 /* GUIX Internal Code */
69 /* */
70 /* RELEASE HISTORY */
71 /* */
72 /* DATE NAME DESCRIPTION */
73 /* */
74 /* 05-19-2020 Kenneth Maxwell Initial Version 6.0 */
75 /* 09-30-2020 Kenneth Maxwell Modified comment(s), */
76 /* resulting in version 6.1 */
77 /* 04-02-2021 Kenneth Maxwell Modified comment(s), */
78 /* improved precision, */
79 /* resulting in version 6.1.6 */
80 /* */
81 /**************************************************************************/
_gx_display_driver_generic_ellipse_draw(GX_DRAW_CONTEXT * context,INT xcenter,INT ycenter,INT a,INT b)82 VOID _gx_display_driver_generic_ellipse_draw(GX_DRAW_CONTEXT *context, INT xcenter, INT ycenter, INT a, INT b)
83 {
84 /* The ellipse draw function is implemented from Bresenham ellipse algorithm. */
85 INT x;
86 INT y;
87 INT d;
88 GX_POINT point;
89 INT sign[4][2] = {{1, 1}, {-1, 1}, {1, -1}, {-1, -1}};
90 INT index;
91 INT aa;
92 INT bb;
93 GX_DISPLAY *display = context -> gx_draw_context_display;
94 GX_BRUSH *brush = &context -> gx_draw_context_brush;
95 GX_RECTANGLE *clip = context -> gx_draw_context_clip;
96
97 #if defined(GX_BRUSH_ALPHA_SUPPORT)
98 GX_UBYTE brush_alpha = brush -> gx_brush_alpha;
99
100 if (display -> gx_display_driver_pixel_blend == GX_NULL)
101 {
102 /* Pixel blend function is null means alpha isn't supported in this driver.
103 So set alpha value to 0xff to make it draw the original color in case GX_BRUSH_ALPHA_SUPPORT is defined. */
104 brush_alpha = 0xff;
105 }
106 else
107 {
108 if (brush_alpha == 0)
109 {
110 /* Nothing to draw here. */
111 return;
112 }
113 }
114 #endif
115
116 /* The ellipse is split into 2 regions, region I with dx > dy and resion II with dx <= dy.
117 In region I, the midpoint between (x + 1, y) and (x + 1, y - 1) is used to select next point that is closer to the ellipse,
118 d(x + 1, y - 0.5) is the distance from the midpoint to the ellipse center,
119 if the decision < 0, the midpoint is inside the ellipse, point (x + 1, y - 1) is closer to the ellipse, and be selected for drawing.
120 otherwise, (x + 1, y) is selected.
121 In region II, the midpoint between (x, y - 1) and (x + 1, y - 1) is used to select next point that is closer to the ellipse,
122 d(x + 0.5, y - 1) is the distance from the midpoint to ellipse center,
123 if the decision < 0, the midpoint is inside the ellipse, point(x + 1, y - 1) is closer to the ellipse, and be selected for drawing,
124 otherwise, (x, y - 1) is selected.
125
126 Ellipse equation is f(x, y) = sqr(b * x) + sqr(a * y) - sqr(a * b).
127 First, we assume ellipse is centered at the origin(0, 0), and the first point is (0, b).
128 Set initial decision value for region I as d = f(1, b - 0.5) = sqr(b) + sqr(a) * (-b + 0.25).
129 */
130
131 aa = a * a;
132 bb = b * b;
133 x = 0;
134 y = b;
135
136 /* Decision is enlarged by 4 to avoid floating point. */
137 d = (bb << 2) + aa * (1 - (b << 2));
138
139 #if defined(GX_BRUSH_ALPHA_SUPPORT)
140 if (brush_alpha != 0xff)
141 {
142
143 /* Region I of the first quarter of the ellipse. */
144 while ((bb << 1) * (x + 1) < aa * (2 * y - 1))
145 {
146 for (index = 0; index < 4; index++)
147 {
148 point.gx_point_x = (GX_VALUE)(x * sign[index][0] + xcenter);
149 point.gx_point_y = (GX_VALUE)(y * sign[index][1] + ycenter);
150
151 if (_gx_utility_rectangle_point_detect(clip, point))
152 {
153 display -> gx_display_driver_pixel_blend(context, point.gx_point_x, point.gx_point_y, brush -> gx_brush_line_color, brush_alpha);
154 }
155 }
156
157 if (d < 0)
158 {
159 d += (bb << 2) * ((x << 1) + 3);
160 }
161 else
162 {
163 d += (bb << 2) * ((x << 1) + 3) + (aa << 3) * (1 - y);
164 y--;
165 }
166 x++;
167 }
168
169 d = bb * ((x << 1) + 1) * ((x << 1) + 1) + (aa << 2) * (y - 1) * (y - 1) - (aa << 2) * bb;
170
171 /* Region II of the first quarter of the ellipse. */
172 while (y >= 0)
173 {
174 for (index = 0; index < 4; index++)
175 {
176 point.gx_point_x = (GX_VALUE)(x * sign[index][0] + xcenter);
177 point.gx_point_y = (GX_VALUE)(y * sign[index][1] + ycenter);
178
179 if (_gx_utility_rectangle_point_detect(clip, point))
180 {
181 display -> gx_display_driver_pixel_blend(context, point.gx_point_x, point.gx_point_y, brush -> gx_brush_line_color, brush_alpha);
182 }
183 }
184
185 if (d < 0)
186 {
187 d += (bb << 3) * (x + 1) + (aa << 2) * (3 - (y << 1));
188 x++;
189 }
190 else
191 {
192 d += (aa << 2) * (3 - (y << 1));
193 }
194
195 y--;
196 }
197 }
198 else
199 {
200 #endif
201 /* Region I of the first quarter of the ellipse. */
202 while ((bb << 1) * (x + 1) < aa * (2 * y - 1))
203 {
204 for (index = 0; index < 4; index++)
205 {
206 point.gx_point_x = (GX_VALUE)(x * sign[index][0] + xcenter);
207 point.gx_point_y = (GX_VALUE)(y * sign[index][1] + ycenter);
208
209 if (_gx_utility_rectangle_point_detect(clip, point))
210 {
211 display -> gx_display_driver_pixel_write(context, point.gx_point_x, point.gx_point_y, brush -> gx_brush_line_color);
212 }
213 }
214
215 if (d < 0)
216 {
217 d += (bb << 2) * ((x << 1) + 3);
218 }
219 else
220 {
221 d += (bb << 2) * ((x << 1) + 3) + (aa << 3) * (1 - y);
222 y--;
223 }
224 x++;
225 }
226
227 /* Set initial decision value for region II as d = f(x + 0.5, y - 1) = sqr(b * (x + 0.5) + sqr(a * (y - 1)) - sqr(a * b).
228 Enlarge the decision by 4 to avoid float calculation. */
229
230 d = bb * ((x << 1) + 1) * ((x << 1) + 1) + (aa << 2) * (y - 1) * (y - 1) - (aa << 2) * bb;
231
232 /* Region II of the first quarter of the ellipse. */
233 while (y >= 0)
234 {
235 for (index = 0; index < 4; index++)
236 {
237 point.gx_point_x = (GX_VALUE)(x * sign[index][0] + xcenter);
238 point.gx_point_y = (GX_VALUE)(y * sign[index][1] + ycenter);
239
240 if (_gx_utility_rectangle_point_detect(clip, point))
241 {
242 display -> gx_display_driver_pixel_write(context, point.gx_point_x, point.gx_point_y, brush -> gx_brush_line_color);
243 }
244 }
245
246 if (d < 0)
247 {
248 d += (bb << 3) * (x + 1) + (aa << 2) * (3 - (y << 1));
249 x++;
250 }
251 else
252 {
253 d += (aa << 2) * (3 - (y << 1));
254 }
255
256 y--;
257 }
258 #if defined(GX_BRUSH_ALPHA_SUPPORT)
259 }
260 #endif
261 }
262
263