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