diff options
Diffstat (limited to 'src/gdisp/gdisp.c')
-rw-r--r-- | src/gdisp/gdisp.c | 3370 |
1 files changed, 3370 insertions, 0 deletions
diff --git a/src/gdisp/gdisp.c b/src/gdisp/gdisp.c new file mode 100644 index 00000000..cebed1ef --- /dev/null +++ b/src/gdisp/gdisp.c @@ -0,0 +1,3370 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +#include "gfx.h" + +#if GFX_USE_GDISP + +/* Include the low level driver information */ +#include "gdisp_driver.h" + +#if 1 + #undef INLINE + #if defined(__KEIL__) || defined(__C51__) + #define INLINE __inline + #else + #define INLINE inline + #endif +#else + #undef INLINE + #define INLINE +#endif + +// Number of milliseconds for the startup logo - 0 means disabled. +#if GDISP_NEED_STARTUP_LOGO + #define GDISP_STARTUP_LOGO_TIMEOUT 1000 +#else + #define GDISP_STARTUP_LOGO_TIMEOUT 0 +#endif + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +#if GDISP_NEED_TIMERFLUSH + static GTimer FlushTimer; +#endif + +GDisplay *GDISP; + +#if GDISP_NEED_MULTITHREAD + #define MUTEX_INIT(g) gfxMutexInit(&(g)->mutex) + #define MUTEX_ENTER(g) gfxMutexEnter(&(g)->mutex) + #define MUTEX_EXIT(g) gfxMutexExit(&(g)->mutex) + #define MUTEX_DEINIT(g) gfxMutexDestroy(&(g)->mutex) +#else + #define MUTEX_INIT(g) + #define MUTEX_ENTER(g) + #define MUTEX_EXIT(g) + #define MUTEX_DEINIT(g) +#endif + +#define NEED_CLIPPING (GDISP_HARDWARE_CLIP != TRUE && (GDISP_NEED_VALIDATION || GDISP_NEED_CLIP)) + +#if !NEED_CLIPPING + #define TEST_CLIP_AREA(g) +#elif GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + #define TEST_CLIP_AREA(g) \ + if (!gvmt(g)->setclip) { \ + if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \ + if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \ + if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \ + if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \ + } \ + if ((g)->p.cx > 0 && (g)->p.cy > 0) +#else + #define TEST_CLIP_AREA(g) \ + if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \ + if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \ + if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \ + if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \ + if ((g)->p.cx > 0 && (g)->p.cy > 0) +#endif + +/*==========================================================================*/ +/* Internal functions. */ +/*==========================================================================*/ + +#if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + static INLINE void setglobalwindow(GDisplay *g) { + coord_t x, y; + x = g->p.x; y = g->p.y; + g->p.x = g->p.y = 0; + g->p.cx = g->g.Width; g->p.cy = g->g.Height; + gdisp_lld_write_start(g); + g->p.x = x; g->p.y = y; + g->flags |= GDISP_FLG_SCRSTREAM; + } +#endif + +#if GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT + #define autoflush_stopdone(g) if (gvmt(g)->flush) gdisp_lld_flush(g) +#elif GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH + #define autoflush_stopdone(g) gdisp_lld_flush(g) +#else + #define autoflush_stopdone(g) +#endif + +#if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + #define autoflush(g) \ + { \ + if ((g->flags & GDISP_FLG_SCRSTREAM)) { \ + gdisp_lld_write_stop(g); \ + g->flags &= ~GDISP_FLG_SCRSTREAM; \ + } \ + autoflush_stopdone(g); \ + } +#else + #define autoflush(g) autoflush_stopdone(g) +#endif + +// drawpixel(g) +// Parameters: x,y +// Alters: cx, cy (if using streaming) +// Does not clip +static INLINE void drawpixel(GDisplay *g) { + + // Best is hardware accelerated pixel draw + #if GDISP_HARDWARE_DRAWPIXEL + #if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + if (gvmt(g)->pixel) + #endif + { + gdisp_lld_draw_pixel(g); + return; + } + #endif + + // Next best is cursor based streaming + #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + { + if (!(g->flags & GDISP_FLG_SCRSTREAM)) + setglobalwindow(g); + gdisp_lld_write_pos(g); + gdisp_lld_write_color(g); + return; + } + #endif + + // Worst is general streaming + #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE + // The following test is unneeded because we are guaranteed to have streaming if we don't have drawpixel + //#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + // if (gvmt(g)->writestart) + //#endif + { + g->p.cx = g->p.cy = 1; + gdisp_lld_write_start(g); + gdisp_lld_write_color(g); + gdisp_lld_write_stop(g); + return; + } + #endif +} + +// drawpixel_clip(g) +// Parameters: x,y +// Alters: cx, cy (if using streaming) +#if NEED_CLIPPING + static INLINE void drawpixel_clip(GDisplay *g) { + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + { + if (g->p.x < g->clipx0 || g->p.x >= g->clipx1 || g->p.y < g->clipy0 || g->p.y >= g->clipy1) + return; + } + drawpixel(g); + } +#else + #define drawpixel_clip(g) drawpixel(g) +#endif + +// fillarea(g) +// Parameters: x,y cx,cy and color +// Alters: nothing +// Note: This is not clipped +// Resets the streaming area if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set. +static INLINE void fillarea(GDisplay *g) { + + // Best is hardware accelerated area fill + #if GDISP_HARDWARE_FILLS + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + gdisp_lld_fill_area(g); + return; + } + #endif + + // Next best is hardware streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + uint32_t area; + + #if GDISP_HARDWARE_STREAM_POS + if ((g->flags & GDISP_FLG_SCRSTREAM)) { + gdisp_lld_write_stop(g); + g->flags &= ~GDISP_FLG_SCRSTREAM; + } + #endif + + area = (uint32_t)g->p.cx * g->p.cy; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + for(; area; area--) + gdisp_lld_write_color(g); + gdisp_lld_write_stop(g); + return; + } + #endif + + // Worst is pixel drawing + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + coord_t x0, y0, x1, y1; + + x0 = g->p.x; + y0 = g->p.y; + x1 = g->p.x + g->p.cx; + y1 = g->p.y + g->p.cy; + for(; g->p.y < y1; g->p.y++, g->p.x = x0) + for(; g->p.x < x1; g->p.x++) + gdisp_lld_draw_pixel(g); + g->p.y = y0; + return; + } + #endif +} + +// Parameters: x,y and x1 +// Alters: x,y x1,y1 cx,cy +// Assumes the window covers the screen and a write_stop() will occur later +// if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set. +static void hline_clip(GDisplay *g) { + // Swap the points if necessary so it always goes from x to x1 + if (g->p.x1 < g->p.x) { + g->p.cx = g->p.x; g->p.x = g->p.x1; g->p.x1 = g->p.cx; + } + + // Clipping + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + { + if (g->p.y < g->clipy0 || g->p.y >= g->clipy1) return; + if (g->p.x < g->clipx0) g->p.x = g->clipx0; + if (g->p.x1 >= g->clipx1) g->p.x1 = g->clipx1 - 1; + if (g->p.x1 < g->p.x) return; + } + #endif + + // This is an optimization for the point case. It is only worthwhile however if we + // have hardware fills or if we support both hardware pixel drawing and hardware streaming + #if GDISP_HARDWARE_FILLS || (GDISP_HARDWARE_DRAWPIXEL && GDISP_HARDWARE_STREAM_WRITE) + // Is this a point + if (g->p.x == g->p.x1) { + drawpixel(g); + return; + } + #endif + + // Best is hardware accelerated area fill + #if GDISP_HARDWARE_FILLS + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + g->p.cx = g->p.x1 - g->p.x + 1; + g->p.cy = 1; + gdisp_lld_fill_area(g); + return; + } + #endif + + // Next best is cursor based streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + { + if (!(g->flags & GDISP_FLG_SCRSTREAM)) + setglobalwindow(g); + g->p.cx = g->p.x1 - g->p.x + 1; + gdisp_lld_write_pos(g); + do { gdisp_lld_write_color(g); } while(--g->p.cx); + return; + } + #endif + + // Next best is streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + g->p.cx = g->p.x1 - g->p.x + 1; + g->p.cy = 1; + gdisp_lld_write_start(g); + do { gdisp_lld_write_color(g); } while(--g->p.cx); + gdisp_lld_write_stop(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + for(; g->p.x <= g->p.x1; g->p.x++) + gdisp_lld_draw_pixel(g); + return; + } + #endif +} + +// Parameters: x,y and y1 +// Alters: x,y x1,y1 cx,cy +static void vline_clip(GDisplay *g) { + // Swap the points if necessary so it always goes from y to y1 + if (g->p.y1 < g->p.y) { + g->p.cy = g->p.y; g->p.y = g->p.y1; g->p.y1 = g->p.cy; + } + + // Clipping + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + { + if (g->p.x < g->clipx0 || g->p.x >= g->clipx1) return; + if (g->p.y < g->clipy0) g->p.y = g->clipy0; + if (g->p.y1 >= g->clipy1) g->p.y1 = g->clipy1 - 1; + if (g->p.y1 < g->p.y) return; + } + #endif + + // This is an optimization for the point case. It is only worthwhile however if we + // have hardware fills or if we support both hardware pixel drawing and hardware streaming + #if GDISP_HARDWARE_FILLS || (GDISP_HARDWARE_DRAWPIXEL && GDISP_HARDWARE_STREAM_WRITE) || (GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE) + // Is this a point + if (g->p.y == g->p.y1) { + drawpixel(g); + return; + } + #endif + + // Best is hardware accelerated area fill + #if GDISP_HARDWARE_FILLS + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + g->p.cy = g->p.y1 - g->p.y + 1; + g->p.cx = 1; + gdisp_lld_fill_area(g); + return; + } + #endif + + // Next best is streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + #if GDISP_HARDWARE_STREAM_POS + if ((g->flags & GDISP_FLG_SCRSTREAM)) { + gdisp_lld_write_stop(g); + g->flags &= ~GDISP_FLG_SCRSTREAM; + } + #endif + g->p.cy = g->p.y1 - g->p.y + 1; + g->p.cx = 1; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + do { gdisp_lld_write_color(g); } while(--g->p.cy); + gdisp_lld_write_stop(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + for(; g->p.y <= g->p.y1; g->p.y++) + gdisp_lld_draw_pixel(g); + return; + } + #endif +} + +// Parameters: x,y and x1,y1 +// Alters: x,y x1,y1 cx,cy +static void line_clip(GDisplay *g) { + int16_t dy, dx; + int16_t addx, addy; + int16_t P, diff, i; + + // Is this a horizontal line (or a point) + if (g->p.y == g->p.y1) { + hline_clip(g); + return; + } + + // Is this a vertical line (or a point) + if (g->p.x == g->p.x1) { + vline_clip(g); + return; + } + + // Not horizontal or vertical + + // Use Bresenham's line drawing algorithm. + // This should be replaced with fixed point slope based line drawing + // which is more efficient on modern processors as it branches less. + // When clipping is needed, all the clipping could also be done up front + // instead of on each pixel. + + if (g->p.x1 >= g->p.x) { + dx = g->p.x1 - g->p.x; + addx = 1; + } else { + dx = g->p.x - g->p.x1; + addx = -1; + } + if (g->p.y1 >= g->p.y) { + dy = g->p.y1 - g->p.y; + addy = 1; + } else { + dy = g->p.y - g->p.y1; + addy = -1; + } + + if (dx >= dy) { + dy <<= 1; + P = dy - dx; + diff = P - dx; + + for(i=0; i<=dx; ++i) { + drawpixel_clip(g); + if (P < 0) { + P += dy; + g->p.x += addx; + } else { + P += diff; + g->p.x += addx; + g->p.y += addy; + } + } + } else { + dx <<= 1; + P = dx - dy; + diff = P - dy; + + for(i=0; i<=dy; ++i) { + drawpixel_clip(g); + if (P < 0) { + P += dx; + g->p.y += addy; + } else { + P += diff; + g->p.x += addx; + g->p.y += addy; + } + } + } +} + +#if GDISP_STARTUP_LOGO_TIMEOUT > 0 + static bool_t initDone; + static void StartupLogoDisplay(GDisplay *g) { + coord_t x, y, w; + const coord_t * p; + static const coord_t blks[] = { + // u + 2, 6, 1, 10, + 3, 11, 4, 1, + 6, 6, 1, 6, + // G + 8, 0, 1, 12, + 9, 0, 6, 1, + 9, 11, 6, 1, + 14, 6, 1, 5, + 12, 6, 2, 1, + // F + 16, 0, 1, 12, + 17, 0, 6, 1, + 17, 6, 3, 1, + // X + 22, 6, 7, 1, + 24, 0, 1, 6, + 22, 7, 1, 5, + 28, 0, 1, 6, + 26, 7, 1, 5, + }; + + // Get a starting position and a scale + // Work on a 8x16 grid for each char, 4 chars (uGFX) in 1 line, using half the screen + w = g->g.Width/(8*4*2); + if (!w) w = 1; + x = (g->g.Width - (8*4)*w)/2; + y = (g->g.Height - (16*1)*w)/2; + + // Simple but crude! + for(p = blks; p < blks+sizeof(blks)/sizeof(blks[0]); p+=4) + gdispGFillArea(g, x+p[0]*w, y+p[1]*w, p[2]*w, p[3]*w, Blue); + } +#endif + +#if GDISP_NEED_TIMERFLUSH + static void FlushTimerFn(void *param) { + GDisplay * g; + (void) param; + + for(g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, 0); g; g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g)) + gdispGFlush(g); + } +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +typedef const GDISPVMT const GDISPVMTLIST[]; + +void _gdispInit(void) +{ + // GDISP_DRIVER_LIST is defined - create each driver instance + #if defined(GDISP_DRIVER_LIST) + { + unsigned i; + + extern GDISPVMTLIST GDISP_DRIVER_LIST; + static GDISPVMTLIST dclist[] = {GDISP_DRIVER_LIST}; + + for(i = 0; i < sizeof(dclist)/sizeof(dclist[0]); i++) + if (!(dclist[i]->d.flags & GDISP_VFLG_DYNAMICONLY)) + gdriverRegister(&dclist[i]->d, 0); + } + #elif GDISP_TOTAL_DISPLAYS > 1 + { + unsigned i; + extern GDISPVMTLIST GDISPVMT_OnlyOne; + + if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY)) { + for(i = 0; i < GDISP_TOTAL_DISPLAYS; i++) + gdriverRegister(&GDISPVMT_OnlyOne->d, 0); + } + } + #else + { + extern GDISPVMTLIST GDISPVMT_OnlyOne; + + if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY)) + gdriverRegister(&GDISPVMT_OnlyOne->d, 0); + } + #endif + + // Re-clear the display after the timeout if we added the logo + #if GDISP_STARTUP_LOGO_TIMEOUT > 0 + { + GDisplay *g; + + gfxSleepMilliseconds(GDISP_STARTUP_LOGO_TIMEOUT); + + for(g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, 0); g; g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g)) { + gdispGClear(g, GDISP_STARTUP_COLOR); + #if GDISP_HARDWARE_FLUSH + gdispGFlush(g); + #endif + } + + initDone = TRUE; + } + #endif + + // Start the automatic timer flush (if required) + #if GDISP_NEED_TIMERFLUSH + gtimerInit(&FlushTimer); + gtimerStart(&FlushTimer, FlushTimerFn, 0, TRUE, GDISP_NEED_TIMERFLUSH); + #endif +} + +void _gdispDeinit(void) +{ + /* ToDo */ +} + +bool_t _gdispInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance) { + #define gd ((GDisplay *)g) + bool_t ret; + + // Intialise fields + gd->systemdisplay = systeminstance; + gd->controllerdisplay = driverinstance; + gd->flags = 0; + gd->priv = param; + MUTEX_INIT(gd); + + // Call the driver init + MUTEX_ENTER(gd); + ret = gdisp_lld_init(gd); + MUTEX_EXIT(gd); + return ret; + + #undef gd +} + +void _gdispPostInitDriver(GDriver *g) { + #define gd ((GDisplay *)g) + + // Set orientation, clip + #if defined(GDISP_DEFAULT_ORIENTATION) && GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL + #if GDISP_NEED_PIXMAP + // Pixmaps should stay in their created orientation (at least initially) + if (!(gvmt(gd)->d.flags & GDISP_VFLG_PIXMAP)) + #endif + gdispGControl(gd, GDISP_CONTROL_ORIENTATION, (void *)GDISP_DEFAULT_ORIENTATION); + #endif + #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP + gdispGSetClip(gd, 0, 0, gd->g.Width, gd->g.Height); + #endif + + // Clear the Screen + gdispGClear(gd, GDISP_STARTUP_COLOR); + + // Display the startup logo if this is a static initialised display + #if GDISP_STARTUP_LOGO_TIMEOUT > 0 + if (!initDone) + StartupLogoDisplay(gd); + #endif + + // Flush + #if GDISP_HARDWARE_FLUSH + gdispGFlush(gd); + #endif + + // If this is the first driver set GDISP + if (!GDISP) + GDISP = gd; + + #undef gd +} + +void _gdispDeInitDriver(GDriver *g) { + #define gd ((GDisplay *)g) + + if (GDISP == gd) + GDISP = (GDisplay *)gdriverGetInstance(GDRIVER_TYPE_DISPLAY, 0); + + #if GDISP_HARDWARE_DEINIT + #if GDISP_HARDWARE_DEINIT == HARDWARE_AUTODETECT + if (gvmt(gd)->deinit) + #endif + { + MUTEX_ENTER(gd); + gdisp_lld_deinit(gd); + MUTEX_EXIT(gd); + } + #endif + MUTEX_DEINIT(gd); + + #undef gd +} + +GDisplay *gdispGetDisplay(unsigned display) { + return (GDisplay *)gdriverGetInstance(GDRIVER_TYPE_DISPLAY, display); +} + +void gdispSetDisplay(GDisplay *g) { + if (g) GDISP = g; +} + +unsigned gdispGetDisplayCount(void) { + return gdriverInstanceCount(GDRIVER_TYPE_DISPLAY); +} + +coord_t gdispGGetWidth(GDisplay *g) { return g->g.Width; } +coord_t gdispGGetHeight(GDisplay *g) { return g->g.Height; } +powermode_t gdispGGetPowerMode(GDisplay *g) { return g->g.Powermode; } +orientation_t gdispGGetOrientation(GDisplay *g) { return g->g.Orientation; } +uint8_t gdispGGetBacklight(GDisplay *g) { return g->g.Backlight; } +uint8_t gdispGGetContrast(GDisplay *g) { return g->g.Contrast; } + +void gdispGFlush(GDisplay *g) { + #if GDISP_HARDWARE_FLUSH + #if GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT + if (gvmt(g)->flush) + #endif + { + MUTEX_ENTER(g); + gdisp_lld_flush(g); + MUTEX_EXIT(g); + } + #else + (void) g; + #endif +} + +#if GDISP_NEED_STREAMING + void gdispGStreamStart(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) { + MUTEX_ENTER(g); + + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + // Test if the area is valid - if not then exit + if (x < g->clipx0 || x+cx > g->clipx1 || y < g->clipy0 || y+cy > g->clipy1) { + MUTEX_EXIT(g); + return; + } + #endif + + g->flags |= GDISP_FLG_INSTREAM; + + // Best is hardware streaming + #if GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + return; + } + #endif + + // Worst - save the parameters and use pixel drawing and/or area fills + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + // Use x,y as the current position, x1,y1 as the save position and x2,y2 as the end position, cx = bufpos + g->p.x1 = g->p.x = x; + g->p.y1 = g->p.y = y; + g->p.x2 = x + cx; + g->p.y2 = y + cy; + #if (GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS) || GDISP_HARDWARE_FILLS + g->p.cx = 0; + g->p.cy = 1; + #endif + return; + } + #endif + + // Don't release the mutex as gdispStreamEnd() will do that. + } + + void gdispGStreamColor(GDisplay *g, color_t color) { + #if !GDISP_HARDWARE_STREAM_WRITE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS + coord_t sx1, sy1; + #endif + + // Don't touch the mutex as we should already own it + + // Ignore this call if we are not streaming + if (!(g->flags & GDISP_FLG_INSTREAM)) + return; + + // Best is hardware streaming + #if GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + g->p.color = color; + gdisp_lld_write_color(g); + return; + } + #endif + + // Next best is to use bitfills with our line buffer + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + if (gvmt(g)->blit) + #endif + { + g->linebuf[g->p.cx++] = color; + if (g->p.cx >= GDISP_LINEBUF_SIZE) { + sx1 = g->p.x1; + sy1 = g->p.y1; + g->p.x1 = 0; + g->p.y1 = 0; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + g->p.x1 = sx1; + g->p.y1 = sy1; + g->p.x += g->p.cx; + g->p.cx = 0; + } + + // Just wrap at end-of-line and end-of-buffer + if (g->p.x+g->p.cx >= g->p.x2) { + if (g->p.cx) { + sx1 = g->p.x1; + sy1 = g->p.y1; + g->p.x1 = 0; + g->p.y1 = 0; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + g->p.x1 = sx1; + g->p.y1 = sy1; + g->p.cx = 0; + } + g->p.x = g->p.x1; + if (++g->p.y >= g->p.y2) + g->p.y = g->p.y1; + } + } + #endif + + // Only slightly better than drawing pixels is to look for runs and use fillarea + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + if (!g->p.cx || g->p.color == color) { + g->p.cx++; + g->p.color = color; + } else { + if (g->p.cx == 1) + gdisp_lld_draw_pixel(g); + else + gdisp_lld_fill_area(g); + g->p.x += g->p.cx; + g->p.color = color; + g->p.cx = 1; + } + // Just wrap at end-of-line and end-of-buffer + if (g->p.x+g->p.cx >= g->p.x2) { + if (g->p.cx) { + if (g->p.cx == 1) + gdisp_lld_draw_pixel(g); + else + gdisp_lld_fill_area(g); + g->p.cx = 0; + } + g->p.x = g->p.x1; + if (++g->p.y >= g->p.y2) + g->p.y = g->p.y1; + } + return; + } + #endif + + // Worst is using pixel drawing + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + g->p.color = color; + gdisp_lld_draw_pixel(g); + + // Just wrap at end-of-line and end-of-buffer + if (++g->p.x >= g->p.x2) { + g->p.x = g->p.x1; + if (++g->p.y >= g->p.y2) + g->p.y = g->p.y1; + } + return; + } + #endif + } + + void gdispGStreamStop(GDisplay *g) { + // Only release the mutex and end the stream if we are actually streaming. + if (!(g->flags & GDISP_FLG_INSTREAM)) + return; + + // Clear the flag + g->flags &= ~GDISP_FLG_INSTREAM; + + // The cleanup below must match the streaming code above. + + #if GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + gdisp_lld_write_stop(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + if (gvmt(g)->blit) + #endif + { + if (g->p.cx) { + g->p.x1 = 0; + g->p.y1 = 0; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + if (g->p.cx) { + if (g->p.cx == 1) + gdisp_lld_draw_pixel(g); + else + gdisp_lld_fill_area(g); + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE + { + autoflush_stopdone(g); + MUTEX_EXIT(g); + } + #endif + } +#endif + +void gdispGDrawPixel(GDisplay *g, coord_t x, coord_t y, color_t color) { + MUTEX_ENTER(g); + g->p.x = x; + g->p.y = y; + g->p.color = color; + drawpixel_clip(g); + autoflush(g); + MUTEX_EXIT(g); +} + +void gdispGDrawLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color) { + MUTEX_ENTER(g); + g->p.x = x0; + g->p.y = y0; + g->p.x1 = x1; + g->p.y1 = y1; + g->p.color = color; + line_clip(g); + autoflush(g); + MUTEX_EXIT(g); +} + +void gdispGClear(GDisplay *g, color_t color) { + // Note - clear() ignores the clipping area. It clears the screen. + MUTEX_ENTER(g); + + // Best is hardware accelerated clear + #if GDISP_HARDWARE_CLEARS + #if GDISP_HARDWARE_CLEARS == HARDWARE_AUTODETECT + if (gvmt(g)->clear) + #endif + { + g->p.color = color; + gdisp_lld_clear(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Next best is hardware accelerated area fill + #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + g->p.x = g->p.y = 0; + g->p.cx = g->g.Width; + g->p.cy = g->g.Height; + g->p.color = color; + gdisp_lld_fill_area(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Next best is streaming + #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + uint32_t area; + + g->p.x = g->p.y = 0; + g->p.cx = g->g.Width; + g->p.cy = g->g.Height; + g->p.color = color; + area = (uint32_t)g->p.cx * g->p.cy; + + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + for(; area; area--) + gdisp_lld_write_color(g); + gdisp_lld_write_stop(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + g->p.color = color; + for(g->p.y = 0; g->p.y < g->g.Height; g->p.y++) + for(g->p.x = 0; g->p.x < g->g.Width; g->p.x++) + gdisp_lld_draw_pixel(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif +} + +void gdispGFillArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { + MUTEX_ENTER(g); + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + g->p.color = color; + TEST_CLIP_AREA(g) { + fillarea(g); + } + autoflush_stopdone(g); + MUTEX_EXIT(g); +} + +void gdispGBlitArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) { + MUTEX_ENTER(g); + + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + { + // This is a different clipping to fillarea(g) as it needs to take into account srcx,srcy + if (x < g->clipx0) { cx -= g->clipx0 - x; srcx += g->clipx0 - x; x = g->clipx0; } + if (y < g->clipy0) { cy -= g->clipy0 - y; srcy += g->clipy0 - x; y = g->clipy0; } + if (x+cx > g->clipx1) cx = g->clipx1 - x; + if (y+cy > g->clipy1) cy = g->clipy1 - y; + if (srcx+cx > srccx) cx = srccx - srcx; + if (cx <= 0 || cy <= 0) { MUTEX_EXIT(g); return; } + } + #endif + + // Best is hardware bitfills + #if GDISP_HARDWARE_BITFILLS + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + if (gvmt(g)->blit) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + g->p.x1 = srcx; + g->p.y1 = srcy; + g->p.x2 = srccx; + g->p.ptr = (void *)buffer; + gdisp_lld_blit_area(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Next best is hardware streaming + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap + buffer += srcy*srccx+srcx; + srcx = x + cx; + srcy = y + cy; + srccx -= cx; + + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (gvmt(g)->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { + for(g->p.x = x; g->p.x < srcx; g->p.x++) { + g->p.color = *buffer++; + gdisp_lld_write_color(g); + } + } + gdisp_lld_write_stop(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Only slightly better than drawing pixels is to look for runs and use fill area + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap + buffer += srcy*srccx+srcx; + srcx = x + cx; + srcy = y + cy; + srccx -= cx; + + g->p.cy = 1; + for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { + for(g->p.x=x; g->p.x < srcx; g->p.x += g->p.cx) { + g->p.cx=1; + g->p.color = *buffer++; + while(g->p.x+g->p.cx < srcx && *buffer == g->p.color) { + g->p.cx++; + buffer++; + } + if (g->p.cx == 1) { + gdisp_lld_draw_pixel(g); + } else { + gdisp_lld_fill_area(g); + } + } + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap + buffer += srcy*srccx+srcx; + srcx = x + cx; + srcy = y + cy; + srccx -= cx; + + for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { + for(g->p.x=x; g->p.x < srcx; g->p.x++) { + g->p.color = *buffer++; + gdisp_lld_draw_pixel(g); + } + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif +} + +#if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION + void gdispGSetClip(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) { + MUTEX_ENTER(g); + + // Best is using hardware clipping + #if GDISP_HARDWARE_CLIP + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (gvmt(g)->setclip) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + gdisp_lld_set_clip(g); + } + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + else + #endif + #endif + + // Worst is using software clipping + #if GDISP_HARDWARE_CLIP != TRUE + { + if (x < 0) { cx += x; x = 0; } + if (y < 0) { cy += y; y = 0; } + if (cx <= 0 || cy <= 0 || x >= g->g.Width || y >= g->g.Height) { MUTEX_EXIT(g); return; } + g->clipx0 = x; + g->clipy0 = y; + g->clipx1 = x+cx; if (g->clipx1 > g->g.Width) g->clipx1 = g->g.Width; + g->clipy1 = y+cy; if (g->clipy1 > g->g.Height) g->clipy1 = g->g.Height; + } + #endif + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_CIRCLE + void gdispGDrawCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) { + coord_t a, b, P; + + MUTEX_ENTER(g); + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + g->p.color = color; + + // Away we go using Bresenham's circle algorithm + // Optimized to prevent double drawing + g->p.x = x; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x + b; g->p.y = y; drawpixel_clip(g); + g->p.x = x - b; g->p.y = y; drawpixel_clip(g); + do { + g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g); + g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g); + g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g); + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_CIRCLE + void gdispGFillCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) { + coord_t a, b, P; + + MUTEX_ENTER(g); + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + g->p.color = color; + + // Away we go using Bresenham's circle algorithm + // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + g->p.y = y+b; g->p.x = x; drawpixel_clip(g); + g->p.y = y-b; g->p.x = x; drawpixel_clip(g); + do { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + if (P < 0) { + P += 3 + 2*a++; + } else { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); + P += 5 + 2*(a++ - b--); + } + } while(a < b); + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ELLIPSE + void gdispGDrawEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) { + coord_t dx, dy; + int32_t a2, b2; + int32_t err, e2; + + MUTEX_ENTER(g); + + // Calculate intermediates + dx = 0; + dy = b; + a2 = a*a; + b2 = b*b; + err = b2-(2*b-1)*a2; + g->p.color = color; + + // Away we go using Bresenham's ellipse algorithm + do { + g->p.x = x + dx; g->p.y = y + dy; drawpixel_clip(g); + g->p.x = x - dx; g->p.y = y + dy; drawpixel_clip(g); + g->p.x = x - dx; g->p.y = y - dy; drawpixel_clip(g); + g->p.x = x + dx; g->p.y = y - dy; drawpixel_clip(g); + + e2 = 2*err; + if(e2 < (2*dx+1)*b2) { + dx++; + err += (2*dx+1)*b2; + } + if(e2 > -(2*dy-1)*a2) { + dy--; + err -= (2*dy-1)*a2; + } + } while(dy >= 0); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ELLIPSE + void gdispGFillEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) { + coord_t dx, dy; + int32_t a2, b2; + int32_t err, e2; + + MUTEX_ENTER(g); + + // Calculate intermediates + dx = 0; + dy = b; + a2 = a*a; + b2 = b*b; + err = b2-(2*b-1)*a2; + g->p.color = color; + + // Away we go using Bresenham's ellipse algorithm + // This is optimized to prevent overdrawing by drawing a line only when a y is about to change value + do { + e2 = 2*err; + if(e2 < (2*dx+1)*b2) { + dx++; + err += (2*dx+1)*b2; + } + if(e2 > -(2*dy-1)*a2) { + g->p.y = y + dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); + if (y) { g->p.y = y - dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); } + dy--; + err -= (2*dy-1)*a2; + } + } while(dy >= 0); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARCSECTORS + void gdispGDrawArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color) { + coord_t a, b, P; + + MUTEX_ENTER(g); + + // Calculate intermediates + a = 1; // x in many explanations + b = radius; // y in many explanations + P = 4 - radius; + g->p.color = color; + + // Away we go using Bresenham's circle algorithm + // Optimized to prevent double drawing + if (sectors & 0x06) { g->p.x = x; g->p.y = y - b; drawpixel_clip(g); } // Upper upper + if (sectors & 0x60) { g->p.x = x; g->p.y = y + b; drawpixel_clip(g); } // Lower lower + if (sectors & 0x81) { g->p.x = x + b; g->p.y = y; drawpixel_clip(g); } // Right right + if (sectors & 0x18) { g->p.x = x - b; g->p.y = y; drawpixel_clip(g); } // Left left + + do { + if (sectors & 0x01) { g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g); } // Upper right right + if (sectors & 0x02) { g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); } // Upper upper right + if (sectors & 0x04) { g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); } // Upper upper left + if (sectors & 0x08) { g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g); } // Upper left left + if (sectors & 0x10) { g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g); } // Lower left left + if (sectors & 0x20) { g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); } // Lower lower left + if (sectors & 0x40) { g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); } // Lower lower right + if (sectors & 0x80) { g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g); } // Lower right right + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + + if (sectors & 0xC0) { g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); } // Lower right + if (sectors & 0x03) { g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); } // Upper right + if (sectors & 0x30) { g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); } // Lower left + if (sectors & 0x0C) { g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); } // Upper left + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARCSECTORS + void gdispGFillArcSectors(GDisplay *g, coord_t x, coord_t y, coord_t radius, uint8_t sectors, color_t color) { + coord_t a, b, P; + + MUTEX_ENTER(g); + + // Calculate intermediates + a = 1; // x in many explanations + b = radius; // y in many explanations + P = 4 - radius; + g->p.color = color; + + // Away we go using Bresenham's circle algorithm + // Optimized to prevent double drawing + if (sectors & 0x06) { g->p.x = x; g->p.y = y - b; drawpixel_clip(g); } // Upper upper + if (sectors & 0x60) { g->p.x = x; g->p.y = y + b; drawpixel_clip(g); } // Lower lower + if (sectors & 0x81) { // Center right + g->p.y = y; g->p.x = x; g->p.x1 = x + b; + if (sectors & 0x18) g->p.x -= b; // Left right + hline_clip(g); + } else if (sectors & 0x18) { // Left center + g->p.x = x - b; g->p.x1 = x; g->p.y = y; + hline_clip(g); + } + + do { + // Top half + switch(sectors & 0x0F) { + case 0x01: + g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x02: + g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + break; + case 0x03: + g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); + break; + case 0x04: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + break; + case 0x05: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x06: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + break; + case 0x07: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x08: + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + break; + case 0x09: + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0A: + g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + break; + case 0x0B: + g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y - a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0C: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); + break; + case 0x0D: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); + g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0E: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x + a; hline_clip(g); + break; + case 0x0F: + g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y - a; g->p.x = x - b; g->p.x1 = x + b; hline_clip(g); + break; + } + + // Bottom half + switch((sectors & 0xF0)>>4) { + case 0x01: + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + break; + case 0x02: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + break; + case 0x03: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); + break; + case 0x04: + g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + break; + case 0x05: + g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + break; + case 0x06: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + break; + case 0x07: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x + a; hline_clip(g); + break; + case 0x08: + g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x09: + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0A: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0B: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x; hline_clip(g); + g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0C: + g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0D: + g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g); + g->p.y = y + a; g->p.x = x; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0E: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - a; g->p.x1 = x + b; hline_clip(g); + break; + case 0x0F: + g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g); + g->p.y = y + a; g->p.x = x - b; g->p.x1 = x + b; hline_clip(g); + break; + } + + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + + // Top half + if (sectors & 0x02) { g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); } + else if (sectors & 0x01) { g->p.y = y - a; g->p.x = x + a; drawpixel_clip(g); } + if (sectors & 0x04) { g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); } + else if (sectors & 0x08) { g->p.y = y - a; g->p.x = x - a; drawpixel_clip(g); } + + // Bottom half + if (sectors & 0x40) { g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); } + else if (sectors & 0x80) { g->p.y = y + a; g->p.x = x + a; drawpixel_clip(g); } + if (sectors & 0x20) { g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); } + else if (sectors & 0x10) { g->p.y = y + a; g->p.x = x - a; drawpixel_clip(g); } + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARC + #if (!GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG) || !GFX_USE_GMISC + #include <math.h> + #endif + + void gdispGDrawArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) { + coord_t a, b, P, sedge, eedge; + uint8_t full, sbit, ebit, tbit; + + // Normalize the angles + if (start < 0) + start -= (start/360-1)*360; + else if (start >= 360) + start %= 360; + if (end < 0) + end -= (end/360-1)*360; + else if (end >= 360) + end %= 360; + + sbit = 1<<(start/45); + ebit = 1<<(end/45); + full = 0; + if (start == end) { + full = 0xFF; + } else if (end < start) { + for(tbit=sbit<<1; tbit; tbit<<=1) full |= tbit; + for(tbit=ebit>>1; tbit; tbit>>=1) full |= tbit; + } else if (sbit < 0x80) { + for(tbit=sbit<<1; tbit < ebit; tbit<<=1) full |= tbit; + } + tbit = start%45 == 0 ? sbit : 0; + + MUTEX_ENTER(g); + g->p.color = color; + + if (full) { + // Draw full sectors + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if (full & 0x60) { g->p.y = y+b; g->p.x = x; drawpixel_clip(g); } + if (full & 0x06) { g->p.y = y-b; g->p.x = x; drawpixel_clip(g); } + if (full & 0x81) { g->p.y = y; g->p.x = x+b; drawpixel_clip(g); } + if (full & 0x18) { g->p.y = y; g->p.x = x-b; drawpixel_clip(g); } + do { + if (full & 0x01) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if (full & 0x02) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x04) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x08) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if (full & 0x10) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if (full & 0x20) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (full & 0x40) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (full & 0x80) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (full & 0xC0) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (full & 0x0C) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x03) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x30) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (full == 0xFF) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + } + + #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG + sedge = NONFIXED(radius * ((sbit & 0x99) ? ffsin(start) : ffcos(start)) + FIXED0_5); + eedge = NONFIXED(radius * ((ebit & 0x99) ? ffsin(end) : ffcos(end)) + FIXED0_5); + #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG + sedge = round(radius * ((sbit & 0x99) ? fsin(start) : fcos(start))); + eedge = round(radius * ((ebit & 0x99) ? fsin(end) : fcos(end))); + #else + sedge = round(radius * ((sbit & 0x99) ? sin(start*M_PI/180) : cos(start*M_PI/180))); + eedge = round(radius * ((ebit & 0x99) ? sin(end*M_PI/180) : cos(end*M_PI/180))); + #endif + if (sbit & 0xB4) sedge = -sedge; + if (ebit & 0xB4) eedge = -eedge; + + if (sbit != ebit) { + // Draw start and end sectors + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if ((sbit & 0x20) || (tbit & 0x40) || (ebit & 0x40)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x02) || (tbit & 0x04) || (ebit & 0x04)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x80) || (tbit & 0x01) || (ebit & 0x01)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } + if ((sbit & 0x08) || (tbit & 0x10) || (ebit & 0x10)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } + do { + if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if (((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge) || ((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) + { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge) || ((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) + { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge) || ((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) + { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge) || ((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) + { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + } else if (end < start) { + // Draw start/end sector where it is a non-internal angle + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if ((sbit & 0x60) || (tbit & 0xC0)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x06) || (tbit & 0x0C)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x81) || (tbit & 0x03)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } + if ((sbit & 0x18) || (tbit & 0x30)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } + do { + if ((sbit & 0x01) && (a >= sedge || a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if ((sbit & 0x02) && (a <= sedge || a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x04) && (a >= sedge || a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x08) && (a <= sedge || a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if ((sbit & 0x10) && (a >= sedge || a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if ((sbit & 0x20) && (a <= sedge || a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x40) && (a >= sedge || a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x80) && (a <= sedge || a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (((sbit & 0x04) && (a >= sedge || a <= eedge)) || ((sbit & 0x08) && (a <= sedge || a >= eedge))) + { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x40) && (a >= sedge || a <= eedge)) || ((sbit & 0x80) && (a <= sedge || a >= eedge))) + { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x01) && (a >= sedge || a <= eedge)) || ((sbit & 0x02) && (a <= sedge || a >= eedge))) + { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x10) && (a >= sedge || a <= eedge)) || ((sbit & 0x20) && (a <= sedge || a >= eedge))) + { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + } else { + // Draw start/end sector where it is a internal angle + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if (((sbit & 0x20) && !eedge) || ((sbit & 0x40) && !sedge)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x02) && !eedge) || ((sbit & 0x04) && !sedge)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x80) && !eedge) || ((sbit & 0x01) && !sedge)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } + if (((sbit & 0x08) && !eedge) || ((sbit & 0x10) && !sedge)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } + do { + if (((sbit & 0x01) && a >= sedge && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x02) && a <= sedge && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x04) && a >= sedge && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x08) && a <= sedge && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if (((sbit & 0x20) && a <= sedge && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x40) && a >= sedge && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x80) && a <= sedge && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (((sbit & 0x04) && a >= sedge && a <= eedge) || ((sbit & 0x08) && a <= sedge && a >= eedge)) + { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x40) && a >= sedge && a <= eedge) || ((sbit & 0x80) && a <= sedge && a >= eedge)) + { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x01) && a >= sedge && a <= eedge) || ((sbit & 0x02) && a <= sedge && a >= eedge)) + { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge && a <= eedge) || ((sbit & 0x20) && a <= sedge && a >= eedge)) + { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + } + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARC + void gdispGFillArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) { + coord_t a, b, P; + coord_t sy, ey; + fixed sxa, sxb, sxd, exa, exb, exd; + uint8_t qtr; + + MUTEX_ENTER(g); + + // Do the trig to get the formulas for the start and end lines. + sxa = exa = FIXED(x)+FIXED0_5; + #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG + sxb = radius*ffcos(start); sy = -NONFIXED(radius*ffsin(start) + FIXED0_5); + exb = radius*ffcos(end); ey = -NONFIXED(radius*ffsin(end) + FIXED0_5); + #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG + sxb = FP2FIXED(radius*fcos(start)); sy = -round(radius*fsin(start)); + exb = FP2FIXED(radius*fcos(end)); ey = -round(radius*fsin(end)); + #else + sxb = FP2FIXED(radius*cos(start*M_PI/180)); sy = -round(radius*sin(start*M_PI/180)); + exb = FP2FIXED(radius*cos(end*M_PI/180)); ey = -round(radius*sin(end*M_PI/180)); + #endif + sxd = sy ? sxb/sy : sxb; + exd = ey ? exb/ey : exb; + + // Calculate which quarters and which direction we are traveling + qtr = 0; + if (sxb > 0) qtr |= 0x01; // S1=0001(1), S2=0000(0), S3=0010(2), S4=0011(3) + if (sy > 0) qtr |= 0x02; + if (exb > 0) qtr |= 0x04; // E1=0100(4), E2=0000(0), E3=1000(8), E4=1100(12) + if (ey > 0) qtr |= 0x08; + if (sy > ey) qtr |= 0x10; // order of start and end lines + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + g->p.color = color; + sxb += sxa; + exb += exa; + + // Away we go using Bresenham's circle algorithm + // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value + + switch(qtr) { + case 0: // S2E2 sy <= ey + case 1: // S1E2 sy <= ey + if (ey && sy) { + g->p.x = x; g->p.x1 = x; // E2S + sxa -= sxd; exa -= exd; + } else if (sy) { + g->p.x = x-b; g->p.x1 = x; // C2S + sxa -= sxd; + } else if (ey) { + g->p.x = x; g->p.x1 = x+b; // E2C + exa -= exd; + } else { + g->p.x = x-b; g->p.x1 = x+b; // C2C + } + g->p.y = y; + hline_clip(g); + do { + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + sxa -= sxd; exa -= exd; + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + sxa -= sxd; + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S + sxb += sxd; exb += exd; + } else if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + sxb += sxd; + } else if (qtr & 1) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 2: // S3E2 sy <= ey + case 3: // S4E2 sy <= ey + case 6: // S3E1 sy <= ey + case 7: // S4E1 sy <= ey + case 18: // S3E2 sy > ey + case 19: // S4E2 sy > ey + case 22: // S3E1 sy > ey + case 23: // S4E1 sy > ey + g->p.y = y; g->p.x = x; g->p.x1 = x+b; hline_clip(g); // SE2C + sxa += sxd; exa -= exd; + do { + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + exa -= exd; + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + exb += exd; + } else if (!(qtr & 4)) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; + } else if (!(qtr & 1)) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+a; hline_clip(g); // S2C + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C + } + break; + + case 4: // S2E1 sy <= ey + case 5: // S1E1 sy <= ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + do { + if (-a >= ey) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + sxa -= sxd; exa -= exd; + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + sxa -= sxd; + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= ey) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + sxb += sxd; exb += exd; + } else if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + sxb += sxd; + } else if (qtr & 1) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= ey) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + break; + + case 8: // S2E3 sy <= ey + case 9: // S1E3 sy <= ey + case 12: // S2E4 sy <= ey + case 13: // S1E4 sy <= ey + case 24: // S2E3 sy > ey + case 25: // S1E3 sy > ey + case 28: // S2E3 sy > ey + case 29: // S1E3 sy > ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x; hline_clip(g); // C2SE + sxa -= sxd; exa += exd; + do { + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + sxa -= sxd; + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + exa += exd; + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + sxb += sxd; + } else if (qtr & 1) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + exb -= exd; + } else if (qtr & 4) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C + } + break; + + case 10: // S3E3 sy <= ey + case 14: // S3E4 sy <= ey + g->p.y = y; g->p.x = x; drawpixel_clip(g); // S2E + sxa += sxd; exa += exd; + do { + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E + sxa += sxd; exa += exd; + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + exa += exd; + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E + sxb -= sxd; exb -= exd; + } else if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + exb -= exd; + } else if (qtr & 4) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 11: // S4E3 sy <= ey + case 15: // S4E4 sy <= ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + do { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= sy) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; exa += exd; + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + exa += exd; + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + if (b <= sy) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; exb -= exd; + } else if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + exb -= exd; + } else if (qtr & 4) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= sy) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 16: // S2E2 sy > ey + case 20: // S2E1 sy > ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + sxa -= sxd; exa -= exd; + do { + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + sxa -= sxd; exa -= exd; + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + exa -= exd; + } else if (!(qtr & 4)){ + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + sxb += sxd; exb += exd; + } else if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + exb += exd; + } else if (!(qtr & 4)){ + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (!(qtr & 4)){ + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + break; + + case 17: // S1E2 sy > ey + case 21: // S1E1 sy > ey + if (sy) { + g->p.x = x; g->p.x1 = x; // E2S + sxa -= sxd; exa -= exd; + } else { + g->p.x = x; g->p.x1 = x+b; // E2C + exa -= exd; + } + g->p.y = y; + hline_clip(g); + do { + if (-a >= sy) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + sxa -= sxd; exa -= exd; + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + exa -= exd; + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= sy) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S + sxb += sxd; exb += exd; + } else if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + exb += exd; + } else if (!(qtr & 4)) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= sy) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 26: // S3E3 sy > ey + case 27: // S4E3 sy > ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + do { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; exa += exd; + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; exb -= exd; + } else if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; + } else if (!(qtr & 1)) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (!(qtr & 4)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 30: // S3E4 sy > ey + case 31: // S4E4 sy > ey + do { + if (a <= ey) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E + sxa += sxd; exa += exd; + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (b <= ey) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E + sxb -= sxd; exb -= exd; + } else if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; + } else if (!(qtr & 1)) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (a <= ey) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (!(qtr & 4)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + } + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARC || GDISP_NEED_ARCSECTORS + void gdispGDrawRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { + if (2*radius > cx || 2*radius > cy) { + gdispGDrawBox(g, x, y, cx, cy, color); + return; + } + + #if GDISP_NEED_ARCSECTORS + gdispGDrawArcSectors(g, x+radius, y+radius, radius, 0x0C, color); + gdispGDrawArcSectors(g, x+cx-1-radius, y+radius, radius, 0x03, color); + gdispGDrawArcSectors(g, x+cx-1-radius, y+cy-1-radius, radius, 0xC0, color); + gdispGDrawArcSectors(g, x+radius, y+cy-1-radius, radius, 0x30, color); + #else + gdispGDrawArc(g, x+radius, y+radius, radius, 90, 180, color); + gdispGDrawArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color); + gdispGDrawArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); + gdispGDrawArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); + #endif + gdispGDrawLine(g, x+radius+1, y, x+cx-2-radius, y, color); + gdispGDrawLine(g, x+cx-1, y+radius+1, x+cx-1, y+cy-2-radius, color); + gdispGDrawLine(g, x+radius+1, y+cy-1, x+cx-2-radius, y+cy-1, color); + gdispGDrawLine(g, x, y+radius+1, x, y+cy-2-radius, color); + } +#endif + +#if GDISP_NEED_ARC || GDISP_NEED_ARCSECTORS + void gdispGFillRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { + coord_t radius2; + + radius2 = radius*2; + if (radius2 > cx || radius2 > cy) { + gdispGFillArea(g, x, y, cx, cy, color); + return; + } + #if GDISP_NEED_ARCSECTORS + gdispGFillArcSectors(g, x+radius, y+radius, radius, 0x0C, color); + gdispGFillArcSectors(g, x+cx-1-radius, y+radius, radius, 0x03, color); + gdispGFillArcSectors(g, x+cx-1-radius, y+cy-1-radius, radius, 0xC0, color); + gdispGFillArcSectors(g, x+radius, y+cy-1-radius, radius, 0x30, color); + #else + gdispGFillArc(g, x+radius, y+radius, radius, 90, 180, color); + gdispGFillArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color); + gdispGFillArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); + gdispGFillArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); + #endif + gdispGFillArea(g, x+radius+1, y, cx-radius2, radius, color); + gdispGFillArea(g, x+radius+1, y+cy-radius, cx-radius2, radius, color); + gdispGFillArea(g, x, y+radius, cx, cy-radius2, color); + } +#endif + +#if GDISP_NEED_PIXELREAD + color_t gdispGGetPixelColor(GDisplay *g, coord_t x, coord_t y) { + color_t c; + + /* Always synchronous as it must return a value */ + MUTEX_ENTER(g); + #if GDISP_HARDWARE_PIXELREAD + #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT + if (gvmt(g)->get) + #endif + { + // Best is direct pixel read + g->p.x = x; + g->p.y = y; + c = gdisp_lld_get_pixel_color(g); + MUTEX_EXIT(g); + return c; + } + #endif + #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ + #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT + if (gvmt(g)->readcolor) + #endif + { + // Next best is hardware streaming + g->p.x = x; + g->p.y = y; + g->p.cx = 1; + g->p.cy = 1; + gdisp_lld_read_start(g); + c = gdisp_lld_read_color(g); + gdisp_lld_read_stop(g); + MUTEX_EXIT(g); + return c; + } + #endif + #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ != TRUE + #if !GDISP_HARDWARE_PIXELREAD && !GDISP_HARDWARE_STREAM_READ + // Worst is "not possible" + #error "GDISP: GDISP_NEED_PIXELREAD has been set but there is no hardware support for reading the display" + #endif + MUTEX_EXIT(g); + return 0; + #endif + } +#endif + +#if GDISP_NEED_SCROLL + void gdispGVerticalScroll(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) { + coord_t abslines; + #if GDISP_HARDWARE_SCROLL != TRUE + coord_t fy, dy, ix, fx, i, j; + #endif + + MUTEX_ENTER(g); + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!gvmt(g)->setclip) + #endif + { + if (x < g->clipx0) { cx -= g->clipx0 - x; x = g->clipx0; } + if (y < g->clipy0) { cy -= g->clipy0 - y; y = g->clipy0; } + if (!lines || cx <= 0 || cy <= 0 || x >= g->clipx1 || y >= g->clipy1) { MUTEX_EXIT(g); return; } + if (x+cx > g->clipx1) cx = g->clipx1 - x; + if (y+cy > g->clipy1) cy = g->clipy1 - y; + } + #endif + + abslines = lines < 0 ? -lines : lines; + if (abslines >= cy) { + abslines = cy; + cy = 0; + } else { + // Best is hardware scroll + #if GDISP_HARDWARE_SCROLL + #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT + if (gvmt(g)->vscroll) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + g->p.y1 = lines; + g->p.color = bgcolor; + gdisp_lld_vertical_scroll(g); + cy -= abslines; + } + #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT + else + #endif + #elif GDISP_LINEBUF_SIZE == 0 + #error "GDISP: GDISP_NEED_SCROLL is set but there is no hardware support and GDISP_LINEBUF_SIZE is zero." + #endif + + // Scroll Emulation + #if GDISP_HARDWARE_SCROLL != TRUE + { + cy -= abslines; + if (lines < 0) { + fy = y+cy-1; + dy = -1; + } else { + fy = y; + dy = 1; + } + // Move the screen - one line at a time + for(i = 0; i < cy; i++, fy += dy) { + + // Handle where the buffer is smaller than a line + for(ix=0; ix < cx; ix += GDISP_LINEBUF_SIZE) { + + // Calculate the data we can move in one operation + fx = cx - ix; + if (fx > GDISP_LINEBUF_SIZE) + fx = GDISP_LINEBUF_SIZE; + + // Read one line of data from the screen + + // Best line read is hardware streaming + #if GDISP_HARDWARE_STREAM_READ + #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT + if (gvmt(g)->readstart) + #endif + { + g->p.x = x+ix; + g->p.y = fy+lines; + g->p.cx = fx; + g->p.cy = 1; + gdisp_lld_read_start(g); + for(j=0; j < fx; j++) + g->linebuf[j] = gdisp_lld_read_color(g); + gdisp_lld_read_stop(g); + } + #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT + else + #endif + #endif + + // Next best line read is single pixel reads + #if GDISP_HARDWARE_STREAM_READ != TRUE && GDISP_HARDWARE_PIXELREAD + #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT + if (gvmt(g)->get) + #endif + { + for(j=0; j < fx; j++) { + g->p.x = x+ix+j; + g->p.y = fy+lines; + g->linebuf[j] = gdisp_lld_get_pixel_color(g); + } + } + #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT + else { + // Worst is "not possible" + MUTEX_EXIT(g); + return; + } + #endif + #endif + + // Worst is "not possible" + #if !GDISP_HARDWARE_STREAM_READ && !GDISP_HARDWARE_PIXELREAD + #error "GDISP: GDISP_NEED_SCROLL is set but there is no hardware support for scrolling or reading pixels." + #endif + + // Write that line to the new location + + // Best line write is hardware bitfills + #if GDISP_HARDWARE_BITFILLS + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + if (gvmt(g)->blit) + #endif + { + g->p.x = x+ix; + g->p.y = fy; + g->p.cx = fx; + g->p.cy = 1; + g->p.x1 = 0; + g->p.y1 = 0; + g->p.x2 = fx; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + } + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + else + #endif + #endif + + // Next best line write is hardware streaming + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (gvmt(g)->writestart) + #endif + { + g->p.x = x+ix; + g->p.y = fy; + g->p.cx = fx; + g->p.cy = 1; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + gdisp_lld_write_pos(g); + #endif + for(j = 0; j < fx; j++) { + g->p.color = g->linebuf[j]; + gdisp_lld_write_color(g); + } + gdisp_lld_write_stop(g); + } + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + else + #endif + #endif + + // Next best line write is drawing pixels in combination with filling + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS && GDISP_HARDWARE_DRAWPIXEL + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (gvmt(g)->fill) + #endif + { + g->p.y = fy; + g->p.cy = 1; + g->p.x = x+ix; + g->p.cx = 1; + for(j = 0; j < fx; ) { + g->p.color = g->linebuf[j]; + if (j + g->p.cx < fx && g->linebuf[j] == g->linebuf[j + g->p.cx]) + g->p.cx++; + else if (g->p.cx == 1) { + gdisp_lld_draw_pixel(g); + j++; + g->p.x++; + } else { + gdisp_lld_fill_area(g); + j += g->p.cx; + g->p.x += g->p.cx; + g->p.cx = 1; + } + } + } + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + else + #endif + #endif + + // Worst line write is drawing pixels + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (gvmt(g)->pixel) + //#endif + { + g->p.y = fy; + for(g->p.x = x+ix, j = 0; j < fx; g->p.x++, j++) { + g->p.color = g->linebuf[j]; + gdisp_lld_draw_pixel(g); + } + } + #endif + } + } + } + #endif + } + + /* fill the remaining gap */ + g->p.x = x; + g->p.y = lines > 0 ? (y+cy) : y; + g->p.cx = cx; + g->p.cy = abslines; + g->p.color = bgcolor; + fillarea(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_CONTROL + #if GDISP_HARDWARE_CONTROL + void gdispGControl(GDisplay *g, unsigned what, void *value) { + #if GDISP_HARDWARE_CONTROL == HARDWARE_AUTODETECT + if (!gvmt(g)->control) + return; + #endif + MUTEX_ENTER(g); + g->p.x = what; + g->p.ptr = value; + if (what == GDISP_CONTROL_ORIENTATION) { + switch ((orientation_t) value) { + case GDISP_ROTATE_LANDSCAPE: + g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_0 : (void *)GDISP_ROTATE_90; + break; + case GDISP_ROTATE_PORTRAIT: + g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_90 : (void *)GDISP_ROTATE_0; + break; + default: + break; + } + } + gdisp_lld_control(g); + #if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION + if (what == GDISP_CONTROL_ORIENTATION) { + // Best is hardware clipping + #if GDISP_HARDWARE_CLIP + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (gvmt(g)->setclip) + #endif + { + g->p.x = 0; + g->p.y = 0; + g->p.cx = g->g.Width; + g->p.cy = g->g.Height; + gdisp_lld_set_clip(g); + } + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + else + #endif + #endif + + // Worst is software clipping + #if GDISP_HARDWARE_CLIP != TRUE + { + g->clipx0 = 0; + g->clipy0 = 0; + g->clipx1 = g->g.Width; + g->clipy1 = g->g.Height; + } + #endif + } + #endif + MUTEX_EXIT(g); + } + #else + void gdispGControl(GDisplay *g, unsigned what, void *value) { + (void)g; + (void)what; + (void)value; + /* Ignore everything */ + } + #endif +#endif + +#if GDISP_NEED_QUERY + #if GDISP_HARDWARE_QUERY + void *gdispGQuery(GDisplay *g, unsigned what) { + void *res; + + #if GDISP_HARDWARE_QUERY == HARDWARE_AUTODETECT + if (!gvmt(g)->query) + return -1; + #endif + MUTEX_ENTER(g); + g->p.x = (coord_t)what; + res = gdisp_lld_query(g); + MUTEX_EXIT(g); + return res; + } + #else + void *gdispGQuery(GDisplay *g, unsigned what) { + (void) what; + return (void *)-1; + } + #endif +#endif + +/*===========================================================================*/ +/* High Level Driver Routines. */ +/*===========================================================================*/ + +void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { + if (cx <= 0 || cy <= 0) return; + cx = x+cx-1; cy = y+cy-1; // cx, cy are now the end point. + + MUTEX_ENTER(g); + + g->p.color = color; + + if (cx - x > 2) { + g->p.x = x; g->p.y = y; g->p.x1 = cx; hline_clip(g); + if (y != cy) { + g->p.x = x; g->p.y = cy; g->p.x1 = cx; hline_clip(g); + if (cy - y > 2) { + y++; cy--; + g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g); + g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g); + } + } + } else { + g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g); + if (x != cx) { + g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g); + } + } + + autoflush(g); + MUTEX_EXIT(g); +} + +#if GDISP_NEED_CONVEX_POLYGON + void gdispGDrawPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) { + const point *epnt, *p; + + epnt = &pntarray[cnt-1]; + + MUTEX_ENTER(g); + g->p.color = color; + for(p = pntarray; p < epnt; p++) { + 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); + } + 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); + + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillConvexPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) { + const point *lpnt, *rpnt, *epnts; + fixed lx, rx, lk, rk; + coord_t y, ymax, lxc, rxc; + + epnts = &pntarray[cnt-1]; + + /* Find a top point */ + rpnt = pntarray; + for(lpnt=pntarray+1; lpnt <= epnts; lpnt++) { + if (lpnt->y < rpnt->y) + rpnt = lpnt; + } + lx = rx = FIXED(rpnt->x); + y = rpnt->y; + + /* Work out the slopes of the two attached line segs */ + for (lpnt = rpnt <= pntarray ? epnts : rpnt-1; lpnt->y == y; cnt--) { + if (!cnt) return; + lx = FIXED(lpnt->x); + lpnt = lpnt <= pntarray ? epnts : lpnt-1; + } + for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) { + if (!cnt) return; + rx = FIXED(rpnt->x); + rpnt = rpnt >= epnts ? pntarray : rpnt+1; + } + lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y); + rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y); + + MUTEX_ENTER(g); + g->p.color = color; + while(1) { + /* Determine our boundary */ + ymax = rpnt->y < lpnt->y ? rpnt->y : lpnt->y; + + /* Scan down the line segments until we hit a boundary */ + for(; y < ymax; y++) { + lxc = NONFIXED(lx); + rxc = NONFIXED(rx); + /* + * Doesn't print the right hand point in order to allow polygon joining. + * Also ensures that we draw from left to right with the minimum number + * of pixels. + */ + if (lxc < rxc) { + g->p.x=tx+lxc; g->p.y=ty+y; g->p.x1=tx+rxc-1; hline_clip(g); + } else if (lxc > rxc) { + g->p.x=tx+rxc; g->p.y=ty+y; g->p.x1=tx+lxc-1; hline_clip(g); + } + + lx += lk; + rx += rk; + } + + if (!cnt) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + cnt--; + + /* Replace the appropriate point */ + if (ymax == lpnt->y) { + for (lpnt = lpnt <= pntarray ? epnts : lpnt-1; lpnt->y == y; cnt--) { + if (!cnt) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + lx = FIXED(lpnt->x); + lpnt = lpnt <= pntarray ? epnts : lpnt-1; + } + lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y); + } else { + for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) { + if (!cnt) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + rx = FIXED(rpnt->x); + rpnt = rpnt >= epnts ? pntarray : rpnt+1; + } + rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y); + } + } + } + + static int32_t rounding_div(const int32_t n, const int32_t d) + { + if ((n < 0) != (d < 0)) + return (n - d/2) / d; + else + return (n + d/2) / d; + } + + /* Find a vector (nx, ny) that is perpendicular to (dx, dy) and has length + * equal to 'norm'. */ + static void get_normal_vector(coord_t dx, coord_t dy, coord_t norm, coord_t *nx, coord_t *ny) + { + int32_t dx2, dy2, len_sq, norm_sq, norm_sq2; + int div, step, best, delta, abs_delta; + + dx2 = dx; dy2 = dy; + norm_sq = (int32_t)norm * norm; + norm_sq2 = norm_sq * 512; + + /* Scale dx2 and dy2 so that + * len_sq / 2 <= norm_sq * 512 <= len_sq * 2. + * The scaling by 512 is to yield higher accuracy in division later. */ + len_sq = dx2 * dx2 + dy2 * dy2; + + if (len_sq < norm_sq2) + { + while (len_sq && len_sq < norm_sq2) + { + len_sq <<= 2; dx2 <<= 1; dy2 <<= 1; + } + } + else if (len_sq > norm_sq2) + { + while (len_sq && len_sq > norm_sq2) + { + len_sq >>= 2; dx2 >>= 1; dy2 >>= 1; + } + } + + /* Now find the divider div so that + * len_sq / div^2 == norm_sq i.e. div = sqrt(len_sq / norm_sq) + * + * This is done using bisection search to avoid the need for floating + * point sqrt. + * + * Based on previous scaling, we know that + * len_sq / 2 <= norm_sq * 512 <=> div <= sqrt(1024) = 32 + * len_sq * 2 >= norm_sq * 512 <=> div >= sqrt(256) = 16 + */ + div = 24; step = 8; + best = 256; + + for (;;) + { + dx = dx2 / div; + dy = dy2 / div; + len_sq = dx*dx + dy*dy; + + delta = len_sq - norm_sq; + + abs_delta = (delta >= 0) ? delta : -delta; + + if (abs_delta < best) + { + *nx = dy; + *ny = -dx; + best = abs_delta; + } + + if (delta > 0) + div += step; + else if (delta < 0) + div -= step; + else if (delta == 0) + break; + + if (step == 0) + break; + else + step >>= 1; /* Do one round with step = 0 to calculate final result. */ + } + } + + void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round) { + coord_t dx, dy, nx = 0, ny = 0; + + /* Compute the direction vector for the line */ + dx = x1 - x0; + dy = y1 - y0; + + /* Draw a small dot if the line length is zero. */ + if (dx == 0 && dy == 0) + dx += 1; + + /* Compute a normal vector with length 'width'. */ + get_normal_vector(dx, dy, width, &nx, &ny); + + /* Handle 1px wide lines gracefully */ + if (nx == 0 && ny == 0) + nx = 1; + + /* Offset the x0,y0 by half the width of the line. This way we + * can keep the width of the line accurate even if it is not evenly + * divisible by 2. + */ + { + x0 -= rounding_div(nx, 2); + y0 -= rounding_div(ny, 2); + } + + /* Fill in the point array */ + if (!round) { + /* We use 4 points for the basic line shape: + * + * pt1 pt2 + * (+n) ------------------------------------ (d+n) + * | | + * (0,0) ----------------------------------- (d) + * pt0 pt3 + */ + point pntarray[4]; + + pntarray[0].x = 0; + pntarray[0].y = 0; + pntarray[1].x = nx; + pntarray[1].y = ny; + pntarray[2].x = dx + nx; + pntarray[2].y = dy + ny; + pntarray[3].x = dx; + pntarray[3].y = dy; + + gdispGFillConvexPoly(g, x0, y0, pntarray, 4, color); + } else { + /* We use 4 points for basic shape, plus 4 extra points for ends: + * + * pt3 ------------------ pt4 + * / \ + * pt2 pt5 + * | | + * pt1 pt6 + * \ / + * pt0 -------------------pt7 + */ + point pntarray[8]; + coord_t nx2, ny2; + + /* Magic numbers: + * 75/256 = sin(45) / (1 + sqrt(2)) diagonal octagon segments + * 106/256 = 1 / (1 + sqrt(2)) octagon side + * 53/256 = 0.5 / (1 + sqrt(2)) half of octagon side + * 150/256 = 1 - 1 / (1 + sqrt(2)) octagon height minus one side + */ + + /* Rotate the normal vector 45 deg counter-clockwise and reduce + * to 1 / (1 + sqrt(2)) length, for forming octagonal ends. */ + nx2 = rounding_div((nx * 75 + ny * 75), 256); + ny2 = rounding_div((-nx * 75 + ny * 75), 256); + + /* Offset and extend the line so that the center of the octagon + * is at the specified points. */ + x0 += ny * 53 / 256; + y0 -= nx * 53 / 256; + dx -= ny * 106 / 256; + dy += nx * 106 / 256; + + /* Now fill in the points by summing the calculated vectors. */ + pntarray[0].x = 0; + pntarray[0].y = 0; + pntarray[1].x = nx2; + pntarray[1].y = ny2; + pntarray[2].x = nx2 + nx * 106/256; + pntarray[2].y = ny2 + ny * 106/256; + pntarray[3].x = nx; + pntarray[3].y = ny; + pntarray[4].x = dx + nx; + pntarray[4].y = dy + ny; + pntarray[5].x = dx + nx - nx2; + pntarray[5].y = dy + ny - ny2; + pntarray[6].x = dx + nx * 150/256 - nx2; + pntarray[6].y = dy + ny * 150/256 - ny2; + pntarray[7].x = dx; + pntarray[7].y = dy; + + gdispGFillConvexPoly(g, x0, y0, pntarray, 8, color); + } + } +#endif + +#if GDISP_NEED_TEXT + #include "mcufont/mcufont.h" + + #if GDISP_NEED_ANTIALIAS && GDISP_HARDWARE_PIXELREAD + static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { + #define GD ((GDisplay *)state) + if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) + return; + if (x < GD->t.clipx0) { + count -= GD->t.clipx0 - x; + x = GD->t.clipx0; + } + if (x+count > GD->t.clipx1) + count = GD->t.clipx1 - x; + if (alpha == 255) { + GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color; + hline_clip(GD); + } else { + for (; count; count--, x++) { + GD->p.x = x; GD->p.y = y; + GD->p.color = gdispBlendColor(GD->t.color, gdisp_lld_get_pixel_color(GD), alpha); + drawpixel_clip(GD); + } + } + #undef GD + } + #else + static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { + #define GD ((GDisplay *)state) + if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) + return; + if (x < GD->t.clipx0) { + count -= GD->t.clipx0 - x; + x = GD->t.clipx0; + } + if (x+count > GD->t.clipx1) + count = GD->t.clipx1 - x; + if (alpha > 0x80) { // A best approximation when using anti-aliased fonts but we can't actually draw them anti-aliased + GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color; + hline_clip(GD); + } + #undef GD + } + #endif + + #if GDISP_NEED_ANTIALIAS + static void fillcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { + #define GD ((GDisplay *)state) + if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) + return; + if (x < GD->t.clipx0) { + count -= GD->t.clipx0 - x; + x = GD->t.clipx0; + } + if (x+count > GD->t.clipx1) + count = GD->t.clipx1 - x; + if (alpha == 255) { + GD->p.color = GD->t.color; + } else { + GD->p.color = gdispBlendColor(GD->t.color, GD->t.bgcolor, alpha); + } + GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; + hline_clip(GD); + #undef GD + } + #else + #define fillcharline drawcharline + #endif + + /* Callback to render characters. */ + static uint8_t drawcharglyph(int16_t x, int16_t y, mf_char ch, void *state) { + #define GD ((GDisplay *)state) + return mf_render_character(GD->t.font, x, y, ch, drawcharline, state); + #undef GD + } + + /* Callback to render characters. */ + static uint8_t fillcharglyph(int16_t x, int16_t y, mf_char ch, void *state) { + #define GD ((GDisplay *)state) + return mf_render_character(GD->t.font, x, y, ch, fillcharline, state); + #undef GD + } + + void gdispGDrawChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color) { + MUTEX_ENTER(g); + g->t.font = font; + g->t.clipx0 = x; + g->t.clipy0 = y; + g->t.clipx1 = x + mf_character_width(font, c) + font->baseline_x; + g->t.clipy1 = y + font->height; + g->t.color = color; + mf_render_character(font, x, y, c, drawcharline, g); + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color, color_t bgcolor) { + MUTEX_ENTER(g); + g->p.cx = mf_character_width(font, c) + font->baseline_x; + g->p.cy = font->height; + g->t.font = font; + g->t.clipx0 = g->p.x = x; + g->t.clipy0 = g->p.y = y; + g->t.clipx1 = g->p.x+g->p.cx; + g->t.clipy1 = g->p.y+g->p.cy; + g->t.color = color; + g->t.bgcolor = g->p.color = bgcolor; + + TEST_CLIP_AREA(g) { + fillarea(g); + mf_render_character(font, x, y, c, fillcharline, g); + } + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGDrawString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color) { + MUTEX_ENTER(g); + g->t.font = font; + g->t.clipx0 = x; + g->t.clipy0 = y; + g->t.clipx1 = x + mf_get_string_width(font, str, 0, 0); + g->t.clipy1 = y + font->height; + g->t.color = color; + + mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, drawcharglyph, g); + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color, color_t bgcolor) { + MUTEX_ENTER(g); + g->p.cx = mf_get_string_width(font, str, 0, 0); + g->p.cy = font->height; + g->t.font = font; + g->t.clipx0 = g->p.x = x; + g->t.clipy0 = g->p.y = y; + g->t.clipx1 = g->p.x+g->p.cx; + g->t.clipy1 = g->p.y+g->p.cy; + g->t.color = color; + g->t.bgcolor = g->p.color = bgcolor; + + TEST_CLIP_AREA(g) { + fillarea(g); + mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, fillcharglyph, g); + } + + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGDrawStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, justify_t justify) { + MUTEX_ENTER(g); + g->t.font = font; + g->t.clipx0 = x; + g->t.clipy0 = y; + g->t.clipx1 = x+cx; + g->t.clipy1 = y+cy; + g->t.color = color; + + /* Select the anchor position */ + switch(justify) { + case justifyCenter: + x += (cx + 1) / 2; + break; + case justifyRight: + x += cx; + break; + default: // justifyLeft + x += font->baseline_x; + break; + } + y += (cy+1 - font->height)/2; + + mf_render_aligned(font, x, y, justify, str, 0, drawcharglyph, g); + + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, color_t bgcolor, justify_t justify) { + MUTEX_ENTER(g); + g->p.cx = cx; + g->p.cy = cy; + g->t.font = font; + g->t.clipx0 = g->p.x = x; + g->t.clipy0 = g->p.y = y; + g->t.clipx1 = x+cx; + g->t.clipy1 = y+cy; + g->t.color = color; + g->t.bgcolor = g->p.color = bgcolor; + + TEST_CLIP_AREA(g) { + + // background fill + fillarea(g); + + /* Select the anchor position */ + switch(justify) { + case justifyCenter: + x += (cx + 1) / 2; + break; + case justifyRight: + x += cx; + break; + default: // justifyLeft + x += font->baseline_x; + break; + } + y += (cy+1 - font->height)/2; + + /* Render */ + mf_render_aligned(font, x, y, justify, str, 0, fillcharglyph, g); + } + + autoflush(g); + MUTEX_EXIT(g); + } + + coord_t gdispGetFontMetric(font_t font, fontmetric_t metric) { + /* No mutex required as we only read static data */ + switch(metric) { + case fontHeight: return font->height; + case fontDescendersHeight: return font->height - font->baseline_y; + case fontLineSpacing: return font->line_height; + case fontCharPadding: return 0; + case fontMinWidth: return font->min_x_advance; + case fontMaxWidth: return font->max_x_advance; + } + return 0; + } + + coord_t gdispGetCharWidth(char c, font_t font) { + /* No mutex required as we only read static data */ + return mf_character_width(font, c); + } + + coord_t gdispGetStringWidth(const char* str, font_t font) { + if (!str) + return 0; + + /* No mutex required as we only read static data */ + return mf_get_string_width(font, str, 0, 0); + } +#endif + +color_t gdispBlendColor(color_t fg, color_t bg, uint8_t alpha) +{ + uint16_t fg_ratio = alpha + 1; + uint16_t bg_ratio = 256 - alpha; + uint16_t r, g, b; + + r = RED_OF(fg) * fg_ratio; + g = GREEN_OF(fg) * fg_ratio; + b = BLUE_OF(fg) * fg_ratio; + + r += RED_OF(bg) * bg_ratio; + g += GREEN_OF(bg) * bg_ratio; + b += BLUE_OF(bg) * bg_ratio; + + r >>= 8; + g >>= 8; + b >>= 8; + + return RGB2COLOR(r, g, b); +} + +color_t gdispContrastColor(color_t color) { + uint16_t r, g, b; + + r = RED_OF(color) > 128 ? 0 : 255; + g = GREEN_OF(color) > 128 ? 0 : 255; + b = BLUE_OF(color) > 128 ? 0 : 255; + + return RGB2COLOR(r, g, b); +} + +#if (!defined(gdispPackPixels) && !defined(GDISP_PIXELFORMAT_CUSTOM)) + void gdispPackPixels(pixel_t *buf, coord_t cx, coord_t x, coord_t y, color_t color) { + /* No mutex required as we only read static data */ + #if defined(GDISP_PIXELFORMAT_RGB888) + #error "GDISP: Packed pixels not supported yet" + #elif defined(GDISP_PIXELFORMAT_RGB444) + #error "GDISP: Packed pixels not supported yet" + #elif defined(GDISP_PIXELFORMAT_RGB666) + #error "GDISP: Packed pixels not supported yet" + #elif + #error "GDISP: Unsupported packed pixel format" + #endif + } +#endif + +#endif /* GFX_USE_GDISP */ |