diff options
author | Tectu <joel@unormal.org> | 2013-04-01 21:15:10 -0700 |
---|---|---|
committer | Tectu <joel@unormal.org> | 2013-04-01 21:15:10 -0700 |
commit | 6783791b5a7b2322b95a7b2c23c5aab8e3f29986 (patch) | |
tree | cfd2698ff4ce1f207ba8f776d1c8fff73718c71f /src | |
parent | 939ca6e1d2fbdb352f6d04fc19aa94c6cfbc4416 (diff) | |
parent | 64971549fd63d131f136a16913deaec3bdbcf2f3 (diff) | |
download | uGFX-6783791b5a7b2322b95a7b2c23c5aab8e3f29986.tar.gz uGFX-6783791b5a7b2322b95a7b2c23c5aab8e3f29986.tar.bz2 uGFX-6783791b5a7b2322b95a7b2c23c5aab8e3f29986.zip |
Merge pull request #51 from inmarket/master
GDISP Image support with Demo
Diffstat (limited to 'src')
-rw-r--r-- | src/gdisp/gdisp.mk | 11 | ||||
-rw-r--r-- | src/gdisp/image.c | 210 | ||||
-rw-r--r-- | src/gdisp/image_bmp.c | 912 | ||||
-rw-r--r-- | src/gdisp/image_gif.c | 47 | ||||
-rw-r--r-- | src/gdisp/image_jpg.c | 34 | ||||
-rw-r--r-- | src/gdisp/image_native.c | 157 | ||||
-rw-r--r-- | src/gdisp/image_png.c | 34 |
7 files changed, 1403 insertions, 2 deletions
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 <joel@unormal.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @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 <fcntl.h> + + 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 <joel@unormal.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @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<<aword; + break; +#endif +#if GDISP_NEED_IMAGE_BMP_24 + case 24: + break; +#endif + default: + goto unsupportedcleanup; + } + priv->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<<aword; + break; +#endif +#if GDISP_NEED_IMAGE_BMP_16 + case 16: +#endif +#if GDISP_NEED_IMAGE_BMP_24 + case 24: +#endif +#if GDISP_NEED_IMAGE_BMP_32 + case 32: +#endif +#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_24 || GDISP_NEED_IMAGE_BMP_32 + break; +#endif + default: + goto unsupportedcleanup; + } + priv->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 <joel@unormal.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @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 <joel@unormal.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @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 <joel@unormal.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @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 <joel@unormal.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @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 */ +/** @} */ |