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