aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Hannam <andrewh@inmarket.com.au>2013-04-03 13:51:43 +1000
committerAndrew Hannam <andrewh@inmarket.com.au>2013-04-03 13:51:43 +1000
commit64971549fd63d131f136a16913deaec3bdbcf2f3 (patch)
treecfd2698ff4ce1f207ba8f776d1c8fff73718c71f /src
parentb5dceeead44b0b825ee2ef7d2d7943bec8200e30 (diff)
downloaduGFX-64971549fd63d131f136a16913deaec3bdbcf2f3.tar.gz
uGFX-64971549fd63d131f136a16913deaec3bdbcf2f3.tar.bz2
uGFX-64971549fd63d131f136a16913deaec3bdbcf2f3.zip
New GDISP image handling with demo
Images currently support Native and BMP (except RLE4,8 and 16 bit - due to bugs) Supports reading from Memory, BaseFileStream or real files (only on the Win32 simulator). Move gdisp_pictures demo to better refect its purpose. Bug fixes for BMP RLE4,8 & 16 bit to come very soon GIF support very soon.
Diffstat (limited to 'src')
-rw-r--r--src/gdisp/gdisp.mk11
-rw-r--r--src/gdisp/image.c210
-rw-r--r--src/gdisp/image_bmp.c912
-rw-r--r--src/gdisp/image_gif.c47
-rw-r--r--src/gdisp/image_jpg.c34
-rw-r--r--src/gdisp/image_native.c157
-rw-r--r--src/gdisp/image_png.c34
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 */
+/** @} */