shear.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %                      SSSSS  H   H  EEEEE   AAA    RRRR                      %
00007 %                      SS     H   H  E      A   A   R   R                     %
00008 %                       SSS   HHHHH  EEE    AAAAA   RRRR                      %
00009 %                         SS  H   H  E      A   A   R R                       %
00010 %                      SSSSS  H   H  EEEEE  A   A   R  R                      %
00011 %                                                                             %
00012 %                                                                             %
00013 %    MagickCore Methods to Shear or Rotate an Image by an Arbitrary Angle     %
00014 %                                                                             %
00015 %                               Software Design                               %
00016 %                                 John Cristy                                 %
00017 %                                  July 1992                                  %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization      %
00021 %  dedicated to making software imaging solutions freely available.           %
00022 %                                                                             %
00023 %  You may not use this file except in compliance with the License.  You may  %
00024 %  obtain a copy of the License at                                            %
00025 %                                                                             %
00026 %    http://www.imagemagick.org/script/license.php                            %
00027 %                                                                             %
00028 %  Unless required by applicable law or agreed to in writing, software        %
00029 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00030 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00031 %  See the License for the specific language governing permissions and        %
00032 %  limitations under the License.                                             %
00033 %                                                                             %
00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00035 %
00036 %  The RotateImage, XShearImage, and YShearImage methods are based on the
00037 %  paper "A Fast Algorithm for General Raster Rotatation" by Alan W. Paeth,
00038 %  Graphics Interface '86 (Vancouver).  RotateImage is adapted from a similar
00039 %  method based on the Paeth paper written by Michael Halle of the Spatial
00040 %  Imaging Group, MIT Media Lab.
00041 %
00042 %
00043 */
00044 
00045 /*
00046   Include declarations.
00047 */
00048 #include "magick/studio.h"
00049 #include "magick/artifact.h"
00050 #include "magick/attribute.h"
00051 #include "magick/blob-private.h"
00052 #include "magick/cache-private.h"
00053 #include "magick/color-private.h"
00054 #include "magick/colorspace-private.h"
00055 #include "magick/composite.h"
00056 #include "magick/composite-private.h"
00057 #include "magick/decorate.h"
00058 #include "magick/distort.h"
00059 #include "magick/draw.h"
00060 #include "magick/exception.h"
00061 #include "magick/exception-private.h"
00062 #include "magick/gem.h"
00063 #include "magick/geometry.h"
00064 #include "magick/image.h"
00065 #include "magick/image-private.h"
00066 #include "magick/memory_.h"
00067 #include "magick/list.h"
00068 #include "magick/monitor.h"
00069 #include "magick/monitor-private.h"
00070 #include "magick/pixel-private.h"
00071 #include "magick/quantum.h"
00072 #include "magick/resource_.h"
00073 #include "magick/shear.h"
00074 #include "magick/statistic.h"
00075 #include "magick/threshold.h"
00076 #include "magick/transform.h"
00077 
00078 /*
00079 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00080 %                                                                             %
00081 %                                                                             %
00082 %                                                                             %
00083 %     A f f i n e T r a n s f o r m I m a g e                                 %
00084 %                                                                             %
00085 %                                                                             %
00086 %                                                                             %
00087 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00088 %
00089 %  AffineTransformImage() transforms an image as dictated by the affine matrix.
00090 %  It allocates the memory necessary for the new Image structure and returns
00091 %  a pointer to the new image.
00092 %
00093 %  The format of the AffineTransformImage method is:
00094 %
00095 %      Image *AffineTransformImage(const Image *image,
00096 %        AffineMatrix *affine_matrix,ExceptionInfo *exception)
00097 %
00098 %  A description of each parameter follows:
00099 %
00100 %    o image: the image.
00101 %
00102 %    o affine_matrix: the affine matrix.
00103 %
00104 %    o exception: return any errors or warnings in this structure.
00105 %
00106 */
00107 MagickExport Image *AffineTransformImage(const Image *image,
00108   const AffineMatrix *affine_matrix,ExceptionInfo *exception)
00109 {
00110   double
00111     distort[6];
00112 
00113   Image
00114     *deskew_image;
00115 
00116   /*
00117     Affine transform image.
00118   */
00119   assert(image->signature == MagickSignature);
00120   if (image->debug != MagickFalse)
00121     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00122   assert(affine_matrix != (AffineMatrix *) NULL);
00123   assert(exception != (ExceptionInfo *) NULL);
00124   assert(exception->signature == MagickSignature);
00125   distort[0]=affine_matrix->sx;
00126   distort[1]=affine_matrix->rx;
00127   distort[2]=affine_matrix->ry;
00128   distort[3]=affine_matrix->sy;
00129   distort[4]=affine_matrix->tx;
00130   distort[5]=affine_matrix->ty;
00131   deskew_image=DistortImage(image,AffineProjectionDistortion,6,distort,
00132     MagickTrue,exception);
00133   return(deskew_image);
00134 }
00135 
00136 /*
00137 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00138 %                                                                             %
00139 %                                                                             %
00140 %                                                                             %
00141 +   C r o p T o F i t I m a g e                                               %
00142 %                                                                             %
00143 %                                                                             %
00144 %                                                                             %
00145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00146 %
00147 %  CropToFitImage() crops the sheared image as determined by the bounding box
00148 %  as defined by width and height and shearing angles.
00149 %
00150 %  The format of the CropToFitImage method is:
00151 %
00152 %      Image *CropToFitImage(Image **image,const MagickRealType x_shear,
00153 %        const MagickRealType x_shear,const MagickRealType width,
00154 %        const MagickRealType height,const MagickBooleanType rotate,
00155 %        ExceptionInfo *exception)
00156 %
00157 %  A description of each parameter follows.
00158 %
00159 %    o image: the image.
00160 %
00161 %    o x_shear, y_shear, width, height: Defines a region of the image to crop.
00162 %
00163 %    o exception: return any errors or warnings in this structure.
00164 %
00165 */
00166 static inline void CropToFitImage(Image **image,const MagickRealType x_shear,
00167   const MagickRealType y_shear,const MagickRealType width,
00168   const MagickRealType height,const MagickBooleanType rotate,
00169   ExceptionInfo *exception)
00170 {
00171   Image
00172     *crop_image;
00173 
00174   PointInfo
00175     extent[4],
00176     min,
00177     max;
00178 
00179   RectangleInfo
00180     geometry,
00181     page;
00182 
00183   register long
00184     i;
00185 
00186   /*
00187     Calculate the rotated image size.
00188   */
00189   extent[0].x=(double) (-width/2.0);
00190   extent[0].y=(double) (-height/2.0);
00191   extent[1].x=(double) width/2.0;
00192   extent[1].y=(double) (-height/2.0);
00193   extent[2].x=(double) (-width/2.0);
00194   extent[2].y=(double) height/2.0;
00195   extent[3].x=(double) width/2.0;
00196   extent[3].y=(double) height/2.0;
00197   for (i=0; i < 4; i++)
00198   {
00199     extent[i].x+=x_shear*extent[i].y;
00200     extent[i].y+=y_shear*extent[i].x;
00201     if (rotate != MagickFalse)
00202       extent[i].x+=x_shear*extent[i].y;
00203     extent[i].x+=(double) (*image)->columns/2.0;
00204     extent[i].y+=(double) (*image)->rows/2.0;
00205   }
00206   min=extent[0];
00207   max=extent[0];
00208   for (i=1; i < 4; i++)
00209   {
00210     if (min.x > extent[i].x)
00211       min.x=extent[i].x;
00212     if (min.y > extent[i].y)
00213       min.y=extent[i].y;
00214     if (max.x < extent[i].x)
00215       max.x=extent[i].x;
00216     if (max.y < extent[i].y)
00217       max.y=extent[i].y;
00218   }
00219   geometry.x=(long) (min.x+0.5);
00220   geometry.y=(long) (min.y+0.5);
00221   geometry.width=(unsigned long) ((long) (max.x+0.5)-(long) (min.x+0.5));
00222   geometry.height=(unsigned long) ((long) (max.y+0.5)-(long) (min.y+0.5));
00223   page=(*image)->page;
00224   (void) ParseAbsoluteGeometry("0x0+0+0",&(*image)->page);
00225   crop_image=CropImage(*image,&geometry,exception);
00226   (*image)->page=page;
00227   if (crop_image != (Image *) NULL)
00228     {
00229       crop_image->page=page;
00230       *image=DestroyImage(*image);
00231       *image=crop_image;
00232     }
00233 }
00234 
00235 /*
00236 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00237 %                                                                             %
00238 %                                                                             %
00239 %                                                                             %
00240 %     D e s k e w I m a g e                                                   %
00241 %                                                                             %
00242 %                                                                             %
00243 %                                                                             %
00244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00245 %
00246 %  DeskewImage() removes skew from the image.  Skew is an artifact that
00247 %  occurs in scanned images because of the camera being misaligned,
00248 %  imperfections in the scanning or surface, or simply because the paper was
00249 %  not placed completely flat when scanned.
00250 %
00251 %  The format of the DeskewImage method is:
00252 %
00253 %      Image *DeskewImage(const Image *image,const double threshold,
00254 %        ExceptionInfo *exception)
00255 %
00256 %  A description of each parameter follows:
00257 %
00258 %    o image: the image.
00259 %
00260 %    o threshold: separate background from foreground.
00261 %
00262 %    o exception: return any errors or warnings in this structure.
00263 %
00264 */
00265 
00266 typedef struct _RadonInfo
00267 {
00268   CacheType
00269     type;
00270 
00271   unsigned long
00272     width,
00273     height;
00274 
00275   MagickSizeType
00276     length;
00277 
00278   MagickBooleanType
00279     mapped;
00280 
00281   char
00282     path[MaxTextExtent];
00283 
00284   int
00285     file;
00286 
00287   unsigned short
00288     *cells;
00289 } RadonInfo;
00290 
00291 static RadonInfo *DestroyRadonInfo(RadonInfo *radon_info)
00292 {
00293   assert(radon_info != (RadonInfo *) NULL);
00294   switch (radon_info->type)
00295   {
00296     case MemoryCache:
00297     {
00298       if (radon_info->mapped == MagickFalse)
00299         radon_info->cells=(unsigned short *) RelinquishMagickMemory(
00300           radon_info->cells);
00301       else
00302         radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,
00303           (size_t) radon_info->length);
00304       RelinquishMagickResource(MemoryResource,radon_info->length);
00305       break;
00306     }
00307     case MapCache:
00308     {
00309       radon_info->cells=(unsigned short *) UnmapBlob(radon_info->cells,(size_t)
00310         radon_info->length);
00311       RelinquishMagickResource(MapResource,radon_info->length);
00312     }
00313     case DiskCache:
00314     {
00315       if (radon_info->file != -1)
00316         (void) close(radon_info->file);
00317       (void) RelinquishUniqueFileResource(radon_info->path);
00318       RelinquishMagickResource(DiskResource,radon_info->length);
00319       break;
00320     }
00321     default:
00322       break;
00323   }
00324   return((RadonInfo *) RelinquishMagickMemory(radon_info));
00325 }
00326 
00327 static MagickBooleanType ResetRadonCells(RadonInfo *radon_info)
00328 {
00329   long
00330     y;
00331 
00332   register long
00333     x;
00334 
00335   ssize_t
00336     count;
00337 
00338   unsigned short
00339     value;
00340 
00341   if (radon_info->type != DiskCache)
00342     {
00343       (void) ResetMagickMemory(radon_info->cells,0,(size_t) radon_info->length);
00344       return(MagickTrue);
00345     }
00346   value=0;
00347   (void) MagickSeek(radon_info->file,0,SEEK_SET);
00348   for (y=0; y < (long) radon_info->height; y++)
00349   {
00350     for (x=0; x < (long) radon_info->width; x++)
00351     {
00352       count=write(radon_info->file,&value,sizeof(*radon_info->cells));
00353       if (count != (ssize_t) sizeof(*radon_info->cells))
00354         break;
00355     }
00356     if (x < (long) radon_info->width)
00357       break;
00358   }
00359   return(y < (long) radon_info->height ? MagickFalse : MagickTrue);
00360 }
00361 
00362 static RadonInfo *AcquireRadonInfo(const Image *image,const unsigned long width,
00363   const unsigned long height,ExceptionInfo *exception)
00364 {
00365   MagickBooleanType
00366     status;
00367 
00368   RadonInfo
00369     *radon_info;
00370 
00371   radon_info=(RadonInfo *) AcquireMagickMemory(sizeof(*radon_info));
00372   if (radon_info == (RadonInfo *) NULL)
00373     return((RadonInfo *) NULL);
00374   (void) ResetMagickMemory(radon_info,0,sizeof(*radon_info));
00375   radon_info->width=width;
00376   radon_info->height=height;
00377   radon_info->length=(MagickSizeType) width*height*sizeof(*radon_info->cells);
00378   radon_info->type=MemoryCache;
00379   status=AcquireMagickResource(AreaResource,radon_info->length);
00380   if ((status != MagickFalse) &&
00381       (radon_info->length == (MagickSizeType) ((size_t) radon_info->length)))
00382     {
00383       status=AcquireMagickResource(MemoryResource,radon_info->length);
00384       if (status != MagickFalse)
00385         {
00386           radon_info->mapped=MagickFalse;
00387           radon_info->cells=(unsigned short *) AcquireMagickMemory((size_t)
00388             radon_info->length);
00389           if (radon_info->cells == (unsigned short *) NULL)
00390             {
00391               radon_info->mapped=MagickTrue;
00392               radon_info->cells=(unsigned short *) MapBlob(-1,IOMode,0,(size_t)
00393                 radon_info->length);
00394             }
00395           if (radon_info->cells == (unsigned short *) NULL)
00396             RelinquishMagickResource(MemoryResource,radon_info->length);
00397         }
00398     }
00399   radon_info->file=(-1);
00400   if (radon_info->cells == (unsigned short *) NULL)
00401     {
00402       status=AcquireMagickResource(DiskResource,radon_info->length);
00403       if (status == MagickFalse)
00404         {
00405           (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
00406             "CacheResourcesExhausted","`%s'",image->filename);
00407           return(DestroyRadonInfo(radon_info));
00408         }
00409       radon_info->type=DiskCache;
00410       (void) AcquireMagickResource(MemoryResource,radon_info->length);
00411       radon_info->file=AcquireUniqueFileResource(radon_info->path);
00412       if (radon_info->file == -1)
00413         return(DestroyRadonInfo(radon_info));
00414       status=AcquireMagickResource(MapResource,radon_info->length);
00415       if (status != MagickFalse)
00416         {
00417           status=ResetRadonCells(radon_info);
00418           if (status != MagickFalse)
00419             {
00420               radon_info->cells=(unsigned short *) MapBlob(radon_info->file,
00421                 IOMode,0,(size_t) radon_info->length);
00422               if (radon_info->cells != (unsigned short *) NULL)
00423                 radon_info->type=MapCache;
00424               else
00425                 RelinquishMagickResource(MapResource,radon_info->length);
00426             }
00427         }
00428     }
00429   return(radon_info);
00430 }
00431 
00432 static inline size_t MagickMin(const size_t x,const size_t y)
00433 {
00434   if (x < y)
00435     return(x);
00436   return(y);
00437 }
00438 
00439 static inline ssize_t ReadRadonCell(const RadonInfo *radon_info,
00440   const off_t offset,const size_t length,unsigned char *buffer)
00441 {
00442   register ssize_t
00443     i;
00444 
00445   ssize_t
00446     count;
00447 
00448 #if !defined(MAGICKCORE_HAVE_PPREAD)
00449 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00450   #pragma omp critical (MagickCore_ReadRadonCell)
00451 #endif
00452   {
00453     i=(-1);
00454     if (MagickSeek(radon_info->file,offset,SEEK_SET) >= 0)
00455       {
00456 #endif
00457         count=0;
00458         for (i=0; i < (ssize_t) length; i+=count)
00459         {
00460 #if !defined(MAGICKCORE_HAVE_PPREAD)
00461           count=read(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00462             SSIZE_MAX));
00463 #else
00464           count=pread(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00465             SSIZE_MAX),(off_t) (offset+i));
00466 #endif
00467           if (count > 0)
00468             continue;
00469           count=0;
00470           if (errno != EINTR)
00471             {
00472               i=(-1);
00473               break;
00474             }
00475         }
00476 #if !defined(MAGICKCORE_HAVE_PPREAD)
00477       }
00478   }
00479 #endif
00480   return(i);
00481 }
00482 
00483 static inline ssize_t WriteRadonCell(const RadonInfo *radon_info,
00484   const off_t offset,const size_t length,const unsigned char *buffer)
00485 {
00486   register ssize_t
00487     i;
00488 
00489   ssize_t
00490     count;
00491 
00492 #if !defined(MAGICKCORE_HAVE_PWRITE)
00493 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00494   #pragma omp critical (MagickCore_WriteRadonCell)
00495 #endif
00496   {
00497     if (MagickSeek(radon_info->file,offset,SEEK_SET) >= 0)
00498       {
00499 #endif
00500         count=0;
00501         for (i=0; i < (ssize_t) length; i+=count)
00502         {
00503 #if !defined(MAGICKCORE_HAVE_PWRITE)
00504           count=write(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00505             SSIZE_MAX));
00506 #else
00507           count=pwrite(radon_info->file,buffer+i,MagickMin(length-i,(size_t)
00508             SSIZE_MAX),(off_t) (offset+i));
00509 #endif
00510           if (count > 0)
00511             continue;
00512           count=0;
00513           if (errno != EINTR)
00514             {
00515               i=(-1);
00516               break;
00517             }
00518         }
00519 #if !defined(MAGICKCORE_HAVE_PWRITE)
00520       }
00521   }
00522 #endif
00523   return(i);
00524 }
00525 
00526 static inline unsigned short GetRadonCell(const RadonInfo *radon_info,
00527   const long x,const long y)
00528 {
00529   off_t
00530     i;
00531 
00532   unsigned short
00533     value;
00534 
00535   i=(off_t) radon_info->height*x+y;
00536   if ((i < 0) ||
00537       ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
00538     return(0);
00539   if (radon_info->type != DiskCache)
00540     return(radon_info->cells[i]);
00541   value=0;
00542   (void) ReadRadonCell(radon_info,i*sizeof(*radon_info->cells),
00543     sizeof(*radon_info->cells),(unsigned char *) &value);
00544   return(value);
00545 }
00546 
00547 static inline MagickBooleanType SetRadonCell(const RadonInfo *radon_info,
00548   const long x,const long y,const unsigned short value)
00549 {
00550   off_t
00551     i;
00552 
00553   ssize_t
00554     count;
00555 
00556   i=(off_t) radon_info->height*x+y;
00557   if ((i < 0) ||
00558       ((MagickSizeType) (i*sizeof(*radon_info->cells)) >= radon_info->length))
00559     return(MagickFalse);
00560   if (radon_info->type != DiskCache)
00561     {
00562       radon_info->cells[i]=value;
00563       return(MagickTrue);
00564     }
00565   count=WriteRadonCell(radon_info,i*sizeof(*radon_info->cells),
00566     sizeof(*radon_info->cells),(const unsigned char *) &value);
00567   if (count != (ssize_t) sizeof(*radon_info->cells))
00568     return(MagickFalse);
00569   return(MagickTrue);
00570 }
00571 
00572 static void RadonProjection(RadonInfo *source_cells,
00573   RadonInfo *destination_cells,const long sign,unsigned long *projection)
00574 {
00575   RadonInfo
00576     *swap;
00577 
00578   register long
00579     x;
00580 
00581   register RadonInfo
00582     *p,
00583     *q;
00584 
00585   unsigned long
00586     step;
00587 
00588   p=source_cells;
00589   q=destination_cells;
00590   for (step=1; step < p->width; step*=2)
00591   {
00592     for (x=0; x < (long) p->width; x+=2*step)
00593     {
00594       long
00595         y;
00596 
00597       register long
00598         i;
00599 
00600       unsigned short
00601         cell;
00602 
00603       for (i=0; i < (long) step; i++)
00604       {
00605         for (y=0; y < (long) (p->height-i-1); y++)
00606         {
00607           cell=GetRadonCell(p,x+i,y);
00608           (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+step,y+i));
00609           (void) SetRadonCell(q,x+2*i+1,y,cell+GetRadonCell(p,x+i+step,y+i+1));
00610         }
00611         for ( ; y < (long) (p->height-i); y++)
00612         {
00613           cell=GetRadonCell(p,x+i,y);
00614           (void) SetRadonCell(q,x+2*i,y,cell+GetRadonCell(p,x+i+step,y+i));
00615           (void) SetRadonCell(q,x+2*i+1,y,cell);
00616         }
00617         for ( ; y < (long) p->height; y++)
00618         {
00619           cell=GetRadonCell(p,x+i,y);
00620           (void) SetRadonCell(q,x+2*i,y,cell);
00621           (void) SetRadonCell(q,x+2*i+1,y,cell);
00622         }
00623       }
00624     }
00625     swap=p;
00626     p=q;
00627     q=swap;
00628   }
00629 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00630   #pragma omp parallel for schedule(dynamic,4)
00631 #endif
00632   for (x=0; x < (long) p->width; x++)
00633   {
00634     register long
00635       y;
00636 
00637     unsigned long
00638       sum;
00639 
00640     sum=0;
00641     for (y=0; y < (long) (p->height-1); y++)
00642     {
00643       long
00644         delta;
00645 
00646       delta=GetRadonCell(p,x,y)-(long) GetRadonCell(p,x,y+1);
00647       sum+=delta*delta;
00648     }
00649     projection[p->width+sign*x-1]=sum;
00650   }
00651 }
00652 
00653 static MagickBooleanType RadonTransform(const Image *image,
00654   const double threshold,unsigned long *projection,ExceptionInfo *exception)
00655 {
00656   CacheView
00657     *image_view;
00658 
00659   long
00660     y;
00661 
00662   MagickBooleanType
00663     status;
00664 
00665   RadonInfo
00666     *destination_cells,
00667     *source_cells;
00668 
00669   register long
00670     i;
00671 
00672   unsigned char
00673     byte;
00674 
00675   unsigned long
00676     count,
00677     width;
00678 
00679   unsigned short
00680     bits[256];
00681 
00682   for (width=1; width < ((image->columns+7)/8); width<<=1) ;
00683   source_cells=AcquireRadonInfo(image,width,image->rows,exception);
00684   destination_cells=AcquireRadonInfo(image,width,image->rows,exception);
00685   if ((source_cells == (RadonInfo *) NULL) ||
00686       (destination_cells == (RadonInfo *) NULL))
00687     {
00688       if (destination_cells != (RadonInfo *) NULL)
00689         destination_cells=DestroyRadonInfo(destination_cells);
00690       if (source_cells != (RadonInfo *) NULL)
00691         source_cells=DestroyRadonInfo(source_cells);
00692       return(MagickFalse);
00693     }
00694   if (ResetRadonCells(source_cells) == MagickFalse)
00695     {
00696       destination_cells=DestroyRadonInfo(destination_cells);
00697       source_cells=DestroyRadonInfo(source_cells);
00698       return(MagickFalse);
00699     }
00700   for (i=0; i < 256; i++)
00701   {
00702     byte=(unsigned char) i;
00703     for (count=0; byte != 0; byte>>=1)
00704       count+=byte & 0x01;
00705     bits[i]=(unsigned short) count;
00706   }
00707   status=MagickTrue;
00708   image_view=AcquireCacheView(image);
00709 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00710   #pragma omp parallel for schedule(dynamic,4) shared(status)
00711 #endif
00712   for (y=0; y < (long) image->rows; y++)
00713   {
00714     register const PixelPacket
00715       *__restrict p;
00716 
00717     register long
00718       i,
00719       x;
00720 
00721     unsigned long
00722       bit,
00723       byte;
00724 
00725     if (status == MagickFalse)
00726       continue;
00727     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00728     if (p == (const PixelPacket *) NULL)
00729       {
00730         status=MagickFalse;
00731         continue;
00732       }
00733     bit=0;
00734     byte=0;
00735     i=(long) (image->columns+7)/8;
00736     for (x=0; x < (long) image->columns; x++)
00737     {
00738       byte<<=1;
00739       if (((MagickRealType) p->red < threshold) ||
00740           ((MagickRealType) p->green < threshold) ||
00741           ((MagickRealType) p->blue < threshold))
00742         byte|=0x01;
00743       bit++;
00744       if (bit == 8)
00745         {
00746           (void) SetRadonCell(source_cells,--i,y,bits[byte]);
00747           bit=0;
00748           byte=0;
00749         }
00750       p++;
00751     }
00752     if (bit != 0)
00753       {
00754         byte<<=(8-bit);
00755         (void) SetRadonCell(source_cells,--i,y,bits[byte]);
00756       }
00757   }
00758   RadonProjection(source_cells,destination_cells,-1,projection);
00759   (void) ResetRadonCells(source_cells);
00760 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00761   #pragma omp parallel for schedule(dynamic,4) shared(status)
00762 #endif
00763   for (y=0; y < (long) image->rows; y++)
00764   {
00765     register const PixelPacket
00766       *__restrict p;
00767 
00768     register long
00769       i,
00770       x;
00771 
00772     unsigned long
00773       bit,
00774       byte;
00775 
00776     if (status == MagickFalse)
00777       continue;
00778     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00779     if (p == (const PixelPacket *) NULL)
00780       {
00781         status=MagickFalse;
00782         continue;
00783       }
00784     bit=0;
00785     byte=0;
00786     i=0;
00787     for (x=0; x < (long) image->columns; x++)
00788     {
00789       byte<<=1;
00790       if (((MagickRealType) p->red < threshold) ||
00791           ((MagickRealType) p->green < threshold) ||
00792           ((MagickRealType) p->blue < threshold))
00793         byte|=0x01;
00794       bit++;
00795       if (bit == 8)
00796         {
00797           (void) SetRadonCell(source_cells,i++,y,bits[byte]);
00798           bit=0;
00799           byte=0;
00800         }
00801       p++;
00802     }
00803     if (bit != 0)
00804       {
00805         byte<<=(8-bit);
00806         (void) SetRadonCell(source_cells,i++,y,bits[byte]);
00807       }
00808   }
00809   RadonProjection(source_cells,destination_cells,1,projection);
00810   image_view=DestroyCacheView(image_view);
00811   destination_cells=DestroyRadonInfo(destination_cells);
00812   source_cells=DestroyRadonInfo(source_cells);
00813   return(MagickTrue);
00814 }
00815 
00816 static void GetImageBackgroundColor(Image *image,const long offset,
00817   ExceptionInfo *exception)
00818 {
00819   CacheView
00820     *image_view;
00821 
00822   long
00823     y;
00824 
00825   MagickPixelPacket
00826     background;
00827 
00828   MagickRealType
00829     count;
00830 
00831   /*
00832     Compute average background color.
00833   */
00834   if (offset <= 0)
00835     return;
00836   GetMagickPixelPacket(image,&background);
00837   count=0.0;
00838   image_view=AcquireCacheView(image);
00839   for (y=0; y < (long) image->rows; y++)
00840   {
00841     register const PixelPacket
00842       *__restrict p;
00843 
00844     register long
00845       x;
00846 
00847     if ((y >= offset) && (y < ((long) image->rows-offset)))
00848       continue;
00849     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
00850     if (p == (const PixelPacket *) NULL)
00851       continue;
00852     for (x=0; x < (long) image->columns; x++)
00853     {
00854       if ((x >= offset) && (x < ((long) image->columns-offset)))
00855         continue;
00856       background.red+=QuantumScale*p->red;
00857       background.green+=QuantumScale*p->green;
00858       background.blue+=QuantumScale*p->blue;
00859       background.opacity+=QuantumScale*p->opacity;
00860       count++;
00861       p++;
00862     }
00863   }
00864   image_view=DestroyCacheView(image_view);
00865   image->background_color.red=RoundToQuantum((MagickRealType) QuantumRange*
00866     background.red/count);
00867   image->background_color.green=RoundToQuantum((MagickRealType) QuantumRange*
00868     background.green/count);
00869   image->background_color.blue=RoundToQuantum((MagickRealType) QuantumRange*
00870     background.blue/count);
00871   image->background_color.opacity=RoundToQuantum((MagickRealType) QuantumRange*
00872     background.opacity/count);
00873 }
00874 
00875 MagickExport Image *DeskewImage(const Image *image,const double threshold,
00876   ExceptionInfo *exception)
00877 {
00878   AffineMatrix
00879     affine_matrix;
00880 
00881   const char
00882     *artifact;
00883 
00884   double
00885     degrees;
00886 
00887   Image
00888     *clone_image,
00889     *crop_image,
00890     *deskew_image,
00891     *median_image;
00892 
00893   long
00894     skew;
00895 
00896   MagickBooleanType
00897     status;
00898 
00899   RectangleInfo
00900     geometry;
00901 
00902   register long
00903     i;
00904 
00905   unsigned long
00906     max_projection,
00907     *projection,
00908     width;
00909 
00910   /*
00911     Compute deskew angle.
00912   */
00913   for (width=1; width < ((image->columns+7)/8); width<<=1) ;
00914   projection=(unsigned long *) AcquireQuantumMemory((size_t) (2*width-1),
00915     sizeof(*projection));
00916   if (projection == (unsigned long *) NULL)
00917     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00918   status=RadonTransform(image,threshold,projection,exception);
00919   if (status == MagickFalse)
00920     {
00921       projection=(unsigned long *) RelinquishMagickMemory(projection);
00922       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00923     }
00924   max_projection=0;
00925   skew=0;
00926   for (i=0; i < (long) (2*width-1); i++)
00927   {
00928     if (projection[i] > max_projection)
00929       {
00930         skew=i-(long) width+1;
00931         max_projection=projection[i];
00932       }
00933   }
00934   projection=(unsigned long *) RelinquishMagickMemory(projection);
00935   /*
00936     Deskew image.
00937   */
00938   clone_image=CloneImage(image,0,0,MagickTrue,exception);
00939   if (clone_image == (Image *) NULL)
00940     return((Image *) NULL);
00941   (void) SetImageVirtualPixelMethod(clone_image,BackgroundVirtualPixelMethod);
00942   degrees=RadiansToDegrees(-atan((double) skew/width/8));
00943   if (image->debug != MagickFalse)
00944     (void) LogMagickEvent(TransformEvent,GetMagickModule(),"  Deskew angle: %g",
00945       degrees);
00946   affine_matrix.sx=cos(DegreesToRadians(fmod((double) degrees,360.0)));
00947   affine_matrix.rx=sin(DegreesToRadians(fmod((double) degrees,360.0)));
00948   affine_matrix.ry=(-sin(DegreesToRadians(fmod((double) degrees,360.0))));
00949   affine_matrix.sy=cos(DegreesToRadians(fmod((double) degrees,360.0)));
00950   affine_matrix.tx=0.0;
00951   affine_matrix.ty=0.0;
00952   artifact=GetImageArtifact(image,"deskew:auto-crop");
00953   if (artifact == (const char *) NULL)
00954     {
00955       deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
00956       clone_image=DestroyImage(clone_image);
00957       return(deskew_image);
00958     }
00959   /*
00960     Auto-crop image.
00961   */
00962   GetImageBackgroundColor(clone_image,atol(artifact),exception);
00963   deskew_image=AffineTransformImage(clone_image,&affine_matrix,exception);
00964   clone_image=DestroyImage(clone_image);
00965   if (deskew_image == (Image *) NULL)
00966     return((Image *) NULL);
00967   median_image=MedianFilterImage(deskew_image,0.0,exception);
00968   if (median_image == (Image *) NULL)
00969     {
00970       deskew_image=DestroyImage(deskew_image);
00971       return((Image *) NULL);
00972     }
00973   geometry=GetImageBoundingBox(median_image,exception);
00974   median_image=DestroyImage(median_image);
00975   if (image->debug != MagickFalse)
00976     (void) LogMagickEvent(TransformEvent,GetMagickModule(),"  Deskew geometry: "
00977       "%lux%lu%+ld%+ld",geometry.width,geometry.height,geometry.x,geometry.y);
00978   crop_image=CropImage(deskew_image,&geometry,exception);
00979   deskew_image=DestroyImage(deskew_image);
00980   return(crop_image);
00981 }
00982 
00983 /*
00984 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00985 %                                                                             %
00986 %                                                                             %
00987 %                                                                             %
00988 +   I n t e g r a l R o t a t e I m a g e                                     %
00989 %                                                                             %
00990 %                                                                             %
00991 %                                                                             %
00992 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00993 %
00994 %  IntegralRotateImage()  rotates the image an integral of 90 degrees.  It
00995 %  allocates the memory necessary for the new Image structure and returns a
00996 %  pointer to the rotated image.
00997 %
00998 %  The format of the IntegralRotateImage method is:
00999 %
01000 %      Image *IntegralRotateImage(const Image *image,unsigned long rotations,
01001 %        ExceptionInfo *exception)
01002 %
01003 %  A description of each parameter follows.
01004 %
01005 %    o image: the image.
01006 %
01007 %    o rotations: Specifies the number of 90 degree rotations.
01008 %
01009 */
01010 static Image *IntegralRotateImage(const Image *image,unsigned long rotations,
01011   ExceptionInfo *exception)
01012 {
01013 #define RotateImageTag  "Rotate/Image"
01014 
01015   CacheView
01016     *image_view,
01017     *rotate_view;
01018 
01019   Image
01020     *rotate_image;
01021 
01022   long
01023     progress,
01024     y;
01025 
01026   MagickBooleanType
01027     status;
01028 
01029   RectangleInfo
01030     page;
01031 
01032   /*
01033     Initialize rotated image attributes.
01034   */
01035   assert(image != (Image *) NULL);
01036   page=image->page;
01037   rotations%=4;
01038   if (rotations == 0)
01039     return(CloneImage(image,0,0,MagickTrue,exception));
01040   if ((rotations == 1) || (rotations == 3))
01041     rotate_image=CloneImage(image,image->rows,image->columns,MagickTrue,
01042       exception);
01043   else
01044     rotate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
01045       exception);
01046   if (rotate_image == (Image *) NULL)
01047     return((Image *) NULL);
01048   /*
01049     Integral rotate the image.
01050   */
01051   status=MagickTrue;
01052   progress=0;
01053   image_view=AcquireCacheView(image);
01054   rotate_view=AcquireCacheView(rotate_image);
01055   switch (rotations)
01056   {
01057     case 0:
01058     {
01059       /*
01060         Rotate 0 degrees.
01061       */
01062       break;
01063     }
01064     case 1:
01065     {
01066       long
01067         tile_y;
01068 
01069       unsigned long
01070         tile_height,
01071         tile_width;
01072 
01073       /*
01074         Rotate 90 degrees.
01075       */
01076       GetPixelCacheTileSize(image,&tile_width,&tile_height);
01077 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01078   #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
01079 #endif
01080       for (tile_y=0; tile_y < (long) image->rows; tile_y+=tile_height)
01081       {
01082         register long
01083           tile_x;
01084 
01085         if (status == MagickFalse)
01086           continue;
01087         for (tile_x=0; tile_x < (long) image->columns; tile_x+=tile_width)
01088         {
01089           MagickBooleanType
01090             sync;
01091 
01092           register const IndexPacket
01093             *__restrict indexes;
01094 
01095           register const PixelPacket
01096             *__restrict p;
01097 
01098           register IndexPacket
01099             *__restrict rotate_indexes;
01100 
01101           register long
01102             y;
01103 
01104           register PixelPacket
01105             *__restrict q;
01106 
01107           unsigned long
01108             height,
01109             width;
01110 
01111           width=tile_width;
01112           if ((tile_x+(long) tile_width) > (long) image->columns)
01113             width=(unsigned long) (tile_width-(tile_x+tile_width-
01114               image->columns));
01115           height=tile_height;
01116           if ((tile_y+(long) tile_height) > (long) image->rows)
01117             height=(unsigned long) (tile_height-(tile_y+tile_height-
01118               image->rows));
01119           p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,height,
01120             exception);
01121           if (p == (const PixelPacket *) NULL)
01122             {
01123               status=MagickFalse;
01124               break;
01125             }
01126           indexes=GetCacheViewVirtualIndexQueue(image_view);
01127           for (y=0; y < (long) width; y++)
01128           {
01129             register const PixelPacket
01130               *__restrict tile_pixels;
01131 
01132             register long
01133               x;
01134 
01135             q=QueueCacheViewAuthenticPixels(rotate_view,(long)
01136               rotate_image->columns-(tile_y+height),y+tile_x,height,
01137               1,exception);
01138             if (q == (PixelPacket *) NULL)
01139               {
01140                 status=MagickFalse;
01141                 break;
01142               }
01143             tile_pixels=p+(height-1)*width+y;
01144             for (x=0; x < (long) height; x++)
01145             {
01146               *q++=(*tile_pixels);
01147               tile_pixels-=width;
01148             }
01149             rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
01150             if ((indexes != (IndexPacket *) NULL) &&
01151                 (rotate_indexes != (IndexPacket *) NULL))
01152               {
01153                 register const IndexPacket
01154                   *__restrict tile_indexes;
01155 
01156                 tile_indexes=indexes+(height-1)*width+y;
01157                 for (x=0; x < (long) height; x++)
01158                 {
01159                   *rotate_indexes++=(*tile_indexes);
01160                   tile_indexes-=width;
01161                 }
01162               }
01163             sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
01164             if (sync == MagickFalse)
01165               status=MagickFalse;
01166           }
01167         }
01168         if (image->progress_monitor != (MagickProgressMonitor) NULL)
01169           {
01170             MagickBooleanType
01171               proceed;
01172 
01173             proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
01174               image->rows);
01175             if (proceed == MagickFalse)
01176               status=MagickFalse;
01177           }
01178       }
01179       (void) SetImageProgress(image,RotateImageTag,image->rows-1,image->rows);
01180       Swap(page.width,page.height);
01181       Swap(page.x,page.y);
01182       if (page.width != 0)
01183         page.x=(long) (page.width-rotate_image->columns-page.x);
01184       break;
01185     }
01186     case 2:
01187     {
01188       /*
01189         Rotate 180 degrees.
01190       */
01191 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01192   #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
01193 #endif
01194       for (y=0; y < (long) image->rows; y++)
01195       {
01196         MagickBooleanType
01197           sync;
01198 
01199         register const IndexPacket
01200           *__restrict indexes;
01201 
01202         register const PixelPacket
01203           *__restrict p;
01204 
01205         register IndexPacket
01206           *__restrict rotate_indexes;
01207 
01208         register long
01209           x;
01210 
01211         register PixelPacket
01212           *__restrict q;
01213 
01214         if (status == MagickFalse)
01215           continue;
01216         p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,
01217           exception);
01218         q=QueueCacheViewAuthenticPixels(rotate_view,0,(long) (image->rows-
01219           y-1),image->columns,1,exception);
01220         if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
01221           {
01222             status=MagickFalse;
01223             continue;
01224           }
01225         indexes=GetCacheViewVirtualIndexQueue(image_view);
01226         rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
01227         q+=image->columns;
01228         for (x=0; x < (long) image->columns; x++)
01229           *--q=(*p++);
01230         if ((indexes != (IndexPacket *) NULL) &&
01231             (rotate_indexes != (IndexPacket *) NULL))
01232           for (x=0; x < (long) image->columns; x++)
01233             rotate_indexes[image->columns-x-1]=indexes[x];
01234         sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
01235         if (sync == MagickFalse)
01236           status=MagickFalse;
01237         if (image->progress_monitor != (MagickProgressMonitor) NULL)
01238           {
01239             MagickBooleanType
01240               proceed;
01241 
01242             proceed=SetImageProgress(image,RotateImageTag,progress++,
01243               image->rows);
01244             if (proceed == MagickFalse)
01245               status=MagickFalse;
01246           }
01247       }
01248       if (page.width != 0)
01249         page.x=(long) (page.width-rotate_image->columns-page.x);
01250       if (page.height != 0)
01251         page.y=(long) (page.height-rotate_image->rows-page.y);
01252       break;
01253     }
01254     case 3:
01255     {
01256       long
01257         tile_y;
01258 
01259       unsigned long
01260         tile_height,
01261         tile_width;
01262 
01263       /*
01264         Rotate 270 degrees.
01265       */
01266       GetPixelCacheTileSize(image,&tile_width,&tile_height);
01267 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01268   #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
01269 #endif
01270       for (tile_y=0; tile_y < (long) image->rows; tile_y+=tile_height)
01271       {
01272         register long
01273           tile_x;
01274 
01275         if (status == MagickFalse)
01276           continue;
01277         for (tile_x=0; tile_x < (long) image->columns; tile_x+=tile_width)
01278         {
01279           MagickBooleanType
01280             sync;
01281 
01282           register const IndexPacket
01283             *__restrict indexes;
01284 
01285           register const PixelPacket
01286             *__restrict p;
01287 
01288           register IndexPacket
01289             *__restrict rotate_indexes;
01290 
01291           register long
01292             y;
01293 
01294           register PixelPacket
01295             *__restrict q;
01296 
01297           unsigned long
01298             height,
01299             width;
01300 
01301           width=tile_width;
01302           if ((tile_x+(long) tile_width) > (long) image->columns)
01303             width=(unsigned long) (tile_width-(tile_x+tile_width-
01304               image->columns));
01305           height=tile_height;
01306           if ((tile_y+(long) tile_height) > (long) image->rows)
01307             height=(unsigned long) (tile_height-(tile_y+tile_height-
01308               image->rows));
01309           p=GetCacheViewVirtualPixels(image_view,tile_x,tile_y,width,
01310             height,exception);
01311           if (p == (const PixelPacket *) NULL)
01312             {
01313               status=MagickFalse;
01314               break;
01315             }
01316           indexes=GetCacheViewVirtualIndexQueue(image_view);
01317           for (y=0; y < (long) width; y++)
01318           {
01319             register const PixelPacket
01320               *__restrict tile_pixels;
01321 
01322             register long
01323               x;
01324 
01325             q=QueueCacheViewAuthenticPixels(rotate_view,tile_y,(long)
01326               y+rotate_image->rows-(tile_x+width),height,1,exception);
01327             if (q == (PixelPacket *) NULL)
01328               {
01329                 status=MagickFalse;
01330                 break;
01331               }
01332             tile_pixels=p+(width-1)-y;
01333             for (x=0; x < (long) height; x++)
01334             {
01335               *q++=(*tile_pixels);
01336               tile_pixels+=width;
01337             }
01338             rotate_indexes=GetCacheViewAuthenticIndexQueue(rotate_view);
01339             if ((indexes != (IndexPacket *) NULL) &&
01340                 (rotate_indexes != (IndexPacket *) NULL))
01341               {
01342                 register const IndexPacket
01343                   *__restrict tile_indexes;
01344 
01345                 tile_indexes=indexes+(width-1)-y;
01346                 for (x=0; x < (long) height; x++)
01347                 {
01348                   *rotate_indexes++=(*tile_indexes);
01349                   tile_indexes+=width;
01350                 }
01351               }
01352             sync=SyncCacheViewAuthenticPixels(rotate_view,exception);
01353             if (sync == MagickFalse)
01354               status=MagickFalse;
01355           }
01356         }
01357         if (image->progress_monitor != (MagickProgressMonitor) NULL)
01358           {
01359             MagickBooleanType
01360               proceed;
01361 
01362             proceed=SetImageProgress(image,RotateImageTag,progress+=tile_height,
01363               image->rows);
01364             if (proceed == MagickFalse)
01365               status=MagickFalse;
01366           }
01367       }
01368       (void) SetImageProgress(image,RotateImageTag,image->rows-1,image->rows);
01369       Swap(page.width,page.height);
01370       Swap(page.x,page.y);
01371       if (page.height != 0)
01372         page.y=(long) (page.height-rotate_image->rows-page.y);
01373       break;
01374     }
01375   }
01376   rotate_view=DestroyCacheView(rotate_view);
01377   image_view=DestroyCacheView(image_view);
01378   rotate_image->type=image->type;
01379   rotate_image->page=page;
01380   if (status == MagickFalse)
01381     rotate_image=DestroyImage(rotate_image);
01382   return(rotate_image);
01383 }
01384 
01385 /*
01386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01387 %                                                                             %
01388 %                                                                             %
01389 %                                                                             %
01390 +   X S h e a r I m a g e                                                     %
01391 %                                                                             %
01392 %                                                                             %
01393 %                                                                             %
01394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01395 %
01396 %  XShearImage() shears the image in the X direction with a shear angle of
01397 %  'degrees'.  Positive angles shear counter-clockwise (right-hand rule), and
01398 %  negative angles shear clockwise.  Angles are measured relative to a vertical
01399 %  Y-axis.  X shears will widen an image creating 'empty' triangles on the left
01400 %  and right sides of the source image.
01401 %
01402 %  The format of the XShearImage method is:
01403 %
01404 %      MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
01405 %        const unsigned long width,const unsigned long height,
01406 %        const long x_offset,const long y_offset)
01407 %
01408 %  A description of each parameter follows.
01409 %
01410 %    o image: the image.
01411 %
01412 %    o degrees: A MagickRealType representing the shearing angle along the X
01413 %      axis.
01414 %
01415 %    o width, height, x_offset, y_offset: Defines a region of the image
01416 %      to shear.
01417 %
01418 */
01419 static MagickBooleanType XShearImage(Image *image,const MagickRealType degrees,
01420   const unsigned long width,const unsigned long height,const long x_offset,
01421   const long y_offset)
01422 {
01423 #define XShearImageTag  "XShear/Image"
01424 
01425   typedef enum
01426   {
01427     LEFT,
01428     RIGHT
01429   } ShearDirection;
01430 
01431   CacheView
01432     *image_view;
01433 
01434   ExceptionInfo
01435     *exception;
01436 
01437   long
01438     progress,
01439     y;
01440 
01441   MagickBooleanType
01442     status;
01443 
01444   MagickPixelPacket
01445     background;
01446 
01447   assert(image != (Image *) NULL);
01448   assert(image->signature == MagickSignature);
01449   if (image->debug != MagickFalse)
01450     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01451   GetMagickPixelPacket(image,&background);
01452   SetMagickPixelPacket(image,&image->background_color,(IndexPacket *) NULL,
01453     &background);
01454   if (image->colorspace == CMYKColorspace)
01455     ConvertRGBToCMYK(&background);
01456   /*
01457     XShear image.
01458   */
01459   status=MagickTrue;
01460   progress=0;
01461   exception=(&image->exception);
01462   image_view=AcquireCacheView(image);
01463 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01464   #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
01465 #endif
01466   for (y=0; y < (long) height; y++)
01467   {
01468     long
01469       step;
01470 
01471     MagickPixelPacket
01472       pixel,
01473       source,
01474       destination;
01475 
01476     MagickRealType
01477       area,
01478       displacement;
01479 
01480     register long
01481       i;
01482 
01483     register IndexPacket
01484       *__restrict indexes,
01485       *__restrict shear_indexes;
01486 
01487     register PixelPacket
01488       *__restrict p,
01489       *__restrict q;
01490 
01491     ShearDirection
01492       direction;
01493 
01494     if (status == MagickFalse)
01495       continue;
01496     p=GetCacheViewAuthenticPixels(image_view,0,y_offset+y,image->columns,1,
01497       exception);
01498     if (p == (PixelPacket *) NULL)
01499       {
01500         status=MagickFalse;
01501         continue;
01502       }
01503     indexes=GetCacheViewAuthenticIndexQueue(image_view);
01504     p+=x_offset;
01505     indexes+=x_offset;
01506     displacement=degrees*(MagickRealType) (y-height/2.0);
01507     if (displacement == 0.0)
01508       continue;
01509     if (displacement > 0.0)
01510       direction=RIGHT;
01511     else
01512       {
01513         displacement*=(-1.0);
01514         direction=LEFT;
01515       }
01516     step=(long) floor((double) displacement);
01517     area=(MagickRealType) (displacement-step);
01518     step++;
01519     pixel=background;
01520     GetMagickPixelPacket(image,&source);
01521     GetMagickPixelPacket(image,&destination);
01522     switch (direction)
01523     {
01524       case LEFT:
01525       {
01526         /*
01527           Transfer pixels left-to-right.
01528         */
01529         if (step > x_offset)
01530           break;
01531         q=p-step;
01532         shear_indexes=indexes-step;
01533         for (i=0; i < (long) width; i++)
01534         {
01535           if ((x_offset+i) < step)
01536             {
01537               SetMagickPixelPacket(image,++p,++indexes,&pixel);
01538               q++;
01539               shear_indexes++;
01540               continue;
01541             }
01542           SetMagickPixelPacket(image,p,indexes,&source);
01543           MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01544             &source,(MagickRealType) p->opacity,area,&destination);
01545           SetPixelPacket(image,&destination,q++,shear_indexes++);
01546           SetMagickPixelPacket(image,p++,indexes++,&pixel);
01547         }
01548         MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01549           &background,(MagickRealType) background.opacity,area,&destination);
01550         SetPixelPacket(image,&destination,q++,shear_indexes++);
01551         for (i=0; i < (step-1); i++)
01552           SetPixelPacket(image,&background,q++,shear_indexes++);
01553         break;
01554       }
01555       case RIGHT:
01556       {
01557         /*
01558           Transfer pixels right-to-left.
01559         */
01560         p+=width;
01561         indexes+=width;
01562         q=p+step;
01563         shear_indexes=indexes+step;
01564         for (i=0; i < (long) width; i++)
01565         {
01566           p--;
01567           indexes--;
01568           q--;
01569           shear_indexes--;
01570           if ((unsigned long) (x_offset+width+step-i) >= image->columns)
01571             continue;
01572           SetMagickPixelPacket(image,p,indexes,&source);
01573           MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01574             &source,(MagickRealType) p->opacity,area,&destination);
01575           SetPixelPacket(image,&destination,q,shear_indexes);
01576           SetMagickPixelPacket(image,p,indexes,&pixel);
01577         }
01578         MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01579           &background,(MagickRealType) background.opacity,area,&destination);
01580         SetPixelPacket(image,&destination,--q,--shear_indexes);
01581         for (i=0; i < (step-1); i++)
01582           SetPixelPacket(image,&background,--q,--shear_indexes);
01583         break;
01584       }
01585     }
01586     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01587       status=MagickFalse;
01588     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01589       {
01590         MagickBooleanType
01591           proceed;
01592 
01593 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01594   #pragma omp critical (MagickCore_XShearImage)
01595 #endif
01596         proceed=SetImageProgress(image,XShearImageTag,progress++,height);
01597         if (proceed == MagickFalse)
01598           status=MagickFalse;
01599       }
01600   }
01601   image_view=DestroyCacheView(image_view);
01602   return(status);
01603 }
01604 
01605 /*
01606 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01607 %                                                                             %
01608 %                                                                             %
01609 %                                                                             %
01610 +   Y S h e a r I m a g e                                                     %
01611 %                                                                             %
01612 %                                                                             %
01613 %                                                                             %
01614 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01615 %
01616 %  YShearImage shears the image in the Y direction with a shear angle of
01617 %  'degrees'.  Positive angles shear counter-clockwise (right-hand rule), and
01618 %  negative angles shear clockwise.  Angles are measured relative to a
01619 %  horizontal X-axis.  Y shears will increase the height of an image creating
01620 %  'empty' triangles on the top and bottom of the source image.
01621 %
01622 %  The format of the YShearImage method is:
01623 %
01624 %      MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
01625 %        const unsigned long width,const unsigned long height,
01626 %        const long x_offset,const long y_offset)
01627 %
01628 %  A description of each parameter follows.
01629 %
01630 %    o image: the image.
01631 %
01632 %    o degrees: A MagickRealType representing the shearing angle along the Y
01633 %      axis.
01634 %
01635 %    o width, height, x_offset, y_offset: Defines a region of the image
01636 %      to shear.
01637 %
01638 */
01639 static MagickBooleanType YShearImage(Image *image,const MagickRealType degrees,
01640   const unsigned long width,const unsigned long height,const long x_offset,
01641   const long y_offset)
01642 {
01643 #define YShearImageTag  "YShear/Image"
01644 
01645   typedef enum
01646   {
01647     UP,
01648     DOWN
01649   } ShearDirection;
01650 
01651   CacheView
01652     *image_view;
01653 
01654   ExceptionInfo
01655     *exception;
01656 
01657   long
01658     progress,
01659     x;
01660 
01661   MagickBooleanType
01662     status;
01663 
01664   MagickPixelPacket
01665     background;
01666 
01667   assert(image != (Image *) NULL);
01668   assert(image->signature == MagickSignature);
01669   if (image->debug != MagickFalse)
01670     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01671   GetMagickPixelPacket(image,&background);
01672   SetMagickPixelPacket(image,&image->background_color,(IndexPacket *) NULL,
01673     &background);
01674   if (image->colorspace == CMYKColorspace)
01675     ConvertRGBToCMYK(&background);
01676   /*
01677     Y Shear image.
01678   */
01679   status=MagickTrue;
01680   progress=0;
01681   exception=(&image->exception);
01682   image_view=AcquireCacheView(image);
01683 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01684   #pragma omp parallel for schedule(dynamic,4) shared(progress, status)
01685 #endif
01686   for (x=0; x < (long) width; x++)
01687   {
01688     long
01689       step;
01690 
01691     MagickPixelPacket
01692       pixel,
01693       source,
01694       destination;
01695 
01696     MagickRealType
01697       area,
01698       displacement;
01699 
01700     register IndexPacket
01701       *__restrict indexes,
01702       *__restrict shear_indexes;
01703 
01704     register long
01705       i;
01706 
01707     register PixelPacket
01708       *__restrict p,
01709       *__restrict q;
01710 
01711     ShearDirection
01712       direction;
01713 
01714     if (status == MagickFalse)
01715       continue;
01716     p=GetCacheViewAuthenticPixels(image_view,x_offset+x,0,1,image->rows,
01717       exception);
01718     if (p == (PixelPacket *) NULL)
01719       {
01720         status=MagickFalse;
01721         continue;
01722       }
01723     indexes=GetCacheViewAuthenticIndexQueue(image_view);
01724     p+=y_offset;
01725     indexes+=y_offset;
01726     displacement=degrees*(MagickRealType) (x-width/2.0);
01727     if (displacement == 0.0)
01728       continue;
01729     if (displacement > 0.0)
01730       direction=DOWN;
01731     else
01732       {
01733         displacement*=(-1.0);
01734         direction=UP;
01735       }
01736     step=(long) floor((double) displacement);
01737     area=(MagickRealType) (displacement-step);
01738     step++;
01739     pixel=background;
01740     GetMagickPixelPacket(image,&source);
01741     GetMagickPixelPacket(image,&destination);
01742     switch (direction)
01743     {
01744       case UP:
01745       {
01746         /*
01747           Transfer pixels top-to-bottom.
01748         */
01749         if (step > y_offset)
01750           break;
01751         q=p-step;
01752         shear_indexes=indexes-step;
01753         for (i=0; i < (long) height; i++)
01754         {
01755           if ((y_offset+i) < step)
01756             {
01757               SetMagickPixelPacket(image,++p,++indexes,&pixel);
01758               q++;
01759               shear_indexes++;
01760               continue;
01761             }
01762           SetMagickPixelPacket(image,p,indexes,&source);
01763           MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01764             &source,(MagickRealType) p->opacity,area,&destination);
01765           SetPixelPacket(image,&destination,q++,shear_indexes++);
01766           SetMagickPixelPacket(image,p++,indexes++,&pixel);
01767         }
01768         MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01769           &background,(MagickRealType) background.opacity,area,&destination);
01770         SetPixelPacket(image,&destination,q++,shear_indexes++);
01771         for (i=0; i < (step-1); i++)
01772           SetPixelPacket(image,&background,q++,shear_indexes++);
01773         break;
01774       }
01775       case DOWN:
01776       {
01777         /*
01778           Transfer pixels bottom-to-top.
01779         */
01780         p+=height;
01781         indexes+=height;
01782         q=p+step;
01783         shear_indexes=indexes+step;
01784         for (i=0; i < (long) height; i++)
01785         {
01786           p--;
01787           indexes--;
01788           q--;
01789           shear_indexes--;
01790           if ((unsigned long) (y_offset+height+step-i) >= image->rows)
01791             continue;
01792           SetMagickPixelPacket(image,p,indexes,&source);
01793           MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01794             &source,(MagickRealType) p->opacity,area,&destination);
01795           SetPixelPacket(image,&destination,q,shear_indexes);
01796           SetMagickPixelPacket(image,p,indexes,&pixel);
01797         }
01798         MagickPixelCompositeAreaBlend(&pixel,(MagickRealType) pixel.opacity,
01799           &background,(MagickRealType) background.opacity,area,&destination);
01800         SetPixelPacket(image,&destination,--q,--shear_indexes);
01801         for (i=0; i < (step-1); i++)
01802           SetPixelPacket(image,&background,--q,--shear_indexes);
01803         break;
01804       }
01805     }
01806     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
01807       status=MagickFalse;
01808     if (image->progress_monitor != (MagickProgressMonitor) NULL)
01809       {
01810         MagickBooleanType
01811           proceed;
01812 
01813 #if defined(MAGICKCORE_OPENMP_SUPPORT)
01814   #pragma omp critical (MagickCore_YShearImage)
01815 #endif
01816         proceed=SetImageProgress(image,YShearImageTag,progress++,image->rows);
01817         if (proceed == MagickFalse)
01818           status=MagickFalse;
01819       }
01820   }
01821   image_view=DestroyCacheView(image_view);
01822   return(status);
01823 }
01824 
01825 /*
01826 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01827 %                                                                             %
01828 %                                                                             %
01829 %                                                                             %
01830 %   R o t a t e I m a g e                                                     %
01831 %                                                                             %
01832 %                                                                             %
01833 %                                                                             %
01834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01835 %
01836 %  RotateImage() creates a new image that is a rotated copy of an existing
01837 %  one.  Positive angles rotate counter-clockwise (right-hand rule), while
01838 %  negative angles rotate clockwise.  Rotated images are usually larger than
01839 %  the originals and have 'empty' triangular corners.  X axis.  Empty
01840 %  triangles left over from shearing the image are filled with the background
01841 %  color defined by member 'background_color' of the image.  RotateImage
01842 %  allocates the memory necessary for the new Image structure and returns a
01843 %  pointer to the new image.
01844 %
01845 %  RotateImage() is based on the paper "A Fast Algorithm for General
01846 %  Raster Rotatation" by Alan W. Paeth.  RotateImage is adapted from a similar
01847 %  method based on the Paeth paper written by Michael Halle of the Spatial
01848 %  Imaging Group, MIT Media Lab.
01849 %
01850 %  The format of the RotateImage method is:
01851 %
01852 %      Image *RotateImage(const Image *image,const double degrees,
01853 %        ExceptionInfo *exception)
01854 %
01855 %  A description of each parameter follows.
01856 %
01857 %    o image: the image.
01858 %
01859 %    o degrees: Specifies the number of degrees to rotate the image.
01860 %
01861 %    o exception: return any errors or warnings in this structure.
01862 %
01863 */
01864 MagickExport Image *RotateImage(const Image *image,const double degrees,
01865   ExceptionInfo *exception)
01866 {
01867   Image
01868     *integral_image,
01869     *rotate_image;
01870 
01871   long
01872     x_offset,
01873     y_offset;
01874 
01875   MagickRealType
01876     angle;
01877 
01878   PointInfo
01879     shear;
01880 
01881   RectangleInfo
01882     border_info;
01883 
01884   unsigned long
01885     height,
01886     rotations,
01887     width,
01888     y_width;
01889 
01890   /*
01891     Adjust rotation angle.
01892   */
01893   assert(image != (Image *) NULL);
01894   assert(image->signature == MagickSignature);
01895   if (image->debug != MagickFalse)
01896     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01897   assert(exception != (ExceptionInfo *) NULL);
01898   assert(exception->signature == MagickSignature);
01899   angle=degrees;
01900   while (angle < -45.0)
01901     angle+=360.0;
01902   for (rotations=0; angle > 45.0; rotations++)
01903     angle-=90.0;
01904   rotations%=4;
01905   /*
01906     Calculate shear equations.
01907   */
01908   integral_image=IntegralRotateImage(image,rotations,exception);
01909   if (integral_image == (Image *) NULL)
01910     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01911   shear.x=(-tan((double) DegreesToRadians(angle)/2.0));
01912   shear.y=sin((double) DegreesToRadians(angle));
01913   if ((shear.x == 0.0) && (shear.y == 0.0))
01914     return(integral_image);
01915   if (SetImageStorageClass(integral_image,DirectClass) == MagickFalse)
01916     {
01917       InheritException(exception,&integral_image->exception);
01918       integral_image=DestroyImage(integral_image);
01919       return(integral_image);
01920     }
01921   if (integral_image->matte == MagickFalse)
01922     (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel);
01923   /*
01924     Compute image size.
01925   */
01926   width=image->columns;
01927   height=image->rows;
01928   if ((rotations == 1) || (rotations == 3))
01929     {
01930       width=image->rows;
01931       height=image->columns;
01932     }
01933   y_width=width+(long) (fabs(shear.x)*height+0.5);
01934   x_offset=(long) (width+((fabs(shear.y)*height)-width)/2.0+0.5);
01935   y_offset=(long) (height+((fabs(shear.y)*y_width)-height)/2.0+0.5);
01936   /*
01937     Surround image with a border.
01938   */
01939   integral_image->border_color=integral_image->background_color;
01940   integral_image->compose=CopyCompositeOp;
01941   border_info.width=(unsigned long) x_offset;
01942   border_info.height=(unsigned long) y_offset;
01943   rotate_image=BorderImage(integral_image,&border_info,exception);
01944   integral_image=DestroyImage(integral_image);
01945   if (rotate_image == (Image *) NULL)
01946     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
01947   /*
01948     Rotate the image.
01949   */
01950   (void) XShearImage(rotate_image,shear.x,width,height,x_offset,
01951     ((long) rotate_image->rows-height)/2);
01952   (void) YShearImage(rotate_image,shear.y,y_width,height,
01953     ((long) rotate_image->columns-y_width)/2,y_offset);
01954   (void) XShearImage(rotate_image,shear.x,y_width,rotate_image->rows,
01955     ((long) rotate_image->columns-y_width)/2,0);
01956   CropToFitImage(&rotate_image,shear.x,shear.y,(MagickRealType) width,
01957     (MagickRealType) height,MagickTrue,exception);
01958   rotate_image->compose=image->compose;
01959   rotate_image->page.width=0;
01960   rotate_image->page.height=0;
01961   return(rotate_image);
01962 }
01963 
01964 /*
01965 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01966 %                                                                             %
01967 %                                                                             %
01968 %                                                                             %
01969 %   S h e a r I m a g e                                                       %
01970 %                                                                             %
01971 %                                                                             %
01972 %                                                                             %
01973 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
01974 %
01975 %  ShearImage() creates a new image that is a shear_image copy of an existing
01976 %  one.  Shearing slides one edge of an image along the X or Y axis, creating
01977 %  a parallelogram.  An X direction shear slides an edge along the X axis,
01978 %  while a Y direction shear slides an edge along the Y axis.  The amount of
01979 %  the shear is controlled by a shear angle.  For X direction shears, x_shear
01980 %  is measured relative to the Y axis, and similarly, for Y direction shears
01981 %  y_shear is measured relative to the X axis.  Empty triangles left over from
01982 %  shearing the image are filled with the background color defined by member
01983 %  'background_color' of the image..  ShearImage() allocates the memory
01984 %  necessary for the new Image structure and returns a pointer to the new image.
01985 %
01986 %  ShearImage() is based on the paper "A Fast Algorithm for General Raster
01987 %  Rotatation" by Alan W. Paeth.
01988 %
01989 %  The format of the ShearImage method is:
01990 %
01991 %      Image *ShearImage(const Image *image,const double x_shear,
01992 %        const double y_shear,ExceptionInfo *exception)
01993 %
01994 %  A description of each parameter follows.
01995 %
01996 %    o image: the image.
01997 %
01998 %    o x_shear, y_shear: Specifies the number of degrees to shear the image.
01999 %
02000 %    o exception: return any errors or warnings in this structure.
02001 %
02002 */
02003 MagickExport Image *ShearImage(const Image *image,const double x_shear,
02004   const double y_shear,ExceptionInfo *exception)
02005 {
02006   Image
02007     *integral_image,
02008     *shear_image;
02009 
02010   long
02011     x_offset,
02012     y_offset;
02013 
02014   PointInfo
02015     shear;
02016 
02017   RectangleInfo
02018     border_info;
02019 
02020   unsigned long
02021     y_width;
02022 
02023   assert(image != (Image *) NULL);
02024   assert(image->signature == MagickSignature);
02025   if (image->debug != MagickFalse)
02026     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
02027   assert(exception != (ExceptionInfo *) NULL);
02028   assert(exception->signature == MagickSignature);
02029   if ((x_shear != 0.0) && (fmod(x_shear,90.0) == 0.0))
02030     ThrowImageException(ImageError,"AngleIsDiscontinuous");
02031   if ((y_shear != 0.0) && (fmod(y_shear,90.0) == 0.0))
02032     ThrowImageException(ImageError,"AngleIsDiscontinuous");
02033   /*
02034     Initialize shear angle.
02035   */
02036   integral_image=CloneImage(image,0,0,MagickTrue,exception);
02037   if (integral_image == (Image *) NULL)
02038     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02039   shear.x=(-tan(DegreesToRadians(fmod(x_shear,360.0))));
02040   shear.y=tan(DegreesToRadians(fmod(y_shear,360.0)));
02041   if ((shear.x == 0.0) && (shear.y == 0.0))
02042     return(integral_image);
02043   if (SetImageStorageClass(integral_image,DirectClass) == MagickFalse)
02044     {
02045       InheritException(exception,&integral_image->exception);
02046       integral_image=DestroyImage(integral_image);
02047       return(integral_image);
02048     }
02049   if (integral_image->matte == MagickFalse)
02050     (void) SetImageAlphaChannel(integral_image,OpaqueAlphaChannel);
02051   /*
02052     Compute image size.
02053   */
02054   y_width=image->columns+(long) (fabs(shear.x)*image->rows+0.5);
02055   x_offset=(long) (image->columns+((fabs(shear.x)*image->rows)-image->columns)/
02056     2.0+0.5);
02057   y_offset=(long) (image->rows+((fabs(shear.y)*y_width)-image->rows)/2.0+0.5);
02058   /*
02059     Surround image with border.
02060   */
02061   integral_image->border_color=integral_image->background_color;
02062   integral_image->compose=CopyCompositeOp;
02063   border_info.width=(unsigned long) x_offset;
02064   border_info.height=(unsigned long) y_offset;
02065   shear_image=BorderImage(integral_image,&border_info,exception);
02066   if (shear_image == (Image *) NULL)
02067     ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
02068   integral_image=DestroyImage(integral_image);
02069   /*
02070     Shear the image.
02071   */
02072   if (shear_image->matte == MagickFalse)
02073     (void) SetImageAlphaChannel(shear_image,OpaqueAlphaChannel);
02074   (void) XShearImage(shear_image,shear.x,image->columns,image->rows,x_offset,
02075     ((long) shear_image->rows-image->rows)/2);
02076   (void) YShearImage(shear_image,shear.y,y_width,image->rows,
02077     ((long) shear_image->columns-y_width)/2,y_offset);
02078   CropToFitImage(&shear_image,shear.x,shear.y,(MagickRealType) image->columns,
02079     (MagickRealType) image->rows,MagickFalse,exception);
02080   shear_image->compose=image->compose;
02081   shear_image->page.width=0;
02082   shear_image->page.height=0;
02083   return(shear_image);
02084 }

Generated on 19 Nov 2009 for MagickCore by  doxygen 1.6.1