diff --git a/include/gdisp/image.h b/include/gdisp/image.h index ff2e9d85..f8c5f6a1 100644 --- a/include/gdisp/image.h +++ b/include/gdisp/image.h @@ -40,6 +40,7 @@ typedef uint16_t gdispImageError; #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 @@ -116,59 +117,78 @@ typedef struct gdispImage { 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 void *memimage); - - #if GFX_USE_OS_CHIBIOS || defined(__DOXYGEN__) - /** - * @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); + gdispImageError DEPRECATED("If possible please use gdispImageOpenFile(), gdispImageOpenMemory() or gdispImageOpenBaseFileStream() instead") + gdispImageOpen(gdispImage *img); + bool_t DEPRECATED("Use gdispImageOpenMemory() instead. GFX_USE_GFILE, GFILE_NEED_MEMFS must also be TRUE") gdispImageSetMemoryReader(gdispImage *img, const void *memimage); + #if GFX_USE_OS_CHIBIOS + bool_t DEPRECATED("Use gdispImageOpenBaseFileStream() instead. GFX_USE_GFILE, GFILE_NEED_CHIBIOSFS must also be TRUE") gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr); #endif - - #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX || 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 gdispImageSetFileReader(gdispImage *img, const char *filename); - /* Old definition */ + #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX + bool_t DEPRECATED("Please use gdispImageOpenFile() instead. GFX_USE_GFILE must also be TRUE") + gdispImageSetFileReader(gdispImage *img, const char *filename); #define gdispImageSetSimulFileReader(img, fname) gdispImageSetFileReader(img, fname) #endif + + #if GFX_USE_GFILE || defined(__DOXYGEN__) + /** + * @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 GFX_USE_GFILE must be TRUE and you must have included the file-system support + * you want to use. + * + * @param[in] img The image structure + * @param[in] filename The filename to open + * + * @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 even after a partial failure in order to + * properly close the file. + */ + gdispImageError gdispImageOpenFile(gdispImage *img, const char *filename); + #endif + #if GFX_USE_OS_CHIBIOS || defined(__DOXYGEN__) + /** + * @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 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 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 even after a partial failure in order to + * properly close the file. + */ + gdispImageError gdispImageOpenBaseFileStream(gdispImage *img, void *BaseFileStreamPtr); + #endif + /** - * @brief Open an image ready for drawing + * @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. * - * @param[in] img The image structure - * - * @pre The io fields should be filled in before calling gdispImageOpen() + * @param[in] img The image structure + * @param[in] memimage A pointer to the image bytes in memory * * @note This determines which decoder to use and then initialises all other fields * in the gdispImage structure. @@ -179,17 +199,17 @@ extern "C" { * 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. + * @note @p gdispImageClose() should be called even after a partial failure in order to + * properly close the file. */ - gdispImageError gdispImageOpen(gdispImage *img); - + gdispImageError gdispImageOpenMemory(gdispImage *img, const void *memimage); + /** * @brief Close an image and release any dynamically allocated working storage. * * @param[in] img The image structure * - * @pre gdispImageOpen() must have returned successfully. + * @pre gdispImageOpenFile() must have returned successfully. * * @note Also calls the IO close function (if it hasn't already been called). */ @@ -282,94 +302,6 @@ extern "C" { */ delaytime_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 gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - delaytime_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 gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - delaytime_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 gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - delaytime_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 gdispGImageDraw_JPG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - delaytime_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 gdispGImageDraw_PNG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - delaytime_t gdispImageNext_PNG(gdispImage *img); - /* @} */ - #endif - #ifdef __cplusplus } #endif diff --git a/src/gdisp/image.c b/src/gdisp/image.c index 62af0aeb..dd219289 100644 --- a/src/gdisp/image.c +++ b/src/gdisp/image.c @@ -18,6 +18,77 @@ #include +static gdispImageError imageOpen(gdispImage *img) { + gdispImageError err; + + 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)) + return err; + goto unrecoverable; + } + img->io.fns->seek(&img->io, 0); + } + + err = GDISP_IMAGE_ERR_BADFORMAT; + img->type = GDISP_IMAGE_TYPE_UNKNOWN; + +unrecoverable: + img->fns->close(img); + img->flags = 0; + img->fns = 0; + img->priv = 0; + return err; +} + +gdispImageError + DEPRECATED("If possible please use gdispImageOpenFile(), gdispImageOpenMemory() or gdispImageOpenBaseFileStream() instead") + gdispImageOpen(gdispImage *img) { + return imageOpen(img); +} + +#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 */ @@ -59,129 +130,87 @@ static gdispImageHandlers ImageHandlers[] = { #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); +static size_t ImageGFileRead(struct gdispImageIO *pio, void *buf, size_t len) { + if (!pio->fd) return 0; + len = gfileRead((GFILE *)pio->fd, buf, len); + if ((int)len < 0) len = 0; 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 ImageGFileSeek(struct gdispImageIO *pio, size_t pos) { + if (!pio->fd) return; + if (pio->pos != pos) { + gfileSetPos((GFILE *)pio->fd, pos); + pio->pos = pos; + } } -static void ImageMemoryClose(struct gdispImageIO *pio) { - pio->fd = (void *)-1; +static void ImageGFileClose(struct gdispImageIO *pio) { + if (!pio->fd) return; + gfileClose((GFILE *)pio->fd); + pio->fd = 0; pio->pos = 0; } -static const gdispImageIOFunctions ImageMemoryFunctions = - { ImageMemoryRead, ImageMemorySeek, ImageMemoryClose }; +static const gdispImageIOFunctions ImageGFileFunctions = + { ImageGFileRead, ImageGFileSeek, ImageGFileClose }; -bool_t gdispImageSetMemoryReader(gdispImage *img, const void *memimage) { - img->io.fns = &ImageMemoryFunctions; - img->io.pos = 0; - img->io.fd = memimage; - return TRUE; -} - -#if GFX_USE_OS_CHIBIOS - 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; +#if GFILE_NEED_MEMFS + gdispImageError gdispImageOpenMemory(gdispImage *img, const void *memimage) { + img->io.fns = &ImageGFileFunctions; img->io.pos = 0; - img->io.fd = BaseFileStreamPtr; - return TRUE; - } -#endif - -#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX - #include - - static size_t ImageFileRead(struct gdispImageIO *pio, void *buf, size_t len) { - if (!pio->fd) return 0; - len = fread(buf, 1, len, (FILE *)pio->fd); - if ((int)len < 0) len = 0; - pio->pos += len; - return len; + img->io.fd = gfileOpenMemory(memimage, "rb"); + if (!img->io.fd) + return GDISP_IMAGE_ERR_NOSUCHFILE; + return imageOpen(img); } - static void ImageFileSeek(struct gdispImageIO *pio, size_t pos) { - if (!pio->fd) return; - if (pio->pos != pos) { - fseek((FILE *)pio->fd, pos, SEEK_SET); - pio->pos = pos; - } - } - - static void ImageFileClose(struct gdispImageIO *pio) { - if (!pio->fd) return; - fclose((FILE *)pio->fd); - pio->fd = 0; - pio->pos = 0; - } - - static const gdispImageIOFunctions ImageFileFunctions = - { ImageFileRead, ImageFileSeek, ImageFileClose }; - - bool_t gdispImageSetFileReader(gdispImage *img, const char *filename) { - img->io.fns = &ImageFileFunctions; + bool_t DEPRECATED("Use gdispImageOpenMemory() instead. GFX_USE_GFILE, GFILE_NEED_MEMFS must also be TRUE") gdispImageSetMemoryReader(gdispImage *img, const void *memimage) { + img->io.fns = &ImageGFileFunctions; img->io.pos = 0; - #if defined(WIN32) || GFX_USE_OS_WIN32 - img->io.fd = (void *)fopen(filename, "rb"); - #else - img->io.fd = (void *)fopen(filename, "r"); - #endif - + img->io.fd = gfileOpenMemory(memimage, "rb"); return img->io.fd != 0; } #endif -gdispImageError gdispImageOpen(gdispImage *img) { - gdispImageError err; - - 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)) - 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; +gdispImageError gdispImageOpenFile(gdispImage *img, const char *filename) { + img->io.fns = &ImageGFileFunctions; + img->io.pos = 0; + img->io.fd = gfileOpen(filename, "rb"); + if (!img->io.fd) + return GDISP_IMAGE_ERR_NOSUCHFILE; + return imageOpen(img); } +#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX + bool_t DEPRECATED("Please use gdispImageOpenFile() instead. GFX_USE_GFILE must also be TRUE") gdispImageSetFileReader(gdispImage *img, const char *filename) { + img->io.fns = &ImageGFileFunctions; + img->io.pos = 0; + img->io.fd = gfileOpen(filename, "rb"); + return img->io.fd != 0; + } +#endif + +#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS + bool_t DEPRECATED("Use gdispImageOpenBaseFileStream() instead. GFX_USE_GFILE, GFILE_NEED_CHIBIOSFS must also be TRUE") gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr) { + img->io.fns = &ImageGFileFunctions; + img->io.pos = 0; + img->io.fd = gfileOpenBaseFileStream(BaseFileStreamPtr, "rb"); + return img->io.fd != 0; + } + + gdispImageError gdispImageOpenBaseFileStream(gdispImage *img, void *BaseFileStreamPtr) { + img->io.fns = &ImageGFileFunctions; + img->io.pos = 0; + img->io.fd = gfileOpenBaseFileStream(BaseFileStreamPtr, "rb"); + if (!img->io.fd) + return GDISP_IMAGE_ERR_NOSUCHFILE; + return imageOpen(img); + } + +#endif + void gdispImageClose(gdispImage *img) { if (img->fns) img->fns->close(img);