BMP image handling now complete. The only standard BMP feature not supported is transparency (alpha).
914 lines
26 KiB
C
914 lines
26 KiB
C
/*
|
|
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 TRUE
|
|
#endif
|
|
#ifndef GDISP_NEED_IMAGE_BMP_8
|
|
#define GDISP_NEED_IMAGE_BMP_8 TRUE
|
|
#endif
|
|
#ifndef GDISP_NEED_IMAGE_BMP_8_RLE
|
|
#define GDISP_NEED_IMAGE_BMP_8_RLE TRUE
|
|
#endif
|
|
#ifndef GDISP_NEED_IMAGE_BMP_16
|
|
#define GDISP_NEED_IMAGE_BMP_16 TRUE
|
|
#endif
|
|
#ifndef GDISP_NEED_IMAGE_BMP_24
|
|
#define GDISP_NEED_IMAGE_BMP_24 TRUE
|
|
#endif
|
|
#ifndef GDISP_NEED_IMAGE_BMP_32
|
|
#define GDISP_NEED_IMAGE_BMP_32 TRUE
|
|
#endif
|
|
|
|
/**
|
|
* How big a pixel array to allocate for blitting (in pixels)
|
|
* Bigger is faster but uses more RAM.
|
|
* This must be greater than 40 bytes and 32 pixels as we read our headers into this space as well
|
|
*/
|
|
#define BLIT_BUFFER_SIZE 32
|
|
|
|
/*
|
|
* Determining endianness as at compile time is not guaranteed or compiler portable.
|
|
* We use the best test we can. If we can't guarantee little endianness we do things the
|
|
* hard way.
|
|
*/
|
|
#define GUARANTEED_LITTLE_ENDIAN (!defined(SAFE_ENDIAN) && !defined(SAFE_ALIGNMENT) && (\
|
|
(defined(__BYTE_ORDER__)&&(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) \
|
|
|| defined(__LITTLE_ENDIAN__) \
|
|
|| defined(__LITTLE_ENDIAN) \
|
|
|| defined(_LITTLE_ENDIAN) \
|
|
/* || (1 == *(unsigned char *)&(const int){1})*/ \
|
|
))
|
|
|
|
|
|
/* This is a runtime test */
|
|
static const uint8_t dwordOrder[4] = { 1, 2, 3, 4 };
|
|
|
|
#define isWordLittleEndian() (*(uint16_t *)&dwordOrder == 0x0201)
|
|
#define isDWordLittleEndian() (*(uint32_t *)&dwordOrder == 0x04030201)
|
|
|
|
#if GUARANTEED_LITTLE_ENDIAN
|
|
/* These are fast routines for guaranteed little endian machines */
|
|
#define CONVERT_FROM_WORD_LE(w)
|
|
#define CONVERT_FROM_DWORD_LE(dw)
|
|
#else
|
|
/* These are slower routines for when little endianness cannot be guaranteed at compile time */
|
|
#define CONVERT_FROM_WORD_LE(w) { if (!isWordLittleEndian()) w = ((((uint16_t)(w))>>8)|(((uint16_t)(w))<<8)); }
|
|
#define CONVERT_FROM_DWORD_LE(dw) { if (!isDWordLittleEndian()) dw = (((uint32_t)(((const uint8_t *)(&dw))[0]))|(((uint32_t)(((const uint8_t *)(&dw))[1]))<<8)|(((uint32_t)(((const uint8_t *)(&dw))[2]))<<16)|(((uint32_t)(((const uint8_t *)(&dw))[3]))<<24)); }
|
|
#endif
|
|
|
|
typedef struct gdispImagePrivate {
|
|
uint8_t bmpflags;
|
|
#define BMP_V2 0x01 // Version 2 (old) header format
|
|
#define BMP_V4 0x02 // Version 4 (alpha support) header format
|
|
#define BMP_PALETTE 0x04 // Uses a palette
|
|
#define BMP_COMP_RLE 0x08 // Uses RLE compression
|
|
#define BMP_COMP_MASK 0x10 // Uses mask & shift decoding
|
|
#define BMP_RLE_ENC 0x20 // Currently in RLE encoded run
|
|
#define BMP_RLE_ABS 0x40 // Currently in RLE absolute run
|
|
#define BMP_TOP_TO_BOTTOM 0x80 // Decodes bottom to top line
|
|
uint8_t bitsperpixel;
|
|
#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE
|
|
uint16_t palsize;
|
|
pixel_t *palette;
|
|
#endif
|
|
#if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE
|
|
uint16_t rlerun;
|
|
uint8_t rlecode;
|
|
#endif
|
|
#if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32
|
|
int8_t shiftred;
|
|
int8_t shiftgreen;
|
|
int8_t shiftblue;
|
|
int8_t shiftalpha;
|
|
uint32_t maskred;
|
|
uint32_t maskgreen;
|
|
uint32_t maskblue;
|
|
uint32_t maskalpha;
|
|
#endif
|
|
size_t frame0pos;
|
|
pixel_t *frame0cache;
|
|
pixel_t buf[BLIT_BUFFER_SIZE];
|
|
} gdispImagePrivate;
|
|
|
|
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 = 0x7C00;
|
|
priv->maskgreen = 0x03E0;
|
|
priv->maskblue = 0x001F;
|
|
priv->maskalpha = 0;
|
|
} else if (priv->bitsperpixel == 32) {
|
|
priv->bmpflags |= BMP_COMP_MASK;
|
|
priv->maskred = 0x00FF0000;
|
|
priv->maskgreen = 0x0000FF00;
|
|
priv->maskblue = 0x000000FF;
|
|
priv->maskalpha = 0;
|
|
}
|
|
|
|
/* We need to adjust the masks and calculate the shift values so the result scales 0 -> 255 */
|
|
if (priv->bmpflags & BMP_COMP_MASK) {
|
|
priv->shiftred = 0;
|
|
priv->shiftgreen = 0;
|
|
priv->shiftblue = 0;
|
|
if (priv->maskred) {
|
|
if (priv->maskred < 256)
|
|
for(adword = priv->maskred; adword < 128; priv->shiftred--, adword <<= 1);
|
|
else
|
|
for(adword = priv->maskred; adword > 255; priv->shiftred++, adword >>= 1);
|
|
}
|
|
if (priv->maskgreen) {
|
|
if (priv->maskgreen < 256)
|
|
for(adword = priv->maskgreen; adword < 128; priv->shiftgreen--, adword <<= 1);
|
|
else
|
|
for(adword = priv->maskgreen; adword > 255; priv->shiftgreen++, adword >>= 1);
|
|
}
|
|
if (priv->maskblue) {
|
|
if (priv->maskblue < 256)
|
|
for(adword = priv->maskblue; adword < 128; priv->shiftblue--, adword <<= 1);
|
|
else
|
|
for(adword = priv->maskblue; adword > 255; priv->shiftblue++, adword >>= 1);
|
|
}
|
|
if (priv->maskalpha) {
|
|
if (priv->maskalpha < 256)
|
|
for(adword = priv->maskalpha; adword < 128; priv->shiftalpha--, adword <<= 1);
|
|
else
|
|
for(adword = priv->maskalpha; adword > 255; priv->shiftalpha++, adword >>= 1);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
if ((img->io.pos - priv->frame0pos)&1) { // Make sure we are on a word boundary
|
|
if (img->io.fns->read(&img->io, &b, 1) != 1)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// We have finished the current run - read a new run
|
|
priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS);
|
|
|
|
// There are always at least 2 bytes in an RLE code
|
|
if (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;
|
|
if ((img->io.pos - priv->frame0pos)&1) { // Make sure we are on a word boundary
|
|
if (img->io.fns->read(&img->io, &b, 1) != 1)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// We have finished the current run - read a new run
|
|
priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS);
|
|
|
|
// There are always at least 2 bytes in an RLE code
|
|
if (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_LE(w[0]);
|
|
CONVERT_FROM_WORD_LE(w[1]);
|
|
if (priv->shiftred < 0)
|
|
r = (color_t)((w[0] & priv->maskred) << -priv->shiftred);
|
|
else
|
|
r = (color_t)((w[0] & priv->maskred) >> priv->shiftred);
|
|
if (priv->shiftgreen < 0)
|
|
g = (color_t)((w[0] & priv->maskgreen) << -priv->shiftgreen);
|
|
else
|
|
g = (color_t)((w[0] & priv->maskgreen) >> priv->shiftgreen);
|
|
if (priv->shiftblue < 0)
|
|
b = (color_t)((w[0] & priv->maskblue) << -priv->shiftblue);
|
|
else
|
|
b = (color_t)((w[0] & priv->maskblue) >> priv->shiftblue);
|
|
/* We don't support alpha yet */
|
|
*pc++ = RGB2COLOR(r, g, b);
|
|
if (priv->shiftred < 0)
|
|
r = (color_t)((w[1] & priv->maskred) << -priv->shiftred);
|
|
else
|
|
r = (color_t)((w[1] & priv->maskred) >> priv->shiftred);
|
|
if (priv->shiftgreen < 0)
|
|
g = (color_t)((w[1] & priv->maskgreen) << -priv->shiftgreen);
|
|
else
|
|
g = (color_t)((w[1] & priv->maskgreen) >> priv->shiftgreen);
|
|
if (priv->shiftblue < 0)
|
|
b = (color_t)((w[1] & priv->maskblue) << -priv->shiftblue);
|
|
else
|
|
b = (uint8_t)((w[1] & priv->maskblue) >> priv->shiftblue);
|
|
/* We don't support alpha yet */
|
|
*pc++ = RGB2COLOR(r, g, b);
|
|
x += 2;
|
|
len += 2;
|
|
}
|
|
}
|
|
return len;
|
|
#endif
|
|
|
|
#if GDISP_NEED_IMAGE_BMP_24
|
|
case 24:
|
|
{
|
|
uint8_t b[3];
|
|
|
|
while(x < img->width && len < BLIT_BUFFER_SIZE) {
|
|
if (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 */
|
|
/** @} */
|