-#include "image.h"
-#include "io.h"
+#define IMAGER_NO_CONTEXT
+
+#include "imager.h"
+#include "imageri.h"
/*
=head1 NAME
i_color *c;
c = i_color_new(red, green, blue, alpha);
ICL_DESTROY(c);
- i = i_img_new();
+ i = i_img_8_new();
i_img_destroy(i);
// and much more
Some of these functions are internal.
-=over 4
+=over
=cut
*/
+im_context_t (*im_get_context)(void) = NULL;
+
#define XAXIS 0
#define YAXIS 1
#define XYAXIS 2
#define minmax(a,b,i) ( ((a>=i)?a: ( (b<=i)?b:i )) )
/* Hack around an obscure linker bug on solaris - probably due to builtin gcc thingies */
-void fake() { ceil(1); }
+void i_linker_bug_fake(void) { ceil(1); }
+
+/*
+=item im_img_alloc(aIMCTX)
+X<im_img_alloc API>X<i_img_alloc API>
+=category Image Implementation
+=synopsis i_img *im = im_img_alloc(aIMCTX);
+=synopsis i_img *im = i_img_alloc();
+
+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 *
+im_img_alloc(pIMCTX) {
+ return mymalloc(sizeof(i_img));
+}
+
+/*
+=item im_img_init(aIMCTX, image)
+X<im_img_init API>X<i_img_init API>
+=category Image Implementation
+=synopsis im_img_init(aIMCTX, im);
+=synopsis i_img_init(im);
+
+Imager internal initialization of images.
+
+See L</im_img_alloc(aIMCTX)> for more information.
+
+=cut
+*/
+
+void
+im_img_init(pIMCTX, i_img *img) {
+ img->im_data = NULL;
+ img->context = aIMCTX;
+ im_context_refinc(aIMCTX, "img_init");
+}
/*
=item ICL_new_internal(r, g, b, a)
i_color *
ICL_new_internal(unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
i_color *cl = NULL;
+ dIMCTX;
- mm_log((1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a));
+ im_log((aIMCTX,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) im_fatal(aIMCTX, 2,"malloc() error\n");
cl->rgba.r = r;
cl->rgba.g = g;
cl->rgba.b = b;
cl->rgba.a = a;
- mm_log((1,"(%p) <- ICL_new_internal\n",cl));
+ im_log((aIMCTX,1,"(%p) <- ICL_new_internal\n",cl));
return cl;
}
i_color *
ICL_set_internal(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
- mm_log((1,"ICL_set_internal(cl* %p,r %d,g %d,b %d,a %d)\n",cl,r,g,b,a));
+ dIMCTX;
+ im_log((aIMCTX,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");
+ im_fatal(aIMCTX, 2,"malloc() error\n");
cl->rgba.r=r;
cl->rgba.g=g;
cl->rgba.b=b;
cl->rgba.a=a;
- mm_log((1,"(%p) <- ICL_set_internal\n",cl));
+ im_log((aIMCTX,1,"(%p) <- ICL_set_internal\n",cl));
return cl;
}
*/
void
-ICL_info(i_color *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));
+ICL_info(i_color const *cl) {
+ dIMCTX;
+ im_log((aIMCTX, 1,"i_color_info(cl* %p)\n",cl));
+ im_log((aIMCTX, 1,"i_color_info: (%d,%d,%d,%d)\n",cl->rgba.r,cl->rgba.g,cl->rgba.b,cl->rgba.a));
}
/*
void
ICL_DESTROY(i_color *cl) {
- mm_log((1,"ICL_DESTROY(cl* %p)\n",cl));
+ dIMCTX;
+ im_log((aIMCTX, 1,"ICL_DESTROY(cl* %p)\n",cl));
myfree(cl);
}
/*
-=item IIM_new(x, y, ch)
-
-Creates a new image object I<x> pixels wide, and I<y> pixels high with I<ch> channels.
+=item i_fcolor_new(double r, double g, double b, double a)
=cut
*/
+i_fcolor *i_fcolor_new(double r, double g, double b, double a) {
+ i_fcolor *cl = NULL;
+ dIMCTX;
+ im_log((aIMCTX, 1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a));
-i_img *
-IIM_new(int x,int y,int ch) {
- i_img *im;
- mm_log((1,"IIM_new(x %d,y %d,ch %d)\n",x,y,ch));
-
- im=i_img_empty_ch(NULL,x,y,ch);
-
- mm_log((1,"(%p) <- IIM_new\n",im));
- return im;
-}
-
-
-void
-IIM_DESTROY(i_img *im) {
- mm_log((1,"IIM_DESTROY(im* %p)\n",im));
- /* myfree(cl); */
-}
-
-
-
-/*
-=item i_img_new()
-
-Create new image reference - notice that this isn't an object yet and
-this should be fixed asap.
-
-=cut
-*/
-
-
-i_img *
-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->xsize=0;
- im->ysize=0;
- im->channels=3;
- im->ch_mask=MAXINT;
- im->bytes=0;
- im->data=NULL;
-
- im->i_f_ppix=i_ppix_d;
- im->i_f_gpix=i_gpix_d;
- im->i_f_plin=i_plin_d;
- im->i_f_glin=i_glin_d;
- im->ext_data=NULL;
-
- mm_log((1,"(%p) <- i_img_struct\n",im));
- return im;
-}
-
-/*
-=item i_img_empty(im, x, y)
-
-Re-new image reference (assumes 3 channels)
-
- im - Image pointer
- x - xsize of destination image
- y - ysize of destination image
-
-=cut
-*/
+ if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) im_fatal(aIMCTX, 2,"malloc() error\n");
+ cl->rgba.r = r;
+ cl->rgba.g = g;
+ cl->rgba.b = b;
+ cl->rgba.a = a;
+ im_log((aIMCTX, 1,"(%p) <- i_fcolor_new\n",cl));
-i_img *
-i_img_empty(i_img *im,int x,int y) {
- mm_log((1,"i_img_empty(*im %p, x %d, y %d)\n",im, x, y));
- if (im==NULL)
- if ( (im=mymalloc(sizeof(i_img))) == NULL)
- m_fatal(2,"malloc() error\n");
-
- im->xsize = x;
- im->ysize = y;
- im->channels = 3;
- im->ch_mask = MAXINT;
- im->bytes=x*y*im->channels;
- if ( (im->data = mymalloc(im->bytes)) == NULL) m_fatal(2,"malloc() error\n");
- memset(im->data, 0, (size_t)im->bytes);
-
- im->i_f_ppix = i_ppix_d;
- im->i_f_gpix = i_gpix_d;
- im->i_f_plin = i_plin_d;
- im->i_f_glin = i_glin_d;
- im->ext_data = NULL;
-
- mm_log((1,"(%p) <- i_img_empty\n", im));
- return im;
+ return cl;
}
-/*
-=item i_img_empty_ch(im, x, y, ch)
-
-Re-new image reference
-
- im - Image pointer
- x - xsize of destination image
- y - ysize of destination image
- ch - number of channels
+/*
+=item i_fcolor_destroy(i_fcolor *cl)
=cut
*/
-
-i_img *
-i_img_empty_ch(i_img *im,int x,int y,int ch) {
- mm_log((1,"i_img_empty_ch(*im %p, x %d, y %d, ch %d)\n", im, x, y, ch));
- if (im == NULL)
- if ( (im=mymalloc(sizeof(i_img))) == NULL)
- m_fatal(2,"malloc() error\n");
-
- im->xsize = x;
- im->ysize = y;
- im->channels = ch;
- im->ch_mask = MAXINT;
- im->bytes=x*y*im->channels;
- if ( (im->data=mymalloc(im->bytes)) == NULL) m_fatal(2,"malloc() error\n");
- memset(im->data,0,(size_t)im->bytes);
-
- im->i_f_ppix = i_ppix_d;
- im->i_f_gpix = i_gpix_d;
- im->i_f_plin = i_plin_d;
- im->i_f_glin = i_glin_d;
- im->ext_data = NULL;
-
- mm_log((1,"(%p) <- i_img_empty_ch\n",im));
- return im;
+void i_fcolor_destroy(i_fcolor *cl) {
+ myfree(cl);
}
/*
void
i_img_exorcise(i_img *im) {
- mm_log((1,"i_img_exorcise(im* 0x%x)\n",im));
- if (im->data != NULL) { myfree(im->data); }
- im->data = NULL;
+ dIMCTXim(im);
+ im_log((aIMCTX,1,"i_img_exorcise(im* %p)\n",im));
+ i_tags_destroy(&im->tags);
+ if (im->i_f_destroy)
+ (im->i_f_destroy)(im);
+ if (im->idata != NULL) { myfree(im->idata); }
+ im->idata = NULL;
im->xsize = 0;
im->ysize = 0;
im->channels = 0;
- im->i_f_ppix=i_ppix_d;
- im->i_f_gpix=i_gpix_d;
- im->i_f_plin=i_plin_d;
- im->i_f_glin=i_glin_d;
im->ext_data=NULL;
}
/*
-=item i_img_destroy(im)
+=item i_img_destroy(C<img>)
+=order 90
+=category Image creation/destruction
+=synopsis i_img_destroy(img)
-Destroy image and free data via exorcise.
-
- im - Image pointer
+Destroy an image object
=cut
*/
void
i_img_destroy(i_img *im) {
- mm_log((1,"i_img_destroy(im* 0x%x)\n",im));
+ dIMCTXim(im);
+ im_log((aIMCTX, 1,"i_img_destroy(im %p)\n",im));
i_img_exorcise(im);
if (im) { myfree(im); }
+ im_context_refdec(aIMCTX, "img_destroy");
}
/*
=item i_img_info(im, info)
+=category Image
+
Return image information
im - Image pointer
void
-i_img_info(i_img *im,int *info) {
- mm_log((1,"i_img_info(im 0x%x)\n",im));
+i_img_info(i_img *im, i_img_dim *info) {
+ dIMCTXim(im);
+ im_log((aIMCTX,1,"i_img_info(im %p)\n",im));
if (im != NULL) {
- mm_log((1,"i_img_info: xsize=%d ysize=%d channels=%d mask=%ud\n",im->xsize,im->ysize,im->channels,im->ch_mask));
- mm_log((1,"i_img_info: data=0x%d\n",im->data));
+ im_log((aIMCTX,1,"i_img_info: xsize=%" i_DF " ysize=%" i_DF " channels=%d "
+ "mask=%ud\n",
+ i_DFc(im->xsize), i_DFc(im->ysize), im->channels,im->ch_mask));
+ im_log((aIMCTX,1,"i_img_info: idata=%p\n",im->idata));
info[0] = im->xsize;
info[1] = im->ysize;
info[2] = im->channels;
}
/*
-=item i_img_setmask(im, ch_mask)
+=item i_img_setmask(C<im>, C<ch_mask>)
+=category Image Information
+=synopsis // only channel 0 writable
+=synopsis i_img_setmask(img, 0x01);
+
+Set the image channel mask for C<im> to C<ch_mask>.
-Set the image channel mask for I<im> to I<ch_mask>.
+The image channel mask gives some control over which channels can be
+written to in the image.
=cut
*/
/*
-=item i_img_getmask(im)
+=item i_img_getmask(C<im>)
+=category Image Information
+=synopsis int mask = i_img_getmask(img);
-Get the image channel mask for I<im>.
+Get the image channel mask for C<im>.
=cut
*/
i_img_getmask(i_img *im) { return im->ch_mask; }
/*
-=item i_img_getchannels(im)
+=item i_img_getchannels(C<im>)
+=category Image Information
+=synopsis int channels = i_img_getchannels(img);
-Get the number of channels in I<im>.
+Get the number of channels in C<im>.
=cut
*/
int
i_img_getchannels(i_img *im) { return im->channels; }
-
-/*
-=item i_ppix(im, x, y, col)
-
-Sets the pixel at (I<x>,I<y>) in I<im> to I<col>.
-
-Returns true if the pixel could be set, false if x or y is out of
-range.
-
-=cut
-*/
-int
-i_ppix(i_img *im, int x, int y, i_color *val) { return im->i_f_ppix(im, x, y, val); }
-
/*
-=item i_gpix(im, x, y, &col)
-
-Get the pixel at (I<x>,I<y>) in I<im> into I<col>.
+=item i_img_get_width(C<im>)
+=category Image Information
+=synopsis i_img_dim width = i_img_get_width(im);
-Returns true if the pixel could be retrieved, false otherwise.
+Returns the width in pixels of the image.
=cut
*/
-int
-i_gpix(i_img *im, int x, int y, i_color *val) { return im->i_f_gpix(im, x, y, val); }
+i_img_dim
+i_img_get_width(i_img *im) {
+ return im->xsize;
+}
/*
-=item i_ppix_d(im, x, y, col)
+=item i_img_get_height(C<im>)
+=category Image Information
+=synopsis i_img_dim height = i_img_get_height(im);
-Internal function.
-
-This is the function kept in the i_f_ppix member of an i_img object.
-It does a normal store of a pixel into the image with range checking.
-
-Returns true if the pixel could be set, false otherwise.
+Returns the height in pixels of the image.
=cut
*/
-int
-i_ppix_d(i_img *im, int x, int y, i_color *val) {
- int ch;
-
- if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
- for(ch=0;ch<im->channels;ch++)
- if (im->ch_mask&(1<<ch))
- im->data[(x+y*im->xsize)*im->channels+ch]=val->channel[ch];
- return 0;
- }
- return -1; /* error was clipped */
+i_img_dim
+i_img_get_height(i_img *im) {
+ return im->ysize;
}
/*
-=item i_gpix_d(im, x, y, &col)
-
-Internal function.
+=item i_img_color_model(im)
+=category Image Information
+=synopsis i_color_model_t cm = i_img_color_model(im);
-This is the function kept in the i_f_gpix member of an i_img object.
-It does normal retrieval of a pixel from the image with range checking.
+Returns the color model for the image.
-Returns true if the pixel could be set, false otherwise.
+A future version of Imager will allow for images with extra channels
+beyond gray/rgb and alpha.
=cut
*/
-int
-i_gpix_d(i_img *im, int x, int y, i_color *val) {
- int ch;
- if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
- for(ch=0;ch<im->channels;ch++)
- val->channel[ch]=im->data[(x+y*im->xsize)*im->channels+ch];
- return 0;
- }
- return -1; /* error was cliped */
+i_color_model_t
+i_img_color_model(i_img *im) {
+ return (i_color_model_t)im->channels;
}
/*
-=item i_glin_d(im, l, r, y, vals)
+=item i_img_alpha_channel(im, &channel)
+=category Image Information
+=synopsis int alpha_channel;
+=synopsis int has_alpha = i_img_alpha_channel(im, &alpha_channel);
-Reads a line of data from the image, storing the pixels at vals.
+Work out the alpha channel for an image.
-The line runs from (l,y) inclusive to (r,y) non-inclusive
+If the image has an alpha channel, sets C<*channel> to the alpha
+channel index and returns non-zero.
-vals should point at space for (r-l) pixels.
+If the image has no alpha channel, returns zero and C<*channel> is not
+modified.
-l should never be less than zero (to avoid confusion about where to
-put the pixels in vals).
+C<channel> may be C<NULL>.
-Returns the number of pixels copied (eg. if r, l or y is out of range)
+=cut
+*/
-=cut */
int
-i_glin_d(i_img *im, int l, int r, int y, i_color *vals) {
- int ch, count, i;
- unsigned char *data;
- if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
- if (r > im->xsize)
- r = im->xsize;
- data = im->data + (l+y*im->xsize) * im->channels;
- count = r - l;
- for (i = 0; i < count; ++i) {
- for (ch = 0; ch < im->channels; ++ch)
- vals[i].channel[ch] = *data++;
- }
- return count;
- }
- else {
+i_img_alpha_channel(i_img *im, int *channel) {
+ i_color_model_t model = i_img_color_model(im);
+ switch (model) {
+ case icm_gray_alpha:
+ case icm_rgb_alpha:
+ if (channel) *channel = (int)model - 1;
+ return 1;
+
+ default:
return 0;
}
}
-/*
-=item i_plin_d(im, l, r, y, vals)
-Writes a line of data into the image, using the pixels at vals.
+/*
+=item i_img_color_channels(im)
+=category Image Information
+=synopsis int color_channels = i_img_color_channels(im);
-The line runs from (l,y) inclusive to (r,y) non-inclusive
+Returns the number of color channels in the image. For now this is
+always 1 (for grayscale) or 3 (for RGB) but may be 0 in some special
+cases in a future release of Imager.
-vals should point at (r-l) pixels.
+=cut
+*/
-l should never be less than zero (to avoid confusion about where to
-get the pixels in vals).
+int
+i_img_color_channels(i_img *im) {
+ i_color_model_t model = i_img_color_model(im);
+ switch (model) {
+ case icm_gray_alpha:
+ case icm_rgb_alpha:
+ return (int)model - 1;
-Returns the number of pixels copied (eg. if r, l or y is out of range)
+ case icm_gray:
+ case icm_rgb:
+ return (int)model;
-=cut */
-int
-i_plin_d(i_img *im, int l, int r, int y, i_color *vals) {
- int ch, count, i;
- unsigned char *data;
- if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
- if (r > im->xsize)
- r = im->xsize;
- data = im->data + (l+y*im->xsize) * im->channels;
- count = r - l;
- for (i = 0; i < count; ++i) {
- for (ch = 0; ch < im->channels; ++ch) {
- if (im->ch_mask & (1 << ch))
- *data = vals[i].channel[ch];
- ++data;
- }
- }
- return count;
- }
- else {
+ default:
return 0;
}
}
/*
-=item i_ppix_pch(im, x, y, ch)
-
-Get the value from the channel I<ch> for pixel (I<x>,I<y>) from I<im>
-scaled to [0,1].
-
-Returns zero if x or y is out of range.
+=item i_copyto_trans(C<im>, C<src>, C<x1>, C<y1>, C<x2>, C<y2>, C<tx>, C<ty>, C<trans>)
-Warning: this ignores the vptr interface for images.
+=category Image
-=cut
-*/
-float
-i_gpix_pch(i_img *im,int x,int y,int ch) {
- if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) return ((float)im->data[(x+y*im->xsize)*im->channels+ch]/255);
- else return 0;
-}
-
-
-/*
-=item i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, trans)
-
-(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.
+(C<x1>,C<y1>) (C<x2>,C<y2>) specifies the region to copy (in the
+source coordinates) (C<tx>,C<ty>) specifies the upper left corner for
+the target image. pass NULL in C<trans> for non transparent i_colors.
=cut
*/
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,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty,const i_color *trans) {
i_color pv;
- int x,y,t,ttx,tty,tt,ch;
+ i_img_dim x,y,t,ttx,tty,tt;
+ int ch;
+ dIMCTXim(im);
- mm_log((1,"i_copyto_trans(im* %p,src 0x%x, x1 %d, y1 %d, x2 %d, y2 %d, tx %d, ty %d, trans* 0x%x)\n",
- im, src, x1, y1, x2, y2, tx, ty, trans));
+ im_log((aIMCTX, 1,"i_copyto_trans(im* %p,src %p, p1(" i_DFp "), p2(" i_DFp "), "
+ "to(" i_DFp "), trans* %p)\n",
+ im, src, i_DFcp(x1, y1), i_DFcp(x2, y2), i_DFcp(tx, ty), trans));
if (x2<x1) { t=x1; x1=x2; x2=t; }
if (y2<y1) { t=y1; y1=y2; y2=t; }
}
/*
-=item i_copyto(dest, src, x1, y1, x2, y2, tx, ty)
-
-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.
-
-If x1 > x2 or y1 > y2 then the corresponding co-ordinates are swapped.
-
-=cut
-*/
-
-void
-i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty) {
- i_color pv;
- int x, y, t, ttx, tty;
-
- if (x2<x1) { t=x1; x1=x2; x2=t; }
- if (y2<y1) { t=y1; y1=y2; y2=t; }
+=item i_copy(source)
- mm_log((1,"i_copyto(im* %p, src %p, x1 %d, y1 %d, x2 %d, y2 %d, tx %d, ty %d)\n",
- im, src, x1, y1, x2, y2, tx, ty));
+=category Image
- tty = ty;
- for(y=y1; y<y2; y++) {
- ttx = tx;
- for(x=x1; x<x2; x++) {
- i_gpix(src, x, y, &pv);
- i_ppix(im, ttx, tty, &pv);
- ttx++;
- }
- tty++;
- }
-}
+Creates a new image that is a copy of the image C<source>.
-/*
-=item i_copy(im, src)
+Tags are not copied, only the image data.
-Copies the contents of the image I<src> over the image I<im>.
+Returns: i_img *
=cut
*/
-void
-i_copy(i_img *im, i_img *src) {
- i_color *pv;
- int y, y1, x1;
+i_img *
+i_copy(i_img *src) {
+ i_img_dim y, y1, x1;
+ dIMCTXim(src);
+ i_img *im = i_sametype(src, src->xsize, src->ysize);
+
+ im_log((aIMCTX,1,"i_copy(src %p)\n", src));
- mm_log((1,"i_copy(im* %p,src %p)\n", im, src));
+ if (!im)
+ return NULL;
x1 = src->xsize;
y1 = src->ysize;
- i_img_empty_ch(im, x1, y1, src->channels);
- pv = mymalloc(sizeof(i_color) * x1);
-
- for (y = 0; y < y1; ++y) {
- i_glin(src, 0, x1, y, pv);
- i_plin(im, 0, x1, y, pv);
- }
- myfree(pv);
-}
-
-
-/*
-=item i_rubthru(im, src, tx, ty)
-
-Takes the image I<src> and applies it at an original (I<tx>,I<ty>) in I<im>.
-
-The alpha channel of each pixel in I<src> is used to control how much
-the existing colour in I<im> is replaced, if it is 255 then the colour
-is completely replaced, if it is 0 then the original colour is left
-unmodified.
-
-=cut
-*/
-
-void
-i_rubthru(i_img *im,i_img *src,int tx,int ty) {
- i_color pv, orig, dest;
- int x, y, ttx, tty;
-
- mm_log((1,"i_rubthru(im %p, src %p, tx %d, ty %d)\n", im, src, tx, ty));
-
- if (im->channels != 3) { fprintf(stderr,"Destination is not in rgb mode.\n"); exit(3); }
- if (src->channels != 4) { fprintf(stderr,"Source is not in rgba mode.\n"); exit(3); }
-
- ttx = tx;
- for(x=0; x<src->xsize; x++) {
- tty=ty;
- for(y=0;y<src->ysize;y++) {
- /* fprintf(stderr,"reading (%d,%d) writing (%d,%d).\n",x,y,ttx,tty); */
- i_gpix(src, x, y, &pv);
- i_gpix(im, ttx, tty, &orig);
- dest.rgb.r = (pv.rgba.a*pv.rgba.r+(255-pv.rgba.a)*orig.rgb.r)/255;
- dest.rgb.g = (pv.rgba.a*pv.rgba.g+(255-pv.rgba.a)*orig.rgb.g)/255;
- dest.rgb.b = (pv.rgba.a*pv.rgba.b+(255-pv.rgba.a)*orig.rgb.b)/255;
- i_ppix(im, ttx, tty, &dest);
- tty++;
- }
- ttx++;
- }
-}
-
-
-/*
-=item i_flipxy(im, axis)
-
-Flips the image inplace around the axis specified.
-Returns 0 if parameters are invalid.
-
- im - Image pointer
- axis - 0 = x, 1 = y, 2 = both
-
-=cut
-*/
-
-undef_int
-i_flipxy(i_img *im, int direction) {
- int x, x2, y, y2, xm, ym;
- int xs = im->xsize;
- int ys = im->ysize;
-
- mm_log((1, "i_flipxy(im %p, direction %d)\n", im, direction ));
-
- if (!im) return 0;
-
- switch (direction) {
- case XAXIS: /* Horizontal flip */
- xm = xs/2;
- ym = ys;
- for(y=0; y<ym; y++) {
- x2 = xs-1;
- for(x=0; x<xm; x++) {
- i_color val1, val2;
- i_gpix(im, x, y, &val1);
- i_gpix(im, x2, y, &val2);
- i_ppix(im, x, y, &val2);
- i_ppix(im, x2, y, &val1);
- x2--;
- }
- }
- break;
- case YAXIS: /* Vertical flip */
- xm = xs;
- ym = ys/2;
- y2 = ys-1;
- for(y=0; y<ym; y++) {
- for(x=0; x<xm; x++) {
- i_color val1, val2;
- i_gpix(im, x, y, &val1);
- i_gpix(im, x, y2, &val2);
- i_ppix(im, x, y, &val2);
- i_ppix(im, x, y2, &val1);
- }
- y2--;
- }
- break;
- case XYAXIS: /* Horizontal and Vertical flip */
- xm = xs/2;
- ym = ys/2;
- y2 = ys-1;
- for(y=0; y<ym; y++) {
- x2 = xs-1;
- for(x=0; x<xm; x++) {
- i_color val1, val2;
- i_gpix(im, x, y, &val1);
- i_gpix(im, x2, y2, &val2);
- i_ppix(im, x, y, &val2);
- i_ppix(im, x2, y2, &val1);
-
- i_gpix(im, x2, y, &val1);
- i_gpix(im, x, y2, &val2);
- i_ppix(im, x2, y, &val2);
- i_ppix(im, x, y2, &val1);
- x2--;
+ if (src->type == i_direct_type) {
+ if (src->bits == i_8_bits) {
+ i_color *pv;
+ pv = mymalloc(sizeof(i_color) * x1);
+
+ for (y = 0; y < y1; ++y) {
+ i_glin(src, 0, x1, y, pv);
+ i_plin(im, 0, x1, y, pv);
}
- y2--;
+ myfree(pv);
}
- if (xm*2 != xs) { /* odd number of column */
- mm_log((1, "i_flipxy: odd number of columns\n"));
- x = xm;
- y2 = ys-1;
- for(y=0; y<ym; y++) {
- i_color val1, val2;
- i_gpix(im, x, y, &val1);
- i_gpix(im, x, y2, &val2);
- i_ppix(im, x, y, &val2);
- i_ppix(im, x, y2, &val1);
- y2--;
+ else {
+ i_fcolor *pv;
+
+ pv = mymalloc(sizeof(i_fcolor) * x1);
+ for (y = 0; y < y1; ++y) {
+ i_glinf(src, 0, x1, y, pv);
+ i_plinf(im, 0, x1, y, pv);
}
+ myfree(pv);
}
- if (ym*2 != ys) { /* odd number of rows */
- mm_log((1, "i_flipxy: odd number of rows\n"));
- y = ym;
- x2 = xs-1;
- for(x=0; x<xm; x++) {
- i_color val1, val2;
- i_gpix(im, x, y, &val1);
- i_gpix(im, x2, y, &val2);
- i_ppix(im, x, y, &val2);
- i_ppix(im, x2, y, &val1);
- x2--;
- }
+ }
+ else {
+ i_palidx *vals;
+
+ vals = mymalloc(sizeof(i_palidx) * x1);
+ for (y = 0; y < y1; ++y) {
+ i_gpal(src, 0, x1, y, vals);
+ i_ppal(im, 0, x1, y, vals);
}
- break;
- default:
- mm_log((1, "i_flipxy: direction is invalid\n" ));
- return 0;
+ myfree(vals);
}
- return 1;
-}
+ return im;
+}
+/*
+http://en.wikipedia.org/wiki/Lanczos_resampling
+*/
static
float
else return(sin(PIx) / PIx * sin(PIx2) / PIx2);
}
+
/*
=item i_scaleaxis(im, value, axis)
*/
i_img*
-i_scaleaxis(i_img *im, float Value, int Axis) {
- int hsize, vsize, i, j, k, l, lMax, iEnd, jEnd;
- int LanczosWidthFactor;
- float *l0, *l1, OldLocation;
- int T, TempJump1, TempJump2;
+i_scaleaxis(i_img *im, double Value, int Axis) {
+ i_img_dim hsize, vsize, i, j, k, l, lMax, iEnd, jEnd;
+ i_img_dim LanczosWidthFactor;
+ float *l0, *l1;
+ double OldLocation;
+ i_img_dim T;
+ double t;
float F, PictureValue[MAXCHANNELS];
short psave;
i_color val,val1,val2;
i_img *new_img;
+ int has_alpha = i_img_has_alpha(im);
+ int color_chans = i_img_color_channels(im);
+ dIMCTXim(im);
- mm_log((1,"i_scaleaxis(im 0x%x,Value %.2f,Axis %d)\n",im,Value,Axis));
+ i_clear_error();
+ im_log((aIMCTX, 1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis));
if (Axis == XAXIS) {
- hsize = (int) ((float) im->xsize * Value);
+ hsize = (i_img_dim)(0.5 + im->xsize * Value);
+ if (hsize < 1) {
+ hsize = 1;
+ Value = 1.0 / im->xsize;
+ }
vsize = im->ysize;
jEnd = hsize;
iEnd = vsize;
-
- TempJump1 = (hsize - 1) * 3;
- TempJump2 = hsize * (vsize - 1) * 3 + TempJump1;
} else {
hsize = im->xsize;
- vsize = (int) ((float) im->ysize * Value);
-
+ vsize = (i_img_dim)(0.5 + im->ysize * Value);
+
+ if (vsize < 1) {
+ vsize = 1;
+ Value = 1.0 / im->ysize;
+ }
+
jEnd = vsize;
iEnd = hsize;
-
- TempJump1 = 0;
- TempJump2 = 0;
}
- new_img=i_img_empty_ch(NULL,hsize,vsize,im->channels);
-
- if (Value >=1) LanczosWidthFactor = 1;
- else LanczosWidthFactor = (int) (1.0/Value);
+ new_img = i_img_8_new(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 : (i_img_dim) (1.4/Value);
lMax = LanczosWidthFactor << 1;
- l0 = (float *) mymalloc(lMax * sizeof(float));
- l1 = (float *) mymalloc(lMax * sizeof(float));
+ l0 = mymalloc(lMax * sizeof(float));
+ l1 = mymalloc(lMax * sizeof(float));
for (j=0; j<jEnd; j++) {
- OldLocation = ((float) j) / Value;
- T = (int) (OldLocation);
- F = OldLocation - (float) T;
+ OldLocation = ((double) j) / Value;
+ T = (i_img_dim) (OldLocation);
+ F = OldLocation - T;
- for (l = 0; l < lMax; l++) {
+ for (l = 0; l<lMax; l++) {
l0[lMax-l-1] = Lanczos(((float) (lMax-l-1) + F) / (float) LanczosWidthFactor);
- l1[l] = Lanczos(((float) (l + 1) - F) / (float) LanczosWidthFactor);
+ l1[l] = Lanczos(((float) (l+1) - F) / (float) LanczosWidthFactor);
+ }
+
+ /* Make sure filter is normalized */
+ t = 0.0;
+ for(l=0; l<lMax; l++) {
+ t+=l0[l];
+ t+=l1[l];
}
+ t /= (double)LanczosWidthFactor;
- if (Axis== XAXIS) {
+ for(l=0; l<lMax; l++) {
+ l0[l] /= t;
+ l1[l] /= t;
+ }
+
+ if (Axis == XAXIS) {
for (i=0; i<iEnd; i++) {
for (k=0; k<im->channels; k++) PictureValue[k] = 0.0;
- for (l=0; l < lMax; l++) {
- i_gpix(im,T+l+1, i, &val1);
- i_gpix(im,T-lMax+l+1, i, &val2);
- for (k=0; k<im->channels; k++) {
- PictureValue[k] += l1[l] * val1.channel[k];
- PictureValue[k] += l0[lMax-l-1] * val2.channel[k];
+ for (l=0; l<lMax; l++) {
+ i_img_dim mx = T-lMax+l+1;
+ i_img_dim Mx = T+l+1;
+ mx = (mx < 0) ? 0 : mx;
+ Mx = (Mx >= im->xsize) ? im->xsize-1 : Mx;
+
+ i_gpix(im, Mx, i, &val1);
+ i_gpix(im, mx, i, &val2);
+
+ if (has_alpha) {
+ i_sample_t alpha1 = val1.channel[color_chans];
+ i_sample_t alpha2 = val2.channel[color_chans];
+ for (k=0; k < color_chans; k++) {
+ PictureValue[k] += l1[l] * val1.channel[k] * alpha1 / 255;
+ PictureValue[k] += l0[lMax-l-1] * val2.channel[k] * alpha2 / 255;
+ }
+ PictureValue[color_chans] += l1[l] * val1.channel[color_chans];
+ PictureValue[color_chans] += l0[lMax-l-1] * val2.channel[color_chans];
+ }
+ else {
+ for (k=0; k<im->channels; k++) {
+ PictureValue[k] += l1[l] * val1.channel[k];
+ PictureValue[k] += l0[lMax-l-1] * val2.channel[k];
+ }
+ }
+ }
+
+ if (has_alpha) {
+ float fa = PictureValue[color_chans] / LanczosWidthFactor;
+ int alpha = minmax(0, 255, fa+0.5);
+ if (alpha) {
+ for (k = 0; k < color_chans; ++k) {
+ psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor * 255 / fa));
+ val.channel[k]=minmax(0,255,psave);
+ }
+ val.channel[color_chans] = alpha;
+ }
+ else {
+ /* zero alpha, so the pixel has no color */
+ for (k = 0; k < im->channels; ++k)
+ val.channel[k] = 0;
}
}
- for(k=0;k<im->channels;k++) {
- psave = (short)( PictureValue[k] / LanczosWidthFactor);
- val.channel[k]=minmax(0,255,psave);
+ else {
+ for(k=0;k<im->channels;k++) {
+ psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor));
+ val.channel[k]=minmax(0,255,psave);
+ }
}
- i_ppix(new_img,j,i,&val);
+ i_ppix(new_img, j, i, &val);
}
} else {
for (i=0; i<iEnd; i++) {
for (k=0; k<im->channels; k++) PictureValue[k] = 0.0;
for (l=0; l < lMax; l++) {
- i_gpix(im,i, T+l+1, &val1);
- i_gpix(im,i, T-lMax+l+1, &val2);
- for (k=0; k<im->channels; k++) {
- PictureValue[k] += l1[l] * val1.channel[k];
- PictureValue[k] += l0[lMax-l-1] * val2.channel[k];
+ i_img_dim mx = T-lMax+l+1;
+ i_img_dim Mx = T+l+1;
+ mx = (mx < 0) ? 0 : mx;
+ Mx = (Mx >= im->ysize) ? im->ysize-1 : Mx;
+
+ i_gpix(im, i, Mx, &val1);
+ i_gpix(im, i, mx, &val2);
+ if (has_alpha) {
+ i_sample_t alpha1 = val1.channel[color_chans];
+ i_sample_t alpha2 = val2.channel[color_chans];
+ for (k=0; k < color_chans; k++) {
+ PictureValue[k] += l1[l] * val1.channel[k] * alpha1 / 255;
+ PictureValue[k] += l0[lMax-l-1] * val2.channel[k] * alpha2 / 255;
+ }
+ PictureValue[color_chans] += l1[l] * val1.channel[color_chans];
+ PictureValue[color_chans] += l0[lMax-l-1] * val2.channel[color_chans];
+ }
+ else {
+ for (k=0; k<im->channels; k++) {
+ PictureValue[k] += l1[l] * val1.channel[k];
+ PictureValue[k] += l0[lMax-l-1] * val2.channel[k];
+ }
+ }
+ }
+ if (has_alpha) {
+ float fa = PictureValue[color_chans] / LanczosWidthFactor;
+ int alpha = minmax(0, 255, fa+0.5);
+ if (alpha) {
+ for (k = 0; k < color_chans; ++k) {
+ psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor * 255 / fa));
+ val.channel[k]=minmax(0,255,psave);
+ }
+ val.channel[color_chans] = alpha;
+ }
+ else {
+ for (k = 0; k < im->channels; ++k)
+ val.channel[k] = 0;
}
}
- for (k=0; k<im->channels; k++) {
- psave = (short)( PictureValue[k] / LanczosWidthFactor);
- val.channel[k]=minmax(0,255,psave);
+ else {
+ for(k=0;k<im->channels;k++) {
+ psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor));
+ val.channel[k]=minmax(0,255,psave);
+ }
}
- i_ppix(new_img,i,j,&val);
+ i_ppix(new_img, i, j, &val);
}
}
myfree(l0);
myfree(l1);
- mm_log((1,"(0x%x) <- i_scaleaxis\n",new_img));
+ im_log((aIMCTX, 1,"(%p) <- i_scaleaxis\n", new_img));
return new_img;
}
i_img*
-i_scale_nn(i_img *im, float scx, float scy) {
+i_scale_nn(i_img *im, double scx, double scy) {
- int nxsize,nysize,nx,ny;
+ i_img_dim nxsize,nysize,nx,ny;
i_img *new_img;
i_color val;
+ dIMCTXim(im);
- mm_log((1,"i_scale_nn(im 0x%x,scx %.2f,scy %.2f)\n",im,scx,scy));
+ im_log((aIMCTX, 1,"i_scale_nn(im %p,scx %.2f,scy %.2f)\n",im,scx,scy));
- nxsize = (int) ((float) im->xsize * scx);
- nysize = (int) ((float) im->ysize * scy);
+ nxsize = (i_img_dim) ((double) im->xsize * scx);
+ if (nxsize < 1) {
+ nxsize = 1;
+ scx = 1.0 / im->xsize;
+ }
+ nysize = (i_img_dim) ((double) im->ysize * scy);
+ if (nysize < 1) {
+ nysize = 1;
+ scy = 1.0 / im->ysize;
+ }
+ im_assert(scx != 0 && scy != 0);
new_img=i_img_empty_ch(NULL,nxsize,nysize,im->channels);
for(ny=0;ny<nysize;ny++) for(nx=0;nx<nxsize;nx++) {
- i_gpix(im,((float)nx)/scx,((float)ny)/scy,&val);
+ i_gpix(im,((double)nx)/scx,((double)ny)/scy,&val);
i_ppix(new_img,nx,ny,&val);
}
- mm_log((1,"(0x%x) <- i_scale_nn\n",new_img));
+ im_log((aIMCTX, 1,"(%p) <- i_scale_nn\n",new_img));
return new_img;
}
-
/*
-=item i_transform(im, opx, opxl, opy, opyl, parm, parmlen)
+=item i_sametype(C<im>, C<xsize>, C<ysize>)
-Spatially transforms I<im> returning a new image.
+=category Image creation/destruction
+=synopsis i_img *img = i_sametype(src, width, height);
-opx for a length of opxl and opy for a length of opy are arrays of
-operators that modify the x and y positions to retreive the pixel data from.
+Returns an image of the same type (sample size, channels, paletted/direct).
-parm and parmlen define extra parameters that the operators may use.
+For paletted images the palette is copied from the source.
-Note that this function is largely superseded by the more flexible
-L<transform.c/i_transform2>.
+=cut
+*/
-Returns the new image.
+i_img *
+i_sametype(i_img *src, i_img_dim xsize, i_img_dim ysize) {
+ dIMCTXim(src);
+
+ if (src->type == i_direct_type) {
+ if (src->bits == 8) {
+ return i_img_empty_ch(NULL, xsize, ysize, src->channels);
+ }
+ else if (src->bits == i_16_bits) {
+ return i_img_16_new(xsize, ysize, src->channels);
+ }
+ else if (src->bits == i_double_bits) {
+ return i_img_double_new(xsize, ysize, src->channels);
+ }
+ else {
+ i_push_error(0, "Unknown image bits");
+ return NULL;
+ }
+ }
+ else {
+ i_color col;
+ int i;
+
+ i_img *targ = i_img_pal_new(xsize, ysize, src->channels, i_maxcolors(src));
+ for (i = 0; i < i_colorcount(src); ++i) {
+ i_getcolors(src, i, &col, 1);
+ i_addcolors(targ, &col, 1);
+ }
+
+ return targ;
+ }
+}
+
+/*
+=item i_sametype_chans(C<im>, C<xsize>, C<ysize>, C<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.
+
+=cut
+*/
+
+i_img *
+i_sametype_chans(i_img *src, i_img_dim xsize, i_img_dim ysize, int channels) {
+ dIMCTXim(src);
+
+ if (src->bits == 8) {
+ return i_img_empty_ch(NULL, xsize, ysize, channels);
+ }
+ else if (src->bits == i_16_bits) {
+ return i_img_16_new(xsize, ysize, channels);
+ }
+ else if (src->bits == i_double_bits) {
+ return i_img_double_new(xsize, ysize, channels);
+ }
+ else {
+ i_push_error(0, "Unknown image bits");
+ return NULL;
+ }
+}
+
+/*
+=item i_transform(im, opx, opxl, opy, opyl, parm, parmlen)
+
+Spatially transforms I<im> returning a new image.
+
+opx for a length of opxl and opy for a length of opy are arrays of
+operators that modify the x and y positions to retreive the pixel data from.
+
+parm and parmlen define extra parameters that the operators may use.
+
+Note that this function is largely superseded by the more flexible
+L<transform.c/i_transform2>.
+
+Returns the new image.
The operators for this function are defined in L<stackmach.c>.
i_img*
i_transform(i_img *im, int *opx,int opxl,int *opy,int opyl,double parm[],int parmlen) {
double rx,ry;
- int nxsize,nysize,nx,ny;
+ i_img_dim nxsize,nysize,nx,ny;
i_img *new_img;
i_color val;
+ dIMCTXim(im);
- mm_log((1,"i_transform(im 0x%x, opx 0x%x, opxl %d, opy 0x%x, opyl %d, parm 0x%x, parmlen %d)\n",im,opx,opxl,opy,opyl,parm,parmlen));
+ im_log((aIMCTX, 1,"i_transform(im %p, opx %p, opxl %d, opy %p, opyl %d, parm %p, parmlen %d)\n",im,opx,opxl,opy,opyl,parm,parmlen));
nxsize = im->xsize;
nysize = im->ysize ;
parm[1]=(double)ny;
/* fprintf(stderr,"(%d,%d) ->",nx,ny); */
- rx=op_run(opx,opxl,parm,parmlen);
- ry=op_run(opy,opyl,parm,parmlen);
+ rx=i_op_run(opx,opxl,parm,parmlen);
+ ry=i_op_run(opy,opyl,parm,parmlen);
/* fprintf(stderr,"(%f,%f)\n",rx,ry); */
i_gpix(im,rx,ry,&val);
i_ppix(new_img,nx,ny,&val);
}
- mm_log((1,"(0x%x) <- i_transform\n",new_img));
+ im_log((aIMCTX, 1,"(%p) <- i_transform\n",new_img));
return new_img;
}
=cut
*/
+
float
i_img_diff(i_img *im1,i_img *im2) {
- int x,y,ch,xb,yb,chb;
+ i_img_dim x, y, xb, yb;
+ int ch, chb;
float tdiff;
i_color val1,val2;
+ dIMCTXim(im1);
- mm_log((1,"i_img_diff(im1 0x%x,im2 0x%x)\n",im1,im2));
+ im_log((aIMCTX, 1,"i_img_diff(im1 %p,im2 %p)\n",im1,im2));
xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
- mm_log((1,"i_img_diff: xb=%d xy=%d chb=%d\n",xb,yb,chb));
+ im_log((aIMCTX, 1,"i_img_diff: b=(" i_DFp ") chb=%d\n",
+ i_DFcp(xb,yb), chb));
tdiff=0;
for(y=0;y<yb;y++) for(x=0;x<xb;x++) {
for(ch=0;ch<chb;ch++) tdiff+=(val1.channel[ch]-val2.channel[ch])*(val1.channel[ch]-val2.channel[ch]);
}
- mm_log((1,"i_img_diff <- (%.2f)\n",tdiff));
+ im_log((aIMCTX, 1,"i_img_diff <- (%.2f)\n",tdiff));
+ return tdiff;
+}
+
+/*
+=item i_img_diffd(im1, im2)
+
+Calculates the sum of the squares of the differences between
+correspoding channels in two images.
+
+If the images are not the same size then only the common area is
+compared, hence even if images are different sizes this function
+can return zero.
+
+This is like i_img_diff() but looks at floating point samples instead.
+
+=cut
+*/
+
+double
+i_img_diffd(i_img *im1,i_img *im2) {
+ i_img_dim x, y, xb, yb;
+ int ch, chb;
+ double tdiff;
+ i_fcolor val1,val2;
+ dIMCTXim(im1);
+
+ im_log((aIMCTX, 1,"i_img_diffd(im1 %p,im2 %p)\n",im1,im2));
+
+ xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
+ yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
+ chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
+
+ im_log((aIMCTX, 1,"i_img_diffd: b(" i_DFp ") chb=%d\n",
+ i_DFcp(xb, yb), chb));
+
+ tdiff=0;
+ for(y=0;y<yb;y++) for(x=0;x<xb;x++) {
+ i_gpixf(im1,x,y,&val1);
+ i_gpixf(im2,x,y,&val2);
+
+ for(ch=0;ch<chb;ch++) {
+ double sdiff = val1.channel[ch]-val2.channel[ch];
+ tdiff += sdiff * sdiff;
+ }
+ }
+ im_log((aIMCTX, 1,"i_img_diffd <- (%.2f)\n",tdiff));
+
return tdiff;
}
+int
+i_img_samef(i_img *im1,i_img *im2, double epsilon, char const *what) {
+ i_img_dim x,y,xb,yb;
+ int ch, chb;
+ i_fcolor val1,val2;
+ dIMCTXim(im1);
+
+ if (what == NULL)
+ what = "(null)";
+
+ im_log((aIMCTX,1,"i_img_samef(im1 %p,im2 %p, epsilon %g, what '%s')\n", im1, im2, epsilon, what));
+
+ xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
+ yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
+ chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
+
+ im_log((aIMCTX, 1,"i_img_samef: b(" i_DFp ") chb=%d\n",
+ i_DFcp(xb, yb), chb));
+
+ for(y = 0; y < yb; y++) {
+ for(x = 0; x < xb; x++) {
+ i_gpixf(im1, x, y, &val1);
+ i_gpixf(im2, x, y, &val2);
+
+ for(ch = 0; ch < chb; ch++) {
+ double sdiff = val1.channel[ch] - val2.channel[ch];
+ if (fabs(sdiff) > epsilon) {
+ im_log((aIMCTX, 1,"i_img_samef <- different %g @(" i_DFp ")\n",
+ sdiff, i_DFcp(x, y)));
+ return 0;
+ }
+ }
+ }
+ }
+ im_log((aIMCTX, 1,"i_img_samef <- same\n"));
+
+ return 1;
+}
+
/* just a tiny demo of haar wavelets */
i_img*
i_haar(i_img *im) {
- int mx,my;
- int fx,fy;
- int x,y;
- int ch,c;
+ i_img_dim mx,my;
+ i_img_dim fx,fy;
+ i_img_dim x,y;
+ int ch;
i_img *new_img,*new_img2;
i_color val1,val2,dval1,dval2;
+ dIMCTXim(im);
mx=im->xsize;
my=im->ysize;
new_img=i_img_empty_ch(NULL,fx*2,fy*2,im->channels);
new_img2=i_img_empty_ch(NULL,fx*2,fy*2,im->channels);
- c=0;
for(y=0;y<my;y++) for(x=0;x<fx;x++) {
i_gpix(im,x*2,y,&val1);
i_gpix(im,x*2+1,y,&val2);
=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;
+ i_img_dim x,y;
int colorcnt;
+ int channels[3];
+ int *samp_chans;
+ i_sample_t * samp;
+ i_img_dim xsize = im->xsize;
+ i_img_dim 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;
+ }
- mm_log((1,"i_count_colors(im 0x%08X,maxc %d)\n"));
-
- xsize=im->xsize;
- ysize=im->ysize;
- ct=octt_new();
-
- colorcnt=0;
- for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
- i_gpix(im,x,y,&val);
- colorcnt+=octt_add(ct,val.rgb.r,val.rgb.g,val.rgb.b);
- if (colorcnt > maxc) { octt_delete(ct); return -1; }
+ 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) {
+ myfree(samp);
+ 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;
+ }
+}
-symbol_table_t symbol_table={i_has_format,ICL_set_internal,ICL_info,
- i_img_new,i_img_empty,i_img_empty_ch,i_img_exorcise,
- i_img_info,i_img_setmask,i_img_getmask,i_ppix,i_gpix,
- i_box,i_draw,i_arc,i_copyto,i_copyto_trans,i_rubthru};
+/* 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;
+ i_img_dim x,y;
+ int colorcnt;
+ unsigned int *col_usage_it;
+ i_sample_t * samp;
+ int channels[3];
+ int *samp_chans;
+
+ i_img_dim xsize = im->xsize;
+ i_img_dim 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;
+ }
+ 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);
+ myfree(samp);
+ 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;
+}
/*
-=item i_gen_reader(i_gen_read_data *info, char *buf, int length)
+=back
-Performs general read buffering for file readers that permit reading
-to be done through a callback.
+=head2 Image method wrappers
-The final callback gets two parameters, a I<need> value, and a I<want>
-value, where I<need> is the amount of data that the file library needs
-to read, and I<want> is the amount of space available in the buffer
-maintained by these functions.
+These functions provide i_fsample_t functions in terms of their
+i_sample_t versions.
-This means if you need to read from a stream that you don't know the
-length of, you can return I<need> bytes, taking the performance hit of
-possibly expensive callbacks (eg. back to perl code), or if you are
-reading from a stream where it doesn't matter if some data is lost, or
-if the total length of the stream is known, you can return I<want>
-bytes.
+=over
-=cut
+=item i_ppixf_fp(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix)
+
+=cut
*/
-int
-i_gen_reader(i_gen_read_data *gci, char *buf, int length) {
- int total;
-
- if (length < gci->length - gci->cpos) {
- /* simplest case */
- memcpy(buf, gci->buffer+gci->cpos, length);
- gci->cpos += length;
- return length;
- }
+int i_ppixf_fp(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *pix) {
+ i_color temp;
+ int ch;
+
+ for (ch = 0; ch < im->channels; ++ch)
+ temp.channel[ch] = SampleFTo8(pix->channel[ch]);
- total = 0;
- memcpy(buf, gci->buffer+gci->cpos, gci->length-gci->cpos);
- total += gci->length - gci->cpos;
- length -= gci->length - gci->cpos;
- buf += gci->length - gci->cpos;
- if (length < (int)sizeof(gci->buffer)) {
- int did_read;
- int copy_size;
- while (length
- && (did_read = (gci->cb)(gci->userdata, gci->buffer, length,
- sizeof(gci->buffer))) > 0) {
- gci->cpos = 0;
- gci->length = did_read;
-
- copy_size = min(length, gci->length);
- memcpy(buf, gci->buffer, copy_size);
- gci->cpos += copy_size;
- buf += copy_size;
- total += copy_size;
- length -= copy_size;
+ return i_ppix(im, x, y, &temp);
+}
+
+/*
+=item i_gpixf_fp(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix)
+
+=cut
+*/
+int i_gpixf_fp(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix) {
+ i_color temp;
+ int ch;
+
+ if (i_gpix(im, x, y, &temp) == 0) {
+ for (ch = 0; ch < im->channels; ++ch)
+ pix->channel[ch] = Sample8ToF(temp.channel[ch]);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+/*
+=item i_plinf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *pix)
+
+=cut
+*/
+i_img_dim
+i_plinf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *pix) {
+ i_color *work;
+
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (r > l) {
+ i_img_dim ret;
+ i_img_dim i;
+ int ch;
+ work = mymalloc(sizeof(i_color) * (r-l));
+ for (i = 0; i < r-l; ++i) {
+ for (ch = 0; ch < im->channels; ++ch)
+ work[i].channel[ch] = SampleFTo8(pix[i].channel[ch]);
+ }
+ ret = i_plin(im, l, r, y, work);
+ myfree(work);
+
+ return ret;
+ }
+ else {
+ return 0;
}
}
else {
- /* just read the rest - too big for our buffer*/
- int did_read;
- while ((did_read = (gci->cb)(gci->userdata, buf, length, length)) > 0) {
- length -= did_read;
- total += did_read;
- buf += did_read;
+ return 0;
+ }
+}
+
+/*
+=item i_glinf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *pix)
+
+=cut
+*/
+i_img_dim
+i_glinf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *pix) {
+ i_color *work;
+
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (r > l) {
+ i_img_dim ret;
+ i_img_dim i;
+ int ch;
+ work = mymalloc(sizeof(i_color) * (r-l));
+ ret = i_plin(im, l, r, y, work);
+ for (i = 0; i < r-l; ++i) {
+ for (ch = 0; ch < im->channels; ++ch)
+ pix[i].channel[ch] = Sample8ToF(work[i].channel[ch]);
+ }
+ myfree(work);
+
+ return ret;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ return 0;
+ }
+}
+
+/*
+=item i_gsampf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samp, int *chans, int chan_count)
+
+=cut
+*/
+
+i_img_dim
+i_gsampf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samp,
+ int const *chans, int chan_count) {
+ i_sample_t *work;
+
+ if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
+ if (r > im->xsize)
+ r = im->xsize;
+ if (r > l) {
+ i_img_dim ret;
+ i_img_dim i;
+ work = mymalloc(sizeof(i_sample_t) * (r-l));
+ ret = i_gsamp(im, l, r, y, work, chans, chan_count);
+ for (i = 0; i < ret; ++i) {
+ samp[i] = Sample8ToF(work[i]);
+ }
+ myfree(work);
+
+ return ret;
+ }
+ else {
+ return 0;
}
}
- return total;
+ else {
+ return 0;
+ }
}
/*
-=item i_gen_read_data_new(i_read_callback_t cb, char *userdata)
+=back
-For use by callback file readers to initialize the reader buffer.
+=head2 Palette wrapper functions
-Allocates, initializes and returns the reader buffer.
+Used for virtual images, these forward palette calls to a wrapped image,
+assuming the wrapped image is the first pointer in the structure that
+im->ext_data points at.
-See also L<image.c/free_gen_read_data> and L<image.c/i_gen_reader>.
+=over
+
+=item i_addcolors_forward(i_img *im, const i_color *colors, int count)
=cut
*/
-i_gen_read_data *
-i_gen_read_data_new(i_read_callback_t cb, char *userdata) {
- i_gen_read_data *self = mymalloc(sizeof(i_gen_read_data));
- self->cb = cb;
- self->userdata = userdata;
- self->length = 0;
- self->cpos = 0;
-
- return self;
+int i_addcolors_forward(i_img *im, const i_color *colors, int count) {
+ return i_addcolors(*(i_img **)im->ext_data, colors, count);
}
/*
-=item free_gen_read_data(i_gen_read_data *)
+=item i_getcolors_forward(i_img *im, int i, i_color *color, int count)
+
+=cut
+*/
+int i_getcolors_forward(i_img *im, int i, i_color *color, int count) {
+ return i_getcolors(*(i_img **)im->ext_data, i, color, count);
+}
-Cleans up.
+/*
+=item i_setcolors_forward(i_img *im, int i, const i_color *color, int count)
=cut
*/
-void free_gen_read_data(i_gen_read_data *self) {
- myfree(self);
+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);
}
/*
-=item i_gen_writer(i_gen_write_data *info, char const *data, int size)
+=item i_colorcount_forward(i_img *im)
-Performs write buffering for a callback based file writer.
+=cut
+*/
+int i_colorcount_forward(i_img *im) {
+ return i_colorcount(*(i_img **)im->ext_data);
+}
-Failures are considered fatal, if a write fails then data will be
-dropped.
+/*
+=item i_maxcolors_forward(i_img *im)
=cut
*/
-int
-i_gen_writer(
-i_gen_write_data *self,
-char const *data,
-int size)
-{
- if (self->filledto && self->filledto+size > self->maxlength) {
- if (self->cb(self->userdata, self->buffer, self->filledto)) {
- self->filledto = 0;
+int i_maxcolors_forward(i_img *im) {
+ return i_maxcolors(*(i_img **)im->ext_data);
+}
+
+/*
+=item i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry)
+
+=cut
+*/
+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
+*/
+
+i_img_dim
+i_gsamp_bits_fb(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *samps,
+ const int *chans, int chan_count, int bits) {
+ dIMCTXim(im);
+
+ 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;
+ i_img_dim 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) {
+ im_push_errorf(aIMCTX, 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 {
- self->filledto = 0;
- return 0;
+ 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;
}
- if (self->filledto+size <= self->maxlength) {
- /* just save it */
- memcpy(self->buffer+self->filledto, data, size);
- self->filledto += size;
+ else {
+ i_push_error(0, "Image position outside of image");
+ return -1;
+ }
+}
+
+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;
}
- /* doesn't fit - hand it off */
- return self->cb(self->userdata, data, size);
+ else {
+ return !memcmp(magic->magic, buffer, magic->magic_size);
+ }
+}
+
+/*
+=item i_test_format_probe(io_glue *data, int length)
+
+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) }
+
+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"),
+
+ /* 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"),
+
+ /* WEBP
+ http://code.google.com/speed/webp/docs/riff_container.html */
+ FORMAT_ENTRY2("RIFF WEBP", "webp", "xxxx xxxx"),
+
+ /* JPEG 2000
+ This might match a little loosely */
+ FORMAT_ENTRY("\x00\x00\x00\x0CjP \x0D\x0A\x87\x0A", "jp2"),
+ };
+ 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;
+ unsigned char head[18];
+ ssize_t rc;
+
+ rc = i_io_peekn(data, head, 18);
+ if (rc == -1) return NULL;
+#if 0
+ {
+ int i;
+ fprintf(stderr, "%d bytes -", (int)rc);
+ for (i = 0; i < rc; ++i)
+ fprintf(stderr, " %02x", head[i]);
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ for(i=0; i<sizeof(formats)/sizeof(formats[0]); i++) {
+ struct magic_entry const *entry = formats + i;
+
+ if (test_magic(head, rc, entry))
+ return entry->name;
+ }
+
+ if ((rc == 18) &&
+ tga_header_verify(head))
+ return "tga";
+
+ for(i=0; i<sizeof(more_formats)/sizeof(more_formats[0]); i++) {
+ struct magic_entry const *entry = more_formats + i;
+
+ if (test_magic(head, rc, entry))
+ return entry->name;
+ }
+
+ return NULL;
+}
+
+/*
+=item i_img_is_monochrome(img, &zero_is_white)
+
+=category Image Information
+
+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. C<pbm> for
+C<pnm>.
+
+For performance of encoders we require monochrome images:
+
+=over
+
+=item *
+
+be paletted
+
+=item *
+
+have a palette of two colors, containing only C<(0,0,0)> and
+C<(255,255,255)> in either order.
+
+=back
+
+C<zero_is_white> is set to non-zero if 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;
}
/*
-=item i_gen_write_data_new(i_write_callback_t cb, char *userdata, int max_length)
+=item i_get_file_background(im, &bg)
+
+=category Files
-Allocates and initializes the data structure used by i_gen_writer.
+Retrieve the file write background color tag from the image.
-This should be released with L<image.c/free_gen_write_data>
+If not present, C<bg> is set to black.
+
+Returns 1 if the C<i_background> tag was found and valid.
=cut
*/
-i_gen_write_data *i_gen_write_data_new(i_write_callback_t cb,
- char *userdata, int max_length)
-{
- i_gen_write_data *self = mymalloc(sizeof(i_gen_write_data));
- self->cb = cb;
- self->userdata = userdata;
- self->maxlength = min(max_length, sizeof(self->buffer));
- if (self->maxlength < 0)
- self->maxlength = sizeof(self->buffer);
- self->filledto = 0;
-
- return self;
+
+int
+i_get_file_background(i_img *im, i_color *bg) {
+ int result = i_tags_get_color(&im->tags, "i_background", 0, bg);
+ if (!result) {
+ /* black default */
+ bg->channel[0] = bg->channel[1] = bg->channel[2] = 0;
+ }
+ /* always full alpha */
+ bg->channel[3] = 255;
+
+ return result;
}
/*
-=item free_gen_write_data(i_gen_write_data *info, int flush)
+=item i_get_file_backgroundf(im, &bg)
+
+=category Files
-Cleans up the write buffer.
+Retrieve the file write background color tag from the image as a
+floating point color.
-Will flush any left-over data if I<flush> is non-zero.
+Implemented in terms of i_get_file_background().
-Returns non-zero if flush is zero or if info->cb() returns non-zero.
+If not present, C<bg> is set to black.
-Return zero only if flush is non-zero and info->cb() returns zero.
-ie. if it fails.
+Returns 1 if the C<i_background> tag was found and valid.
=cut
*/
-int free_gen_write_data(i_gen_write_data *info, int flush)
-{
- int result = !flush ||
- info->filledto == 0 ||
- info->cb(info->userdata, info->buffer, info->filledto);
- myfree(info);
+int
+i_get_file_backgroundf(i_img *im, i_fcolor *fbg) {
+ i_color bg;
+ int result = 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;
return result;
}
/*
=back
+=head1 AUTHOR
+
+Arnar M. Hrafnkelsson <addi@umich.edu>
+
+Tony Cook <tonyc@cpan.org>
+
=head1 SEE ALSO
L<Imager>, L<gif.c>