From: Tony Cook Date: Wed, 15 Aug 2001 15:22:23 +0000 (+0000) Subject: start of bmp support (just writing so far) X-Git-Tag: Imager-0.48^2~598 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/261f91c5a2e40b42358c9b228220fcf63cbefd76 start of bmp support (just writing so far) moving to laptop --- diff --git a/Changes b/Changes index 6142c8b8..6da72b07 100644 --- a/Changes +++ b/Changes @@ -472,6 +472,7 @@ Revision history for Perl extension Imager. - named parameters for specifying colors, with quite a few options. - glyph size issues for freetyp2 - minor problem in handling of canon option + - low-level bmp writing (moving it to laptop) ================================================================= diff --git a/Imager.xs b/Imager.xs index d2e20aa2..12a49dc1 100644 --- a/Imager.xs +++ b/Imager.xs @@ -1589,7 +1589,10 @@ i_writeraw_wiol(im,ig) Imager::ImgRaw im Imager::IO ig - +undef_int +i_writebmp_wiol(im,ig) + Imager::ImgRaw im + Imager::IO ig Imager::ImgRaw i_scaleaxis(im,Value,Axis) diff --git a/Makefile.PL b/Makefile.PL index 6318d461..994fd338 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -62,7 +62,8 @@ if (defined $Config{'d_dlsymun'}) { $OSDEF .= ' -DDLSYMUN'; } gaussian.o conv.o pnm.o raw.o feat.o font.o filters.o dynaload.o stackmach.o datatypes.o regmach.o trans2.o quant.o error.o convert.o - map.o tags.o palimg.o maskimg.o img16.o rotate.o); + map.o tags.o palimg.o maskimg.o img16.o rotate.o + bmp.o); %opts=( 'NAME' => 'Imager', diff --git a/bmp.c b/bmp.c new file mode 100644 index 00000000..c540b27c --- /dev/null +++ b/bmp.c @@ -0,0 +1,545 @@ +#include "image.h" +#include + +/* possibly this belongs in a global utilities library + Reads from the specified "file" the specified sizes. + The format codes match those used by perl's pack() function, + though only a few are implemented. + In all cases the vararg arguement is an int *. + + Returns non-zero if all of the arguments were read. +*/ +static +int read_packed(io_glue *ig, char *format, ...) { + unsigned char buf[4]; + va_list ap; + int *p; + + va_start(ap, format); + + while (*format) { + p = va_arg(ap, int *); + + switch (*format) { + case 'v': + if (ig->readcb(ig, buf, 2) == -1) + return 0; + *p = buf[0] + (buf[1] << 8); + break; + + case 'V': + if (ig->readcb(ig, buf, 4) == -1) + return 0; + *p = buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24); + break; + + case 'C': + if (ig->readcb(ig, buf, 1) == -1) + return 0; + *p = buf[0]; + break; + + case 'c': + if (ig->readcb(ig, buf, 1) == -1) + return 0; + *p = (char)buf[0]; + break; + + default: + m_fatal(1, "Unknown read_packed format code 0x%02x", *format); + } + ++format; + } + return 1; +} + +static int +write_packed(io_glue *ig, char *format, ...) { + unsigned char buf[4]; + va_list ap; + int i; + + va_start(ap, format); + + while (*format) { + i = va_arg(ap, unsigned int); + + switch (*format) { + case 'v': + buf[0] = i & 255; + buf[1] = i / 256; + if (ig->writecb(ig, buf, 2) == -1) + return 0; + break; + + case 'V': + buf[0] = i & 0xFF; + buf[1] = (i >> 8) & 0xFF; + buf[2] = (i >> 16) & 0xFF; + buf[3] = (i >> 24) & 0xFF; + if (ig->writecb(ig, buf, 4) == -1) + return 0; + break; + + case 'C': + case 'c': + buf[0] = i & 0xFF; + if (ig->writecb(ig, buf, 1) == -1) + return 0; + break; + + default: + m_fatal(1, "Unknown read_packed format code 0x%02x", *format); + } + ++format; + } + va_end(ap); + + return 1; +} + +#define FILEHEAD_SIZE 14 +#define INFOHEAD_SIZE 40 +#define BI_RGB 0 +#define BI_RLE8 1 +#define BI_RLE4 2 +#define BI_BITFIELDS 3 + +static +int write_bmphead(io_glue *ig, i_img *im, int bit_count, int data_size) { + double xres, yres; + int got_xres, got_yres, aspect_only; + int colors_used = 0; + int offset = FILEHEAD_SIZE + INFOHEAD_SIZE; + + got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres); + got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres); + if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only)) + aspect_only = 0; + if (!got_xres) { + if (!got_yres) + xres = yres = 72; + else + xres = yres; + } + else { + if (!got_yres) + yres = xres; + } + if (xres <= 0 || yres <= 0) + xres = yres = 72; + if (aspect_only) { + /* scale so the smaller value is 72 */ + double ratio; + if (xres < yres) { + ratio = 72.0 / xres; + } + else { + ratio = 72.0 / yres; + } + xres *= ratio; + yres *= ratio; + } + /* now to pels/meter */ + xres *= 100.0/2.54; + yres *= 100.0/2.54; + + if (im->type == i_palette_type) { + colors_used = i_colorcount(im); + offset += 4 * colors_used; + } + + 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, (int)yres, + colors_used, 0)){ + i_push_error(0, "cannot write bmp header"); + return 0; + } + if (im->type == i_palette_type) { + int i; + i_color c; + + for (i = 0; i < colors_used; ++i) { + i_getcolors(im, i, &c, 1); + if (im->channels >= 3) { + if (!write_packed(ig, "CCCC", c.channel[2], c.channel[1], + c.channel[0], 0)) { + i_push_error(0, "cannot write palette entry"); + return 0; + } + } + else { + if (!write_packed(ig, "CCCC", c.channel[0], c.channel[0], + c.channel[0], 0)) { + i_push_error(0, "cannot write palette entry"); + return 0; + } + } + } + } + + return 1; +} + +static int +write_1bit_data(io_glue *ig, i_img *im) { + i_palidx *line; + unsigned char *packed; + int byte; + int mask; + unsigned char *out; + int line_size = (im->xsize+7) / 8; + int x, y; + + /* round up to nearest multiple of four */ + line_size = (line_size + 3) / 4 * 4; + + if (!write_bmphead(ig, im, 1, line_size * im->ysize)) + return 0; + + line = mymalloc(im->xsize + 8); + memset(line + im->xsize, 0, 8); + + packed = mymalloc(line_size); + memset(packed, 0, line_size); + + for (y = im->ysize-1; y >= 0; --y) { + i_gpal(im, 0, im->xsize, y, line); + mask = 0x80; + byte = 0; + out = packed; + for (x = 0; x < im->xsize; ++x) { + if (line[x]) + byte |= mask; + if ((mask >>= 1) == 0) { + *out++ = byte; + byte = 0; + mask = 0x80; + } + } + if (mask != 0x80) { + *out++ = byte; + } + if (ig->writecb(ig, packed, line_size) < 0) { + myfree(packed); + myfree(line); + i_push_error(0, "writing 1 bit/pixel packed data"); + return 0; + } + } + myfree(packed); + myfree(line); + + return 1; +} + +static int +write_4bit_data(io_glue *ig, i_img *im) { + i_palidx *line; + unsigned char *packed; + unsigned char *out; + int line_size = (im->xsize+1) / 2; + int x, y; + + /* round up to nearest multiple of four */ + line_size = (line_size + 3) / 4 * 4; + + if (!write_bmphead(ig, im, 4, line_size * im->ysize)) + return 0; + + line = mymalloc(im->xsize + 2); + memset(line + im->xsize, 0, 2); + + packed = mymalloc(line_size); + memset(packed, 0, line_size); + + for (y = im->ysize-1; y >= 0; --y) { + i_gpal(im, 0, im->xsize, y, line); + out = packed; + for (x = 0; x < im->xsize; x += 2) { + *out++ = (line[x] << 4) + line[x+1]; + } + if (ig->writecb(ig, packed, line_size) < 0) { + myfree(packed); + myfree(line); + i_push_error(0, "writing 4 bit/pixel packed data"); + return 0; + } + } + myfree(packed); + myfree(line); + + return 1; +} + +static int +write_8bit_data(io_glue *ig, i_img *im) { + i_palidx *line; + int line_size = im->xsize; + int x, y; + + /* round up to nearest multiple of four */ + line_size = (line_size + 3) / 4 * 4; + + if (!write_bmphead(ig, im, 8, line_size * im->ysize)) + return 0; + + line = mymalloc(im->xsize + 4); + memset(line + im->xsize, 0, 4); + + for (y = im->ysize-1; y >= 0; --y) { + i_gpal(im, 0, im->xsize, y, line); + if (ig->writecb(ig, line, line_size) < 0) { + myfree(line); + i_push_error(0, "writing 8 bit/pixel packed data"); + return 0; + } + } + myfree(line); + + return 1; +} + +static int bgr_chans[] = { 2, 1, 0, }; +static int grey_chans[] = { 0, 0, 0, }; + +static int +write_24bit_data(io_glue *ig, i_img *im) { + int *chans; + unsigned char *samples; + int x, y; + int line_size = 3 * im->xsize; + + 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); + for (y = im->ysize-1; y >= 0; --y) { + i_gsamp(im, 0, im->xsize, y, samples, chans, 3); + if (ig->writecb(ig, samples, line_size) < 0) { + i_push_error(0, "writing image data"); + myfree(samples); + return 0; + } + } + myfree(samples); + + return 1; +} + +/* no support for writing compressed (RLE8 or RLE4) BMP files */ +int +i_writebmp_wiol(i_img *im, io_glue *ig) { + io_glue_commit_types(ig); + i_clear_error(); + + /* pick a format */ + if (im->type == i_direct_type) { + return write_24bit_data(ig, im); + } + else { + int pal_size; + + /* must be paletted */ + pal_size = i_colorcount(im); + if (pal_size <= 2) { + return write_1bit_data(ig, im); + } + else if (pal_size <= 16) { + return write_4bit_data(ig, im); + } + else { + return write_8bit_data(ig, im); + } + } +} + +static int +read_bmp_pal(io_glue *ig, i_img *im, int count) { + int i; + int r, g, b, x; + i_color c; + + for (i = 0; i < count; ++i) { + if (!read_packed(ig, "CCCC", &b, &g, &r, &x)) { + i_push_error(0, "reading BMP palette"); + return 0; + } + c.channel[0] = r; + c.channel[1] = g; + c.channel[2] = b; + if (i_addcolors(im, &c, 1) < 0) + return 0; + } + + return 1; +} + +static i_img * +read_1bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used) { + i_img *im; + int x, y, lasty, yinc; + i_palidx *line, *p; + unsigned char *packed; + int line_size = (xsize + 7)/8; + int byte, bit; + unsigned char *in; + + line_size = (line_size+3) / 4 * 4; + + if (ysize > 0) { + y = ysize-1; + lasty = -1; + yinc = -1; + } + else { + /* when ysize is -ve it's a top-down image */ + ysize = -ysize; + y = 0; + lasty = ysize; + yinc = 1; + } + im = i_img_pal_new(xsize, ysize, 3, 256); + if (!read_bmp_pal(ig, im, clr_used)) { + i_img_destroy(im); + return NULL; + } + + packed = mymalloc(line_size); + line = mymalloc(xsize+8); + 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; + } + in = packed; + bit = 0x80; + p = line; + for (x = 0; x < xsize; ++x) { + *p++ = (*in & bit) ? 1 : 0; + bit >>= 1; + if (!bit) { + ++in; + bit = 0x80; + } + } + i_ppal(im, 0, xsize, y, line); + y += yinc; + } + + return im; +} +#if 0 + +static i_img * +read_4bit_bmp(io_glue *ig, int xsize, int ysize, int clr_used) { + i_img *im; + int x, y, lasty, yinc; + i_palidx *line, *p; + unsigned char *packed; + int line_size = (xsize + 1)/2; + unsigned char *in; + + line_size = (line_size+3) / 4 * 4; + + if (ysize > 0) { + y = ysize-1; + lasty = -1; + yinc = -1; + } + else { + /* when ysize is -ve it's a top-down image */ + ysize = -ysize; + y = 0; + lasty = ysize; + yinc = 1; + } + im = i_img_pal_new(xsize, ysize, 3, 256); + if (!read_bmp_pal(ig, im, clr_used)) { + i_img_destroy(im); + return NULL; + } + + packed = mymalloc(line_size); + line = mymalloc(xsize+1); + if (compression == BI_RGB) { + 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; + } + in = packed; + p = line; + for (x = 0; x < xsize; x+=2) { + *p++ = *in >> 4; + *p++ = *in & 0x0F; + ++in; + } + i_ppal(im, 0, xsize, y, line); + y += yinc; + } + } + else if (compression == BI_RLE4) { + return 0; + } + + return im; +} + +#endif + +i_img * +i_readbmp_wiol(io_glue *ig) { +#if 0 + int b_magic, m_magic, filesize, dummy, infohead_size; + int xsize, ysize, planes, bit_count, compression, size_image, xres, yres; + int clr_used; + i_img *im; + + io_glue_commit_types(ig); + i_clear_error(); + + if (!read_packed(ig, "CCVvvVVVVvvVVVVV", &b_magic, &m_magic, &filesize, + &dummy, &dummy, &infohead_size, &xsize, &ysize, &planes, + &bit_count, &compression, &size_image, &xres, &yres, + &clr_used, &dummy)) { + i_push_error(0, "file too short"); + return 0; + } + if (b_magic != 'B' || m_magic != 'M' || infohead_size != INFOHEAD_SIZE + || planes != 1) { + i_push_error(0, "not a BMP file"); + return 0; + } + + switch (bit_count) { + case 1: + im = read_1bit_bmp(ig, xsize, ysize, clr_used); + break; + + case 4: + im = read_4bit_bmp(ig, clr_used, compression); + break; + + case 8: + im = read_8bit_bmp(ig, clr_used, compression); + break; + + case 32: + case 24: + case 16: + im = read_direct_bmp(ig, clr_used, compression); + break; + } +#endif + return 0; +} diff --git a/image.h b/image.h index b99469df..7c6a2230 100644 --- a/image.h +++ b/image.h @@ -99,7 +99,7 @@ int i_glin_d(i_img *im,int l, int r, int y, i_color *val); #define i_ppal(im, l, r, y, vals) \ (((im)->i_f_ppal) ? ((im)->i_f_ppal)((im), (l), (r), (y), (vals)) : 0) #define i_addcolors(im, colors, count) \ - (((im)->i_f_addcolors) ? ((im)->i_f_addcolors)((im), (colors), (count)) : 0) + (((im)->i_f_addcolors) ? ((im)->i_f_addcolors)((im), (colors), (count)) : -1) #define i_getcolors(im, index, color, count) \ (((im)->i_f_getcolors) ? \ ((im)->i_f_getcolors)((im), (index), (color), (count)) : 0) @@ -479,6 +479,7 @@ undef_int i_writeraw_wiol(i_img* im, io_glue *ig); i_img * i_readpnm_wiol(io_glue *ig, int length); undef_int i_writeppm_wiol(i_img *im, io_glue *ig); +extern int i_writebmp_wiol(i_img *im, io_glue *ig); i_img * i_scaleaxis(i_img *im, float Value, int Axis); i_img * i_scale_nn(i_img *im, float scx, float scy); diff --git a/iolayer.c b/iolayer.c index 93e9564a..45ee234a 100644 --- a/iolayer.c +++ b/iolayer.c @@ -775,6 +775,7 @@ data from the io_glue callbacks hasn't been done yet. io_glue * io_new_fd(int fd) { io_glue *ig = mymalloc(sizeof(io_glue)); + memset(ig, 0, sizeof(*ig)); #ifdef _MSC_VER io_obj_setp_cb(&ig->source, (void*)fd, _read, _write, _lseek); #else diff --git a/t/t107bmp.t b/t/t107bmp.t new file mode 100644 index 00000000..a1529b60 --- /dev/null +++ b/t/t107bmp.t @@ -0,0 +1,49 @@ +#!perl -w +print "1..4\n"; +use Imager qw(:all); +use strict; +init_log("testout/t107bmp.log",1); + +my $green=i_color_new(0,255,0,255); +my $blue=i_color_new(0,0,255,255); +my $red=i_color_new(255,0,0,255); + +my $img=Imager::ImgRaw::new(150,150,3); +my $cmpimg=Imager::ImgRaw::new(150,150,3); + +i_box_filled($img,70,25,130,125,$green); +i_box_filled($img,20,25,80,125,$blue); +i_arc($img,75,75,30,0,361,$red); +i_conv($img,[0.1, 0.2, 0.4, 0.2, 0.1]); + +write_test(1, $img, "testout/t107_24bit.bmp"); +# 'webmap' is noticably faster than the default +my $im8 = Imager::i_img_to_pal($img, { make_colors=>'webmap', + translate=>'errdiff'}); +write_test(2, $im8, "testout/t107_8bit.bmp"); +my $im4 = Imager::i_img_to_pal($img, { max_colors=>16 }); +write_test(3, $im4, "testout/t107_4bit.bmp"); +my $im1 = Imager::i_img_to_pal($img, { colors=>[ NC(0, 0, 0), NC(176, 160, 144) ], + make_colors=>'none', translate=>'errdiff' }); +write_test(4, $im1, "testout/t107_1bit.bmp"); + +sub write_test { + my ($test_num, $im, $filename) = @_; + local *FH; + + if (open FH, "> $filename") { + binmode FH; + my $IO = Imager::io_new_fd(fileno(FH)); + if (Imager::i_writebmp_wiol($im, $IO)) { + print "ok $test_num\n"; + } + else { + print "not ok $test_num # ",Imager->_error_as_msg(),"\n"; + } + undef $IO; + close FH; + } + else { + print "not ok $test_num # $!\n"; + } +}