/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % IIIII M M AAA GGGG EEEEE % % I MM MM A A G E % % I M M M AAAAA G GG EEE % % I M M A A G G E % % IIIII M M A A GGGG EEEEE % % % % % % ImageMagick Image Routines % % % % % % % % Software Design % % John Cristy % % July 1992 % % % % % % Copyright 1997 E. I. du Pont de Nemours and Company % % % % Permission to use, copy, modify, distribute, and sell this software and % % its documentation for any purpose is hereby granted without fee, % % provided that the above Copyright notice appear in all copies and that % % both that Copyright notice and this permission notice appear in % % supporting documentation, and that the name of E. I. du Pont de Nemours % % and Company not be used in advertising or publicity pertaining to % % distribution of the software without specific, written prior % % permission. E. I. du Pont de Nemours and Company makes no representations % % about the suitability of this software for any purpose. It is provided % % "as is" without express or implied warranty. % % % % E. I. du Pont de Nemours and Company disclaims all warranties with regard % % to this software, including all implied warranties of merchantability % % and fitness, in no event shall E. I. du Pont de Nemours and Company be % % liable for any special, indirect or consequential damages or any % % damages whatsoever resulting from loss of use, data or profits, whether % % in an action of contract, negligence or other tortious action, arising % % out of or in connection with the use or performance of this software. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % */ /* Include declarations. */ #include "magick.h" #include "formats.h" #include "Colorlist.h" /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % A l l o c a t e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function AllocateImage allocates an Image structure and initializes each % field to a default value. % % The format of the AllocateImage routine is: % % allocated_image=AllocateImage(image_info) % % A description of each parameter follows: % % o allocated_image: Function AllocateImage returns a pointer to an image % structure initialized to default values. A null image is returned if % there is a memory shortage. % % o image_info: Specifies a pointer to a ImageInfo structure. % % */ Export Image *AllocateImage(const ImageInfo *image_info) { Image *allocated_image; XColor color; /* Allocate image structure. */ allocated_image=(Image *) malloc(sizeof(Image)); if (allocated_image == (Image *) NULL) { Warning("Unable to allocate image","Memory allocation failed"); return((Image *) NULL); } /* Initialize Image structure. */ allocated_image->file=(FILE *) NULL; allocated_image->status=False; allocated_image->temporary=False; *allocated_image->filename='\0'; allocated_image->filesize=0; allocated_image->pipe=False; (void) strcpy(allocated_image->magick,"MIFF"); allocated_image->comments=(char *) NULL; allocated_image->label=(char *) NULL; allocated_image->text=(char *) NULL; allocated_image->id=UndefinedId; allocated_image->class=DirectClass; allocated_image->matte=False; allocated_image->compression=RunlengthEncodedCompression; allocated_image->columns=0; allocated_image->rows=0; allocated_image->depth=QuantumDepth; allocated_image->interlace=DefaultInterlace; allocated_image->scene=0; allocated_image->number_scenes=1; allocated_image->units=UndefinedResolution; allocated_image->x_resolution=0.0; allocated_image->y_resolution=0.0; allocated_image->montage=(char *) NULL; allocated_image->directory=(char *) NULL; allocated_image->colormap=(ColorPacket *) NULL; allocated_image->colorspace=RGBColorspace; allocated_image->colors=0; allocated_image->gamma=0.0; allocated_image->normalized_maximum_error=0.0; allocated_image->normalized_mean_error=0.0; allocated_image->mean_error_per_pixel=0; allocated_image->total_colors=0; allocated_image->signature=(char *) NULL; allocated_image->pixels=(RunlengthPacket *) NULL; allocated_image->packet=(RunlengthPacket *) NULL; allocated_image->packets=0; allocated_image->packet_size=0; allocated_image->packed_pixels=(unsigned char *) NULL; *allocated_image->magick_filename='\0'; allocated_image->magick_columns=0; allocated_image->magick_rows=0; allocated_image->magick_time=time((time_t *) NULL); allocated_image->geometry=(char *) NULL; allocated_image->page=(char *) NULL; allocated_image->dispose=0; allocated_image->delay=0; allocated_image->iterations=1; (void) XQueryColorDatabase(BackgroundColor,&color); allocated_image->background_color.red=XDownScale(color.red); allocated_image->background_color.green=XDownScale(color.green); allocated_image->background_color.blue=XDownScale(color.blue); allocated_image->background_color.index=0; (void) XQueryColorDatabase(BorderColor,&color); allocated_image->border_color.red=XDownScale(color.red); allocated_image->border_color.green=XDownScale(color.green); allocated_image->border_color.blue=XDownScale(color.blue); allocated_image->border_color.index=0; (void) XQueryColorDatabase(MatteColor,&color); allocated_image->matte_color.red=XDownScale(color.red); allocated_image->matte_color.green=XDownScale(color.green); allocated_image->matte_color.blue=XDownScale(color.blue); allocated_image->matte_color.index=0; if (image_info != (ImageInfo *) NULL) { (void) strcpy(allocated_image->filename,image_info->filename); (void) strcpy(allocated_image->magick,image_info->magick); } allocated_image->orphan=False; allocated_image->previous=(Image *) NULL; allocated_image->list=(Image *) NULL; allocated_image->next=(Image *) NULL; return(allocated_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % A l l o c a t e N e x t I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function AllocateNextImage allocates an Image structure and initializes each % field to a default value. % % The format of the AllocateNextImage routine is: % % AllocateImage(image_info,image) % % A description of each parameter follows: % % o image_info: Specifies a pointer to a ImageInfo structure. % % o image: The address of a structure of type Image. % % */ Export void AllocateNextImage(const ImageInfo *image_info,Image *image) { /* Allocate image structure. */ image->next=AllocateImage(image_info); if (image->next == (Image *) NULL) return; (void) strcpy(image->next->filename,image_info->filename); image->next->file=image->file; image->next->filesize=image->filesize; image->next->scene=image->scene+1; image->next->previous=image; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % A n n o t a t e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function AnnotateImage annotates an image with test. Optionally the % annotation can include the image filename, type, width, height, or scene % number by embedding special format characters. % % The format of the AnnotateImage routine is: % % AnnotateImage(image,annotate_info) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % o annotate_info: The address of a AnnotateInfo structure. % % */ void AnnotateImage(Image *image,AnnotateInfo *annotate_info) { char *text, **textlist; FILE *file; int flags, offset, status, x, y; register char *p, *q; register int i, j; static AnnotateInfo cache_info; static Display *display = (Display *) NULL; static unsigned int font_type = 0; static XAnnotateInfo xannotate_info; static XFontStruct *font_info; static XPixelInfo pixel_info; static XResourceInfo resource_info; static XrmDatabase resource_database; static XStandardColormap *map_info; static XVisualInfo *visual_info; unsigned int height, indirection, length, width; /* Ensure the annotation info is valid. */ assert(image != (Image *) NULL); assert(annotate_info != (AnnotateInfo *) NULL); if (annotate_info->text == (char *) NULL) return; if (*annotate_info->text == '\0') return; indirection=(*annotate_info->text == '@'); if (indirection) { int c; /* Read text from a file. */ file=(FILE *) fopen(annotate_info->text+1,"r"); if (file == (FILE *) NULL) { Warning("Unable to read text file",annotate_info->text+1); return; } length=MaxTextExtent; annotate_info->text=(char *) malloc(length); for (q=annotate_info->text; annotate_info->text != (char *) NULL; q++) { c=fgetc(file); if (c == EOF) break; if ((q-annotate_info->text+1) >= length) { *q='\0'; length<<=1; annotate_info->text=(char *) realloc(annotate_info->text,length); if (annotate_info->text == (char *) NULL) break; q=annotate_info->text+Extent(annotate_info->text); } *q=(unsigned char) c; } (void) fclose(file); if (annotate_info->text == (char *) NULL) { Warning("Unable to annotate image","Memory allocation failed"); return; } *q='\0'; } /* Allocate and initialize image text. */ p=annotate_info->text; length=Extent(annotate_info->text)+MaxTextExtent; image->text=(char *) malloc(length); for (q=image->text; image->text != (char *) NULL; p++) { *q='\0'; if (*p == '\0') break; if ((q-image->text+MaxTextExtent) >= length) { length<<=1; image->text=(char *) realloc((char *) image->text,length); if (image->text == (char *) NULL) break; q=image->text+Extent(image->text); } /* Process formatting characters in text. */ if ((*p == '\\') && (*(p+1) == 'n')) { *q++='\n'; p++; continue; } if (*p != '%') { *q++=(*p); continue; } p++; switch (*p) { case 'b': { if (image->filesize >= (1 << 24)) (void) sprintf(q,"%ldmb",image->filesize/1024/1024); else if (image->filesize >= (1 << 14)) (void) sprintf(q,"%ldkb",image->filesize/1024); else (void) sprintf(q,"%ldb",image->filesize); q=image->text+Extent(image->text); break; } case 'd': case 'e': case 'f': case 't': { char directory[MaxTextExtent], *extension, *filename; /* Label segment is the base of the filename. */ if (Extent(image->magick_filename) == 0) break; (void) strcpy(directory,image->magick_filename); extension=directory+Extent(directory); filename=extension; while ((filename > directory) && (*(filename-1) != *BasenameSeparator)) { if (*filename == '.') if (*extension == '\0') extension=filename+1; filename--; } switch (*p) { case 'd': { *filename='\0'; (void) strcpy(q,directory); q+=Extent(directory); break; } case 'e': { (void) strcpy(q,extension); q+=Extent(extension); break; } case 'f': { (void) strcpy(q,filename); q+=Extent(filename); break; } case 't': { *(extension-1)='\0'; (void) strcpy(q,filename); q+=Extent(filename); break; } } break; } case 'h': { (void) sprintf(q,"%u",image->magick_rows); q=image->text+Extent(image->text); break; } case 'm': { (void) strcpy(q,image->magick); q+=Extent(image->magick); break; } case 's': { (void) sprintf(q,"%u",image->scene); q=image->text+Extent(image->text); break; } case 'w': { (void) sprintf(q,"%u",image->magick_columns); q=image->text+Extent(image->text); break; } default: { *q++='%'; *q++=(*p); break; } } } if (image->text == (char *) NULL) { Warning("Unable to annotate image","Memory allocation failed"); return; } *q++='\0'; *q++='\0'; if (indirection) free((char *) annotate_info->text); textlist=StringToList(image->text); free(image->text); image->text=(char *) NULL; if (textlist == (char **) NULL) return; length=Extent(textlist[0]); for (i=0; textlist[i] != (char *) NULL; i++) if (Extent(textlist[i]) > length) length=Extent(textlist[i]); text=(char *) malloc(length+4); if (text == (char *) NULL) { Warning("Unable to annotate image","Memory allocation error"); return; } /* Get annotate geometry. */ x=0; y=0; width=image->columns; height=annotate_info->pointsize; flags=NoValue; if (annotate_info->geometry != (char *) NULL) { flags=XParseGeometry(annotate_info->geometry,&x,&y,&width,&height); if ((flags & XNegative) != 0) x+=image->columns; if ((flags & YNegative) != 0) y+=image->rows; } switch (font_type) { case 0: default: { /* Open X server connection. */ if (display != (Display *) NULL) break; display=XOpenDisplay(annotate_info->server_name); if (display != (Display *) NULL) { /* Get user defaults from X resource database. */ XSetErrorHandler(XError); resource_database=XGetResourceDatabase(display,client_name); XGetResourceInfo(resource_database,client_name,&resource_info); resource_info.close_server=False; resource_info.colormap=PrivateColormap; if (annotate_info->font != (char *) NULL) resource_info.font=annotate_info->font; if (annotate_info->box != (char *) NULL) resource_info.background_color=annotate_info->box; if (annotate_info->pen != (char *) NULL) resource_info.foreground_color=annotate_info->pen; map_info=XAllocStandardColormap(); if (map_info == (XStandardColormap *) NULL) Warning("Unable to create standard colormap", "Memory allocation failed"); /* Initialize visual info. */ visual_info=XBestVisualInfo(display,map_info,&resource_info); if (visual_info == (XVisualInfo *) NULL) Warning("Unable to get visual",resource_info.visual_type); map_info->colormap=(Colormap) NULL; pixel_info.pixels=(unsigned long *) NULL; pixel_info.gamma_map=(XColor *) NULL; /* Initialize Standard Colormap info. */ XGetMapInfo(visual_info,XDefaultColormap(display,visual_info->screen), map_info); XGetPixelInfo(display,visual_info,map_info,&resource_info, (Image *) NULL,&pixel_info); pixel_info.annotate_context=XDefaultGC(display,visual_info->screen); /* Initialize font info. */ font_info=XBestFont(display,&resource_info,False); if (font_info == (XFontStruct *) NULL) Warning("Unable to load font",resource_info.font); if ((map_info == (XStandardColormap *) NULL) || (visual_info == (XVisualInfo *) NULL) || (font_info == (XFontStruct *) NULL)) { XFreeResources(display,visual_info,map_info,&pixel_info, font_info,&resource_info,(XWindowInfo *) NULL); display=(Display *) NULL; } cache_info=(*annotate_info); break; } Warning("Unable to load X server fonts","substituting Postscript fonts"); font_type++; } case 1: { char filename[MaxTextExtent], page[MaxTextExtent]; Image *annotate_image; ImageInfo image_info; register RunlengthPacket *q; unsigned int matte, polarity; XColor box_color, pen_color; /* X server fonts are not available, use Postscript to annotate. */ (void) XQueryColorDatabase(annotate_info->box,&box_color); (void) XQueryColorDatabase(annotate_info->pen,&pen_color); GetImageInfo(&image_info); TemporaryFilename(filename); image_info.monochrome=True; (void) sprintf(page,"%ux%u",height*length,height << 1); image_info.page=page; if (annotate_info->font == (char *) NULL) annotate_info->font=DefaultFont; for (i=0; textlist[i] != (char *) NULL; i++) { if ((x >= image->columns) || (y >= image->rows)) break; if (*textlist[i] == '\0') { free(textlist[i]); y+=annotate_info->pointsize; continue; } (void) strcpy(text,textlist[i]); for (j=(Extent(textlist[i])-1) >> 1; j >= 0; j--) { (void) strcpy(image_info.filename,filename); file=fopen(filename,WriteBinaryType); if (file == (FILE *) NULL) break; (void) fprintf(file,"%%!PS-Adobe-3.0\n"); (void) fprintf(file,"/%s findfont %u scalefont setfont\n", annotate_info->font,height); (void) fprintf(file,"0 %u moveto (%s) show\n",height,text); (void) fprintf(file,"showpage\n"); (void) fclose(file); annotate_image=ReadImage(&image_info); if (annotate_image == (Image *) NULL) break; TransformImage(&annotate_image,"0x0",(char *) NULL); if (annotate_image->columns < width) break; DestroyImage(annotate_image); (void) strcpy(text,textlist[i]); (void) strcpy(text+j,"..."); (void) strcat(text,textlist[i]+Extent(textlist[i])-j-1); } (void) remove(filename); free(textlist[i]); if (annotate_image == (Image *) NULL) { Warning("Unable to annotate image",(char *) NULL); break; } /* Composite text onto the image. */ polarity=0; if (annotate_image->class == PseudoClass) polarity=Intensity(annotate_image->colormap[0]) < (MaxRGB >> 1); annotate_image->class=DirectClass; annotate_image->matte=True; q=annotate_image->pixels; for (j=0; j < annotate_image->packets; j++) { if (q->index == polarity) { q->red=XDownScale(box_color.red); q->green=XDownScale(box_color.green); q->blue=XDownScale(box_color.blue); q->index= annotate_info->box == (char *) NULL ? Transparent : Opaque; } else { q->red=XDownScale(pen_color.red); q->green=XDownScale(pen_color.green); q->blue=XDownScale(pen_color.blue); q->index= annotate_info->pen == (char *) NULL ? Transparent : Opaque; } q++; } matte=image->matte; if (annotate_info->alignment == RightAlignment) offset=(-annotate_image->columns); else if (annotate_info->alignment == CenterAlignment) offset=(width >> 1)-(annotate_image->columns >> 1); else offset=0; CompositeImage(image,OverCompositeOp,annotate_image,x+offset,y); image->matte=matte; y+=annotate_info->pointsize; DestroyImage(annotate_image); } free(text); for ( ; textlist[i] != (char *) NULL; i++) free(textlist[i]); free((char *) textlist); return; } } /* Initialize annotate info. */ XGetAnnotateInfo(&xannotate_info); if (cache_info.font != annotate_info->font) { /* Font name has changed. */ XFreeFont(display,font_info); if (annotate_info->font != (char *) NULL) resource_info.font=annotate_info->font; font_info=XBestFont(display,&resource_info,False); if (font_info == (XFontStruct *) NULL) Warning("Unable to load font",resource_info.font); } if ((flags & HeightValue) == 0) height=font_info->ascent+font_info->descent; xannotate_info.font_info=font_info; xannotate_info.text=text; xannotate_info.x=x; xannotate_info.y=y; xannotate_info.width=width; xannotate_info.height=font_info->ascent+font_info->descent; annotate_info->pointsize=font_info->ascent+font_info->descent; if ((annotate_info->pen != (char *) NULL) && (annotate_info->box != (char *) NULL)) xannotate_info.stencil=OpaqueStencil; else if (annotate_info->pen != (char *) NULL) xannotate_info.stencil=ForegroundStencil; else xannotate_info.stencil=BackgroundStencil; if ((cache_info.box != annotate_info->box) || (cache_info.pen != annotate_info->pen)) { /* Pen color has changed. */ if (annotate_info->box != (char *) NULL) resource_info.background_color=annotate_info->box; if (annotate_info->pen != (char *) NULL) resource_info.foreground_color=annotate_info->pen; XGetPixelInfo(display,visual_info,map_info,&resource_info,(Image *) NULL, &pixel_info); } /* Annotate the text image. */ for (i=0; textlist[i] != (char *) NULL; i++) { if ((x >= image->columns) || (y >= image->rows)) break; if (*textlist[i] == '\0') { free(textlist[i]); xannotate_info.y+=height; y+=height; continue; } (void) strcpy(text,textlist[i]); for (j=(Extent(textlist[i])-1) >> 1; j >= 0; j--) { xannotate_info.width=(height*XTextWidth(font_info,text,Extent(text)))/ xannotate_info.height; if (xannotate_info.width < width) break; (void) strcpy(text,textlist[i]); (void) strcpy(text+j,"..."); (void) strcat(text,textlist[i]+Extent(textlist[i])-j-1); } free(textlist[i]); if (annotate_info->alignment == RightAlignment) offset=(-xannotate_info.width); else if (annotate_info->alignment == CenterAlignment) offset=(width >> 1)-(xannotate_info.width >> 1); else offset=0; (void) sprintf(xannotate_info.geometry,"%ux%u%+d%+d",xannotate_info.width, height,(int) (xannotate_info.x+offset),xannotate_info.y); xannotate_info.width=XTextWidth(font_info,text,Extent(text)); status=XAnnotateImage(display,&pixel_info,&xannotate_info,image); if (status == 0) { Warning("Unable to xannotate image","Memory allocation error"); break; } xannotate_info.y+=height; y+=height; } /* Free resources. */ cache_info=(*annotate_info); free(text); for ( ; textlist[i] != (char *) NULL; i++) free(textlist[i]); free((char *) textlist); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % A v e r a g e I m a g e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function AverageImages averages a set of images. All the input images must % be the same size in pixels. % % The format of the AverageImage routine is: % % AverageImages(images) % % A description of each parameter follows: % % o images: The address of a structure of type Image; returned from % ReadImage. % % */ Export Image *AverageImages(Image *images) { Image *averaged_image, *next_image; long blue, green, red; unsigned short index; register int i; register RunlengthPacket *q; /* Ensure the images are uncompressed. */ assert(images != (Image *) NULL); next_image=images; while (next_image != (Image *) NULL) { if ((next_image->columns != images->columns) || (next_image->rows != images->rows)) { Warning("Unable to average image","images are not the same size"); return((Image *) NULL); } if (!UncompressImage(next_image)) return((Image *) NULL); next_image=next_image->next; } /* Initialize average image attributes. */ images->orphan=True; averaged_image=CloneImage(images,images->columns,images->rows,False); images->orphan=False; if (averaged_image == (Image *) NULL) { Warning("Unable to average image","Memory allocation failed"); return((Image *) NULL); } averaged_image->class=DirectClass; q=averaged_image->pixels; for (i=0; i < averaged_image->packets; i++) { red=0; green=0; blue=0; index=0; next_image=images; while (next_image != (Image *) NULL) { if (i < next_image->packets) { red+=next_image->pixels[i].red; green+=next_image->pixels[i].green; blue+=next_image->pixels[i].blue; index++; } next_image=next_image->next; } q->red=(Quantum) ((red+(long) (index >> 1))/(long) index); q->green=(Quantum) ((green+(long) (index >> 1))/(long) index); q->blue=(Quantum) ((blue+(long) (index >> 1))/(long) index); q->index=0; q->length=0; q++; } return(averaged_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % B o r d e r I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function BorderImage takes an image and puts a border around it of a % particular color. It allocates the memory necessary for the new Image % structure and returns a pointer to the new image. % % The format of the BorderImage routine is: % % bordered_image=BorderImage(image,border_info) % % A description of each parameter follows: % % o bordered_image: Function BorderImage returns a pointer to the bordered % image. A null image is returned if there is a a memory shortage. % % o image: The address of a structure of type Image. % % o border_info: Specifies a pointer to a XRectangle which defines the % border region. % */ Image *BorderImage(Image *image,RectangleInfo *border_info) { #define BorderImageText " Adding border to image... " Image *bordered_image; register int x, y; register RunlengthPacket *p, *q; RunlengthPacket border; /* Initialize bordered image attributes. */ assert(image != (Image *) NULL); assert(border_info != (RectangleInfo *) NULL); bordered_image=CloneImage(image,image->columns+(border_info->width << 1), image->rows+(border_info->height << 1),False); if (bordered_image == (Image *) NULL) { Warning("Unable to border image","Memory allocation failed"); return((Image *) NULL); } /* Initialize border color. */ border.red=image->border_color.red; border.green=image->border_color.green; border.blue=image->border_color.blue; border.index=image->border_color.index; border.length=0; /* Copy image and put border around it. */ q=bordered_image->pixels; for (y=0; y < border_info->height; y++) for (x=0; x < bordered_image->columns; x++) *q++=border; p=image->pixels; image->runlength=p->length+1; for (y=0; y < image->rows; y++) { /* Initialize scanline with border color. */ for (x=0; x < border_info->width; x++) *q++=border; /* Transfer scanline. */ for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } *q=(*p); if (image->matte && (p->index == Transparent)) *q=border; q->length=0; q++; } x=0; while (x < (bordered_image->columns-image->columns-border_info->width)) { *q++=border; x++; } ProgressMonitor(BorderImageText,y,image->rows); } for (y=(bordered_image->rows-image->rows-border_info->height-1); y >= 0; y--) for (x=0; x < bordered_image->columns; x++) *q++=border; return(bordered_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C h o p I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ChopImage creates a new image that is a subregion of an existing % one. It allocates the memory necessary for the new Image structure and % returns a pointer to the new image. % % The format of the ChopImage routine is: % % chop_image=ChopImage(image,chop_info) % % A description of each parameter follows: % % o chop_image: Function ChopImage returns a pointer to the chop % image. A null image is returned if there is a a memory shortage or % if the image width or height is zero. % % o image: The address of a structure of type Image. % % o chop_info: Specifies a pointer to a RectangleInfo which defines the % region of the image to crop. % % */ Export Image *ChopImage(Image *image,RectangleInfo *chop_info) { #define ChopImageText " Chopping image... " Image *chopped_image; register int x, y; register RunlengthPacket *p, *q; unsigned int height; /* Check chop geometry. */ assert(image != (Image *) NULL); assert(chop_info != (RectangleInfo *) NULL); if (((chop_info->x+(int) chop_info->width) < 0) || ((chop_info->y+(int) chop_info->height) < 0) || (chop_info->x > (int) image->columns) || (chop_info->y > (int) image->rows)) { Warning("Unable to chop image","geometry does not contain image"); return((Image *) NULL); } if ((chop_info->x+(int) chop_info->width) > (int) image->columns) chop_info->width=(unsigned int) ((int) image->columns-chop_info->x); if ((chop_info->y+(int) chop_info->height) > (int) image->rows) chop_info->height=(unsigned int) ((int) image->rows-chop_info->y); if (chop_info->x < 0) { chop_info->width-=(unsigned int) (-chop_info->x); chop_info->x=0; } if (chop_info->y < 0) { chop_info->height-=(unsigned int) (-chop_info->y); chop_info->y=0; } /* Initialize chop image attributes. */ chopped_image=CloneImage(image,image->columns-chop_info->width, image->rows-chop_info->height,False); if (chopped_image == (Image *) NULL) { Warning("Unable to chop image","Memory allocation failed"); return((Image *) NULL); } /* Extract chop image. */ p=image->pixels; image->runlength=p->length+1; q=chopped_image->pixels; for (y=0; y < chop_info->y; y++) for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } if ((x < chop_info->x) || (x >= (chop_info->x+chop_info->width))) { *q=(*p); q->length=0; q++; } } /* Skip pixels up to the chop image. */ for (x=0; x < (chop_info->height*image->columns); x++) if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } /* Extract chop image. */ height=image->rows-(chop_info->y+chop_info->height); for (y=0; y < height; y++) { for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } if ((x < chop_info->x) || (x >= (chop_info->x+chop_info->width))) { *q=(*p); q->length=0; q++; } } ProgressMonitor(ChopImageText,y,height); } return(chopped_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C l o s e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function CloseImage closes a file associated with the image. If the % filename prefix is '|', the file is a pipe and is closed with PipeClose. % % The format of the CloseImage routine is: % % CloseImage(image) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % */ void CloseImage(Image *image) { /* Close image file. */ assert(image != (Image *) NULL); if (image->file == (FILE *) NULL) return; image->status=ferror(image->file); #if !defined(vms) && !defined(macintosh) && !defined(WIN32) if (image->pipe) (void) pclose(image->file); else #endif if ((image->file != stdin) && (image->file != stdout)) (void) fclose(image->file); image->file=(FILE *) NULL; if (!image->orphan) do { image->file=(FILE *) NULL; image=image->next; } while (image != (Image *) NULL); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C o l o r F l o o d f i l l I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ColorFloodfillImage floodfills the designated area with a color. % The floodfill algorithm is strongly based on a similiar algorithm in % "Graphics Gems" by Paul Heckbert. % % The format of the ColorFloodfillImage routine is: % % ColorFloodfillImage(image,x,y,xcolor,delta) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % o x,y: Unsigned integers representing the current location of the pen. % % o xcolor: A XColor structure. This is the RGB value of the target color. % % o delta: This is the allowed variance in color (fuzzy color). % % */ Export void ColorFloodfillImage(Image *image,int x,int y, const ColorPacket *color,const int delta) { int offset, skip, start, x1, x2; register RunlengthPacket *pixel; register XSegment *p; RunlengthPacket target; XSegment *segment_stack; /* Check boundary conditions. */ assert(image != (Image *) NULL); assert(color != (ColorPacket *) NULL); if ((y < 0) || (y >= image->rows)) return; if ((x < 0) || (x >= image->columns)) return; target=image->pixels[y*image->columns+x]; if (ColorMatch(*color,target,delta)) return; /* Allocate segment stack. */ segment_stack=(XSegment *) malloc(MaxStacksize*sizeof(XSegment)); if (segment_stack == (XSegment *) NULL) { Warning("Unable to recolor image","Memory allocation failed"); return; } /* Push initial segment on stack. */ start=0; p=segment_stack; Push(y,x,x,1); Push(y+1,x,x,-1); while (p > segment_stack) { /* Pop segment off stack. */ p--; x1=p->x1; x2=p->x2; offset=p->y2; y=p->y1+offset; /* Recolor neighboring pixels. */ for (x=x1; x >= 0 ; x--) { pixel=image->pixels+(y*image->columns+x); if (!ColorMatch(*pixel,target,delta)) break; pixel->red=color->red; pixel->green=color->green; pixel->blue=color->blue; } skip=x >= x1; if (!skip) { start=x+1; if (start < x1) Push(y,start,x1-1,-offset); x=x1+1; } do { if (!skip) { for ( ; x < image->columns; x++) { pixel=image->pixels+(y*image->columns+x); if (!ColorMatch(*pixel,target,delta)) break; pixel->red=color->red; pixel->green=color->green; pixel->blue=color->blue; } Push(y,start,x-1,offset); if (x > (x2+1)) Push(y,x2+1,x-1,-offset); } skip=False; for (x++; x <= x2 ; x++) { pixel=image->pixels+(y*image->columns+x); if (ColorMatch(*pixel,target,delta)) break; } start=x; } while (x <= x2); } free((char *) segment_stack); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % C o l o r i z e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ColorizeImage colorizes an image with the pen color. The amount % of the coloring is controled with the opacity levels. % % The format of the ColorizeImage routine is: % % ColorizeImage(image,opaque_color,pen_color) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o opaque_color, % pen_color: A character string that contain an X11 color string. % % */ void ColorizeImage(Image *image,char *opacity,char *pen_color) { #define ColorizeImageText " Colorizing the image... " ColorPacket target; int blue_opacity, count, green_opacity, red_opacity; long value; register int i; register RunlengthPacket *p; unsigned int status; XColor target_color; /* Determine RGB values of the pen color. */ assert(image != (Image *) NULL); if (opacity == (char *) NULL) return; status=XQueryColorDatabase(pen_color,&target_color); if (status == False) return; target.red=XDownScale(target_color.red); target.green=XDownScale(target_color.green); target.blue=XDownScale(target_color.blue); status=XQueryColorDatabase(pen_color,&target_color); if (status == False) return; red_opacity=100; green_opacity=100; blue_opacity=100; count=sscanf(opacity,"%d/%d/%d",&red_opacity,&green_opacity,&blue_opacity); if (count == 1) { if (red_opacity == 0) return; green_opacity=red_opacity; blue_opacity=red_opacity; } switch (image->class) { case DirectClass: default: { /* Colorize DirectClass image. */ p=image->pixels; for (i=0; i < image->packets; i++) { value=(long) (p->red*(100-red_opacity)+target.red*red_opacity)/100; p->red=(Quantum) ((value < 0) ? 0 : (value > MaxRGB) ? MaxRGB : value); value=(long) (p->green*(100-green_opacity)+target.green*green_opacity)/100; p->green=(Quantum) ((value < 0) ? 0 : (value > MaxRGB) ? MaxRGB : value); value=(long) (p->blue*(100-blue_opacity)+target.blue*blue_opacity)/100; p->blue=(Quantum) ((value < 0) ? 0 : (value > MaxRGB) ? MaxRGB : value); p++; if (QuantumTick(i,image)) ProgressMonitor(ColorizeImageText,i,image->packets); } break; } case PseudoClass: { /* Colorize PseudoClass image. */ for (i=0; i < image->colors; i++) { value=(long) (image->colormap[i].red*(100-red_opacity)+target.red*red_opacity)/100; image->colormap[i].red=(Quantum) ((value < 0) ? 0 : (value > MaxRGB) ? MaxRGB : value); value=(long) (image->colormap[i].green*(100-green_opacity)+ target.green*green_opacity)/100; image->colormap[i].green=(Quantum) ((value < 0) ? 0 : (value > MaxRGB) ? MaxRGB : value); value=(long) (image->colormap[i].blue*(100-blue_opacity)+ target.blue*blue_opacity)/100; image->colormap[i].blue=(Quantum) ((value < 0) ? 0 : (value > MaxRGB) ? MaxRGB : value); } SyncImage(image); break; } } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C o m m e n t I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function CommentImage initializes an image comment. Optionally the % comment can include the image filename, type, width, height, or scene % number by embedding special format characters. % % The format of the CommentImage routine is: % % CommentImage(image,comments) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % o comments: The address of a character string containing the comment % format. % % */ Export void CommentImage(Image *image,char *comments) { register char *p, *q; unsigned int indirection, length; assert(image != (Image *) NULL); if (image->comments != (char *) NULL) free((char *) image->comments); image->comments=(char *) NULL; if (comments == (char *) NULL) return; if (*comments == '\0') return; indirection=(*comments == '@'); if (indirection) { FILE *file; int c; /* Read comments from a file. */ file=(FILE *) fopen(comments+1,"r"); if (file == (FILE *) NULL) { Warning("Unable to read comments file",comments+1); return; } length=MaxTextExtent; comments=(char *) malloc(length); for (q=comments; comments != (char *) NULL; q++) { c=fgetc(file); if (c == EOF) break; if ((q-comments+1) >= length) { *q='\0'; length<<=1; comments=(char *) realloc((char *) comments,length); if (comments == (char *) NULL) break; q=comments+Extent(comments); } *q=(unsigned char) c; } (void) fclose(file); if (comments == (char *) NULL) { Warning("Unable to comments image","Memory allocation failed"); return; } *q='\0'; } /* Allocate and initialize image comments. */ p=comments; length=Extent(comments)+MaxTextExtent; image->comments=(char *) malloc(length); for (q=image->comments; image->comments != (char *) NULL; p++) { *q='\0'; if (*p == '\0') break; if ((q-image->comments+MaxTextExtent) >= length) { length<<=1; image->comments=(char *) realloc((char *) image->comments,length); if (image->comments == (char *) NULL) break; q=image->comments+Extent(image->comments); } /* Process formatting characters in comments. */ if ((*p == '\\') && (*(p+1) == 'n')) { *q++='\n'; p++; continue; } if (*p != '%') { *q++=(*p); continue; } p++; switch (*p) { case 'b': { if (image->filesize >= (1 << 24)) (void) sprintf(q,"%ldmb",image->filesize/1024/1024); else if (image->filesize >= (1 << 14)) (void) sprintf(q,"%ldkb",image->filesize/1024); else (void) sprintf(q,"%ldb",image->filesize); q=image->comments+Extent(image->comments); break; } case 'd': case 'e': case 'f': case 't': { char directory[MaxTextExtent], *extension, *filename; /* Label segment is the base of the filename. */ if (Extent(image->magick_filename) == 0) break; (void) strcpy(directory,image->magick_filename); extension=directory+Extent(directory); filename=extension; while ((filename > directory) && (*(filename-1) != *BasenameSeparator)) { if (*filename == '.') if (*extension == '\0') extension=filename+1; filename--; } switch (*p) { case 'd': { *filename='\0'; (void) strcpy(q,directory); q+=Extent(directory); break; } case 'e': { (void) strcpy(q,extension); q+=Extent(extension); break; } case 'f': { (void) strcpy(q,filename); q+=Extent(filename); break; } case 't': { *(extension-1)='\0'; (void) strcpy(q,filename); q+=Extent(filename); break; } } break; } case 'h': { (void) sprintf(q,"%u",image->magick_rows); q=image->comments+Extent(image->comments); break; } case 'm': { (void) strcpy(q,image->magick); q+=Extent(image->magick); break; } case 's': { (void) sprintf(q,"%u",image->scene); q=image->comments+Extent(image->comments); break; } case 'w': { (void) sprintf(q,"%u",image->magick_columns); q=image->comments+Extent(image->comments); break; } default: { *q++='%'; *q++=(*p); break; } } } if (image->comments == (char *) NULL) { Warning("Unable to comment image","Memory allocation failed"); return; } *q='\0'; if (indirection) free((char *) comments); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C o m p r e s s C o l o r m a p % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function CompressColormap compresses an image colormap removing any % unused color entries. % % The format of the CompressColormap routine is: % % CompressColormap(image) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % */ void CompressColormap(Image *image) { ColorPacket *colormap; int number_colors; register int i; register RunlengthPacket *p; register unsigned short index; /* Determine if colormap can be compressed. */ assert(image != (Image *) NULL); if (image->class != PseudoClass) return; number_colors=image->colors; for (i=0; i < image->colors; i++) image->colormap[i].flags=False; image->colors=0; p=image->pixels; for (i=0; i < image->packets; i++) { if (!image->colormap[p->index].flags) { image->colormap[p->index].index=image->colors; image->colormap[p->index].flags=True; image->colors++; } p++; } if (image->colors == number_colors) return; /* no unused entries */ /* Compress colormap. */ colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket)); if (colormap == (ColorPacket *) NULL) { Warning("Unable to compress colormap","Memory allocation failed"); image->colors=number_colors; return; } for (i=0; i < number_colors; i++) if (image->colormap[i].flags) { index=image->colormap[i].index; colormap[index].red=image->colormap[i].red; colormap[index].green=image->colormap[i].green; colormap[index].blue=image->colormap[i].blue; } /* Remap pixels. */ p=image->pixels; for (i=0; i < image->packets; i++) { p->index=image->colormap[p->index].index; p++; } free((char *) image->colormap); image->colormap=colormap; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C o m p r e s s I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function CompressImage compresses an image to the minimum number of % runlength-encoded packets. % % The format of the CompressImage routine is: % % CompressImage(image) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % */ Export void CompressImage(Image *image) { register int i; register RunlengthPacket *p, *q; /* Compress image. */ assert(image != (Image *) NULL); if (image == (Image *) NULL) return; p=image->pixels; image->runlength=p->length+1; image->packets=0; q=image->pixels; q->length=MaxRunlength; if (image->matte) for (i=0; i < (image->columns*image->rows); i++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } if ((p->red == q->red) && (p->green == q->green) && (p->blue == q->blue) && (p->index == q->index) && ((int) q->length < MaxRunlength)) q->length++; else { if (image->packets != 0) q++; image->packets++; *q=(*p); q->length=0; } } else for (i=0; i < (image->columns*image->rows); i++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } if ((p->red == q->red) && (p->green == q->green) && (p->blue == q->blue) && ((int) q->length < MaxRunlength)) q->length++; else { if (image->packets != 0) q++; image->packets++; *q=(*p); q->length=0; } } image->pixels=(RunlengthPacket *) realloc((char *) image->pixels,image->packets*sizeof(RunlengthPacket)); /* Runlength-encode only if it takes up less space than no compression. */ if (image->class == DirectClass) { if (image->packets >= ((image->columns*image->rows*3) >> 2)) image->compression=NoCompression; return; } if (image->packets >= ((image->columns*image->rows) >> 1)) image->compression=NoCompression; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C o m p o s i t e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function CompositeImage returns the second image composited onto the % first at the specified offsets. % % The format of the CompositeImage routine is: % % CompositeImage(image,compose,composite_image,x_offset,y_offset) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % o compose: Specifies an image composite operator. % % o composite_image: The address of a structure of type Image. % % o x_offset: An integer that specifies the column offset of the composited % image. % % o y_offset: An integer that specifies the row offset of the composited % image. % % */ Export void CompositeImage(Image *image,const CompositeOperator compose, Image *composite_image,const int x_offset,const int y_offset) { #define CompositeImageText " Compositing image... " long blue, green, index, red; Quantum shade; register int i, x, y; register RunlengthPacket *p, *q; /* Check composite geometry. */ assert(image != (Image *) NULL); assert(composite_image != (Image *) NULL); if (((x_offset+(int) image->columns) < 0) || ((y_offset+(int) image->rows) < 0) || (x_offset > (int) image->columns) || (y_offset > (int) image->rows)) { Warning("Unable to composite image","geometry does not contain image"); return; } /* Image must be uncompressed. */ if (!UncompressImage(image)) return; switch (compose) { case XorCompositeOp: case PlusCompositeOp: case MinusCompositeOp: case AddCompositeOp: case SubtractCompositeOp: case DifferenceCompositeOp: case BumpmapCompositeOp: case MatteReplaceCompositeOp: case AddMaskCompositeOp: case BlendCompositeOp: break; case DisplaceCompositeOp: { double x_displace, y_displace; float horizontal_scale, vertical_scale; Image *displaced_image; register RunlengthPacket *r; /* Allocate the displaced image. */ composite_image->orphan=True; displaced_image=CloneImage(composite_image,composite_image->columns, composite_image->rows,False); composite_image->orphan=False; if (displaced_image == (Image *) NULL) { Warning("Unable to display image","Memory allocation failed"); return; } horizontal_scale=20.0; vertical_scale=20.0; if (composite_image->geometry != (char *) NULL) { int count; /* Determine the horizontal and vertical displacement scale. */ count=sscanf(composite_image->geometry,"%fx%f\n", &horizontal_scale,&vertical_scale); if (count == 1) vertical_scale=horizontal_scale; } /* Shift image pixels as defined by a displacement map. */ p=composite_image->pixels; composite_image->runlength=p->length+1; r=displaced_image->pixels; for (y=0; y < composite_image->rows; y++) { if (((y_offset+y) < 0) || ((y_offset+y) >= image->rows)) continue; q=image->pixels+(y_offset+y)*image->columns+x_offset; for (x=0; x < composite_image->columns; x++) { if (composite_image->runlength != 0) composite_image->runlength--; else { p++; composite_image->runlength=p->length; } if (((x_offset+x) < 0) || ((x_offset+x) >= image->columns)) { q++; continue; } x_displace=(horizontal_scale*((float) Intensity(*p)- ((MaxRGB+1) >> 1)))/((MaxRGB+1) >> 1); y_displace=x_displace; if (composite_image->matte) y_displace=(vertical_scale*((float) p->index- ((MaxRGB+1) >> 1)))/((MaxRGB+1) >> 1); *r=Interpolate(image,q,x_offset+x+x_displace,y_offset+y+y_displace); r->length=0; q++; r++; } } composite_image=displaced_image; break; } case ReplaceCompositeOp: { /* Promote image to DirectClass if colormaps differ. */ if (image->class == PseudoClass) if ((composite_image->class == DirectClass) || (composite_image->colors != image->colors)) image->class=DirectClass; else { int status; status=memcmp((char *) composite_image->colormap, (char *) image->colormap,composite_image->colors* sizeof(ColorPacket)); if (status != 0) image->class=DirectClass; } if (image->matte && !composite_image->matte) { p=composite_image->pixels; for (i=0; i < composite_image->packets; i++) { p->index=Opaque; p++; } composite_image->class=DirectClass; composite_image->matte=True; } break; } default: { /* Initialize image matte data. */ if (!image->matte) { q=image->pixels; for (i=0; i < image->packets; i++) { q->index=Opaque; q++; } image->class=DirectClass; image->matte=True; } if (!composite_image->matte) { p=composite_image->pixels; red=p->red; green=p->green; blue=p->blue; if (IsMonochromeImage(composite_image)) { red=composite_image->background_color.red; green=composite_image->background_color.green; blue=composite_image->background_color.blue; } for (i=0; i < composite_image->packets; i++) { p->index=Opaque; if ((p->red == red) && (p->green == green) && (p->blue == blue)) p->index=Transparent; p++; } composite_image->class=DirectClass; composite_image->matte=True; } break; } } /* Initialize composited image. */ p=composite_image->pixels; composite_image->runlength=p->length+1; for (y=0; y < composite_image->rows; y++) { if (((y_offset+y) < 0) || ((y_offset+y) >= image->rows)) continue; q=image->pixels+(y_offset+y)*image->columns+x_offset; for (x=0; x < composite_image->columns; x++) { if (composite_image->runlength != 0) composite_image->runlength--; else { p++; composite_image->runlength=p->length; } if (((x_offset+x) < 0) || ((x_offset+x) >= image->columns)) { q++; continue; } switch (compose) { case OverCompositeOp: default: { if (p->index == Transparent) { red=q->red; green=q->green; blue=q->blue; index=q->index; } else if (p->index == Opaque) { red=p->red; green=p->green; blue=p->blue; index=p->index; } else { red=(long) (p->red*Opaque+q->red*(Opaque-p->index))/Opaque; green=(long) (p->green*Opaque+q->green*(Opaque-p->index))/Opaque; blue=(long) (p->blue*Opaque+q->blue*(Opaque-p->index))/Opaque; index=(long) (p->index*Opaque+q->index*(Opaque-p->index))/Opaque; } break; } case InCompositeOp: { red=(long) (p->red*q->index)/Opaque; green=(long) (p->green*q->index)/Opaque; blue=(long) (p->blue*q->index)/Opaque; index=(long) (p->index*q->index)/Opaque; break; } case OutCompositeOp: { red=(long) (p->red*(Opaque-q->index))/Opaque; green=(long) (p->green*(Opaque-q->index))/Opaque; blue=(long) (p->blue*(Opaque-q->index))/Opaque; index=(long) (p->index*(Opaque-q->index))/Opaque; break; } case AtopCompositeOp: { red=(long) (p->red*q->index+q->red*(Opaque-p->index))/Opaque; green=(long) (p->green*q->index+q->green*(Opaque-p->index))/Opaque; blue=(long) (p->blue*q->index+q->blue*(Opaque-p->index))/Opaque; index=(long) (p->index*q->index+q->index*(Opaque-p->index))/Opaque; break; } case XorCompositeOp: { red=(long) (p->red*(Opaque-q->index)+q->red*(Opaque-p->index))/Opaque; green=(long) (p->green*(Opaque-q->index)+q->green*(Opaque-p->index))/Opaque; blue=(long) (p->blue*(Opaque-q->index)+q->blue*(Opaque-p->index))/Opaque; index=(long) (p->index*(Opaque-q->index)+q->index*(Opaque-p->index))/Opaque; break; } case PlusCompositeOp: { red=(long) p->red+(long) q->red; green=(long) p->green+(long) q->green; blue=(long) p->blue+(long) q->blue; index=(long) p->index+(long) q->index; break; } case MinusCompositeOp: { red=(long) p->red-(long) q->red; green=(long) p->green-(long) q->green; blue=(long) p->blue-(long) q->blue; index=Opaque; break; } case AddCompositeOp: { red=(long) p->red+(long) q->red; if (red > Opaque) red-=(Opaque+1); green=(long) p->green+(long) q->green; if (green > Opaque) green-=(Opaque+1); blue=(long) p->blue+(long) q->blue; if (blue > Opaque) blue-=(Opaque+1); index=(long) p->index+(long) q->index; if (index > Opaque) index-=(Opaque+1); break; } case SubtractCompositeOp: { red=(long) p->red-(long) q->red; if (red < 0) red+=(Opaque+1); green=(long) p->green-(long) q->green; if (green < 0) green+=(Opaque+1); blue=(long) p->blue-(long) q->blue; if (blue < 0) blue+=(Opaque+1); index=(long) p->index-(long) q->index; if (index < 0) index+=(Opaque+1); break; } case DifferenceCompositeOp: { red=AbsoluteValue((long) p->red-(long) q->red); green=AbsoluteValue((long) p->green-(long) q->green); blue=AbsoluteValue((long) p->blue-(long) q->blue); index=AbsoluteValue((long) p->index-(long) q->index); break; } case BumpmapCompositeOp: { shade=Intensity(*p); red=(long) (q->red*shade)/Opaque; green=(long) (q->green*shade)/Opaque; blue=(long) (q->blue*shade)/Opaque; index=(long) (q->index*shade)/Opaque; break; } case ReplaceCompositeOp: { red=p->red; green=p->green; blue=p->blue; index=p->index; break; } case MatteReplaceCompositeOp: { red=q->red; green=q->green; blue=q->blue; index=p->index; break; } case AddMaskCompositeOp: { red=q->red; green=q->green; blue=q->blue; index=DownScale(Intensity(*p)); break; } case BlendCompositeOp: { red=(long) (p->red*p->index+q->red*q->index)/Opaque; green=(long) (p->green*p->index+q->green*q->index)/Opaque; blue=(long) (p->blue*p->index+q->blue*q->index)/Opaque; index=Opaque; break; } case DisplaceCompositeOp: { red=p->red; green=p->green; blue=p->blue; index=p->index; break; } } q->red=(Quantum) ((red < 0) ? 0 : (red > MaxRGB) ? MaxRGB : red); q->green=(Quantum) ((green < 0) ? 0 : (green > MaxRGB) ? MaxRGB : green); q->blue=(Quantum) ((blue < 0) ? 0 : (blue > MaxRGB) ? MaxRGB : blue); q->index=(unsigned short) ((index < Transparent) ? Transparent : (index > Opaque) ? Opaque : index); q->length=0; q++; } ProgressMonitor(CompositeImageText,y,composite_image->rows); } if (compose == BlendCompositeOp) image->matte=False; if (compose == DisplaceCompositeOp) { image->matte=False; DestroyImage(composite_image); } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % C o n t r a s t I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ContrastImage enhances the intensity differences between the % lighter and darker elements of the image. % % The format of the ContrastImage routine is: % % ContrastImage(image,sharpen) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o sharpen: If True, the intensity is increased otherwise it is % decreased. % % */ void ContrastImage(Image *image,const unsigned int sharpen) { #define DullContrastImageText " Dulling image contrast... " #define SharpenContrastImageText " Sharpening image contrast... " int sign; register int i; register RunlengthPacket *p; assert(image != (Image *) NULL); sign=sharpen ? 1 : -1; switch (image->class) { case DirectClass: default: { /* Contrast enhance DirectClass image. */ p=image->pixels; for (i=0; i < image->packets; i++) { Contrast(sign,&p->red,&p->green,&p->blue); p++; if (QuantumTick(i,image)) if (sharpen) ProgressMonitor(SharpenContrastImageText,i,image->packets); else ProgressMonitor(DullContrastImageText,i,image->packets); } break; } case PseudoClass: { /* Contrast enhance PseudoClass image. */ for (i=0; i < image->colors; i++) Contrast(sign,&image->colormap[i].red,&image->colormap[i].green, &image->colormap[i].blue); SyncImage(image); break; } } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C l o n e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function CloneImage returns a copy of all fields of the input image. The % the pixel memory is allocated but the pixel data is not copied. % % The format of the CloneImage routine is: % % clone_image=CloneImage(image,columns,rows,clone_pixels) % % A description of each parameter follows: % % o clone_image: Function CloneImage returns a pointer to the image after % copying. A null image is returned if there is a memory shortage. % % o image: The address of a structure of type Image. % % o columns: An integer that specifies the number of columns in the copied % image. % % o rows: An integer that specifies the number of rows in the copied % image. % % o clone_pixels: Specifies whether the pixel data is copied. Must be % either True or False; % % */ Export Image *CloneImage(Image *image,const unsigned int columns, const unsigned int rows,const unsigned int clone_pixels) { Image *clone_image; register int i; /* Allocate image structure. */ assert(image != (Image *) NULL); clone_image=(Image *) malloc(sizeof(Image)); if (clone_image == (Image *) NULL) return((Image *) NULL); /* Allocate the image pixels. */ *clone_image=(*image); clone_image->columns=columns; clone_image->rows=rows; if (clone_pixels) clone_image->pixels=(RunlengthPacket *) malloc((unsigned int) image->packets*sizeof(RunlengthPacket)); else { clone_image->packets=clone_image->columns*clone_image->rows; clone_image->pixels=(RunlengthPacket *) malloc((unsigned int) clone_image->packets*sizeof(RunlengthPacket)); } if (clone_image->pixels == (RunlengthPacket *) NULL) return((Image *) NULL); if (clone_pixels) { register RunlengthPacket *p, *q; /* Copy image pixels. */ p=image->pixels; q=clone_image->pixels; for (i=0; i < image->packets; i++) { *q=(*p); p++; q++; } } clone_image->packed_pixels=(unsigned char *) NULL; if (image->colormap != (ColorPacket *) NULL) { /* Allocate and copy the image colormap. */ clone_image->colormap=(ColorPacket *) malloc(image->colors*sizeof(ColorPacket)); if (clone_image->colormap == (ColorPacket *) NULL) return((Image *) NULL); for (i=0; i < image->colors; i++) clone_image->colormap[i]=image->colormap[i]; } if (image->comments != (char *) NULL) { /* Allocate and copy the image comments. */ clone_image->comments=(char *) malloc((unsigned int) Extent(image->comments)+1); if (clone_image->comments == (char *) NULL) return((Image *) NULL); (void) strcpy(clone_image->comments,image->comments); } if (image->label != (char *) NULL) { /* Allocate and copy the image label. */ clone_image->label=(char *) malloc((unsigned int) Extent(image->label)+1); if (clone_image->label == (char *) NULL) return((Image *) NULL); (void) strcpy(clone_image->label,image->label); } if (image->signature != (char *) NULL) { /* Allocate and copy the image signature. */ clone_image->signature=(char *) malloc((unsigned int) Extent(image->signature)+1); if (clone_image->signature == (char *) NULL) return((Image *) NULL); (void) strcpy(clone_image->signature,image->signature); } clone_image->page=(char *) NULL; if ((image->columns == columns) && (image->rows == rows)) if (image->page != (char *) NULL) { /* Allocate and copy the image page. */ clone_image->page=(char *) malloc((unsigned int) Extent(image->page)+1); if (clone_image->page == (char *) NULL) return((Image *) NULL); (void) strcpy(clone_image->page,image->page); } clone_image->montage=(char *) NULL; if ((image->columns == columns) && (image->rows == rows)) if (image->montage != (char *) NULL) { /* Allocate and copy the image montage. */ clone_image->montage=(char *) malloc((unsigned int) Extent(image->montage)+1); if (clone_image->montage == (char *) NULL) return((Image *) NULL); (void) strcpy(clone_image->montage,image->montage); } clone_image->directory=(char *) NULL; if ((image->columns == columns) && (image->rows == rows)) if (image->directory != (char *) NULL) { /* Allocate and copy the image directory. */ clone_image->directory=(char *) malloc((unsigned int) Extent(image->directory)+1); if (clone_image->directory == (char *) NULL) return((Image *) NULL); (void) strcpy(clone_image->directory,image->directory); } if (image->orphan) { clone_image->file=(FILE *) NULL; clone_image->previous=(Image *) NULL; clone_image->next=(Image *) NULL; } else { /* Link image into image list. */ if (clone_image->previous != (Image *) NULL) clone_image->previous->next=clone_image; if (clone_image->next != (Image *) NULL) clone_image->next->previous=clone_image; } clone_image->orphan=False; return(clone_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C r o p I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function CropImage creates a new image that is a subregion of an existing % one. It allocates the memory necessary for the new Image structure and % returns a pointer to the new image. This routine is optimized to perserve % the runlength encoding. That is, the cropped image will always use less % memory than the original. % % The format of the CropImage routine is: % % cropped_image=CropImage(image,crop_info) % % A description of each parameter follows: % % o cropped_image: Function CropImage returns a pointer to the cropped % image. A null image is returned if there is a a memory shortage or % if the image width or height is zero. % % o image: The address of a structure of type Image. % % o crop_info: Specifies a pointer to a RectangleInfo which defines the % region of the image to crop. % % */ Export Image *CropImage(Image *image,RectangleInfo *crop_info) { #define CropImageText " Cropping image... " #define DeltaX 16 Image *cropped_image; int x, y; register RunlengthPacket *p, *q; /* Check crop geometry. */ assert(image != (Image *) NULL); assert(crop_info != (RectangleInfo *) NULL); if (((crop_info->x+(int) crop_info->width) < 0) || ((crop_info->y+(int) crop_info->height) < 0) || (crop_info->x > (int) image->columns) || (crop_info->y > (int) image->rows)) { Warning("Unable to crop image","geometry does not contain image"); return((Image *) NULL); } if ((crop_info->x+(int) crop_info->width) > (int) image->columns) crop_info->width=(unsigned int) ((int) image->columns-crop_info->x); if ((crop_info->y+(int) crop_info->height) > (int) image->rows) crop_info->height=(unsigned int) ((int) image->rows-crop_info->y); if (crop_info->x < 0) { crop_info->width-=(unsigned int) (-crop_info->x); crop_info->x=0; } if (crop_info->y < 0) { crop_info->height-=(unsigned int) (-crop_info->y); crop_info->y=0; } if ((crop_info->width == 0) && (crop_info->height == 0)) { register int i; RunlengthPacket corners[4]; /* Set bounding box to the image dimensions. */ crop_info->width=0; crop_info->height=0; crop_info->x=image->columns; crop_info->y=image->rows; p=image->pixels; image->runlength=p->length+1; corners[0]=(*p); for (i=1; i <= (image->rows*image->columns); i++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } if (i == image->columns) corners[1]=(*p); if (i == (image->rows*image->columns-image->columns+1)) corners[2]=(*p); if (i == (image->rows*image->columns)) corners[3]=(*p); } p=image->pixels; image->runlength=p->length+1; for (y=0; y < image->rows; y++) { for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } if (!ColorMatch(*p,corners[0],DeltaX)) if (x < crop_info->x) crop_info->x=x; if (!ColorMatch(*p,corners[1],DeltaX)) if (x > crop_info->width) crop_info->width=x; if (!ColorMatch(*p,corners[0],DeltaX)) if (y < crop_info->y) crop_info->y=y; if (!ColorMatch(*p,corners[2],DeltaX)) if (y > crop_info->height) crop_info->height=y; } } if ((crop_info->width != 0) || (crop_info->height != 0)) { crop_info->width-=crop_info->x-1; crop_info->height-=crop_info->y-1; } } if ((crop_info->width == 0) || (crop_info->height == 0)) { Warning("Unable to crop image","geometry dimensions are zero"); return((Image *) NULL); } if ((crop_info->width == image->columns) && (crop_info->height == image->rows) && (crop_info->x == 0) && (crop_info->y == 0)) return((Image *) NULL); /* Initialize cropped image attributes. */ cropped_image=CloneImage(image,crop_info->width,crop_info->height,True); if (cropped_image == (Image *) NULL) { Warning("Unable to crop image","Memory allocation failed"); return((Image *) NULL); } /* Skip pixels up to the cropped image. */ p=image->pixels; image->runlength=p->length+1; for (x=0; x < (crop_info->y*image->columns+crop_info->x); x++) if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } /* Extract cropped image. */ cropped_image->packets=0; q=cropped_image->pixels; q->red=0; q->green=0; q->blue=0; q->index=0; q->length=MaxRunlength; for (y=0; y < (cropped_image->rows-1); y++) { /* Transfer scanline. */ for (x=0; x < cropped_image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } if ((p->red == q->red) && (p->green == q->green) && (p->blue == q->blue) && (p->index == q->index) && ((int) q->length < MaxRunlength)) q->length++; else { if (cropped_image->packets != 0) q++; cropped_image->packets++; *q=(*p); q->length=0; } } /* Skip to next scanline. */ for (x=0; x < (int) (image->columns-cropped_image->columns); x++) if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } ProgressMonitor(CropImageText,y,cropped_image->rows-1); } /* Transfer last scanline. */ for (x=0; x < cropped_image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } if ((p->red == q->red) && (p->green == q->green) && (p->blue == q->blue) && (p->index == q->index) && ((int) q->length < MaxRunlength)) q->length++; else { if (cropped_image->packets != 0) q++; cropped_image->packets++; *q=(*p); q->length=0; } } cropped_image->pixels=(RunlengthPacket *) realloc((char *) cropped_image->pixels,cropped_image->packets*sizeof(RunlengthPacket)); return(cropped_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % C y c l e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function CycleColormapImage cycles the image colormap by a specified % amount. % % The format of the CycleColormapImage routine is: % % CycleColormapImage(image,amount) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o amount: An unsigned value that specifies the offset of the colormap. % % */ void CycleColormapImage(Image *image,int amount) { #define CycleColormapImageText " Cycling image... " int index; register RunlengthPacket *q; register unsigned int i; assert(image != (Image *) NULL); if (image->class == DirectClass) { QuantizeInfo quantize_info; GetQuantizeInfo(&quantize_info); quantize_info.number_colors=MaxColormapSize; QuantizeImage(&quantize_info,image); } q=image->pixels; for (i=0; i < image->packets; i++) { index=((int) q->index+amount) % image->colors; if (index < 0) index+=image->colors; q->index=(unsigned short) index; q++; } SyncImage(image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % D e s c r i b e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function DescribeImage describes an image by printing its attributes to % stderr. % % The format of the DescribeImage routine is: % % DescribeImage(image,file,verbose) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % o file: send the image attributes to this file. % % o verbose: an unsigned value other than zero prints detailed information % about the image. % % */ Export void DescribeImage(Image *image,FILE *file,const unsigned int verbose) { char **textlist; Image *p; register int i; unsigned int count; assert(image != (Image *) NULL); assert(file != (FILE *) NULL); if (!verbose) { /* Display detailed info about the image. */ if (*image->magick_filename != '\0') if (strcmp(image->magick_filename,image->filename) != 0) (void) fprintf(file,"%s=>",image->magick_filename); if ((image->previous == (Image *) NULL) && (image->next == (Image *) NULL) && (image->scene == 0)) (void) fprintf(file,"%s ",image->filename); else (void) fprintf(file,"%s[%u] ",image->filename,image->scene); if ((image->magick_columns != 0) || (image->magick_rows != 0)) if ((image->magick_columns != image->columns) || (image->magick_rows != image->rows)) (void) fprintf(file,"%ux%u=>",image->magick_columns, image->magick_rows); if (image->page == (char *) NULL) (void) fprintf(file,"%ux%u ",image->columns,image->rows); else { int x, y; unsigned int sans; (void) XParseGeometry(image->page,&x,&y,&sans,&sans); (void) fprintf(file,"%ux%u%+d%+d ",image->columns,image->rows,x,y); } if (image->class == DirectClass) { (void) fprintf(file,"DirectClass "); if (image->total_colors != 0) (void) fprintf(file,"%luc ",image->total_colors); } else if (image->total_colors <= image->colors) (void) fprintf(file,"PseudoClass %uc ",image->colors); else { (void) fprintf(file,"PseudoClass %lu=>%uc ",image->total_colors, image->colors); (void) fprintf(file,"%u/%.6f/%.6fe ",image->mean_error_per_pixel, image->normalized_mean_error,image->normalized_maximum_error); } if (image->filesize != 0) if (image->filesize >= (1 << 24)) (void) fprintf(file,"%ldmb ",image->filesize/1024/1024); else if (image->filesize >= (1 << 14)) (void) fprintf(file,"%ldkb ",image->filesize/1024); else (void) fprintf(file,"%ldb ",image->filesize); (void) fprintf(file,"%s %lds\n",image->magick,time((time_t *) NULL)- image->magick_time+1); return; } /* Display verbose info about the image. */ (void) fprintf(file,"Image: %s\n",image->filename); if (image->class == DirectClass) (void) fprintf(file," class: DirectClass\n"); else (void) fprintf(file," class: PseudoClass\n"); if (image->class == DirectClass) { if (image->total_colors > 0) (void) fprintf(file," colors: %lu\n",image->total_colors); } else if (image->total_colors <= image->colors) (void) fprintf(file," colors: %u\n",image->colors); else (void) fprintf(file," colors: %lu=>%u\n",image->total_colors, image->colors); if (image->class == DirectClass) { if (image->total_colors < 1024) NumberColors(image,file); } else { char name[MaxTextExtent]; ColorPacket *p; double distance_squared, min_distance; int distance; register int i; register XColorlist *q; /* Display image colormap. */ p=image->colormap; for (i=0; i < image->colors; i++) { (void) fprintf(file," %d: (%3d,%3d,%3d) #%02x%02x%02x", i,p->red,p->green,p->blue,(unsigned int) p->red, (unsigned int) p->green,(unsigned int) p->blue); min_distance=3.0*65536.0*65536.0; for (q=Colorlist; q->name != (char *) NULL; q++) { distance=(int) DownScale(p->red)-(int) q->red; distance_squared=(unsigned int) (distance*distance); distance=(int) DownScale(p->green)-(int) q->green; distance_squared+=(unsigned int) (distance*distance); distance=(int) DownScale(p->blue)-(int) q->blue; distance_squared+=(unsigned int) (distance*distance); if (distance_squared < min_distance) { min_distance=distance_squared; (void) strcpy(name,q->name); } } (void) fprintf(file," "); if (min_distance < 16) { if (min_distance > 0) (void) fprintf(file,"~"); (void) fprintf(file,"%s",name); } (void) fprintf(file,"\n"); p++; } } if (image->signature != (char *) NULL) (void) fprintf(file," signature: %s\n",image->signature); if (image->matte) (void) fprintf(file," matte: True\n"); else (void) fprintf(file," matte: False\n"); if (image->gamma != 0.0) (void) fprintf(file," gamma: %f\n",image->gamma); if (image->packets < (image->columns*image->rows)) (void) fprintf(file," runlength packets: %u of %u\n",image->packets, image->columns*image->rows); (void) fprintf(file," geometry: %ux%u\n",image->columns,image->rows); if ((image->x_resolution != 0.0) && (image->y_resolution != 0.0)) { /* Display image resolution. */ (void) fprintf(file," resolution: %gx%g",image->x_resolution, image->y_resolution); if (image->units == UndefinedResolution) (void) fprintf(file," pixels\n"); else if (image->units == PixelsPerInchResolution) (void) fprintf(file," pixels/inch\n"); else if (image->units == PixelsPerCentimeterResolution) (void) fprintf(file," pixels/centimeter\n"); else (void) fprintf(file,"\n"); } (void) fprintf(file," depth: %u\n",image->depth); if (image->filesize != 0) if (image->filesize >= (1 << 24)) (void) fprintf(file," filesize: %ldmb\n",image->filesize/1024*1024); else if (image->filesize >= (1 << 14)) (void) fprintf(file," filesize: %ldkb\n",image->filesize/1024); else (void) fprintf(file," filesize: %ldb\n",image->filesize); if (image->interlace) (void) fprintf(file," interlaced: True\n"); else (void) fprintf(file," interlaced: False\n"); if (image->page != (char *) NULL) (void) fprintf(file," page geometry: %s\n",image->page); if (image->dispose) (void) fprintf(file," dispose method: %d\n",image->dispose); if (image->delay) (void) fprintf(file," delay: %d\n",image->delay); if (image->iterations != 1) (void) fprintf(file," iterations: %d\n",image->iterations); (void) fprintf(file," format: %s\n",image->magick); p=image; while (p->previous != (Image *) NULL) p=p->previous; for (count=1; p->next != (Image *) NULL; count++) p=p->next; if (count > 1) (void) fprintf(file," scene: %u of %u\n",image->scene,count); else if (image->scene != 0) (void) fprintf(file," scene: %u\n",image->scene); if (image->label != (char *) NULL) (void) fprintf(file," label: %s\n",image->label); if (image->comments != (char *) NULL) { /* Display image comment. */ (void) fprintf(file," comments:\n"); textlist=StringToList(image->comments); if (textlist != (char **) NULL) { for (i=0; textlist[i] != (char *) NULL; i++) { (void) fprintf(file," %s\n",textlist[i]); free(textlist[i]); } free((char *) textlist); } } if (image->montage != (char *) NULL) (void) fprintf(file," montage: %s\n",image->montage); if (image->directory != (char *) NULL) { char filename[MaxTextExtent]; Image *tile; ImageInfo image_info; register char *p, *q; /* Display visual image directory. */ GetImageInfo(&image_info); image_info.filename=filename; image_info.size="64x64"; (void) fprintf(file," directory:\n"); for (p=image->directory; *p != '\0'; p++) { q=p; while ((*q != '\n') && (*q != '\0')) q++; (void) strncpy(image_info.filename,p,q-p); image_info.filename[q-p]='\0'; p=q; (void) fprintf(file," %s",image_info.filename); tile=ReadImage(&image_info); if (tile == (Image *) NULL) { (void) fprintf(file,"\n"); continue; } (void) fprintf(file," %ux%u %s\n",tile->magick_columns, tile->magick_rows,tile->magick); if (tile->comments != (char *) NULL) { /* Display tile comment. */ textlist=StringToList(tile->comments); if (textlist != (char **) NULL) { for (i=0; textlist[i] != (char *) NULL; i++) { (void) fprintf(file," %s\n",textlist[i]); free(textlist[i]); } free((char *) textlist); } } DestroyImage(tile); } } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % D e s t r o y I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function DestroyImage deallocates memory associated with an image. % % The format of the DestroyImage routine is: % % DestroyImage(image) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % */ Export void DestroyImage(Image *image) { /* Close image. */ assert(image != (Image *) NULL); if (image->file != (FILE *) NULL) CloseImage(image); /* Deallocate the image comments. */ if (image->comments != (char *) NULL) free((char *) image->comments); /* Deallocate the image label. */ if (image->label != (char *) NULL) free((char *) image->label); /* Deallocate the image montage directory. */ if (image->montage != (char *) NULL) free((char *) image->montage); if (image->directory != (char *) NULL) free((char *) image->directory); /* Deallocate the image colormap. */ if (image->colormap != (ColorPacket *) NULL) free((char *) image->colormap); /* Deallocate the image signature. */ if (image->signature != (char *) NULL) free((char *) image->signature); /* Deallocate the image pixels. */ if (image->pixels != (RunlengthPacket *) NULL) free((char *) image->pixels); if (image->packed_pixels != (unsigned char *) NULL) free((char *) image->packed_pixels); /* Deallocate the image page geometry. */ if (image->page != (char *) NULL) free((char *) image->page); if (!image->orphan) { /* Unlink from linked list. */ if (image->previous != (Image *) NULL) if (image->next != (Image *) NULL) image->previous->next=image->next; else image->previous->next=(Image *) NULL; if (image->next != (Image *) NULL) if (image->previous != (Image *) NULL) image->next->previous=image->previous; else image->next->previous=(Image *) NULL; } /* Deallocate the image structure. */ free((char *) image); image=(Image *) NULL; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % D e s t r o y I m a g e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function DestroyImages deallocates memory associated with a linked list % of images. % % The format of the DestroyImages routine is: % % DestroyImages(image) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % */ Export void DestroyImages(Image *image) { Image *next_image; /* Proceed to the top of the image list. */ assert(image != (Image *) NULL); while (image->previous != (Image *) NULL) image=image->previous; do { /* Destroy this image. */ next_image=image->next; if (next_image != (Image *)NULL) next_image->previous=(Image *)NULL; DestroyImage(image); image=next_image; } while (image != (Image *) NULL); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % D r a w I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function DrawImage draws a primitive (line, rectangle, ellipse) on the % image. % % The format of the DrawImage routine is: % % DrawImage(image,annotate_info) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % o annotate_info: The address of a DrawInfo structure. % % */ void DrawImage(Image *image,AnnotateInfo *annotate_info) { #define DrawImageText " Drawing on image... " char keyword[MaxTextExtent]; float mid, slope, trix, triy; int j, n, y; PrimitiveInfo *primitive_info; PrimitiveType primitive; register char *p; register int i, inside, x; register RunlengthPacket *q; unsigned int indirection, length, number_coordinates; XColor pen_color; /* Ensure the annotation info is valid. */ assert(image != (Image *) NULL); assert(annotate_info != (AnnotateInfo *) NULL); if (annotate_info->primitive == (char *) NULL) return; if (!UncompressImage(image)) return; indirection=(*annotate_info->primitive == '@'); if (indirection) { FILE *file; int c; register char *q; /* Read text from a file. */ file=(FILE *) fopen(annotate_info->primitive+1,"r"); if (file == (FILE *) NULL) { Warning("Unable to read primitive file",annotate_info->primitive+1); return; } length=MaxTextExtent; annotate_info->primitive=(char *) malloc(length); q=annotate_info->primitive; while (annotate_info->primitive != (char *) NULL) { c=fgetc(file); if (c == EOF) break; if ((q-annotate_info->primitive+1) >= length) { *q='\0'; length<<=1; annotate_info->primitive=(char *) realloc(annotate_info->primitive,length); if (annotate_info->primitive == (char *) NULL) break; q=annotate_info->primitive+Extent(annotate_info->primitive); } *q++=(unsigned char) c; } (void) fclose(file); if (annotate_info->primitive == (char *) NULL) { Warning("Unable to draw image","Memory allocation failed"); return; } *q='\0'; } /* Allocate primitive info memory. */ number_coordinates=2048; primitive_info=(PrimitiveInfo *) malloc(number_coordinates*sizeof(PrimitiveInfo)); annotate_info->geometry=(char *) malloc(MaxTextExtent*sizeof(char)); annotate_info->text=(char *) malloc(Extent(annotate_info->primitive)*sizeof(char)); if ((primitive_info == (PrimitiveInfo *) NULL) || (annotate_info->geometry == (char *) NULL) || (annotate_info->text == (char *) NULL)) { if (indirection) free((char *) annotate_info->primitive); Warning("Unable to draw image","Memory allocation failed"); return; } /* Parse the primitive attributes. */ (void) XQueryColorDatabase(annotate_info->pen,&pen_color); primitive=UndefinedPrimitive; p=annotate_info->primitive; for (i=0; *p != '\0'; ) { /* Define primitive. */ while (isspace(*p)) p++; for (x=0; isalpha(*p); x++) keyword[x]=(*p++); keyword[x]='\0'; if (*keyword == '\0') break; primitive=UndefinedPrimitive; if (Latin1Compare("Point",keyword) == 0) primitive=PointPrimitive; if (Latin1Compare("Line",keyword) == 0) primitive=LinePrimitive; if (Latin1Compare("Rectangle",keyword) == 0) primitive=RectanglePrimitive; if (Latin1Compare("FillRectangle",keyword) == 0) primitive=FillRectanglePrimitive; if (Latin1Compare("Circle",keyword) == 0) primitive=EllipsePrimitive; if (Latin1Compare("FillCircle",keyword) == 0) primitive=FillEllipsePrimitive; if (Latin1Compare("Polygon",keyword) == 0) primitive=PolygonPrimitive; if (Latin1Compare("FillPolygon",keyword) == 0) primitive=FillPolygonPrimitive; if (Latin1Compare("Color",keyword) == 0) primitive=ColorPrimitive; if (Latin1Compare("Matte",keyword) == 0) primitive=MattePrimitive; if (Latin1Compare("Text",keyword) == 0) primitive=TextPrimitive; if (Latin1Compare("Image",keyword) == 0) primitive=ImagePrimitive; if (primitive == UndefinedPrimitive) break; j=i; for (x=0; *p != '\0'; x++) { /* Define points. */ while (isspace(*p)) p++; if (!IsGeometry(p)) break; primitive_info[i].primitive=primitive; primitive_info[i].coordinates=0; (void) sscanf(p,"%d%d%n",&primitive_info[i].x,&primitive_info[i].y,&n); (void) sscanf(p,"%d,%d%n",&primitive_info[i].x,&primitive_info[i].y,&n); (void) sscanf(p,"%d, %d%n",&primitive_info[i].x,&primitive_info[i].y,&n); (void) sscanf(p,"%d %d%n",&primitive_info[i].x,&primitive_info[i].y,&n); p+=n; i++; if (i < (number_coordinates-1)) continue; number_coordinates<<=1; primitive_info=(PrimitiveInfo *) realloc(primitive_info,number_coordinates*sizeof(PrimitiveInfo)); if (primitive_info != (PrimitiveInfo *) NULL) continue; if (indirection) free((char *) annotate_info->primitive); free((char *) annotate_info->geometry); free((char *) annotate_info->text); Warning("Unable to draw image","Memory allocation failed"); return; } primitive_info[j].coordinates=x; primitive_info[j].method=FloodfillMethod; if ((primitive == ColorPrimitive) || (primitive == MattePrimitive)) { /* Define method. */ while (isspace(*p)) p++; for (x=0; isalpha(*p); x++) keyword[x]=(*p++); keyword[x]='\0'; if (*keyword == '\0') break; if (Latin1Compare("point",keyword) == 0) primitive_info[j].method=PointMethod; else if (Latin1Compare("replace",keyword) == 0) primitive_info[j].method=ReplaceMethod; else if (Latin1Compare("floodfill",keyword) == 0) primitive_info[j].method=FloodfillMethod; else if (Latin1Compare("reset",keyword) == 0) primitive_info[j].method=ResetMethod; else primitive=UndefinedPrimitive; while (isspace(*p)) p++; } primitive_info[j].text=(char *) NULL; if ((primitive == TextPrimitive) || (primitive == ImagePrimitive)) { primitive_info[j].text=p; if (*p == '"') for (p++; (*p != '"') && (*p != '\0'); p++); else if (*p == '\'') for (p++; (*p != '\'') && (*p != '\0'); p++); else for (p++; (*p != ' ') && (*p != '\0'); p++); if (*p != '\0') p++; } } primitive_info[i].primitive=UndefinedPrimitive; if (primitive == UndefinedPrimitive) { Warning("Non-conforming drawing primitive definition",p); free((char *) primitive_info); if (indirection) free((char *) annotate_info->primitive); free((char *) annotate_info->geometry); free((char *) annotate_info->text); return; } /* Draw the primitive on the image. */ mid=annotate_info->linewidth/2.0; image->class=DirectClass; q=image->pixels; for (y=0; y < image->rows; y++) { for (x=0; x < image->columns; x++) { register PrimitiveInfo *p; inside=False; for (p=primitive_info; (p->primitive != UndefinedPrimitive) && !inside; ) { register PrimitiveInfo *q; q=p+p->coordinates-1; switch (p->primitive) { case PointPrimitive: default: { for ( ; (p < q) && !inside; p++) inside=(x == p->x) && (y == p->y); break; } case LinePrimitive: { for ( ; (p < q) && !inside; p++) { if (p->x == (p+1)->x) inside=(x >= (p->x-mid)) && (x < (p->x+mid)) && (y >= Min(p->y,(p+1)->y)) && (y <= Max(p->y,(p+1)->y)); else if (p->y == (p+1)->y) inside=(x >= Min(p->x,(p+1)->x)) && (x <= Max(p->x,(p+1)->x)) && (y >= (p->y-mid)) && (y < (p->y+mid)); else { slope=(float) (p->y-(p+1)->y)/(p->x-(p+1)->x); trix=(slope*p->x+x/slope+y-p->y)/(slope+1.0/slope); triy=slope*(trix-p->x)+p->y; inside= (((x-trix)*(x-trix)+(y-triy)*(y-triy)) <= (mid*mid)) && (trix >= Min(p->x,(p+1)->x)) && (trix <= Max(p->x,(p+1)->x)); } p++; } break; } case RectanglePrimitive: { for ( ; (p < q) && !inside; p++) { inside=(x >= Min(p->x-mid,(p+1)->x+mid)) && (x < Max(p->x-mid,(p+1)->x+mid)) && (y >= Min(p->y-mid,(p+1)->y+mid)) && (y < Max(p->y-mid,(p+1)->y+mid)); inside&=!((x >= Min(p->x+mid,(p+1)->x-mid)) && (x < Max(p->x+mid,(p+1)->x-mid)) && (y >= Min(p->y+mid,(p+1)->y-mid)) && (y < Max(p->y+mid,(p+1)->y-mid))); p++; } break; } case FillRectanglePrimitive: { for ( ; (p < q) && !inside; p++) { inside=(x >= Min(p->x,(p+1)->x)) && (x <= Max(p->x,(p+1)->x)) && (y >= Min(p->y,(p+1)->y)) && (y <= Max(p->y,(p+1)->y)); p++; } break; } case EllipsePrimitive: { for ( ; (p < q) && !inside; p++) { inside=(((p->y-y)*(p->y-y))+((p->x-x)*(p->x-x))) <= (((p->y-(p+1)->y-mid)*(p->y-(p+1)->y-mid))+ ((p->x-(p+1)->x-mid)*(p->x-(p+1)->x-mid))); inside&=!((((p->y-y)*(p->y-y))+((p->x-x)*(p->x-x))) <= (((p->y-(p+1)->y+mid)*(p->y-(p+1)->y+mid))+ ((p->x-(p+1)->x+mid)*(p->x-(p+1)->x+mid)))); p++; } break; } case FillEllipsePrimitive: { for ( ; (p < q) && !inside; p++) { inside=((p->y-y)*(p->y-y))+((p->x-x)*(p->x-x)) <= ((p->y-(p+1)->y)*(p->y-(p+1)->y))+((p->x-(p+1)->x)* (p->x-(p+1)->x)); p++; } break; } case PolygonPrimitive: { for ( ; (p < q) && !inside; p++) { if (p->x == (p+1)->x) { inside|=(x >= (p->x-mid)) && (x < (p->x+mid)) && (y >= Min(p->y,(p+1)->y)) && (y <= Max(p->y,(p+1)->y)); continue; } if (p->y == (p+1)->y) { inside|=(x >= Min(p->x,(p+1)->x)) && (x <= Max(p->x,(p+1)->x)) && (y >= (p->y-mid)) && (y < (p->y+mid)); continue; } slope=(float) (p->y-(p+1)->y)/(p->x-(p+1)->x); trix=(slope*p->x+x/slope+y-p->y)/(slope+1.0/slope); triy=slope*(trix-p->x)+p->y; inside|=(((x-trix)*(x-trix)+(y-triy)*(y-triy)) <= (mid*mid)) && (trix >= Min(p->x,(p+1)->x)) && (trix <= Max(p->x,(p+1)->x)); } while (p <= q) p++; if (inside) break; p--; q=primitive_info; if (p->x == q->x) inside=(x >= (p->x-mid)) && (x < (p->x+mid)) && (y >= Min(p->y,q->y)) && (y <= Max(p->y,q->y)); else if (p->y == q->y) inside=(x >= Min(p->x,q->x)) && (x <= Max(p->x,q->x)) && (y >= (p->y-mid)) && (y < (p->y+mid)); else { slope=(float) (p->y-q->y)/(p->x-q->x); trix=(slope*p->x+x/slope+y-p->y)/(slope+1.0/slope); triy=slope*(trix-p->x)+p->y; inside=(((x-trix)*(x-trix)+(y-triy)*(y-triy)) <= (mid*mid)) && (trix >= Min(p->x,q->x)) && (trix <= Max(p->x,q->x)); } p++; break; } case FillPolygonPrimitive: { int crossing, crossings; crossings=0; if ((q->y >= y) != (p->y >= y)) { crossing=q->x >= x; if (crossing != (p->x >= x)) crossings+=(q->x-(q->y-y)*(p->x-q->x)/(p->y-q->y)) >= x; else if (crossing) crossings++; } for (p++; p <= q; p++) { if ((p-1)->y >= y) { while ((p <= q) && (p->y >= y)) p++; if (p > q) break; crossing=(p-1)->x >= x; if (crossing != (p->x >= x)) crossings+=((p-1)->x-((p-1)->y-y)*(p->x-(p-1)->x)/ (p->y-(p-1)->y)) >= x; else if (crossing) crossings++; continue; } while ((p <= q) && (p->y < y)) p++; if (p > q) break; crossing=(p-1)->x >= x; if (crossing != (p->x >= x)) crossings+= ((p-1)->x-((p-1)->y-y)*(p->x-(p-1)->x)/(p->y-(p-1)->y)) >= x; else if (crossing) crossings++; } inside=crossings & 0x01; break; } case ColorPrimitive: { for ( ; (p <= q) && !inside; p++) switch (p->method) { case PointMethod: default: { if ((p->x != x) || (p->y != y)) break; inside=True; break; } case ReplaceMethod: { RunlengthPacket color; static RunlengthPacket target; if ((x == 0) && (y == 0)) target=image->pixels[p->y*image->columns+p->x]; color=image->pixels[y*image->columns+x]; inside=ColorMatch(color,target,0); break; } case FloodfillMethod: { ColorPacket color; XColor pen_color; if ((p->x != x) || (p->y != y)) break; (void) XQueryColorDatabase(annotate_info->pen,&pen_color); color.red=XDownScale(pen_color.red); color.green=XDownScale(pen_color.green); color.blue=XDownScale(pen_color.blue); ColorFloodfillImage(image,x,y,&color,0); break; } case ResetMethod: { inside=True; break; } } break; } case MattePrimitive: { if (!image->matte) { /* Initialize matte image. */ image->matte=True; for (i=0; i < image->packets; i++) image->pixels[i].index=Opaque; } for ( ; p <= q; p++) switch (p->method) { case PointMethod: default: { if ((p->x != x) || (p->y != y)) break; image->pixels[y*image->columns+x].index=Transparent; break; } case ReplaceMethod: { RunlengthPacket color; static RunlengthPacket target; if ((x == 0) && (y == 0)) target=image->pixels[p->y*image->columns+p->x]; color=image->pixels[y*image->columns+x]; if (ColorMatch(color,target,0)) image->pixels[y*image->columns+x].index=Transparent; break; } case FloodfillMethod: { if ((p->x != x) || (p->y != y)) break; MatteFloodfillImage(image,x,y,Transparent,0); break; } case ResetMethod: { image->pixels[y*image->columns+x].index=Transparent; break; } } break; } case TextPrimitive: case ImagePrimitive: { register char *r; for ( ; p <= q; p++) { if ((p->x != x) || (p->y != y)) continue; r=p->text; if (*r == '"') { p->text++; for (r++; (*r != '"') && (*r != '\0'); r++); } else if (*r == '\'') { p->text++; for (r++; (*r != '\'') && (*r != '\0'); r++); } else for (r++; (*r != ' ') && (*r != '\0'); r++); (void) strncpy(annotate_info->text,p->text,r-p->text); annotate_info->text[r-p->text]='\0'; if (p->primitive == TextPrimitive) { (void) sprintf(annotate_info->geometry,"%+d%+d",p->x,p->y); AnnotateImage(image,annotate_info); } else { Image *composite_image; ImageInfo composite_info; GetImageInfo(&composite_info); (void) strcpy(composite_info.filename,annotate_info->text); composite_image=ReadImage(&composite_info); if (composite_image != (Image *) NULL) { CompositeImage(image,ReplaceCompositeOp,composite_image, p->x,p->y); DestroyImage(composite_image); } } } break; } } } if (inside) { q->red=XDownScale(pen_color.red); q->green=XDownScale(pen_color.green); q->blue=XDownScale(pen_color.blue); } q++; } ProgressMonitor(DrawImageText,y,image->rows); } free((char *) primitive_info); if (indirection) { free((char *) annotate_info->primitive); annotate_info->primitive=(char *) NULL; } free((char *) annotate_info->geometry); annotate_info->geometry=(char *) NULL; free((char *) annotate_info->text); annotate_info->text=(char *) NULL; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % E q u a l i z e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function EqualizeImage performs histogram equalization on the reference % image. % % The format of the EqualizeImage routine is: % % EqualizeImage(image) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % */ void EqualizeImage(Image *image) { #define EqualizeImageText " Equalizing image... " Quantum *equalize_map; register int i, j; register RunlengthPacket *p; unsigned int high, *histogram, low, *map; /* Allocate and initialize histogram arrays. */ assert(image != (Image *) NULL); histogram=(unsigned int *) malloc((MaxRGB+1)*sizeof(unsigned int)); map=(unsigned int *) malloc((MaxRGB+1)*sizeof(unsigned int)); equalize_map=(Quantum *) malloc((MaxRGB+1)*sizeof(Quantum)); if ((histogram == (unsigned int *) NULL) || (map == (unsigned int *) NULL) || (equalize_map == (Quantum *) NULL)) { Warning("Unable to equalize image","Memory allocation failed"); return; } /* Form histogram. */ for (i=0; i <= MaxRGB; i++) histogram[i]=0; p=image->pixels; for (i=0; i < image->packets; i++) { histogram[Intensity(*p)]+=(p->length+1); p++; } /* Integrate the histogram to get the equalization map. */ j=0; for (i=0; i <= MaxRGB; i++) { j+=histogram[i]; map[i]=j; } free((char *) histogram); if (map[MaxRGB] == 0) { free((char *) equalize_map); free((char *) map); return; } /* Equalize. */ low=map[0]; high=map[MaxRGB]; for (i=0; i <= MaxRGB; i++) equalize_map[i]=(Quantum) ((((double) (map[i]-low))*MaxRGB)/Max(high-low,1)); free((char *) map); /* Stretch the histogram. */ switch (image->class) { case DirectClass: default: { /* Equalize DirectClass packets. */ p=image->pixels; for (i=0; i < image->packets; i++) { p->red=equalize_map[p->red]; p->green=equalize_map[p->green]; p->blue=equalize_map[p->blue]; p++; if (QuantumTick(i,image)) ProgressMonitor(EqualizeImageText,i,image->packets); } break; } case PseudoClass: { /* Equalize PseudoClass packets. */ for (i=0; i < image->colors; i++) { image->colormap[i].red=equalize_map[image->colormap[i].red]; image->colormap[i].green=equalize_map[image->colormap[i].green]; image->colormap[i].blue=equalize_map[image->colormap[i].blue]; } SyncImage(image); break; } } free((char *) equalize_map); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % F l i p I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function FlipImage creates a new image that reflects each scanline in the % vertical direction It allocates the memory necessary for the new Image % structure and returns a pointer to the new image. % % The format of the FlipImage routine is: % % flipped_image=FlipImage(image) % % A description of each parameter follows: % % o flipped_image: Function FlipImage returns a pointer to the image % after reflecting. A null image is returned if there is a memory % shortage. % % o image: The address of a structure of type Image. % % */ Image *FlipImage(Image *image) { #define FlipImageText " Flipping image... " Image *flipped_image; register RunlengthPacket *p, *q, *s; register unsigned int x, y; RunlengthPacket *scanline; /* Initialize flipped image attributes. */ assert(image != (Image *) NULL); flipped_image=CloneImage(image,image->columns,image->rows,False); if (flipped_image == (Image *) NULL) { Warning("Unable to flip image","Memory allocation failed"); return((Image *) NULL); } /* Allocate scan line buffer and column offset buffers. */ scanline=(RunlengthPacket *) malloc(image->columns*sizeof(RunlengthPacket)); if (scanline == (RunlengthPacket *) NULL) { Warning("Unable to reflect image","Memory allocation failed"); DestroyImage(flipped_image); return((Image *) NULL); } /* Flip each row. */ p=image->pixels; image->runlength=p->length+1; q=flipped_image->pixels+flipped_image->packets-1; for (y=0; y < flipped_image->rows; y++) { /* Read a scan line. */ s=scanline; for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } *s=(*p); s++; } /* Flip each column. */ s=scanline+image->columns; for (x=0; x < flipped_image->columns; x++) { s--; *q=(*s); q->length=0; q--; } ProgressMonitor(FlipImageText,y,flipped_image->rows); } free((char *) scanline); return(flipped_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % F l o p I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function FlopImage creates a new image that reflects each scanline in the % horizontal direction It allocates the memory necessary for the new Image % structure and returns a pointer to the new image. % % The format of the FlopImage routine is: % % flopped_image=FlopImage(image) % % A description of each parameter follows: % % o flopped_image: Function FlopImage returns a pointer to the image % after reflecting. A null image is returned if there is a memory % shortage. % % o image: The address of a structure of type Image. % % */ Image *FlopImage(Image *image) { #define FlopImageText " Flopping image... " Image *flopped_image; register RunlengthPacket *p, *q, *s; register unsigned int x, y; RunlengthPacket *scanline; /* Initialize flopped image attributes. */ assert(image != (Image *) NULL); flopped_image=CloneImage(image,image->columns,image->rows,False); if (flopped_image == (Image *) NULL) { Warning("Unable to reflect image","Memory allocation failed"); return((Image *) NULL); } /* Allocate scan line buffer and column offset buffers. */ scanline=(RunlengthPacket *) malloc(image->columns*sizeof(RunlengthPacket)); if (scanline == (RunlengthPacket *) NULL) { Warning("Unable to reflect image","Memory allocation failed"); DestroyImage(flopped_image); return((Image *) NULL); } /* Flop each row. */ p=image->pixels; image->runlength=p->length+1; q=flopped_image->pixels; for (y=0; y < flopped_image->rows; y++) { /* Read a scan line. */ s=scanline; for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } *s=(*p); s++; } /* Flop each column. */ s=scanline+image->columns; for (x=0; x < flopped_image->columns; x++) { s--; *q=(*s); q->length=0; q++; } ProgressMonitor(FlopImageText,y,flopped_image->rows); } free((char *) scanline); return(flopped_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % F r a m e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function FrameImage takes an image and puts a frame around it of a % particular color. It allocates the memory necessary for the new Image % structure and returns a pointer to the new image. % % The format of the FrameImage routine is: % % framed_image=FrameImage(image,frame_info) % % A description of each parameter follows: % % o framed_image: Function FrameImage returns a pointer to the framed % image. A null image is returned if there is a a memory shortage. % % o image: The address of a structure of type Image. % % o frame_info: Specifies a pointer to a FrameInfo structure which % defines the framed region. % % */ Image *FrameImage(Image *image,FrameInfo *frame_info) { #define FrameImageText " Adding frame to image... " Image *framed_image; int height, width; register int x, y; register RunlengthPacket *p, *q; RunlengthPacket accentuate, highlight, matte, shadow, trough; unsigned int bevel_width; /* Check frame geometry. */ assert(image != (Image *) NULL); assert(frame_info != (FrameInfo *) NULL); if ((frame_info->outer_bevel < 0) || (frame_info->inner_bevel < 0)) { Warning("Unable to frame image","bevel width is negative"); return((Image *) NULL); } bevel_width=frame_info->outer_bevel+frame_info->inner_bevel; width=(int) frame_info->width-frame_info->x-bevel_width; height=(int) frame_info->height-frame_info->y-bevel_width; if ((width < image->columns) || (height < image->rows)) { Warning("Unable to frame image","frame is less than image size"); return((Image *) NULL); } /* Initialize framed image attributes. */ framed_image=CloneImage(image,frame_info->width,frame_info->height,False); if (framed_image == (Image *) NULL) { Warning("Unable to frame image","Memory allocation failed"); return((Image *) NULL); } image->class=DirectClass; /* Initialize 3D effects color. */ matte.red=image->matte_color.red; matte.green=image->matte_color.green; matte.blue=image->matte_color.blue; matte.index=Opaque; matte.length=0; accentuate.red=(unsigned int) (matte.red*AccentuateModulate+ (MaxRGB-AccentuateModulate)*MaxRGB)/MaxRGB; accentuate.green=(unsigned int) (matte.green*AccentuateModulate+ (MaxRGB-AccentuateModulate)*MaxRGB)/MaxRGB; accentuate.blue=(unsigned int) (matte.blue*AccentuateModulate+ (MaxRGB-AccentuateModulate)*MaxRGB)/MaxRGB; accentuate.index=Opaque; accentuate.length=0; highlight.red=(unsigned int) (matte.red*HighlightModulate+ (MaxRGB-HighlightModulate)*MaxRGB)/MaxRGB; highlight.green=(unsigned int) (matte.green*HighlightModulate+ (MaxRGB-HighlightModulate)*MaxRGB)/MaxRGB; highlight.blue=(unsigned int) (matte.blue*HighlightModulate+ (MaxRGB-HighlightModulate)*MaxRGB)/MaxRGB; highlight.index=Opaque; highlight.length=0; shadow.red=(unsigned int) (matte.red*ShadowModulate)/MaxRGB; shadow.green=(unsigned int) (matte.green*ShadowModulate)/MaxRGB; shadow.blue=(unsigned int) (matte.blue*ShadowModulate)/MaxRGB; shadow.index=Opaque; shadow.length=0; trough.red=(unsigned int) (matte.red*TroughModulate)/MaxRGB; trough.green=(unsigned int) (matte.green*TroughModulate)/MaxRGB; trough.blue=(unsigned int) (matte.blue*TroughModulate)/MaxRGB; trough.index=Opaque; trough.length=0; /* Put an ornamental border around the image. */ q=framed_image->pixels; for (y=0; y < frame_info->outer_bevel; y++) { for (x=0; x < (int) (framed_image->columns-y); x++) if (x < y) *q++=highlight; else *q++=accentuate; for ( ; x < framed_image->columns; x++) *q++=shadow; } for (y=0; y < (int) (frame_info->y-bevel_width); y++) { for (x=0; x < frame_info->outer_bevel; x++) *q++=highlight; for (x=0; x < (int) (framed_image->columns-(frame_info->outer_bevel << 1)); x++) *q++=matte; for (x=0; x < frame_info->outer_bevel; x++) *q++=shadow; } for (y=0; y < frame_info->inner_bevel; y++) { for (x=0; x < frame_info->outer_bevel; x++) *q++=highlight; for (x=0; x < (int) (frame_info->x-bevel_width); x++) *q++=matte; for (x=0; x < (int) (image->columns+(frame_info->inner_bevel << 1)-y); x++) if (x < y) *q++=shadow; else *q++=trough; for ( ; x < (image->columns+(frame_info->inner_bevel << 1)); x++) *q++=highlight; width=frame_info->width-frame_info->x-image->columns-bevel_width; for (x=0; x < width; x++) *q++=matte; for (x=0; x < frame_info->outer_bevel; x++) *q++=shadow; } p=image->pixels; image->runlength=p->length+1; for (y=0; y < image->rows; y++) { /* Initialize scanline with border color. */ for (x=0; x < frame_info->outer_bevel; x++) *q++=highlight; for (x=0; x < (int) (frame_info->x-bevel_width); x++) *q++=matte; for (x=0; x < frame_info->inner_bevel; x++) *q++=shadow; /* Transfer scanline. */ for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } *q=(*p); q->length=0; q++; } for (x=0; x < frame_info->inner_bevel; x++) *q++=highlight; width=frame_info->width-frame_info->x-image->columns-bevel_width; for (x=0; x < width; x++) *q++=matte; for (x=0; x < frame_info->outer_bevel; x++) *q++=shadow; ProgressMonitor(FrameImageText,y,image->rows); } for (y=frame_info->inner_bevel-1; y >= 0; y--) { for (x=0; x < frame_info->outer_bevel; x++) *q++=highlight; for (x=0; x < (int) (frame_info->x-bevel_width); x++) *q++=matte; for (x=0; x < y; x++) *q++=shadow; for ( ; x < (image->columns+(frame_info->inner_bevel << 1)); x++) if (x >= (image->columns+(frame_info->inner_bevel << 1)-y)) *q++=highlight; else *q++=accentuate; width=frame_info->width-frame_info->x-image->columns-bevel_width; for (x=0; x < width; x++) *q++=matte; for (x=0; x < frame_info->outer_bevel; x++) *q++=shadow; } height=frame_info->height-frame_info->y-image->rows-bevel_width; for (y=0; y < height; y++) { for (x=0; x < frame_info->outer_bevel; x++) *q++=highlight; for (x=0; x < (int) (framed_image->columns-(frame_info->outer_bevel << 1)); x++) *q++=matte; for (x=0; x < frame_info->outer_bevel; x++) *q++=shadow; } for (y=frame_info->outer_bevel-1; y >= 0; y--) { for (x=0; x < y; x++) *q++=highlight; for ( ; x < framed_image->columns; x++) if (x >= (framed_image->columns-y)) *q++=shadow; else *q++=trough; } return(framed_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % G a m m a I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function GammaImage converts the reference image to gamma corrected colors. % % The format of the GammaImage routine is: % % GammaImage(image,gamma) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o gamma: A character string indicating the level of gamma correction. % % */ void GammaImage(Image *image,char *gamma) { #define GammaImageText " Gamma correcting the image... " ColorPacket *gamma_map; double blue_gamma, green_gamma, red_gamma; int count; register int i; register RunlengthPacket *p; assert(image != (Image *) NULL); if (gamma == (char *) NULL) return; red_gamma=1.0; green_gamma=1.0; blue_gamma=1.0; count=sscanf(gamma,"%lf,%lf,%lf",&red_gamma,&green_gamma,&blue_gamma); count=sscanf(gamma,"%lf/%lf/%lf",&red_gamma,&green_gamma,&blue_gamma); if (count == 1) { if (red_gamma == 1.0) return; green_gamma=red_gamma; blue_gamma=red_gamma; } /* Allocate and initialize gamma maps. */ gamma_map=(ColorPacket *) malloc((MaxRGB+1)*sizeof(ColorPacket)); if (gamma_map == (ColorPacket *) NULL) { Warning("Unable to gamma image","Memory allocation failed"); return; } for (i=0; i <= MaxRGB; i++) { gamma_map[i].red=0; gamma_map[i].green=0; gamma_map[i].blue=0; } /* Initialize gamma table. */ for (i=0; i <= MaxRGB; i++) { if (red_gamma != 0.0) gamma_map[i].red=(Quantum) ((pow((double) i/MaxRGB,1.0/red_gamma)*MaxRGB)+0.5); if (green_gamma != 0.0) gamma_map[i].green=(Quantum) ((pow((double) i/MaxRGB,1.0/green_gamma)*MaxRGB)+0.5); if (blue_gamma != 0.0) gamma_map[i].blue=(Quantum) ((pow((double) i/MaxRGB,1.0/blue_gamma)*MaxRGB)+0.5); } switch (image->class) { case DirectClass: default: { /* Gamma-correct DirectClass image. */ p=image->pixels; for (i=0; i < image->packets; i++) { p->red=gamma_map[p->red].red; p->green=gamma_map[p->green].green; p->blue=gamma_map[p->blue].blue; p++; if (QuantumTick(i,image)) ProgressMonitor(GammaImageText,i,image->packets); } break; } case PseudoClass: { /* Gamma-correct PseudoClass image. */ for (i=0; i < image->colors; i++) { image->colormap[i].red=gamma_map[image->colormap[i].red].red; image->colormap[i].green=gamma_map[image->colormap[i].green].green; image->colormap[i].blue=gamma_map[image->colormap[i].blue].blue; } SyncImage(image); break; } } if (image->gamma != 0.0) image->gamma*=(red_gamma+green_gamma+blue_gamma)/3.0; free((char *) gamma_map); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t A n n o t a t e I n f o % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function GetAnnotateInfo initializes the AnnotateInfo structure. % % The format of the GetAnnotateInfo routine is: % % GetAnnotateInfo(annotate_info) % % A description of each parameter follows: % % o annotate_info: Specifies a pointer to a AnnotateInfo structure. % % */ void GetAnnotateInfo(AnnotateInfo *annotate_info) { assert(annotate_info != (AnnotateInfo *) NULL); annotate_info->server_name=(char *) NULL; annotate_info->font=(char *) NULL; annotate_info->pointsize=atoi(DefaultPointSize); annotate_info->box=(char *) NULL; annotate_info->pen=(char *) NULL; annotate_info->geometry=(char *) NULL; annotate_info->text=(char *) NULL; annotate_info->primitive=(char *) NULL; annotate_info->linewidth=1; annotate_info->alignment=LeftAlignment; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t I m a g e I n f o % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function GetImageInfo initializes the ImageInfo structure. % % The format of the GetImageInfo routine is: % % GetImageInfo(image_info) % % A description of each parameter follows: % % o image_info: Specifies a pointer to a ImageInfo structure. % % */ Export void GetImageInfo(ImageInfo *image_info) { assert(image_info != (ImageInfo *) NULL); *image_info->magick='\0'; image_info->filename=(char *) malloc(MaxTextExtent); if (image_info->filename == (char *) NULL) Error("Unable to get image info","Memory allocation failed"); *image_info->filename='\0'; image_info->affirm=False; image_info->subimage=0; image_info->subrange=0; image_info->server_name=(char *) NULL; image_info->font=(char *) NULL; image_info->size=(char *) NULL; image_info->tile=(char *) NULL; image_info->density=(char *) NULL; image_info->page=(char *) NULL; image_info->dispose=(char *) NULL; image_info->delay=(char *) NULL; image_info->iterations=(char *) NULL; image_info->texture=(char *) NULL; image_info->adjoin=True; image_info->compression=RunlengthEncodedCompression; #ifdef HasPNG image_info->compression=ZipCompression; #endif image_info->dither=True; image_info->interlace=DefaultInterlace; image_info->monochrome=False; image_info->pointsize=atoi(DefaultPointSize); image_info->quality=atoi(DefaultImageQuality); image_info->verbose=False; image_info->preview_type=GammaPreview; image_info->undercolor=(char *) NULL; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % I s G e o m e t r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function IsGeometry returns True if the geometry specification is valid % as determined by XParseGeometry. % % The format of the IsGeometry routine is: % % status=IsGeometry(geometry) % % A description of each parameter follows: % % o status: Function IsGeometry returns True if the image is gray_scale % otherwise False is returned. % % o geometry: This string is the geometry specification. % % */ Export unsigned int IsGeometry(char *geometry) { float value; int x, y; unsigned int flags, height, width; if (geometry == (char *) NULL) return(NoValue); flags=XParseGeometry(geometry,&x,&y,&width,&height); return(flags || sscanf(geometry,"%f",&value)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % I s G r a y I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function IsGrayImage returns True if the image is gray_scale otherwise % False is returned. If the image is DirectClass and gray_scale, it is demoted % to PseudoClass. % % The format of the IsGrayImage routine is: % % status=IsGrayImage(image) % % A description of each parameter follows: % % o status: Function IsGrayImage returns True if the image is gray_scale % otherwise False is returned. % % o image: The address of a structure of type Image; returned from % ReadImage. % % */ unsigned int IsGrayImage(Image *image) { register int i; unsigned int gray_scale; /* Determine if image is gray_scale. */ assert(image != (Image *) NULL); gray_scale=True; switch (image->class) { case DirectClass: default: { register RunlengthPacket *p; if (image->matte) return(False); p=image->pixels; for (i=0; i < image->packets; i++) { if (!IsGray(*p)) { gray_scale=False; break; } p++; } if (gray_scale) { QuantizeInfo quantize_info; GetQuantizeInfo(&quantize_info); quantize_info.colorspace=GRAYColorspace; QuantizeImage(&quantize_info,image); SyncImage(image); } break; } case PseudoClass: { for (i=0; i < image->colors; i++) if (!IsGray(image->colormap[i])) { gray_scale=False; break; } break; } } return(gray_scale); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % L a b e l I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function LabelImage initializes an image label. Optionally the label % can include the image filename, type, width, height, or scene number by % embedding special format characters. % % The format of the LabelImage routine is: % % LabelImage(image,label) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % o label: The address of a character string containing the label format. % % */ void LabelImage(Image *image,char *label) { register char *p, *q; unsigned int indirection, length; assert(image != (Image *) NULL); if (image->label != (char *) NULL) free((char *) image->label); image->label=(char *) NULL; if (label == (char *) NULL) return; if (*label == '\0') return; indirection=(*label == '@'); if (indirection) { FILE *file; int c; /* Read label from a file. */ file=(FILE *) fopen(label+1,"r"); if (file == (FILE *) NULL) { Warning("Unable to read label file",label+1); return; } length=MaxTextExtent; label=(char *) malloc(length); for (q=label; label != (char *) NULL; q++) { c=fgetc(file); if (c == EOF) break; if ((q-label+1) >= length) { *q='\0'; length<<=1; label=(char *) realloc((char *) label,length); if (label == (char *) NULL) break; q=label+Extent(label); } *q=(unsigned char) c; } (void) fclose(file); if (label == (char *) NULL) { Warning("Unable to label image","Memory allocation failed"); return; } *q='\0'; } /* Allocate and initialize image label. */ p=label; length=Extent(label)+MaxTextExtent; image->label=(char *) malloc(length); for (q=image->label; image->label != (char *) NULL; p++) { *q='\0'; if (*p == '\0') break; if ((q-image->label+MaxTextExtent) >= length) { length<<=1; image->label=(char *) realloc((char *) image->label,length); if (image->label == (char *) NULL) break; q=image->label+Extent(image->label); } /* Process formatting characters in label. */ if ((*p == '\\') && (*(p+1) == 'n')) { *q++='\n'; p++; continue; } if (*p != '%') { *q++=(*p); continue; } p++; switch (*p) { case 'b': { if (image->filesize >= (1 << 24)) (void) sprintf(q,"%ldmb",image->filesize/1024/1024); else if (image->filesize >= (1 << 14)) (void) sprintf(q,"%ldkb",image->filesize/1024); else (void) sprintf(q,"%ldb",image->filesize); q=image->label+Extent(image->label); break; } case 'd': case 'e': case 'f': case 't': { char directory[MaxTextExtent], *extension, *filename; /* Label segment is the base of the filename. */ if (Extent(image->magick_filename) == 0) break; (void) strcpy(directory,image->magick_filename); extension=directory+Extent(directory); filename=extension; while ((filename > directory) && (*(filename-1) != *BasenameSeparator)) { if (*filename == '.') if (*extension == '\0') extension=filename+1; filename--; } switch (*p) { case 'd': { *filename='\0'; (void) strcpy(q,directory); q+=Extent(directory); break; } case 'e': { (void) strcpy(q,extension); q+=Extent(extension); break; } case 'f': { (void) strcpy(q,filename); q+=Extent(filename); break; } case 't': { *(extension-1)='\0'; (void) strcpy(q,filename); q+=Extent(filename); break; } } break; } case 'h': { (void) sprintf(q,"%u",image->magick_rows); q=image->label+Extent(image->label); break; } case 'm': { (void) strcpy(q,image->magick); q+=Extent(image->magick); break; } case 's': { (void) sprintf(q,"%u",image->scene); q=image->label+Extent(image->label); break; } case 'w': { (void) sprintf(q,"%u",image->magick_columns); q=image->label+Extent(image->label); break; } default: { *q++='%'; *q++=(*p); break; } } } if (image->label == (char *) NULL) { Warning("Unable to label image","Memory allocation failed"); return; } *q='\0'; if (indirection) free((char *) label); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % L i s t T o G r o u p I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ListToGroupImage converts a linked list of images to a sequential % array. % % The format of the ListToGroupImage routine is: % % images=ListToGroupImage(images,number_images) % % A description of each parameter follows: % % o images: Function ListToGroupImage converts a linked list of images to % a sequential array and returns the array.. % % o images: The address of a structure of type Image; returned from % ReadImage. % % o number_images: A pointer to an unsigned integer. The number of images % in the image array is returned here. % % */ Export Image **ListToGroupImage(Image *image,unsigned int *number_images) { Image **images, *next_image; register int i; /* Determine the number of images in the list. */ assert(image != (Image *) NULL); assert(number_images != (unsigned int *) NULL); next_image=image; for (i=0; next_image != (Image *) NULL; i++) next_image=next_image->next; images=(Image **) malloc(i*sizeof(Image *)); if (images == (Image **) NULL) { Warning("Unable to convert image list","Memory allocation failed"); return((Image **) NULL); } *number_images=i; /* Add each image in the linked list to the group. */ next_image=image; for (i=0; next_image != (Image *) NULL; i++) { images[i]=next_image; next_image=next_image->next; } return(images); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % M a g n i f y I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function MagnifyImage creates a new image that is a integral size greater % than an existing one. It allocates the memory necessary for the new Image % structure and returns a pointer to the new image. % % MagnifyImage scans the reference image to create a magnified image by % bilinear interpolation. The magnified image columns and rows become: % % number_columns << 1 % number_rows << 1 % % The format of the MagnifyImage routine is: % % magnified_image=MagnifyImage(image) % % A description of each parameter follows: % % o magnified_image: Function MagnifyImage returns a pointer to the image % after magnification. A null image is returned if there is a a memory % shortage. % % o image: The address of a structure of type Image. % % */ Image *MagnifyImage(Image *image) { #define MagnifyImageText " Magnifying the image... " Image *magnified_image; int y; register int x; register RunlengthPacket *p, *q, *r; /* Initialize magnified image attributes. */ assert(image != (Image *) NULL); magnified_image=CloneImage(image,image->columns << 1,image->rows << 1,False); if (magnified_image == (Image *) NULL) { Warning("Unable to zoom image","Memory allocation failed"); return((Image *) NULL); } magnified_image->class=DirectClass; /* Initialize zoom image pixels. */ p=image->pixels; image->runlength=p->length+1; q=magnified_image->pixels; for (y=0; y < image->rows; y++) { for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } *q=(*p); q->length=0; q++; } q+=image->columns; } /* Magnify each row. */ for (y=0; y < image->rows; y++) { p=magnified_image->pixels+(image->rows-1-y)*magnified_image->columns+ (image->columns-1); q=magnified_image->pixels+((image->rows-1-y) << 1)*magnified_image->columns+ ((image->columns-1) << 1); *q=(*p); *(q+1)=(*(p)); for (x=1; x < image->columns; x++) { p--; q-=2; *q=(*p); (q+1)->red=(((int) p->red)+((int) (p+1)->red)+1) >> 1; (q+1)->green=(((int) p->green)+((int) (p+1)->green)+1) >> 1; (q+1)->blue=(((int) p->blue)+((int) (p+1)->blue)+1) >> 1; (q+1)->index=(((int) p->index)+((int) (p+1)->index)+1) >> 1; (q+1)->length=0; } } for (y=0; y < (image->rows-1); y++) { p=magnified_image->pixels+(y << 1)*magnified_image->columns; q=p+magnified_image->columns; r=q+magnified_image->columns; for (x=0; x < (image->columns-1); x++) { q->red=(((int) p->red)+((int) r->red)+1) >> 1; q->green=(((int) p->green)+((int) r->green)+1) >> 1; q->blue=(((int) p->blue)+((int) r->blue)+1) >> 1; q->index=(((int) p->index)+((int) r->index)+1) >> 1; q->length=0; (q+1)->red=(((int) p->red)+((int) (p+2)->red)+((int) r->red)+ ((int) (r+2)->red)+2) >> 2; (q+1)->green=(((int) p->green)+((int) (p+2)->green)+((int) r->green)+ ((int) (r+2)->green)+2) >> 2; (q+1)->blue=(((int) p->blue)+((int) (p+2)->blue)+((int) r->blue)+ ((int) (r+2)->blue)+2) >> 2; (q+1)->index=(((int) p->index)+((int) (p+2)->index)+((int) r->index)+ ((int) (r+2)->index)+2) >> 2; (q+1)->length=0; q+=2; p+=2; r+=2; } q->red=(((int) p->red)+((int) r->red)+1) >> 1; q->green=(((int) p->green)+((int) r->green)+1) >> 1; q->blue=(((int) p->blue)+((int) r->blue)+1) >> 1; q->index=(((int) p->index)+((int) r->index)+1) >> 1; q->length=0; p++; q++; r++; q->red=(((int) p->red)+((int) r->red)+1) >> 1; q->green=(((int) p->green)+((int) r->green)+1) >> 1; q->blue=(((int) p->blue)+((int) r->blue)+1) >> 1; q->index=(((int) p->index)+((int) r->index)+1) >> 1; q->length=0; p++; q++; r++; ProgressMonitor(MagnifyImageText,y,image->rows); } p=magnified_image->pixels+(2*image->rows-2)*magnified_image->columns; q=magnified_image->pixels+(2*image->rows-1)*magnified_image->columns; for (x=0; x < image->columns; x++) { *q++=(*p++); *q++=(*p++); } return(magnified_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % M a t t e F l o o d f i l l I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function MatteFloodfillImage floodfills the designated area with a matte % value. The floodfill algorithm is strongly based on a similiar algorithm in % "Graphics Gems" by Paul Heckbert. % % The format of the MatteFloodfillImage routine is: % % MatteFloodfillImage(image,x,y,matte,delta) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % o x,y: Unsigned integers representing the current location of the pen. % % o matte: A integer value representing the amount of transparency. % % o delta: This is the allowed variance in color (fuzzy color). % % */ Export void MatteFloodfillImage(Image *image,int x,int y, const unsigned int matte,const int delta) { int offset, skip, start, x1, x2; register RunlengthPacket *pixel; register XSegment *p; RunlengthPacket target; XSegment *segment_stack; /* Check boundary conditions. */ assert(image != (Image *) NULL); if ((y < 0) || (y >= image->rows)) return; if ((x < 0) || (x >= image->columns)) return; target=image->pixels[y*image->columns+x]; if (target.index == (unsigned short) matte) return; /* Allocate segment stack. */ segment_stack=(XSegment *) malloc(MaxStacksize*sizeof(XSegment)); if (segment_stack == (XSegment *) NULL) { Warning("Unable to floodfill","Memory allocation failed"); return; } /* Push initial segment on stack. */ start=0; p=segment_stack; Push(y,x,x,1); Push(y+1,x,x,-1); while (p > segment_stack) { /* Pop segment off stack. */ p--; x1=p->x1; x2=p->x2; offset=p->y2; y=p->y1+offset; /* Update matte information in neighboring pixels. */ for (x=x1; x >= 0 ; x--) { pixel=image->pixels+(y*image->columns+x); if (!MatteMatch(*pixel,target,delta)) break; pixel->index=(unsigned short) matte; } skip=x >= x1; if (!skip) { start=x+1; if (start < x1) Push(y,start,x1-1,-offset); x=x1+1; } do { if (!skip) { for ( ; x < image->columns; x++) { pixel=image->pixels+(y*image->columns+x); if (!MatteMatch(*pixel,target,delta)) break; pixel->index=(unsigned short) matte; } Push(y,start,x-1,offset); if (x > (x2+1)) Push(y,x2+1,x-1,-offset); } skip=False; for (x++; x <= x2 ; x++) { pixel=image->pixels+(y*image->columns+x); if (MatteMatch(*pixel,target,delta)) break; } start=x; } while (x <= x2); } free((char *) segment_stack); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % M i n i f y I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function MinifyImage creates a new image that is a integral size less than % an existing one. It allocates the memory necessary for the new Image % structure and returns a pointer to the new image. % % MinifyImage scans the reference image to create a minified image by computing % the weighted average of a 4x4 cell centered at each reference pixel. The % target pixel requires two columns and two rows of the reference pixels. % Therefore the minified image columns and rows become: % % number_columns/2 % number_rows/2 % % Weights assume that the importance of neighboring pixels is negately % proportional to the square of their distance from the target pixel. % % The scan only processes pixels that have a full set of neighbors. Pixels % in the top, bottom, left, and right pairs of rows and columns are omitted % from the scan. % % The format of the MinifyImage routine is: % % minified_image=MinifyImage(image) % % A description of each parameter follows: % % o minified_image: Function MinifyImage returns a pointer to the image % after reducing. A null image is returned if there is a a memory % shortage or if the image size is less than IconSize*2. % % o image: The address of a structure of type Image. % % */ Image *MinifyImage(Image *image) { #define Minify(weight) \ total_red+=(weight)*(s->red); \ total_green+=(weight)*(s->green); \ total_blue+=(weight)*(s->blue); \ total_matte+=(weight)*(s->index); \ s++; #define MinifyImageText " Minifying image... " Image *minified_image; register RunlengthPacket *p, *q, *s, *s0, *s1, *s2, *s3; register unsigned int x; RunlengthPacket *scanline; unsigned int y; unsigned int blue, green, packets, red; unsigned long total_matte, total_blue, total_green, total_red; unsigned short index; assert(image != (Image *) NULL); if ((image->columns < 4) || (image->rows < 4)) { Warning("Unable to reduce image","image size must exceed 3x3"); return((Image *) NULL); } /* Initialize minified image attributes. */ packets=Max(image->packets >> 2,1); minified_image=CloneImage(image,packets,1,False); if (minified_image == (Image *) NULL) { Warning("Unable to reduce image","Memory allocation failed"); return((Image *) NULL); } minified_image->class=DirectClass; minified_image->columns=image->columns >> 1; minified_image->rows=image->rows >> 1; minified_image->packets=0; /* Allocate image buffer and scanline buffer for 4 rows of the image. */ scanline=(RunlengthPacket *) malloc(4*(image->columns+1)*sizeof(RunlengthPacket)); if (scanline == (RunlengthPacket *) NULL) { Warning("Unable to reduce image","Memory allocation failed"); DestroyImage(minified_image); return((Image *) NULL); } /* Preload the first 2 rows of the image. */ p=image->pixels; image->runlength=p->length+1; for (x=0; x < (4*(image->columns+1)); x++) scanline[x]=(*p); s=scanline; for (x=0; x < (image->columns << 1); x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } *s=(*p); s++; } /* Reduce each row. */ p=image->pixels; image->runlength=p->length+1; q=minified_image->pixels; q->red=0; q->green=0; q->blue=0; q->index=0; q->length=MaxRunlength; for (y=0; y < (image->rows-1); y+=2) { /* Initialize sliding window pointers. */ s0=scanline+image->columns*((y+0) % 4); s1=scanline+image->columns*((y+1) % 4); s2=scanline+image->columns*((y+2) % 4); s3=scanline+image->columns*((y+3) % 4); /* Read another scan line. */ s=s2; for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } *s=(*p); s++; } /* Read another scan line. */ s=s3; for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } *s=(*p); s++; } for (x=0; x < (image->columns-1); x+=2) { /* Compute weighted average of target pixel color components. These particular coefficients total to 128. Use 128/2-1 or 63 to insure correct round off. */ total_red=0; total_green=0; total_blue=0; total_matte=0; s=s0; Minify(3); Minify(7); Minify(7); Minify(3); s=s1; Minify(7); Minify(15); Minify(15); Minify(7); s=s2; Minify(7); Minify(15); Minify(15); Minify(7); s=s3; Minify(3); Minify(7); Minify(7); Minify(3); s0+=2; s1+=2; s2+=2; s3+=2; red=(Quantum) ((total_red+63) >> 7); green=(Quantum) ((total_green+63) >> 7); blue=(Quantum) ((total_blue+63) >> 7); index=(unsigned short) ((total_matte+63) >> 7); if ((red == q->red) && (green == q->green) && (blue == q->blue) && (index == q->index) && ((int) q->length < MaxRunlength)) q->length++; else { if (minified_image->packets != 0) q++; minified_image->packets++; if (minified_image->packets == packets) { packets<<=1; minified_image->pixels=(RunlengthPacket *) realloc((char *) minified_image->pixels,packets*sizeof(RunlengthPacket)); if (minified_image->pixels == (RunlengthPacket *) NULL) { Warning("Unable to reduce image","Memory allocation failed"); DestroyImage(minified_image); return((Image *) NULL); } q=minified_image->pixels+minified_image->packets-1; } q->red=red; q->green=green; q->blue=blue; q->index=index; q->length=0; } } ProgressMonitor(MinifyImageText,y,image->rows-1); } minified_image->pixels=(RunlengthPacket *) realloc((char *) minified_image->pixels,minified_image->packets*sizeof(RunlengthPacket)); free((char *) scanline); return(minified_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % M o d u l a t e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ModulateImage modulates the hue, saturation, and brightness of an % image. % % The format of the ModulateImage routine is: % % ModulateImage(image,modulate) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o modulate: A character string indicating the percent change in hue, % saturation, and brightness. % % */ void ModulateImage(Image *image,char *modulate) { #define ModulateImageText " Modulating image... " double percent_brightness, percent_hue, percent_saturation; register int i; register RunlengthPacket *p; /* Initialize gamma table. */ assert(image != (Image *) NULL); if (modulate == (char *) NULL) return; percent_hue=0.0; percent_brightness=0.0; percent_saturation=0.0; (void) sscanf(modulate,"%lf,%lf,%lf",&percent_brightness,&percent_saturation, &percent_hue); (void) sscanf(modulate,"%lf/%lf/%lf",&percent_brightness,&percent_saturation, &percent_hue); switch (image->class) { case DirectClass: default: { /* Modulate the color for a DirectClass image. */ p=image->pixels; for (i=0; i < image->packets; i++) { Modulate(percent_hue,percent_saturation,percent_brightness, &p->red,&p->green,&p->blue); p++; if (QuantumTick(i,image)) ProgressMonitor(ModulateImageText,i,image->packets); } break; } case PseudoClass: { /* Modulate the color for a PseudoClass image. */ for (i=0; i < image->colors; i++) Modulate(percent_hue,percent_saturation,percent_brightness, &image->colormap[i].red,&image->colormap[i].green, &image->colormap[i].blue); SyncImage(image); break; } } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % M o g r i f y I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function MogrifyImage applies image processing options to an image as % prescribed by command line options. % % The format of the MogrifyImage routine is: % % MogrifyImage(image_info,argc,argv,image) % % A description of each parameter follows: % % o image_info: Specifies a pointer to a ImageInfo structure. % % o argc: Specifies a pointer to an integer describing the number of % elements in the argument vector. % % o argv: Specifies a pointer to a text array containing the command line % arguments. % % o image: The address of a structure of type Image; returned from % ReadImage. % % */ Export void MogrifyImage(ImageInfo *image_info,int argc,char **argv, Image **image) { AnnotateInfo annotate_info; char *option; Image *region_image; int flags, x, y; QuantizeInfo quantize_info; register int i; unsigned int height, width; XColor target_color; /* Initialize routine variables. */ assert(image_info != (ImageInfo *) NULL); assert(image != (Image **) NULL); GetAnnotateInfo(&annotate_info); GetQuantizeInfo(&quantize_info); quantize_info.number_colors=0; quantize_info.tree_depth=0; quantize_info.dither=True; if (image_info->monochrome) if (!IsMonochromeImage(*image)) { quantize_info.number_colors=2; quantize_info.tree_depth=8; quantize_info.colorspace=GRAYColorspace; } region_image=(Image *) NULL; /* Transmogrify the image. */ for (i=1; i < argc; i++) { option=argv[i]; if ((Extent(option) <= 1) || ((*option != '-') && (*option != '+'))) continue; if (strncmp("-blur",option,4) == 0) { double factor; Image *blurred_image; /* Blur an image. */ factor=atof(argv[++i]); blurred_image=BlurImage(*image,factor); if (blurred_image != (Image *) NULL) { DestroyImage(*image); *image=blurred_image; } continue; } if (strcmp("-border",option) == 0) { Image *bordered_image; RectangleInfo border_info; /* Surround image with a border of solid color. */ border_info.width=0; border_info.height=0; flags=XParseGeometry(argv[++i],&border_info.x,&border_info.y, &border_info.width,&border_info.height); if ((flags & HeightValue) == 0) border_info.height=border_info.width; bordered_image=BorderImage(*image,&border_info); if (bordered_image != (Image *) NULL) { DestroyImage(*image); bordered_image->class=DirectClass; *image=bordered_image; } continue; } if (strncmp("-bordercolor",option,8) == 0) { /* Determine RGB values of the border color. */ (void) XQueryColorDatabase(argv[++i],&target_color); (*image)->border_color.red=XDownScale(target_color.red); (*image)->border_color.green=XDownScale(target_color.green); (*image)->border_color.blue=XDownScale(target_color.blue); continue; } if (strncmp("-box",option,3) == 0) { annotate_info.box=argv[++i]; continue; } if (strncmp("-charcoal",option,3) == 0) { char *commands[8]; QuantizeInfo local_info; /* Charcoal drawing. */ i++; GetQuantizeInfo(&local_info); local_info.dither=quantize_info.dither; local_info.colorspace=GRAYColorspace; QuantizeImage(&local_info,*image); SyncImage(*image); commands[0]=client_name; commands[1]="-edge"; commands[2]=argv[i]; commands[3]="-blur"; commands[4]=argv[i]; commands[5]="-normalize"; commands[6]="-negate"; commands[7]="-grayscale"; MogrifyImage(image_info,8,commands,image); continue; } if (strncmp("-colorize",option,8) == 0) { ColorizeImage(*image,argv[++i],annotate_info.pen); continue; } if (strcmp("-colors",option) == 0) { quantize_info.number_colors=atoi(argv[++i]); continue; } if (strncmp("-colorspace",option,8) == 0) { i++; option=argv[i]; if (Latin1Compare("gray",option) == 0) { quantize_info.colorspace=GRAYColorspace; if (quantize_info.number_colors == 0) quantize_info.number_colors=256; quantize_info.tree_depth=8; } if (Latin1Compare("ohta",option) == 0) quantize_info.colorspace=OHTAColorspace; if (Latin1Compare("rgb",option) == 0) quantize_info.colorspace=RGBColorspace; if (Latin1Compare("transparent",option) == 0) quantize_info.colorspace=TransparentColorspace; if (Latin1Compare("xyz",option) == 0) quantize_info.colorspace=XYZColorspace; if (Latin1Compare("ycbcr",option) == 0) quantize_info.colorspace=YCbCrColorspace; if (Latin1Compare("yiq",option) == 0) quantize_info.colorspace=YIQColorspace; if (Latin1Compare("ypbpr",option) == 0) quantize_info.colorspace=YPbPrColorspace; if (Latin1Compare("yuv",option) == 0) quantize_info.colorspace=YUVColorspace; continue; } if (strncmp("comment",option+1,4) == 0) { if (*option == '-') CommentImage(*image,argv[++i]); else CommentImage(*image,(char *) NULL); continue; } if (strncmp("contrast",option+1,3) == 0) { ContrastImage(*image,(unsigned int) (*option == '-')); continue; } if (strncmp("-crop",option,3) == 0) { TransformImage(image,argv[++i],(char *) NULL); continue; } if (strncmp("-cycle",option,3) == 0) { /* Cycle an image colormap. */ CycleColormapImage(*image,atoi(argv[++i])); continue; } if (strncmp("delay",option+1,3) == 0) { (*image)->delay=atoi(argv[++i]); continue; } if (strncmp("density",option+1,3) == 0) { int count; /* Set image density. */ count=sscanf(argv[++i],"%fx%f",&(*image)->x_resolution, &(*image)->y_resolution); if (count == 1) (*image)->y_resolution=(*image)->x_resolution; continue; } if (strncmp("-despeckle",option,4) == 0) { Image *despeckled_image; /* Reduce the speckles within an image. */ despeckled_image=DespeckleImage(*image); if (despeckled_image != (Image *) NULL) { DestroyImage(*image); *image=despeckled_image; } continue; } if (strncmp("-display",option,6) == 0) { annotate_info.server_name=argv[++i]; image_info->server_name=argv[i]; continue; } if (strncmp("dispose",option+1,5) == 0) { (*image)->dispose=atoi(argv[++i]); continue; } if (strncmp("dither",option+1,3) == 0) { quantize_info.dither=(*option == '-'); continue; } if (strncmp("-draw",option,3) == 0) { annotate_info.primitive=argv[++i]; DrawImage(*image,&annotate_info); continue; } if (strncmp("-edge",option,3) == 0) { double factor; Image *edged_image; /* Detect edges in the image. */ factor=atof(argv[++i]); edged_image=EdgeImage(*image,factor); if (edged_image != (Image *) NULL) { DestroyImage(*image); *image=edged_image; } continue; } if (strncmp("-emboss",option,3) == 0) { Image *embossed_image; /* Emboss image. */ embossed_image=EmbossImage(*image); if (embossed_image != (Image *) NULL) { DestroyImage(*image); *image=embossed_image; } continue; } if (strncmp("-enhance",option,3) == 0) { Image *enhanced_image; /* Enhance image. */ enhanced_image=EnhanceImage(*image); if (enhanced_image != (Image *) NULL) { DestroyImage(*image); *image=enhanced_image; } continue; } if (strncmp("-equalize",option,3) == 0) { EqualizeImage(*image); continue; } if (strncmp("-flip",option,4) == 0) { Image *flipped_image; /* Flip image scanlines. */ flipped_image=FlipImage(*image); if (flipped_image != (Image *) NULL) { DestroyImage(*image); *image=flipped_image; } continue; } if (strncmp("-flop",option,4) == 0) { Image *flopped_image; /* Flop image scanlines. */ flopped_image=FlopImage(*image); if (flopped_image != (Image *) NULL) { DestroyImage(*image); *image=flopped_image; } continue; } if (strncmp("-font",option,3) == 0) { annotate_info.font=argv[++i]; continue; } if (strcmp("-frame",option) == 0) { Image *framed_image; FrameInfo frame_info; /* Surround image with an ornamental border. */ frame_info.width=0; frame_info.height=0; flags=XParseGeometry(argv[++i],&frame_info.outer_bevel, &frame_info.inner_bevel,&frame_info.width,&frame_info.height); if ((flags & HeightValue) == 0) frame_info.height=frame_info.width; if ((flags & XValue) == 0) frame_info.outer_bevel=(frame_info.width >> 2)+1; if ((flags & YValue) == 0) frame_info.inner_bevel=frame_info.outer_bevel; frame_info.x=frame_info.width; frame_info.y=frame_info.height; frame_info.width=(*image)->columns+(frame_info.width << 1); frame_info.height=(*image)->rows+(frame_info.height << 1); framed_image=FrameImage(*image,&frame_info); if (framed_image != (Image *) NULL) { DestroyImage(*image); framed_image->class=DirectClass; *image=framed_image; } continue; } if (strncmp("-gamma",option,3) == 0) { GammaImage(*image,argv[++i]); continue; } if (strncmp("-geometry",option,4) == 0) { TransformImage(image,(char *) NULL,argv[++i]); annotate_info.geometry=argv[i]; continue; } if (strncmp("-implode",option,4) == 0) { double amount; Image *imploded_image; /* Implode image. */ amount=atof(argv[++i]); imploded_image=ImplodeImage(*image,amount); if (imploded_image != (Image *) NULL) { DestroyImage(*image); *image=imploded_image; } continue; } if (strncmp("interlace",option+1,3) == 0) { image_info->interlace=NoneInterlace; if (*option == '-') { option=argv[++i]; if (Latin1Compare("none",option) == 0) image_info->interlace=NoneInterlace; if (Latin1Compare("line",option) == 0) image_info->interlace=LineInterlace; if (Latin1Compare("plane",option) == 0) image_info->interlace=PlaneInterlace; if (Latin1Compare("partition",option) == 0) image_info->interlace=PartitionInterlace; } continue; } if (strncmp("label",option+1,2) == 0) { if (*option == '-') LabelImage(*image,argv[++i]); else LabelImage(*image,(char *) NULL); continue; } if (strncmp("-linewidth",option,3) == 0) { annotate_info.linewidth=atoi(argv[++i]); continue; } if (strncmp("loop",option+1,2) == 0) { (*image)->iterations=atoi(argv[++i]); continue; } if (strcmp("-map",option) == 0) { Image *map_image; ImageInfo local_info; /* Transform image colors to match this set of colors. */ local_info=(*image_info); (void) strcpy(local_info.filename,argv[++i]); map_image=ReadImage(&local_info); if (map_image != (Image *) NULL) { MapImage(*image,map_image,local_info.dither); DestroyImage(map_image); } continue; } if (strcmp("matte",option+1) == 0) { (*image)->matte=(*option == '-'); continue; } if (strncmp("-mattecolor",option,7) == 0) { /* Determine RGB values of the border color. */ (void) XQueryColorDatabase(argv[++i],&target_color); (*image)->matte_color.red=XDownScale(target_color.red); (*image)->matte_color.green=XDownScale(target_color.green); (*image)->matte_color.blue=XDownScale(target_color.blue); continue; } if (strncmp("-modulate",option,4) == 0) { ModulateImage(*image,argv[++i]); continue; } if (strncmp("-monochrome",option,4) == 0) { quantize_info.number_colors=2; quantize_info.tree_depth=8; quantize_info.colorspace=GRAYColorspace; continue; } if (strncmp("negate",option+1,3) == 0) { NegateImage(*image,*option == '+'); continue; } if (strncmp("noise",option+1,4) == 0) { Image *noisy_image; /* Reduce noise in image. */ if (*option == '-') noisy_image=ReduceNoiseImage(*image); else { NoiseType noise_type; option=argv[++i]; noise_type=UniformNoise; if (Latin1Compare("gaussian",option) == 0) noise_type=GaussianNoise; if (Latin1Compare("multiplicative",option) == 0) noise_type=MultiplicativeGaussianNoise; if (Latin1Compare("impulse",option) == 0) noise_type=ImpulseNoise; if (Latin1Compare("laplacian",option) == 0) noise_type=LaplacianNoise; if (Latin1Compare("poisson",option) == 0) noise_type=PoissonNoise; noisy_image=AddNoiseImage(*image,noise_type); } if (noisy_image != (Image *) NULL) { DestroyImage(*image); *image=noisy_image; } continue; } if (strncmp("-normalize",option,4) == 0) { NormalizeImage(*image); continue; } if (strncmp("-opaque",option,3) == 0) { OpaqueImage(*image,argv[++i],annotate_info.pen); continue; } if (strncmp("page",option+1,3) == 0) { if ((*image)->page != (char *) NULL) free((char *) (*image)->page); (*image)->page=(char *) NULL; if (*option == '+') continue; (*image)->page=PostscriptGeometry(argv[++i]); continue; } if (strncmp("-paint",option,4) == 0) { Image *painted_image; /* Oil paint image. */ painted_image=OilPaintImage(*image,atoi(argv[++i])); if (painted_image != (Image *) NULL) { DestroyImage(*image); *image=painted_image; } continue; } if (strcmp("-pen",option) == 0) { annotate_info.pen=argv[++i]; continue; } if (strncmp("-pointsize",option,3) == 0) { annotate_info.pointsize=atoi(argv[++i]); continue; } if (strncmp("-normalize",option,4) == 0) { NormalizeImage(*image); continue; } if (strncmp("raise",option+1,2) == 0) { RectangleInfo raise_info; /* Surround image with a raise of solid color. */ raise_info.width=0; raise_info.height=0; flags=XParseGeometry(argv[++i],&raise_info.x,&raise_info.y, &raise_info.width,&raise_info.height); if ((flags & HeightValue) == 0) raise_info.height=raise_info.width; RaiseImage(*image,&raise_info,*option == '-'); continue; } if (strncmp("region",option+1,2) == 0) { if (region_image != (Image *) NULL) { /* Composite region. */ XParseGeometry(region_image->geometry,&x,&y,&width,&height); CompositeImage(region_image,ReplaceCompositeOp,*image,x,y); DestroyImage(*image); *image=region_image; } if (*option == '+') continue; region_image=CloneImage(*image,(*image)->columns,(*image)->rows,True); if (region_image == (Image *) NULL) continue; region_image->geometry=argv[++i]; TransformImage(image,region_image->geometry,(char *) NULL); continue; } if (strncmp("-roll",option,4) == 0) { Image *rolled_image; /* Roll image. */ x=0; y=0; flags=XParseGeometry(argv[++i],&x,&y,&width,&height); rolled_image=RollImage(*image,x,y); if (rolled_image != (Image *) NULL) { DestroyImage(*image); *image=rolled_image; } continue; } if (strncmp("-rotate",option,4) == 0) { double degrees; Image *rotated_image; /* Check for conditional image rotation. */ i++; if (strchr(argv[i],'>') != (char *) NULL) if ((*image)->columns <= (*image)->rows) break; if (strchr(argv[i],'<') != (char *) NULL) if ((*image)->columns >= (*image)->rows) break; /* Rotate image. */ degrees=atof(argv[i]); rotated_image=RotateImage(*image,degrees,False,True); if (rotated_image != (Image *) NULL) { DestroyImage(*image); *image=rotated_image; } continue; } if (strncmp("-sample",option,3) == 0) { Image *sampled_image; /* Sample image with pixel replication. */ width=(*image)->columns; height=(*image)->rows; (void) ParseImageGeometry(argv[++i],&x,&y,&width,&height); sampled_image=SampleImage(*image,width,height); if (sampled_image != (Image *) NULL) { DestroyImage(*image); *image=sampled_image; } continue; } if (strncmp("sans",option+1,2) == 0) if (*option == '-') i++; if (strcmp("-scene",option) == 0) { (*image)->scene=atoi(argv[++i]); continue; } if (strncmp("-segment",option,4) == 0) { float cluster_threshold, smoothing_threshold; /* Segment image. */ cluster_threshold=1.0; smoothing_threshold=1.5; (void) sscanf(argv[++i],"%fx%f",&cluster_threshold, &smoothing_threshold); SegmentImage(*image,quantize_info.colorspace,image_info->verbose, (double) cluster_threshold,(double) smoothing_threshold); SyncImage(*image); continue; } if (strncmp("shade",option+1,5) == 0) { float azimuth, elevation; Image *shaded_image; /* Shade image. */ azimuth=30.0; elevation=30.0; if (*option == '-') (void) sscanf(argv[++i],"%fx%f",&azimuth,&elevation); shaded_image=ShadeImage(*image,*option == '-',(double) azimuth, (double) elevation); if (shaded_image != (Image *) NULL) { DestroyImage(*image); *image=shaded_image; } continue; } if (strncmp("-sharpen",option,5) == 0) { double factor; Image *sharpened_image; /* Sharpen an image. */ factor=atof(argv[++i]); sharpened_image=SharpenImage(*image,factor); if (sharpened_image != (Image *) NULL) { DestroyImage(*image); *image=sharpened_image; } continue; } if (strncmp("-shear",option,4) == 0) { float x_shear, y_shear; Image *sheared_image; /* Shear image. */ x_shear=0.0; y_shear=0.0; (void) sscanf(argv[++i],"%fx%f",&x_shear,&y_shear); sheared_image= ShearImage(*image,(double) x_shear,(double) y_shear,False); if (sheared_image != (Image *) NULL) { DestroyImage(*image); sheared_image->class=DirectClass; *image=sheared_image; } continue; } if (strncmp("-solarize",option,3) == 0) { SolarizeImage(*image,atof(argv[++i])); continue; } if (strncmp("-spread",option,3) == 0) { unsigned int amount; Image *spread_image; /* Spread an image. */ amount=atoi(argv[++i]); spread_image=SpreadImage(*image,amount); if (spread_image != (Image *) NULL) { DestroyImage(*image); *image=spread_image; } continue; } if (strncmp("-swirl",option,3) == 0) { double degrees; Image *swirled_image; /* Swirl image. */ degrees=atof(argv[++i]); swirled_image=SwirlImage(*image,degrees); if (swirled_image != (Image *) NULL) { DestroyImage(*image); *image=swirled_image; } continue; } if (strncmp("-threshold",option,3) == 0) { double threshold; /* Threshold image. */ threshold=atof(argv[++i]); ThresholdImage(*image,threshold); continue; } if (strncmp("-transparent",option,4) == 0) { TransparentImage(*image,argv[++i]); continue; } if (strncmp("-treedepth",option,4) == 0) { quantize_info.tree_depth=atoi(argv[++i]); continue; } if (strcmp("wave",option+1) == 0) { float amplitude, wavelength; Image *waved_image; /* Wave image. */ amplitude=10.0; wavelength=10.0; if (*option == '-') (void) sscanf(argv[++i],"%fx%f",&litude,&wavelength); waved_image=WaveImage(*image,(double) amplitude,(double) wavelength); if (waved_image != (Image *) NULL) { DestroyImage(*image); *image=waved_image; } continue; } } if (quantize_info.number_colors != 0) { /* Reduce the number of colors in the image. */ if (((*image)->class == DirectClass) || ((*image)->colors > quantize_info.number_colors) || (quantize_info.colorspace == GRAYColorspace)) QuantizeImage(&quantize_info,*image); /* Measure quantization error. */ if (image_info->verbose) QuantizationError(*image); SyncImage(*image); } if (region_image != (Image *) NULL) { /* Composite region. */ XParseGeometry(region_image->geometry,&x,&y,&width,&height); CompositeImage(region_image,ReplaceCompositeOp,*image,x,y); DestroyImage(*image); *image=region_image; } if ((*image)->packets == ((*image)->columns*(*image)->rows)) CompressImage(*image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % M o g r i f y I m a g e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function MogrifyImages applies image processing options to a sequence of % images as prescribed by command line options. % % The format of the MogrifyImage routine is: % % MogrifyImages(image_info,argc,argv,images) % % A description of each parameter follows: % % o image_info: Specifies a pointer to a ImageInfo structure. % % o argc: Specifies a pointer to an integer describing the number of % elements in the argument vector. % % o argv: Specifies a pointer to a text array containing the command line % arguments. % % o images: The address of a structure of type Image; returned from % ReadImage. % % */ Export void MogrifyImages(ImageInfo *image_info,int argc,char **argv, Image **images) { #define MogrifyImageText " Transforming images... " Image *image, *mogrify_image; register int i; MonitorHandler handler; unsigned int number_images; assert(image_info != (ImageInfo *) NULL); assert(images != (Image **) NULL); image=(*images); for (number_images=1; image->next != (Image *) NULL; number_images++) image=image->next; ProgressMonitor(MogrifyImageText,0,number_images); handler=SetMonitorHandler((MonitorHandler) NULL); MogrifyImage(image_info,argc,argv,images); (void) SetMonitorHandler(handler); image=(*images); mogrify_image=(*images)->next; if (image_info->verbose) DescribeImage(image,stderr,False); for (i=1; mogrify_image != (Image *) NULL; i++) { handler=SetMonitorHandler((MonitorHandler) NULL); MogrifyImage(image_info,argc,argv,&mogrify_image); image->next=mogrify_image; image->next->previous=image; image=image->next; if (image_info->verbose) DescribeImage(mogrify_image,stderr,False); mogrify_image=mogrify_image->next; (void) SetMonitorHandler(handler); ProgressMonitor(MogrifyImageText,i,number_images); } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % N e g a t e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function NegateImage negates the colors in the reference image. The % Grayscale option means that only grayscale values within the image are % negated. % % The format of the NegateImage routine is: % % NegateImage(image,grayscale) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % */ void NegateImage(Image *image,unsigned int grayscale) { #define NegateImageText " Negating the image colors... " register int i; register RunlengthPacket *p; assert(image != (Image *) NULL); switch (image->class) { case DirectClass: default: { /* Negate DirectClass packets. */ p=image->pixels; for (i=0; i < image->packets; i++) { if (grayscale) if ((p->red != p->green) || (p->green != p->blue)) continue; p->red=(~p->red); p->green=(~p->green); p->blue=(~p->blue); p++; if (QuantumTick(i,image)) ProgressMonitor(NegateImageText,i,image->packets); } break; } case PseudoClass: { /* Negate PseudoClass packets. */ for (i=0; i < image->colors; i++) { if (grayscale) if ((image->colormap[i].red != image->colormap[i].green) || (image->colormap[i].green != image->colormap[i].blue)) continue; image->colormap[i].red=(~image->colormap[i].red); image->colormap[i].green=(~image->colormap[i].green); image->colormap[i].blue=(~image->colormap[i].blue); } SyncImage(image); break; } } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % N o r m a l i z e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function NormalizeImage normalizes the pixel values to span the full % range of color values. This is a contrast enhancement technique. % % The format of the NormalizeImage routine is: % % NormalizeImage(image) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % */ void NormalizeImage(Image *image) { #define NormalizeImageText " Normalizing image... " int histogram[MaxRGB+1], threshold_intensity; Quantum gray_value, normalize_map[MaxRGB+1]; register int i, intensity; register RunlengthPacket *p; unsigned int high, low; /* Form histogram. */ assert(image != (Image *) NULL); for (i=0; i <= MaxRGB; i++) histogram[i]=0; p=image->pixels; for (i=0; i < image->packets; i++) { gray_value=Intensity(*p); histogram[gray_value]+=p->length+1; p++; } /* Find the histogram boundaries by locating the 1 percent levels. */ threshold_intensity=(image->columns*image->rows)/100; intensity=0; for (low=0; low < MaxRGB; low++) { intensity+=histogram[low]; if (intensity > threshold_intensity) break; } intensity=0; for (high=MaxRGB; high != 0; high--) { intensity+=histogram[high]; if (intensity > threshold_intensity) break; } if (low == high) { /* Unreasonable contrast; use zero threshold to determine boundaries. */ threshold_intensity=0; intensity=0; for (low=0; low < MaxRGB; low++) { intensity+=histogram[low]; if (intensity > threshold_intensity) break; } intensity=0; for (high=MaxRGB; high != 0; high--) { intensity+=histogram[high]; if (intensity > threshold_intensity) break; } if (low == high) return; /* zero span bound */ } /* Stretch the histogram to create the normalized image mapping. */ for (i=0; i <= MaxRGB; i++) if (i < (int) low) normalize_map[i]=0; else if (i > (int) high) normalize_map[i]=MaxRGB; else normalize_map[i]=(MaxRGB-1)*(i-low)/(high-low); /* Normalize the image. */ switch (image->class) { case DirectClass: default: { /* Normalize DirectClass image. */ p=image->pixels; for (i=0; i < image->packets; i++) { p->red=normalize_map[p->red]; p->green=normalize_map[p->green]; p->blue=normalize_map[p->blue]; p++; if (QuantumTick(i,image)) ProgressMonitor(NormalizeImageText,i,image->packets); } break; } case PseudoClass: { /* Normalize PseudoClass image. */ for (i=0; i < image->colors; i++) { image->colormap[i].red=normalize_map[image->colormap[i].red]; image->colormap[i].green=normalize_map[image->colormap[i].green]; image->colormap[i].blue=normalize_map[image->colormap[i].blue]; } SyncImage(image); break; } } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % O p a g u e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function OpaqueImage changes the color of an opaque pixel to the pen color. % % The format of the OpaqueImage routine is: % % OpaqueImage(image,opaque_color,pen_color) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o opaque_color, % pen_color: A character string that contain an X11 color string. % % */ void OpaqueImage(Image *image,char *opaque_color,char *pen_color) { #define OpaqueImageText " Setting opaque color in the image... " ColorPacket target; register int i; unsigned int status; XColor target_color; /* Determine RGB values of the opaque color. */ assert(image != (Image *) NULL); status=XQueryColorDatabase(opaque_color,&target_color); if (status == False) return; target.red=XDownScale(target_color.red); target.green=XDownScale(target_color.green); target.blue=XDownScale(target_color.blue); status=XQueryColorDatabase(pen_color,&target_color); if (status == False) return; /* Make image color opaque. */ switch (image->class) { case DirectClass: default: { register RunlengthPacket *p; /* Make DirectClass image opaque. */ p=image->pixels; for (i=0; i < image->packets; i++) { if (ColorMatch(*p,target,0)) { p->red=XDownScale(target_color.red); p->green=XDownScale(target_color.green); p->blue=XDownScale(target_color.blue); } p++; if (QuantumTick(i,image)) ProgressMonitor(OpaqueImageText,i,image->packets); } break; } case PseudoClass: { register ColorPacket *p; /* Make PseudoClass image opaque. */ p=image->colormap; for (i=0; i < image->colors; i++) { if (ColorMatch(*p,target,0)) { p->red=XDownScale(target_color.red); p->green=XDownScale(target_color.green); p->blue=XDownScale(target_color.blue); } p++; if (QuantumTick(i,image)) ProgressMonitor(OpaqueImageText,i,image->packets); } break; } } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % O p e n I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function OpenImage open a file associated with the image. A file name of % '-' sets the file to stdin for type 'r' and stdout for type 'w'. If the % filename suffix is '.gz' or '.Z', the image is decompressed for type 'r' % and compressed for type 'w'. If the filename prefix is '|', it is piped % to or from a system command. % % The format of the OpenImage routine is: % % OpenImage(image_info,image,type) % % A description of each parameter follows: % % o image_info: Specifies a pointer to a ImageInfo structure. % % o image: The address of a structure of type Image. % % o type: 'r' for reading; 'w' for writing. % */ void OpenImage(const ImageInfo *image_info,Image *image,const char *type) { char filename[MaxTextExtent]; assert(image_info != (ImageInfo *) NULL); assert(image != (Image *) NULL); assert(type != (char *) NULL); (void) strcpy(filename,image->filename); if (*filename != '|') if ((Extent(filename) > 3) && (strcmp(filename+Extent(filename)-3,".gz") == 0)) { /* Uncompress/compress image file with GNU compress utilities. */ if (*type == 'r') (void) sprintf(filename,GunzipCommand,image->filename); else (void) sprintf(filename,GzipCommand,image->filename); } else if ((Extent(filename) > 2) && (strcmp(filename+Extent(filename)-2,".Z") == 0)) { /* Uncompress/compress image file with UNIX compress utilities. */ if (*type == 'r') (void) sprintf(filename,UncompressCommand,image->filename); else (void) sprintf(filename,CompressCommand,image->filename); } /* Open image file. */ image->pipe=False; if (strcmp(filename,"-") == 0) image->file=(*type == 'r') ? stdin : stdout; else #if !defined(vms) && !defined(macintosh) && !defined(WIN32) if (*filename == '|') { char mode[MaxTextExtent]; /* Pipe image to or from a system command. */ if (*type == 'w') (void) signal(SIGPIPE,SIG_IGN); (void) strncpy(mode,type,1); image->file=(FILE *) popen(filename+1,mode); image->pipe=True; } else #endif { if ((*type == 'w') && !image_info->adjoin) if ((image->previous != (Image *) NULL) || (image->next != (Image *) NULL)) { /* Form filename for multi-part images. */ (void) sprintf(filename,image->filename,image->scene); if (strcmp(filename,image->filename) == 0) (void) sprintf(filename,"%s.%u",image->filename,image->scene); if (image->next != (Image *) NULL) (void) strcpy(image->next->magick,image->magick); (void) strcpy(image->filename,filename); } #if defined(macintosh) if (*type == 'w') { OSType filetype; Str255 name; (void) strcpy((char *) name,filename); CtoPstr((char *) name); filetype=' '; (void) strncpy((char *) &filetype,image_info->magick, Min(Extent(image_info->magick),4)); Create(name,0,'8BIM',filetype); } #endif image->file=(FILE *) fopen(filename,type); if (image->file != (FILE *) NULL) { (void) fseek(image->file,0L,SEEK_END); image->filesize=ftell(image->file); (void) fseek(image->file,0L,SEEK_SET); } } image->status=False; if (*type == 'r') { image->next=(Image *) NULL; image->previous=(Image *) NULL; } return; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % P a r s e I m a g e G e o m e t r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ParseImageGeometry parse a geometry specification and returns the % width and height values. % % The format of the ParseImageGeometry routine is: % % flags=ParseImageGeometry(image_geometry,x,y,width,height) % % A description of each parameter follows: % % o flags: Function ParseImageGeometry returns a bitmask that indicates % which of the four values (width, height, xoffset, and yoffset) were % actually found in the string, and whether the x and y values are % negative. % % o image_geometry: Specifies a character string representing the geometry % specification. % % o x,y: A pointer to an integer. The x and y offset as determined by % the geometry specification is returned here. % % o width,height: A pointer to an unsigned integer. The width and height % as determined by the geometry specification is returned here. % % */ Export int ParseImageGeometry(char *image_geometry,int *x, int *y, unsigned int *width,unsigned int *height) { char geometry[MaxTextExtent]; int flags; register char *p; unsigned int aspect_ratio, former_height, former_width, greater, less, percentage; /* Ensure the image geometry is valid. */ assert(x != (int *) NULL); assert(y != (int *) NULL); assert(width != (unsigned int *) NULL); assert(height != (unsigned int *) NULL); if (image_geometry == (char *) NULL) return(NoValue); /* Remove whitespaces and % and ! characters from geometry specification. */ (void) strcpy(geometry,image_geometry); aspect_ratio=True; greater=False; less=False; percentage=False; p=geometry; while (Extent(p) > 0) { if (isspace(*p)) (void) strcpy(p,p+1); else switch (*p) { case '%': { percentage=True; (void) strcpy(p,p+1); break; } case '!': { aspect_ratio=False; (void) strcpy(p,p+1); break; } case '<': { less=True; (void) strcpy(p,p+1); break; } case '>': { greater=True; (void) strcpy(p,p+1); break; } case '~': { aspect_ratio=False; greater=False; less=False; percentage=False; (void) strcpy(p,p+1); break; } default: p++; } } /* Parse geometry using XParseGeometry. */ former_width=(*width); former_height=(*height); flags=XParseGeometry(geometry,x,y,width,height); if (((flags & WidthValue) != 0) && (flags & HeightValue) == 0) *height=(*width); if (percentage) { int count; float x_scale, y_scale; /* Geometry is a percentage of the image size. */ x_scale=(*width); y_scale=(*height); count=sscanf(geometry,"%fx%f",&x_scale,&y_scale); if (count == 1) y_scale=x_scale; *width=Max((unsigned int) ((x_scale*former_width)/100.0),1); *height=Max((unsigned int) ((y_scale*former_height)/100.0),1); former_width=(*width); former_height=(*height); } if (aspect_ratio) { unsigned long scale_factor; /* Respect aspect ratio of the image. */ scale_factor=UpShift(1); if ((former_width*former_height) != 0) if (((flags & WidthValue) != 0) && (flags & HeightValue) != 0) { scale_factor=UpShift(*width)/former_width; if (scale_factor > (UpShift(*height)/former_height)) scale_factor=UpShift(*height)/former_height; } else if ((flags & WidthValue) != 0) scale_factor=UpShift(*width)/former_width; else scale_factor=UpShift(*height)/former_height; *width=Max(DownShift(former_width*scale_factor),1); *height=Max(DownShift(former_height*scale_factor),1); } if (greater) { if (former_width < *width) *width=former_width; if (former_height < *height) *height=former_height; } if (less) { if (former_width > *width) *width=former_width; if (former_height > *height) *height=former_height; } return(flags); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % R G B T r a n s f o r m I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function RGBTransformImage converts the reference image from RGB to % an alternate colorspace. The transformation matrices are not the standard % ones: the weights are rescaled to normalized the range of the transformed % values to be [0..MaxRGB]. % % The format of the RGBTransformImage routine is: % % RGBTransformImage(image,colorspace) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o colorspace: An unsigned integer value that indicates which colorspace % to transform the image. % % */ void RGBTransformImage(Image *image,const unsigned int colorspace) { #define RGBTransformImageText " Transforming image colors... " #define X 0 #define Y (MaxRGB+1) #define Z (MaxRGB+1)*2 long tx, ty, tz, *x, *y, *z; Quantum *range_table; register int blue, green, i, red; register Quantum *range_limit; register RunlengthPacket *p; assert(image != (Image *) NULL); if ((colorspace == RGBColorspace) || (colorspace == TransparentColorspace)) return; if (colorspace == GRAYColorspace) { /* Return if the image is already gray_scale. */ p=image->pixels; for (i=0; i < image->packets; i++) { if ((p->red != p->green) || (p->green != p->blue)) break; p++; } if (i == image->packets) return; } /* Allocate the tables. */ x=(long *) malloc(3*(MaxRGB+1)*sizeof(long)); y=(long *) malloc(3*(MaxRGB+1)*sizeof(long)); z=(long *) malloc(3*(MaxRGB+1)*sizeof(long)); range_table=(Quantum *) malloc(4*(MaxRGB+1)*sizeof(Quantum)); if ((x == (long *) NULL) || (y == (long *) NULL) || (z == (long *) NULL) || (range_table == (Quantum *) NULL)) { Warning("Unable to transform color space","Memory allocation failed"); return; } /* Pre-compute conversion tables. */ for (i=0; i <= MaxRGB; i++) { range_table[i]=0; range_table[i+(MaxRGB+1)]=(Quantum) i; range_table[i+(MaxRGB+1)*2]=MaxRGB; } for (i=0; i <= MaxRGB; i++) range_table[i+(MaxRGB+1)*3]=MaxRGB; range_limit=range_table+(MaxRGB+1); tx=0; ty=0; tz=0; switch (colorspace) { case GRAYColorspace: { /* Initialize GRAY tables: G = 0.29900*R+0.58600*G+0.11400*B */ for (i=0; i <= MaxRGB; i++) { x[i+X]=UpShifted(0.29900)*i; y[i+X]=UpShifted(0.58600)*i; z[i+X]=UpShifted(0.11400)*i; x[i+Y]=UpShifted(0.29900)*i; y[i+Y]=UpShifted(0.58600)*i; z[i+Y]=UpShifted(0.11400)*i; x[i+Z]=UpShifted(0.29900)*i; y[i+Z]=UpShifted(0.58600)*i; z[i+Z]=UpShifted(0.11400)*i; } break; } case OHTAColorspace: { /* Initialize OHTA tables: I1 = 0.33333*R+0.33334*G+0.33333*B I2 = 0.50000*R+0.00000*G-0.50000*B I3 =-0.25000*R+0.50000*G-0.25000*B I and Q, normally -0.5 through 0.5, are normalized to the range 0 through MaxRGB. */ ty=UpShifted((MaxRGB+1) >> 1); tz=UpShifted((MaxRGB+1) >> 1); for (i=0; i <= MaxRGB; i++) { x[i+X]=UpShifted(0.33333)*i; y[i+X]=UpShifted(0.33334)*i; z[i+X]=UpShifted(0.33333)*i; x[i+Y]=UpShifted(0.50000)*i; y[i+Y]=0; z[i+Y]=(-UpShifted(0.50000))*i; x[i+Z]=(-UpShifted(0.25000))*i; y[i+Z]=UpShifted(0.50000)*i; z[i+Z]=(-UpShifted(0.25000))*i; } break; } case XYZColorspace: { /* Initialize CIE XYZ tables: X = 0.412453*X+0.357580*Y+0.180423*Z Y = 0.212671*X+0.715160*Y+0.072169*Z Z = 0.019334*X+0.119193*Y+0.950227*Z */ for (i=0; i <= MaxRGB; i++) { x[i+X]=UpShifted(0.412453)*i; y[i+X]=UpShifted(0.357580)*i; z[i+X]=UpShifted(0.180423)*i; x[i+Y]=UpShifted(0.212671)*i; y[i+Y]=UpShifted(0.715160)*i; z[i+Y]=UpShifted(0.072169)*i; x[i+Z]=UpShifted(0.019334)*i; y[i+Z]=UpShifted(0.119193)*i; z[i+Z]=UpShifted(0.950227)*i; } break; } case YCbCrColorspace: { /* Initialize YCbCr tables: Y = 0.299000*R+0.586000*G+0.114000*B Cb= -0.172586*R-0.338828*G+0.511414*B Cr= 0.511414*R-0.428246*G-0.083168*B Cb and Cr, normally -0.5 through 0.5, are normalized to the range 0 through MaxRGB. */ ty=UpShifted((MaxRGB+1) >> 1); tz=UpShifted((MaxRGB+1) >> 1); for (i=0; i <= MaxRGB; i++) { x[i+X]=UpShifted(0.299000)*i; y[i+X]=UpShifted(0.586000)*i; z[i+X]=UpShifted(0.114000)*i; x[i+Y]=(-UpShifted(0.172586))*i; y[i+Y]=(-UpShifted(0.338828))*i; z[i+Y]=UpShifted(0.511414)*i; x[i+Z]=UpShifted(0.511414)*i; y[i+Z]=(-UpShifted(0.428246))*i; z[i+Z]=(-UpShifted(0.083168))*i; } break; } case YCCColorspace: { /* Initialize YCC tables: Y = 0.29900*R+0.58600*G+0.11400*B C1= -0.29900*R-0.58600*G+0.88600*B C2= 0.70100*R-0.58600*G-0.11400*B YCC is scaled by 1.3584. C1 zero is 156 and C2 is at 137. */ ty=UpShifted((unsigned int) UpScale(156)); tz=UpShifted((unsigned int) UpScale(137)); for (i=0; i <= (int) (0.018*MaxRGB); i++) { x[i+X]=(long) (UpShifted(0.29900/1.3584)*0.018*MaxRGB*i); y[i+X]=(long) (UpShifted(0.58600/1.3584)*0.018*MaxRGB*i); z[i+X]=(long) (UpShifted(0.11400/1.3584)*0.018*MaxRGB*i); x[i+Y]=(long) ((-UpShifted(0.29900/2.2179))*0.018*MaxRGB*i); y[i+Y]=(long) ((-UpShifted(0.58600/2.2179))*0.018*MaxRGB*i); z[i+Y]=(long) (UpShifted(0.88600/2.2179)*0.018*MaxRGB*i); x[i+Z]=(long) (UpShifted(0.70100/1.8215)*0.018*MaxRGB*i); y[i+Z]=(long) ((-UpShifted(0.58600/1.8215))*0.018*MaxRGB*i); z[i+Z]=(long) ((-UpShifted(0.11400/1.8215))*0.018*MaxRGB*i); } for ( ; i <= MaxRGB; i++) { x[i+X]=(long) (UpShifted(0.29900/1.3584)*(1.099*i-0.099)); y[i+X]=(long) (UpShifted(0.58600/1.3584)*(1.099*i-0.099)); z[i+X]=(long) (UpShifted(0.11400/1.3584)*(1.099*i-0.099)); x[i+Y]=(long) ((-UpShifted(0.29900/2.2179))*(1.099*i-0.099)); y[i+Y]=(long) ((-UpShifted(0.58600/2.2179))*(1.099*i-0.099)); z[i+Y]=(long) (UpShifted(0.88600/2.2179)*(1.099*i-0.099)); x[i+Z]=(long) (UpShifted(0.70100/1.8215)*(1.099*i-0.099)); y[i+Z]=(long) ((-UpShifted(0.58600/1.8215))*(1.099*i-0.099)); z[i+Z]=(long) ((-UpShifted(0.11400/1.8215))*(1.099*i-0.099)); } break; } case YIQColorspace: { /* Initialize YIQ tables: Y = 0.29900*R+0.58600*G+0.11400*B I = 0.50000*R-0.23000*G-0.27000*B Q = 0.20200*R-0.50000*G+0.29800*B I and Q, normally -0.5 through 0.5, are normalized to the range 0 through MaxRGB. */ ty=UpShifted((MaxRGB+1) >> 1); tz=UpShifted((MaxRGB+1) >> 1); for (i=0; i <= MaxRGB; i++) { x[i+X]=UpShifted(0.29900)*i; y[i+X]=UpShifted(0.58600)*i; z[i+X]=UpShifted(0.11400)*i; x[i+Y]=UpShifted(0.50000)*i; y[i+Y]=(-UpShifted(0.23000))*i; z[i+Y]=(-UpShifted(0.27000))*i; x[i+Z]=UpShifted(0.20200)*i; y[i+Z]=(-UpShifted(0.50000))*i; z[i+Z]=UpShifted(0.29800)*i; } break; } case YPbPrColorspace: { /* Initialize YPbPr tables: Y = 0.299000*R+0.587000*G+0.114000*B Pb= -0.168736*R-0.331264*G+0.500000*B Pr= 0.500000*R-0.418688*G-0.081312*B Pb and Pr, normally -0.5 through 0.5, are normalized to the range 0 through MaxRGB. */ ty=UpShifted((MaxRGB+1) >> 1); tz=UpShifted((MaxRGB+1) >> 1); for (i=0; i <= MaxRGB; i++) { x[i+X]=UpShifted(0.299000)*i; y[i+X]=UpShifted(0.587000)*i; z[i+X]=UpShifted(0.114000)*i; x[i+Y]=(-UpShifted(0.168736))*i; y[i+Y]=(-UpShifted(0.331264))*i; z[i+Y]=UpShifted(0.500000)*i; x[i+Z]=UpShifted(0.500000)*i; y[i+Z]=(-UpShifted(0.418688))*i; z[i+Z]=(-UpShifted(0.081312))*i; } break; } case YUVColorspace: default: { /* Initialize YUV tables: Y = 0.29900*R+0.58600*G+0.11400*B U = -0.14740*R-0.28950*G+0.43690*B V = 0.61500*R-0.51500*G-0.10000*B U and V, normally -0.5 through 0.5, are normalized to the range 0 through MaxRGB. Note that U = 0.493*(B-Y), V = 0.877*(R-Y). */ ty=UpShifted((MaxRGB+1) >> 1); tz=UpShifted((MaxRGB+1) >> 1); for (i=0; i <= MaxRGB; i++) { x[i+X]=UpShifted(0.29900)*i; y[i+X]=UpShifted(0.58600)*i; z[i+X]=UpShifted(0.11400)*i; x[i+Y]=(-UpShifted(0.14740))*i; y[i+Y]=(-UpShifted(0.28950))*i; z[i+Y]=UpShifted(0.43690)*i; x[i+Z]=UpShifted(0.61500)*i; y[i+Z]=(-UpShifted(0.51500))*i; z[i+Z]=(-UpShifted(0.10000))*i; } break; } } /* Convert from RGB. */ switch (image->class) { case DirectClass: default: { /* Convert DirectClass image. */ p=image->pixels; for (i=0; i < image->packets; i++) { red=p->red; green=p->green; blue=p->blue; p->red=range_limit[DownShift(x[red+X]+y[green+X]+z[blue+X]+tx)]; p->green=range_limit[DownShift(x[red+Y]+y[green+Y]+z[blue+Y]+ty)]; p->blue=range_limit[DownShift(x[red+Z]+y[green+Z]+z[blue+Z]+tz)]; p++; if (QuantumTick(i,image)) ProgressMonitor(RGBTransformImageText,i,image->packets); } break; } case PseudoClass: { /* Convert PseudoClass image. */ for (i=0; i < image->colors; i++) { red=image->colormap[i].red; green=image->colormap[i].green; blue=image->colormap[i].blue; image->colormap[i].red= range_limit[DownShift(x[red+X]+y[green+X]+z[blue+X]+tx)]; image->colormap[i].green= range_limit[DownShift(x[red+Y]+y[green+Y]+z[blue+Y]+ty)]; image->colormap[i].blue= range_limit[DownShift(x[red+Z]+y[green+Z]+z[blue+Z]+tz)]; } SyncImage(image); break; } } /* Free allocated memory. */ free((char *) range_table); free((char *) z); free((char *) y); free((char *) x); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R o l l I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function RollImage rolls an image vertically and horizontally. It % allocates the memory necessary for the new Image structure and returns a % pointer to the new image. % % The format of the RollImage routine is: % % rolled_image=RollImage(image,x_offset,y_offset) % % A description of each parameter follows: % % o rolled_image: Function RollImage returns a pointer to the image after % rolling. A null image is returned if there is a memory shortage. % % o image: The address of a structure of type Image. % % o x_offset: An integer that specifies the number of columns to roll % in the horizontal direction. % % o y_offset: An integer that specifies the number of rows to roll in the % vertical direction. % % */ Image *RollImage(Image *image,int x_offset,int y_offset) { #define RollImageText " Rolling image... " Image *rolled_image; int y; register int x; register RunlengthPacket *p, *q; /* Initialize rolled image attributes. */ assert(image != (Image *) NULL); rolled_image=CloneImage(image,image->columns,image->rows,False); if (rolled_image == (Image *) NULL) { Warning("Unable to roll image","Memory allocation failed"); return((Image *) NULL); } /* Roll image. */ x_offset%=(int) image->columns; if (x_offset < 0) x_offset+=(int) image->columns; y_offset%=(int) image->rows; if (y_offset < 0) y_offset+=(int) image->rows; p=image->pixels; image->runlength=p->length+1; for (y=0; y < image->rows; y++) { /* Transfer scanline. */ for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } q=rolled_image->pixels+((y_offset+y) % image->rows)*image->columns+ ((x+x_offset) % image->columns); *q=(*p); q->length=0; } ProgressMonitor(RollImageText,y,image->rows); } return(rolled_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S a m p l e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function SampleImage creates a new image that is a scaled size of an % existing one using pixel sampling. It allocates the memory necessary % for the new Image structure and returns a pointer to the new image. % % The format of the SampleImage routine is: % % sampled_image=SampleImage(image,columns,rows) % % A description of each parameter follows: % % o sampled_image: Function SampleImage returns a pointer to the image after % scaling. A null image is returned if there is a memory shortage. % % o image: The address of a structure of type Image. % % o columns: An integer that specifies the number of columns in the sampled % image. % % o rows: An integer that specifies the number of rows in the sampled % image. % % */ Image *SampleImage(Image *image,unsigned int columns,unsigned int rows) { #define SampleImageText " Sampling image... " Image *sampled_image; int y; register RunlengthPacket *p, *q, *s; register int x; RunlengthPacket *scanline; unsigned int *x_offset, *y_offset; unsigned long scale_factor; assert(image != (Image *) NULL); if ((columns == 0) || (rows == 0)) { Warning("Unable to sample image","image dimensions are zero"); return((Image *) NULL); } /* Initialize sampled image attributes. */ sampled_image=CloneImage(image,columns,rows,False); if (sampled_image == (Image *) NULL) { Warning("Unable to sample image","Memory allocation failed"); return((Image *) NULL); } /* Allocate scan line buffer and column offset buffers. */ scanline=(RunlengthPacket *) malloc(image->columns*sizeof(RunlengthPacket)); x_offset=(unsigned int *) malloc(sampled_image->columns*sizeof(unsigned int)); y_offset=(unsigned int *) malloc(sampled_image->rows*sizeof(unsigned int)); if ((scanline == (RunlengthPacket *) NULL) || (x_offset == (unsigned int *) NULL) || (y_offset == (unsigned int *) NULL)) { Warning("Unable to sample image","Memory allocation failed"); DestroyImage(sampled_image); return((Image *) NULL); } /* Initialize column pixel offsets. */ scale_factor=UpShift(image->columns-1)/sampled_image->columns; columns=0; for (x=0; x < sampled_image->columns; x++) { x_offset[x]=DownShift((x+1)*scale_factor)-(int) columns; columns+=x_offset[x]; } /* Initialize row pixel offsets. */ scale_factor=UpShift(image->rows-1)/sampled_image->rows; rows=0; for (y=0; y < sampled_image->rows; y++) { y_offset[y]=DownShift((y+1)*scale_factor)-(int) rows; rows+=y_offset[y]; } y_offset[sampled_image->rows-1]=0; /* Preload first scanline. */ p=image->pixels; image->runlength=p->length+1; s=scanline; for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } *s=(*p); s->length=0; s++; } /* Sample each row. */ q=sampled_image->pixels; for (y=0; y < sampled_image->rows; y++) { /* Sample each column. */ s=scanline; for (x=0; x < sampled_image->columns; x++) { *q=(*s); q++; s+=x_offset[x]; } if (y_offset[y] != 0) { /* Skip a scan line. */ if (y_offset[y] > 1) for (x=0; x < (image->columns*(y_offset[y]-1)); x++) if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } /* Read a scan line. */ s=scanline; for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } *s=(*p); s->length=0; s++; } } ProgressMonitor(SampleImageText,y,sampled_image->rows); } free((char *) scanline); free((char *) x_offset); free((char *) y_offset); return(sampled_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S c a l e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ScaleImage creates a new image that is a scaled size of an % existing one. It allocates the memory necessary for the new Image % structure and returns a pointer to the new image. To scale a scanline % from x pixels to y pixels, each new pixel represents x/y old pixels. To % read x/y pixels, read (x/y rounded up) pixels but only count the required % fraction of the last old pixel read in your new pixel. The remainder % of the old pixel will be counted in the next new pixel. % % The scaling algorithm was suggested by rjohnson@shell.com and is adapted % from pnmscale(1) of PBMPLUS by Jef Poskanzer. % % The format of the ScaleImage routine is: % % scaled_image=ScaleImage(image,columns,rows) % % A description of each parameter follows: % % o scaled_image: Function ScaleImage returns a pointer to the image after % scaling. A null image is returned if there is a memory shortage. % % o image: The address of a structure of type Image. % % o columns: An integer that specifies the number of columns in the scaled % image. % % o rows: An integer that specifies the number of rows in the scaled % image. % % */ Image *ScaleImage(Image *image,const unsigned int columns, const unsigned int rows) { #define ScaleImageText " Scaling image... " typedef struct ScaledPacket { long red, green, blue, index; } ScaledPacket; Image *scaled_image; int next_row, number_rows; long x_scale, x_span; register RunlengthPacket *p, *q; register ScaledPacket *s, *t; register unsigned int x; ScaledPacket *scaled_scanline, *scanline, *y_vector, *x_vector; unsigned int packets, y; unsigned long blue, green, index, red, scale_factor; assert(image != (Image *) NULL); if ((columns == 0) || (rows == 0)) { Warning("Unable to scale image","image dimensions are zero"); return((Image *) NULL); } /* Initialize scaled image attributes. */ scale_factor=UpShift(columns*rows)/(image->columns*image->rows); packets=Max(DownShift(image->packets*scale_factor),1); scaled_image=CloneImage(image,packets,1,False); if (scaled_image == (Image *) NULL) { Warning("Unable to scale image","Memory allocation failed"); return((Image *) NULL); } scaled_image->class=DirectClass; scaled_image->columns=columns; scaled_image->rows=rows; scaled_image->packets=0; /* Allocate memory. */ x_vector=(ScaledPacket *) malloc(image->columns*sizeof(ScaledPacket)); scanline=x_vector; if (scaled_image->rows != image->rows) scanline=(ScaledPacket *) malloc(image->columns*sizeof(ScaledPacket)); scaled_scanline=(ScaledPacket *) malloc(scaled_image->columns*sizeof(ScaledPacket)); y_vector=(ScaledPacket *) malloc(image->columns*sizeof(ScaledPacket)); if ((x_vector == (ScaledPacket *) NULL) || (scanline == (ScaledPacket *) NULL) || (scaled_scanline == (ScaledPacket *) NULL) || (y_vector == (ScaledPacket *) NULL)) { Warning("Unable to scale image","Memory allocation failed"); DestroyImage(scaled_image); return((Image *) NULL); } /* Scale image. */ index=0; number_rows=0; next_row=True; x_scale=UpShift(scaled_image->rows)/image->rows; x_span=UpShift(1); for (x=0; x < image->columns; x++) { y_vector[x].red=0; y_vector[x].green=0; y_vector[x].blue=0; y_vector[x].index=0; } p=image->pixels; image->runlength=p->length+1; q=scaled_image->pixels; q->red=0; q->green=0; q->blue=0; q->index=0; q->length=MaxRunlength; for (y=0; y < scaled_image->rows; y++) { if (scaled_image->rows == image->rows) for (x=0; x < image->columns; x++) { /* Read a new scanline. */ if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } x_vector[x].red=p->red; x_vector[x].green=p->green; x_vector[x].blue=p->blue; x_vector[x].index=p->index; } else { /* Scale Y direction. */ while (x_scale < x_span) { if (next_row && (number_rows < image->rows)) { /* Read a new scanline. */ for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } x_vector[x].red=p->red; x_vector[x].green=p->green; x_vector[x].blue=p->blue; x_vector[x].index=p->index; } number_rows++; } for (x=0; x < image->columns; x++) { y_vector[x].red+=x_scale*x_vector[x].red; y_vector[x].green+=x_scale*x_vector[x].green; y_vector[x].blue+=x_scale*x_vector[x].blue; y_vector[x].index+=x_scale*x_vector[x].index; } x_span-=x_scale; x_scale=UpShift(scaled_image->rows)/image->rows; next_row=True; } if (next_row && (number_rows < image->rows)) { /* Read a new scanline. */ for (x=0; x < image->columns; x++) { if (image->runlength != 0) image->runlength--; else { p++; image->runlength=p->length; } x_vector[x].red=p->red; x_vector[x].green=p->green; x_vector[x].blue=p->blue; x_vector[x].index=p->index; } number_rows++; next_row=False; } s=scanline; for (x=0; x < image->columns; x++) { red=DownShift(y_vector[x].red+x_span*x_vector[x].red); green=DownShift(y_vector[x].green+x_span*x_vector[x].green); blue=DownShift(y_vector[x].blue+x_span*x_vector[x].blue); index=DownShift(y_vector[x].index+x_span*x_vector[x].index); s->red=(Quantum) (red > MaxRGB ? MaxRGB : red); s->green=(Quantum) (green > MaxRGB ? MaxRGB : green); s->blue=(Quantum) (blue > MaxRGB ? MaxRGB : blue); s->index=(unsigned short) (index > MaxColormapSize ? MaxColormapSize : index); s++; y_vector[x].red=0; y_vector[x].green=0; y_vector[x].blue=0; y_vector[x].index=0; } x_scale-=x_span; if (x_scale == 0) { x_scale=UpShift(scaled_image->rows)/image->rows; next_row=True; } x_span=UpShift(1); } if (scaled_image->columns == image->columns) { /* Transfer scanline to scaled image. */ s=scanline; for (x=0; x < scaled_image->columns; x++) { if ((s->red == q->red) && (s->green == q->green) && (s->blue == q->blue) && (s->index == q->index) && ((int) q->length < MaxRunlength)) q->length++; else { if (scaled_image->packets != 0) q++; scaled_image->packets++; if (scaled_image->packets == packets) { packets<<=1; scaled_image->pixels=(RunlengthPacket *) realloc((char *) scaled_image->pixels,packets*sizeof(RunlengthPacket)); if (scaled_image->pixels == (RunlengthPacket *) NULL) { Warning("Unable to scale image", "Memory allocation failed"); DestroyImage(scaled_image); return((Image *) NULL); } q=scaled_image->pixels+scaled_image->packets-1; } q->red=(Quantum) s->red; q->green=(Quantum) s->green; q->blue=(Quantum) s->blue; q->index=(unsigned short) s->index; q->length=0; } s++; } } else { int next_column; long y_scale, y_span; /* Scale X direction. */ red=0; green=0; blue=0; next_column=False; y_span=UpShift(1); s=scanline; t=scaled_scanline; for (x=0; x < image->columns; x++) { y_scale=UpShift(scaled_image->columns)/image->columns; while (y_scale >= y_span) { if (next_column) { red=0; green=0; blue=0; index=0; t++; } red=DownShift(red+y_span*s->red); green=DownShift(green+y_span*s->green); blue=DownShift(blue+y_span*s->blue); index=DownShift(index+y_span*s->index); t->red=(Quantum) (red > MaxRGB ? MaxRGB : red); t->green=(Quantum) (green > MaxRGB ? MaxRGB : green); t->blue=(Quantum) (blue > MaxRGB ? MaxRGB : blue); t->index=(unsigned short) (index > MaxColormapSize ? MaxColormapSize : index); y_scale-=y_span; y_span=UpShift(1); next_column=True; } if (y_scale > 0) { if (next_column) { red=0; green=0; blue=0; index=0; next_column=False; t++; } red+=y_scale*s->red; green+=y_scale*s->green; blue+=y_scale*s->blue; index+=y_scale*s->index; y_span-=y_scale; } s++; } if (y_span > 0) { s--; red+=y_span*s->red; green+=y_span*s->green; blue+=y_span*s->blue; index+=y_span*s->index; } if (!next_column) { red=DownShift(red); green=DownShift(green); blue=DownShift(blue); index=DownShift(index); t->red=(Quantum) (red > MaxRGB ? MaxRGB : red); t->green=(Quantum) (green > MaxRGB ? MaxRGB : green); t->blue=(Quantum) (blue > MaxRGB ? MaxRGB : blue); t->index=(unsigned short) (index > MaxRGB ? MaxRGB : index); } /* Transfer scanline to scaled image. */ t=scaled_scanline; for (x=0; x < scaled_image->columns; x++) { if ((t->red == q->red) && (t->green == q->green) && (t->blue == q->blue) && (t->index == q->index) && ((int) q->length < MaxRunlength)) q->length++; else { if (scaled_image->packets != 0) q++; scaled_image->packets++; if (scaled_image->packets == packets) { packets<<=1; scaled_image->pixels=(RunlengthPacket *) realloc((char *) scaled_image->pixels,packets*sizeof(RunlengthPacket)); if (scaled_image->pixels == (RunlengthPacket *) NULL) { Warning("Unable to scale image","Memory allocation failed"); DestroyImage(scaled_image); return((Image *) NULL); } q=scaled_image->pixels+scaled_image->packets-1; } q->red=(Quantum) t->red; q->green=(Quantum) t->green; q->blue=(Quantum) t->blue; q->index=(unsigned short) t->index; q->length=0; } t++; } } ProgressMonitor(ScaleImageText,y,scaled_image->rows); } scaled_image->pixels=(RunlengthPacket *) realloc((char *) scaled_image->pixels,scaled_image->packets*sizeof(RunlengthPacket)); /* Free allocated memory. */ free((char *) y_vector); free((char *) scaled_scanline); if (scanline != x_vector) free((char *) scanline); free((char *) x_vector); return(scaled_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S e t I m a g e I n f o % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function SetImageInfo initializes the `magick' field of the ImageInfo % structure. It is set to a type of image format based on the prefix or % suffix of the filename. For example, `ps:image' returns PS indicating % a Postscript image. JPEG is returned for this filename: `image.jpg'. % The filename prefix has precedance over the suffix. Use an optional index % enclosed in brackets after a file name to specify a desired subimage of a % multi-resolution image format like Photo CD (e.g. img0001.pcd[4]). % % The format of the SetImageInfo routine is: % % SetImageInfo(image_info,recify) % % A description of each parameter follows: % % o image_info: Specifies a pointer to a ImageInfo structure. % % o rectify: an unsigned value other than zero rectifies the attribute for % multi-frame support (user may want multi-frame but image format may not % support it). % % */ Export void SetImageInfo(ImageInfo *image_info,unsigned int rectify) { char c, magick[MaxTextExtent]; int flags, x, y; register char *p, *q; register int i; unsigned int height, width; /* Look for 'image.format' in filename. */ assert(image_info != (ImageInfo *) NULL); *magick='\0'; p=image_info->filename+Extent(image_info->filename)-1; if (*p == ']') for (q=p-1; q > image_info->filename; q--) { if (*q != '[') continue; image_info->tile=(char *) malloc((p-q)*sizeof(char)); if (image_info->tile == (char *) NULL) break; (void) strncpy(image_info->tile,q+1,p-q-1); image_info->tile[p-q-1]='\0'; if (!IsGeometry(image_info->tile)) { free(image_info->tile); image_info->tile=(char *) NULL; break; } flags=XParseGeometry(image_info->tile,&x,&y,&width,&height); if (!(flags & HeightValue)) { /* Sub-image specified (e.g. img0001.pcd[4]). */ image_info->subimage=atoi(image_info->tile); image_info->subrange=atoi(image_info->tile); (void) sscanf(image_info->tile,"%u-%u",&image_info->subimage, &image_info->subrange); image_info->subrange-=image_info->subimage-1; } *q='\0'; p=q; break; } while ((*p != '.') && (p > image_info->filename)) p--; if ((strcmp(p,".gz") == 0) || (strcmp(p,".Z") == 0)) do { p--; } while ((*p != '.') && (p > image_info->filename)); if ((*p == '.') && (Extent(p) < sizeof(magick))) { /* User specified image format. */ (void) strcpy(magick,p+1); for (q=magick; *q != '\0'; q++) { if (*q == '.') { *q='\0'; break; } c=(*q); if (islower(c)) *q=toupper(c); } for (i=0; ImageFormats[i][0] != (char *) NULL; i++) if (strcmp(magick,ImageFormats[i][0]) == 0) { /* SGI and RGB are ambiguous; TMP must be set explicitly. */ if (((strncmp(image_info->magick,"SGI",3) != 0) || (strcmp(ImageFormats[i][0],"RGB") != 0)) && (strcmp(ImageFormats[i][0],"TMP") != 0)) (void) strcpy(image_info->magick,magick); break; } } /* Look for explicit 'format:image' in filename. */ image_info->affirm=False; p=image_info->filename; while ((*p != ':') && (*p != '\0')) p++; if ((*p == ':') && ((p-image_info->filename) < sizeof(magick))) { /* User specified image format. */ (void) strncpy(magick,image_info->filename,p-image_info->filename); magick[p-image_info->filename]='\0'; for (q=magick; *q != '\0'; q++) { c=(*q); if (islower(c)) *q=toupper(c); } for (i=0; ImageFormats[i][0] != (char *) NULL; i++) if (strcmp(magick,ImageFormats[i][0]) == 0) { /* Strip off image format prefix. */ p++; (void) strcpy(image_info->filename,p); (void) strcpy(image_info->magick,magick); if (strcmp(magick,"TMP") != 0) image_info->affirm=True; break; } } /* Rectify multi-image file support. */ if (rectify) for (i=0; ImageFormats[i][0] != (char *) NULL; i++) if (strcmp(image_info->magick,ImageFormats[i][0]) == 0) image_info->adjoin&=IsTrue(ImageFormats[i][1]); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S e t N u m b e r S c e n e s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function SetNumberScenes sets the number of scenes in an image sequence. % % The format of the SetNumberScenes routine is: % % SetNumberScenes(image) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % */ Export void SetNumberScenes(Image *image) { Image *next_image; unsigned int number_scenes; /* Compute the number of scenes in the image. */ assert(image != (Image *) NULL); while (image->previous != (Image *) NULL) image=image->previous; next_image=image; for (number_scenes=0; next_image != (Image *) NULL; number_scenes++) next_image=next_image->next; for ( ; image != (Image *) NULL; image=image->next) image->number_scenes=number_scenes; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S o r t C o l o r m a p B y I n t e n t s i t y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function SortColormapByIntensity sorts the colormap of a PseudoClass image % by decreasing color intensity. % % The format of the SortColormapByIntensity routine is: % % SortColormapByIntensity(image) % % A description of each parameter follows: % % o image: A pointer to a Image structure. % % */ static int IntensityCompare(const void *x,const void *y) { ColorPacket *color_1, *color_2; color_1=(ColorPacket *) x; color_2=(ColorPacket *) y; return((int) Intensity(*color_2)-(int) Intensity(*color_1)); } void SortColormapByIntensity(Image *image) { register int i; register RunlengthPacket *p; register unsigned short index; unsigned short *pixels; assert(image != (Image *) NULL); if (image->class != PseudoClass) return; /* Allocate memory for pixel indexes. */ pixels=(unsigned short *) malloc(image->colors*sizeof(unsigned short)); if (pixels == (unsigned short *) NULL) { Warning("Unable to sort colormap","Memory allocation failed"); return; } /* Assign index values to colormap entries. */ for (i=0; i < image->colors; i++) image->colormap[i].index=(unsigned short) i; /* Sort image colormap by decreasing color popularity. */ qsort((void *) image->colormap,(int) image->colors,sizeof(ColorPacket), (int (*)(const void *, const void *)) IntensityCompare); /* Update image colormap indexes to sorted colormap order. */ for (i=0; i < image->colors; i++) pixels[image->colormap[i].index]=(unsigned short) i; p=image->pixels; for (i=0; i < image->packets; i++) { index=pixels[p->index]; p->red=image->colormap[index].red; p->green=image->colormap[index].green; p->blue=image->colormap[index].blue; p->index=index; p++; } free((char *) pixels); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S t e r e o I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function StereoImage combines two images and produces a single image that % is the composite of a left and right image of a stereo pair. The left % image is converted to gray_scale and written to the red channel of the % stereo image. The right image is converted to gray_scale and written to the % blue channel of the stereo image. View the composite image with red-blue % glasses to create a stereo effect. % % The format of the StereoImage routine is: % % stereo_image=StereoImage(left_image,right_image) % % A description of each parameter follows: % % o stereo_image: Function StereoImage returns a pointer to the stereo % image. A null image is returned if there is a memory shortage. % % o left_image: The address of a structure of type Image. % % o right_image: The address of a structure of type Image. % % */ Export Image *StereoImage(Image *left_image,Image *right_image) { #define StereoImageText " Stereo image... " Image *stereo_image; int y; QuantizeInfo quantize_info; register int x; register RunlengthPacket *p, *q, *r; assert(left_image != (Image *) NULL); assert(right_image != (Image *) NULL); if ((left_image->columns != right_image->columns) || (left_image->rows != right_image->rows)) { Warning("Unable to create stereo image", "left and right image sizes differ"); return((Image *) NULL); } /* Initialize stereo image attributes. */ stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,False); if (stereo_image == (Image *) NULL) { Warning("Unable to create stereo image","Memory allocation failed"); return((Image *) NULL); } stereo_image->class=DirectClass; /* Copy left image to red channel and right image to blue channel. */ GetQuantizeInfo(&quantize_info); quantize_info.colorspace=GRAYColorspace; QuantizeImage(&quantize_info,left_image); SyncImage(left_image); p=left_image->pixels; left_image->runlength=p->length+1; QuantizeImage(&quantize_info,right_image); SyncImage(right_image); q=right_image->pixels; right_image->runlength=q->length+1; r=stereo_image->pixels; for (y=0; y < stereo_image->rows; y++) { for (x=0; x < stereo_image->columns; x++) { if (left_image->runlength != 0) left_image->runlength--; else { p++; left_image->runlength=p->length; } if (right_image->runlength != 0) right_image->runlength--; else { q++; right_image->runlength=q->length; } r->red=(unsigned int) (p->red*12) >> 4; r->green=0; r->blue=q->blue; r->index=0; r->length=0; r++; } ProgressMonitor(StereoImageText,y,stereo_image->rows); } return(stereo_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S y n c I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function SyncImage initializes the red, green, and blue intensities of each % pixel as defined by the colormap index. % % The format of the SyncImage routine is: % % SyncImage(image) % % A description of each parameter follows: % % o image: The address of a structure of type Image. % % */ Export void SyncImage(Image *image) { register int i; register RunlengthPacket *p; register unsigned short index; assert(image != (Image *) NULL); if (image->class == DirectClass) return; for (i=0; i < image->colors; i++) { image->colormap[i].index=0; image->colormap[i].flags=0; } p=image->pixels; for (i=0; i < image->packets; i++) { index=p->index; p->red=image->colormap[index].red; p->green=image->colormap[index].green; p->blue=image->colormap[index].blue; p++; } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % T e x t u r e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function TextureImage layers a texture onto the background of an image. % % The format of the TextureImage routine is: % % TextureImage(image,filename) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o filename: This file contains the texture to layer on the background. % % */ void TextureImage(Image *image,char *filename) { #define TextureImageText " Appling image texture... " Image *texture_image; ImageInfo texture_info; int x, y; assert(image != (Image *) NULL); if (filename == (char *) NULL) return; /* Read the texture image. */ GetImageInfo(&texture_info); (void) strcpy(texture_info.filename,filename); texture_image=ReadImage(&texture_info); free(texture_info.filename); if (texture_image == (Image *) NULL) return; /* Tile texture onto the image background. */ for (y=0; y < image->rows; y+=texture_image->rows) { for (x=0; x < image->columns; x+=texture_image->columns) CompositeImage(image,ReplaceCompositeOp,texture_image,x,y); ProgressMonitor(TextureImageText,y,image->rows); } DestroyImage(texture_image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % T h r e s h o l d I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ThresholdImage thresholds the reference image. % % The format of the ThresholdImage routine is: % % ThresholdImage(image,threshold) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o threshold: A double indicating the threshold value. % % */ void ThresholdImage(Image *image,double threshold) { #define ThresholdImageText " Threshold the image... " ColorPacket *colormap; register int i; register RunlengthPacket *p; /* Threshold image. */ assert(image != (Image *) NULL); colormap=(ColorPacket *) malloc(2*sizeof(ColorPacket)); if (colormap == (ColorPacket *) NULL) { Warning("Unable to allocate image","Memory allocation failed"); return; } if (image->colormap != (ColorPacket *) NULL) free((char *) image->colormap); image->class=PseudoClass; image->colors=2; image->colormap=colormap; image->colormap[0].red=0; image->colormap[0].green=0; image->colormap[0].blue=0; image->colormap[1].red=MaxRGB; image->colormap[1].green=MaxRGB; image->colormap[1].blue=MaxRGB; p=image->pixels; for (i=0; i < image->packets; i++) { p->index=Intensity(*p) < threshold ? 0 : 1; p++; if (QuantumTick(i,image)) ProgressMonitor(ThresholdImageText,i,image->packets); } SyncImage(image); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % T r a n s f o r m I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function TransformImage creates a new image that is a transformed size of % of existing one as specified by the crop and image geometries. It % allocates the memory necessary for the new Image structure and returns a % pointer to the new image. % % If a crop geometry is specified a subregion of the image is obtained. % If the specified image size, as defined by the image and scale geometries, % is smaller than the actual image size, the image is first minified to an % integral of the specified image size with an antialias digital filter. The % image is then scaled to the exact specified image size with pixel % replication. If the specified image size is greater than the actual image % size, the image is first enlarged to an integral of the specified image % size with bilinear interpolation. The image is then scaled to the exact % specified image size with pixel replication. % % The format of the TransformImage routine is: % % TransformImage(image,crop_geometry,image_geometry) % % A description of each parameter follows: % % o image: The address of an address of a structure of type Image. The % transformed image is returned as this parameter. % % o crop_geometry: Specifies a pointer to a crop geometry string. % This geometry defines a subregion of the image. % % o image_geometry: Specifies a pointer to a image geometry string. % The specified width and height of this geometry string are absolute. % % */ Export void TransformImage(Image **image,char *crop_geometry, char *image_geometry) { Image *transformed_image; int flags, x, y; unsigned int height, sharpen, width; assert(image != (Image **) NULL); transformed_image=(*image); if (crop_geometry != (char *) NULL) { Image *cropped_image; RectangleInfo crop_info; /* Crop image to a user specified size. */ crop_info.x=0; crop_info.y=0; flags= XParseGeometry(crop_geometry,&crop_info.x,&crop_info.y,&width,&height); if ((flags & WidthValue) == 0) width=(unsigned int) ((int) transformed_image->columns-crop_info.x); if ((flags & HeightValue) == 0) height=(unsigned int) ((int) transformed_image->rows-crop_info.y); if ((flags & XNegative) != 0) crop_info.x+=transformed_image->columns-width; if ((flags & YNegative) != 0) crop_info.y+=transformed_image->rows-height; if (strchr(crop_geometry,'%') != (char *) NULL) { /* Crop geometry is relative to image size. */ (void) ParseImageGeometry(crop_geometry,&x,&y,&width,&height); if (width > transformed_image->columns) width=transformed_image->columns; if (height > transformed_image->rows) height=transformed_image->rows; crop_info.x=width >> 1; crop_info.y=height >> 1; width=transformed_image->columns-width; height=transformed_image->rows-height; flags|=XValue | YValue; } crop_info.width=width; crop_info.height=height; if ((width == 0) || (height == 0) || ((flags & XValue) != 0) || ((flags & YValue) != 0)) cropped_image=CropImage(transformed_image,&crop_info); else { Image *next_image; /* Crop repeatedly to create uniform subimages. */ next_image=(Image *) NULL; cropped_image=(Image *) NULL; for (y=0; y < transformed_image->rows; y+=height) { for (x=0; x < transformed_image->columns; x+=width) { crop_info.width=width; crop_info.height=height; crop_info.x=x; crop_info.y=y; next_image=CropImage(transformed_image,&crop_info); if (next_image == (Image *) NULL) break; if (cropped_image == (Image *) NULL) cropped_image=next_image; else { next_image->previous=cropped_image; cropped_image->next=next_image; cropped_image=cropped_image->next; } } if (next_image == (Image *) NULL) break; } } if (cropped_image != (Image *) NULL) { DestroyImage(transformed_image); while (cropped_image->previous != (Image *) NULL) cropped_image=cropped_image->previous; transformed_image=cropped_image; } } /* Scale image to a user specified size. */ width=transformed_image->columns; height=transformed_image->rows; (void) ParseImageGeometry(image_geometry,&x,&y,&width,&height); sharpen=(width*height) < (transformed_image->rows*transformed_image->columns); if ((transformed_image->columns != width) || (transformed_image->rows != height)) { Image *zoomed_image; /* Zoom image. */ zoomed_image=ZoomImage(transformed_image,width,height,MitchellFilter); if (zoomed_image == (Image *) NULL) zoomed_image=ScaleImage(transformed_image,width,height); if (zoomed_image != (Image *) NULL) { DestroyImage(transformed_image); transformed_image=zoomed_image; } } if (sharpen) if ((transformed_image->columns >= 3) && (transformed_image->rows >= 3)) { Image *sharpened_image; /* Sharpen image. */ sharpened_image=SharpenImage(transformed_image,SharpenFactor); if (sharpened_image != (Image *) NULL) { DestroyImage(transformed_image); transformed_image=sharpened_image; } } *image=transformed_image; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % T r a n s f o r m R G B I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function TransformRGBImage converts the reference image from an alternate % colorspace. The transformation matrices are not the standard ones: the % weights are rescaled to normalized the range of the transformed values to % be [0..MaxRGB]. % % The format of the TransformRGBImage routine is: % % TransformRGBImage(image,colorspace) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o colorspace: An unsigned integer value that indicates the colorspace % the image is currently in. On return the image is in the RGB % color space. % % */ void TransformRGBImage(Image *image,const unsigned int colorspace) { #define B (MaxRGB+1)*2 #define G (MaxRGB+1) #define R 0 #define TransformRGBImageText " Transforming image colors... " static Quantum PCDMap[348] = /* Photo CD information beyond 100% white, Gamma 2.2 */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 76, 77, 78, 79, 80, 81, 82, 83, 84, 84, 85, 86, 87, 88, 89, 90, 91, 92, 92, 93, 94, 95, 96, 97, 98, 99, 99, 100, 101, 102, 103, 104, 105, 106, 106, 107, 108, 109, 110, 111, 112, 113, 114, 114, 115, 116, 117, 118, 119, 120, 121, 122, 122, 123, 124, 125, 126, 127, 128, 129, 129, 130, 131, 132, 133, 134, 135, 136, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151, 152, 153, 153, 154, 155, 156, 157, 158, 158, 159, 160, 161, 162, 163, 164, 165, 165, 166, 167, 168, 169, 170, 171, 172, 173, 173, 174, 175, 176, 177, 178, 178, 179, 180, 181, 182, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 192, 193, 194, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 205, 206, 207, 208, 209, 210, 210, 211, 212, 213, 214, 215, 216, 216, 217, 218, 219, 220, 221, 221, 222, 223, 224, 225, 225, 226, 227, 228, 228, 229, 230, 230, 231, 232, 233, 233, 234, 235, 235, 236, 237, 237, 238, 239, 239, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 247, 248, 248, 248, 249, 249, 249, 250, 250, 250, 250, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 255, 255, 255, 255, 255, 255 }; long *blue, *green, *red; Quantum *range_table; register int i, x, y, z; register Quantum *range_limit; register RunlengthPacket *p; assert(image != (Image *) NULL); if ((colorspace == RGBColorspace) || (colorspace == GRAYColorspace) || (colorspace == TransparentColorspace)) return; /* Allocate the tables. */ red=(long *) malloc(3*(MaxRGB+1)*sizeof(long)); green=(long *) malloc(3*(MaxRGB+1)*sizeof(long)); blue=(long *) malloc(3*(MaxRGB+1)*sizeof(long)); range_table=(Quantum *) malloc(4*(MaxRGB+1)*sizeof(Quantum)); if ((red == (long *) NULL) || (green == (long *) NULL) || (blue == (long *) NULL) || (range_table == (Quantum *) NULL)) { Warning("Unable to transform color space","Memory allocation failed"); return; } /* Initialize tables. */ for (i=0; i <= MaxRGB; i++) { range_table[i]=0; range_table[i+(MaxRGB+1)]=(Quantum) i; range_table[i+(MaxRGB+1)*2]=MaxRGB; } for (i=0; i <= MaxRGB; i++) range_table[i+(MaxRGB+1)*3]=MaxRGB; range_limit=range_table+(MaxRGB+1); switch (colorspace) { case OHTAColorspace: { /* Initialize OHTA tables: R = I1+1.00000*I2-0.66668*I3 G = I1+0.00000*I2+1.33333*I3 B = I1-1.00000*I2-0.66668*I3 I and Q, normally -0.5 through 0.5, must be normalized to the range 0 through MaxRGB. */ for (i=0; i <= MaxRGB; i++) { red[i+R]=UpShifted(1.00000)*i; green[i+R]=UpShifted(1.0000*0.5)*((i << 1)-MaxRGB); blue[i+R]=(-UpShifted(0.66668*0.5))*((i << 1)-MaxRGB); red[i+G]=UpShifted(1.00000)*i; green[i+G]=0; blue[i+G]=UpShifted(1.33333*0.5)*((i << 1)-MaxRGB); red[i+B]=UpShifted(1.00000)*i; green[i+B]=(-UpShifted(1.00000*0.5))*((i << 1)-MaxRGB); blue[i+B]=(-UpShifted(0.66668*0.5))*((i << 1)-MaxRGB); } break; } case XYZColorspace: { /* Initialize CIE XYZ tables: R = 3.240479*R-1.537150*G-0.498535*B G = -0.969256*R+1.875992*G+0.041556*B B = 0.055648*R-0.204043*G+1.057311*B */ for (i=0; i <= MaxRGB; i++) { red[i+R]=UpShifted(3.240479)*i; green[i+R]=(-UpShifted(1.537150))*i; blue[i+R]=(-UpShifted(0.498535))*i; red[i+G]=(-UpShifted(0.969256))*i; green[i+G]=UpShifted(1.875992)*i; blue[i+G]=UpShifted(0.041556)*i; red[i+B]=UpShifted(0.055648)*i; green[i+B]=(-UpShifted(0.204043))*i; blue[i+B]=UpShifted(1.057311)*i; } break; } case YCbCrColorspace: { /* Initialize YCbCr tables: R = Y +1.370707*Cr G = Y-0.336453*Cb-0.698195*Cr B = Y+1.732445*Cb Cb and Cr, normally -0.5 through 0.5, must be normalized to the range 0 through MaxRGB. */ for (i=0; i <= MaxRGB; i++) { red[i+R]=UpShifted(1.000000)*i; green[i+R]=0; blue[i+R]=UpShifted(1.370707*0.5)*((i << 1)-MaxRGB); red[i+G]=UpShifted(1.000000)*i; green[i+G]=(-UpShifted(0.336453*0.5))*((i << 1)-MaxRGB); blue[i+G]=(-UpShifted(0.698195*0.5))*((i << 1)-MaxRGB); red[i+B]=UpShifted(1.000000)*i; green[i+B]=UpShifted(1.732445*0.5)*((i << 1)-MaxRGB); blue[i+B]=0; } break; } case YCCColorspace: { /* Initialize YCC tables: R = Y +1.340762*C2 G = Y-0.317038*C1-0.682243*C2 B = Y+1.632639*C1 YCC is scaled by 1.3584. C1 zero is 156 and C2 is at 137. */ for (i=0; i <= MaxRGB; i++) { red[i+R]=UpShifted(1.3584)*i; green[i+R]=0; blue[i+R]=UpShifted(1.8215)*(i-UpScale(137)); red[i+G]=UpShifted(1.3584)*i; green[i+G]=(-(UpShifted(0.194*2.2179)*(i-UpScale(156)))); blue[i+G]=(-(UpShifted(0.509*1.8215)*(i-UpScale(137)))); red[i+B]=UpShifted(1.3584)*i; green[i+B]=UpShifted(2.2179)*(i-UpScale(156)); blue[i+B]=0; range_table[i+(MaxRGB+1)]=(Quantum) UpScale(PCDMap[DownScale(i)]); } for ( ; i < UpScale(348); i++) range_table[i+(MaxRGB+1)]=(Quantum) UpScale(PCDMap[DownScale(i)]); break; } case YIQColorspace: { /* Initialize YIQ tables: R = 0.97087*Y+1.17782*I+0.59800*Q G = 0.97087*Y-0.28626*I-0.72851*Q B = 0.97087*Y-1.27870*I+1.72801*Q I and Q, normally -0.5 through 0.5, must be normalized to the range 0 through MaxRGB. */ for (i=0; i <= MaxRGB; i++) { red[i+R]=UpShifted(0.97087)*i; green[i+R]=UpShifted(1.17782*0.5)*((i << 1)-MaxRGB); blue[i+R]=UpShifted(0.59800*0.5)*((i << 1)-MaxRGB); red[i+G]=UpShifted(0.97087)*i; green[i+G]=(-UpShifted(0.28626*0.5))*((i << 1)-MaxRGB); blue[i+G]=(-UpShifted(0.72851*0.5))*((i << 1)-MaxRGB); red[i+B]=UpShifted(0.97087)*i; green[i+B]=(-UpShifted(1.27870*0.5))*((i << 1)-MaxRGB); blue[i+B]=UpShifted(1.72801*0.5)*((i << 1)-MaxRGB); } break; } case YPbPrColorspace: { /* Initialize YPbPr tables: R = Y +1.402000*C2 G = Y-0.344136*C1+0.714136*C2 B = Y+1.772000*C1 Pb and Pr, normally -0.5 through 0.5, must be normalized to the range 0 through MaxRGB. */ for (i=0; i <= MaxRGB; i++) { red[i+R]=UpShifted(1.000000)*i; green[i+R]=0; blue[i+R]=UpShifted(1.402000*0.5)*((i << 1)-MaxRGB); red[i+G]=UpShifted(1.000000)*i; green[i+G]=(-UpShifted(0.344136*0.5))*((i << 1)-MaxRGB); blue[i+G]=UpShifted(0.714136*0.5)*((i << 1)-MaxRGB); red[i+B]=UpShifted(1.000000)*i; green[i+B]=UpShifted(1.772000*0.5)*((i << 1)-MaxRGB); blue[i+B]=0; } break; } case YUVColorspace: default: { /* Initialize YUV tables: R = Y +1.13980*V G = Y-0.39380*U-0.58050*V B = Y+2.02790*U U and V, normally -0.5 through 0.5, must be normalized to the range 0 through MaxRGB. */ for (i=0; i <= MaxRGB; i++) { red[i+R]=UpShifted(1.00000)*i; green[i+R]=0; blue[i+R]=UpShifted(1.13980*0.5)*((i << 1)-MaxRGB); red[i+G]=UpShifted(1.00000)*i; green[i+G]=(-UpShifted(0.39380*0.5))*((i << 1)-MaxRGB); blue[i+G]=(-UpShifted(0.58050*0.5))*((i << 1)-MaxRGB); red[i+B]=UpShifted(1.00000)*i; green[i+B]=UpShifted(2.02790*0.5)*((i << 1)-MaxRGB); blue[i+B]=0; } break; } } /* Convert to RGB. */ switch (image->class) { case DirectClass: default: { /* Convert DirectClass image. */ p=image->pixels; for (i=0; i < image->packets; i++) { x=p->red; y=p->green; z=p->blue; p->red=range_limit[DownShift(red[x+R]+green[y+R]+blue[z+R])]; p->green=range_limit[DownShift(red[x+G]+green[y+G]+blue[z+G])]; p->blue=range_limit[DownShift(red[x+B]+green[y+B]+blue[z+B])]; p++; if (QuantumTick(i,image)) ProgressMonitor(TransformRGBImageText,i,image->packets); } break; } case PseudoClass: { /* Convert PseudoClass image. */ for (i=0; i < image->colors; i++) { x=image->colormap[i].red; y=image->colormap[i].green; z=image->colormap[i].blue; image->colormap[i].red= range_limit[DownShift(red[x+R]+green[y+R]+blue[z+R])]; image->colormap[i].green= range_limit[DownShift(red[x+G]+green[y+G]+blue[z+G])]; image->colormap[i].blue= range_limit[DownShift(red[x+B]+green[y+B]+blue[z+B])]; } p=image->pixels; for (i=0; i < image->packets; i++) { x=p->red; y=p->green; z=p->blue; p->red=range_limit[DownShift(red[x+R]+green[y+R]+blue[z+R])]; p->green=range_limit[DownShift(red[x+G]+green[y+G]+blue[z+G])]; p->blue=range_limit[DownShift(red[x+B]+green[y+B]+blue[z+B])]; p++; } break; } } /* Free allocated memory. */ free((char *) range_table); free((char *) blue); free((char *) green); free((char *) red); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % T r a n s p a r e n t I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function TransparentImage creates a matte image associated with the % image. All pixel locations are initially set to opaque. Any pixel % that matches the specified color are set to transparent. % % The format of the TransparentImage routine is: % % TransparentImage(image,color) % % A description of each parameter follows: % % o image: The address of a structure of type Image; returned from % ReadImage. % % o color: A character string that contain an X11 color string. % % */ Export void TransparentImage(Image *image,char *color) { #define TransparentImageText " Setting transparent color in the image... " ColorPacket target; register int i; register RunlengthPacket *p; unsigned int status; XColor target_color; /* Determine RGB values of the transparent color. */ assert(image != (Image *) NULL); status=XQueryColorDatabase(color,&target_color); if (status == False) return; target.red=XDownScale(target_color.red); target.green=XDownScale(target_color.green); target.blue=XDownScale(target_color.blue); /* Make image color transparent. */ if (!image->matte) { /* Initialize image matte to opaque. */ image->class=DirectClass; image->matte=True; p=image->pixels; for (i=0; i < image->packets; i++) { p->index=Opaque; p++; } } p=image->pixels; for (i=0; i < image->packets; i++) { if (ColorMatch(*p,target,0)) p->index=Transparent; p++; if (QuantumTick(i,image)) ProgressMonitor(TransparentImageText,i,image->packets); } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % U n c o m p r e s s I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function UncompressImage uncompresses runlength-encoded pixels packets to % a rectangular array of pixels. % % The format of the UncompressImage routine is: % % status=UncompressImage(image) % % A description of each parameter follows: % % o status: Function UncompressImage returns True if the image is % uncompressed otherwise False. % % o image: The address of a structure of type Image. % % */ Export unsigned int UncompressImage(Image *image) { int length; register int i, j; register RunlengthPacket *p, *q; RunlengthPacket *uncompressed_pixels; assert(image != (Image *) NULL); if (image->packets == (image->columns*image->rows)) return(True); /* Uncompress runlength-encoded packets. */ uncompressed_pixels=(RunlengthPacket *) realloc((char *) image->pixels, image->columns*image->rows*sizeof(RunlengthPacket)); if (uncompressed_pixels == (RunlengthPacket *) NULL) { Warning("Unable to uncompress image","Memory allocation failed"); return(False); } p=uncompressed_pixels+(image->packets-1); q=uncompressed_pixels+(image->columns*image->rows-1); for (i=0; i < image->packets; i++) { length=p->length; for (j=0; j <= length; j++) { *q=(*p); q->length=0; q--; } p--; } image->packets=image->columns*image->rows; image->pixels=uncompressed_pixels; return(True); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % Z o o m I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ZoomImage creates a new image that is a scaled size of an % existing one. It allocates the memory necessary for the new Image % structure and returns a pointer to the new image. The Point filter gives % fast pixel replication, Triangle is equivalent to bi-linear interpolation, % and Mitchel giver slower, very high-quality results. % % The format of the ZoomImage routine is: % % zoomed_image=ZoomImage(image,columns,rows,filter) % % A description of each parameter follows: % % o zoomed_image: Function ZoomImage returns a pointer to the image after % scaling. A null image is returned if there is a memory shortage. % % o image: The address of a structure of type Image. % % o columns: An integer that specifies the number of columns in the zoomed % image. % % o rows: An integer that specifies the number of rows in the scaled % image. % % o filter: This unsigned integer is the filter type to used to zoom % the image. % % */ #define ZoomImageText " Zooming image... " static double Box(double x) { if ((x > -0.5) && (x <= 0.5)) return(1.0); return(0.0); } static double Mitchell(double x) { double b, c; b=1.0/3.0; c=1.0/3.0; if (x < 0) x=(-x); if (x < 1.0) { x=(((12.0-9.0*b-6.0*c)*(x*x*x))+((-18.0+12.0*b+6.0*c)*x*x)+(6.0-2.0*b))/ 6.0; return(x); } if (x < 2.0) { x=(((-1.0*b-6.0*c)*(x*x*x))+((6.0*b+30.0*c)*x*x)+((-12.0*b-48.0*c)*x)+ (8.0*b+24.0*c))/6.0; return(x); } return(0.0); } static double Triangle(double x) { if (x < 0.0) x=(-x); if (x < 1.0) return(1.0-x); return(0.0); } static void HorizontalFilter(Image *source,Image *destination,double x_factor, double (*FilterFunction)(double),ContributionInfo *contribution_info, Quantum *range_limit,double width,unsigned int span,unsigned int *quantum) { double center, scale_factor; int n, x; long blue_weight, green_weight, index_weight, red_weight, weight; register int i, j, y; register RunlengthPacket *p, *q; /* Apply filter to zoom horizontally from source to destination. */ scale_factor=1.0; if (x_factor < 1.0) { width/=x_factor; scale_factor/=x_factor; } for (x=0; x < destination->columns; x++) { n=0; center=(double) (x+0.5)/x_factor; for (i=(int) (center-width+0.5); i < (int) (center+width+0.5); i++) { j=i; if (j < 0) j=(-j); else if (j >= source->columns) j=(source->columns << 1)-j-1; if (j >= source->columns) j=0; contribution_info[n].pixel=j; contribution_info[n].weight= UpShifted(FilterFunction((i-center+0.5)/scale_factor)/scale_factor); n++; } q=destination->pixels+x; for (y=0; y < destination->rows; y++) { blue_weight=0; green_weight=0; red_weight=0; index_weight=0; for (i=0; i < n; i++) { weight=contribution_info[i].weight; p=source->pixels+(y*source->columns)+contribution_info[i].pixel; red_weight+=weight*p->red; green_weight+=weight*p->green; blue_weight+=weight*p->blue; index_weight+=weight*p->index; } q->red=range_limit[DownShift(red_weight)]; q->green=range_limit[DownShift(green_weight)]; q->blue=range_limit[DownShift(blue_weight)]; if (index_weight > UpShift(Opaque)) q->index=Opaque; else if (index_weight < UpShift(Transparent)) q->index=Transparent; else q->index=DownShift(index_weight); q->length=0; q+=destination->columns; } ProgressMonitor(ZoomImageText,*quantum,span); (*quantum)++; } } static void VerticalFilter(Image *source,Image *destination,double y_factor, double (*FilterFunction)(double),ContributionInfo *contribution_info, Quantum *range_limit,double width,unsigned int span,unsigned int *quantum) { double center, scale_factor; int n, y; long blue_weight, green_weight, index_weight, red_weight, weight; register int i, j, x; register RunlengthPacket *p, *q; /* Apply filter to zoom vertically from source to destination. */ scale_factor=1.0; if (y_factor < 1.0) { width/=y_factor; scale_factor/=y_factor; } q=destination->pixels; for (y=0; y < destination->rows; y++) { n=0; center=(double) (y+0.5)/y_factor; for (i=(int) (center-width+0.5); i < (int) (center+width+0.5); i++) { j=i; if (j < 0) j=(-j); else if (j >= source->rows) j=(source->rows << 1)-j-1; if (j >= source->rows) j=0; contribution_info[n].pixel=j; contribution_info[n].weight= UpShifted(FilterFunction((i-center+0.5)/scale_factor)/scale_factor); n++; } for (x=0; x < destination->columns; x++) { blue_weight=0; green_weight=0; red_weight=0; index_weight=0; for (i=0; i < n; i++) { weight=contribution_info[i].weight; p=source->pixels+(contribution_info[i].pixel*source->columns)+x; red_weight+=weight*p->red; green_weight+=weight*p->green; blue_weight+=weight*p->blue; index_weight+=weight*p->index; } q->red=range_limit[DownShift(red_weight)]; q->green=range_limit[DownShift(green_weight)]; q->blue=range_limit[DownShift(blue_weight)]; if (index_weight > UpShift(Opaque)) q->index=Opaque; else if (index_weight < UpShift(Transparent)) q->index=Transparent; else q->index=DownShift(index_weight); q->length=0; q++; } ProgressMonitor(ZoomImageText,*quantum,span); (*quantum)++; } } Export Image *ZoomImage(Image *image,const unsigned int columns, const unsigned int rows,const FilterType filter) { ContributionInfo *contribution_info; double (*FilterFunction)(double), filter_width, width, x_factor, y_factor; Image *source_image, *zoomed_image; Quantum *range_table; register int i; register Quantum *range_limit; unsigned int quantum, span; assert(image != (Image *) NULL); if ((columns == 0) || (rows == 0)) { Warning("Unable to zoom image","image dimensions are zero"); return((Image *) NULL); } /* Image must be uncompressed. */ if (!UncompressImage(image)) return((Image *) NULL); /* Initialize zoomed image attributes. */ zoomed_image=CloneImage(image,columns,rows,False); if (zoomed_image == (Image *) NULL) { Warning("Unable to zoom image","Memory allocation failed"); return((Image *) NULL); } zoomed_image->class=DirectClass; image->orphan=True; if (zoomed_image->rows >= image->rows) source_image=CloneImage(image,zoomed_image->columns,image->rows,False); else source_image=CloneImage(image,image->columns,zoomed_image->rows,False); image->orphan=False; if (source_image == (Image *) NULL) { Warning("Unable to zoom image","Memory allocation failed"); DestroyImage(zoomed_image); return((Image *) NULL); } /* Allocate the range table. */ range_table=(Quantum *) malloc(3*(MaxRGB+1)*sizeof(Quantum)); if (range_table == (Quantum *) NULL) { Warning("Unable to zoom image","Memory allocation failed"); DestroyImage(source_image); DestroyImage(zoomed_image); return((Image *) NULL); } /* Pre-compute conversion tables. */ for (i=0; i <= MaxRGB; i++) { range_table[i]=0; range_table[i+(MaxRGB+1)]=(Quantum) i; range_table[i+(MaxRGB+1)*2]=MaxRGB; } range_limit=range_table+(MaxRGB+1); /* Allocate filter info list. */ switch (filter) { case BoxFilter: { FilterFunction=Box; filter_width=0.5; break; } case TriangleFilter: { FilterFunction=Triangle; filter_width=1.0; break; } case MitchellFilter: default: { FilterFunction=Mitchell; filter_width=2.0; break; } } x_factor=(double) zoomed_image->columns/(double) image->columns; y_factor=(double) zoomed_image->rows/(double) image->rows; width=Max(filter_width/x_factor,filter_width/y_factor); if (width < filter_width) width=filter_width; contribution_info=(ContributionInfo *) malloc((int) (width*2+1)*sizeof(ContributionInfo)); if (contribution_info == (ContributionInfo *) NULL) { Warning("Unable to zoom image","Memory allocation failed"); free((char *) range_table); DestroyImage(source_image); DestroyImage(zoomed_image); return((Image *) NULL); } /* Zoom image. */ quantum=0; if (zoomed_image->rows >= image->rows) { span=source_image->columns+zoomed_image->rows; HorizontalFilter(image,source_image,x_factor,FilterFunction, contribution_info,range_limit,filter_width,span,&quantum); VerticalFilter(source_image,zoomed_image,y_factor,FilterFunction, contribution_info,range_limit,filter_width,span,&quantum); } else { span=zoomed_image->columns+source_image->columns; VerticalFilter(image,source_image,y_factor,FilterFunction, contribution_info,range_limit,filter_width,span,&quantum); HorizontalFilter(source_image,zoomed_image,x_factor,FilterFunction, contribution_info,range_limit,filter_width,span,&quantum); } /* Free allocated memory. */ free((char *) contribution_info); free((char *) range_table); DestroyImage(source_image); return(zoomed_image); }