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