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 /**   Canvas Management (Canvas)                                          */
18 /**                                                                       */
19 /**************************************************************************/
20 
21 #define GX_SOURCE_CODE
22 
23 
24 /* Include necessary system files.  */
25 
26 #include "gx_api.h"
27 #include "gx_system.h"
28 #include "gx_utility.h"
29 #include "gx_canvas.h"
30 
31 
32 /**************************************************************************/
33 /*                                                                        */
34 /*  FUNCTION                                               RELEASE        */
35 /*                                                                        */
36 /*    _gx_canvas_line_draw                                PORTABLE C      */
37 /*                                                           6.3.0        */
38 /*  AUTHOR                                                                */
39 /*                                                                        */
40 /*    Kenneth Maxwell, Microsoft Corporation                              */
41 /*                                                                        */
42 /*  DESCRIPTION                                                           */
43 /*                                                                        */
44 /*    This draws the specified line on the current context.               */
45 /*                                                                        */
46 /*  INPUT                                                                 */
47 /*                                                                        */
48 /*    x_start                            x-coord of endpoint1             */
49 /*    y_start                            y-coord of endpoint1             */
50 /*    x_end                              x-coord of endpoint2             */
51 /*    y_end                              y-coord of endpoint2             */
52 /*                                                                        */
53 /*  OUTPUT                                                                */
54 /*                                                                        */
55 /*    status                             Completion status                */
56 /*                                                                        */
57 /*  CALLS                                                                 */
58 /*                                                                        */
59 /*    gx_utility_rectanlge_define        Define a rectangle               */
60 /*    gx_utility_rectangle_overlap_detect                                 */
61 /*                                       Detect rectangle overlap         */
62 /*    [gx_display_driver_line_draw]      The generic display driver line  */
63 /*                                         drawing routine                */
64 /*    [gx_display_driver_horizontal_line_draw]                            */
65 /*                                       The display driver horizontal    */
66 /*                                         line drawing function          */
67 /*    [gx_display_driver_vertical_line_draw]                              */
68 /*                                       The display driver vertical      */
69 /*                                         line drawing function          */
70 /*    [gx_display_driver_horizontal_pattern_line_draw]                    */
71 /*                                       The display driver horizontal    */
72 /*                                         pattern line drawing function  */
73 /*    [gx_display_driver_vertical_pattern_line_draw]                      */
74 /*                                       The display driver vertical      */
75 /*                                         pattern line drawing function  */
76 /*                                                                        */
77 /*  CALLED BY                                                             */
78 /*                                                                        */
79 /*    Application Code                                                    */
80 /*    GUIX Internal Code                                                  */
81 /*                                                                        */
82 /*  RELEASE HISTORY                                                       */
83 /*                                                                        */
84 /*    DATE              NAME                      DESCRIPTION             */
85 /*                                                                        */
86 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
87 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
88 /*                                            resulting in version 6.1    */
89 /*  10-31-2023     Ting Zhu                 Modified comment(s), fixed a  */
90 /*                                            pattern line draw issue,    */
91 /*                                            resulting in version 6.3.0  */
92 /*                                                                        */
93 /**************************************************************************/
_gx_canvas_line_draw(GX_VALUE x_start,GX_VALUE y_start,GX_VALUE x_end,GX_VALUE y_end)94 UINT  _gx_canvas_line_draw(GX_VALUE x_start, GX_VALUE y_start, GX_VALUE x_end, GX_VALUE y_end)
95 {
96 GX_DRAW_CONTEXT *context;
97 GX_DISPLAY      *display;
98 GX_RECTANGLE     clip_rect;
99 GX_RECTANGLE     bound;
100 GX_VIEW         *view;
101 GX_BRUSH        *brush;
102 GX_BOOL          simple_line = GX_FALSE;
103 GX_VALUE         width;
104 GX_BOOL          anti_aliased;
105 GX_VALUE         brush_width;
106 
107     /* calculate rectangle that bounds the line. This bounding rectangle
108        is later adjusted based on line width */
109 
110     /* are line coordinates left to right? */
111 
112     if (x_start <= x_end)
113     {
114         /* are line coordinates bottom to top? */
115         if (y_start >= y_end)
116         {
117             _gx_utility_rectangle_define(&bound, x_start, y_end, x_end, y_start);
118         }
119         else
120         {
121             /* line is defined left to right, top to bottom */
122             _gx_utility_rectangle_define(&bound, x_start, y_start, x_end, y_end);
123         }
124     }
125     else
126     {
127         if (y_start >= y_end)
128         {
129             /* line is defined right to left, bottom to top */
130             _gx_utility_rectangle_define(&bound, x_end, y_end, x_start, y_start);
131         }
132         else
133         {
134             /* line is defined right to left, top to bottom */
135             _gx_utility_rectangle_define(&bound, x_end, y_start, x_start, y_end);
136         }
137     }
138 
139     /* pick up the current drawing context */
140     context = _gx_system_current_draw_context;
141     brush = &context -> gx_draw_context_brush;
142     brush_width = brush -> gx_brush_width;
143 
144     if (brush_width == 0)
145     {
146         /* Check brush width. Just return if width is 0. */
147         return(GX_SUCCESS);
148     }
149 
150     if ((brush_width == 1) ||
151         ((brush -> gx_brush_style & GX_BRUSH_ROUND) == 0))
152     {
153         /* If we are drawing a horizontal or vertial line with
154         square line ends, we can do a much simpler and faster
155         line drawing function. Check for these special cases.
156         */
157 
158         /* the brush 1 pixel wide or not round, check for horizontal or vertical */
159         if ((y_start == y_end) || (x_start == x_end))
160         {
161             /* yes, simple line case */
162             simple_line = GX_TRUE;
163         }
164     }
165 
166     /* pick up current display driver */
167     display = context->gx_draw_context_display;
168     /* Angled line using current context brush and canvas: */
169     anti_aliased = GX_FALSE;
170 
171     if (brush -> gx_brush_style & GX_BRUSH_ALIAS)
172     {
173         /* The caller requested an anti-aliased line. Does the driver
174         support it? */
175         if (brush_width == 1)
176         {
177             if (display -> gx_display_driver_anti_aliased_line_draw)
178             {
179                 /* Yes, the driver supports anti-aliased lines, so use them: */
180                 anti_aliased = GX_TRUE;
181             }
182         }
183         else
184         {
185             if (display -> gx_display_driver_anti_aliased_wide_line_draw)
186             {
187                 /* Yes, the driver supports anti-aliased lines, so use them: */
188                 anti_aliased = GX_TRUE;
189             }
190         }
191     }
192 
193     /* We need to expand the bounding rectangle to account for
194        rounded ends and line width > 1
195      */
196     if (simple_line)
197     {
198         if(!brush -> gx_brush_line_pattern)
199         {
200             if (x_start == x_end)
201             {
202                 /* vertical line centered around x coords */
203                 bound.gx_rectangle_left  = (GX_VALUE)(x_start - (brush_width / 2));
204                 bound.gx_rectangle_right = (GX_VALUE)(bound.gx_rectangle_left + brush_width - 1);
205             }
206             else
207             {
208                 /* horizontal line centered around y coords */
209                 bound.gx_rectangle_top = (GX_VALUE)(y_start - (brush_width / 2));
210                 bound.gx_rectangle_bottom = (GX_VALUE)(bound.gx_rectangle_top + brush_width - 1);
211             }
212         }
213     }
214     else
215     {
216         width = (GX_VALUE)((brush_width + 1) / 2);
217 
218         if (anti_aliased)
219         {
220             width = (GX_VALUE)(width + 1);
221         }
222 
223         /* increase the bound by 1/2 the line width */
224         bound.gx_rectangle_top    = (GX_VALUE)(bound.gx_rectangle_top - width);
225         bound.gx_rectangle_left   = (GX_VALUE)(bound.gx_rectangle_left - width);
226         bound.gx_rectangle_right  = (GX_VALUE)(bound.gx_rectangle_right + width);
227         bound.gx_rectangle_bottom = (GX_VALUE)(bound.gx_rectangle_bottom + width);
228     }
229 
230     /* clip the line bounding box to the dirty rectangle */
231     if (!_gx_utility_rectangle_overlap_detect(&bound, &context -> gx_draw_context_dirty, &bound))
232     {
233         /* nothing to draw, return */
234         return GX_SUCCESS;
235     }
236 
237     /* test to determine if the bounding rectangle overlaps the region we are allowed to draw
238        into. For each view that overlaps the bounding rectangle, do some drawing.
239      */
240     view = context -> gx_draw_context_view_head;
241 
242     while (view)
243     {
244         if (!_gx_utility_rectangle_overlap_detect(&view -> gx_view_rectangle, &bound, &clip_rect))
245         {
246             view = view -> gx_view_next;
247             continue;
248         }
249 
250         /* we have a view into which we can draw the line, do it */
251         context -> gx_draw_context_clip = &clip_rect;
252 
253         if (simple_line)
254         {
255             if (y_start == y_end)
256             {
257                 if (brush -> gx_brush_line_pattern)
258                 {
259                     if (clip_rect.gx_rectangle_left > x_start)
260                     {
261                         width = (GX_VALUE)((clip_rect.gx_rectangle_left - x_start) & 0x1F);
262                         context -> gx_draw_context_brush.gx_brush_pattern_mask >>= width;
263                     }
264 
265                     /* Call display driver's simple horizontal pattern line drawing function.  */
266                     display -> gx_display_driver_horizontal_pattern_line_draw(context,
267                                                                               clip_rect.gx_rectangle_left,
268                                                                               clip_rect.gx_rectangle_right,
269                                                                               y_start);
270 
271                     if (clip_rect.gx_rectangle_right < x_end)
272                     {
273                         width = (GX_VALUE)((x_end - clip_rect.gx_rectangle_right) & 0x1F);
274                         if ((context -> gx_draw_context_brush.gx_brush_pattern_mask >> width) == 0)
275                         {
276                             context -> gx_draw_context_brush.gx_brush_pattern_mask <<= (32 - width);
277                         }
278                         else
279                         {
280                             context -> gx_draw_context_brush.gx_brush_pattern_mask >>= width;
281                         }
282                     }
283                 }
284                 else
285                 {
286                     /* Call display driver's simple horizontal line drawing function.  */
287                     display -> gx_display_driver_horizontal_line_draw(context,
288                                                                       clip_rect.gx_rectangle_left,
289                                                                       clip_rect.gx_rectangle_right,
290                                                                       clip_rect.gx_rectangle_top,
291                                                                       clip_rect.gx_rectangle_bottom - clip_rect.gx_rectangle_top + 1,
292                                                                       brush -> gx_brush_line_color);
293                 }
294             }
295             else
296             {
297                 if (brush -> gx_brush_line_pattern)
298                 {
299                     if (clip_rect.gx_rectangle_top > y_start)
300                     {
301                         width = (GX_VALUE)((clip_rect.gx_rectangle_top - y_start) & 0x1F);
302                         context -> gx_draw_context_brush.gx_brush_pattern_mask >>= width;
303                     }
304 
305                     /* Call display driver's simple vertical line drawing function.  */
306                     display -> gx_display_driver_vertical_pattern_line_draw(context,
307                                                                             clip_rect.gx_rectangle_top,
308                                                                             clip_rect.gx_rectangle_bottom,
309                                                                             x_start);
310 
311                     if (clip_rect.gx_rectangle_bottom < y_end)
312                     {
313                         width = (GX_VALUE)((y_end - clip_rect.gx_rectangle_bottom) & 0x1F);
314                         if ((context -> gx_draw_context_brush.gx_brush_pattern_mask >> width) == 0)
315                         {
316                             context -> gx_draw_context_brush.gx_brush_pattern_mask <<= (32 - width);
317                         }
318                         else
319                         {
320                             context -> gx_draw_context_brush.gx_brush_pattern_mask >>= width;
321                         }
322                     }
323                 }
324                 else
325                 {
326                     /* Call display driver's simple vertical line drawing function.  */
327                     display -> gx_display_driver_vertical_line_draw(context,
328                                                                     clip_rect.gx_rectangle_top,
329                                                                     clip_rect.gx_rectangle_bottom,
330                                                                     clip_rect.gx_rectangle_left,
331                                                                     clip_rect.gx_rectangle_right - clip_rect.gx_rectangle_left + 1,
332                                                                     brush -> gx_brush_line_color);
333                 }
334             }
335             view = view -> gx_view_next;
336             continue;
337         }
338 
339 
340         if (anti_aliased)
341         {
342             if (brush_width == 1)
343             {
344                 display -> gx_display_driver_anti_aliased_line_draw(context, x_start, y_start, x_end, y_end);
345             }
346             else
347             {
348                 display -> gx_display_driver_anti_aliased_wide_line_draw(context, x_start, y_start, x_end, y_end);
349             }
350         }
351         else
352         {
353             if (brush_width == 1)
354             {
355                 display -> gx_display_driver_simple_line_draw(context, x_start, y_start, x_end, y_end);
356             }
357             else
358             {
359                 display -> gx_display_driver_simple_wide_line_draw(context, x_start, y_start, x_end, y_end);
360             }
361         }
362         view = view -> gx_view_next;
363     }
364 
365     /* Return successful completion.  */
366     return(GX_SUCCESS);
367 }
368 
369