diff --git a/bmp.hpp b/bmp.hpp index 0ac2cf4..251a364 100644 --- a/bmp.hpp +++ b/bmp.hpp @@ -241,7 +241,7 @@ struct BMP { uint32_t channels = bmp_info_header.bit_count / 8; for (uint32_t y = y0; y < y0 + h; ++y) { for (uint32_t x = x0; x < x0 + w; ++x) { - if (!(x + w > (uint32_t)bmp_info_header.width) || !(y + h > (uint32_t)-1)) { + if (!(x + w > (uint32_t)bmp_info_header.width) || this->bmp_info_header.height - y - h < (uint32_t)0) { data[channels * (y * bmp_info_header.width + x) + 0] = B; data[channels * (y * bmp_info_header.width + x) + 1] = G; @@ -549,6 +549,21 @@ struct BMP { } } + void set_pixel_df(uint32_t x0, uint32_t y0, uint8_t B, uint8_t G, uint8_t R, uint8_t A) { + uint32_t y1 = this->bmp_info_header.height - y0; + if (x0 >= (uint32_t)bmp_info_header.width || y1 <= (uint32_t)bmp_info_header.width || x0 < 0 || y0 > 0) { + return;//throw std::runtime_error("The point is outside the image boundaries!"); + } + + uint32_t channels = bmp_info_header.bit_count / 8; + data[channels * (y0 * bmp_info_header.width + x0) + 0] = B; + data[channels * (y0 * bmp_info_header.width + x0) + 1] = G; + data[channels * (y0 * bmp_info_header.width + x0) + 2] = R; + if (channels == 4) { + data[channels * (y0 * bmp_info_header.width + x0) + 3] = A; + } + } + void draw_rectangle(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, uint8_t B, uint8_t G, uint8_t R, uint8_t A, uint8_t line_w) { if (x0 + w > (uint32_t)bmp_info_header.width || y0 + h > (uint32_t)bmp_info_header.height) { @@ -561,6 +576,18 @@ struct BMP { fill_region(x0, (y0 + line_w), line_w, (h - (2 * line_w)), B, G, R, A); // left line } + void draw_rectangle_df(uint32_t x0, uint32_t y0, uint32_t w, uint32_t h, + uint8_t B, uint8_t G, uint8_t R, uint8_t A, uint8_t line_w) { + if (x0 + w > (uint32_t)bmp_info_header.width || y0 + h > (uint32_t)bmp_info_header.height) { + return;//throw std::runtime_error("The rectangle does not fit in the image!"); + } + + fill_region_df(x0, y0, w, line_w, B, G, R, A); // top line + fill_region_df(x0, (y0 + h - line_w), w, line_w, B, G, R, A); // bottom line + fill_region_df((x0 + w - line_w), (y0 + line_w), line_w, (h - (2 * line_w)), B, G, R, A); // right line + fill_region_df(x0, (y0 + line_w), line_w, (h - (2 * line_w)), B, G, R, A); // left line + } + private: uint32_t row_stride{ 0 }; diff --git a/external/libnsbmp/COPYING b/external/libnsbmp/COPYING new file mode 100644 index 0000000..5e3d030 --- /dev/null +++ b/external/libnsbmp/COPYING @@ -0,0 +1,20 @@ +Copyright (C) 2006 Richard Wilson +Copyright (C) 2008 Sean Fox + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/external/libnsbmp/libnsbmp.c b/external/libnsbmp/libnsbmp.c new file mode 100644 index 0000000..378a20f --- /dev/null +++ b/external/libnsbmp/libnsbmp.c @@ -0,0 +1,1388 @@ +/* + * 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 decoding implementation + * + * This library decode windows bitmaps and icons from their disc images. + * + * The image format is described in several documents: + * https://msdn.microsoft.com/en-us/library/dd183391(v=vs.85).aspx + * http://www.fileformat.info/format/bmp/egff.htm + * https://en.wikipedia.org/wiki/BMP_file_format + * + * Despite the format being clearly defined many bitmaps found on the web are + * not compliant and this implementation attempts to cope with as many issues + * as possible rather than simply failing. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "log.h" + +/* squashes unused variable compiler warnings */ +#define UNUSED(x) ((x)=(x)) + +/* BMP entry sizes */ +#define BMP_FILE_HEADER_SIZE 14 +#define ICO_FILE_HEADER_SIZE 6 +#define ICO_DIR_ENTRY_SIZE 16 + +/* the bitmap information header types (encoded as lengths) */ +#define BITMAPCOREHEADER 12 + +#ifdef WE_NEED_INT8_READING_NOW +static inline int8_t read_int8(uint8_t *data, unsigned int o) { + return (int8_t) data[o]; +} +#endif + +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) ((unsigned)data[o] | + ((unsigned)data[o+1] << 8) | + ((unsigned)data[o+2] << 16) | + ((unsigned)data[o+3] << 24)); +} + +static inline uint32_t read_uint32(uint8_t *data, unsigned int o) { + return (uint32_t) ((unsigned)data[o] | + ((unsigned)data[o+1] << 8) | + ((unsigned)data[o+2] << 16) | + ((unsigned)data[o+3] << 24)); +} + + +/** + * Parse the bitmap info header + */ +static bmp_result bmp_info_header_parse(bmp_image *bmp, uint8_t *data) +{ + uint32_t header_size; + uint32_t i; + uint8_t j; + int32_t width, height; + uint8_t palette_size; + unsigned int flags = 0; + + /* must be at least enough data for a core header */ + if (bmp->buffer_size < (BMP_FILE_HEADER_SIZE + BITMAPCOREHEADER)) { + return BMP_INSUFFICIENT_DATA; + } + + header_size = read_uint32(data, 0); + + /* ensure there is enough data for the declared header size*/ + if ((bmp->buffer_size - BMP_FILE_HEADER_SIZE) < header_size) { + return BMP_INSUFFICIENT_DATA; + } + + /* a variety of different bitmap headers can follow, depending + * on the BMP variant. The header length field determines the type. + */ + if (header_size == BITMAPCOREHEADER) { + /* 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; + if (bmp->bpp < 16) + 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; + if (height <= -INT32_MAX) { + height = INT32_MAX; + } else { + 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, 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 acquire 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 { + if (header_size < 56) + return BMP_INSUFFICIENT_DATA; + 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] & ((unsigned)1 << j)) { + if ((j - 7) > 0) + bmp->mask[i] &= (unsigned)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->bpp < 16) + 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++) { + uint32_t colour = data[2] | (data[1] << 8) | (data[0] << 16); + if (bmp->opaque) + colour |= ((uint32_t)0xff << 24); + data += palette_size; + bmp->colour_table[i] = read_uint32((uint8_t *)&colour,0); + } + + /* some bitmaps have a bad offset if there is a pallete, work + * round this by fixing up the data offset to after the palette + * but only if there is data following the palette as some + * bitmaps encode data in the palette! + */ + if ((bmp->bitmap_offset < (uint32_t)(data - bmp->bmp_data)) && + ((bmp->buffer_size - (data - bmp->bmp_data)) > 0)) { + bmp->bitmap_offset = data - bmp->bmp_data; + } + } + + /* 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; + return BMP_OK; +} + + +/** + * Parse the bitmap file header + * + * \param bmp The bitmap. + * \param data The data for the file header + * \return BMP_OK on success or error code on faliure + */ +static bmp_result bmp_file_header_parse(bmp_image *bmp, uint8_t *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); + + /* check the offset to data lies within the file */ + if (bmp->bitmap_offset >= bmp->buffer_size) { + return BMP_INSUFFICIENT_DATA; + } + + 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; +} + + +/** + * Parse the icon file header + * + * \param ico The icon collection. + * \param data The header data to parse. + * \return BMP_OK on successful parse else error code + */ +static bmp_result ico_header_parse(ico_collection *ico, uint8_t *data) +{ + uint16_t count, i; + bmp_result result; + int area, max_area = 0; + + /* 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); + if (image->bmp.bmp_data + image->bmp.buffer_size > + ico->ico_data + ico->buffer_size) + return BMP_INSUFFICIENT_DATA; + image->bmp.ico = true; + data += ICO_DIR_ENTRY_SIZE; + + /* Ensure that the bitmap data resides in the buffer */ + if (image->bmp.bmp_data - ico->ico_data >= 0 && + (uint32_t)(image->bmp.bmp_data - + ico->ico_data) >= ico->buffer_size) + return BMP_DATA_ERROR; + + /* Ensure that we have sufficient data to read the bitmap */ + if (image->bmp.buffer_size - ICO_DIR_ENTRY_SIZE >= + ico->buffer_size - (ico->ico_data - data)) + return BMP_INSUFFICIENT_DATA; + + result = bmp_info_header_parse(&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; +} + + +/** + * Decode BMP data stored in 32bpp 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_rgb32(bmp_image *bmp, uint8_t **start, int bytes) +{ + uint8_t *top, *bottom, *end, *data; + uint32_t *scanline; + uint32_t x, y; + uint32_t swidth; + uint8_t i; + uint32_t word; + + assert(bmp->bpp == 32); + + 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; + bmp->decoded = true; + + /* Determine transparent index */ + if (bmp->limited_trans) { + if ((data + 4) > 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++) { + if ((data + (4 * bmp->width)) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->reversed) + scanline = (void *)(top + (y * swidth)); + else + scanline = (void *)(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] |= ((unsigned)0xff << 24); + data += 4; + scanline[x] = read_uint32((uint8_t *)&scanline[x],0); + } + } 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] |= ((unsigned)0xff << 24); + } else { + scanline[x] |= (unsigned)data[3] << 24; + } + data += 4; + scanline[x] = read_uint32((uint8_t *)&scanline[x],0); + } + } + } + *start = data; + return BMP_OK; +} + + +/** + * 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; + intptr_t addr; + + assert(bmp->encoding == BMP_ENCODING_RGB); + assert(bmp->bpp == 24); + + 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 + 3) > end) { + return BMP_INSUFFICIENT_DATA; + } + + bmp->transparent_index = data[2] | (data[1] << 8) | (data[0] << 16); + } + + for (y = 0; y < bmp->height; y++) { + if ((data + (3 * bmp->width)) > end) { + return BMP_INSUFFICIENT_DATA; + } + + if (bmp->reversed) { + scanline = (void *)(top + (y * swidth)); + } else { + scanline = (void *)(bottom - (y * swidth)); + } + + 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; + } else { + scanline[x] |= ((uint32_t)0xff << 24); + } + data += 3; + scanline[x] = read_uint32((uint8_t *)&scanline[x],0); + } + + while (addr != (((intptr_t)data) & 3)) { + data++; + } + } + *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++) { + if ((data + (2 * bmp->width)) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->reversed) + scanline = (void *)(top + (y * swidth)); + else + scanline = (void *)(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] |= ((unsigned)0xff << 24); + } + data += 2; + scanline[x] = read_uint32((uint8_t *)&scanline[x],0); + } + } 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] |= ((unsigned)0xff << 24); + data += 2; + scanline[x] = read_uint32((uint8_t *)&scanline[x],0); + } + } + while (addr != (((intptr_t)data) & 3)) + 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; + + /* Belt and braces, we shouldn't get here unless this holds */ + assert(ppb >= 1); + + 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) { + uint32_t idx = (*data >> bit_shifts[0]) & bit_mask; + if (idx >= bmp->colours) + return BMP_DATA_ERROR; + bmp->transparent_index = bmp->colour_table[idx]; + } + + for (y = 0; y < bmp->height; y++) { + bit = 8; + if ((data + ((bmp->width + ppb - 1) / ppb)) > end) + return BMP_INSUFFICIENT_DATA; + if (bmp->reversed) + scanline = (void *)(top + (y * swidth)); + else + scanline = (void *)(bottom - (y * swidth)); + for (x = 0; x < bmp->width; x++) { + uint32_t idx; + if (bit >= ppb) { + bit = 0; + cur_byte = *data++; + } + idx = (cur_byte >> bit_shifts[bit++]) & bit_mask; + if (idx < bmp->colours) { + /* ensure colour table index is in bounds */ + scanline[x] = bmp->colour_table[idx]; + if ((bmp->limited_trans) && + (scanline[x] == bmp->transparent_index)) { + scanline[x] = bmp->trans_colour; + } + } + } + while (addr != (((intptr_t)data) & 3)) + data++; + } + *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++) { + if ((data + (bmp->width >> 3)) > end) + return BMP_INSUFFICIENT_DATA; + scanline = (void *)(bottom - (y * swidth)); + for (x = 0; x < bmp->width; x++) { + if ((x & 7) == 0) + cur_byte = *data++; + scanline[x] = read_uint32((uint8_t *)&scanline[x], 0); + if ((cur_byte & 128) == 0) { + scanline[x] |= ((unsigned)0xff << 24); + } else { + scanline[x] &= 0xffffff; + } + scanline[x] = read_uint32((uint8_t *)&scanline[x], 0); + cur_byte = cur_byte << 1; + } + while (addr != (((intptr_t)data) & 3)) + data++; + } + return BMP_OK; +} + + +/** + * Decode BMP data stored encoded in RLE8. + * + * \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 + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result +bmp_decode_rle8(bmp_image *bmp, uint8_t *data, int bytes) +{ + 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; + + 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++; + switch (length) { + case 0: + /* 00 - 00 means end of scanline */ + x = 0; + if (last_y == y) { + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + } + last_y = y; + break; + + case 1: + /* 00 - 01 means end of RLE data */ + return BMP_OK; + + case 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; + break; + + default: + /* 00 - NN means escape NN pixels */ + if (bmp->reversed) { + pixels_left = (bmp->height - y) * bmp->width - x; + scanline = (void *)(top + (y * swidth)); + } else { + pixels_left = (y + 1) * bmp->width - x; + scanline = (void *)(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 simple copying routines if so + */ + for (i = 0; i < length; i++) { + uint32_t idx = (uint32_t) *data++; + if (x >= bmp->width) { + x = 0; + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + if (bmp->reversed) { + scanline += bmp->width; + } else { + scanline -= bmp->width; + } + } + if (idx >= bmp->colours) + return BMP_DATA_ERROR; + scanline[x++] = bmp->colour_table[idx]; + } + + if ((length & 1) && (*data++ != 0x00)) + return BMP_DATA_ERROR; + + break; + } + } else { + uint32_t idx; + + /* NN means perform RLE for NN pixels */ + if (bmp->reversed) { + pixels_left = (bmp->height - y) * bmp->width - x; + scanline = (void *)(top + (y * swidth)); + } else { + pixels_left = (y + 1) * bmp->width - x; + scanline = (void *)(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 + */ + idx = (uint32_t) *data++; + if (idx >= bmp->colours) + return BMP_DATA_ERROR; + + pixel = bmp->colour_table[idx]; + for (i = 0; i < length; i++) { + if (x >= bmp->width) { + x = 0; + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + if (bmp->reversed) { + scanline += bmp->width; + } else { + scanline -= bmp->width; + } + } + scanline[x++] = pixel; + } + } + } while (data < end); + + return BMP_OK; +} + + +/** + * Decode BMP data stored encoded in RLE4. + * + * \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 + * BMP_INSUFFICIENT_DATA if the bitmap data ends unexpectedly; + * in this case, the image may be partially viewable + */ +static bmp_result +bmp_decode_rle4(bmp_image *bmp, uint8_t *data, int bytes) +{ + 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++; + switch (length) { + case 0: + /* 00 - 00 means end of scanline */ + x = 0; + if (last_y == y) { + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + } + last_y = y; + break; + + case 1: + /* 00 - 01 means end of RLE data */ + return BMP_OK; + + case 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; + break; + + default: + /* 00 - NN means escape NN pixels */ + if (bmp->reversed) { + pixels_left = (bmp->height - y) * bmp->width - x; + scanline = (void *)(top + (y * swidth)); + } else { + pixels_left = (y + 1) * bmp->width - x; + scanline = (void *)(bottom - (y * swidth)); + } + if (length > pixels_left) + length = pixels_left; + if (data + ((length + 1) / 2) > end) + return BMP_INSUFFICIENT_DATA; + + /* the following code could be easily optimised + * by simply checking the bounds on entry and + * using some simple copying routines + */ + + for (i = 0; i < length; i++) { + if (x >= bmp->width) { + x = 0; + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + if (bmp->reversed) { + scanline += bmp->width; + } else { + scanline -= bmp->width; + } + + } + if ((i & 1) == 0) { + pixel = *data++; + if ((pixel >> 4) >= bmp->colours) + return BMP_DATA_ERROR; + scanline[x++] = bmp->colour_table + [pixel >> 4]; + } else { + if ((pixel & 0xf) >= bmp->colours) + return BMP_DATA_ERROR; + scanline[x++] = bmp->colour_table + [pixel & 0xf]; + } + } + length = (length + 1) >> 1; + + if ((length & 1) && (*data++ != 0x00)) + return BMP_DATA_ERROR; + + break; + } + } else { + /* NN means perform RLE for NN pixels */ + if (bmp->reversed) { + pixels_left = (bmp->height - y) * bmp->width - x; + scanline = (void *)(top + (y * swidth)); + } else { + pixels_left = (y + 1) * bmp->width - x; + scanline = (void *)(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 + * simple copying routines + */ + + pixel2 = *data++; + if ((pixel2 >> 4) >= bmp->colours || + (pixel2 & 0xf) >= bmp->colours) + return BMP_DATA_ERROR; + pixel = bmp->colour_table[pixel2 >> 4]; + pixel2 = bmp->colour_table[pixel2 & 0xf]; + for (i = 0; i < length; i++) { + if (x >= bmp->width) { + x = 0; + y++; + if (y >= bmp->height) + return BMP_DATA_ERROR; + if (bmp->reversed) { + scanline += bmp->width; + } else { + scanline -= bmp->width; + } + } + if ((i & 1) == 0) + scanline[x++] = pixel; + else + scanline[x++] = pixel2; + } + + } + } while (data < end); + + return BMP_OK; +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result +bmp_create(bmp_image *bmp, + bmp_bitmap_callback_vt *bitmap_callbacks) +{ + memset(bmp, 0, sizeof(bmp_image)); + bmp->bitmap_callbacks = *bitmap_callbacks; + + return BMP_OK; +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result +ico_collection_create(ico_collection *ico, + bmp_bitmap_callback_vt *bitmap_callbacks) +{ + + memset(ico, 0, sizeof(ico_collection)); + ico->bitmap_callbacks = *bitmap_callbacks; + + return BMP_OK; +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data) +{ + bmp_result res; + + /* ensure we aren't already initialised */ + if (bmp->bitmap) { + return BMP_OK; + } + + /* initialize source data values */ + bmp->buffer_size = size; + bmp->bmp_data = data; + + res = bmp_file_header_parse(bmp, data); + if (res == BMP_OK) { + res = bmp_info_header_parse(bmp, data + BMP_FILE_HEADER_SIZE); + } + return res; +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data) +{ + /* ensure we aren't already initialised */ + if (ico->first) + return BMP_OK; + + /* initialize values */ + ico->buffer_size = size; + ico->ico_data = data; + + return ico_header_parse(ico, data); +} + + +/* exported interface documented in libnsbmp.h */ +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: + switch (bmp->bpp) { + case 32: + result = bmp_decode_rgb32(bmp, &data, bytes); + break; + + case 24: + result = bmp_decode_rgb24(bmp, &data, bytes); + break; + + case 16: + result = bmp_decode_rgb16(bmp, &data, bytes); + break; + + default: + result = bmp_decode_rgb(bmp, &data, bytes); + break; + } + break; + + case BMP_ENCODING_RLE8: + result = bmp_decode_rle8(bmp, data, bytes); + break; + + case BMP_ENCODING_RLE4: + result = bmp_decode_rle4(bmp, data, bytes); + break; + + case BMP_ENCODING_BITFIELDS: + switch (bmp->bpp) { + case 32: + result = bmp_decode_rgb32(bmp, &data, bytes); + break; + + case 16: + result = bmp_decode_rgb16(bmp, &data, bytes); + break; + + default: + result = BMP_DATA_ERROR; + break; + } + break; + } + + /* icons with less than 32bpp have a 1bpp alpha mask */ + if ((result == BMP_OK) && (bmp->ico) && (bmp->bpp != 32)) { + bytes = (uintptr_t)bmp->bmp_data + bmp->buffer_size - (uintptr_t)data; + result = bmp_decode_mask(bmp, data, bytes); + } + return result; +} + + +/* exported interface documented in libnsbmp.h */ +bmp_result bmp_decode_trans(bmp_image *bmp, uint32_t colour) +{ + bmp->limited_trans = true; + bmp->trans_colour = colour; + return bmp_decode(bmp); +} + + +/* exported interface documented in libnsbmp.h */ +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; +} + + +/* exported interface documented in libnsbmp.h */ +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; +} + + +/* exported interface documented in libnsbmp.h */ +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/external/libnsbmp/libnsbmp.h b/external/libnsbmp/libnsbmp.h new file mode 100644 index 0000000..7e90b4a --- /dev/null +++ b/external/libnsbmp/libnsbmp.h @@ -0,0 +1,250 @@ +/* + * 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 + * Bitmap file decoding interface. + */ + +#ifndef libnsbmp_h_ +#define libnsbmp_h_ + +#include +#include +#include + +/* bmp flags */ +#define BMP_NEW 0 +/** image is opaque (as opposed to having an alpha mask) */ +#define BMP_OPAQUE (1 << 0) +/** memory should be wiped */ +#define BMP_CLEAR_MEMORY (1 << 1) + +/** + * 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 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 { + /** Callback to allocate bitmap storage. */ + bmp_bitmap_cb_create bitmap_create; + /** Called to free bitmap storage. */ + bmp_bitmap_cb_destroy bitmap_destroy; + /** Return a pointer to the pixel data in a bitmap. */ + bmp_bitmap_cb_get_buffer bitmap_get_buffer; + /** Find the width of a pixel row in bytes. */ + bmp_bitmap_cb_get_bpp bitmap_get_bpp; +} bmp_bitmap_callback_vt; + +/** + * bitmap image + */ +typedef struct bmp_image { + /** callbacks for bitmap functions */ + bmp_bitmap_callback_vt bitmap_callbacks; + /** pointer to BMP data */ + uint8_t *bmp_data; + /** width of BMP (valid after _analyse) */ + uint32_t width; + /** heigth of BMP (valid after _analyse) */ + uint32_t height; + /** whether the image has been decoded */ + bool decoded; + /** decoded image */ + void *bitmap; + + /* Internal members are listed below */ + /** total number of bytes of BMP data available */ + uint32_t buffer_size; + /** pixel encoding type */ + bmp_encoding encoding; + /** offset of bitmap data */ + uint32_t bitmap_offset; + /** bits per pixel */ + uint16_t bpp; + /** number of colours */ + uint32_t colours; + /** colour table */ + uint32_t *colour_table; + /** whether to use bmp's limited transparency */ + bool limited_trans; + /** colour to display for "transparent" pixels when using limited + * transparency + */ + uint32_t trans_colour; + /** scanlines are top to bottom */ + bool reversed; + /** image is part of an ICO, mask follows */ + bool ico; + /** true if the bitmap does not contain an alpha channel */ + bool opaque; + /** four bitwise mask */ + uint32_t mask[4]; + /** four bitwise shifts */ + int32_t shift[4]; + /** colour representing "transparency" in the bitmap */ + uint32_t transparent_index; +} bmp_image; + +typedef struct ico_image { + bmp_image bmp; + struct ico_image *next; +} ico_image; + +/** + * icon image collection + */ +typedef struct ico_collection { + /** callbacks for bitmap functions */ + bmp_bitmap_callback_vt bitmap_callbacks; + /** width of largest BMP */ + uint16_t width; + /** heigth of largest BMP */ + uint16_t height; + + /* Internal members are listed below */ + /** pointer to ICO data */ + uint8_t *ico_data; + /** total number of bytes of ICO data available */ + uint32_t buffer_size; + /** root of linked list of images */ + ico_image *first; +} ico_collection; + +/** + * Initialises bitmap ready for analysing the bitmap. + * + * \param bmp The Bitmap to initialise + * \param callbacks The callbacks the library will call on operations. + * \return BMP_OK on success or appropriate error code. + */ +bmp_result bmp_create(bmp_image *bmp, bmp_bitmap_callback_vt *callbacks); + +/** + * Initialises icon ready for analysing the icon + * + * \param bmp The Bitmap to initialise + * \param callbacks The callbacks the library will call on operations. + * \return BMP_OK on success or appropriate error code. + */ +bmp_result ico_collection_create(ico_collection *ico, + bmp_bitmap_callback_vt *callbacks); + +/** + * Analyse a BMP prior to decoding. + * + * This will scan the data provided and perform checks to ensure the data is a + * valid BMP and prepare the bitmap image structure ready for decode. + * + * This function must be called and resturn BMP_OK before bmp_decode() as it + * prepares the bmp internal state for the decode process. + * + * \param bmp the BMP image to analyse. + * \param size The size of data in cdata. + * \param data The bitmap source data. + * \return BMP_OK on success or error code on faliure. + */ +bmp_result bmp_analyse(bmp_image *bmp, size_t size, uint8_t *data); + +/** + * Analyse an ICO prior to decoding. + * + * This function will scan the data provided and perform checks to ensure the + * data is a valid ICO. + * + * This function must be called before ico_find(). + * + * \param ico the ICO image to analyse + * \param size The size of data in cdata. + * \param data The bitmap source data. + * \return BMP_OK on success + */ +bmp_result ico_analyse(ico_collection *ico, size_t size, uint8_t *data); + +/** + * 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); + +/** + * 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 transparent_colour); + +/** + * 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); + +/** + * Finalise a BMP prior to destruction. + * + * \param bmp the BMP image to finalise. + */ +void bmp_finalise(bmp_image *bmp); + +/** + * Finalise an ICO prior to destruction. + * + * \param ico the ICO image to finalise, + */ +void ico_finalise(ico_collection *ico); + +#endif diff --git a/external/libnsbmp/log.h b/external/libnsbmp/log.h new file mode 100644 index 0000000..b63f084 --- /dev/null +++ b/external/libnsbmp/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/renderd7.cpp b/renderd7.cpp index 47647b9..936c572 100644 --- a/renderd7.cpp +++ b/renderd7.cpp @@ -1421,22 +1421,84 @@ std::string RenderD7::GetTimeStr(void) struct tm* timeStruct = gmtime((const time_t*)&unixTime); return RenderD7::FormatString("%02i:%02i:%02i", timeStruct->tm_hour, timeStruct->tm_min, timeStruct->tm_sec); } -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" +extern "C" +{ + #include "libnsbmp.h" +} +static const u32 BYTES_PER_PIXEL = 4; +#define MAX_IMAGE_BYTES (48 * 1024 * 1024) -unsigned Image_to_C3D(C2D_Image img, const std::vector& bmp) { - C2D_Image* images; - C3DTexToC2DImage(images, 1024, 1024, (u8*)bmp.data()); - img.tex = images->tex; - img.subtex = images->subtex; - return false; +namespace LIBBMP { + static void *bitmap_create(int width, int height, [[maybe_unused]] unsigned int state) { + /* ensure a stupidly large (>50Megs or so) bitmap is not created */ + if ((static_cast(width) * static_cast(height)) > (MAX_IMAGE_BYTES/BYTES_PER_PIXEL)) + return nullptr; + + return std::calloc(width * height, BYTES_PER_PIXEL); + } + + static unsigned char *bitmap_get_buffer(void *bitmap) { + assert(bitmap); + return static_cast(bitmap); + } + + static size_t bitmap_get_bpp([[maybe_unused]] void *bitmap) { + return BYTES_PER_PIXEL; + } + + static void bitmap_destroy(void *bitmap) { + assert(bitmap); + std::free(bitmap); + } +} + + +unsigned Image_to_C3D(C2D_Image img, const std::vector& bmpc) { + bmp_bitmap_callback_vt bitmap_callbacks = { + LIBBMP::bitmap_create, + LIBBMP::bitmap_destroy, + LIBBMP::bitmap_get_buffer, + LIBBMP::bitmap_get_bpp + }; + + bmp_result code = BMP_OK; + bmp_image bmp; + bmp_create(&bmp, &bitmap_callbacks); + + code = bmp_analyse(&bmp, bmpc.size(), (u8*)bmpc.data()); + if (code != BMP_OK) { + bmp_finalise(&bmp); + return 1; + } + + code = bmp_decode(&bmp); + if (code != BMP_OK) { + if ((code != BMP_INSUFFICIENT_DATA) && (code != BMP_DATA_ERROR)) { + bmp_finalise(&bmp); + return 2; + } + + /* skip if the decoded image would be ridiculously large */ + if ((bmp.width * bmp.height) > 200000) { + bmp_finalise(&bmp); + return 3; + } + } + C2D_Image* texture; + bool ret = C3DTexToC2DImage(texture, static_cast(bmp.width), static_cast(bmp.height), static_cast(bmp.bitmap)); + bmp_finalise(&bmp); + if (!ret) + { + return 4; + } + return 0; } void RenderD7::Image::LoadFromBitmap(BMP bitmap) { unsigned error = Image_to_C3D(this->img, bitmap.DATA()); - if(error) { + if(error) { std::cout << "BMP decoding error " << error << std::endl; RenderD7::AddOvl(std::make_unique("Bmp - Error", "Code: " + std::to_string(error))); } @@ -1460,7 +1522,7 @@ void RenderD7::Toast::Draw(void) const void RenderD7::Toast::Logic() { this->delay++/*=(int)RenderD7::GetDeltaTime()*/; - if (msgposy > 170 && delay < 5*60) msgposy--/*=(int)RenderD7::GetDeltaTime()*/; + if (msgposy > 170 && delay < 2*60) msgposy--/*=(int)RenderD7::GetDeltaTime()*/; if (delay >= 5*60) {