This is shear.c in view mode; [Download] [Up]
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% SSSSS H H EEEEE AAA RRRR %
% SS H H E A A R R %
% SSS HHHHH EEE AAAAA RRRR %
% SS H H E A A R R %
% SSSSS H H EEEEE A A R R %
% %
% %
% Shear or rotate a raster image by an arbitrary angle. %
% %
% %
% %
% 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. %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Function RotateImage, XShearImage, and YShearImage is based on the paper
% "A Fast Algorithm for General Raster Rotatation" by Alan W. Paeth,
% Graphics Interface '86 (Vancouver). RotateImage is adapted from a similiar
% routine based on the Paeth paper written by Michael Halle of the Spatial
% Imaging Group, MIT Media Lab.
%
%
*/
/*
Include declarations.
*/
#include "magick.h"
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% C r o p S h e a r I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Function CropShearImage crops the sheared image as determined by the
% bounding box as defined by width and height and shearing angles.
%
% The format of the CropShearImage routine is:
%
% CropShearImage(image,x_shear,y_shear,width,height,crop)
%
% A description of each parameter follows.
%
% o image: The address of a structure of type Image.
%
% o x_shear, y_shear, width, height: Defines a region of the image to crop.
%
% o crop: A value other than zero crops the corners of the rotated
% image and retains the original image size.
%
%
*/
static void CropShearImage(Image **image,const double x_shear,
const double y_shear,const unsigned int width,const unsigned int height,
const unsigned int crop)
{
typedef struct Point
{
double
x,
y;
} Point;
char
geometry[MaxTextExtent];
double
x_max,
x_min,
y_max,
y_min;
Point
corners[4];
RectangleInfo
crop_info;
register int
i;
/*
Calculate the rotated image size.
*/
crop_info.width=width;
crop_info.height=height;
corners[0].x=(-((int) crop_info.width)/2.0);
corners[0].y=(-((int) crop_info.height)/2.0);
corners[1].x=((int) crop_info.width)/2.0;
corners[1].y=(-((int) crop_info.height)/2.0);
corners[2].x=(-((int) crop_info.width)/2.0);
corners[2].y=((int) crop_info.height)/2.0;
corners[3].x=((int) crop_info.width)/2.0;
corners[3].y=((int) crop_info.height)/2.0;
for (i=0; i < 4; i++)
{
corners[i].x+=x_shear*corners[i].y;
corners[i].y+=y_shear*corners[i].x;
corners[i].x+=x_shear*corners[i].y;
corners[i].x+=((*image)->columns-1)/2.0;
corners[i].y+=((*image)->rows-3)/2.0;
}
x_min=corners[0].x;
y_min=corners[0].y;
x_max=corners[0].x;
y_max=corners[0].y;
for (i=1; i < 4; i++)
{
if (x_min > corners[i].x)
x_min=corners[i].x;
if (y_min > corners[i].y)
y_min=corners[i].y;
if (x_max < corners[i].x)
x_max=corners[i].x;
if (y_max < corners[i].y)
y_max=corners[i].y;
}
x_min=floor((double) x_min);
x_max=ceil((double) x_max);
y_min=floor((double) y_min);
y_max=ceil((double) y_max);
if (!crop)
{
/*
Do not crop sheared image.
*/
crop_info.width=(unsigned int) (x_max-x_min)-1;
crop_info.height=(unsigned int) (y_max-y_min)-1;
}
crop_info.x=(int) x_min+(((int) (x_max-x_min)-crop_info.width) >> 1)+1;
crop_info.y=(int) y_min+(((int) (y_max-y_min)-crop_info.height) >> 1)+2;
/*
Crop image and return.
*/
(void) sprintf(geometry,"%ux%u%+d%+d",crop_info.width,crop_info.height,
crop_info.x,crop_info.y);
TransformImage(image,geometry,(char *) NULL);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% I n t e g r a l R o t a t e I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Function IntegralRotateImage rotates the image an integral of 90 degrees.
% It allocates the memory necessary for the new Image structure and returns
% a pointer to the rotated image.
%
% The format of the IntegralRotateImage routine is:
%
% rotated_image=IntegralRotateImage(image,rotations)
%
% A description of each parameter follows.
%
% o rotated_image: Function IntegralRotateImage returns a pointer to the
% rotated image. A null image is returned if there is a a memory shortage.
%
% o image: The address of a structure of type Image.
%
% o rotations: Specifies the number of 90 degree rotations.
%
%
*/
static Image *IntegralRotateImage(Image *image,unsigned int rotations)
{
#define RotateImageText " Rotating image... "
Image
*rotated_image;
register RunlengthPacket
*p,
*q;
register int
x,
y;
/*
Initialize rotated image attributes.
*/
rotations%=4;
if ((rotations == 1) || (rotations == 3))
rotated_image=CloneImage(image,image->rows,image->columns,False);
else
rotated_image=CloneImage(image,image->columns,image->rows,False);
if (rotated_image == (Image *) NULL)
{
Warning("Unable to rotate image","Memory allocation failed");
return((Image *) NULL);
}
/*
Expand runlength packets into a rectangular array of pixels.
*/
p=image->pixels;
image->runlength=p->length+1;
switch (rotations)
{
case 0:
{
/*
Rotate 0 degrees.
*/
q=rotated_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++;
}
ProgressMonitor(RotateImageText,y,image->rows);
}
break;
}
case 1:
{
/*
Rotate 90 degrees.
*/
for (x=0; x < rotated_image->columns; x++)
{
q=rotated_image->pixels+(rotated_image->columns-x)-1;
for (y=0; y < rotated_image->rows; y++)
{
if (image->runlength != 0)
image->runlength--;
else
{
p++;
image->runlength=p->length;
}
*q=(*p);
q->length=0;
q+=rotated_image->columns;
}
ProgressMonitor(RotateImageText,x,rotated_image->columns);
}
break;
}
case 2:
{
/*
Rotate 180 degrees.
*/
q=rotated_image->pixels+(rotated_image->columns*rotated_image->rows)-1;
for (y=image->rows-1; y >= 0; 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--;
}
ProgressMonitor(RotateImageText,image->rows-y,image->rows);
}
break;
}
case 3:
{
/*
Rotate 270 degrees.
*/
for (x=rotated_image->columns-1; x >= 0; x--)
{
q=rotated_image->pixels+(rotated_image->columns*rotated_image->rows)-
x-1;
for (y=0; y < rotated_image->rows; y++)
{
if (image->runlength != 0)
image->runlength--;
else
{
p++;
image->runlength=p->length;
}
*q=(*p);
q->length=0;
q-=rotated_image->columns;
}
ProgressMonitor(RotateImageText,rotated_image->columns-x,
rotated_image->columns);
}
break;
}
}
return(rotated_image);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% X S h e a r I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Procedure XShearImage shears the image in the X direction with a shear angle
% of 'degrees'. Positive angles shear counter-clockwise (right-hand rule),
% and negative angles shear clockwise. Angles are measured relative to a
% vertical Y-axis. X shears will widen an image creating 'empty' triangles
% on the left and right sides of the source image.
%
% The format of the XShearImage routine is:
%
% XShearImage(image,degrees,width,height,x_offset,y_offset,range_limit)
%
% A description of each parameter follows.
%
% o image: The address of a structure of type Image.
%
% o degrees: A double representing the shearing angle along the X axis.
%
% o width, height, x_offset, y_offset: Defines a region of the image
% to shear.
%
*/
static void XShearImage(Image *image,const double degrees,
const unsigned int width,const unsigned int height,const int x_offset,
int y_offset,register Quantum *range_limit)
{
#define XShearImageText " X Shear image... "
double
displacement;
enum {LEFT,RIGHT}
direction;
int
step,
y;
long
fractional_step;
register RunlengthPacket
*p,
*q;
register int
blue,
green,
i,
index,
red;
RunlengthPacket
last_pixel;
y_offset--;
for (y=0; y < height; y++)
{
y_offset++;
displacement=degrees*(((double) y)-(height-1)/2.0);
if (displacement == 0.0)
continue;
if (displacement > 0.0)
direction=RIGHT;
else
{
displacement*=(-1.0);
direction=LEFT;
}
step=(int) floor(displacement);
fractional_step=UpShifted(displacement-(double) step);
if (fractional_step == 0)
{
/*
No fractional displacement-- just copy.
*/
switch (direction)
{
case LEFT:
{
/*
Transfer pixels left-to-right.
*/
p=image->pixels+image->columns*y_offset+x_offset;
q=p-step;
for (i=0; i < width; i++)
{
*q=(*p);
q++;
p++;
}
/*
Set old row to background color.
*/
for (i=0; i < step; i++)
{
q->red=image->background_color.red;
q->green=image->background_color.green;
q->blue=image->background_color.blue;
q->index=image->background_color.index;
q++;
}
break;
}
case RIGHT:
{
/*
Transfer pixels right-to-left.
*/
p=image->pixels+image->columns*y_offset+x_offset+width;
q=p+step;
for (i=0; i < width; i++)
{
p--;
q--;
*q=(*p);
}
/*
Set old row to background color.
*/
for (i=0; i < step; i++)
{
q--;
q->red=image->background_color.red;
q->green=image->background_color.green;
q->blue=image->background_color.blue;
q->index=image->background_color.index;
}
break;
}
}
continue;
}
/*
Fractional displacement.
*/
step++;
last_pixel.red=image->background_color.red;
last_pixel.green=image->background_color.green;
last_pixel.blue=image->background_color.blue;
last_pixel.index=image->background_color.index;
switch (direction)
{
case LEFT:
{
/*
Transfer pixels left-to-right.
*/
p=image->pixels+image->columns*y_offset+x_offset;
q=p-step;
for (i=0; i < width; i++)
{
red=DownShift(last_pixel.red*(UpShift(1)-fractional_step)+p->red*
fractional_step);
green=DownShift(last_pixel.green*(UpShift(1)-fractional_step)+
p->green*fractional_step);
blue=DownShift(last_pixel.blue*(UpShift(1)-fractional_step)+p->blue*
fractional_step);
index=DownShift(last_pixel.index*(UpShift(1)-fractional_step)+
p->index*fractional_step);
last_pixel=(*p);
p++;
q->red=range_limit[red];
q->green=range_limit[green];
q->blue=range_limit[blue];
if (index < 0)
q->index=0;
else
if (index > MaxColormapSize)
q->index=MaxColormapSize;
else
q->index=(unsigned short) index;
q++;
}
/*
Set old row to background color.
*/
red=DownShift(last_pixel.red*(UpShift(1)-fractional_step)+
image->background_color.red*fractional_step);
green=DownShift(last_pixel.green*(UpShift(1)-fractional_step)+
image->background_color.green*fractional_step);
blue=DownShift(last_pixel.blue*(UpShift(1)-fractional_step)+
image->background_color.blue*fractional_step);
index=DownShift(last_pixel.index*(UpShift(1)-fractional_step)+
image->background_color.index*fractional_step);
q->red=range_limit[red];
q->green=range_limit[green];
q->blue=range_limit[blue];
if (index < 0)
q->index=0;
else
if (index > MaxColormapSize)
q->index=MaxColormapSize;
else
q->index=(unsigned short) index;
q++;
for (i=0; i < step-1; i++)
{
q->red=image->background_color.red;
q->green=image->background_color.green;
q->blue=image->background_color.blue;
q->index=image->background_color.index;
q++;
}
break;
}
case RIGHT:
{
/*
Transfer pixels right-to-left.
*/
p=image->pixels+image->columns*y_offset+x_offset+width;
q=p+step;
for (i=0; i < width; i++)
{
p--;
red=DownShift(last_pixel.red*(UpShift(1)-fractional_step)+p->red*
fractional_step);
green=DownShift(last_pixel.green*(UpShift(1)-fractional_step)+
p->green*fractional_step);
blue=DownShift(last_pixel.blue*(UpShift(1)-fractional_step)+p->blue*
fractional_step);
index=DownShift(last_pixel.index*(UpShift(1)-fractional_step)+
p->index*fractional_step);
last_pixel=(*p);
q--;
q->red=range_limit[red];
q->green=range_limit[green];
q->blue=range_limit[blue];
if (index < 0)
q->index=0;
else
if (index > MaxColormapSize)
q->index=MaxColormapSize;
else
q->index=(unsigned short) index;
}
/*
Set old row to background color.
*/
red=DownShift(last_pixel.red*(UpShift(1)-fractional_step)+
image->background_color.red*fractional_step);
green=DownShift(last_pixel.green*(UpShift(1)-fractional_step)+
image->background_color.green*fractional_step);
blue=DownShift(last_pixel.blue*(UpShift(1)-fractional_step)+
image->background_color.blue*fractional_step);
index=DownShift(last_pixel.index*(UpShift(1)-fractional_step)+
image->background_color.index*fractional_step);
q--;
q->red=range_limit[red];
q->green=range_limit[green];
q->blue=range_limit[blue];
if (index < 0)
q->index=0;
else
if (index > MaxColormapSize)
q->index=MaxColormapSize;
else
q->index=(unsigned short) index;
for (i=0; i < step-1; i++)
{
q--;
q->red=image->background_color.red;
q->green=image->background_color.green;
q->blue=image->background_color.blue;
q->index=image->background_color.index;
}
break;
}
}
ProgressMonitor(XShearImageText,y,height);
}
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% Y S h e a r I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Procedure YShearImage shears the image in the Y direction with a shear
% angle of 'degrees'. Positive angles shear counter-clockwise (right-hand
% rule), and negative angles shear clockwise. Angles are measured relative
% to a horizontal X-axis. Y shears will increase the height of an image
% creating 'empty' triangles on the top and bottom of the source image.
%
% The format of the YShearImage routine is:
%
% YShearImage(image,degrees,width,height,x_offset,y_offset,range_limit)
%
% A description of each parameter follows.
%
% o image: The address of a structure of type Image.
%
% o degrees: A double representing the shearing angle along the Y axis.
%
% o width, height, x_offset, y_offset: Defines a region of the image
% to shear.
%
%
*/
static void YShearImage(Image *image,const double degrees,
const unsigned int width,const unsigned int height,int x_offset,
const int y_offset,register Quantum *range_limit)
{
#define YShearImageText " Y Shear image... "
double
displacement;
enum {UP,DOWN}
direction;
int
step,
y;
long
fractional_step;
register RunlengthPacket
*p,
*q;
register int
blue,
green,
i,
index,
red;
RunlengthPacket
last_pixel;
x_offset--;
for (y=0; y < width; y++)
{
x_offset++;
displacement=degrees*(((double) y)-(width-1)/2.0);
if (displacement == 0.0)
continue;
if (displacement > 0.0)
direction=DOWN;
else
{
displacement*=(-1.0);
direction=UP;
}
step=(int) floor(displacement);
fractional_step=UpShifted(displacement-(double) step);
if (fractional_step == 0)
{
/*
No fractional displacement-- just copy the pixels.
*/
switch (direction)
{
case UP:
{
/*
Transfer pixels top-to-bottom.
*/
p=image->pixels+image->columns*y_offset+x_offset;
q=p-step*image->columns;
for (i=0; i < height; i++)
{
*q=(*p);
q+=image->columns;
p+=image->columns;
}
/*
Set old column to background color.
*/
for (i=0; i < step; i++)
{
q->red=image->background_color.red;
q->green=image->background_color.green;
q->blue=image->background_color.blue;
q->index=image->background_color.index;
q+=image->columns;
}
break;
}
case DOWN:
{
/*
Transfer pixels bottom-to-top.
*/
p=image->pixels+image->columns*(y_offset+height)+x_offset;
q=p+step*image->columns;
for (i=0; i < height; i++)
{
q-=image->columns;
p-=image->columns;
*q=(*p);
}
/*
Set old column to background color.
*/
for (i=0; i < step; i++)
{
q-=image->columns;
q->red=image->background_color.red;
q->green=image->background_color.green;
q->blue=image->background_color.blue;
q->index=image->background_color.index;
}
break;
}
}
continue;
}
/*
Fractional displacment.
*/
step++;
last_pixel.red=image->background_color.red;
last_pixel.green=image->background_color.green;
last_pixel.blue=image->background_color.blue;
last_pixel.index=image->background_color.index;
switch (direction)
{
case UP:
{
/*
Transfer pixels top-to-bottom.
*/
p=image->pixels+image->columns*y_offset+x_offset;
q=p-step*image->columns;
for (i=0; i < height; i++)
{
red=DownShift(last_pixel.red*(UpShift(1)-fractional_step)+p->red*
fractional_step);
green=DownShift(last_pixel.green*(UpShift(1)-fractional_step)+
p->green*fractional_step);
blue=DownShift(last_pixel.blue*(UpShift(1)-fractional_step)+p->blue*
fractional_step);
index=DownShift(last_pixel.index*(UpShift(1)-fractional_step)+
p->index*fractional_step);
last_pixel=(*p);
p+=image->columns;
q->red=range_limit[red];
q->green=range_limit[green];
q->blue=range_limit[blue];
if (index < 0)
q->index=0;
else
if (index > MaxColormapSize)
q->index=MaxColormapSize;
else
q->index=(unsigned short) index;
q+=image->columns;
}
/*
Set old column to background color.
*/
red=DownShift(last_pixel.red*(UpShift(1)-fractional_step)+
image->background_color.red*fractional_step);
green=DownShift(last_pixel.green*(UpShift(1)-fractional_step)+
image->background_color.green*fractional_step);
blue=DownShift(last_pixel.blue*(UpShift(1)-fractional_step)+
image->background_color.blue*fractional_step);
index=DownShift(last_pixel.index*(UpShift(1)-fractional_step)+
image->background_color.index*fractional_step);
q->red=range_limit[red];
q->green=range_limit[green];
q->blue=range_limit[blue];
if (index < 0)
q->index=0;
else
if (index > MaxColormapSize)
q->index=MaxColormapSize;
else
q->index=(unsigned short) index;
q+=image->columns;
for (i=0; i < step-1; i++)
{
q->red=image->background_color.red;
q->green=image->background_color.green;
q->blue=image->background_color.blue;
q->index=image->background_color.index;
q+=image->columns;
}
break;
}
case DOWN:
{
/*
Transfer pixels bottom-to-top.
*/
p=image->pixels+image->columns*(y_offset+height)+x_offset;
q=p+step*image->columns;
for (i=0; i < height; i++)
{
p-=image->columns;
red=DownShift(last_pixel.red*(UpShift(1)-fractional_step)+p->red*
fractional_step);
green=DownShift(last_pixel.green*(UpShift(1)-fractional_step)+
p->green*fractional_step);
blue=DownShift(last_pixel.blue*(UpShift(1)-fractional_step)+p->blue*
fractional_step);
index=DownShift(last_pixel.index*(UpShift(1)-fractional_step)+
p->index*fractional_step);
last_pixel=(*p);
q-=image->columns;
q->red=range_limit[red];
q->green=range_limit[green];
q->blue=range_limit[blue];
if (index < 0)
q->index=0;
else
if (index > MaxColormapSize)
q->index=MaxColormapSize;
else
q->index=(unsigned short) index;
}
/*
Set old column to background color.
*/
red=DownShift(last_pixel.red*(UpShift(1)-fractional_step)+
image->background_color.red*fractional_step);
green=DownShift(last_pixel.green*(UpShift(1)-fractional_step)+
image->background_color.green*fractional_step);
blue=DownShift(last_pixel.blue*(UpShift(1)-fractional_step)+
image->background_color.blue*fractional_step);
index=DownShift(last_pixel.index*(UpShift(1)-fractional_step)+
image->background_color.index*fractional_step);
q-=image->columns;
q->red=range_limit[red];
q->green=range_limit[green];
q->blue=range_limit[blue];
if (index < 0)
q->index=0;
else
if (index > MaxColormapSize)
q->index=MaxColormapSize;
else
q->index=(unsigned short) index;
for (i=0; i < step-1; i++)
{
q-=image->columns;
q->red=image->background_color.red;
q->green=image->background_color.green;
q->blue=image->background_color.blue;
q->index=image->background_color.index;
}
break;
}
}
ProgressMonitor(YShearImageText,y,width);
}
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% R o t a t e I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Function RotateImage creates a new image that is a rotated copy of an
% existing one. Positive angles rotate counter-clockwise (right-hand rule),
% while negative angles rotate clockwise. Rotated images are usually larger
% than the originals and have 'empty' triangular corners. X axis. Empty
% triangles left over from shearing the image are filled with the color
% defined by the pixel at location (0,0). RotateImage allocates the memory
% necessary for the new Image structure and returns a pointer to the new
% image.
%
% Function RotateImage is based on the paper "A Fast Algorithm for General
% Raster Rotatation" by Alan W. Paeth. RotateImage is adapted from a similiar
% routine based on the Paeth paper written by Michael Halle of the Spatial
% Imaging Group, MIT Media Lab.
%
% The format of the RotateImage routine is:
%
% RotateImage(image,degrees,crop,sharpen)
%
% A description of each parameter follows.
%
% o status: Function RotateImage returns a pointer to the image after
% rotating. A null image is returned if there is a memory shortage.
%
% o image: The address of a structure of type Image; returned from
% ReadImage.
%
% o degrees: Specifies the number of degrees to rotate the image.
%
% o crop: A value other than zero crops the corners of the rotated
% image and retains the original image size.
%
% o sharpen: A value other than zero sharpens the image after it is
% rotated.
%
%
*/
Export Image *RotateImage(Image *image,double degrees,const unsigned int crop,
const unsigned int sharpen)
{
double
x_shear,
y_shear;
Image
*integral_image,
*rotated_image,
*sharpened_image;
int
x_offset,
y_offset;
Quantum
*range_limit,
*range_table;
RectangleInfo
border_info;
register int
i;
unsigned int
height,
rotations,
width,
y_width;
/*
Adjust rotation angle.
*/
assert(image != (Image *) NULL);
while (degrees < -45.0)
degrees+=360.0;
for (rotations=0; degrees > 45.0; rotations++)
degrees-=90.0;
rotations%=4;
/*
Calculate shear equations.
*/
x_shear=(-tan(DegreesToRadians(degrees)/2.0));
y_shear=sin(DegreesToRadians(degrees));
integral_image=IntegralRotateImage(image,rotations);
if ((x_shear == 0.0) || (y_shear == 0.0))
return(integral_image);
/*
Initialize range table.
*/
range_table=(Quantum *) malloc(3*(MaxRGB+1)*sizeof(Quantum));
if (range_table == (Quantum *) NULL)
{
DestroyImage(integral_image);
Warning("Unable to rotate image","Memory allocation failed");
return((Image *) NULL);
}
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);
/*
Compute image size.
*/
width=image->columns;
height=image->rows;
if ((rotations == 1) || (rotations == 3))
{
width=image->rows;
height=image->columns;
}
y_width=width+(int) ceil(fabs(x_shear)*(double) (height-1));
x_offset=(width+
((int) ceil(fabs(x_shear)*(double) (height-1)) << 1)-width) >> 1;
y_offset=(height+(int) ceil(fabs(y_shear)*(double) (y_width-1))-height) >> 1;
/*
Surround image with border of background color.
*/
border_info.width=x_offset;
border_info.height=y_offset+1;
rotated_image=BorderImage(integral_image,&border_info);
DestroyImage(integral_image);
if (rotated_image == (Image *) NULL)
{
Warning("Unable to rotate image","Memory allocation failed");
return((Image *) NULL);
}
rotated_image->class=DirectClass;
/*
Perform a fractional rotation. First, shear the image rows.
*/
XShearImage(rotated_image,x_shear,width,height,x_offset,
((int) (rotated_image->rows-height) >> 1),range_limit);
/*
Shear the image columns.
*/
YShearImage(rotated_image,y_shear,y_width,height,
((int) (rotated_image->columns-y_width) >> 1),y_offset+1,range_limit);
/*
Shear the image rows again.
*/
XShearImage(rotated_image,x_shear,y_width,rotated_image->rows-2,
((int) (rotated_image->columns-y_width) >> 1),1,range_limit);
free((char *) range_table);
/*
Crop image.
*/
CropShearImage(&rotated_image,x_shear,y_shear,width,height,crop);
if (sharpen)
{
/*
Sharpen image.
*/
sharpened_image=SharpenImage(rotated_image,SharpenFactor);
if (sharpened_image != (Image *) NULL)
{
DestroyImage(rotated_image);
rotated_image=sharpened_image;
}
}
return(rotated_image);
}
/*
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% %
% %
% S h e a r I m a g e %
% %
% %
% %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Function ShearImage creates a new image that is a sheared copy of an
% existing one. Shearing slides one edge of an image along the X or Y
% axis, creating a parallelogram. An X direction shear slides an edge
% along the X axis, while a Y direction shear slides an edge along the Y
% axis. The amount of the shear is controlled by a shear angle. For X
% direction shears, x_shear is measured relative to the Y axis, and
% similarly, for Y direction shears y_shear is measured relative to the
% X axis. Empty triangles left over from shearing the image are filled
% with the color defined by the pixel at location (0,0). ShearImage
% allocates the memory necessary for the new Image structure and returns
% a pointer to the new image.
%
% Function ShearImage is based on the paper "A Fast Algorithm for General
% Raster Rotatation" by Alan W. Paeth.
%
% The format of the ShearImage routine is:
%
% ShearImage(image,x_shear,y_shear,crop)
%
% A description of each parameter follows.
%
% o status: Function ShearImage returns a pointer to the image after
% rotating. A null image is returned if there is a memory shortage.
%
% o image: The address of a structure of type Image; returned from
% ReadImage.
%
% o x_shear, y_shear: Specifies the number of degrees to shear the image.
%
% o crop: A value other than zero crops the corners of the rotated
% image and retains the original image size.
%
%
*/
Export Image *ShearImage(Image *image,double x_shear,double y_shear,
const unsigned int crop)
{
Image
*sharpened_image,
*sheared_image;
int
x_offset,
y_offset;
Quantum
*range_limit,
*range_table;
RectangleInfo
border_info;
register int
i;
unsigned int
y_width;
assert(image != (Image *) NULL);
if ((x_shear == 180.0) || (y_shear == 180.0))
{
Warning("Unable to shear image","angle is discontinuous");
return((Image *) NULL);
}
/*
Initialize shear angle.
*/
x_shear=(-tan(DegreesToRadians(x_shear)/2.0));
y_shear=sin(DegreesToRadians(y_shear));
/*
Initialize range table.
*/
range_table=(Quantum *) malloc(3*(MaxRGB+1)*sizeof(Quantum));
if (range_table == (Quantum *) NULL)
{
Warning("Unable to shear image","Memory allocation failed");
return((Image *) NULL);
}
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);
/*
Compute image size.
*/
y_width=image->columns+(int) ceil(fabs(x_shear)*(double) (image->rows-1));
x_offset=(image->columns+((int) ceil(fabs(x_shear)*(double)
(image->rows-1)) << 1)-image->columns) >> 1;
y_offset=(image->rows+(int) ceil(fabs(y_shear)*(double) (y_width-1))-
image->rows) >> 1;
/*
Surround image with border of background color.
*/
border_info.width=x_offset;
border_info.height=y_offset+1;
sheared_image=BorderImage(image,&border_info);
if (sheared_image == (Image *) NULL)
{
Warning("Unable to shear image","Memory allocation failed");
return((Image *) NULL);
}
sheared_image->class=DirectClass;
/*
Shear the image rows.
*/
XShearImage(sheared_image,x_shear,image->columns,image->rows,x_offset,
((int) (sheared_image->rows-image->rows) >> 1),range_limit);
/*
Shear the image columns.
*/
YShearImage(sheared_image,y_shear,y_width,image->rows,
((int) (sheared_image->columns-y_width) >> 1),y_offset+1,range_limit);
free((char *) range_table);
/*
Crop image.
*/
CropShearImage(&sheared_image,x_shear,y_shear,image->columns,image->rows,
crop);
/*
Sharpen image.
*/
sharpened_image=SharpenImage(sheared_image,SharpenFactor);
if (sharpened_image != (Image *) NULL)
{
DestroyImage(sheared_image);
sheared_image=sharpened_image;
}
return(sheared_image);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.