From da4a9313f1260531ec3dde07b32c122f120f695c Mon Sep 17 00:00:00 2001 From: Richard Wilson Date: Sat, 25 Feb 2006 18:51:51 +0000 Subject: [PATCH] [project @ 2006-02-25 18:50:34 by rjw] Support for BMP files svn path=/import/netsurf/; revision=2095 --- !NetSurf/Resources/de/Messages | 2 + !NetSurf/Resources/en/Messages | 2 + !NetSurf/Resources/fr/Messages | 2 + !NetSurf/Resources/nl/Messages | 2 + content/content.c | 40 +++ content/content.h | 6 + content/content_type.h | 3 + image/bmp.c | 127 +++++++++ image/bmp.h | 33 +++ image/bmpread.c | 483 +++++++++++++++++++++++++++++++++ image/bmpread.h | 54 ++++ makefile | 2 +- render/box_construct.c | 5 +- riscos/filetype.c | 7 +- riscos/gui.c | 5 + riscos/save.c | 5 + riscos/window.c | 3 + utils/config.h | 2 +- 18 files changed, 776 insertions(+), 7 deletions(-) create mode 100644 image/bmp.c create mode 100644 image/bmp.h create mode 100644 image/bmpread.c create mode 100644 image/bmpread.h diff --git a/!NetSurf/Resources/de/Messages b/!NetSurf/Resources/de/Messages index 0f8deba2f..a493d92a8 100644 --- a/!NetSurf/Resources/de/Messages +++ b/!NetSurf/Resources/de/Messages @@ -325,6 +325,7 @@ SelectMenu:Select # DrawTitle:Draw Bild (%lux%lu, %lu bytes) GIFTitle:GIF Bild (%lux%lu, %lu bytes) +BMPTitle:BMP Bild (%lux%lu, %lu bytes) JPEGTitle:JPEG Bild (%ux%u, %lu bytes) PNGTitle:PNG Bild (%lux%lu, %lu bytes) JNGTitle:JNG Bild (%lux%lu, %lu bytes) @@ -436,6 +437,7 @@ BadObject:Warning: bad object type ObjError:Error loading object: %s ParsingFail:Parsing the document failed. BadGIF:Reading GIF failed. +BadBMP:Reading BMP failed. PNGError:PNG library error. MNGError:MNG library error. BadSprite:Invalid or corrupt Sprite data. diff --git a/!NetSurf/Resources/en/Messages b/!NetSurf/Resources/en/Messages index b10955fc7..fac4bbcf0 100644 --- a/!NetSurf/Resources/en/Messages +++ b/!NetSurf/Resources/en/Messages @@ -325,6 +325,7 @@ SelectMenu:Select # DrawTitle:Draw image (%lux%lu, %lu bytes) GIFTitle:GIF image (%lux%lu, %lu bytes) +BMPTitle:BMP image (%lux%lu, %lu bytes) JPEGTitle:JPEG image (%ux%u, %lu bytes) PNGTitle:PNG image (%lux%lu, %lu bytes) JNGTitle:JNG image (%lux%lu, %lu bytes) @@ -435,6 +436,7 @@ BadObject:Warning: bad object type ObjError:Error loading object: %s ParsingFail:Parsing the document failed. BadGIF:Reading GIF failed. +BadBMP:Reading BMP failed. PNGError:PNG library error. MNGError:MNG library error. BadSprite:Invalid or corrupt Sprite data. diff --git a/!NetSurf/Resources/fr/Messages b/!NetSurf/Resources/fr/Messages index cb7053755..97b6b11a3 100644 --- a/!NetSurf/Resources/fr/Messages +++ b/!NetSurf/Resources/fr/Messages @@ -325,6 +325,7 @@ SelectMenu:Sélection # DrawTitle:Image Draw (%lux%lu, %lu octets) GIFTitle:Image GIF (%lux%lu, %lu octets) +BMPTitle:Image BMP (%lux%lu, %lu octets) JPEGTitle:Image JPEG (%ux%u, %lu octets) PNGTitle:Image PNG (%lux%lu, %lu octets) JNGTitle:Image JNG (%lux%lu, %lu octets) @@ -436,6 +437,7 @@ BadObject:Attention: mauvais type d'objet ObjError:Erreur lors du chargement de: %s ParsingFail:L'analyse syntaxique du document a échoué. BadGIF:Erreur de lecture de GIF. +BadBMP:Erreur de lecture de BMP. PNGError:Erreur dans la bibliothèque PNG. MNGError:Erreur dans la bibliothèque MNG. BadSprite:Les données du sprite sont invalides ou corrompues. diff --git a/!NetSurf/Resources/nl/Messages b/!NetSurf/Resources/nl/Messages index d700fc061..6aaeda156 100644 --- a/!NetSurf/Resources/nl/Messages +++ b/!NetSurf/Resources/nl/Messages @@ -326,6 +326,7 @@ SelectMenu:Select # DrawTitle:Draw image (%lux%lu, %lu bytes) GIFTitle:GIF image (%lux%lu, %lu bytes) +BMPTitle:BMP image (%lux%lu, %lu bytes) JPEGTitle:JPEG image (%ux%u, %lu bytes) PNGTitle:PNG image (%lux%lu, %lu bytes) JNGTitle:JNG image (%lux%lu, %lu bytes) @@ -438,6 +439,7 @@ BadObject:melding: fout object type ObjError:fout bij laden object: %s ParsingFail:fout bij ontleden van dit document. BadGIF:fout bij lezen GIF. +BadBMP:fout bij lezen BMP. PNGError:PNG library fout. MNGError:MNG library fout. BadSprite:foutief sprite bestand. diff --git a/content/content.c b/content/content.c index ff65f7bfd..f18f401cf 100644 --- a/content/content.c +++ b/content/content.c @@ -36,6 +36,9 @@ #ifdef WITH_GIF #include "netsurf/image/gif.h" #endif +#ifdef WITH_BMP +#include "netsurf/image/bmp.h" +#endif #ifdef WITH_SPRITE #include "netsurf/riscos/sprite.h" #endif @@ -68,17 +71,32 @@ static const struct mime_entry mime_map[] = { #ifdef WITH_ARTWORKS {"application/artworks", CONTENT_ARTWORKS}, #endif +#ifdef WITH_BMP + {"application/bmp", CONTENT_BMP}, +#endif #ifdef WITH_DRAW {"application/drawfile", CONTENT_DRAW}, +#endif +#ifdef WITH_BMP + {"application/preview", CONTENT_BMP}, + {"application/x-bmp", CONTENT_BMP}, +#endif +#ifdef WITH_DRAW {"application/x-drawfile", CONTENT_DRAW}, #endif #ifdef WITH_THEME_INSTALL {"application/x-netsurf-theme", CONTENT_THEME}, +#endif +#ifdef WITH_BMP + {"application/x-win-bitmap", CONTENT_BMP}, #endif {"application/xhtml+xml", CONTENT_HTML}, #ifdef WITH_ARTWORKS {"image/artworks", CONTENT_ARTWORKS}, #endif +#ifdef WITH_BMP + {"image/bmp", CONTENT_BMP}, +#endif #ifdef WITH_DRAW {"image/drawfile", CONTENT_DRAW}, #endif @@ -94,12 +112,19 @@ static const struct mime_entry mime_map[] = { #ifdef WITH_MNG {"image/mng", CONTENT_MNG}, #endif +#ifdef WITH_BMP + {"image/ms-bmp", CONTENT_BMP}, +#endif #ifdef WITH_JPEG {"image/pjpeg", CONTENT_JPEG}, #endif #ifdef WITH_MNG {"image/png", CONTENT_PNG}, #endif +#ifdef WITH_BMP + {"image/x-bitmap", CONTENT_BMP}, + {"image/x-bmp", CONTENT_BMP}, +#endif #ifdef WITH_DRAW {"image/x-drawfile", CONTENT_DRAW}, #endif @@ -107,8 +132,16 @@ static const struct mime_entry mime_map[] = { {"image/x-jng", CONTENT_JNG}, {"image/x-mng", CONTENT_MNG}, #endif +#ifdef WITH_BMP + {"image/x-ms-bmp", CONTENT_BMP}, +#endif #ifdef WITH_SPRITE {"image/x-riscos-sprite", CONTENT_SPRITE}, +#endif +#ifdef WITH_BMP + {"image/x-win-bitmap", CONTENT_BMP}, + {"image/x-windows-bmp", CONTENT_BMP}, + {"image/x-xbitmap", CONTENT_BMP}, #endif {"text/css", CONTENT_CSS}, {"text/html", CONTENT_HTML}, @@ -130,6 +163,9 @@ const char *content_type_name[] = { #ifdef WITH_GIF "GIF", #endif +#ifdef WITH_BMP + "BMP", +#endif #ifdef WITH_MNG "PNG", "JNG", @@ -206,6 +242,10 @@ static const struct handler_entry handler_map[] = { {nsgif_create, 0, nsgif_convert, 0, nsgif_destroy, 0, nsgif_redraw, nsgif_redraw_tiled, 0, 0, false}, #endif +#ifdef WITH_BMP + {nsbmp_create, 0, nsbmp_convert, 0, nsbmp_destroy, 0, + nsbmp_redraw, nsbmp_redraw_tiled, 0, 0, false}, +#endif #ifdef WITH_MNG {nsmng_create, nsmng_process_data, nsmng_convert, 0, nsmng_destroy, 0, nsmng_redraw, nsmng_redraw_tiled, diff --git a/content/content.h b/content/content.h index 21c4f399c..52a22e5ef 100644 --- a/content/content.h +++ b/content/content.h @@ -115,6 +115,9 @@ #ifdef WITH_GIF #include "netsurf/image/gif.h" #endif +#ifdef WITH_BMP +#include "netsurf/image/bmp.h" +#endif #ifdef WITH_PLUGIN #include "netsurf/riscos/plugin.h" #endif @@ -230,6 +233,9 @@ struct content { #ifdef WITH_GIF struct content_gif_data gif; #endif +#ifdef WITH_BMP + struct content_bmp_data bmp; +#endif #ifdef WITH_MNG struct content_mng_data mng; #endif diff --git a/content/content_type.h b/content/content_type.h index 4760b9f9f..ef184ce7c 100644 --- a/content/content_type.h +++ b/content/content_type.h @@ -28,6 +28,9 @@ typedef enum { #ifdef WITH_GIF CONTENT_GIF, #endif +#ifdef WITH_BMP + CONTENT_BMP, +#endif #ifdef WITH_MNG CONTENT_PNG, CONTENT_JNG, diff --git a/image/bmp.c b/image/bmp.c new file mode 100644 index 000000000..b2fc59f86 --- /dev/null +++ b/image/bmp.c @@ -0,0 +1,127 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2006 Richard Wilson + */ + +#include +#include +#include +#include +#include "netsurf/utils/config.h" +#include "netsurf/content/content.h" +#include "netsurf/desktop/browser.h" +#include "netsurf/desktop/options.h" +#include "netsurf/desktop/plotters.h" +#include "netsurf/image/bitmap.h" +#include "netsurf/image/bmp.h" +#include "netsurf/image/bmpread.h" +#include "netsurf/utils/log.h" +#include "netsurf/utils/messages.h" +#include "netsurf/utils/utils.h" + +#ifdef WITH_BMP + +static void nsbmp_invalidate(struct bitmap *bitmap, void *private_word); + +bool nsbmp_create(struct content *c, const char *params[]) { + union content_msg_data msg_data; + + c->data.bmp.bmp = calloc(sizeof(struct bmp_image), 1); + if (!c->data.bmp.bmp) { + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + warn_user("NoMemory", 0); + return false; + } + return true; +} + + +bool nsbmp_convert(struct content *c, int iwidth, int iheight) { + bmp_result res; + struct bmp_image *bmp; + union content_msg_data msg_data; + + /* set our source data */ + bmp = c->data.bmp.bmp; + bmp->bmp_data = c->source_data; + bmp->buffer_size = c->source_size; + + /* analyse the BMP */ + res = bmp_analyse(bmp); + switch (res) { + case BMP_OK: + break; + case BMP_INSUFFICIENT_MEMORY: + msg_data.error = messages_get("NoMemory"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + warn_user("NoMemory", 0); + return false; + case BMP_INSUFFICIENT_DATA: + case BMP_DATA_ERROR: + msg_data.error = messages_get("BadBMP"); + content_broadcast(c, CONTENT_MSG_ERROR, msg_data); + return false; + } + + /* Store our content width and description + */ + c->width = bmp->width; + c->height = bmp->height; + c->title = malloc(100); + if (c->title) + snprintf(c->title, 100, messages_get("BMPTitle"), c->width, + c->height, c->source_size); + c->size += (bmp->width * bmp->height * 4) + 16 + 44 + 100; + + /* make so that the bitmap code can free our image quickly */ + bitmap_set_suspendable(bmp->bitmap, bmp, nsbmp_invalidate); + + /* exit as a success */ + c->bitmap = bmp->bitmap; + c->status = CONTENT_STATUS_DONE; + return true; +} + +void nsbmp_invalidate(struct bitmap *bitmap, void *private_word) { + struct bmp_image *bmp = (struct bmp_image *)private_word; + + bmp->decoded = false; +} + +bool nsbmp_redraw(struct content *c, int x, int y, + int width, int height, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale, unsigned long background_colour) { + + if (!c->data.bmp.bmp->decoded) + bmp_decode(c->data.bmp.bmp); + c->bitmap = c->data.bmp.bmp->bitmap; + return plot.bitmap(x, y, width, height, c->bitmap, background_colour); +} + + +bool nsbmp_redraw_tiled(struct content *c, int x, int y, + int width, int height, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale, unsigned long background_colour, + bool repeat_x, bool repeat_y) { + + if (!c->data.bmp.bmp->decoded) + bmp_decode(c->data.bmp.bmp); + c->bitmap = c->data.bmp.bmp->bitmap; + return plot.bitmap_tile(x, y, width, height, c->bitmap, + background_colour, repeat_x, repeat_y); +} + + +void nsbmp_destroy(struct content *c) +{ + bmp_finalise(c->data.bmp.bmp); + free(c->data.bmp.bmp); + free(c->title); +} + +#endif diff --git a/image/bmp.h b/image/bmp.h new file mode 100644 index 000000000..11f329b4d --- /dev/null +++ b/image/bmp.h @@ -0,0 +1,33 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2006 Richard Wilson + */ + +#ifndef _NETSURF_IMAGE_BMP_H_ +#define _NETSURF_IMAGE_BMP_H_ + +#include +#include "netsurf/image/bmpread.h" + +struct content; + +struct content_bmp_data { + struct bmp_image *bmp; /** BMP image data */ +}; + +bool nsbmp_create(struct content *c, const char *params[]); +bool nsbmp_convert(struct content *c, int width, int height); +void nsbmp_destroy(struct content *c); +bool nsbmp_redraw(struct content *c, int x, int y, + int width, int height, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale, unsigned long background_colour); +bool nsbmp_redraw_tiled(struct content *c, int x, int y, + int width, int height, + int clip_x0, int clip_y0, int clip_x1, int clip_y1, + float scale, unsigned long background_colour, + bool repeat_x, bool repeat_y); + +#endif diff --git a/image/bmpread.c b/image/bmpread.c new file mode 100644 index 000000000..15c103ae0 --- /dev/null +++ b/image/bmpread.c @@ -0,0 +1,483 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2006 Richard Wilson + */ + + +#include +#include +#include +#include +#include +#include "netsurf/image/bmpread.h" +#include "netsurf/image/bitmap.h" +#include "netsurf/utils/log.h" + +#define READ_SHORT(a, o) (a[o]|(a[o+1]<<8)) +#define READ_INT(a, o) (a[o]|(a[o+1]<<8)|(a[o+2]<<16)|(a[o+3]<<24)) + +bmp_result bmp_decode_rgb24(struct bmp_image *bmp, char *data, int bytes); +bmp_result bmp_decode_rgb(struct bmp_image *bmp, char *data, int bytes); +bmp_result bmp_decode_rle(struct bmp_image *bmp, char *data, int bytes, int size); + + +/** + * 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(struct bmp_image *bmp) { + char *data = bmp->bmp_data; + unsigned int header_size; + unsigned int i; + int width, height; + int palette_size; + + /* ensure we aren't already initialised */ + if (bmp->bitmap) + return BMP_OK; + + /* standard 14-byte BMP file header is: + * + * +0 SHORT 'BM' + * +2 INT size of file (in bytes) + * +6 SHORT reserved field (1) + * +8 SHORT reserved field (2) + * +10 INT starting position of image data (in bytes) + */ + if (bmp->buffer_size < 14) + return BMP_INSUFFICIENT_DATA; + if ((data[0] != 'B') || (data[1] != 'M')) + return BMP_DATA_ERROR; + bmp->bitmap_offset = READ_INT(data, 10); + data += 14; + + /* a variety of different bitmap headers can now follow, depending + * on the BMP variant. A full description of the various headers + * can be found at http://www.fileformat.info/format/bmp/ + */ + header_size = READ_INT(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 INT size of this header (in bytes) + * +4 SHORT image width (in pixels) + * +6 SHORT image height (in pixels) + * +8 SHORT number of color planes (always 1) + * +10 SHORT number of bits per pixel + */ + width = READ_SHORT(data, 4); + height = READ_SHORT(data, 6); + if (width < 0) + return BMP_DATA_ERROR; + if (height < 0) { + bmp->reversed = true; + height = -height; + } + bmp->width = width; + bmp->height = height; + if (READ_SHORT(data, 8) != 1) + return BMP_DATA_ERROR; + bmp->bpp = READ_SHORT(data, 10); + 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 INT size of this header (in bytes) + * +4 INT image width (in pixels) + * +8 INT image height (in pixels) +  * +12 SHORT number of color planes (always 1) + * +14 SHORT number of bits per pixel + * +16 INT compression methods used + * +20 INT size of bitmap (in bytes) + * +24 INT horizontal resolution (in pixels per meter) + * +28 INT vertical resolution (in pixels per meter) + * +32 INT number of colors in the image + * +36 INT number of important colors + * +40 INT mask identifying bits of red component + * +44 INT mask identifying bits of green component + * +48 INT mask identifying bits of blue component + * +52 INT mask identifying bits of alpha component + * +56 INT color space type + * +60 INT x coordinate of red endpoint + * +64 INT y coordinate of red endpoint + * +68 INT z coordinate of red endpoint + * +72 INT x coordinate of green endpoint + * +76 INT y coordinate of green endpoint + * +80 INT z coordinate of green endpoint + * +84 INT x coordinate of blue endpoint + * +88 INT y coordinate of blue endpoint + * +92 INT z coordinate of blue endpoint + * +96 INT gamma red coordinate scale value + * +100 INT gamma green coordinate scale value + * +104 INT gamma blue coordinate scale value + */ + width = READ_INT(data, 4); + height = READ_INT(data, 8); + if (width < 0) + return BMP_DATA_ERROR; + if (height < 0) { + bmp->reversed = true; + height = -height; + } + bmp->width = width; + bmp->height = height; + if (READ_SHORT(data, 12) != 1) + return BMP_DATA_ERROR; + bmp->bpp = READ_SHORT(data, 14); + bmp->encoding = READ_INT(data, 16); + if (bmp->encoding >= BMP_ENCODING_BITFIELDS) /* unsupported so far */ + return BMP_DATA_ERROR; + bmp->colours = READ_INT(data, 32); + if (bmp->colours == 0) + bmp->colours = (1 << bmp->bpp); + palette_size = 4; + /* we don't understand the rest of the data yet */ + } + data += header_size; + + /* 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. + */ + if (bmp->buffer_size < (14 + header_size + (4 * bmp->colours))) + return BMP_INSUFFICIENT_DATA; + bmp->colour_table = (unsigned int *) + malloc(bmp->colours * sizeof(int)); + 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) | (0xff << 24)); + data += palette_size; + } + } + + /* create our bitmap */ + bmp->bitmap = bitmap_create(bmp->width, bmp->height, + BITMAP_NEW | BITMAP_OPAQUE | BITMAP_CLEAR_MEMORY); + if (!bmp->bitmap) { + if (bmp->colour_table) + free(bmp->colour_table); + bmp->colour_table = NULL; + return BMP_INSUFFICIENT_MEMORY; + } + return BMP_OK; +} + + +/** + * 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(struct bmp_image *bmp) { + char *data; + int bytes; + + 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) + return bmp_decode_rgb24(bmp, data, bytes); + else if (bmp->bpp > 8) + return BMP_DATA_ERROR; + else + return bmp_decode_rgb(bmp, data, bytes); + case BMP_ENCODING_RLE8: + return bmp_decode_rle(bmp, data, bytes, 8); + case BMP_ENCODING_RLE4: + return bmp_decode_rle(bmp, data, bytes, 4); + case BMP_ENCODING_BITFIELDS: + /* todo: implement me */ + break; + } + return BMP_DATA_ERROR; +} + + +/** + * Decode BMP data stored in 24bpp colour. + * + * \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_result bmp_decode_rgb24(struct bmp_image *bmp, char *data, int bytes) { + char *top, *bottom, *end; + unsigned int *scanline; + unsigned int x, y, swidth, skip; + unsigned int addr; + + swidth = bitmap_get_rowstride(bmp->bitmap); + top = bitmap_get_buffer(bmp->bitmap); + bottom = top + swidth * (bmp->height - 1); + end = data + bytes; + addr = ((unsigned int)data) & 3; + skip = bmp->bpp >> 3; + bmp->decoded = true; + + for (y = 0; y < bmp->height; y++) { + while (addr != (((unsigned int)data) & 3)) + data++; + if ((data + (skip * bmp->width)) > 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++) { + scanline[x] = data[2] | (data[1] << 8) | (data[0] << 16); + data += skip; + } + } + return BMP_OK; +} + + +/** + * Decode BMP data stored with a palette and in 8bpp colour or less. + * + * \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_result bmp_decode_rgb(struct bmp_image *bmp, char *data, int bytes) { + char *top, *bottom, *end; + unsigned int *scanline; + unsigned int addr; + unsigned int x, y, swidth; + int i; + int bit_shifts[8]; + int ppb = 8 / bmp->bpp; + int bit_mask = (1 << bmp->bpp) - 1; + int cur_byte = 0, bit; + + for (i = 0; i < ppb; i++) + bit_shifts[i] = 8 - ((i + 1) * bmp->bpp); + + swidth = bitmap_get_rowstride(bmp->bitmap); + top = bitmap_get_buffer(bmp->bitmap); + bottom = top + swidth * (bmp->height - 1); + end = data + bytes; + addr = ((unsigned int)data) & 3; + bmp->decoded = true; + + for (y = 0; y < bmp->height; y++) { + while (addr != (((unsigned int)data) & 3)) + data++; + bit = 32; + 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]; + } + } + 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_result bmp_decode_rle(struct bmp_image *bmp, char *data, int bytes, int size) { + char *top, *bottom, *end; + unsigned int *scanline; + unsigned int swidth; + int i, length, pixels_left; + unsigned int x = 0, y = 0, last_y = 0; + unsigned int pixel = 0, pixel2; + + swidth = bitmap_get_rowstride(bmp->bitmap); + top = bitmap_get_buffer(bmp->bitmap); + bottom = top + 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 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; + + /* 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(struct bmp_image *bmp) { + if (bmp->bitmap) + bitmap_destroy(bmp->bitmap); + bmp->bitmap = NULL; + if (bmp->colour_table) + free(bmp->colour_table); + bmp->colour_table = NULL; +} diff --git a/image/bmpread.h b/image/bmpread.h new file mode 100644 index 000000000..45468a9ed --- /dev/null +++ b/image/bmpread.h @@ -0,0 +1,54 @@ +/* + * This file is part of NetSurf, http://netsurf.sourceforge.net/ + * Licensed under the GNU General Public License, + * http://www.opensource.org/licenses/gpl-license + * Copyright 2006 Richard Wilson + */ + +/** \file + * BMP file decoding (interface). + */ + +#ifndef _NETSURF_IMAGE_BMPREAD_H_ +#define _NETSURF_IMAGE_BMPREAD_H_ + +#include +#include "netsurf/image/bitmap.h" + +/* 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; + + +struct bmp_image { + unsigned char *bmp_data; /** pointer to BMP data */ + unsigned int buffer_size; /** total number of bytes of BMP data available */ + unsigned int width; /** width of BMP (valid after _analyse) */ + unsigned int height; /** heigth of BMP (valid after _analyse) */ + bmp_encoding encoding; /** pixel encoding type */ + unsigned int bitmap_offset; /** offset of bitmap data */ + unsigned int bpp; /** bits per pixel */ + unsigned int colours; /** number of colours */ + unsigned int *colour_table; /** colour table */ + bool reversed; /** scanlines are top to bottom */ + bool decoded; /** whether the image has been decoded */ + struct bitmap *bitmap; /** decoded image */ +}; + +bmp_result bmp_analyse(struct bmp_image *bmp); +bmp_result bmp_decode(struct bmp_image *bmp); +void bmp_finalise(struct bmp_image *bmp); + +#endif diff --git a/makefile b/makefile index b7866fa74..e272d5b1b 100644 --- a/makefile +++ b/makefile @@ -27,7 +27,7 @@ OBJECTS_COMMON += messages.o talloc.o url.o utf8.o \ utils.o # utils/ OBJECTS_COMMON += options.o tree.o # desktop/ -OBJECTS_IMAGE = gif.o gifread.o jpeg.o mng.o # image/ +OBJECTS_IMAGE = bmp.o bmpread.o gif.o gifread.o jpeg.o mng.o # image/ OBJECTS_RISCOS = $(OBJECTS_COMMON) $(OBJECTS_IMAGE) OBJECTS_RISCOS += browser.o netsurf.o selection.o textinput.o \ diff --git a/render/box_construct.c b/render/box_construct.c index 795caf8f0..b8d8e359a 100644 --- a/render/box_construct.c +++ b/render/box_construct.c @@ -51,10 +51,11 @@ static const content_type image_types[] = { #ifdef WITH_GIF CONTENT_GIF, #endif -#ifdef WITH_PNG - CONTENT_PNG, +#ifdef WITH_BMP + CONTENT_BMP, #endif #ifdef WITH_MNG + CONTENT_PNG, CONTENT_JNG, CONTENT_MNG, #endif diff --git a/riscos/filetype.c b/riscos/filetype.c index 08228d6a5..89be38e99 100644 --- a/riscos/filetype.c +++ b/riscos/filetype.c @@ -237,16 +237,17 @@ int ro_content_filetype_from_type(content_type type) { #ifdef WITH_MNG case CONTENT_JNG: return 0xf78; case CONTENT_MNG: return 0xf84; + case CONTENT_PNG: return 0xb60; #endif #ifdef WITH_JPEG case CONTENT_JPEG: return 0xc85; #endif -#ifdef WITH_PNG - case CONTENT_PNG: return 0xb60; -#endif #ifdef WITH_GIF case CONTENT_GIF: return 0x695; #endif +#ifdef WITH_BMP + case CONTENT_BMP: return 0x69c; +#endif #ifdef WITH_SPRITE case CONTENT_SPRITE: return 0xff9; #endif diff --git a/riscos/gui.c b/riscos/gui.c index 5d58e8e36..23897a629 100644 --- a/riscos/gui.c +++ b/riscos/gui.c @@ -109,6 +109,9 @@ #ifndef FILETYPE_GIF #define FILETYPE_GIF 0x695 #endif +#ifndef FILETYPE_BMP +#define FILETYPE_BMP 0x69c +#endif #ifndef FILETYPE_PNG #define FILETYPE_PNG 0xb60 #endif @@ -1492,6 +1495,7 @@ void ro_msg_dataload(wimp_message *message) case FILETYPE_CSS: case FILETYPE_MNG: case FILETYPE_GIF: + case FILETYPE_BMP: case osfile_TYPE_DRAW: case FILETYPE_PNG: case FILETYPE_JPEG: @@ -1779,6 +1783,7 @@ void ro_msg_datasave(wimp_message *message) case FILETYPE_CSS: case FILETYPE_MNG: case FILETYPE_GIF: + case FILETYPE_BMP: case osfile_TYPE_DRAW: case FILETYPE_PNG: case FILETYPE_JPEG: diff --git a/riscos/save.c b/riscos/save.c index e288150a6..ac2e2267a 100644 --- a/riscos/save.c +++ b/riscos/save.c @@ -812,6 +812,11 @@ void ro_gui_save_object_native(struct content *c, char *path) case CONTENT_GIF: bitmap_save(c->bitmap, path); break; +#endif +#ifdef WITH_BMP + case CONTENT_BMP: + bitmap_save(c->bitmap, path); + break; #endif default: break; diff --git a/riscos/window.c b/riscos/window.c index 00d6e3eea..386e4d984 100644 --- a/riscos/window.c +++ b/riscos/window.c @@ -496,6 +496,9 @@ void ro_gui_window_redraw(struct gui_window *g, wimp_draw *redraw) #ifdef WITH_GIF case CONTENT_GIF: #endif +#ifdef WITH_BMP + case CONTENT_BMP: +#endif #ifdef WITH_MNG case CONTENT_JNG: case CONTENT_MNG: diff --git a/utils/config.h b/utils/config.h index 9b871ec3c..a54f56885 100644 --- a/utils/config.h +++ b/utils/config.h @@ -28,9 +28,9 @@ #endif /* Image renderering modules */ +#define WITH_BMP #define WITH_JPEG #define WITH_MNG -#define WITH_PNG #define WITH_GIF #if defined(riscos) || defined(ncos) #define WITH_DRAW