diff --git a/demos/modules/gdisp/gdisp_pictures/fruits1.h b/demos/modules/gdisp/gdisp_compiled_pictures/fruits1.h similarity index 100% rename from demos/modules/gdisp/gdisp_pictures/fruits1.h rename to demos/modules/gdisp/gdisp_compiled_pictures/fruits1.h diff --git a/demos/modules/gdisp/gdisp_pictures/fruits1.jpg b/demos/modules/gdisp/gdisp_compiled_pictures/fruits1.jpg similarity index 100% rename from demos/modules/gdisp/gdisp_pictures/fruits1.jpg rename to demos/modules/gdisp/gdisp_compiled_pictures/fruits1.jpg diff --git a/demos/modules/gdisp/gdisp_pictures/gfxconf.h b/demos/modules/gdisp/gdisp_compiled_pictures/gfxconf.h similarity index 96% rename from demos/modules/gdisp/gdisp_pictures/gfxconf.h rename to demos/modules/gdisp/gdisp_compiled_pictures/gfxconf.h index b60fd1cb..464c2675 100644 --- a/demos/modules/gdisp/gdisp_pictures/gfxconf.h +++ b/demos/modules/gdisp/gdisp_compiled_pictures/gfxconf.h @@ -1,43 +1,43 @@ -/** - * This file has a different license to the rest of the GFX system. - * You can copy, modify and distribute this file as you see fit. - * You do not need to publish your source modifications to this file. - * The only thing you are not permitted to do is to relicense it - * under a different license. - */ - -#ifndef _GFXCONF_H -#define _GFXCONF_H - -/* GFX sub-systems to turn on */ -#define GFX_USE_GDISP TRUE -#define GFX_USE_GWIN FALSE -#define GFX_USE_GEVENT FALSE -#define GFX_USE_GTIMER FALSE -#define GFX_USE_GINPUT FALSE - -/* Features for the GDISP sub-system. */ -#define GDISP_NEED_VALIDATION TRUE -#define GDISP_NEED_CLIP TRUE -#define GDISP_NEED_TEXT TRUE -#define GDISP_NEED_CIRCLE TRUE -#define GDISP_NEED_ELLIPSE FALSE -#define GDISP_NEED_ARC FALSE -#define GDISP_NEED_SCROLL FALSE -#define GDISP_NEED_PIXELREAD FALSE -#define GDISP_NEED_CONTROL TRUE -#define GDISP_NEED_MULTITHREAD TRUE -#define GDISP_NEED_ASYNC FALSE -#define GDISP_NEED_MSGAPI FALSE - -/* Builtin Fonts */ -#define GDISP_INCLUDE_FONT_SMALL FALSE -#define GDISP_INCLUDE_FONT_LARGER FALSE -#define GDISP_INCLUDE_FONT_UI1 FALSE -#define GDISP_INCLUDE_FONT_UI2 TRUE -#define GDISP_INCLUDE_FONT_LARGENUMBERS TRUE - -/* Features for the GINPUT sub-system. */ -#define GINPUT_NEED_MOUSE FALSE - -#endif /* _GFXCONF_H */ +/** + * This file has a different license to the rest of the GFX system. + * You can copy, modify and distribute this file as you see fit. + * You do not need to publish your source modifications to this file. + * The only thing you are not permitted to do is to relicense it + * under a different license. + */ + +#ifndef _GFXCONF_H +#define _GFXCONF_H + +/* GFX sub-systems to turn on */ +#define GFX_USE_GDISP TRUE +#define GFX_USE_GWIN FALSE +#define GFX_USE_GEVENT FALSE +#define GFX_USE_GTIMER FALSE +#define GFX_USE_GINPUT FALSE + +/* Features for the GDISP sub-system. */ +#define GDISP_NEED_VALIDATION TRUE +#define GDISP_NEED_CLIP TRUE +#define GDISP_NEED_TEXT TRUE +#define GDISP_NEED_CIRCLE TRUE +#define GDISP_NEED_ELLIPSE FALSE +#define GDISP_NEED_ARC FALSE +#define GDISP_NEED_SCROLL FALSE +#define GDISP_NEED_PIXELREAD FALSE +#define GDISP_NEED_CONTROL TRUE +#define GDISP_NEED_MULTITHREAD TRUE +#define GDISP_NEED_ASYNC FALSE +#define GDISP_NEED_MSGAPI FALSE + +/* Builtin Fonts */ +#define GDISP_INCLUDE_FONT_SMALL FALSE +#define GDISP_INCLUDE_FONT_LARGER FALSE +#define GDISP_INCLUDE_FONT_UI1 FALSE +#define GDISP_INCLUDE_FONT_UI2 TRUE +#define GDISP_INCLUDE_FONT_LARGENUMBERS TRUE + +/* Features for the GINPUT sub-system. */ +#define GINPUT_NEED_MOUSE FALSE + +#endif /* _GFXCONF_H */ diff --git a/demos/modules/gdisp/gdisp_pictures/main.c b/demos/modules/gdisp/gdisp_compiled_pictures/main.c similarity index 96% rename from demos/modules/gdisp/gdisp_pictures/main.c rename to demos/modules/gdisp/gdisp_compiled_pictures/main.c index 9eae3319..b437619e 100644 --- a/demos/modules/gdisp/gdisp_pictures/main.c +++ b/demos/modules/gdisp/gdisp_compiled_pictures/main.c @@ -1,26 +1,26 @@ -#include "ch.h" -#include "hal.h" -#include "gfx.h" -#include "matterhorn1.h" -#include "matterhorn2.h" -#include "fruits1.h" - -int main(void) { - halInit(); - chSysInit(); - - gdispInit(); - gdispSetOrientation(GDISP_ROTATE_90); - - while(1) { - gdispBlitArea(0, 0, fruits1.width, fruits1.height, (const pixel_t*)fruits1.pixel_data); - chThdSleepMilliseconds(3000); - - gdispBlitArea(0, 0, matterhorn1.width, matterhorn1.height, (const pixel_t*)matterhorn1.pixel_data); - chThdSleepMilliseconds(3000); - - gdispBlitArea(0, 0, matterhorn2.width, matterhorn2.height, (const pixel_t*)matterhorn2.pixel_data); - chThdSleepMilliseconds(3000); - } -} - +#include "ch.h" +#include "hal.h" +#include "gfx.h" +#include "matterhorn1.h" +#include "matterhorn2.h" +#include "fruits1.h" + +int main(void) { + halInit(); + chSysInit(); + + gdispInit(); + gdispSetOrientation(GDISP_ROTATE_90); + + while(1) { + gdispBlitArea(0, 0, fruits1.width, fruits1.height, (const pixel_t*)fruits1.pixel_data); + chThdSleepMilliseconds(3000); + + gdispBlitArea(0, 0, matterhorn1.width, matterhorn1.height, (const pixel_t*)matterhorn1.pixel_data); + chThdSleepMilliseconds(3000); + + gdispBlitArea(0, 0, matterhorn2.width, matterhorn2.height, (const pixel_t*)matterhorn2.pixel_data); + chThdSleepMilliseconds(3000); + } +} + diff --git a/demos/modules/gdisp/gdisp_pictures/matterhorn.jpg b/demos/modules/gdisp/gdisp_compiled_pictures/matterhorn.jpg similarity index 100% rename from demos/modules/gdisp/gdisp_pictures/matterhorn.jpg rename to demos/modules/gdisp/gdisp_compiled_pictures/matterhorn.jpg diff --git a/demos/modules/gdisp/gdisp_pictures/matterhorn1.h b/demos/modules/gdisp/gdisp_compiled_pictures/matterhorn1.h similarity index 100% rename from demos/modules/gdisp/gdisp_pictures/matterhorn1.h rename to demos/modules/gdisp/gdisp_compiled_pictures/matterhorn1.h diff --git a/demos/modules/gdisp/gdisp_pictures/matterhorn2.h b/demos/modules/gdisp/gdisp_compiled_pictures/matterhorn2.h similarity index 100% rename from demos/modules/gdisp/gdisp_pictures/matterhorn2.h rename to demos/modules/gdisp/gdisp_compiled_pictures/matterhorn2.h diff --git a/demos/modules/gdisp/gdisp_pictures/matterhorn2.jpg b/demos/modules/gdisp/gdisp_compiled_pictures/matterhorn2.jpg similarity index 100% rename from demos/modules/gdisp/gdisp_pictures/matterhorn2.jpg rename to demos/modules/gdisp/gdisp_compiled_pictures/matterhorn2.jpg diff --git a/demos/modules/gdisp/gdisp_images/gfxconf.h b/demos/modules/gdisp/gdisp_images/gfxconf.h new file mode 100644 index 00000000..77e70f36 --- /dev/null +++ b/demos/modules/gdisp/gdisp_images/gfxconf.h @@ -0,0 +1,56 @@ +/** + * This file has a different license to the rest of the GFX system. + * You can copy, modify and distribute this file as you see fit. + * You do not need to publish your source modifications to this file. + * The only thing you are not permitted to do is to relicense it + * under a different license. + */ + +#ifndef _GFXCONF_H +#define _GFXCONF_H + +/* GFX sub-systems to turn on */ +#define GFX_USE_GDISP TRUE +#define GFX_USE_GWIN FALSE +#define GFX_USE_GEVENT FALSE +#define GFX_USE_GTIMER FALSE +#define GFX_USE_GINPUT FALSE + +/* Features for the GDISP sub-system. */ +#define GDISP_NEED_VALIDATION TRUE +#define GDISP_NEED_CLIP TRUE +#define GDISP_NEED_TEXT FALSE +#define GDISP_NEED_CIRCLE FALSE +#define GDISP_NEED_ELLIPSE FALSE +#define GDISP_NEED_ARC FALSE +#define GDISP_NEED_CONVEX_POLYGON FALSE +#define GDISP_NEED_SCROLL FALSE +#define GDISP_NEED_PIXELREAD FALSE +#define GDISP_NEED_CONTROL FALSE +#define GDISP_NEED_IMAGE TRUE +#define GDISP_NEED_MULTITHREAD FALSE +#define GDISP_NEED_ASYNC FALSE +#define GDISP_NEED_MSGAPI FALSE + +/* Builtin Fonts */ +#define GDISP_INCLUDE_FONT_SMALL FALSE +#define GDISP_INCLUDE_FONT_LARGER FALSE +#define GDISP_INCLUDE_FONT_UI1 FALSE +#define GDISP_INCLUDE_FONT_UI2 FALSE +#define GDISP_INCLUDE_FONT_LARGENUMBERS FALSE + +/* GDISP image decoders */ +#define GDISP_NEED_IMAGE_NATIVE FALSE +#define GDISP_NEED_IMAGE_GIF FALSE +#define GDISP_NEED_IMAGE_BMP TRUE +#define GDISP_NEED_IMAGE_JPG FALSE +#define GDISP_NEED_IMAGE_PNG FALSE + +/* Features for the GWIN sub-system. */ +#define GWIN_NEED_BUTTON FALSE +#define GWIN_NEED_CONSOLE FALSE + +/* Features for the GINPUT sub-system. */ +#define GINPUT_NEED_MOUSE FALSE + +#endif /* _GFXCONF_H */ diff --git a/demos/modules/gdisp/gdisp_images/main.c b/demos/modules/gdisp/gdisp_images/main.c new file mode 100644 index 00000000..6ca5f640 --- /dev/null +++ b/demos/modules/gdisp/gdisp_images/main.c @@ -0,0 +1,52 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "ch.h" +#include "hal.h" +#include "gfx.h" + +static gdispImage myImage; + +int main(void) { + coord_t swidth, sheight; + + halInit(); // Initialise the Hardware + chSysInit(); // Initialize the OS + gdispInit(); // Initialize the display + + gdispClear(Black); + + // Get the display dimensions + swidth = gdispGetWidth(); + sheight = gdispGetHeight(); + + // Set up IO for our image + gdispImageSetSimulFileReader(&myImage, "test-pal8.bmp"); + + gdispImageOpen(&myImage); + gdispImageDraw(&myImage, 0, 0, swidth, sheight, 0, 0); + gdispImageClose(&myImage); + + while(1) { + chThdSleepMilliseconds(1000); + } + + return 0; +} diff --git a/demos/modules/gdisp/gdisp_images/results_650x493.png b/demos/modules/gdisp/gdisp_images/results_650x493.png new file mode 100644 index 00000000..66dcd6e3 Binary files /dev/null and b/demos/modules/gdisp/gdisp_images/results_650x493.png differ diff --git a/demos/modules/gdisp/gdisp_images/test-pal8.bmp b/demos/modules/gdisp/gdisp_images/test-pal8.bmp new file mode 100644 index 00000000..96b2f866 Binary files /dev/null and b/demos/modules/gdisp/gdisp_images/test-pal8.bmp differ diff --git a/gfxconf.example.h b/gfxconf.example.h index 9db7d1b5..c9a91f2d 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -38,16 +38,36 @@ #define GDISP_NEED_PIXELREAD FALSE #define GDISP_NEED_CONTROL FALSE #define GDISP_NEED_QUERY FALSE +#define GDISP_NEED_IMAGE FALSE #define GDISP_NEED_MULTITHREAD FALSE #define GDISP_NEED_ASYNC FALSE #define GDISP_NEED_MSGAPI FALSE /* GDISP - builtin fonts */ -#define GDISP_INCLUDE_FONT_SMALL TRUE -#define GDISP_INCLUDE_FONT_LARGER TRUE -#define GDISP_INCLUDE_FONT_UI1 TRUE +#define GDISP_INCLUDE_FONT_SMALL FALSE +#define GDISP_INCLUDE_FONT_LARGER FALSE +#define GDISP_INCLUDE_FONT_UI1 FALSE #define GDISP_INCLUDE_FONT_UI2 TRUE -#define GDISP_INCLUDE_FONT_LARGENUMBERS TRUE +#define GDISP_INCLUDE_FONT_LARGENUMBERS FALSE + +/* GDISP image decoders */ +#define GDISP_NEED_IMAGE_NATIVE FALSE +#define GDISP_NEED_IMAGE_GIF FALSE +#define GDISP_NEED_IMAGE_BMP FALSE +#define GDISP_NEED_IMAGE_JPG FALSE +#define GDISP_NEED_IMAGE_PNG FALSE + +/* Optional image support that can be turned off */ +/* + #define GDISP_NEED_IMAGE_BMP_1 TRUE + #define GDISP_NEED_IMAGE_BMP_4 TRUE + #define GDISP_NEED_IMAGE_BMP_4_RLE TRUE + #define GDISP_NEED_IMAGE_BMP_8 TRUE + #define GDISP_NEED_IMAGE_BMP_8_RLE TRUE + #define GDISP_NEED_IMAGE_BMP_16 TRUE + #define GDISP_NEED_IMAGE_BMP_24 TRUE + #define GDISP_NEED_IMAGE_BMP_32 TRUE +*/ /* Features for the TDISP subsystem. */ #define TDISP_NEED_MULTITHREAD FALSE diff --git a/include/gdisp/gdisp.h b/include/gdisp/gdisp.h index 9e408578..85f9b4ea 100644 --- a/include/gdisp/gdisp.h +++ b/include/gdisp/gdisp.h @@ -966,6 +966,10 @@ void gdispDrawBox(coord_t x, coord_t y, coord_t cx, coord_t cy, color_t color); } #endif +#if GDISP_NEED_IMAGE || defined(__DOXYGEN__) + #include "gdisp/image.h" +#endif + #endif /* GFX_USE_GDISP */ #endif /* _GDISP_H */ diff --git a/include/gdisp/image.h b/include/gdisp/image.h new file mode 100644 index 00000000..8e71437b --- /dev/null +++ b/include/gdisp/image.h @@ -0,0 +1,355 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file include/gdisp/image.h + * @brief GDISP image header file. + * + * @addtogroup 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) + +/** + * @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 */ + coord_t width, height; /* @< The image dimensions */ + gdispImageIO io; /* @< The image IO functions */ + uint32_t membytes; /* @< How much RAM has been allocated */ + const struct gdispImageHandlers * fns; /* @< Don't mess with this! */ + struct gdispImagePrivate * priv; /* @< Don't mess with this! */ +} gdispImage; + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @brief Sets the io fields in the image structure to routines + * that support reading from an image stored in RAM or Flash. + * + * @return TRUE if the IO open function succeeds + * + * @param[in] img The image structure + * @param[in] memimage A pointer to the image in RAM or Flash + * + * @note Always returns TRUE for a Memory Reader + */ + bool_t gdispImageSetMemoryReader(gdispImage *img, const char *memimage); + + /** + * @brief Sets the io fields in the image structure to routines + * that support reading from an image stored on a BaseFileStream (eg SDCard). + * + * @return TRUE if the IO open function succeeds + * + * @param[in] img The image structure + * @param[in] BaseFileStreamPtr A pointer to the (open) BaseFileStream object. + * + */ + bool_t gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr); + + #if defined(WIN32) || defined(__DOXYGEN__) + /** + * @brief Sets the io fields in the image structure to routines + * that support reading from an image stored in Win32 simulators native + * file system. + * @pre Only available on the Win32 simulator + * + * @return TRUE if the IO open function succeeds + * + * @param[in] img The image structure + * @param[in] filename The filename to open + * + */ + bool_t gdispImageSetSimulFileReader(gdispImage *img, const char *filename); + #endif + + /** + * @brief Open an image 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 + * + * @pre The io fields should be filled in before calling gdispImageOpen() + * + * @note This determines which decoder to use and then initialises all other fields + * in the gdispImage structure. + * @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() can be called even after a failure to open the image to ensure + * that the IO close routine gets called. + */ + gdispImageError gdispImageOpen(gdispImage *img); + + /** + * @brief Close an image and release any dynamicly allocated working storage. + * + * @param[in] img The image structure + * + * @pre gdispImageOpen() must have returned successfully. + * + * @note Also calls the IO close function (if it hasn't already been called). + */ + void gdispImageClose(gdispImage *img); + + /** + * @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] 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 gdispImageDraw(gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t 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. + */ + systime_t gdispImageNext(gdispImage *img); + + #if GDISP_NEED_IMAGE_NATIVE + /** + * @brief The image drawing routines for a NATIVE format image. + * + * @note Only use these functions if you absolutely know the format + * of the image you are decoding. Generally you should use the + * generic functions and it will auto-detect the format. + * @note A NATIVE format image is defined as an 8 byte header described below, immediately + * followed by the bitmap data. The bitmap data is stored in the native format for + * the display controller. If the pixel format specified in the header does not + * match the controller native format then the image is rejected. + * @note The 8 byte header: + * { 'N', 'I', width.hi, width.lo, height.hi, height.lo, format.hi, format.lo } + * The format word = GDISP_PIXELFORMAT + * @{ + */ + gdispImageError gdispImageOpen_NATIVE(gdispImage *img); + void gdispImageClose_NATIVE(gdispImage *img); + gdispImageError gdispImageCache_NATIVE(gdispImage *img); + gdispImageError gdispImageDraw_NATIVE(gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + systime_t gdispImageNext_NATIVE(gdispImage *img); + /* @} */ + #endif + + #if GDISP_NEED_IMAGE_GIF + /** + * @brief The image drawing routines for a GIF image. + * @note Only use these functions if you absolutely know the format + * of the image you are decoding. Generally you should use the + * generic functions and it will auto-detect the format. + * @{ + */ + gdispImageError gdispImageOpen_GIF(gdispImage *img); + void gdispImageClose_GIF(gdispImage *img); + gdispImageError gdispImageCache_GIF(gdispImage *img); + gdispImageError gdispImageDraw_GIF(gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + systime_t gdispImageNext_GIF(gdispImage *img); + /* @} */ + #endif + + #if GDISP_NEED_IMAGE_BMP + /** + * @brief The image drawing routines for a BMP image. + * @note Only use these functions if you absolutely know the format + * of the image you are decoding. Generally you should use the + * generic functions and it will auto-detect the format. + * @{ + */ + gdispImageError gdispImageOpen_BMP(gdispImage *img); + void gdispImageClose_BMP(gdispImage *img); + gdispImageError gdispImageCache_BMP(gdispImage *img); + gdispImageError gdispImageDraw_BMP(gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + systime_t gdispImageNext_BMP(gdispImage *img); + /* @} */ + #endif + + #if GDISP_NEED_IMAGE_JPG + /** + * @brief The image drawing routines for a JPG image. + * @note Only use these functions if you absolutely know the format + * of the image you are decoding. Generally you should use the + * generic functions and it will auto-detect the format. + * @{ + */ + gdispImageError gdispImageOpen_JPG(gdispImage *img); + void gdispImageClose_JPG(gdispImage *img); + gdispImageError gdispImageCache_JPG(gdispImage *img); + gdispImageError gdispImageDraw_JPG(gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + systime_t gdispImageNext_JPG(gdispImage *img); + /* @} */ + #endif + + #if GDISP_NEED_IMAGE_PNG + /** + * @brief The image drawing routines for a PNG image. + * @note Only use these functions if you absolutely know the format + * of the image you are decoding. Generally you should use the + * generic functions and it will auto-detect the format. + * @{ + */ + gdispImageError gdispImageOpen_PNG(gdispImage *img); + void gdispImageClose_PNG(gdispImage *img); + gdispImageError gdispImageCache_PNG(gdispImage *img); + gdispImageError gdispImageDraw_PNG(gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + systime_t gdispImageNext_PNG(gdispImage *img); + /* @} */ + #endif + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ +#endif /* _GDISP_IMAGE_H */ +/** @} */ + diff --git a/include/gdisp/options.h b/include/gdisp/options.h index 78e48a0a..deacc036 100644 --- a/include/gdisp/options.h +++ b/include/gdisp/options.h @@ -130,6 +130,13 @@ #ifndef GDISP_NEED_QUERY #define GDISP_NEED_QUERY FALSE #endif + /** + * @brief Is the image interface required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE + #define GDISP_NEED_IMAGE FALSE + #endif /** * @brief Is the messaging api interface required. * @details Defaults to FALSE @@ -137,6 +144,48 @@ #ifndef GDISP_NEED_MSGAPI #define GDISP_NEED_MSGAPI FALSE #endif +/** + * @} + * + * @name GDISP Image Options + * @pre GDISP_NEED_IMAGE must be TRUE + * @{ + */ + /** + * @brief Is native image decoding required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_NATIVE + #define GDISP_NEED_IMAGE_NATIVE FALSE + #endif + /** + * @brief Is GIF image decoding required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_GIF + #define GDISP_NEED_IMAGE_GIF FALSE + #endif + /** + * @brief Is BMP image decoding required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_BMP + #define GDISP_NEED_IMAGE_BMP FALSE + #endif + /** + * @brief Is JPG image decoding required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_JPG + #define GDISP_NEED_IMAGE_JPG FALSE + #endif + /** + * @brief Is PNG image decoding required. + * @details Defaults to FALSE + */ + #ifndef GDISP_NEED_IMAGE_PNG + #define GDISP_NEED_IMAGE_PNG FALSE + #endif /** * @} * diff --git a/src/gdisp/gdisp.mk b/src/gdisp/gdisp.mk index 426e9e2c..84570e9e 100644 --- a/src/gdisp/gdisp.mk +++ b/src/gdisp/gdisp.mk @@ -1,2 +1,9 @@ -GFXSRC += $(GFXLIB)/src/gdisp/gdisp.c \ - $(GFXLIB)/src/gdisp/fonts.c +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 + \ No newline at end of file diff --git a/src/gdisp/image.c b/src/gdisp/image.c new file mode 100644 index 00000000..616aab11 --- /dev/null +++ b/src/gdisp/image.c @@ -0,0 +1,210 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file src/gdisp/image.c + * @brief GDISP generic image code. + */ +#include "ch.h" +#include "hal.h" +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE + +/* 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)(gdispImage *img, + coord_t x, coord_t y, + coord_t cx, coord_t cy, + coord_t sx, coord_t sy); /* The draw function */ + systime_t (*next)(gdispImage *img); /* The next frame function */ +} gdispImageHandlers; + +static gdispImageHandlers ImageHandlers[] = { + #if GDISP_NEED_IMAGE_NATIVE + { gdispImageOpen_NATIVE, gdispImageClose_NATIVE, + gdispImageCache_NATIVE, gdispImageDraw_NATIVE, gdispImageNext_NATIVE, + }, + #endif + #if GDISP_NEED_IMAGE_GIF + { gdispImageOpen_GIF, gdispImageClose_GIF, + gdispImageCache_GIF, gdispImageDraw_GIF, gdispImageNext_GIF, + }, + #endif + #if GDISP_NEED_IMAGE_BMP + { gdispImageOpen_BMP, gdispImageClose_BMP, + gdispImageCache_BMP, gdispImageDraw_BMP, gdispImageNext_BMP, + }, + #endif + #if GDISP_NEED_IMAGE_JPG + { gdispImageOpen_JPG, gdispImageClose_JPG, + gdispImageCache_JPG, gdispImageDraw_JPG, gdispImageNext_JPG, + }, + #endif + #if GDISP_NEED_IMAGE_PNG + { gdispImageOpen_PNG, gdispImageClose_PNG, + gdispImageCache_PNG, gdispImageDraw_PNG, gdispImageNext_PNG, + }, + #endif +}; + +static size_t ImageMemoryRead(struct gdispImageIO *pio, void *buf, size_t len) { + if (pio->fd == (void *)-1) return 0; + memcpy(buf, ((const char *)pio->fd)+pio->pos, len); + pio->pos += len; + return len; +} + +static void ImageMemorySeek(struct gdispImageIO *pio, size_t pos) { + if (pio->fd == (void *)-1) return; + pio->pos = pos; +} + +static void ImageMemoryClose(struct gdispImageIO *pio) { + pio->fd = (void *)-1; + pio->pos = 0; +} + +static const gdispImageIOFunctions ImageMemoryFunctions = + { ImageMemoryRead, ImageMemorySeek, ImageMemoryClose }; + +bool_t gdispImageSetMemoryReader(gdispImage *img, const char *memimage) { + img->io.fns = &ImageMemoryFunctions; + img->io.pos = 0; + img->io.fd = (void *)memimage; + return TRUE; +} + +static size_t ImageBaseFileStreamRead(struct gdispImageIO *pio, void *buf, size_t len) { + if (pio->fd == (void *)-1) return 0; + len = chSequentialStreamRead(((BaseFileStream *)pio->fd), (uint8_t *)buf, len); + pio->pos += len; + return len; +} + +static void ImageBaseFileStreamSeek(struct gdispImageIO *pio, size_t pos) { + if (pio->fd == (void *)-1) return; + if (pio->pos != pos) { + chFileStreamSeek(((BaseFileStream *)pio->fd), pos); + pio->pos = pos; + } +} + +static void ImageBaseFileStreamClose(struct gdispImageIO *pio) { + if (pio->fd == (void *)-1) return; + chFileStreamClose(((BaseFileStream *)pio->fd)); + pio->fd = (void *)-1; + pio->pos = 0; +} + +static const gdispImageIOFunctions ImageBaseFileStreamFunctions = + { ImageBaseFileStreamRead, ImageBaseFileStreamSeek, ImageBaseFileStreamClose }; + +bool_t gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr) { + img->io.fns = &ImageBaseFileStreamFunctions; + img->io.pos = 0; + img->io.fd = BaseFileStreamPtr; + return TRUE; +} + +#if defined(WIN32) + #include + + static size_t ImageSimulFileRead(struct gdispImageIO *pio, void *buf, size_t len) { + if (pio->fd == (void *)-1) return 0; + len = read((int)pio->fd, buf, len); + if ((int)len < 0) len = 0; + pio->pos += len; + return len; + } + + static void ImageSimulFileSeek(struct gdispImageIO *pio, size_t pos) { + if (pio->fd == (void *)-1) return; + if (pio->pos != pos) { + lseek((int)pio->fd, pos, SEEK_SET); + pio->pos = pos; + } + } + + static void ImageSimulFileClose(struct gdispImageIO *pio) { + if (pio->fd == (void *)-1) return; + close((int)pio->fd); + pio->fd = (void *)-1; + pio->pos = 0; + } + + static const gdispImageIOFunctions ImageSimulFileFunctions = + { ImageSimulFileRead, ImageSimulFileSeek, ImageSimulFileClose }; + + bool_t gdispImageSetSimulFileReader(gdispImage *img, const char *filename) { + img->io.fns = &ImageSimulFileFunctions; + img->io.pos = 0; + img->io.fd = (void *)open(filename, O_RDONLY|O_BINARY); + return img->io.fd != (void *)-1; + } +#endif + +gdispImageError gdispImageOpen(gdispImage *img) { + gdispImageError err; + + 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)) + img->fns = 0; + return err; + } + img->io.fns->seek(&img->io, 0); + } + img->type = GDISP_IMAGE_TYPE_UNKNOWN; + img->flags = 0; + img->fns = 0; + img->priv = 0; + return GDISP_IMAGE_ERR_BADFORMAT; +} + +void gdispImageClose(gdispImage *img) { + if (img->fns) + img->fns->close(img); + else + img->io.fns->close(&img->io); +} + +gdispImageError gdispImageCache(gdispImage *img) { + if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; + return img->fns->cache(img); +} + +gdispImageError gdispImageDraw(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(img, x, y, cx, cy, sx, sy); +} + +systime_t gdispImageNext(gdispImage *img) { + if (!img->fns) return GDISP_IMAGE_ERR_BADFORMAT; + return img->fns->next(img); +} + + +#endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE */ +/** @} */ diff --git a/src/gdisp/image_bmp.c b/src/gdisp/image_bmp.c new file mode 100644 index 00000000..f286003e --- /dev/null +++ b/src/gdisp/image_bmp.c @@ -0,0 +1,912 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file src/gdisp/image_bmp.c + * @brief GDISP native image code. + */ +#include "ch.h" +#include "hal.h" +#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 FALSE // Currently Broken +#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 FALSE // Currently Broken +#endif +#ifndef GDISP_NEED_IMAGE_BMP_16 + #define GDISP_NEED_IMAGE_BMP_16 FALSE // Currently Broken +#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 + +/** + * 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 +#define CONVERT_FROM_WORD_BE(w) { if (isWordLittleEndian()) w = ((((uint16_t)(w))>>8)|(((uint16_t)(w))<<8)); } + +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; + +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 (img->io.fns->read(&img->io, 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 *)chHeapAlloc(NULL, sizeof(gdispImagePrivate)))) + return GDISP_IMAGE_ERR_NOMEMORY; + img->membytes = sizeof(gdispImagePrivate); + + /* 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 (img->io.fns->read(&img->io, priv->buf, 8) != 8) + goto baddatacleanup; + + /* Get the offset to the bitmap data */ + if (img->io.fns->read(&img->io, &priv->frame0pos, 4) != 4) + goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->frame0pos); + + /* Process the BITMAPCOREHEADER structure */ + + /* Get the offset to the colour data */ + if (img->io.fns->read(&img->io, &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 (img->io.fns->read(&img->io, 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 (img->io.fns->read(&img->io, 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) { + img->io.fns->seek(&img->io, offsetColorTable); + + if (!(priv->palette = (color_t *)chHeapAlloc(NULL, priv->palsize*sizeof(color_t)))) + return GDISP_IMAGE_ERR_NOMEMORY; + img->membytes += priv->palsize * sizeof(color_t); + if (priv->bmpflags & BMP_V2) { + for(aword = 0; aword < priv->palsize; aword++) { + if (img->io.fns->read(&img->io, &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 (img->io.fns->read(&img->io, &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) { + img->io.fns->seek(&img->io, offsetColorTable); + if (img->io.fns->read(&img->io, &priv->maskred, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskred); + if (img->io.fns->read(&img->io, &priv->maskgreen, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskgreen); + if (img->io.fns->read(&img->io, &priv->maskblue, 4) != 4) goto baddatacleanup; + CONVERT_FROM_DWORD_LE(priv->maskblue); + if (priv->bmpflags & BMP_V4) { + if (img->io.fns->read(&img->io, &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 = 0x001F0000; + priv->maskgreen = 0x07E00000; + priv->maskblue = 0xF8000000; + 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->bitsperpixel == 16) { + priv->maskred >>= 16; + priv->maskgreen >>= 16; + priv->maskblue >>= 16; + } + 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 + + 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 +} + +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) + chHeapFree((void *)img->priv->palette); +#endif + if (img->priv->frame0cache) + chHeapFree((void *)img->priv->frame0cache); + chHeapFree((void *)img->priv); + img->priv = 0; + } + img->membytes = 0; + img->io.fns->close(&img->io); +} + +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 (img->io.fns->read(&img->io, &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 (img->io.fns->read(&img->io, &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; + } + + // 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 (img->io.fns->read(&img->io, &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 (img->io.fns->read(&img->io, &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 (img->io.fns->read(&img->io, &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 (img->io.fns->read(&img->io, &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; + } + + // 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 (img->io.fns->read(&img->io, &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 (img->io.fns->read(&img->io, &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 (img->io.fns->read(&img->io, &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 (img->io.fns->read(&img->io, &w, 4) != 4) + return 0; + CONVERT_FROM_WORD_BE(w[0]); + CONVERT_FROM_WORD_BE(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 (img->io.fns->read(&img->io, &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) && img->io.fns->read(&img->io, &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 (img->io.fns->read(&img->io, &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 *)chHeapAlloc(NULL, len); + if (!priv->frame0cache) + return GDISP_IMAGE_ERR_NOMEMORY; + img->membytes += len; + + /* Read the entire bitmap into cache */ + img->io.fns->seek(&img->io, 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 gdispImageDraw_BMP(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) { + gdispBlitAreaEx(x, y, cx, cy, sx, sy, img->width, priv->frame0cache); + return GDISP_IMAGE_ERR_OK; + } + + /* Start decoding from the beginning */ + img->io.fns->seek(&img->io, 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) + gdispDrawPixel(x+mx+st-sx, y+my-sy, priv->buf[st]); + else + gdispBlitAreaEx(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) + gdispDrawPixel(x+mx+st-sx, y+my-sy, priv->buf[st]); + else + gdispBlitAreaEx(x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf); + } + mx += pos; + } + } + } + + return GDISP_IMAGE_ERR_OK; +} + +systime_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 new file mode 100644 index 00000000..b7a940ad --- /dev/null +++ b/src/gdisp/image_gif.c @@ -0,0 +1,47 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file src/gdisp/image_gif.c + * @brief GDISP native image code. + */ +#include "ch.h" +#include "hal.h" +#include "gfx.h" + +#if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_GIF + +#error "GIF support not implemented yet" + +/* A pallete structure */ +typedef struct gdispImagePallete { + uint8_t flags; + #define GDISP_IMAGE_FLG_INT_TRANSPARENT 0x01 + uint8_t idxtrans; /* The transparent idx */ + uint8_t maxidx; /* The maximum index (0..255) */ + uint8_t repidx; /* The index to use if the image data > maxidx */ + color_t pal[256]; /* The pallete entries - not all may actually be allocated */ +} gdispImagePallete; + +/* Draw a single palletized line (or partial line) */ +static void gdispDrawPalleteLine(const gdispImagePallete *pal, const uint8_t *line, coord_t x, coord_t y, coord_t cx); + +#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 new file mode 100644 index 00000000..e8b5eeeb --- /dev/null +++ b/src/gdisp/image_jpg.c @@ -0,0 +1,34 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file src/gdisp/image_jpg.c + * @brief GDISP native image code. + */ +#include "ch.h" +#include "hal.h" +#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 new file mode 100644 index 00000000..4a41e9c8 --- /dev/null +++ b/src/gdisp/image_native.c @@ -0,0 +1,157 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file src/gdisp/image_native.c + * @brief GDISP native image code. + */ +#include "ch.h" +#include "hal.h" +#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) + +typedef struct gdispImagePrivate { + pixel_t *frame0cache; + pixel_t buf[BLIT_BUFFER_SIZE]; + } gdispImagePrivate; + +gdispImageError gdispImageOpen_NATIVE(gdispImage *img) { + uint8_t hdr[HEADER_SIZE]; + + /* Read the 8 byte header */ + if (img->io.fns->read(&img->io, 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 *)chHeapAlloc(NULL, sizeof(gdispImagePrivate)))) + return GDISP_IMAGE_ERR_NOMEMORY; + img->membytes = sizeof(gdispImagePrivate); + img->priv->frame0cache = 0; + + return GDISP_IMAGE_ERR_OK; +} + +void gdispImageClose_NATIVE(gdispImage *img) { + if (img->priv) { + if (img->priv->frame0cache) + chHeapFree((void *)img->priv->frame0cache); + chHeapFree((void *)img->priv); + img->priv = 0; + } + img->membytes = 0; + img->io.fns->close(&img->io); +} + +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 *)chHeapAlloc(NULL, len); + if (!img->priv->frame0cache) + return GDISP_IMAGE_ERR_NOMEMORY; + img->membytes += len; + + /* Read the entire bitmap into cache */ + img->io.fns->seek(&img->io, FRAME0POS); + if (img->io.fns->read(&img->io, img->priv->frame0cache, len) != len) + return GDISP_IMAGE_ERR_BADDATA; + + return GDISP_IMAGE_ERR_OK; +} + +gdispImageError gdispImageDraw_NATIVE(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) { + gdispBlitAreaEx(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 + cx) * sizeof(pixel_t); + + /* Cycle through the lines */ + for(;cy;cy--, y++) { + /* Move to the start of the line */ + img->io.fns->seek(&img->io, pos); + + /* Draw the line in chunks using BitBlt */ + for(mx = x, mcx = cx; mcx > 0; mcx -= len, mx += len) { + // Read the data + len = img->io.fns->read(&img->io, + img->priv->buf, + mx > BLIT_BUFFER_SIZE ? (BLIT_BUFFER_SIZE*sizeof(pixel_t)) : (mx * sizeof(pixel_t))) + / sizeof(pixel_t); + if (!len) + return GDISP_IMAGE_ERR_BADDATA; + + /* Blit the chunk of data */ + gdispBlitAreaEx(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; +} + +systime_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 new file mode 100644 index 00000000..3dca3645 --- /dev/null +++ b/src/gdisp/image_png.c @@ -0,0 +1,34 @@ +/* + ChibiOS/GFX - Copyright (C) 2012, 2013 + Joel Bodenmann aka Tectu + + This file is part of ChibiOS/GFX. + + ChibiOS/GFX is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS/GFX is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file src/gdisp/image_png.c + * @brief GDISP native image code. + */ +#include "ch.h" +#include "hal.h" +#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 */ +/** @} */