version 2.8
gdisp.c
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.org/license.html
6  */
7 
8 #include "../../gfx.h"
9 
10 #if GFX_USE_GDISP
11 
12 /* Include the low level driver information */
13 #include "gdisp_driver.h"
14 
15 // Number of milliseconds for the startup logo - 0 means disabled.
16 #if GDISP_NEED_STARTUP_LOGO
17  #define GDISP_STARTUP_LOGO_TIMEOUT 1000
18  #define GDISP_STARTUP_LOGO_COLOR White
19 #else
20  #define GDISP_STARTUP_LOGO_TIMEOUT 0
21 #endif
22 
23 /*===========================================================================*/
24 /* Driver local variables. */
25 /*===========================================================================*/
26 
27 #if GDISP_NEED_TIMERFLUSH
28  static GTimer FlushTimer;
29 #endif
30 
31 GDisplay *GDISP;
32 
33 #if GDISP_NEED_MULTITHREAD
34  #define MUTEX_INIT(g) gfxMutexInit(&(g)->mutex)
35  #define MUTEX_ENTER(g) gfxMutexEnter(&(g)->mutex)
36  #define MUTEX_EXIT(g) gfxMutexExit(&(g)->mutex)
37  #define MUTEX_DEINIT(g) gfxMutexDestroy(&(g)->mutex)
38 #else
39  #define MUTEX_INIT(g)
40  #define MUTEX_ENTER(g)
41  #define MUTEX_EXIT(g)
42  #define MUTEX_DEINIT(g)
43 #endif
44 
45 #define NEED_CLIPPING (GDISP_HARDWARE_CLIP != TRUE && (GDISP_NEED_VALIDATION || GDISP_NEED_CLIP))
46 
47 #if !NEED_CLIPPING
48  #define TEST_CLIP_AREA(g)
49 #elif GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
50  #define TEST_CLIP_AREA(g) \
51  if (!gvmt(g)->setclip) { \
52  if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \
53  if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \
54  if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \
55  if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \
56  } \
57  if ((g)->p.cx > 0 && (g)->p.cy > 0)
58 #else
59  #define TEST_CLIP_AREA(g) \
60  if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \
61  if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \
62  if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \
63  if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \
64  if ((g)->p.cx > 0 && (g)->p.cy > 0)
65 #endif
66 
67 /*==========================================================================*/
68 /* Internal functions. */
69 /*==========================================================================*/
70 
71 #if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE
72  static GFXINLINE void setglobalwindow(GDisplay *g) {
73  coord_t x, y;
74  x = g->p.x; y = g->p.y;
75  g->p.x = g->p.y = 0;
76  g->p.cx = g->g.Width; g->p.cy = g->g.Height;
77  gdisp_lld_write_start(g);
78  g->p.x = x; g->p.y = y;
79  g->flags |= GDISP_FLG_SCRSTREAM;
80  }
81 #endif
82 
83 #if GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT
84  #define autoflush_stopdone(g) if (gvmt(g)->flush) gdisp_lld_flush(g)
85 #elif GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH
86  #define autoflush_stopdone(g) gdisp_lld_flush(g)
87 #else
88  #define autoflush_stopdone(g)
89 #endif
90 
91 #if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE
92  #define autoflush(g) \
93  { \
94  if ((g->flags & GDISP_FLG_SCRSTREAM)) { \
95  gdisp_lld_write_stop(g); \
96  g->flags &= ~GDISP_FLG_SCRSTREAM; \
97  } \
98  autoflush_stopdone(g); \
99  }
100 #else
101  #define autoflush(g) autoflush_stopdone(g)
102 #endif
103 
104 // drawpixel(g)
105 // Parameters: x,y
106 // Alters: cx, cy (if using streaming)
107 // Does not clip
108 static GFXINLINE void drawpixel(GDisplay *g) {
109 
110  // Best is hardware accelerated pixel draw
111  #if GDISP_HARDWARE_DRAWPIXEL
112  #if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
113  if (gvmt(g)->pixel)
114  #endif
115  {
116  gdisp_lld_draw_pixel(g);
117  return;
118  }
119  #endif
120 
121  // Next best is cursor based streaming
122  #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE
123  #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT
124  if (gvmt(g)->writepos)
125  #endif
126  {
127  if (!(g->flags & GDISP_FLG_SCRSTREAM))
128  setglobalwindow(g);
129  gdisp_lld_write_pos(g);
130  gdisp_lld_write_color(g);
131  return;
132  }
133  #endif
134 
135  // Worst is general streaming
136  #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE
137  // The following test is unneeded because we are guaranteed to have streaming if we don't have drawpixel
138  //#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
139  // if (gvmt(g)->writestart)
140  //#endif
141  {
142  g->p.cx = g->p.cy = 1;
143  gdisp_lld_write_start(g);
144  gdisp_lld_write_color(g);
145  gdisp_lld_write_stop(g);
146  return;
147  }
148  #endif
149 }
150 
151 // drawpixel_clip(g)
152 // Parameters: x,y
153 // Alters: cx, cy (if using streaming)
154 #if NEED_CLIPPING
155  static GFXINLINE void drawpixel_clip(GDisplay *g) {
156  #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
157  if (!gvmt(g)->setclip)
158  #endif
159  {
160  if (g->p.x < g->clipx0 || g->p.x >= g->clipx1 || g->p.y < g->clipy0 || g->p.y >= g->clipy1)
161  return;
162  }
163  drawpixel(g);
164  }
165 #else
166  #define drawpixel_clip(g) drawpixel(g)
167 #endif
168 
169 // fillarea(g)
170 // Parameters: x,y cx,cy and color
171 // Alters: nothing
172 // Note: This is not clipped
173 // Resets the streaming area if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set.
174 static GFXINLINE void fillarea(GDisplay *g) {
175 
176  // Best is hardware accelerated area fill
177  #if GDISP_HARDWARE_FILLS
178  #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
179  if (gvmt(g)->fill)
180  #endif
181  {
182  #if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE
183  if ((g->flags & GDISP_FLG_SCRSTREAM)) {
184  gdisp_lld_write_stop(g);
185  g->flags &= ~GDISP_FLG_SCRSTREAM;
186  }
187  #endif
188  gdisp_lld_fill_area(g);
189  return;
190  }
191  #endif
192 
193  // Next best is hardware streaming
194  #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE
195  #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
196  if (gvmt(g)->writestart)
197  #endif
198  {
199  uint32_t area;
200 
201  #if GDISP_HARDWARE_STREAM_POS
202  if ((g->flags & GDISP_FLG_SCRSTREAM)) {
203  gdisp_lld_write_stop(g);
204  g->flags &= ~GDISP_FLG_SCRSTREAM;
205  }
206  #endif
207 
208  area = (uint32_t)g->p.cx * g->p.cy;
209  gdisp_lld_write_start(g);
210  #if GDISP_HARDWARE_STREAM_POS
211  #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT
212  if (gvmt(g)->writepos)
213  #endif
214  gdisp_lld_write_pos(g);
215  #endif
216  for(; area; area--)
217  gdisp_lld_write_color(g);
218  gdisp_lld_write_stop(g);
219  return;
220  }
221  #endif
222 
223  // Worst is pixel drawing
224  #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL
225  // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
226  //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
227  // if (gvmt(g)->pixel)
228  //#endif
229  {
230  coord_t x0, y0, x1, y1;
231 
232  x0 = g->p.x;
233  y0 = g->p.y;
234  x1 = g->p.x + g->p.cx;
235  y1 = g->p.y + g->p.cy;
236  for(; g->p.y < y1; g->p.y++, g->p.x = x0)
237  for(; g->p.x < x1; g->p.x++)
238  gdisp_lld_draw_pixel(g);
239  g->p.y = y0;
240  return;
241  }
242  #endif
243 }
244 
245 // Parameters: x,y and x1
246 // Alters: x,y x1,y1 cx,cy
247 // Assumes the window covers the screen and a write_stop() will occur later
248 // if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set.
249 static void hline_clip(GDisplay *g) {
250  // Swap the points if necessary so it always goes from x to x1
251  if (g->p.x1 < g->p.x) {
252  g->p.cx = g->p.x; g->p.x = g->p.x1; g->p.x1 = g->p.cx;
253  }
254 
255  // Clipping
256  #if NEED_CLIPPING
257  #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
258  if (!gvmt(g)->setclip)
259  #endif
260  {
261  if (g->p.y < g->clipy0 || g->p.y >= g->clipy1) return;
262  if (g->p.x < g->clipx0) g->p.x = g->clipx0;
263  if (g->p.x1 >= g->clipx1) g->p.x1 = g->clipx1 - 1;
264  if (g->p.x1 < g->p.x) return;
265  }
266  #endif
267 
268  // This is an optimization for the point case. It is only worthwhile however if we
269  // have hardware fills or if we support both hardware pixel drawing and hardware streaming
270  #if GDISP_HARDWARE_FILLS || (GDISP_HARDWARE_DRAWPIXEL && GDISP_HARDWARE_STREAM_WRITE)
271  // Is this a point
272  if (g->p.x == g->p.x1) {
273  drawpixel(g);
274  return;
275  }
276  #endif
277 
278  // Best is hardware accelerated area fill
279  #if GDISP_HARDWARE_FILLS
280  #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
281  if (gvmt(g)->fill)
282  #endif
283  {
284  g->p.cx = g->p.x1 - g->p.x + 1;
285  g->p.cy = 1;
286  gdisp_lld_fill_area(g);
287  return;
288  }
289  #endif
290 
291  // Next best is cursor based streaming
292  #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE
293  #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT
294  if (gvmt(g)->writepos)
295  #endif
296  {
297  if (!(g->flags & GDISP_FLG_SCRSTREAM))
298  setglobalwindow(g);
299  g->p.cx = g->p.x1 - g->p.x + 1;
300  gdisp_lld_write_pos(g);
301  do { gdisp_lld_write_color(g); } while(--g->p.cx);
302  return;
303  }
304  #endif
305 
306  // Next best is streaming
307  #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE
308  #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
309  if (gvmt(g)->writestart)
310  #endif
311  {
312  g->p.cx = g->p.x1 - g->p.x + 1;
313  g->p.cy = 1;
314  gdisp_lld_write_start(g);
315  do { gdisp_lld_write_color(g); } while(--g->p.cx);
316  gdisp_lld_write_stop(g);
317  return;
318  }
319  #endif
320 
321  // Worst is drawing pixels
322  #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL
323  // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
324  //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
325  // if (gvmt(g)->pixel)
326  //#endif
327  {
328  for(; g->p.x <= g->p.x1; g->p.x++)
329  gdisp_lld_draw_pixel(g);
330  return;
331  }
332  #endif
333 }
334 
335 // Parameters: x,y and y1
336 // Alters: x,y x1,y1 cx,cy
337 static void vline_clip(GDisplay *g) {
338  // Swap the points if necessary so it always goes from y to y1
339  if (g->p.y1 < g->p.y) {
340  g->p.cy = g->p.y; g->p.y = g->p.y1; g->p.y1 = g->p.cy;
341  }
342 
343  // Clipping
344  #if NEED_CLIPPING
345  #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
346  if (!gvmt(g)->setclip)
347  #endif
348  {
349  if (g->p.x < g->clipx0 || g->p.x >= g->clipx1) return;
350  if (g->p.y < g->clipy0) g->p.y = g->clipy0;
351  if (g->p.y1 >= g->clipy1) g->p.y1 = g->clipy1 - 1;
352  if (g->p.y1 < g->p.y) return;
353  }
354  #endif
355 
356  // This is an optimization for the point case. It is only worthwhile however if we
357  // have hardware fills or if we support both hardware pixel drawing and hardware streaming
358  #if GDISP_HARDWARE_FILLS || (GDISP_HARDWARE_DRAWPIXEL && GDISP_HARDWARE_STREAM_WRITE) || (GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE)
359  // Is this a point
360  if (g->p.y == g->p.y1) {
361  drawpixel(g);
362  return;
363  }
364  #endif
365 
366  // Best is hardware accelerated area fill
367  #if GDISP_HARDWARE_FILLS
368  #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
369  if (gvmt(g)->fill)
370  #endif
371  {
372  #if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE
373  if ((g->flags & GDISP_FLG_SCRSTREAM)) {
374  gdisp_lld_write_stop(g);
375  g->flags &= ~GDISP_FLG_SCRSTREAM;
376  }
377  #endif
378  g->p.cy = g->p.y1 - g->p.y + 1;
379  g->p.cx = 1;
380  gdisp_lld_fill_area(g);
381  return;
382  }
383  #endif
384 
385  // Next best is streaming
386  #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE
387  #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
388  if (gvmt(g)->writestart)
389  #endif
390  {
391  #if GDISP_HARDWARE_STREAM_POS
392  if ((g->flags & GDISP_FLG_SCRSTREAM)) {
393  gdisp_lld_write_stop(g);
394  g->flags &= ~GDISP_FLG_SCRSTREAM;
395  }
396  #endif
397  g->p.cy = g->p.y1 - g->p.y + 1;
398  g->p.cx = 1;
399  gdisp_lld_write_start(g);
400  #if GDISP_HARDWARE_STREAM_POS
401  #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT
402  if (gvmt(g)->writepos)
403  #endif
404  gdisp_lld_write_pos(g);
405  #endif
406  do { gdisp_lld_write_color(g); } while(--g->p.cy);
407  gdisp_lld_write_stop(g);
408  return;
409  }
410  #endif
411 
412  // Worst is drawing pixels
413  #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL
414  // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
415  //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
416  // if (gvmt(g)->pixel)
417  //#endif
418  {
419  for(; g->p.y <= g->p.y1; g->p.y++)
420  gdisp_lld_draw_pixel(g);
421  return;
422  }
423  #endif
424 }
425 
426 // Parameters: x,y and x1,y1
427 // Alters: x,y x1,y1 cx,cy
428 static void line_clip(GDisplay *g) {
429  int16_t dy, dx;
430  int16_t addx, addy;
431  int16_t P, diff, i;
432 
433  // Is this a horizontal line (or a point)
434  if (g->p.y == g->p.y1) {
435  hline_clip(g);
436  return;
437  }
438 
439  // Is this a vertical line (or a point)
440  if (g->p.x == g->p.x1) {
441  vline_clip(g);
442  return;
443  }
444 
445  // Not horizontal or vertical
446 
447  // Use Bresenham's line drawing algorithm.
448  // This should be replaced with fixed point slope based line drawing
449  // which is more efficient on modern processors as it branches less.
450  // When clipping is needed, all the clipping could also be done up front
451  // instead of on each pixel.
452 
453  if (g->p.x1 >= g->p.x) {
454  dx = g->p.x1 - g->p.x;
455  addx = 1;
456  } else {
457  dx = g->p.x - g->p.x1;
458  addx = -1;
459  }
460  if (g->p.y1 >= g->p.y) {
461  dy = g->p.y1 - g->p.y;
462  addy = 1;
463  } else {
464  dy = g->p.y - g->p.y1;
465  addy = -1;
466  }
467 
468  if (dx >= dy) {
469  dy <<= 1;
470  P = dy - dx;
471  diff = P - dx;
472 
473  for(i=0; i<=dx; ++i) {
474  drawpixel_clip(g);
475  if (P < 0) {
476  P += dy;
477  g->p.x += addx;
478  } else {
479  P += diff;
480  g->p.x += addx;
481  g->p.y += addy;
482  }
483  }
484  } else {
485  dx <<= 1;
486  P = dx - dy;
487  diff = P - dy;
488 
489  for(i=0; i<=dy; ++i) {
490  drawpixel_clip(g);
491  if (P < 0) {
492  P += dx;
493  g->p.y += addy;
494  } else {
495  P += diff;
496  g->p.x += addx;
497  g->p.y += addy;
498  }
499  }
500  }
501 }
502 
503 #if GDISP_STARTUP_LOGO_TIMEOUT > 0
504  static bool_t gdispInitDone;
505  static void StartupLogoDisplay(GDisplay *g) {
506  coord_t x, y, w;
507  const coord_t * p;
508  static const coord_t blks[] = {
509  // u
510  2, 6, 1, 10,
511  3, 11, 4, 1,
512  6, 6, 1, 6,
513  // G
514  8, 0, 1, 12,
515  9, 0, 6, 1,
516  9, 11, 6, 1,
517  14, 6, 1, 5,
518  12, 6, 2, 1,
519  // F
520  16, 0, 1, 12,
521  17, 0, 6, 1,
522  17, 6, 3, 1,
523  // X
524  22, 6, 7, 1,
525  24, 0, 1, 6,
526  22, 7, 1, 5,
527  28, 0, 1, 6,
528  26, 7, 1, 5,
529  };
530 
531  // Get a starting position and a scale
532  // Work on a 8x16 grid for each char, 4 chars (uGFX) in 1 line, using half the screen
533  w = g->g.Width/(8*4*2);
534  if (!w) w = 1;
535  x = (g->g.Width - (8*4)*w)/2;
536  y = (g->g.Height - (16*1)*w)/2;
537 
538  // Simple but crude!
539  for(p = blks; p < blks+sizeof(blks)/sizeof(blks[0]); p+=4)
540  gdispGFillArea(g, x+p[0]*w, y+p[1]*w, p[2]*w, p[3]*w, GDISP_STARTUP_LOGO_COLOR);
541  }
542 #endif
543 
544 #if GDISP_NEED_TIMERFLUSH
545  static void FlushTimerFn(void *param) {
546  GDisplay * g;
547  (void) param;
548 
549  for(g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, 0); g; g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g))
550  gdispGFlush(g);
551  }
552 #endif
553 
554 /*===========================================================================*/
555 /* Driver exported functions. */
556 /*===========================================================================*/
557 
558 void _gdispInit(void)
559 {
560  // GDISP_DRIVER_LIST is defined - create each driver instance
561  #if defined(GDISP_DRIVER_LIST)
562  {
563  unsigned i;
564  typedef const GDISPVMT const GDISPVMTLIST[1];
565 
566  extern GDISPVMTLIST GDISP_DRIVER_LIST;
567  static const GDISPVMT * const dclist[] = {GDISP_DRIVER_LIST};
568 
569  for(i = 0; i < sizeof(dclist)/sizeof(dclist[0]); i++) {
570  if (!(dclist[i]->d.flags & GDISP_VFLG_DYNAMICONLY))
571  gdriverRegister(&dclist[i]->d, 0);
572  }
573  }
574  #elif GDISP_TOTAL_DISPLAYS > 1
575  {
576  unsigned i;
577  extern const GDISPVMT const GDISPVMT_OnlyOne[1];
578 
579  if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY)) {
580  for(i = 0; i < GDISP_TOTAL_DISPLAYS; i++)
581  gdriverRegister(&GDISPVMT_OnlyOne->d, 0);
582  }
583  }
584  #else
585  {
586  extern const GDISPVMT const GDISPVMT_OnlyOne[1];
587 
588  if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY))
589  gdriverRegister(&GDISPVMT_OnlyOne->d, 0);
590  }
591  #endif
592 
593  // Re-clear the display after the timeout if we added the logo
594  #if GDISP_STARTUP_LOGO_TIMEOUT > 0
595  {
596  GDisplay *g;
597 
598  gfxSleepMilliseconds(GDISP_STARTUP_LOGO_TIMEOUT);
599 
600  for(g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, 0); g; g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g)) {
602  #if GDISP_HARDWARE_FLUSH
603  gdispGFlush(g);
604  #endif
605  }
606 
607  gdispInitDone = TRUE;
608  }
609  #endif
610 
611  // Start the automatic timer flush (if required)
612  #if GDISP_NEED_TIMERFLUSH
613  gtimerInit(&FlushTimer);
614  gtimerStart(&FlushTimer, FlushTimerFn, 0, TRUE, GDISP_NEED_TIMERFLUSH);
615  #endif
616 }
617 
618 void _gdispDeinit(void)
619 {
620  /* ToDo */
621 }
622 
623 bool_t _gdispInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance) {
624  #define gd ((GDisplay *)g)
625  bool_t ret;
626 
627  // Intialise fields
628  gd->systemdisplay = systeminstance;
629  gd->controllerdisplay = driverinstance;
630  gd->flags = 0;
631  gd->priv = param;
632  MUTEX_INIT(gd);
633 
634  // Call the driver init
635  MUTEX_ENTER(gd);
636  ret = gdisp_lld_init(gd);
637  MUTEX_EXIT(gd);
638  return ret;
639 
640  #undef gd
641 }
642 
643 void _gdispPostInitDriver(GDriver *g) {
644  #define gd ((GDisplay *)g)
645 
646  // Set orientation, clip
647  #if defined(GDISP_DEFAULT_ORIENTATION) && GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
648  #if GDISP_NEED_PIXMAP
649  // Pixmaps should stay in their created orientation (at least initially)
650  if (!(gvmt(gd)->d.flags & GDISP_VFLG_PIXMAP))
651  #endif
652  gdispGControl(gd, GDISP_CONTROL_ORIENTATION, (void *)GDISP_DEFAULT_ORIENTATION);
653  #endif
654  #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
655  gdispGSetClip(gd, 0, 0, gd->g.Width, gd->g.Height);
656  #endif
657 
658  // Clear the Screen
660 
661  // Display the startup logo if this is a static initialised display
662  #if GDISP_STARTUP_LOGO_TIMEOUT > 0
663  if (!gdispInitDone)
664  StartupLogoDisplay(gd);
665  #endif
666 
667  // Flush
668  #if GDISP_HARDWARE_FLUSH
669  gdispGFlush(gd);
670  #endif
671 
672  // If this is the first driver set GDISP
673  if (!GDISP)
674  GDISP = gd;
675 
676  #undef gd
677 }
678 
679 void _gdispDeInitDriver(GDriver *g) {
680  #define gd ((GDisplay *)g)
681 
682  if (GDISP == gd)
683  GDISP = (GDisplay *)gdriverGetInstance(GDRIVER_TYPE_DISPLAY, 0);
684 
685  #if GDISP_HARDWARE_DEINIT
686  #if GDISP_HARDWARE_DEINIT == HARDWARE_AUTODETECT
687  if (gvmt(gd)->deinit)
688  #endif
689  {
690  MUTEX_ENTER(gd);
691  gdisp_lld_deinit(gd);
692  MUTEX_EXIT(gd);
693  }
694  #endif
695  MUTEX_DEINIT(gd);
696 
697  #undef gd
698 }
699 
700 GDisplay *gdispGetDisplay(unsigned display) {
701  return (GDisplay *)gdriverGetInstance(GDRIVER_TYPE_DISPLAY, display);
702 }
703 
704 void gdispSetDisplay(GDisplay *g) {
705  if (g) GDISP = g;
706 }
707 
708 unsigned gdispGetDisplayCount(void) {
709  return gdriverInstanceCount(GDRIVER_TYPE_DISPLAY);
710 }
711 
712 coord_t gdispGGetWidth(GDisplay *g) { return g->g.Width; }
713 coord_t gdispGGetHeight(GDisplay *g) { return g->g.Height; }
714 powermode_t gdispGGetPowerMode(GDisplay *g) { return g->g.Powermode; }
715 orientation_t gdispGGetOrientation(GDisplay *g) { return g->g.Orientation; }
716 uint8_t gdispGGetBacklight(GDisplay *g) { return g->g.Backlight; }
717 uint8_t gdispGGetContrast(GDisplay *g) { return g->g.Contrast; }
718 
719 void gdispGFlush(GDisplay *g) {
720  #if GDISP_HARDWARE_FLUSH
721  #if GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT
722  if (gvmt(g)->flush)
723  #endif
724  {
725  MUTEX_ENTER(g);
726  gdisp_lld_flush(g);
727  MUTEX_EXIT(g);
728  }
729  #else
730  (void) g;
731  #endif
732 }
733 
734 #if GDISP_NEED_STREAMING
735  void gdispGStreamStart(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) {
736  MUTEX_ENTER(g);
737 
738  #if NEED_CLIPPING
739  #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
740  if (!gvmt(g)->setclip)
741  #endif
742  // Test if the area is valid - if not then exit
743  if (x < g->clipx0 || x+cx > g->clipx1 || y < g->clipy0 || y+cy > g->clipy1) {
744  MUTEX_EXIT(g);
745  return;
746  }
747  #endif
748 
749  g->flags |= GDISP_FLG_INSTREAM;
750 
751  // Best is hardware streaming
752  #if GDISP_HARDWARE_STREAM_WRITE
753  #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
754  if (gvmt(g)->writestart)
755  #endif
756  {
757  g->p.x = x;
758  g->p.y = y;
759  g->p.cx = cx;
760  g->p.cy = cy;
761  gdisp_lld_write_start(g);
762  #if GDISP_HARDWARE_STREAM_POS
763  #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT
764  if (gvmt(g)->writepos)
765  #endif
766  gdisp_lld_write_pos(g);
767  #endif
768  return;
769  }
770  #endif
771 
772  // Worst - save the parameters and use pixel drawing and/or area fills
773  #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL
774  // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
775  //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
776  // if (gvmt(g)->pixel)
777  //#endif
778  {
779  // Use x,y as the current position, x1,y1 as the save position and x2,y2 as the end position, cx = bufpos
780  g->p.x1 = g->p.x = x;
781  g->p.y1 = g->p.y = y;
782  g->p.x2 = x + cx;
783  g->p.y2 = y + cy;
784  #if (GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS) || GDISP_HARDWARE_FILLS
785  g->p.cx = 0;
786  g->p.cy = 1;
787  #endif
788  return;
789  }
790  #endif
791 
792  // Don't release the mutex as gdispStreamEnd() will do that.
793  }
794 
795  void gdispGStreamColor(GDisplay *g, color_t color) {
796  #if !GDISP_HARDWARE_STREAM_WRITE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS
797  coord_t sx1, sy1;
798  #endif
799 
800  // Don't touch the mutex as we should already own it
801 
802  // Ignore this call if we are not streaming
803  if (!(g->flags & GDISP_FLG_INSTREAM))
804  return;
805 
806  // Best is hardware streaming
807  #if GDISP_HARDWARE_STREAM_WRITE
808  #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
809  if (gvmt(g)->writestart)
810  #endif
811  {
812  g->p.color = color;
813  gdisp_lld_write_color(g);
814  return;
815  }
816  #endif
817 
818  // Next best is to use bitfills with our line buffer
819  #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS
820  #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT
821  if (gvmt(g)->blit)
822  #endif
823  {
824  g->linebuf[g->p.cx++] = color;
825  if (g->p.cx >= GDISP_LINEBUF_SIZE) {
826  sx1 = g->p.x1;
827  sy1 = g->p.y1;
828  g->p.x1 = 0;
829  g->p.y1 = 0;
830  g->p.ptr = (void *)g->linebuf;
831  gdisp_lld_blit_area(g);
832  g->p.x1 = sx1;
833  g->p.y1 = sy1;
834  g->p.x += g->p.cx;
835  g->p.cx = 0;
836  }
837 
838  // Just wrap at end-of-line and end-of-buffer
839  if (g->p.x+g->p.cx >= g->p.x2) {
840  if (g->p.cx) {
841  sx1 = g->p.x1;
842  sy1 = g->p.y1;
843  g->p.x1 = 0;
844  g->p.y1 = 0;
845  g->p.ptr = (void *)g->linebuf;
846  gdisp_lld_blit_area(g);
847  g->p.x1 = sx1;
848  g->p.y1 = sy1;
849  g->p.cx = 0;
850  }
851  g->p.x = g->p.x1;
852  if (++g->p.y >= g->p.y2)
853  g->p.y = g->p.y1;
854  }
855  }
856  #endif
857 
858  // Only slightly better than drawing pixels is to look for runs and use fillarea
859  #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS
860  // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming.
861  #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
862  if (gvmt(g)->fill)
863  #endif
864  {
865  if (!g->p.cx || g->p.color == color) {
866  g->p.cx++;
867  g->p.color = color;
868  } else {
869  if (g->p.cx == 1)
870  gdisp_lld_draw_pixel(g);
871  else
872  gdisp_lld_fill_area(g);
873  g->p.x += g->p.cx;
874  g->p.color = color;
875  g->p.cx = 1;
876  }
877  // Just wrap at end-of-line and end-of-buffer
878  if (g->p.x+g->p.cx >= g->p.x2) {
879  if (g->p.cx) {
880  if (g->p.cx == 1)
881  gdisp_lld_draw_pixel(g);
882  else
883  gdisp_lld_fill_area(g);
884  g->p.cx = 0;
885  }
886  g->p.x = g->p.x1;
887  if (++g->p.y >= g->p.y2)
888  g->p.y = g->p.y1;
889  }
890  return;
891  }
892  #endif
893 
894  // Worst is using pixel drawing
895  #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL
896  // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
897  //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
898  // if (gvmt(g)->pixel)
899  //#endif
900  {
901  g->p.color = color;
902  gdisp_lld_draw_pixel(g);
903 
904  // Just wrap at end-of-line and end-of-buffer
905  if (++g->p.x >= g->p.x2) {
906  g->p.x = g->p.x1;
907  if (++g->p.y >= g->p.y2)
908  g->p.y = g->p.y1;
909  }
910  return;
911  }
912  #endif
913  }
914 
915  void gdispGStreamStop(GDisplay *g) {
916  // Only release the mutex and end the stream if we are actually streaming.
917  if (!(g->flags & GDISP_FLG_INSTREAM))
918  return;
919 
920  // Clear the flag
921  g->flags &= ~GDISP_FLG_INSTREAM;
922 
923  // The cleanup below must match the streaming code above.
924 
925  #if GDISP_HARDWARE_STREAM_WRITE
926  #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
927  if (gvmt(g)->writestart)
928  #endif
929  {
930  gdisp_lld_write_stop(g);
931  autoflush_stopdone(g);
932  MUTEX_EXIT(g);
933  return;
934  }
935  #endif
936 
937  #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS
938  #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT
939  if (gvmt(g)->blit)
940  #endif
941  {
942  if (g->p.cx) {
943  g->p.x1 = 0;
944  g->p.y1 = 0;
945  g->p.ptr = (void *)g->linebuf;
946  gdisp_lld_blit_area(g);
947  }
948  autoflush_stopdone(g);
949  MUTEX_EXIT(g);
950  return;
951  }
952  #endif
953 
954  #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS
955  // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming.
956  #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
957  if (gvmt(g)->fill)
958  #endif
959  {
960  if (g->p.cx) {
961  if (g->p.cx == 1)
962  gdisp_lld_draw_pixel(g);
963  else
964  gdisp_lld_fill_area(g);
965  }
966  autoflush_stopdone(g);
967  MUTEX_EXIT(g);
968  return;
969  }
970  #endif
971 
972  #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE
973  {
974  autoflush_stopdone(g);
975  MUTEX_EXIT(g);
976  }
977  #endif
978  }
979 #endif
980 
981 void gdispGDrawPixel(GDisplay *g, coord_t x, coord_t y, color_t color) {
982  MUTEX_ENTER(g);
983  g->p.x = x;
984  g->p.y = y;
985  g->p.color = color;
986  drawpixel_clip(g);
987  autoflush(g);
988  MUTEX_EXIT(g);
989 }
990 
991 void gdispGDrawLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color) {
992  MUTEX_ENTER(g);
993  g->p.x = x0;
994  g->p.y = y0;
995  g->p.x1 = x1;
996  g->p.y1 = y1;
997  g->p.color = color;
998  line_clip(g);
999  autoflush(g);
1000  MUTEX_EXIT(g);
1001 }
1002 
1003 void gdispGClear(GDisplay *g, color_t color) {
1004  // Note - clear() ignores the clipping area. It clears the screen.
1005  MUTEX_ENTER(g);
1006 
1007  // Best is hardware accelerated clear
1008  #if GDISP_HARDWARE_CLEARS
1009  #if GDISP_HARDWARE_CLEARS == HARDWARE_AUTODETECT
1010  if (gvmt(g)->clear)
1011  #endif
1012  {
1013  g->p.color = color;
1014  gdisp_lld_clear(g);
1015  autoflush_stopdone(g);
1016  MUTEX_EXIT(g);
1017  return;
1018  }
1019  #endif
1020 
1021  // Next best is hardware accelerated area fill
1022  #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS
1023  #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
1024  if (gvmt(g)->fill)
1025  #endif
1026  {
1027  g->p.x = g->p.y = 0;
1028  g->p.cx = g->g.Width;
1029  g->p.cy = g->g.Height;
1030  g->p.color = color;
1031  gdisp_lld_fill_area(g);
1032  autoflush_stopdone(g);
1033  MUTEX_EXIT(g);
1034  return;
1035  }
1036  #endif
1037 
1038  // Next best is streaming
1039  #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE
1040  #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
1041  if (gvmt(g)->writestart)
1042  #endif
1043  {
1044  uint32_t area;
1045 
1046  g->p.x = g->p.y = 0;
1047  g->p.cx = g->g.Width;
1048  g->p.cy = g->g.Height;
1049  g->p.color = color;
1050  area = (uint32_t)g->p.cx * g->p.cy;
1051 
1052  gdisp_lld_write_start(g);
1053  #if GDISP_HARDWARE_STREAM_POS
1054  #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT
1055  if (gvmt(g)->writepos)
1056  #endif
1057  gdisp_lld_write_pos(g);
1058  #endif
1059  for(; area; area--)
1060  gdisp_lld_write_color(g);
1061  gdisp_lld_write_stop(g);
1062  autoflush_stopdone(g);
1063  MUTEX_EXIT(g);
1064  return;
1065  }
1066  #endif
1067 
1068  // Worst is drawing pixels
1069  #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL
1070  // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
1071  //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
1072  // if (gvmt(g)->pixel)
1073  //#endif
1074  {
1075  g->p.color = color;
1076  for(g->p.y = 0; g->p.y < g->g.Height; g->p.y++)
1077  for(g->p.x = 0; g->p.x < g->g.Width; g->p.x++)
1078  gdisp_lld_draw_pixel(g);
1079  autoflush_stopdone(g);
1080  MUTEX_EXIT(g);
1081  return;
1082  }
1083  #endif
1084 }
1085 
1086 void gdispGFillArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) {
1087  MUTEX_ENTER(g);
1088  g->p.x = x;
1089  g->p.y = y;
1090  g->p.cx = cx;
1091  g->p.cy = cy;
1092  g->p.color = color;
1093  TEST_CLIP_AREA(g) {
1094  fillarea(g);
1095  }
1096  autoflush_stopdone(g);
1097  MUTEX_EXIT(g);
1098 }
1099 
1100 void gdispGBlitArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) {
1101  MUTEX_ENTER(g);
1102 
1103  #if NEED_CLIPPING
1104  #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
1105  if (!gvmt(g)->setclip)
1106  #endif
1107  {
1108  // This is a different clipping to fillarea(g) as it needs to take into account srcx,srcy
1109  if (x < g->clipx0) { cx -= g->clipx0 - x; srcx += g->clipx0 - x; x = g->clipx0; }
1110  if (y < g->clipy0) { cy -= g->clipy0 - y; srcy += g->clipy0 - x; y = g->clipy0; }
1111  if (x+cx > g->clipx1) cx = g->clipx1 - x;
1112  if (y+cy > g->clipy1) cy = g->clipy1 - y;
1113  if (srcx+cx > srccx) cx = srccx - srcx;
1114  if (cx <= 0 || cy <= 0) { MUTEX_EXIT(g); return; }
1115  }
1116  #endif
1117 
1118  // Best is hardware bitfills
1119  #if GDISP_HARDWARE_BITFILLS
1120  #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT
1121  if (gvmt(g)->blit)
1122  #endif
1123  {
1124  g->p.x = x;
1125  g->p.y = y;
1126  g->p.cx = cx;
1127  g->p.cy = cy;
1128  g->p.x1 = srcx;
1129  g->p.y1 = srcy;
1130  g->p.x2 = srccx;
1131  g->p.ptr = (void *)buffer;
1132  gdisp_lld_blit_area(g);
1133  autoflush_stopdone(g);
1134  MUTEX_EXIT(g);
1135  return;
1136  }
1137  #endif
1138 
1139  // Next best is hardware streaming
1140  #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE
1141  #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
1142  if (gvmt(g)->writestart)
1143  #endif
1144  {
1145  // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap
1146  buffer += srcy*srccx+srcx;
1147  srcx = x + cx;
1148  srcy = y + cy;
1149  srccx -= cx;
1150 
1151  g->p.x = x;
1152  g->p.y = y;
1153  g->p.cx = cx;
1154  g->p.cy = cy;
1155  gdisp_lld_write_start(g);
1156  #if GDISP_HARDWARE_STREAM_POS
1157  #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT
1158  if (gvmt(g)->writepos)
1159  #endif
1160  gdisp_lld_write_pos(g);
1161  #endif
1162  for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) {
1163  for(g->p.x = x; g->p.x < srcx; g->p.x++) {
1164  g->p.color = *buffer++;
1165  gdisp_lld_write_color(g);
1166  }
1167  }
1168  gdisp_lld_write_stop(g);
1169  autoflush_stopdone(g);
1170  MUTEX_EXIT(g);
1171  return;
1172  }
1173  #endif
1174 
1175  // Only slightly better than drawing pixels is to look for runs and use fill area
1176  #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS
1177  // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming.
1178  #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
1179  if (gvmt(g)->fill)
1180  #endif
1181  {
1182  // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap
1183  buffer += srcy*srccx+srcx;
1184  srcx = x + cx;
1185  srcy = y + cy;
1186  srccx -= cx;
1187 
1188  g->p.cy = 1;
1189  for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) {
1190  for(g->p.x=x; g->p.x < srcx; g->p.x += g->p.cx) {
1191  g->p.cx=1;
1192  g->p.color = *buffer++;
1193  while(g->p.x+g->p.cx < srcx && *buffer == g->p.color) {
1194  g->p.cx++;
1195  buffer++;
1196  }
1197  if (g->p.cx == 1) {
1198  gdisp_lld_draw_pixel(g);
1199  } else {
1200  gdisp_lld_fill_area(g);
1201  }
1202  }
1203  }
1204  autoflush_stopdone(g);
1205  MUTEX_EXIT(g);
1206  return;
1207  }
1208  #endif
1209 
1210  // Worst is drawing pixels
1211  #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL
1212  // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
1213  //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
1214  // if (gvmt(g)->pixel)
1215  //#endif
1216  {
1217  // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap
1218  buffer += srcy*srccx+srcx;
1219  srcx = x + cx;
1220  srcy = y + cy;
1221  srccx -= cx;
1222 
1223  for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) {
1224  for(g->p.x=x; g->p.x < srcx; g->p.x++) {
1225  g->p.color = *buffer++;
1226  gdisp_lld_draw_pixel(g);
1227  }
1228  }
1229  autoflush_stopdone(g);
1230  MUTEX_EXIT(g);
1231  return;
1232  }
1233  #endif
1234 }
1235 
1236 #if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION
1237  void gdispGSetClip(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) {
1238  MUTEX_ENTER(g);
1239 
1240  // Best is using hardware clipping
1241  #if GDISP_HARDWARE_CLIP
1242  #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
1243  if (gvmt(g)->setclip)
1244  #endif
1245  {
1246  g->p.x = x;
1247  g->p.y = y;
1248  g->p.cx = cx;
1249  g->p.cy = cy;
1250  gdisp_lld_set_clip(g);
1251  }
1252  #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
1253  else
1254  #endif
1255  #endif
1256 
1257  // Worst is using software clipping
1258  #if GDISP_HARDWARE_CLIP != TRUE
1259  {
1260  if (x < 0) { cx += x; x = 0; }
1261  if (y < 0) { cy += y; y = 0; }
1262  if (cx <= 0 || cy <= 0 || x >= g->g.Width || y >= g->g.Height) { x = y = cx = cy = 0; }
1263  g->clipx0 = x;
1264  g->clipy0 = y;
1265  g->clipx1 = x+cx; if (g->clipx1 > g->g.Width) g->clipx1 = g->g.Width;
1266  g->clipy1 = y+cy; if (g->clipy1 > g->g.Height) g->clipy1 = g->g.Height;
1267  }
1268  #endif
1269  MUTEX_EXIT(g);
1270  }
1271 #endif
1272 
1273 #if GDISP_NEED_CIRCLE
1274  void gdispGDrawCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) {
1275  coord_t a, b, P;
1276 
1277  MUTEX_ENTER(g);
1278 
1279  // Calculate intermediates
1280  a = 1;
1281  b = radius;
1282  P = 4 - radius;
1283  g->p.color = color;
1284 
1285  // Away we go using Bresenham's circle algorithm
1286  // Optimized to prevent double drawing
1287  g->p.x = x; g->p.y = y + b; drawpixel_clip(g);
1288  g->p.x = x; g->p.y = y - b; drawpixel_clip(g);
1289  g->p.x = x + b; g->p.y = y; drawpixel_clip(g);
1290  g->p.x = x - b; g->p.y = y; drawpixel_clip(g);
1291  do {
1292  g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g);
1293  g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g);
1294  g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g);
1295  g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g);
1296  g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g);
1297  g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g);
1298  g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g);
1299  g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g);
1300  if (P < 0)
1301  P += 3 + 2*a++;
1302  else
1303  P += 5 + 2*(a++ - b--);
1304  } while(a < b);
1305  g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g);
1306  g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g);
1307  g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g);
1308  g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g);
1309 
1310  autoflush(g);
1311  MUTEX_EXIT(g);
1312  }
1313 #endif
1314 
1315 #if GDISP_NEED_CIRCLE
1316  void gdispGFillCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) {
1317  coord_t a, b, P;
1318 
1319  MUTEX_ENTER(g);
1320 
1321  // Calculate intermediates
1322  a = 1;
1323  b = radius;
1324  P = 4 - radius;
1325  g->p.color = color;
1326 
1327  // Away we go using Bresenham's circle algorithm
1328  // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value
1329  g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g);
1330  g->p.y = y+b; g->p.x = x; drawpixel_clip(g);
1331  g->p.y = y-b; g->p.x = x; drawpixel_clip(g);
1332  do {
1333  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g);
1334  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g);
1335  if (P < 0) {
1336  P += 3 + 2*a++;
1337  } else {
1338  g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g);
1339  g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g);
1340  P += 5 + 2*(a++ - b--);
1341  }
1342  } while(a < b);
1343  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g);
1344  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g);
1345 
1346  autoflush(g);
1347  MUTEX_EXIT(g);
1348  }
1349 #endif
1350 
1351 #if GDISP_NEED_DUALCIRCLE
1352 
1353  #define DRAW_DUALLINE(yval, r1, r2) \
1354  g->p.y = yval; \
1355  g->p.x = x-r1; g->p.x1 = x-r2+1; hline_clip(g); \
1356  g->p.x = x-r2; g->p.x1 = x+r2; g->p.color = color2; hline_clip(g); \
1357  g->p.x = x+r2+1; g->p.x1 = x+r1; g->p.color = color1; hline_clip(g)
1358  #define DRAW_SINGLELINE(yval, r) g->p.y = yval; g->p.x = x-r; g->p.x1 = x+r; hline_clip(g)
1359 
1360  void gdispGFillDualCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius1, color_t color1, coord_t radius2, color_t color2) {
1361  coord_t a, b1, b2, p1, p2;
1362 
1363  MUTEX_ENTER(g);
1364 
1365  // Do the combined circle where the inner circle < 45 deg (and outer circle)
1366  g->p.color = color1;
1367  a = 0; b1 = radius1; b2 = radius2; p1 = p2 = 1;
1368  do {
1369  DRAW_DUALLINE(y+a, b1, b2);
1370  DRAW_DUALLINE(y-a, b1, b2);
1371  if (p1 >= 0) p1 -= b1--;
1372  p1 += a;
1373  if (p2 >= 0) p2 -= b2--;
1374  p2 += a;
1375  } while(++a < b2);
1376 
1377  // Do the combined circle where inner circle > 45 deg, outer circle < 45
1378  do {
1379  DRAW_DUALLINE(y+a, b1, b2);
1380  DRAW_DUALLINE(y-a, b1, b2);
1381  if (p1 >= 0) p1 -= b1--;
1382  p1 += a;
1383  do { p2 -= --b2; } while (p2+a >= b2);
1384  p2 += a;
1385  } while(++a <= radius2 && a < b1);
1386 
1387  if (a < radius2) {
1388  // Do the combined circle where inner circle > 45 deg, outer circle > 45
1389  do {
1390  DRAW_DUALLINE(y+a, b1, b2);
1391  DRAW_DUALLINE(y-a, b1, b2);
1392  do { p1 -= --b1; } while (p1+a >= b1);
1393  p1 += a;
1394  do { p2 -= --b2; } while (p2+a >= b2);
1395  p2 += a++;
1396  } while(b2 > 0);
1397 
1398  } else {
1399  // Do the outer circle above the inner circle but < 45 deg
1400  do {
1401  DRAW_SINGLELINE(y+a, b1);
1402  DRAW_SINGLELINE(y-a, b1);
1403  if (p1 >= 0) p1 -= b1--;
1404  p1 += a++;
1405  } while(a < b1);
1406  DRAW_SINGLELINE(y+a, b1);
1407  DRAW_SINGLELINE(y-a, b1);
1408  }
1409 
1410  // Do the top and bottom part of the outer circle (outer circle > 45deg and above inner circle)
1411  a = 0; b1 = radius1; p1 = 1;
1412  do {
1413  if (p1 >= 0) {
1414  DRAW_SINGLELINE(y+b1, a);
1415  DRAW_SINGLELINE(y-b1, a);
1416  p1 -= b1--;
1417  }
1418  p1 += a++;
1419  } while(b1 > radius2 && a < b1);
1420 
1421  autoflush(g);
1422  MUTEX_EXIT(g);
1423  }
1424  #undef DRAW_DUALLINE
1425  #undef DRAW_SINGLELINE
1426 #endif
1427 
1428 #if GDISP_NEED_ELLIPSE
1429  void gdispGDrawEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) {
1430  coord_t dx, dy;
1431  int32_t a2, b2;
1432  int32_t err, e2;
1433 
1434  MUTEX_ENTER(g);
1435 
1436  // Calculate intermediates
1437  dx = 0;
1438  dy = b;
1439  a2 = a*a;
1440  b2 = b*b;
1441  err = b2-(2*b-1)*a2;
1442  g->p.color = color;
1443 
1444  // Away we go using Bresenham's ellipse algorithm
1445  do {
1446  g->p.x = x + dx; g->p.y = y + dy; drawpixel_clip(g);
1447  g->p.x = x - dx; g->p.y = y + dy; drawpixel_clip(g);
1448  g->p.x = x - dx; g->p.y = y - dy; drawpixel_clip(g);
1449  g->p.x = x + dx; g->p.y = y - dy; drawpixel_clip(g);
1450 
1451  e2 = 2*err;
1452  if(e2 < (2*dx+1)*b2) {
1453  dx++;
1454  err += (2*dx+1)*b2;
1455  }
1456  if(e2 > -(2*dy-1)*a2) {
1457  dy--;
1458  err -= (2*dy-1)*a2;
1459  }
1460  } while(dy >= 0);
1461 
1462  autoflush(g);
1463  MUTEX_EXIT(g);
1464  }
1465 #endif
1466 
1467 #if GDISP_NEED_ELLIPSE
1468  void gdispGFillEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) {
1469  coord_t dx, dy;
1470  int32_t a2, b2;
1471  int32_t err, e2;
1472 
1473  MUTEX_ENTER(g);
1474 
1475  // Calculate intermediates
1476  dx = 0;
1477  dy = b;
1478  a2 = a*a;
1479  b2 = b*b;
1480  err = b2-(2*b-1)*a2;
1481  g->p.color = color;
1482 
1483  // Away we go using Bresenham's ellipse algorithm
1484  // This is optimized to prevent overdrawing by drawing a line only when a y is about to change value
1485  do {
1486  e2 = 2*err;
1487  if(e2 < (2*dx+1)*b2) {
1488  dx++;
1489  err += (2*dx+1)*b2;
1490  }
1491  if(e2 > -(2*dy-1)*a2) {
1492  g->p.y = y + dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g);
1493  if (y) { g->p.y = y - dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); }
1494  dy--;
1495  err -= (2*dy-1)*a2;
1496  }
1497  } while(dy >= 0);
1498 
1499  autoflush(g);
1500  MUTEX_EXIT(g);
1501  }
1502 #endif
1503 
1504 #if GDISP_NEED_ARCSECTORS
1505  void gdispGDrawArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color) {
1506  coord_t a, b, P;
1507 
1508  MUTEX_ENTER(g);
1509 
1510  // Calculate intermediates
1511  a = 1; // x in many explanations
1512  b = radius; // y in many explanations
1513  P = 4 - radius;
1514  g->p.color = color;
1515 
1516  // Away we go using Bresenham's circle algorithm
1517  // Optimized to prevent double drawing
1518  if (sectors & 0x06) { g->p.x = x; g->p.y = y - b; drawpixel_clip(g); } // Upper upper
1519  if (sectors & 0x60) { g->p.x = x; g->p.y = y + b; drawpixel_clip(g); } // Lower lower
1520  if (sectors & 0x81) { g->p.x = x + b; g->p.y = y; drawpixel_clip(g); } // Right right
1521  if (sectors & 0x18) { g->p.x = x - b; g->p.y = y; drawpixel_clip(g); } // Left left
1522 
1523  do {
1524  if (sectors & 0x01) { g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g); } // Upper right right
1525  if (sectors & 0x02) { g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); } // Upper upper right
1526  if (sectors & 0x04) { g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); } // Upper upper left
1527  if (sectors & 0x08) { g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g); } // Upper left left
1528  if (sectors & 0x10) { g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g); } // Lower left left
1529  if (sectors & 0x20) { g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); } // Lower lower left
1530  if (sectors & 0x40) { g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); } // Lower lower right
1531  if (sectors & 0x80) { g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g); } // Lower right right
1532  if (P < 0)
1533  P += 3 + 2*a++;
1534  else
1535  P += 5 + 2*(a++ - b--);
1536  } while(a < b);
1537 
1538  if (sectors & 0xC0) { g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); } // Lower right
1539  if (sectors & 0x03) { g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); } // Upper right
1540  if (sectors & 0x30) { g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); } // Lower left
1541  if (sectors & 0x0C) { g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); } // Upper left
1542 
1543  autoflush(g);
1544  MUTEX_EXIT(g);
1545  }
1546 #endif
1547 
1548 #if GDISP_NEED_ARCSECTORS
1549  void gdispGFillArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color) {
1550  coord_t a, b, P;
1551 
1552  MUTEX_ENTER(g);
1553 
1554  // Calculate intermediates
1555  a = 1; // x in many explanations
1556  b = radius; // y in many explanations
1557  P = 4 - radius;
1558  g->p.color = color;
1559 
1560  // Away we go using Bresenham's circle algorithm
1561  // Optimized to prevent double drawing
1562  if (sectors & 0x06) { g->p.x = x; g->p.y = y - b; drawpixel_clip(g); } // Upper upper
1563  if (sectors & 0x60) { g->p.x = x; g->p.y = y + b; drawpixel_clip(g); } // Lower lower
1564  if (sectors & 0x81) { // Center right
1565  g->p.y = y; g->p.x = x; g->p.x1 = x + b;
1566  if (sectors & 0x18) g->p.x -= b; // Left right
1567  hline_clip(g);
1568  } else if (sectors & 0x18) { // Left center
1569  g->p.x = x - b; g->p.x1 = x; g->p.y = y;
1570  hline_clip(g);
1571  }
1572 
1573  do {
1574  // Top half
1575  switch(sectors & 0x0F) {
1576  case 0x01:
1577  g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
1578  break;
1579  case 0x02:
1580  g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1581  g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1582  break;
1583  case 0x03:
1584  g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1585  g->p.y = y - a; g->p.x = x; g->p.x1 = x + b; hline_clip(g);
1586  break;
1587  case 0x04:
1588  g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1589  g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1590  break;
1591  case 0x05:
1592  g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1593  g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1594  g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
1595  break;
1596  case 0x06:
1597  g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
1598  g->p.y = y - a; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
1599  break;
1600  case 0x07:
1601  g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
1602  g->p.y = y - a; g->p.x = x - a; g->p.x1 = x + b; hline_clip(g);
1603  break;
1604  case 0x08:
1605  g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
1606  break;
1607  case 0x09:
1608  g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
1609  g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
1610  break;
1611  case 0x0A:
1612  g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1613  g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
1614  g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1615  break;
1616  case 0x0B:
1617  g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1618  g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
1619  g->p.y = y - a; g->p.x = x; g->p.x1 = x + b; hline_clip(g);
1620  break;
1621  case 0x0C:
1622  g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1623  g->p.y = y - a; g->p.x = x - b; g->p.x1 = x; hline_clip(g);
1624  break;
1625  case 0x0D:
1626  g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1627  g->p.y = y - a; g->p.x = x - b; g->p.x1 = x; hline_clip(g);
1628  g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
1629  break;
1630  case 0x0E:
1631  g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
1632  g->p.y = y - a; g->p.x = x - b; g->p.x1 = x + a; hline_clip(g);
1633  break;
1634  case 0x0F:
1635  g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
1636  g->p.y = y - a; g->p.x = x - b; g->p.x1 = x + b; hline_clip(g);
1637  break;
1638  }
1639 
1640  // Bottom half
1641  switch((sectors & 0xF0)>>4) {
1642  case 0x01:
1643  g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
1644  break;
1645  case 0x02:
1646  g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1647  g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1648  break;
1649  case 0x03:
1650  g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1651  g->p.y = y + a; g->p.x = x - b; g->p.x1 = x; hline_clip(g);
1652  break;
1653  case 0x04:
1654  g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1655  g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1656  break;
1657  case 0x05:
1658  g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1659  g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
1660  g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1661  break;
1662  case 0x06:
1663  g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
1664  g->p.y = y + a; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
1665  break;
1666  case 0x07:
1667  g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
1668  g->p.y = y + a; g->p.x = x - b; g->p.x1 = x + a; hline_clip(g);
1669  break;
1670  case 0x08:
1671  g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
1672  break;
1673  case 0x09:
1674  g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
1675  g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
1676  break;
1677  case 0x0A:
1678  g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1679  g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1680  g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
1681  break;
1682  case 0x0B:
1683  g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
1684  g->p.y = y + a; g->p.x = x - b; g->p.x1 = x; hline_clip(g);
1685  g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
1686  break;
1687  case 0x0C:
1688  g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1689  g->p.y = y + a; g->p.x = x; g->p.x1 = x + b; hline_clip(g);
1690  break;
1691  case 0x0D:
1692  g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
1693  g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
1694  g->p.y = y + a; g->p.x = x; g->p.x1 = x + b; hline_clip(g);
1695  break;
1696  case 0x0E:
1697  g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
1698  g->p.y = y + a; g->p.x = x - a; g->p.x1 = x + b; hline_clip(g);
1699  break;
1700  case 0x0F:
1701  g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
1702  g->p.y = y + a; g->p.x = x - b; g->p.x1 = x + b; hline_clip(g);
1703  break;
1704  }
1705 
1706  if (P < 0)
1707  P += 3 + 2*a++;
1708  else
1709  P += 5 + 2*(a++ - b--);
1710  } while(a < b);
1711 
1712  // Top half
1713  if (sectors & 0x02) { g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); }
1714  else if (sectors & 0x01) { g->p.y = y - a; g->p.x = x + a; drawpixel_clip(g); }
1715  if (sectors & 0x04) { g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); }
1716  else if (sectors & 0x08) { g->p.y = y - a; g->p.x = x - a; drawpixel_clip(g); }
1717 
1718  // Bottom half
1719  if (sectors & 0x40) { g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); }
1720  else if (sectors & 0x80) { g->p.y = y + a; g->p.x = x + a; drawpixel_clip(g); }
1721  if (sectors & 0x20) { g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); }
1722  else if (sectors & 0x10) { g->p.y = y + a; g->p.x = x - a; drawpixel_clip(g); }
1723 
1724  autoflush(g);
1725  MUTEX_EXIT(g);
1726  }
1727 #endif
1728 
1729 #if GDISP_NEED_ARC
1730  #if (!GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG) || !GFX_USE_GMISC
1731  #include <math.h>
1732  #endif
1733 
1734  void gdispGDrawArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) {
1735  coord_t a, b, P, sedge, eedge;
1736  uint8_t full, sbit, ebit, tbit;
1737 
1738  // Normalize the angles
1739  if (start < 0)
1740  start -= (start/360-1)*360;
1741  else if (start >= 360)
1742  start %= 360;
1743  if (end < 0)
1744  end -= (end/360-1)*360;
1745  else if (end >= 360)
1746  end %= 360;
1747 
1748  sbit = 1<<(start/45);
1749  ebit = 1<<(end/45);
1750  full = 0;
1751  if (start == end) {
1752  full = 0xFF;
1753  } else if (end < start) {
1754  for(tbit=sbit<<1; tbit; tbit<<=1) full |= tbit;
1755  for(tbit=ebit>>1; tbit; tbit>>=1) full |= tbit;
1756  } else if (sbit < 0x80) {
1757  for(tbit=sbit<<1; tbit < ebit; tbit<<=1) full |= tbit;
1758  }
1759  tbit = start%45 == 0 ? sbit : 0;
1760 
1761  MUTEX_ENTER(g);
1762  g->p.color = color;
1763 
1764  if (full) {
1765  // Draw full sectors
1766  // Optimized to prevent double drawing
1767  a = 1;
1768  b = radius;
1769  P = 4 - radius;
1770  if (full & 0x60) { g->p.y = y+b; g->p.x = x; drawpixel_clip(g); }
1771  if (full & 0x06) { g->p.y = y-b; g->p.x = x; drawpixel_clip(g); }
1772  if (full & 0x81) { g->p.y = y; g->p.x = x+b; drawpixel_clip(g); }
1773  if (full & 0x18) { g->p.y = y; g->p.x = x-b; drawpixel_clip(g); }
1774  do {
1775  if (full & 0x01) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); }
1776  if (full & 0x02) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
1777  if (full & 0x04) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
1778  if (full & 0x08) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); }
1779  if (full & 0x10) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); }
1780  if (full & 0x20) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
1781  if (full & 0x40) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
1782  if (full & 0x80) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); }
1783  if (P < 0)
1784  P += 3 + 2*a++;
1785  else
1786  P += 5 + 2*(a++ - b--);
1787  } while(a < b);
1788  if (full & 0xC0) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
1789  if (full & 0x0C) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
1790  if (full & 0x03) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
1791  if (full & 0x30) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
1792  if (full == 0xFF) {
1793  autoflush(g);
1794  MUTEX_EXIT(g);
1795  return;
1796  }
1797  }
1798 
1799  #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG
1800  sedge = NONFIXED(radius * ((sbit & 0x99) ? ffsin(start) : ffcos(start)) + FIXED0_5);
1801  eedge = NONFIXED(radius * ((ebit & 0x99) ? ffsin(end) : ffcos(end)) + FIXED0_5);
1802  #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG
1803  sedge = floor(radius * ((sbit & 0x99) ? fsin(start) : fcos(start)) + 0.5);
1804  eedge = floor(radius * ((ebit & 0x99) ? fsin(end) : fcos(end)) + 0.5);
1805  #else
1806  sedge = floor(radius * ((sbit & 0x99) ? sin(start*GFX_PI/180) : cos(start*GFX_PI/180)) + 0.5);
1807  eedge = floor(radius * ((ebit & 0x99) ? sin(end*GFX_PI/180) : cos(end*GFX_PI/180)) + 0.5);
1808  #endif
1809  if (sbit & 0xB4) sedge = -sedge;
1810  if (ebit & 0xB4) eedge = -eedge;
1811 
1812  if (sbit != ebit) {
1813  // Draw start and end sectors
1814  // Optimized to prevent double drawing
1815  a = 1;
1816  b = radius;
1817  P = 4 - radius;
1818  if ((sbit & 0x20) || (tbit & 0x40) || (ebit & 0x40)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); }
1819  if ((sbit & 0x02) || (tbit & 0x04) || (ebit & 0x04)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); }
1820  if ((sbit & 0x80) || (tbit & 0x01) || (ebit & 0x01)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); }
1821  if ((sbit & 0x08) || (tbit & 0x10) || (ebit & 0x10)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); }
1822  do {
1823  if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); }
1824  if (((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
1825  if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
1826  if (((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); }
1827  if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); }
1828  if (((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
1829  if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
1830  if (((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); }
1831  if (P < 0)
1832  P += 3 + 2*a++;
1833  else
1834  P += 5 + 2*(a++ - b--);
1835  } while(a < b);
1836  if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge) || ((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge))
1837  { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
1838  if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge) || ((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge))
1839  { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
1840  if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge) || ((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge))
1841  { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
1842  if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge) || ((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge))
1843  { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
1844  } else if (end < start) {
1845  // Draw start/end sector where it is a non-internal angle
1846  // Optimized to prevent double drawing
1847  a = 1;
1848  b = radius;
1849  P = 4 - radius;
1850  if ((sbit & 0x60) || (tbit & 0xC0)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); }
1851  if ((sbit & 0x06) || (tbit & 0x0C)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); }
1852  if ((sbit & 0x81) || (tbit & 0x03)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); }
1853  if ((sbit & 0x18) || (tbit & 0x30)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); }
1854  do {
1855  if ((sbit & 0x01) && (a >= sedge || a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); }
1856  if ((sbit & 0x02) && (a <= sedge || a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
1857  if ((sbit & 0x04) && (a >= sedge || a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
1858  if ((sbit & 0x08) && (a <= sedge || a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); }
1859  if ((sbit & 0x10) && (a >= sedge || a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); }
1860  if ((sbit & 0x20) && (a <= sedge || a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
1861  if ((sbit & 0x40) && (a >= sedge || a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
1862  if ((sbit & 0x80) && (a <= sedge || a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); }
1863  if (P < 0)
1864  P += 3 + 2*a++;
1865  else
1866  P += 5 + 2*(a++ - b--);
1867  } while(a < b);
1868  if (((sbit & 0x04) && (a >= sedge || a <= eedge)) || ((sbit & 0x08) && (a <= sedge || a >= eedge)))
1869  { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
1870  if (((sbit & 0x40) && (a >= sedge || a <= eedge)) || ((sbit & 0x80) && (a <= sedge || a >= eedge)))
1871  { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
1872  if (((sbit & 0x01) && (a >= sedge || a <= eedge)) || ((sbit & 0x02) && (a <= sedge || a >= eedge)))
1873  { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
1874  if (((sbit & 0x10) && (a >= sedge || a <= eedge)) || ((sbit & 0x20) && (a <= sedge || a >= eedge)))
1875  { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
1876  } else {
1877  // Draw start/end sector where it is a internal angle
1878  // Optimized to prevent double drawing
1879  a = 1;
1880  b = radius;
1881  P = 4 - radius;
1882  if (((sbit & 0x20) && !eedge) || ((sbit & 0x40) && !sedge)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); }
1883  if (((sbit & 0x02) && !eedge) || ((sbit & 0x04) && !sedge)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); }
1884  if (((sbit & 0x80) && !eedge) || ((sbit & 0x01) && !sedge)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); }
1885  if (((sbit & 0x08) && !eedge) || ((sbit & 0x10) && !sedge)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); }
1886  do {
1887  if (((sbit & 0x01) && a >= sedge && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); }
1888  if (((sbit & 0x02) && a <= sedge && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
1889  if (((sbit & 0x04) && a >= sedge && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
1890  if (((sbit & 0x08) && a <= sedge && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); }
1891  if (((sbit & 0x10) && a >= sedge && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); }
1892  if (((sbit & 0x20) && a <= sedge && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
1893  if (((sbit & 0x40) && a >= sedge && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
1894  if (((sbit & 0x80) && a <= sedge && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); }
1895  if (P < 0)
1896  P += 3 + 2*a++;
1897  else
1898  P += 5 + 2*(a++ - b--);
1899  } while(a < b);
1900  if (((sbit & 0x04) && a >= sedge && a <= eedge) || ((sbit & 0x08) && a <= sedge && a >= eedge))
1901  { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
1902  if (((sbit & 0x40) && a >= sedge && a <= eedge) || ((sbit & 0x80) && a <= sedge && a >= eedge))
1903  { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
1904  if (((sbit & 0x01) && a >= sedge && a <= eedge) || ((sbit & 0x02) && a <= sedge && a >= eedge))
1905  { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
1906  if (((sbit & 0x10) && a >= sedge && a <= eedge) || ((sbit & 0x20) && a <= sedge && a >= eedge))
1907  { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
1908  }
1909 
1910  autoflush(g);
1911  MUTEX_EXIT(g);
1912  }
1913 #endif
1914 
1915 #if GDISP_NEED_ARC
1916  #if (!GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG) || !GFX_USE_GMISC
1917  #include <math.h>
1918  #endif
1919 
1920  void gdispGDrawThickArc(GDisplay *g, coord_t xc, coord_t yc, coord_t radiusStart, coord_t radiusEnd, coord_t start, coord_t end, color_t color) {
1921  coord_t x, y, d, r;
1922  coord_t startTan, endTan, curangle;
1923  coord_t precision = 512;
1924 
1925  // Normalize the angles
1926  if (start < 0)
1927  start -= (start/360-1)*360;
1928  else if (start >= 360)
1929  start %= 360;
1930  if (end < 0)
1931  end -= (end/360-1)*360;
1932  else if (end >= 360)
1933  end %= 360;
1934 
1935  #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG
1936  if((start / 45) % 2 == 0){
1937  startTan = ffsin(start % 45) * precision / ffcos(start % 45) + start / 45 * precision;}
1938  else{
1939  startTan = ffsin(start % 45 - 45) * precision / ffcos(start % 45 - 45) + start / 45 * precision + precision;}
1940 
1941  if((end / 45) % 2 == 0){
1942  endTan = ffsin(end % 45) * precision / ffcos(end % 45) + end / 45 * precision;}
1943  else{
1944  endTan = ffsin(end % 45 - 45) * precision / ffcos(end % 45 - 45) + end / 45 * precision + precision;}
1945  #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG
1946  if((start / 45) % 2 == 0){
1947  startTan = fsin(start % 45) * precision / fcos(start % 45) + start / 45 * precision;}
1948  else{
1949  startTan = fsin(start % 45 - 45) * precision / fcos(start % 45 - 45) + start / 45 * precision + precision;}
1950 
1951  if((end / 45) % 2 == 0){
1952  endTan = fsin(end % 45) * precision / fcos(end % 45) + end / 45 * precision;}
1953  else{
1954  endTan = fsin(end % 45 - 45) * precision / fcos(end % 45 - 45) + end / 45 * precision + precision;}
1955  #else
1956  if((start / 45) % 2 == 0){
1957  startTan = (tan((start % 45)*GFX_PI/180) + start / 45)* precision;}
1958  else{
1959  startTan = (1+tan((start % 45 - 45)*GFX_PI/180) + start / 45)* precision;}
1960 
1961  if((end / 45) % 2 == 0){
1962  endTan = (tan((end % 45) *GFX_PI/180) + end / 45) * precision;}
1963  else{
1964  endTan = (1+tan((end % 45 - 45) *GFX_PI/180) + end / 45) * precision;}
1965  #endif
1966 
1967  MUTEX_ENTER(g);
1968  g->p.color = color;
1969 
1970  //Draw concentric circles using Andres algorithm
1971  for(r = radiusStart; r <= radiusEnd; r++)
1972  {
1973  x = 0;
1974  y = r;
1975  d = r - 1;
1976 
1977  while (y >= x){
1978  //approximate tan
1979  curangle = x*precision/y;
1980 
1981  if(end > start){
1982  g->p.color = color;
1983  //Draw points by symmetry
1984  if(curangle > startTan && curangle < endTan){g->p.y = yc - x; g->p.x = xc + y; drawpixel_clip(g);}
1985  if(curangle + 2*precision > startTan && curangle + 2*precision < endTan){g->p.y = yc - y; g->p.x = xc - x; drawpixel_clip(g);}
1986  if(curangle + 4*precision > startTan && curangle + 4*precision < endTan){g->p.y = yc + x; g->p.x = xc - y; drawpixel_clip(g);}
1987  if(curangle + 6*precision > startTan && curangle + 6*precision < endTan){g->p.y = yc + y; g->p.x = xc + x; drawpixel_clip(g);}
1988 
1989  curangle = precision - curangle;
1990 
1991  if(curangle + precision > startTan && curangle + precision < endTan){g->p.y = yc - y; g->p.x = xc + x; drawpixel_clip(g);}
1992  if(curangle + 3*precision > startTan && curangle + 3*precision < endTan){g->p.y = yc - x; g->p.x = xc - y; drawpixel_clip(g);}
1993  if(curangle + 5*precision > startTan && curangle + 5*precision < endTan){g->p.y = yc + y; g->p.x = xc - x; drawpixel_clip(g);}
1994  if(curangle + 7*precision > startTan && curangle + 7*precision < endTan){g->p.y = yc + x; g->p.x = xc + y; drawpixel_clip(g);}
1995 
1996  }
1997  else{
1998  //Draw points by symmetry
1999  if(curangle > startTan || curangle < endTan){g->p.y = yc - x; g->p.x = xc + y; drawpixel_clip(g);}
2000  if(curangle + 2*precision > startTan || curangle + 2*precision < endTan){g->p.y = yc - y; g->p.x = xc - x; drawpixel_clip(g);}
2001  if(curangle + 4*precision > startTan || curangle + 4*precision < endTan){g->p.y = yc + x; g->p.x = xc - y; drawpixel_clip(g);}
2002  if(curangle + 6*precision > startTan || curangle + 6*precision < endTan){g->p.y = yc + y; g->p.x = xc + x; drawpixel_clip(g);}
2003 
2004  curangle = precision - curangle;
2005 
2006  if(curangle + precision > startTan || curangle + precision < endTan){g->p.y = yc - y; g->p.x = xc + x; drawpixel_clip(g);}
2007  if(curangle + 3*precision > startTan || curangle + 3*precision < endTan){g->p.y = yc - x; g->p.x = xc - y; drawpixel_clip(g);}
2008  if(curangle + 5*precision > startTan || curangle + 5*precision < endTan){g->p.y = yc + y; g->p.x = xc - x; drawpixel_clip(g);}
2009  if(curangle + 7*precision > startTan || curangle + 7*precision < endTan){g->p.y = yc + x; g->p.x = xc + y; drawpixel_clip(g);}
2010  }
2011 
2012  //Compute next point
2013  if (d >= 2 * x){
2014  d -= 2 * x + 1;
2015  x++;
2016  }
2017  else if (d < 2 * (r - y)){
2018  d += 2 * y - 1;
2019  y--;
2020  }
2021  else{
2022  d += 2 * (y - x - 1);
2023  y--;
2024  x++;
2025  }
2026  }
2027  }
2028 
2029  autoflush(g);
2030  MUTEX_EXIT(g);
2031  }
2032 #endif
2033 
2034 #if GDISP_NEED_ARC
2035  void gdispGFillArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) {
2036  coord_t a, b, P;
2037  coord_t sy, ey;
2038  fixed sxa, sxb, sxd, exa, exb, exd;
2039  uint8_t qtr;
2040 
2041  MUTEX_ENTER(g);
2042 
2043  // We add a half pixel so that we are drawing from the centre of the pixel
2044  // instead of the left edge of the pixel. This also fixes the implied floor()
2045  // when converting back to a coord_t
2046  sxa = exa = FIXED(x) + FIXED0_5;
2047 
2048  // Do the trig to get the formulas for the start and end lines.
2049  #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG
2050  sxb = radius*ffcos(start); sy = NONFIXED(FIXED0_5 - radius*ffsin(start));
2051  exb = radius*ffcos(end); ey = NONFIXED(FIXED0_5 - radius*ffsin(end));
2052  #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG
2053  sxb = FP2FIXED(radius*fcos(start)); sy = floor(0.5-radius*fsin(start));
2054  exb = FP2FIXED(radius*fcos(end)); ey = floor(0.5-radius*fsin(end));
2055  #else
2056  sxb = FP2FIXED(radius*cos(start*GFX_PI/180)); sy = floor(0.5-radius*sin(start*GFX_PI/180));
2057  exb = FP2FIXED(radius*cos(end*GFX_PI/180)); ey = floor(0.5-radius*sin(end*GFX_PI/180));
2058  #endif
2059  sxd = sy ? sxb/sy : sxb;
2060  exd = ey ? exb/ey : exb;
2061 
2062  // Calculate which quarters and which direction we are traveling
2063  qtr = 0;
2064  if (sxb > 0) qtr |= 0x01; // S1=0001(1), S2=0000(0), S3=0010(2), S4=0011(3)
2065  if (sy > 0) qtr |= 0x02;
2066  if (exb > 0) qtr |= 0x04; // E1=0100(4), E2=0000(0), E3=1000(8), E4=1100(12)
2067  if (ey > 0) qtr |= 0x08;
2068  if (sy > ey || (sy == ey && sxb > 0)) qtr |= 0x10; // order of start and end lines
2069 
2070  // Calculate intermediates
2071  a = 1;
2072  b = radius;
2073  P = 4 - radius;
2074  g->p.color = color;
2075  sxb += sxa;
2076  exb += exa;
2077 
2078  // Away we go using Bresenham's circle algorithm
2079  // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value
2080 
2081  switch(qtr) {
2082  case 0: // S2E2 sy <= ey
2083  case 1: // S1E2 sy <= ey
2084  if (ey && sy) {
2085  g->p.x = x; g->p.x1 = x; // E2S
2086  sxa -= sxd; exa -= exd;
2087  } else if (sy) {
2088  g->p.x = x-b; g->p.x1 = x; // C2S
2089  sxa -= sxd;
2090  } else if (ey) {
2091  g->p.x = x; g->p.x1 = x+b; // E2C
2092  exa -= exd;
2093  } else {
2094  g->p.x = x-b; g->p.x1 = x+b; // C2C
2095  }
2096  g->p.y = y;
2097  hline_clip(g);
2098  do {
2099  if (-a >= ey) {
2100  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S
2101  sxa -= sxd; exa -= exd;
2102  } else if (-a >= sy) {
2103  g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S
2104  sxa -= sxd;
2105  } else if (qtr & 1) {
2106  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2107  }
2108  if (P < 0) {
2109  P += 3 + 2*a++;
2110  } else {
2111  if (-b >= ey) {
2112  g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S
2113  sxb += sxd; exb += exd;
2114  } else if (-b >= sy) {
2115  g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S
2116  sxb += sxd;
2117  } else if (qtr & 1) {
2118  g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2119  }
2120  P += 5 + 2*(a++ - b--);
2121  }
2122  } while(a < b);
2123  if (-a >= ey) {
2124  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S
2125  } else if (-a >= sy) {
2126  g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S
2127  } else if (qtr & 1) {
2128  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2129  }
2130  break;
2131 
2132  case 2: // S3E2 sy <= ey
2133  case 3: // S4E2 sy <= ey
2134  case 6: // S3E1 sy <= ey
2135  case 7: // S4E1 sy <= ey
2136  case 18: // S3E2 sy > ey
2137  case 19: // S4E2 sy > ey
2138  case 22: // S3E1 sy > ey
2139  case 23: // S4E1 sy > ey
2140  g->p.y = y; g->p.x = x; g->p.x1 = x+b; hline_clip(g); // SE2C
2141  sxa += sxd; exa -= exd;
2142  do {
2143  if (-a >= ey) {
2144  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C
2145  exa -= exd;
2146  } else if (!(qtr & 4)) {
2147  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2148  }
2149  if (a <= sy) {
2150  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C
2151  sxa += sxd;
2152  } else if (!(qtr & 1)) {
2153  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2154  }
2155  if (P < 0) {
2156  P += 3 + 2*a++;
2157  } else {
2158  if (-b >= ey) {
2159  g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C
2160  exb += exd;
2161  } else if (!(qtr & 4)) {
2162  g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2163  }
2164  if (b <= sy) {
2165  g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C
2166  sxb -= sxd;
2167  } else if (!(qtr & 1)) {
2168  g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2169  }
2170  P += 5 + 2*(a++ - b--);
2171  }
2172  } while(a < b);
2173  if (-a >= ey) {
2174  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C
2175  } else if (!(qtr & 4)) {
2176  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2177  }
2178  if (a <= sy) {
2179  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+a; hline_clip(g); // S2C
2180  } else if (!(qtr & 1)) {
2181  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C
2182  }
2183  break;
2184 
2185  case 4: // S2E1 sy <= ey
2186  case 5: // S1E1 sy <= ey
2187  g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2188  do {
2189  if (-a >= ey) {
2190  g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S
2191  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C
2192  sxa -= sxd; exa -= exd;
2193  } else if (-a >= sy) {
2194  g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S
2195  sxa -= sxd;
2196  } else if (qtr & 1) {
2197  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2198  }
2199  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2200  if (P < 0) {
2201  P += 3 + 2*a++;
2202  } else {
2203  if (-b >= ey) {
2204  g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S
2205  g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C
2206  sxb += sxd; exb += exd;
2207  } else if (-b >= sy) {
2208  g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S
2209  sxb += sxd;
2210  } else if (qtr & 1) {
2211  g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2212  }
2213  g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2214  P += 5 + 2*(a++ - b--);
2215  }
2216  } while(a < b);
2217  if (-a >= ey) {
2218  g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S
2219  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C
2220  } else if (-a >= sy) {
2221  g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S
2222  } else if (qtr & 1) {
2223  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2224  }
2225  g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2226  break;
2227 
2228  case 8: // S2E3 sy <= ey
2229  case 9: // S1E3 sy <= ey
2230  case 12: // S2E4 sy <= ey
2231  case 13: // S1E4 sy <= ey
2232  case 24: // S2E3 sy > ey
2233  case 25: // S1E3 sy > ey
2234  case 28: // S2E3 sy > ey
2235  case 29: // S1E3 sy > ey
2236  g->p.y = y; g->p.x = x-b; g->p.x1 = x; hline_clip(g); // C2SE
2237  sxa -= sxd; exa += exd;
2238  do {
2239  if (-a >= sy) {
2240  g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S
2241  sxa -= sxd;
2242  } else if (qtr & 1) {
2243  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2244  }
2245  if (a <= ey) {
2246  g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E
2247  exa += exd;
2248  } else if (qtr & 4) {
2249  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2250  }
2251  if (P < 0) {
2252  P += 3 + 2*a++;
2253  } else {
2254  if (-b >= sy) {
2255  g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S
2256  sxb += sxd;
2257  } else if (qtr & 1) {
2258  g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2259  }
2260  if (b <= ey) {
2261  g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E
2262  exb -= exd;
2263  } else if (qtr & 4) {
2264  g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2265  }
2266  P += 5 + 2*(a++ - b--);
2267  }
2268  } while(a < b);
2269  if (-a >= sy) {
2270  g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S
2271  } else if (qtr & 1) {
2272  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2273  }
2274  if (a <= ey) {
2275  g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E
2276  } else if (qtr & 4) {
2277  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C
2278  }
2279  break;
2280 
2281  case 10: // S3E3 sy <= ey
2282  case 14: // S3E4 sy <= ey
2283  g->p.y = y; g->p.x = x; drawpixel_clip(g); // S2E
2284  sxa += sxd; exa += exd;
2285  do {
2286  if (a <= sy) {
2287  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E
2288  sxa += sxd; exa += exd;
2289  } else if (a <= ey) {
2290  g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E
2291  exa += exd;
2292  } else if (qtr & 4) {
2293  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2294  }
2295  if (P < 0) {
2296  P += 3 + 2*a++;
2297  } else {
2298  if (b <= sy) {
2299  g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E
2300  sxb -= sxd; exb -= exd;
2301  } else if (b <= ey) {
2302  g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E
2303  exb -= exd;
2304  } else if (qtr & 4) {
2305  g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2306  }
2307  P += 5 + 2*(a++ - b--);
2308  }
2309  } while(a < b);
2310  if (a <= sy) {
2311  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E
2312  } else if (a <= ey) {
2313  g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E
2314  } else if (qtr & 4) {
2315  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2316  }
2317  break;
2318 
2319  case 11: // S4E3 sy <= ey
2320  case 15: // S4E4 sy <= ey
2321  g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2322  do {
2323  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2324  if (a <= sy) {
2325  g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E
2326  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C
2327  sxa += sxd; exa += exd;
2328  } else if (a <= ey) {
2329  g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E
2330  exa += exd;
2331  } else if (qtr & 4) {
2332  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2333  }
2334  if (P < 0) {
2335  P += 3 + 2*a++;
2336  } else {
2337  g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2338  if (b <= sy) {
2339  g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E
2340  g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C
2341  sxb -= sxd; exb -= exd;
2342  } else if (b <= ey) {
2343  g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E
2344  exb -= exd;
2345  } else if (qtr & 4) {
2346  g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2347  }
2348  P += 5 + 2*(a++ - b--);
2349  }
2350  } while(a < b);
2351  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2352  if (a <= sy) {
2353  g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E
2354  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C
2355  } else if (a <= ey) {
2356  g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E
2357  } else if (qtr & 4) {
2358  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2359  }
2360  break;
2361 
2362  case 16: // S2E2 sy > ey
2363  case 20: // S2E1 sy > ey
2364  g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2365  sxa -= sxd; exa -= exd;
2366  do {
2367  if (-a >= sy) {
2368  g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S
2369  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C
2370  sxa -= sxd; exa -= exd;
2371  } else if (-a >= ey) {
2372  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C
2373  exa -= exd;
2374  } else if (!(qtr & 4)){
2375  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2376  }
2377  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2378  if (P < 0) {
2379  P += 3 + 2*a++;
2380  } else {
2381  if (-b >= sy) {
2382  g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S
2383  g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C
2384  sxb += sxd; exb += exd;
2385  } else if (-b >= ey) {
2386  g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C
2387  exb += exd;
2388  } else if (!(qtr & 4)){
2389  g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2390  }
2391  g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2392  P += 5 + 2*(a++ - b--);
2393  }
2394  } while(a < b);
2395  if (-a >= sy) {
2396  g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S
2397  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C
2398  } else if (-a >= ey) {
2399  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C
2400  } else if (!(qtr & 4)){
2401  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2402  }
2403  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2404  break;
2405 
2406  case 17: // S1E2 sy > ey
2407  case 21: // S1E1 sy > ey
2408  if (sy) {
2409  g->p.x = x; g->p.x1 = x; // E2S
2410  sxa -= sxd; exa -= exd;
2411  } else {
2412  g->p.x = x; g->p.x1 = x+b; // E2C
2413  exa -= exd;
2414  }
2415  g->p.y = y;
2416  hline_clip(g);
2417  do {
2418  if (-a >= sy) {
2419  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S
2420  sxa -= sxd; exa -= exd;
2421  } else if (-a >= ey) {
2422  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C
2423  exa -= exd;
2424  } else if (!(qtr & 4)) {
2425  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2426  }
2427  if (P < 0) {
2428  P += 3 + 2*a++;
2429  } else {
2430  if (-b >= sy) {
2431  g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S
2432  sxb += sxd; exb += exd;
2433  } else if (-b >= ey) {
2434  g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C
2435  exb += exd;
2436  } else if (!(qtr & 4)) {
2437  g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2438  }
2439  P += 5 + 2*(a++ - b--);
2440  }
2441  } while(a < b);
2442  if (-a >= sy) {
2443  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S
2444  } else if (-a >= ey) {
2445  g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C
2446  } else if (!(qtr & 4)) {
2447  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2448  }
2449  break;
2450 
2451  case 26: // S3E3 sy > ey
2452  case 27: // S4E3 sy > ey
2453  g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2454  do {
2455  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2456  if (a <= ey) {
2457  g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E
2458  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C
2459  sxa += sxd; exa += exd;
2460  } else if (a <= sy) {
2461  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C
2462  sxa += sxd;
2463  } else if (!(qtr & 1)) {
2464  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2465  }
2466  if (P < 0) {
2467  P += 3 + 2*a++;
2468  } else {
2469  g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2470  if (b <= ey) {
2471  g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E
2472  g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C
2473  sxb -= sxd; exb -= exd;
2474  } else if (b <= sy) {
2475  g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C
2476  sxb -= sxd;
2477  } else if (!(qtr & 1)) {
2478  g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2479  }
2480  P += 5 + 2*(a++ - b--);
2481  }
2482  } while(a < b);
2483  g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2484  if (a <= ey) {
2485  g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E
2486  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C
2487  } else if (a <= sy) {
2488  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C
2489  } else if (!(qtr & 4)) {
2490  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2491  }
2492  break;
2493 
2494  case 30: // S3E4 sy > ey
2495  case 31: // S4E4 sy > ey
2496  do {
2497  if (a <= ey) {
2498  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E
2499  sxa += sxd; exa += exd;
2500  } else if (a <= sy) {
2501  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C
2502  sxa += sxd;
2503  } else if (!(qtr & 1)) {
2504  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2505  }
2506  if (P < 0) {
2507  P += 3 + 2*a++;
2508  } else {
2509  if (b <= ey) {
2510  g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E
2511  sxb -= sxd; exb -= exd;
2512  } else if (b <= sy) {
2513  g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C
2514  sxb -= sxd;
2515  } else if (!(qtr & 1)) {
2516  g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C
2517  }
2518  P += 5 + 2*(a++ - b--);
2519  }
2520  } while(a < b);
2521  if (a <= ey) {
2522  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E
2523  } else if (a <= sy) {
2524  g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C
2525  } else if (!(qtr & 4)) {
2526  g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C
2527  }
2528  break;
2529  }
2530 
2531  autoflush(g);
2532  MUTEX_EXIT(g);
2533  }
2534 #endif
2535 
2536 #if GDISP_NEED_ARC || GDISP_NEED_ARCSECTORS
2537  void gdispGDrawRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) {
2538  if (2*radius > cx || 2*radius > cy) {
2539  gdispGDrawBox(g, x, y, cx, cy, color);
2540  return;
2541  }
2542 
2543  #if GDISP_NEED_ARCSECTORS
2544  gdispGDrawArcSectors(g, x+radius, y+radius, radius, 0x0C, color);
2545  gdispGDrawArcSectors(g, x+cx-1-radius, y+radius, radius, 0x03, color);
2546  gdispGDrawArcSectors(g, x+cx-1-radius, y+cy-1-radius, radius, 0xC0, color);
2547  gdispGDrawArcSectors(g, x+radius, y+cy-1-radius, radius, 0x30, color);
2548  #else
2549  gdispGDrawArc(g, x+radius, y+radius, radius, 90, 180, color);
2550  gdispGDrawArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color);
2551  gdispGDrawArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color);
2552  gdispGDrawArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color);
2553  #endif
2554  gdispGDrawLine(g, x+radius+1, y, x+cx-2-radius, y, color);
2555  gdispGDrawLine(g, x+cx-1, y+radius+1, x+cx-1, y+cy-2-radius, color);
2556  gdispGDrawLine(g, x+radius+1, y+cy-1, x+cx-2-radius, y+cy-1, color);
2557  gdispGDrawLine(g, x, y+radius+1, x, y+cy-2-radius, color);
2558  }
2559 #endif
2560 
2561 #if GDISP_NEED_ARC || GDISP_NEED_ARCSECTORS
2562  void gdispGFillRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) {
2563  coord_t radius2;
2564 
2565  radius2 = radius*2;
2566  if (radius2 > cx || radius2 > cy) {
2567  gdispGFillArea(g, x, y, cx, cy, color);
2568  return;
2569  }
2570  #if GDISP_NEED_ARCSECTORS
2571  gdispGFillArcSectors(g, x+radius, y+radius, radius, 0x0C, color);
2572  gdispGFillArcSectors(g, x+cx-1-radius, y+radius, radius, 0x03, color);
2573  gdispGFillArcSectors(g, x+cx-1-radius, y+cy-1-radius, radius, 0xC0, color);
2574  gdispGFillArcSectors(g, x+radius, y+cy-1-radius, radius, 0x30, color);
2575  #else
2576  gdispGFillArc(g, x+radius, y+radius, radius, 90, 180, color);
2577  gdispGFillArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color);
2578  gdispGFillArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color);
2579  gdispGFillArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color);
2580  #endif
2581  gdispGFillArea(g, x+radius+1, y, cx-radius2, radius, color);
2582  gdispGFillArea(g, x+radius+1, y+cy-radius, cx-radius2, radius, color);
2583  gdispGFillArea(g, x, y+radius, cx, cy-radius2, color);
2584  }
2585 #endif
2586 
2587 #if GDISP_NEED_PIXELREAD
2588  color_t gdispGGetPixelColor(GDisplay *g, coord_t x, coord_t y) {
2589  color_t c;
2590 
2591  /* Always synchronous as it must return a value */
2592  MUTEX_ENTER(g);
2593  #if GDISP_HARDWARE_PIXELREAD
2594  #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT
2595  if (gvmt(g)->get)
2596  #endif
2597  {
2598  // Best is direct pixel read
2599  g->p.x = x;
2600  g->p.y = y;
2601  c = gdisp_lld_get_pixel_color(g);
2602  MUTEX_EXIT(g);
2603  return c;
2604  }
2605  #endif
2606  #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ
2607  #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT
2608  if (gvmt(g)->readcolor)
2609  #endif
2610  {
2611  // Next best is hardware streaming
2612  g->p.x = x;
2613  g->p.y = y;
2614  g->p.cx = 1;
2615  g->p.cy = 1;
2616  gdisp_lld_read_start(g);
2617  c = gdisp_lld_read_color(g);
2618  gdisp_lld_read_stop(g);
2619  MUTEX_EXIT(g);
2620  return c;
2621  }
2622  #endif
2623  #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ != TRUE
2624  #if !GDISP_HARDWARE_PIXELREAD && !GDISP_HARDWARE_STREAM_READ
2625  // Worst is "not possible"
2626  #error "GDISP: GDISP_NEED_PIXELREAD has been set but there is no hardware support for reading the display"
2627  #endif
2628  MUTEX_EXIT(g);
2629  return 0;
2630  #endif
2631  }
2632 #endif
2633 
2634 #if GDISP_NEED_SCROLL
2635  void gdispGVerticalScroll(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) {
2636  coord_t abslines;
2637  #if GDISP_HARDWARE_SCROLL != TRUE
2638  coord_t fy, dy, ix, fx, i, j;
2639  #endif
2640 
2641  if (!lines) return;
2642 
2643  MUTEX_ENTER(g);
2644  #if NEED_CLIPPING
2645  #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
2646  if (!gvmt(g)->setclip)
2647  #endif
2648  {
2649  if (x < g->clipx0) { cx -= g->clipx0 - x; x = g->clipx0; }
2650  if (y < g->clipy0) { cy -= g->clipy0 - y; y = g->clipy0; }
2651  if (cx <= 0 || cy <= 0 || x >= g->clipx1 || y >= g->clipy1) { MUTEX_EXIT(g); return; }
2652  if (x+cx > g->clipx1) cx = g->clipx1 - x;
2653  if (y+cy > g->clipy1) cy = g->clipy1 - y;
2654  }
2655  #endif
2656 
2657  abslines = lines < 0 ? -lines : lines;
2658  if (abslines >= cy) {
2659  abslines = cy;
2660  cy = 0;
2661  } else {
2662  // Best is hardware scroll
2663  #if GDISP_HARDWARE_SCROLL
2664  #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT
2665  if (gvmt(g)->vscroll)
2666  #endif
2667  {
2668  g->p.x = x;
2669  g->p.y = y;
2670  g->p.cx = cx;
2671  g->p.cy = cy;
2672  g->p.y1 = lines;
2673  g->p.color = bgcolor;
2674  gdisp_lld_vertical_scroll(g);
2675  cy -= abslines;
2676  }
2677  #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT
2678  else
2679  #endif
2680  #elif GDISP_LINEBUF_SIZE == 0
2681  #error "GDISP: GDISP_NEED_SCROLL is set but there is no hardware support and GDISP_LINEBUF_SIZE is zero."
2682  #endif
2683 
2684  // Scroll Emulation
2685  #if GDISP_HARDWARE_SCROLL != TRUE
2686  {
2687  cy -= abslines;
2688  if (lines < 0) {
2689  fy = y+cy-1;
2690  dy = -1;
2691  } else {
2692  fy = y;
2693  dy = 1;
2694  }
2695  // Move the screen - one line at a time
2696  for(i = 0; i < cy; i++, fy += dy) {
2697 
2698  // Handle where the buffer is smaller than a line
2699  for(ix=0; ix < cx; ix += GDISP_LINEBUF_SIZE) {
2700 
2701  // Calculate the data we can move in one operation
2702  fx = cx - ix;
2703  if (fx > GDISP_LINEBUF_SIZE)
2704  fx = GDISP_LINEBUF_SIZE;
2705 
2706  // Read one line of data from the screen
2707 
2708  // Best line read is hardware streaming
2709  #if GDISP_HARDWARE_STREAM_READ
2710  #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT
2711  if (gvmt(g)->readstart)
2712  #endif
2713  {
2714  g->p.x = x+ix;
2715  g->p.y = fy+lines;
2716  g->p.cx = fx;
2717  g->p.cy = 1;
2718  gdisp_lld_read_start(g);
2719  for(j=0; j < fx; j++)
2720  g->linebuf[j] = gdisp_lld_read_color(g);
2721  gdisp_lld_read_stop(g);
2722  }
2723  #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT
2724  else
2725  #endif
2726  #endif
2727 
2728  // Next best line read is single pixel reads
2729  #if GDISP_HARDWARE_STREAM_READ != TRUE && GDISP_HARDWARE_PIXELREAD
2730  #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT
2731  if (gvmt(g)->get)
2732  #endif
2733  {
2734  for(j=0; j < fx; j++) {
2735  g->p.x = x+ix+j;
2736  g->p.y = fy+lines;
2737  g->linebuf[j] = gdisp_lld_get_pixel_color(g);
2738  }
2739  }
2740  #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT
2741  else {
2742  // Worst is "not possible"
2743  MUTEX_EXIT(g);
2744  return;
2745  }
2746  #endif
2747  #endif
2748 
2749  // Worst is "not possible"
2750  #if !GDISP_HARDWARE_STREAM_READ && !GDISP_HARDWARE_PIXELREAD
2751  #error "GDISP: GDISP_NEED_SCROLL is set but there is no hardware support for scrolling or reading pixels."
2752  #endif
2753 
2754  // Write that line to the new location
2755 
2756  // Best line write is hardware bitfills
2757  #if GDISP_HARDWARE_BITFILLS
2758  #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT
2759  if (gvmt(g)->blit)
2760  #endif
2761  {
2762  g->p.x = x+ix;
2763  g->p.y = fy;
2764  g->p.cx = fx;
2765  g->p.cy = 1;
2766  g->p.x1 = 0;
2767  g->p.y1 = 0;
2768  g->p.x2 = fx;
2769  g->p.ptr = (void *)g->linebuf;
2770  gdisp_lld_blit_area(g);
2771  }
2772  #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT
2773  else
2774  #endif
2775  #endif
2776 
2777  // Next best line write is hardware streaming
2778  #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE
2779  #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
2780  if (gvmt(g)->writestart)
2781  #endif
2782  {
2783  g->p.x = x+ix;
2784  g->p.y = fy;
2785  g->p.cx = fx;
2786  g->p.cy = 1;
2787  gdisp_lld_write_start(g);
2788  #if GDISP_HARDWARE_STREAM_POS
2789  gdisp_lld_write_pos(g);
2790  #endif
2791  for(j = 0; j < fx; j++) {
2792  g->p.color = g->linebuf[j];
2793  gdisp_lld_write_color(g);
2794  }
2795  gdisp_lld_write_stop(g);
2796  }
2797  #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
2798  else
2799  #endif
2800  #endif
2801 
2802  // Next best line write is drawing pixels in combination with filling
2803  #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS && GDISP_HARDWARE_DRAWPIXEL
2804  // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming.
2805  #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
2806  if (gvmt(g)->fill)
2807  #endif
2808  {
2809  g->p.y = fy;
2810  g->p.cy = 1;
2811  g->p.x = x+ix;
2812  g->p.cx = 1;
2813  for(j = 0; j < fx; ) {
2814  g->p.color = g->linebuf[j];
2815  if (j + g->p.cx < fx && g->linebuf[j] == g->linebuf[j + g->p.cx])
2816  g->p.cx++;
2817  else if (g->p.cx == 1) {
2818  gdisp_lld_draw_pixel(g);
2819  j++;
2820  g->p.x++;
2821  } else {
2822  gdisp_lld_fill_area(g);
2823  j += g->p.cx;
2824  g->p.x += g->p.cx;
2825  g->p.cx = 1;
2826  }
2827  }
2828  }
2829  #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
2830  else
2831  #endif
2832  #endif
2833 
2834  // Worst line write is drawing pixels
2835  #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL
2836  // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
2837  //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
2838  // if (gvmt(g)->pixel)
2839  //#endif
2840  {
2841  g->p.y = fy;
2842  for(g->p.x = x+ix, j = 0; j < fx; g->p.x++, j++) {
2843  g->p.color = g->linebuf[j];
2844  gdisp_lld_draw_pixel(g);
2845  }
2846  }
2847  #endif
2848  }
2849  }
2850  }
2851  #endif
2852  }
2853 
2854  /* fill the remaining gap */
2855  g->p.x = x;
2856  g->p.y = lines > 0 ? (y+cy) : y;
2857  g->p.cx = cx;
2858  g->p.cy = abslines;
2859  g->p.color = bgcolor;
2860  fillarea(g);
2861  autoflush_stopdone(g);
2862  MUTEX_EXIT(g);
2863  }
2864 #endif
2865 
2866 #if GDISP_NEED_CONTROL
2867  #if GDISP_HARDWARE_CONTROL
2868  void gdispGControl(GDisplay *g, unsigned what, void *value) {
2869  #if GDISP_HARDWARE_CONTROL == HARDWARE_AUTODETECT
2870  if (!gvmt(g)->control)
2871  return;
2872  #endif
2873  MUTEX_ENTER(g);
2874  g->p.x = what;
2875  g->p.ptr = value;
2876  if (what == GDISP_CONTROL_ORIENTATION) {
2877  switch ((orientation_t) value) {
2879  g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_0 : (void *)GDISP_ROTATE_90;
2880  break;
2881  case GDISP_ROTATE_PORTRAIT:
2882  g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_90 : (void *)GDISP_ROTATE_0;
2883  break;
2884  default:
2885  break;
2886  }
2887  }
2888  gdisp_lld_control(g);
2889  #if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION
2890  if (what == GDISP_CONTROL_ORIENTATION) {
2891  // Best is hardware clipping
2892  #if GDISP_HARDWARE_CLIP
2893  #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
2894  if (gvmt(g)->setclip)
2895  #endif
2896  {
2897  g->p.x = 0;
2898  g->p.y = 0;
2899  g->p.cx = g->g.Width;
2900  g->p.cy = g->g.Height;
2901  gdisp_lld_set_clip(g);
2902  }
2903  #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
2904  else
2905  #endif
2906  #endif
2907 
2908  // Worst is software clipping
2909  #if GDISP_HARDWARE_CLIP != TRUE
2910  {
2911  g->clipx0 = 0;
2912  g->clipy0 = 0;
2913  g->clipx1 = g->g.Width;
2914  g->clipy1 = g->g.Height;
2915  }
2916  #endif
2917  }
2918  #endif
2919  MUTEX_EXIT(g);
2920  }
2921  #else
2922  void gdispGControl(GDisplay *g, unsigned what, void *value) {
2923  (void)g;
2924  (void)what;
2925  (void)value;
2926  /* Ignore everything */
2927  }
2928  #endif
2929 #endif
2930 
2931 #if GDISP_NEED_QUERY
2932  #if GDISP_HARDWARE_QUERY
2933  void *gdispGQuery(GDisplay *g, unsigned what) {
2934  void *res;
2935 
2936  #if GDISP_HARDWARE_QUERY == HARDWARE_AUTODETECT
2937  if (!gvmt(g)->query)
2938  return -1;
2939  #endif
2940  MUTEX_ENTER(g);
2941  g->p.x = (coord_t)what;
2942  res = gdisp_lld_query(g);
2943  MUTEX_EXIT(g);
2944  return res;
2945  }
2946  #else
2947  void *gdispGQuery(GDisplay *g, unsigned what) {
2948  (void) what;
2949  return (void *)-1;
2950  }
2951  #endif
2952 #endif
2953 
2954 /*===========================================================================*/
2955 /* High Level Driver Routines. */
2956 /*===========================================================================*/
2957 
2958 void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) {
2959  if (cx <= 0 || cy <= 0) return;
2960  cx = x+cx-1; cy = y+cy-1; // cx, cy are now the end point.
2961 
2962  MUTEX_ENTER(g);
2963 
2964  g->p.color = color;
2965 
2966  if (cx - x > 2) {
2967  g->p.x = x; g->p.y = y; g->p.x1 = cx; hline_clip(g);
2968  if (y != cy) {
2969  g->p.x = x; g->p.y = cy; g->p.x1 = cx; hline_clip(g);
2970  if (cy - y > 2) {
2971  y++; cy--;
2972  g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g);
2973  g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g);
2974  }
2975  }
2976  } else {
2977  g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g);
2978  if (x != cx) {
2979  g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g);
2980  }
2981  }
2982 
2983  autoflush(g);
2984  MUTEX_EXIT(g);
2985 }
2986 
2987 #if GDISP_NEED_CONVEX_POLYGON
2988  void gdispGDrawPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) {
2989  const point *epnt, *p;
2990 
2991  epnt = &pntarray[cnt-1];
2992 
2993  MUTEX_ENTER(g);
2994  g->p.color = color;
2995  for(p = pntarray; p < epnt; p++) {
2996  g->p.x=tx+p->x; g->p.y=ty+p->y; g->p.x1=tx+p[1].x; g->p.y1=ty+p[1].y; line_clip(g);
2997  }
2998  g->p.x=tx+p->x; g->p.y=ty+p->y; g->p.x1=tx+pntarray->x; g->p.y1=ty+pntarray->y; line_clip(g);
2999 
3000  autoflush(g);
3001  MUTEX_EXIT(g);
3002  }
3003 
3004  void gdispGFillConvexPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) {
3005  const point *lpnt, *rpnt, *epnts;
3006  fixed lx, rx, lk, rk;
3007  coord_t y, ymax, lxc, rxc;
3008 
3009  epnts = &pntarray[cnt-1];
3010 
3011  /* Find a top point */
3012  rpnt = pntarray;
3013  for(lpnt=pntarray+1; lpnt <= epnts; lpnt++) {
3014  if (lpnt->y < rpnt->y)
3015  rpnt = lpnt;
3016  }
3017  lx = rx = FIXED(rpnt->x);
3018  y = rpnt->y;
3019 
3020  /* Work out the slopes of the two attached line segs */
3021  for (lpnt = rpnt <= pntarray ? epnts : rpnt-1; lpnt->y == y; cnt--) {
3022  if (!cnt) return;
3023  lx = FIXED(lpnt->x);
3024  lpnt = lpnt <= pntarray ? epnts : lpnt-1;
3025  }
3026  for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) {
3027  if (!cnt) return;
3028  rx = FIXED(rpnt->x);
3029  rpnt = rpnt >= epnts ? pntarray : rpnt+1;
3030  }
3031  lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y);
3032  rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y);
3033 
3034  // Add error correction for rounding
3035  lx += FIXED0_5;
3036  rx += FIXED0_5;
3037 
3038  // Do all the line segments
3039  MUTEX_ENTER(g);
3040  g->p.color = color;
3041  while(1) {
3042  /* Determine our boundary */
3043  ymax = rpnt->y < lpnt->y ? rpnt->y : lpnt->y;
3044 
3045  /* Scan down the line segments until we hit a boundary */
3046  for(; y < ymax; y++) {
3047  lxc = NONFIXED(lx);
3048  rxc = NONFIXED(rx);
3049  /*
3050  * Doesn't print the right hand point in order to allow polygon joining.
3051  * Also ensures that we draw from left to right with the minimum number
3052  * of pixels.
3053  */
3054  if (lxc < rxc) {
3055  g->p.x=tx+lxc; g->p.y=ty+y; g->p.x1=tx+rxc-1; hline_clip(g);
3056  } else if (lxc > rxc) {
3057  g->p.x=tx+rxc; g->p.y=ty+y; g->p.x1=tx+lxc-1; hline_clip(g);
3058  }
3059 
3060  lx += lk;
3061  rx += rk;
3062  }
3063 
3064  if (!cnt) {
3065  autoflush(g);
3066  MUTEX_EXIT(g);
3067  return;
3068  }
3069  cnt--;
3070 
3071  /* Replace the appropriate point */
3072  if (ymax == lpnt->y) {
3073  lx -= FIXED0_5;
3074  for (lpnt = lpnt <= pntarray ? epnts : lpnt-1; lpnt->y == y; cnt--) {
3075  if (!cnt) {
3076  autoflush(g);
3077  MUTEX_EXIT(g);
3078  return;
3079  }
3080  lx = FIXED(lpnt->x);
3081  lpnt = lpnt <= pntarray ? epnts : lpnt-1;
3082  }
3083  lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y);
3084  lx += FIXED0_5;
3085  } else {
3086  rx -= FIXED0_5;
3087  for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) {
3088  if (!cnt) {
3089  autoflush(g);
3090  MUTEX_EXIT(g);
3091  return;
3092  }
3093  rx = FIXED(rpnt->x);
3094  rpnt = rpnt >= epnts ? pntarray : rpnt+1;
3095  }
3096  rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y);
3097  rx += FIXED0_5;
3098  }
3099  }
3100  }
3101 
3102  static int32_t rounding_div(const int32_t n, const int32_t d)
3103  {
3104  if ((n < 0) != (d < 0))
3105  return (n - d/2) / d;
3106  else
3107  return (n + d/2) / d;
3108  }
3109 
3110  /* Find a vector (nx, ny) that is perpendicular to (dx, dy) and has length
3111  * equal to 'norm'. */
3112  static void get_normal_vector(coord_t dx, coord_t dy, coord_t norm, coord_t *nx, coord_t *ny)
3113  {
3114  coord_t absDx, absDy;
3115  int32_t len_n, len, len2;
3116  char maxSteps;
3117 
3118  /* Take the absolute value of dx and dy, multiplied by 2 for precision */
3119  absDx = (dx >= 0 ? dx : -dx) * 2;
3120  absDy = (dy >= 0 ? dy : -dy) * 2;
3121 
3122  /* Compute the quadrate length */
3123  len2 = absDx * absDx + absDy * absDy;
3124 
3125  /* First aproximation : length = |dx| + |dy| */
3126  len = absDx + absDy;
3127 
3128  /* Give a max number of steps, the calculation usually takes 3 or 4 */
3129  for(maxSteps = 8; maxSteps > 0; maxSteps--)
3130  {
3131  /* Use an adapted version of Newton's algorithm to find the correct length
3132  * This calculation converge quadratically towards the correct length
3133  * n(x+1) = (n(x) + len^2 / n(x)) / 2
3134  */
3135  len_n = (len + len2 / len) / 2;
3136 
3137  /* We reach max precision when the last result is equal or greater than the previous one */
3138  if(len_n >= len){
3139  break;
3140  }
3141 
3142  len = len_n;
3143  }
3144 
3145  /* Compute the normal vector using nx = dy * desired length / vector length
3146  * The solution is rounded to the nearest integer
3147  */
3148  *nx = rounding_div(dy * norm * 2, len);
3149  *ny = rounding_div(-dx * norm * 2, len);
3150  return;
3151  }
3152 
3153  void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round) {
3154  coord_t dx, dy, nx = 0, ny = 0;
3155 
3156  /* Compute the direction vector for the line */
3157  dx = x1 - x0;
3158  dy = y1 - y0;
3159 
3160  /* Draw a small dot if the line length is zero. */
3161  if (dx == 0 && dy == 0)
3162  dx += 1;
3163 
3164  /* Compute a normal vector with length 'width'. */
3165  get_normal_vector(dx, dy, width, &nx, &ny);
3166 
3167  /* Handle 1px wide lines gracefully */
3168  if (nx == 0 && ny == 0)
3169  nx = 1;
3170 
3171  /* Offset the x0,y0 by half the width of the line. This way we
3172  * can keep the width of the line accurate even if it is not evenly
3173  * divisible by 2.
3174  */
3175  {
3176  x0 -= rounding_div(nx, 2);
3177  y0 -= rounding_div(ny, 2);
3178  }
3179 
3180  /* Fill in the point array */
3181  if (!round) {
3182  /* We use 4 points for the basic line shape:
3183  *
3184  * pt1 pt2
3185  * (+n) ------------------------------------ (d+n)
3186  * | |
3187  * (0,0) ----------------------------------- (d)
3188  * pt0 pt3
3189  */
3190  point pntarray[4];
3191 
3192  pntarray[0].x = 0;
3193  pntarray[0].y = 0;
3194  pntarray[1].x = nx;
3195  pntarray[1].y = ny;
3196  pntarray[2].x = dx + nx;
3197  pntarray[2].y = dy + ny;
3198  pntarray[3].x = dx;
3199  pntarray[3].y = dy;
3200 
3201  gdispGFillConvexPoly(g, x0, y0, pntarray, 4, color);
3202  } else {
3203  /* We use 4 points for basic shape, plus 4 extra points for ends:
3204  *
3205  * pt3 ------------------ pt4
3206  * / \
3207  * pt2 pt5
3208  * | |
3209  * pt1 pt6
3210  * \ /
3211  * pt0 -------------------pt7
3212  */
3213  point pntarray[8];
3214  coord_t nx2, ny2;
3215 
3216  /* Magic numbers:
3217  * 75/256 = sin(45) / (1 + sqrt(2)) diagonal octagon segments
3218  * 106/256 = 1 / (1 + sqrt(2)) octagon side
3219  * 53/256 = 0.5 / (1 + sqrt(2)) half of octagon side
3220  * 150/256 = 1 - 1 / (1 + sqrt(2)) octagon height minus one side
3221  */
3222 
3223  /* Rotate the normal vector 45 deg counter-clockwise and reduce
3224  * to 1 / (1 + sqrt(2)) length, for forming octagonal ends. */
3225  nx2 = rounding_div((nx * 75 + ny * 75), 256);
3226  ny2 = rounding_div((-nx * 75 + ny * 75), 256);
3227 
3228  /* Offset and extend the line so that the center of the octagon
3229  * is at the specified points. */
3230  x0 += ny * 53 / 256;
3231  y0 -= nx * 53 / 256;
3232  dx -= ny * 106 / 256;
3233  dy += nx * 106 / 256;
3234 
3235  /* Now fill in the points by summing the calculated vectors. */
3236  pntarray[0].x = 0;
3237  pntarray[0].y = 0;
3238  pntarray[1].x = nx2;
3239  pntarray[1].y = ny2;
3240  pntarray[2].x = nx2 + nx * 106/256;
3241  pntarray[2].y = ny2 + ny * 106/256;
3242  pntarray[3].x = nx;
3243  pntarray[3].y = ny;
3244  pntarray[4].x = dx + nx;
3245  pntarray[4].y = dy + ny;
3246  pntarray[5].x = dx + nx - nx2;
3247  pntarray[5].y = dy + ny - ny2;
3248  pntarray[6].x = dx + nx * 150/256 - nx2;
3249  pntarray[6].y = dy + ny * 150/256 - ny2;
3250  pntarray[7].x = dx;
3251  pntarray[7].y = dy;
3252 
3253  gdispGFillConvexPoly(g, x0, y0, pntarray, 8, color);
3254  }
3255  }
3256 #endif
3257 
3258 #if GDISP_NEED_TEXT
3259  #include "mcufont/mcufont.h"
3260 
3261  #if GDISP_NEED_ANTIALIAS && GDISP_HARDWARE_PIXELREAD
3262  static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) {
3263  #define GD ((GDisplay *)state)
3264  if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1)
3265  return;
3266  if (x < GD->t.clipx0) {
3267  count -= GD->t.clipx0 - x;
3268  x = GD->t.clipx0;
3269  }
3270  if (x+count > GD->t.clipx1)
3271  count = GD->t.clipx1 - x;
3272  if (alpha == 255) {
3273  GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color;
3274  hline_clip(GD);
3275  } else {
3276  for (; count; count--, x++) {
3277  GD->p.x = x; GD->p.y = y;
3278  GD->p.color = gdispBlendColor(GD->t.color, gdisp_lld_get_pixel_color(GD), alpha);
3279  drawpixel_clip(GD);
3280  }
3281  }
3282  #undef GD
3283  }
3284  #else
3285  static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) {
3286  #define GD ((GDisplay *)state)
3287  if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1)
3288  return;
3289  if (x < GD->t.clipx0) {
3290  count -= GD->t.clipx0 - x;
3291  x = GD->t.clipx0;
3292  }
3293  if (x+count > GD->t.clipx1)
3294  count = GD->t.clipx1 - x;
3295  if (alpha > 0x80) { // A best approximation when using anti-aliased fonts but we can't actually draw them anti-aliased
3296  GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color;
3297  hline_clip(GD);
3298  }
3299  #undef GD
3300  }
3301  #endif
3302 
3303  #if GDISP_NEED_ANTIALIAS
3304  static void fillcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) {
3305  #define GD ((GDisplay *)state)
3306  if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1)
3307  return;
3308  if (x < GD->t.clipx0) {
3309  count -= GD->t.clipx0 - x;
3310  x = GD->t.clipx0;
3311  }
3312  if (x+count > GD->t.clipx1)
3313  count = GD->t.clipx1 - x;
3314  if (alpha == 255) {
3315  GD->p.color = GD->t.color;
3316  } else {
3317  GD->p.color = gdispBlendColor(GD->t.color, GD->t.bgcolor, alpha);
3318  }
3319  GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1;
3320  hline_clip(GD);
3321  #undef GD
3322  }
3323  #else
3324  #define fillcharline drawcharline
3325  #endif
3326 
3327  /* Callback to render characters. */
3328  static uint8_t drawcharglyph(int16_t x, int16_t y, mf_char ch, void *state) {
3329  #define GD ((GDisplay *)state)
3330  return mf_render_character(GD->t.font, x, y, ch, drawcharline, state);
3331  #undef GD
3332  }
3333 
3334  /* Callback to render characters. */
3335  static uint8_t fillcharglyph(int16_t x, int16_t y, mf_char ch, void *state) {
3336  #define GD ((GDisplay *)state)
3337  return mf_render_character(GD->t.font, x, y, ch, fillcharline, state);
3338  #undef GD
3339  }
3340 
3341  /* Callback to render string boxes with word wrap. */
3342  #if GDISP_NEED_TEXT_WORDWRAP
3343  static bool mf_countline_callback(mf_str line, uint16_t count, void *state) {
3344  (void) line;
3345  (void) count;
3346 
3347  ((coord_t*)state)[0]++;
3348  return TRUE;
3349  }
3350  static bool mf_drawline_callback(mf_str line, uint16_t count, void *state) {
3351  #define GD ((GDisplay *)state)
3352  mf_render_aligned(GD->t.font, GD->t.wrapx, GD->t.wrapy, GD->t.lrj, line, count, drawcharglyph, state);
3353  GD->t.wrapy += GD->t.font->line_height;
3354  #undef GD
3355  return TRUE;
3356  }
3357  static bool mf_fillline_callback(mf_str line, uint16_t count, void *state) {
3358  #define GD ((GDisplay *)state)
3359  mf_render_aligned(GD->t.font, GD->t.wrapx, GD->t.wrapy, GD->t.lrj, line, count, fillcharglyph, state);
3360  GD->t.wrapy += GD->t.font->line_height;
3361  #undef GD
3362  return TRUE;
3363  }
3364  #endif
3365 
3366  void gdispGDrawChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color) {
3367  if (!font)
3368  return;
3369  MUTEX_ENTER(g);
3370  g->t.font = font;
3371  g->t.clipx0 = x;
3372  g->t.clipy0 = y;
3373  g->t.clipx1 = x + mf_character_width(font, c) + font->baseline_x;
3374  g->t.clipy1 = y + font->height;
3375  g->t.color = color;
3376  mf_render_character(font, x, y, c, drawcharline, g);
3377  autoflush(g);
3378  MUTEX_EXIT(g);
3379  }
3380 
3381  void gdispGFillChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color, color_t bgcolor) {
3382  if (!font)
3383  return;
3384  MUTEX_ENTER(g);
3385  g->p.cx = mf_character_width(font, c) + font->baseline_x;
3386  g->p.cy = font->height;
3387  g->t.font = font;
3388  g->t.clipx0 = g->p.x = x;
3389  g->t.clipy0 = g->p.y = y;
3390  g->t.clipx1 = g->p.x+g->p.cx;
3391  g->t.clipy1 = g->p.y+g->p.cy;
3392  g->t.color = color;
3393  g->t.bgcolor = g->p.color = bgcolor;
3394 
3395  TEST_CLIP_AREA(g) {
3396  fillarea(g);
3397  mf_render_character(font, x, y, c, fillcharline, g);
3398  }
3399  autoflush(g);
3400  MUTEX_EXIT(g);
3401  }
3402 
3403  void gdispGDrawString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color) {
3404  if (!font)
3405  return;
3406  MUTEX_ENTER(g);
3407  g->t.font = font;
3408  g->t.clipx0 = x;
3409  g->t.clipy0 = y;
3410  g->t.clipx1 = 32767; //x + mf_get_string_width(font, str, 0, 0) + font->baseline_x;
3411  g->t.clipy1 = y + font->height;
3412  g->t.color = color;
3413 
3414  mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, drawcharglyph, g);
3415  autoflush(g);
3416  MUTEX_EXIT(g);
3417  }
3418 
3419  void gdispGFillString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color, color_t bgcolor) {
3420  if (!font)
3421  return;
3422  MUTEX_ENTER(g);
3423  g->p.cx = mf_get_string_width(font, str, 0, 0) + font->baseline_x;
3424  g->p.cy = font->height;
3425  g->t.font = font;
3426  g->t.clipx0 = g->p.x = x;
3427  g->t.clipy0 = g->p.y = y;
3428  g->t.clipx1 = g->p.x+g->p.cx;
3429  g->t.clipy1 = g->p.y+g->p.cy;
3430  g->t.color = color;
3431  g->t.bgcolor = g->p.color = bgcolor;
3432 
3433  TEST_CLIP_AREA(g) {
3434  fillarea(g);
3435  mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, fillcharglyph, g);
3436  }
3437 
3438  autoflush(g);
3439  MUTEX_EXIT(g);
3440  }
3441 
3442  void gdispGDrawStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, justify_t justify) {
3443  coord_t totalHeight;
3444 
3445  if (!font)
3446  return;
3447  MUTEX_ENTER(g);
3448 
3449  // Apply padding
3450  #if GDISP_NEED_TEXT_BOXPADLR != 0 || GDISP_NEED_TEXT_BOXPADTB != 0
3451  if (!(justify & justifyNoPad)) {
3452  #if GDISP_NEED_TEXT_BOXPADLR != 0
3454  cx -= 2*GDISP_NEED_TEXT_BOXPADLR;
3455  #endif
3456  #if GDISP_NEED_TEXT_BOXPADTB != 0
3458  cy -= 2*GDISP_NEED_TEXT_BOXPADTB;
3459  #endif
3460  }
3461  #endif
3462 
3463  // Save the clipping area
3464  g->t.clipx0 = x;
3465  g->t.clipy0 = y;
3466  g->t.clipx1 = x+cx;
3467  g->t.clipy1 = y+cy;
3468 
3469  // Calculate the total text height
3470  #if GDISP_NEED_TEXT_WORDWRAP
3471  if (!(justify & justifyNoWordWrap)) {
3472  // Count the number of lines
3473  totalHeight = 0;
3474  mf_wordwrap(font, cx, str, mf_countline_callback, &totalHeight);
3475  totalHeight *= font->height;
3476  } else
3477  #endif
3478  totalHeight = font->height;
3479 
3480  // Select the anchor position
3481  switch((justify & JUSTIFYMASK_TOPBOTTOM)) {
3482  case justifyTop:
3483  break;
3484  case justifyBottom:
3485  y += cy - totalHeight;
3486  break;
3487  default: // justifyMiddle
3488  y += (cy+1 - totalHeight)/2;
3489  break;
3490  }
3491  switch((justify & JUSTIFYMASK_LEFTRIGHT)) {
3492  case justifyCenter:
3493  x += (cx + 1) / 2;
3494  break;
3495  case justifyRight:
3496  x += cx;
3497  break;
3498  default: // justifyLeft
3499  break;
3500  }
3501 
3502  /* Render */
3503  g->t.font = font;
3504  g->t.color = color;
3505  #if GDISP_NEED_TEXT_WORDWRAP
3506  if (!(justify & justifyNoWordWrap)) {
3507  g->t.lrj = (justify & JUSTIFYMASK_LEFTRIGHT);
3508  g->t.wrapx = x;
3509  g->t.wrapy = y;
3510 
3511  mf_wordwrap(font, cx, str, mf_drawline_callback, g);
3512  } else
3513  #endif
3514  mf_render_aligned(font, x, y, (justify & JUSTIFYMASK_LEFTRIGHT), str, 0, drawcharglyph, g);
3515 
3516  autoflush(g);
3517  MUTEX_EXIT(g);
3518  }
3519 
3520  void gdispGFillStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, color_t bgcolor, justify_t justify) {
3521  coord_t totalHeight;
3522 
3523  if (!font)
3524  return;
3525  MUTEX_ENTER(g);
3526 
3527  g->p.x = x;
3528  g->p.y = y;
3529  g->p.cx = cx;
3530  g->p.cy = cy;
3531 
3532  TEST_CLIP_AREA(g) {
3533 
3534  // background fill
3535  g->p.color = bgcolor;
3536  fillarea(g);
3537 
3538  // Apply padding
3539  #if GDISP_NEED_TEXT_BOXPADLR != 0 || GDISP_NEED_TEXT_BOXPADTB != 0
3540  if (!(justify & justifyNoPad)) {
3541  #if GDISP_NEED_TEXT_BOXPADLR != 0
3543  cx -= 2*GDISP_NEED_TEXT_BOXPADLR;
3544  #endif
3545  #if GDISP_NEED_TEXT_BOXPADTB != 0
3547  cy -= 2*GDISP_NEED_TEXT_BOXPADTB;
3548  #endif
3549  }
3550  #endif
3551 
3552  // Save the clipping area
3553  g->t.clipx0 = x;
3554  g->t.clipy0 = y;
3555  g->t.clipx1 = x+cx;
3556  g->t.clipy1 = y+cy;
3557 
3558  // Calculate the total text height
3559  #if GDISP_NEED_TEXT_WORDWRAP
3560  if (!(justify & justifyNoWordWrap)) {
3561  // Count the number of lines
3562  totalHeight = 0;
3563  mf_wordwrap(font, cx, str, mf_countline_callback, &totalHeight);
3564  totalHeight *= font->height;
3565  } else
3566  #endif
3567  totalHeight = font->height;
3568 
3569  // Select the anchor position
3570  switch((justify & JUSTIFYMASK_TOPBOTTOM)) {
3571  case justifyTop:
3572  break;
3573  case justifyBottom:
3574  y += cy - totalHeight;
3575  break;
3576  default: // justifyMiddle
3577  y += (cy+1 - totalHeight)/2;
3578  break;
3579  }
3580  switch((justify & JUSTIFYMASK_LEFTRIGHT)) {
3581  case justifyCenter:
3582  x += (cx + 1) / 2;
3583  break;
3584  case justifyRight:
3585  x += cx;
3586  break;
3587  default: // justifyLeft
3588  break;
3589  }
3590 
3591  /* Render */
3592  g->t.font = font;
3593  g->t.color = color;
3594  g->t.bgcolor = bgcolor;
3595  #if GDISP_NEED_TEXT_WORDWRAP
3596  if (!(justify & justifyNoWordWrap)) {
3597  g->t.lrj = (justify & JUSTIFYMASK_LEFTRIGHT);
3598  g->t.wrapx = x;
3599  g->t.wrapy = y;
3600 
3601  mf_wordwrap(font, cx, str, mf_fillline_callback, g);
3602  } else
3603  #endif
3604  mf_render_aligned(font, x, y, (justify & JUSTIFYMASK_LEFTRIGHT), str, 0, fillcharglyph, g);
3605  }
3606 
3607  autoflush(g);
3608  MUTEX_EXIT(g);
3609  }
3610 
3611  coord_t gdispGetFontMetric(font_t font, fontmetric_t metric) {
3612  if (!font)
3613  return 0;
3614  /* No mutex required as we only read static data */
3615  switch(metric) {
3616  case fontHeight: return font->height;
3617  case fontDescendersHeight: return font->height - font->baseline_y;
3618  case fontLineSpacing: return font->line_height;
3619  case fontCharPadding: return 0;
3620  case fontMinWidth: return font->min_x_advance;
3621  case fontMaxWidth: return font->max_x_advance;
3622  case fontBaselineX: return font->baseline_x;
3623  case fontBaselineY: return font->baseline_y;
3624  }
3625  return 0;
3626  }
3627 
3628  coord_t gdispGetCharWidth(char c, font_t font) {
3629  if (!font)
3630  return 0;
3631  /* No mutex required as we only read static data */
3632  return mf_character_width(font, c);
3633  }
3634 
3635  coord_t gdispGetStringWidthCount(const char* str, font_t font, uint16_t count) {
3636  if (!str || !font)
3637  return 0;
3638 
3639  // No mutex required as we only read static data
3640  #if GDISP_NEED_TEXT_KERNING
3641  return mf_get_string_width(font, str, count, TRUE);
3642  #else
3643  return mf_get_string_width(font, str, count, FALSE);
3644  #endif
3645  }
3646 
3647  coord_t gdispGetStringWidth(const char* str, font_t font) {
3648  return gdispGetStringWidthCount(str, font, 0);
3649  }
3650 #endif
3651 
3652 #if GDISP_PIXELFORMAT == GDISP_PIXELFORMAT_RGB888
3653  // Special alpha hacked version.
3654  // Note: this will still work with real RGB888
3655  color_t gdispBlendColor(color_t fg, color_t bg, uint8_t alpha)
3656  {
3657  uint32_t ratio;
3658  uint32_t a1, r1, g1, b1;
3659  uint32_t a2, r2, g2, b2;
3660 
3661  // Ratio - add one to get 1 to 256
3662  ratio = (uint32_t)alpha + 1; // 0 to 1 in 0.8 fixed point
3663 
3664  // Calculate the pre-multiplied values of r, g, b for the fg color
3665  a1 = ALPHA_OF(fg); // 0 to 1 in 0.8 fixed point
3666  r1 = RED_OF(fg) * a1; // 0 to 1 in 0.16 fixed point
3667  g1 = GREEN_OF(fg) * a1; // 0 to 1 in 0.16 fixed point
3668  b1 = BLUE_OF(fg) * a1; // 0 to 1 in 0.16 fixed point
3669 
3670  // Calculate the pre-multiplied values of r, g, b for the bg color
3671  a2 = ALPHA_OF(bg); // 0 to 1 in 0.8 fixed point
3672  r2 = RED_OF(bg) * a2; // 0 to 1 in 0.16 fixed point
3673  g2 = GREEN_OF(bg) * a2; // 0 to 1 in 0.16 fixed point
3674  b2 = BLUE_OF(bg) * a2; // 0 to 1 in 0.16 fixed point
3675 
3676  // Calculate the mixed color values
3677  a1 = ratio * (a1 - a2) + (a2<<8); // 0 to 1 in 0.16 fixed point
3678  if (!a1) return GFXTRANSPARENT;
3679  r1 = ((ratio * (r1 - r2))>>8) + r2; // 0 to 1 in 0.16 fixed point
3680  g1 = ((ratio * (g1 - g2))>>8) + g2; // 0 to 1 in 0.16 fixed point
3681  b1 = ((ratio * (b1 - b2))>>8) + b2; // 0 to 1 in 0.16 fixed point
3682 
3683  // Fix precision
3684  #if 1
3685  // Convert back to un-multiplied values
3686  ratio = 0x80000000 / a1; // Divide 1 (0.31 fixed point) by a1 (0.16 fixed point) to get the a1 reciprocal in 0.15 fixed point
3687  a1 >>= 8; // Shift to get back to 0.8 fixed point
3688  r1 = (r1 * ratio) >> 23; // Multiply by ratio to get 0.31 and then shift to get back to 0.8 fixed point
3689  g1 = (g1 * ratio) >> 23; // Multiply by ratio to get 0.31 and then shift to get back to 0.8 fixed point
3690  b1 = (b1 * ratio) >> 23; // Multiply by ratio to get 0.31 and then shift to get back to 0.8 fixed point
3691  #else
3692  // Leave as pre-multiplied values
3693  a1 >>= 8; // Shift to get back to 0.8 fixed point
3694  r1 >>= 8; // Shift to get back to 0.8 fixed point
3695  g1 >>= 8; // Shift to get back to 0.8 fixed point
3696  b1 >>= 8; // Shift to get back to 0.8 fixed point
3697  #endif
3698 
3699  return ARGB2COLOR(a1, r1, g1, b1);
3700  }
3701 #else
3702  color_t gdispBlendColor(color_t fg, color_t bg, uint8_t alpha)
3703  {
3704  uint16_t fg_ratio = alpha + 1;
3705  uint16_t bg_ratio = 256 - alpha;
3706  uint16_t r, g, b;
3707 
3708  r = RED_OF(fg) * fg_ratio;
3709  g = GREEN_OF(fg) * fg_ratio;
3710  b = BLUE_OF(fg) * fg_ratio;
3711 
3712  r += RED_OF(bg) * bg_ratio;
3713  g += GREEN_OF(bg) * bg_ratio;
3714  b += BLUE_OF(bg) * bg_ratio;
3715 
3716  r >>= 8;
3717  g >>= 8;
3718  b >>= 8;
3719 
3720  return RGB2COLOR(r, g, b);
3721  }
3722 #endif
3723 
3725  uint16_t r, g, b;
3726 
3727  r = RED_OF(color) > 128 ? 0 : 255;
3728  g = GREEN_OF(color) > 128 ? 0 : 255;
3729  b = BLUE_OF(color) > 128 ? 0 : 255;
3730 
3731  return RGB2COLOR(r, g, b);
3732 }
3733 
3734 #if (!defined(gdispPackPixels) && !defined(GDISP_PIXELFORMAT_CUSTOM))
3735  void gdispPackPixels(pixel_t *buf, coord_t cx, coord_t x, coord_t y, color_t color) {
3736  /* No mutex required as we only read static data */
3737  #if defined(GDISP_PIXELFORMAT_RGB888)
3738  #error "GDISP: Packed pixels not supported yet"
3739  #elif defined(GDISP_PIXELFORMAT_RGB444)
3740  #error "GDISP: Packed pixels not supported yet"
3741  #elif defined(GDISP_PIXELFORMAT_RGB666)
3742  #error "GDISP: Packed pixels not supported yet"
3743  #elif
3744  #error "GDISP: Unsupported packed pixel format"
3745  #endif
3746  }
3747 #endif
3748 
3749 #endif /* GFX_USE_GDISP */
void gdispGFillStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char *str, font_t font, color_t color, color_t bgColor, justify_t justify)
Draw a text string vertically centered within the specified box. The box background is filled with th...
void gdispGControl(GDisplay *g, unsigned what, void *value)
Control hardware specific parts of the display. eg powermodes, backlight etc.
const struct mf_font_s * font_t
The type of a font.
Definition: gdisp.h:93
void gdispGDrawEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color)
Draw an ellipse.
color_t gdispBlendColor(color_t fg, color_t bg, uint8_t alpha)
Blend 2 colors according to the alpha.
void gdispGStreamStop(GDisplay *g)
Finish the current streaming operation.
void gdispGClear(GDisplay *g, color_t color)
Clear the display to the specified color.
GDisplay * GDISP
The default screen to use for the gdispXXXX calls.
void gtimerInit(GTimer *pt)
Initialise a timer.
Definition: gtimer.c:129
#define GDISP_NEED_TIMERFLUSH
Should drawing operations be automatically flushed on a timer.
Definition: gdisp_options.h:48
void gdispGFillArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle, color_t color)
Draw a filled arc.
int16_t coord_t
The type for a coordinate or length on the screen.
Definition: gdisp.h:39
#define GDISP_NEED_TEXT_BOXPADTB
Adding pixels to the top and bottom side of the box to pad text.
void gdispGDrawArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t startangle, coord_t endangle, color_t color)
Draw an arc.
A GTimer structure.
Definition: gtimer.h:52
void gdispGSetClip(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy)
Clip all drawing to the defined area.
void gdispGFillConvexPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color)
Fill a convex polygon.
void gdispGFillArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color)
Fill a selection of 45 degree arcs of a circle.
GDriver * gdriverRegister(const GDriverVMT *vmt, void *param)
Register a new driver instance.
Definition: gdriver.c:31
void gdispGStreamColor(GDisplay *g, color_t color)
Send pixel data to the stream.
#define GDISP_NEED_TEXT_BOXPADLR
Adding pixels to the left and right side of the box to pad text.
int32_t fixed
The type for a fixed point type.
Definition: gmisc.h:60
coord_t gdispGGetHeight(GDisplay *g)
Get the display height in pixels.
GDISP Graphic Driver subsystem low level driver header.
color_t gdispGGetPixelColor(GDisplay *g, coord_t x, coord_t y)
Get the color of a pixel.
void gdispGDrawPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color)
Draw an enclosed polygon (convex, non-convex or complex).
#define GFXINLINE
Mark a function as inline.
void gdispGFlush(GDisplay *g)
Flush current drawing operations to the display.
coord_t y
Definition: gdisp.h:53
void gdispGDrawPixel(GDisplay *g, coord_t x, coord_t y, color_t color)
Set a pixel in the specified color.
unsigned gdispGetDisplayCount(void)
Get the count of currently active displays.
coord_t gdispGetFontMetric(font_t font, fontmetric_t metric)
Get a metric of a font.
coord_t gdispGetStringWidth(const char *str, font_t font)
Get the pixel width of an entire string.
#define FALSE
Generic &#39;false&#39; boolean constant.
Definition: gfx.h:31
void * gdispGQuery(GDisplay *g, unsigned what)
Query a property of the display.
coord_t gdispGGetWidth(GDisplay *g)
Get the display width in pixels.
#define GDISP_STARTUP_COLOR
Define the initial background color for all displays in the system.
void gdispGStreamStart(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy)
Start a streaming operation.
justify
Type for the text justification.
Definition: gdisp.h:60
void gdispGFillEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color)
Draw a filled ellipse.
void gdispGDrawCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color)
Draw a circle.
All runtime driver structures start with this structure.
Definition: gdriver.h:58
void gdispGFillChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color, color_t bgcolor)
Draw a text character with a filled background.
void gdispGDrawLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color)
Draw a line.
#define GDISP_LINEBUF_SIZE
Define the default orientation for all displays in the system.
orientation_t gdispGGetOrientation(GDisplay *g)
Get the current display orientation.
void gdispGFillArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color)
Fill an area with a color.
void gdispGDrawArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color)
Draw a selection of 45 degree arcs of a circle.
#define GDISP_DRIVER_LIST
The list of display drivers.
powermode_t gdispGGetPowerMode(GDisplay *g)
Get the current display power mode.
void gdispGFillCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color)
Draw a filled circle.
void gdispGDrawRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color)
Draw a rectangular box with rounded corners.
unsigned gdriverInstanceCount(uint16_t type)
Get the count of instances of a type of device.
Definition: gdriver.c:114
void gdispGDrawStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char *str, font_t font, color_t color, justify_t justify)
Draw a text string vertically centered within the specified box.
void gdispGDrawThickArc(GDisplay *g, coord_t xc, coord_t yc, coord_t startradius, coord_t endradius, coord_t startangle, coord_t endangle, color_t color)
Draw a thick arc.
GDisplay * gdispGetDisplay(unsigned display)
Get the specified display.
void gdispGVerticalScroll(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor)
Scroll vertically a section of the screen.
void gdispGDrawChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color)
Draw a text character.
#define RGB2COLOR(r, g, b)
Convert red, green, blue (each 0 to 255) into a color value.
Definition: gdisp_colors.h:171
#define GDISP_TOTAL_DISPLAYS
The total number of displays using the default driver.
#define FIXED(x)
Macros to convert to and from a fixed point.
Definition: gmisc.h:66
void gtimerStart(GTimer *pt, GTimerFunction fn, void *param, bool_t periodic, delaytime_t millisec)
Set a timer going or alter its properties if it is already going.
Definition: gtimer.c:139
Type for a 2D point on the screen.
Definition: gdisp.h:51
void gdispGDrawString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color)
Draw a text string.
GDriver * gdriverGetNext(uint16_t type, GDriver *driver)
Get the next driver for a type of device.
Definition: gdriver.c:127
uint8_t gdispGGetContrast(GDisplay *g)
Get the current display contrast.
void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color)
Draw a rectangular box.
fixed ffsin(int degrees)
Fast Table Based Trig functions.
void gdispGFillDualCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius1, color_t color1, coord_t radius2, color_t color2)
Draw two filled circles with the same centre.
color_t gdispContrastColor(color_t color)
Find a contrasting color.
COLOR_TYPE color_t
The color type definition.
Definition: gdisp_colors.h:412
uint8_t gdispGGetBacklight(GDisplay *g)
Get the current display backlight brightness.
coord_t gdispGetStringWidthCount(const char *str, font_t font, uint16_t count)
Get the pixel width of a string of a given character length.
void gdispSetDisplay(GDisplay *g)
Set the current default display to the specified display.
void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round)
Draw a line with a specified thickness.
coord_t x
Definition: gdisp.h:52
void gdispGBlitArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer)
Fill an area using the supplied bitmap.
coord_t gdispGetCharWidth(char c, font_t font)
Get the pixel width of a character.
double fsin(int degrees)
Fast Table Based Trig functions.
void gdispGFillString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color, color_t bgcolor)
Draw a text string.
#define TRUE
Generic &#39;true&#39; boolean constant.
Definition: gfx.h:38
void gfxSleepMilliseconds(delaytime_t ms)
Put the current thread to sleep for the specified period in milliseconds.
color_t pixel_t
The pixel format.
Definition: gdisp.h:226
void gdispGFillRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color)
Draw a filled rectangular box with rounded corners.
GDriver * gdriverGetInstance(uint16_t type, unsigned instance)
Get the driver for a particular instance of a type of device.
Definition: gdriver.c:98