From 0f3f8f68f87233bf59c3fa68c3dfe1f492a1a478 Mon Sep 17 00:00:00 2001 From: inmarket Date: Wed, 20 Aug 2014 17:42:53 +1000 Subject: Rename lots of files to help prevent compile time name conflicts. --- src/gdisp/colors.h | 377 ----- src/gdisp/fonts.c | 96 -- src/gdisp/fonts/build_fonts.sh | 2 +- src/gdisp/fonts/fonts.h | 34 +- src/gdisp/gdisp.c | 3100 ---------------------------------------- src/gdisp/gdisp_colors.h | 377 +++++ src/gdisp/gdisp_fonts.c | 96 ++ src/gdisp/gdisp_gdisp.c | 3100 ++++++++++++++++++++++++++++++++++++++++ src/gdisp/gdisp_image.c | 233 +++ src/gdisp/gdisp_image.h | 315 ++++ src/gdisp/gdisp_image_bmp.c | 904 ++++++++++++ src/gdisp/gdisp_image_gif.c | 1208 ++++++++++++++++ src/gdisp/gdisp_image_jpg.c | 19 + src/gdisp/gdisp_image_native.c | 145 ++ src/gdisp/gdisp_image_png.c | 19 + src/gdisp/image.c | 233 --- src/gdisp/image.h | 315 ---- src/gdisp/image_bmp.c | 904 ------------ src/gdisp/image_gif.c | 1208 ---------------- src/gdisp/image_jpg.c | 19 - src/gdisp/image_native.c | 145 -- src/gdisp/image_png.c | 19 - src/gdisp/sys_defs.h | 4 +- src/gdisp/sys_make.mk | 16 +- 24 files changed, 6444 insertions(+), 6444 deletions(-) delete mode 100644 src/gdisp/colors.h delete mode 100644 src/gdisp/fonts.c delete mode 100644 src/gdisp/gdisp.c create mode 100644 src/gdisp/gdisp_colors.h create mode 100644 src/gdisp/gdisp_fonts.c create mode 100644 src/gdisp/gdisp_gdisp.c create mode 100644 src/gdisp/gdisp_image.c create mode 100644 src/gdisp/gdisp_image.h create mode 100644 src/gdisp/gdisp_image_bmp.c create mode 100644 src/gdisp/gdisp_image_gif.c create mode 100644 src/gdisp/gdisp_image_jpg.c create mode 100644 src/gdisp/gdisp_image_native.c create mode 100644 src/gdisp/gdisp_image_png.c delete mode 100644 src/gdisp/image.c delete mode 100644 src/gdisp/image.h delete mode 100644 src/gdisp/image_bmp.c delete mode 100644 src/gdisp/image_gif.c delete mode 100644 src/gdisp/image_jpg.c delete mode 100644 src/gdisp/image_native.c delete mode 100644 src/gdisp/image_png.c (limited to 'src/gdisp') diff --git a/src/gdisp/colors.h b/src/gdisp/colors.h deleted file mode 100644 index 6e9a7663..00000000 --- a/src/gdisp/colors.h +++ /dev/null @@ -1,377 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/colors.h - * @brief GDISP color definitions header file. - * - * @defgroup Colors Colors - * @ingroup GDISP - * @{ - */ - -#ifndef _GDISP_COLORS_H -#define _GDISP_COLORS_H - -#include "gfx.h" - -#if GFX_USE_GDISP || defined(__DOXYGEN__) - -/** - * For pixel formats we do some assignment of codes to enable - * format auto-calculation. (Undocumented). - * 0x2RGB TRUECOLOR RGB format, R = red bits, G = green bits, B = blue bits - * 0x3RGB TRUECOLOR BGR format, R = red bits, G = green bits, B = blue bits - * 0x40XX GRAYSCALE XX = bits - * 0x60XX PALLETTE XX = bits - * 0x8XXX CUSTOM format. - */ -#define GDISP_COLORSYSTEM_MASK 0xF000 -#define GDISP_COLORSYSTEM_RGB 0x2000 -#define GDISP_COLORSYSTEM_BGR 0x3000 - -/** - * @brief Color Type Constants - * @{ - */ -#define GDISP_COLORSYSTEM_TRUECOLOR 0x2000 -#define GDISP_COLORSYSTEM_GRAYSCALE 0x4000 -#define GDISP_COLORSYSTEM_PALETTE 0x6000 -/** @} */ - -/** - * @brief Pixel Format Constants - * @{ - */ -#define GDISP_PIXELFORMAT_MONO (GDISP_COLORSYSTEM_GRAYSCALE|0x0001) -#define GDISP_PIXELFORMAT_GRAY4 (GDISP_COLORSYSTEM_GRAYSCALE|0x0002) -#define GDISP_PIXELFORMAT_GRAY16 (GDISP_COLORSYSTEM_GRAYSCALE|0x0004) -#define GDISP_PIXELFORMAT_GRAY256 (GDISP_COLORSYSTEM_GRAYSCALE|0x0008) -#define GDISP_PIXELFORMAT_RGB565 (GDISP_COLORSYSTEM_RGB|0x0565) -#define GDISP_PIXELFORMAT_BGR565 (GDISP_COLORSYSTEM_BGR|0x0565) -#define GDISP_PIXELFORMAT_RGB888 (GDISP_COLORSYSTEM_RGB|0x0888) -#define GDISP_PIXELFORMAT_BGR888 (GDISP_COLORSYSTEM_BGR|0x0888) -#define GDISP_PIXELFORMAT_RGB444 (GDISP_COLORSYSTEM_RGB|0x0444) -#define GDISP_PIXELFORMAT_BGR444 (GDISP_COLORSYSTEM_BGR|0x0444) -#define GDISP_PIXELFORMAT_RGB332 (GDISP_COLORSYSTEM_RGB|0x0332) -#define GDISP_PIXELFORMAT_BGR332 (GDISP_COLORSYSTEM_BGR|0x0332) -#define GDISP_PIXELFORMAT_RGB666 (GDISP_COLORSYSTEM_RGB|0x0666) -#define GDISP_PIXELFORMAT_BGR666 (GDISP_COLORSYSTEM_BGR|0x0666) -#define GDISP_PIXELFORMAT_ERROR 0x0000 -/** @} */ - -/** - * @name Some basic colors - * @{ - */ -#define White HTML2COLOR(0xFFFFFF) -#define Black HTML2COLOR(0x000000) -#define Gray HTML2COLOR(0x808080) -#define Grey Gray -#define Blue HTML2COLOR(0x0000FF) -#define Red HTML2COLOR(0xFF0000) -#define Fuchsia HTML2COLOR(0xFF00FF) -#define Magenta Fuchsia -#define Green HTML2COLOR(0x008000) -#define Yellow HTML2COLOR(0xFFFF00) -#define Aqua HTML2COLOR(0x00FFFF) -#define Cyan Aqua -#define Lime HTML2COLOR(0x00FF00) -#define Maroon HTML2COLOR(0x800000) -#define Navy HTML2COLOR(0x000080) -#define Olive HTML2COLOR(0x808000) -#define Purple HTML2COLOR(0x800080) -#define Silver HTML2COLOR(0xC0C0C0) -#define Teal HTML2COLOR(0x008080) -#define Orange HTML2COLOR(0xFFA500) -#define Pink HTML2COLOR(0xFFC0CB) -#define SkyBlue HTML2COLOR(0x87CEEB) -/** @} */ - -#if defined(__DOXYGEN__) - /** - * @brief The color system (grayscale, palette or truecolor) - */ - #define COLOR_SYSTEM GDISP_COLORSYSTEM_TRUECOLOR - /** - * @brief The number of bits in a color value - */ - #define COLOR_BITS 16 - /** - * @brief The number of bits for each of red, green and blue - * @{ - */ - #define COLOR_BITS_R 5 - #define COLOR_BITS_G 6 - #define COLOR_BITS_B 5 - /** @} */ - /** - * @brief The number of bits to shift each of red, green and blue to put it in the correct place in the color - * @{ - */ - #define COLOR_SHIFT_R 11 - #define COLOR_SHIFT_G 5 - #define COLOR_SHIFT_B 0 - /** @} */ - /** - * @brief Does the color need masking to remove invalid bits - */ - #define COLOR_NEEDS_MASK FALSE - /** - * @brief If the color needs masking to remove invalid bits, this is the mask - */ - #define COLOR_MASK 0xFFFF - /** - * @brief The color type - * @{ - */ - #define COLOR_TYPE uint16_t - /** @} */ - /** - * @brief The number of bits in the color type (not necessarily the same as COLOR_BITS). - */ - #define COLOR_TYPE_BITS 16 - /** - * @brief Convert a luminance (0 to 255) into a color value. - * @note The word "Luma" is used instead of grey or gray due to the spelling ambiguities of the word grey - * @note This is not a weighted luminance conversion in the color tv style. - * @note @p LUMA2COLOR() uses a linear conversion (0.33R + 0.33G + 0.33B). Note this is different to color - * tv luminance (0.26126R + 0.7152G + 0.0722B), digital tv luminance of (0.299R + 0.587G + 0.114B), or - * @p LUMA_OF() which uses (0.25R + 0.5G + 0.25B). - */ - #define LUMA2COLOR(l) ((color_t)((((l) & 0xF8)<<8) | (((l) & 0xFC)<<3) | (((l) & 0xF8)>>3))) - /** - * @brief Convert red, green, blue (each 0 to 255) into a color value. - */ - #define RGB2COLOR(r,g,b) ((color_t)((((r) & 0xF8)<<8) | (((g) & 0xFC)<<3) | (((b) & 0xF8)>>3))) - /** - * @brief Convert a 6 digit HTML code (hex) into a color value. - */ - #define HTML2COLOR(h) ((color_t)((((h) & 0xF80000)>>8) | (((h) & 0x00FC00)>>5) | (((h) & 0x0000F8)>>3))) - /** - * @brief Extract the luma/red/green/blue component (0 to 255) of a color value. - * @note This uses quick and dirty bit shifting. If you want more exact colors - * use @p EXACT_RED_OF() etc which uses multiplies and divides. For constant - * colors using @p EXACT_RED_OF() is no more expensive because the compiler - * evaluates the arithmetic. - * @note @p LUMA_OF() returns a roughly weighted luminance (0.25R + 0.5G + 0.25B). Note this is - * different to @p LUMA2COLOR() which uses a linear conversion (0.33R + 0.33G + 0.33B) and - * color tv luminance of (0.26126R + 0.7152G + 0.0722B) and digital tv luminance of (0.299R + 0.587G + 0.114B). - * @note A 5 bit color component maximum value (0x1F) converts to 0xF8 (slightly off-color) - * @{ - */ - #define LUMA_OF(c) ((RED_OF(c)+((uint16_t)GREEN_OF(c)<<1)+BLUE_OF(c))>>2) - #define RED_OF(c) (((c) & 0xF800)>>8) - #define GREEN_OF(c) (((c)&0x007E)>>3) - #define BLUE_OF(c) (((c)&0x001F)<<3) - /** @} */ - /** - * @brief Extract the exact luma/red/green/blue component (0 to 255) of a color value. - * @note This uses multiplies and divides rather than bit shifting. - * This gives exact equivalent colors at the expense of more cpu intensive - * operations. Note for constants this is no more expensive than @p REF_OF() - * because the compiler evaluates the arithmetic. - * @note @p EXACT_LUMA_OF() returns a roughly weighted luminance (0.25R + 0.5G + 0.25B). Note this is - * different to @p LUMA2COLOR() which uses a linear conversion (0.33R + 0.33G + 0.33B) and - * color tv luminance of (0.26126R + 0.7152G + 0.0722B) and digital tv luminance of (0.299R + 0.587G + 0.114B). - * @note A 5 bit color component maximum value (0x1F) converts to 0xFF (the true equivalent color) - * @{ - */ - #define EXACT_LUMA_OF(c) ((EXACT_RED_OF(c)+((uint16_t)EXACT_GREEN_OF(c)<<1)+EXACT_BLUE_OF(c))>>2) - #define EXACT_RED_OF(c) (((((c)>>11)&0x1F)*255)/31) - #define EXACT_GREEN_OF(c) (((((c)>>5)&0x3F)*255)/63) - #define EXACT_BLUE_OF(c) (((((c)>>0)&0x1F)*255)/31) - /** @} */ -#endif - -/* - * We use this big mess of macros to calculate all the components - * to prevent user errors in the color definitions. It greatly simplifies - * the above definitions and ensures a consistent implementation. - */ - -//------------------------- -// True-Color color system -//------------------------- -#if GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_TRUECOLOR - #define COLOR_SYSTEM GDISP_COLORSYSTEM_TRUECOLOR - - // Calculate the number of bits - #define COLOR_BITS_R ((GDISP_PIXELFORMAT>>8) & 0x0F) - #define COLOR_BITS_G ((GDISP_PIXELFORMAT>>4) & 0x0F) - #define COLOR_BITS_B ((GDISP_PIXELFORMAT>>0) & 0x0F) - #define COLOR_BITS (COLOR_BITS_R + COLOR_BITS_G + COLOR_BITS_B) - - // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking - #if COLOR_BITS <= 8 - #define COLOR_TYPE uint8_t - #define COLOR_TYPE_BITS 8 - #elif COLOR_BITS <= 16 - #define COLOR_TYPE uint16_t - #define COLOR_TYPE_BITS 16 - #elif COLOR_BITS <= 32 - #define COLOR_TYPE uint32_t - #define COLOR_TYPE_BITS 32 - #else - #error "GDISP: Cannot define color types with more than 32 bits" - #endif - #if COLOR_TYPE_BITS == COLOR_BITS - #define COLOR_NEEDS_MASK FALSE - #else - #define COLOR_NEEDS_MASK TRUE - #endif - #define COLOR_MASK() ((1 << COLOR_BITS)-1) - - // Calculate the component bit shifts - #if (GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_RGB - #define COLOR_SHIFT_R (COLOR_BITS_B+COLOR_BITS_G) - #define COLOR_SHIFT_G COLOR_BITS_B - #define COLOR_SHIFT_B 0 - #else - #define COLOR_SHIFT_B (COLOR_BITS_R+COLOR_BITS_G) - #define COLOR_SHIFT_G COLOR_BITS_R - #define COLOR_SHIFT_R 0 - #endif - - // Calculate RED_OF, GREEN_OF, BLUE_OF and RGB2COLOR - #if COLOR_BITS_R + COLOR_SHIFT_R == 8 - #define RED_OF(c) ((c) & (((1< 8 - #define RED_OF(c) (((c) & (((1<> (COLOR_BITS_R+COLOR_SHIFT_R-8)) - #define RGB2COLOR_R(r) (((COLOR_TYPE)((r) & (0xFF & ~((1<<(8-COLOR_BITS_R))-1)))) << (COLOR_BITS_R+COLOR_SHIFT_R-8)) - #else // COLOR_BITS_R + COLOR_SHIFT_R < 8 - #define RED_OF(c) (((c) & (((1<> (8-(COLOR_BITS_R+COLOR_SHIFT_R))) - #endif - #if COLOR_BITS_G + COLOR_SHIFT_G == 8 - #define GREEN_OF(c) ((c) & (((1< 8 - #define GREEN_OF(c) (((c) & (((1<> (COLOR_BITS_G+COLOR_SHIFT_G-8)) - #define RGB2COLOR_G(g) (((COLOR_TYPE)((g) & (0xFF & ~((1<<(8-COLOR_BITS_G))-1)))) << (COLOR_BITS_G+COLOR_SHIFT_G-8)) - #else // COLOR_BITS_G + COLOR_SHIFT_G < 8 - #define GREEN_OF(c) (((c) & (((1<> (8-(COLOR_BITS_G+COLOR_SHIFT_G))) - #endif - #if COLOR_BITS_B + COLOR_SHIFT_B == 8 - #define BLUE_OF(c) ((c) & (((1< 8 - #define BLUE_OF(c) (((c) & (((1<> (COLOR_BITS_B+COLOR_SHIFT_B-8)) - #define RGB2COLOR_B(b) (((COLOR_TYPE)((b) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1)))) << (COLOR_BITS_B+COLOR_SHIFT_B-8)) - #else // COLOR_BITS_B + COLOR_SHIFT_B < 8 - #define BLUE_OF(c) (((c) & (((1<> (8-(COLOR_BITS_B+COLOR_SHIFT_B))) - #endif - #define LUMA_OF(c) ((RED_OF(c)+((uint16_t)GREEN_OF(c)<<1)+BLUE_OF(c))>>2) - #define EXACT_RED_OF(c) (((uint16_t)(((c)>>COLOR_SHIFT_R)&((1<>COLOR_SHIFT_G)&((1<>COLOR_SHIFT_B)&((1<>2) - #define LUMA2COLOR(l) (RGB2COLOR_R(l) | RGB2COLOR_G(l) | RGB2COLOR_B(l)) - #define RGB2COLOR(r,g,b) (RGB2COLOR_R(r) | RGB2COLOR_G(g) | RGB2COLOR_B(b)) - - // Calculate HTML2COLOR - #if COLOR_BITS_R + COLOR_SHIFT_R == 24 - #define HTML2COLOR_R(h) ((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) - #elif COLOR_BITS_R + COLOR_SHIFT_R > 24 - #define HTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) << (COLOR_BITS_R+COLOR_SHIFT_R-24)) - #else // COLOR_BITS_R + COLOR_SHIFT_R < 24 - #define HTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) >> (24-(COLOR_BITS_R+COLOR_SHIFT_R))) - #endif - #if COLOR_BITS_G + COLOR_SHIFT_G == 16 - #define HTML2COLOR_G(h) ((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) - #elif COLOR_BITS_G + COLOR_SHIFT_G > 16 - #define HTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) << (COLOR_BITS_G+COLOR_SHIFT_G-16)) - #else // COLOR_BITS_G + COLOR_SHIFT_G < 16 - #define HTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) >> (16-(COLOR_BITS_G+COLOR_SHIFT_G))) - #endif - #if COLOR_BITS_B + COLOR_SHIFT_B == 8 - #define HTML2COLOR_B(h) ((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) - #elif COLOR_BITS_B + COLOR_SHIFT_B > 8 - #define HTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) << (COLOR_BITS_B+COLOR_SHIFT_B-8)) - #else // COLOR_BITS_B + COLOR_SHIFT_B < 8 - #define HTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) >> (8-(COLOR_BITS_B+COLOR_SHIFT_B))) - #endif - #define HTML2COLOR(h) ((COLOR_TYPE)(HTML2COLOR_R(h) | HTML2COLOR_G(h) | HTML2COLOR_B(h))) - -//------------------------- -// Gray-scale color system -//------------------------- -#elif (GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_GRAYSCALE - #define COLOR_SYSTEM GDISP_COLORSYSTEM_GRAYSCALE - - // Calculate the number of bits and shifts - #define COLOR_BITS (GDISP_PIXELFORMAT & 0xFF) - #define COLOR_BITS_R COLOR_BITS - #define COLOR_BITS_G COLOR_BITS - #define COLOR_BITS_B COLOR_BITS - #define COLOR_SHIFT_R 0 - #define COLOR_SHIFT_G 0 - #define COLOR_SHIFT_B 0 - - // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking - #if COLOR_BITS <= 8 - #define COLOR_TYPE uint8_t - #define COLOR_TYPE_BITS 8 - #else - #error "GDISP: Cannot define gray-scale color types with more than 8 bits" - #endif - #if COLOR_TYPE_BITS == COLOR_BITS - #define COLOR_NEEDS_MASK FALSE - #else - #define COLOR_NEEDS_MASK TRUE - #endif - #define COLOR_MASK() ((1 << COLOR_BITS)-1) - - #if COLOR_BITS == 1 - #define RGB2COLOR(r,g,b) (((r)|(g)|(b)) ? 1 : 0) - #define LUMA2COLOR(l) ((l) ? 1 : 0) - #define HTML2COLOR(h) ((h) ? 1 : 0) - #define LUMA_OF(c) ((c) ? 255 : 0) - #define EXACT_LUMA_OF(c) LUMA_OF(c) - #else - // They eye is more sensitive to green - #define RGB2COLOR(r,g,b) ((COLOR_TYPE)(((uint16_t)(r)+(g)+(g)+(b)) >> (10-COLOR_BITS))) - #define LUMA2COLOR(l) ((COLOR_TYPE)((l)>>(8-COLOR_BITS))) - #define HTML2COLOR(h) ((COLOR_TYPE)(((((h)&0xFF0000)>>16)+(((h)&0x00FF00)>>7)+((h)&0x0000FF)) >> (10-COLOR_BITS))) - #define LUMA_OF(c) (((c) & ((1<next) { - if (matchfont(name, fp->font->full_name)) - return fp->font; - } - - // Try the short names if no long names match - for(fp = mf_get_font_list(); fp; fp = fp->next) { - if (matchfont(name, fp->font->short_name)) - return fp->font; - } - - /* Return default font.. better than nothing. */ - return mf_get_font_list()->font; -} - -void gdispCloseFont(font_t font) { - if (font->flags & FONT_FLAG_DYNAMIC) - { - struct mf_font_s *dfont = (struct mf_font_s *)font; - - /* Make sure that no-one can successfully use font after closing */ - dfont->render_character = 0; - - /* Release the allocated memory */ - gfxFree(dfont); - } -} - -font_t gdispScaleFont(font_t font, uint8_t scale_x, uint8_t scale_y) -{ - struct mf_scaledfont_s *newfont = gfxAlloc(sizeof(struct mf_scaledfont_s)); - mf_scale_font(newfont, font, scale_x, scale_y); - return (font_t)newfont; -} - -const char *gdispGetFontName(font_t font) { - return font->short_name; -} - -#endif /* GFX_USE_GDISP && GDISP_NEED_TEXT */ -/** @} */ diff --git a/src/gdisp/fonts/build_fonts.sh b/src/gdisp/fonts/build_fonts.sh index 4ce93a40..b40f7fb6 100644 --- a/src/gdisp/fonts/build_fonts.sh +++ b/src/gdisp/fonts/build_fonts.sh @@ -72,7 +72,7 @@ for file in *.c; do defname='GDISP_INCLUDE_FONT_'$upper echo '#if defined('$defname') && '$defname >> fonts.h echo '#define GDISP_FONT_FOUND' >> fonts.h - echo '#include "src/gdisp/fonts/'$file'"' >> fonts.h + echo '#include "'$file'"' >> fonts.h echo '#endif' >> fonts.h done diff --git a/src/gdisp/fonts/fonts.h b/src/gdisp/fonts/fonts.h index af851dbd..253d0e2d 100644 --- a/src/gdisp/fonts/fonts.h +++ b/src/gdisp/fonts/fonts.h @@ -3,87 +3,87 @@ #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS10) && GDISP_INCLUDE_FONT_DEJAVUSANS10 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans10.c" +#include "DejaVuSans10.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS12_AA) && GDISP_INCLUDE_FONT_DEJAVUSANS12_AA #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans12_aa.c" +#include "DejaVuSans12_aa.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS12) && GDISP_INCLUDE_FONT_DEJAVUSANS12 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans12.c" +#include "DejaVuSans12.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS16_AA) && GDISP_INCLUDE_FONT_DEJAVUSANS16_AA #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans16_aa.c" +#include "DejaVuSans16_aa.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS16) && GDISP_INCLUDE_FONT_DEJAVUSANS16 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans16.c" +#include "DejaVuSans16.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS24_AA) && GDISP_INCLUDE_FONT_DEJAVUSANS24_AA #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans24_aa.c" +#include "DejaVuSans24_aa.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS24) && GDISP_INCLUDE_FONT_DEJAVUSANS24 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans24.c" +#include "DejaVuSans24.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS32_AA) && GDISP_INCLUDE_FONT_DEJAVUSANS32_AA #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans32_aa.c" +#include "DejaVuSans32_aa.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANS32) && GDISP_INCLUDE_FONT_DEJAVUSANS32 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSans32.c" +#include "DejaVuSans32.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12_AA) && GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12_AA #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSansBold12_aa.c" +#include "DejaVuSansBold12_aa.c" #endif #if defined(GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12) && GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/DejaVuSansBold12.c" +#include "DejaVuSansBold12.c" #endif #if defined(GDISP_INCLUDE_FONT_FIXED_10X20) && GDISP_INCLUDE_FONT_FIXED_10X20 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/fixed_10x20.c" +#include "fixed_10x20.c" #endif #if defined(GDISP_INCLUDE_FONT_FIXED_5X8) && GDISP_INCLUDE_FONT_FIXED_5X8 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/fixed_5x8.c" +#include "fixed_5x8.c" #endif #if defined(GDISP_INCLUDE_FONT_FIXED_7X14) && GDISP_INCLUDE_FONT_FIXED_7X14 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/fixed_7x14.c" +#include "fixed_7x14.c" #endif #if defined(GDISP_INCLUDE_FONT_LARGENUMBERS) && GDISP_INCLUDE_FONT_LARGENUMBERS #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/LargeNumbers.c" +#include "LargeNumbers.c" #endif #if defined(GDISP_INCLUDE_FONT_UI1) && GDISP_INCLUDE_FONT_UI1 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/UI1.c" +#include "UI1.c" #endif #if defined(GDISP_INCLUDE_FONT_UI2) && GDISP_INCLUDE_FONT_UI2 #define GDISP_FONT_FOUND -#include "src/gdisp/fonts/UI2.c" +#include "UI2.c" #endif #if defined(GDISP_INCLUDE_USER_FONTS) && GDISP_INCLUDE_USER_FONTS diff --git a/src/gdisp/gdisp.c b/src/gdisp/gdisp.c deleted file mode 100644 index b8b4a847..00000000 --- a/src/gdisp/gdisp.c +++ /dev/null @@ -1,3100 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/gdisp.c - * @brief GDISP Driver code. - * - * @addtogroup GDISP - * @{ - */ -#include "gfx.h" - -#if GFX_USE_GDISP - -/* Include the low level driver information */ -#include "src/gdisp/driver.h" - -#if 1 - #undef INLINE - #if defined(__KEIL__) || defined(__C51__) - #define INLINE __inline - #else - #define INLINE inline - #endif -#else - #undef INLINE - #define INLINE -#endif - -// Number of milliseconds for the startup logo - 0 means disabled. -#if GDISP_NEED_STARTUP_LOGO - #define GDISP_STARTUP_LOGO_TIMEOUT 1000 -#else - #define GDISP_STARTUP_LOGO_TIMEOUT 0 -#endif - -// The color to clear the display on startup -#define GDISP_STARTUP_COLOR Black - -/*===========================================================================*/ -/* Driver local variables. */ -/*===========================================================================*/ - -// The controller array, the display array and the default display -#if GDISP_TOTAL_CONTROLLERS > 1 - typedef const struct GDISPVMT const VMTEL[1]; - extern VMTEL GDISP_CONTROLLER_LIST; - static const struct GDISPVMT const * ControllerList[GDISP_TOTAL_CONTROLLERS] = {GDISP_CONTROLLER_LIST}; - static const unsigned DisplayCountList[GDISP_TOTAL_CONTROLLERS] = {GDISP_CONTROLLER_DISPLAYS}; -#endif - -#if GDISP_NEED_TIMERFLUSH - static GTimer FlushTimer; -#endif - -static GDisplay GDisplayArray[GDISP_TOTAL_DISPLAYS]; -GDisplay *GDISP = GDisplayArray; - -#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) -#else - #define MUTEX_INIT(g) - #define MUTEX_ENTER(g) - #define MUTEX_EXIT(g) -#endif - -#define NEED_CLIPPING (GDISP_HARDWARE_CLIP != TRUE && (GDISP_NEED_VALIDATION || GDISP_NEED_CLIP)) - -#if !NEED_CLIPPING - #define TEST_CLIP_AREA(g) -#elif GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - #define TEST_CLIP_AREA(g) \ - if (!g->vmt->setclip) { \ - if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \ - if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \ - if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \ - if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \ - } \ - if ((g)->p.cx > 0 && (g)->p.cy > 0) -#else - #define TEST_CLIP_AREA(g) \ - if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \ - if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \ - if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \ - if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \ - if ((g)->p.cx > 0 && (g)->p.cy > 0) -#endif - -/*==========================================================================*/ -/* Internal functions. */ -/*==========================================================================*/ - -#if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE - static INLINE void setglobalwindow(GDisplay *g) { - coord_t x, y; - x = g->p.x; y = g->p.y; - g->p.x = g->p.y = 0; - g->p.cx = g->g.Width; g->p.cy = g->g.Height; - gdisp_lld_write_start(g); - g->p.x = x; g->p.y = y; - g->flags |= GDISP_FLG_SCRSTREAM; - } -#endif - -#if GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT - #define autoflush_stopdone(g) if (g->vmt->flush) gdisp_lld_flush(g) -#elif GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH - #define autoflush_stopdone(g) gdisp_lld_flush(g) -#else - #define autoflush_stopdone(g) -#endif - -#if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE - #define autoflush(g) \ - { \ - if ((g->flags & GDISP_FLG_SCRSTREAM)) { \ - gdisp_lld_write_stop(g); \ - g->flags &= ~GDISP_FLG_SCRSTREAM; \ - } \ - autoflush_stopdone(g); \ - } -#else - #define autoflush(g) autoflush_stopdone(g) -#endif - -// drawpixel(g) -// Parameters: x,y -// Alters: cx, cy (if using streaming) -// Does not clip -static INLINE void drawpixel(GDisplay *g) { - - // Best is hardware accelerated pixel draw - #if GDISP_HARDWARE_DRAWPIXEL - #if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - if (g->vmt->pixel) - #endif - { - gdisp_lld_draw_pixel(g); - return; - } - #endif - - // Next best is cursor based streaming - #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (g->vmt->writepos) - #endif - { - if (!(g->flags & GDISP_FLG_SCRSTREAM)) - setglobalwindow(g); - gdisp_lld_write_pos(g); - gdisp_lld_write_color(g); - return; - } - #endif - - // Worst is general streaming - #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE - // The following test is unneeded because we are guaranteed to have streaming if we don't have drawpixel - //#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - // if (g->vmt->writestart) - //#endif - { - g->p.cx = g->p.cy = 1; - gdisp_lld_write_start(g); - gdisp_lld_write_color(g); - gdisp_lld_write_stop(g); - return; - } - #endif -} - -// drawpixel_clip(g) -// Parameters: x,y -// Alters: cx, cy (if using streaming) -#if NEED_CLIPPING - static INLINE void drawpixel_clip(GDisplay *g) { - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!g->vmt->setclip) - #endif - { - if (g->p.x < g->clipx0 || g->p.x >= g->clipx1 || g->p.y < g->clipy0 || g->p.y >= g->clipy1) - return; - } - drawpixel(g); - } -#else - #define drawpixel_clip(g) drawpixel(g) -#endif - -// fillarea(g) -// Parameters: x,y cx,cy and color -// Alters: nothing -// Note: This is not clipped -// Resets the streaming area if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set. -static INLINE void fillarea(GDisplay *g) { - - // Best is hardware accelerated area fill - #if GDISP_HARDWARE_FILLS - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (g->vmt->fill) - #endif - { - gdisp_lld_fill_area(g); - return; - } - #endif - - // Next best is hardware streaming - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (g->vmt->writestart) - #endif - { - uint32_t area; - - #if GDISP_HARDWARE_STREAM_POS - if ((g->flags & GDISP_FLG_SCRSTREAM)) { - gdisp_lld_write_stop(g); - g->flags &= ~GDISP_FLG_SCRSTREAM; - } - #endif - - area = (uint32_t)g->p.cx * g->p.cy; - gdisp_lld_write_start(g); - #if GDISP_HARDWARE_STREAM_POS - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (g->vmt->writepos) - #endif - gdisp_lld_write_pos(g); - #endif - for(; area; area--) - gdisp_lld_write_color(g); - gdisp_lld_write_stop(g); - return; - } - #endif - - // Worst is pixel drawing - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (g->vmt->pixel) - //#endif - { - coord_t x0, y0, x1, y1; - - x0 = g->p.x; - y0 = g->p.y; - x1 = g->p.x + g->p.cx; - y1 = g->p.y + g->p.cy; - for(; g->p.y < y1; g->p.y++, g->p.x = x0) - for(; g->p.x < x1; g->p.x++) - gdisp_lld_draw_pixel(g); - g->p.y = y0; - return; - } - #endif -} - -// Parameters: x,y and x1 -// Alters: x,y x1,y1 cx,cy -// Assumes the window covers the screen and a write_stop() will occur later -// if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set. -static void hline_clip(GDisplay *g) { - // Swap the points if necessary so it always goes from x to x1 - if (g->p.x1 < g->p.x) { - g->p.cx = g->p.x; g->p.x = g->p.x1; g->p.x1 = g->p.cx; - } - - // Clipping - #if NEED_CLIPPING - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!g->vmt->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 (g->vmt->fill) - #endif - { - g->p.cx = g->p.x1 - g->p.x + 1; - g->p.cy = 1; - gdisp_lld_fill_area(g); - return; - } - #endif - - // Next best is cursor based streaming - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (g->vmt->writepos) - #endif - { - if (!(g->flags & GDISP_FLG_SCRSTREAM)) - setglobalwindow(g); - g->p.cx = g->p.x1 - g->p.x + 1; - gdisp_lld_write_pos(g); - do { gdisp_lld_write_color(g); } while(--g->p.cx); - return; - } - #endif - - // Next best is streaming - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (g->vmt->writestart) - #endif - { - g->p.cx = g->p.x1 - g->p.x + 1; - g->p.cy = 1; - gdisp_lld_write_start(g); - do { gdisp_lld_write_color(g); } while(--g->p.cx); - gdisp_lld_write_stop(g); - return; - } - #endif - - // Worst is drawing pixels - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (g->vmt->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 (!g->vmt->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 (g->vmt->fill) - #endif - { - g->p.cy = g->p.y1 - g->p.y + 1; - g->p.cx = 1; - gdisp_lld_fill_area(g); - return; - } - #endif - - // Next best is streaming - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (g->vmt->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 (g->vmt->writepos) - #endif - gdisp_lld_write_pos(g); - #endif - do { gdisp_lld_write_color(g); } while(--g->p.cy); - gdisp_lld_write_stop(g); - return; - } - #endif - - // Worst is drawing pixels - #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (g->vmt->pixel) - //#endif - { - for(; g->p.y <= g->p.y1; g->p.y++) - gdisp_lld_draw_pixel(g); - return; - } - #endif -} - -// Parameters: x,y and x1,y1 -// Alters: x,y x1,y1 cx,cy -static void line_clip(GDisplay *g) { - int16_t dy, dx; - int16_t addx, addy; - int16_t P, diff, i; - - // Is this a horizontal line (or a point) - if (g->p.y == g->p.y1) { - hline_clip(g); - return; - } - - // Is this a vertical line (or a point) - if (g->p.x == g->p.x1) { - vline_clip(g); - return; - } - - // Not horizontal or vertical - - // Use Bresenham's line drawing algorithm. - // This should be replaced with fixed point slope based line drawing - // which is more efficient on modern processors as it branches less. - // When clipping is needed, all the clipping could also be done up front - // instead of on each pixel. - - if (g->p.x1 >= g->p.x) { - dx = g->p.x1 - g->p.x; - addx = 1; - } else { - dx = g->p.x - g->p.x1; - addx = -1; - } - if (g->p.y1 >= g->p.y) { - dy = g->p.y1 - g->p.y; - addy = 1; - } else { - dy = g->p.y - g->p.y1; - addy = -1; - } - - if (dx >= dy) { - dy <<= 1; - P = dy - dx; - diff = P - dx; - - for(i=0; i<=dx; ++i) { - drawpixel_clip(g); - if (P < 0) { - P += dy; - g->p.x += addx; - } else { - P += diff; - g->p.x += addx; - g->p.y += addy; - } - } - } else { - dx <<= 1; - P = dx - dy; - diff = P - dy; - - for(i=0; i<=dy; ++i) { - drawpixel_clip(g); - if (P < 0) { - P += dx; - g->p.y += addy; - } else { - P += diff; - g->p.x += addx; - g->p.y += addy; - } - } - } -} - -#if GDISP_STARTUP_LOGO_TIMEOUT > 0 - static void StartupLogoDisplay(GDisplay *g) { - coord_t x, y, w; - const coord_t * p; - static const coord_t blks[] = { - // u - 2, 6, 1, 10, - 3, 11, 4, 1, - 6, 6, 1, 6, - // G - 8, 0, 1, 12, - 9, 0, 6, 1, - 9, 11, 6, 1, - 14, 6, 1, 5, - 12, 6, 2, 1, - // F - 16, 0, 1, 12, - 17, 0, 6, 1, - 17, 6, 3, 1, - // X - 22, 6, 7, 1, - 24, 0, 1, 6, - 22, 7, 1, 5, - 28, 0, 1, 6, - 26, 7, 1, 5, - }; - - // Get a starting position and a scale - // Work on a 8x16 grid for each char, 4 chars (uGFX) in 1 line, using half the screen - w = g->g.Width/(8*4*2); - if (!w) w = 1; - x = (g->g.Width - (8*4)*w)/2; - y = (g->g.Height - (16*1)*w)/2; - - // Simple but crude! - for(p = blks; p < blks+sizeof(blks)/sizeof(blks[0]); p+=4) - gdispGFillArea(g, x+p[0]*w, y+p[1]*w, p[2]*w, p[3]*w, Blue); - } -#endif - -#if GDISP_NEED_TIMERFLUSH - static void FlushTimerFn(void *param) { - GDisplay * g; - (void) param; - - for(g = GDisplayArray; g < &GDisplayArray[GDISP_TOTAL_DISPLAYS]; g++) - gdispGFlush(g); - } -#endif - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -void _gdispInit(void) -{ - GDisplay *g; - uint16_t i; - - /* Initialise all controllers */ - #if GDISP_TOTAL_CONTROLLERS > 1 - uint16_t j; - - for(g = GDisplayArray, j=0; j < GDISP_TOTAL_CONTROLLERS; j++) - for(i = 0; i < DisplayCountList[j]; g++, i++) { - g->vmt = ControllerList[j]; - g->systemdisplay = j*GDISP_TOTAL_CONTROLLERS+i; - g->controllerdisplay = i; - #else - for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { - g->systemdisplay = i; - g->controllerdisplay = i; - #endif - MUTEX_INIT(g); - MUTEX_ENTER(g); - g->flags = 0; - gdisp_lld_init(g); - MUTEX_EXIT(g); - } - - // Set the orientation, the clipping area, clear all the displays (and add the logo if required) - for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { - #if defined(GDISP_DEFAULT_ORIENTATION) && GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL - gdispGControl(g, GDISP_CONTROL_ORIENTATION, (void *)GDISP_DEFAULT_ORIENTATION); - #endif - #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP - gdispGSetClip(g, 0, 0, g->g.Width, g->g.Height); - #endif - gdispGClear(g, GDISP_STARTUP_COLOR); - #if GDISP_STARTUP_LOGO_TIMEOUT > 0 - StartupLogoDisplay(g); - #endif - #if GDISP_HARDWARE_FLUSH - gdispGFlush(g); - #endif - } - - // Re-clear the display after the timeout if we added the logo - #if GDISP_STARTUP_LOGO_TIMEOUT > 0 - gfxSleepMilliseconds(GDISP_STARTUP_LOGO_TIMEOUT); - for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { - gdispGClear(g, GDISP_STARTUP_COLOR); - #if GDISP_HARDWARE_FLUSH - gdispGFlush(g); - #endif - } - #endif - - // Start the automatic timer flush (if required) - #if GDISP_NEED_TIMERFLUSH - gtimerInit(&FlushTimer); - gtimerStart(&FlushTimer, FlushTimerFn, 0, TRUE, GDISP_NEED_TIMERFLUSH); - #endif -} - -void _gdispDeinit(void) -{ - /* ToDo */ -} - -GDisplay *gdispGetDisplay(unsigned display) { - if (display >= GDISP_TOTAL_DISPLAYS) - return 0; - return &GDisplayArray[display]; -} - -void gdispSetDisplay(GDisplay *g) { - if (g) GDISP = g; -} - -void gdispGFlush(GDisplay *g) { - #if GDISP_HARDWARE_FLUSH - #if GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT - if (g->vmt->flush) - #endif - { - MUTEX_ENTER(g); - gdisp_lld_flush(g); - MUTEX_EXIT(g); - } - #else - (void) g; - #endif -} - -#if GDISP_NEED_STREAMING - void gdispGStreamStart(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) { - MUTEX_ENTER(g); - - #if NEED_CLIPPING - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!g->vmt->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 (g->vmt->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 (g->vmt->writepos) - #endif - gdisp_lld_write_pos(g); - #endif - return; - } - #endif - - // Worst - save the parameters and use pixel drawing and/or area fills - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (g->vmt->pixel) - //#endif - { - // Use x,y as the current position, x1,y1 as the save position and x2,y2 as the end position, cx = bufpos - g->p.x1 = g->p.x = x; - g->p.y1 = g->p.y = y; - g->p.x2 = x + cx; - g->p.y2 = y + cy; - #if (GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS) || GDISP_HARDWARE_FILLS - g->p.cx = 0; - g->p.cy = 1; - #endif - return; - } - #endif - - // Don't release the mutex as gdispStreamEnd() will do that. - } - - void gdispGStreamColor(GDisplay *g, color_t color) { - #if !GDISP_HARDWARE_STREAM_WRITE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS - coord_t sx1, sy1; - #endif - - // Don't touch the mutex as we should already own it - - // Ignore this call if we are not streaming - if (!(g->flags & GDISP_FLG_INSTREAM)) - return; - - // Best is hardware streaming - #if GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (g->vmt->writestart) - #endif - { - g->p.color = color; - gdisp_lld_write_color(g); - return; - } - #endif - - // Next best is to use bitfills with our line buffer - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS - #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT - if (g->vmt->blit) - #endif - { - g->linebuf[g->p.cx++] = color; - if (g->p.cx >= GDISP_LINEBUF_SIZE) { - sx1 = g->p.x1; - sy1 = g->p.y1; - g->p.x1 = 0; - g->p.y1 = 0; - g->p.ptr = (void *)g->linebuf; - gdisp_lld_blit_area(g); - g->p.x1 = sx1; - g->p.y1 = sy1; - g->p.x += g->p.cx; - g->p.cx = 0; - } - - // Just wrap at end-of-line and end-of-buffer - if (g->p.x+g->p.cx >= g->p.x2) { - if (g->p.cx) { - sx1 = g->p.x1; - sy1 = g->p.y1; - g->p.x1 = 0; - g->p.y1 = 0; - g->p.ptr = (void *)g->linebuf; - gdisp_lld_blit_area(g); - g->p.x1 = sx1; - g->p.y1 = sy1; - g->p.cx = 0; - } - g->p.x = g->p.x1; - if (++g->p.y >= g->p.y2) - g->p.y = g->p.y1; - } - } - #endif - - // Only slightly better than drawing pixels is to look for runs and use fillarea - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS - // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (g->vmt->fill) - #endif - { - if (!g->p.cx || g->p.color == color) { - g->p.cx++; - g->p.color = color; - } else { - if (g->p.cx == 1) - gdisp_lld_draw_pixel(g); - else - gdisp_lld_fill_area(g); - g->p.x += g->p.cx; - g->p.color = color; - g->p.cx = 1; - } - // Just wrap at end-of-line and end-of-buffer - if (g->p.x+g->p.cx >= g->p.x2) { - if (g->p.cx) { - if (g->p.cx == 1) - gdisp_lld_draw_pixel(g); - else - gdisp_lld_fill_area(g); - g->p.cx = 0; - } - g->p.x = g->p.x1; - if (++g->p.y >= g->p.y2) - g->p.y = g->p.y1; - } - return; - } - #endif - - // Worst is using pixel drawing - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (g->vmt->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 (g->vmt->writestart) - #endif - { - gdisp_lld_write_stop(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS - #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT - if (g->vmt->blit) - #endif - { - if (g->p.cx) { - g->p.x1 = 0; - g->p.y1 = 0; - g->p.ptr = (void *)g->linebuf; - gdisp_lld_blit_area(g); - } - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS - // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (g->vmt->fill) - #endif - { - if (g->p.cx) { - if (g->p.cx == 1) - gdisp_lld_draw_pixel(g); - else - gdisp_lld_fill_area(g); - } - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE - { - autoflush_stopdone(g); - MUTEX_EXIT(g); - } - #endif - } -#endif - -void gdispGDrawPixel(GDisplay *g, coord_t x, coord_t y, color_t color) { - MUTEX_ENTER(g); - g->p.x = x; - g->p.y = y; - g->p.color = color; - drawpixel_clip(g); - autoflush(g); - MUTEX_EXIT(g); -} - -void gdispGDrawLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color) { - MUTEX_ENTER(g); - g->p.x = x0; - g->p.y = y0; - g->p.x1 = x1; - g->p.y1 = y1; - g->p.color = color; - line_clip(g); - autoflush(g); - MUTEX_EXIT(g); -} - -void gdispGClear(GDisplay *g, color_t color) { - // Note - clear() ignores the clipping area. It clears the screen. - MUTEX_ENTER(g); - - // Best is hardware accelerated clear - #if GDISP_HARDWARE_CLEARS - #if GDISP_HARDWARE_CLEARS == HARDWARE_AUTODETECT - if (g->vmt->clear) - #endif - { - g->p.color = color; - gdisp_lld_clear(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Next best is hardware accelerated area fill - #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (g->vmt->fill) - #endif - { - g->p.x = g->p.y = 0; - g->p.cx = g->g.Width; - g->p.cy = g->g.Height; - g->p.color = color; - gdisp_lld_fill_area(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Next best is streaming - #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (g->vmt->writestart) - #endif - { - uint32_t area; - - g->p.x = g->p.y = 0; - g->p.cx = g->g.Width; - g->p.cy = g->g.Height; - g->p.color = color; - area = (uint32_t)g->p.cx * g->p.cy; - - gdisp_lld_write_start(g); - #if GDISP_HARDWARE_STREAM_POS - #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT - if (g->vmt->writepos) - #endif - gdisp_lld_write_pos(g); - #endif - for(; area; area--) - gdisp_lld_write_color(g); - gdisp_lld_write_stop(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Worst is drawing pixels - #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (g->vmt->pixel) - //#endif - { - g->p.color = color; - for(g->p.y = 0; g->p.y < g->g.Height; g->p.y++) - for(g->p.x = 0; g->p.x < g->g.Width; g->p.x++) - gdisp_lld_draw_pixel(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif -} - -void gdispGFillArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { - MUTEX_ENTER(g); - g->p.x = x; - g->p.y = y; - g->p.cx = cx; - g->p.cy = cy; - g->p.color = color; - TEST_CLIP_AREA(g) { - fillarea(g); - } - autoflush_stopdone(g); - MUTEX_EXIT(g); -} - -void gdispGBlitArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) { - MUTEX_ENTER(g); - - #if NEED_CLIPPING - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!g->vmt->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 (g->vmt->blit) - #endif - { - g->p.x = x; - g->p.y = y; - g->p.cx = cx; - g->p.cy = cy; - g->p.x1 = srcx; - g->p.y1 = srcy; - g->p.x2 = srccx; - g->p.ptr = (void *)buffer; - gdisp_lld_blit_area(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Next best is hardware streaming - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (g->vmt->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 (g->vmt->writepos) - #endif - gdisp_lld_write_pos(g); - #endif - for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { - for(g->p.x = x; g->p.x < srcx; g->p.x++) { - g->p.color = *buffer++; - gdisp_lld_write_color(g); - } - } - gdisp_lld_write_stop(g); - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Only slightly better than drawing pixels is to look for runs and use fill area - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS - // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (g->vmt->fill) - #endif - { - // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap - buffer += srcy*srccx+srcx; - srcx = x + cx; - srcy = y + cy; - srccx -= cx; - - g->p.cy = 1; - for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { - for(g->p.x=x; g->p.x < srcx; g->p.x += g->p.cx) { - g->p.cx=1; - g->p.color = *buffer++; - while(g->p.x+g->p.cx < srcx && *buffer == g->p.color) { - g->p.cx++; - buffer++; - } - if (g->p.cx == 1) { - gdisp_lld_draw_pixel(g); - } else { - gdisp_lld_fill_area(g); - } - } - } - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif - - // Worst is drawing pixels - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (g->vmt->pixel) - //#endif - { - // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap - buffer += srcy*srccx+srcx; - srcx = x + cx; - srcy = y + cy; - srccx -= cx; - - for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { - for(g->p.x=x; g->p.x < srcx; g->p.x++) { - g->p.color = *buffer++; - gdisp_lld_draw_pixel(g); - } - } - autoflush_stopdone(g); - MUTEX_EXIT(g); - return; - } - #endif -} - -#if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION - void gdispGSetClip(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) { - MUTEX_ENTER(g); - - // Best is using hardware clipping - #if GDISP_HARDWARE_CLIP - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (g->vmt->setclip) - #endif - { - g->p.x = x; - g->p.y = y; - g->p.cx = cx; - g->p.cy = cy; - gdisp_lld_set_clip(g); - } - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - else - #endif - #endif - - // Worst is using software clipping - #if GDISP_HARDWARE_CLIP != TRUE - { - if (x < 0) { cx += x; x = 0; } - if (y < 0) { cy += y; y = 0; } - if (cx <= 0 || cy <= 0 || x >= g->g.Width || y >= g->g.Height) { MUTEX_EXIT(g); return; } - g->clipx0 = x; - g->clipy0 = y; - g->clipx1 = x+cx; if (g->clipx1 > g->g.Width) g->clipx1 = g->g.Width; - g->clipy1 = y+cy; if (g->clipy1 > g->g.Height) g->clipy1 = g->g.Height; - } - #endif - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_CIRCLE - void gdispGDrawCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) { - coord_t a, b, P; - - MUTEX_ENTER(g); - - // Calculate intermediates - a = 1; - b = radius; - P = 4 - radius; - g->p.color = color; - - // Away we go using Bresenham's circle algorithm - // Optimized to prevent double drawing - g->p.x = x; g->p.y = y + b; drawpixel_clip(g); - g->p.x = x; g->p.y = y - b; drawpixel_clip(g); - g->p.x = x + b; g->p.y = y; drawpixel_clip(g); - g->p.x = x - b; g->p.y = y; drawpixel_clip(g); - do { - g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); - g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); - g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g); - g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g); - g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); - g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); - g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g); - g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g); - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); - g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); - g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); - g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_CIRCLE - void gdispGFillCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) { - coord_t a, b, P; - - MUTEX_ENTER(g); - - // Calculate intermediates - a = 1; - b = radius; - P = 4 - radius; - g->p.color = color; - - // Away we go using Bresenham's circle algorithm - // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value - g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); - g->p.y = y+b; g->p.x = x; drawpixel_clip(g); - g->p.y = y-b; g->p.x = x; drawpixel_clip(g); - do { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); - if (P < 0) { - P += 3 + 2*a++; - } else { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); - P += 5 + 2*(a++ - b--); - } - } while(a < b); - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ELLIPSE - void gdispGDrawEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) { - coord_t dx, dy; - int32_t a2, b2; - int32_t err, e2; - - MUTEX_ENTER(g); - - // Calculate intermediates - dx = 0; - dy = b; - a2 = a*a; - b2 = b*b; - err = b2-(2*b-1)*a2; - g->p.color = color; - - // Away we go using Bresenham's ellipse algorithm - do { - g->p.x = x + dx; g->p.y = y + dy; drawpixel_clip(g); - g->p.x = x - dx; g->p.y = y + dy; drawpixel_clip(g); - g->p.x = x - dx; g->p.y = y - dy; drawpixel_clip(g); - g->p.x = x + dx; g->p.y = y - dy; drawpixel_clip(g); - - e2 = 2*err; - if(e2 < (2*dx+1)*b2) { - dx++; - err += (2*dx+1)*b2; - } - if(e2 > -(2*dy-1)*a2) { - dy--; - err -= (2*dy-1)*a2; - } - } while(dy >= 0); - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ELLIPSE - void gdispGFillEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) { - coord_t dx, dy; - int32_t a2, b2; - int32_t err, e2; - - MUTEX_ENTER(g); - - // Calculate intermediates - dx = 0; - dy = b; - a2 = a*a; - b2 = b*b; - err = b2-(2*b-1)*a2; - g->p.color = color; - - // Away we go using Bresenham's ellipse algorithm - // This is optimized to prevent overdrawing by drawing a line only when a y is about to change value - do { - e2 = 2*err; - if(e2 < (2*dx+1)*b2) { - dx++; - err += (2*dx+1)*b2; - } - if(e2 > -(2*dy-1)*a2) { - g->p.y = y + dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); - if (y) { g->p.y = y - dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); } - dy--; - err -= (2*dy-1)*a2; - } - } while(dy >= 0); - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ARC - #if !GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG - #include - #endif - - void gdispGDrawArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) { - coord_t a, b, P, sedge, eedge; - uint8_t full, sbit, ebit, tbit; - - // Normalize the angles - if (start < 0) - start -= (start/360-1)*360; - else if (start >= 360) - start %= 360; - if (end < 0) - end -= (end/360-1)*360; - else if (end >= 360) - end %= 360; - - sbit = 1<<(start/45); - ebit = 1<<(end/45); - full = 0; - if (start == end) { - full = 0xFF; - } else if (end < start) { - for(tbit=sbit<<1; tbit; tbit<<=1) full |= tbit; - for(tbit=ebit>>1; tbit; tbit>>=1) full |= tbit; - } else if (sbit < 0x80) { - for(tbit=sbit<<1; tbit < ebit; tbit<<=1) full |= tbit; - } - tbit = start%45 == 0 ? sbit : 0; - - MUTEX_ENTER(g); - g->p.color = color; - - if (full) { - // Draw full sectors - // Optimized to prevent double drawing - a = 1; - b = radius; - P = 4 - radius; - if (full & 0x60) { g->p.y = y+b; g->p.x = x; drawpixel_clip(g); } - if (full & 0x06) { g->p.y = y-b; g->p.x = x; drawpixel_clip(g); } - if (full & 0x81) { g->p.y = y; g->p.x = x+b; drawpixel_clip(g); } - if (full & 0x18) { g->p.y = y; g->p.x = x-b; drawpixel_clip(g); } - do { - if (full & 0x01) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } - if (full & 0x02) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (full & 0x04) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (full & 0x08) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } - if (full & 0x10) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } - if (full & 0x20) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - if (full & 0x40) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (full & 0x80) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - if (full & 0xC0) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (full & 0x0C) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (full & 0x03) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (full & 0x30) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - if (full == 0xFF) { - autoflush(g); - MUTEX_EXIT(g); - return; - } - } - - #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG - sedge = NONFIXED(radius * ((sbit & 0x99) ? ffsin(start) : ffcos(start)) + FIXED0_5); - eedge = NONFIXED(radius * ((ebit & 0x99) ? ffsin(end) : ffcos(end)) + FIXED0_5); - #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG - sedge = round(radius * ((sbit & 0x99) ? fsin(start) : fcos(start))); - eedge = round(radius * ((ebit & 0x99) ? fsin(end) : fcos(end))); - #else - sedge = round(radius * ((sbit & 0x99) ? sin(start*M_PI/180) : cos(start*M_PI/180))); - eedge = round(radius * ((ebit & 0x99) ? sin(end*M_PI/180) : cos(end*M_PI/180))); - #endif - if (sbit & 0xB4) sedge = -sedge; - if (ebit & 0xB4) eedge = -eedge; - - if (sbit != ebit) { - // Draw start and end sectors - // Optimized to prevent double drawing - a = 1; - b = radius; - P = 4 - radius; - if ((sbit & 0x20) || (tbit & 0x40) || (ebit & 0x40)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } - if ((sbit & 0x02) || (tbit & 0x04) || (ebit & 0x04)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } - if ((sbit & 0x80) || (tbit & 0x01) || (ebit & 0x01)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } - if ((sbit & 0x08) || (tbit & 0x10) || (ebit & 0x10)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } - do { - if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } - if (((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } - if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } - if (((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge) || ((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) - { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge) || ((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) - { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge) || ((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) - { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge) || ((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) - { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - } else if (end < start) { - // Draw start/end sector where it is a non-internal angle - // Optimized to prevent double drawing - a = 1; - b = radius; - P = 4 - radius; - if ((sbit & 0x60) || (tbit & 0xC0)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } - if ((sbit & 0x06) || (tbit & 0x0C)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } - if ((sbit & 0x81) || (tbit & 0x03)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } - if ((sbit & 0x18) || (tbit & 0x30)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } - do { - if ((sbit & 0x01) && (a >= sedge || a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } - if ((sbit & 0x02) && (a <= sedge || a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if ((sbit & 0x04) && (a >= sedge || a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if ((sbit & 0x08) && (a <= sedge || a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } - if ((sbit & 0x10) && (a >= sedge || a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } - if ((sbit & 0x20) && (a <= sedge || a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - if ((sbit & 0x40) && (a >= sedge || a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if ((sbit & 0x80) && (a <= sedge || a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - if (((sbit & 0x04) && (a >= sedge || a <= eedge)) || ((sbit & 0x08) && (a <= sedge || a >= eedge))) - { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x40) && (a >= sedge || a <= eedge)) || ((sbit & 0x80) && (a <= sedge || a >= eedge))) - { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x01) && (a >= sedge || a <= eedge)) || ((sbit & 0x02) && (a <= sedge || a >= eedge))) - { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x10) && (a >= sedge || a <= eedge)) || ((sbit & 0x20) && (a <= sedge || a >= eedge))) - { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - } else { - // Draw start/end sector where it is a internal angle - // Optimized to prevent double drawing - a = 1; - b = radius; - P = 4 - radius; - if (((sbit & 0x20) && !eedge) || ((sbit & 0x40) && !sedge)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x02) && !eedge) || ((sbit & 0x04) && !sedge)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x80) && !eedge) || ((sbit & 0x01) && !sedge)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } - if (((sbit & 0x08) && !eedge) || ((sbit & 0x10) && !sedge)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } - do { - if (((sbit & 0x01) && a >= sedge && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } - if (((sbit & 0x02) && a <= sedge && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x04) && a >= sedge && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x08) && a <= sedge && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } - if (((sbit & 0x10) && a >= sedge && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } - if (((sbit & 0x20) && a <= sedge && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x40) && a >= sedge && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x80) && a <= sedge && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } - if (P < 0) - P += 3 + 2*a++; - else - P += 5 + 2*(a++ - b--); - } while(a < b); - if (((sbit & 0x04) && a >= sedge && a <= eedge) || ((sbit & 0x08) && a <= sedge && a >= eedge)) - { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x40) && a >= sedge && a <= eedge) || ((sbit & 0x80) && a <= sedge && a >= eedge)) - { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } - if (((sbit & 0x01) && a >= sedge && a <= eedge) || ((sbit & 0x02) && a <= sedge && a >= eedge)) - { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } - if (((sbit & 0x10) && a >= sedge && a <= eedge) || ((sbit & 0x20) && a <= sedge && a >= eedge)) - { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } - } - - autoflush(g); - MUTEX_EXIT(g); - } -#endif - -#if GDISP_NEED_ARC - void gdispGFillArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) { - coord_t a, b, P; - coord_t sy, ey; - fixed sxa, sxb, sxd, exa, exb, exd; - uint8_t qtr; - - MUTEX_ENTER(g); - - // Do the trig to get the formulas for the start and end lines. - sxa = exa = FIXED(x)+FIXED0_5; - #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG - sxb = radius*ffcos(start); sy = -NONFIXED(radius*ffsin(start) + FIXED0_5); - exb = radius*ffcos(end); ey = -NONFIXED(radius*ffsin(end) + FIXED0_5); - #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG - sxb = FP2FIXED(radius*fcos(start)); sy = -round(radius*fsin(start)); - exb = FP2FIXED(radius*fcos(end)); ey = -round(radius*fsin(end)); - #else - sxb = FP2FIXED(radius*cos(start*M_PI/180)); sy = -round(radius*sin(start*M_PI/180)); - exb = FP2FIXED(radius*cos(end*M_PI/180)); ey = -round(radius*sin(end*M_PI/180)); - #endif - sxd = sy ? sxb/sy : sxb; - exd = ey ? exb/ey : exb; - - // Calculate which quarters and which direction we are traveling - qtr = 0; - if (sxb > 0) qtr |= 0x01; // S1=0001(1), S2=0000(0), S3=0010(2), S4=0011(3) - if (sy > 0) qtr |= 0x02; - if (exb > 0) qtr |= 0x04; // E1=0100(4), E2=0000(0), E3=1000(8), E4=1100(12) - if (ey > 0) qtr |= 0x08; - if (sy > ey) qtr |= 0x10; // order of start and end lines - - // Calculate intermediates - a = 1; - b = radius; - P = 4 - radius; - g->p.color = color; - sxb += sxa; - exb += exa; - - // Away we go using Bresenham's circle algorithm - // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value - - switch(qtr) { - case 0: // S2E2 sy <= ey - case 1: // S1E2 sy <= ey - if (ey && sy) { - g->p.x = x; g->p.x1 = x; // E2S - sxa -= sxd; exa -= exd; - } else if (sy) { - g->p.x = x-b; g->p.x1 = x; // C2S - sxa -= sxd; - } else if (ey) { - g->p.x = x; g->p.x1 = x+b; // E2C - exa -= exd; - } else { - g->p.x = x-b; g->p.x1 = x+b; // C2C - } - g->p.y = y; - hline_clip(g); - do { - if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S - sxa -= sxd; exa -= exd; - } else if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - sxa -= sxd; - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= ey) { - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S - sxb += sxd; exb += exd; - } else if (-b >= sy) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S - sxb += sxd; - } else if (qtr & 1) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S - } else if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - - case 2: // S3E2 sy <= ey - case 3: // S4E2 sy <= ey - case 6: // S3E1 sy <= ey - case 7: // S4E1 sy <= ey - case 18: // S3E2 sy > ey - case 19: // S4E2 sy > ey - case 22: // S3E1 sy > ey - case 23: // S4E1 sy > ey - g->p.y = y; g->p.x = x; g->p.x1 = x+b; hline_clip(g); // SE2C - sxa += sxd; exa -= exd; - do { - if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - exa -= exd; - } else if (!(qtr & 4)) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - sxa += sxd; - } else if (!(qtr & 1)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= ey) { - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C - exb += exd; - } else if (!(qtr & 4)) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - if (b <= sy) { - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C - sxb -= sxd; - } else if (!(qtr & 1)) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - } else if (!(qtr & 4)) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+a; hline_clip(g); // S2C - } else if (!(qtr & 1)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C - } - break; - - case 4: // S2E1 sy <= ey - case 5: // S1E1 sy <= ey - g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - do { - if (-a >= ey) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - sxa -= sxd; exa -= exd; - } else if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - sxa -= sxd; - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= ey) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C - sxb += sxd; exb += exd; - } else if (-b >= sy) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S - sxb += sxd; - } else if (qtr & 1) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= ey) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - } else if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - break; - - case 8: // S2E3 sy <= ey - case 9: // S1E3 sy <= ey - case 12: // S2E4 sy <= ey - case 13: // S1E4 sy <= ey - case 24: // S2E3 sy > ey - case 25: // S1E3 sy > ey - case 28: // S2E3 sy > ey - case 29: // S1E3 sy > ey - g->p.y = y; g->p.x = x-b; g->p.x1 = x; hline_clip(g); // C2SE - sxa -= sxd; exa += exd; - do { - if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - sxa -= sxd; - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - exa += exd; - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= sy) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S - sxb += sxd; - } else if (qtr & 1) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - if (b <= ey) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E - exb -= exd; - } else if (qtr & 4) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - } else if (qtr & 1) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C - } - break; - - case 10: // S3E3 sy <= ey - case 14: // S3E4 sy <= ey - g->p.y = y; g->p.x = x; drawpixel_clip(g); // S2E - sxa += sxd; exa += exd; - do { - if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E - sxa += sxd; exa += exd; - } else if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - exa += exd; - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (b <= sy) { - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E - sxb -= sxd; exb -= exd; - } else if (b <= ey) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E - exb -= exd; - } else if (qtr & 4) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E - } else if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - - case 11: // S4E3 sy <= ey - case 15: // S4E4 sy <= ey - g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - do { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (a <= sy) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - sxa += sxd; exa += exd; - } else if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - exa += exd; - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - if (b <= sy) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C - sxb -= sxd; exb -= exd; - } else if (b <= ey) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E - exb -= exd; - } else if (qtr & 4) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (a <= sy) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - } else if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - } else if (qtr & 4) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - - case 16: // S2E2 sy > ey - case 20: // S2E1 sy > ey - g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - sxa -= sxd; exa -= exd; - do { - if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - sxa -= sxd; exa -= exd; - } else if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - exa -= exd; - } else if (!(qtr & 4)){ - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= sy) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C - sxb += sxd; exb += exd; - } else if (-b >= ey) { - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C - exb += exd; - } else if (!(qtr & 4)){ - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= sy) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - } else if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - } else if (!(qtr & 4)){ - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - break; - - case 17: // S1E2 sy > ey - case 21: // S1E1 sy > ey - if (sy) { - g->p.x = x; g->p.x1 = x; // E2S - sxa -= sxd; exa -= exd; - } else { - g->p.x = x; g->p.x1 = x+b; // E2C - exa -= exd; - } - g->p.y = y; - hline_clip(g); - do { - if (-a >= sy) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S - sxa -= sxd; exa -= exd; - } else if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - exa -= exd; - } else if (!(qtr & 4)) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (-b >= sy) { - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S - sxb += sxd; exb += exd; - } else if (-b >= ey) { - g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C - exb += exd; - } else if (!(qtr & 4)) { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (-a >= sy) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S - } else if (-a >= ey) { - g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C - } else if (!(qtr & 4)) { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - - case 26: // S3E3 sy > ey - case 27: // S4E3 sy > ey - g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - do { - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - sxa += sxd; exa += exd; - } else if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - sxa += sxd; - } else if (!(qtr & 1)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - if (b <= ey) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C - sxb -= sxd; exb -= exd; - } else if (b <= sy) { - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C - sxb -= sxd; - } else if (!(qtr & 1)) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - if (a <= ey) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - } else if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - } else if (!(qtr & 4)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - - case 30: // S3E4 sy > ey - case 31: // S4E4 sy > ey - do { - if (a <= ey) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E - sxa += sxd; exa += exd; - } else if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - sxa += sxd; - } else if (!(qtr & 1)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - if (P < 0) { - P += 3 + 2*a++; - } else { - if (b <= ey) { - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E - sxb -= sxd; exb -= exd; - } else if (b <= sy) { - g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C - sxb -= sxd; - } else if (!(qtr & 1)) { - g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C - } - P += 5 + 2*(a++ - b--); - } - } while(a < b); - if (a <= ey) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - } else if (a <= sy) { - g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C - } else if (!(qtr & 4)) { - g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C - } - break; - } - - autoflush(g); - MUTEX_EXIT(g); - } - -#endif - -#if GDISP_NEED_ARC - void gdispGDrawRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { - if (2*radius > cx || 2*radius > cy) { - gdispGDrawBox(g, x, y, cx, cy, color); - return; - } - gdispGDrawArc(g, x+radius, y+radius, radius, 90, 180, color); - gdispGDrawLine(g, x+radius+1, y, x+cx-2-radius, y, color); - gdispGDrawArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color); - gdispGDrawLine(g, x+cx-1, y+radius+1, x+cx-1, y+cy-2-radius, color); - gdispGDrawArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); - gdispGDrawLine(g, x+radius+1, y+cy-1, x+cx-2-radius, y+cy-1, color); - gdispGDrawArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); - gdispGDrawLine(g, x, y+radius+1, x, y+cy-2-radius, color); - } -#endif - -#if GDISP_NEED_ARC - void gdispGFillRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { - coord_t radius2; - - radius2 = radius*2; - if (radius2 > cx || radius2 > cy) { - gdispGFillArea(g, x, y, cx, cy, color); - return; - } - gdispGFillArc(g, x+radius, y+radius, radius, 90, 180, color); - gdispGFillArea(g, x+radius+1, y, cx-radius2, radius, 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); - gdispGFillArea(g, x+radius+1, y+cy-radius, cx-radius2, radius, color); - gdispGFillArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); - gdispGFillArea(g, x, y+radius, cx, cy-radius2, color); - } -#endif - -#if GDISP_NEED_PIXELREAD - color_t gdispGGetPixelColor(GDisplay *g, coord_t x, coord_t y) { - color_t c; - - /* Always synchronous as it must return a value */ - MUTEX_ENTER(g); - #if GDISP_HARDWARE_PIXELREAD - #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT - if (g->vmt->get) - #endif - { - // Best is direct pixel read - g->p.x = x; - g->p.y = y; - c = gdisp_lld_get_pixel_color(g); - MUTEX_EXIT(g); - return c; - } - #endif - #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ - #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT - if (g->vmt->readcolor) - #endif - { - // Next best is hardware streaming - g->p.x = x; - g->p.y = y; - g->p.cx = 1; - g->p.cy = 1; - gdisp_lld_read_start(g); - c = gdisp_lld_read_color(g); - gdisp_lld_read_stop(g); - MUTEX_EXIT(g); - return c; - } - #endif - #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ != TRUE - #if !GDISP_HARDWARE_PIXELREAD && !GDISP_HARDWARE_STREAM_READ - // Worst is "not possible" - #error "GDISP: GDISP_NEED_PIXELREAD has been set but there is no hardware support for reading the display" - #endif - MUTEX_EXIT(g); - return 0; - #endif - } -#endif - -#if GDISP_NEED_SCROLL - void gdispGVerticalScroll(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) { - coord_t abslines; - #if GDISP_HARDWARE_SCROLL != TRUE - coord_t fy, dy, ix, fx, i, j; - #endif - - MUTEX_ENTER(g); - #if NEED_CLIPPING - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (!g->vmt->setclip) - #endif - { - if (x < g->clipx0) { cx -= g->clipx0 - x; x = g->clipx0; } - if (y < g->clipy0) { cy -= g->clipy0 - y; y = g->clipy0; } - if (!lines || cx <= 0 || cy <= 0 || x >= g->clipx1 || y >= g->clipy1) { MUTEX_EXIT(g); return; } - if (x+cx > g->clipx1) cx = g->clipx1 - x; - if (y+cy > g->clipy1) cy = g->clipy1 - y; - } - #endif - - abslines = lines < 0 ? -lines : lines; - if (abslines >= cy) { - abslines = cy; - cy = 0; - } else { - // Best is hardware scroll - #if GDISP_HARDWARE_SCROLL - #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT - if (g->vmt->vscroll) - #endif - { - g->p.x = x; - g->p.y = y; - g->p.cx = cx; - g->p.cy = cy; - g->p.y1 = lines; - g->p.color = bgcolor; - gdisp_lld_vertical_scroll(g); - cy -= abslines; - } - #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT - else - #endif - #elif GDISP_LINEBUF_SIZE == 0 - #error "GDISP: GDISP_NEED_SCROLL is set but there is no hardware support and GDISP_LINEBUF_SIZE is zero." - #endif - - // Scroll Emulation - #if GDISP_HARDWARE_SCROLL != TRUE - { - cy -= abslines; - if (lines < 0) { - fy = y+cy-1; - dy = -1; - } else { - fy = y; - dy = 1; - } - // Move the screen - one line at a time - for(i = 0; i < cy; i++, fy += dy) { - - // Handle where the buffer is smaller than a line - for(ix=0; ix < cx; ix += GDISP_LINEBUF_SIZE) { - - // Calculate the data we can move in one operation - fx = cx - ix; - if (fx > GDISP_LINEBUF_SIZE) - fx = GDISP_LINEBUF_SIZE; - - // Read one line of data from the screen - - // Best line read is hardware streaming - #if GDISP_HARDWARE_STREAM_READ - #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT - if (g->vmt->readstart) - #endif - { - g->p.x = x+ix; - g->p.y = fy+lines; - g->p.cx = fx; - g->p.cy = 1; - gdisp_lld_read_start(g); - for(j=0; j < fx; j++) - g->linebuf[j] = gdisp_lld_read_color(g); - gdisp_lld_read_stop(g); - } - #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT - else - #endif - #endif - - // Next best line read is single pixel reads - #if GDISP_HARDWARE_STREAM_READ != TRUE && GDISP_HARDWARE_PIXELREAD - #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT - if (g->vmt->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 (g->vmt->blit) - #endif - { - g->p.x = x+ix; - g->p.y = fy; - g->p.cx = fx; - g->p.cy = 1; - g->p.x1 = 0; - g->p.y1 = 0; - g->p.x2 = fx; - g->p.ptr = (void *)g->linebuf; - gdisp_lld_blit_area(g); - } - #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT - else - #endif - #endif - - // Next best line write is hardware streaming - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - if (g->vmt->writestart) - #endif - { - g->p.x = x+ix; - g->p.y = fy; - g->p.cx = fx; - g->p.cy = 1; - gdisp_lld_write_start(g); - #if GDISP_HARDWARE_STREAM_POS - gdisp_lld_write_pos(g); - #endif - for(j = 0; j < fx; j++) { - g->p.color = g->linebuf[j]; - gdisp_lld_write_color(g); - } - gdisp_lld_write_stop(g); - } - #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT - else - #endif - #endif - - // Next best line write is drawing pixels in combination with filling - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS && GDISP_HARDWARE_DRAWPIXEL - // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - if (g->vmt->fill) - #endif - { - g->p.y = fy; - g->p.cy = 1; - g->p.x = x+ix; - g->p.cx = 1; - for(j = 0; j < fx; ) { - g->p.color = g->linebuf[j]; - if (j + g->p.cx < fx && g->linebuf[j] == g->linebuf[j + g->p.cx]) - g->p.cx++; - else if (g->p.cx == 1) { - gdisp_lld_draw_pixel(g); - j++; - g->p.x++; - } else { - gdisp_lld_fill_area(g); - j += g->p.cx; - g->p.x += g->p.cx; - g->p.cx = 1; - } - } - } - #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT - else - #endif - #endif - - // Worst line write is drawing pixels - #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL - // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming - //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT - // if (g->vmt->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 (!g->vmt->control) - return; - #endif - MUTEX_ENTER(g); - g->p.x = what; - g->p.ptr = value; - if (what == GDISP_CONTROL_ORIENTATION) { - switch ((orientation_t) value) { - case GDISP_ROTATE_LANDSCAPE: - g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_0 : (void *)GDISP_ROTATE_90; - break; - case GDISP_ROTATE_PORTRAIT: - g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_90 : (void *)GDISP_ROTATE_0; - break; - default: - break; - } - } - gdisp_lld_control(g); - #if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION - if (what == GDISP_CONTROL_ORIENTATION) { - // Best is hardware clipping - #if GDISP_HARDWARE_CLIP - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - if (g->vmt->setclip) - #endif - { - g->p.x = 0; - g->p.y = 0; - g->p.cx = g->g.Width; - g->p.cy = g->g.Height; - gdisp_lld_set_clip(g); - } - #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT - else - #endif - #endif - - // Worst is software clipping - #if GDISP_HARDWARE_CLIP != TRUE - { - g->clipx0 = 0; - g->clipy0 = 0; - g->clipx1 = g->g.Width; - g->clipy1 = g->g.Height; - } - #endif - } - #endif - MUTEX_EXIT(g); - } - #else - void gdispGControl(GDisplay *g, unsigned what, void *value) { - (void)g; - (void)what; - (void)value; - /* Ignore everything */ - } - #endif -#endif - -#if GDISP_NEED_QUERY - #if GDISP_HARDWARE_QUERY - void *gdispGQuery(GDisplay *g, unsigned what) { - void *res; - - #if GDISP_HARDWARE_QUERY == HARDWARE_AUTODETECT - if (!g->vmt->query) - return -1; - #endif - MUTEX_ENTER(g); - g->p.x = (coord_t)what; - res = gdisp_lld_query(g); - MUTEX_EXIT(g); - return res; - } - #else - void *gdispGQuery(GDisplay *g, unsigned what) { - (void) what; - return (void *)-1; - } - #endif -#endif - -/*===========================================================================*/ -/* High Level Driver Routines. */ -/*===========================================================================*/ - -void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { - if (cx <= 0 || cy <= 0) return; - cx = x+cx-1; cy = y+cy-1; // cx, cy are now the end point. - - MUTEX_ENTER(g); - - g->p.color = color; - - if (cx - x > 2) { - g->p.x = x; g->p.y = y; g->p.x1 = cx; hline_clip(g); - if (y != cy) { - g->p.x = x; g->p.y = cy; g->p.x1 = cx; hline_clip(g); - if (cy - y > 2) { - y++; cy--; - g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g); - g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g); - } - } - } else { - g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g); - if (x != cx) { - g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g); - } - } - - autoflush(g); - MUTEX_EXIT(g); -} - -#if GDISP_NEED_CONVEX_POLYGON - void gdispGDrawPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) { - const point *epnt, *p; - - epnt = &pntarray[cnt-1]; - - MUTEX_ENTER(g); - g->p.color = color; - for(p = pntarray; p < epnt; p++) { - g->p.x=tx+p->x; g->p.y=ty+p->y; g->p.x1=tx+p[1].x; g->p.y1=ty+p[1].y; line_clip(g); - } - g->p.x=tx+p->x; g->p.y=ty+p->y; g->p.x1=tx+pntarray->x; g->p.y1=ty+pntarray->y; line_clip(g); - - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGFillConvexPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) { - const point *lpnt, *rpnt, *epnts; - fixed lx, rx, lk, rk; - coord_t y, ymax, lxc, rxc; - - epnts = &pntarray[cnt-1]; - - /* Find a top point */ - rpnt = pntarray; - for(lpnt=pntarray+1; lpnt <= epnts; lpnt++) { - if (lpnt->y < rpnt->y) - rpnt = lpnt; - } - lx = rx = FIXED(rpnt->x); - y = rpnt->y; - - /* Work out the slopes of the two attached line segs */ - for (lpnt = rpnt <= pntarray ? epnts : rpnt-1; lpnt->y == y; cnt--) { - if (!cnt) return; - lx = FIXED(lpnt->x); - lpnt = lpnt <= pntarray ? epnts : lpnt-1; - } - for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) { - if (!cnt) return; - rx = FIXED(rpnt->x); - rpnt = rpnt >= epnts ? pntarray : rpnt+1; - } - lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y); - rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y); - - MUTEX_ENTER(g); - g->p.color = color; - while(1) { - /* Determine our boundary */ - ymax = rpnt->y < lpnt->y ? rpnt->y : lpnt->y; - - /* Scan down the line segments until we hit a boundary */ - for(; y < ymax; y++) { - lxc = NONFIXED(lx); - rxc = NONFIXED(rx); - /* - * Doesn't print the right hand point in order to allow polygon joining. - * Also ensures that we draw from left to right with the minimum number - * of pixels. - */ - if (lxc < rxc) { - g->p.x=tx+lxc; g->p.y=ty+y; g->p.x1=tx+rxc-1; hline_clip(g); - } else if (lxc > rxc) { - g->p.x=tx+rxc; g->p.y=ty+y; g->p.x1=tx+lxc-1; hline_clip(g); - } - - lx += lk; - rx += rk; - } - - if (!cnt) { - autoflush(g); - MUTEX_EXIT(g); - return; - } - cnt--; - - /* Replace the appropriate point */ - if (ymax == lpnt->y) { - for (lpnt = lpnt <= pntarray ? epnts : lpnt-1; lpnt->y == y; cnt--) { - if (!cnt) { - autoflush(g); - MUTEX_EXIT(g); - return; - } - lx = FIXED(lpnt->x); - lpnt = lpnt <= pntarray ? epnts : lpnt-1; - } - lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y); - } else { - for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) { - if (!cnt) { - autoflush(g); - MUTEX_EXIT(g); - return; - } - rx = FIXED(rpnt->x); - rpnt = rpnt >= epnts ? pntarray : rpnt+1; - } - rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y); - } - } - } - - static int32_t rounding_div(const int32_t n, const int32_t d) - { - if ((n < 0) != (d < 0)) - return (n - d/2) / d; - else - return (n + d/2) / d; - } - - /* Find a vector (nx, ny) that is perpendicular to (dx, dy) and has length - * equal to 'norm'. */ - static void get_normal_vector(coord_t dx, coord_t dy, coord_t norm, coord_t *nx, coord_t *ny) - { - int32_t dx2, dy2, len_sq, norm_sq, norm_sq2; - int div, step, best, delta, abs_delta; - - dx2 = dx; dy2 = dy; - norm_sq = (int32_t)norm * norm; - norm_sq2 = norm_sq * 512; - - /* Scale dx2 and dy2 so that - * len_sq / 2 <= norm_sq * 512 <= len_sq * 2. - * The scaling by 512 is to yield higher accuracy in division later. */ - len_sq = dx2 * dx2 + dy2 * dy2; - - if (len_sq < norm_sq2) - { - while (len_sq && len_sq < norm_sq2) - { - len_sq <<= 2; dx2 <<= 1; dy2 <<= 1; - } - } - else if (len_sq > norm_sq2) - { - while (len_sq && len_sq > norm_sq2) - { - len_sq >>= 2; dx2 >>= 1; dy2 >>= 1; - } - } - - /* Now find the divider div so that - * len_sq / div^2 == norm_sq i.e. div = sqrt(len_sq / norm_sq) - * - * This is done using bisection search to avoid the need for floating - * point sqrt. - * - * Based on previous scaling, we know that - * len_sq / 2 <= norm_sq * 512 <=> div <= sqrt(1024) = 32 - * len_sq * 2 >= norm_sq * 512 <=> div >= sqrt(256) = 16 - */ - div = 24; step = 8; - best = 256; - - for (;;) - { - dx = dx2 / div; - dy = dy2 / div; - len_sq = dx*dx + dy*dy; - - delta = len_sq - norm_sq; - - abs_delta = (delta >= 0) ? delta : -delta; - - if (abs_delta < best) - { - *nx = dy; - *ny = -dx; - best = abs_delta; - } - - if (delta > 0) - div += step; - else if (delta < 0) - div -= step; - else if (delta == 0) - break; - - if (step == 0) - break; - else - step >>= 1; /* Do one round with step = 0 to calculate final result. */ - } - } - - void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round) { - coord_t dx, dy, nx = 0, ny = 0; - - /* Compute the direction vector for the line */ - dx = x1 - x0; - dy = y1 - y0; - - /* Draw a small dot if the line length is zero. */ - if (dx == 0 && dy == 0) - dx += 1; - - /* Compute a normal vector with length 'width'. */ - get_normal_vector(dx, dy, width, &nx, &ny); - - /* Handle 1px wide lines gracefully */ - if (nx == 0 && ny == 0) - nx = 1; - - /* Offset the x0,y0 by half the width of the line. This way we - * can keep the width of the line accurate even if it is not evenly - * divisible by 2. - */ - { - x0 -= rounding_div(nx, 2); - y0 -= rounding_div(ny, 2); - } - - /* Fill in the point array */ - if (!round) { - /* We use 4 points for the basic line shape: - * - * pt1 pt2 - * (+n) ------------------------------------ (d+n) - * | | - * (0,0) ----------------------------------- (d) - * pt0 pt3 - */ - point pntarray[4]; - - pntarray[0].x = 0; - pntarray[0].y = 0; - pntarray[1].x = nx; - pntarray[1].y = ny; - pntarray[2].x = dx + nx; - pntarray[2].y = dy + ny; - pntarray[3].x = dx; - pntarray[3].y = dy; - - gdispGFillConvexPoly(g, x0, y0, pntarray, 4, color); - } else { - /* We use 4 points for basic shape, plus 4 extra points for ends: - * - * pt3 ------------------ pt4 - * / \ - * pt2 pt5 - * | | - * pt1 pt6 - * \ / - * pt0 -------------------pt7 - */ - point pntarray[8]; - coord_t nx2, ny2; - - /* Magic numbers: - * 75/256 = sin(45) / (1 + sqrt(2)) diagonal octagon segments - * 106/256 = 1 / (1 + sqrt(2)) octagon side - * 53/256 = 0.5 / (1 + sqrt(2)) half of octagon side - * 150/256 = 1 - 1 / (1 + sqrt(2)) octagon height minus one side - */ - - /* Rotate the normal vector 45 deg counter-clockwise and reduce - * to 1 / (1 + sqrt(2)) length, for forming octagonal ends. */ - nx2 = rounding_div((nx * 75 + ny * 75), 256); - ny2 = rounding_div((-nx * 75 + ny * 75), 256); - - /* Offset and extend the line so that the center of the octagon - * is at the specified points. */ - x0 += ny * 53 / 256; - y0 -= nx * 53 / 256; - dx -= ny * 106 / 256; - dy += nx * 106 / 256; - - /* Now fill in the points by summing the calculated vectors. */ - pntarray[0].x = 0; - pntarray[0].y = 0; - pntarray[1].x = nx2; - pntarray[1].y = ny2; - pntarray[2].x = nx2 + nx * 106/256; - pntarray[2].y = ny2 + ny * 106/256; - pntarray[3].x = nx; - pntarray[3].y = ny; - pntarray[4].x = dx + nx; - pntarray[4].y = dy + ny; - pntarray[5].x = dx + nx - nx2; - pntarray[5].y = dy + ny - ny2; - pntarray[6].x = dx + nx * 150/256 - nx2; - pntarray[6].y = dy + ny * 150/256 - ny2; - pntarray[7].x = dx; - pntarray[7].y = dy; - - gdispGFillConvexPoly(g, x0, y0, pntarray, 8, color); - } - } -#endif - -#if GDISP_NEED_TEXT - #include "mcufont.h" - - #if GDISP_NEED_ANTIALIAS && GDISP_HARDWARE_PIXELREAD - static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { - #define GD ((GDisplay *)state) - if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) - return; - if (x < GD->t.clipx0) { - count -= GD->t.clipx0 - x; - x = GD->t.clipx0; - } - if (x+count > GD->t.clipx1) - count = GD->t.clipx1 - x; - if (alpha == 255) { - GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color; - hline_clip(GD); - } else { - for (; count; count--, x++) { - GD->p.x = x; GD->p.y = y; - GD->p.color = gdispBlendColor(GD->t.color, gdisp_lld_get_pixel_color(GD), alpha); - drawpixel_clip(GD); - } - } - #undef GD - } - #else - static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { - #define GD ((GDisplay *)state) - if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) - return; - if (x < GD->t.clipx0) { - count -= GD->t.clipx0 - x; - x = GD->t.clipx0; - } - if (x+count > GD->t.clipx1) - count = GD->t.clipx1 - x; - if (alpha > 0x80) { // A best approximation when using anti-aliased fonts but we can't actually draw them anti-aliased - GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color; - hline_clip(GD); - } - #undef GD - } - #endif - - #if GDISP_NEED_ANTIALIAS - static void fillcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { - #define GD ((GDisplay *)state) - if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) - return; - if (x < GD->t.clipx0) { - count -= GD->t.clipx0 - x; - x = GD->t.clipx0; - } - if (x+count > GD->t.clipx1) - count = GD->t.clipx1 - x; - if (alpha == 255) { - GD->p.color = GD->t.color; - } else { - GD->p.color = gdispBlendColor(GD->t.color, GD->t.bgcolor, alpha); - } - GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; - hline_clip(GD); - #undef GD - } - #else - #define fillcharline drawcharline - #endif - - /* Callback to render characters. */ - static uint8_t drawcharglyph(int16_t x, int16_t y, mf_char ch, void *state) { - #define GD ((GDisplay *)state) - return mf_render_character(GD->t.font, x, y, ch, drawcharline, state); - #undef GD - } - - /* Callback to render characters. */ - static uint8_t fillcharglyph(int16_t x, int16_t y, mf_char ch, void *state) { - #define GD ((GDisplay *)state) - return mf_render_character(GD->t.font, x, y, ch, fillcharline, state); - #undef GD - } - - void gdispGDrawChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color) { - MUTEX_ENTER(g); - g->t.font = font; - g->t.clipx0 = x; - g->t.clipy0 = y; - g->t.clipx1 = x + mf_character_width(font, c) + font->baseline_x; - g->t.clipy1 = y + font->height; - g->t.color = color; - mf_render_character(font, x, y, c, drawcharline, g); - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGFillChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color, color_t bgcolor) { - MUTEX_ENTER(g); - g->p.cx = mf_character_width(font, c) + font->baseline_x; - g->p.cy = font->height; - g->t.font = font; - g->t.clipx0 = g->p.x = x; - g->t.clipy0 = g->p.y = y; - g->t.clipx1 = g->p.x+g->p.cx; - g->t.clipy1 = g->p.y+g->p.cy; - g->t.color = color; - g->t.bgcolor = g->p.color = bgcolor; - - TEST_CLIP_AREA(g) { - fillarea(g); - mf_render_character(font, x, y, c, fillcharline, g); - } - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGDrawString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color) { - MUTEX_ENTER(g); - g->t.font = font; - g->t.clipx0 = x; - g->t.clipy0 = y; - g->t.clipx1 = x + mf_get_string_width(font, str, 0, 0); - g->t.clipy1 = y + font->height; - g->t.color = color; - - mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, drawcharglyph, g); - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGFillString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color, color_t bgcolor) { - MUTEX_ENTER(g); - g->p.cx = mf_get_string_width(font, str, 0, 0); - g->p.cy = font->height; - g->t.font = font; - g->t.clipx0 = g->p.x = x; - g->t.clipy0 = g->p.y = y; - g->t.clipx1 = g->p.x+g->p.cx; - g->t.clipy1 = g->p.y+g->p.cy; - g->t.color = color; - g->t.bgcolor = g->p.color = bgcolor; - - TEST_CLIP_AREA(g) { - fillarea(g); - mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, fillcharglyph, g); - } - - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGDrawStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, justify_t justify) { - MUTEX_ENTER(g); - g->t.font = font; - g->t.clipx0 = x; - g->t.clipy0 = y; - g->t.clipx1 = x+cx; - g->t.clipy1 = y+cy; - g->t.color = color; - - /* Select the anchor position */ - switch(justify) { - case justifyCenter: - x += (cx + 1) / 2; - break; - case justifyRight: - x += cx; - break; - default: // justifyLeft - x += font->baseline_x; - break; - } - y += (cy+1 - font->height)/2; - - mf_render_aligned(font, x, y, justify, str, 0, drawcharglyph, g); - - autoflush(g); - MUTEX_EXIT(g); - } - - void gdispGFillStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, color_t bgcolor, justify_t justify) { - MUTEX_ENTER(g); - g->p.cx = cx; - g->p.cy = cy; - g->t.font = font; - g->t.clipx0 = g->p.x = x; - g->t.clipy0 = g->p.y = y; - g->t.clipx1 = x+cx; - g->t.clipy1 = y+cy; - g->t.color = color; - g->t.bgcolor = g->p.color = bgcolor; - - TEST_CLIP_AREA(g) { - - // background fill - fillarea(g); - - /* Select the anchor position */ - switch(justify) { - case justifyCenter: - x += (cx + 1) / 2; - break; - case justifyRight: - x += cx; - break; - default: // justifyLeft - x += font->baseline_x; - break; - } - y += (cy+1 - font->height)/2; - - /* Render */ - mf_render_aligned(font, x, y, justify, str, 0, fillcharglyph, g); - } - - autoflush(g); - MUTEX_EXIT(g); - } - - coord_t gdispGetFontMetric(font_t font, fontmetric_t metric) { - /* No mutex required as we only read static data */ - switch(metric) { - case fontHeight: return font->height; - case fontDescendersHeight: return font->height - font->baseline_y; - case fontLineSpacing: return font->line_height; - case fontCharPadding: return 0; - case fontMinWidth: return font->min_x_advance; - case fontMaxWidth: return font->max_x_advance; - } - return 0; - } - - coord_t gdispGetCharWidth(char c, font_t font) { - /* No mutex required as we only read static data */ - return mf_character_width(font, c); - } - - coord_t gdispGetStringWidth(const char* str, font_t font) { - /* No mutex required as we only read static data */ - return mf_get_string_width(font, str, 0, 0); - } -#endif - -color_t gdispBlendColor(color_t fg, color_t bg, uint8_t alpha) -{ - uint16_t fg_ratio = alpha + 1; - uint16_t bg_ratio = 256 - alpha; - uint16_t r, g, b; - - r = RED_OF(fg) * fg_ratio; - g = GREEN_OF(fg) * fg_ratio; - b = BLUE_OF(fg) * fg_ratio; - - r += RED_OF(bg) * bg_ratio; - g += GREEN_OF(bg) * bg_ratio; - b += BLUE_OF(bg) * bg_ratio; - - r >>= 8; - g >>= 8; - b >>= 8; - - return RGB2COLOR(r, g, b); -} - -color_t gdispContrastColor(color_t color) { - uint16_t r, g, b; - - r = RED_OF(color) > 128 ? 0 : 255; - g = GREEN_OF(color) > 128 ? 0 : 255; - b = BLUE_OF(color) > 128 ? 0 : 255; - - return RGB2COLOR(r, g, b); -} - -#if (!defined(gdispPackPixels) && !defined(GDISP_PIXELFORMAT_CUSTOM)) - void gdispPackPixels(pixel_t *buf, coord_t cx, coord_t x, coord_t y, color_t color) { - /* No mutex required as we only read static data */ - #if defined(GDISP_PIXELFORMAT_RGB888) - #error "GDISP: Packed pixels not supported yet" - #elif defined(GDISP_PIXELFORMAT_RGB444) - #error "GDISP: Packed pixels not supported yet" - #elif defined(GDISP_PIXELFORMAT_RGB666) - #error "GDISP: Packed pixels not supported yet" - #elif - #error "GDISP: Unsupported packed pixel format" - #endif - } -#endif - -#if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT - LLDCOLOR_TYPE gdispColor2Native(color_t c) { - #if COLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE || LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE - #if GDISP_HARDWARE_USE_EXACT_COLOR - return LLDLUMA2COLOR(EXACT_LUMA_OF(c)); - #else - return LLDLUMA2COLOR(LUMA_OF(c)); - #endif - #elif COLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR && LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR - #if GDISP_HARDWARE_USE_EXACT_COLOR - return LLDRGB2COLOR(EXACT_RED_OF(c), EXACT_GREEN_OF(c), EXACT_BLUE_OF(c)); - #else - return LLDRGB2COLOR(RED_OF(c), GREEN_OF(c), BLUE_OF(c)); - #endif - #else - #error "GDISP: This pixel format conversion is not supported yet" - #endif - } -#endif - -#if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT - color_t gdispNative2Color(LLDCOLOR_TYPE c) { - #if COLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE || LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE - #if GDISP_HARDWARE_USE_EXACT_COLOR - return LUMA2COLOR(LLDEXACT_LUMA_OF(c)); - #else - return LUMA2COLOR(LLDLUMA_OF(c)); - #endif - #elif COLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR && LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR - #if GDISP_HARDWARE_USE_EXACT_COLOR - return RGB2COLOR(LLDEXACT_RED_OF(c), LLDEXACT_GREEN_OF(c), LLDEXACT_BLUE_OF(c)); - #else - return RGB2COLOR(LLDRED_OF(c), LLDGREEN_OF(c), LLDBLUE_OF(c)); - #endif - #else - #error "GDISP: This pixel format conversion is not supported yet" - #endif - } -#endif - -#endif /* GFX_USE_GDISP */ -/** @} */ diff --git a/src/gdisp/gdisp_colors.h b/src/gdisp/gdisp_colors.h new file mode 100644 index 00000000..b764ccfe --- /dev/null +++ b/src/gdisp/gdisp_colors.h @@ -0,0 +1,377 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/gdisp_colors.h + * @brief GDISP color definitions header file. + * + * @defgroup Colors Colors + * @ingroup GDISP + * @{ + */ + +#ifndef _GDISP_COLORS_H +#define _GDISP_COLORS_H + +#include "gfx.h" + +#if GFX_USE_GDISP || defined(__DOXYGEN__) + +/** + * For pixel formats we do some assignment of codes to enable + * format auto-calculation. (Undocumented). + * 0x2RGB TRUECOLOR RGB format, R = red bits, G = green bits, B = blue bits + * 0x3RGB TRUECOLOR BGR format, R = red bits, G = green bits, B = blue bits + * 0x40XX GRAYSCALE XX = bits + * 0x60XX PALLETTE XX = bits + * 0x8XXX CUSTOM format. + */ +#define GDISP_COLORSYSTEM_MASK 0xF000 +#define GDISP_COLORSYSTEM_RGB 0x2000 +#define GDISP_COLORSYSTEM_BGR 0x3000 + +/** + * @brief Color Type Constants + * @{ + */ +#define GDISP_COLORSYSTEM_TRUECOLOR 0x2000 +#define GDISP_COLORSYSTEM_GRAYSCALE 0x4000 +#define GDISP_COLORSYSTEM_PALETTE 0x6000 +/** @} */ + +/** + * @brief Pixel Format Constants + * @{ + */ +#define GDISP_PIXELFORMAT_MONO (GDISP_COLORSYSTEM_GRAYSCALE|0x0001) +#define GDISP_PIXELFORMAT_GRAY4 (GDISP_COLORSYSTEM_GRAYSCALE|0x0002) +#define GDISP_PIXELFORMAT_GRAY16 (GDISP_COLORSYSTEM_GRAYSCALE|0x0004) +#define GDISP_PIXELFORMAT_GRAY256 (GDISP_COLORSYSTEM_GRAYSCALE|0x0008) +#define GDISP_PIXELFORMAT_RGB565 (GDISP_COLORSYSTEM_RGB|0x0565) +#define GDISP_PIXELFORMAT_BGR565 (GDISP_COLORSYSTEM_BGR|0x0565) +#define GDISP_PIXELFORMAT_RGB888 (GDISP_COLORSYSTEM_RGB|0x0888) +#define GDISP_PIXELFORMAT_BGR888 (GDISP_COLORSYSTEM_BGR|0x0888) +#define GDISP_PIXELFORMAT_RGB444 (GDISP_COLORSYSTEM_RGB|0x0444) +#define GDISP_PIXELFORMAT_BGR444 (GDISP_COLORSYSTEM_BGR|0x0444) +#define GDISP_PIXELFORMAT_RGB332 (GDISP_COLORSYSTEM_RGB|0x0332) +#define GDISP_PIXELFORMAT_BGR332 (GDISP_COLORSYSTEM_BGR|0x0332) +#define GDISP_PIXELFORMAT_RGB666 (GDISP_COLORSYSTEM_RGB|0x0666) +#define GDISP_PIXELFORMAT_BGR666 (GDISP_COLORSYSTEM_BGR|0x0666) +#define GDISP_PIXELFORMAT_ERROR 0x0000 +/** @} */ + +/** + * @name Some basic colors + * @{ + */ +#define White HTML2COLOR(0xFFFFFF) +#define Black HTML2COLOR(0x000000) +#define Gray HTML2COLOR(0x808080) +#define Grey Gray +#define Blue HTML2COLOR(0x0000FF) +#define Red HTML2COLOR(0xFF0000) +#define Fuchsia HTML2COLOR(0xFF00FF) +#define Magenta Fuchsia +#define Green HTML2COLOR(0x008000) +#define Yellow HTML2COLOR(0xFFFF00) +#define Aqua HTML2COLOR(0x00FFFF) +#define Cyan Aqua +#define Lime HTML2COLOR(0x00FF00) +#define Maroon HTML2COLOR(0x800000) +#define Navy HTML2COLOR(0x000080) +#define Olive HTML2COLOR(0x808000) +#define Purple HTML2COLOR(0x800080) +#define Silver HTML2COLOR(0xC0C0C0) +#define Teal HTML2COLOR(0x008080) +#define Orange HTML2COLOR(0xFFA500) +#define Pink HTML2COLOR(0xFFC0CB) +#define SkyBlue HTML2COLOR(0x87CEEB) +/** @} */ + +#if defined(__DOXYGEN__) + /** + * @brief The color system (grayscale, palette or truecolor) + */ + #define COLOR_SYSTEM GDISP_COLORSYSTEM_TRUECOLOR + /** + * @brief The number of bits in a color value + */ + #define COLOR_BITS 16 + /** + * @brief The number of bits for each of red, green and blue + * @{ + */ + #define COLOR_BITS_R 5 + #define COLOR_BITS_G 6 + #define COLOR_BITS_B 5 + /** @} */ + /** + * @brief The number of bits to shift each of red, green and blue to put it in the correct place in the color + * @{ + */ + #define COLOR_SHIFT_R 11 + #define COLOR_SHIFT_G 5 + #define COLOR_SHIFT_B 0 + /** @} */ + /** + * @brief Does the color need masking to remove invalid bits + */ + #define COLOR_NEEDS_MASK FALSE + /** + * @brief If the color needs masking to remove invalid bits, this is the mask + */ + #define COLOR_MASK 0xFFFF + /** + * @brief The color type + * @{ + */ + #define COLOR_TYPE uint16_t + /** @} */ + /** + * @brief The number of bits in the color type (not necessarily the same as COLOR_BITS). + */ + #define COLOR_TYPE_BITS 16 + /** + * @brief Convert a luminance (0 to 255) into a color value. + * @note The word "Luma" is used instead of grey or gray due to the spelling ambiguities of the word grey + * @note This is not a weighted luminance conversion in the color tv style. + * @note @p LUMA2COLOR() uses a linear conversion (0.33R + 0.33G + 0.33B). Note this is different to color + * tv luminance (0.26126R + 0.7152G + 0.0722B), digital tv luminance of (0.299R + 0.587G + 0.114B), or + * @p LUMA_OF() which uses (0.25R + 0.5G + 0.25B). + */ + #define LUMA2COLOR(l) ((color_t)((((l) & 0xF8)<<8) | (((l) & 0xFC)<<3) | (((l) & 0xF8)>>3))) + /** + * @brief Convert red, green, blue (each 0 to 255) into a color value. + */ + #define RGB2COLOR(r,g,b) ((color_t)((((r) & 0xF8)<<8) | (((g) & 0xFC)<<3) | (((b) & 0xF8)>>3))) + /** + * @brief Convert a 6 digit HTML code (hex) into a color value. + */ + #define HTML2COLOR(h) ((color_t)((((h) & 0xF80000)>>8) | (((h) & 0x00FC00)>>5) | (((h) & 0x0000F8)>>3))) + /** + * @brief Extract the luma/red/green/blue component (0 to 255) of a color value. + * @note This uses quick and dirty bit shifting. If you want more exact colors + * use @p EXACT_RED_OF() etc which uses multiplies and divides. For constant + * colors using @p EXACT_RED_OF() is no more expensive because the compiler + * evaluates the arithmetic. + * @note @p LUMA_OF() returns a roughly weighted luminance (0.25R + 0.5G + 0.25B). Note this is + * different to @p LUMA2COLOR() which uses a linear conversion (0.33R + 0.33G + 0.33B) and + * color tv luminance of (0.26126R + 0.7152G + 0.0722B) and digital tv luminance of (0.299R + 0.587G + 0.114B). + * @note A 5 bit color component maximum value (0x1F) converts to 0xF8 (slightly off-color) + * @{ + */ + #define LUMA_OF(c) ((RED_OF(c)+((uint16_t)GREEN_OF(c)<<1)+BLUE_OF(c))>>2) + #define RED_OF(c) (((c) & 0xF800)>>8) + #define GREEN_OF(c) (((c)&0x007E)>>3) + #define BLUE_OF(c) (((c)&0x001F)<<3) + /** @} */ + /** + * @brief Extract the exact luma/red/green/blue component (0 to 255) of a color value. + * @note This uses multiplies and divides rather than bit shifting. + * This gives exact equivalent colors at the expense of more cpu intensive + * operations. Note for constants this is no more expensive than @p REF_OF() + * because the compiler evaluates the arithmetic. + * @note @p EXACT_LUMA_OF() returns a roughly weighted luminance (0.25R + 0.5G + 0.25B). Note this is + * different to @p LUMA2COLOR() which uses a linear conversion (0.33R + 0.33G + 0.33B) and + * color tv luminance of (0.26126R + 0.7152G + 0.0722B) and digital tv luminance of (0.299R + 0.587G + 0.114B). + * @note A 5 bit color component maximum value (0x1F) converts to 0xFF (the true equivalent color) + * @{ + */ + #define EXACT_LUMA_OF(c) ((EXACT_RED_OF(c)+((uint16_t)EXACT_GREEN_OF(c)<<1)+EXACT_BLUE_OF(c))>>2) + #define EXACT_RED_OF(c) (((((c)>>11)&0x1F)*255)/31) + #define EXACT_GREEN_OF(c) (((((c)>>5)&0x3F)*255)/63) + #define EXACT_BLUE_OF(c) (((((c)>>0)&0x1F)*255)/31) + /** @} */ +#endif + +/* + * We use this big mess of macros to calculate all the components + * to prevent user errors in the color definitions. It greatly simplifies + * the above definitions and ensures a consistent implementation. + */ + +//------------------------- +// True-Color color system +//------------------------- +#if GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_TRUECOLOR + #define COLOR_SYSTEM GDISP_COLORSYSTEM_TRUECOLOR + + // Calculate the number of bits + #define COLOR_BITS_R ((GDISP_PIXELFORMAT>>8) & 0x0F) + #define COLOR_BITS_G ((GDISP_PIXELFORMAT>>4) & 0x0F) + #define COLOR_BITS_B ((GDISP_PIXELFORMAT>>0) & 0x0F) + #define COLOR_BITS (COLOR_BITS_R + COLOR_BITS_G + COLOR_BITS_B) + + // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking + #if COLOR_BITS <= 8 + #define COLOR_TYPE uint8_t + #define COLOR_TYPE_BITS 8 + #elif COLOR_BITS <= 16 + #define COLOR_TYPE uint16_t + #define COLOR_TYPE_BITS 16 + #elif COLOR_BITS <= 32 + #define COLOR_TYPE uint32_t + #define COLOR_TYPE_BITS 32 + #else + #error "GDISP: Cannot define color types with more than 32 bits" + #endif + #if COLOR_TYPE_BITS == COLOR_BITS + #define COLOR_NEEDS_MASK FALSE + #else + #define COLOR_NEEDS_MASK TRUE + #endif + #define COLOR_MASK() ((1 << COLOR_BITS)-1) + + // Calculate the component bit shifts + #if (GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_RGB + #define COLOR_SHIFT_R (COLOR_BITS_B+COLOR_BITS_G) + #define COLOR_SHIFT_G COLOR_BITS_B + #define COLOR_SHIFT_B 0 + #else + #define COLOR_SHIFT_B (COLOR_BITS_R+COLOR_BITS_G) + #define COLOR_SHIFT_G COLOR_BITS_R + #define COLOR_SHIFT_R 0 + #endif + + // Calculate RED_OF, GREEN_OF, BLUE_OF and RGB2COLOR + #if COLOR_BITS_R + COLOR_SHIFT_R == 8 + #define RED_OF(c) ((c) & (((1< 8 + #define RED_OF(c) (((c) & (((1<> (COLOR_BITS_R+COLOR_SHIFT_R-8)) + #define RGB2COLOR_R(r) (((COLOR_TYPE)((r) & (0xFF & ~((1<<(8-COLOR_BITS_R))-1)))) << (COLOR_BITS_R+COLOR_SHIFT_R-8)) + #else // COLOR_BITS_R + COLOR_SHIFT_R < 8 + #define RED_OF(c) (((c) & (((1<> (8-(COLOR_BITS_R+COLOR_SHIFT_R))) + #endif + #if COLOR_BITS_G + COLOR_SHIFT_G == 8 + #define GREEN_OF(c) ((c) & (((1< 8 + #define GREEN_OF(c) (((c) & (((1<> (COLOR_BITS_G+COLOR_SHIFT_G-8)) + #define RGB2COLOR_G(g) (((COLOR_TYPE)((g) & (0xFF & ~((1<<(8-COLOR_BITS_G))-1)))) << (COLOR_BITS_G+COLOR_SHIFT_G-8)) + #else // COLOR_BITS_G + COLOR_SHIFT_G < 8 + #define GREEN_OF(c) (((c) & (((1<> (8-(COLOR_BITS_G+COLOR_SHIFT_G))) + #endif + #if COLOR_BITS_B + COLOR_SHIFT_B == 8 + #define BLUE_OF(c) ((c) & (((1< 8 + #define BLUE_OF(c) (((c) & (((1<> (COLOR_BITS_B+COLOR_SHIFT_B-8)) + #define RGB2COLOR_B(b) (((COLOR_TYPE)((b) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1)))) << (COLOR_BITS_B+COLOR_SHIFT_B-8)) + #else // COLOR_BITS_B + COLOR_SHIFT_B < 8 + #define BLUE_OF(c) (((c) & (((1<> (8-(COLOR_BITS_B+COLOR_SHIFT_B))) + #endif + #define LUMA_OF(c) ((RED_OF(c)+((uint16_t)GREEN_OF(c)<<1)+BLUE_OF(c))>>2) + #define EXACT_RED_OF(c) (((uint16_t)(((c)>>COLOR_SHIFT_R)&((1<>COLOR_SHIFT_G)&((1<>COLOR_SHIFT_B)&((1<>2) + #define LUMA2COLOR(l) (RGB2COLOR_R(l) | RGB2COLOR_G(l) | RGB2COLOR_B(l)) + #define RGB2COLOR(r,g,b) (RGB2COLOR_R(r) | RGB2COLOR_G(g) | RGB2COLOR_B(b)) + + // Calculate HTML2COLOR + #if COLOR_BITS_R + COLOR_SHIFT_R == 24 + #define HTML2COLOR_R(h) ((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) + #elif COLOR_BITS_R + COLOR_SHIFT_R > 24 + #define HTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) << (COLOR_BITS_R+COLOR_SHIFT_R-24)) + #else // COLOR_BITS_R + COLOR_SHIFT_R < 24 + #define HTML2COLOR_R(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_R))-1))<<16)) >> (24-(COLOR_BITS_R+COLOR_SHIFT_R))) + #endif + #if COLOR_BITS_G + COLOR_SHIFT_G == 16 + #define HTML2COLOR_G(h) ((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) + #elif COLOR_BITS_G + COLOR_SHIFT_G > 16 + #define HTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) << (COLOR_BITS_G+COLOR_SHIFT_G-16)) + #else // COLOR_BITS_G + COLOR_SHIFT_G < 16 + #define HTML2COLOR_G(h) (((h) & ((0xFF & ~((1<<(8-COLOR_BITS_G))-1))<<8)) >> (16-(COLOR_BITS_G+COLOR_SHIFT_G))) + #endif + #if COLOR_BITS_B + COLOR_SHIFT_B == 8 + #define HTML2COLOR_B(h) ((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) + #elif COLOR_BITS_B + COLOR_SHIFT_B > 8 + #define HTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) << (COLOR_BITS_B+COLOR_SHIFT_B-8)) + #else // COLOR_BITS_B + COLOR_SHIFT_B < 8 + #define HTML2COLOR_B(h) (((h) & (0xFF & ~((1<<(8-COLOR_BITS_B))-1))) >> (8-(COLOR_BITS_B+COLOR_SHIFT_B))) + #endif + #define HTML2COLOR(h) ((COLOR_TYPE)(HTML2COLOR_R(h) | HTML2COLOR_G(h) | HTML2COLOR_B(h))) + +//------------------------- +// Gray-scale color system +//------------------------- +#elif (GDISP_PIXELFORMAT & GDISP_COLORSYSTEM_MASK) == GDISP_COLORSYSTEM_GRAYSCALE + #define COLOR_SYSTEM GDISP_COLORSYSTEM_GRAYSCALE + + // Calculate the number of bits and shifts + #define COLOR_BITS (GDISP_PIXELFORMAT & 0xFF) + #define COLOR_BITS_R COLOR_BITS + #define COLOR_BITS_G COLOR_BITS + #define COLOR_BITS_B COLOR_BITS + #define COLOR_SHIFT_R 0 + #define COLOR_SHIFT_G 0 + #define COLOR_SHIFT_B 0 + + // From the number of bits determine COLOR_TYPE, COLOR_TYPE_BITS and masking + #if COLOR_BITS <= 8 + #define COLOR_TYPE uint8_t + #define COLOR_TYPE_BITS 8 + #else + #error "GDISP: Cannot define gray-scale color types with more than 8 bits" + #endif + #if COLOR_TYPE_BITS == COLOR_BITS + #define COLOR_NEEDS_MASK FALSE + #else + #define COLOR_NEEDS_MASK TRUE + #endif + #define COLOR_MASK() ((1 << COLOR_BITS)-1) + + #if COLOR_BITS == 1 + #define RGB2COLOR(r,g,b) (((r)|(g)|(b)) ? 1 : 0) + #define LUMA2COLOR(l) ((l) ? 1 : 0) + #define HTML2COLOR(h) ((h) ? 1 : 0) + #define LUMA_OF(c) ((c) ? 255 : 0) + #define EXACT_LUMA_OF(c) LUMA_OF(c) + #else + // They eye is more sensitive to green + #define RGB2COLOR(r,g,b) ((COLOR_TYPE)(((uint16_t)(r)+(g)+(g)+(b)) >> (10-COLOR_BITS))) + #define LUMA2COLOR(l) ((COLOR_TYPE)((l)>>(8-COLOR_BITS))) + #define HTML2COLOR(h) ((COLOR_TYPE)(((((h)&0xFF0000)>>16)+(((h)&0x00FF00)>>7)+((h)&0x0000FF)) >> (10-COLOR_BITS))) + #define LUMA_OF(c) (((c) & ((1<next) { + if (matchfont(name, fp->font->full_name)) + return fp->font; + } + + // Try the short names if no long names match + for(fp = mf_get_font_list(); fp; fp = fp->next) { + if (matchfont(name, fp->font->short_name)) + return fp->font; + } + + /* Return default font.. better than nothing. */ + return mf_get_font_list()->font; +} + +void gdispCloseFont(font_t font) { + if (font->flags & FONT_FLAG_DYNAMIC) + { + struct mf_font_s *dfont = (struct mf_font_s *)font; + + /* Make sure that no-one can successfully use font after closing */ + dfont->render_character = 0; + + /* Release the allocated memory */ + gfxFree(dfont); + } +} + +font_t gdispScaleFont(font_t font, uint8_t scale_x, uint8_t scale_y) +{ + struct mf_scaledfont_s *newfont = gfxAlloc(sizeof(struct mf_scaledfont_s)); + mf_scale_font(newfont, font, scale_x, scale_y); + return (font_t)newfont; +} + +const char *gdispGetFontName(font_t font) { + return font->short_name; +} + +#endif /* GFX_USE_GDISP && GDISP_NEED_TEXT */ +/** @} */ diff --git a/src/gdisp/gdisp_gdisp.c b/src/gdisp/gdisp_gdisp.c new file mode 100644 index 00000000..02ee4711 --- /dev/null +++ b/src/gdisp/gdisp_gdisp.c @@ -0,0 +1,3100 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/gdisp_gdisp.c + * @brief GDISP Driver code. + * + * @addtogroup GDISP + * @{ + */ +#include "gfx.h" + +#if GFX_USE_GDISP + +/* Include the low level driver information */ +#include "driver.h" + +#if 1 + #undef INLINE + #if defined(__KEIL__) || defined(__C51__) + #define INLINE __inline + #else + #define INLINE inline + #endif +#else + #undef INLINE + #define INLINE +#endif + +// Number of milliseconds for the startup logo - 0 means disabled. +#if GDISP_NEED_STARTUP_LOGO + #define GDISP_STARTUP_LOGO_TIMEOUT 1000 +#else + #define GDISP_STARTUP_LOGO_TIMEOUT 0 +#endif + +// The color to clear the display on startup +#define GDISP_STARTUP_COLOR Black + +/*===========================================================================*/ +/* Driver local variables. */ +/*===========================================================================*/ + +// The controller array, the display array and the default display +#if GDISP_TOTAL_CONTROLLERS > 1 + typedef const struct GDISPVMT const VMTEL[1]; + extern VMTEL GDISP_CONTROLLER_LIST; + static const struct GDISPVMT const * ControllerList[GDISP_TOTAL_CONTROLLERS] = {GDISP_CONTROLLER_LIST}; + static const unsigned DisplayCountList[GDISP_TOTAL_CONTROLLERS] = {GDISP_CONTROLLER_DISPLAYS}; +#endif + +#if GDISP_NEED_TIMERFLUSH + static GTimer FlushTimer; +#endif + +static GDisplay GDisplayArray[GDISP_TOTAL_DISPLAYS]; +GDisplay *GDISP = GDisplayArray; + +#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) +#else + #define MUTEX_INIT(g) + #define MUTEX_ENTER(g) + #define MUTEX_EXIT(g) +#endif + +#define NEED_CLIPPING (GDISP_HARDWARE_CLIP != TRUE && (GDISP_NEED_VALIDATION || GDISP_NEED_CLIP)) + +#if !NEED_CLIPPING + #define TEST_CLIP_AREA(g) +#elif GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + #define TEST_CLIP_AREA(g) \ + if (!g->vmt->setclip) { \ + if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \ + if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \ + if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \ + if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \ + } \ + if ((g)->p.cx > 0 && (g)->p.cy > 0) +#else + #define TEST_CLIP_AREA(g) \ + if ((g)->p.x < (g)->clipx0) { (g)->p.cx -= (g)->clipx0 - (g)->p.x; (g)->p.x = (g)->clipx0; } \ + if ((g)->p.y < (g)->clipy0) { (g)->p.cy -= (g)->clipy0 - (g)->p.y; (g)->p.y = (g)->clipy0; } \ + if ((g)->p.x + (g)->p.cx > (g)->clipx1) (g)->p.cx = (g)->clipx1 - (g)->p.x; \ + if ((g)->p.y + (g)->p.cy > (g)->clipy1) (g)->p.cy = (g)->clipy1 - (g)->p.y; \ + if ((g)->p.cx > 0 && (g)->p.cy > 0) +#endif + +/*==========================================================================*/ +/* Internal functions. */ +/*==========================================================================*/ + +#if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + static INLINE void setglobalwindow(GDisplay *g) { + coord_t x, y; + x = g->p.x; y = g->p.y; + g->p.x = g->p.y = 0; + g->p.cx = g->g.Width; g->p.cy = g->g.Height; + gdisp_lld_write_start(g); + g->p.x = x; g->p.y = y; + g->flags |= GDISP_FLG_SCRSTREAM; + } +#endif + +#if GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT + #define autoflush_stopdone(g) if (g->vmt->flush) gdisp_lld_flush(g) +#elif GDISP_NEED_AUTOFLUSH && GDISP_HARDWARE_FLUSH + #define autoflush_stopdone(g) gdisp_lld_flush(g) +#else + #define autoflush_stopdone(g) +#endif + +#if GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + #define autoflush(g) \ + { \ + if ((g->flags & GDISP_FLG_SCRSTREAM)) { \ + gdisp_lld_write_stop(g); \ + g->flags &= ~GDISP_FLG_SCRSTREAM; \ + } \ + autoflush_stopdone(g); \ + } +#else + #define autoflush(g) autoflush_stopdone(g) +#endif + +// drawpixel(g) +// Parameters: x,y +// Alters: cx, cy (if using streaming) +// Does not clip +static INLINE void drawpixel(GDisplay *g) { + + // Best is hardware accelerated pixel draw + #if GDISP_HARDWARE_DRAWPIXEL + #if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + if (g->vmt->pixel) + #endif + { + gdisp_lld_draw_pixel(g); + return; + } + #endif + + // Next best is cursor based streaming + #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (g->vmt->writepos) + #endif + { + if (!(g->flags & GDISP_FLG_SCRSTREAM)) + setglobalwindow(g); + gdisp_lld_write_pos(g); + gdisp_lld_write_color(g); + return; + } + #endif + + // Worst is general streaming + #if GDISP_HARDWARE_DRAWPIXEL != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE + // The following test is unneeded because we are guaranteed to have streaming if we don't have drawpixel + //#if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + // if (g->vmt->writestart) + //#endif + { + g->p.cx = g->p.cy = 1; + gdisp_lld_write_start(g); + gdisp_lld_write_color(g); + gdisp_lld_write_stop(g); + return; + } + #endif +} + +// drawpixel_clip(g) +// Parameters: x,y +// Alters: cx, cy (if using streaming) +#if NEED_CLIPPING + static INLINE void drawpixel_clip(GDisplay *g) { + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!g->vmt->setclip) + #endif + { + if (g->p.x < g->clipx0 || g->p.x >= g->clipx1 || g->p.y < g->clipy0 || g->p.y >= g->clipy1) + return; + } + drawpixel(g); + } +#else + #define drawpixel_clip(g) drawpixel(g) +#endif + +// fillarea(g) +// Parameters: x,y cx,cy and color +// Alters: nothing +// Note: This is not clipped +// Resets the streaming area if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set. +static INLINE void fillarea(GDisplay *g) { + + // Best is hardware accelerated area fill + #if GDISP_HARDWARE_FILLS + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (g->vmt->fill) + #endif + { + gdisp_lld_fill_area(g); + return; + } + #endif + + // Next best is hardware streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (g->vmt->writestart) + #endif + { + uint32_t area; + + #if GDISP_HARDWARE_STREAM_POS + if ((g->flags & GDISP_FLG_SCRSTREAM)) { + gdisp_lld_write_stop(g); + g->flags &= ~GDISP_FLG_SCRSTREAM; + } + #endif + + area = (uint32_t)g->p.cx * g->p.cy; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (g->vmt->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + for(; area; area--) + gdisp_lld_write_color(g); + gdisp_lld_write_stop(g); + return; + } + #endif + + // Worst is pixel drawing + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (g->vmt->pixel) + //#endif + { + coord_t x0, y0, x1, y1; + + x0 = g->p.x; + y0 = g->p.y; + x1 = g->p.x + g->p.cx; + y1 = g->p.y + g->p.cy; + for(; g->p.y < y1; g->p.y++, g->p.x = x0) + for(; g->p.x < x1; g->p.x++) + gdisp_lld_draw_pixel(g); + g->p.y = y0; + return; + } + #endif +} + +// Parameters: x,y and x1 +// Alters: x,y x1,y1 cx,cy +// Assumes the window covers the screen and a write_stop() will occur later +// if GDISP_HARDWARE_STREAM_WRITE and GDISP_HARDWARE_STREAM_POS is set. +static void hline_clip(GDisplay *g) { + // Swap the points if necessary so it always goes from x to x1 + if (g->p.x1 < g->p.x) { + g->p.cx = g->p.x; g->p.x = g->p.x1; g->p.x1 = g->p.cx; + } + + // Clipping + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!g->vmt->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 (g->vmt->fill) + #endif + { + g->p.cx = g->p.x1 - g->p.x + 1; + g->p.cy = 1; + gdisp_lld_fill_area(g); + return; + } + #endif + + // Next best is cursor based streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (g->vmt->writepos) + #endif + { + if (!(g->flags & GDISP_FLG_SCRSTREAM)) + setglobalwindow(g); + g->p.cx = g->p.x1 - g->p.x + 1; + gdisp_lld_write_pos(g); + do { gdisp_lld_write_color(g); } while(--g->p.cx); + return; + } + #endif + + // Next best is streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_POS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (g->vmt->writestart) + #endif + { + g->p.cx = g->p.x1 - g->p.x + 1; + g->p.cy = 1; + gdisp_lld_write_start(g); + do { gdisp_lld_write_color(g); } while(--g->p.cx); + gdisp_lld_write_stop(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (g->vmt->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 (!g->vmt->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 (g->vmt->fill) + #endif + { + g->p.cy = g->p.y1 - g->p.y + 1; + g->p.cx = 1; + gdisp_lld_fill_area(g); + return; + } + #endif + + // Next best is streaming + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (g->vmt->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 (g->vmt->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + do { gdisp_lld_write_color(g); } while(--g->p.cy); + gdisp_lld_write_stop(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (g->vmt->pixel) + //#endif + { + for(; g->p.y <= g->p.y1; g->p.y++) + gdisp_lld_draw_pixel(g); + return; + } + #endif +} + +// Parameters: x,y and x1,y1 +// Alters: x,y x1,y1 cx,cy +static void line_clip(GDisplay *g) { + int16_t dy, dx; + int16_t addx, addy; + int16_t P, diff, i; + + // Is this a horizontal line (or a point) + if (g->p.y == g->p.y1) { + hline_clip(g); + return; + } + + // Is this a vertical line (or a point) + if (g->p.x == g->p.x1) { + vline_clip(g); + return; + } + + // Not horizontal or vertical + + // Use Bresenham's line drawing algorithm. + // This should be replaced with fixed point slope based line drawing + // which is more efficient on modern processors as it branches less. + // When clipping is needed, all the clipping could also be done up front + // instead of on each pixel. + + if (g->p.x1 >= g->p.x) { + dx = g->p.x1 - g->p.x; + addx = 1; + } else { + dx = g->p.x - g->p.x1; + addx = -1; + } + if (g->p.y1 >= g->p.y) { + dy = g->p.y1 - g->p.y; + addy = 1; + } else { + dy = g->p.y - g->p.y1; + addy = -1; + } + + if (dx >= dy) { + dy <<= 1; + P = dy - dx; + diff = P - dx; + + for(i=0; i<=dx; ++i) { + drawpixel_clip(g); + if (P < 0) { + P += dy; + g->p.x += addx; + } else { + P += diff; + g->p.x += addx; + g->p.y += addy; + } + } + } else { + dx <<= 1; + P = dx - dy; + diff = P - dy; + + for(i=0; i<=dy; ++i) { + drawpixel_clip(g); + if (P < 0) { + P += dx; + g->p.y += addy; + } else { + P += diff; + g->p.x += addx; + g->p.y += addy; + } + } + } +} + +#if GDISP_STARTUP_LOGO_TIMEOUT > 0 + static void StartupLogoDisplay(GDisplay *g) { + coord_t x, y, w; + const coord_t * p; + static const coord_t blks[] = { + // u + 2, 6, 1, 10, + 3, 11, 4, 1, + 6, 6, 1, 6, + // G + 8, 0, 1, 12, + 9, 0, 6, 1, + 9, 11, 6, 1, + 14, 6, 1, 5, + 12, 6, 2, 1, + // F + 16, 0, 1, 12, + 17, 0, 6, 1, + 17, 6, 3, 1, + // X + 22, 6, 7, 1, + 24, 0, 1, 6, + 22, 7, 1, 5, + 28, 0, 1, 6, + 26, 7, 1, 5, + }; + + // Get a starting position and a scale + // Work on a 8x16 grid for each char, 4 chars (uGFX) in 1 line, using half the screen + w = g->g.Width/(8*4*2); + if (!w) w = 1; + x = (g->g.Width - (8*4)*w)/2; + y = (g->g.Height - (16*1)*w)/2; + + // Simple but crude! + for(p = blks; p < blks+sizeof(blks)/sizeof(blks[0]); p+=4) + gdispGFillArea(g, x+p[0]*w, y+p[1]*w, p[2]*w, p[3]*w, Blue); + } +#endif + +#if GDISP_NEED_TIMERFLUSH + static void FlushTimerFn(void *param) { + GDisplay * g; + (void) param; + + for(g = GDisplayArray; g < &GDisplayArray[GDISP_TOTAL_DISPLAYS]; g++) + gdispGFlush(g); + } +#endif + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +void _gdispInit(void) +{ + GDisplay *g; + uint16_t i; + + /* Initialise all controllers */ + #if GDISP_TOTAL_CONTROLLERS > 1 + uint16_t j; + + for(g = GDisplayArray, j=0; j < GDISP_TOTAL_CONTROLLERS; j++) + for(i = 0; i < DisplayCountList[j]; g++, i++) { + g->vmt = ControllerList[j]; + g->systemdisplay = j*GDISP_TOTAL_CONTROLLERS+i; + g->controllerdisplay = i; + #else + for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { + g->systemdisplay = i; + g->controllerdisplay = i; + #endif + MUTEX_INIT(g); + MUTEX_ENTER(g); + g->flags = 0; + gdisp_lld_init(g); + MUTEX_EXIT(g); + } + + // Set the orientation, the clipping area, clear all the displays (and add the logo if required) + for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { + #if defined(GDISP_DEFAULT_ORIENTATION) && GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL + gdispGControl(g, GDISP_CONTROL_ORIENTATION, (void *)GDISP_DEFAULT_ORIENTATION); + #endif + #if GDISP_NEED_VALIDATION || GDISP_NEED_CLIP + gdispGSetClip(g, 0, 0, g->g.Width, g->g.Height); + #endif + gdispGClear(g, GDISP_STARTUP_COLOR); + #if GDISP_STARTUP_LOGO_TIMEOUT > 0 + StartupLogoDisplay(g); + #endif + #if GDISP_HARDWARE_FLUSH + gdispGFlush(g); + #endif + } + + // Re-clear the display after the timeout if we added the logo + #if GDISP_STARTUP_LOGO_TIMEOUT > 0 + gfxSleepMilliseconds(GDISP_STARTUP_LOGO_TIMEOUT); + for(g = GDisplayArray, i = 0; i < GDISP_TOTAL_DISPLAYS; g++, i++) { + gdispGClear(g, GDISP_STARTUP_COLOR); + #if GDISP_HARDWARE_FLUSH + gdispGFlush(g); + #endif + } + #endif + + // Start the automatic timer flush (if required) + #if GDISP_NEED_TIMERFLUSH + gtimerInit(&FlushTimer); + gtimerStart(&FlushTimer, FlushTimerFn, 0, TRUE, GDISP_NEED_TIMERFLUSH); + #endif +} + +void _gdispDeinit(void) +{ + /* ToDo */ +} + +GDisplay *gdispGetDisplay(unsigned display) { + if (display >= GDISP_TOTAL_DISPLAYS) + return 0; + return &GDisplayArray[display]; +} + +void gdispSetDisplay(GDisplay *g) { + if (g) GDISP = g; +} + +void gdispGFlush(GDisplay *g) { + #if GDISP_HARDWARE_FLUSH + #if GDISP_HARDWARE_FLUSH == HARDWARE_AUTODETECT + if (g->vmt->flush) + #endif + { + MUTEX_ENTER(g); + gdisp_lld_flush(g); + MUTEX_EXIT(g); + } + #else + (void) g; + #endif +} + +#if GDISP_NEED_STREAMING + void gdispGStreamStart(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) { + MUTEX_ENTER(g); + + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!g->vmt->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 (g->vmt->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 (g->vmt->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + return; + } + #endif + + // Worst - save the parameters and use pixel drawing and/or area fills + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (g->vmt->pixel) + //#endif + { + // Use x,y as the current position, x1,y1 as the save position and x2,y2 as the end position, cx = bufpos + g->p.x1 = g->p.x = x; + g->p.y1 = g->p.y = y; + g->p.x2 = x + cx; + g->p.y2 = y + cy; + #if (GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS) || GDISP_HARDWARE_FILLS + g->p.cx = 0; + g->p.cy = 1; + #endif + return; + } + #endif + + // Don't release the mutex as gdispStreamEnd() will do that. + } + + void gdispGStreamColor(GDisplay *g, color_t color) { + #if !GDISP_HARDWARE_STREAM_WRITE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS + coord_t sx1, sy1; + #endif + + // Don't touch the mutex as we should already own it + + // Ignore this call if we are not streaming + if (!(g->flags & GDISP_FLG_INSTREAM)) + return; + + // Best is hardware streaming + #if GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (g->vmt->writestart) + #endif + { + g->p.color = color; + gdisp_lld_write_color(g); + return; + } + #endif + + // Next best is to use bitfills with our line buffer + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + if (g->vmt->blit) + #endif + { + g->linebuf[g->p.cx++] = color; + if (g->p.cx >= GDISP_LINEBUF_SIZE) { + sx1 = g->p.x1; + sy1 = g->p.y1; + g->p.x1 = 0; + g->p.y1 = 0; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + g->p.x1 = sx1; + g->p.y1 = sy1; + g->p.x += g->p.cx; + g->p.cx = 0; + } + + // Just wrap at end-of-line and end-of-buffer + if (g->p.x+g->p.cx >= g->p.x2) { + if (g->p.cx) { + sx1 = g->p.x1; + sy1 = g->p.y1; + g->p.x1 = 0; + g->p.y1 = 0; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + g->p.x1 = sx1; + g->p.y1 = sy1; + g->p.cx = 0; + } + g->p.x = g->p.x1; + if (++g->p.y >= g->p.y2) + g->p.y = g->p.y1; + } + } + #endif + + // Only slightly better than drawing pixels is to look for runs and use fillarea + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (g->vmt->fill) + #endif + { + if (!g->p.cx || g->p.color == color) { + g->p.cx++; + g->p.color = color; + } else { + if (g->p.cx == 1) + gdisp_lld_draw_pixel(g); + else + gdisp_lld_fill_area(g); + g->p.x += g->p.cx; + g->p.color = color; + g->p.cx = 1; + } + // Just wrap at end-of-line and end-of-buffer + if (g->p.x+g->p.cx >= g->p.x2) { + if (g->p.cx) { + if (g->p.cx == 1) + gdisp_lld_draw_pixel(g); + else + gdisp_lld_fill_area(g); + g->p.cx = 0; + } + g->p.x = g->p.x1; + if (++g->p.y >= g->p.y2) + g->p.y = g->p.y1; + } + return; + } + #endif + + // Worst is using pixel drawing + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (g->vmt->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 (g->vmt->writestart) + #endif + { + gdisp_lld_write_stop(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_LINEBUF_SIZE != 0 && GDISP_HARDWARE_BITFILLS + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + if (g->vmt->blit) + #endif + { + if (g->p.cx) { + g->p.x1 = 0; + g->p.y1 = 0; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (g->vmt->fill) + #endif + { + if (g->p.cx) { + if (g->p.cx == 1) + gdisp_lld_draw_pixel(g); + else + gdisp_lld_fill_area(g); + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + #if GDISP_HARDWARE_STREAM_WRITE != TRUE && (GDISP_LINEBUF_SIZE == 0 || GDISP_HARDWARE_BITFILLS != TRUE) && GDISP_HARDWARE_FILLS != TRUE + { + autoflush_stopdone(g); + MUTEX_EXIT(g); + } + #endif + } +#endif + +void gdispGDrawPixel(GDisplay *g, coord_t x, coord_t y, color_t color) { + MUTEX_ENTER(g); + g->p.x = x; + g->p.y = y; + g->p.color = color; + drawpixel_clip(g); + autoflush(g); + MUTEX_EXIT(g); +} + +void gdispGDrawLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color) { + MUTEX_ENTER(g); + g->p.x = x0; + g->p.y = y0; + g->p.x1 = x1; + g->p.y1 = y1; + g->p.color = color; + line_clip(g); + autoflush(g); + MUTEX_EXIT(g); +} + +void gdispGClear(GDisplay *g, color_t color) { + // Note - clear() ignores the clipping area. It clears the screen. + MUTEX_ENTER(g); + + // Best is hardware accelerated clear + #if GDISP_HARDWARE_CLEARS + #if GDISP_HARDWARE_CLEARS == HARDWARE_AUTODETECT + if (g->vmt->clear) + #endif + { + g->p.color = color; + gdisp_lld_clear(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Next best is hardware accelerated area fill + #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (g->vmt->fill) + #endif + { + g->p.x = g->p.y = 0; + g->p.cx = g->g.Width; + g->p.cy = g->g.Height; + g->p.color = color; + gdisp_lld_fill_area(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Next best is streaming + #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (g->vmt->writestart) + #endif + { + uint32_t area; + + g->p.x = g->p.y = 0; + g->p.cx = g->g.Width; + g->p.cy = g->g.Height; + g->p.color = color; + area = (uint32_t)g->p.cx * g->p.cy; + + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + #if GDISP_HARDWARE_STREAM_POS == HARDWARE_AUTODETECT + if (g->vmt->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + for(; area; area--) + gdisp_lld_write_color(g); + gdisp_lld_write_stop(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_CLEARS != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (g->vmt->pixel) + //#endif + { + g->p.color = color; + for(g->p.y = 0; g->p.y < g->g.Height; g->p.y++) + for(g->p.x = 0; g->p.x < g->g.Width; g->p.x++) + gdisp_lld_draw_pixel(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif +} + +void gdispGFillArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { + MUTEX_ENTER(g); + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + g->p.color = color; + TEST_CLIP_AREA(g) { + fillarea(g); + } + autoflush_stopdone(g); + MUTEX_EXIT(g); +} + +void gdispGBlitArea(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t srcx, coord_t srcy, coord_t srccx, const pixel_t *buffer) { + MUTEX_ENTER(g); + + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!g->vmt->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 (g->vmt->blit) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + g->p.x1 = srcx; + g->p.y1 = srcy; + g->p.x2 = srccx; + g->p.ptr = (void *)buffer; + gdisp_lld_blit_area(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Next best is hardware streaming + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (g->vmt->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 (g->vmt->writepos) + #endif + gdisp_lld_write_pos(g); + #endif + for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { + for(g->p.x = x; g->p.x < srcx; g->p.x++) { + g->p.color = *buffer++; + gdisp_lld_write_color(g); + } + } + gdisp_lld_write_stop(g); + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Only slightly better than drawing pixels is to look for runs and use fill area + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (g->vmt->fill) + #endif + { + // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap + buffer += srcy*srccx+srcx; + srcx = x + cx; + srcy = y + cy; + srccx -= cx; + + g->p.cy = 1; + for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { + for(g->p.x=x; g->p.x < srcx; g->p.x += g->p.cx) { + g->p.cx=1; + g->p.color = *buffer++; + while(g->p.x+g->p.cx < srcx && *buffer == g->p.color) { + g->p.cx++; + buffer++; + } + if (g->p.cx == 1) { + gdisp_lld_draw_pixel(g); + } else { + gdisp_lld_fill_area(g); + } + } + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif + + // Worst is drawing pixels + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (g->vmt->pixel) + //#endif + { + // Translate buffer to the real image data, use srcx,srcy as the end point, srccx as the buffer line gap + buffer += srcy*srccx+srcx; + srcx = x + cx; + srcy = y + cy; + srccx -= cx; + + for(g->p.y = y; g->p.y < srcy; g->p.y++, buffer += srccx) { + for(g->p.x=x; g->p.x < srcx; g->p.x++) { + g->p.color = *buffer++; + gdisp_lld_draw_pixel(g); + } + } + autoflush_stopdone(g); + MUTEX_EXIT(g); + return; + } + #endif +} + +#if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION + void gdispGSetClip(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy) { + MUTEX_ENTER(g); + + // Best is using hardware clipping + #if GDISP_HARDWARE_CLIP + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (g->vmt->setclip) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + gdisp_lld_set_clip(g); + } + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + else + #endif + #endif + + // Worst is using software clipping + #if GDISP_HARDWARE_CLIP != TRUE + { + if (x < 0) { cx += x; x = 0; } + if (y < 0) { cy += y; y = 0; } + if (cx <= 0 || cy <= 0 || x >= g->g.Width || y >= g->g.Height) { MUTEX_EXIT(g); return; } + g->clipx0 = x; + g->clipy0 = y; + g->clipx1 = x+cx; if (g->clipx1 > g->g.Width) g->clipx1 = g->g.Width; + g->clipy1 = y+cy; if (g->clipy1 > g->g.Height) g->clipy1 = g->g.Height; + } + #endif + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_CIRCLE + void gdispGDrawCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) { + coord_t a, b, P; + + MUTEX_ENTER(g); + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + g->p.color = color; + + // Away we go using Bresenham's circle algorithm + // Optimized to prevent double drawing + g->p.x = x; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x + b; g->p.y = y; drawpixel_clip(g); + g->p.x = x - b; g->p.y = y; drawpixel_clip(g); + do { + g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x + b; g->p.y = y + a; drawpixel_clip(g); + g->p.x = x - b; g->p.y = y + a; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x + b; g->p.y = y - a; drawpixel_clip(g); + g->p.x = x - b; g->p.y = y - a; drawpixel_clip(g); + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + g->p.x = x + a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x + a; g->p.y = y - b; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y + b; drawpixel_clip(g); + g->p.x = x - a; g->p.y = y - b; drawpixel_clip(g); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_CIRCLE + void gdispGFillCircle(GDisplay *g, coord_t x, coord_t y, coord_t radius, color_t color) { + coord_t a, b, P; + + MUTEX_ENTER(g); + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + g->p.color = color; + + // Away we go using Bresenham's circle algorithm + // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + g->p.y = y+b; g->p.x = x; drawpixel_clip(g); + g->p.y = y-b; g->p.x = x; drawpixel_clip(g); + do { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + if (P < 0) { + P += 3 + 2*a++; + } else { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); + P += 5 + 2*(a++ - b--); + } + } while(a < b); + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ELLIPSE + void gdispGDrawEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) { + coord_t dx, dy; + int32_t a2, b2; + int32_t err, e2; + + MUTEX_ENTER(g); + + // Calculate intermediates + dx = 0; + dy = b; + a2 = a*a; + b2 = b*b; + err = b2-(2*b-1)*a2; + g->p.color = color; + + // Away we go using Bresenham's ellipse algorithm + do { + g->p.x = x + dx; g->p.y = y + dy; drawpixel_clip(g); + g->p.x = x - dx; g->p.y = y + dy; drawpixel_clip(g); + g->p.x = x - dx; g->p.y = y - dy; drawpixel_clip(g); + g->p.x = x + dx; g->p.y = y - dy; drawpixel_clip(g); + + e2 = 2*err; + if(e2 < (2*dx+1)*b2) { + dx++; + err += (2*dx+1)*b2; + } + if(e2 > -(2*dy-1)*a2) { + dy--; + err -= (2*dy-1)*a2; + } + } while(dy >= 0); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ELLIPSE + void gdispGFillEllipse(GDisplay *g, coord_t x, coord_t y, coord_t a, coord_t b, color_t color) { + coord_t dx, dy; + int32_t a2, b2; + int32_t err, e2; + + MUTEX_ENTER(g); + + // Calculate intermediates + dx = 0; + dy = b; + a2 = a*a; + b2 = b*b; + err = b2-(2*b-1)*a2; + g->p.color = color; + + // Away we go using Bresenham's ellipse algorithm + // This is optimized to prevent overdrawing by drawing a line only when a y is about to change value + do { + e2 = 2*err; + if(e2 < (2*dx+1)*b2) { + dx++; + err += (2*dx+1)*b2; + } + if(e2 > -(2*dy-1)*a2) { + g->p.y = y + dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); + if (y) { g->p.y = y - dy; g->p.x = x - dx; g->p.x1 = x + dx; hline_clip(g); } + dy--; + err -= (2*dy-1)*a2; + } + } while(dy >= 0); + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARC + #if !GMISC_NEED_FIXEDTRIG && !GMISC_NEED_FASTTRIG + #include + #endif + + void gdispGDrawArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) { + coord_t a, b, P, sedge, eedge; + uint8_t full, sbit, ebit, tbit; + + // Normalize the angles + if (start < 0) + start -= (start/360-1)*360; + else if (start >= 360) + start %= 360; + if (end < 0) + end -= (end/360-1)*360; + else if (end >= 360) + end %= 360; + + sbit = 1<<(start/45); + ebit = 1<<(end/45); + full = 0; + if (start == end) { + full = 0xFF; + } else if (end < start) { + for(tbit=sbit<<1; tbit; tbit<<=1) full |= tbit; + for(tbit=ebit>>1; tbit; tbit>>=1) full |= tbit; + } else if (sbit < 0x80) { + for(tbit=sbit<<1; tbit < ebit; tbit<<=1) full |= tbit; + } + tbit = start%45 == 0 ? sbit : 0; + + MUTEX_ENTER(g); + g->p.color = color; + + if (full) { + // Draw full sectors + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if (full & 0x60) { g->p.y = y+b; g->p.x = x; drawpixel_clip(g); } + if (full & 0x06) { g->p.y = y-b; g->p.x = x; drawpixel_clip(g); } + if (full & 0x81) { g->p.y = y; g->p.x = x+b; drawpixel_clip(g); } + if (full & 0x18) { g->p.y = y; g->p.x = x-b; drawpixel_clip(g); } + do { + if (full & 0x01) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if (full & 0x02) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x04) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x08) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if (full & 0x10) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if (full & 0x20) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (full & 0x40) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (full & 0x80) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (full & 0xC0) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (full & 0x0C) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x03) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (full & 0x30) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (full == 0xFF) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + } + + #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG + sedge = NONFIXED(radius * ((sbit & 0x99) ? ffsin(start) : ffcos(start)) + FIXED0_5); + eedge = NONFIXED(radius * ((ebit & 0x99) ? ffsin(end) : ffcos(end)) + FIXED0_5); + #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG + sedge = round(radius * ((sbit & 0x99) ? fsin(start) : fcos(start))); + eedge = round(radius * ((ebit & 0x99) ? fsin(end) : fcos(end))); + #else + sedge = round(radius * ((sbit & 0x99) ? sin(start*M_PI/180) : cos(start*M_PI/180))); + eedge = round(radius * ((ebit & 0x99) ? sin(end*M_PI/180) : cos(end*M_PI/180))); + #endif + if (sbit & 0xB4) sedge = -sedge; + if (ebit & 0xB4) eedge = -eedge; + + if (sbit != ebit) { + // Draw start and end sectors + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if ((sbit & 0x20) || (tbit & 0x40) || (ebit & 0x40)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x02) || (tbit & 0x04) || (ebit & 0x04)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x80) || (tbit & 0x01) || (ebit & 0x01)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } + if ((sbit & 0x08) || (tbit & 0x10) || (ebit & 0x10)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } + do { + if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if (((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (((sbit & 0x40) && a >= sedge) || ((ebit & 0x40) && a <= eedge) || ((sbit & 0x80) && a <= sedge) || ((ebit & 0x80) && a >= eedge)) + { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x04) && a >= sedge) || ((ebit & 0x04) && a <= eedge) || ((sbit & 0x08) && a <= sedge) || ((ebit & 0x08) && a >= eedge)) + { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x01) && a >= sedge) || ((ebit & 0x01) && a <= eedge) || ((sbit & 0x02) && a <= sedge) || ((ebit & 0x02) && a >= eedge)) + { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge) || ((ebit & 0x10) && a <= eedge) || ((sbit & 0x20) && a <= sedge) || ((ebit & 0x20) && a >= eedge)) + { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + } else if (end < start) { + // Draw start/end sector where it is a non-internal angle + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if ((sbit & 0x60) || (tbit & 0xC0)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x06) || (tbit & 0x0C)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x81) || (tbit & 0x03)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } + if ((sbit & 0x18) || (tbit & 0x30)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } + do { + if ((sbit & 0x01) && (a >= sedge || a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if ((sbit & 0x02) && (a <= sedge || a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x04) && (a >= sedge || a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if ((sbit & 0x08) && (a <= sedge || a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if ((sbit & 0x10) && (a >= sedge || a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if ((sbit & 0x20) && (a <= sedge || a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x40) && (a >= sedge || a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if ((sbit & 0x80) && (a <= sedge || a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (((sbit & 0x04) && (a >= sedge || a <= eedge)) || ((sbit & 0x08) && (a <= sedge || a >= eedge))) + { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x40) && (a >= sedge || a <= eedge)) || ((sbit & 0x80) && (a <= sedge || a >= eedge))) + { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x01) && (a >= sedge || a <= eedge)) || ((sbit & 0x02) && (a <= sedge || a >= eedge))) + { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x10) && (a >= sedge || a <= eedge)) || ((sbit & 0x20) && (a <= sedge || a >= eedge))) + { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + } else { + // Draw start/end sector where it is a internal angle + // Optimized to prevent double drawing + a = 1; + b = radius; + P = 4 - radius; + if (((sbit & 0x20) && !eedge) || ((sbit & 0x40) && !sedge)) { g->p.x = x; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x02) && !eedge) || ((sbit & 0x04) && !sedge)) { g->p.x = x; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x80) && !eedge) || ((sbit & 0x01) && !sedge)) { g->p.x = x+b; g->p.y = y; drawpixel_clip(g); } + if (((sbit & 0x08) && !eedge) || ((sbit & 0x10) && !sedge)) { g->p.x = x-b; g->p.y = y; drawpixel_clip(g); } + do { + if (((sbit & 0x01) && a >= sedge && a <= eedge)) { g->p.x = x+b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x02) && a <= sedge && a >= eedge)) { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x04) && a >= sedge && a <= eedge)) { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x08) && a <= sedge && a >= eedge)) { g->p.x = x-b; g->p.y = y-a; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge && a <= eedge)) { g->p.x = x-b; g->p.y = y+a; drawpixel_clip(g); } + if (((sbit & 0x20) && a <= sedge && a >= eedge)) { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x40) && a >= sedge && a <= eedge)) { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x80) && a <= sedge && a >= eedge)) { g->p.x = x+b; g->p.y = y+a; drawpixel_clip(g); } + if (P < 0) + P += 3 + 2*a++; + else + P += 5 + 2*(a++ - b--); + } while(a < b); + if (((sbit & 0x04) && a >= sedge && a <= eedge) || ((sbit & 0x08) && a <= sedge && a >= eedge)) + { g->p.x = x-a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x40) && a >= sedge && a <= eedge) || ((sbit & 0x80) && a <= sedge && a >= eedge)) + { g->p.x = x+a; g->p.y = y+b; drawpixel_clip(g); } + if (((sbit & 0x01) && a >= sedge && a <= eedge) || ((sbit & 0x02) && a <= sedge && a >= eedge)) + { g->p.x = x+a; g->p.y = y-b; drawpixel_clip(g); } + if (((sbit & 0x10) && a >= sedge && a <= eedge) || ((sbit & 0x20) && a <= sedge && a >= eedge)) + { g->p.x = x-a; g->p.y = y+b; drawpixel_clip(g); } + } + + autoflush(g); + MUTEX_EXIT(g); + } +#endif + +#if GDISP_NEED_ARC + void gdispGFillArc(GDisplay *g, coord_t x, coord_t y, coord_t radius, coord_t start, coord_t end, color_t color) { + coord_t a, b, P; + coord_t sy, ey; + fixed sxa, sxb, sxd, exa, exb, exd; + uint8_t qtr; + + MUTEX_ENTER(g); + + // Do the trig to get the formulas for the start and end lines. + sxa = exa = FIXED(x)+FIXED0_5; + #if GFX_USE_GMISC && GMISC_NEED_FIXEDTRIG + sxb = radius*ffcos(start); sy = -NONFIXED(radius*ffsin(start) + FIXED0_5); + exb = radius*ffcos(end); ey = -NONFIXED(radius*ffsin(end) + FIXED0_5); + #elif GFX_USE_GMISC && GMISC_NEED_FASTTRIG + sxb = FP2FIXED(radius*fcos(start)); sy = -round(radius*fsin(start)); + exb = FP2FIXED(radius*fcos(end)); ey = -round(radius*fsin(end)); + #else + sxb = FP2FIXED(radius*cos(start*M_PI/180)); sy = -round(radius*sin(start*M_PI/180)); + exb = FP2FIXED(radius*cos(end*M_PI/180)); ey = -round(radius*sin(end*M_PI/180)); + #endif + sxd = sy ? sxb/sy : sxb; + exd = ey ? exb/ey : exb; + + // Calculate which quarters and which direction we are traveling + qtr = 0; + if (sxb > 0) qtr |= 0x01; // S1=0001(1), S2=0000(0), S3=0010(2), S4=0011(3) + if (sy > 0) qtr |= 0x02; + if (exb > 0) qtr |= 0x04; // E1=0100(4), E2=0000(0), E3=1000(8), E4=1100(12) + if (ey > 0) qtr |= 0x08; + if (sy > ey) qtr |= 0x10; // order of start and end lines + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + g->p.color = color; + sxb += sxa; + exb += exa; + + // Away we go using Bresenham's circle algorithm + // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value + + switch(qtr) { + case 0: // S2E2 sy <= ey + case 1: // S1E2 sy <= ey + if (ey && sy) { + g->p.x = x; g->p.x1 = x; // E2S + sxa -= sxd; exa -= exd; + } else if (sy) { + g->p.x = x-b; g->p.x1 = x; // C2S + sxa -= sxd; + } else if (ey) { + g->p.x = x; g->p.x1 = x+b; // E2C + exa -= exd; + } else { + g->p.x = x-b; g->p.x1 = x+b; // C2C + } + g->p.y = y; + hline_clip(g); + do { + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + sxa -= sxd; exa -= exd; + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + sxa -= sxd; + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S + sxb += sxd; exb += exd; + } else if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + sxb += sxd; + } else if (qtr & 1) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 2: // S3E2 sy <= ey + case 3: // S4E2 sy <= ey + case 6: // S3E1 sy <= ey + case 7: // S4E1 sy <= ey + case 18: // S3E2 sy > ey + case 19: // S4E2 sy > ey + case 22: // S3E1 sy > ey + case 23: // S4E1 sy > ey + g->p.y = y; g->p.x = x; g->p.x1 = x+b; hline_clip(g); // SE2C + sxa += sxd; exa -= exd; + do { + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + exa -= exd; + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + exb += exd; + } else if (!(qtr & 4)) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; + } else if (!(qtr & 1)) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+a; hline_clip(g); // S2C + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C + } + break; + + case 4: // S2E1 sy <= ey + case 5: // S1E1 sy <= ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + do { + if (-a >= ey) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + sxa -= sxd; exa -= exd; + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + sxa -= sxd; + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= ey) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + sxb += sxd; exb += exd; + } else if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + sxb += sxd; + } else if (qtr & 1) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= ey) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + break; + + case 8: // S2E3 sy <= ey + case 9: // S1E3 sy <= ey + case 12: // S2E4 sy <= ey + case 13: // S1E4 sy <= ey + case 24: // S2E3 sy > ey + case 25: // S1E3 sy > ey + case 28: // S2E3 sy > ey + case 29: // S1E3 sy > ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x; hline_clip(g); // C2SE + sxa -= sxd; exa += exd; + do { + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + sxa -= sxd; + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + exa += exd; + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + sxb += sxd; + } else if (qtr & 1) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + exb -= exd; + } else if (qtr & 4) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + } else if (qtr & 1) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+a; hline_clip(g); // C2C + } + break; + + case 10: // S3E3 sy <= ey + case 14: // S3E4 sy <= ey + g->p.y = y; g->p.x = x; drawpixel_clip(g); // S2E + sxa += sxd; exa += exd; + do { + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E + sxa += sxd; exa += exd; + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + exa += exd; + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E + sxb -= sxd; exb -= exd; + } else if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + exb -= exd; + } else if (qtr & 4) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 11: // S4E3 sy <= ey + case 15: // S4E4 sy <= ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + do { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= sy) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; exa += exd; + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + exa += exd; + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + if (b <= sy) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; exb -= exd; + } else if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + exb -= exd; + } else if (qtr & 4) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= sy) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + } else if (qtr & 4) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 16: // S2E2 sy > ey + case 20: // S2E1 sy > ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + sxa -= sxd; exa -= exd; + do { + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + sxa -= sxd; exa -= exd; + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + exa -= exd; + } else if (!(qtr & 4)){ + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= sy) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = NONFIXED(sxb); hline_clip(g); // C2S + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + sxb += sxd; exb += exd; + } else if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + exb += exd; + } else if (!(qtr & 4)){ + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= sy) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = NONFIXED(sxa); hline_clip(g); // C2S + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (!(qtr & 4)){ + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + break; + + case 17: // S1E2 sy > ey + case 21: // S1E1 sy > ey + if (sy) { + g->p.x = x; g->p.x1 = x; // E2S + sxa -= sxd; exa -= exd; + } else { + g->p.x = x; g->p.x1 = x+b; // E2C + exa -= exd; + } + g->p.y = y; + hline_clip(g); + do { + if (-a >= sy) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + sxa -= sxd; exa -= exd; + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + exa -= exd; + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (-b >= sy) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = NONFIXED(sxb); hline_clip(g); // E2S + sxb += sxd; exb += exd; + } else if (-b >= ey) { + g->p.y = y-b; g->p.x = NONFIXED(exb); g->p.x1 = x+a; hline_clip(g); // E2C + exb += exd; + } else if (!(qtr & 4)) { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (-a >= sy) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = NONFIXED(sxa); hline_clip(g); // E2S + } else if (-a >= ey) { + g->p.y = y-a; g->p.x = NONFIXED(exa); g->p.x1 = x+b; hline_clip(g); // E2C + } else if (!(qtr & 4)) { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 26: // S3E3 sy > ey + case 27: // S4E3 sy > ey + g->p.y = y; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + do { + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; exa += exd; + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + g->p.y = y-b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + if (b <= ey) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = NONFIXED(exb); hline_clip(g); // C2E + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; exb -= exd; + } else if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; + } else if (!(qtr & 1)) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + g->p.y = y-a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + if (a <= ey) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = NONFIXED(exa); hline_clip(g); // C2E + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (!(qtr & 4)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + + case 30: // S3E4 sy > ey + case 31: // S4E4 sy > ey + do { + if (a <= ey) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = NONFIXED(exa); hline_clip(g); // S2E + sxa += sxd; exa += exd; + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + sxa += sxd; + } else if (!(qtr & 1)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + if (P < 0) { + P += 3 + 2*a++; + } else { + if (b <= ey) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = NONFIXED(exb); hline_clip(g); // S2E + sxb -= sxd; exb -= exd; + } else if (b <= sy) { + g->p.y = y+b; g->p.x = NONFIXED(sxb); g->p.x1 = x+a; hline_clip(g); // S2C + sxb -= sxd; + } else if (!(qtr & 1)) { + g->p.y = y+b; g->p.x = x-a; g->p.x1 = x+a; hline_clip(g); // C2C + } + P += 5 + 2*(a++ - b--); + } + } while(a < b); + if (a <= ey) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (a <= sy) { + g->p.y = y+a; g->p.x = NONFIXED(sxa); g->p.x1 = x+b; hline_clip(g); // S2C + } else if (!(qtr & 4)) { + g->p.y = y+a; g->p.x = x-b; g->p.x1 = x+b; hline_clip(g); // C2C + } + break; + } + + autoflush(g); + MUTEX_EXIT(g); + } + +#endif + +#if GDISP_NEED_ARC + void gdispGDrawRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { + if (2*radius > cx || 2*radius > cy) { + gdispGDrawBox(g, x, y, cx, cy, color); + return; + } + gdispGDrawArc(g, x+radius, y+radius, radius, 90, 180, color); + gdispGDrawLine(g, x+radius+1, y, x+cx-2-radius, y, color); + gdispGDrawArc(g, x+cx-1-radius, y+radius, radius, 0, 90, color); + gdispGDrawLine(g, x+cx-1, y+radius+1, x+cx-1, y+cy-2-radius, color); + gdispGDrawArc(g, x+cx-1-radius, y+cy-1-radius, radius, 270, 360, color); + gdispGDrawLine(g, x+radius+1, y+cy-1, x+cx-2-radius, y+cy-1, color); + gdispGDrawArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); + gdispGDrawLine(g, x, y+radius+1, x, y+cy-2-radius, color); + } +#endif + +#if GDISP_NEED_ARC + void gdispGFillRoundedBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t radius, color_t color) { + coord_t radius2; + + radius2 = radius*2; + if (radius2 > cx || radius2 > cy) { + gdispGFillArea(g, x, y, cx, cy, color); + return; + } + gdispGFillArc(g, x+radius, y+radius, radius, 90, 180, color); + gdispGFillArea(g, x+radius+1, y, cx-radius2, radius, 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); + gdispGFillArea(g, x+radius+1, y+cy-radius, cx-radius2, radius, color); + gdispGFillArc(g, x+radius, y+cy-1-radius, radius, 180, 270, color); + gdispGFillArea(g, x, y+radius, cx, cy-radius2, color); + } +#endif + +#if GDISP_NEED_PIXELREAD + color_t gdispGGetPixelColor(GDisplay *g, coord_t x, coord_t y) { + color_t c; + + /* Always synchronous as it must return a value */ + MUTEX_ENTER(g); + #if GDISP_HARDWARE_PIXELREAD + #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT + if (g->vmt->get) + #endif + { + // Best is direct pixel read + g->p.x = x; + g->p.y = y; + c = gdisp_lld_get_pixel_color(g); + MUTEX_EXIT(g); + return c; + } + #endif + #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ + #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT + if (g->vmt->readcolor) + #endif + { + // Next best is hardware streaming + g->p.x = x; + g->p.y = y; + g->p.cx = 1; + g->p.cy = 1; + gdisp_lld_read_start(g); + c = gdisp_lld_read_color(g); + gdisp_lld_read_stop(g); + MUTEX_EXIT(g); + return c; + } + #endif + #if GDISP_HARDWARE_PIXELREAD != TRUE && GDISP_HARDWARE_STREAM_READ != TRUE + #if !GDISP_HARDWARE_PIXELREAD && !GDISP_HARDWARE_STREAM_READ + // Worst is "not possible" + #error "GDISP: GDISP_NEED_PIXELREAD has been set but there is no hardware support for reading the display" + #endif + MUTEX_EXIT(g); + return 0; + #endif + } +#endif + +#if GDISP_NEED_SCROLL + void gdispGVerticalScroll(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, int lines, color_t bgcolor) { + coord_t abslines; + #if GDISP_HARDWARE_SCROLL != TRUE + coord_t fy, dy, ix, fx, i, j; + #endif + + MUTEX_ENTER(g); + #if NEED_CLIPPING + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (!g->vmt->setclip) + #endif + { + if (x < g->clipx0) { cx -= g->clipx0 - x; x = g->clipx0; } + if (y < g->clipy0) { cy -= g->clipy0 - y; y = g->clipy0; } + if (!lines || cx <= 0 || cy <= 0 || x >= g->clipx1 || y >= g->clipy1) { MUTEX_EXIT(g); return; } + if (x+cx > g->clipx1) cx = g->clipx1 - x; + if (y+cy > g->clipy1) cy = g->clipy1 - y; + } + #endif + + abslines = lines < 0 ? -lines : lines; + if (abslines >= cy) { + abslines = cy; + cy = 0; + } else { + // Best is hardware scroll + #if GDISP_HARDWARE_SCROLL + #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT + if (g->vmt->vscroll) + #endif + { + g->p.x = x; + g->p.y = y; + g->p.cx = cx; + g->p.cy = cy; + g->p.y1 = lines; + g->p.color = bgcolor; + gdisp_lld_vertical_scroll(g); + cy -= abslines; + } + #if GDISP_HARDWARE_SCROLL == HARDWARE_AUTODETECT + else + #endif + #elif GDISP_LINEBUF_SIZE == 0 + #error "GDISP: GDISP_NEED_SCROLL is set but there is no hardware support and GDISP_LINEBUF_SIZE is zero." + #endif + + // Scroll Emulation + #if GDISP_HARDWARE_SCROLL != TRUE + { + cy -= abslines; + if (lines < 0) { + fy = y+cy-1; + dy = -1; + } else { + fy = y; + dy = 1; + } + // Move the screen - one line at a time + for(i = 0; i < cy; i++, fy += dy) { + + // Handle where the buffer is smaller than a line + for(ix=0; ix < cx; ix += GDISP_LINEBUF_SIZE) { + + // Calculate the data we can move in one operation + fx = cx - ix; + if (fx > GDISP_LINEBUF_SIZE) + fx = GDISP_LINEBUF_SIZE; + + // Read one line of data from the screen + + // Best line read is hardware streaming + #if GDISP_HARDWARE_STREAM_READ + #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT + if (g->vmt->readstart) + #endif + { + g->p.x = x+ix; + g->p.y = fy+lines; + g->p.cx = fx; + g->p.cy = 1; + gdisp_lld_read_start(g); + for(j=0; j < fx; j++) + g->linebuf[j] = gdisp_lld_read_color(g); + gdisp_lld_read_stop(g); + } + #if GDISP_HARDWARE_STREAM_READ == HARDWARE_AUTODETECT + else + #endif + #endif + + // Next best line read is single pixel reads + #if GDISP_HARDWARE_STREAM_READ != TRUE && GDISP_HARDWARE_PIXELREAD + #if GDISP_HARDWARE_PIXELREAD == HARDWARE_AUTODETECT + if (g->vmt->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 (g->vmt->blit) + #endif + { + g->p.x = x+ix; + g->p.y = fy; + g->p.cx = fx; + g->p.cy = 1; + g->p.x1 = 0; + g->p.y1 = 0; + g->p.x2 = fx; + g->p.ptr = (void *)g->linebuf; + gdisp_lld_blit_area(g); + } + #if GDISP_HARDWARE_BITFILLS == HARDWARE_AUTODETECT + else + #endif + #endif + + // Next best line write is hardware streaming + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + if (g->vmt->writestart) + #endif + { + g->p.x = x+ix; + g->p.y = fy; + g->p.cx = fx; + g->p.cy = 1; + gdisp_lld_write_start(g); + #if GDISP_HARDWARE_STREAM_POS + gdisp_lld_write_pos(g); + #endif + for(j = 0; j < fx; j++) { + g->p.color = g->linebuf[j]; + gdisp_lld_write_color(g); + } + gdisp_lld_write_stop(g); + } + #if GDISP_HARDWARE_STREAM_WRITE == HARDWARE_AUTODETECT + else + #endif + #endif + + // Next best line write is drawing pixels in combination with filling + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS && GDISP_HARDWARE_DRAWPIXEL + // We don't need to test for auto-detect on drawpixel as we know we have it because we don't have streaming. + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + if (g->vmt->fill) + #endif + { + g->p.y = fy; + g->p.cy = 1; + g->p.x = x+ix; + g->p.cx = 1; + for(j = 0; j < fx; ) { + g->p.color = g->linebuf[j]; + if (j + g->p.cx < fx && g->linebuf[j] == g->linebuf[j + g->p.cx]) + g->p.cx++; + else if (g->p.cx == 1) { + gdisp_lld_draw_pixel(g); + j++; + g->p.x++; + } else { + gdisp_lld_fill_area(g); + j += g->p.cx; + g->p.x += g->p.cx; + g->p.cx = 1; + } + } + } + #if GDISP_HARDWARE_FILLS == HARDWARE_AUTODETECT + else + #endif + #endif + + // Worst line write is drawing pixels + #if GDISP_HARDWARE_BITFILLS != TRUE && GDISP_HARDWARE_STREAM_WRITE != TRUE && GDISP_HARDWARE_FILLS != TRUE && GDISP_HARDWARE_DRAWPIXEL + // The following test is unneeded because we are guaranteed to have draw pixel if we don't have streaming + //#if GDISP_HARDWARE_DRAWPIXEL == HARDWARE_AUTODETECT + // if (g->vmt->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 (!g->vmt->control) + return; + #endif + MUTEX_ENTER(g); + g->p.x = what; + g->p.ptr = value; + if (what == GDISP_CONTROL_ORIENTATION) { + switch ((orientation_t) value) { + case GDISP_ROTATE_LANDSCAPE: + g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_0 : (void *)GDISP_ROTATE_90; + break; + case GDISP_ROTATE_PORTRAIT: + g->p.ptr = g->g.Width >= g->g.Height ? (void *)GDISP_ROTATE_90 : (void *)GDISP_ROTATE_0; + break; + default: + break; + } + } + gdisp_lld_control(g); + #if GDISP_NEED_CLIP || GDISP_NEED_VALIDATION + if (what == GDISP_CONTROL_ORIENTATION) { + // Best is hardware clipping + #if GDISP_HARDWARE_CLIP + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + if (g->vmt->setclip) + #endif + { + g->p.x = 0; + g->p.y = 0; + g->p.cx = g->g.Width; + g->p.cy = g->g.Height; + gdisp_lld_set_clip(g); + } + #if GDISP_HARDWARE_CLIP == HARDWARE_AUTODETECT + else + #endif + #endif + + // Worst is software clipping + #if GDISP_HARDWARE_CLIP != TRUE + { + g->clipx0 = 0; + g->clipy0 = 0; + g->clipx1 = g->g.Width; + g->clipy1 = g->g.Height; + } + #endif + } + #endif + MUTEX_EXIT(g); + } + #else + void gdispGControl(GDisplay *g, unsigned what, void *value) { + (void)g; + (void)what; + (void)value; + /* Ignore everything */ + } + #endif +#endif + +#if GDISP_NEED_QUERY + #if GDISP_HARDWARE_QUERY + void *gdispGQuery(GDisplay *g, unsigned what) { + void *res; + + #if GDISP_HARDWARE_QUERY == HARDWARE_AUTODETECT + if (!g->vmt->query) + return -1; + #endif + MUTEX_ENTER(g); + g->p.x = (coord_t)what; + res = gdisp_lld_query(g); + MUTEX_EXIT(g); + return res; + } + #else + void *gdispGQuery(GDisplay *g, unsigned what) { + (void) what; + return (void *)-1; + } + #endif +#endif + +/*===========================================================================*/ +/* High Level Driver Routines. */ +/*===========================================================================*/ + +void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color) { + if (cx <= 0 || cy <= 0) return; + cx = x+cx-1; cy = y+cy-1; // cx, cy are now the end point. + + MUTEX_ENTER(g); + + g->p.color = color; + + if (cx - x > 2) { + g->p.x = x; g->p.y = y; g->p.x1 = cx; hline_clip(g); + if (y != cy) { + g->p.x = x; g->p.y = cy; g->p.x1 = cx; hline_clip(g); + if (cy - y > 2) { + y++; cy--; + g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g); + g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g); + } + } + } else { + g->p.x = x; g->p.y = y; g->p.y1 = cy; vline_clip(g); + if (x != cx) { + g->p.x = cx; g->p.y = y; g->p.y1 = cy; vline_clip(g); + } + } + + autoflush(g); + MUTEX_EXIT(g); +} + +#if GDISP_NEED_CONVEX_POLYGON + void gdispGDrawPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) { + const point *epnt, *p; + + epnt = &pntarray[cnt-1]; + + MUTEX_ENTER(g); + g->p.color = color; + for(p = pntarray; p < epnt; p++) { + g->p.x=tx+p->x; g->p.y=ty+p->y; g->p.x1=tx+p[1].x; g->p.y1=ty+p[1].y; line_clip(g); + } + g->p.x=tx+p->x; g->p.y=ty+p->y; g->p.x1=tx+pntarray->x; g->p.y1=ty+pntarray->y; line_clip(g); + + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillConvexPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color) { + const point *lpnt, *rpnt, *epnts; + fixed lx, rx, lk, rk; + coord_t y, ymax, lxc, rxc; + + epnts = &pntarray[cnt-1]; + + /* Find a top point */ + rpnt = pntarray; + for(lpnt=pntarray+1; lpnt <= epnts; lpnt++) { + if (lpnt->y < rpnt->y) + rpnt = lpnt; + } + lx = rx = FIXED(rpnt->x); + y = rpnt->y; + + /* Work out the slopes of the two attached line segs */ + for (lpnt = rpnt <= pntarray ? epnts : rpnt-1; lpnt->y == y; cnt--) { + if (!cnt) return; + lx = FIXED(lpnt->x); + lpnt = lpnt <= pntarray ? epnts : lpnt-1; + } + for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) { + if (!cnt) return; + rx = FIXED(rpnt->x); + rpnt = rpnt >= epnts ? pntarray : rpnt+1; + } + lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y); + rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y); + + MUTEX_ENTER(g); + g->p.color = color; + while(1) { + /* Determine our boundary */ + ymax = rpnt->y < lpnt->y ? rpnt->y : lpnt->y; + + /* Scan down the line segments until we hit a boundary */ + for(; y < ymax; y++) { + lxc = NONFIXED(lx); + rxc = NONFIXED(rx); + /* + * Doesn't print the right hand point in order to allow polygon joining. + * Also ensures that we draw from left to right with the minimum number + * of pixels. + */ + if (lxc < rxc) { + g->p.x=tx+lxc; g->p.y=ty+y; g->p.x1=tx+rxc-1; hline_clip(g); + } else if (lxc > rxc) { + g->p.x=tx+rxc; g->p.y=ty+y; g->p.x1=tx+lxc-1; hline_clip(g); + } + + lx += lk; + rx += rk; + } + + if (!cnt) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + cnt--; + + /* Replace the appropriate point */ + if (ymax == lpnt->y) { + for (lpnt = lpnt <= pntarray ? epnts : lpnt-1; lpnt->y == y; cnt--) { + if (!cnt) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + lx = FIXED(lpnt->x); + lpnt = lpnt <= pntarray ? epnts : lpnt-1; + } + lk = (FIXED(lpnt->x) - lx) / (lpnt->y - y); + } else { + for (rpnt = rpnt >= epnts ? pntarray : rpnt+1; rpnt->y == y; cnt--) { + if (!cnt) { + autoflush(g); + MUTEX_EXIT(g); + return; + } + rx = FIXED(rpnt->x); + rpnt = rpnt >= epnts ? pntarray : rpnt+1; + } + rk = (FIXED(rpnt->x) - rx) / (rpnt->y - y); + } + } + } + + static int32_t rounding_div(const int32_t n, const int32_t d) + { + if ((n < 0) != (d < 0)) + return (n - d/2) / d; + else + return (n + d/2) / d; + } + + /* Find a vector (nx, ny) that is perpendicular to (dx, dy) and has length + * equal to 'norm'. */ + static void get_normal_vector(coord_t dx, coord_t dy, coord_t norm, coord_t *nx, coord_t *ny) + { + int32_t dx2, dy2, len_sq, norm_sq, norm_sq2; + int div, step, best, delta, abs_delta; + + dx2 = dx; dy2 = dy; + norm_sq = (int32_t)norm * norm; + norm_sq2 = norm_sq * 512; + + /* Scale dx2 and dy2 so that + * len_sq / 2 <= norm_sq * 512 <= len_sq * 2. + * The scaling by 512 is to yield higher accuracy in division later. */ + len_sq = dx2 * dx2 + dy2 * dy2; + + if (len_sq < norm_sq2) + { + while (len_sq && len_sq < norm_sq2) + { + len_sq <<= 2; dx2 <<= 1; dy2 <<= 1; + } + } + else if (len_sq > norm_sq2) + { + while (len_sq && len_sq > norm_sq2) + { + len_sq >>= 2; dx2 >>= 1; dy2 >>= 1; + } + } + + /* Now find the divider div so that + * len_sq / div^2 == norm_sq i.e. div = sqrt(len_sq / norm_sq) + * + * This is done using bisection search to avoid the need for floating + * point sqrt. + * + * Based on previous scaling, we know that + * len_sq / 2 <= norm_sq * 512 <=> div <= sqrt(1024) = 32 + * len_sq * 2 >= norm_sq * 512 <=> div >= sqrt(256) = 16 + */ + div = 24; step = 8; + best = 256; + + for (;;) + { + dx = dx2 / div; + dy = dy2 / div; + len_sq = dx*dx + dy*dy; + + delta = len_sq - norm_sq; + + abs_delta = (delta >= 0) ? delta : -delta; + + if (abs_delta < best) + { + *nx = dy; + *ny = -dx; + best = abs_delta; + } + + if (delta > 0) + div += step; + else if (delta < 0) + div -= step; + else if (delta == 0) + break; + + if (step == 0) + break; + else + step >>= 1; /* Do one round with step = 0 to calculate final result. */ + } + } + + void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round) { + coord_t dx, dy, nx = 0, ny = 0; + + /* Compute the direction vector for the line */ + dx = x1 - x0; + dy = y1 - y0; + + /* Draw a small dot if the line length is zero. */ + if (dx == 0 && dy == 0) + dx += 1; + + /* Compute a normal vector with length 'width'. */ + get_normal_vector(dx, dy, width, &nx, &ny); + + /* Handle 1px wide lines gracefully */ + if (nx == 0 && ny == 0) + nx = 1; + + /* Offset the x0,y0 by half the width of the line. This way we + * can keep the width of the line accurate even if it is not evenly + * divisible by 2. + */ + { + x0 -= rounding_div(nx, 2); + y0 -= rounding_div(ny, 2); + } + + /* Fill in the point array */ + if (!round) { + /* We use 4 points for the basic line shape: + * + * pt1 pt2 + * (+n) ------------------------------------ (d+n) + * | | + * (0,0) ----------------------------------- (d) + * pt0 pt3 + */ + point pntarray[4]; + + pntarray[0].x = 0; + pntarray[0].y = 0; + pntarray[1].x = nx; + pntarray[1].y = ny; + pntarray[2].x = dx + nx; + pntarray[2].y = dy + ny; + pntarray[3].x = dx; + pntarray[3].y = dy; + + gdispGFillConvexPoly(g, x0, y0, pntarray, 4, color); + } else { + /* We use 4 points for basic shape, plus 4 extra points for ends: + * + * pt3 ------------------ pt4 + * / \ + * pt2 pt5 + * | | + * pt1 pt6 + * \ / + * pt0 -------------------pt7 + */ + point pntarray[8]; + coord_t nx2, ny2; + + /* Magic numbers: + * 75/256 = sin(45) / (1 + sqrt(2)) diagonal octagon segments + * 106/256 = 1 / (1 + sqrt(2)) octagon side + * 53/256 = 0.5 / (1 + sqrt(2)) half of octagon side + * 150/256 = 1 - 1 / (1 + sqrt(2)) octagon height minus one side + */ + + /* Rotate the normal vector 45 deg counter-clockwise and reduce + * to 1 / (1 + sqrt(2)) length, for forming octagonal ends. */ + nx2 = rounding_div((nx * 75 + ny * 75), 256); + ny2 = rounding_div((-nx * 75 + ny * 75), 256); + + /* Offset and extend the line so that the center of the octagon + * is at the specified points. */ + x0 += ny * 53 / 256; + y0 -= nx * 53 / 256; + dx -= ny * 106 / 256; + dy += nx * 106 / 256; + + /* Now fill in the points by summing the calculated vectors. */ + pntarray[0].x = 0; + pntarray[0].y = 0; + pntarray[1].x = nx2; + pntarray[1].y = ny2; + pntarray[2].x = nx2 + nx * 106/256; + pntarray[2].y = ny2 + ny * 106/256; + pntarray[3].x = nx; + pntarray[3].y = ny; + pntarray[4].x = dx + nx; + pntarray[4].y = dy + ny; + pntarray[5].x = dx + nx - nx2; + pntarray[5].y = dy + ny - ny2; + pntarray[6].x = dx + nx * 150/256 - nx2; + pntarray[6].y = dy + ny * 150/256 - ny2; + pntarray[7].x = dx; + pntarray[7].y = dy; + + gdispGFillConvexPoly(g, x0, y0, pntarray, 8, color); + } + } +#endif + +#if GDISP_NEED_TEXT + #include "mcufont/mcufont.h" + + #if GDISP_NEED_ANTIALIAS && GDISP_HARDWARE_PIXELREAD + static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { + #define GD ((GDisplay *)state) + if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) + return; + if (x < GD->t.clipx0) { + count -= GD->t.clipx0 - x; + x = GD->t.clipx0; + } + if (x+count > GD->t.clipx1) + count = GD->t.clipx1 - x; + if (alpha == 255) { + GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color; + hline_clip(GD); + } else { + for (; count; count--, x++) { + GD->p.x = x; GD->p.y = y; + GD->p.color = gdispBlendColor(GD->t.color, gdisp_lld_get_pixel_color(GD), alpha); + drawpixel_clip(GD); + } + } + #undef GD + } + #else + static void drawcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { + #define GD ((GDisplay *)state) + if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) + return; + if (x < GD->t.clipx0) { + count -= GD->t.clipx0 - x; + x = GD->t.clipx0; + } + if (x+count > GD->t.clipx1) + count = GD->t.clipx1 - x; + if (alpha > 0x80) { // A best approximation when using anti-aliased fonts but we can't actually draw them anti-aliased + GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; GD->p.color = GD->t.color; + hline_clip(GD); + } + #undef GD + } + #endif + + #if GDISP_NEED_ANTIALIAS + static void fillcharline(int16_t x, int16_t y, uint8_t count, uint8_t alpha, void *state) { + #define GD ((GDisplay *)state) + if (y < GD->t.clipy0 || y >= GD->t.clipy1 || x+count <= GD->t.clipx0 || x >= GD->t.clipx1) + return; + if (x < GD->t.clipx0) { + count -= GD->t.clipx0 - x; + x = GD->t.clipx0; + } + if (x+count > GD->t.clipx1) + count = GD->t.clipx1 - x; + if (alpha == 255) { + GD->p.color = GD->t.color; + } else { + GD->p.color = gdispBlendColor(GD->t.color, GD->t.bgcolor, alpha); + } + GD->p.x = x; GD->p.y = y; GD->p.x1 = x+count-1; + hline_clip(GD); + #undef GD + } + #else + #define fillcharline drawcharline + #endif + + /* Callback to render characters. */ + static uint8_t drawcharglyph(int16_t x, int16_t y, mf_char ch, void *state) { + #define GD ((GDisplay *)state) + return mf_render_character(GD->t.font, x, y, ch, drawcharline, state); + #undef GD + } + + /* Callback to render characters. */ + static uint8_t fillcharglyph(int16_t x, int16_t y, mf_char ch, void *state) { + #define GD ((GDisplay *)state) + return mf_render_character(GD->t.font, x, y, ch, fillcharline, state); + #undef GD + } + + void gdispGDrawChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color) { + MUTEX_ENTER(g); + g->t.font = font; + g->t.clipx0 = x; + g->t.clipy0 = y; + g->t.clipx1 = x + mf_character_width(font, c) + font->baseline_x; + g->t.clipy1 = y + font->height; + g->t.color = color; + mf_render_character(font, x, y, c, drawcharline, g); + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillChar(GDisplay *g, coord_t x, coord_t y, uint16_t c, font_t font, color_t color, color_t bgcolor) { + MUTEX_ENTER(g); + g->p.cx = mf_character_width(font, c) + font->baseline_x; + g->p.cy = font->height; + g->t.font = font; + g->t.clipx0 = g->p.x = x; + g->t.clipy0 = g->p.y = y; + g->t.clipx1 = g->p.x+g->p.cx; + g->t.clipy1 = g->p.y+g->p.cy; + g->t.color = color; + g->t.bgcolor = g->p.color = bgcolor; + + TEST_CLIP_AREA(g) { + fillarea(g); + mf_render_character(font, x, y, c, fillcharline, g); + } + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGDrawString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color) { + MUTEX_ENTER(g); + g->t.font = font; + g->t.clipx0 = x; + g->t.clipy0 = y; + g->t.clipx1 = x + mf_get_string_width(font, str, 0, 0); + g->t.clipy1 = y + font->height; + g->t.color = color; + + mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, drawcharglyph, g); + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillString(GDisplay *g, coord_t x, coord_t y, const char *str, font_t font, color_t color, color_t bgcolor) { + MUTEX_ENTER(g); + g->p.cx = mf_get_string_width(font, str, 0, 0); + g->p.cy = font->height; + g->t.font = font; + g->t.clipx0 = g->p.x = x; + g->t.clipy0 = g->p.y = y; + g->t.clipx1 = g->p.x+g->p.cx; + g->t.clipy1 = g->p.y+g->p.cy; + g->t.color = color; + g->t.bgcolor = g->p.color = bgcolor; + + TEST_CLIP_AREA(g) { + fillarea(g); + mf_render_aligned(font, x+font->baseline_x, y, MF_ALIGN_LEFT, str, 0, fillcharglyph, g); + } + + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGDrawStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, justify_t justify) { + MUTEX_ENTER(g); + g->t.font = font; + g->t.clipx0 = x; + g->t.clipy0 = y; + g->t.clipx1 = x+cx; + g->t.clipy1 = y+cy; + g->t.color = color; + + /* Select the anchor position */ + switch(justify) { + case justifyCenter: + x += (cx + 1) / 2; + break; + case justifyRight: + x += cx; + break; + default: // justifyLeft + x += font->baseline_x; + break; + } + y += (cy+1 - font->height)/2; + + mf_render_aligned(font, x, y, justify, str, 0, drawcharglyph, g); + + autoflush(g); + MUTEX_EXIT(g); + } + + void gdispGFillStringBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, const char* str, font_t font, color_t color, color_t bgcolor, justify_t justify) { + MUTEX_ENTER(g); + g->p.cx = cx; + g->p.cy = cy; + g->t.font = font; + g->t.clipx0 = g->p.x = x; + g->t.clipy0 = g->p.y = y; + g->t.clipx1 = x+cx; + g->t.clipy1 = y+cy; + g->t.color = color; + g->t.bgcolor = g->p.color = bgcolor; + + TEST_CLIP_AREA(g) { + + // background fill + fillarea(g); + + /* Select the anchor position */ + switch(justify) { + case justifyCenter: + x += (cx + 1) / 2; + break; + case justifyRight: + x += cx; + break; + default: // justifyLeft + x += font->baseline_x; + break; + } + y += (cy+1 - font->height)/2; + + /* Render */ + mf_render_aligned(font, x, y, justify, str, 0, fillcharglyph, g); + } + + autoflush(g); + MUTEX_EXIT(g); + } + + coord_t gdispGetFontMetric(font_t font, fontmetric_t metric) { + /* No mutex required as we only read static data */ + switch(metric) { + case fontHeight: return font->height; + case fontDescendersHeight: return font->height - font->baseline_y; + case fontLineSpacing: return font->line_height; + case fontCharPadding: return 0; + case fontMinWidth: return font->min_x_advance; + case fontMaxWidth: return font->max_x_advance; + } + return 0; + } + + coord_t gdispGetCharWidth(char c, font_t font) { + /* No mutex required as we only read static data */ + return mf_character_width(font, c); + } + + coord_t gdispGetStringWidth(const char* str, font_t font) { + /* No mutex required as we only read static data */ + return mf_get_string_width(font, str, 0, 0); + } +#endif + +color_t gdispBlendColor(color_t fg, color_t bg, uint8_t alpha) +{ + uint16_t fg_ratio = alpha + 1; + uint16_t bg_ratio = 256 - alpha; + uint16_t r, g, b; + + r = RED_OF(fg) * fg_ratio; + g = GREEN_OF(fg) * fg_ratio; + b = BLUE_OF(fg) * fg_ratio; + + r += RED_OF(bg) * bg_ratio; + g += GREEN_OF(bg) * bg_ratio; + b += BLUE_OF(bg) * bg_ratio; + + r >>= 8; + g >>= 8; + b >>= 8; + + return RGB2COLOR(r, g, b); +} + +color_t gdispContrastColor(color_t color) { + uint16_t r, g, b; + + r = RED_OF(color) > 128 ? 0 : 255; + g = GREEN_OF(color) > 128 ? 0 : 255; + b = BLUE_OF(color) > 128 ? 0 : 255; + + return RGB2COLOR(r, g, b); +} + +#if (!defined(gdispPackPixels) && !defined(GDISP_PIXELFORMAT_CUSTOM)) + void gdispPackPixels(pixel_t *buf, coord_t cx, coord_t x, coord_t y, color_t color) { + /* No mutex required as we only read static data */ + #if defined(GDISP_PIXELFORMAT_RGB888) + #error "GDISP: Packed pixels not supported yet" + #elif defined(GDISP_PIXELFORMAT_RGB444) + #error "GDISP: Packed pixels not supported yet" + #elif defined(GDISP_PIXELFORMAT_RGB666) + #error "GDISP: Packed pixels not supported yet" + #elif + #error "GDISP: Unsupported packed pixel format" + #endif + } +#endif + +#if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT + LLDCOLOR_TYPE gdispColor2Native(color_t c) { + #if COLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE || LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE + #if GDISP_HARDWARE_USE_EXACT_COLOR + return LLDLUMA2COLOR(EXACT_LUMA_OF(c)); + #else + return LLDLUMA2COLOR(LUMA_OF(c)); + #endif + #elif COLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR && LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR + #if GDISP_HARDWARE_USE_EXACT_COLOR + return LLDRGB2COLOR(EXACT_RED_OF(c), EXACT_GREEN_OF(c), EXACT_BLUE_OF(c)); + #else + return LLDRGB2COLOR(RED_OF(c), GREEN_OF(c), BLUE_OF(c)); + #endif + #else + #error "GDISP: This pixel format conversion is not supported yet" + #endif + } +#endif + +#if GDISP_PIXELFORMAT != GDISP_LLD_PIXELFORMAT + color_t gdispNative2Color(LLDCOLOR_TYPE c) { + #if COLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE || LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_GRAYSCALE + #if GDISP_HARDWARE_USE_EXACT_COLOR + return LUMA2COLOR(LLDEXACT_LUMA_OF(c)); + #else + return LUMA2COLOR(LLDLUMA_OF(c)); + #endif + #elif COLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR && LLDCOLOR_SYSTEM == GDISP_COLORSYSTEM_TRUECOLOR + #if GDISP_HARDWARE_USE_EXACT_COLOR + return RGB2COLOR(LLDEXACT_RED_OF(c), LLDEXACT_GREEN_OF(c), LLDEXACT_BLUE_OF(c)); + #else + return RGB2COLOR(LLDRED_OF(c), LLDGREEN_OF(c), LLDBLUE_OF(c)); + #endif + #else + #error "GDISP: This pixel format conversion is not supported yet" + #endif + } +#endif + +#endif /* GFX_USE_GDISP */ +/** @} */ diff --git a/src/gdisp/gdisp_image.c b/src/gdisp/gdisp_image.c new file mode 100644 index 00000000..2851a7c8 --- /dev/null +++ b/src/gdisp/gdisp_image.c @@ -0,0 +1,233 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/gdisp_image.c + * @brief GDISP generic image code. + * + * @defgroup Image Image + * @ingroup GDISP + */ +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE + +#if GDISP_NEED_IMAGE_NATIVE + extern gdispImageError gdispImageOpen_NATIVE(gdispImage *img); + extern void gdispImageClose_NATIVE(gdispImage *img); + extern gdispImageError gdispImageCache_NATIVE(gdispImage *img); + extern gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_NATIVE(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_GIF + extern gdispImageError gdispImageOpen_GIF(gdispImage *img); + extern void gdispImageClose_GIF(gdispImage *img); + extern gdispImageError gdispImageCache_GIF(gdispImage *img); + extern gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_GIF(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_BMP + extern gdispImageError gdispImageOpen_BMP(gdispImage *img); + extern void gdispImageClose_BMP(gdispImage *img); + extern gdispImageError gdispImageCache_BMP(gdispImage *img); + extern gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_BMP(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_JPG + extern gdispImageError gdispImageOpen_JPG(gdispImage *img); + extern void gdispImageClose_JPG(gdispImage *img); + extern gdispImageError gdispImageCache_JPG(gdispImage *img); + extern gdispImageError gdispGImageDraw_JPG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_JPG(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_PNG + extern gdispImageError gdispImageOpen_PNG(gdispImage *img); + extern void gdispImageClose_PNG(gdispImage *img); + extern gdispImageError gdispImageCache_PNG(gdispImage *img); + extern gdispImageError gdispGImageDraw_PNG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_PNG(gdispImage *img); +#endif + +/* The structure defining the routines for image drawing */ +typedef struct gdispImageHandlers { + gdispImageError (*open)(gdispImage *img); /* The open function */ + void (*close)(gdispImage *img); /* The close function */ + gdispImageError (*cache)(gdispImage *img); /* The cache function */ + gdispImageError (*draw)(GDisplay *g, + gdispImage *img, + coord_t x, coord_t y, + coord_t cx, coord_t cy, + coord_t sx, coord_t sy); /* The draw function */ + delaytime_t (*next)(gdispImage *img); /* The next frame function */ +} gdispImageHandlers; + +static gdispImageHandlers ImageHandlers[] = { + #if GDISP_NEED_IMAGE_NATIVE + { gdispImageOpen_NATIVE, gdispImageClose_NATIVE, + gdispImageCache_NATIVE, gdispGImageDraw_NATIVE, gdispImageNext_NATIVE, + }, + #endif + #if GDISP_NEED_IMAGE_GIF + { gdispImageOpen_GIF, gdispImageClose_GIF, + gdispImageCache_GIF, gdispGImageDraw_GIF, gdispImageNext_GIF, + }, + #endif + #if GDISP_NEED_IMAGE_BMP + { gdispImageOpen_BMP, gdispImageClose_BMP, + gdispImageCache_BMP, gdispGImageDraw_BMP, gdispImageNext_BMP, + }, + #endif + #if GDISP_NEED_IMAGE_JPG + { gdispImageOpen_JPG, gdispImageClose_JPG, + gdispImageCache_JPG, gdispGImageDraw_JPG, gdispImageNext_JPG, + }, + #endif + #if GDISP_NEED_IMAGE_PNG + { gdispImageOpen_PNG, gdispImageClose_PNG, + gdispImageCache_PNG, gdispGImageDraw_PNG, gdispImageNext_PNG, + }, + #endif +}; + +gdispImageError + DEPRECATED("Use gdispImageOpenGFile() instead") + gdispImageOpen(gdispImage *img) { + return gdispImageOpenGFile(img, img->f); +} + +#if GFILE_NEED_MEMFS + bool_t + DEPRECATED("Use gdispImageOpenMemory() instead") + gdispImageSetMemoryReader(gdispImage *img, const void *memimage) { + img->f = gfileOpenMemory((void *)memimage, "rb"); + return img->f != 0; + } +#endif + +#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX + bool_t + DEPRECATED("Use gdispImageOpenFile() instead") + gdispImageSetFileReader(gdispImage *img, const char *filename) { + img->f = gfileOpen(filename, "rb"); + return img->f != 0; + } +#endif + +#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS + bool_t + DEPRECATED("Use gdispImageOpenBaseFileStream() instead") + gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr) { + img->f = gfileOpenBaseFileStream(BaseFileStreamPtr, "rb"); + return img->f != 0; + } +#endif + +void gdispImageInit(gdispImage *img) { + img->type = GDISP_IMAGE_TYPE_UNKNOWN; +} + +gdispImageError gdispImageOpenGFile(gdispImage *img, GFILE *f) { + gdispImageError err; + + if (!f) + return GDISP_IMAGE_ERR_NOSUCHFILE; + img->f = f; + img->bgcolor = White; + for(img->fns = ImageHandlers; img->fns < ImageHandlers+sizeof(ImageHandlers)/sizeof(ImageHandlers[0]); img->fns++) { + err = img->fns->open(img); + if (err != GDISP_IMAGE_ERR_BADFORMAT) { + if ((err & GDISP_IMAGE_ERR_UNRECOVERABLE)) + goto unrecoverable; + + // Everything is possible + return err; + } + + // Try the next decoder + gfileSetPos(img->f, 0); + } + + err = GDISP_IMAGE_ERR_BADFORMAT; + img->type = GDISP_IMAGE_TYPE_UNKNOWN; + +unrecoverable: + gfileClose(img->f); + img->f = 0; + img->flags = 0; + img->fns = 0; + img->priv = 0; + return err; +} + +void gdispImageClose(gdispImage *img) { + if (img->fns) + img->fns->close(img); + gfileClose(img->f); + img->type = GDISP_IMAGE_TYPE_UNKNOWN; + img->flags = 0; + img->fns = 0; + img->priv = 0; +} + +bool_t gdispImageIsOpen(gdispImage *img) { + return img->type != GDISP_IMAGE_TYPE_UNKNOWN && img->fns != 0; +} + +void gdispImageSetBgColor(gdispImage *img, color_t bgcolor) { + img->bgcolor = bgcolor; +} + +gdispImageError gdispImageCache(gdispImage *img) { + if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; + return img->fns->cache(img); +} + +gdispImageError gdispGImageDraw(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { + if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; + return img->fns->draw(g, img, x, y, cx, cy, sx, sy); +} + +delaytime_t gdispImageNext(gdispImage *img) { + if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; + return img->fns->next(img); +} + +// Helper Routines +void *gdispImageAlloc(gdispImage *img, size_t sz) { + #if GDISP_NEED_IMAGE_ACCOUNTING + void *ptr; + + ptr = gfxAlloc(sz); + if (ptr) { + img->memused += sz; + if (img->memused > img->maxmemused) + img->maxmemused = img->memused; + } + return ptr; + #else + (void) img; + return gfxAlloc(sz); + #endif +} + +void gdispImageFree(gdispImage *img, void *ptr, size_t sz) { + #if GDISP_NEED_IMAGE_ACCOUNTING + gfxFree(ptr); + img->memused -= sz; + #else + (void) img; + (void) sz; + gfxFree(ptr); + #endif +} + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ +/** @} */ diff --git a/src/gdisp/gdisp_image.h b/src/gdisp/gdisp_image.h new file mode 100644 index 00000000..c0941fd5 --- /dev/null +++ b/src/gdisp/gdisp_image.h @@ -0,0 +1,315 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/gdisp_image.h + * @brief GDISP image header file. + * + * @defgroup Image Image + * @ingroup GDISP + * @{ + */ + +#ifndef _GDISP_IMAGE_H +#define _GDISP_IMAGE_H +#if (GFX_USE_GDISP && GDISP_NEED_IMAGE) || defined(__DOXYGEN__) + +/** + * @brief The type of image + */ +typedef uint16_t gdispImageType; + #define GDISP_IMAGE_TYPE_UNKNOWN 0 + #define GDISP_IMAGE_TYPE_NATIVE 1 + #define GDISP_IMAGE_TYPE_GIF 2 + #define GDISP_IMAGE_TYPE_BMP 3 + #define GDISP_IMAGE_TYPE_JPG 4 + #define GDISP_IMAGE_TYPE_PNG 5 + +/** + * @brief An image error code + */ +typedef uint16_t gdispImageError; + #define GDISP_IMAGE_ERR_OK 0 + #define GDISP_IMAGE_ERR_UNRECOVERABLE 0x8000 + #define GDISP_IMAGE_ERR_BADFORMAT (GDISP_IMAGE_ERR_UNRECOVERABLE+1) + #define GDISP_IMAGE_ERR_BADDATA (GDISP_IMAGE_ERR_UNRECOVERABLE+2) + #define GDISP_IMAGE_ERR_UNSUPPORTED (GDISP_IMAGE_ERR_UNRECOVERABLE+3) + #define GDISP_IMAGE_ERR_UNSUPPORTED_OK 3 + #define GDISP_IMAGE_ERR_NOMEMORY (GDISP_IMAGE_ERR_UNRECOVERABLE+4) + #define GDISP_IMAGE_ERR_NOSUCHFILE (GDISP_IMAGE_ERR_UNRECOVERABLE+5) + +/** + * @brief Image flags + */ +typedef uint16_t gdispImageFlags; + #define GDISP_IMAGE_FLG_TRANSPARENT 0x0001 /* The image has transparency */ + #define GDISP_IMAGE_FLG_ANIMATED 0x0002 /* The image has animation */ + #define GDISP_IMAGE_FLG_MULTIPAGE 0x0004 /* The image has multiple pages */ + +struct gdispImageIO; + +/** + * @brief An image IO close function + * + * @param[in] pio Pointer to the io structure + * @param[in] desc The descriptor. A filename or an image structure pointer. + * + */ +typedef void (*gdispImageIOCloseFn)(struct gdispImageIO *pio); + +/** + * @brief An image IO read function + * @returns The number of bytes actually read or 0 on error + * + * @param[in] pio Pointer to the io structure + * @param[in] buf Where the results should be placed + * @param[in] len The number of bytes to read + * + */ +typedef size_t (*gdispImageIOReadFn)(struct gdispImageIO *pio, void *buf, size_t len); + +/** + * @brief An image IO seek function + * + * @param[in] pio Pointer to the io structure + * @param[in] pos Which byte to seek to relative to the start of the "file". + * + */ +typedef void (*gdispImageIOSeekFn)(struct gdispImageIO *pio, size_t pos); + +typedef struct gdispImageIOFunctions { + gdispImageIOReadFn read; /* @< The function to read input */ + gdispImageIOSeekFn seek; /* @< The function to seek input */ + gdispImageIOCloseFn close; /* @< The function to close input */ + } gdispImageIOFunctions; + +/** + * @brief The structure defining the IO routines for image handling + */ +typedef struct gdispImageIO { + const void * fd; /* @< The "file" descriptor */ + size_t pos; /* @< The current "file" position */ + const gdispImageIOFunctions *fns; /* @< The current "file" functions */ +} gdispImageIO; + +/** + * @brief The structure for an image + */ +typedef struct gdispImage { + gdispImageType type; /* @< The image type */ + gdispImageFlags flags; /* @< The image flags */ + color_t bgcolor; /* @< The default background color */ + coord_t width, height; /* @< The image dimensions */ + GFILE * f; /* @< The underlying GFILE */ + #if GDISP_NEED_IMAGE_ACCOUNTING + uint32_t memused; /* @< How much RAM is currently allocated */ + uint32_t maxmemused; /* @< How much RAM has been allocated (maximum) */ + #endif + const struct gdispImageHandlers * fns; /* @< Don't mess with this! */ + struct gdispImagePrivate * priv; /* @< Don't mess with this! */ +} gdispImage; + +#ifdef __cplusplus +extern "C" { +#endif + + /* + * Deprecated Functions. + */ + gdispImageError DEPRECATED("Use gdispImageOpenGFile() instead") gdispImageOpen(gdispImage *img); + bool_t DEPRECATED("Use gdispImageOpenMemory() instead") gdispImageSetMemoryReader(gdispImage *img, const void *memimage); + #if GFX_USE_OS_CHIBIOS + bool_t DEPRECATED("Use gdispImageOpenBaseFileStream() instead") gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr); + #endif + #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX + bool_t DEPRECATED("Please use gdispImageOpenFile() instead") gdispImageSetFileReader(gdispImage *img, const char *filename); + #define gdispImageSetSimulFileReader(img, fname) gdispImageSetFileReader(img, fname) + #endif + + /** + * @brief Initialise a gdispImage object + * + * @param[in] img The image structure to initialise + * + */ + void gdispImageInit(gdispImage *img); + + /** + * @brief Open an image using an open GFILE and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @param[in] img The image structure + * @param[in] f The open GFILE stream. + * + * @pre The GFILE must be open for reading. + * + * @note This determines which decoder to use and then initialises all other fields + * in the gdispImage structure. + * @note The image background color is set to White. + * @note There are three types of return - everything OK, partial success and unrecoverable + * failures. For everything OK it returns GDISP_IMAGE_ERR_OK. A partial success can + * be distinguished from a unrecoverable failure by testing the GDISP_IMAGE_ERR_UNRECOVERABLE + * bit in the error code. + * A partial success return code means an image can still be drawn but perhaps with + * reduced functionality eg only the first page of a multi-page image. + * @note @p gdispImageClose() should be called when finished with the image. This will close + * the image and its underlying GFILE file. Note that images opened with partial success + * (eg GDISP_IMAGE_ERR_UNSUPPORTED_OK) + * still need to be closed when you are finished with them. + */ + gdispImageError gdispImageOpenGFile(gdispImage *img, GFILE *f); + + /** + * @brief Open an image in a file and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @pre You must have included the file-system support into GFILE that you want to use. + * + * @param[in] img The image structure + * @param[in] filename The filename to open + * + * @note This function just opens the GFILE using the filename and passes it to @p gdispImageOpenGFile(). + */ + #define gdispImageOpenFile(img, filename) gdispImageOpenGFile((img), gfileOpen((filename), "rb")) + + /** + * @brief Open an image in a ChibiOS basefilestream and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @pre GFILE_NEED_CHIBIOSFS and GFX_USE_OS_CHIBIOS must be TRUE. This only makes sense on the ChibiOS + * operating system. + * + * @param[in] img The image structure + * @param[in] BaseFileStreamPtr A pointer to an open BaseFileStream + * + * @note This function just opens the GFILE using the basefilestream and passes it to @p gdispImageOpenGFile(). + */ + #define gdispImageOpenBaseFileStream(img, BaseFileStreamPtr) gdispImageOpenGFile((img), gfileOpenBaseFileStream((BaseFileStreamPtr), "rb")) + + /** + * @brief Open an image in memory and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @pre GFILE_NEED_MEMFS must be TRUE + * + * @param[in] img The image structure + * @param[in] ptr A pointer to the image bytes in memory + * + * @note This function just opens the GFILE using the basefilestream and passes it to @p gdispImageOpenGFile(). + */ + #define gdispImageOpenMemory(img, ptr) gdispImageOpenGFile((img), gfileOpenMemory((void *)(ptr), "rb")) + + /** + * @brief Close an image and release any dynamically allocated working storage. + * + * @param[in] img The image structure + * + * @pre gdispImageOpenFile() must have returned successfully. + * + * @note Also calls the IO close function (if it hasn't already been called). + */ + void gdispImageClose(gdispImage *img); + + /** + * @brief Is an image open. + * @return TRUE if the image is currently open. + * + * @param[in] img The image structure + * + * @note Be careful with calling this on an uninitialized image structure as the image + * will contain random data which may be interpreted as meaning the image + * is open. Clearing the Image structure to 0's will guarantee the image + * is seen as being closed. + */ + bool_t gdispImageIsOpen(gdispImage *img); + + /** + * @brief Set the background color of the image. + * + * @param[in] img The image structure + * @param[in] bgcolor The background color to use + * + * @pre gdispImageOpen() must have returned successfully. + * + * @note This color is only used when an image has to restore part of the background before + * continuing with drawing that includes transparency eg some GIF animations. + */ + void gdispImageSetBgColor(gdispImage *img, color_t bgcolor); + + /** + * @brief Cache the image + * @details Decodes and caches the current frame into RAM. + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @param[in] img The image structure + * + * @pre gdispImageOpen() must have returned successfully. + * + * @note This can use a LOT of RAM! + * @note The decoder may choose to ignore the request for caching. If it does so it will + * return GDISP_IMAGE_ERR_UNSUPPORTED_OK. + * @note A fatal error here does not necessarily mean that drawing the image will fail. For + * example, a GDISP_IMAGE_ERR_NOMEMORY error simply means there isn't enough RAM to + * cache the image. + */ + gdispImageError gdispImageCache(gdispImage *img); + + /** + * @brief Draw the image + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @param[in] g The display to draw on + * @param[in] img The image structure + * @param[in] x,y The screen location to draw the image + * @param[in] cx,cy The area on the screen to draw + * @param[in] sx,sy The image position to start drawing at + * + * @pre gdispImageOpen() must have returned successfully. + * + * @note If sx,sy + cx,cy is outside the image boundaries the area outside the image + * is simply not drawn. + * @note If @p gdispImageCache() has been called first for this frame, this routine will draw using a + * fast blit from the cached frame. If not, it reads the input and decodes it as it + * is drawing. This may be significantly slower than if the image has been cached (but + * uses a lot less RAM) + */ + gdispImageError gdispGImageDraw(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + #define gdispImageDraw(img,x,y,cx,cy,sx,sy) gdispGImageDraw(GDISP,img,x,y,cx,cy,sx,sy) + + /** + * @brief Prepare for the next frame/page in the image file. + * @return A time in milliseconds to keep displaying the current frame before trying to draw + * the next frame. Watch out for the special values TIME_IMMEDIATE and TIME_INFINITE. + * + * @param[in] img The image structure + * + * @pre gdispImageOpen() must have returned successfully. + * + * @note It will return TIME_IMMEDIATE if the first frame/page hasn't been drawn or if the next frame + * should be drawn immediately. + * @note It will return TIME_INFINITE if another image frame doesn't exist or an error has occurred. + * @note Images that support multiple pages (eg TIFF files) will return TIME_IMMEDIATE between pages + * and then TIME_INFINITE when there are no more pages. + * @note An image that displays a looped animation will never return TIME_INFINITE unless it + * gets an error. + * @note Calling gdispImageDraw() after getting a TIME_INFINITE will go back to drawing the first + * frame/page. + */ + delaytime_t gdispImageNext(gdispImage *img); + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ +#endif /* _GDISP_IMAGE_H */ +/** @} */ + diff --git a/src/gdisp/gdisp_image_bmp.c b/src/gdisp/gdisp_image_bmp.c new file mode 100644 index 00000000..8ff40ca0 --- /dev/null +++ b/src/gdisp/gdisp_image_bmp.c @@ -0,0 +1,904 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/image_bmp.c + * @brief GDISP native image code. + * + * @defgroup Image Image + * @ingroup GDISP + */ +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_BMP + +#ifndef GDISP_NEED_IMAGE_BMP_1 + #define GDISP_NEED_IMAGE_BMP_1 TRUE +#endif +#ifndef GDISP_NEED_IMAGE_BMP_4 + #define GDISP_NEED_IMAGE_BMP_4 TRUE +#endif +#ifndef GDISP_NEED_IMAGE_BMP_4_RLE + #define GDISP_NEED_IMAGE_BMP_4_RLE TRUE +#endif +#ifndef GDISP_NEED_IMAGE_BMP_8 + #define GDISP_NEED_IMAGE_BMP_8 TRUE +#endif +#ifndef GDISP_NEED_IMAGE_BMP_8_RLE + #define GDISP_NEED_IMAGE_BMP_8_RLE TRUE +#endif +#ifndef GDISP_NEED_IMAGE_BMP_16 + #define GDISP_NEED_IMAGE_BMP_16 TRUE +#endif +#ifndef GDISP_NEED_IMAGE_BMP_24 + #define GDISP_NEED_IMAGE_BMP_24 TRUE +#endif +#ifndef GDISP_NEED_IMAGE_BMP_32 + #define GDISP_NEED_IMAGE_BMP_32 TRUE +#endif + +/** + * Helper Routines Needed + */ +void *gdispImageAlloc(gdispImage *img, size_t sz); +void gdispImageFree(gdispImage *img, void *ptr, size_t sz); + +/** + * How big a pixel array to allocate for blitting (in pixels) + * Bigger is faster but uses more RAM. + * This must be greater than 40 bytes and 32 pixels as we read our headers into this space as well + */ +#define BLIT_BUFFER_SIZE 32 + +/* + * Determining endianness as at compile time is not guaranteed or compiler portable. + * We use the best test we can. If we can't guarantee little endianness we do things the + * hard way. + */ +#define GUARANTEED_LITTLE_ENDIAN (!defined(SAFE_ENDIAN) && !defined(SAFE_ALIGNMENT) && (\ + (defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \ + || defined(__LITTLE_ENDIAN__) \ + || defined(__LITTLE_ENDIAN) \ + || defined(_LITTLE_ENDIAN) \ +/* || (1 == *(unsigned char *)&(const int){1})*/ \ + )) + + +/* This is a runtime test */ +static const uint8_t dwordOrder[4] = { 1, 2, 3, 4 }; + +#define isWordLittleEndian() (*(uint16_t *)&dwordOrder == 0x0201) +#define isDWordLittleEndian() (*(uint32_t *)&dwordOrder == 0x04030201) + +#if GUARANTEED_LITTLE_ENDIAN + /* These are fast routines for guaranteed little endian machines */ + #define CONVERT_FROM_WORD_LE(w) + #define CONVERT_FROM_DWORD_LE(dw) +#else + /* These are slower routines for when little endianness cannot be guaranteed at compile time */ + #define CONVERT_FROM_WORD_LE(w) { if (!isWordLittleEndian()) w = ((((uint16_t)(w))>>8)|(((uint16_t)(w))<<8)); } + #define CONVERT_FROM_DWORD_LE(dw) { if (!isDWordLittleEndian()) dw = (((uint32_t)(((const uint8_t *)(&dw))[0]))|(((uint32_t)(((const uint8_t *)(&dw))[1]))<<8)|(((uint32_t)(((const uint8_t *)(&dw))[2]))<<16)|(((uint32_t)(((const uint8_t *)(&dw))[3]))<<24)); } +#endif + +typedef struct gdispImagePrivate { + uint8_t bmpflags; + #define BMP_V2 0x01 // Version 2 (old) header format + #define BMP_V4 0x02 // Version 4 (alpha support) header format + #define BMP_PALETTE 0x04 // Uses a palette + #define BMP_COMP_RLE 0x08 // Uses RLE compression + #define BMP_COMP_MASK 0x10 // Uses mask & shift decoding + #define BMP_RLE_ENC 0x20 // Currently in RLE encoded run + #define BMP_RLE_ABS 0x40 // Currently in RLE absolute run + #define BMP_TOP_TO_BOTTOM 0x80 // Decodes bottom to top line + uint8_t bitsperpixel; +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + uint16_t palsize; + pixel_t *palette; +#endif +#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE + uint16_t rlerun; + uint8_t rlecode; +#endif +#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 + int8_t shiftred; + int8_t shiftgreen; + int8_t shiftblue; + int8_t shiftalpha; + uint32_t maskred; + uint32_t maskgreen; + uint32_t maskblue; + uint32_t maskalpha; +#endif + size_t frame0pos; + pixel_t *frame0cache; + pixel_t buf[BLIT_BUFFER_SIZE]; + } gdispImagePrivate; + +void gdispImageClose_BMP(gdispImage *img) { + if (img->priv) { +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + if (img->priv->palette) + gdispImageFree(img, (void *)img->priv->palette, img->priv->palsize*sizeof(color_t)); +#endif + if (img->priv->frame0cache) + gdispImageFree(img, (void *)img->priv->frame0cache, img->width*img->height*sizeof(pixel_t)); + gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); + img->priv = 0; + } +} + +gdispImageError gdispImageOpen_BMP(gdispImage *img) { + gdispImagePrivate *priv; + uint8_t hdr[2]; + uint16_t aword; + uint32_t adword; + uint32_t offsetColorTable; + + /* Read the file identifier */ + if (gfileRead(img->f, hdr, 2) != 2) + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + /* Process the BITMAPFILEHEADER structure */ + + /** + * We only accept Windows V2+ bitmaps. + * - we don't support OS/2 bitmaps, icons, pointers, or Windows V1 bitmaps. + */ + if (hdr[0] != 'B' || hdr[1] != 'M') + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + /* We know we are a BMP format image */ + img->flags = 0; + + /* Allocate our private area */ + if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) + return GDISP_IMAGE_ERR_NOMEMORY; + + /* Initialise the essential bits in the private area */ + priv = img->priv; + priv->frame0cache = 0; + priv->bmpflags = 0; +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + priv->palette = 0; +#endif + + /* Skip the size field and the 2 reserved fields */ + if (gfileRead(img->f, priv->buf, 8) != 8) + goto baddatacleanup; + + /* Get the offset to the bitmap data */ + if (gfileRead(img->f, &priv->frame0pos, 4) != 4) + goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->frame0pos); + + /* Process the BITMAPCOREHEADER structure */ + + /* Get the offset to the colour data */ + if (gfileRead(img->f, &offsetColorTable, 4) != 4) + goto baddatacleanup; + CONVERT_FROM_DWORD_LE(offsetColorTable); + offsetColorTable += 14; // Add the size of the BITMAPFILEHEADER + + // Detect our bitmap version + if (offsetColorTable == 12+14) { + img->priv->bmpflags |= BMP_V2; + + // Read the header + if (gfileRead(img->f, priv->buf, 12-4) != 12-4) + goto baddatacleanup; + // Get the width + img->width = *(uint16_t *)(((uint8_t *)priv->buf)+0); + CONVERT_FROM_WORD_LE(img->width); + // Get the height + img->height = *(uint16_t *)(((uint8_t *)priv->buf)+2); + CONVERT_FROM_WORD_LE(img->height); + if (img->height < 0) { + img->priv->bmpflags |= BMP_TOP_TO_BOTTOM; + img->height = -img->height; + } + // Get the planes + aword = *(uint16_t *)(((uint8_t *)priv->buf)+4); + CONVERT_FROM_WORD_LE(aword); + if (aword != 1) + goto unsupportedcleanup; + // Get the bits per pixel + aword = *(uint16_t *)(((uint8_t *)priv->buf)+6); + CONVERT_FROM_WORD_LE(aword); + switch(aword) { +#if GDISP_NEED_IMAGE_BMP_1 + case 1: +#endif +#if GDISP_NEED_IMAGE_BMP_4 + case 4: +#endif +#if GDISP_NEED_IMAGE_BMP_8 + case 8: +#endif +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_8 + priv->bmpflags |= BMP_PALETTE; + priv->palsize = 1<bitsperpixel = aword; + + } else if (offsetColorTable >= 40+14) { + if (offsetColorTable > 40+14) + priv->bmpflags |= BMP_V4; + + // Read the header + if (gfileRead(img->f, priv->buf, 40-4) != 40-4) + goto baddatacleanup; + // Get the width + adword = *(uint32_t *)(((uint8_t *)priv->buf)+0); + CONVERT_FROM_DWORD_LE(adword); + if (adword > 32768) // This also picks up negative values + goto unsupportedcleanup; + img->width = adword; + // Get the height + adword = *(uint32_t *)(((uint8_t *)priv->buf)+4); + CONVERT_FROM_DWORD_LE(adword); + if ((int32_t)adword < 0) { // Negative test + priv->bmpflags |= BMP_TOP_TO_BOTTOM; + adword = -adword; + } + if (adword > 32768) + goto unsupportedcleanup; + img->height = adword; + // Get the planes + aword = *(uint16_t *)(((uint8_t *)priv->buf)+8); + CONVERT_FROM_WORD_LE(aword); + if (aword != 1) + goto unsupportedcleanup; + // Get the bits per pixel + aword = *(uint16_t *)(((uint8_t *)priv->buf)+10); + CONVERT_FROM_WORD_LE(aword); + switch(aword) { +#if GDISP_NEED_IMAGE_BMP_1 + case 1: +#endif +#if GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE + case 4: +#endif +#if GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + case 8: +#endif +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + priv->bmpflags |= BMP_PALETTE; + priv->palsize = 1<bitsperpixel = aword; + // Get the compression + adword = *(uint32_t *)(((uint8_t *)priv->buf)+12); + CONVERT_FROM_DWORD_LE(adword); + switch(adword) { + case 0: // BI_RGB - uncompressed + break; +#if GDISP_NEED_IMAGE_BMP_8_RLE + case 1: // BI_RLE8 compression + if (priv->bitsperpixel != 8) + goto unsupportedcleanup; + priv->bmpflags |= BMP_COMP_RLE; + break; +#endif +#if GDISP_NEED_IMAGE_BMP_4_RLE + case 2: // BI_RLE4 compression + if (priv->bitsperpixel != 4) + goto unsupportedcleanup; + priv->bmpflags |= BMP_COMP_RLE; + break; +#endif +#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 + case 3: // BI_BITFIELDS decoding + if (priv->bitsperpixel < 16 || priv->bitsperpixel == 24) + goto unsupportedcleanup; + priv->bmpflags |= BMP_COMP_MASK; + if (priv->bmpflags & BMP_V4) // V4 stored the masks in the header + offsetColorTable = 40+14; + break; +#endif + default: + goto unsupportedcleanup; + } + priv->bitsperpixel = aword; +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + // Get the actual colors used + adword = *(uint32_t *)(((uint8_t *)priv->buf)+28); + CONVERT_FROM_DWORD_LE(adword); + if (adword && adword < priv->palsize) + priv->palsize = adword; +#endif + } else + goto baddatacleanup; + +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + /* Load the palette tables */ + if (priv->bmpflags & BMP_PALETTE) { + gfileSetPos(img->f, offsetColorTable); + + if (!(priv->palette = (color_t *)gdispImageAlloc(img, priv->palsize*sizeof(color_t)))) + return GDISP_IMAGE_ERR_NOMEMORY; + if (priv->bmpflags & BMP_V2) { + for(aword = 0; aword < priv->palsize; aword++) { + if (gfileRead(img->f, &priv->buf, 3) != 3) goto baddatacleanup; + priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[2], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[0]); + } + } else { + for(aword = 0; aword < priv->palsize; aword++) { + if (gfileRead(img->f, &priv->buf, 4) != 4) goto baddatacleanup; + priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[2], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[0]); + } + } + + } +#endif + +#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 + /* Load the bit masks */ + if (priv->bmpflags & BMP_COMP_MASK) { + gfileSetPos(img->f, offsetColorTable); + if (gfileRead(img->f, &priv->maskred, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskred); + if (gfileRead(img->f, &priv->maskgreen, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskgreen); + if (gfileRead(img->f, &priv->maskblue, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskblue); + if (priv->bmpflags & BMP_V4) { + if (gfileRead(img->f, &priv->maskalpha, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskalpha); + } else + priv->maskalpha = 0; + } else if (priv->bitsperpixel == 16) { + priv->bmpflags |= BMP_COMP_MASK; + priv->maskred = 0x7C00; + priv->maskgreen = 0x03E0; + priv->maskblue = 0x001F; + priv->maskalpha = 0; + } else if (priv->bitsperpixel == 32) { + priv->bmpflags |= BMP_COMP_MASK; + priv->maskred = 0x00FF0000; + priv->maskgreen = 0x0000FF00; + priv->maskblue = 0x000000FF; + priv->maskalpha = 0; + } + + /* We need to adjust the masks and calculate the shift values so the result scales 0 -> 255 */ + if (priv->bmpflags & BMP_COMP_MASK) { + priv->shiftred = 0; + priv->shiftgreen = 0; + priv->shiftblue = 0; + if (priv->maskred) { + if (priv->maskred < 256) + for(adword = priv->maskred; adword < 128; priv->shiftred--, adword <<= 1); + else + for(adword = priv->maskred; adword > 255; priv->shiftred++, adword >>= 1); + } + if (priv->maskgreen) { + if (priv->maskgreen < 256) + for(adword = priv->maskgreen; adword < 128; priv->shiftgreen--, adword <<= 1); + else + for(adword = priv->maskgreen; adword > 255; priv->shiftgreen++, adword >>= 1); + } + if (priv->maskblue) { + if (priv->maskblue < 256) + for(adword = priv->maskblue; adword < 128; priv->shiftblue--, adword <<= 1); + else + for(adword = priv->maskblue; adword > 255; priv->shiftblue++, adword >>= 1); + } + if (priv->maskalpha) { + if (priv->maskalpha < 256) + for(adword = priv->maskalpha; adword < 128; priv->shiftalpha--, adword <<= 1); + else + for(adword = priv->maskalpha; adword > 255; priv->shiftalpha++, adword >>= 1); + } + } +#endif + + img->type = GDISP_IMAGE_TYPE_BMP; + return GDISP_IMAGE_ERR_OK; + +baddatacleanup: + gdispImageClose_BMP(img); // Clean up the private data area + return GDISP_IMAGE_ERR_BADDATA; // Oops - something wrong + +unsupportedcleanup: + gdispImageClose_BMP(img); // Clean up the private data area + return GDISP_IMAGE_ERR_UNSUPPORTED; // Not supported +} + +static coord_t getPixels(gdispImage *img, coord_t x) { + gdispImagePrivate * priv; + color_t * pc; + coord_t len; + + priv = img->priv; + pc = priv->buf; + len = 0; + + switch(priv->bitsperpixel) { +#if GDISP_NEED_IMAGE_BMP_1 + case 1: + { + uint8_t b[4]; + uint8_t m; + + priv = img->priv; + pc = priv->buf; + len = 0; + + while(x < img->width && len <= BLIT_BUFFER_SIZE-32) { + if (gfileRead(img->f, &b, 4) != 4) + return 0; + + for(m=0x80; m; m >>= 1, pc++) + pc[0] = priv->palette[(m&b[0]) ? 1 : 0]; + for(m=0x80; m; m >>= 1, pc++) + pc[0] = priv->palette[(m&b[1]) ? 1 : 0]; + for(m=0x80; m; m >>= 1, pc++) + pc[0] = priv->palette[(m&b[2]) ? 1 : 0]; + for(m=0x80; m; m >>= 1, pc++) + pc[0] = priv->palette[(m&b[3]) ? 1 : 0]; + len += 32; + x += 32; + } + } + return len; +#endif + +#if GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE + case 4: + #if GDISP_NEED_IMAGE_BMP_4_RLE + #if GDISP_NEED_IMAGE_BMP_4 + if (priv->bmpflags & BMP_COMP_RLE) + #endif + { + uint8_t b[4]; + + while(x < img->width) { + if (priv->bmpflags & BMP_RLE_ENC) { + while (priv->rlerun && len <= BLIT_BUFFER_SIZE-2 && x < img->width) { + *pc++ = priv->palette[priv->rlecode >> 4]; + priv->rlerun--; + len++; + x++; + if (priv->rlerun) { + *pc++ = priv->palette[priv->rlecode & 0x0F]; + priv->rlerun--; + len++; + x++; + } + } + if (priv->rlerun) // Return if we have more run to do + return len; + } else if (priv->bmpflags & BMP_RLE_ABS) { + while (priv->rlerun && len <= BLIT_BUFFER_SIZE-2 && x < img->width) { + if (gfileRead(img->f, &b, 1) != 1) + return 0; + *pc++ = priv->palette[b[0] >> 4]; + priv->rlerun--; + len++; + x++; + if (priv->rlerun) { + *pc++ = priv->palette[b[0] & 0x0F]; + priv->rlerun--; + len++; + x++; + } + } + if (priv->rlerun) // Return if we have more run to do + return len; + if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary + if (gfileRead(img->f, &b, 1) != 1) + return 0; + } + } + + // We have finished the current run - read a new run + priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS); + + // There are always at least 2 bytes in an RLE code + if (gfileRead(img->f, &b, 2) != 2) + return 0; + + if (b[0]) { // Encoded mode + priv->rlerun = b[0]; + priv->rlecode = b[1]; + priv->bmpflags |= BMP_RLE_ENC; + } else if (b[1] == 0) { // End of line + if (x < img->width) { + priv->rlerun = img->width - x; + priv->rlecode = 0; // Who knows what color this should really be + priv->bmpflags |= BMP_RLE_ENC; + } + } else if (b[1] == 1) { // End of file + return len; + } else if (b[1] == 2) { // Delta x, y + // There are always at least 2 bytes in an RLE code + if (gfileRead(img->f, &b, 2) != 2) + return 0; + priv->rlerun = b[0] + (uint16_t)b[1] * img->width; + priv->rlecode = 0; // Who knows what color this should really be + priv->bmpflags |= BMP_RLE_ENC; + } else { // Absolute mode + priv->rlerun = b[1]; + priv->bmpflags |= BMP_RLE_ABS; + } + } + return len; + } + #endif + #if GDISP_NEED_IMAGE_BMP_4 + { + uint8_t b[4]; + + while(x < img->width && len <= BLIT_BUFFER_SIZE-8) { + if (gfileRead(img->f, &b, 4) != 4) + return 0; + + *pc++ = priv->palette[b[0] >> 4]; + *pc++ = priv->palette[b[0] & 0x0F]; + *pc++ = priv->palette[b[1] >> 4]; + *pc++ = priv->palette[b[1] & 0x0F]; + *pc++ = priv->palette[b[2] >> 4]; + *pc++ = priv->palette[b[2] & 0x0F]; + *pc++ = priv->palette[b[3] >> 4]; + *pc++ = priv->palette[b[3] & 0x0F]; + len += 8; + x += 8; + } + return len; + } + #endif +#endif + +#if GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + case 8: + #if GDISP_NEED_IMAGE_BMP_8_RLE + #if GDISP_NEED_IMAGE_BMP_8 + if (priv->bmpflags & BMP_COMP_RLE) + #endif + { + uint8_t b[4]; + + while(x < img->width) { + if (priv->bmpflags & BMP_RLE_ENC) { + while (priv->rlerun && len < BLIT_BUFFER_SIZE && x < img->width) { + *pc++ = priv->palette[priv->rlecode]; + priv->rlerun--; + len++; + x++; + } + if (priv->rlerun) // Return if we have more run to do + return len; + } else if (priv->bmpflags & BMP_RLE_ABS) { + while (priv->rlerun && len < BLIT_BUFFER_SIZE && x < img->width) { + if (gfileRead(img->f, &b, 1) != 1) + return 0; + *pc++ = priv->palette[b[0]]; + priv->rlerun--; + len++; + x++; + } + if (priv->rlerun) // Return if we have more run to do + return len; + if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary + if (gfileRead(img->f, &b, 1) != 1) + return 0; + } + } + + // We have finished the current run - read a new run + priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS); + + // There are always at least 2 bytes in an RLE code + if (gfileRead(img->f, &b, 2) != 2) + return 0; + + if (b[0]) { // Encoded mode + priv->rlerun = b[0]; + priv->rlecode = b[1]; + priv->bmpflags |= BMP_RLE_ENC; + } else if (b[1] == 0) { // End of line + if (x < img->width) { + priv->rlerun = img->width - x; + priv->rlecode = 0; // Who knows what color this should really be + priv->bmpflags |= BMP_RLE_ENC; + } + } else if (b[1] == 1) { // End of file + return len; + } else if (b[1] == 2) { // Delta x, y + // There are always at least 2 bytes in an RLE code + if (gfileRead(img->f, &b, 2) != 2) + return GDISP_IMAGE_ERR_BADDATA; + priv->rlerun = b[0] + (uint16_t)b[1] * img->width; + priv->rlecode = 0; // Who knows what color this should really be + priv->bmpflags |= BMP_RLE_ENC; + } else { // Absolute mode + priv->rlerun = b[1]; + priv->bmpflags |= BMP_RLE_ABS; + } + } + return len; + } + #endif + #if GDISP_NEED_IMAGE_BMP_8 + { + uint8_t b[4]; + + while(x < img->width && len <= BLIT_BUFFER_SIZE-4) { + if (gfileRead(img->f, &b, 4) != 4) + return 0; + + *pc++ = priv->palette[b[0]]; + *pc++ = priv->palette[b[1]]; + *pc++ = priv->palette[b[2]]; + *pc++ = priv->palette[b[3]]; + len += 4; + x += 4; + } + return len; + } + #endif +#endif + +#if GDISP_NEED_IMAGE_BMP_16 + case 16: + { + uint16_t w[2]; + color_t r, g, b; + + while(x < img->width && len <= BLIT_BUFFER_SIZE-2) { + if (gfileRead(img->f, &w, 4) != 4) + return 0; + CONVERT_FROM_WORD_LE(w[0]); + CONVERT_FROM_WORD_LE(w[1]); + if (priv->shiftred < 0) + r = (color_t)((w[0] & priv->maskred) << -priv->shiftred); + else + r = (color_t)((w[0] & priv->maskred) >> priv->shiftred); + if (priv->shiftgreen < 0) + g = (color_t)((w[0] & priv->maskgreen) << -priv->shiftgreen); + else + g = (color_t)((w[0] & priv->maskgreen) >> priv->shiftgreen); + if (priv->shiftblue < 0) + b = (color_t)((w[0] & priv->maskblue) << -priv->shiftblue); + else + b = (color_t)((w[0] & priv->maskblue) >> priv->shiftblue); + /* We don't support alpha yet */ + *pc++ = RGB2COLOR(r, g, b); + if (priv->shiftred < 0) + r = (color_t)((w[1] & priv->maskred) << -priv->shiftred); + else + r = (color_t)((w[1] & priv->maskred) >> priv->shiftred); + if (priv->shiftgreen < 0) + g = (color_t)((w[1] & priv->maskgreen) << -priv->shiftgreen); + else + g = (color_t)((w[1] & priv->maskgreen) >> priv->shiftgreen); + if (priv->shiftblue < 0) + b = (color_t)((w[1] & priv->maskblue) << -priv->shiftblue); + else + b = (uint8_t)((w[1] & priv->maskblue) >> priv->shiftblue); + /* We don't support alpha yet */ + *pc++ = RGB2COLOR(r, g, b); + x += 2; + len += 2; + } + } + return len; +#endif + +#if GDISP_NEED_IMAGE_BMP_24 + case 24: + { + uint8_t b[3]; + + while(x < img->width && len < BLIT_BUFFER_SIZE) { + if (gfileRead(img->f, &b, 3) != 3) + return 0; + *pc++ = RGB2COLOR(b[2], b[1], b[0]); + x++; + len++; + } + + if (x >= img->width) { + // Make sure we have read a multiple of 4 bytes for the line + if ((x & 3) && gfileRead(img->f, &b, x & 3) != (x & 3)) + return 0; + } + } + return len; +#endif + +#if GDISP_NEED_IMAGE_BMP_32 + case 32: + { + uint32_t dw; + color_t r, g, b; + + while(x < img->width && len < BLIT_BUFFER_SIZE) { + if (gfileRead(img->f, &dw, 4) != 4) + return 0; + CONVERT_FROM_DWORD_LE(dw); + if (priv->shiftred < 0) + r = (color_t)((dw & priv->maskred) << -priv->shiftred); + else + r = (color_t)((dw & priv->maskred) >> priv->shiftred); + if (priv->shiftgreen < 0) + g = (color_t)((dw & priv->maskgreen) << -priv->shiftgreen); + else + g = (color_t)((dw & priv->maskgreen) >> priv->shiftgreen); + if (priv->shiftblue < 0) + b = (color_t)((dw & priv->maskblue) << -priv->shiftblue); + else + b = (color_t)((dw & priv->maskblue) >> priv->shiftblue); + /* We don't support alpha yet */ + *pc++ = RGB2COLOR(r, g, b); + x++; + len++; + } + } + return len; +#endif + + default: + return len; + } +} + +gdispImageError gdispImageCache_BMP(gdispImage *img) { + gdispImagePrivate * priv; + color_t * pcs; + color_t * pcd; + coord_t pos, x, y; + size_t len; + + /* If we are already cached - just return OK */ + priv = img->priv; + if (priv->frame0cache) + return GDISP_IMAGE_ERR_OK; + + /* We need to allocate the cache */ + len = img->width * img->height * sizeof(pixel_t); + priv->frame0cache = (pixel_t *)gdispImageAlloc(img, len); + if (!priv->frame0cache) + return GDISP_IMAGE_ERR_NOMEMORY; + + /* Read the entire bitmap into cache */ + gfileSetPos(img->f, priv->frame0pos); +#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE + priv->rlerun = 0; + priv->rlecode = 0; +#endif + + if (priv->bmpflags & BMP_TOP_TO_BOTTOM) { + for(y = 0, pcd = priv->frame0cache; y < img->height; y++) { + x = 0; pos = 0; + while(x < img->width) { + if (!pos) { + if (!(pos = getPixels(img, x))) + return GDISP_IMAGE_ERR_BADDATA; + pcs = priv->buf; + } + *pcd++ = *pcs++; + x++; pos--; + } + } + } else { + for(y = img->height-1, pcd = priv->frame0cache + img->width*(img->height-1); y >= 0; y--, pcd -= 2*img->width) { + x = 0; pos = 0; + while(x < img->width) { + if (!pos) { + if (!(pos = getPixels(img, x))) + return GDISP_IMAGE_ERR_BADDATA; + pcs = priv->buf; + } + *pcd++ = *pcs++; + x++; pos--; + } + } + } + + return GDISP_IMAGE_ERR_OK; +} + +gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { + gdispImagePrivate * priv; + coord_t mx, my; + coord_t pos, len, st; + + priv = img->priv; + + /* Check some reasonableness */ + if (sx >= img->width || sy >= img->height) return GDISP_IMAGE_ERR_OK; + if (sx + cx > img->width) cx = img->width - sx; + if (sy + cy > img->height) cy = img->height - sy; + + /* Draw from the image cache - if it exists */ + if (priv->frame0cache) { + gdispGBlitArea(g, x, y, cx, cy, sx, sy, img->width, priv->frame0cache); + return GDISP_IMAGE_ERR_OK; + } + + /* Start decoding from the beginning */ + gfileSetPos(img->f, priv->frame0pos); +#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE + priv->rlerun = 0; + priv->rlecode = 0; +#endif + + if (priv->bmpflags & BMP_TOP_TO_BOTTOM) { + for(my = 0; my < img->height; my++) { + mx = 0; + while(mx < img->width) { + if (!(pos = getPixels(img, mx))) + return GDISP_IMAGE_ERR_BADDATA; + if (my >= sy && my < sy+cy && mx < sx+cx && mx+pos >= sx) { + st = mx < sx ? sx - mx : 0; + len = pos-st; + if (mx+st+len > sx+cx) len = sx+cx-mx-st; + if (len == 1) + gdispGDrawPixel(g, x+mx+st-sx, y+my-sy, priv->buf[st]); + else + gdispGBlitArea(g, x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf); + } + mx += pos; + } + } + } else { + for(my = img->height-1; my >= 0; my--) { + mx = 0; + while(mx < img->width) { + if (!(pos = getPixels(img, mx))) + return GDISP_IMAGE_ERR_BADDATA; + if (my >= sy && my < sy+cy && mx < sx+cx && mx+pos >= sx) { + st = mx < sx ? sx - mx : 0; + len = pos-st; + if (mx+st+len > sx+cx) len = sx+cx-mx-st; + if (len == 1) + gdispGDrawPixel(g, x+mx+st-sx, y+my-sy, priv->buf[st]); + else + gdispGBlitArea(g, x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf); + } + mx += pos; + } + } + } + + return GDISP_IMAGE_ERR_OK; +} + +delaytime_t gdispImageNext_BMP(gdispImage *img) { + (void) img; + + /* No more frames/pages */ + return TIME_INFINITE; +} + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_BMP */ +/** @} */ diff --git a/src/gdisp/gdisp_image_gif.c b/src/gdisp/gdisp_image_gif.c new file mode 100644 index 00000000..06f4ef6a --- /dev/null +++ b/src/gdisp/gdisp_image_gif.c @@ -0,0 +1,1208 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/image_gif.c + * @brief GDISP native image code. + * + * @defgroup Image Image + * @ingroup GDISP +*/ +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_GIF + +/** + * Helper Routines Needed + */ +void *gdispImageAlloc(gdispImage *img, size_t sz); +void gdispImageFree(gdispImage *img, void *ptr, size_t sz); + +/** + * How big an array to allocate for blitting (in pixels) + * Bigger is faster but uses more RAM. + */ +#define BLIT_BUFFER_SIZE 32 + +/* + * Determining endianness as at compile time is not guaranteed or compiler portable. + * We use the best test we can. If we can't guarantee little endianness we do things the + * hard way. + */ +#define GUARANTEED_LITTLE_ENDIAN (!defined(SAFE_ENDIAN) && !defined(SAFE_ALIGNMENT) && (\ + (defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \ + || defined(__LITTLE_ENDIAN__) \ + || defined(__LITTLE_ENDIAN) \ + || defined(_LITTLE_ENDIAN) \ +/* || (1 == *(unsigned char *)&(const int){1})*/ \ + )) + + +/* This is a runtime test */ +static const uint8_t dwordOrder[4] = { 1, 2, 3, 4 }; + +#define isWordLittleEndian() (*(uint16_t *)&dwordOrder == 0x0201) +#define isDWordLittleEndian() (*(uint32_t *)&dwordOrder == 0x04030201) + +#if GUARANTEED_LITTLE_ENDIAN + /* These are fast routines for guaranteed little endian machines */ + #define CONVERT_FROM_WORD_LE(w) + #define CONVERT_FROM_DWORD_LE(dw) +#else + /* These are slower routines for when little endianness cannot be guaranteed at compile time */ + #define CONVERT_FROM_WORD_LE(w) { if (!isWordLittleEndian()) w = ((((uint16_t)(w))>>8)|(((uint16_t)(w))<<8)); } + #define CONVERT_FROM_DWORD_LE(dw) { if (!isDWordLittleEndian()) dw = (((uint32_t)(((const uint8_t *)(&dw))[0]))|(((uint32_t)(((const uint8_t *)(&dw))[1]))<<8)|(((uint32_t)(((const uint8_t *)(&dw))[2]))<<16)|(((uint32_t)(((const uint8_t *)(&dw))[3]))<<24)); } +#endif + +// We need a special error to indicate the end of file (which may not actually be an error) +#define GDISP_IMAGE_EOF ((gdispImageError)-1) +#define GDISP_IMAGE_LOOP ((gdispImageError)-2) + +#define MAX_CODE_BITS 12 +#define CODE_MAX ((1<priv; + + // We need the decode ram, and possibly a palette + if (!(decode = (imgdecode *)gdispImageAlloc(img, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)))) + return GDISP_IMAGE_ERR_NOMEMORY; + + // We currently have not read any image data block + decode->blocksz = 0; + + // Set the palette + if (priv->frame.palsize) { + // Local palette + decode->maxpixel = priv->frame.palsize-1; + decode->palette = (color_t *)(decode+1); + gfileSetPos(img->f, priv->frame.pospal); + for(cnt = 0; cnt < priv->frame.palsize; cnt++) { + if (gfileRead(img->f, &decode->buf, 3) != 3) + goto baddatacleanup; + decode->palette[cnt] = RGB2COLOR(decode->buf[0], decode->buf[1], decode->buf[2]); + } + } else if (priv->palette) { + // Global palette + decode->maxpixel = priv->palsize-1; + decode->palette = priv->palette; + } else { + // Oops - we must have a palette + goto baddatacleanup; + } + + // Get the initial lzw code size and values + gfileSetPos(img->f, priv->frame.posimg); + if (gfileRead(img->f, &decode->bitsperpixel, 1) != 1 || decode->bitsperpixel >= MAX_CODE_BITS) + goto baddatacleanup; + decode->code_clear = 1 << decode->bitsperpixel; + decode->code_eof = decode->code_clear + 1; + decode->code_max = decode->code_clear + 2; + decode->code_last = CODE_NONE; + decode->bitspercode = decode->bitsperpixel+1; + decode->maxcodesz = 1 << decode->bitspercode; + decode->shiftbits = 0; + decode->shiftdata = 0; + decode->stackcnt = 0; + for(cnt = 0; cnt <= CODE_MAX; cnt++) + decode->prefix[cnt] = CODE_NONE; + + // All ready to go + priv->decode = decode; + return GDISP_IMAGE_ERR_OK; + +baddatacleanup: + gdispImageFree(img, decode, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)); + return GDISP_IMAGE_ERR_BADDATA; +} + +/** + * Stop decoding a frame. + * + * Pre: Frame info has been read. + */ +static void stopDecode(gdispImage *img) { + gdispImagePrivate * priv; + + priv = img->priv; + + // Free the decode data + if (priv->decode) { + gdispImageFree(img, (void *)priv->decode, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)); + priv->decode = 0; + } +} + +static uint16_t getPrefix(imgdecode *decode, uint16_t code) { + uint16_t i; + + for(i=0; code > decode->code_clear && i <= CODE_MAX; i++, code = decode->prefix[code]) { + if (code > CODE_MAX) + return CODE_NONE; + } + return code; +} + +/** + * Decode some pixels from a frame. + * + * Pre: We are ready for decoding. + * + * Return: The number of pixels decoded 0 .. BLIT_BUFFER_SIZE-1. 0 means EOF + * + * Note: The resulting pixels are stored in decode->buf + */ +static uint16_t getbytes(gdispImage *img) { + gdispImagePrivate * priv; + imgdecode * decode; + uint16_t cnt; + uint16_t code, prefix; + uint8_t bdata; + + priv = img->priv; + decode = priv->decode; + cnt = 0; + + // At EOF + if (decode->code_last == decode->code_eof) + return 0; + + while(cnt < sizeof(decode->buf)) { + // Use the stack up first + if (decode->stackcnt > 0) { + decode->buf[cnt++] = decode->stack[--decode->stackcnt]; + continue; + } + + // Get another code - a code is made up of decode->bitspercode bits. + while (decode->shiftbits < decode->bitspercode) { + // Get a byte - we may have to start a new data block + if ((!decode->blocksz && (gfileRead(img->f, &decode->blocksz, 1) != 1 || !decode->blocksz)) + || gfileRead(img->f, &bdata, 1) != 1) { + // Pretend we got the EOF code - some encoders seem to just end the file + decode->code_last = decode->code_eof; + return cnt; + } + decode->blocksz--; + + decode->shiftdata |= ((unsigned long)bdata) << decode->shiftbits; + decode->shiftbits += 8; + } + code = decode->shiftdata & BitMask[decode->bitspercode]; + decode->shiftdata >>= decode->bitspercode; + decode->shiftbits -= decode->bitspercode; + /** + * If code cannot fit into bitspercode bits we must raise its size. + * Note that codes above CODE_MAX are used for special signaling. + * If we're using MAX_CODE_BITS bits already and we're at the max code, just + * keep using the table as it is, don't increment decode->bitspercode. + */ + if (decode->code_max < CODE_MAX + 2 && ++decode->code_max > decode->maxcodesz && decode->bitspercode < MAX_CODE_BITS) { + decode->maxcodesz <<= 1; + decode->bitspercode++; + } + + // EOF - the appropriate way to stop decoding + if (code == decode->code_eof) { + // Skip to the end of the data blocks + do { + gfileSetPos(img->f, gfileGetPos(img->f)+decode->blocksz); + } while (gfileRead(img->f, &decode->blocksz, 1) == 1 && decode->blocksz); + + // Mark the end + decode->code_last = decode->code_eof; + break; + } + + if (code == decode->code_clear) { + // Start again + for(prefix = 0; prefix <= CODE_MAX; prefix++) + decode->prefix[prefix] = CODE_NONE; + decode->code_max = decode->code_eof + 1; + decode->bitspercode = decode->bitsperpixel + 1; + decode->maxcodesz = 1 << decode->bitspercode; + decode->code_last = CODE_NONE; + continue; + } + + if (code < decode->code_clear) { + // Simple unencoded pixel - add it + decode->buf[cnt++] = code; + + } else { + /** + * Its a LZW code - trace the linked list until the prefix is a + * valid pixel while pushing the suffix pixels on the stack. + * If done, pop the stack in reverse order adding the pixels + */ + if (decode->prefix[code] != CODE_NONE) + prefix = code; + + /** + * Only allowed if the code equals the partial code. + * In that case code = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! + */ + else if (code == decode->code_max - 2 && decode->stackcnt < sizeof(decode->stack)) { + prefix = decode->code_last; + decode->suffix[decode->code_max - 2] = decode->stack[decode->stackcnt++] = getPrefix(decode, decode->code_last); + } else + return 0; + + /** + * If the image is OK we should not get a CODE_NONE while tracing. + * To prevent looping with a bad image we use StackPtr as loop counter + * and stop before overflowing Stack[]. + */ + while (decode->stackcnt < sizeof(decode->stack) && prefix > decode->code_clear && prefix <= CODE_MAX) { + decode->stack[decode->stackcnt++] = decode->suffix[prefix]; + prefix = decode->prefix[prefix]; + } + if (decode->stackcnt >= sizeof(decode->stack) || prefix > CODE_MAX) + return 0; + + /* Push the last character on stack: */ + decode->stack[decode->stackcnt++] = prefix; + } + + if (decode->code_last != CODE_NONE && decode->prefix[decode->code_max - 2] == CODE_NONE) { + decode->prefix[decode->code_max - 2] = decode->code_last; + + /* Only allowed if code is exactly the running code: + * In that case code = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + decode->suffix[decode->code_max - 2] = getPrefix(decode, code == decode->code_max - 2 ? decode->code_last : code); + } + decode->code_last = code; + } + return cnt; +} + +/** + * Read the info on a frame. + * + * Pre: The file position is at the start of the frame. + */ +static gdispImageError initFrame(gdispImage *img) { + gdispImagePrivate * priv; + imgcache * cache; + uint8_t blocktype; + uint8_t blocksz; + + priv = img->priv; + + // Save the dispose info from the existing frame + priv->dispose.flags = priv->frame.flags; + priv->dispose.paltrans = priv->frame.paltrans; + priv->dispose.x = priv->frame.x; + priv->dispose.y = priv->frame.y; + priv->dispose.width = priv->frame.width; + priv->dispose.height = priv->frame.height; + + // Check for a cached version of this image + for(cache=priv->cache; cache && cache->frame.posstart <= (size_t)gfileGetPos(img->f); cache=cache->next) { + if (cache->frame.posstart == (size_t)gfileGetPos(img->f)) { + priv->frame = cache->frame; + priv->curcache = cache; + return GDISP_IMAGE_ERR_OK; + } + } + + // Get ready for a new image + priv->curcache = 0; + priv->frame.posstart = gfileGetPos(img->f); + priv->frame.flags = 0; + priv->frame.delay = 0; + priv->frame.palsize = 0; + + // Process blocks until we reach the image descriptor + while(1) { + if (gfileRead(img->f, &blocktype, 1) != 1) + return GDISP_IMAGE_ERR_BADDATA; + + switch(blocktype) { + case 0x2C: //',' - IMAGE_DESC_RECORD_TYPE; + // Read the Image Descriptor + if (gfileRead(img->f, priv->buf, 9) != 9) + return GDISP_IMAGE_ERR_BADDATA; + priv->frame.x = *(uint16_t *)(((uint8_t *)priv->buf)+0); + CONVERT_FROM_WORD_LE(priv->frame.x); + priv->frame.y = *(uint16_t *)(((uint8_t *)priv->buf)+2); + CONVERT_FROM_WORD_LE(priv->frame.y); + priv->frame.width = *(uint16_t *)(((uint8_t *)priv->buf)+4); + CONVERT_FROM_WORD_LE(priv->frame.width); + priv->frame.height = *(uint16_t *)(((uint8_t *)priv->buf)+6); + CONVERT_FROM_WORD_LE(priv->frame.height); + if (((uint8_t *)priv->buf)[8] & 0x80) // Local color table? + priv->frame.palsize = 2 << (((uint8_t *)priv->buf)[8] & 0x07); + if (((uint8_t *)priv->buf)[8] & 0x40) // Interlaced? + priv->frame.flags |= GIFL_INTERLACE; + + // We are ready to go for the actual palette read and image decode + priv->frame.pospal = gfileGetPos(img->f); + priv->frame.posimg = priv->frame.pospal+priv->frame.palsize*3; + priv->frame.posend = 0; + + // Mark this as an animated image if more than 1 frame. + if (priv->frame.posstart != priv->frame0pos) + img->flags |= GDISP_IMAGE_FLG_ANIMATED; + return GDISP_IMAGE_ERR_OK; + + case 0x21: //'!' - EXTENSION_RECORD_TYPE; + // Read the extension type + if (gfileRead(img->f, &blocktype, 1) != 1) + return GDISP_IMAGE_ERR_BADDATA; + + switch(blocktype) { + case 0xF9: // EXTENSION - Graphics Control Block + // Read the GCB + if (gfileRead(img->f, priv->buf, 6) != 6) + return GDISP_IMAGE_ERR_BADDATA; + // Check we have read a 4 byte data block and a data block terminator (0) + if (((uint8_t *)priv->buf)[0] != 4 || ((uint8_t *)priv->buf)[5] != 0) + return GDISP_IMAGE_ERR_BADDATA; + // Process the flags + switch(((uint8_t *)priv->buf)[1] & 0x1C) { + case 0x00: case 0x04: break; // Dispose = do nothing + case 0x08: priv->frame.flags |= GIFL_DISPOSECLEAR; break; // Dispose = clear + case 0x0C: case 0x10: priv->frame.flags |= GIFL_DISPOSEREST; break; // Dispose = restore. Value 0x10 is a hack for bad encoders + default: return GDISP_IMAGE_ERR_UNSUPPORTED; + } + if (((uint8_t *)priv->buf)[1] & 0x01) { + priv->frame.flags |= GIFL_TRANSPARENT; + img->flags |= GDISP_IMAGE_FLG_TRANSPARENT; // We set this but never clear it + } + if (((uint8_t *)priv->buf)[1] & 0x02) // Wait for user input? + img->flags |= GDISP_IMAGE_FLG_MULTIPAGE; + else + img->flags &= ~GDISP_IMAGE_FLG_MULTIPAGE; + // Process frame delay and the transparent color (if any) + priv->frame.delay = *(uint16_t *)(((uint8_t *)priv->buf)+2); + CONVERT_FROM_WORD_LE(priv->frame.delay); + priv->frame.paltrans = ((uint8_t *)priv->buf)[4]; + break; + + case 0xFF: // EXTENSION - Application + // We only handle this for the special Netscape loop counter for animation + if (priv->flags & GIF_LOOP) + goto skipdatablocks; + // Read the Application header + if (gfileRead(img->f, priv->buf, 16) != 16) + return GDISP_IMAGE_ERR_BADDATA; + // Check we have read a 11 byte data block + if (((uint8_t *)priv->buf)[0] != 11 && ((uint8_t *)priv->buf)[12] != 3) + return GDISP_IMAGE_ERR_BADDATA; + // Check the vendor + if (((uint8_t *)priv->buf)[1] == 'N' && ((uint8_t *)priv->buf)[2] == 'E' && ((uint8_t *)priv->buf)[3] == 'T' + && ((uint8_t *)priv->buf)[4] == 'S' && ((uint8_t *)priv->buf)[5] == 'C' && ((uint8_t *)priv->buf)[6] == 'A' + && ((uint8_t *)priv->buf)[7] == 'P' && ((uint8_t *)priv->buf)[8] == 'E' && ((uint8_t *)priv->buf)[9] == '2' + && ((uint8_t *)priv->buf)[10] == '.' && ((uint8_t *)priv->buf)[11] == '0') { + if (((uint8_t *)priv->buf)[13] == 1) { + priv->loops = *(uint16_t *)(((uint8_t *)priv->buf)+14); + CONVERT_FROM_WORD_LE(priv->loops); + priv->flags |= GIF_LOOP; + if (!priv->loops) + priv->flags |= GIF_LOOPFOREVER; + } + } + goto skipdatablocks; + + case 0x01: // EXTENSION - Plain Text (Graphics Rendering) + case 0xFE: // EXTENSION - Comment + default: + // 0x00-0x7F (0-127) are the Graphic Rendering blocks + if (blocktype <= 0x7F) + return GDISP_IMAGE_ERR_UNSUPPORTED; + // 0x80-0xF9 (128-249) are the Control blocks + // 0xFA-0xFF (250-255) are the Special Purpose blocks + // We don't understand this extension - just skip it by skipping data blocks + skipdatablocks: + while(1) { + if (gfileRead(img->f, &blocksz, 1) != 1) + return GDISP_IMAGE_ERR_BADDATA; + if (!blocksz) + break; + gfileSetPos(img->f, gfileGetPos(img->f) + blocksz); + } + break; + } + break; + + case 0x3B: //';' - TERMINATE_RECORD_TYPE; + // Are we an looping animation + if (!(priv->flags & GIF_LOOP)) + return GDISP_IMAGE_EOF; + if (!(priv->flags & GIF_LOOPFOREVER)) { + if (!priv->loops) + return GDISP_IMAGE_EOF; + priv->loops--; + } + + // Seek back to frame0 + gfileSetPos(img->f, priv->frame0pos); + return GDISP_IMAGE_LOOP; + + default: // UNDEFINED_RECORD_TYPE; + return GDISP_IMAGE_ERR_UNSUPPORTED; + } + } +} + +void gdispImageClose_GIF(gdispImage *img) { + gdispImagePrivate * priv; + imgcache * cache; + imgcache * ncache; + + priv = img->priv; + if (priv) { + // Free any stored frames + cache = priv->cache; + while(cache) { + ncache = cache->next; + gdispImageFree(img, (void *)cache, sizeof(imgcache)+cache->frame.width*cache->frame.height+cache->frame.palsize*sizeof(color_t)); + cache = ncache; + } + if (priv->palette) + gdispImageFree(img, (void *)priv->palette, priv->palsize*sizeof(color_t)); + gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); + img->priv = 0; + } +} + +gdispImageError gdispImageOpen_GIF(gdispImage *img) { + gdispImagePrivate *priv; + uint8_t hdr[6]; + uint16_t aword; + + /* Read the file identifier */ + if (gfileRead(img->f, hdr, 6) != 6) + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + /* Process the GIFFILEHEADER structure */ + + if (hdr[0] != 'G' || hdr[1] != 'I' || hdr[2] != 'F' + || hdr[3] != '8' || (hdr[4] != '7' && hdr[4] != '9') || hdr[5] != 'a') + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + /* We know we are a GIF format image */ + img->flags = 0; + + /* Allocate our private area */ + if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) + return GDISP_IMAGE_ERR_NOMEMORY; + + /* Initialise the essential bits in the private area */ + priv = img->priv; + priv->flags = 0; + priv->palsize = 0; + priv->palette = 0; + priv->frame.flags = 0; + priv->cache = 0; + priv->curcache = 0; + + /* Process the Screen Descriptor structure */ + + // Read the screen descriptor + if (gfileRead(img->f, priv->buf, 7) != 7) + goto baddatacleanup; + // Get the width + img->width = *(uint16_t *)(((uint8_t *)priv->buf)+0); + CONVERT_FROM_WORD_LE(img->width); + // Get the height + img->height = *(uint16_t *)(((uint8_t *)priv->buf)+2); + CONVERT_FROM_WORD_LE(img->height); + if (((uint8_t *)priv->buf)[4] & 0x80) { + // Global color table + priv->palsize = 2 << (((uint8_t *)priv->buf)[4] & 0x07); + // Allocate the global palette + if (!(priv->palette = (color_t *)gdispImageAlloc(img, priv->palsize*sizeof(color_t)))) + goto nomemcleanup; + // Read the global palette + for(aword = 0; aword < priv->palsize; aword++) { + if (gfileRead(img->f, &priv->buf, 3) != 3) + goto baddatacleanup; + priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[0], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[2]); + } + } + priv->bgcolor = ((uint8_t *)priv->buf)[5]; + + // Save the fram0pos + priv->frame0pos = gfileGetPos(img->f); + + // Read the first frame descriptor + switch(initFrame(img)) { + case GDISP_IMAGE_ERR_OK: // Everything OK + img->type = GDISP_IMAGE_TYPE_GIF; + return GDISP_IMAGE_ERR_OK; + case GDISP_IMAGE_ERR_UNSUPPORTED: // Unsupported + gdispImageClose_GIF(img); // Clean up the private data area + return GDISP_IMAGE_ERR_UNSUPPORTED; + case GDISP_IMAGE_ERR_NOMEMORY: // Out of Memory + nomemcleanup: + gdispImageClose_GIF(img); // Clean up the private data area + return GDISP_IMAGE_ERR_NOMEMORY; + case GDISP_IMAGE_EOF: // We should have a frame but we don't seem to + case GDISP_IMAGE_LOOP: // We should have a frame but we don't seem to + case GDISP_IMAGE_ERR_BADDATA: // Oops - something wrong with the data + default: + baddatacleanup: + gdispImageClose_GIF(img); // Clean up the private data area + return GDISP_IMAGE_ERR_BADDATA; + } +} + +gdispImageError gdispImageCache_GIF(gdispImage *img) { + gdispImagePrivate * priv; + imgcache * cache; + imgdecode * decode; + uint8_t * p; + uint8_t * q; + coord_t mx, my; + uint16_t cnt; + + /* If we are already cached - just return OK */ + priv = img->priv; + if (priv->curcache) + return GDISP_IMAGE_ERR_OK; + + /* We need to allocate the frame, the palette and bits for the image */ + if (!(cache = (imgcache *)gdispImageAlloc(img, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height))) + return GDISP_IMAGE_ERR_NOMEMORY; + + /* Initialise the cache */ + decode = 0; + cache->frame = priv->frame; + cache->imagebits = (uint8_t *)(cache+1) + cache->frame.palsize*sizeof(color_t); + cache->next = 0; + + /* Start the decode */ + switch(startDecode(img)) { + case GDISP_IMAGE_ERR_OK: break; + case GDISP_IMAGE_ERR_NOMEMORY: goto nomemcleanup; + case GDISP_IMAGE_ERR_BADDATA: + default: goto baddatacleanup; + } + decode = priv->decode; + + // Save the palette + if (cache->frame.palsize) { + cache->palette = (color_t *)(cache+1); + + /* Copy the local palette into the cache */ + for(cnt = 0; cnt < cache->frame.palsize; cnt++) + cache->palette[cnt] = decode->palette[cnt]; + } else + cache->palette = priv->palette; + + // Check for interlacing + cnt = 0; + if (cache->frame.flags & GIFL_INTERLACE) { + // Every 8th row starting at row 0 + for(p=cache->imagebits, my=0; my < cache->frame.height; my+=8, p += cache->frame.width*7) { + for(mx=0; mx < cache->frame.width; mx++) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + while(cnt < sizeof(decode->buf)) + decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; + } + q = decode->buf; + } + *p++ = *q++; + cnt--; + } + } + // Every 8th row starting at row 4 + for(p=cache->imagebits+cache->frame.width*4, my=4; my < cache->frame.height; my+=8, p += cache->frame.width*7) { + for(mx=0; mx < cache->frame.width; mx++) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + while(cnt < sizeof(decode->buf)) + decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; + } + q = decode->buf; + } + *p++ = *q++; + cnt--; + } + } + // Every 4th row starting at row 2 + for(p=cache->imagebits+cache->frame.width*2, my=2; my < cache->frame.height; my+=4, p += cache->frame.width*3) { + for(mx=0; mx < cache->frame.width; mx++) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + while(cnt < sizeof(decode->buf)) + decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; + } + q = decode->buf; + } + *p++ = *q++; + cnt--; + } + } + // Every 2nd row starting at row 1 + for(p=cache->imagebits+cache->frame.width, my=1; my < cache->frame.height; my+=2, p += cache->frame.width) { + for(mx=0; mx < cache->frame.width; mx++) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + while(cnt < sizeof(decode->buf)) + decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; + } + q = decode->buf; + } + *p++ = *q++; + cnt--; + } + } + } else { + // Every row in sequence + p=cache->imagebits; + for(my=0; my < cache->frame.height; my++) { + for(mx=0; mx < cache->frame.width; mx++) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + while(cnt < sizeof(decode->buf)) + decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; + } + q = decode->buf; + } + *p++ = *q++; + cnt--; + } + } + } + // We could be pedantic here but extra bytes won't hurt us + while(getbytes(img)); + priv->frame.posend = cache->frame.posend = gfileGetPos(img->f); + + // Save everything + priv->curcache = cache; + if (!priv->cache) + priv->cache = cache; + else if (priv->cache->frame.posstart > cache->frame.posstart) { + cache->next = priv->cache; + priv->cache = cache; + } else { + imgcache *pc; + + for(pc = priv->cache; pc; pc = pc->next) { + if (!pc->next || pc->next->frame.posstart > cache->frame.posstart) { + cache->next = pc->next; + pc->next = cache; + break; + } + } + } + stopDecode(img); + return GDISP_IMAGE_ERR_OK; + +nomemcleanup: + stopDecode(img); + gdispImageFree(img, cache, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height); + return GDISP_IMAGE_ERR_NOMEMORY; + +baddatacleanup: + stopDecode(img); + gdispImageFree(img, cache, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height); + return GDISP_IMAGE_ERR_BADDATA; +} + +gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { + gdispImagePrivate * priv; + imgdecode * decode; + uint8_t * q = 0; + coord_t mx, my, fx, fy; + uint16_t cnt, gcnt; + uint8_t col; + + priv = img->priv; + + /* Handle previous frame disposing */ + if (priv->dispose.flags & (GIFL_DISPOSECLEAR|GIFL_DISPOSEREST)) { + // Clip to the disposal area - clip area = mx,my -> fx, fy (sx,sy,cx,cy are unchanged) + mx = priv->dispose.x; + my = priv->dispose.y; + fx = priv->dispose.x+priv->dispose.width; + fy = priv->dispose.y+priv->dispose.height; + if (sx > mx) mx = sx; + if (sy > my) my = sy; + if (sx+cx <= fx) fx = sx+cx; + if (sy+cy <= fy) fy = sy+cy; + if (fx > mx && fy > my) { + // We only support clearing (not restoring). The specification says that we are allowed to do this. + // Calculate the bgcolor + // The spec says to restore the backgound color (priv->bgcolor) but in practice if there is transparency + // image decoders tend to assume that a restore to the transparent color is required instead + if (((priv->dispose.flags & GIFL_TRANSPARENT) /*&& priv->dispose.paltrans == priv->bgcolor*/) || priv->bgcolor >= priv->palsize) + gdispGFillArea(g, x+mx-sx, y+my-sy, fx-mx, fy-my, img->bgcolor); + else + gdispGFillArea(g, x+mx-sx, y+my-sy, fx-mx, fy-my, priv->palette[priv->bgcolor]); + } + } + + /* Clip to just this frame - clip area = sx,sy -> fx, fy */ + fx = priv->frame.x+priv->frame.width; + fy = priv->frame.y+priv->frame.height; + if (sx >= fx || sy >= fy || sx+cx < priv->frame.x || sy+cy < priv->frame.y) return GDISP_IMAGE_ERR_OK; + if (sx < priv->frame.x) { mx = priv->frame.x - sx; x += mx; cx -= mx; sx = priv->frame.x; } + if (sy < priv->frame.y) { my = priv->frame.y - sy; y += my; cy -= my; sy = priv->frame.y; } + if (sx+cx > fx) cx = fx-sx; + if (sy+cy > fy) cy = fy-sy; + + // Make sx, sy relative to this frame so we are not adding priv->frame.x & priv->frame.y each time + sx -= priv->frame.x; sy -= priv->frame.y; + fx = sx + cx; + fy = sy + cy; + + /* Draw from the image cache - if it exists */ + if (priv->curcache) { + imgcache * cache; + + cache = priv->curcache; + q = cache->imagebits+priv->frame.width*sy+sx; + + for(my=sy; my < fy; my++, q += priv->frame.width - cx) { + for(gcnt=0, mx=sx, cnt=0; mx < fx; mx++) { + col = *q++; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = cache->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + + return GDISP_IMAGE_ERR_OK; + } + + /* Start the decode */ + switch(startDecode(img)) { + case GDISP_IMAGE_ERR_OK: break; + case GDISP_IMAGE_ERR_NOMEMORY: return GDISP_IMAGE_ERR_NOMEMORY; + case GDISP_IMAGE_ERR_BADDATA: + default: return GDISP_IMAGE_ERR_BADDATA; + } + decode = priv->decode; + + // Check for interlacing + cnt = 0; + if (priv->frame.flags & GIFL_INTERLACE) { + // Every 8th row starting at row 0 + for(my=0; my < priv->frame.height; my+=8) { + for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + mx++; + break; + } + q = decode->buf; + } + if (my >= sy && my < fy && mx >= sx && mx < fx) { + col = *q; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = decode->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + continue; + } + // We have finished the visible area - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + // Every 8th row starting at row 4 + for(my=4; my < priv->frame.height; my+=8) { + for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + mx++; + break; + } + q = decode->buf; + } + if (my >= sy && my < fy && mx >= sx && mx < fx) { + col = *q; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = decode->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + continue; + } + // We have finished the visible area - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + // Every 4th row starting at row 2 + for(my=2; my < priv->frame.height; my+=4) { + for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + mx++; + break; + } + q = decode->buf; + } + if (my >= sy && my < fy && mx >= sx && mx < fx) { + col = *q; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = decode->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + continue; + } + // We have finished the visible area - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + // Every 2nd row starting at row 1 + for(my=1; my < priv->frame.height; my+=2) { + for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + mx++; + break; + } + q = decode->buf; + } + if (my >= sy && my < fy && mx >= sx && mx < fx) { + col = *q; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = decode->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + continue; + } + // We have finished the visible area - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + } else { + // Every row in sequence + for(my=0; my < priv->frame.height; my++) { + for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { + if (!cnt) { + if (!(cnt = getbytes(img))) { + // Sometimes the image EOF is a bit early - treat the rest as transparent + if (decode->code_last != decode->code_eof) + goto baddatacleanup; + mx++; + break; + } + q = decode->buf; + } + if (my >= sy && my < fy && mx >= sx && mx < fx) { + col = *q; + if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { + // We have a transparent pixel - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + continue; + } + priv->buf[gcnt++] = decode->palette[col]; + if (gcnt >= BLIT_BUFFER_SIZE) { + // We have run out of buffer - dump it to the display + gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); + gcnt = 0; + } + continue; + } + // We have finished the visible area - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; + } + } + // We have finished the line - dump the buffer to the display + switch(gcnt) { + case 0: break; + case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; + default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; + } + } + } + // We could be pedantic here but extra bytes won't hurt us + while (getbytes(img)); + priv->frame.posend = gfileGetPos(img->f); + + stopDecode(img); + return GDISP_IMAGE_ERR_OK; + +baddatacleanup: + stopDecode(img); + return GDISP_IMAGE_ERR_BADDATA; +} + +delaytime_t gdispImageNext_GIF(gdispImage *img) { + gdispImagePrivate * priv; + delaytime_t delay; + uint8_t blocksz; + + priv = img->priv; + + // Save the delay and convert to millisecs + delay = (delaytime_t)priv->frame.delay * 10; + + // We need to get to the end of this frame + if (!priv->frame.posend) { + // We don't know where the end of the frame is yet - find it! + gfileSetPos(img->f, priv->frame.posimg+1); // Skip the code size byte too + while(1) { + if (gfileRead(img->f, &blocksz, 1) != 1) + return TIME_INFINITE; + if (!blocksz) + break; + gfileSetPos(img->f, gfileGetPos(img->f) + blocksz); + } + priv->frame.posend = gfileGetPos(img->f); + } + + // Seek to the end of this frame + gfileSetPos(img->f, priv->frame.posend); + + // Read the next frame descriptor + for(blocksz=0; blocksz < 2; blocksz++) { // 2 loops max to prevent cycling forever with a bad file + switch(initFrame(img)) { + case GDISP_IMAGE_ERR_OK: // Everything OK + return delay; + case GDISP_IMAGE_LOOP: // Back to the beginning + break; + case GDISP_IMAGE_EOF: // The real End-Of-File + case GDISP_IMAGE_ERR_BADDATA: // Oops - something wrong with the data + case GDISP_IMAGE_ERR_NOMEMORY: // Out of Memory + case GDISP_IMAGE_ERR_UNSUPPORTED: // Unsupported + default: + return TIME_INFINITE; + } + } + return TIME_INFINITE; +} + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_GIF */ +/** @} */ diff --git a/src/gdisp/gdisp_image_jpg.c b/src/gdisp/gdisp_image_jpg.c new file mode 100644 index 00000000..2f6a9392 --- /dev/null +++ b/src/gdisp/gdisp_image_jpg.c @@ -0,0 +1,19 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/image_jpg.c + * @brief GDISP native image code. + */ +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG + +#error "JPG support not implemented yet" + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG */ +/** @} */ diff --git a/src/gdisp/gdisp_image_native.c b/src/gdisp/gdisp_image_native.c new file mode 100644 index 00000000..81344642 --- /dev/null +++ b/src/gdisp/gdisp_image_native.c @@ -0,0 +1,145 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/image_native.c + * @brief GDISP native image code. + */ +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_NATIVE + +/** + * How big a pixel array to allocate for blitting + * Bigger is faster but uses more RAM. + */ +#define BLIT_BUFFER_SIZE 32 + +#define HEADER_SIZE 8 +#define FRAME0POS (HEADER_SIZE) + +/** + * Helper Routines Needed + */ +void *gdispImageAlloc(gdispImage *img, size_t sz); +void gdispImageFree(gdispImage *img, void *ptr, size_t sz); + +typedef struct gdispImagePrivate { + pixel_t *frame0cache; + pixel_t buf[BLIT_BUFFER_SIZE]; + } gdispImagePrivate; + +void gdispImageClose_NATIVE(gdispImage *img) { + if (img->priv) { + if (img->priv->frame0cache) + gdispImageFree(img, (void *)img->priv->frame0cache, img->width * img->height * sizeof(pixel_t)); + gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); + img->priv = 0; + } +} + +gdispImageError gdispImageOpen_NATIVE(gdispImage *img) { + uint8_t hdr[HEADER_SIZE]; + + /* Read the 8 byte header */ + if (gfileRead(img->f, hdr, 8) != 8) + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + if (hdr[0] != 'N' || hdr[1] != 'I') + return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us + + if (hdr[6] != GDISP_PIXELFORMAT/256 || hdr[7] != (GDISP_PIXELFORMAT & 0xFF)) + return GDISP_IMAGE_ERR_UNSUPPORTED; // Unsupported pixel format + + /* We know we are a native format image */ + img->flags = 0; + img->width = (((uint16_t)hdr[2])<<8) | (hdr[3]); + img->height = (((uint16_t)hdr[4])<<8) | (hdr[5]); + if (img->width < 1 || img->height < 1) + return GDISP_IMAGE_ERR_BADDATA; + if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) + return GDISP_IMAGE_ERR_NOMEMORY; + img->priv->frame0cache = 0; + + img->type = GDISP_IMAGE_TYPE_NATIVE; + return GDISP_IMAGE_ERR_OK; +} + +gdispImageError gdispImageCache_NATIVE(gdispImage *img) { + size_t len; + + /* If we are already cached - just return OK */ + if (img->priv->frame0cache) + return GDISP_IMAGE_ERR_OK; + + /* We need to allocate the cache */ + len = img->width * img->height * sizeof(pixel_t); + img->priv->frame0cache = (pixel_t *)gdispImageAlloc(img, len); + if (!img->priv->frame0cache) + return GDISP_IMAGE_ERR_NOMEMORY; + + /* Read the entire bitmap into cache */ + gfileSetPos(img->f, FRAME0POS); + if (gfileRead(img->f, img->priv->frame0cache, len) != len) + return GDISP_IMAGE_ERR_BADDATA; + + return GDISP_IMAGE_ERR_OK; +} + +gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { + coord_t mx, mcx; + size_t pos, len; + + /* Check some reasonableness */ + if (sx >= img->width || sy >= img->height) return GDISP_IMAGE_ERR_OK; + if (sx + cx > img->width) cx = img->width - sx; + if (sy + cy > img->height) cy = img->height - sy; + + /* Draw from the image cache - if it exists */ + if (img->priv->frame0cache) { + gdispGBlitArea(g, x, y, cx, cy, sx, sy, img->width, img->priv->frame0cache); + return GDISP_IMAGE_ERR_OK; + } + + /* For this image decoder we cheat and just seek straight to the region we want to display */ + pos = FRAME0POS + (img->width * sy + sx) * sizeof(pixel_t); + + /* Cycle through the lines */ + for(;cy;cy--, y++) { + /* Move to the start of the line */ + gfileSetPos(img->f, pos); + + /* Draw the line in chunks using BitBlt */ + for(mx = x, mcx = cx; mcx > 0; mcx -= len, mx += len) { + // Read the data + len = gfileRead(img->f, + img->priv->buf, + mcx > BLIT_BUFFER_SIZE ? (BLIT_BUFFER_SIZE*sizeof(pixel_t)) : (mcx * sizeof(pixel_t))) + / sizeof(pixel_t); + if (!len) + return GDISP_IMAGE_ERR_BADDATA; + + /* Blit the chunk of data */ + gdispGBlitArea(g, mx, y, len, 1, 0, 0, len, img->priv->buf); + } + + /* Get the position for the start of the next line */ + pos += img->width*sizeof(pixel_t); + } + + return GDISP_IMAGE_ERR_OK; +} + +delaytime_t gdispImageNext_NATIVE(gdispImage *img) { + (void) img; + + /* No more frames/pages */ + return TIME_INFINITE; +} + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_NATIVE */ +/** @} */ diff --git a/src/gdisp/gdisp_image_png.c b/src/gdisp/gdisp_image_png.c new file mode 100644 index 00000000..f35174d9 --- /dev/null +++ b/src/gdisp/gdisp_image_png.c @@ -0,0 +1,19 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gdisp/image_png.c + * @brief GDISP native image code. + */ +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_PNG + +#error "PNG support not implemented yet" + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_PNG */ +/** @} */ diff --git a/src/gdisp/image.c b/src/gdisp/image.c deleted file mode 100644 index e2b7d758..00000000 --- a/src/gdisp/image.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/image.c - * @brief GDISP generic image code. - * - * @defgroup Image Image - * @ingroup GDISP - */ -#include "gfx.h" - -#if GFX_USE_GDISP && GDISP_NEED_IMAGE - -#if GDISP_NEED_IMAGE_NATIVE - extern gdispImageError gdispImageOpen_NATIVE(gdispImage *img); - extern void gdispImageClose_NATIVE(gdispImage *img); - extern gdispImageError gdispImageCache_NATIVE(gdispImage *img); - extern gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - extern delaytime_t gdispImageNext_NATIVE(gdispImage *img); -#endif - -#if GDISP_NEED_IMAGE_GIF - extern gdispImageError gdispImageOpen_GIF(gdispImage *img); - extern void gdispImageClose_GIF(gdispImage *img); - extern gdispImageError gdispImageCache_GIF(gdispImage *img); - extern gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - extern delaytime_t gdispImageNext_GIF(gdispImage *img); -#endif - -#if GDISP_NEED_IMAGE_BMP - extern gdispImageError gdispImageOpen_BMP(gdispImage *img); - extern void gdispImageClose_BMP(gdispImage *img); - extern gdispImageError gdispImageCache_BMP(gdispImage *img); - extern gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - extern delaytime_t gdispImageNext_BMP(gdispImage *img); -#endif - -#if GDISP_NEED_IMAGE_JPG - extern gdispImageError gdispImageOpen_JPG(gdispImage *img); - extern void gdispImageClose_JPG(gdispImage *img); - extern gdispImageError gdispImageCache_JPG(gdispImage *img); - extern gdispImageError gdispGImageDraw_JPG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - extern delaytime_t gdispImageNext_JPG(gdispImage *img); -#endif - -#if GDISP_NEED_IMAGE_PNG - extern gdispImageError gdispImageOpen_PNG(gdispImage *img); - extern void gdispImageClose_PNG(gdispImage *img); - extern gdispImageError gdispImageCache_PNG(gdispImage *img); - extern gdispImageError gdispGImageDraw_PNG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - extern delaytime_t gdispImageNext_PNG(gdispImage *img); -#endif - -/* The structure defining the routines for image drawing */ -typedef struct gdispImageHandlers { - gdispImageError (*open)(gdispImage *img); /* The open function */ - void (*close)(gdispImage *img); /* The close function */ - gdispImageError (*cache)(gdispImage *img); /* The cache function */ - gdispImageError (*draw)(GDisplay *g, - gdispImage *img, - coord_t x, coord_t y, - coord_t cx, coord_t cy, - coord_t sx, coord_t sy); /* The draw function */ - delaytime_t (*next)(gdispImage *img); /* The next frame function */ -} gdispImageHandlers; - -static gdispImageHandlers ImageHandlers[] = { - #if GDISP_NEED_IMAGE_NATIVE - { gdispImageOpen_NATIVE, gdispImageClose_NATIVE, - gdispImageCache_NATIVE, gdispGImageDraw_NATIVE, gdispImageNext_NATIVE, - }, - #endif - #if GDISP_NEED_IMAGE_GIF - { gdispImageOpen_GIF, gdispImageClose_GIF, - gdispImageCache_GIF, gdispGImageDraw_GIF, gdispImageNext_GIF, - }, - #endif - #if GDISP_NEED_IMAGE_BMP - { gdispImageOpen_BMP, gdispImageClose_BMP, - gdispImageCache_BMP, gdispGImageDraw_BMP, gdispImageNext_BMP, - }, - #endif - #if GDISP_NEED_IMAGE_JPG - { gdispImageOpen_JPG, gdispImageClose_JPG, - gdispImageCache_JPG, gdispGImageDraw_JPG, gdispImageNext_JPG, - }, - #endif - #if GDISP_NEED_IMAGE_PNG - { gdispImageOpen_PNG, gdispImageClose_PNG, - gdispImageCache_PNG, gdispGImageDraw_PNG, gdispImageNext_PNG, - }, - #endif -}; - -gdispImageError - DEPRECATED("Use gdispImageOpenGFile() instead") - gdispImageOpen(gdispImage *img) { - return gdispImageOpenGFile(img, img->f); -} - -#if GFILE_NEED_MEMFS - bool_t - DEPRECATED("Use gdispImageOpenMemory() instead") - gdispImageSetMemoryReader(gdispImage *img, const void *memimage) { - img->f = gfileOpenMemory((void *)memimage, "rb"); - return img->f != 0; - } -#endif - -#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX - bool_t - DEPRECATED("Use gdispImageOpenFile() instead") - gdispImageSetFileReader(gdispImage *img, const char *filename) { - img->f = gfileOpen(filename, "rb"); - return img->f != 0; - } -#endif - -#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS - bool_t - DEPRECATED("Use gdispImageOpenBaseFileStream() instead") - gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr) { - img->f = gfileOpenBaseFileStream(BaseFileStreamPtr, "rb"); - return img->f != 0; - } -#endif - -void gdispImageInit(gdispImage *img) { - img->type = GDISP_IMAGE_TYPE_UNKNOWN; -} - -gdispImageError gdispImageOpenGFile(gdispImage *img, GFILE *f) { - gdispImageError err; - - if (!f) - return GDISP_IMAGE_ERR_NOSUCHFILE; - img->f = f; - img->bgcolor = White; - for(img->fns = ImageHandlers; img->fns < ImageHandlers+sizeof(ImageHandlers)/sizeof(ImageHandlers[0]); img->fns++) { - err = img->fns->open(img); - if (err != GDISP_IMAGE_ERR_BADFORMAT) { - if ((err & GDISP_IMAGE_ERR_UNRECOVERABLE)) - goto unrecoverable; - - // Everything is possible - return err; - } - - // Try the next decoder - gfileSetPos(img->f, 0); - } - - err = GDISP_IMAGE_ERR_BADFORMAT; - img->type = GDISP_IMAGE_TYPE_UNKNOWN; - -unrecoverable: - gfileClose(img->f); - img->f = 0; - img->flags = 0; - img->fns = 0; - img->priv = 0; - return err; -} - -void gdispImageClose(gdispImage *img) { - if (img->fns) - img->fns->close(img); - gfileClose(img->f); - img->type = GDISP_IMAGE_TYPE_UNKNOWN; - img->flags = 0; - img->fns = 0; - img->priv = 0; -} - -bool_t gdispImageIsOpen(gdispImage *img) { - return img->type != GDISP_IMAGE_TYPE_UNKNOWN && img->fns != 0; -} - -void gdispImageSetBgColor(gdispImage *img, color_t bgcolor) { - img->bgcolor = bgcolor; -} - -gdispImageError gdispImageCache(gdispImage *img) { - if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; - return img->fns->cache(img); -} - -gdispImageError gdispGImageDraw(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { - if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; - return img->fns->draw(g, img, x, y, cx, cy, sx, sy); -} - -delaytime_t gdispImageNext(gdispImage *img) { - if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; - return img->fns->next(img); -} - -// Helper Routines -void *gdispImageAlloc(gdispImage *img, size_t sz) { - #if GDISP_NEED_IMAGE_ACCOUNTING - void *ptr; - - ptr = gfxAlloc(sz); - if (ptr) { - img->memused += sz; - if (img->memused > img->maxmemused) - img->maxmemused = img->memused; - } - return ptr; - #else - (void) img; - return gfxAlloc(sz); - #endif -} - -void gdispImageFree(gdispImage *img, void *ptr, size_t sz) { - #if GDISP_NEED_IMAGE_ACCOUNTING - gfxFree(ptr); - img->memused -= sz; - #else - (void) img; - (void) sz; - gfxFree(ptr); - #endif -} - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ -/** @} */ diff --git a/src/gdisp/image.h b/src/gdisp/image.h deleted file mode 100644 index 76e8bdea..00000000 --- a/src/gdisp/image.h +++ /dev/null @@ -1,315 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/image.h - * @brief GDISP image header file. - * - * @defgroup Image Image - * @ingroup GDISP - * @{ - */ - -#ifndef _GDISP_IMAGE_H -#define _GDISP_IMAGE_H -#if (GFX_USE_GDISP && GDISP_NEED_IMAGE) || defined(__DOXYGEN__) - -/** - * @brief The type of image - */ -typedef uint16_t gdispImageType; - #define GDISP_IMAGE_TYPE_UNKNOWN 0 - #define GDISP_IMAGE_TYPE_NATIVE 1 - #define GDISP_IMAGE_TYPE_GIF 2 - #define GDISP_IMAGE_TYPE_BMP 3 - #define GDISP_IMAGE_TYPE_JPG 4 - #define GDISP_IMAGE_TYPE_PNG 5 - -/** - * @brief An image error code - */ -typedef uint16_t gdispImageError; - #define GDISP_IMAGE_ERR_OK 0 - #define GDISP_IMAGE_ERR_UNRECOVERABLE 0x8000 - #define GDISP_IMAGE_ERR_BADFORMAT (GDISP_IMAGE_ERR_UNRECOVERABLE+1) - #define GDISP_IMAGE_ERR_BADDATA (GDISP_IMAGE_ERR_UNRECOVERABLE+2) - #define GDISP_IMAGE_ERR_UNSUPPORTED (GDISP_IMAGE_ERR_UNRECOVERABLE+3) - #define GDISP_IMAGE_ERR_UNSUPPORTED_OK 3 - #define GDISP_IMAGE_ERR_NOMEMORY (GDISP_IMAGE_ERR_UNRECOVERABLE+4) - #define GDISP_IMAGE_ERR_NOSUCHFILE (GDISP_IMAGE_ERR_UNRECOVERABLE+5) - -/** - * @brief Image flags - */ -typedef uint16_t gdispImageFlags; - #define GDISP_IMAGE_FLG_TRANSPARENT 0x0001 /* The image has transparency */ - #define GDISP_IMAGE_FLG_ANIMATED 0x0002 /* The image has animation */ - #define GDISP_IMAGE_FLG_MULTIPAGE 0x0004 /* The image has multiple pages */ - -struct gdispImageIO; - -/** - * @brief An image IO close function - * - * @param[in] pio Pointer to the io structure - * @param[in] desc The descriptor. A filename or an image structure pointer. - * - */ -typedef void (*gdispImageIOCloseFn)(struct gdispImageIO *pio); - -/** - * @brief An image IO read function - * @returns The number of bytes actually read or 0 on error - * - * @param[in] pio Pointer to the io structure - * @param[in] buf Where the results should be placed - * @param[in] len The number of bytes to read - * - */ -typedef size_t (*gdispImageIOReadFn)(struct gdispImageIO *pio, void *buf, size_t len); - -/** - * @brief An image IO seek function - * - * @param[in] pio Pointer to the io structure - * @param[in] pos Which byte to seek to relative to the start of the "file". - * - */ -typedef void (*gdispImageIOSeekFn)(struct gdispImageIO *pio, size_t pos); - -typedef struct gdispImageIOFunctions { - gdispImageIOReadFn read; /* @< The function to read input */ - gdispImageIOSeekFn seek; /* @< The function to seek input */ - gdispImageIOCloseFn close; /* @< The function to close input */ - } gdispImageIOFunctions; - -/** - * @brief The structure defining the IO routines for image handling - */ -typedef struct gdispImageIO { - const void * fd; /* @< The "file" descriptor */ - size_t pos; /* @< The current "file" position */ - const gdispImageIOFunctions *fns; /* @< The current "file" functions */ -} gdispImageIO; - -/** - * @brief The structure for an image - */ -typedef struct gdispImage { - gdispImageType type; /* @< The image type */ - gdispImageFlags flags; /* @< The image flags */ - color_t bgcolor; /* @< The default background color */ - coord_t width, height; /* @< The image dimensions */ - GFILE * f; /* @< The underlying GFILE */ - #if GDISP_NEED_IMAGE_ACCOUNTING - uint32_t memused; /* @< How much RAM is currently allocated */ - uint32_t maxmemused; /* @< How much RAM has been allocated (maximum) */ - #endif - const struct gdispImageHandlers * fns; /* @< Don't mess with this! */ - struct gdispImagePrivate * priv; /* @< Don't mess with this! */ -} gdispImage; - -#ifdef __cplusplus -extern "C" { -#endif - - /* - * Deprecated Functions. - */ - gdispImageError DEPRECATED("Use gdispImageOpenGFile() instead") gdispImageOpen(gdispImage *img); - bool_t DEPRECATED("Use gdispImageOpenMemory() instead") gdispImageSetMemoryReader(gdispImage *img, const void *memimage); - #if GFX_USE_OS_CHIBIOS - bool_t DEPRECATED("Use gdispImageOpenBaseFileStream() instead") gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr); - #endif - #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX - bool_t DEPRECATED("Please use gdispImageOpenFile() instead") gdispImageSetFileReader(gdispImage *img, const char *filename); - #define gdispImageSetSimulFileReader(img, fname) gdispImageSetFileReader(img, fname) - #endif - - /** - * @brief Initialise a gdispImage object - * - * @param[in] img The image structure to initialise - * - */ - void gdispImageInit(gdispImage *img); - - /** - * @brief Open an image using an open GFILE and get it ready for drawing - * @details Determine the image format and get ready to decode the first image frame - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @param[in] img The image structure - * @param[in] f The open GFILE stream. - * - * @pre The GFILE must be open for reading. - * - * @note This determines which decoder to use and then initialises all other fields - * in the gdispImage structure. - * @note The image background color is set to White. - * @note There are three types of return - everything OK, partial success and unrecoverable - * failures. For everything OK it returns GDISP_IMAGE_ERR_OK. A partial success can - * be distinguished from a unrecoverable failure by testing the GDISP_IMAGE_ERR_UNRECOVERABLE - * bit in the error code. - * A partial success return code means an image can still be drawn but perhaps with - * reduced functionality eg only the first page of a multi-page image. - * @note @p gdispImageClose() should be called when finished with the image. This will close - * the image and its underlying GFILE file. Note that images opened with partial success - * (eg GDISP_IMAGE_ERR_UNSUPPORTED_OK) - * still need to be closed when you are finished with them. - */ - gdispImageError gdispImageOpenGFile(gdispImage *img, GFILE *f); - - /** - * @brief Open an image in a file and get it ready for drawing - * @details Determine the image format and get ready to decode the first image frame - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @pre You must have included the file-system support into GFILE that you want to use. - * - * @param[in] img The image structure - * @param[in] filename The filename to open - * - * @note This function just opens the GFILE using the filename and passes it to @p gdispImageOpenGFile(). - */ - #define gdispImageOpenFile(img, filename) gdispImageOpenGFile((img), gfileOpen((filename), "rb")) - - /** - * @brief Open an image in a ChibiOS basefilestream and get it ready for drawing - * @details Determine the image format and get ready to decode the first image frame - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @pre GFILE_NEED_CHIBIOSFS and GFX_USE_OS_CHIBIOS must be TRUE. This only makes sense on the ChibiOS - * operating system. - * - * @param[in] img The image structure - * @param[in] BaseFileStreamPtr A pointer to an open BaseFileStream - * - * @note This function just opens the GFILE using the basefilestream and passes it to @p gdispImageOpenGFile(). - */ - #define gdispImageOpenBaseFileStream(img, BaseFileStreamPtr) gdispImageOpenGFile((img), gfileOpenBaseFileStream((BaseFileStreamPtr), "rb")) - - /** - * @brief Open an image in memory and get it ready for drawing - * @details Determine the image format and get ready to decode the first image frame - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @pre GFILE_NEED_MEMFS must be TRUE - * - * @param[in] img The image structure - * @param[in] ptr A pointer to the image bytes in memory - * - * @note This function just opens the GFILE using the basefilestream and passes it to @p gdispImageOpenGFile(). - */ - #define gdispImageOpenMemory(img, ptr) gdispImageOpenGFile((img), gfileOpenMemory((void *)(ptr), "rb")) - - /** - * @brief Close an image and release any dynamically allocated working storage. - * - * @param[in] img The image structure - * - * @pre gdispImageOpenFile() must have returned successfully. - * - * @note Also calls the IO close function (if it hasn't already been called). - */ - void gdispImageClose(gdispImage *img); - - /** - * @brief Is an image open. - * @return TRUE if the image is currently open. - * - * @param[in] img The image structure - * - * @note Be careful with calling this on an uninitialized image structure as the image - * will contain random data which may be interpreted as meaning the image - * is open. Clearing the Image structure to 0's will guarantee the image - * is seen as being closed. - */ - bool_t gdispImageIsOpen(gdispImage *img); - - /** - * @brief Set the background color of the image. - * - * @param[in] img The image structure - * @param[in] bgcolor The background color to use - * - * @pre gdispImageOpen() must have returned successfully. - * - * @note This color is only used when an image has to restore part of the background before - * continuing with drawing that includes transparency eg some GIF animations. - */ - void gdispImageSetBgColor(gdispImage *img, color_t bgcolor); - - /** - * @brief Cache the image - * @details Decodes and caches the current frame into RAM. - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @param[in] img The image structure - * - * @pre gdispImageOpen() must have returned successfully. - * - * @note This can use a LOT of RAM! - * @note The decoder may choose to ignore the request for caching. If it does so it will - * return GDISP_IMAGE_ERR_UNSUPPORTED_OK. - * @note A fatal error here does not necessarily mean that drawing the image will fail. For - * example, a GDISP_IMAGE_ERR_NOMEMORY error simply means there isn't enough RAM to - * cache the image. - */ - gdispImageError gdispImageCache(gdispImage *img); - - /** - * @brief Draw the image - * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @param[in] g The display to draw on - * @param[in] img The image structure - * @param[in] x,y The screen location to draw the image - * @param[in] cx,cy The area on the screen to draw - * @param[in] sx,sy The image position to start drawing at - * - * @pre gdispImageOpen() must have returned successfully. - * - * @note If sx,sy + cx,cy is outside the image boundaries the area outside the image - * is simply not drawn. - * @note If @p gdispImageCache() has been called first for this frame, this routine will draw using a - * fast blit from the cached frame. If not, it reads the input and decodes it as it - * is drawing. This may be significantly slower than if the image has been cached (but - * uses a lot less RAM) - */ - gdispImageError gdispGImageDraw(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - #define gdispImageDraw(img,x,y,cx,cy,sx,sy) gdispGImageDraw(GDISP,img,x,y,cx,cy,sx,sy) - - /** - * @brief Prepare for the next frame/page in the image file. - * @return A time in milliseconds to keep displaying the current frame before trying to draw - * the next frame. Watch out for the special values TIME_IMMEDIATE and TIME_INFINITE. - * - * @param[in] img The image structure - * - * @pre gdispImageOpen() must have returned successfully. - * - * @note It will return TIME_IMMEDIATE if the first frame/page hasn't been drawn or if the next frame - * should be drawn immediately. - * @note It will return TIME_INFINITE if another image frame doesn't exist or an error has occurred. - * @note Images that support multiple pages (eg TIFF files) will return TIME_IMMEDIATE between pages - * and then TIME_INFINITE when there are no more pages. - * @note An image that displays a looped animation will never return TIME_INFINITE unless it - * gets an error. - * @note Calling gdispImageDraw() after getting a TIME_INFINITE will go back to drawing the first - * frame/page. - */ - delaytime_t gdispImageNext(gdispImage *img); - -#ifdef __cplusplus -} -#endif - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ -#endif /* _GDISP_IMAGE_H */ -/** @} */ - diff --git a/src/gdisp/image_bmp.c b/src/gdisp/image_bmp.c deleted file mode 100644 index 8ff40ca0..00000000 --- a/src/gdisp/image_bmp.c +++ /dev/null @@ -1,904 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/image_bmp.c - * @brief GDISP native image code. - * - * @defgroup Image Image - * @ingroup GDISP - */ -#include "gfx.h" - -#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_BMP - -#ifndef GDISP_NEED_IMAGE_BMP_1 - #define GDISP_NEED_IMAGE_BMP_1 TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_4 - #define GDISP_NEED_IMAGE_BMP_4 TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_4_RLE - #define GDISP_NEED_IMAGE_BMP_4_RLE TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_8 - #define GDISP_NEED_IMAGE_BMP_8 TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_8_RLE - #define GDISP_NEED_IMAGE_BMP_8_RLE TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_16 - #define GDISP_NEED_IMAGE_BMP_16 TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_24 - #define GDISP_NEED_IMAGE_BMP_24 TRUE -#endif -#ifndef GDISP_NEED_IMAGE_BMP_32 - #define GDISP_NEED_IMAGE_BMP_32 TRUE -#endif - -/** - * Helper Routines Needed - */ -void *gdispImageAlloc(gdispImage *img, size_t sz); -void gdispImageFree(gdispImage *img, void *ptr, size_t sz); - -/** - * How big a pixel array to allocate for blitting (in pixels) - * Bigger is faster but uses more RAM. - * This must be greater than 40 bytes and 32 pixels as we read our headers into this space as well - */ -#define BLIT_BUFFER_SIZE 32 - -/* - * Determining endianness as at compile time is not guaranteed or compiler portable. - * We use the best test we can. If we can't guarantee little endianness we do things the - * hard way. - */ -#define GUARANTEED_LITTLE_ENDIAN (!defined(SAFE_ENDIAN) && !defined(SAFE_ALIGNMENT) && (\ - (defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \ - || defined(__LITTLE_ENDIAN__) \ - || defined(__LITTLE_ENDIAN) \ - || defined(_LITTLE_ENDIAN) \ -/* || (1 == *(unsigned char *)&(const int){1})*/ \ - )) - - -/* This is a runtime test */ -static const uint8_t dwordOrder[4] = { 1, 2, 3, 4 }; - -#define isWordLittleEndian() (*(uint16_t *)&dwordOrder == 0x0201) -#define isDWordLittleEndian() (*(uint32_t *)&dwordOrder == 0x04030201) - -#if GUARANTEED_LITTLE_ENDIAN - /* These are fast routines for guaranteed little endian machines */ - #define CONVERT_FROM_WORD_LE(w) - #define CONVERT_FROM_DWORD_LE(dw) -#else - /* These are slower routines for when little endianness cannot be guaranteed at compile time */ - #define CONVERT_FROM_WORD_LE(w) { if (!isWordLittleEndian()) w = ((((uint16_t)(w))>>8)|(((uint16_t)(w))<<8)); } - #define CONVERT_FROM_DWORD_LE(dw) { if (!isDWordLittleEndian()) dw = (((uint32_t)(((const uint8_t *)(&dw))[0]))|(((uint32_t)(((const uint8_t *)(&dw))[1]))<<8)|(((uint32_t)(((const uint8_t *)(&dw))[2]))<<16)|(((uint32_t)(((const uint8_t *)(&dw))[3]))<<24)); } -#endif - -typedef struct gdispImagePrivate { - uint8_t bmpflags; - #define BMP_V2 0x01 // Version 2 (old) header format - #define BMP_V4 0x02 // Version 4 (alpha support) header format - #define BMP_PALETTE 0x04 // Uses a palette - #define BMP_COMP_RLE 0x08 // Uses RLE compression - #define BMP_COMP_MASK 0x10 // Uses mask & shift decoding - #define BMP_RLE_ENC 0x20 // Currently in RLE encoded run - #define BMP_RLE_ABS 0x40 // Currently in RLE absolute run - #define BMP_TOP_TO_BOTTOM 0x80 // Decodes bottom to top line - uint8_t bitsperpixel; -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - uint16_t palsize; - pixel_t *palette; -#endif -#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE - uint16_t rlerun; - uint8_t rlecode; -#endif -#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 - int8_t shiftred; - int8_t shiftgreen; - int8_t shiftblue; - int8_t shiftalpha; - uint32_t maskred; - uint32_t maskgreen; - uint32_t maskblue; - uint32_t maskalpha; -#endif - size_t frame0pos; - pixel_t *frame0cache; - pixel_t buf[BLIT_BUFFER_SIZE]; - } gdispImagePrivate; - -void gdispImageClose_BMP(gdispImage *img) { - if (img->priv) { -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - if (img->priv->palette) - gdispImageFree(img, (void *)img->priv->palette, img->priv->palsize*sizeof(color_t)); -#endif - if (img->priv->frame0cache) - gdispImageFree(img, (void *)img->priv->frame0cache, img->width*img->height*sizeof(pixel_t)); - gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); - img->priv = 0; - } -} - -gdispImageError gdispImageOpen_BMP(gdispImage *img) { - gdispImagePrivate *priv; - uint8_t hdr[2]; - uint16_t aword; - uint32_t adword; - uint32_t offsetColorTable; - - /* Read the file identifier */ - if (gfileRead(img->f, hdr, 2) != 2) - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - /* Process the BITMAPFILEHEADER structure */ - - /** - * We only accept Windows V2+ bitmaps. - * - we don't support OS/2 bitmaps, icons, pointers, or Windows V1 bitmaps. - */ - if (hdr[0] != 'B' || hdr[1] != 'M') - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - /* We know we are a BMP format image */ - img->flags = 0; - - /* Allocate our private area */ - if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) - return GDISP_IMAGE_ERR_NOMEMORY; - - /* Initialise the essential bits in the private area */ - priv = img->priv; - priv->frame0cache = 0; - priv->bmpflags = 0; -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - priv->palette = 0; -#endif - - /* Skip the size field and the 2 reserved fields */ - if (gfileRead(img->f, priv->buf, 8) != 8) - goto baddatacleanup; - - /* Get the offset to the bitmap data */ - if (gfileRead(img->f, &priv->frame0pos, 4) != 4) - goto baddatacleanup; - CONVERT_FROM_DWORD_LE(priv->frame0pos); - - /* Process the BITMAPCOREHEADER structure */ - - /* Get the offset to the colour data */ - if (gfileRead(img->f, &offsetColorTable, 4) != 4) - goto baddatacleanup; - CONVERT_FROM_DWORD_LE(offsetColorTable); - offsetColorTable += 14; // Add the size of the BITMAPFILEHEADER - - // Detect our bitmap version - if (offsetColorTable == 12+14) { - img->priv->bmpflags |= BMP_V2; - - // Read the header - if (gfileRead(img->f, priv->buf, 12-4) != 12-4) - goto baddatacleanup; - // Get the width - img->width = *(uint16_t *)(((uint8_t *)priv->buf)+0); - CONVERT_FROM_WORD_LE(img->width); - // Get the height - img->height = *(uint16_t *)(((uint8_t *)priv->buf)+2); - CONVERT_FROM_WORD_LE(img->height); - if (img->height < 0) { - img->priv->bmpflags |= BMP_TOP_TO_BOTTOM; - img->height = -img->height; - } - // Get the planes - aword = *(uint16_t *)(((uint8_t *)priv->buf)+4); - CONVERT_FROM_WORD_LE(aword); - if (aword != 1) - goto unsupportedcleanup; - // Get the bits per pixel - aword = *(uint16_t *)(((uint8_t *)priv->buf)+6); - CONVERT_FROM_WORD_LE(aword); - switch(aword) { -#if GDISP_NEED_IMAGE_BMP_1 - case 1: -#endif -#if GDISP_NEED_IMAGE_BMP_4 - case 4: -#endif -#if GDISP_NEED_IMAGE_BMP_8 - case 8: -#endif -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_8 - priv->bmpflags |= BMP_PALETTE; - priv->palsize = 1<bitsperpixel = aword; - - } else if (offsetColorTable >= 40+14) { - if (offsetColorTable > 40+14) - priv->bmpflags |= BMP_V4; - - // Read the header - if (gfileRead(img->f, priv->buf, 40-4) != 40-4) - goto baddatacleanup; - // Get the width - adword = *(uint32_t *)(((uint8_t *)priv->buf)+0); - CONVERT_FROM_DWORD_LE(adword); - if (adword > 32768) // This also picks up negative values - goto unsupportedcleanup; - img->width = adword; - // Get the height - adword = *(uint32_t *)(((uint8_t *)priv->buf)+4); - CONVERT_FROM_DWORD_LE(adword); - if ((int32_t)adword < 0) { // Negative test - priv->bmpflags |= BMP_TOP_TO_BOTTOM; - adword = -adword; - } - if (adword > 32768) - goto unsupportedcleanup; - img->height = adword; - // Get the planes - aword = *(uint16_t *)(((uint8_t *)priv->buf)+8); - CONVERT_FROM_WORD_LE(aword); - if (aword != 1) - goto unsupportedcleanup; - // Get the bits per pixel - aword = *(uint16_t *)(((uint8_t *)priv->buf)+10); - CONVERT_FROM_WORD_LE(aword); - switch(aword) { -#if GDISP_NEED_IMAGE_BMP_1 - case 1: -#endif -#if GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE - case 4: -#endif -#if GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - case 8: -#endif -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - priv->bmpflags |= BMP_PALETTE; - priv->palsize = 1<bitsperpixel = aword; - // Get the compression - adword = *(uint32_t *)(((uint8_t *)priv->buf)+12); - CONVERT_FROM_DWORD_LE(adword); - switch(adword) { - case 0: // BI_RGB - uncompressed - break; -#if GDISP_NEED_IMAGE_BMP_8_RLE - case 1: // BI_RLE8 compression - if (priv->bitsperpixel != 8) - goto unsupportedcleanup; - priv->bmpflags |= BMP_COMP_RLE; - break; -#endif -#if GDISP_NEED_IMAGE_BMP_4_RLE - case 2: // BI_RLE4 compression - if (priv->bitsperpixel != 4) - goto unsupportedcleanup; - priv->bmpflags |= BMP_COMP_RLE; - break; -#endif -#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 - case 3: // BI_BITFIELDS decoding - if (priv->bitsperpixel < 16 || priv->bitsperpixel == 24) - goto unsupportedcleanup; - priv->bmpflags |= BMP_COMP_MASK; - if (priv->bmpflags & BMP_V4) // V4 stored the masks in the header - offsetColorTable = 40+14; - break; -#endif - default: - goto unsupportedcleanup; - } - priv->bitsperpixel = aword; -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - // Get the actual colors used - adword = *(uint32_t *)(((uint8_t *)priv->buf)+28); - CONVERT_FROM_DWORD_LE(adword); - if (adword && adword < priv->palsize) - priv->palsize = adword; -#endif - } else - goto baddatacleanup; - -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - /* Load the palette tables */ - if (priv->bmpflags & BMP_PALETTE) { - gfileSetPos(img->f, offsetColorTable); - - if (!(priv->palette = (color_t *)gdispImageAlloc(img, priv->palsize*sizeof(color_t)))) - return GDISP_IMAGE_ERR_NOMEMORY; - if (priv->bmpflags & BMP_V2) { - for(aword = 0; aword < priv->palsize; aword++) { - if (gfileRead(img->f, &priv->buf, 3) != 3) goto baddatacleanup; - priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[2], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[0]); - } - } else { - for(aword = 0; aword < priv->palsize; aword++) { - if (gfileRead(img->f, &priv->buf, 4) != 4) goto baddatacleanup; - priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[2], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[0]); - } - } - - } -#endif - -#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 - /* Load the bit masks */ - if (priv->bmpflags & BMP_COMP_MASK) { - gfileSetPos(img->f, offsetColorTable); - if (gfileRead(img->f, &priv->maskred, 4) != 4) goto baddatacleanup; - CONVERT_FROM_DWORD_LE(priv->maskred); - if (gfileRead(img->f, &priv->maskgreen, 4) != 4) goto baddatacleanup; - CONVERT_FROM_DWORD_LE(priv->maskgreen); - if (gfileRead(img->f, &priv->maskblue, 4) != 4) goto baddatacleanup; - CONVERT_FROM_DWORD_LE(priv->maskblue); - if (priv->bmpflags & BMP_V4) { - if (gfileRead(img->f, &priv->maskalpha, 4) != 4) goto baddatacleanup; - CONVERT_FROM_DWORD_LE(priv->maskalpha); - } else - priv->maskalpha = 0; - } else if (priv->bitsperpixel == 16) { - priv->bmpflags |= BMP_COMP_MASK; - priv->maskred = 0x7C00; - priv->maskgreen = 0x03E0; - priv->maskblue = 0x001F; - priv->maskalpha = 0; - } else if (priv->bitsperpixel == 32) { - priv->bmpflags |= BMP_COMP_MASK; - priv->maskred = 0x00FF0000; - priv->maskgreen = 0x0000FF00; - priv->maskblue = 0x000000FF; - priv->maskalpha = 0; - } - - /* We need to adjust the masks and calculate the shift values so the result scales 0 -> 255 */ - if (priv->bmpflags & BMP_COMP_MASK) { - priv->shiftred = 0; - priv->shiftgreen = 0; - priv->shiftblue = 0; - if (priv->maskred) { - if (priv->maskred < 256) - for(adword = priv->maskred; adword < 128; priv->shiftred--, adword <<= 1); - else - for(adword = priv->maskred; adword > 255; priv->shiftred++, adword >>= 1); - } - if (priv->maskgreen) { - if (priv->maskgreen < 256) - for(adword = priv->maskgreen; adword < 128; priv->shiftgreen--, adword <<= 1); - else - for(adword = priv->maskgreen; adword > 255; priv->shiftgreen++, adword >>= 1); - } - if (priv->maskblue) { - if (priv->maskblue < 256) - for(adword = priv->maskblue; adword < 128; priv->shiftblue--, adword <<= 1); - else - for(adword = priv->maskblue; adword > 255; priv->shiftblue++, adword >>= 1); - } - if (priv->maskalpha) { - if (priv->maskalpha < 256) - for(adword = priv->maskalpha; adword < 128; priv->shiftalpha--, adword <<= 1); - else - for(adword = priv->maskalpha; adword > 255; priv->shiftalpha++, adword >>= 1); - } - } -#endif - - img->type = GDISP_IMAGE_TYPE_BMP; - return GDISP_IMAGE_ERR_OK; - -baddatacleanup: - gdispImageClose_BMP(img); // Clean up the private data area - return GDISP_IMAGE_ERR_BADDATA; // Oops - something wrong - -unsupportedcleanup: - gdispImageClose_BMP(img); // Clean up the private data area - return GDISP_IMAGE_ERR_UNSUPPORTED; // Not supported -} - -static coord_t getPixels(gdispImage *img, coord_t x) { - gdispImagePrivate * priv; - color_t * pc; - coord_t len; - - priv = img->priv; - pc = priv->buf; - len = 0; - - switch(priv->bitsperpixel) { -#if GDISP_NEED_IMAGE_BMP_1 - case 1: - { - uint8_t b[4]; - uint8_t m; - - priv = img->priv; - pc = priv->buf; - len = 0; - - while(x < img->width && len <= BLIT_BUFFER_SIZE-32) { - if (gfileRead(img->f, &b, 4) != 4) - return 0; - - for(m=0x80; m; m >>= 1, pc++) - pc[0] = priv->palette[(m&b[0]) ? 1 : 0]; - for(m=0x80; m; m >>= 1, pc++) - pc[0] = priv->palette[(m&b[1]) ? 1 : 0]; - for(m=0x80; m; m >>= 1, pc++) - pc[0] = priv->palette[(m&b[2]) ? 1 : 0]; - for(m=0x80; m; m >>= 1, pc++) - pc[0] = priv->palette[(m&b[3]) ? 1 : 0]; - len += 32; - x += 32; - } - } - return len; -#endif - -#if GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE - case 4: - #if GDISP_NEED_IMAGE_BMP_4_RLE - #if GDISP_NEED_IMAGE_BMP_4 - if (priv->bmpflags & BMP_COMP_RLE) - #endif - { - uint8_t b[4]; - - while(x < img->width) { - if (priv->bmpflags & BMP_RLE_ENC) { - while (priv->rlerun && len <= BLIT_BUFFER_SIZE-2 && x < img->width) { - *pc++ = priv->palette[priv->rlecode >> 4]; - priv->rlerun--; - len++; - x++; - if (priv->rlerun) { - *pc++ = priv->palette[priv->rlecode & 0x0F]; - priv->rlerun--; - len++; - x++; - } - } - if (priv->rlerun) // Return if we have more run to do - return len; - } else if (priv->bmpflags & BMP_RLE_ABS) { - while (priv->rlerun && len <= BLIT_BUFFER_SIZE-2 && x < img->width) { - if (gfileRead(img->f, &b, 1) != 1) - return 0; - *pc++ = priv->palette[b[0] >> 4]; - priv->rlerun--; - len++; - x++; - if (priv->rlerun) { - *pc++ = priv->palette[b[0] & 0x0F]; - priv->rlerun--; - len++; - x++; - } - } - if (priv->rlerun) // Return if we have more run to do - return len; - if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary - if (gfileRead(img->f, &b, 1) != 1) - return 0; - } - } - - // We have finished the current run - read a new run - priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS); - - // There are always at least 2 bytes in an RLE code - if (gfileRead(img->f, &b, 2) != 2) - return 0; - - if (b[0]) { // Encoded mode - priv->rlerun = b[0]; - priv->rlecode = b[1]; - priv->bmpflags |= BMP_RLE_ENC; - } else if (b[1] == 0) { // End of line - if (x < img->width) { - priv->rlerun = img->width - x; - priv->rlecode = 0; // Who knows what color this should really be - priv->bmpflags |= BMP_RLE_ENC; - } - } else if (b[1] == 1) { // End of file - return len; - } else if (b[1] == 2) { // Delta x, y - // There are always at least 2 bytes in an RLE code - if (gfileRead(img->f, &b, 2) != 2) - return 0; - priv->rlerun = b[0] + (uint16_t)b[1] * img->width; - priv->rlecode = 0; // Who knows what color this should really be - priv->bmpflags |= BMP_RLE_ENC; - } else { // Absolute mode - priv->rlerun = b[1]; - priv->bmpflags |= BMP_RLE_ABS; - } - } - return len; - } - #endif - #if GDISP_NEED_IMAGE_BMP_4 - { - uint8_t b[4]; - - while(x < img->width && len <= BLIT_BUFFER_SIZE-8) { - if (gfileRead(img->f, &b, 4) != 4) - return 0; - - *pc++ = priv->palette[b[0] >> 4]; - *pc++ = priv->palette[b[0] & 0x0F]; - *pc++ = priv->palette[b[1] >> 4]; - *pc++ = priv->palette[b[1] & 0x0F]; - *pc++ = priv->palette[b[2] >> 4]; - *pc++ = priv->palette[b[2] & 0x0F]; - *pc++ = priv->palette[b[3] >> 4]; - *pc++ = priv->palette[b[3] & 0x0F]; - len += 8; - x += 8; - } - return len; - } - #endif -#endif - -#if GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - case 8: - #if GDISP_NEED_IMAGE_BMP_8_RLE - #if GDISP_NEED_IMAGE_BMP_8 - if (priv->bmpflags & BMP_COMP_RLE) - #endif - { - uint8_t b[4]; - - while(x < img->width) { - if (priv->bmpflags & BMP_RLE_ENC) { - while (priv->rlerun && len < BLIT_BUFFER_SIZE && x < img->width) { - *pc++ = priv->palette[priv->rlecode]; - priv->rlerun--; - len++; - x++; - } - if (priv->rlerun) // Return if we have more run to do - return len; - } else if (priv->bmpflags & BMP_RLE_ABS) { - while (priv->rlerun && len < BLIT_BUFFER_SIZE && x < img->width) { - if (gfileRead(img->f, &b, 1) != 1) - return 0; - *pc++ = priv->palette[b[0]]; - priv->rlerun--; - len++; - x++; - } - if (priv->rlerun) // Return if we have more run to do - return len; - if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary - if (gfileRead(img->f, &b, 1) != 1) - return 0; - } - } - - // We have finished the current run - read a new run - priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS); - - // There are always at least 2 bytes in an RLE code - if (gfileRead(img->f, &b, 2) != 2) - return 0; - - if (b[0]) { // Encoded mode - priv->rlerun = b[0]; - priv->rlecode = b[1]; - priv->bmpflags |= BMP_RLE_ENC; - } else if (b[1] == 0) { // End of line - if (x < img->width) { - priv->rlerun = img->width - x; - priv->rlecode = 0; // Who knows what color this should really be - priv->bmpflags |= BMP_RLE_ENC; - } - } else if (b[1] == 1) { // End of file - return len; - } else if (b[1] == 2) { // Delta x, y - // There are always at least 2 bytes in an RLE code - if (gfileRead(img->f, &b, 2) != 2) - return GDISP_IMAGE_ERR_BADDATA; - priv->rlerun = b[0] + (uint16_t)b[1] * img->width; - priv->rlecode = 0; // Who knows what color this should really be - priv->bmpflags |= BMP_RLE_ENC; - } else { // Absolute mode - priv->rlerun = b[1]; - priv->bmpflags |= BMP_RLE_ABS; - } - } - return len; - } - #endif - #if GDISP_NEED_IMAGE_BMP_8 - { - uint8_t b[4]; - - while(x < img->width && len <= BLIT_BUFFER_SIZE-4) { - if (gfileRead(img->f, &b, 4) != 4) - return 0; - - *pc++ = priv->palette[b[0]]; - *pc++ = priv->palette[b[1]]; - *pc++ = priv->palette[b[2]]; - *pc++ = priv->palette[b[3]]; - len += 4; - x += 4; - } - return len; - } - #endif -#endif - -#if GDISP_NEED_IMAGE_BMP_16 - case 16: - { - uint16_t w[2]; - color_t r, g, b; - - while(x < img->width && len <= BLIT_BUFFER_SIZE-2) { - if (gfileRead(img->f, &w, 4) != 4) - return 0; - CONVERT_FROM_WORD_LE(w[0]); - CONVERT_FROM_WORD_LE(w[1]); - if (priv->shiftred < 0) - r = (color_t)((w[0] & priv->maskred) << -priv->shiftred); - else - r = (color_t)((w[0] & priv->maskred) >> priv->shiftred); - if (priv->shiftgreen < 0) - g = (color_t)((w[0] & priv->maskgreen) << -priv->shiftgreen); - else - g = (color_t)((w[0] & priv->maskgreen) >> priv->shiftgreen); - if (priv->shiftblue < 0) - b = (color_t)((w[0] & priv->maskblue) << -priv->shiftblue); - else - b = (color_t)((w[0] & priv->maskblue) >> priv->shiftblue); - /* We don't support alpha yet */ - *pc++ = RGB2COLOR(r, g, b); - if (priv->shiftred < 0) - r = (color_t)((w[1] & priv->maskred) << -priv->shiftred); - else - r = (color_t)((w[1] & priv->maskred) >> priv->shiftred); - if (priv->shiftgreen < 0) - g = (color_t)((w[1] & priv->maskgreen) << -priv->shiftgreen); - else - g = (color_t)((w[1] & priv->maskgreen) >> priv->shiftgreen); - if (priv->shiftblue < 0) - b = (color_t)((w[1] & priv->maskblue) << -priv->shiftblue); - else - b = (uint8_t)((w[1] & priv->maskblue) >> priv->shiftblue); - /* We don't support alpha yet */ - *pc++ = RGB2COLOR(r, g, b); - x += 2; - len += 2; - } - } - return len; -#endif - -#if GDISP_NEED_IMAGE_BMP_24 - case 24: - { - uint8_t b[3]; - - while(x < img->width && len < BLIT_BUFFER_SIZE) { - if (gfileRead(img->f, &b, 3) != 3) - return 0; - *pc++ = RGB2COLOR(b[2], b[1], b[0]); - x++; - len++; - } - - if (x >= img->width) { - // Make sure we have read a multiple of 4 bytes for the line - if ((x & 3) && gfileRead(img->f, &b, x & 3) != (x & 3)) - return 0; - } - } - return len; -#endif - -#if GDISP_NEED_IMAGE_BMP_32 - case 32: - { - uint32_t dw; - color_t r, g, b; - - while(x < img->width && len < BLIT_BUFFER_SIZE) { - if (gfileRead(img->f, &dw, 4) != 4) - return 0; - CONVERT_FROM_DWORD_LE(dw); - if (priv->shiftred < 0) - r = (color_t)((dw & priv->maskred) << -priv->shiftred); - else - r = (color_t)((dw & priv->maskred) >> priv->shiftred); - if (priv->shiftgreen < 0) - g = (color_t)((dw & priv->maskgreen) << -priv->shiftgreen); - else - g = (color_t)((dw & priv->maskgreen) >> priv->shiftgreen); - if (priv->shiftblue < 0) - b = (color_t)((dw & priv->maskblue) << -priv->shiftblue); - else - b = (color_t)((dw & priv->maskblue) >> priv->shiftblue); - /* We don't support alpha yet */ - *pc++ = RGB2COLOR(r, g, b); - x++; - len++; - } - } - return len; -#endif - - default: - return len; - } -} - -gdispImageError gdispImageCache_BMP(gdispImage *img) { - gdispImagePrivate * priv; - color_t * pcs; - color_t * pcd; - coord_t pos, x, y; - size_t len; - - /* If we are already cached - just return OK */ - priv = img->priv; - if (priv->frame0cache) - return GDISP_IMAGE_ERR_OK; - - /* We need to allocate the cache */ - len = img->width * img->height * sizeof(pixel_t); - priv->frame0cache = (pixel_t *)gdispImageAlloc(img, len); - if (!priv->frame0cache) - return GDISP_IMAGE_ERR_NOMEMORY; - - /* Read the entire bitmap into cache */ - gfileSetPos(img->f, priv->frame0pos); -#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE - priv->rlerun = 0; - priv->rlecode = 0; -#endif - - if (priv->bmpflags & BMP_TOP_TO_BOTTOM) { - for(y = 0, pcd = priv->frame0cache; y < img->height; y++) { - x = 0; pos = 0; - while(x < img->width) { - if (!pos) { - if (!(pos = getPixels(img, x))) - return GDISP_IMAGE_ERR_BADDATA; - pcs = priv->buf; - } - *pcd++ = *pcs++; - x++; pos--; - } - } - } else { - for(y = img->height-1, pcd = priv->frame0cache + img->width*(img->height-1); y >= 0; y--, pcd -= 2*img->width) { - x = 0; pos = 0; - while(x < img->width) { - if (!pos) { - if (!(pos = getPixels(img, x))) - return GDISP_IMAGE_ERR_BADDATA; - pcs = priv->buf; - } - *pcd++ = *pcs++; - x++; pos--; - } - } - } - - return GDISP_IMAGE_ERR_OK; -} - -gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { - gdispImagePrivate * priv; - coord_t mx, my; - coord_t pos, len, st; - - priv = img->priv; - - /* Check some reasonableness */ - if (sx >= img->width || sy >= img->height) return GDISP_IMAGE_ERR_OK; - if (sx + cx > img->width) cx = img->width - sx; - if (sy + cy > img->height) cy = img->height - sy; - - /* Draw from the image cache - if it exists */ - if (priv->frame0cache) { - gdispGBlitArea(g, x, y, cx, cy, sx, sy, img->width, priv->frame0cache); - return GDISP_IMAGE_ERR_OK; - } - - /* Start decoding from the beginning */ - gfileSetPos(img->f, priv->frame0pos); -#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE - priv->rlerun = 0; - priv->rlecode = 0; -#endif - - if (priv->bmpflags & BMP_TOP_TO_BOTTOM) { - for(my = 0; my < img->height; my++) { - mx = 0; - while(mx < img->width) { - if (!(pos = getPixels(img, mx))) - return GDISP_IMAGE_ERR_BADDATA; - if (my >= sy && my < sy+cy && mx < sx+cx && mx+pos >= sx) { - st = mx < sx ? sx - mx : 0; - len = pos-st; - if (mx+st+len > sx+cx) len = sx+cx-mx-st; - if (len == 1) - gdispGDrawPixel(g, x+mx+st-sx, y+my-sy, priv->buf[st]); - else - gdispGBlitArea(g, x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf); - } - mx += pos; - } - } - } else { - for(my = img->height-1; my >= 0; my--) { - mx = 0; - while(mx < img->width) { - if (!(pos = getPixels(img, mx))) - return GDISP_IMAGE_ERR_BADDATA; - if (my >= sy && my < sy+cy && mx < sx+cx && mx+pos >= sx) { - st = mx < sx ? sx - mx : 0; - len = pos-st; - if (mx+st+len > sx+cx) len = sx+cx-mx-st; - if (len == 1) - gdispGDrawPixel(g, x+mx+st-sx, y+my-sy, priv->buf[st]); - else - gdispGBlitArea(g, x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf); - } - mx += pos; - } - } - } - - return GDISP_IMAGE_ERR_OK; -} - -delaytime_t gdispImageNext_BMP(gdispImage *img) { - (void) img; - - /* No more frames/pages */ - return TIME_INFINITE; -} - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_BMP */ -/** @} */ diff --git a/src/gdisp/image_gif.c b/src/gdisp/image_gif.c deleted file mode 100644 index 06f4ef6a..00000000 --- a/src/gdisp/image_gif.c +++ /dev/null @@ -1,1208 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/image_gif.c - * @brief GDISP native image code. - * - * @defgroup Image Image - * @ingroup GDISP -*/ -#include "gfx.h" - -#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_GIF - -/** - * Helper Routines Needed - */ -void *gdispImageAlloc(gdispImage *img, size_t sz); -void gdispImageFree(gdispImage *img, void *ptr, size_t sz); - -/** - * How big an array to allocate for blitting (in pixels) - * Bigger is faster but uses more RAM. - */ -#define BLIT_BUFFER_SIZE 32 - -/* - * Determining endianness as at compile time is not guaranteed or compiler portable. - * We use the best test we can. If we can't guarantee little endianness we do things the - * hard way. - */ -#define GUARANTEED_LITTLE_ENDIAN (!defined(SAFE_ENDIAN) && !defined(SAFE_ALIGNMENT) && (\ - (defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \ - || defined(__LITTLE_ENDIAN__) \ - || defined(__LITTLE_ENDIAN) \ - || defined(_LITTLE_ENDIAN) \ -/* || (1 == *(unsigned char *)&(const int){1})*/ \ - )) - - -/* This is a runtime test */ -static const uint8_t dwordOrder[4] = { 1, 2, 3, 4 }; - -#define isWordLittleEndian() (*(uint16_t *)&dwordOrder == 0x0201) -#define isDWordLittleEndian() (*(uint32_t *)&dwordOrder == 0x04030201) - -#if GUARANTEED_LITTLE_ENDIAN - /* These are fast routines for guaranteed little endian machines */ - #define CONVERT_FROM_WORD_LE(w) - #define CONVERT_FROM_DWORD_LE(dw) -#else - /* These are slower routines for when little endianness cannot be guaranteed at compile time */ - #define CONVERT_FROM_WORD_LE(w) { if (!isWordLittleEndian()) w = ((((uint16_t)(w))>>8)|(((uint16_t)(w))<<8)); } - #define CONVERT_FROM_DWORD_LE(dw) { if (!isDWordLittleEndian()) dw = (((uint32_t)(((const uint8_t *)(&dw))[0]))|(((uint32_t)(((const uint8_t *)(&dw))[1]))<<8)|(((uint32_t)(((const uint8_t *)(&dw))[2]))<<16)|(((uint32_t)(((const uint8_t *)(&dw))[3]))<<24)); } -#endif - -// We need a special error to indicate the end of file (which may not actually be an error) -#define GDISP_IMAGE_EOF ((gdispImageError)-1) -#define GDISP_IMAGE_LOOP ((gdispImageError)-2) - -#define MAX_CODE_BITS 12 -#define CODE_MAX ((1<priv; - - // We need the decode ram, and possibly a palette - if (!(decode = (imgdecode *)gdispImageAlloc(img, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)))) - return GDISP_IMAGE_ERR_NOMEMORY; - - // We currently have not read any image data block - decode->blocksz = 0; - - // Set the palette - if (priv->frame.palsize) { - // Local palette - decode->maxpixel = priv->frame.palsize-1; - decode->palette = (color_t *)(decode+1); - gfileSetPos(img->f, priv->frame.pospal); - for(cnt = 0; cnt < priv->frame.palsize; cnt++) { - if (gfileRead(img->f, &decode->buf, 3) != 3) - goto baddatacleanup; - decode->palette[cnt] = RGB2COLOR(decode->buf[0], decode->buf[1], decode->buf[2]); - } - } else if (priv->palette) { - // Global palette - decode->maxpixel = priv->palsize-1; - decode->palette = priv->palette; - } else { - // Oops - we must have a palette - goto baddatacleanup; - } - - // Get the initial lzw code size and values - gfileSetPos(img->f, priv->frame.posimg); - if (gfileRead(img->f, &decode->bitsperpixel, 1) != 1 || decode->bitsperpixel >= MAX_CODE_BITS) - goto baddatacleanup; - decode->code_clear = 1 << decode->bitsperpixel; - decode->code_eof = decode->code_clear + 1; - decode->code_max = decode->code_clear + 2; - decode->code_last = CODE_NONE; - decode->bitspercode = decode->bitsperpixel+1; - decode->maxcodesz = 1 << decode->bitspercode; - decode->shiftbits = 0; - decode->shiftdata = 0; - decode->stackcnt = 0; - for(cnt = 0; cnt <= CODE_MAX; cnt++) - decode->prefix[cnt] = CODE_NONE; - - // All ready to go - priv->decode = decode; - return GDISP_IMAGE_ERR_OK; - -baddatacleanup: - gdispImageFree(img, decode, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)); - return GDISP_IMAGE_ERR_BADDATA; -} - -/** - * Stop decoding a frame. - * - * Pre: Frame info has been read. - */ -static void stopDecode(gdispImage *img) { - gdispImagePrivate * priv; - - priv = img->priv; - - // Free the decode data - if (priv->decode) { - gdispImageFree(img, (void *)priv->decode, sizeof(imgdecode)+priv->frame.palsize*sizeof(color_t)); - priv->decode = 0; - } -} - -static uint16_t getPrefix(imgdecode *decode, uint16_t code) { - uint16_t i; - - for(i=0; code > decode->code_clear && i <= CODE_MAX; i++, code = decode->prefix[code]) { - if (code > CODE_MAX) - return CODE_NONE; - } - return code; -} - -/** - * Decode some pixels from a frame. - * - * Pre: We are ready for decoding. - * - * Return: The number of pixels decoded 0 .. BLIT_BUFFER_SIZE-1. 0 means EOF - * - * Note: The resulting pixels are stored in decode->buf - */ -static uint16_t getbytes(gdispImage *img) { - gdispImagePrivate * priv; - imgdecode * decode; - uint16_t cnt; - uint16_t code, prefix; - uint8_t bdata; - - priv = img->priv; - decode = priv->decode; - cnt = 0; - - // At EOF - if (decode->code_last == decode->code_eof) - return 0; - - while(cnt < sizeof(decode->buf)) { - // Use the stack up first - if (decode->stackcnt > 0) { - decode->buf[cnt++] = decode->stack[--decode->stackcnt]; - continue; - } - - // Get another code - a code is made up of decode->bitspercode bits. - while (decode->shiftbits < decode->bitspercode) { - // Get a byte - we may have to start a new data block - if ((!decode->blocksz && (gfileRead(img->f, &decode->blocksz, 1) != 1 || !decode->blocksz)) - || gfileRead(img->f, &bdata, 1) != 1) { - // Pretend we got the EOF code - some encoders seem to just end the file - decode->code_last = decode->code_eof; - return cnt; - } - decode->blocksz--; - - decode->shiftdata |= ((unsigned long)bdata) << decode->shiftbits; - decode->shiftbits += 8; - } - code = decode->shiftdata & BitMask[decode->bitspercode]; - decode->shiftdata >>= decode->bitspercode; - decode->shiftbits -= decode->bitspercode; - /** - * If code cannot fit into bitspercode bits we must raise its size. - * Note that codes above CODE_MAX are used for special signaling. - * If we're using MAX_CODE_BITS bits already and we're at the max code, just - * keep using the table as it is, don't increment decode->bitspercode. - */ - if (decode->code_max < CODE_MAX + 2 && ++decode->code_max > decode->maxcodesz && decode->bitspercode < MAX_CODE_BITS) { - decode->maxcodesz <<= 1; - decode->bitspercode++; - } - - // EOF - the appropriate way to stop decoding - if (code == decode->code_eof) { - // Skip to the end of the data blocks - do { - gfileSetPos(img->f, gfileGetPos(img->f)+decode->blocksz); - } while (gfileRead(img->f, &decode->blocksz, 1) == 1 && decode->blocksz); - - // Mark the end - decode->code_last = decode->code_eof; - break; - } - - if (code == decode->code_clear) { - // Start again - for(prefix = 0; prefix <= CODE_MAX; prefix++) - decode->prefix[prefix] = CODE_NONE; - decode->code_max = decode->code_eof + 1; - decode->bitspercode = decode->bitsperpixel + 1; - decode->maxcodesz = 1 << decode->bitspercode; - decode->code_last = CODE_NONE; - continue; - } - - if (code < decode->code_clear) { - // Simple unencoded pixel - add it - decode->buf[cnt++] = code; - - } else { - /** - * Its a LZW code - trace the linked list until the prefix is a - * valid pixel while pushing the suffix pixels on the stack. - * If done, pop the stack in reverse order adding the pixels - */ - if (decode->prefix[code] != CODE_NONE) - prefix = code; - - /** - * Only allowed if the code equals the partial code. - * In that case code = XXXCode, CrntCode or the - * prefix code is last code and the suffix char is - * exactly the prefix of last code! - */ - else if (code == decode->code_max - 2 && decode->stackcnt < sizeof(decode->stack)) { - prefix = decode->code_last; - decode->suffix[decode->code_max - 2] = decode->stack[decode->stackcnt++] = getPrefix(decode, decode->code_last); - } else - return 0; - - /** - * If the image is OK we should not get a CODE_NONE while tracing. - * To prevent looping with a bad image we use StackPtr as loop counter - * and stop before overflowing Stack[]. - */ - while (decode->stackcnt < sizeof(decode->stack) && prefix > decode->code_clear && prefix <= CODE_MAX) { - decode->stack[decode->stackcnt++] = decode->suffix[prefix]; - prefix = decode->prefix[prefix]; - } - if (decode->stackcnt >= sizeof(decode->stack) || prefix > CODE_MAX) - return 0; - - /* Push the last character on stack: */ - decode->stack[decode->stackcnt++] = prefix; - } - - if (decode->code_last != CODE_NONE && decode->prefix[decode->code_max - 2] == CODE_NONE) { - decode->prefix[decode->code_max - 2] = decode->code_last; - - /* Only allowed if code is exactly the running code: - * In that case code = XXXCode, CrntCode or the - * prefix code is last code and the suffix char is - * exactly the prefix of last code! */ - decode->suffix[decode->code_max - 2] = getPrefix(decode, code == decode->code_max - 2 ? decode->code_last : code); - } - decode->code_last = code; - } - return cnt; -} - -/** - * Read the info on a frame. - * - * Pre: The file position is at the start of the frame. - */ -static gdispImageError initFrame(gdispImage *img) { - gdispImagePrivate * priv; - imgcache * cache; - uint8_t blocktype; - uint8_t blocksz; - - priv = img->priv; - - // Save the dispose info from the existing frame - priv->dispose.flags = priv->frame.flags; - priv->dispose.paltrans = priv->frame.paltrans; - priv->dispose.x = priv->frame.x; - priv->dispose.y = priv->frame.y; - priv->dispose.width = priv->frame.width; - priv->dispose.height = priv->frame.height; - - // Check for a cached version of this image - for(cache=priv->cache; cache && cache->frame.posstart <= (size_t)gfileGetPos(img->f); cache=cache->next) { - if (cache->frame.posstart == (size_t)gfileGetPos(img->f)) { - priv->frame = cache->frame; - priv->curcache = cache; - return GDISP_IMAGE_ERR_OK; - } - } - - // Get ready for a new image - priv->curcache = 0; - priv->frame.posstart = gfileGetPos(img->f); - priv->frame.flags = 0; - priv->frame.delay = 0; - priv->frame.palsize = 0; - - // Process blocks until we reach the image descriptor - while(1) { - if (gfileRead(img->f, &blocktype, 1) != 1) - return GDISP_IMAGE_ERR_BADDATA; - - switch(blocktype) { - case 0x2C: //',' - IMAGE_DESC_RECORD_TYPE; - // Read the Image Descriptor - if (gfileRead(img->f, priv->buf, 9) != 9) - return GDISP_IMAGE_ERR_BADDATA; - priv->frame.x = *(uint16_t *)(((uint8_t *)priv->buf)+0); - CONVERT_FROM_WORD_LE(priv->frame.x); - priv->frame.y = *(uint16_t *)(((uint8_t *)priv->buf)+2); - CONVERT_FROM_WORD_LE(priv->frame.y); - priv->frame.width = *(uint16_t *)(((uint8_t *)priv->buf)+4); - CONVERT_FROM_WORD_LE(priv->frame.width); - priv->frame.height = *(uint16_t *)(((uint8_t *)priv->buf)+6); - CONVERT_FROM_WORD_LE(priv->frame.height); - if (((uint8_t *)priv->buf)[8] & 0x80) // Local color table? - priv->frame.palsize = 2 << (((uint8_t *)priv->buf)[8] & 0x07); - if (((uint8_t *)priv->buf)[8] & 0x40) // Interlaced? - priv->frame.flags |= GIFL_INTERLACE; - - // We are ready to go for the actual palette read and image decode - priv->frame.pospal = gfileGetPos(img->f); - priv->frame.posimg = priv->frame.pospal+priv->frame.palsize*3; - priv->frame.posend = 0; - - // Mark this as an animated image if more than 1 frame. - if (priv->frame.posstart != priv->frame0pos) - img->flags |= GDISP_IMAGE_FLG_ANIMATED; - return GDISP_IMAGE_ERR_OK; - - case 0x21: //'!' - EXTENSION_RECORD_TYPE; - // Read the extension type - if (gfileRead(img->f, &blocktype, 1) != 1) - return GDISP_IMAGE_ERR_BADDATA; - - switch(blocktype) { - case 0xF9: // EXTENSION - Graphics Control Block - // Read the GCB - if (gfileRead(img->f, priv->buf, 6) != 6) - return GDISP_IMAGE_ERR_BADDATA; - // Check we have read a 4 byte data block and a data block terminator (0) - if (((uint8_t *)priv->buf)[0] != 4 || ((uint8_t *)priv->buf)[5] != 0) - return GDISP_IMAGE_ERR_BADDATA; - // Process the flags - switch(((uint8_t *)priv->buf)[1] & 0x1C) { - case 0x00: case 0x04: break; // Dispose = do nothing - case 0x08: priv->frame.flags |= GIFL_DISPOSECLEAR; break; // Dispose = clear - case 0x0C: case 0x10: priv->frame.flags |= GIFL_DISPOSEREST; break; // Dispose = restore. Value 0x10 is a hack for bad encoders - default: return GDISP_IMAGE_ERR_UNSUPPORTED; - } - if (((uint8_t *)priv->buf)[1] & 0x01) { - priv->frame.flags |= GIFL_TRANSPARENT; - img->flags |= GDISP_IMAGE_FLG_TRANSPARENT; // We set this but never clear it - } - if (((uint8_t *)priv->buf)[1] & 0x02) // Wait for user input? - img->flags |= GDISP_IMAGE_FLG_MULTIPAGE; - else - img->flags &= ~GDISP_IMAGE_FLG_MULTIPAGE; - // Process frame delay and the transparent color (if any) - priv->frame.delay = *(uint16_t *)(((uint8_t *)priv->buf)+2); - CONVERT_FROM_WORD_LE(priv->frame.delay); - priv->frame.paltrans = ((uint8_t *)priv->buf)[4]; - break; - - case 0xFF: // EXTENSION - Application - // We only handle this for the special Netscape loop counter for animation - if (priv->flags & GIF_LOOP) - goto skipdatablocks; - // Read the Application header - if (gfileRead(img->f, priv->buf, 16) != 16) - return GDISP_IMAGE_ERR_BADDATA; - // Check we have read a 11 byte data block - if (((uint8_t *)priv->buf)[0] != 11 && ((uint8_t *)priv->buf)[12] != 3) - return GDISP_IMAGE_ERR_BADDATA; - // Check the vendor - if (((uint8_t *)priv->buf)[1] == 'N' && ((uint8_t *)priv->buf)[2] == 'E' && ((uint8_t *)priv->buf)[3] == 'T' - && ((uint8_t *)priv->buf)[4] == 'S' && ((uint8_t *)priv->buf)[5] == 'C' && ((uint8_t *)priv->buf)[6] == 'A' - && ((uint8_t *)priv->buf)[7] == 'P' && ((uint8_t *)priv->buf)[8] == 'E' && ((uint8_t *)priv->buf)[9] == '2' - && ((uint8_t *)priv->buf)[10] == '.' && ((uint8_t *)priv->buf)[11] == '0') { - if (((uint8_t *)priv->buf)[13] == 1) { - priv->loops = *(uint16_t *)(((uint8_t *)priv->buf)+14); - CONVERT_FROM_WORD_LE(priv->loops); - priv->flags |= GIF_LOOP; - if (!priv->loops) - priv->flags |= GIF_LOOPFOREVER; - } - } - goto skipdatablocks; - - case 0x01: // EXTENSION - Plain Text (Graphics Rendering) - case 0xFE: // EXTENSION - Comment - default: - // 0x00-0x7F (0-127) are the Graphic Rendering blocks - if (blocktype <= 0x7F) - return GDISP_IMAGE_ERR_UNSUPPORTED; - // 0x80-0xF9 (128-249) are the Control blocks - // 0xFA-0xFF (250-255) are the Special Purpose blocks - // We don't understand this extension - just skip it by skipping data blocks - skipdatablocks: - while(1) { - if (gfileRead(img->f, &blocksz, 1) != 1) - return GDISP_IMAGE_ERR_BADDATA; - if (!blocksz) - break; - gfileSetPos(img->f, gfileGetPos(img->f) + blocksz); - } - break; - } - break; - - case 0x3B: //';' - TERMINATE_RECORD_TYPE; - // Are we an looping animation - if (!(priv->flags & GIF_LOOP)) - return GDISP_IMAGE_EOF; - if (!(priv->flags & GIF_LOOPFOREVER)) { - if (!priv->loops) - return GDISP_IMAGE_EOF; - priv->loops--; - } - - // Seek back to frame0 - gfileSetPos(img->f, priv->frame0pos); - return GDISP_IMAGE_LOOP; - - default: // UNDEFINED_RECORD_TYPE; - return GDISP_IMAGE_ERR_UNSUPPORTED; - } - } -} - -void gdispImageClose_GIF(gdispImage *img) { - gdispImagePrivate * priv; - imgcache * cache; - imgcache * ncache; - - priv = img->priv; - if (priv) { - // Free any stored frames - cache = priv->cache; - while(cache) { - ncache = cache->next; - gdispImageFree(img, (void *)cache, sizeof(imgcache)+cache->frame.width*cache->frame.height+cache->frame.palsize*sizeof(color_t)); - cache = ncache; - } - if (priv->palette) - gdispImageFree(img, (void *)priv->palette, priv->palsize*sizeof(color_t)); - gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); - img->priv = 0; - } -} - -gdispImageError gdispImageOpen_GIF(gdispImage *img) { - gdispImagePrivate *priv; - uint8_t hdr[6]; - uint16_t aword; - - /* Read the file identifier */ - if (gfileRead(img->f, hdr, 6) != 6) - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - /* Process the GIFFILEHEADER structure */ - - if (hdr[0] != 'G' || hdr[1] != 'I' || hdr[2] != 'F' - || hdr[3] != '8' || (hdr[4] != '7' && hdr[4] != '9') || hdr[5] != 'a') - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - /* We know we are a GIF format image */ - img->flags = 0; - - /* Allocate our private area */ - if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) - return GDISP_IMAGE_ERR_NOMEMORY; - - /* Initialise the essential bits in the private area */ - priv = img->priv; - priv->flags = 0; - priv->palsize = 0; - priv->palette = 0; - priv->frame.flags = 0; - priv->cache = 0; - priv->curcache = 0; - - /* Process the Screen Descriptor structure */ - - // Read the screen descriptor - if (gfileRead(img->f, priv->buf, 7) != 7) - goto baddatacleanup; - // Get the width - img->width = *(uint16_t *)(((uint8_t *)priv->buf)+0); - CONVERT_FROM_WORD_LE(img->width); - // Get the height - img->height = *(uint16_t *)(((uint8_t *)priv->buf)+2); - CONVERT_FROM_WORD_LE(img->height); - if (((uint8_t *)priv->buf)[4] & 0x80) { - // Global color table - priv->palsize = 2 << (((uint8_t *)priv->buf)[4] & 0x07); - // Allocate the global palette - if (!(priv->palette = (color_t *)gdispImageAlloc(img, priv->palsize*sizeof(color_t)))) - goto nomemcleanup; - // Read the global palette - for(aword = 0; aword < priv->palsize; aword++) { - if (gfileRead(img->f, &priv->buf, 3) != 3) - goto baddatacleanup; - priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[0], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[2]); - } - } - priv->bgcolor = ((uint8_t *)priv->buf)[5]; - - // Save the fram0pos - priv->frame0pos = gfileGetPos(img->f); - - // Read the first frame descriptor - switch(initFrame(img)) { - case GDISP_IMAGE_ERR_OK: // Everything OK - img->type = GDISP_IMAGE_TYPE_GIF; - return GDISP_IMAGE_ERR_OK; - case GDISP_IMAGE_ERR_UNSUPPORTED: // Unsupported - gdispImageClose_GIF(img); // Clean up the private data area - return GDISP_IMAGE_ERR_UNSUPPORTED; - case GDISP_IMAGE_ERR_NOMEMORY: // Out of Memory - nomemcleanup: - gdispImageClose_GIF(img); // Clean up the private data area - return GDISP_IMAGE_ERR_NOMEMORY; - case GDISP_IMAGE_EOF: // We should have a frame but we don't seem to - case GDISP_IMAGE_LOOP: // We should have a frame but we don't seem to - case GDISP_IMAGE_ERR_BADDATA: // Oops - something wrong with the data - default: - baddatacleanup: - gdispImageClose_GIF(img); // Clean up the private data area - return GDISP_IMAGE_ERR_BADDATA; - } -} - -gdispImageError gdispImageCache_GIF(gdispImage *img) { - gdispImagePrivate * priv; - imgcache * cache; - imgdecode * decode; - uint8_t * p; - uint8_t * q; - coord_t mx, my; - uint16_t cnt; - - /* If we are already cached - just return OK */ - priv = img->priv; - if (priv->curcache) - return GDISP_IMAGE_ERR_OK; - - /* We need to allocate the frame, the palette and bits for the image */ - if (!(cache = (imgcache *)gdispImageAlloc(img, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height))) - return GDISP_IMAGE_ERR_NOMEMORY; - - /* Initialise the cache */ - decode = 0; - cache->frame = priv->frame; - cache->imagebits = (uint8_t *)(cache+1) + cache->frame.palsize*sizeof(color_t); - cache->next = 0; - - /* Start the decode */ - switch(startDecode(img)) { - case GDISP_IMAGE_ERR_OK: break; - case GDISP_IMAGE_ERR_NOMEMORY: goto nomemcleanup; - case GDISP_IMAGE_ERR_BADDATA: - default: goto baddatacleanup; - } - decode = priv->decode; - - // Save the palette - if (cache->frame.palsize) { - cache->palette = (color_t *)(cache+1); - - /* Copy the local palette into the cache */ - for(cnt = 0; cnt < cache->frame.palsize; cnt++) - cache->palette[cnt] = decode->palette[cnt]; - } else - cache->palette = priv->palette; - - // Check for interlacing - cnt = 0; - if (cache->frame.flags & GIFL_INTERLACE) { - // Every 8th row starting at row 0 - for(p=cache->imagebits, my=0; my < cache->frame.height; my+=8, p += cache->frame.width*7) { - for(mx=0; mx < cache->frame.width; mx++) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - while(cnt < sizeof(decode->buf)) - decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; - } - q = decode->buf; - } - *p++ = *q++; - cnt--; - } - } - // Every 8th row starting at row 4 - for(p=cache->imagebits+cache->frame.width*4, my=4; my < cache->frame.height; my+=8, p += cache->frame.width*7) { - for(mx=0; mx < cache->frame.width; mx++) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - while(cnt < sizeof(decode->buf)) - decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; - } - q = decode->buf; - } - *p++ = *q++; - cnt--; - } - } - // Every 4th row starting at row 2 - for(p=cache->imagebits+cache->frame.width*2, my=2; my < cache->frame.height; my+=4, p += cache->frame.width*3) { - for(mx=0; mx < cache->frame.width; mx++) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - while(cnt < sizeof(decode->buf)) - decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; - } - q = decode->buf; - } - *p++ = *q++; - cnt--; - } - } - // Every 2nd row starting at row 1 - for(p=cache->imagebits+cache->frame.width, my=1; my < cache->frame.height; my+=2, p += cache->frame.width) { - for(mx=0; mx < cache->frame.width; mx++) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - while(cnt < sizeof(decode->buf)) - decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; - } - q = decode->buf; - } - *p++ = *q++; - cnt--; - } - } - } else { - // Every row in sequence - p=cache->imagebits; - for(my=0; my < cache->frame.height; my++) { - for(mx=0; mx < cache->frame.width; mx++) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - while(cnt < sizeof(decode->buf)) - decode->buf[cnt++] = (cache->frame.flags & GIFL_TRANSPARENT) ? cache->frame.paltrans : 0; - } - q = decode->buf; - } - *p++ = *q++; - cnt--; - } - } - } - // We could be pedantic here but extra bytes won't hurt us - while(getbytes(img)); - priv->frame.posend = cache->frame.posend = gfileGetPos(img->f); - - // Save everything - priv->curcache = cache; - if (!priv->cache) - priv->cache = cache; - else if (priv->cache->frame.posstart > cache->frame.posstart) { - cache->next = priv->cache; - priv->cache = cache; - } else { - imgcache *pc; - - for(pc = priv->cache; pc; pc = pc->next) { - if (!pc->next || pc->next->frame.posstart > cache->frame.posstart) { - cache->next = pc->next; - pc->next = cache; - break; - } - } - } - stopDecode(img); - return GDISP_IMAGE_ERR_OK; - -nomemcleanup: - stopDecode(img); - gdispImageFree(img, cache, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height); - return GDISP_IMAGE_ERR_NOMEMORY; - -baddatacleanup: - stopDecode(img); - gdispImageFree(img, cache, sizeof(imgcache) + priv->frame.palsize*sizeof(color_t) + priv->frame.width*priv->frame.height); - return GDISP_IMAGE_ERR_BADDATA; -} - -gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { - gdispImagePrivate * priv; - imgdecode * decode; - uint8_t * q = 0; - coord_t mx, my, fx, fy; - uint16_t cnt, gcnt; - uint8_t col; - - priv = img->priv; - - /* Handle previous frame disposing */ - if (priv->dispose.flags & (GIFL_DISPOSECLEAR|GIFL_DISPOSEREST)) { - // Clip to the disposal area - clip area = mx,my -> fx, fy (sx,sy,cx,cy are unchanged) - mx = priv->dispose.x; - my = priv->dispose.y; - fx = priv->dispose.x+priv->dispose.width; - fy = priv->dispose.y+priv->dispose.height; - if (sx > mx) mx = sx; - if (sy > my) my = sy; - if (sx+cx <= fx) fx = sx+cx; - if (sy+cy <= fy) fy = sy+cy; - if (fx > mx && fy > my) { - // We only support clearing (not restoring). The specification says that we are allowed to do this. - // Calculate the bgcolor - // The spec says to restore the backgound color (priv->bgcolor) but in practice if there is transparency - // image decoders tend to assume that a restore to the transparent color is required instead - if (((priv->dispose.flags & GIFL_TRANSPARENT) /*&& priv->dispose.paltrans == priv->bgcolor*/) || priv->bgcolor >= priv->palsize) - gdispGFillArea(g, x+mx-sx, y+my-sy, fx-mx, fy-my, img->bgcolor); - else - gdispGFillArea(g, x+mx-sx, y+my-sy, fx-mx, fy-my, priv->palette[priv->bgcolor]); - } - } - - /* Clip to just this frame - clip area = sx,sy -> fx, fy */ - fx = priv->frame.x+priv->frame.width; - fy = priv->frame.y+priv->frame.height; - if (sx >= fx || sy >= fy || sx+cx < priv->frame.x || sy+cy < priv->frame.y) return GDISP_IMAGE_ERR_OK; - if (sx < priv->frame.x) { mx = priv->frame.x - sx; x += mx; cx -= mx; sx = priv->frame.x; } - if (sy < priv->frame.y) { my = priv->frame.y - sy; y += my; cy -= my; sy = priv->frame.y; } - if (sx+cx > fx) cx = fx-sx; - if (sy+cy > fy) cy = fy-sy; - - // Make sx, sy relative to this frame so we are not adding priv->frame.x & priv->frame.y each time - sx -= priv->frame.x; sy -= priv->frame.y; - fx = sx + cx; - fy = sy + cy; - - /* Draw from the image cache - if it exists */ - if (priv->curcache) { - imgcache * cache; - - cache = priv->curcache; - q = cache->imagebits+priv->frame.width*sy+sx; - - for(my=sy; my < fy; my++, q += priv->frame.width - cx) { - for(gcnt=0, mx=sx, cnt=0; mx < fx; mx++) { - col = *q++; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = cache->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - - return GDISP_IMAGE_ERR_OK; - } - - /* Start the decode */ - switch(startDecode(img)) { - case GDISP_IMAGE_ERR_OK: break; - case GDISP_IMAGE_ERR_NOMEMORY: return GDISP_IMAGE_ERR_NOMEMORY; - case GDISP_IMAGE_ERR_BADDATA: - default: return GDISP_IMAGE_ERR_BADDATA; - } - decode = priv->decode; - - // Check for interlacing - cnt = 0; - if (priv->frame.flags & GIFL_INTERLACE) { - // Every 8th row starting at row 0 - for(my=0; my < priv->frame.height; my+=8) { - for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - mx++; - break; - } - q = decode->buf; - } - if (my >= sy && my < fy && mx >= sx && mx < fx) { - col = *q; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = decode->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - continue; - } - // We have finished the visible area - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - // Every 8th row starting at row 4 - for(my=4; my < priv->frame.height; my+=8) { - for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - mx++; - break; - } - q = decode->buf; - } - if (my >= sy && my < fy && mx >= sx && mx < fx) { - col = *q; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = decode->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - continue; - } - // We have finished the visible area - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - // Every 4th row starting at row 2 - for(my=2; my < priv->frame.height; my+=4) { - for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - mx++; - break; - } - q = decode->buf; - } - if (my >= sy && my < fy && mx >= sx && mx < fx) { - col = *q; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = decode->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - continue; - } - // We have finished the visible area - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - // Every 2nd row starting at row 1 - for(my=1; my < priv->frame.height; my+=2) { - for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - mx++; - break; - } - q = decode->buf; - } - if (my >= sy && my < fy && mx >= sx && mx < fx) { - col = *q; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = decode->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - continue; - } - // We have finished the visible area - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - } else { - // Every row in sequence - for(my=0; my < priv->frame.height; my++) { - for(gcnt=0, mx=0; mx < priv->frame.width; mx++, q++, cnt--) { - if (!cnt) { - if (!(cnt = getbytes(img))) { - // Sometimes the image EOF is a bit early - treat the rest as transparent - if (decode->code_last != decode->code_eof) - goto baddatacleanup; - mx++; - break; - } - q = decode->buf; - } - if (my >= sy && my < fy && mx >= sx && mx < fx) { - col = *q; - if ((priv->frame.flags & GIFL_TRANSPARENT) && col == priv->frame.paltrans) { - // We have a transparent pixel - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - continue; - } - priv->buf[gcnt++] = decode->palette[col]; - if (gcnt >= BLIT_BUFFER_SIZE) { - // We have run out of buffer - dump it to the display - gdispGBlitArea(g, x+mx-sx-gcnt+1, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); - gcnt = 0; - } - continue; - } - // We have finished the visible area - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); gcnt = 0; break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); gcnt = 0; break; - } - } - // We have finished the line - dump the buffer to the display - switch(gcnt) { - case 0: break; - case 1: gdispGDrawPixel(g, x+mx-sx-gcnt, y+my-sy, priv->buf[0]); break; - default: gdispGBlitArea(g, x+mx-sx-gcnt, y+my-sy, gcnt, 1, 0, 0, gcnt, priv->buf); break; - } - } - } - // We could be pedantic here but extra bytes won't hurt us - while (getbytes(img)); - priv->frame.posend = gfileGetPos(img->f); - - stopDecode(img); - return GDISP_IMAGE_ERR_OK; - -baddatacleanup: - stopDecode(img); - return GDISP_IMAGE_ERR_BADDATA; -} - -delaytime_t gdispImageNext_GIF(gdispImage *img) { - gdispImagePrivate * priv; - delaytime_t delay; - uint8_t blocksz; - - priv = img->priv; - - // Save the delay and convert to millisecs - delay = (delaytime_t)priv->frame.delay * 10; - - // We need to get to the end of this frame - if (!priv->frame.posend) { - // We don't know where the end of the frame is yet - find it! - gfileSetPos(img->f, priv->frame.posimg+1); // Skip the code size byte too - while(1) { - if (gfileRead(img->f, &blocksz, 1) != 1) - return TIME_INFINITE; - if (!blocksz) - break; - gfileSetPos(img->f, gfileGetPos(img->f) + blocksz); - } - priv->frame.posend = gfileGetPos(img->f); - } - - // Seek to the end of this frame - gfileSetPos(img->f, priv->frame.posend); - - // Read the next frame descriptor - for(blocksz=0; blocksz < 2; blocksz++) { // 2 loops max to prevent cycling forever with a bad file - switch(initFrame(img)) { - case GDISP_IMAGE_ERR_OK: // Everything OK - return delay; - case GDISP_IMAGE_LOOP: // Back to the beginning - break; - case GDISP_IMAGE_EOF: // The real End-Of-File - case GDISP_IMAGE_ERR_BADDATA: // Oops - something wrong with the data - case GDISP_IMAGE_ERR_NOMEMORY: // Out of Memory - case GDISP_IMAGE_ERR_UNSUPPORTED: // Unsupported - default: - return TIME_INFINITE; - } - } - return TIME_INFINITE; -} - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_GIF */ -/** @} */ diff --git a/src/gdisp/image_jpg.c b/src/gdisp/image_jpg.c deleted file mode 100644 index 2f6a9392..00000000 --- a/src/gdisp/image_jpg.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/image_jpg.c - * @brief GDISP native image code. - */ -#include "gfx.h" - -#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG - -#error "JPG support not implemented yet" - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_JPG */ -/** @} */ diff --git a/src/gdisp/image_native.c b/src/gdisp/image_native.c deleted file mode 100644 index 81344642..00000000 --- a/src/gdisp/image_native.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/image_native.c - * @brief GDISP native image code. - */ -#include "gfx.h" - -#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_NATIVE - -/** - * How big a pixel array to allocate for blitting - * Bigger is faster but uses more RAM. - */ -#define BLIT_BUFFER_SIZE 32 - -#define HEADER_SIZE 8 -#define FRAME0POS (HEADER_SIZE) - -/** - * Helper Routines Needed - */ -void *gdispImageAlloc(gdispImage *img, size_t sz); -void gdispImageFree(gdispImage *img, void *ptr, size_t sz); - -typedef struct gdispImagePrivate { - pixel_t *frame0cache; - pixel_t buf[BLIT_BUFFER_SIZE]; - } gdispImagePrivate; - -void gdispImageClose_NATIVE(gdispImage *img) { - if (img->priv) { - if (img->priv->frame0cache) - gdispImageFree(img, (void *)img->priv->frame0cache, img->width * img->height * sizeof(pixel_t)); - gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); - img->priv = 0; - } -} - -gdispImageError gdispImageOpen_NATIVE(gdispImage *img) { - uint8_t hdr[HEADER_SIZE]; - - /* Read the 8 byte header */ - if (gfileRead(img->f, hdr, 8) != 8) - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - if (hdr[0] != 'N' || hdr[1] != 'I') - return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us - - if (hdr[6] != GDISP_PIXELFORMAT/256 || hdr[7] != (GDISP_PIXELFORMAT & 0xFF)) - return GDISP_IMAGE_ERR_UNSUPPORTED; // Unsupported pixel format - - /* We know we are a native format image */ - img->flags = 0; - img->width = (((uint16_t)hdr[2])<<8) | (hdr[3]); - img->height = (((uint16_t)hdr[4])<<8) | (hdr[5]); - if (img->width < 1 || img->height < 1) - return GDISP_IMAGE_ERR_BADDATA; - if (!(img->priv = (gdispImagePrivate *)gdispImageAlloc(img, sizeof(gdispImagePrivate)))) - return GDISP_IMAGE_ERR_NOMEMORY; - img->priv->frame0cache = 0; - - img->type = GDISP_IMAGE_TYPE_NATIVE; - return GDISP_IMAGE_ERR_OK; -} - -gdispImageError gdispImageCache_NATIVE(gdispImage *img) { - size_t len; - - /* If we are already cached - just return OK */ - if (img->priv->frame0cache) - return GDISP_IMAGE_ERR_OK; - - /* We need to allocate the cache */ - len = img->width * img->height * sizeof(pixel_t); - img->priv->frame0cache = (pixel_t *)gdispImageAlloc(img, len); - if (!img->priv->frame0cache) - return GDISP_IMAGE_ERR_NOMEMORY; - - /* Read the entire bitmap into cache */ - gfileSetPos(img->f, FRAME0POS); - if (gfileRead(img->f, img->priv->frame0cache, len) != len) - return GDISP_IMAGE_ERR_BADDATA; - - return GDISP_IMAGE_ERR_OK; -} - -gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy) { - coord_t mx, mcx; - size_t pos, len; - - /* Check some reasonableness */ - if (sx >= img->width || sy >= img->height) return GDISP_IMAGE_ERR_OK; - if (sx + cx > img->width) cx = img->width - sx; - if (sy + cy > img->height) cy = img->height - sy; - - /* Draw from the image cache - if it exists */ - if (img->priv->frame0cache) { - gdispGBlitArea(g, x, y, cx, cy, sx, sy, img->width, img->priv->frame0cache); - return GDISP_IMAGE_ERR_OK; - } - - /* For this image decoder we cheat and just seek straight to the region we want to display */ - pos = FRAME0POS + (img->width * sy + sx) * sizeof(pixel_t); - - /* Cycle through the lines */ - for(;cy;cy--, y++) { - /* Move to the start of the line */ - gfileSetPos(img->f, pos); - - /* Draw the line in chunks using BitBlt */ - for(mx = x, mcx = cx; mcx > 0; mcx -= len, mx += len) { - // Read the data - len = gfileRead(img->f, - img->priv->buf, - mcx > BLIT_BUFFER_SIZE ? (BLIT_BUFFER_SIZE*sizeof(pixel_t)) : (mcx * sizeof(pixel_t))) - / sizeof(pixel_t); - if (!len) - return GDISP_IMAGE_ERR_BADDATA; - - /* Blit the chunk of data */ - gdispGBlitArea(g, mx, y, len, 1, 0, 0, len, img->priv->buf); - } - - /* Get the position for the start of the next line */ - pos += img->width*sizeof(pixel_t); - } - - return GDISP_IMAGE_ERR_OK; -} - -delaytime_t gdispImageNext_NATIVE(gdispImage *img) { - (void) img; - - /* No more frames/pages */ - return TIME_INFINITE; -} - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_NATIVE */ -/** @} */ diff --git a/src/gdisp/image_png.c b/src/gdisp/image_png.c deleted file mode 100644 index f35174d9..00000000 --- a/src/gdisp/image_png.c +++ /dev/null @@ -1,19 +0,0 @@ -/* - * This file is subject to the terms of the GFX License. If a copy of - * the license was not distributed with this file, you can obtain one at: - * - * http://ugfx.org/license.html - */ - -/** - * @file src/gdisp/image_png.c - * @brief GDISP native image code. - */ -#include "gfx.h" - -#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_PNG - -#error "PNG support not implemented yet" - -#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_PNG */ -/** @} */ diff --git a/src/gdisp/sys_defs.h b/src/gdisp/sys_defs.h index 72fdc621..253de49e 100644 --- a/src/gdisp/sys_defs.h +++ b/src/gdisp/sys_defs.h @@ -185,7 +185,7 @@ extern GDisplay *GDISP; /*===========================================================================*/ /* Load our color definitions and pixel formats */ -#include "colors.h" +#include "gdisp_colors.h" /** * @brief The type of a pixel. @@ -995,7 +995,7 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co #endif #if GDISP_NEED_IMAGE || defined(__DOXYGEN__) - #include "src/gdisp/image.h" + #include "gdisp_image.h" #endif #endif /* GFX_USE_GDISP */ diff --git a/src/gdisp/sys_make.mk b/src/gdisp/sys_make.mk index a133ce37..cbce4224 100644 --- a/src/gdisp/sys_make.mk +++ b/src/gdisp/sys_make.mk @@ -1,11 +1,11 @@ -GFXSRC += $(GFXLIB)/src/gdisp/gdisp.c \ - $(GFXLIB)/src/gdisp/fonts.c \ - $(GFXLIB)/src/gdisp/image.c \ - $(GFXLIB)/src/gdisp/image_native.c \ - $(GFXLIB)/src/gdisp/image_gif.c \ - $(GFXLIB)/src/gdisp/image_bmp.c \ - $(GFXLIB)/src/gdisp/image_jpg.c \ - $(GFXLIB)/src/gdisp/image_png.c +GFXSRC += $(GFXLIB)/src/gdisp/gdisp_gdisp.c \ + $(GFXLIB)/src/gdisp/gdisp_fonts.c \ + $(GFXLIB)/src/gdisp/gdisp_image.c \ + $(GFXLIB)/src/gdisp/gdisp_image_native.c \ + $(GFXLIB)/src/gdisp/gdisp_image_gif.c \ + $(GFXLIB)/src/gdisp/gdisp_image_bmp.c \ + $(GFXLIB)/src/gdisp/gdisp_image_jpg.c \ + $(GFXLIB)/src/gdisp/gdisp_image_png.c MFDIR = $(GFXLIB)/src/gdisp/mcufont include $(GFXLIB)/src/gdisp/mcufont/mcufont.mk -- cgit v1.2.3