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 #define GX_SOURCE_CODE
22 
23 
24 /* Include necessary system files.  */
25 
26 #include "gx_api.h"
27 #include "gx_utility.h"
28 #include "gx_display.h"
29 
30 /**************************************************************************/
31 /*                                                                        */
32 /*  FUNCTION                                               RELEASE        */
33 /*                                                                        */
34 /*    _gx_display_driver_1bpp_simple_line_draw            PORTABLE C      */
35 /*                                                           6.1          */
36 /*  AUTHOR                                                                */
37 /*                                                                        */
38 /*    Kenneth Maxwell, Microsoft Corporation                              */
39 /*                                                                        */
40 /*  DESCRIPTION                                                           */
41 /*                                                                        */
42 /*    Simple line draw function for the 1bpp display driver.              */
43 /*                                                                        */
44 /*  INPUT                                                                 */
45 /*                                                                        */
46 /*    context                               Drawing context               */
47 /*    xstart                                x-coord of endpoint           */
48 /*    ystart                                y-coord of endpoint           */
49 /*    xend                                  x-coord of endpoint           */
50 /*    yend                                  y-coord of endpoint           */
51 /*                                                                        */
52 /*  OUTPUT                                                                */
53 /*                                                                        */
54 /*    None                                                                */
55 /*                                                                        */
56 /*  CALLS                                                                 */
57 /*                                                                        */
58 /*    GX_ABS                                Compute the absolute value    */
59 /*    GX_SWAP_VALUE                         Swap two values               */
60 /*                                                                        */
61 /*  CALLED BY                                                             */
62 /*                                                                        */
63 /*    GUIX Internal Code                                                  */
64 /*                                                                        */
65 /*  RELEASE HISTORY                                                       */
66 /*                                                                        */
67 /*    DATE              NAME                      DESCRIPTION             */
68 /*                                                                        */
69 /*  05-19-2020     Kenneth Maxwell          Initial Version 6.0           */
70 /*  09-30-2020     Kenneth Maxwell          Modified comment(s),          */
71 /*                                            resulting in version 6.1    */
72 /*                                                                        */
73 /**************************************************************************/
_gx_display_driver_1bpp_simple_line_draw(GX_DRAW_CONTEXT * context,INT xstart,INT ystart,INT xend,INT yend)74 VOID _gx_display_driver_1bpp_simple_line_draw(GX_DRAW_CONTEXT *context, INT xstart, INT ystart, INT xend, INT yend)
75 {
76 
77 /**************************************************************************/
78 /* Integer non-aliased line draw. This algorithm is taken directly from   */
79 /* Roger Stevens "The C++ graphics programming handbook" pages 281-282.   */
80 /* Removed the pattern test and renamed the variables and converted from  */
81 /* C++ to C. Also modified the algorithm to draw from both ends in to     */
82 /* the middle, instead of drawing from one end to the other.              */
83 /**************************************************************************/
84 
85 INT           curx;
86 INT           cury;
87 INT           x_sign;
88 INT           y_sign;
89 INT           decision;
90 INT           nextx;
91 INT           nexty;
92 INT           y_increment;
93 GX_POINT      end_point;
94 GX_POINT      mid_point;
95 GX_RECTANGLE  half_rectangle;
96 GX_RECTANGLE  half_over;
97 INT           sign;
98 INT           steps;
99 INT           pos_start;
100 INT           pos_end;
101 GX_UBYTE      start_mask;
102 GX_UBYTE      end_mask;
103 
104 GX_UBYTE     *start_address;
105 GX_UBYTE     *put;
106 GX_UBYTE     *next_put;
107 
108 GX_BOOL       clipped = GX_TRUE;
109 INT           dx = GX_ABS(xend - xstart);
110 INT           dy = GX_ABS(yend - ystart);
111 
112 GX_RECTANGLE *clip = context -> gx_draw_context_clip;
113 GX_UBYTE      linecolor = context -> gx_draw_context_brush.gx_brush_line_color & 0x01;
114 INT           stride;
115 
116     if (((dx >= dy && (xstart > xend)) || ((dy > dx) && ystart > yend)))
117     {
118         GX_SWAP_VALS(xend, xstart);
119         GX_SWAP_VALS(yend, ystart);
120     }
121     x_sign = (xend - xstart) / dx;
122     y_sign = (yend - ystart) / dy;
123 
124     if (y_sign > 0)
125     {
126         y_increment = context -> gx_draw_context_pitch;
127     }
128     else
129     {
130         y_increment = 0 - context -> gx_draw_context_pitch;
131     }
132 
133     stride = (context -> gx_draw_context_pitch + 7) >> 3;
134 
135     start_address = (GX_UBYTE *)(context -> gx_draw_context_memory);
136     pos_start = ystart * (stride << 3) + xstart;
137     pos_end = yend * (stride << 3) + xend;
138 
139     end_point.gx_point_x = (GX_VALUE)xstart;
140     end_point.gx_point_y = (GX_VALUE)ystart;
141 
142     if (_gx_utility_rectangle_point_detect(clip, end_point))
143     {
144         end_point.gx_point_x = (GX_VALUE)xend;
145         end_point.gx_point_y = (GX_VALUE)yend;
146 
147         if (_gx_utility_rectangle_point_detect(clip, end_point))
148         {
149             clipped = GX_FALSE;
150         }
151     }
152 
153     if (clipped)
154     {
155         /* here if we must do clipping in the inner loop, because one
156            or both of the end points are outside clipping rectangle */
157 
158         /* Calculate the middle point of the line.  */
159         mid_point.gx_point_x = (GX_VALUE)((xend + xstart) >> 1);
160         mid_point.gx_point_y = (GX_VALUE)((yend + ystart) >> 1);
161 
162         /* Judge the clip in which side.  */
163         if (_gx_utility_rectangle_point_detect(clip, mid_point))
164         {
165 
166             /* the clip in two sides.  */
167             if (dx >= dy)
168             {
169                 /* walk out the clipping point.  */
170                 for (curx = xstart, cury = ystart, decision = (dx >> 1); curx < mid_point.gx_point_x;
171                      curx++, decision += dy)
172                 {
173                     if (decision >= dx)
174                     {
175                         decision -= dx;
176                         cury += y_sign;
177 
178                         pos_start += y_increment;
179                     }
180 
181                     if (curx >= clip -> gx_rectangle_left &&
182                         cury >= clip -> gx_rectangle_top &&
183                         cury <= clip -> gx_rectangle_bottom)
184                     {
185                         break;
186                     }
187                     pos_start++;
188                 }
189                 for (; curx <= mid_point.gx_point_x;
190                      curx++, decision += dy)
191                 {
192                     if (decision >= dx)
193                     {
194                         decision -= dx;
195                         cury += y_sign;
196                         pos_start += y_increment;
197                     }
198                     put = start_address;
199                     put += pos_start >> 3;
200                     start_mask = (GX_UBYTE)(0x80 >> (pos_start & 0x07));
201                     if (linecolor == 0x00)
202                     {
203                         *put = (GX_UBYTE)((*put) & (~start_mask));
204                     }
205                     else
206                     {
207                         *put |= start_mask;
208                     }
209                     pos_start++;
210                 }
211                 for (nextx = xend, nexty = yend, decision = (dx >> 1); nextx > mid_point.gx_point_x;
212                      nextx--, decision += dy)
213                 {
214                     if (decision >= dx)
215                     {
216                         decision -= dx;
217                         nexty -= y_sign;
218                         pos_end -= y_increment;
219                     }
220                     if (nextx <= clip -> gx_rectangle_right &&
221                         nexty >= clip -> gx_rectangle_top &&
222                         nexty <= clip -> gx_rectangle_bottom)
223                     {
224                         break;
225                     }
226                     pos_end--;
227                 }
228 
229                 for (; nextx > mid_point.gx_point_x;
230                      nextx--, decision += dy)
231                 {
232                     if (decision >= dx)
233                     {
234                         decision -= dx;
235                         nexty -= y_sign;
236                         pos_end -= y_increment;
237                     }
238                     next_put = start_address;
239                     next_put += pos_end >> 3;
240                     end_mask = (GX_UBYTE)(0x80 >> (pos_end & 0x07));
241                     if (linecolor == 0x00)
242                     {
243                         *next_put = (GX_UBYTE)((*next_put) & (~end_mask));
244                     }
245                     else
246                     {
247                         *next_put |= end_mask;
248                     }
249                     pos_end--;
250                 }
251             }
252             else
253             {
254                 for (nextx = xend, nexty = yend, decision = (dy >> 1); nexty > mid_point.gx_point_y;
255                      nexty--, decision += dx)
256                 {
257                     if (decision >= dy)
258                     {
259                         decision -= dy;
260                         nextx -= x_sign;
261                         pos_end -= x_sign;
262                     }
263                     if (nextx >= clip -> gx_rectangle_left &&
264                         nextx <= clip -> gx_rectangle_right &&
265                         nexty <= clip -> gx_rectangle_bottom)
266                     {
267                         break;
268                     }
269                     pos_end -= context -> gx_draw_context_pitch;
270                 }
271 
272                 for (; nexty > mid_point.gx_point_y;
273                      nexty--, decision += dx)
274                 {
275                     if (decision >= dy)
276                     {
277                         decision -= dy;
278                         nextx -= x_sign;
279                         pos_end -= x_sign;
280                     }
281                     next_put = start_address;
282                     next_put += pos_end >> 3;
283                     end_mask = (GX_UBYTE)(0x80 >> (pos_end & 0x07));
284                     if (linecolor == 0x00)
285                     {
286                         *next_put = (GX_UBYTE)((*next_put) & (~end_mask));
287                     }
288                     else
289                     {
290                         *next_put |= end_mask;
291                     }
292                     pos_end -= context -> gx_draw_context_pitch;
293                 }
294 
295                 /* walk out the clipping point.  */
296                 for (curx = xstart, cury = ystart, decision = (dy >> 1); cury < mid_point.gx_point_y;
297                      cury++, decision += dx)
298                 {
299                     if (decision >= dy)
300                     {
301                         decision -= dy;
302                         curx += x_sign;
303                         pos_start += x_sign;
304                     }
305 
306                     if (curx >= clip -> gx_rectangle_left &&
307                         curx <= clip -> gx_rectangle_right &&
308                         cury >= clip -> gx_rectangle_top)
309                     {
310                         break;
311                     }
312                     pos_start += context -> gx_draw_context_pitch;
313                 }
314                 for (; cury <= mid_point.gx_point_y;
315                      cury++, decision += dx)
316                 {
317                     if (decision >= dy)
318                     {
319                         decision -= dy;
320                         curx += x_sign;
321                         pos_start += x_sign;
322                     }
323                     put = start_address;
324                     put += pos_start >> 3;
325                     start_mask = (GX_UBYTE)(0x80 >> (pos_start & 0x07));
326                     if (linecolor == 0x00)
327                     {
328                         *put = (GX_UBYTE)((*put) & (~start_mask));
329                     }
330                     else
331                     {
332                         *put |= start_mask;
333                     }
334                     pos_start += context -> gx_draw_context_pitch;
335                 }
336             }
337         }
338         else
339         {
340             /* The clip stay at one side.  */
341             if (dx >= dy)
342             {
343                 half_rectangle.gx_rectangle_left = (GX_VALUE)xstart;
344                 half_rectangle.gx_rectangle_right = mid_point.gx_point_x;
345                 if (y_sign == 1)
346                 {
347                     half_rectangle.gx_rectangle_top = (GX_VALUE)ystart;
348                     half_rectangle.gx_rectangle_bottom = mid_point.gx_point_y;
349                 }
350                 else
351                 {
352                     half_rectangle.gx_rectangle_top = mid_point.gx_point_y;
353                     half_rectangle.gx_rectangle_bottom = (GX_VALUE)ystart;
354                 }
355 
356                 if (_gx_utility_rectangle_overlap_detect(clip, &half_rectangle, &half_over))
357                 {
358                     curx = xstart;
359                     cury = ystart;
360                     steps = mid_point.gx_point_x - curx + 1;
361                     sign = 1;
362                 }
363                 else
364                 {
365                     curx = xend;
366                     cury = yend;
367                     steps = xend - mid_point.gx_point_x;
368                     sign = -1;
369                     y_increment = 0 - y_increment;
370                     y_sign = 0 - y_sign;
371                     pos_start = pos_end;
372                 }
373                 for (decision = (dx >> 1); steps > 0; curx += sign, decision += dy, steps--)
374                 {
375                     if (decision >= dx)
376                     {
377                         decision -= dx;
378                         cury += y_sign;
379                         pos_start += y_increment;
380                     }
381 
382                     if (curx >= clip -> gx_rectangle_left &&
383                         curx <= clip -> gx_rectangle_right &&
384                         cury >= clip -> gx_rectangle_top &&
385                         cury <= clip -> gx_rectangle_bottom)
386                     {
387                         put = start_address;
388                         put += pos_start >> 3;
389                         start_mask = (GX_UBYTE)(0x80 >> (pos_start & 0x07));
390                         if (linecolor == 0x00)
391                         {
392                             *put = (GX_UBYTE)((*put) & (~start_mask));
393                         }
394                         else
395                         {
396                             *put |= start_mask;
397                         }
398                     }
399                     pos_start += sign;
400                 }
401             }
402             else
403             {
404                 half_rectangle.gx_rectangle_top = (GX_VALUE)ystart;
405                 half_rectangle.gx_rectangle_bottom = mid_point.gx_point_y;
406                 if (x_sign == 1)
407                 {
408                     half_rectangle.gx_rectangle_right = mid_point.gx_point_x;
409                     half_rectangle.gx_rectangle_left = (GX_VALUE)xstart;
410                 }
411                 else
412                 {
413                     half_rectangle.gx_rectangle_right = (GX_VALUE)xstart;
414                     half_rectangle.gx_rectangle_left = mid_point.gx_point_x;
415                 }
416 
417                 if (_gx_utility_rectangle_overlap_detect(clip, &half_rectangle, &half_over))
418                 {
419                     curx = xstart;
420                     cury = ystart;
421                     steps = mid_point.gx_point_y - cury + 1;
422                     y_increment = context -> gx_draw_context_pitch;
423                     sign = 1;
424                 }
425                 else
426                 {
427                     curx = xend;
428                     cury = yend;
429                     steps = yend - mid_point.gx_point_y;
430                     sign = -1;
431                     y_increment = 0 - context -> gx_draw_context_pitch;
432                     x_sign = 0 - x_sign;
433                     pos_start = pos_end;
434                 }
435 
436                 for (decision = (dy >> 1); steps > 0; cury += sign, decision += dx, steps--)
437                 {
438                     if (decision >= dy)
439                     {
440                         decision -= dy;
441                         curx += x_sign;
442                         pos_start += x_sign;
443                     }
444                     if (curx >= clip -> gx_rectangle_left &&
445                         curx <= clip -> gx_rectangle_right &&
446                         cury >= clip -> gx_rectangle_top &&
447                         cury <= clip -> gx_rectangle_bottom)
448                     {
449                         put = start_address;
450                         put += pos_start >> 3;
451                         start_mask = (GX_UBYTE)(0x80 >> (pos_start & 0x07));
452                         if (linecolor == 0x00)
453                         {
454                             *put = (GX_UBYTE)((*put) & (~start_mask));
455                         }
456                         else
457                         {
458                             *put |= start_mask;
459                         }
460                     }
461                     pos_start += y_increment;
462                 }
463             }
464         }
465     }
466     else
467     {
468         /* here if both line ends lie within clipping rectangle, we can
469            run a faster inner loop */
470         if (dx >= dy)
471         {
472             for (curx = xstart, cury = ystart, nextx = xend, nexty = yend,
473                  decision = (dx >> 1); curx <= nextx; curx++, nextx--,
474                  decision += dy)
475             {
476 
477                 if (decision >= dx)
478                 {
479                     decision -= dx;
480                     cury += y_sign;
481                     nexty -= y_sign;
482 
483                     pos_start += y_increment;
484                     pos_end -= y_increment;
485                 }
486                 put = start_address;
487                 put += pos_start >> 3;
488                 start_mask = (GX_UBYTE)(0x80 >> (pos_start & 0x07));
489                 next_put = start_address;
490                 next_put += pos_end >> 3;
491                 end_mask = (GX_UBYTE)(0x80 >> (pos_end & 0x07));
492                 if (linecolor == 0x00)
493                 {
494                     *put = (GX_UBYTE)((*put) & (~start_mask));
495                     *next_put = (GX_UBYTE)((*next_put) & (~end_mask));
496                 }
497                 else
498                 {
499                     *put |= start_mask;
500                     *next_put |= end_mask;
501                 }
502 
503                 pos_start++;
504                 pos_end--;
505             }
506         }
507         else
508         {
509             for (curx = xstart, cury = ystart, nextx = xend, nexty = yend,
510                  decision = (dy >> 1); cury <= nexty; cury++, nexty--,
511                  decision += dx)
512             {
513                 if (decision >= dy)
514                 {
515                     decision -= dy;
516                     curx += x_sign;
517                     nextx -= x_sign;
518 
519                     pos_start += x_sign;
520                     pos_end -= x_sign;
521                 }
522                 put = start_address;
523                 put += pos_start >> 3;
524                 start_mask = (GX_UBYTE)(0x80 >> (pos_start & 0x07));
525                 next_put = start_address;
526                 next_put += pos_end >> 3;
527                 end_mask = (GX_UBYTE)(0x80 >> (pos_end & 0x07));
528                 if (linecolor == 0x00)
529                 {
530                     *put = (GX_UBYTE)((*put) & (~start_mask));
531                     *next_put = (GX_UBYTE)((*next_put) & (~end_mask));
532                 }
533                 else
534                 {
535                     *put |= start_mask;
536                     *next_put |= end_mask;
537                 }
538 
539                 pos_start += context -> gx_draw_context_pitch;
540                 pos_end -= context -> gx_draw_context_pitch;
541             }
542         }
543     }
544 }
545 
546