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_4bpp_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 4bpp 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_4bpp_simple_line_draw(GX_DRAW_CONTEXT * context,INT xstart,INT ystart,INT xend,INT yend)74 VOID _gx_display_driver_4bpp_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 GX_UBYTE     *start_address;
104 GX_UBYTE     *put;
105 GX_UBYTE     *next_put;
106 GX_BOOL       clipped = GX_TRUE;
107 INT           dx = GX_ABS(xend - xstart);
108 INT           dy = GX_ABS(yend - ystart);
109 
110 GX_RECTANGLE *clip = context -> gx_draw_context_clip;
111 GX_UBYTE      linecolor = context -> gx_draw_context_brush.gx_brush_line_color & 0x0f;
112 GX_UBYTE      color = linecolor;
113 INT           stride;
114 
115     if (((dx >= dy && (xstart > xend)) || ((dy > dx) && ystart > yend)))
116     {
117         GX_SWAP_VALS(xend, xstart);
118         GX_SWAP_VALS(yend, ystart);
119     }
120     x_sign = (xend - xstart) / dx;
121     y_sign = (yend - ystart) / dy;
122 
123     if (y_sign > 0)
124     {
125         y_increment = context -> gx_draw_context_pitch;
126     }
127     else
128     {
129         y_increment = 0 - context -> gx_draw_context_pitch;
130     }
131 
132     stride = (context -> gx_draw_context_pitch + 1) >> 1;
133 
134     start_address = (GX_UBYTE *)(context -> gx_draw_context_memory);
135     pos_start = ystart * (stride << 1) + xstart;
136     pos_end = yend * (stride << 1) + xend;
137 
138     end_point.gx_point_x = (GX_VALUE)xstart;
139     end_point.gx_point_y = (GX_VALUE)ystart;
140 
141     if (_gx_utility_rectangle_point_detect(clip, end_point))
142     {
143         end_point.gx_point_x = (GX_VALUE)xend;
144         end_point.gx_point_y = (GX_VALUE)yend;
145 
146         if (_gx_utility_rectangle_point_detect(clip, end_point))
147         {
148             clipped = GX_FALSE;
149         }
150     }
151 
152     if (clipped)
153     {
154         /* here if we must do clipping in the inner loop, because one
155            or both of the end points are outside clipping rectangle */
156 
157         /* Calculate the middle point of the line.  */
158         mid_point.gx_point_x = (GX_VALUE)((xend + xstart) >> 1);
159         mid_point.gx_point_y = (GX_VALUE)((yend + ystart) >> 1);
160 
161         /* Judge the clip in which side.  */
162         if (_gx_utility_rectangle_point_detect(clip, mid_point))
163         {
164 
165             /* the clip in two sides.  */
166             if (dx >= dy)
167             {
168                 /* walk out the clipping point.  */
169                 for (curx = xstart, cury = ystart, decision = (dx >> 1); curx < mid_point.gx_point_x;
170                      curx++, decision += dy)
171                 {
172                     if (decision >= dx)
173                     {
174                         decision -= dx;
175                         cury += y_sign;
176 
177                         pos_start += y_increment;
178                     }
179 
180                     if (curx >= clip -> gx_rectangle_left &&
181                         cury >= clip -> gx_rectangle_top &&
182                         cury <= clip -> gx_rectangle_bottom)
183                     {
184                         break;
185                     }
186                     pos_start++;
187                 }
188                 for (; curx <= mid_point.gx_point_x;
189                      curx++, decision += dy)
190                 {
191                     if (decision >= dx)
192                     {
193                         decision -= dx;
194                         cury += y_sign;
195                         pos_start += y_increment;
196                     }
197                     put = start_address;
198                     put += pos_start >> 1;
199                     if (pos_start & 0x01)
200                     {
201                         start_mask = 0x0f;
202                         color = linecolor;
203                     }
204                     else
205                     {
206                         start_mask = 0xf0;
207                         color = (GX_UBYTE)(linecolor << 4);
208                     }
209                     *put = (GX_UBYTE)((GX_UBYTE)((*put) & (~start_mask)) | color);
210                     pos_start++;
211                 }
212                 for (nextx = xend, nexty = yend, decision = (dx >> 1); nextx > mid_point.gx_point_x;
213                      nextx--, decision += dy)
214                 {
215                     if (decision >= dx)
216                     {
217                         decision -= dx;
218                         nexty -= y_sign;
219                         pos_end -= y_increment;
220                     }
221                     if (nextx <= clip -> gx_rectangle_right &&
222                         nexty >= clip -> gx_rectangle_top &&
223                         nexty <= clip -> gx_rectangle_bottom)
224                     {
225                         break;
226                     }
227                     pos_end--;
228                 }
229 
230                 for (; nextx > mid_point.gx_point_x;
231                      nextx--, decision += dy)
232                 {
233                     if (decision >= dx)
234                     {
235                         decision -= dx;
236                         nexty -= y_sign;
237                         pos_end -= y_increment;
238                     }
239                     next_put = start_address;
240                     next_put += pos_end >> 1;
241                     if (pos_end & 0x01)
242                     {
243                         end_mask = 0x0f;
244                         color = linecolor;
245                     }
246                     else
247                     {
248                         end_mask = 0xf0;
249                         color = (GX_UBYTE)(linecolor << 4);
250                     }
251                     *next_put = (GX_UBYTE)((GX_UBYTE)((*next_put) & (~end_mask)) | color);
252                     pos_end--;
253                 }
254             }
255             else
256             {
257                 for (nextx = xend, nexty = yend, decision = (dy >> 1); nexty > mid_point.gx_point_y;
258                      nexty--, decision += dx)
259                 {
260                     if (decision >= dy)
261                     {
262                         decision -= dy;
263                         nextx -= x_sign;
264                         pos_end -= x_sign;
265                     }
266                     if (nextx >= clip -> gx_rectangle_left &&
267                         nextx <= clip -> gx_rectangle_right &&
268                         nexty <= clip -> gx_rectangle_bottom)
269                     {
270                         break;
271                     }
272                     pos_end -= context -> gx_draw_context_pitch;
273                 }
274 
275                 for (; nexty > mid_point.gx_point_y;
276                      nexty--, decision += dx)
277                 {
278                     if (decision >= dy)
279                     {
280                         decision -= dy;
281                         nextx -= x_sign;
282                         pos_end -= x_sign;
283                     }
284                     next_put = start_address;
285                     next_put += pos_end >> 1;
286                     if (pos_end & 0x01)
287                     {
288                         end_mask = 0x0f;
289                         color = linecolor;
290                     }
291                     else
292                     {
293                         end_mask = 0xf0;
294                         color = (GX_UBYTE)(linecolor << 4);
295                     }
296                     *next_put = (GX_UBYTE)((GX_UBYTE)((*next_put) & (~end_mask)) | color);
297                     pos_end -= context -> gx_draw_context_pitch;
298                 }
299 
300                 /* walk out the clipping point.  */
301                 for (curx = xstart, cury = ystart, decision = (dy >> 1); cury < mid_point.gx_point_y;
302                      cury++, decision += dx)
303                 {
304                     if (decision >= dy)
305                     {
306                         decision -= dy;
307                         curx += x_sign;
308                         pos_start += x_sign;
309                     }
310 
311                     if (curx >= clip -> gx_rectangle_left &&
312                         curx <= clip -> gx_rectangle_right &&
313                         cury >= clip -> gx_rectangle_top)
314                     {
315                         break;
316                     }
317                     pos_start += context -> gx_draw_context_pitch;
318                 }
319                 for (; cury <= mid_point.gx_point_y;
320                      cury++, decision += dx)
321                 {
322                     if (decision >= dy)
323                     {
324                         decision -= dy;
325                         curx += x_sign;
326                         pos_start += x_sign;
327                     }
328                     put = start_address;
329                     put += pos_start >> 1;
330                     if (pos_start & 0x01)
331                     {
332                         start_mask = 0x0f;
333                         color = linecolor;
334                     }
335                     else
336                     {
337                         start_mask = 0xf0;
338                         color = (GX_UBYTE)(linecolor << 4);
339                     }
340                     *put = (GX_UBYTE)((GX_UBYTE)((*put) & (~start_mask)) | color);
341                     pos_start += context -> gx_draw_context_pitch;
342                 }
343             }
344         }
345         else
346         {
347             /* The clip stay at one side.  */
348             if (dx >= dy)
349             {
350                 half_rectangle.gx_rectangle_left = (GX_VALUE)xstart;
351                 half_rectangle.gx_rectangle_right = mid_point.gx_point_x;
352                 if (y_sign == 1)
353                 {
354                     half_rectangle.gx_rectangle_top = (GX_VALUE)ystart;
355                     half_rectangle.gx_rectangle_bottom = mid_point.gx_point_y;
356                 }
357                 else
358                 {
359                     half_rectangle.gx_rectangle_top = mid_point.gx_point_y;
360                     half_rectangle.gx_rectangle_bottom = (GX_VALUE)ystart;
361                 }
362 
363                 if (_gx_utility_rectangle_overlap_detect(clip, &half_rectangle, &half_over))
364                 {
365                     curx = xstart;
366                     cury = ystart;
367                     steps = mid_point.gx_point_x - curx + 1;
368                     sign = 1;
369                 }
370                 else
371                 {
372                     curx = xend;
373                     cury = yend;
374                     steps = xend - mid_point.gx_point_x;
375                     sign = -1;
376                     y_increment = 0 - y_increment;
377                     y_sign = 0 - y_sign;
378                     pos_start = pos_end;
379                 }
380                 for (decision = (dx >> 1); steps > 0; curx += sign, decision += dy, steps--)
381                 {
382                     if (decision >= dx)
383                     {
384                         decision -= dx;
385                         cury += y_sign;
386                         pos_start += y_increment;
387                     }
388 
389                     if (curx >= clip -> gx_rectangle_left &&
390                         curx <= clip -> gx_rectangle_right &&
391                         cury >= clip -> gx_rectangle_top &&
392                         cury <= clip -> gx_rectangle_bottom)
393                     {
394                         put = start_address;
395                         put += pos_start >> 1;
396                         if (pos_start & 0x01)
397                         {
398                             start_mask = 0x0f;
399                             color = linecolor;
400                         }
401                         else
402                         {
403                             start_mask = 0xf0;
404                             color = (GX_UBYTE)(linecolor << 4);
405                         }
406                         *put = (GX_UBYTE)((GX_UBYTE)((*put) & (~start_mask)) | color);
407                     }
408                     pos_start += sign;
409                 }
410             }
411             else
412             {
413                 half_rectangle.gx_rectangle_top = (GX_VALUE)ystart;
414                 half_rectangle.gx_rectangle_bottom = mid_point.gx_point_y;
415                 if (x_sign == 1)
416                 {
417                     half_rectangle.gx_rectangle_right = mid_point.gx_point_x;
418                     half_rectangle.gx_rectangle_left = (GX_VALUE)xstart;
419                 }
420                 else
421                 {
422                     half_rectangle.gx_rectangle_right = (GX_VALUE)xstart;
423                     half_rectangle.gx_rectangle_left = mid_point.gx_point_x;
424                 }
425 
426                 if (_gx_utility_rectangle_overlap_detect(clip, &half_rectangle, &half_over))
427                 {
428                     curx = xstart;
429                     cury = ystart;
430                     steps = mid_point.gx_point_y - cury + 1;
431                     y_increment = context -> gx_draw_context_pitch;
432                     sign = 1;
433                 }
434                 else
435                 {
436                     curx = xend;
437                     cury = yend;
438                     steps = yend - mid_point.gx_point_y;
439                     sign = -1;
440                     y_increment = 0 - context -> gx_draw_context_pitch;
441                     x_sign = 0 - x_sign;
442                     pos_start = pos_end;
443                 }
444 
445                 for (decision = (dy >> 1); steps > 0; cury += sign, decision += dx, steps--)
446                 {
447                     if (decision >= dy)
448                     {
449                         decision -= dy;
450                         curx += x_sign;
451                         pos_start += x_sign;
452                     }
453                     if (curx >= clip -> gx_rectangle_left &&
454                         curx <= clip -> gx_rectangle_right &&
455                         cury >= clip -> gx_rectangle_top &&
456                         cury <= clip -> gx_rectangle_bottom)
457                     {
458                         put = start_address;
459                         put += pos_start >> 1;
460                         if (pos_start & 0x01)
461                         {
462                             start_mask = 0x0f;
463                             color = linecolor;
464                         }
465                         else
466                         {
467                             start_mask = 0xf0;
468                             color = (GX_UBYTE)(linecolor << 4);
469                         }
470                         *put = (GX_UBYTE)((GX_UBYTE)((*put) & (~start_mask)) | color);
471                     }
472                     pos_start += y_increment;
473                 }
474             }
475         }
476     }
477     else
478     {
479         /* here if both line ends lie within clipping rectangle, we can
480            run a faster inner loop */
481         if (dx >= dy)
482         {
483             for (curx = xstart, cury = ystart, nextx = xend, nexty = yend,
484                  decision = (dx >> 1); curx <= nextx; curx++, nextx--,
485                  decision += dy)
486             {
487 
488                 if (decision >= dx)
489                 {
490                     decision -= dx;
491                     cury += y_sign;
492                     nexty -= y_sign;
493 
494                     pos_start += y_increment;
495                     pos_end -= y_increment;
496                 }
497                 put = start_address;
498                 put += pos_start >> 1;
499                 if (pos_start & 0x01)
500                 {
501                     start_mask = 0x0f;
502                     color = linecolor;
503                 }
504                 else
505                 {
506                     start_mask = 0xf0;
507                     color = (GX_UBYTE)(linecolor << 4);
508                 }
509                 *put = (GX_UBYTE)((GX_UBYTE)((*put) & (~start_mask)) | color);
510                 next_put = start_address;
511                 next_put += pos_end >> 1;
512                 if (pos_end & 0x01)
513                 {
514                     end_mask = 0x0f;
515                     color = linecolor;
516                 }
517                 else
518                 {
519                     end_mask = 0xf0;
520                     color = (GX_UBYTE)(linecolor << 4);
521                 }
522                 *next_put = (GX_UBYTE)((GX_UBYTE)((*next_put) & (~end_mask)) | color);
523                 pos_start++;
524                 pos_end--;
525             }
526         }
527         else
528         {
529             for (curx = xstart, cury = ystart, nextx = xend, nexty = yend,
530                  decision = (dy >> 1); cury <= nexty; cury++, nexty--,
531                  decision += dx)
532             {
533                 if (decision >= dy)
534                 {
535                     decision -= dy;
536                     curx += x_sign;
537                     nextx -= x_sign;
538 
539                     pos_start += x_sign;
540                     pos_end -= x_sign;
541                 }
542                 put = start_address;
543                 put += pos_start >> 1;
544                 if (pos_start & 0x01)
545                 {
546                     start_mask = 0x0f;
547                     color = linecolor;
548                 }
549                 else
550                 {
551                     start_mask = 0xf0;
552                     color = (GX_UBYTE)(linecolor << 4);
553                 }
554                 *put = (GX_UBYTE)((GX_UBYTE)((*put) & (~start_mask)) | color);
555                 next_put = start_address;
556                 next_put += pos_end >> 1;
557                 if (pos_end & 0x01)
558                 {
559                     end_mask = 0x0f;
560                     color = linecolor;
561                 }
562                 else
563                 {
564                     end_mask = 0xf0;
565                     color = (GX_UBYTE)(linecolor << 4);
566                 }
567                 *next_put = (GX_UBYTE)((GX_UBYTE)((*next_put) & (~end_mask)) | color);
568 
569                 pos_start += context -> gx_draw_context_pitch;
570                 pos_end -= context -> gx_draw_context_pitch;
571             }
572         }
573     }
574 }
575 
576