/* * 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.io/license.html */ #include "../../gfx.h" #if GFX_USE_GDISP /* Include the low level driver information */ #include "gdisp_driver.h" // Number of milliseconds for the startup logo - 0 means disabled. #if GDISP_NEED_STARTUP_LOGO #define GDISP_STARTUP_LOGO_TIMEOUT 1000 #define GDISP_STARTUP_LOGO_COLOR GFX_WHITE #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 != GFXON && (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 GFXINLINE void setglobalwindow(GDisplay *g) { gCoord 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 GFXINLINE 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 != GFXON && 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 != GFXON && GDISP_HARDWARE_STREAM_POS != GFXON && 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 GFXINLINE 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 GFXINLINE 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 { #if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE if ((g->flags & GDISP_FLG_SCRSTREAM)) { gdisp_lld_write_stop(g); g->flags &= ~GDISP_FLG_SCRSTREAM; } #endif gdisp_lld_fill_area(g); return; } #endif // Next best is hardware streaming #if GDISP_HARDWARE_FILLS != GFXON && GDISP_HARDWARE_STREAM_WRITE #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT if (gvmt(g)->writestart) #endif { gU32 area; #if GDISP_HARDWARE_STREAM_POS if ((g->flags & GDISP_FLG_SCRSTREAM)) { gdisp_lld_write_stop(g); g->flags &= ~GDISP_FLG_SCRSTREAM; } #endif area = (gU32)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 != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && 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 { gCoord 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 != GFXON && 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 != GFXON && GDISP_HARDWARE_STREAM_POS != GFXON && 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 != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && 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 { #if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE 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_fill_area(g); return; } #endif // Next best is streaming #if GDISP_HARDWARE_FILLS != GFXON && 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 != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && 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) { gI16 dy, dx; gI16 addx, addy; gI16 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 gBool gdispInitDone; static void StartupLogoDisplay(GDisplay *g) { gCoord x, y, w; const gCoord * p; static const gCoord 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, GDISP_STARTUP_LOGO_COLOR); } #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. */ /*===========================================================================*/ // Gather GDISP VMT(S) // These are only needed in _gdispInit(). However, we want to prevent generating nested-externs compiler warnings. #if defined(GDISP_DRIVER_LIST) extern GDISPVMTLIST GDISP_DRIVER_LIST; #else extern const GDISPVMT GDISPVMT_OnlyOne[1]; #endif void _gdispInit(void) { // GDISP_DRIVER_LIST is defined - create each driver instance #if defined(GDISP_DRIVER_LIST) { unsigned i; typedef const GDISPVMT const GDISPVMTLIST[1]; static const GDISPVMT * const 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; if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY)) { for(i = 0; i < GDISP_TOTAL_DISPLAYS; i++) gdriverRegister(&GDISPVMT_OnlyOne->d, 0); } } #else { 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 } gdispInitDone = gTrue; } #endif // Start the automatic timer flush (if required) #if GDISP_NEED_TIMERFLUSH gtimerInit(&FlushTimer); gtimerStart(&FlushTimer, FlushTimerFn, 0, gTrue, GDISP_NEED_TIMERFLUSH); #endif } void _gdispDeinit(void) { /* ToDo */ } gBool _gdispInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance) { #define gd ((GDisplay *)g) gBool 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 (!gdispInitDone) 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); } gCoord gdispGGetWidth(GDisplay *g) { return g->g.Width; } gCoord gdispGGetHeight(GDisplay *g) { return g->g.Height; } gPowermode gdispGGetPowerMode(GDisplay *g) { return g->g.Powermode; } gOrientation gdispGGetOrientation(GDisplay *g) { return g->g.Orientation; } gU8 gdispGGetBacklight(GDisplay *g) { return g->g.Backlight; } gU8 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, gCoord x, gCoord y, gCoord cx, gCoord 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 != GFXON && 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, gColor color) { #if !GDISP_HARDWARE_STREAM_WRITE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS gCoord 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 != GFXON && 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 != GFXON && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != GFXON) && 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 != GFXON && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != GFXON) && GDISP_HARDWARE_FILLS != GFXON && 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 != GFXON && 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 != GFXON && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != GFXON) && 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 != GFXON && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != GFXON) && GDISP_HARDWARE_FILLS != GFXON { autoflush_stopdone(g); MUTEX_EXIT(g); } #endif } #endif void gdispGDrawPixel(GDisplay *g, gCoord x, gCoord y, gColor 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, gCoord x0, gCoord y0, gCoord x1, gCoord y1, gColor 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, gColor 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 != GFXON && 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 != GFXON && GDISP_HARDWARE_FILLS != GFXON && GDISP_HARDWARE_STREAM_WRITE #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT if (gvmt(g)->writestart) #endif { gU32 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 = (gU32)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 != GFXON && GDISP_HARDWARE_FILLS != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && 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, gCoord x, gCoord y, gCoord cx, gCoord cy, gColor 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, gCoord x, gCoord y, gCoord cx, gCoord cy, gCoord srcx, gCoord srcy, gCoord srccx, const gPixel *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 != GFXON && 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 != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && 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 != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && GDISP_HARDWARE_FILLS != GFXON && 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, gCoord x, gCoord y, gCoord cx, gCoord 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 != GFXON { 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) { x = y = cx = cy = 0; } 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, gCoord x, gCoord y, gCoord radius, gColor color) { gCoord 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, gCoord x, gCoord y, gCoord radius, gColor color) { gCoord 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_DUALCIRCLE #define DRAW_DUALLINE(yval, r1, r2) \ g->p.y = yval; \ g->p.x = x-r1; g->p.x1 = x-r2+1; hline_clip(g); \ g->p.x = x-r2; g->p.x1 = x+r2; g->p.color = color2; hline_clip(g); \ g->p.x = x+r2+1; g->p.x1 = x+r1; g->p.color = color1; hline_clip(g) #define DRAW_SINGLELINE(yval, r) g->p.y = yval; g->p.x = x-r; g->p.x1 = x+r; hline_clip(g) void gdispGFillDualCircle(GDisplay *g, gCoord x, gCoord y, gCoord radius1, gColor color1, gCoord radius2, gColor color2) { gCoord a, b1, b2, p1, p2; MUTEX_ENTER(g); // Do the combined circle where the inner circle < 45 deg (and outer circle) g->p.color = color1; a = 0; b1 = radius1; b2 = radius2; p1 = p2 = 1; do { DRAW_DUALLINE(y+a, b1, b2); DRAW_DUALLINE(y-a, b1, b2); if (p1 >= 0) p1 -= b1--; p1 += a; if (p2 >= 0) p2 -= b2--; p2 += a; } while(++a < b2); // Do the combined circle where inner circle > 45 deg, outer circle < 45 do { DRAW_DUALLINE(y+a, b1, b2); DRAW_DUALLINE(y-a, b1, b2); if (p1 >= 0) p1 -= b1--; p1 += a; do { p2 -= --b2; } while (p2+a >= b2); p2 += a; } while(++a <= radius2 && a < b1); if (a < radius2) { // Do the combined circle where inner circle > 45 deg, outer circle > 45 do { DRAW_DUALLINE(y+a, b1, b2); DRAW_DUALLINE(y-a, b1, b2); do { p1 -= --b1; } while (p1+a >= b1); p1 += a; do { p2 -= --b2; } while (p2+a >= b2); p2 += a++; } while(b2 > 0); } else { // Do the outer circle above the inner circle but < 45 deg do { DRAW_SINGLELINE(y+a, b1); DRAW_SINGLELINE(y-a, b1); if (p1 >= 0) p1 -= b1--; p1 += a++; } while(a < b1); DRAW_SINGLELINE(y+a, b1); DRAW_SINGLELINE(y-a, b1); } // Do the top and bottom part of the outer circle (outer circle > 45deg and above inner circle) a = 0; b1 = radius1; p1 = 1; do { if (p1 >= 0) { DRAW_SINGLELINE(y+b1, a); DRAW_SINGLELINE(y-b1, a); p1 -= b1--; } p1 += a++; } while(b1 > radius2 && a < b1); autoflush(g); MUTEX_EXIT(g); } #undef DRAW_DUALLINE #undef DRAW_SINGLELINE #endif #if GDISP_NEED_ELLIPSE void gdispGDrawEllipse(GDisplay *g, gCoord x, gCoord y, gCoord a, gCoord b, gColor color) { gCoord dx, dy; gI32 a2, b2; gI32 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, gCoord x, gCoord y, gCoord a, gCoord b, gColor color) { gCoord dx, dy; gI32 a2, b2; gI32 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, gCoord x, gCoord y, gCoord radius, gU8 sectors, gColor color) { gCoord 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, gCoord x, gCoord y, gCoord radius, gU8 sectors, gColor color) { gCoord 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 #endif void gdispGDrawArc(GDisplay *g, gCoord x, gCoord y, gCoord radius, gCoord start, gCoord end, gColor color) { gCoord a, b, P, sedge, eedge; gU8 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 = floor(radius * ((sbit & 0x99) ? fsin(start) : fcos(start)) + 0.5); eedge = floor(radius * ((ebit & 0x99) ? fsin(end) : fcos(end)) + 0.5); #else sedge = floor(radius * ((sbit & 0x99) ? sin(start*GFX_PI/180) : cos(start*GFX_PI/180)) + 0.5); eedge = floor(radius * ((ebit & 0x99) ? sin(end*GFX_PI/180) : cos(end*GFX_PI/180)) + 0.5); #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 #if (!GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG) || !GFX_USE_GMISC #include #endif void gdispGDrawThickArc(GDisplay *g, gCoord xc, gCoord yc, gCoord radiusStart, gCoord radiusEnd, gCoord start, gCoord end, gColor color) { gCoord x, y, d, r; gCoord startTan, endTan, curangle; gCoord precision = 512; // 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; #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG if((start / 45) % 2 == 0){ startTan = ffsin(start % 45) * precision / ffcos(start % 45) + start / 45 * precision;} else{ startTan = ffsin(start % 45 - 45) * precision / ffcos(start % 45 - 45) + start / 45 * precision + precision;} if((end / 45) % 2 == 0){ endTan = ffsin(end % 45) * precision / ffcos(end % 45) + end / 45 * precision;} else{ endTan = ffsin(end % 45 - 45) * precision / ffcos(end % 45 - 45) + end / 45 * precision + precision;} #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG if((start / 45) % 2 == 0){ startTan = fsin(start % 45) * precision / fcos(start % 45) + start / 45 * precision;} else{ startTan = fsin(start % 45 - 45) * precision / fcos(start % 45 - 45) + start / 45 * precision + precision;} if((end / 45) % 2 == 0){ endTan = fsin(end % 45) * precision / fcos(end % 45) + end / 45 * precision;} else{ endTan = fsin(end % 45 - 45) * precision / fcos(end % 45 - 45) + end / 45 * precision + precision;} #else if((start / 45) % 2 == 0){ startTan = (tan((start % 45)*GFX_PI/180) + start / 45)* precision;} else{ startTan = (1+tan((start % 45 - 45)*GFX_PI/180) + start / 45)* precision;} if((end / 45) % 2 == 0){ endTan = (tan((end % 45) *GFX_PI/180) + end / 45) * precision;} else{ endTan = (1+tan((end % 45 - 45) *GFX_PI/180) + end / 45) * precision;} #endif MUTEX_ENTER(g); g->p.color = color; //Draw concentric circles using Andres algorithm for(r = radiusStart; r <= radiusEnd; r++) { x = 0; y = r; d = r - 1; while (y >= x){ //approximate tan curangle = x*precision/y; if(end > start){ g->p.color = color; //Draw points by symmetry if(curangle > startTan && curangle < endTan){g->p.y = yc - x; g->p.x = xc + y; drawpixel_clip(g);} if(curangle + 2*precision > startTan && curangle + 2*precision < endTan){g->p.y = yc - y; g->p.x = xc - x; drawpixel_clip(g);} if(curangle + 4*precision > startTan && curangle + 4*precision < endTan){g->p.y = yc + x; g->p.x = xc - y; drawpixel_clip(g);} if(curangle + 6*precision > startTan && curangle + 6*precision < endTan){g->p.y = yc + y; g->p.x = xc + x; drawpixel_clip(g);} curangle = precision - curangle; if(curangle + precision > startTan && curangle + precision < endTan){g->p.y = yc - y; g->p.x = xc + x; drawpixel_clip(g);} if(curangle + 3*precision > startTan && curangle + 3*precision < endTan){g->p.y = yc - x; g->p.x = xc - y; drawpixel_clip(g);} if(curangle + 5*precision > startTan && curangle + 5*precision < endTan){g->p.y = yc + y; g->p.x = xc - x; drawpixel_clip(g);} if(curangle + 7*precision > startTan && curangle + 7*precision < endTan){g->p.y = yc + x; g->p.x = xc + y; drawpixel_clip(g);} } else{ //Draw points by symmetry if(curangle > startTan || curangle < endTan){g->p.y = yc - x; g->p.x = xc + y; drawpixel_clip(g);} if(curangle + 2*precision > startTan || curangle + 2*precision < endTan){g->p.y = yc - y; g->p.x = xc - x; drawpixel_clip(g);} if(curangle + 4*precision > startTan || curangle + 4*precision < endTan){g->p.y = yc + x; g->p.x = xc - y; drawpixel_clip(g);} if(curangle + 6*precision > startTan || curangle + 6*precision < endTan){g->p.y = yc + y; g->p.x = xc + x; drawpixel_clip(g);} curangle = precision - curangle; if(curangle + precision > startTan || curangle + precision < endTan){g->p.y = yc - y; g->p.x = xc + x; drawpixel_clip(g);} if(curangle + 3*precision > startTan || curangle + 3*precision < endTan){g->p.y = yc - x; g->p.x = xc - y; drawpixel_clip(g);} if(curangle + 5*precision > startTan || curangle + 5*precision < endTan){g->p.y = yc + y; g->p.x = xc - x; drawpixel_clip(g);} if(curangle + 7*precision > startTan || curangle + 7*precision < endTan){g->p.y = yc + x; g->p.x = xc + y; drawpixel_clip(g);} } //Compute next point if (d >= 2 * x){ d -= 2 * x + 1; x++; } else if (d < 2 * (r - y)){ d += 2 * y - 1; y--; } else{ d += 2 * (y - x - 1); y--; x++; } } } autoflush(g); MUTEX_EXIT(g); } #endif #if GDISP_NEED_ARC void gdispGFillArc(GDisplay *g, gCoord x, gCoord y, gCoord radius, gCoord start, gCoord end, gColor color) { gCoord a, b, P; gCoord sy, ey; fixed sxa, sxb, sxd, exa, exb, exd; gU8 qtr; MUTEX_ENTER(g); // We add a half pixel so that we are drawing from the centre of the pixel // instead of the left edge of the pixel. This also fixes the implied floor() // when converting back to a gCoord sxa = exa = FIXED(x) + FIXED0_5; // Do the trig to get the formulas for the start and end lines. #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG sxb = radius*ffcos(start); sy = NONFIXED(FIXED0_5 - radius*ffsin(start)); exb = radius*ffcos(end); ey = NONFIXED(FIXED0_5 - radius*ffsin(end)); #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG sxb = FP2FIXED(radius*fcos(start)); sy = floor(0.5-radius*fsin(start)); exb = FP2FIXED(radius*fcos(end)); ey = floor(0.5-radius*fsin(end)); #else sxb = FP2FIXED(radius*cos(start*GFX_PI/180)); sy = floor(0.5-radius*sin(start*GFX_PI/180)); exb = FP2FIXED(radius*cos(end*GFX_PI/180)); ey = floor(0.5-radius*sin(end*GFX_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 || (sy == ey && sxb > 0)) 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 = NONFIXED(exa); hline_clip(g); // S2E } 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, gCoord x, gCoord y, gCoord cx, gCoord cy, gCoord radius, gColor 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, gCoord x, gCoord y, gCoord cx, gCoord cy, gCoord radius, gColor color) { gCoord 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 gColor gdispGGetPixelColor(GDisplay *g, gCoord x, gCoord y) { gColor 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 != GFXON && 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 != GFXON && GDISP_HARDWARE_STREAM_READ != GFXON #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, gCoord x, gCoord y, gCoord cx, gCoord cy, int lines, gColor bgcolor) { gCoord abslines; #if GDISP_HARDWARE_SCROLL != GFXON gCoord fy, dy, ix, fx, i, j; #endif if (!lines) return; 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 (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 != GFXON { 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 != GFXON && 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 != GFXON && 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 != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && 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 != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && GDISP_HARDWARE_FILLS != GFXON && 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 ((gOrientation) value) { case gOrientationLandscape: g->p.ptr = g->g.Width >= g->g.Height ? (void *)gOrientation0 : (void *)gOrientation90; break; case gOrientationPortrait: g->p.ptr = g->g.Width >= g->g.Height ? (void *)gOrientation90 : (void *)gOrientation0; 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 != GFXON { 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 = (gCoord)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, gCoord x, gCoord y, gCoord cx, gCoord cy, gColor 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, gCoord tx, gCoord ty, const gPoint *pntarray, unsigned cnt, gColor color) { const gPoint *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, gCoord tx, gCoord ty, const gPoint *pntarray, unsigned cnt, gColor color) { const gPoint *lpnt, *rpnt, *epnts; fixed lx, rx, lk, rk; gCoord 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); // Add error correction for rounding lx += FIXED0_5; rx += FIXED0_5; // Do all the line segments 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) { lx -= FIXED0_5; 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); lx += FIXED0_5; } else { rx -= FIXED0_5; 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); rx += FIXED0_5; } } } static gI32 rounding_div(const gI32 n, const gI32 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(gCoord dx, gCoord dy, gCoord norm, gCoord *nx, gCoord *ny) { gCoord absDx, absDy; gI32 len_n, len, len2; char maxSteps; /* Take the absolute value of dx and dy, multiplied by 2 for precision */ absDx = (dx >= 0 ? dx : -dx) * 2; absDy = (dy >= 0 ? dy : -dy) * 2; /* Compute the quadrate length */ len2 = absDx * absDx + absDy * absDy; /* First aproximation : length = |dx| + |dy| */ len = absDx + absDy; /* Give a max number of steps, the calculation usually takes 3 or 4 */ for(maxSteps = 8; maxSteps > 0; maxSteps--) { /* Use an adapted version of Newton's algorithm to find the correct length * This calculation converge quadratically towards the correct length * n(x+1) = (n(x) + len^2 / n(x)) / 2 */ len_n = (len + len2 / len) / 2; /* We reach max precision when the last result is equal or greater than the previous one */ if(len_n >= len){ break; } len = len_n; } /* Compute the normal vector using nx = dy * desired length / vector length * The solution is rounded to the nearest integer */ *nx = rounding_div(dy * norm * 2, len); *ny = rounding_div(-dx * norm * 2, len); return; } void gdispGDrawThickLine(GDisplay *g, gCoord x0, gCoord y0, gCoord x1, gCoord y1, gColor color, gCoord width, gBool round) { gCoord 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 */ gPoint 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 */ gPoint pntarray[8]; gCoord 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(gI16 x, gI16 y, gU8 count, gU8 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(gI16 x, gI16 y, gU8 count, gU8 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(gI16 x, gI16 y, gU8 count, gU8 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 gU8 drawcharglyph(gI16 x, gI16 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 gU8 fillcharglyph(gI16 x, gI16 y, mf_char ch, void *state) { #define GD ((GDisplay *)state) return mf_render_character(GD->t.font, x, y, ch, fillcharline, state); #undef GD } /* Callback to render string boxes with word wrap. */ #if GDISP_NEED_TEXT_WORDWRAP static gBool mf_countline_callback(mf_str line, gU16 count, void *state) { (void) line; (void) count; ((gCoord*)state)[0]++; return gTrue; } static gBool mf_drawline_callback(mf_str line, gU16 count, void *state) { #define GD ((GDisplay *)state) mf_render_aligned(GD->t.font, GD->t.wrapx, GD->t.wrapy, GD->t.lrj, line, count, drawcharglyph, state); GD->t.wrapy += GD->t.font->line_height; #undef GD return gTrue; } static gBool mf_fillline_callback(mf_str line, gU16 count, void *state) { #define GD ((GDisplay *)state) mf_render_aligned(GD->t.font, GD->t.wrapx, GD->t.wrapy, GD->t.lrj, line, count, fillcharglyph, state); GD->t.wrapy += GD->t.font->line_height; #undef GD return gTrue; } #endif void gdispGDrawChar(GDisplay *g, gCoord x, gCoord y, gU16 c, gFont font, gColor color) { if (!font) return; 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, gCoord x, gCoord y, gU16 c, gFont font, gColor color, gColor bgcolor) { if (!font) return; 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, gCoord x, gCoord y, const char *str, gFont font, gColor color) { if (!font) return; MUTEX_ENTER(g); g->t.font = font; g->t.clipx0 = x; g->t.clipy0 = y; g->t.clipx1 = 32767; //x + mf_get_string_width(font, str, 0, 0) + font->baseline_x; 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, gCoord x, gCoord y, const char *str, gFont font, gColor color, gColor bgcolor) { if (!font) return; MUTEX_ENTER(g); g->p.cx = mf_get_string_width(font, str, 0, 0) + 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_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, fillcharglyph, g); } autoflush(g); MUTEX_EXIT(g); } void gdispGDrawStringBox(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, const char* str, gFont font, gColor color, gJustify justify) { gCoord totalHeight; if (!font) return; MUTEX_ENTER(g); // Apply padding #if GDISP_NEED_TEXT_BOXPADLR != 0 || GDISP_NEED_TEXT_BOXPADTB != 0 if (!(justify & gJustifyNoPad)) { #if GDISP_NEED_TEXT_BOXPADLR != 0 x += GDISP_NEED_TEXT_BOXPADLR; cx -= 2*GDISP_NEED_TEXT_BOXPADLR; #endif #if GDISP_NEED_TEXT_BOXPADTB != 0 y += GDISP_NEED_TEXT_BOXPADTB; cy -= 2*GDISP_NEED_TEXT_BOXPADTB; #endif } #endif // Save the clipping area g->t.clipx0 = x; g->t.clipy0 = y; g->t.clipx1 = x+cx; g->t.clipy1 = y+cy; // Calculate the total text height #if GDISP_NEED_TEXT_WORDWRAP if (!(justify & gJustifyNoWordWrap)) { // Count the number of lines totalHeight = 0; mf_wordwrap(font, cx, str, mf_countline_callback, &totalHeight); totalHeight *= font->height; } else #endif totalHeight = font->height; // Select the anchor position switch((justify & JUSTIFYMASK_VERTICAL)) { case gJustifyTop: break; case gJustifyBottom: y += cy - totalHeight; break; default: // gJustifyMiddle y += (cy+1 - totalHeight)/2; break; } switch((justify & JUSTIFYMASK_HORIZONTAL)) { case gJustifyCenter: x += (cx + 1) / 2; break; case gJustifyRight: x += cx; break; default: // gJustifyLeft break; } /* Render */ g->t.font = font; g->t.color = color; #if GDISP_NEED_TEXT_WORDWRAP if (!(justify & gJustifyNoWordWrap)) { g->t.lrj = (justify & JUSTIFYMASK_HORIZONTAL); g->t.wrapx = x; g->t.wrapy = y; mf_wordwrap(font, cx, str, mf_drawline_callback, g); } else #endif mf_render_aligned(font, x, y, (justify & JUSTIFYMASK_HORIZONTAL), str, 0, drawcharglyph, g); autoflush(g); MUTEX_EXIT(g); } void gdispGFillStringBox(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, const char* str, gFont font, gColor color, gColor bgcolor, gJustify justify) { gCoord totalHeight; if (!font) return; MUTEX_ENTER(g); g->p.x = x; g->p.y = y; g->p.cx = cx; g->p.cy = cy; TEST_CLIP_AREA(g) { // background fill g->p.color = bgcolor; fillarea(g); // Apply padding #if GDISP_NEED_TEXT_BOXPADLR != 0 || GDISP_NEED_TEXT_BOXPADTB != 0 if (!(justify & gJustifyNoPad)) { #if GDISP_NEED_TEXT_BOXPADLR != 0 x += GDISP_NEED_TEXT_BOXPADLR; cx -= 2*GDISP_NEED_TEXT_BOXPADLR; #endif #if GDISP_NEED_TEXT_BOXPADTB != 0 y += GDISP_NEED_TEXT_BOXPADTB; cy -= 2*GDISP_NEED_TEXT_BOXPADTB; #endif } #endif // Save the clipping area g->t.clipx0 = x; g->t.clipy0 = y; g->t.clipx1 = x+cx; g->t.clipy1 = y+cy; // Calculate the total text height #if GDISP_NEED_TEXT_WORDWRAP if (!(justify & gJustifyNoWordWrap)) { // Count the number of lines totalHeight = 0; mf_wordwrap(font, cx, str, mf_countline_callback, &totalHeight); totalHeight *= font->height; } else #endif totalHeight = font->height; // Select the anchor position switch((justify & JUSTIFYMASK_VERTICAL)) { case gJustifyTop: break; case gJustifyBottom: y += cy - totalHeight; break; default: // gJustifyMiddle y += (cy+1 - totalHeight)/2; break; } switch((justify & JUSTIFYMASK_HORIZONTAL)) { case gJustifyCenter: x += (cx + 1) / 2; break; case gJustifyRight: x += cx; break; default: // gJustifyLeft break; } /* Render */ g->t.font = font; g->t.color = color; g->t.bgcolor = bgcolor; #if GDISP_NEED_TEXT_WORDWRAP if (!(justify & gJustifyNoWordWrap)) { g->t.lrj = (justify & JUSTIFYMASK_HORIZONTAL); g->t.wrapx = x; g->t.wrapy = y; mf_wordwrap(font, cx, str, mf_fillline_callback, g); } else #endif mf_render_aligned(font, x, y, (justify & JUSTIFYMASK_HORIZONTAL), str, 0, fillcharglyph, g); } autoflush(g); MUTEX_EXIT(g); } gCoord gdispGetFontMetric(gFont font, gFontmetric metric) { if (!font) return 0; /* No mutex required as we only read static data */ switch(metric) { case gFontHeight: return font->height; case gFontDescendersHeight: return font->height - font->baseline_y; case gFontLineSpacing: return font->line_height; case gFontCharPadding: return 0; case gFontMinWidth: return font->min_x_advance; case gFontMaxWidth: return font->max_x_advance; case gFontBaselineX: return font->baseline_x; case gFontBaselineY: return font->baseline_y; } return 0; } gCoord gdispGetCharWidth(char c, gFont font) { if (!font) return 0; /* No mutex required as we only read static data */ return mf_character_width(font, c); } gCoord gdispGetStringWidthCount(const char* str, gFont font, gU16 count) { if (!str || !font) return 0; // No mutex required as we only read static data #if GDISP_NEED_TEXT_KERNING return mf_get_string_width(font, str, count, gTrue); #else return mf_get_string_width(font, str, count, gFalse); #endif } gCoord gdispGetStringWidth(const char* str, gFont font) { return gdispGetStringWidthCount(str, font, 0); } #endif #if GDISP_PIXELFORMAT == GDISP_PIXELFORMAT_RGB888 // Special alpha hacked version. // Note: this will still work with real RGB888 gColor gdispBlendColor(gColor fg, gColor bg, gU8 alpha) { gU32 ratio; gU32 a1, r1, g1, b1; gU32 a2, r2, g2, b2; // Ratio - add one to get 1 to 256 ratio = (gU32)alpha + 1; // 0 to 1 in 0.8 fixed point // Calculate the pre-multiplied values of r, g, b for the fg color a1 = ALPHA_OF(fg); // 0 to 1 in 0.8 fixed point r1 = RED_OF(fg) * a1; // 0 to 1 in 0.16 fixed point g1 = GREEN_OF(fg) * a1; // 0 to 1 in 0.16 fixed point b1 = BLUE_OF(fg) * a1; // 0 to 1 in 0.16 fixed point // Calculate the pre-multiplied values of r, g, b for the bg color a2 = ALPHA_OF(bg); // 0 to 1 in 0.8 fixed point r2 = RED_OF(bg) * a2; // 0 to 1 in 0.16 fixed point g2 = GREEN_OF(bg) * a2; // 0 to 1 in 0.16 fixed point b2 = BLUE_OF(bg) * a2; // 0 to 1 in 0.16 fixed point // Calculate the mixed color values a1 = ratio * (a1 - a2) + (a2<<8); // 0 to 1 in 0.16 fixed point if (!a1) return GFXTRANSPARENT; r1 = ((ratio * (r1 - r2))>>8) + r2; // 0 to 1 in 0.16 fixed point g1 = ((ratio * (g1 - g2))>>8) + g2; // 0 to 1 in 0.16 fixed point b1 = ((ratio * (b1 - b2))>>8) + b2; // 0 to 1 in 0.16 fixed point // Fix precision #if 1 // Convert back to un-multiplied values ratio = 0x80000000 / a1; // Divide 1 (0.31 fixed point) by a1 (0.16 fixed point) to get the a1 reciprocal in 0.15 fixed point a1 >>= 8; // Shift to get back to 0.8 fixed point r1 = (r1 * ratio) >> 23; // Multiply by ratio to get 0.31 and then shift to get back to 0.8 fixed point g1 = (g1 * ratio) >> 23; // Multiply by ratio to get 0.31 and then shift to get back to 0.8 fixed point b1 = (b1 * ratio) >> 23; // Multiply by ratio to get 0.31 and then shift to get back to 0.8 fixed point #else // Leave as pre-multiplied values a1 >>= 8; // Shift to get back to 0.8 fixed point r1 >>= 8; // Shift to get back to 0.8 fixed point g1 >>= 8; // Shift to get back to 0.8 fixed point b1 >>= 8; // Shift to get back to 0.8 fixed point #endif return ARGB2COLOR(a1, r1, g1, b1); } #else gColor gdispBlendColor(gColor fg, gColor bg, gU8 alpha) { gU16 fg_ratio = alpha + 1; gU16 bg_ratio = 256 - alpha; gU16 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); } #endif gColor gdispContrastColor(gColor color) { gU16 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(gPixel *buf, gCoord cx, gCoord x, gCoord y, gColor 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 */