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