X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/7ac6a2e94b36fac75cf949ea96c0b0dabac88528..ed3c09a19185bbdaa970a9686cfd9c9a4f577c6a:/image.c diff --git a/image.c b/image.c index 8dfdb092..5dba629f 100644 --- a/image.c +++ b/image.c @@ -1,5 +1,5 @@ -#include "image.h" -#include "imagei.h" +#include "imager.h" +#include "imageri.h" /* =head1 NAME @@ -39,18 +39,68 @@ Some of these functions are internal. /* Hack around an obscure linker bug on solaris - probably due to builtin gcc thingies */ static void fake(void) { ceil(1); } -static int i_ppix_d(i_img *im, int x, int y, i_color *val); +static int i_ppix_d(i_img *im, int x, int y, const i_color *val); static int i_gpix_d(i_img *im, int x, int y, i_color *val); static int i_glin_d(i_img *im, int l, int r, int y, i_color *vals); -static int i_plin_d(i_img *im, int l, int r, int y, i_color *vals); -static int i_ppixf_d(i_img *im, int x, int y, i_fcolor *val); +static int i_plin_d(i_img *im, int l, int r, int y, const i_color *vals); +static int i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val); static int i_gpixf_d(i_img *im, int x, int y, i_fcolor *val); static int i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals); -static int i_plinf_d(i_img *im, int l, int r, int y, i_fcolor *vals); +static int i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals); static int i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, const int *chans, int chan_count); static int i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, const int *chans, int chan_count); -/*static int i_psamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count); - static int i_psampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count);*/ + +/* +=item i_img_alloc() +=category Image Implementation + +Allocates a new i_img structure. + +When implementing a new image type perform the following steps in your +image object creation function: + +=over + +=item 1. + +allocate the image with i_img_alloc(). + +=item 2. + +initialize any function pointers or other data as needed, you can +overwrite the whole block if you need to. + +=item 3. + +initialize Imager's internal data by calling i_img_init() on the image +object. + +=back + +=cut +*/ + +i_img * +i_img_alloc(void) { + return mymalloc(sizeof(i_img)); +} + +/* +=item i_img_init(img) +=category Image Implementation + +Imager interal initialization of images. + +Currently this does very little, in the future it may be used to +support threads, or color profiles. + +=cut +*/ + +void +i_img_init(i_img *img) { + img->im_data = NULL; +} /* =item ICL_new_internal(r, g, b, a) @@ -71,7 +121,7 @@ ICL_new_internal(unsigned char r,unsigned char g,unsigned char b,unsigned char a mm_log((1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a)); - if ( (cl=mymalloc(sizeof(i_color))) == NULL) m_fatal(2,"malloc() error\n"); + if ( (cl=mymalloc(sizeof(i_color))) == NULL) i_fatal(2,"malloc() error\n"); cl->rgba.r = r; cl->rgba.g = g; cl->rgba.b = b; @@ -100,7 +150,7 @@ ICL_set_internal(i_color *cl,unsigned char r,unsigned char g,unsigned char b,uns mm_log((1,"ICL_set_internal(cl* %p,r %d,g %d,b %d,a %d)\n",cl,r,g,b,a)); if (cl == NULL) if ( (cl=mymalloc(sizeof(i_color))) == NULL) - m_fatal(2,"malloc() error\n"); + i_fatal(2,"malloc() error\n"); cl->rgba.r=r; cl->rgba.g=g; cl->rgba.b=b; @@ -142,7 +192,7 @@ Dump color information to log - strictly for debugging. */ void -ICL_info(i_color *cl) { +ICL_info(i_color const *cl) { mm_log((1,"i_color_info(cl* %p)\n",cl)); mm_log((1,"i_color_info: (%d,%d,%d,%d)\n",cl->rgba.r,cl->rgba.g,cl->rgba.b,cl->rgba.a)); } @@ -173,7 +223,7 @@ i_fcolor *i_fcolor_new(double r, double g, double b, double a) { mm_log((1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a)); - if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) m_fatal(2,"malloc() error\n"); + if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) i_fatal(2,"malloc() error\n"); cl->rgba.r = r; cl->rgba.g = g; cl->rgba.b = b; @@ -232,6 +282,9 @@ static i_img IIM_base_8bit_direct = NULL, /* i_f_setcolors */ NULL, /* i_f_destroy */ + + i_gsamp_bits_fb, + NULL, /* i_f_psamp_bits */ }; /*static void set_8bit_direct(i_img *im) { @@ -254,7 +307,14 @@ static i_img IIM_base_8bit_direct = /* =item IIM_new(x, y, ch) -Creates a new image object I pixels wide, and I pixels high with I channels. +=item i_img_8_new(x, y, ch) + +=category Image creation/destruction + +=synopsis i_img *img = i_img_8_new(width, height, channels); + +Creates a new image object I pixels wide, and I pixels high with +I channels. =cut */ @@ -279,8 +339,6 @@ IIM_DESTROY(i_img *im) { /* myfree(cl); */ } - - /* =item i_img_new() @@ -296,8 +354,8 @@ i_img_new() { i_img *im; mm_log((1,"i_img_struct()\n")); - if ( (im=mymalloc(sizeof(i_img))) == NULL) - m_fatal(2,"malloc() error\n"); + + im = i_img_alloc(); *im = IIM_base_8bit_direct; im->xsize=0; @@ -306,6 +364,8 @@ i_img_new() { im->ch_mask=MAXINT; im->bytes=0; im->idata=NULL; + + i_img_init(im); mm_log((1,"(%p) <- i_img_struct\n",im)); return im; @@ -348,6 +408,8 @@ Re-new image reference i_img * i_img_empty_ch(i_img *im,int x,int y,int ch) { + int bytes; + mm_log((1,"i_img_empty_ch(*im %p, x %d, y %d, ch %d)\n", im, x, y, ch)); if (x < 1 || y < 1) { @@ -358,10 +420,15 @@ i_img_empty_ch(i_img *im,int x,int y,int ch) { i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS); return NULL; } + /* check this multiplication doesn't overflow */ + bytes = x*y*ch; + if (bytes / y / ch != x) { + i_push_errorf(0, "integer overflow calculating image allocation"); + return NULL; + } if (im == NULL) - if ( (im=mymalloc(sizeof(i_img))) == NULL) - m_fatal(2,"malloc() error\n"); + im = i_img_alloc(); memcpy(im, &IIM_base_8bit_direct, sizeof(i_img)); i_tags_new(&im->tags); @@ -369,11 +436,14 @@ i_img_empty_ch(i_img *im,int x,int y,int ch) { im->ysize = y; im->channels = ch; im->ch_mask = MAXINT; - im->bytes=x*y*im->channels; - if ( (im->idata=mymalloc(im->bytes)) == NULL) m_fatal(2,"malloc() error\n"); + im->bytes=bytes; + if ( (im->idata=mymalloc(im->bytes)) == NULL) + i_fatal(2,"malloc() error\n"); memset(im->idata,0,(size_t)im->bytes); im->ext_data = NULL; + + i_img_init(im); mm_log((1,"(%p) <- i_img_empty_ch\n",im)); return im; @@ -409,11 +479,12 @@ i_img_exorcise(i_img *im) { } /* -=item i_img_destroy(im) +=item i_img_destroy(img) -Destroy image and free data via exorcise. +=category Image creation/destruction +=synopsis i_img_destroy(img) - im - Image pointer +Destroy an image object =cut */ @@ -428,6 +499,8 @@ i_img_destroy(i_img *im) { /* =item i_img_info(im, info) +=category Image + Return image information im - Image pointer @@ -465,6 +538,9 @@ i_img_info(i_img *im,int *info) { /* =item i_img_setmask(im, ch_mask) +=synopsis // only channel 0 writeable +=synopsis i_img_setmask(img, 0x01); + Set the image channel mask for I to I. =cut @@ -476,6 +552,8 @@ i_img_setmask(i_img *im,int ch_mask) { im->ch_mask=ch_mask; } /* =item i_img_getmask(im) +=synopsis mask = i_img_getmask(img); + Get the image channel mask for I. =cut @@ -486,6 +564,8 @@ i_img_getmask(i_img *im) { return im->ch_mask; } /* =item i_img_getchannels(im) +=synopsis channels = i_img_getchannels(img); + Get the number of channels in I. =cut @@ -493,11 +573,39 @@ Get the number of channels in I. int i_img_getchannels(i_img *im) { return im->channels; } +/* +=item i_img_get_width(im) + +=synopsis width = i_img_get_width(im); +Returns the width in pixels of the image. + +=cut +*/ +i_img_dim +i_img_get_width(i_img *im) { + return im->xsize; +} + +/* +=item i_img_get_height(im) + +=synopsis height = i_img_get_height(im); + +Returns the height in pixels of the image. + +=cut +*/ +i_img_dim +i_img_get_height(i_img *im) { + return im->ysize; +} /* =item i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, trans) +=category Image + (x1,y1) (x2,y2) specifies the region to copy (in the source coordinates) (tx,ty) specifies the upper left corner for the target image. pass NULL in trans for non transparent i_colors. @@ -506,7 +614,7 @@ pass NULL in trans for non transparent i_colors. */ void -i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,i_color *trans) { +i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,const i_color *trans) { i_color pv; int x,y,t,ttx,tty,tt,ch; @@ -536,75 +644,34 @@ i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,i_ } /* -=item i_copyto(dest, src, x1, y1, x2, y2, tx, ty) +=item i_copy(src) -Copies image data from the area (x1,y1)-[x2,y2] in the source image to -a rectangle the same size with it's top-left corner at (tx,ty) in the -destination image. +=category Image -If x1 > x2 or y1 > y2 then the corresponding co-ordinates are swapped. +Creates a new image that is a copy of src. -=cut -*/ +Tags are not copied, only the image data. -void -i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty) { - int x, y, t, ttx, tty; - - if (x2bits == i_8_bits) { - i_color pv; - tty = ty; - for(y=y1; y over the image I. +Returns: i_img * =cut */ -void -i_copy(i_img *im, i_img *src) { +i_img * +i_copy(i_img *src) { int y, y1, x1; + i_img *im = i_sametype(src, src->xsize, src->ysize); - mm_log((1,"i_copy(im* %p,src %p)\n", im, src)); + mm_log((1,"i_copy(src %p)\n", src)); + + if (!im) + return NULL; x1 = src->xsize; y1 = src->ysize; if (src->type == i_direct_type) { if (src->bits == i_8_bits) { i_color *pv; - i_img_empty_ch(im, x1, y1, src->channels); pv = mymalloc(sizeof(i_color) * x1); for (y = 0; y < y1; ++y) { @@ -615,14 +682,6 @@ i_copy(i_img *im, i_img *src) { } else { i_fcolor *pv; - if (src->bits == i_16_bits) - i_img_16_new_low(im, x1, y1, src->channels); - else if (src->bits == i_double_bits) - i_img_double_new_low(im, x1, y1, src->channels); - else { - fprintf(stderr, "i_copy(): Unknown image bit size %d\n", src->bits); - return; /* I dunno */ - } pv = mymalloc(sizeof(i_fcolor) * x1); for (y = 0; y < y1; ++y) { @@ -633,20 +692,8 @@ i_copy(i_img *im, i_img *src) { } } else { - i_color temp; - int index; - int count; i_palidx *vals; - /* paletted image */ - i_img_pal_new_low(im, x1, y1, src->channels, i_maxcolors(src)); - /* copy across the palette */ - count = i_colorcount(src); - for (index = 0; index < count; ++index) { - i_getcolors(src, index, &temp, 1); - i_addcolors(im, &temp, 1); - } - vals = mymalloc(sizeof(i_palidx) * x1); for (y = 0; y < y1; ++y) { i_gpal(src, 0, x1, y, vals); @@ -654,102 +701,8 @@ i_copy(i_img *im, i_img *src) { } myfree(vals); } -} - - -/* -=item i_rubthru(im, src, tx, ty, src_minx, src_miny, src_maxx, src_maxy ) - -Takes the sub image I and -overlays it at (I,I) on the image object. - -The alpha channel of each pixel in I is used to control how much -the existing colour in I is replaced, if it is 255 then the colour -is completely replaced, if it is 0 then the original colour is left -unmodified. - -=cut -*/ -int -i_rubthru(i_img *im, i_img *src, int tx, int ty, int src_minx, int src_miny, - int src_maxx, int src_maxy) { - int x, y, ttx, tty; - int chancount; - int chans[3]; - int alphachan; - int ch; - - mm_log((1,"i_rubthru(im %p, src %p, tx %d, ty %d, src_minx %d, " - "src_miny %d, src_maxx %d, src_maxy %d)\n", - im, src, tx, ty, src_minx, src_miny, src_maxx, src_maxy)); - i_clear_error(); - - if (im->channels == 3 && src->channels == 4) { - chancount = 3; - chans[0] = 0; chans[1] = 1; chans[2] = 2; - alphachan = 3; - } - else if (im->channels == 3 && src->channels == 2) { - chancount = 3; - chans[0] = chans[1] = chans[2] = 0; - alphachan = 1; - } - else if (im->channels == 1 && src->channels == 2) { - chancount = 1; - chans[0] = 0; - alphachan = 1; - } - else { - i_push_error(0, "rubthru can only work where (dest, src) channels are (3,4), (3,2) or (1,2)"); - return 0; - } - - if (im->bits <= 8) { - /* if you change this code, please make sure the else branch is - changed in a similar fashion - TC */ - int alpha; - i_color pv, orig, dest; - tty = ty; - for(y = src_miny; y < src_maxy; y++) { - ttx = tx; - for(x = src_minx; x < src_maxx; x++) { - i_gpix(src, x, y, &pv); - i_gpix(im, ttx, tty, &orig); - alpha = pv.channel[alphachan]; - for (ch = 0; ch < chancount; ++ch) { - dest.channel[ch] = (alpha * pv.channel[chans[ch]] - + (255 - alpha) * orig.channel[ch])/255; - } - i_ppix(im, ttx, tty, &dest); - ttx++; - } - tty++; - } - } - else { - double alpha; - i_fcolor pv, orig, dest; - - tty = ty; - for(y = src_miny; y < src_maxy; y++) { - ttx = tx; - for(x = src_minx; x < src_maxx; x++) { - i_gpixf(src, x, y, &pv); - i_gpixf(im, ttx, tty, &orig); - alpha = pv.channel[alphachan]; - for (ch = 0; ch < chancount; ++ch) { - dest.channel[ch] = alpha * pv.channel[chans[ch]] - + (1 - alpha) * orig.channel[ch]; - } - i_ppixf(im, ttx, tty, &dest); - ttx++; - } - tty++; - } - } - - return 1; + return im; } @@ -900,6 +853,7 @@ i_scaleaxis(i_img *im, float Value, int Axis) { i_color val,val1,val2; i_img *new_img; + i_clear_error(); mm_log((1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis)); @@ -927,6 +881,10 @@ i_scaleaxis(i_img *im, float Value, int Axis) { } new_img = i_img_empty_ch(NULL, hsize, vsize, im->channels); + if (!new_img) { + i_push_error(0, "cannot create output image"); + return NULL; + } /* 1.4 is a magic number, setting it to 2 will cause rather blurred images */ LanczosWidthFactor = (Value >= 1) ? 1 : (int) (1.4/Value); @@ -1064,6 +1022,9 @@ i_scale_nn(i_img *im, float scx, float scy) { /* =item i_sametype(i_img *im, int xsize, int ysize) +=category Image creation/destruction +=synopsis i_img *img = i_sametype(src, width, height); + Returns an image of the same type (sample size, channels, paletted/direct). For paletted images the palette is copied from the source. @@ -1104,6 +1065,9 @@ i_img *i_sametype(i_img *src, int xsize, int ysize) { /* =item i_sametype_chans(i_img *im, int xsize, int ysize, int channels) +=category Image creation/destruction +=synopsis i_img *img = i_sametype_chans(src, width, height, channels); + Returns an image of the same type (sample size). For paletted images the equivalent direct type is returned. @@ -1273,26 +1237,139 @@ to indicate that it was more than max colors =cut */ +/* This function has been changed and is now faster. It's using + * i_gsamp instead of i_gpix */ int i_count_colors(i_img *im,int maxc) { struct octt *ct; int x,y; - int xsize,ysize; - i_color val; int colorcnt; + int channels[3]; + int *samp_chans; + i_sample_t * samp; + int xsize = im->xsize; + int ysize = im->ysize; + int samp_cnt = 3 * xsize; + + if (im->channels >= 3) { + samp_chans = NULL; + } + else { + channels[0] = channels[1] = channels[2] = 0; + samp_chans = channels; + } + + ct = octt_new(); + + samp = (i_sample_t *) mymalloc( xsize * 3 * sizeof(i_sample_t)); + + colorcnt = 0; + for(y = 0; y < ysize; ) { + i_gsamp(im, 0, xsize, y++, samp, samp_chans, 3); + for(x = 0; x < samp_cnt; ) { + colorcnt += octt_add(ct, samp[x], samp[x+1], samp[x+2]); + x += 3; + if (colorcnt > maxc) { + octt_delete(ct); + return -1; + } + } + } + myfree(samp); + octt_delete(ct); + return colorcnt; +} + +/* sorts the array ra[0..n-1] into increasing order using heapsort algorithm + * (adapted from the Numerical Recipes) + */ +/* Needed by get_anonymous_color_histo */ +static void +hpsort(unsigned int n, unsigned *ra) { + unsigned int i, + ir, + j, + l, + rra; + + if (n < 2) return; + l = n >> 1; + ir = n - 1; + for(;;) { + if (l > 0) { + rra = ra[--l]; + } + else { + rra = ra[ir]; + ra[ir] = ra[0]; + if (--ir == 0) { + ra[0] = rra; + break; + } + } + i = l; + j = 2 * l + 1; + while (j <= ir) { + if (j < ir && ra[j] < ra[j+1]) j++; + if (rra < ra[j]) { + ra[i] = ra[j]; + i = j; + j++; j <<= 1; j--; + } + else break; + } + ra[i] = rra; + } +} - mm_log((1,"i_count_colors(im 0x%08X,maxc %d)\n")); +/* This function constructs an ordered list which represents how much the + * different colors are used. So for instance (100, 100, 500) means that one + * color is used for 500 pixels, another for 100 pixels and another for 100 + * pixels. It's tuned for performance. You might not like the way I've hardcoded + * the maxc ;-) and you might want to change the name... */ +/* Uses octt_histo */ +int +i_get_anonymous_color_histo(i_img *im, unsigned int **col_usage, int maxc) { + struct octt *ct; + int x,y; + int colorcnt; + unsigned int *col_usage_it; + i_sample_t * samp; + int channels[3]; + int *samp_chans; + + int xsize = im->xsize; + int ysize = im->ysize; + int samp_cnt = 3 * xsize; + ct = octt_new(); + + samp = (i_sample_t *) mymalloc( xsize * 3 * sizeof(i_sample_t)); + + if (im->channels >= 3) { + samp_chans = NULL; + } + else { + channels[0] = channels[1] = channels[2] = 0; + samp_chans = channels; + } - xsize=im->xsize; - ysize=im->ysize; - ct=octt_new(); - - colorcnt=0; - for(y=0;y maxc) { octt_delete(ct); return -1; } + colorcnt = 0; + for(y = 0; y < ysize; ) { + i_gsamp(im, 0, xsize, y++, samp, samp_chans, 3); + for(x = 0; x < samp_cnt; ) { + colorcnt += octt_add(ct, samp[x], samp[x+1], samp[x+2]); + x += 3; + if (colorcnt > maxc) { + octt_delete(ct); + return -1; + } + } } + myfree(samp); + /* Now that we know the number of colours... */ + col_usage_it = *col_usage = (unsigned int *) mymalloc(colorcnt * sizeof(unsigned int)); + octt_histo(ct, &col_usage_it); + hpsort(colorcnt, *col_usage); octt_delete(ct); return colorcnt; } @@ -1319,7 +1396,7 @@ Returns 0 if the pixel could be set, -1 otherwise. */ static int -i_ppix_d(i_img *im, int x, int y, i_color *val) { +i_ppix_d(i_img *im, int x, int y, const i_color *val) { int ch; if ( x>-1 && xxsize && y>-1 && yysize ) { @@ -1411,7 +1488,7 @@ Returns the number of pixels copied (eg. if r, l or y is out of range) */ static int -i_plin_d(i_img *im, int l, int r, int y, i_color *vals) { +i_plin_d(i_img *im, int l, int r, int y, const i_color *vals) { int ch, count, i; unsigned char *data; if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) { @@ -1440,7 +1517,7 @@ i_plin_d(i_img *im, int l, int r, int y, i_color *vals) { */ static int -i_ppixf_d(i_img *im, int x, int y, i_fcolor *val) { +i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val) { int ch; if ( x>-1 && xxsize && y>-1 && yysize ) { @@ -1528,7 +1605,7 @@ Returns the number of pixels copied (eg. if r, l or y is out of range) */ static int -i_plinf_d(i_img *im, int l, int r, int y, i_fcolor *vals) { +i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals) { int ch, count, i; unsigned char *data; if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) { @@ -1592,6 +1669,11 @@ i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, } } else { + if (chan_count <= 0 || chan_count > im->channels) { + i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", + chan_count); + return 0; + } for (i = 0; i < w; ++i) { for (ch = 0; ch < chan_count; ++ch) { *samps++ = data[ch]; @@ -1654,6 +1736,11 @@ i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, } } else { + if (chan_count <= 0 || chan_count > im->channels) { + i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", + chan_count); + return 0; + } for (i = 0; i < w; ++i) { for (ch = 0; ch < chan_count; ++ch) { *samps++ = Sample8ToF(data[ch]); @@ -1684,7 +1771,7 @@ i_sample_t versions. =cut */ -int i_ppixf_fp(i_img *im, int x, int y, i_fcolor *pix) { +int i_ppixf_fp(i_img *im, int x, int y, const i_fcolor *pix) { i_color temp; int ch; @@ -1717,7 +1804,7 @@ int i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix) { =cut */ -int i_plinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix) { +int i_plinf_fp(i_img *im, int l, int r, int y, const i_fcolor *pix) { i_color *work; if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) { @@ -1822,11 +1909,11 @@ im->ext_data points at. =over -=item i_addcolors_forward(i_img *im, i_color *colors, int count) +=item i_addcolors_forward(i_img *im, const i_color *colors, int count) =cut */ -int i_addcolors_forward(i_img *im, i_color *colors, int count) { +int i_addcolors_forward(i_img *im, const i_color *colors, int count) { return i_addcolors(*(i_img **)im->ext_data, colors, count); } @@ -1840,11 +1927,11 @@ int i_getcolors_forward(i_img *im, int i, i_color *color, int count) { } /* -=item i_setcolors_forward(i_img *im, int i, i_color *color, int count) +=item i_setcolors_forward(i_img *im, int i, const i_color *color, int count) =cut */ -int i_setcolors_forward(i_img *im, int i, i_color *color, int count) { +int i_setcolors_forward(i_img *im, int i, const i_color *color, int count) { return i_setcolors(*(i_img **)im->ext_data, i, color, count); } @@ -1867,17 +1954,91 @@ int i_maxcolors_forward(i_img *im) { } /* -=item i_findcolor_forward(i_img *im, i_color *color, i_palidx *entry) +=item i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry) =cut */ -int i_findcolor_forward(i_img *im, i_color *color, i_palidx *entry) { +int i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry) { return i_findcolor(*(i_img **)im->ext_data, color, entry); } /* =back +=head2 Fallback handler + +=over + +=item i_gsamp_bits_fb + +=cut +*/ + +int +i_gsamp_bits_fb(i_img *im, int l, int r, int y, unsigned *samps, + const int *chans, int chan_count, int bits) { + if (bits < 1 || bits > 32) { + i_push_error(0, "Invalid bits, must be 1..32"); + return -1; + } + + if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) { + double scale; + int ch, count, i, w; + + if (bits == 32) + scale = 4294967295.0; + else + scale = (double)(1 << bits) - 1; + + if (r > im->xsize) + r = im->xsize; + w = r - l; + count = 0; + + if (chans) { + /* make sure we have good channel numbers */ + for (ch = 0; ch < chan_count; ++ch) { + if (chans[ch] < 0 || chans[ch] >= im->channels) { + i_push_errorf(0, "No channel %d in this image", chans[ch]); + return -1; + } + } + for (i = 0; i < w; ++i) { + i_fcolor c; + i_gpixf(im, l+i, y, &c); + for (ch = 0; ch < chan_count; ++ch) { + *samps++ = (unsigned)(c.channel[ch] * scale + 0.5); + ++count; + } + } + } + else { + if (chan_count <= 0 || chan_count > im->channels) { + i_push_error(0, "Invalid channel count"); + return -1; + } + for (i = 0; i < w; ++i) { + i_fcolor c; + i_gpixf(im, l+i, y, &c); + for (ch = 0; ch < chan_count; ++ch) { + *samps++ = (unsigned)(c.channel[ch] * scale + 0.5); + ++count; + } + } + } + + return count; + } + else { + i_push_error(0, "Image position outside of image"); + return -1; + } +} + +/* +=back + =head2 Stream reading and writing wrapper functions =over @@ -2063,7 +2224,37 @@ int i_free_gen_write_data(i_gen_write_data *info, int flush) return result; } +struct magic_entry { + unsigned char *magic; + size_t magic_size; + char *name; + unsigned char *mask; +}; + +static int +test_magic(unsigned char *buffer, size_t length, struct magic_entry const *magic) { + if (length < magic->magic_size) + return 0; + if (magic->mask) { + int i; + unsigned char *bufp = buffer, + *maskp = magic->mask, + *magicp = magic->magic; + + for (i = 0; i < magic->magic_size; ++i) { + int mask = *maskp == 'x' ? 0xFF : *maskp == ' ' ? 0 : *maskp; + ++maskp; + if ((*bufp++ & mask) != (*magicp++ & mask)) + return 0; + } + + return 1; + } + else { + return !memcmp(magic->magic, buffer, magic->magic_size); + } +} /* =item i_test_format_probe(io_glue *data, int length) @@ -2073,31 +2264,80 @@ Check the beginning of the supplied file for a 'magic number' =cut */ +#define FORMAT_ENTRY(magic, type) \ + { (unsigned char *)(magic ""), sizeof(magic)-1, type } +#define FORMAT_ENTRY2(magic, type, mask) \ + { (unsigned char *)(magic ""), sizeof(magic)-1, type, (unsigned char *)(mask) } -char * +const char * i_test_format_probe(io_glue *data, int length) { + static const struct magic_entry formats[] = { + FORMAT_ENTRY("\xFF\xD8", "jpeg"), + FORMAT_ENTRY("GIF87a", "gif"), + FORMAT_ENTRY("GIF89a", "gif"), + FORMAT_ENTRY("MM\0*", "tiff"), + FORMAT_ENTRY("II*\0", "tiff"), + FORMAT_ENTRY("BM", "bmp"), + FORMAT_ENTRY("\x89PNG\x0d\x0a\x1a\x0a", "png"), + FORMAT_ENTRY("P1", "pnm"), + FORMAT_ENTRY("P2", "pnm"), + FORMAT_ENTRY("P3", "pnm"), + FORMAT_ENTRY("P4", "pnm"), + FORMAT_ENTRY("P5", "pnm"), + FORMAT_ENTRY("P6", "pnm"), + FORMAT_ENTRY("/* XPM", "xpm"), + FORMAT_ENTRY("\x8aMNG", "mng"), + FORMAT_ENTRY("\x8aJNG", "jng"), + /* SGI RGB - with various possible parameters to avoid false positives + on similar files + values are: 2 byte magic, rle flags (0 or 1), bytes/sample (1 or 2) + */ + FORMAT_ENTRY("\x01\xDA\x00\x01", "sgi"), + FORMAT_ENTRY("\x01\xDA\x00\x02", "sgi"), + FORMAT_ENTRY("\x01\xDA\x01\x01", "sgi"), + FORMAT_ENTRY("\x01\xDA\x01\x02", "sgi"), + + FORMAT_ENTRY2("FORM ILBM", "ilbm", "xxxx xxxx"), + + /* different versions of PCX format + http://www.fileformat.info/format/pcx/ + */ + FORMAT_ENTRY("\x0A\x00\x01", "pcx"), + FORMAT_ENTRY("\x0A\x02\x01", "pcx"), + FORMAT_ENTRY("\x0A\x03\x01", "pcx"), + FORMAT_ENTRY("\x0A\x04\x01", "pcx"), + FORMAT_ENTRY("\x0A\x05\x01", "pcx"), + + /* FITS - http://fits.gsfc.nasa.gov/ */ + FORMAT_ENTRY("SIMPLE =", "fits"), + + /* PSD - Photoshop */ + FORMAT_ENTRY("8BPS\x00\x01", "psd"), + + /* EPS - Encapsulated Postscript */ + /* only reading 18 chars, so we don't include the F in EPSF */ + FORMAT_ENTRY("%!PS-Adobe-2.0 EPS", "eps"), - static struct { - char *magic; - char *name; - } formats[] = { - {"\xFF\xD8", "jpeg"}, - {"GIF87a", "gif"}, - {"GIF89a", "gif"}, - {"MM\0*", "tiff"}, - {"II*\0", "tiff"}, - {"BM", "bmp"}, - {"\x89PNG\x0d\x0a\x1a\x0a", "png"}, - {"P1", "pnm"}, - {"P2", "pnm"}, - {"P3", "pnm"}, - {"P4", "pnm"}, - {"P5", "pnm"}, - {"P6", "pnm"}, + /* Utah RLE */ + FORMAT_ENTRY("\x52\xCC", "utah"), + + /* GZIP compressed, only matching deflate for now */ + FORMAT_ENTRY("\x1F\x8B\x08", "gzip"), + + /* bzip2 compressed */ + FORMAT_ENTRY("BZh", "bzip2"), }; + static const struct magic_entry more_formats[] = { + /* these were originally both listed as ico, but cur files can + include hotspot information */ + FORMAT_ENTRY("\x00\x00\x01\x00", "ico"), /* Windows icon */ + FORMAT_ENTRY("\x00\x00\x02\x00", "cur"), /* Windows cursor */ + FORMAT_ENTRY2("\x00\x00\x00\x00\x00\x00\x00\x07", + "xwd", " xxxx"), /* X Windows Dump */ + }; + unsigned int i; - char head[18]; - char *match = NULL; + unsigned char head[18]; ssize_t rc; io_glue_commit_types(data); @@ -2106,36 +2346,141 @@ i_test_format_probe(io_glue *data, int length) { data->seekcb(data, -rc, SEEK_CUR); for(i=0; iname; } - /* - if (match && !strcmp(match, "jpeg")) { - unsigned int x0, x1; - rc = data->readcb(data, head, 18); - if (rc == -1) return NULL; - x0 = (unsigned char)head[0]; - x1 = (unsigned char)head[1]; - data->seekcb(data, -rc, SEEK_CUR); - printf("Jpeg reread: %x %x\n", x0, x1); + if ((rc == 18) && + tga_header_verify(head)) + return "tga"; + + for(i=0; iname; + } + + return NULL; +} + +/* +=item i_img_is_monochrome(img, &zero_is_white) + +Tests an image to check it meets our monochrome tests. + +The idea is that a file writer can use this to test where it should +write the image in whatever bi-level format it uses, eg. pbm for pnm. + +For performance of encoders we require monochrome images: + +=over + +=item * + +be paletted + +=item * + +have a palette of two colors, containing only (0,0,0) and +(255,255,255) in either order. + +=back + +zero_is_white is set to non-zero iff the first palette entry is white. + +=cut +*/ + +int +i_img_is_monochrome(i_img *im, int *zero_is_white) { + if (im->type == i_palette_type + && i_colorcount(im) == 2) { + i_color colors[2]; + i_getcolors(im, 0, colors, 2); + if (im->channels == 3) { + if (colors[0].rgb.r == 255 && + colors[0].rgb.g == 255 && + colors[0].rgb.b == 255 && + colors[1].rgb.r == 0 && + colors[1].rgb.g == 0 && + colors[1].rgb.b == 0) { + *zero_is_white = 1; + return 1; + } + else if (colors[0].rgb.r == 0 && + colors[0].rgb.g == 0 && + colors[0].rgb.b == 0 && + colors[1].rgb.r == 255 && + colors[1].rgb.g == 255 && + colors[1].rgb.b == 255) { + *zero_is_white = 0; + return 1; + } + } + else if (im->channels == 1) { + if (colors[0].channel[0] == 255 && + colors[1].channel[0] == 0) { + *zero_is_white = 1; + return 1; + } + else if (colors[0].channel[0] == 0 && + colors[1].channel[0] == 255) { + *zero_is_white = 0; + return 1; + } } - */ + } + + *zero_is_white = 0; + return 0; +} - if (!match && - (rc == 18) && - tga_header_verify(head)) return "tga"; - return match; +/* +=item i_get_file_background(im, &bg) + +Retrieve the file write background color tag from the image. + +If not present, returns black. + +=cut +*/ + +void +i_get_file_background(i_img *im, i_color *bg) { + if (!i_tags_get_color(&im->tags, "i_background", 0, bg)) { + /* black default */ + bg->channel[0] = bg->channel[1] = bg->channel[2] = 0; + } + /* always full alpha */ + bg->channel[3] = 255; } +/* +=item i_get_file_backgroundf(im, &bg) + +Retrieve the file write background color tag from the image as a +floating point color. + +Implemented in terms of i_get_file_background(). + +If not present, returns black. +=cut +*/ +void +i_get_file_backgroundf(i_img *im, i_fcolor *fbg) { + i_color bg; + + i_get_file_background(im, &bg); + fbg->rgba.r = Sample8ToF(bg.rgba.r); + fbg->rgba.g = Sample8ToF(bg.rgba.g); + fbg->rgba.b = Sample8ToF(bg.rgba.b); + fbg->rgba.a = 1.0; +} /* =back