ugfx/src/gdisp/gdisp.c
Joel Bodenmann 9c0678a291 Avoid duplicate const specifier compiler warnings
The original code is perfectly valid standard C. However, some compilers (especially GCC) complain about duplicate const specifiers anyway.
At this point we cave in as there doesn't seem to be any efforts to fix this problem by the corresponding compiler vendors.

uGFX v3 will no longer suffer from this problem as the driver interface works differently in this area.
2021-08-12 12:20:07 +02:00

3749 lines
114 KiB
C

/*
* 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. */
/*===========================================================================*/
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];
extern GDISPVMTLIST GDISP_DRIVER_LIST;
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;
extern const GDISPVMT GDISPVMT_OnlyOne[1];
if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY)) {
for(i = 0; i < GDISP_TOTAL_DISPLAYS; i++)
gdriverRegister(&GDISPVMT_OnlyOne->d, 0);
}
}
#else
{
extern const GDISPVMT GDISPVMT_OnlyOne[1];
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 && 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 */