974 lines
28 KiB
C
974 lines
28 KiB
C
#include "mupdf/fitz.h"
|
|
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
static const unsigned char web_palette[] = {
|
|
0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x66, 0x00, 0x00, 0x99, 0x00, 0x00, 0xCC, 0x00, 0x00, 0xFF, 0x00, 0x00,
|
|
0x00, 0x00, 0x33, 0x33, 0x00, 0x33, 0x66, 0x00, 0x33, 0x99, 0x00, 0x33, 0xCC, 0x00, 0x33, 0xFF, 0x00, 0x33,
|
|
0x00, 0x00, 0x66, 0x33, 0x00, 0x66, 0x66, 0x00, 0x66, 0x99, 0x00, 0x66, 0xCC, 0x00, 0x66, 0xFF, 0x00, 0x66,
|
|
0x00, 0x00, 0x99, 0x33, 0x00, 0x99, 0x66, 0x00, 0x99, 0x99, 0x00, 0x99, 0xCC, 0x00, 0x99, 0xFF, 0x00, 0x99,
|
|
0x00, 0x00, 0xCC, 0x33, 0x00, 0xCC, 0x66, 0x00, 0xCC, 0x99, 0x00, 0xCC, 0xCC, 0x00, 0xCC, 0xFF, 0x00, 0xCC,
|
|
0x00, 0x00, 0xFF, 0x33, 0x00, 0xFF, 0x66, 0x00, 0xFF, 0x99, 0x00, 0xFF, 0xCC, 0x00, 0xFF, 0xFF, 0x00, 0xFF,
|
|
0x00, 0x33, 0x00, 0x33, 0x33, 0x00, 0x66, 0x33, 0x00, 0x99, 0x33, 0x00, 0xCC, 0x33, 0x00, 0xFF, 0x33, 0x00,
|
|
0x00, 0x33, 0x33, 0x33, 0x33, 0x33, 0x66, 0x33, 0x33, 0x99, 0x33, 0x33, 0xCC, 0x33, 0x33, 0xFF, 0x33, 0x33,
|
|
0x00, 0x33, 0x66, 0x33, 0x33, 0x66, 0x66, 0x33, 0x66, 0x99, 0x33, 0x66, 0xCC, 0x33, 0x66, 0xFF, 0x33, 0x66,
|
|
0x00, 0x33, 0x99, 0x33, 0x33, 0x99, 0x66, 0x33, 0x99, 0x99, 0x33, 0x99, 0xCC, 0x33, 0x99, 0xFF, 0x33, 0x99,
|
|
0x00, 0x33, 0xCC, 0x33, 0x33, 0xCC, 0x66, 0x33, 0xCC, 0x99, 0x33, 0xCC, 0xCC, 0x33, 0xCC, 0xFF, 0x33, 0xCC,
|
|
0x00, 0x33, 0xFF, 0x33, 0x33, 0xFF, 0x66, 0x33, 0xFF, 0x99, 0x33, 0xFF, 0xCC, 0x33, 0xFF, 0xFF, 0x33, 0xFF,
|
|
0x00, 0x66, 0x00, 0x33, 0x66, 0x00, 0x66, 0x66, 0x00, 0x99, 0x66, 0x00, 0xCC, 0x66, 0x00, 0xFF, 0x66, 0x00,
|
|
0x00, 0x66, 0x33, 0x33, 0x66, 0x33, 0x66, 0x66, 0x33, 0x99, 0x66, 0x33, 0xCC, 0x66, 0x33, 0xFF, 0x66, 0x33,
|
|
0x00, 0x66, 0x66, 0x33, 0x66, 0x66, 0x66, 0x66, 0x66, 0x99, 0x66, 0x66, 0xCC, 0x66, 0x66, 0xFF, 0x66, 0x66,
|
|
0x00, 0x66, 0x99, 0x33, 0x66, 0x99, 0x66, 0x66, 0x99, 0x99, 0x66, 0x99, 0xCC, 0x66, 0x99, 0xFF, 0x66, 0x99,
|
|
0x00, 0x66, 0xCC, 0x33, 0x66, 0xCC, 0x66, 0x66, 0xCC, 0x99, 0x66, 0xCC, 0xCC, 0x66, 0xCC, 0xFF, 0x66, 0xCC,
|
|
0x00, 0x66, 0xFF, 0x33, 0x66, 0xFF, 0x66, 0x66, 0xFF, 0x99, 0x66, 0xFF, 0xCC, 0x66, 0xFF, 0xFF, 0x66, 0xFF,
|
|
0x00, 0x99, 0x00, 0x33, 0x99, 0x00, 0x66, 0x99, 0x00, 0x99, 0x99, 0x00, 0xCC, 0x99, 0x00, 0xFF, 0x99, 0x00,
|
|
0x00, 0x99, 0x33, 0x33, 0x99, 0x33, 0x66, 0x99, 0x33, 0x99, 0x99, 0x33, 0xCC, 0x99, 0x33, 0xFF, 0x99, 0x33,
|
|
0x00, 0x99, 0x66, 0x33, 0x99, 0x66, 0x66, 0x99, 0x66, 0x99, 0x99, 0x66, 0xCC, 0x99, 0x66, 0xFF, 0x99, 0x66,
|
|
0x00, 0x99, 0x99, 0x33, 0x99, 0x99, 0x66, 0x99, 0x99, 0x99, 0x99, 0x99, 0xCC, 0x99, 0x99, 0xFF, 0x99, 0x99,
|
|
0x00, 0x99, 0xCC, 0x33, 0x99, 0xCC, 0x66, 0x99, 0xCC, 0x99, 0x99, 0xCC, 0xCC, 0x99, 0xCC, 0xFF, 0x99, 0xCC,
|
|
0x00, 0x99, 0xFF, 0x33, 0x99, 0xFF, 0x66, 0x99, 0xFF, 0x99, 0x99, 0xFF, 0xCC, 0x99, 0xFF, 0xFF, 0x99, 0xFF,
|
|
0x00, 0xCC, 0x00, 0x33, 0xCC, 0x00, 0x66, 0xCC, 0x00, 0x99, 0xCC, 0x00, 0xCC, 0xCC, 0x00, 0xFF, 0xCC, 0x00,
|
|
0x00, 0xCC, 0x33, 0x33, 0xCC, 0x33, 0x66, 0xCC, 0x33, 0x99, 0xCC, 0x33, 0xCC, 0xCC, 0x33, 0xFF, 0xCC, 0x33,
|
|
0x00, 0xCC, 0x66, 0x33, 0xCC, 0x66, 0x66, 0xCC, 0x66, 0x99, 0xCC, 0x66, 0xCC, 0xCC, 0x66, 0xFF, 0xCC, 0x66,
|
|
0x00, 0xCC, 0x99, 0x33, 0xCC, 0x99, 0x66, 0xCC, 0x99, 0x99, 0xCC, 0x99, 0xCC, 0xCC, 0x99, 0xFF, 0xCC, 0x99,
|
|
0x00, 0xCC, 0xCC, 0x33, 0xCC, 0xCC, 0x66, 0xCC, 0xCC, 0x99, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xFF, 0xCC, 0xCC,
|
|
0x00, 0xCC, 0xFF, 0x33, 0xCC, 0xFF, 0x66, 0xCC, 0xFF, 0x99, 0xCC, 0xFF, 0xCC, 0xCC, 0xFF, 0xFF, 0xCC, 0xFF,
|
|
0x00, 0xFF, 0x00, 0x33, 0xFF, 0x00, 0x66, 0xFF, 0x00, 0x99, 0xFF, 0x00, 0xCC, 0xFF, 0x00, 0xFF, 0xFF, 0x00,
|
|
0x00, 0xFF, 0x33, 0x33, 0xFF, 0x33, 0x66, 0xFF, 0x33, 0x99, 0xFF, 0x33, 0xCC, 0xFF, 0x33, 0xFF, 0xFF, 0x33,
|
|
0x00, 0xFF, 0x66, 0x33, 0xFF, 0x66, 0x66, 0xFF, 0x66, 0x99, 0xFF, 0x66, 0xCC, 0xFF, 0x66, 0xFF, 0xFF, 0x66,
|
|
0x00, 0xFF, 0x99, 0x33, 0xFF, 0x99, 0x66, 0xFF, 0x99, 0x99, 0xFF, 0x99, 0xCC, 0xFF, 0x99, 0xFF, 0xFF, 0x99,
|
|
0x00, 0xFF, 0xCC, 0x33, 0xFF, 0xCC, 0x66, 0xFF, 0xCC, 0x99, 0xFF, 0xCC, 0xCC, 0xFF, 0xCC, 0xFF, 0xFF, 0xCC,
|
|
0x00, 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0x66, 0xFF, 0xFF, 0x99, 0xFF, 0xFF, 0xCC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
};
|
|
|
|
static const unsigned char vga_palette[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
|
|
0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
|
|
0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
|
|
0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF,
|
|
};
|
|
|
|
static const unsigned char gray_palette[] = {
|
|
0x00, 0x00, 0x00, 0x54, 0x54, 0x54,
|
|
0xA8, 0xA8, 0xA8, 0xFF, 0xFF, 0xFF,
|
|
};
|
|
|
|
static const unsigned char bw_palette[] = {
|
|
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF,
|
|
};
|
|
|
|
enum {
|
|
BI_RLE24 = -1,
|
|
BI_RGB = 0,
|
|
BI_RLE8 = 1,
|
|
BI_RLE4 = 2,
|
|
BI_BITFIELDS = 3,
|
|
BI_JPEG = 4,
|
|
BI_PNG = 5,
|
|
BI_ALPHABITS = 6,
|
|
BI_UNSUPPORTED = 42,
|
|
};
|
|
|
|
struct info
|
|
{
|
|
int filesize;
|
|
int offset;
|
|
int topdown;
|
|
int width, height;
|
|
int xres, yres;
|
|
int bitcount;
|
|
int compression;
|
|
int colors;
|
|
int rmask, gmask, bmask, amask;
|
|
unsigned char palette[256 * 3];
|
|
|
|
int extramasks;
|
|
int palettetype;
|
|
unsigned char *samples;
|
|
|
|
int rshift, gshift, bshift, ashift;
|
|
int rbits, gbits, bbits, abits;
|
|
};
|
|
|
|
#define read8(p) ((p)[0])
|
|
#define read16(p) (((p)[1] << 8) | (p)[0])
|
|
#define read32(p) (((p)[3] << 24) | ((p)[2] << 16) | ((p)[1] << 8) | (p)[0])
|
|
|
|
static const unsigned char *
|
|
bmp_read_file_header(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end)
|
|
{
|
|
if (end - p < 14)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in file header in bmp image");
|
|
|
|
if (memcmp(&p[0], "BM", 2))
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "invalid signature in bmp image");
|
|
|
|
info->filesize = read32(p + 2);
|
|
info->offset = read32(p + 10);
|
|
|
|
return p + 14;
|
|
}
|
|
|
|
static const unsigned char *
|
|
bmp_read_bitmap_core_header(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end)
|
|
{
|
|
int size;
|
|
|
|
size = read32(p + 0);
|
|
if (size != 12)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported core header size in bmp image");
|
|
|
|
if (size >= 12)
|
|
{
|
|
if (end - p < 12)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in bitmap core header in bmp image");
|
|
|
|
info->width = read16(p + 4);
|
|
info->height = read16(p + 6);
|
|
info->bitcount = read16(p + 10);
|
|
}
|
|
|
|
info->xres = 2835;
|
|
info->yres = 2835;
|
|
info->compression = BI_RGB;
|
|
info->palettetype = 0;
|
|
|
|
return p + size;
|
|
}
|
|
|
|
static const unsigned char *
|
|
bmp_read_bitmap_os2_header(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end)
|
|
{
|
|
int size;
|
|
|
|
size = read32(p + 0);
|
|
if (size != 16 && size != 64)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported os2 header size in bmp image");
|
|
|
|
if (size >= 16)
|
|
{
|
|
if (end - p < 16)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in bitmap os2 header in bmp image");
|
|
|
|
info->width = read32(p + 4);
|
|
info->height = read32(p + 8);
|
|
info->bitcount = read16(p + 14);
|
|
|
|
info->compression = BI_RGB;
|
|
}
|
|
if (size >= 64)
|
|
{
|
|
if (end - p < 64)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in bitmap os2 header in bmp image");
|
|
|
|
info->compression = read32(p + 16);
|
|
info->xres = read32(p + 24);
|
|
info->yres = read32(p + 28);
|
|
info->colors = read32(p + 32);
|
|
|
|
/* 4 in this header is interpreted as 24 bit RLE encoding */
|
|
if (info->compression < 0)
|
|
info->compression = BI_UNSUPPORTED;
|
|
else if (info->compression == 4)
|
|
info->compression = BI_RLE24;
|
|
}
|
|
|
|
info->palettetype = 1;
|
|
|
|
return p + size;
|
|
}
|
|
|
|
static void maskinfo(unsigned int mask, int *shift, int *bits)
|
|
{
|
|
*bits = 0;
|
|
*shift = 0;
|
|
if (mask) {
|
|
while ((mask & 1) == 0) {
|
|
*shift += 1;
|
|
mask >>= 1;
|
|
}
|
|
while ((mask & 1) == 1) {
|
|
*bits += 1;
|
|
mask >>= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static const unsigned char *
|
|
bmp_read_bitmap_info_header(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end)
|
|
{
|
|
int size;
|
|
|
|
size = read32(p + 0);
|
|
if (size != 40 && size != 52 && size != 56 && size != 64 &&
|
|
size != 108 && size != 124)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported info header size in bmp image");
|
|
|
|
if (size >= 40)
|
|
{
|
|
if (end - p < 40)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in bitmap info header in bmp image");
|
|
|
|
info->width = read32(p + 4);
|
|
info->topdown = (p[8 + 3] & 0x80) != 0;
|
|
if (info->topdown)
|
|
info->height = -read32(p + 8);
|
|
else
|
|
info->height = read32(p + 8);
|
|
info->bitcount = read16(p + 14);
|
|
info->compression = read32(p + 16);
|
|
info->xres = read32(p + 24);
|
|
info->yres = read32(p + 28);
|
|
info->colors = read32(p + 32);
|
|
|
|
if (size == 40 && info->compression == BI_BITFIELDS && (info->bitcount == 16 || info->bitcount == 32))
|
|
info->extramasks = 1;
|
|
else if (size == 40 && info->compression == BI_ALPHABITS && (info->bitcount == 16 || info->bitcount == 32))
|
|
info->extramasks = 1;
|
|
|
|
if (info->bitcount == 16) {
|
|
info->rmask = 0x00007c00;
|
|
info->gmask = 0x000003e0;
|
|
info->bmask = 0x0000001f;
|
|
info->amask = 0x00000000;
|
|
} else if (info->bitcount == 32) {
|
|
info->rmask = 0x00ff0000;
|
|
info->gmask = 0x0000ff00;
|
|
info->bmask = 0x000000ff;
|
|
info->amask = 0x00000000;
|
|
}
|
|
}
|
|
if (size >= 52)
|
|
{
|
|
if (end - p < 52)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in bitmap info header in bmp image");
|
|
|
|
if (info->compression == BI_BITFIELDS) {
|
|
info->rmask = read32(p + 40);
|
|
info->gmask = read32(p + 44);
|
|
info->bmask = read32(p + 48);
|
|
}
|
|
}
|
|
if (size >= 56)
|
|
{
|
|
if (end - p < 56)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in bitmap info header in bmp image");
|
|
|
|
if (info->compression == BI_BITFIELDS) {
|
|
info->amask = read32(p + 52);
|
|
}
|
|
}
|
|
|
|
info->palettetype = 1;
|
|
|
|
return p + size;
|
|
}
|
|
|
|
static const unsigned char *
|
|
bmp_read_extra_masks(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end)
|
|
{
|
|
int size = 0;
|
|
|
|
if (info->compression == BI_BITFIELDS)
|
|
{
|
|
size = 12;
|
|
if (end - p < 12)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in mask header in bmp image");
|
|
|
|
info->rmask = read32(p + 0);
|
|
info->gmask = read32(p + 4);
|
|
info->bmask = read32(p + 8);
|
|
}
|
|
else if (info->compression == BI_ALPHABITS)
|
|
{
|
|
size = 16;
|
|
if (end - p < 16)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in mask header in bmp image");
|
|
|
|
/* ignore alpha mask */
|
|
info->rmask = read32(p + 0);
|
|
info->gmask = read32(p + 4);
|
|
info->bmask = read32(p + 8);
|
|
}
|
|
|
|
return p + size;
|
|
}
|
|
|
|
static int
|
|
bmp_palette_is_gray(fz_context *ctx, struct info *info, int readcolors)
|
|
{
|
|
int i;
|
|
for (i = 0; i < readcolors; i++)
|
|
{
|
|
int rgdiff = fz_absi(info->palette[3 * i + 0] - info->palette[3 * i + 1]);
|
|
int gbdiff = fz_absi(info->palette[3 * i + 1] - info->palette[3 * i + 2]);
|
|
int rbdiff = fz_absi(info->palette[3 * i + 0] - info->palette[3 * i + 2]);
|
|
if (rgdiff > 2 || gbdiff > 2 || rbdiff > 2)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
bmp_load_default_palette(fz_context *ctx, struct info *info, int readcolors)
|
|
{
|
|
int i;
|
|
|
|
fz_warn(ctx, "color table too short; loading default palette");
|
|
|
|
if (info->bitcount == 8)
|
|
{
|
|
if (!bmp_palette_is_gray(ctx, info, readcolors))
|
|
memcpy(&info->palette[readcolors * 3], &web_palette[readcolors * 3],
|
|
sizeof(web_palette) - readcolors * 3);
|
|
else
|
|
for (i = readcolors; i < 256; i++)
|
|
{
|
|
info->palette[3 * i + 0] = i;
|
|
info->palette[3 * i + 1] = i;
|
|
info->palette[3 * i + 2] = i;
|
|
}
|
|
}
|
|
else if (info->bitcount == 4)
|
|
{
|
|
if (!bmp_palette_is_gray(ctx, info, readcolors))
|
|
memcpy(&info->palette[readcolors * 3], &vga_palette[readcolors * 3],
|
|
sizeof(vga_palette) - readcolors * 3);
|
|
else
|
|
for (i = readcolors; i < 16; i++)
|
|
{
|
|
info->palette[3 * i + 0] = (i << 4) | i;
|
|
info->palette[3 * i + 1] = (i << 4) | i;
|
|
info->palette[3 * i + 2] = (i << 4) | i;
|
|
}
|
|
}
|
|
else if (info->bitcount == 2)
|
|
memcpy(info->palette, gray_palette, sizeof(gray_palette));
|
|
else if (info->bitcount == 1)
|
|
memcpy(info->palette, bw_palette, sizeof(bw_palette));
|
|
}
|
|
|
|
static const unsigned char *
|
|
bmp_read_color_table(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end)
|
|
{
|
|
int i, colors, readcolors;
|
|
|
|
if (info->bitcount > 8)
|
|
return p;
|
|
|
|
if (info->colors == 0)
|
|
colors = 1 << info->bitcount;
|
|
else
|
|
colors = info->colors;
|
|
|
|
colors = fz_mini(colors, 1 << info->bitcount);
|
|
|
|
if (info->palettetype == 0)
|
|
{
|
|
readcolors = fz_mini(colors, (end - p) / 3);
|
|
for (i = 0; i < readcolors; i++)
|
|
{
|
|
info->palette[3 * i + 0] = read8(p + i * 3 + 2);
|
|
info->palette[3 * i + 1] = read8(p + i * 3 + 1);
|
|
info->palette[3 * i + 2] = read8(p + i * 3 + 0);
|
|
}
|
|
if (readcolors < colors)
|
|
bmp_load_default_palette(ctx, info, readcolors);
|
|
return p + readcolors * 3;
|
|
}
|
|
else
|
|
{
|
|
readcolors = fz_mini(colors, (end - p) / 4);
|
|
for (i = 0; i < readcolors; i++)
|
|
{
|
|
/* ignore alpha channel */
|
|
info->palette[3 * i + 0] = read8(p + i * 4 + 2);
|
|
info->palette[3 * i + 1] = read8(p + i * 4 + 1);
|
|
info->palette[3 * i + 2] = read8(p + i * 4 + 0);
|
|
}
|
|
if (readcolors < colors)
|
|
bmp_load_default_palette(ctx, info, readcolors);
|
|
return p + readcolors * 4;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
static unsigned char *
|
|
bmp_decompress_rle24(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char **end)
|
|
{
|
|
const unsigned char *sp;
|
|
unsigned char *dp, *ep, *decompressed;
|
|
int width = info->width;
|
|
int height = info->height;
|
|
int stride;
|
|
int x, i;
|
|
|
|
stride = (width*3 + 3) / 4 * 4;
|
|
|
|
sp = p;
|
|
dp = decompressed = fz_calloc(ctx, height, stride);
|
|
ep = dp + height * stride;
|
|
x = 0;
|
|
|
|
while (sp + 2 <= *end)
|
|
{
|
|
if (sp[0] == 0 && sp[1] == 0)
|
|
{ /* end of line */
|
|
if (x*3 < stride)
|
|
dp += stride - x*3;
|
|
sp += 2;
|
|
x = 0;
|
|
}
|
|
else if (sp[0] == 0 && sp[1] == 1)
|
|
{ /* end of bitmap */
|
|
dp = ep;
|
|
break;
|
|
}
|
|
else if (sp[0] == 0 && sp[1] == 2)
|
|
{ /* delta */
|
|
int deltax, deltay;
|
|
if (sp + 4 > *end)
|
|
break;
|
|
deltax = sp[2];
|
|
deltay = sp[3];
|
|
dp += deltax*3 + deltay * stride;
|
|
sp += 4;
|
|
x += deltax;
|
|
}
|
|
else if (sp[0] == 0 && sp[1] >= 3)
|
|
{ /* absolute */
|
|
int n = sp[1] * 3;
|
|
int nn = (n + 1) / 2 * 2;
|
|
if (sp + 2 + nn > *end)
|
|
break;
|
|
if (dp + n > ep) {
|
|
fz_warn(ctx, "buffer overflow in bitmap data in bmp image");
|
|
break;
|
|
}
|
|
sp += 2;
|
|
for (i = 0; i < n; i++)
|
|
dp[i] = sp[i];
|
|
dp += n;
|
|
sp += (n + 1) / 2 * 2;
|
|
x += n;
|
|
}
|
|
else
|
|
{ /* encoded */
|
|
int n = sp[0] * 3;
|
|
if (sp + 1 + 3 > *end)
|
|
break;
|
|
if (dp + n > ep) {
|
|
fz_warn(ctx, "buffer overflow in bitmap data in bmp image");
|
|
break;
|
|
}
|
|
for (i = 0; i < n / 3; i++) {
|
|
dp[i * 3 + 0] = sp[1];
|
|
dp[i * 3 + 1] = sp[2];
|
|
dp[i * 3 + 2] = sp[3];
|
|
}
|
|
dp += n;
|
|
sp += 1 + 3;
|
|
x += n;
|
|
}
|
|
}
|
|
|
|
if (dp < ep)
|
|
fz_warn(ctx, "premature end of bitmap data in bmp image");
|
|
|
|
info->compression = BI_RGB;
|
|
info->bitcount = 24;
|
|
*end = ep;
|
|
return decompressed;
|
|
}
|
|
|
|
static unsigned char *
|
|
bmp_decompress_rle8(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char **end)
|
|
{
|
|
const unsigned char *sp;
|
|
unsigned char *dp, *ep, *decompressed;
|
|
int width = info->width;
|
|
int height = info->height;
|
|
int stride;
|
|
int x, i;
|
|
|
|
stride = (width + 3) / 4 * 4;
|
|
|
|
sp = p;
|
|
dp = decompressed = fz_calloc(ctx, height, stride);
|
|
ep = dp + height * stride;
|
|
x = 0;
|
|
|
|
while (sp + 2 <= *end)
|
|
{
|
|
if (sp[0] == 0 && sp[1] == 0)
|
|
{ /* end of line */
|
|
if (x < stride)
|
|
dp += stride - x;
|
|
sp += 2;
|
|
x = 0;
|
|
}
|
|
else if (sp[0] == 0 && sp[1] == 1)
|
|
{ /* end of bitmap */
|
|
dp = ep;
|
|
break;
|
|
}
|
|
else if (sp[0] == 0 && sp[1] == 2)
|
|
{ /* delta */
|
|
int deltax, deltay;
|
|
if (sp + 4 > *end)
|
|
break;
|
|
deltax = sp[2];
|
|
deltay = sp[3];
|
|
dp += deltax + deltay * stride;
|
|
sp += 4;
|
|
x += deltax;
|
|
}
|
|
else if (sp[0] == 0 && sp[1] >= 3)
|
|
{ /* absolute */
|
|
int n = sp[1];
|
|
int nn = (n + 1) / 2 * 2;
|
|
if (sp + 2 + nn > *end)
|
|
break;
|
|
if (dp + n > ep) {
|
|
fz_warn(ctx, "buffer overflow in bitmap data in bmp image");
|
|
break;
|
|
}
|
|
sp += 2;
|
|
for (i = 0; i < n; i++)
|
|
dp[i] = sp[i];
|
|
dp += n;
|
|
sp += (n + 1) / 2 * 2;
|
|
x += n;
|
|
}
|
|
else
|
|
{ /* encoded */
|
|
int n = sp[0];
|
|
if (dp + n > ep) {
|
|
fz_warn(ctx, "buffer overflow in bitmap data in bmp image");
|
|
break;
|
|
}
|
|
for (i = 0; i < n; i++)
|
|
dp[i] = sp[1];
|
|
dp += n;
|
|
sp += 2;
|
|
x += n;
|
|
}
|
|
}
|
|
|
|
if (dp < ep)
|
|
fz_warn(ctx, "premature end of bitmap data in bmp image");
|
|
|
|
info->compression = BI_RGB;
|
|
info->bitcount = 8;
|
|
*end = ep;
|
|
return decompressed;
|
|
}
|
|
|
|
static unsigned char *
|
|
bmp_decompress_rle4(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char **end)
|
|
{
|
|
const unsigned char *sp;
|
|
unsigned char *dp, *ep, *decompressed;
|
|
int width = info->width;
|
|
int height = info->height;
|
|
int stride;
|
|
int i, x;
|
|
|
|
stride = ((width + 1) / 2 + 3) / 4 * 4;
|
|
|
|
sp = p;
|
|
dp = decompressed = fz_calloc(ctx, height, stride);
|
|
ep = dp + height * stride;
|
|
x = 0;
|
|
|
|
while (sp + 2 <= *end)
|
|
{
|
|
if (sp[0] == 0 && sp[1] == 0)
|
|
{ /* end of line */
|
|
int xx = x / 2;
|
|
if (xx < stride)
|
|
dp += stride - xx;
|
|
sp += 2;
|
|
x = 0;
|
|
}
|
|
else if (sp[0] == 0 && sp[1] == 1)
|
|
{ /* end of bitmap */
|
|
dp = ep;
|
|
break;
|
|
}
|
|
else if (sp[0] == 0 && sp[1] == 2)
|
|
{ /* delta */
|
|
int deltax, deltay, startlow;
|
|
if (sp + 4 > *end)
|
|
break;
|
|
deltax = sp[2];
|
|
deltay = sp[3];
|
|
startlow = x & 1;
|
|
dp += (deltax + startlow) / 2 + deltay * stride;
|
|
sp += 4;
|
|
x += deltax;
|
|
}
|
|
else if (sp[0] == 0 && sp[1] >= 3)
|
|
{ /* absolute */
|
|
int n = sp[1];
|
|
int nn = ((n + 1) / 2 + 1) / 2 * 2;
|
|
if (sp + 2 + nn > *end)
|
|
break;
|
|
if (dp + n / 2 > ep) {
|
|
fz_warn(ctx, "buffer overflow in bitmap data in bmp image");
|
|
break;
|
|
}
|
|
sp += 2;
|
|
for (i = 0; i < n; i++, x++)
|
|
{
|
|
int val = i & 1 ? (sp[i/2]) & 0xF : (sp[i/2] >> 4) & 0xF;
|
|
if (x & 1)
|
|
*dp++ |= val;
|
|
else
|
|
*dp |= val << 4;
|
|
}
|
|
sp += nn;
|
|
}
|
|
else
|
|
{ /* encoded */
|
|
int n = sp[0];
|
|
int hi = (sp[1] >> 4) & 0xF;
|
|
int lo = sp[1] & 0xF;
|
|
if (dp + n / 2 + (x & 1) > ep) {
|
|
fz_warn(ctx, "buffer overflow in bitmap data in bmp image");
|
|
break;
|
|
}
|
|
for (i = 0; i < n; i++, x++)
|
|
{
|
|
int val = i & 1 ? lo : hi;
|
|
if (x & 1)
|
|
*dp++ |= val;
|
|
else
|
|
*dp |= val << 4;
|
|
}
|
|
sp += 2;
|
|
}
|
|
}
|
|
|
|
info->compression = BI_RGB;
|
|
info->bitcount = 4;
|
|
*end = ep;
|
|
return decompressed;
|
|
}
|
|
|
|
static fz_pixmap *
|
|
bmp_read_bitmap(fz_context *ctx, struct info *info, const unsigned char *p, const unsigned char *end)
|
|
{
|
|
const int mults[] = { 0, 8191, 2730, 1170, 546, 264, 130, 64 };
|
|
fz_pixmap *pix;
|
|
const unsigned char *ssp;
|
|
unsigned char *ddp;
|
|
unsigned char *decompressed = NULL;
|
|
int bitcount, width, height;
|
|
int sstride, dstride;
|
|
int rmult, gmult, bmult, amult;
|
|
int rtrunc, gtrunc, btrunc, atrunc;
|
|
int x, y;
|
|
|
|
if (info->compression == BI_RLE8)
|
|
ssp = decompressed = bmp_decompress_rle8(ctx, info, p, &end);
|
|
else if (info->compression == BI_RLE4)
|
|
ssp = decompressed = bmp_decompress_rle4(ctx, info, p, &end);
|
|
else if (info->compression == BI_RLE24)
|
|
ssp = decompressed = bmp_decompress_rle24(ctx, info, p, &end);
|
|
else
|
|
ssp = p;
|
|
|
|
bitcount = info->bitcount;
|
|
width = info->width;
|
|
height = info->height;
|
|
|
|
sstride = ((width * bitcount + 31) / 32) * 4;
|
|
|
|
if (ssp + sstride * height > end)
|
|
{
|
|
fz_free(ctx, decompressed);
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in bitmap data in bmp image");
|
|
}
|
|
|
|
fz_try(ctx)
|
|
pix = fz_new_pixmap(ctx, fz_device_rgb(ctx), width, height, NULL, 1);
|
|
fz_catch(ctx)
|
|
{
|
|
fz_free(ctx, decompressed);
|
|
fz_rethrow(ctx);
|
|
}
|
|
|
|
ddp = pix->samples;
|
|
dstride = pix->stride;
|
|
if (!info->topdown)
|
|
{
|
|
ddp = pix->samples + (height - 1) * dstride;
|
|
dstride = -dstride;
|
|
}
|
|
|
|
/* These only apply for components in 16-bit and 32-bit mode
|
|
1-bit (1 * 8191) / 32
|
|
2-bit (3 * 2730) / 32
|
|
3-bit (7 * 1170) / 32
|
|
4-bit (15 * 546) / 32
|
|
5-bit (31 * 264) / 32
|
|
6-bit (63 * 130) / 32
|
|
7-bit (127 * 64) / 32
|
|
*/
|
|
rmult = info->rbits < 8 ? mults[info->rbits] : 1;
|
|
gmult = info->gbits < 8 ? mults[info->gbits] : 1;
|
|
bmult = info->bbits < 8 ? mults[info->bbits] : 1;
|
|
amult = info->abits < 8 ? mults[info->abits] : 1;
|
|
rtrunc = info->rbits < 8 ? 5 : (info->rbits - 8);
|
|
gtrunc = info->gbits < 8 ? 5 : (info->gbits - 8);
|
|
btrunc = info->bbits < 8 ? 5 : (info->bbits - 8);
|
|
atrunc = info->abits < 8 ? 5 : (info->abits - 8);
|
|
|
|
for (y = 0; y < height; y++)
|
|
{
|
|
const unsigned char *sp = ssp + y * sstride;
|
|
unsigned char *dp = ddp + y * dstride;
|
|
|
|
switch (bitcount)
|
|
{
|
|
case 32:
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
unsigned int sample = (sp[3] << 24) | (sp[2] << 16) | (sp[1] << 8) | sp[0];
|
|
unsigned int r = (sample & info->rmask) >> info->rshift;
|
|
unsigned int g = (sample & info->gmask) >> info->gshift;
|
|
unsigned int b = (sample & info->bmask) >> info->bshift;
|
|
unsigned int a = info->abits == 0 ? 255 : (sample & info->amask) >> info->ashift;
|
|
*dp++ = (r * rmult) >> rtrunc;
|
|
*dp++ = (g * gmult) >> gtrunc;
|
|
*dp++ = (b * bmult) >> btrunc;
|
|
*dp++ = info->abits == 0 ? a : (a * amult) >> atrunc;
|
|
sp += 4;
|
|
}
|
|
break;
|
|
case 24:
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
*dp++ = sp[2];
|
|
*dp++ = sp[1];
|
|
*dp++ = sp[0];
|
|
*dp++ = 255;
|
|
sp += 3;
|
|
}
|
|
break;
|
|
case 16:
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
unsigned int sample = (sp[1] << 8) | sp[0];
|
|
unsigned int r = (sample & info->rmask) >> info->rshift;
|
|
unsigned int g = (sample & info->gmask) >> info->gshift;
|
|
unsigned int b = (sample & info->bmask) >> info->bshift;
|
|
unsigned int a = (sample & info->amask) >> info->ashift;
|
|
*dp++ = (r * rmult) >> rtrunc;
|
|
*dp++ = (g * gmult) >> gtrunc;
|
|
*dp++ = (b * bmult) >> btrunc;
|
|
*dp++ = info->abits == 0 ? 255 : (a * amult) >> atrunc;
|
|
sp += 2;
|
|
}
|
|
break;
|
|
case 8:
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
*dp++ = info->palette[3 * sp[0] + 0];
|
|
*dp++ = info->palette[3 * sp[0] + 1];
|
|
*dp++ = info->palette[3 * sp[0] + 2];
|
|
*dp++ = 255;
|
|
sp++;
|
|
}
|
|
break;
|
|
case 4:
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
int idx;
|
|
switch (x & 1)
|
|
{
|
|
case 0: idx = (sp[0] >> 4) & 0x0f; break;
|
|
case 1: idx = (sp[0] >> 0) & 0x0f; sp++; break;
|
|
}
|
|
*dp++ = info->palette[3 * idx + 0];
|
|
*dp++ = info->palette[3 * idx + 1];
|
|
*dp++ = info->palette[3 * idx + 2];
|
|
*dp++ = 255;
|
|
}
|
|
break;
|
|
case 2:
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
int idx;
|
|
switch (x & 3)
|
|
{
|
|
case 0: idx = (sp[0] >> 6) & 0x03; break;
|
|
case 1: idx = (sp[0] >> 4) & 0x03; break;
|
|
case 2: idx = (sp[0] >> 2) & 0x03; break;
|
|
case 3: idx = (sp[0] >> 0) & 0x03; sp++; break;
|
|
}
|
|
*dp++ = info->palette[3 * idx + 0];
|
|
*dp++ = info->palette[3 * idx + 1];
|
|
*dp++ = info->palette[3 * idx + 2];
|
|
*dp++ = 255;
|
|
}
|
|
break;
|
|
case 1:
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
int idx;
|
|
switch (x & 7)
|
|
{
|
|
case 0: idx = (sp[0] >> 7) & 0x01; break;
|
|
case 1: idx = (sp[0] >> 6) & 0x01; break;
|
|
case 2: idx = (sp[0] >> 5) & 0x01; break;
|
|
case 3: idx = (sp[0] >> 4) & 0x01; break;
|
|
case 4: idx = (sp[0] >> 3) & 0x01; break;
|
|
case 5: idx = (sp[0] >> 2) & 0x01; break;
|
|
case 6: idx = (sp[0] >> 1) & 0x01; break;
|
|
case 7: idx = (sp[0] >> 0) & 0x01; sp++; break;
|
|
}
|
|
*dp++ = info->palette[3 * idx + 0];
|
|
*dp++ = info->palette[3 * idx + 1];
|
|
*dp++ = info->palette[3 * idx + 2];
|
|
*dp++ = 255;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
fz_free(ctx, decompressed);
|
|
fz_premultiply_pixmap(ctx, pix);
|
|
return pix;
|
|
}
|
|
|
|
static fz_pixmap *
|
|
bmp_read_image(fz_context *ctx, struct info *info, const unsigned char *p, size_t total, int only_metadata)
|
|
{
|
|
const unsigned char *begin = p;
|
|
const unsigned char *end = p + total;
|
|
int size;
|
|
|
|
memset(info, 0x00, sizeof (*info));
|
|
|
|
p = bmp_read_file_header(ctx, info, p, end);
|
|
|
|
info->filesize = fz_mini(info->filesize, (int)total);
|
|
|
|
if (end - p < 4)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "premature end in bitmap core header in bmp image");
|
|
size = read32(p + 0);
|
|
|
|
if (size == 12)
|
|
p = bmp_read_bitmap_core_header(ctx, info, p, end);
|
|
else if (size == 40 || size == 52 || size == 56 || size == 108 || size == 124)
|
|
{
|
|
p = bmp_read_bitmap_info_header(ctx, info, p, end);
|
|
if (info->extramasks)
|
|
p = bmp_read_extra_masks(ctx, info, p, end);
|
|
}
|
|
else if (size == 16 || size == 64)
|
|
p = bmp_read_bitmap_os2_header(ctx, info, p, end);
|
|
else
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "invalid header size (%d) in bmp image", size);
|
|
|
|
maskinfo(info->rmask, &info->rshift, &info->rbits);
|
|
maskinfo(info->gmask, &info->gshift, &info->gbits);
|
|
maskinfo(info->bmask, &info->bshift, &info->bbits);
|
|
maskinfo(info->amask, &info->ashift, &info->abits);
|
|
|
|
if (info->width <= 0 || info->width > SHRT_MAX || info->height <= 0 || info->height > SHRT_MAX)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "dimensions (%d x %d) out of range in bmp image",
|
|
info->width, info->height);
|
|
if (info->compression != BI_RGB && info->compression != BI_RLE8 &&
|
|
info->compression != BI_RLE4 && info->compression != BI_BITFIELDS &&
|
|
info->compression != BI_JPEG && info->compression != BI_PNG &&
|
|
info->compression != BI_ALPHABITS && info->compression != BI_RLE24)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported compression method (%d) in bmp image", info->compression);
|
|
if ((info->compression == BI_RGB && info->bitcount != 1 &&
|
|
info->bitcount != 2 && info->bitcount != 4 &&
|
|
info->bitcount != 8 && info->bitcount != 16 &&
|
|
info->bitcount != 24 && info->bitcount != 32) ||
|
|
(info->compression == BI_RLE8 && info->bitcount != 8) ||
|
|
(info->compression == BI_RLE4 && info->bitcount != 4) ||
|
|
(info->compression == BI_BITFIELDS && info->bitcount != 16 && info->bitcount != 32) ||
|
|
(info->compression == BI_JPEG && info->bitcount != 0) ||
|
|
(info->compression == BI_PNG && info->bitcount != 0) ||
|
|
(info->compression == BI_ALPHABITS && info->bitcount != 16 && info->bitcount != 32) ||
|
|
(info->compression == BI_RLE24 && info->bitcount != 24))
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "invalid bits per pixel (%d) for compression (%d) in bmp image",
|
|
info->bitcount, info->compression);
|
|
if (info->rbits < 0 || info->rbits > info->bitcount)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported %d bit red mask in bmp image", info->rbits);
|
|
if (info->gbits < 0 || info->gbits > info->bitcount)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported %d bit green mask in bmp image", info->gbits);
|
|
if (info->bbits < 0 || info->bbits > info->bitcount)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported %d bit blue mask in bmp image", info->bbits);
|
|
if (info->abits < 0 || info->abits > info->bitcount)
|
|
fz_throw(ctx, FZ_ERROR_GENERIC, "unsupported %d bit alpha mask in bmp image", info->abits);
|
|
|
|
if (only_metadata)
|
|
return NULL;
|
|
|
|
if (info->compression == BI_JPEG)
|
|
{
|
|
if (p - begin < info->offset)
|
|
p = begin + info->offset;
|
|
return fz_load_jpeg(ctx, p, end - p);
|
|
}
|
|
else if (info->compression == BI_PNG)
|
|
{
|
|
if (p - begin < info->offset)
|
|
p = begin + info->offset;
|
|
return fz_load_png(ctx, p, end - p);
|
|
}
|
|
else
|
|
{
|
|
const unsigned char *color_table_end = begin + info->offset;
|
|
if (end - begin < info->offset)
|
|
color_table_end = end;
|
|
p = bmp_read_color_table(ctx, info, p, color_table_end);
|
|
|
|
if (p - begin < info->offset)
|
|
p = begin + info->offset;
|
|
return bmp_read_bitmap(ctx, info, p, end);
|
|
}
|
|
}
|
|
|
|
fz_pixmap *
|
|
fz_load_bmp(fz_context *ctx, const unsigned char *p, size_t total)
|
|
{
|
|
struct info bmp;
|
|
fz_pixmap *image;
|
|
|
|
image = bmp_read_image(ctx, &bmp, p, total, 0);
|
|
image->xres = bmp.xres / (1000.0f / 25.4f);
|
|
image->yres = bmp.yres / (1000.0f / 25.4f);
|
|
|
|
return image;
|
|
}
|
|
|
|
void
|
|
fz_load_bmp_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
|
|
{
|
|
struct info bmp;
|
|
|
|
bmp_read_image(ctx, &bmp, p, total, 1);
|
|
|
|
*cspacep = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
|
|
*wp = bmp.width;
|
|
*hp = bmp.height;
|
|
*xresp = bmp.xres / (1000.0f / 25.4f);
|
|
*yresp = bmp.yres / (1000.0f / 25.4f);
|
|
}
|