/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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 % % % % % %