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.
3757 lines
114 KiB
C
3757 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. */
|
|
/*===========================================================================*/
|
|
|
|
// Gather GDISP VMT(S)
|
|
// These are only needed in _gdispInit(). However, we want to prevent generating nested-externs compiler warnings.
|
|
#if defined(GDISP_DRIVER_LIST)
|
|
extern GDISPVMTLIST GDISP_DRIVER_LIST;
|
|
#else
|
|
extern const GDISPVMT GDISPVMT_OnlyOne[1];
|
|
#endif
|
|
|
|
void _gdispInit(void)
|
|
{
|
|
// GDISP_DRIVER_LIST is defined - create each driver instance
|
|
#if defined(GDISP_DRIVER_LIST)
|
|
{
|
|
unsigned i;
|
|
typedef const GDISPVMT GDISPVMTLIST[1];
|
|
|
|
static const GDISPVMT* dclist[] = { GDISP_DRIVER_LIST };
|
|
|
|
for(i = 0; i < sizeof(dclist)/sizeof(dclist[0]); i++) {
|
|
if (!(dclist[i]->d.flags & GDISP_VFLG_DYNAMICONLY))
|
|
gdriverRegister(&dclist[i]->d, 0);
|
|
}
|
|
}
|
|
#elif GDISP_TOTAL_DISPLAYS > 1
|
|
{
|
|
unsigned i;
|
|
|
|
if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY)) {
|
|
for(i = 0; i < GDISP_TOTAL_DISPLAYS; i++)
|
|
gdriverRegister(&GDISPVMT_OnlyOne->d, 0);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
if (!(GDISPVMT_OnlyOne->d.flags & GDISP_VFLG_DYNAMICONLY))
|
|
gdriverRegister(&GDISPVMT_OnlyOne->d, 0);
|
|
}
|
|
#endif
|
|
|
|
// Re-clear the display after the timeout if we added the logo
|
|
#if GDISP_STARTUP_LOGO_TIMEOUT > 0
|
|
{
|
|
GDisplay *g;
|
|
|
|
gfxSleepMilliseconds(GDISP_STARTUP_LOGO_TIMEOUT);
|
|
|
|
for(g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, 0); g; g = (GDisplay *)gdriverGetNext(GDRIVER_TYPE_DISPLAY, (GDriver *)g)) {
|
|
gdispGClear(g, GDISP_STARTUP_COLOR);
|
|
#if GDISP_HARDWARE_FLUSH
|
|
gdispGFlush(g);
|
|
#endif
|
|
}
|
|
|
|
gdispInitDone = gTrue;
|
|
}
|
|
#endif
|
|
|
|
// Start the automatic timer flush (if required)
|
|
#if GDISP_NEED_TIMERFLUSH
|
|
gtimerInit(&FlushTimer);
|
|
gtimerStart(&FlushTimer, FlushTimerFn, 0, gTrue, GDISP_NEED_TIMERFLUSH);
|
|
#endif
|
|
}
|
|
|
|
void _gdispDeinit(void)
|
|
{
|
|
/* ToDo */
|
|
}
|
|
|
|
gBool _gdispInitDriver(GDriver *g, void *param, unsigned driverinstance, unsigned systeminstance) {
|
|
#define gd ((GDisplay *)g)
|
|
gBool ret;
|
|
|
|
// Intialise fields
|
|
gd->systemdisplay = systeminstance;
|
|
gd->controllerdisplay = driverinstance;
|
|
gd->flags = 0;
|
|
gd->priv = param;
|
|
MUTEX_INIT(gd);
|
|
|
|
// Call the driver init
|
|
MUTEX_ENTER(gd);
|
|
ret = gdisp_lld_init(gd);
|
|
MUTEX_EXIT(gd);
|
|
return ret;
|
|
|
|
#undef gd
|
|
}
|
|
|
|
void _gdispPostInitDriver(GDriver *g) {
|
|
#define gd ((GDisplay *)g)
|
|
|
|
// Set orientation, clip
|
|
#if defined(GDISP_DEFAULT_ORIENTATION) && GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
|
|
#if GDISP_NEED_PIXMAP
|
|
// Pixmaps should stay in their created orientation (at least initially)
|
|
if (!(gvmt(gd)->d.flags & GDISP_VFLG_PIXMAP))
|
|
#endif
|
|
gdispGControl(gd, GDISP_CONTROL_ORIENTATION, (void *)GDISP_DEFAULT_ORIENTATION);
|
|
#endif
|
|
#if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP
|
|
gdispGSetClip(gd, 0, 0, gd->g.Width, gd->g.Height);
|
|
#endif
|
|
|
|
// Clear the Screen
|
|
gdispGClear(gd, GDISP_STARTUP_COLOR);
|
|
|
|
// Display the startup logo if this is a static initialised display
|
|
#if GDISP_STARTUP_LOGO_TIMEOUT > 0
|
|
if (!gdispInitDone)
|
|
StartupLogoDisplay(gd);
|
|
#endif
|
|
|
|
// Flush
|
|
#if GDISP_HARDWARE_FLUSH
|
|
gdispGFlush(gd);
|
|
#endif
|
|
|
|
// If this is the first driver set GDISP
|
|
if (!GDISP)
|
|
GDISP = gd;
|
|
|
|
#undef gd
|
|
}
|
|
|
|
void _gdispDeInitDriver(GDriver *g) {
|
|
#define gd ((GDisplay *)g)
|
|
|
|
if (GDISP == gd)
|
|
GDISP = (GDisplay *)gdriverGetInstance(GDRIVER_TYPE_DISPLAY, 0);
|
|
|
|
#if GDISP_HARDWARE_DEINIT
|
|
#if GDISP_HARDWARE_DEINIT == HARDWARE_AUTODETECT
|
|
if (gvmt(gd)->deinit)
|
|
#endif
|
|
{
|
|
MUTEX_ENTER(gd);
|
|
gdisp_lld_deinit(gd);
|
|
MUTEX_EXIT(gd);
|
|
}
|
|
#endif
|
|
MUTEX_DEINIT(gd);
|
|
|
|
#undef gd
|
|
}
|
|
|
|
GDisplay *gdispGetDisplay(unsigned display) {
|
|
return (GDisplay *)gdriverGetInstance(GDRIVER_TYPE_DISPLAY, display);
|
|
}
|
|
|
|
void gdispSetDisplay(GDisplay *g) {
|
|
if (g) GDISP = g;
|
|
}
|
|
|
|
unsigned gdispGetDisplayCount(void) {
|
|
return gdriverInstanceCount(GDRIVER_TYPE_DISPLAY);
|
|
}
|
|
|
|
gCoord gdispGGetWidth(GDisplay *g) { return g->g.Width; }
|
|
gCoord gdispGGetHeight(GDisplay *g) { return g->g.Height; }
|
|
gPowermode gdispGGetPowerMode(GDisplay *g) { return g->g.Powermode; }
|
|
gOrientation gdispGGetOrientation(GDisplay *g) { return g->g.Orientation; }
|
|
gU8 gdispGGetBacklight(GDisplay *g) { return g->g.Backlight; }
|
|
gU8 gdispGGetContrast(GDisplay *g) { return g->g.Contrast; }
|
|
|
|
void gdispGFlush(GDisplay *g) {
|
|
#if GDISP_HARDWARE_FLUSH
|
|
#if GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->flush)
|
|
#endif
|
|
{
|
|
MUTEX_ENTER(g);
|
|
gdisp_lld_flush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#else
|
|
(void) g;
|
|
#endif
|
|
}
|
|
|
|
#if GDISP_NEED_STREAMING
|
|
void gdispGStreamStart(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy) {
|
|
MUTEX_ENTER(g);
|
|
|
|
#if NEED_CLIPPING
|
|
#if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
|
|
if (!gvmt(g)->setclip)
|
|
#endif
|
|
// Test if the area is valid - if not then exit
|
|
if (x < g->clipx0 || x+cx > g->clipx1 || y < g->clipy0 || y+cy > g->clipy1) {
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
g->flags |= GDISP_FLG_INSTREAM;
|
|
|
|
// Best is hardware streaming
|
|
#if GDISP_HARDWARE_STREAM_WRITE
|
|
#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->writestart)
|
|
#endif
|
|
{
|
|
g->p.x = x;
|
|
g->p.y = y;
|
|
g->p.cx = cx;
|
|
g->p.cy = cy;
|
|
gdisp_lld_write_start(g);
|
|
#if GDISP_HARDWARE_STREAM_POS
|
|
#if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->writepos)
|
|
#endif
|
|
gdisp_lld_write_pos(g);
|
|
#endif
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Worst - save the parameters and use pixel drawing and/or area fills
|
|
#if GDISP_HARDWARE_STREAM_WRITE != GFXON && GDISP_HARDWARE_DRAWPIXEL
|
|
// The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
|
|
//#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
|
|
// if (gvmt(g)->pixel)
|
|
//#endif
|
|
{
|
|
// Use x,y as the current position, x1,y1 as the save position and x2,y2 as the end position, cx = bufpos
|
|
g->p.x1 = g->p.x = x;
|
|
g->p.y1 = g->p.y = y;
|
|
g->p.x2 = x + cx;
|
|
g->p.y2 = y + cy;
|
|
#if (GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS) || GDISP_HARDWARE_FILLS
|
|
g->p.cx = 0;
|
|
g->p.cy = 1;
|
|
#endif
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Don't release the mutex as gdispStreamEnd() will do that.
|
|
}
|
|
|
|
void gdispGStreamColor(GDisplay *g, gColor color) {
|
|
#if !GDISP_HARDWARE_STREAM_WRITE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS
|
|
gCoord sx1, sy1;
|
|
#endif
|
|
|
|
// Don't touch the mutex as we should already own it
|
|
|
|
// Ignore this call if we are not streaming
|
|
if (!(g->flags & GDISP_FLG_INSTREAM))
|
|
return;
|
|
|
|
// Best is hardware streaming
|
|
#if GDISP_HARDWARE_STREAM_WRITE
|
|
#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->writestart)
|
|
#endif
|
|
{
|
|
g->p.color = color;
|
|
gdisp_lld_write_color(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Next best is to use bitfills with our line buffer
|
|
#if GDISP_HARDWARE_STREAM_WRITE != GFXON && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS
|
|
#if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->blit)
|
|
#endif
|
|
{
|
|
g->linebuf[g->p.cx++] = color;
|
|
if (g->p.cx >= GDISP_LINEBUF_SIZE) {
|
|
sx1 = g->p.x1;
|
|
sy1 = g->p.y1;
|
|
g->p.x1 = 0;
|
|
g->p.y1 = 0;
|
|
g->p.ptr = (void *)g->linebuf;
|
|
gdisp_lld_blit_area(g);
|
|
g->p.x1 = sx1;
|
|
g->p.y1 = sy1;
|
|
g->p.x += g->p.cx;
|
|
g->p.cx = 0;
|
|
}
|
|
|
|
// Just wrap at end-of-line and end-of-buffer
|
|
if (g->p.x+g->p.cx >= g->p.x2) {
|
|
if (g->p.cx) {
|
|
sx1 = g->p.x1;
|
|
sy1 = g->p.y1;
|
|
g->p.x1 = 0;
|
|
g->p.y1 = 0;
|
|
g->p.ptr = (void *)g->linebuf;
|
|
gdisp_lld_blit_area(g);
|
|
g->p.x1 = sx1;
|
|
g->p.y1 = sy1;
|
|
g->p.cx = 0;
|
|
}
|
|
g->p.x = g->p.x1;
|
|
if (++g->p.y >= g->p.y2)
|
|
g->p.y = g->p.y1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Only slightly better than drawing pixels is to look for runs and use fillarea
|
|
#if GDISP_HARDWARE_STREAM_WRITE != GFXON && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != GFXON) && GDISP_HARDWARE_FILLS
|
|
// We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming.
|
|
#if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->fill)
|
|
#endif
|
|
{
|
|
if (!g->p.cx || g->p.color == color) {
|
|
g->p.cx++;
|
|
g->p.color = color;
|
|
} else {
|
|
if (g->p.cx == 1)
|
|
gdisp_lld_draw_pixel(g);
|
|
else
|
|
gdisp_lld_fill_area(g);
|
|
g->p.x += g->p.cx;
|
|
g->p.color = color;
|
|
g->p.cx = 1;
|
|
}
|
|
// Just wrap at end-of-line and end-of-buffer
|
|
if (g->p.x+g->p.cx >= g->p.x2) {
|
|
if (g->p.cx) {
|
|
if (g->p.cx == 1)
|
|
gdisp_lld_draw_pixel(g);
|
|
else
|
|
gdisp_lld_fill_area(g);
|
|
g->p.cx = 0;
|
|
}
|
|
g->p.x = g->p.x1;
|
|
if (++g->p.y >= g->p.y2)
|
|
g->p.y = g->p.y1;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Worst is using pixel drawing
|
|
#if GDISP_HARDWARE_STREAM_WRITE != GFXON && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != GFXON) && GDISP_HARDWARE_FILLS != GFXON && GDISP_HARDWARE_DRAWPIXEL
|
|
// The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
|
|
//#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
|
|
// if (gvmt(g)->pixel)
|
|
//#endif
|
|
{
|
|
g->p.color = color;
|
|
gdisp_lld_draw_pixel(g);
|
|
|
|
// Just wrap at end-of-line and end-of-buffer
|
|
if (++g->p.x >= g->p.x2) {
|
|
g->p.x = g->p.x1;
|
|
if (++g->p.y >= g->p.y2)
|
|
g->p.y = g->p.y1;
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void gdispGStreamStop(GDisplay *g) {
|
|
// Only release the mutex and end the stream if we are actually streaming.
|
|
if (!(g->flags & GDISP_FLG_INSTREAM))
|
|
return;
|
|
|
|
// Clear the flag
|
|
g->flags &= ~GDISP_FLG_INSTREAM;
|
|
|
|
// The cleanup below must match the streaming code above.
|
|
|
|
#if GDISP_HARDWARE_STREAM_WRITE
|
|
#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->writestart)
|
|
#endif
|
|
{
|
|
gdisp_lld_write_stop(g);
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_HARDWARE_STREAM_WRITE != GFXON && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS
|
|
#if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->blit)
|
|
#endif
|
|
{
|
|
if (g->p.cx) {
|
|
g->p.x1 = 0;
|
|
g->p.y1 = 0;
|
|
g->p.ptr = (void *)g->linebuf;
|
|
gdisp_lld_blit_area(g);
|
|
}
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_HARDWARE_STREAM_WRITE != GFXON && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != GFXON) && GDISP_HARDWARE_FILLS
|
|
// We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming.
|
|
#if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->fill)
|
|
#endif
|
|
{
|
|
if (g->p.cx) {
|
|
if (g->p.cx == 1)
|
|
gdisp_lld_draw_pixel(g);
|
|
else
|
|
gdisp_lld_fill_area(g);
|
|
}
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_HARDWARE_STREAM_WRITE != GFXON && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != GFXON) && GDISP_HARDWARE_FILLS != GFXON
|
|
{
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
void gdispGDrawPixel(GDisplay *g, gCoord x, gCoord y, gColor color) {
|
|
MUTEX_ENTER(g);
|
|
g->p.x = x;
|
|
g->p.y = y;
|
|
g->p.color = color;
|
|
drawpixel_clip(g);
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
|
|
void gdispGDrawLine(GDisplay *g, gCoord x0, gCoord y0, gCoord x1, gCoord y1, gColor color) {
|
|
MUTEX_ENTER(g);
|
|
g->p.x = x0;
|
|
g->p.y = y0;
|
|
g->p.x1 = x1;
|
|
g->p.y1 = y1;
|
|
g->p.color = color;
|
|
line_clip(g);
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
|
|
void gdispGClear(GDisplay *g, gColor color) {
|
|
// Note - clear() ignores the clipping area. It clears the screen.
|
|
MUTEX_ENTER(g);
|
|
|
|
// Best is hardware accelerated clear
|
|
#if GDISP_HARDWARE_CLEARS
|
|
#if GDISP_HARDWARE_CLEARS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->clear)
|
|
#endif
|
|
{
|
|
g->p.color = color;
|
|
gdisp_lld_clear(g);
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Next best is hardware accelerated area fill
|
|
#if GDISP_HARDWARE_CLEARS != GFXON && GDISP_HARDWARE_FILLS
|
|
#if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->fill)
|
|
#endif
|
|
{
|
|
g->p.x = g->p.y = 0;
|
|
g->p.cx = g->g.Width;
|
|
g->p.cy = g->g.Height;
|
|
g->p.color = color;
|
|
gdisp_lld_fill_area(g);
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Next best is streaming
|
|
#if GDISP_HARDWARE_CLEARS != GFXON && GDISP_HARDWARE_FILLS != GFXON && GDISP_HARDWARE_STREAM_WRITE
|
|
#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->writestart)
|
|
#endif
|
|
{
|
|
gU32 area;
|
|
|
|
g->p.x = g->p.y = 0;
|
|
g->p.cx = g->g.Width;
|
|
g->p.cy = g->g.Height;
|
|
g->p.color = color;
|
|
area = (gU32)g->p.cx * g->p.cy;
|
|
|
|
gdisp_lld_write_start(g);
|
|
#if GDISP_HARDWARE_STREAM_POS
|
|
#if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->writepos)
|
|
#endif
|
|
gdisp_lld_write_pos(g);
|
|
#endif
|
|
for(; area; area--)
|
|
gdisp_lld_write_color(g);
|
|
gdisp_lld_write_stop(g);
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Worst is drawing pixels
|
|
#if GDISP_HARDWARE_CLEARS != GFXON && GDISP_HARDWARE_FILLS != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && GDISP_HARDWARE_DRAWPIXEL
|
|
// The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
|
|
//#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
|
|
// if (gvmt(g)->pixel)
|
|
//#endif
|
|
{
|
|
g->p.color = color;
|
|
for(g->p.y = 0; g->p.y < g->g.Height; g->p.y++)
|
|
for(g->p.x = 0; g->p.x < g->g.Width; g->p.x++)
|
|
gdisp_lld_draw_pixel(g);
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void gdispGFillArea(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gColor color) {
|
|
MUTEX_ENTER(g);
|
|
g->p.x = x;
|
|
g->p.y = y;
|
|
g->p.cx = cx;
|
|
g->p.cy = cy;
|
|
g->p.color = color;
|
|
TEST_CLIP_AREA(g) {
|
|
fillarea(g);
|
|
}
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
|
|
void gdispGBlitArea(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gCoord srcx, gCoord srcy, gCoord srccx, const gPixel *buffer) {
|
|
MUTEX_ENTER(g);
|
|
|
|
#if NEED_CLIPPING
|
|
#if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
|
|
if (!gvmt(g)->setclip)
|
|
#endif
|
|
{
|
|
// This is a different clipping to fillarea(g) as it needs to take into account srcx,srcy
|
|
if (x < g->clipx0) { cx -= g->clipx0 - x; srcx += g->clipx0 - x; x = g->clipx0; }
|
|
if (y < g->clipy0) { cy -= g->clipy0 - y; srcy += g->clipy0 - x; y = g->clipy0; }
|
|
if (x+cx > g->clipx1) cx = g->clipx1 - x;
|
|
if (y+cy > g->clipy1) cy = g->clipy1 - y;
|
|
if (srcx+cx > srccx) cx = srccx - srcx;
|
|
if (cx <= 0 || cy <= 0) { MUTEX_EXIT(g); return; }
|
|
}
|
|
#endif
|
|
|
|
// Best is hardware bitfills
|
|
#if GDISP_HARDWARE_BITFILLS
|
|
#if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->blit)
|
|
#endif
|
|
{
|
|
g->p.x = x;
|
|
g->p.y = y;
|
|
g->p.cx = cx;
|
|
g->p.cy = cy;
|
|
g->p.x1 = srcx;
|
|
g->p.y1 = srcy;
|
|
g->p.x2 = srccx;
|
|
g->p.ptr = (void *)buffer;
|
|
gdisp_lld_blit_area(g);
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Next best is hardware streaming
|
|
#if GDISP_HARDWARE_BITFILLS != GFXON && GDISP_HARDWARE_STREAM_WRITE
|
|
#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->writestart)
|
|
#endif
|
|
{
|
|
// Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap
|
|
buffer += srcy*srccx+srcx;
|
|
srcx = x + cx;
|
|
srcy = y + cy;
|
|
srccx -= cx;
|
|
|
|
g->p.x = x;
|
|
g->p.y = y;
|
|
g->p.cx = cx;
|
|
g->p.cy = cy;
|
|
gdisp_lld_write_start(g);
|
|
#if GDISP_HARDWARE_STREAM_POS
|
|
#if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->writepos)
|
|
#endif
|
|
gdisp_lld_write_pos(g);
|
|
#endif
|
|
for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) {
|
|
for(g->p.x = x; g->p.x < srcx; g->p.x++) {
|
|
g->p.color = *buffer++;
|
|
gdisp_lld_write_color(g);
|
|
}
|
|
}
|
|
gdisp_lld_write_stop(g);
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Only slightly better than drawing pixels is to look for runs and use fill area
|
|
#if GDISP_HARDWARE_BITFILLS != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && GDISP_HARDWARE_FILLS
|
|
// We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming.
|
|
#if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->fill)
|
|
#endif
|
|
{
|
|
// Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap
|
|
buffer += srcy*srccx+srcx;
|
|
srcx = x + cx;
|
|
srcy = y + cy;
|
|
srccx -= cx;
|
|
|
|
g->p.cy = 1;
|
|
for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) {
|
|
for(g->p.x=x; g->p.x < srcx; g->p.x += g->p.cx) {
|
|
g->p.cx=1;
|
|
g->p.color = *buffer++;
|
|
while(g->p.x+g->p.cx < srcx && *buffer == g->p.color) {
|
|
g->p.cx++;
|
|
buffer++;
|
|
}
|
|
if (g->p.cx == 1) {
|
|
gdisp_lld_draw_pixel(g);
|
|
} else {
|
|
gdisp_lld_fill_area(g);
|
|
}
|
|
}
|
|
}
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Worst is drawing pixels
|
|
#if GDISP_HARDWARE_BITFILLS != GFXON && GDISP_HARDWARE_STREAM_WRITE != GFXON && GDISP_HARDWARE_FILLS != GFXON && GDISP_HARDWARE_DRAWPIXEL
|
|
// The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming
|
|
//#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT
|
|
// if (gvmt(g)->pixel)
|
|
//#endif
|
|
{
|
|
// Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap
|
|
buffer += srcy*srccx+srcx;
|
|
srcx = x + cx;
|
|
srcy = y + cy;
|
|
srccx -= cx;
|
|
|
|
for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) {
|
|
for(g->p.x=x; g->p.x < srcx; g->p.x++) {
|
|
g->p.color = *buffer++;
|
|
gdisp_lld_draw_pixel(g);
|
|
}
|
|
}
|
|
autoflush_stopdone(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION
|
|
void gdispGSetClip(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy) {
|
|
MUTEX_ENTER(g);
|
|
|
|
// Best is using hardware clipping
|
|
#if GDISP_HARDWARE_CLIP
|
|
#if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
|
|
if (gvmt(g)->setclip)
|
|
#endif
|
|
{
|
|
g->p.x = x;
|
|
g->p.y = y;
|
|
g->p.cx = cx;
|
|
g->p.cy = cy;
|
|
gdisp_lld_set_clip(g);
|
|
}
|
|
#if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT
|
|
else
|
|
#endif
|
|
#endif
|
|
|
|
// Worst is using software clipping
|
|
#if GDISP_HARDWARE_CLIP != GFXON
|
|
{
|
|
if (x < 0) { cx += x; x = 0; }
|
|
if (y < 0) { cy += y; y = 0; }
|
|
if (cx <= 0 || cy <= 0 || x >= g->g.Width || y >= g->g.Height) { x = y = cx = cy = 0; }
|
|
g->clipx0 = x;
|
|
g->clipy0 = y;
|
|
g->clipx1 = x+cx; if (g->clipx1 > g->g.Width) g->clipx1 = g->g.Width;
|
|
g->clipy1 = y+cy; if (g->clipy1 > g->g.Height) g->clipy1 = g->g.Height;
|
|
}
|
|
#endif
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_NEED_CIRCLE
|
|
void gdispGDrawCircle(GDisplay *g, gCoord x, gCoord y, gCoord radius, gColor color) {
|
|
gCoord a, b, P;
|
|
|
|
MUTEX_ENTER(g);
|
|
|
|
// Calculate intermediates
|
|
a = 1;
|
|
b = radius;
|
|
P = 4 - radius;
|
|
g->p.color = color;
|
|
|
|
// Away we go using Bresenham's circle algorithm
|
|
// Optimized to prevent double drawing
|
|
g->p.x = x; g->p.y = y + b; drawpixel_clip(g);
|
|
g->p.x = x; g->p.y = y - b; drawpixel_clip(g);
|
|
g->p.x = x + b; g->p.y = y; drawpixel_clip(g);
|
|
g->p.x = x - b; g->p.y = y; drawpixel_clip(g);
|
|
do {
|
|
g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g);
|
|
g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g);
|
|
g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g);
|
|
g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g);
|
|
g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g);
|
|
g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g);
|
|
g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g);
|
|
g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g);
|
|
if (P < 0)
|
|
P += 3 + 2*a++;
|
|
else
|
|
P += 5 + 2*(a++ - b--);
|
|
} while(a < b);
|
|
g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g);
|
|
g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g);
|
|
g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g);
|
|
g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g);
|
|
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_NEED_CIRCLE
|
|
void gdispGFillCircle(GDisplay *g, gCoord x, gCoord y, gCoord radius, gColor color) {
|
|
gCoord a, b, P;
|
|
|
|
MUTEX_ENTER(g);
|
|
|
|
// Calculate intermediates
|
|
a = 1;
|
|
b = radius;
|
|
P = 4 - radius;
|
|
g->p.color = color;
|
|
|
|
// Away we go using Bresenham's circle algorithm
|
|
// This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value
|
|
g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g);
|
|
g->p.y = y+b; g->p.x = x; drawpixel_clip(g);
|
|
g->p.y = y-b; g->p.x = x; drawpixel_clip(g);
|
|
do {
|
|
g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g);
|
|
g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g);
|
|
if (P < 0) {
|
|
P += 3 + 2*a++;
|
|
} else {
|
|
g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g);
|
|
g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g);
|
|
P += 5 + 2*(a++ - b--);
|
|
}
|
|
} while(a < b);
|
|
g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g);
|
|
g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g);
|
|
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_NEED_DUALCIRCLE
|
|
|
|
#define DRAW_DUALLINE(yval, r1, r2) \
|
|
g->p.y = yval; \
|
|
g->p.x = x-r1; g->p.x1 = x-r2+1; hline_clip(g); \
|
|
g->p.x = x-r2; g->p.x1 = x+r2; g->p.color = color2; hline_clip(g); \
|
|
g->p.x = x+r2+1; g->p.x1 = x+r1; g->p.color = color1; hline_clip(g)
|
|
#define DRAW_SINGLELINE(yval, r) g->p.y = yval; g->p.x = x-r; g->p.x1 = x+r; hline_clip(g)
|
|
|
|
void gdispGFillDualCircle(GDisplay *g, gCoord x, gCoord y, gCoord radius1, gColor color1, gCoord radius2, gColor color2) {
|
|
gCoord a, b1, b2, p1, p2;
|
|
|
|
MUTEX_ENTER(g);
|
|
|
|
// Do the combined circle where the inner circle < 45 deg (and outer circle)
|
|
g->p.color = color1;
|
|
a = 0; b1 = radius1; b2 = radius2; p1 = p2 = 1;
|
|
do {
|
|
DRAW_DUALLINE(y+a, b1, b2);
|
|
DRAW_DUALLINE(y-a, b1, b2);
|
|
if (p1 >= 0) p1 -= b1--;
|
|
p1 += a;
|
|
if (p2 >= 0) p2 -= b2--;
|
|
p2 += a;
|
|
} while(++a < b2);
|
|
|
|
// Do the combined circle where inner circle > 45 deg, outer circle < 45
|
|
do {
|
|
DRAW_DUALLINE(y+a, b1, b2);
|
|
DRAW_DUALLINE(y-a, b1, b2);
|
|
if (p1 >= 0) p1 -= b1--;
|
|
p1 += a;
|
|
do { p2 -= --b2; } while (p2+a >= b2);
|
|
p2 += a;
|
|
} while(++a <= radius2 && a < b1);
|
|
|
|
if (a < radius2) {
|
|
// Do the combined circle where inner circle > 45 deg, outer circle > 45
|
|
do {
|
|
DRAW_DUALLINE(y+a, b1, b2);
|
|
DRAW_DUALLINE(y-a, b1, b2);
|
|
do { p1 -= --b1; } while (p1+a >= b1);
|
|
p1 += a;
|
|
do { p2 -= --b2; } while (p2+a >= b2);
|
|
p2 += a++;
|
|
} while(b2 > 0);
|
|
|
|
} else {
|
|
// Do the outer circle above the inner circle but < 45 deg
|
|
do {
|
|
DRAW_SINGLELINE(y+a, b1);
|
|
DRAW_SINGLELINE(y-a, b1);
|
|
if (p1 >= 0) p1 -= b1--;
|
|
p1 += a++;
|
|
} while(a < b1);
|
|
DRAW_SINGLELINE(y+a, b1);
|
|
DRAW_SINGLELINE(y-a, b1);
|
|
}
|
|
|
|
// Do the top and bottom part of the outer circle (outer circle > 45deg and above inner circle)
|
|
a = 0; b1 = radius1; p1 = 1;
|
|
do {
|
|
if (p1 >= 0) {
|
|
DRAW_SINGLELINE(y+b1, a);
|
|
DRAW_SINGLELINE(y-b1, a);
|
|
p1 -= b1--;
|
|
}
|
|
p1 += a++;
|
|
} while(b1 > radius2 && a < b1);
|
|
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#undef DRAW_DUALLINE
|
|
#undef DRAW_SINGLELINE
|
|
#endif
|
|
|
|
#if GDISP_NEED_ELLIPSE
|
|
void gdispGDrawEllipse(GDisplay *g, gCoord x, gCoord y, gCoord a, gCoord b, gColor color) {
|
|
gCoord dx, dy;
|
|
gI32 a2, b2;
|
|
gI32 err, e2;
|
|
|
|
MUTEX_ENTER(g);
|
|
|
|
// Calculate intermediates
|
|
dx = 0;
|
|
dy = b;
|
|
a2 = a*a;
|
|
b2 = b*b;
|
|
err = b2-(2*b-1)*a2;
|
|
g->p.color = color;
|
|
|
|
// Away we go using Bresenham's ellipse algorithm
|
|
do {
|
|
g->p.x = x + dx; g->p.y = y + dy; drawpixel_clip(g);
|
|
g->p.x = x - dx; g->p.y = y + dy; drawpixel_clip(g);
|
|
g->p.x = x - dx; g->p.y = y - dy; drawpixel_clip(g);
|
|
g->p.x = x + dx; g->p.y = y - dy; drawpixel_clip(g);
|
|
|
|
e2 = 2*err;
|
|
if(e2 < (2*dx+1)*b2) {
|
|
dx++;
|
|
err += (2*dx+1)*b2;
|
|
}
|
|
if(e2 > -(2*dy-1)*a2) {
|
|
dy--;
|
|
err -= (2*dy-1)*a2;
|
|
}
|
|
} while(dy >= 0);
|
|
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_NEED_ELLIPSE
|
|
void gdispGFillEllipse(GDisplay *g, gCoord x, gCoord y, gCoord a, gCoord b, gColor color) {
|
|
gCoord dx, dy;
|
|
gI32 a2, b2;
|
|
gI32 err, e2;
|
|
|
|
MUTEX_ENTER(g);
|
|
|
|
// Calculate intermediates
|
|
dx = 0;
|
|
dy = b;
|
|
a2 = a*a;
|
|
b2 = b*b;
|
|
err = b2-(2*b-1)*a2;
|
|
g->p.color = color;
|
|
|
|
// Away we go using Bresenham's ellipse algorithm
|
|
// This is optimized to prevent overdrawing by drawing a line only when a y is about to change value
|
|
do {
|
|
e2 = 2*err;
|
|
if(e2 < (2*dx+1)*b2) {
|
|
dx++;
|
|
err += (2*dx+1)*b2;
|
|
}
|
|
if(e2 > -(2*dy-1)*a2) {
|
|
g->p.y = y + dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g);
|
|
if (y) { g->p.y = y - dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); }
|
|
dy--;
|
|
err -= (2*dy-1)*a2;
|
|
}
|
|
} while(dy >= 0);
|
|
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_NEED_ARCSECTORS
|
|
void gdispGDrawArcSectors(GDisplay *g, gCoord x, gCoord y, gCoord radius, gU8 sectors, gColor color) {
|
|
gCoord a, b, P;
|
|
|
|
MUTEX_ENTER(g);
|
|
|
|
// Calculate intermediates
|
|
a = 1; // x in many explanations
|
|
b = radius; // y in many explanations
|
|
P = 4 - radius;
|
|
g->p.color = color;
|
|
|
|
// Away we go using Bresenham's circle algorithm
|
|
// Optimized to prevent double drawing
|
|
if (sectors & 0x06) { g->p.x = x; g->p.y = y - b; drawpixel_clip(g); } // Upper upper
|
|
if (sectors & 0x60) { g->p.x = x; g->p.y = y + b; drawpixel_clip(g); } // Lower lower
|
|
if (sectors & 0x81) { g->p.x = x + b; g->p.y = y; drawpixel_clip(g); } // Right right
|
|
if (sectors & 0x18) { g->p.x = x - b; g->p.y = y; drawpixel_clip(g); } // Left left
|
|
|
|
do {
|
|
if (sectors & 0x01) { g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g); } // Upper right right
|
|
if (sectors & 0x02) { g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); } // Upper upper right
|
|
if (sectors & 0x04) { g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); } // Upper upper left
|
|
if (sectors & 0x08) { g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g); } // Upper left left
|
|
if (sectors & 0x10) { g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g); } // Lower left left
|
|
if (sectors & 0x20) { g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); } // Lower lower left
|
|
if (sectors & 0x40) { g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); } // Lower lower right
|
|
if (sectors & 0x80) { g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g); } // Lower right right
|
|
if (P < 0)
|
|
P += 3 + 2*a++;
|
|
else
|
|
P += 5 + 2*(a++ - b--);
|
|
} while(a < b);
|
|
|
|
if (sectors & 0xC0) { g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); } // Lower right
|
|
if (sectors & 0x03) { g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); } // Upper right
|
|
if (sectors & 0x30) { g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); } // Lower left
|
|
if (sectors & 0x0C) { g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); } // Upper left
|
|
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_NEED_ARCSECTORS
|
|
void gdispGFillArcSectors(GDisplay *g, gCoord x, gCoord y, gCoord radius, gU8 sectors, gColor color) {
|
|
gCoord a, b, P;
|
|
|
|
MUTEX_ENTER(g);
|
|
|
|
// Calculate intermediates
|
|
a = 1; // x in many explanations
|
|
b = radius; // y in many explanations
|
|
P = 4 - radius;
|
|
g->p.color = color;
|
|
|
|
// Away we go using Bresenham's circle algorithm
|
|
// Optimized to prevent double drawing
|
|
if (sectors & 0x06) { g->p.x = x; g->p.y = y - b; drawpixel_clip(g); } // Upper upper
|
|
if (sectors & 0x60) { g->p.x = x; g->p.y = y + b; drawpixel_clip(g); } // Lower lower
|
|
if (sectors & 0x81) { // Center right
|
|
g->p.y = y; g->p.x = x; g->p.x1 = x + b;
|
|
if (sectors & 0x18) g->p.x -= b; // Left right
|
|
hline_clip(g);
|
|
} else if (sectors & 0x18) { // Left center
|
|
g->p.x = x - b; g->p.x1 = x; g->p.y = y;
|
|
hline_clip(g);
|
|
}
|
|
|
|
do {
|
|
// Top half
|
|
switch(sectors & 0x0F) {
|
|
case 0x01:
|
|
g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x02:
|
|
g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
break;
|
|
case 0x03:
|
|
g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x04:
|
|
g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
break;
|
|
case 0x05:
|
|
g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x06:
|
|
g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
|
|
break;
|
|
case 0x07:
|
|
g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x - a; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x08:
|
|
g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
|
|
break;
|
|
case 0x09:
|
|
g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x0A:
|
|
g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
break;
|
|
case 0x0B:
|
|
g->p.y = y - b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x0C:
|
|
g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x - b; g->p.x1 = x; hline_clip(g);
|
|
break;
|
|
case 0x0D:
|
|
g->p.y = y - b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x - b; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x0E:
|
|
g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x - b; g->p.x1 = x + a; hline_clip(g);
|
|
break;
|
|
case 0x0F:
|
|
g->p.y = y - b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y - a; g->p.x = x - b; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
}
|
|
|
|
// Bottom half
|
|
switch((sectors & 0xF0)>>4) {
|
|
case 0x01:
|
|
g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
|
|
break;
|
|
case 0x02:
|
|
g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
break;
|
|
case 0x03:
|
|
g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x - b; g->p.x1 = x; hline_clip(g);
|
|
break;
|
|
case 0x04:
|
|
g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
break;
|
|
case 0x05:
|
|
g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
break;
|
|
case 0x06:
|
|
g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
|
|
break;
|
|
case 0x07:
|
|
g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x - b; g->p.x1 = x + a; hline_clip(g);
|
|
break;
|
|
case 0x08:
|
|
g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x09:
|
|
g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x0A:
|
|
g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x0B:
|
|
g->p.y = y + b; g->p.x = x - a; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x - b; g->p.x1 = x; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x + a; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x0C:
|
|
g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x0D:
|
|
g->p.y = y + b; g->p.x = x; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x - b; g->p.x1 = x - a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x0E:
|
|
g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x - a; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
case 0x0F:
|
|
g->p.y = y + b; g->p.x = x - a; g->p.x1 = x + a; hline_clip(g);
|
|
g->p.y = y + a; g->p.x = x - b; g->p.x1 = x + b; hline_clip(g);
|
|
break;
|
|
}
|
|
|
|
if (P < 0)
|
|
P += 3 + 2*a++;
|
|
else
|
|
P += 5 + 2*(a++ - b--);
|
|
} while(a < b);
|
|
|
|
// Top half
|
|
if (sectors & 0x02) { g->p.y = y - a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); }
|
|
else if (sectors & 0x01) { g->p.y = y - a; g->p.x = x + a; drawpixel_clip(g); }
|
|
if (sectors & 0x04) { g->p.y = y - a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); }
|
|
else if (sectors & 0x08) { g->p.y = y - a; g->p.x = x - a; drawpixel_clip(g); }
|
|
|
|
// Bottom half
|
|
if (sectors & 0x40) { g->p.y = y + a; g->p.x = x; g->p.x1 = x + a; hline_clip(g); }
|
|
else if (sectors & 0x80) { g->p.y = y + a; g->p.x = x + a; drawpixel_clip(g); }
|
|
if (sectors & 0x20) { g->p.y = y + a; g->p.x = x - a; g->p.x1 = x; hline_clip(g); }
|
|
else if (sectors & 0x10) { g->p.y = y + a; g->p.x = x - a; drawpixel_clip(g); }
|
|
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_NEED_ARC
|
|
#if (!GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG) || !GFX_USE_GMISC
|
|
#include <math.h>
|
|
#endif
|
|
|
|
void gdispGDrawArc(GDisplay *g, gCoord x, gCoord y, gCoord radius, gCoord start, gCoord end, gColor color) {
|
|
gCoord a, b, P, sedge, eedge;
|
|
gU8 full, sbit, ebit, tbit;
|
|
|
|
// Normalize the angles
|
|
if (start < 0)
|
|
start -= (start/360-1)*360;
|
|
else if (start >= 360)
|
|
start %= 360;
|
|
if (end < 0)
|
|
end -= (end/360-1)*360;
|
|
else if (end >= 360)
|
|
end %= 360;
|
|
|
|
sbit = 1<<(start/45);
|
|
ebit = 1<<(end/45);
|
|
full = 0;
|
|
if (start == end) {
|
|
full = 0xFF;
|
|
} else if (end < start) {
|
|
for(tbit=sbit<<1; tbit; tbit<<=1) full |= tbit;
|
|
for(tbit=ebit>>1; tbit; tbit>>=1) full |= tbit;
|
|
} else if (sbit < 0x80) {
|
|
for(tbit=sbit<<1; tbit < ebit; tbit<<=1) full |= tbit;
|
|
}
|
|
tbit = start%45 == 0 ? sbit : 0;
|
|
|
|
MUTEX_ENTER(g);
|
|
g->p.color = color;
|
|
|
|
if (full) {
|
|
// Draw full sectors
|
|
// Optimized to prevent double drawing
|
|
a = 1;
|
|
b = radius;
|
|
P = 4 - radius;
|
|
if (full & 0x60) { g->p.y = y+b; g->p.x = x; drawpixel_clip(g); }
|
|
if (full & 0x06) { g->p.y = y-b; g->p.x = x; drawpixel_clip(g); }
|
|
if (full & 0x81) { g->p.y = y; g->p.x = x+b; drawpixel_clip(g); }
|
|
if (full & 0x18) { g->p.y = y; g->p.x = x-b; drawpixel_clip(g); }
|
|
do {
|
|
if (full & 0x01) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); }
|
|
if (full & 0x02) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (full & 0x04) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (full & 0x08) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); }
|
|
if (full & 0x10) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); }
|
|
if (full & 0x20) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (full & 0x40) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (full & 0x80) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); }
|
|
if (P < 0)
|
|
P += 3 + 2*a++;
|
|
else
|
|
P += 5 + 2*(a++ - b--);
|
|
} while(a < b);
|
|
if (full & 0xC0) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (full & 0x0C) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (full & 0x03) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (full & 0x30) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (full == 0xFF) {
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
return;
|
|
}
|
|
}
|
|
|
|
#if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG
|
|
sedge = NONFIXED(radius * ((sbit & 0x99) ? ffsin(start) : ffcos(start)) + FIXED0_5);
|
|
eedge = NONFIXED(radius * ((ebit & 0x99) ? ffsin(end) : ffcos(end)) + FIXED0_5);
|
|
#elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG
|
|
sedge = floor(radius * ((sbit & 0x99) ? fsin(start) : fcos(start)) + 0.5);
|
|
eedge = floor(radius * ((ebit & 0x99) ? fsin(end) : fcos(end)) + 0.5);
|
|
#else
|
|
sedge = floor(radius * ((sbit & 0x99) ? sin(start*GFX_PI/180) : cos(start*GFX_PI/180)) + 0.5);
|
|
eedge = floor(radius * ((ebit & 0x99) ? sin(end*GFX_PI/180) : cos(end*GFX_PI/180)) + 0.5);
|
|
#endif
|
|
if (sbit & 0xB4) sedge = -sedge;
|
|
if (ebit & 0xB4) eedge = -eedge;
|
|
|
|
if (sbit != ebit) {
|
|
// Draw start and end sectors
|
|
// Optimized to prevent double drawing
|
|
a = 1;
|
|
b = radius;
|
|
P = 4 - radius;
|
|
if ((sbit & 0x20) || (tbit & 0x40) || (ebit & 0x40)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); }
|
|
if ((sbit & 0x02) || (tbit & 0x04) || (ebit & 0x04)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); }
|
|
if ((sbit & 0x80) || (tbit & 0x01) || (ebit & 0x01)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); }
|
|
if ((sbit & 0x08) || (tbit & 0x10) || (ebit & 0x10)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); }
|
|
do {
|
|
if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); }
|
|
if (((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); }
|
|
if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); }
|
|
if (((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); }
|
|
if (P < 0)
|
|
P += 3 + 2*a++;
|
|
else
|
|
P += 5 + 2*(a++ - b--);
|
|
} while(a < b);
|
|
if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge) || ((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge))
|
|
{ g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge) || ((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge))
|
|
{ g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge) || ((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge))
|
|
{ g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge) || ((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge))
|
|
{ g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
|
|
} else if (end < start) {
|
|
// Draw start/end sector where it is a non-internal angle
|
|
// Optimized to prevent double drawing
|
|
a = 1;
|
|
b = radius;
|
|
P = 4 - radius;
|
|
if ((sbit & 0x60) || (tbit & 0xC0)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); }
|
|
if ((sbit & 0x06) || (tbit & 0x0C)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); }
|
|
if ((sbit & 0x81) || (tbit & 0x03)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); }
|
|
if ((sbit & 0x18) || (tbit & 0x30)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); }
|
|
do {
|
|
if ((sbit & 0x01) && (a >= sedge || a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); }
|
|
if ((sbit & 0x02) && (a <= sedge || a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if ((sbit & 0x04) && (a >= sedge || a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if ((sbit & 0x08) && (a <= sedge || a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); }
|
|
if ((sbit & 0x10) && (a >= sedge || a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); }
|
|
if ((sbit & 0x20) && (a <= sedge || a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if ((sbit & 0x40) && (a >= sedge || a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if ((sbit & 0x80) && (a <= sedge || a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); }
|
|
if (P < 0)
|
|
P += 3 + 2*a++;
|
|
else
|
|
P += 5 + 2*(a++ - b--);
|
|
} while(a < b);
|
|
if (((sbit & 0x04) && (a >= sedge || a <= eedge)) || ((sbit & 0x08) && (a <= sedge || a >= eedge)))
|
|
{ g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x40) && (a >= sedge || a <= eedge)) || ((sbit & 0x80) && (a <= sedge || a >= eedge)))
|
|
{ g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (((sbit & 0x01) && (a >= sedge || a <= eedge)) || ((sbit & 0x02) && (a <= sedge || a >= eedge)))
|
|
{ g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x10) && (a >= sedge || a <= eedge)) || ((sbit & 0x20) && (a <= sedge || a >= eedge)))
|
|
{ g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
|
|
} else {
|
|
// Draw start/end sector where it is a internal angle
|
|
// Optimized to prevent double drawing
|
|
a = 1;
|
|
b = radius;
|
|
P = 4 - radius;
|
|
if (((sbit & 0x20) && !eedge) || ((sbit & 0x40) && !sedge)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (((sbit & 0x02) && !eedge) || ((sbit & 0x04) && !sedge)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x80) && !eedge) || ((sbit & 0x01) && !sedge)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); }
|
|
if (((sbit & 0x08) && !eedge) || ((sbit & 0x10) && !sedge)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); }
|
|
do {
|
|
if (((sbit & 0x01) && a >= sedge && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); }
|
|
if (((sbit & 0x02) && a <= sedge && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x04) && a >= sedge && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x08) && a <= sedge && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); }
|
|
if (((sbit & 0x10) && a >= sedge && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); }
|
|
if (((sbit & 0x20) && a <= sedge && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (((sbit & 0x40) && a >= sedge && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (((sbit & 0x80) && a <= sedge && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); }
|
|
if (P < 0)
|
|
P += 3 + 2*a++;
|
|
else
|
|
P += 5 + 2*(a++ - b--);
|
|
} while(a < b);
|
|
if (((sbit & 0x04) && a >= sedge && a <= eedge) || ((sbit & 0x08) && a <= sedge && a >= eedge))
|
|
{ g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x40) && a >= sedge && a <= eedge) || ((sbit & 0x80) && a <= sedge && a >= eedge))
|
|
{ g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); }
|
|
if (((sbit & 0x01) && a >= sedge && a <= eedge) || ((sbit & 0x02) && a <= sedge && a >= eedge))
|
|
{ g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); }
|
|
if (((sbit & 0x10) && a >= sedge && a <= eedge) || ((sbit & 0x20) && a <= sedge && a >= eedge))
|
|
{ g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); }
|
|
}
|
|
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_NEED_ARC
|
|
#if (!GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG) || !GFX_USE_GMISC
|
|
#include <math.h>
|
|
#endif
|
|
|
|
void gdispGDrawThickArc(GDisplay *g, gCoord xc, gCoord yc, gCoord radiusStart, gCoord radiusEnd, gCoord start, gCoord end, gColor color) {
|
|
gCoord x, y, d, r;
|
|
gCoord startTan, endTan, curangle;
|
|
gCoord precision = 512;
|
|
|
|
// Normalize the angles
|
|
if (start < 0)
|
|
start -= (start/360-1)*360;
|
|
else if (start >= 360)
|
|
start %= 360;
|
|
if (end < 0)
|
|
end -= (end/360-1)*360;
|
|
else if (end >= 360)
|
|
end %= 360;
|
|
|
|
#if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG
|
|
if((start / 45) % 2 == 0){
|
|
startTan = ffsin(start % 45) * precision / ffcos(start % 45) + start / 45 * precision;}
|
|
else{
|
|
startTan = ffsin(start % 45 - 45) * precision / ffcos(start % 45 - 45) + start / 45 * precision + precision;}
|
|
|
|
if((end / 45) % 2 == 0){
|
|
endTan = ffsin(end % 45) * precision / ffcos(end % 45) + end / 45 * precision;}
|
|
else{
|
|
endTan = ffsin(end % 45 - 45) * precision / ffcos(end % 45 - 45) + end / 45 * precision + precision;}
|
|
#elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG
|
|
if((start / 45) % 2 == 0){
|
|
startTan = fsin(start % 45) * precision / fcos(start % 45) + start / 45 * precision;}
|
|
else{
|
|
startTan = fsin(start % 45 - 45) * precision / fcos(start % 45 - 45) + start / 45 * precision + precision;}
|
|
|
|
if((end / 45) % 2 == 0){
|
|
endTan = fsin(end % 45) * precision / fcos(end % 45) + end / 45 * precision;}
|
|
else{
|
|
endTan = fsin(end % 45 - 45) * precision / fcos(end % 45 - 45) + end / 45 * precision + precision;}
|
|
#else
|
|
if((start / 45) % 2 == 0){
|
|
startTan = (tan((start % 45)*GFX_PI/180) + start / 45)* precision;}
|
|
else{
|
|
startTan = (1+tan((start % 45 - 45)*GFX_PI/180) + start / 45)* precision;}
|
|
|
|
if((end / 45) % 2 == 0){
|
|
endTan = (tan((end % 45) *GFX_PI/180) + end / 45) * precision;}
|
|
else{
|
|
endTan = (1+tan((end % 45 - 45) *GFX_PI/180) + end / 45) * precision;}
|
|
#endif
|
|
|
|
MUTEX_ENTER(g);
|
|
g->p.color = color;
|
|
|
|
//Draw concentric circles using Andres algorithm
|
|
for(r = radiusStart; r <= radiusEnd; r++)
|
|
{
|
|
x = 0;
|
|
y = r;
|
|
d = r - 1;
|
|
|
|
while (y >= x){
|
|
//approximate tan
|
|
curangle = x*precision/y;
|
|
|
|
if(end > start){
|
|
g->p.color = color;
|
|
//Draw points by symmetry
|
|
if(curangle > startTan && curangle < endTan){g->p.y = yc - x; g->p.x = xc + y; drawpixel_clip(g);}
|
|
if(curangle + 2*precision > startTan && curangle + 2*precision < endTan){g->p.y = yc - y; g->p.x = xc - x; drawpixel_clip(g);}
|
|
if(curangle + 4*precision > startTan && curangle + 4*precision < endTan){g->p.y = yc + x; g->p.x = xc - y; drawpixel_clip(g);}
|
|
if(curangle + 6*precision > startTan && curangle + 6*precision < endTan){g->p.y = yc + y; g->p.x = xc + x; drawpixel_clip(g);}
|
|
|
|
curangle = precision - curangle;
|
|
|
|
if(curangle + precision > startTan && curangle + precision < endTan){g->p.y = yc - y; g->p.x = xc + x; drawpixel_clip(g);}
|
|
if(curangle + 3*precision > startTan && curangle + 3*precision < endTan){g->p.y = yc - x; g->p.x = xc - y; drawpixel_clip(g);}
|
|
if(curangle + 5*precision > startTan && curangle + 5*precision < endTan){g->p.y = yc + y; g->p.x = xc - x; drawpixel_clip(g);}
|
|
if(curangle + 7*precision > startTan && curangle + 7*precision < endTan){g->p.y = yc + x; g->p.x = xc + y; drawpixel_clip(g);}
|
|
|
|
}
|
|
else{
|
|
//Draw points by symmetry
|
|
if(curangle > startTan || curangle < endTan){g->p.y = yc - x; g->p.x = xc + y; drawpixel_clip(g);}
|
|
if(curangle + 2*precision > startTan || curangle + 2*precision < endTan){g->p.y = yc - y; g->p.x = xc - x; drawpixel_clip(g);}
|
|
if(curangle + 4*precision > startTan || curangle + 4*precision < endTan){g->p.y = yc + x; g->p.x = xc - y; drawpixel_clip(g);}
|
|
if(curangle + 6*precision > startTan || curangle + 6*precision < endTan){g->p.y = yc + y; g->p.x = xc + x; drawpixel_clip(g);}
|
|
|
|
curangle = precision - curangle;
|
|
|
|
if(curangle + precision > startTan || curangle + precision < endTan){g->p.y = yc - y; g->p.x = xc + x; drawpixel_clip(g);}
|
|
if(curangle + 3*precision > startTan || curangle + 3*precision < endTan){g->p.y = yc - x; g->p.x = xc - y; drawpixel_clip(g);}
|
|
if(curangle + 5*precision > startTan || curangle + 5*precision < endTan){g->p.y = yc + y; g->p.x = xc - x; drawpixel_clip(g);}
|
|
if(curangle + 7*precision > startTan || curangle + 7*precision < endTan){g->p.y = yc + x; g->p.x = xc + y; drawpixel_clip(g);}
|
|
}
|
|
|
|
//Compute next point
|
|
if (d >= 2 * x){
|
|
d -= 2 * x + 1;
|
|
x++;
|
|
}
|
|
else if (d < 2 * (r - y)){
|
|
d += 2 * y - 1;
|
|
y--;
|
|
}
|
|
else{
|
|
d += 2 * (y - x - 1);
|
|
y--;
|
|
x++;
|
|
}
|
|
}
|
|
}
|
|
|
|
autoflush(g);
|
|
MUTEX_EXIT(g);
|
|
}
|
|
#endif
|
|
|
|
#if GDISP_NEED_ARC
|
|
void gdispGFillArc(GDisplay *g, gCoord x, gCoord y, gCoord radius, gCoord start, gCoord end, gColor color) {
|
|
gCoord a, b, P;
|
|
gCoord sy, ey;
|
|
fixed sxa, sxb, sxd, exa, exb, exd;
|
|
gU8 qtr;
|
|
|
|
MUTEX_ENTER(g);
|
|
|
|
// We add a half pixel so that we are drawing from the centre of the pixel
|
|
// instead of the left edge of the pixel. This also fixes the implied floor()
|
|
// when converting back to a gCoord
|
|
sxa = exa = FIXED(x) + FIXED0_5;
|
|
|
|
// Do the trig to get the formulas for the start and end lines.
|
|
#if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG
|
|
sxb = radius*ffcos(start); sy = NONFIXED(FIXED0_5 - radius*ffsin(start));
|
|
exb = radius*ffcos(end); ey = NONFIXED(FIXED0_5 - radius*ffsin(end));
|
|
#elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG
|
|
sxb = FP2FIXED(radius*fcos(start)); sy = floor(0.5-radius*fsin(start));
|
|
exb = FP2FIXED(radius*fcos(end)); ey = floor(0.5-radius*fsin(end));
|
|
#else
|
|
sxb = FP2FIXED(radius*cos(start*GFX_PI/180)); sy = floor(0.5-radius*sin(start*GFX_PI/180));
|
|
exb = FP2FIXED(radius*cos(end*GFX_PI/180)); ey = floor(0.5-radius*sin(end*GFX_PI/180));
|
|
#endif
|
|
sxd = sy ? sxb/sy : sxb;
|
|
exd = ey ? exb/ey : exb;
|
|
|
|
// Calculate which quarters and which direction we are traveling
|
|
qtr = 0;
|
|
if (sxb > 0) qtr |= 0x01; // S1=0001(1), S2=0000(0), S3=0010(2), S4=0011(3)
|
|
if (sy > 0) qtr |= 0x02;
|
|
if (exb > 0) qtr |= 0x04; // E1=0100(4), E2=0000(0), E3=1000(8), E4=1100(12)
|
|
if (ey > 0) qtr |= 0x08;
|
|
if (sy > ey || (sy == ey && sxb > 0)) qtr |= 0x10; // order of start and end lines
|
|
|
|
// Calculate intermediates
|
|
a = 1;
|
|
b = radius;
|
|
P = 4 - radius;
|
|
g->p.color = color;
|
|
sxb += sxa;
|
|
exb += exa;
|
|
|
|
// Away we go using Bresenham's circle algorithm
|
|
// This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value
|
|
|
|
switch(qtr) {
|
|
case 0: // S2E2 sy <= ey
|
|
case 1: // S1E2 sy <= ey
|
|
if (ey && 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;
|
|
case gOrientation0:
|
|
case gOrientation90:
|
|
case gOrientation180:
|
|
case gOrientation270:
|
|
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 */
|