1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** GUIX Component                                                        */
16 /**                                                                       */
17 /**   Display Management (Display)                                        */
18 /**                                                                       */
19 /**************************************************************************/
20 
21 
22 #define PIXEL_WRITE(loc, val) (*(loc) = ((USHORT)val))
23 
24 #define GX_SOURCE_CODE
25 
26 /* Include necessary system files.  */
27 
28 #include "gx_api.h"
29 #include "gx_utility.h"
30 #include "gx_display.h"
31 
32 #if defined (GX_BRUSH_ALPHA_SUPPORT)
33 /**************************************************************************/
34 /*                                                                        */
35 /*  FUNCTION                                               RELEASE        */
36 /*                                                                        */
37 /*    _gx_display_driver_simple_line_alpha_draw           PORTABLE C      */
38 /*                                                           6.1          */
39 /*  AUTHOR                                                                */
40 /*                                                                        */
41 /*    Kenneth Maxwell, Microsoft Corporation                              */
42 /*                                                                        */
43 /*  DESCRIPTION                                                           */
44 /*                                                                        */
45 /*    Generic driver function that handles drawing lines with brush       */
46 /*    alpha.                                                              */
47 /*                                                                        */
48 /*  INPUT                                                                 */
49 /*                                                                        */
50 /*    context                               Drawing context               */
51 /*    xstart                                x-coord of endpoint           */
52 /*    ystart                                y-coord of endpoint           */
53 /*    xend                                  x-coord of endpoint           */
54 /*    yend                                  y-coord of endpoint           */
55 /*                                                                        */
56 /*  OUTPUT                                                                */
57 /*                                                                        */
58 /*    None                                                                */
59 /*                                                                        */
60 /*  CALLS                                                                 */
61 /*                                                                        */
62 /*    GX_ABS                                Compute the absolute value    */
63 /*    GX_SWAP_VALUE                         Swap two values               */
64 /*    [PIXEL_WRITE]                         Driver level pixel write      */
65 /*                                            routine                     */
66 /*    [gx_display_driver_pixel_blend]       Basic display driver pixel    */
67 /*                                            blend function              */
68 /*                                                                        */
69 /*  CALLED BY                                                             */
70 /*                                                                        */
71 /*    GUIX Internal Code                                                  */
72 /*                                                                        */
73 /*  RELEASE HISTORY                                                       */
74 /*                                                                        */
75 /*    DATE              NAME                      DESCRIPTION             */
76 /*                                                                        */
77 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
78 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
79 /*                                            resulting in version 6.1    */
80 /*                                                                        */
81 /**************************************************************************/
_gx_display_driver_simple_line_alpha_draw(GX_DRAW_CONTEXT * context,INT xstart,INT ystart,INT xend,INT yend,GX_UBYTE alpha)82 VOID _gx_display_driver_simple_line_alpha_draw(GX_DRAW_CONTEXT *context, INT xstart, INT ystart, INT xend, INT yend, GX_UBYTE alpha)
83 {
84 INT           curx;
85 INT           cury;
86 INT           x_sign;
87 INT           y_sign;
88 INT           decision;
89 INT           nextx;
90 INT           nexty;
91 GX_POINT      end_point;
92 GX_POINT      mid_point;
93 GX_RECTANGLE  half_rectangle;
94 GX_RECTANGLE  half_over;
95 INT           sign;
96 INT           steps;
97 GX_BOOL       clipped = GX_TRUE;
98 INT           dx = GX_ABS(xend - xstart);
99 INT           dy = GX_ABS(yend - ystart);
100 
101 GX_RECTANGLE *clip = context -> gx_draw_context_clip;
102 GX_COLOR      linecolor = context -> gx_draw_context_brush.gx_brush_line_color;
103 VOID        (*blend_func)(GX_DRAW_CONTEXT *context, INT x, INT y, GX_COLOR fcolor, GX_UBYTE alpha);
104 
105     blend_func = context -> gx_draw_context_display -> gx_display_driver_pixel_blend;
106 
107     if (blend_func == GX_NULL)
108     {
109         return;
110     }
111 
112     if (((dx >= dy && (xstart > xend)) || ((dy > dx) && ystart > yend)))
113     {
114         GX_SWAP_VALS(xend, xstart);
115         GX_SWAP_VALS(yend, ystart);
116     }
117     x_sign = (xend - xstart) / dx;
118     y_sign = (yend - ystart) / dy;
119 
120     end_point.gx_point_x = (GX_VALUE)xstart;
121     end_point.gx_point_y = (GX_VALUE)ystart;
122 
123     if (_gx_utility_rectangle_point_detect(clip, end_point))
124     {
125         end_point.gx_point_x = (GX_VALUE)xend;
126         end_point.gx_point_y = (GX_VALUE)yend;
127 
128         if (_gx_utility_rectangle_point_detect(clip, end_point))
129         {
130             clipped = GX_FALSE;
131         }
132     }
133 
134     if (clipped)
135     {
136         /* here if we must do clipping in the inner loop, because one
137         or both of the end points are outside clipping rectangle */
138 
139         /* Calculate the middle point of the line.  */
140         mid_point.gx_point_x = (GX_VALUE)((xend + xstart) >> 1);
141         mid_point.gx_point_y = (GX_VALUE)((yend + ystart) >> 1);
142 
143         /* Judge the clip in which side.  */
144         if (_gx_utility_rectangle_point_detect(clip, mid_point))
145         {
146 
147             /* the clip in two sides.  */
148             if (dx >= dy)
149             {
150                 /* walk out the clipping point.  */
151                 for (curx = xstart, cury = ystart, decision = (dx >> 1); curx < mid_point.gx_point_x;
152                     curx++, decision += dy)
153                 {
154                     if (decision >= dx)
155                     {
156                         decision -= dx;
157                         cury += y_sign;
158                     }
159 
160                     if (curx >= clip -> gx_rectangle_left &&
161                         cury >= clip -> gx_rectangle_top &&
162                         cury <= clip -> gx_rectangle_bottom)
163                     {
164                         break;
165                     }
166                 }
167                 for (; curx <= mid_point.gx_point_x;
168                     curx++, decision += dy)
169                 {
170                     if (decision >= dx)
171                     {
172                         decision -= dx;
173                         cury += y_sign;
174                     }
175 
176                     blend_func(context, curx, cury, linecolor, alpha);
177                 }
178                 for (nextx = xend, nexty = yend, decision = (dx >> 1); nextx > mid_point.gx_point_x;
179                     nextx--, decision += dy)
180                 {
181                     if (decision >= dx)
182                     {
183                         decision -= dx;
184                         nexty -= y_sign;
185                     }
186                     if (nextx <= clip -> gx_rectangle_right &&
187                         nexty >= clip -> gx_rectangle_top &&
188                         nexty <= clip -> gx_rectangle_bottom)
189                     {
190                         break;
191                     }
192                 }
193 
194                 for (; nextx > mid_point.gx_point_x;
195                     nextx--, decision += dy)
196                 {
197                     if (decision >= dx)
198                     {
199                         decision -= dx;
200                         nexty -= y_sign;
201                     }
202                     blend_func(context, nextx, nexty, linecolor, alpha);
203                 }
204             }
205             else
206             {
207                 for (nextx = xend, nexty = yend, decision = (dy >> 1); nexty > mid_point.gx_point_y;
208                     nexty--, decision += dx)
209                 {
210                     if (decision >= dy)
211                     {
212                         decision -= dy;
213                         nextx -= x_sign;
214                     }
215                     if (nextx >= clip -> gx_rectangle_left &&
216                         nextx <= clip -> gx_rectangle_right &&
217                         nexty <= clip -> gx_rectangle_bottom)
218                     {
219                         break;
220                     }
221                 }
222 
223                 for (; nexty > mid_point.gx_point_y;
224                     nexty--, decision += dx)
225                 {
226                     if (decision >= dy)
227                     {
228                         decision -= dy;
229                         nextx -= x_sign;
230                     }
231                     blend_func(context, nextx, nexty, linecolor, alpha);
232                 }
233 
234                 /* walk out the clipping point.  */
235                 for (curx = xstart, cury = ystart, decision = (dy >> 1); cury < mid_point.gx_point_y;
236                     cury++, decision += dx)
237                 {
238                     if (decision >= dy)
239                     {
240                         decision -= dy;
241                         curx += x_sign;
242                     }
243 
244                     if (curx >= clip -> gx_rectangle_left &&
245                         curx <= clip -> gx_rectangle_right &&
246                         cury >= clip -> gx_rectangle_top)
247                     {
248                         break;
249                     }
250                 }
251                 for (; cury <= mid_point.gx_point_y;
252                     cury++, decision += dx)
253                 {
254                     if (decision >= dy)
255                     {
256                         decision -= dy;
257                         curx += x_sign;
258                     }
259                     blend_func(context, curx, cury, linecolor, alpha);
260                 }
261             }
262         }
263         else
264         {
265             /* The clip stay at one side.  */
266             if (dx >= dy)
267             {
268                 half_rectangle.gx_rectangle_left = (GX_VALUE)xstart;
269                 half_rectangle.gx_rectangle_right = mid_point.gx_point_x;
270                 if (y_sign == 1)
271                 {
272                     half_rectangle.gx_rectangle_top = (GX_VALUE)ystart;
273                     half_rectangle.gx_rectangle_bottom = mid_point.gx_point_y;
274                 }
275                 else
276                 {
277                     half_rectangle.gx_rectangle_top = mid_point.gx_point_y;
278                     half_rectangle.gx_rectangle_bottom = (GX_VALUE)ystart;
279                 }
280 
281                 if (_gx_utility_rectangle_overlap_detect(clip, &half_rectangle, &half_over))
282                 {
283                     curx = xstart;
284                     cury = ystart;
285                     steps = mid_point.gx_point_x - curx + 1;
286                     sign = 1;
287                 }
288                 else
289                 {
290                     curx = xend;
291                     cury = yend;
292                     steps = xend - mid_point.gx_point_x;
293                     sign = -1;
294                     y_sign = 0 - y_sign;
295                 }
296                 for (decision = (dx >> 1); steps > 0; curx += sign, decision += dy, steps--)
297                 {
298                     if (decision >= dx)
299                     {
300                         decision -= dx;
301                         cury += y_sign;
302                     }
303 
304                     if (curx >= clip -> gx_rectangle_left &&
305                         curx <= clip -> gx_rectangle_right &&
306                         cury >= clip -> gx_rectangle_top &&
307                         cury <= clip -> gx_rectangle_bottom)
308                     {
309                         blend_func(context, curx, cury, linecolor, alpha);
310                     }
311                 }
312             }
313             else
314             {
315                 half_rectangle.gx_rectangle_top = (GX_VALUE)ystart;
316                 half_rectangle.gx_rectangle_bottom = mid_point.gx_point_y;
317                 if (x_sign == 1)
318                 {
319                     half_rectangle.gx_rectangle_right = mid_point.gx_point_x;
320                     half_rectangle.gx_rectangle_left = (GX_VALUE)xstart;
321                 }
322                 else
323                 {
324                     half_rectangle.gx_rectangle_right = (GX_VALUE)xstart;
325                     half_rectangle.gx_rectangle_left = mid_point.gx_point_x;
326                 }
327 
328                 if (_gx_utility_rectangle_overlap_detect(clip, &half_rectangle, &half_over))
329                 {
330                     curx = xstart;
331                     cury = ystart;
332                     steps = mid_point.gx_point_y - cury + 1;
333                     sign = 1;
334                 }
335                 else
336                 {
337                     curx = xend;
338                     cury = yend;
339                     steps = yend - mid_point.gx_point_y;
340                     sign = -1;
341                     x_sign = 0 - x_sign;
342                 }
343 
344                 for (decision = (dy >> 1); steps > 0; cury += sign, decision += dx, steps--)
345                 {
346                     if (decision >= dy)
347                     {
348                         decision -= dy;
349                         curx += x_sign;
350                     }
351                     if (curx >= clip -> gx_rectangle_left &&
352                         curx <= clip -> gx_rectangle_right &&
353                         cury >= clip -> gx_rectangle_top &&
354                         cury <= clip -> gx_rectangle_bottom)
355                     {
356                         blend_func(context, curx, cury, linecolor, alpha);
357                     }
358                 }
359             }
360         }
361     }
362     else
363     {
364         /* here if both line ends lie within clipping rectangle, we can
365         run a faster inner loop */
366         if (dx >= dy)
367         {
368             for (curx = xstart, cury = ystart, nextx = xend, nexty = yend,
369                 decision = (dx >> 1); curx <= nextx; curx++, nextx--,
370                 decision += dy)
371             {
372 
373                 if (decision >= dx)
374                 {
375                     decision -= dx;
376                     cury += y_sign;
377                     nexty -= y_sign;
378 
379                 }
380                 blend_func(context, curx, cury, linecolor, alpha);
381                 blend_func(context, nextx, nexty, linecolor, alpha);
382             }
383         }
384         else
385         {
386 
387             for (curx = xstart, cury = ystart, nextx = xend, nexty = yend,
388                 decision = (dy >> 1); cury <= nexty; cury++, nexty--,
389                 decision += dx)
390             {
391                 if (decision >= dy)
392                 {
393                     decision -= dy;
394                     curx += x_sign;
395                     nextx -= x_sign;
396                 }
397                 blend_func(context, curx, cury, linecolor, alpha);
398                 blend_func(context, nextx, nexty, linecolor, alpha);
399             }
400         }
401     }
402 }
403 
404 #endif /* GX_BRUSH_ALPHA_SUPPORT */
405