The offical µGFX repository.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

3757 lines
114 KiB

/*
* 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 GDISPVMTLIST[1];
static const GDISPVMT* 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 <math.h>
#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 <math.h>
#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 &&