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