X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/4dfa55222d1fe433a6f5e0e8c67b1168a9cb1ee3..d9cd69d38f400962110f54f89fe7feb5bf182f0f:/bmp.c diff --git a/bmp.c b/bmp.c index 533fdbc9..8f37d32a 100644 --- a/bmp.c +++ b/bmp.c @@ -1,5 +1,5 @@ -#include "image.h" #include +#include "imageri.h" /* =head1 NAME @@ -44,14 +44,15 @@ static int write_4bit_data(io_glue *ig, i_img *im); static int write_8bit_data(io_glue *ig, i_img *im); static int write_24bit_data(io_glue *ig, i_img *im); static int read_bmp_pal(io_glue *ig, i_img *im, int count); -static i_img *read_1bit_bmp(io_glue *ig, int xsize, int ysize, - int clr_used); +static i_img *read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, + int compression, long offbits, int allow_incomplete); static i_img *read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, - int compression); + int compression, long offbits, int allow_incomplete); static i_img *read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, - int compression); + int compression, long offbits, int allow_incomplete); static i_img *read_direct_bmp(io_glue *ig, int xsize, int ysize, - int bit_count, int clr_used, int compression); + int bit_count, int clr_used, int compression, + long offbits, int allow_incomplete); /* =item i_writebmp_wiol(im, io_glue) @@ -101,21 +102,23 @@ BI_BITFIELDS images too, but I need a test image. */ i_img * -i_readbmp_wiol(io_glue *ig) { - int b_magic, m_magic, filesize, dummy, infohead_size; +i_readbmp_wiol(io_glue *ig, int allow_incomplete) { + int b_magic, m_magic, filesize, res1, res2, infohead_size; int xsize, ysize, planes, bit_count, compression, size_image, xres, yres; int clr_used, clr_important, offbits; i_img *im; + + mm_log((1, "i_readbmp_wiol(ig %p)\n", ig)); io_glue_commit_types(ig); i_clear_error(); if (!read_packed(ig, "CCVvvVVVVvvVVVVVV", &b_magic, &m_magic, &filesize, - &dummy, &dummy, &offbits, &infohead_size, + &res1, &res2, &offbits, &infohead_size, &xsize, &ysize, &planes, &bit_count, &compression, &size_image, &xres, &yres, &clr_used, &clr_important)) { - i_push_error(0, "file too short"); + i_push_error(0, "file too short to be a BMP file"); return 0; } if (b_magic != 'B' || m_magic != 'M' || infohead_size != INFOHEAD_SIZE @@ -123,24 +126,39 @@ i_readbmp_wiol(io_glue *ig) { i_push_error(0, "not a BMP file"); return 0; } + + mm_log((1, " bmp header: filesize %d offbits %d xsize %d ysize %d planes %d " + "bit_count %d compression %d size %d xres %d yres %d clr_used %d " + "clr_important %d\n", filesize, offbits, xsize, ysize, planes, + bit_count, compression, size_image, xres, yres, clr_used, + clr_important)); + + if (!i_int_check_image_file_limits(xsize, abs(ysize), 3, sizeof(i_sample_t))) { + mm_log((1, "i_readbmp_wiol: image size exceeds limits\n")); + return NULL; + } switch (bit_count) { case 1: - im = read_1bit_bmp(ig, xsize, ysize, clr_used); + im = read_1bit_bmp(ig, xsize, ysize, clr_used, compression, offbits, + allow_incomplete); break; case 4: - im = read_4bit_bmp(ig, xsize, ysize, clr_used, compression); + im = read_4bit_bmp(ig, xsize, ysize, clr_used, compression, offbits, + allow_incomplete); break; case 8: - im = read_8bit_bmp(ig, xsize, ysize, clr_used, compression); + im = read_8bit_bmp(ig, xsize, ysize, clr_used, compression, offbits, + allow_incomplete); break; case 32: case 24: case 16: - im = read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression); + im = read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression, + offbits, allow_incomplete); break; default: @@ -148,17 +166,23 @@ i_readbmp_wiol(io_glue *ig) { return NULL; } - /* store the resolution */ - if (xres && !yres) - yres = xres; - else if (yres && !xres) - xres = yres; - if (xres) { - i_tags_set_float(&im->tags, "i_xres", 0, xres * 0.0254); - i_tags_set_float(&im->tags, "i_yres", 0, yres * 0.0254); + if (im) { + /* store the resolution */ + if (xres && !yres) + yres = xres; + else if (yres && !xres) + xres = yres; + if (xres) { + i_tags_set_float2(&im->tags, "i_xres", 0, xres * 0.0254, 4); + i_tags_set_float2(&im->tags, "i_yres", 0, yres * 0.0254, 4); + } + i_tags_addn(&im->tags, "bmp_compression", 0, compression); + i_tags_addn(&im->tags, "bmp_important_colors", 0, clr_important); + i_tags_addn(&im->tags, "bmp_used_colors", 0, clr_used); + i_tags_addn(&im->tags, "bmp_filesize", 0, filesize); + i_tags_addn(&im->tags, "bmp_bit_count", 0, bit_count); + i_tags_add(&im->tags, "i_format", 0, "bmp", 3, 0); } - i_tags_addn(&im->tags, "bmp_compression", 0, compression); - i_tags_addn(&im->tags, "bmp_important_colors", 0, clr_important); return im; } @@ -195,37 +219,37 @@ int read_packed(io_glue *ig, char *format, ...) { switch (*format) { case 'v': - if (ig->readcb(ig, buf, 2) == -1) + if (ig->readcb(ig, buf, 2) != 2) return 0; *p = buf[0] + (buf[1] << 8); break; case 'V': - if (ig->readcb(ig, buf, 4) == -1) + if (ig->readcb(ig, buf, 4) != 4) return 0; *p = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24); break; case 'C': - if (ig->readcb(ig, buf, 1) == -1) + if (ig->readcb(ig, buf, 1) != 1) return 0; *p = buf[0]; break; case 'c': - if (ig->readcb(ig, buf, 1) == -1) + if (ig->readcb(ig, buf, 1) != 1) return 0; *p = (char)buf[0]; break; case '3': /* extension - 24-bit number */ - if (ig->readcb(ig, buf, 3) == -1) + if (ig->readcb(ig, buf, 3) != 3) return 0; *p = buf[0] + (buf[1] << 8) + (buf[2] << 16); break; default: - m_fatal(1, "Unknown read_packed format code 0x%02x", *format); + i_fatal(1, "Unknown read_packed format code 0x%02x", *format); } ++format; } @@ -278,7 +302,7 @@ write_packed(io_glue *ig, char *format, ...) { break; default: - m_fatal(1, "Unknown read_packed format code 0x%02x", *format); + i_fatal(1, "Unknown write_packed format code 0x%02x", *format); } ++format; } @@ -343,7 +367,7 @@ int write_bmphead(io_glue *ig, i_img *im, int bit_count, int data_size) { if (!write_packed(ig, "CCVvvVVVVvvVVVVVV", 'B', 'M', data_size+offset, 0, 0, offset, INFOHEAD_SIZE, im->xsize, im->ysize, 1, - bit_count, BI_RGB, 0, (int)(xres+0.5), (int)(yres+0.5), + bit_count, BI_RGB, data_size, (int)(xres+0.5), (int)(yres+0.5), colors_used, colors_used)){ i_push_error(0, "cannot write bmp header"); return 0; @@ -392,6 +416,7 @@ write_1bit_data(io_glue *ig, i_img *im) { unsigned char *out; int line_size = (im->xsize+7) / 8; int x, y; + int unpacked_size; /* round up to nearest multiple of four */ line_size = (line_size + 3) / 4 * 4; @@ -399,10 +424,18 @@ write_1bit_data(io_glue *ig, i_img *im) { if (!write_bmphead(ig, im, 1, line_size * im->ysize)) return 0; - line = mymalloc(im->xsize + 8); + /* this shouldn't be an issue, but let's be careful */ + unpacked_size = im->xsize + 8; + if (unpacked_size < im->xsize) { + i_push_error(0, "integer overflow during memory allocation"); + return 0; + } + line = mymalloc(unpacked_size); /* checked 29jun05 tonyc */ memset(line + im->xsize, 0, 8); - - packed = mymalloc(line_size); + + /* size allocated here is always much smaller than xsize, hence + can't overflow int */ + packed = mymalloc(line_size); /* checked 29jun05 tonyc */ memset(packed, 0, line_size); for (y = im->ysize-1; y >= 0; --y) { @@ -432,6 +465,8 @@ write_1bit_data(io_glue *ig, i_img *im) { myfree(packed); myfree(line); + ig->closecb(ig); + return 1; } @@ -451,6 +486,7 @@ write_4bit_data(io_glue *ig, i_img *im) { unsigned char *out; int line_size = (im->xsize+1) / 2; int x, y; + int unpacked_size; /* round up to nearest multiple of four */ line_size = (line_size + 3) / 4 * 4; @@ -458,10 +494,18 @@ write_4bit_data(io_glue *ig, i_img *im) { if (!write_bmphead(ig, im, 4, line_size * im->ysize)) return 0; - line = mymalloc(im->xsize + 2); + /* this shouldn't be an issue, but let's be careful */ + unpacked_size = im->xsize + 2; + if (unpacked_size < im->xsize) { + i_push_error(0, "integer overflow during memory allocation"); + return 0; + } + line = mymalloc(unpacked_size); /* checked 29jun05 tonyc */ memset(line + im->xsize, 0, 2); - packed = mymalloc(line_size); + /* size allocated here is always much smaller than xsize, hence + can't overflow int */ + packed = mymalloc(line_size); /* checked 29jun05 tonyc */ memset(packed, 0, line_size); for (y = im->ysize-1; y >= 0; --y) { @@ -480,6 +524,8 @@ write_4bit_data(io_glue *ig, i_img *im) { myfree(packed); myfree(line); + ig->closecb(ig); + return 1; } @@ -496,7 +542,8 @@ static int write_8bit_data(io_glue *ig, i_img *im) { i_palidx *line; int line_size = im->xsize; - int x, y; + int y; + int unpacked_size; /* round up to nearest multiple of four */ line_size = (line_size + 3) / 4 * 4; @@ -504,7 +551,13 @@ write_8bit_data(io_glue *ig, i_img *im) { if (!write_bmphead(ig, im, 8, line_size * im->ysize)) return 0; - line = mymalloc(im->xsize + 4); + /* this shouldn't be an issue, but let's be careful */ + unpacked_size = im->xsize + 4; + if (unpacked_size < im->xsize) { + i_push_error(0, "integer overflow during memory allocation"); + return 0; + } + line = mymalloc(unpacked_size); /* checked 29jun05 tonyc */ memset(line + im->xsize, 0, 4); for (y = im->ysize-1; y >= 0; --y) { @@ -517,12 +570,11 @@ write_8bit_data(io_glue *ig, i_img *im) { } myfree(line); + ig->closecb(ig); + return 1; } -static int bgr_chans[] = { 2, 1, 0, }; -static int grey_chans[] = { 0, 0, 0, }; - /* =item write_24bit_data(ig, im) @@ -534,19 +586,36 @@ Returns non-zero on success. */ static int write_24bit_data(io_glue *ig, i_img *im) { - int *chans; unsigned char *samples; - int x, y; + int y; int line_size = 3 * im->xsize; + i_color bg; + + i_get_file_background(im, &bg); + + /* just in case we implement a direct format with 2bytes/pixel + (unlikely though) */ + if (line_size / 3 != im->xsize) { + i_push_error(0, "integer overflow during memory allocation"); + return 0; + } line_size = (line_size + 3) / 4 * 4; if (!write_bmphead(ig, im, 24, line_size * im->ysize)) return 0; - chans = im->channels >= 3 ? bgr_chans : grey_chans; - samples = mymalloc(line_size); + samples = mymalloc(4 * im->xsize); + memset(samples, 0, line_size); for (y = im->ysize-1; y >= 0; --y) { - i_gsamp(im, 0, im->xsize, y, samples, chans, 3); + unsigned char *samplep = samples; + int x; + i_gsamp_bg(im, 0, im->xsize, y, samples, 3, &bg); + for (x = 0; x < im->xsize; ++x) { + unsigned char tmp = samplep[2]; + samplep[2] = samplep[0]; + samplep[0] = tmp; + samplep += 3; + } if (ig->writecb(ig, samples, line_size) < 0) { i_push_error(0, "writing image data"); myfree(samples); @@ -555,6 +624,8 @@ write_24bit_data(io_glue *ig, i_img *im) { } myfree(samples); + ig->closecb(ig); + return 1; } @@ -581,15 +652,17 @@ read_bmp_pal(io_glue *ig, i_img *im, int count) { c.channel[0] = r; c.channel[1] = g; c.channel[2] = b; - if (i_addcolors(im, &c, 1) < 0) + if (i_addcolors(im, &c, 1) < 0) { + i_push_error(0, "out of space in image palette"); return 0; + } } return 1; } /* -=item read_1bit_bmp(ig, xsize, ysize, clr_used) +=item read_1bit_bmp(ig, xsize, ysize, clr_used, compression, offbits) Reads in the palette and image data for a 1-bit/pixel image. @@ -598,46 +671,99 @@ Returns the image or NULL. =cut */ static i_img * -read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used) { +read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, + int compression, long offbits, int allow_incomplete) { i_img *im; - int x, y, lasty, yinc; + int x, y, lasty, yinc, start_y; i_palidx *line, *p; unsigned char *packed; int line_size = (xsize + 7)/8; - int byte, bit; + int bit; unsigned char *in; + long base_offset; + + if (compression != BI_RGB) { + i_push_errorf(0, "unknown 1-bit BMP compression (%d)", compression); + return NULL; + } + + if (xsize + 8 < xsize) { /* if there was overflow */ + /* we check with 8 because we allocate that much for the decoded + line buffer */ + i_push_error(0, "integer overflow during memory allocation"); + return NULL; + } + /* if xsize+7 is ok then (xsize+7)/8 will be and the minor + adjustments below won't make it overflow */ line_size = (line_size+3) / 4 * 4; if (ysize > 0) { - y = ysize-1; + start_y = ysize-1; lasty = -1; yinc = -1; } else { /* when ysize is -ve it's a top-down image */ ysize = -ysize; - y = 0; + start_y = 0; lasty = ysize; yinc = 1; } - im = i_img_pal_new(xsize, ysize, 3, 256); + y = start_y; if (!clr_used) clr_used = 2; + if (clr_used < 0 || clr_used > 2) { + i_push_errorf(0, "out of range colors used (%d)", clr_used); + return NULL; + } + + base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4; + if (offbits < base_offset) { + i_push_errorf(0, "image data offset too small (%ld)", offbits); + return NULL; + } + + im = i_img_pal_new(xsize, ysize, 3, 256); + if (!im) + return NULL; if (!read_bmp_pal(ig, im, clr_used)) { i_img_destroy(im); return NULL; } - packed = mymalloc(line_size); - line = mymalloc(xsize+8); + if (offbits > base_offset) { + /* this will be slow if the offset is large, but that should be + rare */ + char buffer; + while (base_offset < offbits) { + if (ig->readcb(ig, &buffer, 1) != 1) { + i_img_destroy(im); + i_push_error(0, "failed skipping to image data offset"); + return NULL; + } + ++base_offset; + } + } + + i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RGB", -1, 0); + + packed = mymalloc(line_size); /* checked 29jun05 tonyc */ + line = mymalloc(xsize+8); /* checked 29jun05 tonyc */ while (y != lasty) { if (ig->readcb(ig, packed, line_size) != line_size) { myfree(packed); myfree(line); - i_push_error(0, "reading 1-bit bmp data"); - i_img_destroy(im); - return NULL; + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", abs(start_y - y)); + return im; + } + else { + i_push_error(0, "failed reading 1-bit bmp data"); + i_img_destroy(im); + return NULL; + } } in = packed; bit = 0x80; @@ -654,6 +780,8 @@ read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used) { y += yinc; } + myfree(packed); + myfree(line); return im; } @@ -671,7 +799,7 @@ point. */ static i_img * read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, - int compression) { + int compression, long offbits, int allow_incomplete) { i_img *im; int x, y, lasty, yinc; i_palidx *line, *p; @@ -679,42 +807,84 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, int line_size = (xsize + 1)/2; unsigned char *in; int size, i; + long base_offset; + int starty; + /* line_size is going to be smaller than xsize in most cases (and + when it's not, xsize is itself small), and hence not overflow */ line_size = (line_size+3) / 4 * 4; if (ysize > 0) { - y = ysize-1; + starty = ysize-1; lasty = -1; yinc = -1; } else { /* when ysize is -ve it's a top-down image */ ysize = -ysize; - y = 0; + starty = 0; lasty = ysize; yinc = 1; } - im = i_img_pal_new(xsize, ysize, 3, 256); + y = starty; if (!clr_used) clr_used = 16; + + if (clr_used > 16 || clr_used < 0) { + i_push_errorf(0, "out of range colors used (%d)", clr_used); + return NULL; + } + + base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4; + if (offbits < base_offset) { + i_push_errorf(0, "image data offset too small (%ld)", offbits); + return NULL; + } + + im = i_img_pal_new(xsize, ysize, 3, 256); + if (!im) /* error should have been pushed already */ + return NULL; if (!read_bmp_pal(ig, im, clr_used)) { i_img_destroy(im); return NULL; } + if (offbits > base_offset) { + /* this will be slow if the offset is large, but that should be + rare */ + char buffer; + while (base_offset < offbits) { + if (ig->readcb(ig, &buffer, 1) != 1) { + i_img_destroy(im); + i_push_error(0, "failed skipping to image data offset"); + return NULL; + } + ++base_offset; + } + } + if (line_size < 260) - packed = mymalloc(260); + packed = mymalloc(260); /* checked 29jun05 tonyc */ else - packed = mymalloc(line_size); - line = mymalloc(xsize+1); + packed = mymalloc(line_size); /* checked 29jun05 tonyc */ + /* xsize won't approach MAXINT */ + line = mymalloc(xsize+1); /* checked 29jun05 tonyc */ if (compression == BI_RGB) { + i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RGB", -1, 0); while (y != lasty) { if (ig->readcb(ig, packed, line_size) != line_size) { myfree(packed); myfree(line); - i_push_error(0, "reading 4-bit bmp data"); - i_img_destroy(im); - return NULL; + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", abs(y - starty)); + return im; + } + else { + i_push_error(0, "failed reading 4-bit bmp data"); + i_img_destroy(im); + return NULL; + } } in = packed; p = line; @@ -726,23 +896,40 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, i_ppal(im, 0, xsize, y, line); y += yinc; } + myfree(packed); + myfree(line); } else if (compression == BI_RLE4) { int read_size; - int want_high; int count; + i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RLE4", -1, 0); x = 0; while (1) { /* there's always at least 2 bytes in a sequence */ if (ig->readcb(ig, packed, 2) != 2) { myfree(packed); myfree(line); - i_push_error(0, "missing data during decompression"); - i_img_destroy(im); - return NULL; + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", abs(y - starty)); + return im; + } + else { + i_push_error(0, "missing data during decompression"); + i_img_destroy(im); + return NULL; + } } else if (packed[0]) { + if (x + packed[0] > xsize) { + /* this file is corrupt */ + myfree(packed); + myfree(line); + i_push_error(0, "invalid data during decompression"); + i_img_destroy(im); + return NULL; + } line[0] = packed[1] >> 4; line[1] = packed[1] & 0x0F; for (i = 0; i < packed[0]; i += 2) { @@ -768,9 +955,16 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, if (ig->readcb(ig, packed, 2) != 2) { myfree(packed); myfree(line); - i_push_error(0, "missing data during decompression"); - i_img_destroy(im); - return NULL; + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", abs(y - starty)); + return im; + } + else { + i_push_error(0, "missing data during decompression"); + i_img_destroy(im); + return NULL; + } } x += packed[0]; y += yinc * packed[1]; @@ -778,14 +972,29 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, default: count = packed[1]; + if (x + count > xsize) { + /* this file is corrupt */ + myfree(packed); + myfree(line); + i_push_error(0, "invalid data during decompression"); + i_img_destroy(im); + return NULL; + } size = (count + 1) / 2; read_size = (size+1) / 2 * 2; if (ig->readcb(ig, packed, read_size) != read_size) { myfree(packed); myfree(line); - i_push_error(0, "missing data during decompression"); - /*i_img_destroy(im);*/ - return im; + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", abs(y - starty)); + return im; + } + else { + i_push_error(0, "missing data during decompression"); + i_img_destroy(im); + return NULL; + } } for (i = 0; i < size; ++i) { line[0] = packed[i] >> 4; @@ -801,7 +1010,7 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, else { /*if (compression == BI_RLE4) {*/ myfree(packed); myfree(line); - i_push_error(0, "bad compression for 4-bit image"); + i_push_errorf(0, "unknown 4-bit BMP compression (%d)", compression); i_img_destroy(im); return NULL; } @@ -810,7 +1019,7 @@ read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, } /* -=item read_8bit_bmp(ig, xsize, ysize, clr_used, compression) +=item read_8bit_bmp(ig, xsize, ysize, clr_used, compression, allow_incomplete) Reads in the palette and image data for a 8-bit/pixel image. @@ -820,64 +1029,119 @@ Returns the image or NULL. */ static i_img * read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, - int compression) { + int compression, long offbits, int allow_incomplete) { i_img *im; - int x, y, lasty, yinc; - i_palidx *line, *p; + int x, y, lasty, yinc, start_y; + i_palidx *line; int line_size = xsize; - unsigned char *in; + long base_offset; line_size = (line_size+3) / 4 * 4; + if (line_size < xsize) { /* if it overflowed (unlikely, but check) */ + i_push_error(0, "integer overflow during memory allocation"); + return NULL; + } if (ysize > 0) { - y = ysize-1; + start_y = ysize-1; lasty = -1; yinc = -1; } else { /* when ysize is -ve it's a top-down image */ ysize = -ysize; - y = 0; + start_y = 0; lasty = ysize; yinc = 1; } - im = i_img_pal_new(xsize, ysize, 3, 256); + y = start_y; if (!clr_used) clr_used = 256; + if (clr_used > 256 || clr_used < 0) { + i_push_errorf(0, "out of range colors used (%d)", clr_used); + return NULL; + } + + base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE + clr_used * 4; + if (offbits < base_offset) { + i_push_errorf(0, "image data offset too small (%ld)", offbits); + return NULL; + } + + im = i_img_pal_new(xsize, ysize, 3, 256); + if (!im) + return NULL; if (!read_bmp_pal(ig, im, clr_used)) { i_img_destroy(im); return NULL; } - line = mymalloc(line_size); + if (offbits > base_offset) { + /* this will be slow if the offset is large, but that should be + rare */ + char buffer; + while (base_offset < offbits) { + if (ig->readcb(ig, &buffer, 1) != 1) { + i_img_destroy(im); + i_push_error(0, "failed skipping to image data offset"); + return NULL; + } + ++base_offset; + } + } + + line = mymalloc(line_size); /* checked 29jun05 tonyc */ if (compression == BI_RGB) { + i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RGB", -1, 0); while (y != lasty) { if (ig->readcb(ig, line, line_size) != line_size) { myfree(line); - i_push_error(0, "reading 8-bit bmp data"); - i_img_destroy(im); - return NULL; + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", abs(start_y - y)); + return im; + } + else { + i_push_error(0, "failed reading 8-bit bmp data"); + i_img_destroy(im); + return NULL; + } } i_ppal(im, 0, xsize, y, line); y += yinc; } + myfree(line); } else if (compression == BI_RLE8) { int read_size; - int want_high; int count; unsigned char packed[2]; + i_tags_add(&im->tags, "bmp_compression_name", 0, "BI_RLE8", -1, 0); x = 0; while (1) { /* there's always at least 2 bytes in a sequence */ if (ig->readcb(ig, packed, 2) != 2) { myfree(line); - i_push_error(0, "missing data during decompression"); - i_img_destroy(im); - return NULL; + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", abs(start_y-y)); + return im; + } + else { + i_push_error(0, "missing data during decompression"); + i_img_destroy(im); + return NULL; + } } if (packed[0]) { + if (x + packed[0] > xsize) { + /* this file isn't incomplete, it's corrupt */ + myfree(line); + i_push_error(0, "invalid data during decompression"); + i_img_destroy(im); + return NULL; + } memset(line, packed[1], packed[0]); i_ppal(im, x, x+packed[0], y, line); x += packed[0]; @@ -895,9 +1159,16 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, case BMPRLE_DELTA: if (ig->readcb(ig, packed, 2) != 2) { myfree(line); - i_push_error(0, "missing data during decompression"); - i_img_destroy(im); - return NULL; + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", abs(start_y-y)); + return im; + } + else { + i_push_error(0, "missing data during decompression"); + i_img_destroy(im); + return NULL; + } } x += packed[0]; y += yinc * packed[1]; @@ -905,12 +1176,27 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, default: count = packed[1]; + if (x + count > xsize) { + /* runs shouldn't cross a line boundary */ + /* this file isn't incomplete, it's corrupt */ + myfree(line); + i_push_error(0, "invalid data during decompression"); + i_img_destroy(im); + return NULL; + } read_size = (count+1) / 2 * 2; if (ig->readcb(ig, line, read_size) != read_size) { myfree(line); - i_push_error(0, "missing data during decompression"); - i_img_destroy(im); - return NULL; + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", abs(start_y-y)); + return im; + } + else { + i_push_error(0, "missing data during decompression"); + i_img_destroy(im); + return NULL; + } } i_ppal(im, x, x+count, y, line); x += count; @@ -921,7 +1207,7 @@ read_8bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used, } else { myfree(line); - i_push_errorf(0, "unknown 8-bit BMP compression %d", compression); + i_push_errorf(0, "unknown 8-bit BMP compression (%d)", compression); i_img_destroy(im); return NULL; } @@ -950,7 +1236,7 @@ static struct bm_masks std_masks[] = }; /* -=item read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression) +=item read_direct_bmp(ig, xsize, ysize, bit_count, clr_used, compression, allow_incomplete) Skips the palette and reads in the image data for a direct colour image. @@ -960,11 +1246,11 @@ Returns the image or NULL. */ static i_img * read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count, - int clr_used, int compression) { + int clr_used, int compression, long offbits, + int allow_incomplete) { i_img *im; - int x, y, lasty, yinc; + int x, y, starty, lasty, yinc; i_color *line, *p; - unsigned char *in; int pix_size = bit_count / 8; int line_size = xsize * pix_size; struct bm_masks masks; @@ -972,6 +1258,9 @@ read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count, int i; int extras; char junk[4]; + const char *compression_name; + int bytes; + long base_offset = FILEHEAD_SIZE + INFOHEAD_SIZE; unpack_code[0] = *("v3V"+pix_size-2); unpack_code[1] = '\0'; @@ -980,19 +1269,20 @@ read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count, extras = line_size - xsize * pix_size; if (ysize > 0) { - y = ysize-1; + starty = ysize-1; lasty = -1; yinc = -1; } else { /* when ysize is -ve it's a top-down image */ ysize = -ysize; - y = 0; + starty = 0; lasty = ysize; yinc = 1; } - line = mymalloc(line_size); + y = starty; if (compression == BI_RGB) { + compression_name = "BI_RGB"; masks = std_masks[pix_size-2]; /* there's a potential "palette" after the header */ @@ -1002,10 +1292,13 @@ read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count, i_push_error(0, "skipping colors"); return 0; } + base_offset += 4; } } else if (compression == BI_BITFIELDS) { int pos, bit; + compression_name = "BI_BITFIELDS"; + for (i = 0; i < 3; ++i) { if (!read_packed(ig, "V", masks.masks+i)) { i_push_error(0, "reading pixel masks"); @@ -1020,20 +1313,62 @@ read_direct_bmp(io_glue *ig, int xsize, int ysize, int bit_count, } masks.shifts[i] = pos - 8; } + base_offset += 4 * 4; + } + else { + i_push_errorf(0, "unknown 24-bit BMP compression (%d)", compression); + return NULL; } + if (offbits < base_offset) { + i_push_errorf(0, "image data offset too small (%ld)", offbits); + return NULL; + } + + if (offbits > base_offset) { + /* this will be slow if the offset is large, but that should be + rare */ + char buffer; + while (base_offset < offbits) { + if (ig->readcb(ig, &buffer, 1) != 1) { + i_push_error(0, "failed skipping to image data offset"); + return NULL; + } + ++base_offset; + } + } + im = i_img_empty(NULL, xsize, ysize); + if (!im) + return NULL; - line = mymalloc(sizeof(i_color) * xsize); + i_tags_add(&im->tags, "bmp_compression_name", 0, compression_name, -1, 0); + + /* I wasn't able to make this overflow in testing, but better to be + safe */ + bytes = sizeof(i_color) * xsize; + if (bytes / sizeof(i_color) != xsize) { + i_img_destroy(im); + i_push_error(0, "integer overflow calculating buffer size"); + return NULL; + } + line = mymalloc(bytes); /* checked 29jun05 tonyc */ while (y != lasty) { p = line; for (x = 0; x < xsize; ++x) { unsigned pixel; if (!read_packed(ig, unpack_code, &pixel)) { - i_push_error(0, "reading image data"); myfree(line); - i_img_destroy(im); - return NULL; + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", abs(starty - y)); + return im; + } + else { + i_push_error(0, "failed reading image data"); + i_img_destroy(im); + return NULL; + } } for (i = 0; i < 3; ++i) { if (masks.shifts[i] > 0) @@ -1074,5 +1409,7 @@ Doesn't handle OS/2 bitmaps. BI_BITFIELDS compression hasn't been tested (I need an image). +The header handling for paletted images needs to be refactored + =cut */