From 0b32a222231826bc894557c322f1f1b908103e3c Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Sun, 29 Mar 2009 01:30:16 +0000 Subject: First cut at a port to the new buildsystem svn path=/trunk/libnsbmp/; revision=6981 --- Makefile | 116 +----- include/libnsbmp.h | 114 ++++++ libnsbmp.c | 1124 --------------------------------------------------- libnsbmp.h | 123 ------ src/Makefile | 4 + src/libnsbmp.c | 1126 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/utils/log.h | 27 ++ utils/log.h | 27 -- 8 files changed, 1292 insertions(+), 1369 deletions(-) create mode 100644 include/libnsbmp.h delete mode 100644 libnsbmp.c delete mode 100644 libnsbmp.h create mode 100644 src/Makefile create mode 100644 src/libnsbmp.c create mode 100644 src/utils/log.h delete mode 100644 utils/log.h diff --git a/Makefile b/Makefile index f720120..53d214b 100644 --- a/Makefile +++ b/Makefile @@ -1,95 +1,21 @@ -# -# This file is part of Libnsbmp -# - -SOURCE = libnsbmp.c -HDRS = libnsbmp.h utils/log.h - -CFLAGS = -Wall -Wextra -Wundef -Wpointer-arith -Wcast-align \ - -Wwrite-strings -Wstrict-prototypes \ - -Wnested-externs -pedantic -std=c99 \ - -Wno-format-zero-length -Wformat-security -Wstrict-aliasing=2 \ - -Wmissing-format-attribute -Wunused -Wunreachable-code \ - -Wformat=2 -Werror-implicit-function-declaration \ - -Wmissing-declarations -Wmissing-prototypes -ARFLAGS = -cr -INSTALL = install -SED = sed -DOXYGEN = doxygen - -ifeq ($(TARGET),riscos) - GCCSDK_INSTALL_CROSSBIN ?= /home/riscos/cross/bin - GCCSDK_INSTALL_ENV ?= /home/riscos/env - CC := $(wildcard $(GCCSDK_INSTALL_CROSSBIN)/*gcc) - AR := $(wildcard $(GCCSDK_INSTALL_CROSSBIN)/*ar) - CFLAGS += -Driscos -mpoke-function-name -I$(GCCSDK_INSTALL_ENV)/include - LIBS = -L$(GCCSDK_INSTALL_ENV)/lib - ifneq (,$(findstring arm-unknown-riscos-gcc,$(CC))) - EXEEXT := ,e1f - SUBTARGET := -elf- - else - EXEEXT := ,ff8 - SUBTARGET := -aof- - endif - PREFIX = $(GCCSDK_INSTALL_ENV) -else - CFLAGS += -g - LIBS = - PREFIX = /usr/local -endif - --include Makefile.config - -OBJDIR = build-$(TARGET)$(SUBTARGET)objects -LIBDIR = build-$(TARGET)$(SUBTARGET)lib -BINDIR = build-$(TARGET)$(SUBTARGET)bin - -OBJS = $(addprefix $(OBJDIR)/, $(SOURCE:.c=.o)) - -.PHONY: all clean docs install uninstall - -all: $(LIBDIR)/libnsbmp.a $(BINDIR)/decode_bmp$(EXEEXT) $(BINDIR)/decode_ico$(EXEEXT) - -$(LIBDIR)/libnsbmp.a: $(OBJS) $(LIBDIR)/libnsbmp.pc - @echo " LINK:" $@ - @mkdir -p $(LIBDIR) - @$(AR) $(ARFLAGS) $@ $(OBJS) - -$(LIBDIR)/libnsbmp.pc: libnsbmp.pc.in - @echo " SED:" $@ - @mkdir -p $(LIBDIR) - @$(SED) -e 's#PREFIX#$(PREFIX)#' $^ > $@ - -$(BINDIR)/decode_bmp$(EXEEXT): examples/decode_bmp.c $(LIBDIR)/libnsbmp.a - @echo " LINK:" $@ - @mkdir -p $(BINDIR) - @$(CC) $(CFLAGS) -I. -o $@ $^ - -$(BINDIR)/decode_ico$(EXEEXT): examples/decode_ico.c $(LIBDIR)/libnsbmp.a - @echo " LINK:" $@ - @mkdir -p $(BINDIR) - @$(CC) $(CFLAGS) -I. -o $@ $^ - -$(OBJDIR)/%.o: %.c $(HDRS) - @echo " COMPILE:" $< - @mkdir -p $(OBJDIR) - @$(CC) $(CFLAGS) -c -o $@ $< - -docs: - ${DOXYGEN} - -install: $(LIBDIR)/libnsbmp.a $(LIBDIR)/libnsbmp.pc - mkdir -p $(DESTDIR)$(PREFIX)/lib/pkgconfig - mkdir -p $(DESTDIR)$(PREFIX)/lib - mkdir -p $(DESTDIR)$(PREFIX)/include - $(INSTALL) -m 644 $(LIBDIR)/libnsbmp.a $(DESTDIR)$(PREFIX)/lib - $(INSTALL) -m 644 libnsbmp.h $(DESTDIR)$(PREFIX)/include - $(INSTALL) -m 644 $(LIBDIR)/libnsbmp.pc $(DESTDIR)$(PREFIX)/lib/pkgconfig - -uninstall: - rm $(DESTDIR)$(PREFIX)/lib/libnsbmp.a - rm $(DESTDIR)$(PREFIX)/include/libnsbmp.h - rm $(DESTDIR)$(PREFIX)/lib/pkgconfig/libnsbmp.pc - -clean: - -rm -rf $(OBJDIR) $(LIBDIR) $(BINDIR) doc +# Component settings +COMPONENT := nsbmp +# Default to a static library +COMPONENT_TYPE ?= lib-static + +# Setup the tooling +include build/makefiles/Makefile.tools + +# Toolchain flags +WARNFLAGS := -Wall -Wextra -Wundef -Wpointer-arith -Wcast-align \ + -Wwrite-strings -Wstrict-prototypes -Wmissing-prototypes \ + -Wmissing-declarations -Wnested-externs -Werror -pedantic +CFLAGS := $(CFLAGS) -std=c99 -D_BSD_SOURCE -I$(CURDIR)/include/ \ + -I$(CURDIR)/src $(WARNFLAGS) + +include build/makefiles/Makefile.top + +# Extra installation rules +INSTALL_ITEMS := $(INSTALL_ITEMS) /include:include/libnsbmp.h +INSTALL_ITEMS := $(INSTALL_ITEMS) /lib/pkgconfig:lib$(COMPONENT).pc.in +INSTALL_ITEMS := $(INSTALL_ITEMS) /lib:$(BUILDDIR)/lib$(COMPONENT)$(LIBEXT) diff --git a/include/libnsbmp.h b/include/libnsbmp.h new file mode 100644 index 0000000..1ccf820 --- /dev/null +++ b/include/libnsbmp.h @@ -0,0 +1,114 @@ +/* + * Copyright 2006 Richard Wilson + * Copyright 2008 Sean Fox + * + * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/ + * Licenced under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + */ + +/** \file + * BMP file decoding (interface). + */ + +#ifndef libnsbmp_h_ +#define libnsbmp_h_ + +#include +#include + +/* bmp flags */ +#define BMP_NEW 0 +#define BMP_OPAQUE (1 << 0) /** image is opaque (as opposed to having an alpha mask) */ +#define BMP_CLEAR_MEMORY (1 << 1) /** memory should be wiped */ + +/* error return values */ +typedef enum { + BMP_OK = 0, + BMP_INSUFFICIENT_MEMORY = 1, + BMP_INSUFFICIENT_DATA = 2, + BMP_DATA_ERROR = 3 +} bmp_result; + +/* encoding types */ +typedef enum { + BMP_ENCODING_RGB = 0, + BMP_ENCODING_RLE8 = 1, + BMP_ENCODING_RLE4 = 2, + BMP_ENCODING_BITFIELDS = 3 +} bmp_encoding; + +/* API for Bitmap callbacks +*/ +typedef void* (*bmp_bitmap_cb_create)(int width, int height, unsigned int state); +typedef void (*bmp_bitmap_cb_destroy)(void *bitmap); +typedef void (*bmp_bitmap_cb_set_suspendable)(void *bitmap, void *private_word, + void (*invalidate)(void *bitmap, void *private_word)); +typedef unsigned char* (*bmp_bitmap_cb_get_buffer)(void *bitmap); +typedef size_t (*bmp_bitmap_cb_get_bpp)(void *bitmap); + +/* The Bitmap callbacks function table +*/ +typedef struct bmp_bitmap_callback_vt_s { + bmp_bitmap_cb_create bitmap_create; /**< Create a bitmap. */ + bmp_bitmap_cb_destroy bitmap_destroy; /**< Free a bitmap. */ + bmp_bitmap_cb_set_suspendable bitmap_set_suspendable; /**< The bitmap image can be suspended. */ + bmp_bitmap_cb_get_buffer bitmap_get_buffer; /**< Return a pointer to the pixel data in a bitmap. */ + bmp_bitmap_cb_get_bpp bitmap_get_bpp; /**< Find the width of a pixel row in bytes. */ +} bmp_bitmap_callback_vt; + +typedef struct bmp_image { + bmp_bitmap_callback_vt bitmap_callbacks; /**< callbacks for bitmap functions */ + uint8_t *bmp_data; /** pointer to BMP data */ + uint32_t width; /** width of BMP (valid after _analyse) */ + uint32_t height; /** heigth of BMP (valid after _analyse) */ + bool decoded; /** whether the image has been decoded */ + void *bitmap; /** decoded image */ + /** Internal members are listed below + */ + uint32_t buffer_size; /** total number of bytes of BMP data available */ + bmp_encoding encoding; /** pixel encoding type */ + uint32_t bitmap_offset; /** offset of bitmap data */ + uint16_t bpp; /** bits per pixel */ + uint32_t colours; /** number of colours */ + uint32_t *colour_table; /** colour table */ + bool limited_trans; /** whether to use bmp's limited transparency */ + uint32_t trans_colour; /** colour to display for "transparent" pixels when + * using limited transparency */ + bool reversed; /** scanlines are top to bottom */ + bool ico; /** image is part of an ICO, mask follows */ + bool opaque; /** true if the bitmap does not contain an alpha channel */ + uint32_t mask[4]; /** four bitwise mask */ + int32_t shift[4]; /** four bitwise shifts */ + uint32_t transparent_index; /** colour representing "transparency" in the bitmap */ +} bmp_image; + +typedef struct ico_image { + bmp_image bmp; + struct ico_image *next; +} ico_image; + +typedef struct ico_collection { + bmp_bitmap_callback_vt bitmap_callbacks; /**< callbacks for bitmap functions */ + uint16_t width; /** width of largest BMP */ + uint16_t height; /** heigth of largest BMP */ + /** Internal members are listed below + */ + uint8_t *ico_data; /** pointer to ICO data */ + uint32_t buffer_size; /** total number of bytes of ICO data available */ + ico_image *first; +} ico_collection; + +void bmp_create(bmp_image *gif, bmp_bitmap_callback_vt *bitmap_callbacks); +void ico_collection_create(ico_collection *ico, + bmp_bitmap_callback_vt *bitmap_callbacks); +bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data); +bmp_result bmp_decode(bmp_image *bmp); +bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t transparent_colour); +void bmp_finalise(bmp_image *bmp); + +bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data); +bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height); +void ico_finalise(ico_collection *ico); + +#endif diff --git a/libnsbmp.c b/libnsbmp.c deleted file mode 100644 index 6101e07..0000000 --- a/libnsbmp.c +++ /dev/null @@ -1,1124 +0,0 @@ -/* - * Copyright 2006 Richard Wilson - * Copyright 2008 Sean Fox - * - * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/ - * Licenced under the MIT License, - * http://www.opensource.org/licenses/mit-license.php - */ - -#include -#include -#include -#include -#include -#include -#include "libnsbmp.h" -#include "utils/log.h" - -/* The functions provided by this file allow for the decoding of - Microsoft's BMP and ICO image file formats. - - READING BMP FILES - ================= - - To begin decoding a BMP, the caller should initialise a - 'bmp_bitmap_callback_vt' structure with the appropriate values necessary - to handle bitmap images. Next, a 'bmp_image' structure should be - initialised by calling bmp_create(). This structure should then be - passed to bmp_analyse() along with the BMP data to process and the size - of this data. - - Once the analysis has begun, the decoder completes the width and height - variables. - - To decode the image, the caller must use bmp_decode() which selects the - proper decoding method based on the BMP info header and assigns the - decoded bitmap image to the 'bitmap' member of the 'bmp_image' - structure. The bitmap image is stored with 4 bytes-per-pixel in RGBA - format. - - It should be noted that bmp_finalise() should always be called, even if - the image was never decoded. It is also the responsibility of the - caller to free 'bmp_data'. - - READING ICO FILES - ================= - - To begin decoding an ICO, the caller should initialise a - 'bmp_bitmap_callback_vt' structure with the appropriate values necessary - to handle bitmap images. Next, an 'ico_collection' structure should be - initialised by calling ico_create(). This structure should then be - passed to ico_analyse() along with the ICO data to process and the size - of this data. - - Once the analysis has begun, the decoder completes the width and height - variables. Because ICO collections contain multiple bitmap images, the - width and height will contain the values of the largest available image. - - The caller then obtains a BMP from the ICO collection by calling - ico_find() with the requested width and height. - - To decode the image, the caller must use bmp_decode() which selects the - proper decoding method based on the BMP info header and assigns the - decoded bitmap image to the 'bitmap' member of the 'bmp_image' - structure. The bitmap image is stored with 4 bytes-per-pixel in RGBA - format. - - It should be noted that ico_finalise() should always be called, even if - no images were decoded. Because ico_finalise() calls bmp_finalise() for - each bitmap within the collection, the caller is not required to perform - this function. However, it is the responsibility of the caller to free - 'ico_data'. - - [dynis] - Tue 1st July 2008 -*/ - -/* squashes unused variable compiler warnings */ -#define UNUSED(x) ((x)=(x)) - -/* BMP flags */ -#define BMP_FILE_HEADER_SIZE 14 -#define ICO_FILE_HEADER_SIZE 6 -#define ICO_DIR_ENTRY_SIZE 16 - -static inline int8_t read_int8(uint8_t *data, unsigned int o) { - return (int8_t) data[o]; -} - -static inline uint8_t read_uint8(uint8_t *data, unsigned int o) { - return (uint8_t) data[o]; -} - -static inline int16_t read_int16(uint8_t *data, unsigned int o) { - return (int16_t) (data[o] | (data[o+1] << 8)); -} - -static inline uint16_t read_uint16(uint8_t *data, unsigned int o) { - return (uint16_t) (data[o] | (data[o+1] << 8)); -} - -static inline int32_t read_int32(uint8_t *data, unsigned int o) { - return (int32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); -} - -static inline uint32_t read_uint32(uint8_t *data, unsigned int o) { - return (uint32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); -} - -static bmp_result next_ico_image(ico_collection *ico, ico_image *image); -static bmp_result bmp_analyse_header(bmp_image *bmp, unsigned char *data); -static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes); -static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes); -static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes); -static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes); -static bmp_result bmp_decode_rle(bmp_image *bmp, uint8_t *data, int bytes, int size); -static void bmp_invalidate(void *bitmap, void *private_word); - - - -/** Initialises necessary bmp_image members. -*/ -void bmp_create(bmp_image *bmp, bmp_bitmap_callback_vt *bitmap_callbacks) { - memset(bmp, 0, sizeof(bmp_image)); - bmp->bitmap_callbacks = *bitmap_callbacks; -} - - -/** Initialises necessary ico_collection members. -*/ -void ico_collection_create(ico_collection *ico, bmp_bitmap_callback_vt *bitmap_callbacks) { - memset(ico, 0, sizeof(ico_collection)); - ico->bitmap_callbacks = *bitmap_callbacks; -} - - -/** - * Analyse a BMP prior to decoding. - * - * This function will scan the data provided and perform simple checks to - * ensure the data is a valid BMP. - * - * This function must be called before bmp_decode() and sets up all the - * relevant values in the bmp structure. - * - * \param bmp the BMP image to analyse - * \return BMP_OK on success - */ -bmp_result bmp_analyse(bmp_image *bmp, size_t size, unsigned char *cdata) { - uint8_t *data = (uint8_t *)cdata; - - /* ensure we aren't already initialised */ - if (bmp->bitmap) - return BMP_OK; - - /* initialize values */ - bmp->buffer_size = size; - bmp->bmp_data = data; - - /* standard 14-byte BMP file header is: - * - * +0 UINT16 File Type ('BM') - * +2 UINT32 Size of File (in bytes) - * +6 INT16 Reserved Field (1) - * +8 INT16 Reserved Field (2) - * +10 UINT32 Starting Position of Image Data (offset in bytes) - */ - if (bmp->buffer_size < BMP_FILE_HEADER_SIZE) - return BMP_INSUFFICIENT_DATA; - if ((data[0] != (uint8_t)'B') || (data[1] != (uint8_t)'M')) - return BMP_DATA_ERROR; - bmp->bitmap_offset = read_uint32(data, 10); - data += BMP_FILE_HEADER_SIZE; - - /* boundary checking */ - if (bmp->bitmap_offset >= size) - return BMP_INSUFFICIENT_DATA; - - /* decode the BMP header */ - return bmp_analyse_header(bmp, data); -} - - -/** - * Analyse an ICO prior to decoding. - * - * This function will scan the data provided and perform simple checks to - * ensure the data is a valid ICO. - * - * This function must be called before ico_find(). - * - * \param ico the ICO image to analyse - * \return BMP_OK on success - */ -bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data) { - uint16_t count, i; - bmp_result result; - int area, max_area = 0; - - /* ensure we aren't already initialised */ - if (ico->first) - return BMP_OK; - - /* initialize values */ - ico->buffer_size = size; - ico->ico_data = data; - - /* 6-byte ICO file header is: - * - * +0 INT16 Reserved (should be 0) - * +2 UINT16 Type (1 for ICO, 2 for CUR) - * +4 UINT16 Number of BMPs to follow - */ - if (ico->buffer_size < ICO_FILE_HEADER_SIZE) - return BMP_INSUFFICIENT_DATA; -// if (read_int16(data, 2) != 0x0000) -// return BMP_DATA_ERROR; - if (read_uint16(data, 2) != 0x0001) - return BMP_DATA_ERROR; - count = read_uint16(data, 4); - if (count == 0) - return BMP_DATA_ERROR; - data += ICO_FILE_HEADER_SIZE; - - /* check if we have enough data for the directory */ - if (ico->buffer_size < (uint32_t)(ICO_FILE_HEADER_SIZE + (ICO_DIR_ENTRY_SIZE * count))) - return BMP_INSUFFICIENT_DATA; - - /* Decode the BMP files. - * - * 16-byte ICO directory entry is: - * - * +0 UINT8 Width (0 for 256 pixels) - * +1 UINT8 Height (0 for 256 pixels) - * +2 UINT8 Colour count (0 if more than 256 colours) - * +3 INT8 Reserved (should be 0, but may not be) - * +4 UINT16 Colour Planes (should be 0 or 1) - * +6 UINT16 Bits Per Pixel - * +8 UINT32 Size of BMP info header + bitmap data in bytes - * +12 UINT32 Offset (points to the BMP info header, not the bitmap data) - */ - for (i = 0; i < count; i++) { - ico_image *image; - image = calloc(1, sizeof(ico_image)); - if (!image) - return BMP_INSUFFICIENT_MEMORY; - result = next_ico_image(ico, image); - if (result != BMP_OK) - return result; - image->bmp.width = read_uint8(data, 0); - if (image->bmp.width == 0) - image->bmp.width = 256; - image->bmp.height = read_uint8(data, 1); - if (image->bmp.height == 0) - image->bmp.height = 256; - image->bmp.buffer_size = read_uint32(data, 8); - image->bmp.bmp_data = ico->ico_data + read_uint32(data, 12); - image->bmp.ico = true; - data += ICO_DIR_ENTRY_SIZE; - result = bmp_analyse_header(&image->bmp, image->bmp.bmp_data); - if (result != BMP_OK) - return result; - /* adjust the size based on the images available */ - area = image->bmp.width * image->bmp.height; - if (area > max_area) { - ico->width = image->bmp.width; - ico->height = image->bmp.height; - max_area = area; - } - } - return BMP_OK; -} - - -/** - * Allocates memory for the next BMP in an ICO collection - * - * Sets proper structure values - * - * \param ico the ICO collection to add the image to - * \param image a pointer to the ICO image to be initialised - */ -static bmp_result next_ico_image(ico_collection *ico, ico_image *image) { - bmp_create(&image->bmp, &ico->bitmap_callbacks); - image->next = ico->first; - ico->first = image; - return BMP_OK; -} - - -static bmp_result bmp_analyse_header(bmp_image *bmp, uint8_t *data) { - uint32_t header_size; - uint32_t i; - uint8_t j; - int32_t width, height; - uint32_t uheight; - uint8_t palette_size; - unsigned int flags; - - /* a variety of different bitmap headers can follow, depending - * on the BMP variant. A full description of the various headers - * can be found at - * http://msdn.microsoft.com/en-us/library/ms532301(VS.85).aspx - */ - header_size = read_uint32(data, 0); - if (bmp->buffer_size < (14 + header_size)) - return BMP_INSUFFICIENT_DATA; - if (header_size == 12) { - /* the following header is for os/2 and windows 2.x and consists of: - * - * +0 UINT32 size of this header (in bytes) - * +4 INT16 image width (in pixels) - * +6 INT16 image height (in pixels) - * +8 UINT16 number of colour planes (always 1) - * +10 UINT16 number of bits per pixel - */ - width = read_int16(data, 4); - height = read_int16(data, 6); - if ((width <= 0) || (height == 0)) - return BMP_DATA_ERROR; - if (height < 0) { - bmp->reversed = true; - height = -height; - } - /* ICOs only support 256*256 resolutions - * In the case of the ICO header, the height is actually the added - * height of XOR-Bitmap and AND-Bitmap (double the visible height) - * Technically we could remove this check and ICOs with bitmaps - * of any size could be processed; this is to conform to the spec. - */ - if (bmp->ico) { - if ((width > 256) || (height > 512)) { - return BMP_DATA_ERROR; - } else { - bmp->width = width; - bmp->height = height / 2; - } - } else { - bmp->width = width; - bmp->height = height; - } - if (read_uint16(data, 8) != 1) - return BMP_DATA_ERROR; - bmp->bpp = read_uint16(data, 10); - /** - * The bpp value should be in the range 1-32, but the only - * values considered legal are: - * RGB ENCODING: 1, 4, 8, 16, 24 and 32 - */ - if ((bmp->bpp != 1) && (bmp->bpp != 4) && - (bmp->bpp != 8) && - (bmp->bpp != 16) && - (bmp->bpp != 24) && - (bmp->bpp != 32)) - return BMP_DATA_ERROR; - bmp->colours = (1 << bmp->bpp); - palette_size = 3; - } else if (header_size < 40) { - return BMP_DATA_ERROR; - } else { - /* the following header is for windows 3.x and onwards. it is a - * minimum of 40 bytes and (as of Windows 95) a maximum of 108 bytes. - * - * +0 UINT32 size of this header (in bytes) - * +4 INT32 image width (in pixels) - * +8 INT32 image height (in pixels) - * +12 UINT16 number of colour planes (always 1) - * +14 UINT16 number of bits per pixel - * +16 UINT32 compression methods used - * +20 UINT32 size of bitmap (in bytes) - * +24 UINT32 horizontal resolution (in pixels per meter) - * +28 UINT32 vertical resolution (in pixels per meter) - * +32 UINT32 number of colours in the image - * +36 UINT32 number of important colours - * +40 UINT32 mask identifying bits of red component - * +44 UINT32 mask identifying bits of green component - * +48 UINT32 mask identifying bits of blue component - * +52 UINT32 mask identifying bits of alpha component - * +56 UINT32 color space type - * +60 UINT32 x coordinate of red endpoint - * +64 UINT32 y coordinate of red endpoint - * +68 UINT32 z coordinate of red endpoint - * +72 UINT32 x coordinate of green endpoint - * +76 UINT32 y coordinate of green endpoint - * +80 UINT32 z coordinate of green endpoint - * +84 UINT32 x coordinate of blue endpoint - * +88 UINT32 y coordinate of blue endpoint - * +92 UINT32 z coordinate of blue endpoint - * +96 UINT32 gamma red coordinate scale value - * +100 UINT32 gamma green coordinate scale value - * +104 UINT32 gamma blue coordinate scale value - */ - width = read_int32(data, 4); - height = read_int32(data, 8); - if ((width <= 0) || (height == 0)) - return BMP_DATA_ERROR; - if (height < 0) { - bmp->reversed = true; - uheight = -height; - } - /* ICOs only support 256*256 resolutions - * In the case of the ICO header, the height is actually the added - * height of XOR-Bitmap and AND-Bitmap (double the visible height) - * Technically we could remove this check and ICOs with bitmaps - * of any size could be processed; this is to conform to the spec. - */ - if (bmp->ico) { - if ((width > 256) || (height > 512)) { - return BMP_DATA_ERROR; - } else { - bmp->width = width; - bmp->height = height / 2; - } - } else { - bmp->width = width; - bmp->height = height; - } - if (read_uint16(data, 12) != 1) - return BMP_DATA_ERROR; - bmp->bpp = read_uint16(data, 14); - if (bmp->bpp == 0) - bmp->bpp = 8; - bmp->encoding = read_uint32(data, 16); - /** - * The bpp value should be in the range 1-32, but the only - * values considered legal are: - * RGB ENCODING: 1, 4, 8, 16, 24 and 32 - * RLE4 ENCODING: 4 - * RLE8 ENCODING: 8 - * BITFIELD ENCODING: 16 and 32 - */ - switch (bmp->encoding) { - case BMP_ENCODING_RGB: - if ((bmp->bpp != 1) && (bmp->bpp != 4) && - (bmp->bpp != 8) && - (bmp->bpp != 16) && - (bmp->bpp != 24) && - (bmp->bpp != 32)) - return BMP_DATA_ERROR; - break; - case BMP_ENCODING_RLE8: - if (bmp->bpp != 8) - return BMP_DATA_ERROR; - break; - case BMP_ENCODING_RLE4: - if (bmp->bpp != 4) - return BMP_DATA_ERROR; - break; - case BMP_ENCODING_BITFIELDS: - if ((bmp->bpp != 16) && (bmp->bpp != 32)) - return BMP_DATA_ERROR; - break; - /* invalid encoding */ - default: - return BMP_DATA_ERROR; - break; - } - /* Bitfield encoding means we have red, green, blue, and alpha masks. - * Here we aquire the masks and determine the required bit shift to - * align them in our 24-bit color 8-bit alpha format. - */ - if (bmp->encoding == BMP_ENCODING_BITFIELDS) { - if (header_size == 40) { - header_size += 12; - if (bmp->buffer_size < (14 + header_size)) - return BMP_INSUFFICIENT_DATA; - for (i = 0; i < 3; i++) - bmp->mask[i] = read_uint32(data, 40 + (i << 2)); - } else { - for (i = 0; i < 4; i++) - bmp->mask[i] = read_uint32(data, 40 + (i << 2)); - } - for (i = 0; i < 4; i++) { - if (bmp->mask[i] == 0) - break; - for (j = 31; j > 0; j--) - if (bmp->mask[i] & (1 << j)) { - if ((j - 7) > 0) - bmp->mask[i] &= 0xff << (j - 7); - else - bmp->mask[i] &= 0xff >> (-(j - 7)); - bmp->shift[i] = (i << 3) - (j - 7); - break; - } - } - } - bmp->colours = read_uint32(data, 32); - if (bmp->colours == 0) - bmp->colours = (1 << bmp->bpp); - palette_size = 4; - } - data += header_size; - - /* if there's no alpha mask, flag the bmp opaque */ - if ((!bmp->ico) && (bmp->mask[3] == 0)) { - flags |= BMP_OPAQUE; - bmp->opaque = true; - } - - /* we only have a palette for <16bpp */ - if (bmp->bpp < 16) { - /* we now have a series of palette entries of the format: - * - * +0 BYTE blue - * +1 BYTE green - * +2 BYTE red - * - * if the palette is from an OS/2 or Win2.x file then the entries - * are padded with an extra byte. - */ - - /* boundary checking */ - if (bmp->buffer_size < (14 + header_size + ((uint64_t)4 * bmp->colours))) - return BMP_INSUFFICIENT_DATA; - - /* create the colour table */ - bmp->colour_table = (uint32_t *)malloc(bmp->colours * 4); - if (!bmp->colour_table) - return BMP_INSUFFICIENT_MEMORY; - for (i = 0; i < bmp->colours; i++) { - bmp->colour_table[i] = data[2] | (data[1] << 8) | (data[0] << 16); - if (bmp->opaque) - bmp->colour_table[i] |= (0xff << 24); - data += palette_size; - } - } - - /* create our bitmap */ - flags |= BMP_NEW | BMP_CLEAR_MEMORY; - bmp->bitmap = bmp->bitmap_callbacks.bitmap_create(bmp->width, bmp->height, flags); - if (!bmp->bitmap) { - if (bmp->colour_table) - free(bmp->colour_table); - bmp->colour_table = NULL; - return BMP_INSUFFICIENT_MEMORY; - } - /* BMPs within ICOs don't have BMP file headers, so the image data should - * always be right after the colour table. - */ - if (bmp->ico) - bmp->bitmap_offset = (intptr_t)data - (intptr_t)bmp->bmp_data; - bmp->bitmap_callbacks.bitmap_set_suspendable(bmp->bitmap, bmp, bmp_invalidate); - return BMP_OK; -} - - -/** - * Finds the closest BMP within an ICO collection - * - * This function finds the BMP with dimensions as close to a specified set - * as possible from the images in the collection. - * - * \param ico the ICO collection to examine - * \param width the preferred width (0 to use ICO header width) - * \param height the preferred height (0 to use ICO header height) - */ -bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height) { - bmp_image *bmp = NULL; - ico_image *image; - int x, y, cur, distance = (1 << 24); - - if (width == 0) - width = ico->width; - if (height == 0) - height = ico->height; - for (image = ico->first; image; image = image->next) { - if ((image->bmp.width == width) && (image->bmp.height == height)) - return &image->bmp; - x = image->bmp.width - width; - y = image->bmp.height - height; - cur = (x * x) + (y * y); - if (cur < distance) { - distance = cur; - bmp = &image->bmp; - } - } - return bmp; -} - - -/** - * Invalidates a BMP - * - * This function sets the BMP into a state such that the bitmap image data - * can be released from memory. - * - * \param bmp the BMP image to invalidate - */ -static void bmp_invalidate(void *bitmap, void *private_word) { - bmp_image *bmp = (bmp_image *)private_word; - UNUSED(bitmap); - - bmp->decoded = false; -} - - -/** - * Decode a BMP - * - * This function decodes the BMP data such that bmp->bitmap is a valid - * image. The state of bmp->decoded is set to TRUE on exit such that it - * can easily be identified which BMPs are in a fully decoded state. - * - * \param bmp the BMP image to decode - * \return BMP_OK on success - */ -bmp_result bmp_decode(bmp_image *bmp) { - uint8_t *data; - uint32_t bytes; - bmp_result result = BMP_OK; - - assert(bmp->bitmap); - - data = bmp->bmp_data + bmp->bitmap_offset; - bytes = bmp->buffer_size - bmp->bitmap_offset; - - switch (bmp->encoding) { - case BMP_ENCODING_RGB: - if ((bmp->bpp == 24) || (bmp->bpp == 32)) - result = bmp_decode_rgb24(bmp, &data, bytes); - else if (bmp->bpp == 16) - result = bmp_decode_rgb16(bmp, &data, bytes); - else - result = bmp_decode_rgb(bmp, &data, bytes); - break; - case BMP_ENCODING_RLE8: - result = bmp_decode_rle(bmp, data, bytes, 8); - break; - case BMP_ENCODING_RLE4: - result = bmp_decode_rle(bmp, data, bytes, 4); - break; - case BMP_ENCODING_BITFIELDS: - if (bmp->bpp == 32) - result = bmp_decode_rgb24(bmp, &data, bytes); - else if (bmp->bpp == 16) - result = bmp_decode_rgb16(bmp, &data, bytes); - else - return BMP_DATA_ERROR; - } - - if ((!bmp->ico) || (result != BMP_OK)) - return result; - - bytes = (uintptr_t)bmp->bmp_data + bmp->buffer_size - (uintptr_t)data; - return bmp_decode_mask(bmp, data, bytes); -} - -/** - * Decode a BMP using "limited transparency" - * - * Bitmaps do not have native transparency support. However, there is a - * "trick" that is used in some instances in which the first pixel of the - * bitmap becomes the "transparency index". The decoding application can - * replace this index with whatever background colour it chooses to - * create the illusion of transparency. - * - * When to use transparency is at the discretion of the decoding - * application. - * - * \param bmp the BMP image to decode - * \param colour the colour to use as "transparent" - * \return BMP_OK on success - */ -bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour) { - bmp->limited_trans = true; - bmp->trans_colour = colour; - return bmp_decode(bmp); -} - - -/** - * Decode BMP data stored in 24bpp colour. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes) { - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - uint32_t x, y; - uint32_t swidth, skip; - intptr_t addr; - uint8_t i; - uint32_t word; - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - addr = ((intptr_t)data) & 3; - skip = bmp->bpp >> 3; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) { - if ((data + skip) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->encoding == BMP_ENCODING_BITFIELDS) - bmp->transparent_index = read_uint32(data, 0); - else - bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16); - } - - for (y = 0; y < bmp->height; y++) { - while (addr != (((intptr_t)data) & 3)) - data++; - if ((data + (skip * bmp->width)) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->reversed) - scanline = (uint32_t *)(top + (y * swidth)); - else - scanline = (uint32_t *)(bottom - (y * swidth)); - if (bmp->encoding == BMP_ENCODING_BITFIELDS) { - for (x = 0; x < bmp->width; x++) { - word = read_uint32(data, 0); - for (i = 0; i < 4; i++) - if (bmp->shift[i] > 0) - scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); - else - scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); - /* 32-bit BMPs have alpha masks, but sometimes they're not utilized */ - if (bmp->opaque) - scanline[x] |= (0xff << 24); - data += skip; - } - } else { - for (x = 0; x < bmp->width; x++) { - scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16); - if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) - scanline[x] = bmp->trans_colour; - if (bmp->opaque) - scanline[x] |= (0xff << 24); - data += skip; - } - } - } - *start = data; - return BMP_OK; -} - - -/** - * Decode BMP data stored in 16bpp colour. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes) { - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - uint32_t x, y, swidth; - intptr_t addr; - uint8_t i; - uint16_t word; - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - addr = ((intptr_t)data) & 3; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) { - if ((data + 2) > end) - return BMP_INSUFFICIENT_DATA; - bmp->transparent_index = read_uint16(data, 0); - } - - for (y = 0; y < bmp->height; y++) { - while (addr != (((intptr_t)data) & 3)) - data += 2; - if ((data + (2 * bmp->width)) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->reversed) - scanline = (uint32_t *)(top + (y * swidth)); - else - scanline = (uint32_t *)(bottom - (y * swidth)); - if (bmp->encoding == BMP_ENCODING_BITFIELDS) { - for (x = 0; x < bmp->width; x++) { - word = read_uint16(data, 0); - if ((bmp->limited_trans) && (word == bmp->transparent_index)) - scanline[x] = bmp->trans_colour; - else { - scanline[x] = 0; - for (i = 0; i < 4; i++) - if (bmp->shift[i] > 0) - scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); - else - scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); - if (bmp->opaque) - scanline[x] |= (0xff << 24); - } - data += 2; - } - } else { - for (x = 0; x < bmp->width; x++) { - word = read_uint16(data, 0); - if ((bmp->limited_trans) && (word == bmp->transparent_index)) - scanline[x] = bmp->trans_colour; - else { - /* 16-bit RGB defaults to RGB555 */ - scanline[x] = ((word & (31 << 0)) << 19) | - ((word & (31 << 5)) << 6) | - ((word & (31 << 10)) >> 7); - } - if (bmp->opaque) - scanline[x] |= (0xff << 24); - data += 2; - } - } - } - *start = data; - return BMP_OK; -} - - -/** - * Decode BMP data stored with a palette and in 8bpp colour or less. - * - * \param bmp the BMP image to decode - * \param start the data to decode, updated to last byte read on success - * \param bytes the number of bytes of data available - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes) { - uint8_t *top, *bottom, *end, *data; - uint32_t *scanline; - intptr_t addr; - uint32_t x, y, swidth; - uint8_t bit_shifts[8]; - uint8_t ppb = 8 / bmp->bpp; - uint8_t bit_mask = (1 << bmp->bpp) - 1; - uint8_t cur_byte = 0, bit, i; - - for (i = 0; i < ppb; i++) - bit_shifts[i] = 8 - ((i + 1) * bmp->bpp); - - data = *start; - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - addr = ((intptr_t)data) & 3; - bmp->decoded = true; - - /* Determine transparent index */ - if (bmp->limited_trans) - bmp->transparent_index = bmp->colour_table[(*data >> bit_shifts[0]) & bit_mask]; - - for (y = 0; y < bmp->height; y++) { - while (addr != (((intptr_t)data) & 3)) - data++; - bit = 8; - if ((data + (bmp->width / ppb)) > end) - return BMP_INSUFFICIENT_DATA; - if (bmp->reversed) - scanline = (unsigned int *)(top + (y * swidth)); - else - scanline = (unsigned int *)(bottom - (y * swidth)); - for (x = 0; x < bmp->width; x++) { - if (bit >= ppb) { - bit = 0; - cur_byte = *data++; - } - scanline[x] = bmp->colour_table[(cur_byte >> bit_shifts[bit++]) & bit_mask]; - if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) - scanline[x] = bmp->trans_colour; - } - } - *start = data; - return BMP_OK; -} - - -/** - * Decode a 1bpp mask for an ICO - * - * \param bmp the BMP image to decode - * \param data the data to decode - * \param bytes the number of bytes of data available - * \return BMP_OK on success - */ -static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes) { - uint8_t *top, *bottom, *end; - uint32_t *scanline; - intptr_t addr; - uint32_t x, y, swidth; - uint32_t cur_byte = 0; - - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - addr = ((intptr_t)data) & 3; - - for (y = 0; y < bmp->height; y++) { - while (addr != (((intptr_t)data) & 3)) - data++; - if ((data + (bmp->width >> 3)) > end) - return BMP_INSUFFICIENT_DATA; - scanline = (uint32_t *)(bottom - (y * swidth)); - for (x = 0; x < bmp->width; x++) { - if ((x & 7) == 0) - cur_byte = *data++; - if ((cur_byte & 128) == 0) - scanline[x] |= (0xff << 24); - cur_byte = cur_byte << 1; - } - } - return BMP_OK; -} - - -/** - * Decode BMP data stored encoded in either RLE4 or RLE8. - * - * \param bmp the BMP image to decode - * \param data the data to decode - * \param bytes the number of bytes of data available - * \param size the size of the RLE tokens (4 or 8) - * \return BMP_OK on success - * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; - * in this case, the image may be partially viewable - */ -static bmp_result bmp_decode_rle(bmp_image *bmp, uint8_t *data, int bytes, int size) { - uint8_t *top, *bottom, *end; - uint32_t *scanline; - uint32_t swidth; - uint32_t i, length, pixels_left; - uint32_t x = 0, y = 0, last_y = 0; - uint32_t pixel = 0, pixel2; - - if (bmp->ico) - return BMP_DATA_ERROR; - - swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; - top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); - if (!top) - return BMP_INSUFFICIENT_MEMORY; - bottom = top + (uint64_t)swidth * (bmp->height - 1); - end = data + bytes; - bmp->decoded = true; - - do { - if (data + 2 > end) - return BMP_INSUFFICIENT_DATA; - length = *data++; - if (length == 0) { - length = *data++; - if (length == 0) { - /* 00 - 00 means end of scanline */ - x = 0; - if (last_y == y) { - if (++y > bmp->height) - return BMP_DATA_ERROR; - } - last_y = y; - } else if (length == 1) { - /* 00 - 01 means end of RLE data */ - return BMP_OK; - } else if (length == 2) { - /* 00 - 02 - XX - YY means move cursor */ - if (data + 2 > end) - return BMP_INSUFFICIENT_DATA; - x += *data++; - if (x >= bmp->width) - return BMP_DATA_ERROR; - y += *data++; - if (y >= bmp->height) - return BMP_DATA_ERROR; - } else { - /* 00 - NN means escape NN pixels */ - if (bmp->reversed) { - pixels_left = (y + 1) * bmp->width - x; - scanline = (unsigned int *)(top + (y * swidth)); - } else { - pixels_left = (bmp->height - y + 1) * bmp->width - x; - scanline = (unsigned int *)(bottom - (y * swidth)); - } - if (length > pixels_left) - length = pixels_left; - if (data + length > end) - return BMP_INSUFFICIENT_DATA; - - /* the following code could be easily optimised by simply - * checking the bounds on entry and using some simply copying - * routines if so */ - if (size == 8) { - for (i = 0; i < length; i++) { - if (x >= bmp->width) { - x = 0; - if (++y > bmp->height) - return BMP_DATA_ERROR; - scanline -= bmp->width; - } - scanline[x++] = bmp->colour_table[(int)*data++]; - } - } else { - for (i = 0; i < length; i++) { - if (x >= bmp->width) { - x = 0; - if (++y > bmp->height) - return BMP_DATA_ERROR; - scanline -= bmp->width; - } - if ((i & 1) == 0) { - pixel = *data++; - scanline[x++] = bmp->colour_table - [pixel >> 4]; - } else { - scanline[x++] = bmp->colour_table - [pixel & 0xf]; - } - } - length = (length + 1) >> 1; - } - if ((length & 1) && (*data++ != 0x00)) - return BMP_DATA_ERROR; - - } - } else { - /* NN means perform RLE for NN pixels */ - if (bmp->reversed) { - pixels_left = (y + 1) * bmp->width - x; - scanline = (unsigned int *)(top + (y * swidth)); - } else { - pixels_left = (bmp->height - y + 1) * bmp->width - x; - scanline = (unsigned int *)(bottom - (y * swidth)); - } - if (length > pixels_left) - length = pixels_left; - - /* boundary checking */ - if (data + 1 > end) - return BMP_INSUFFICIENT_DATA; - - /* the following code could be easily optimised by simply - * checking the bounds on entry and using some simply copying - * routines if so */ - if (size == 8) { - pixel = bmp->colour_table[(int)*data++]; - for (i = 0; i < length; i++) { - if (x >= bmp->width) { - x = 0; - if (++y > bmp->height) - return BMP_DATA_ERROR; - scanline -= bmp->width; - } - scanline[x++] = pixel; - } - } else { - pixel2 = *data++; - pixel = bmp->colour_table[pixel2 >> 4]; - pixel2 = bmp->colour_table[pixel2 & 0xf]; - for (i = 0; i < length; i++) { - if (x >= bmp->width) { - x = 0; - if (++y > bmp->height) - return BMP_DATA_ERROR; - scanline -= bmp->width; - } - if ((i & 1) == 0) - scanline[x++] = pixel; - else - scanline[x++] = pixel2; - } - } - } - } while (data < end); - return BMP_OK; -} - - -/** - * Finalise a BMP prior to destruction. - * - * \param bmp the BMP image to finalise - */ -void bmp_finalise(bmp_image *bmp) { - if (bmp->bitmap) - bmp->bitmap_callbacks.bitmap_destroy(bmp->bitmap); - bmp->bitmap = NULL; - if (bmp->colour_table) - free(bmp->colour_table); - bmp->colour_table = NULL; -} - - -/** - * Finalise an ICO prior to destruction. - * - * \param ico the ICO image to finalise - */ -void ico_finalise(ico_collection *ico) { - ico_image *image; - - for (image = ico->first; image; image = image->next) - bmp_finalise(&image->bmp); - while (ico->first) { - image = ico->first; - ico->first = image->next; - free(image); - } -} diff --git a/libnsbmp.h b/libnsbmp.h deleted file mode 100644 index 4bdda23..0000000 --- a/libnsbmp.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2006 Richard Wilson - * Copyright 2008 Sean Fox - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * - * NetSurf 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; version 2 of the License. - * - * NetSurf is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/** \file - * BMP file decoding (interface). - */ - -#ifndef _NETSURF_IMAGE_BMPREAD_H_ -#define _NETSURF_IMAGE_BMPREAD_H_ - -#include -#include - -/* bmp flags */ -#define BMP_NEW 0 -#define BMP_OPAQUE (1 << 0) /** image is opaque (as opposed to having an alpha mask) */ -#define BMP_CLEAR_MEMORY (1 << 1) /** memory should be wiped */ - -/* error return values */ -typedef enum { - BMP_OK = 0, - BMP_INSUFFICIENT_MEMORY = 1, - BMP_INSUFFICIENT_DATA = 2, - BMP_DATA_ERROR = 3 -} bmp_result; - -/* encoding types */ -typedef enum { - BMP_ENCODING_RGB = 0, - BMP_ENCODING_RLE8 = 1, - BMP_ENCODING_RLE4 = 2, - BMP_ENCODING_BITFIELDS = 3 -} bmp_encoding; - -/* API for Bitmap callbacks -*/ -typedef void* (*bmp_bitmap_cb_create)(int width, int height, unsigned int state); -typedef void (*bmp_bitmap_cb_destroy)(void *bitmap); -typedef void (*bmp_bitmap_cb_set_suspendable)(void *bitmap, void *private_word, - void (*invalidate)(void *bitmap, void *private_word)); -typedef unsigned char* (*bmp_bitmap_cb_get_buffer)(void *bitmap); -typedef size_t (*bmp_bitmap_cb_get_bpp)(void *bitmap); - -/* The Bitmap callbacks function table -*/ -typedef struct bmp_bitmap_callback_vt_s { - bmp_bitmap_cb_create bitmap_create; /**< Create a bitmap. */ - bmp_bitmap_cb_destroy bitmap_destroy; /**< Free a bitmap. */ - bmp_bitmap_cb_set_suspendable bitmap_set_suspendable; /**< The bitmap image can be suspended. */ - bmp_bitmap_cb_get_buffer bitmap_get_buffer; /**< Return a pointer to the pixel data in a bitmap. */ - bmp_bitmap_cb_get_bpp bitmap_get_bpp; /**< Find the width of a pixel row in bytes. */ -} bmp_bitmap_callback_vt; - -typedef struct bmp_image { - bmp_bitmap_callback_vt bitmap_callbacks; /**< callbacks for bitmap functions */ - uint8_t *bmp_data; /** pointer to BMP data */ - uint32_t width; /** width of BMP (valid after _analyse) */ - uint32_t height; /** heigth of BMP (valid after _analyse) */ - bool decoded; /** whether the image has been decoded */ - void *bitmap; /** decoded image */ - /** Internal members are listed below - */ - uint32_t buffer_size; /** total number of bytes of BMP data available */ - bmp_encoding encoding; /** pixel encoding type */ - uint32_t bitmap_offset; /** offset of bitmap data */ - uint16_t bpp; /** bits per pixel */ - uint32_t colours; /** number of colours */ - uint32_t *colour_table; /** colour table */ - bool limited_trans; /** whether to use bmp's limited transparency */ - uint32_t trans_colour; /** colour to display for "transparent" pixels when - * using limited transparency */ - bool reversed; /** scanlines are top to bottom */ - bool ico; /** image is part of an ICO, mask follows */ - bool opaque; /** true if the bitmap does not contain an alpha channel */ - uint32_t mask[4]; /** four bitwise mask */ - int32_t shift[4]; /** four bitwise shifts */ - uint32_t transparent_index; /** colour representing "transparency" in the bitmap */ -} bmp_image; - -typedef struct ico_image { - bmp_image bmp; - struct ico_image *next; -} ico_image; - -typedef struct ico_collection { - bmp_bitmap_callback_vt bitmap_callbacks; /**< callbacks for bitmap functions */ - uint16_t width; /** width of largest BMP */ - uint16_t height; /** heigth of largest BMP */ - /** Internal members are listed below - */ - uint8_t *ico_data; /** pointer to ICO data */ - uint32_t buffer_size; /** total number of bytes of ICO data available */ - ico_image *first; -} ico_collection; - -void bmp_create(bmp_image *gif, bmp_bitmap_callback_vt *bitmap_callbacks); -void ico_collection_create(ico_collection *ico, bmp_bitmap_callback_vt *bitmap_callbacks); -bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data); -bmp_result bmp_decode(bmp_image *bmp); -bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t transparent_colour); -void bmp_finalise(bmp_image *bmp); - -bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data); -bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height); -void ico_finalise(ico_collection *ico); - -#endif diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..c46200b --- /dev/null +++ b/src/Makefile @@ -0,0 +1,4 @@ +# Sources +DIR_SOURCES := libnsbmp.c + +include build/makefiles/Makefile.subdir diff --git a/src/libnsbmp.c b/src/libnsbmp.c new file mode 100644 index 0000000..7d1a81c --- /dev/null +++ b/src/libnsbmp.c @@ -0,0 +1,1126 @@ +/* + * Copyright 2006 Richard Wilson + * Copyright 2008 Sean Fox + * + * This file is part of NetSurf's libnsbmp, http://www.netsurf-browser.org/ + * Licenced under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "utils/log.h" + +/* The functions provided by this file allow for the decoding of + Microsoft's BMP and ICO image file formats. + + READING BMP FILES + ================= + + To begin decoding a BMP, the caller should initialise a + 'bmp_bitmap_callback_vt' structure with the appropriate values necessary + to handle bitmap images. Next, a 'bmp_image' structure should be + initialised by calling bmp_create(). This structure should then be + passed to bmp_analyse() along with the BMP data to process and the size + of this data. + + Once the analysis has begun, the decoder completes the width and height + variables. + + To decode the image, the caller must use bmp_decode() which selects the + proper decoding method based on the BMP info header and assigns the + decoded bitmap image to the 'bitmap' member of the 'bmp_image' + structure. The bitmap image is stored with 4 bytes-per-pixel in RGBA + format. + + It should be noted that bmp_finalise() should always be called, even if + the image was never decoded. It is also the responsibility of the + caller to free 'bmp_data'. + + READING ICO FILES + ================= + + To begin decoding an ICO, the caller should initialise a + 'bmp_bitmap_callback_vt' structure with the appropriate values necessary + to handle bitmap images. Next, an 'ico_collection' structure should be + initialised by calling ico_create(). This structure should then be + passed to ico_analyse() along with the ICO data to process and the size + of this data. + + Once the analysis has begun, the decoder completes the width and height + variables. Because ICO collections contain multiple bitmap images, the + width and height will contain the values of the largest available image. + + The caller then obtains a BMP from the ICO collection by calling + ico_find() with the requested width and height. + + To decode the image, the caller must use bmp_decode() which selects the + proper decoding method based on the BMP info header and assigns the + decoded bitmap image to the 'bitmap' member of the 'bmp_image' + structure. The bitmap image is stored with 4 bytes-per-pixel in RGBA + format. + + It should be noted that ico_finalise() should always be called, even if + no images were decoded. Because ico_finalise() calls bmp_finalise() for + each bitmap within the collection, the caller is not required to perform + this function. However, it is the responsibility of the caller to free + 'ico_data'. + + [dynis] - Tue 1st July 2008 +*/ + +/* squashes unused variable compiler warnings */ +#define UNUSED(x) ((x)=(x)) + +/* BMP flags */ +#define BMP_FILE_HEADER_SIZE 14 +#define ICO_FILE_HEADER_SIZE 6 +#define ICO_DIR_ENTRY_SIZE 16 + +static inline int8_t read_int8(uint8_t *data, unsigned int o) { + return (int8_t) data[o]; +} + +static inline uint8_t read_uint8(uint8_t *data, unsigned int o) { + return (uint8_t) data[o]; +} + +static inline int16_t read_int16(uint8_t *data, unsigned int o) { + return (int16_t) (data[o] | (data[o+1] << 8)); +} + +static inline uint16_t read_uint16(uint8_t *data, unsigned int o) { + return (uint16_t) (data[o] | (data[o+1] << 8)); +} + +static inline int32_t read_int32(uint8_t *data, unsigned int o) { + return (int32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); +} + +static inline uint32_t read_uint32(uint8_t *data, unsigned int o) { + return (uint32_t) (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)); +} + +static bmp_result next_ico_image(ico_collection *ico, ico_image *image); +static bmp_result bmp_analyse_header(bmp_image *bmp, unsigned char *data); +static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes); +static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes); +static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes); +static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes); +static bmp_result bmp_decode_rle(bmp_image *bmp, uint8_t *data, int bytes, int size); +static void bmp_invalidate(void *bitmap, void *private_word); + + + +/** Initialises necessary bmp_image members. +*/ +void bmp_create(bmp_image *bmp, bmp_bitmap_callback_vt *bitmap_callbacks) { + memset(bmp, 0, sizeof(bmp_image)); + bmp->bitmap_callbacks = *bitmap_callbacks; +} + + +/** Initialises necessary ico_collection members. +*/ +void ico_collection_create(ico_collection *ico, bmp_bitmap_callback_vt *bitmap_callbacks) { + memset(ico, 0, sizeof(ico_collection)); + ico->bitmap_callbacks = *bitmap_callbacks; +} + + +/** + * Analyse a BMP prior to decoding. + * + * This function will scan the data provided and perform simple checks to + * ensure the data is a valid BMP. + * + * This function must be called before bmp_decode() and sets up all the + * relevant values in the bmp structure. + * + * \param bmp the BMP image to analyse + * \return BMP_OK on success + */ +bmp_result bmp_analyse(bmp_image *bmp, size_t size, unsigned char *cdata) { + uint8_t *data = (uint8_t *)cdata; + + /* ensure we aren't already initialised */ + if (bmp->bitmap) + return BMP_OK; + + /* initialize values */ + bmp->buffer_size = size; + bmp->bmp_data = data; + + /* standard 14-byte BMP file header is: + * + * +0 UINT16 File Type ('BM') + * +2 UINT32 Size of File (in bytes) + * +6 INT16 Reserved Field (1) + * +8 INT16 Reserved Field (2) + * +10 UINT32 Starting Position of Image Data (offset in bytes) + */ + if (bmp->buffer_size < BMP_FILE_HEADER_SIZE) + return BMP_INSUFFICIENT_DATA; + if ((data[0] != (uint8_t)'B') || (data[1] != (uint8_t)'M')) + return BMP_DATA_ERROR; + bmp->bitmap_offset = read_uint32(data, 10); + data += BMP_FILE_HEADER_SIZE; + + /* boundary checking */ + if (bmp->bitmap_offset >= size) + return BMP_INSUFFICIENT_DATA; + + /* decode the BMP header */ + return bmp_analyse_header(bmp, data); +} + + +/** + * Analyse an ICO prior to decoding. + * + * This function will scan the data provided and perform simple checks to + * ensure the data is a valid ICO. + * + * This function must be called before ico_find(). + * + * \param ico the ICO image to analyse + * \return BMP_OK on success + */ +bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data) { + uint16_t count, i; + bmp_result result; + int area, max_area = 0; + + /* ensure we aren't already initialised */ + if (ico->first) + return BMP_OK; + + /* initialize values */ + ico->buffer_size = size; + ico->ico_data = data; + + /* 6-byte ICO file header is: + * + * +0 INT16 Reserved (should be 0) + * +2 UINT16 Type (1 for ICO, 2 for CUR) + * +4 UINT16 Number of BMPs to follow + */ + if (ico->buffer_size < ICO_FILE_HEADER_SIZE) + return BMP_INSUFFICIENT_DATA; +// if (read_int16(data, 2) != 0x0000) +// return BMP_DATA_ERROR; + if (read_uint16(data, 2) != 0x0001) + return BMP_DATA_ERROR; + count = read_uint16(data, 4); + if (count == 0) + return BMP_DATA_ERROR; + data += ICO_FILE_HEADER_SIZE; + + /* check if we have enough data for the directory */ + if (ico->buffer_size < (uint32_t)(ICO_FILE_HEADER_SIZE + (ICO_DIR_ENTRY_SIZE * count))) + return BMP_INSUFFICIENT_DATA; + + /* Decode the BMP files. + * + * 16-byte ICO directory entry is: + * + * +0 UINT8 Width (0 for 256 pixels) + * +1 UINT8 Height (0 for 256 pixels) + * +2 UINT8 Colour count (0 if more than 256 colours) + * +3 INT8 Reserved (should be 0, but may not be) + * +4 UINT16 Colour Planes (should be 0 or 1) + * +6 UINT16 Bits Per Pixel + * +8 UINT32 Size of BMP info header + bitmap data in bytes + * +12 UINT32 Offset (points to the BMP info header, not the bitmap data) + */ + for (i = 0; i < count; i++) { + ico_image *image; + image = calloc(1, sizeof(ico_image)); + if (!image) + return BMP_INSUFFICIENT_MEMORY; + result = next_ico_image(ico, image); + if (result != BMP_OK) + return result; + image->bmp.width = read_uint8(data, 0); + if (image->bmp.width == 0) + image->bmp.width = 256; + image->bmp.height = read_uint8(data, 1); + if (image->bmp.height == 0) + image->bmp.height = 256; + image->bmp.buffer_size = read_uint32(data, 8); + image->bmp.bmp_data = ico->ico_data + read_uint32(data, 12); + image->bmp.ico = true; + data += ICO_DIR_ENTRY_SIZE; + result = bmp_analyse_header(&image->bmp, image->bmp.bmp_data); + if (result != BMP_OK) + return result; + /* adjust the size based on the images available */ + area = image->bmp.width * image->bmp.height; + if (area > max_area) { + ico->width = image->bmp.width; + ico->height = image->bmp.height; + max_area = area; + } + } + return BMP_OK; +} + + +/** + * Allocates memory for the next BMP in an ICO collection + * + * Sets proper structure values + * + * \param ico the ICO collection to add the image to + * \param image a pointer to the ICO image to be initialised + */ +static bmp_result next_ico_image(ico_collection *ico, ico_image *image) { + bmp_create(&image->bmp, &ico->bitmap_callbacks); + image->next = ico->first; + ico->first = image; + return BMP_OK; +} + + +static bmp_result bmp_analyse_header(bmp_image *bmp, uint8_t *data) { + uint32_t header_size; + uint32_t i; + uint8_t j; + int32_t width, height; + uint32_t uheight; + uint8_t palette_size; + unsigned int flags; + + /* a variety of different bitmap headers can follow, depending + * on the BMP variant. A full description of the various headers + * can be found at + * http://msdn.microsoft.com/en-us/library/ms532301(VS.85).aspx + */ + header_size = read_uint32(data, 0); + if (bmp->buffer_size < (14 + header_size)) + return BMP_INSUFFICIENT_DATA; + if (header_size == 12) { + /* the following header is for os/2 and windows 2.x and consists of: + * + * +0 UINT32 size of this header (in bytes) + * +4 INT16 image width (in pixels) + * +6 INT16 image height (in pixels) + * +8 UINT16 number of colour planes (always 1) + * +10 UINT16 number of bits per pixel + */ + width = read_int16(data, 4); + height = read_int16(data, 6); + if ((width <= 0) || (height == 0)) + return BMP_DATA_ERROR; + if (height < 0) { + bmp->reversed = true; + height = -height; + } + /* ICOs only support 256*256 resolutions + * In the case of the ICO header, the height is actually the added + * height of XOR-Bitmap and AND-Bitmap (double the visible height) + * Technically we could remove this check and ICOs with bitmaps + * of any size could be processed; this is to conform to the spec. + */ + if (bmp->ico) { + if ((width > 256) || (height > 512)) { + return BMP_DATA_ERROR; + } else { + bmp->width = width; + bmp->height = height / 2; + } + } else { + bmp->width = width; + bmp->height = height; + } + if (read_uint16(data, 8) != 1) + return BMP_DATA_ERROR; + bmp->bpp = read_uint16(data, 10); + /** + * The bpp value should be in the range 1-32, but the only + * values considered legal are: + * RGB ENCODING: 1, 4, 8, 16, 24 and 32 + */ + if ((bmp->bpp != 1) && (bmp->bpp != 4) && + (bmp->bpp != 8) && + (bmp->bpp != 16) && + (bmp->bpp != 24) && + (bmp->bpp != 32)) + return BMP_DATA_ERROR; + bmp->colours = (1 << bmp->bpp); + palette_size = 3; + } else if (header_size < 40) { + return BMP_DATA_ERROR; + } else { + /* the following header is for windows 3.x and onwards. it is a + * minimum of 40 bytes and (as of Windows 95) a maximum of 108 bytes. + * + * +0 UINT32 size of this header (in bytes) + * +4 INT32 image width (in pixels) + * +8 INT32 image height (in pixels) + * +12 UINT16 number of colour planes (always 1) + * +14 UINT16 number of bits per pixel + * +16 UINT32 compression methods used + * +20 UINT32 size of bitmap (in bytes) + * +24 UINT32 horizontal resolution (in pixels per meter) + * +28 UINT32 vertical resolution (in pixels per meter) + * +32 UINT32 number of colours in the image + * +36 UINT32 number of important colours + * +40 UINT32 mask identifying bits of red component + * +44 UINT32 mask identifying bits of green component + * +48 UINT32 mask identifying bits of blue component + * +52 UINT32 mask identifying bits of alpha component + * +56 UINT32 color space type + * +60 UINT32 x coordinate of red endpoint + * +64 UINT32 y coordinate of red endpoint + * +68 UINT32 z coordinate of red endpoint + * +72 UINT32 x coordinate of green endpoint + * +76 UINT32 y coordinate of green endpoint + * +80 UINT32 z coordinate of green endpoint + * +84 UINT32 x coordinate of blue endpoint + * +88 UINT32 y coordinate of blue endpoint + * +92 UINT32 z coordinate of blue endpoint + * +96 UINT32 gamma red coordinate scale value + * +100 UINT32 gamma green coordinate scale value + * +104 UINT32 gamma blue coordinate scale value + */ + width = read_int32(data, 4); + height = read_int32(data, 8); + if ((width <= 0) || (height == 0)) + return BMP_DATA_ERROR; + if (height < 0) { + bmp->reversed = true; + uheight = -height; + } + /* ICOs only support 256*256 resolutions + * In the case of the ICO header, the height is actually the added + * height of XOR-Bitmap and AND-Bitmap (double the visible height) + * Technically we could remove this check and ICOs with bitmaps + * of any size could be processed; this is to conform to the spec. + */ + if (bmp->ico) { + if ((width > 256) || (height > 512)) { + return BMP_DATA_ERROR; + } else { + bmp->width = width; + bmp->height = height / 2; + } + } else { + bmp->width = width; + bmp->height = height; + } + if (read_uint16(data, 12) != 1) + return BMP_DATA_ERROR; + bmp->bpp = read_uint16(data, 14); + if (bmp->bpp == 0) + bmp->bpp = 8; + bmp->encoding = read_uint32(data, 16); + /** + * The bpp value should be in the range 1-32, but the only + * values considered legal are: + * RGB ENCODING: 1, 4, 8, 16, 24 and 32 + * RLE4 ENCODING: 4 + * RLE8 ENCODING: 8 + * BITFIELD ENCODING: 16 and 32 + */ + switch (bmp->encoding) { + case BMP_ENCODING_RGB: + if ((bmp->bpp != 1) && (bmp->bpp != 4) && + (bmp->bpp != 8) && + (bmp->bpp != 16) && + (bmp->bpp != 24) && + (bmp->bpp != 32)) + return BMP_DATA_ERROR; + break; + case BMP_ENCODING_RLE8: + if (bmp->bpp != 8) + return BMP_DATA_ERROR; + break; + case BMP_ENCODING_RLE4: + if (bmp->bpp != 4) + return BMP_DATA_ERROR; + break; + case BMP_ENCODING_BITFIELDS: + if ((bmp->bpp != 16) && (bmp->bpp != 32)) + return BMP_DATA_ERROR; + break; + /* invalid encoding */ + default: + return BMP_DATA_ERROR; + break; + } + /* Bitfield encoding means we have red, green, blue, and alpha masks. + * Here we aquire the masks and determine the required bit shift to + * align them in our 24-bit color 8-bit alpha format. + */ + if (bmp->encoding == BMP_ENCODING_BITFIELDS) { + if (header_size == 40) { + header_size += 12; + if (bmp->buffer_size < (14 + header_size)) + return BMP_INSUFFICIENT_DATA; + for (i = 0; i < 3; i++) + bmp->mask[i] = read_uint32(data, 40 + (i << 2)); + } else { + for (i = 0; i < 4; i++) + bmp->mask[i] = read_uint32(data, 40 + (i << 2)); + } + for (i = 0; i < 4; i++) { + if (bmp->mask[i] == 0) + break; + for (j = 31; j > 0; j--) + if (bmp->mask[i] & (1 << j)) { + if ((j - 7) > 0) + bmp->mask[i] &= 0xff << (j - 7); + else + bmp->mask[i] &= 0xff >> (-(j - 7)); + bmp->shift[i] = (i << 3) - (j - 7); + break; + } + } + } + bmp->colours = read_uint32(data, 32); + if (bmp->colours == 0) + bmp->colours = (1 << bmp->bpp); + palette_size = 4; + } + data += header_size; + + /* if there's no alpha mask, flag the bmp opaque */ + if ((!bmp->ico) && (bmp->mask[3] == 0)) { + flags |= BMP_OPAQUE; + bmp->opaque = true; + } + + /* we only have a palette for <16bpp */ + if (bmp->bpp < 16) { + /* we now have a series of palette entries of the format: + * + * +0 BYTE blue + * +1 BYTE green + * +2 BYTE red + * + * if the palette is from an OS/2 or Win2.x file then the entries + * are padded with an extra byte. + */ + + /* boundary checking */ + if (bmp->buffer_size < (14 + header_size + ((uint64_t)4 * bmp->colours))) + return BMP_INSUFFICIENT_DATA; + + /* create the colour table */ + bmp->colour_table = (uint32_t *)malloc(bmp->colours * 4); + if (!bmp->colour_table) + return BMP_INSUFFICIENT_MEMORY; + for (i = 0; i < bmp->colours; i++) { + bmp->colour_table[i] = data[2] | (data[1] << 8) | (data[0] << 16); + if (bmp->opaque) + bmp->colour_table[i] |= (0xff << 24); + data += palette_size; + } + } + + /* create our bitmap */ + flags |= BMP_NEW | BMP_CLEAR_MEMORY; + bmp->bitmap = bmp->bitmap_callbacks.bitmap_create(bmp->width, bmp->height, flags); + if (!bmp->bitmap) { + if (bmp->colour_table) + free(bmp->colour_table); + bmp->colour_table = NULL; + return BMP_INSUFFICIENT_MEMORY; + } + /* BMPs within ICOs don't have BMP file headers, so the image data should + * always be right after the colour table. + */ + if (bmp->ico) + bmp->bitmap_offset = (intptr_t)data - (intptr_t)bmp->bmp_data; + bmp->bitmap_callbacks.bitmap_set_suspendable(bmp->bitmap, bmp, bmp_invalidate); + return BMP_OK; +} + + +/** + * Finds the closest BMP within an ICO collection + * + * This function finds the BMP with dimensions as close to a specified set + * as possible from the images in the collection. + * + * \param ico the ICO collection to examine + * \param width the preferred width (0 to use ICO header width) + * \param height the preferred height (0 to use ICO header height) + */ +bmp_image *ico_find(ico_collection *ico, uint16_t width, uint16_t height) { + bmp_image *bmp = NULL; + ico_image *image; + int x, y, cur, distance = (1 << 24); + + if (width == 0) + width = ico->width; + if (height == 0) + height = ico->height; + for (image = ico->first; image; image = image->next) { + if ((image->bmp.width == width) && (image->bmp.height == height)) + return &image->bmp; + x = image->bmp.width - width; + y = image->bmp.height - height; + cur = (x * x) + (y * y); + if (cur < distance) { + distance = cur; + bmp = &image->bmp; + } + } + return bmp; +} + + +/** + * Invalidates a BMP + * + * This function sets the BMP into a state such that the bitmap image data + * can be released from memory. + * + * \param bmp the BMP image to invalidate + */ +static void bmp_invalidate(void *bitmap, void *private_word) { + bmp_image *bmp = (bmp_image *)private_word; + UNUSED(bitmap); + + bmp->decoded = false; +} + + +/** + * Decode a BMP + * + * This function decodes the BMP data such that bmp->bitmap is a valid + * image. The state of bmp->decoded is set to TRUE on exit such that it + * can easily be identified which BMPs are in a fully decoded state. + * + * \param bmp the BMP image to decode + * \return BMP_OK on success + */ +bmp_result bmp_decode(bmp_image *bmp) { + uint8_t *data; + uint32_t bytes; + bmp_result result = BMP_OK; + + assert(bmp->bitmap); + + data = bmp->bmp_data + bmp->bitmap_offset; + bytes = bmp->buffer_size - bmp->bitmap_offset; + + switch (bmp->encoding) { + case BMP_ENCODING_RGB: + if ((bmp->bpp == 24) || (bmp->bpp == 32)) + result = bmp_decode_rgb24(bmp, &data, bytes); + else if (bmp->bpp == 16) + result = bmp_decode_rgb16(bmp, &data, bytes); + else + result = bmp_decode_rgb(bmp, &data, bytes); + break; + case BMP_ENCODING_RLE8: + result = bmp_decode_rle(bmp, data, bytes, 8); + break; + case BMP_ENCODING_RLE4: + result = bmp_decode_rle(bmp, data, bytes, 4); + break; + case BMP_ENCODING_BITFIELDS: + if (bmp->bpp == 32) + result = bmp_decode_rgb24(bmp, &data, bytes); + else if (bmp->bpp == 16) + result = bmp_decode_rgb16(bmp, &data, bytes); + else + return BMP_DATA_ERROR; + } + + if ((!bmp->ico) || (result != BMP_OK)) + return result; + + bytes = (uintptr_t)bmp->bmp_data + bmp->buffer_size - (uintptr_t)data; + return bmp_decode_mask(bmp, data, bytes); +} + +/** + * Decode a BMP using "limited transparency" + * + * Bitmaps do not have native transparency support. However, there is a + * "trick" that is used in some instances in which the first pixel of the + * bitmap becomes the "transparency index". The decoding application can + * replace this index with whatever background colour it chooses to + * create the illusion of transparency. + * + * When to use transparency is at the discretion of the decoding + * application. + * + * \param bmp the BMP image to decode + * \param colour the colour to use as "transparent" + * \return BMP_OK on success + */ +bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour) { + bmp->limited_trans = true; + bmp->trans_colour = colour; + return bmp_decode(bmp); +} + + +/** + * Decode BMP data stored in 24bpp colour. + * + * \param bmp the BMP image to decode + * \param start the data to decode, updated to last byte read on success + * \param bytes the number of bytes of data available + * \return BMP_OK on success + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result bmp_decode_rgb24(bmp_image *bmp, uint8_t **start, int bytes) { + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; + uint32_t x, y; + uint32_t swidth, skip; + intptr_t addr; + uint8_t i; + uint32_t word; + + data = *start; + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + addr = ((intptr_t)data) & 3; + skip = bmp->bpp >> 3; + bmp->decoded = true; + + /* Determine transparent index */ + if (bmp->limited_trans) { + if ((data + skip) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->encoding == BMP_ENCODING_BITFIELDS) + bmp->transparent_index = read_uint32(data, 0); + else + bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16); + } + + for (y = 0; y < bmp->height; y++) { + while (addr != (((intptr_t)data) & 3)) + data++; + if ((data + (skip * bmp->width)) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->reversed) + scanline = (uint32_t *)(top + (y * swidth)); + else + scanline = (uint32_t *)(bottom - (y * swidth)); + if (bmp->encoding == BMP_ENCODING_BITFIELDS) { + for (x = 0; x < bmp->width; x++) { + word = read_uint32(data, 0); + for (i = 0; i < 4; i++) + if (bmp->shift[i] > 0) + scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); + else + scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); + /* 32-bit BMPs have alpha masks, but sometimes they're not utilized */ + if (bmp->opaque) + scanline[x] |= (0xff << 24); + data += skip; + } + } else { + for (x = 0; x < bmp->width; x++) { + scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16); + if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) + scanline[x] = bmp->trans_colour; + if (bmp->opaque) + scanline[x] |= (0xff << 24); + data += skip; + } + } + } + *start = data; + return BMP_OK; +} + + +/** + * Decode BMP data stored in 16bpp colour. + * + * \param bmp the BMP image to decode + * \param start the data to decode, updated to last byte read on success + * \param bytes the number of bytes of data available + * \return BMP_OK on success + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result bmp_decode_rgb16(bmp_image *bmp, uint8_t **start, int bytes) { + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; + uint32_t x, y, swidth; + intptr_t addr; + uint8_t i; + uint16_t word; + + data = *start; + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + addr = ((intptr_t)data) & 3; + bmp->decoded = true; + + /* Determine transparent index */ + if (bmp->limited_trans) { + if ((data + 2) > end) + return BMP_INSUFFICIENT_DATA; + bmp->transparent_index = read_uint16(data, 0); + } + + for (y = 0; y < bmp->height; y++) { + while (addr != (((intptr_t)data) & 3)) + data += 2; + if ((data + (2 * bmp->width)) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->reversed) + scanline = (uint32_t *)(top + (y * swidth)); + else + scanline = (uint32_t *)(bottom - (y * swidth)); + if (bmp->encoding == BMP_ENCODING_BITFIELDS) { + for (x = 0; x < bmp->width; x++) { + word = read_uint16(data, 0); + if ((bmp->limited_trans) && (word == bmp->transparent_index)) + scanline[x] = bmp->trans_colour; + else { + scanline[x] = 0; + for (i = 0; i < 4; i++) + if (bmp->shift[i] > 0) + scanline[x] |= ((word & bmp->mask[i]) << bmp->shift[i]); + else + scanline[x] |= ((word & bmp->mask[i]) >> (-bmp->shift[i])); + if (bmp->opaque) + scanline[x] |= (0xff << 24); + } + data += 2; + } + } else { + for (x = 0; x < bmp->width; x++) { + word = read_uint16(data, 0); + if ((bmp->limited_trans) && (word == bmp->transparent_index)) + scanline[x] = bmp->trans_colour; + else { + /* 16-bit RGB defaults to RGB555 */ + scanline[x] = ((word & (31 << 0)) << 19) | + ((word & (31 << 5)) << 6) | + ((word & (31 << 10)) >> 7); + } + if (bmp->opaque) + scanline[x] |= (0xff << 24); + data += 2; + } + } + } + *start = data; + return BMP_OK; +} + + +/** + * Decode BMP data stored with a palette and in 8bpp colour or less. + * + * \param bmp the BMP image to decode + * \param start the data to decode, updated to last byte read on success + * \param bytes the number of bytes of data available + * \return BMP_OK on success + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result bmp_decode_rgb(bmp_image *bmp, uint8_t **start, int bytes) { + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; + intptr_t addr; + uint32_t x, y, swidth; + uint8_t bit_shifts[8]; + uint8_t ppb = 8 / bmp->bpp; + uint8_t bit_mask = (1 << bmp->bpp) - 1; + uint8_t cur_byte = 0, bit, i; + + for (i = 0; i < ppb; i++) + bit_shifts[i] = 8 - ((i + 1) * bmp->bpp); + + data = *start; + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + addr = ((intptr_t)data) & 3; + bmp->decoded = true; + + /* Determine transparent index */ + if (bmp->limited_trans) + bmp->transparent_index = bmp->colour_table[(*data >> bit_shifts[0]) & bit_mask]; + + for (y = 0; y < bmp->height; y++) { + while (addr != (((intptr_t)data) & 3)) + data++; + bit = 8; + if ((data + (bmp->width / ppb)) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->reversed) + scanline = (unsigned int *)(top + (y * swidth)); + else + scanline = (unsigned int *)(bottom - (y * swidth)); + for (x = 0; x < bmp->width; x++) { + if (bit >= ppb) { + bit = 0; + cur_byte = *data++; + } + scanline[x] = bmp->colour_table[(cur_byte >> bit_shifts[bit++]) & bit_mask]; + if ((bmp->limited_trans) && (scanline[x] == bmp->transparent_index)) + scanline[x] = bmp->trans_colour; + } + } + *start = data; + return BMP_OK; +} + + +/** + * Decode a 1bpp mask for an ICO + * + * \param bmp the BMP image to decode + * \param data the data to decode + * \param bytes the number of bytes of data available + * \return BMP_OK on success + */ +static bmp_result bmp_decode_mask(bmp_image *bmp, uint8_t *data, int bytes) { + uint8_t *top, *bottom, *end; + uint32_t *scanline; + intptr_t addr; + uint32_t x, y, swidth; + uint32_t cur_byte = 0; + + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + addr = ((intptr_t)data) & 3; + + for (y = 0; y < bmp->height; y++) { + while (addr != (((intptr_t)data) & 3)) + data++; + if ((data + (bmp->width >> 3)) > end) + return BMP_INSUFFICIENT_DATA; + scanline = (uint32_t *)(bottom - (y * swidth)); + for (x = 0; x < bmp->width; x++) { + if ((x & 7) == 0) + cur_byte = *data++; + if ((cur_byte & 128) == 0) + scanline[x] |= (0xff << 24); + cur_byte = cur_byte << 1; + } + } + return BMP_OK; +} + + +/** + * Decode BMP data stored encoded in either RLE4 or RLE8. + * + * \param bmp the BMP image to decode + * \param data the data to decode + * \param bytes the number of bytes of data available + * \param size the size of the RLE tokens (4 or 8) + * \return BMP_OK on success + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result bmp_decode_rle(bmp_image *bmp, uint8_t *data, int bytes, int size) { + uint8_t *top, *bottom, *end; + uint32_t *scanline; + uint32_t swidth; + uint32_t i, length, pixels_left; + uint32_t x = 0, y = 0, last_y = 0; + uint32_t pixel = 0, pixel2; + + if (bmp->ico) + return BMP_DATA_ERROR; + + swidth = bmp->bitmap_callbacks.bitmap_get_bpp(bmp->bitmap) * bmp->width; + top = bmp->bitmap_callbacks.bitmap_get_buffer(bmp->bitmap); + if (!top) + return BMP_INSUFFICIENT_MEMORY; + bottom = top + (uint64_t)swidth * (bmp->height - 1); + end = data + bytes; + bmp->decoded = true; + + do { + if (data + 2 > end) + return BMP_INSUFFICIENT_DATA; + length = *data++; + if (length == 0) { + length = *data++; + if (length == 0) { + /* 00 - 00 means end of scanline */ + x = 0; + if (last_y == y) { + if (++y > bmp->height) + return BMP_DATA_ERROR; + } + last_y = y; + } else if (length == 1) { + /* 00 - 01 means end of RLE data */ + return BMP_OK; + } else if (length == 2) { + /* 00 - 02 - XX - YY means move cursor */ + if (data + 2 > end) + return BMP_INSUFFICIENT_DATA; + x += *data++; + if (x >= bmp->width) + return BMP_DATA_ERROR; + y += *data++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + } else { + /* 00 - NN means escape NN pixels */ + if (bmp->reversed) { + pixels_left = (y + 1) * bmp->width - x; + scanline = (unsigned int *)(top + (y * swidth)); + } else { + pixels_left = (bmp->height - y + 1) * bmp->width - x; + scanline = (unsigned int *)(bottom - (y * swidth)); + } + if (length > pixels_left) + length = pixels_left; + if (data + length > end) + return BMP_INSUFFICIENT_DATA; + + /* the following code could be easily optimised by simply + * checking the bounds on entry and using some simply copying + * routines if so */ + if (size == 8) { + for (i = 0; i < length; i++) { + if (x >= bmp->width) { + x = 0; + if (++y > bmp->height) + return BMP_DATA_ERROR; + scanline -= bmp->width; + } + scanline[x++] = bmp->colour_table[(int)*data++]; + } + } else { + for (i = 0; i < length; i++) { + if (x >= bmp->width) { + x = 0; + if (++y > bmp->height) + return BMP_DATA_ERROR; + scanline -= bmp->width; + } + if ((i & 1) == 0) { + pixel = *data++; + scanline[x++] = bmp->colour_table + [pixel >> 4]; + } else { + scanline[x++] = bmp->colour_table + [pixel & 0xf]; + } + } + length = (length + 1) >> 1; + } + if ((length & 1) && (*data++ != 0x00)) + return BMP_DATA_ERROR; + + } + } else { + /* NN means perform RLE for NN pixels */ + if (bmp->reversed) { + pixels_left = (y + 1) * bmp->width - x; + scanline = (unsigned int *)(top + (y * swidth)); + } else { + pixels_left = (bmp->height - y + 1) * bmp->width - x; + scanline = (unsigned int *)(bottom - (y * swidth)); + } + if (length > pixels_left) + length = pixels_left; + + /* boundary checking */ + if (data + 1 > end) + return BMP_INSUFFICIENT_DATA; + + /* the following code could be easily optimised by simply + * checking the bounds on entry and using some simply copying + * routines if so */ + if (size == 8) { + pixel = bmp->colour_table[(int)*data++]; + for (i = 0; i < length; i++) { + if (x >= bmp->width) { + x = 0; + if (++y > bmp->height) + return BMP_DATA_ERROR; + scanline -= bmp->width; + } + scanline[x++] = pixel; + } + } else { + pixel2 = *data++; + pixel = bmp->colour_table[pixel2 >> 4]; + pixel2 = bmp->colour_table[pixel2 & 0xf]; + for (i = 0; i < length; i++) { + if (x >= bmp->width) { + x = 0; + if (++y > bmp->height) + return BMP_DATA_ERROR; + scanline -= bmp->width; + } + if ((i & 1) == 0) + scanline[x++] = pixel; + else + scanline[x++] = pixel2; + } + } + } + } while (data < end); + return BMP_OK; +} + + +/** + * Finalise a BMP prior to destruction. + * + * \param bmp the BMP image to finalise + */ +void bmp_finalise(bmp_image *bmp) { + if (bmp->bitmap) + bmp->bitmap_callbacks.bitmap_destroy(bmp->bitmap); + bmp->bitmap = NULL; + if (bmp->colour_table) + free(bmp->colour_table); + bmp->colour_table = NULL; +} + + +/** + * Finalise an ICO prior to destruction. + * + * \param ico the ICO image to finalise + */ +void ico_finalise(ico_collection *ico) { + ico_image *image; + + for (image = ico->first; image; image = image->next) + bmp_finalise(&image->bmp); + while (ico->first) { + image = ico->first; + ico->first = image->next; + free(image); + } +} diff --git a/src/utils/log.h b/src/utils/log.h new file mode 100644 index 0000000..b63f084 --- /dev/null +++ b/src/utils/log.h @@ -0,0 +1,27 @@ +/* + * Copyright 2003 James Bursa + * Copyright 2004 John Tytgat + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * Licenced under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + */ + +#include + +#ifndef _LIBNSBMP_LOG_H_ +#define _LIBNSBMP_LOG_H_ + +#ifdef NDEBUG +# define LOG(x) ((void) 0) +#else +# ifdef __GNUC__ +# define LOG(x) do { printf x, fputc('\n', stdout)); } while (0) +# elif defined(__CC_NORCROFT) +# define LOG(x) do { printf x, fputc('\n', stdout)); } while (0) +# else +# define LOG(x) do { printf x, fputc('\n', stdout)); } while (0) +# endif +#endif + +#endif diff --git a/utils/log.h b/utils/log.h deleted file mode 100644 index b63f084..0000000 --- a/utils/log.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2003 James Bursa - * Copyright 2004 John Tytgat - * - * This file is part of NetSurf, http://www.netsurf-browser.org/ - * Licenced under the MIT License, - * http://www.opensource.org/licenses/mit-license.php - */ - -#include - -#ifndef _LIBNSBMP_LOG_H_ -#define _LIBNSBMP_LOG_H_ - -#ifdef NDEBUG -# define LOG(x) ((void) 0) -#else -# ifdef __GNUC__ -# define LOG(x) do { printf x, fputc('\n', stdout)); } while (0) -# elif defined(__CC_NORCROFT) -# define LOG(x) do { printf x, fputc('\n', stdout)); } while (0) -# else -# define LOG(x) do { printf x, fputc('\n', stdout)); } while (0) -# endif -#endif - -#endif -- cgit v1.2.3