Home | History | Annotate | Download | only in MagickCore
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %       TTTTT  RRRR    AAA   N   N  SSSSS  FFFFF   OOO   RRRR   M   M         %
      7 %         T    R   R  A   A  NN  N  SS     F      O   O  R   R  MM MM         %
      8 %         T    RRRR   AAAAA  N N N   SSS   FFF    O   O  RRRR   M M M         %
      9 %         T    R R    A   A  N  NN     SS  F      O   O  R R    M   M         %
     10 %         T    R  R   A   A  N   N  SSSSS  F       OOO   R  R   M   M         %
     11 %                                                                             %
     12 %                                                                             %
     13 %                    MagickCore Image Transform Methods                       %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                                   Cristy                                    %
     17 %                                 July 1992                                   %
     18 %                                                                             %
     19 %                                                                             %
     20 %  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
     21 %  dedicated to making software imaging solutions freely available.           %
     22 %                                                                             %
     23 %  You may not use this file except in compliance with the License.  You may  %
     24 %  obtain a copy of the License at                                            %
     25 %                                                                             %
     26 %    http://www.imagemagick.org/script/license.php                            %
     27 %                                                                             %
     28 %  Unless required by applicable law or agreed to in writing, software        %
     29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
     30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
     31 %  See the License for the specific language governing permissions and        %
     32 %  limitations under the License.                                             %
     33 %                                                                             %
     34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     35 %
     36 %
     37 */
     38 
     39 /*
     41   Include declarations.
     42 */
     43 #include "MagickCore/studio.h"
     44 #include "MagickCore/attribute.h"
     45 #include "MagickCore/cache.h"
     46 #include "MagickCore/cache-view.h"
     47 #include "MagickCore/color.h"
     48 #include "MagickCore/color-private.h"
     49 #include "MagickCore/colorspace-private.h"
     50 #include "MagickCore/composite.h"
     51 #include "MagickCore/distort.h"
     52 #include "MagickCore/draw.h"
     53 #include "MagickCore/effect.h"
     54 #include "MagickCore/exception.h"
     55 #include "MagickCore/exception-private.h"
     56 #include "MagickCore/geometry.h"
     57 #include "MagickCore/image.h"
     58 #include "MagickCore/memory_.h"
     59 #include "MagickCore/layer.h"
     60 #include "MagickCore/list.h"
     61 #include "MagickCore/monitor.h"
     62 #include "MagickCore/monitor-private.h"
     63 #include "MagickCore/pixel-accessor.h"
     64 #include "MagickCore/resource_.h"
     65 #include "MagickCore/resize.h"
     66 #include "MagickCore/statistic.h"
     67 #include "MagickCore/string_.h"
     68 #include "MagickCore/thread-private.h"
     69 #include "MagickCore/transform.h"
     70 
     71 /*
     73 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     74 %                                                                             %
     75 %                                                                             %
     76 %                                                                             %
     77 %   A u t o O r i e n t I m a g e                                             %
     78 %                                                                             %
     79 %                                                                             %
     80 %                                                                             %
     81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     82 %
     83 %  AutoOrientImage() adjusts an image so that its orientation is suitable for
     84 %  viewing (i.e. top-left orientation).
     85 %
     86 %  The format of the AutoOrientImage method is:
     87 %
     88 %      Image *AutoOrientImage(const Image *image,
     89 %        const OrientationType orientation,ExceptionInfo *exception)
     90 %
     91 %  A description of each parameter follows:
     92 %
     93 %    o image: The image.
     94 %
     95 %    o orientation: Current image orientation.
     96 %
     97 %    o exception: Return any errors or warnings in this structure.
     98 %
     99 */
    100 MagickExport Image *AutoOrientImage(const Image *image,
    101   const OrientationType orientation,ExceptionInfo *exception)
    102 {
    103   Image
    104     *orient_image;
    105 
    106   assert(image != (const Image *) NULL);
    107   assert(image->signature == MagickCoreSignature);
    108   assert(exception != (ExceptionInfo *) NULL);
    109   assert(exception->signature == MagickCoreSignature);
    110   orient_image=(Image *) NULL;
    111   switch(orientation)
    112   {
    113     case UndefinedOrientation:
    114     case TopLeftOrientation:
    115     default:
    116     {
    117       orient_image=CloneImage(image,0,0,MagickTrue,exception);
    118       break;
    119     }
    120     case TopRightOrientation:
    121     {
    122       orient_image=FlopImage(image,exception);
    123       break;
    124     }
    125     case BottomRightOrientation:
    126     {
    127       orient_image=RotateImage(image,180.0,exception);
    128       break;
    129     }
    130     case BottomLeftOrientation:
    131     {
    132       orient_image=FlipImage(image,exception);
    133       break;
    134     }
    135     case LeftTopOrientation:
    136     {
    137       orient_image=TransposeImage(image,exception);
    138       break;
    139     }
    140     case RightTopOrientation:
    141     {
    142       orient_image=RotateImage(image,90.0,exception);
    143       break;
    144     }
    145     case RightBottomOrientation:
    146     {
    147       orient_image=TransverseImage(image,exception);
    148       break;
    149     }
    150     case LeftBottomOrientation:
    151     {
    152       orient_image=RotateImage(image,270.0,exception);
    153       break;
    154     }
    155   }
    156   if (orient_image != (Image *) NULL)
    157     orient_image->orientation=TopLeftOrientation;
    158   return(orient_image);
    159 }
    160 
    161 /*
    163 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    164 %                                                                             %
    165 %                                                                             %
    166 %                                                                             %
    167 %   C h o p I m a g e                                                         %
    168 %                                                                             %
    169 %                                                                             %
    170 %                                                                             %
    171 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    172 %
    173 %  ChopImage() removes a region of an image and collapses the image to occupy
    174 %  the removed portion.
    175 %
    176 %  The format of the ChopImage method is:
    177 %
    178 %      Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
    179 %        ExceptionInfo *exception)
    180 %
    181 %  A description of each parameter follows:
    182 %
    183 %    o image: the image.
    184 %
    185 %    o chop_info: Define the region of the image to chop.
    186 %
    187 %    o exception: return any errors or warnings in this structure.
    188 %
    189 */
    190 MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
    191   ExceptionInfo *exception)
    192 {
    193 #define ChopImageTag  "Chop/Image"
    194 
    195   CacheView
    196     *chop_view,
    197     *image_view;
    198 
    199   Image
    200     *chop_image;
    201 
    202   MagickBooleanType
    203     status;
    204 
    205   MagickOffsetType
    206     progress;
    207 
    208   RectangleInfo
    209     extent;
    210 
    211   ssize_t
    212     y;
    213 
    214   /*
    215     Check chop geometry.
    216   */
    217   assert(image != (const Image *) NULL);
    218   assert(image->signature == MagickCoreSignature);
    219   if (image->debug != MagickFalse)
    220     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    221   assert(exception != (ExceptionInfo *) NULL);
    222   assert(exception->signature == MagickCoreSignature);
    223   assert(chop_info != (RectangleInfo *) NULL);
    224   if (((chop_info->x+(ssize_t) chop_info->width) < 0) ||
    225       ((chop_info->y+(ssize_t) chop_info->height) < 0) ||
    226       (chop_info->x > (ssize_t) image->columns) ||
    227       (chop_info->y > (ssize_t) image->rows))
    228     ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
    229   extent=(*chop_info);
    230   if ((extent.x+(ssize_t) extent.width) > (ssize_t) image->columns)
    231     extent.width=(size_t) ((ssize_t) image->columns-extent.x);
    232   if ((extent.y+(ssize_t) extent.height) > (ssize_t) image->rows)
    233     extent.height=(size_t) ((ssize_t) image->rows-extent.y);
    234   if (extent.x < 0)
    235     {
    236       extent.width-=(size_t) (-extent.x);
    237       extent.x=0;
    238     }
    239   if (extent.y < 0)
    240     {
    241       extent.height-=(size_t) (-extent.y);
    242       extent.y=0;
    243     }
    244   chop_image=CloneImage(image,image->columns-extent.width,image->rows-
    245     extent.height,MagickTrue,exception);
    246   if (chop_image == (Image *) NULL)
    247     return((Image *) NULL);
    248   /*
    249     Extract chop image.
    250   */
    251   status=MagickTrue;
    252   progress=0;
    253   image_view=AcquireVirtualCacheView(image,exception);
    254   chop_view=AcquireAuthenticCacheView(chop_image,exception);
    255 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    256   #pragma omp parallel for schedule(static,4) shared(status) \
    257     magick_threads(image,chop_image,1,1)
    258 #endif
    259   for (y=0; y < (ssize_t) extent.y; y++)
    260   {
    261     register const Quantum
    262       *magick_restrict p;
    263 
    264     register ssize_t
    265       x;
    266 
    267     register Quantum
    268       *magick_restrict q;
    269 
    270     if (status == MagickFalse)
    271       continue;
    272     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
    273     q=QueueCacheViewAuthenticPixels(chop_view,0,y,chop_image->columns,1,
    274       exception);
    275     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
    276       {
    277         status=MagickFalse;
    278         continue;
    279       }
    280     for (x=0; x < (ssize_t) image->columns; x++)
    281     {
    282       if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
    283         {
    284           register ssize_t
    285             i;
    286 
    287           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
    288           {
    289             PixelChannel channel=GetPixelChannelChannel(image,i);
    290             PixelTrait traits=GetPixelChannelTraits(image,channel);
    291             PixelTrait chop_traits=GetPixelChannelTraits(chop_image,channel);
    292             if ((traits == UndefinedPixelTrait) ||
    293                 (chop_traits == UndefinedPixelTrait))
    294               continue;
    295             SetPixelChannel(chop_image,channel,p[i],q);
    296           }
    297           q+=GetPixelChannels(chop_image);
    298         }
    299       p+=GetPixelChannels(image);
    300     }
    301     if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
    302       status=MagickFalse;
    303     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    304       {
    305         MagickBooleanType
    306           proceed;
    307 
    308 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    309         #pragma omp critical (MagickCore_ChopImage)
    310 #endif
    311         proceed=SetImageProgress(image,ChopImageTag,progress++,image->rows);
    312         if (proceed == MagickFalse)
    313           status=MagickFalse;
    314       }
    315   }
    316   /*
    317     Extract chop image.
    318   */
    319 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    320   #pragma omp parallel for schedule(static,4) shared(progress,status) \
    321     magick_threads(image,chop_image,1,1)
    322 #endif
    323   for (y=0; y < (ssize_t) (image->rows-(extent.y+extent.height)); y++)
    324   {
    325     register const Quantum
    326       *magick_restrict p;
    327 
    328     register ssize_t
    329       x;
    330 
    331     register Quantum
    332       *magick_restrict q;
    333 
    334     if (status == MagickFalse)
    335       continue;
    336     p=GetCacheViewVirtualPixels(image_view,0,extent.y+extent.height+y,
    337       image->columns,1,exception);
    338     q=QueueCacheViewAuthenticPixels(chop_view,0,extent.y+y,chop_image->columns,
    339       1,exception);
    340     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
    341       {
    342         status=MagickFalse;
    343         continue;
    344       }
    345     for (x=0; x < (ssize_t) image->columns; x++)
    346     {
    347       if ((x < extent.x) || (x >= (ssize_t) (extent.x+extent.width)))
    348         {
    349           register ssize_t
    350             i;
    351 
    352           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
    353           {
    354             PixelChannel channel=GetPixelChannelChannel(image,i);
    355             PixelTrait traits=GetPixelChannelTraits(image,channel);
    356             PixelTrait chop_traits=GetPixelChannelTraits(chop_image,channel);
    357             if ((traits == UndefinedPixelTrait) ||
    358                 (chop_traits == UndefinedPixelTrait))
    359               continue;
    360             SetPixelChannel(chop_image,channel,p[i],q);
    361           }
    362           q+=GetPixelChannels(chop_image);
    363         }
    364       p+=GetPixelChannels(image);
    365     }
    366     if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
    367       status=MagickFalse;
    368     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    369       {
    370         MagickBooleanType
    371           proceed;
    372 
    373 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    374         #pragma omp critical (MagickCore_ChopImage)
    375 #endif
    376         proceed=SetImageProgress(image,ChopImageTag,progress++,image->rows);
    377         if (proceed == MagickFalse)
    378           status=MagickFalse;
    379       }
    380   }
    381   chop_view=DestroyCacheView(chop_view);
    382   image_view=DestroyCacheView(image_view);
    383   chop_image->type=image->type;
    384   if (status == MagickFalse)
    385     chop_image=DestroyImage(chop_image);
    386   return(chop_image);
    387 }
    388 
    389 /*
    391 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    392 %                                                                             %
    393 %                                                                             %
    394 %                                                                             %
    395 +     C o n s o l i d a t e C M Y K I m a g e                                 %
    396 %                                                                             %
    397 %                                                                             %
    398 %                                                                             %
    399 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    400 %
    401 %  ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
    402 %  single image.
    403 %
    404 %  The format of the ConsolidateCMYKImage method is:
    405 %
    406 %      Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
    407 %
    408 %  A description of each parameter follows:
    409 %
    410 %    o image: the image sequence.
    411 %
    412 %    o exception: return any errors or warnings in this structure.
    413 %
    414 */
    415 MagickExport Image *ConsolidateCMYKImages(const Image *images,
    416   ExceptionInfo *exception)
    417 {
    418   CacheView
    419     *cmyk_view,
    420     *image_view;
    421 
    422   Image
    423     *cmyk_image,
    424     *cmyk_images;
    425 
    426   register ssize_t
    427     j;
    428 
    429   ssize_t
    430     y;
    431 
    432   /*
    433     Consolidate separate C, M, Y, and K planes into a single image.
    434   */
    435   assert(images != (Image *) NULL);
    436   assert(images->signature == MagickCoreSignature);
    437   if (images->debug != MagickFalse)
    438     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
    439   assert(exception != (ExceptionInfo *) NULL);
    440   assert(exception->signature == MagickCoreSignature);
    441   cmyk_images=NewImageList();
    442   for (j=0; j < (ssize_t) GetImageListLength(images); j+=4)
    443   {
    444     register ssize_t
    445       i;
    446 
    447     assert(images != (Image *) NULL);
    448     cmyk_image=CloneImage(images,images->columns,images->rows,MagickTrue,
    449       exception);
    450     if (cmyk_image == (Image *) NULL)
    451       break;
    452     if (SetImageStorageClass(cmyk_image,DirectClass,exception) == MagickFalse)
    453       break;
    454     (void) SetImageColorspace(cmyk_image,CMYKColorspace,exception);
    455     for (i=0; i < 4; i++)
    456     {
    457       image_view=AcquireVirtualCacheView(images,exception);
    458       cmyk_view=AcquireAuthenticCacheView(cmyk_image,exception);
    459       for (y=0; y < (ssize_t) images->rows; y++)
    460       {
    461         register const Quantum
    462           *magick_restrict p;
    463 
    464         register ssize_t
    465           x;
    466 
    467         register Quantum
    468           *magick_restrict q;
    469 
    470         p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
    471         q=QueueCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
    472           exception);
    473         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
    474           break;
    475         for (x=0; x < (ssize_t) images->columns; x++)
    476         {
    477           Quantum
    478             pixel;
    479 
    480           pixel=QuantumRange-GetPixelIntensity(images,p);
    481           switch (i)
    482           {
    483             case 0: SetPixelCyan(cmyk_image,pixel,q);  break;
    484             case 1: SetPixelMagenta(cmyk_image,pixel,q);  break;
    485             case 2: SetPixelYellow(cmyk_image,pixel,q);  break;
    486             case 3: SetPixelBlack(cmyk_image,pixel,q);  break;
    487             default: break;
    488           }
    489           p+=GetPixelChannels(images);
    490           q+=GetPixelChannels(cmyk_image);
    491         }
    492         if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
    493           break;
    494       }
    495       cmyk_view=DestroyCacheView(cmyk_view);
    496       image_view=DestroyCacheView(image_view);
    497       images=GetNextImageInList(images);
    498       if (images == (Image *) NULL)
    499         break;
    500     }
    501     AppendImageToList(&cmyk_images,cmyk_image);
    502   }
    503   return(cmyk_images);
    504 }
    505 
    506 /*
    508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    509 %                                                                             %
    510 %                                                                             %
    511 %                                                                             %
    512 %   C r o p I m a g e                                                         %
    513 %                                                                             %
    514 %                                                                             %
    515 %                                                                             %
    516 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    517 %
    518 %  CropImage() extracts a region of the image starting at the offset defined
    519 %  by geometry.  Region must be fully defined, and no special handling of
    520 %  geometry flags is performed.
    521 %
    522 %  The format of the CropImage method is:
    523 %
    524 %      Image *CropImage(const Image *image,const RectangleInfo *geometry,
    525 %        ExceptionInfo *exception)
    526 %
    527 %  A description of each parameter follows:
    528 %
    529 %    o image: the image.
    530 %
    531 %    o geometry: Define the region of the image to crop with members
    532 %      x, y, width, and height.
    533 %
    534 %    o exception: return any errors or warnings in this structure.
    535 %
    536 */
    537 MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
    538   ExceptionInfo *exception)
    539 {
    540 #define CropImageTag  "Crop/Image"
    541 
    542   CacheView
    543     *crop_view,
    544     *image_view;
    545 
    546   Image
    547     *crop_image;
    548 
    549   MagickBooleanType
    550     status;
    551 
    552   MagickOffsetType
    553     progress;
    554 
    555   OffsetInfo
    556     offset;
    557 
    558   RectangleInfo
    559     bounding_box,
    560     page;
    561 
    562   ssize_t
    563     y;
    564 
    565   /*
    566     Check crop geometry.
    567   */
    568   assert(image != (const Image *) NULL);
    569   assert(image->signature == MagickCoreSignature);
    570   if (image->debug != MagickFalse)
    571     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    572   assert(geometry != (const RectangleInfo *) NULL);
    573   assert(exception != (ExceptionInfo *) NULL);
    574   assert(exception->signature == MagickCoreSignature);
    575   bounding_box=image->page;
    576   if ((bounding_box.width == 0) || (bounding_box.height == 0))
    577     {
    578       bounding_box.width=image->columns;
    579       bounding_box.height=image->rows;
    580     }
    581   page=(*geometry);
    582   if (page.width == 0)
    583     page.width=bounding_box.width;
    584   if (page.height == 0)
    585     page.height=bounding_box.height;
    586   if (((bounding_box.x-page.x) >= (ssize_t) page.width) ||
    587       ((bounding_box.y-page.y) >= (ssize_t) page.height) ||
    588       ((page.x-bounding_box.x) > (ssize_t) image->columns) ||
    589       ((page.y-bounding_box.y) > (ssize_t) image->rows))
    590     {
    591       /*
    592         Crop is not within virtual canvas, return 1 pixel transparent image.
    593       */
    594       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
    595         "GeometryDoesNotContainImage","`%s'",image->filename);
    596       crop_image=CloneImage(image,1,1,MagickTrue,exception);
    597       if (crop_image == (Image *) NULL)
    598         return((Image *) NULL);
    599       crop_image->background_color.alpha=(Quantum) TransparentAlpha;
    600       crop_image->alpha_trait=BlendPixelTrait;
    601       (void) SetImageBackgroundColor(crop_image,exception);
    602       crop_image->page=bounding_box;
    603       crop_image->page.x=(-1);
    604       crop_image->page.y=(-1);
    605       if (crop_image->dispose == BackgroundDispose)
    606         crop_image->dispose=NoneDispose;
    607       return(crop_image);
    608     }
    609   if ((page.x < 0) && (bounding_box.x >= 0))
    610     {
    611       page.width+=page.x-bounding_box.x;
    612       page.x=0;
    613     }
    614   else
    615     {
    616       page.width-=bounding_box.x-page.x;
    617       page.x-=bounding_box.x;
    618       if (page.x < 0)
    619         page.x=0;
    620     }
    621   if ((page.y < 0) && (bounding_box.y >= 0))
    622     {
    623       page.height+=page.y-bounding_box.y;
    624       page.y=0;
    625     }
    626   else
    627     {
    628       page.height-=bounding_box.y-page.y;
    629       page.y-=bounding_box.y;
    630       if (page.y < 0)
    631         page.y=0;
    632     }
    633   if ((page.x+(ssize_t) page.width) > (ssize_t) image->columns)
    634     page.width=image->columns-page.x;
    635   if ((geometry->width != 0) && (page.width > geometry->width))
    636     page.width=geometry->width;
    637   if ((page.y+(ssize_t) page.height) > (ssize_t) image->rows)
    638     page.height=image->rows-page.y;
    639   if ((geometry->height != 0) && (page.height > geometry->height))
    640     page.height=geometry->height;
    641   bounding_box.x+=page.x;
    642   bounding_box.y+=page.y;
    643   if ((page.width == 0) || (page.height == 0))
    644     {
    645       (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
    646         "GeometryDoesNotContainImage","`%s'",image->filename);
    647       return((Image *) NULL);
    648     }
    649   /*
    650     Initialize crop image attributes.
    651   */
    652   crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
    653   if (crop_image == (Image *) NULL)
    654     return((Image *) NULL);
    655   crop_image->page.width=image->page.width;
    656   crop_image->page.height=image->page.height;
    657   offset.x=(ssize_t) (bounding_box.x+bounding_box.width);
    658   offset.y=(ssize_t) (bounding_box.y+bounding_box.height);
    659   if ((offset.x > (ssize_t) image->page.width) ||
    660       (offset.y > (ssize_t) image->page.height))
    661     {
    662       crop_image->page.width=bounding_box.width;
    663       crop_image->page.height=bounding_box.height;
    664     }
    665   crop_image->page.x=bounding_box.x;
    666   crop_image->page.y=bounding_box.y;
    667   /*
    668     Crop image.
    669   */
    670   status=MagickTrue;
    671   progress=0;
    672   image_view=AcquireVirtualCacheView(image,exception);
    673   crop_view=AcquireAuthenticCacheView(crop_image,exception);
    674 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    675   #pragma omp parallel for schedule(static,4) shared(status) \
    676     magick_threads(image,crop_image,1,1)
    677 #endif
    678   for (y=0; y < (ssize_t) crop_image->rows; y++)
    679   {
    680     register const Quantum
    681       *magick_restrict p;
    682 
    683     register Quantum
    684       *magick_restrict q;
    685 
    686     register ssize_t
    687       x;
    688 
    689     if (status == MagickFalse)
    690       continue;
    691     p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
    692       1,exception);
    693     q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
    694       exception);
    695     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
    696       {
    697         status=MagickFalse;
    698         continue;
    699       }
    700     for (x=0; x < (ssize_t) crop_image->columns; x++)
    701     {
    702       register ssize_t
    703         i;
    704 
    705       if (GetPixelReadMask(image,p) == 0)
    706         {
    707           SetPixelBackgoundColor(crop_image,q);
    708           p+=GetPixelChannels(image);
    709           q+=GetPixelChannels(crop_image);
    710           continue;
    711         }
    712       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
    713       {
    714         PixelChannel channel=GetPixelChannelChannel(image,i);
    715         PixelTrait traits=GetPixelChannelTraits(image,channel);
    716         PixelTrait crop_traits=GetPixelChannelTraits(crop_image,channel);
    717         if ((traits == UndefinedPixelTrait) ||
    718             (crop_traits == UndefinedPixelTrait))
    719           continue;
    720         SetPixelChannel(crop_image,channel,p[i],q);
    721       }
    722       p+=GetPixelChannels(image);
    723       q+=GetPixelChannels(crop_image);
    724     }
    725     if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
    726       status=MagickFalse;
    727     if (image->progress_monitor != (MagickProgressMonitor) NULL)
    728       {
    729         MagickBooleanType
    730           proceed;
    731 
    732 #if defined(MAGICKCORE_OPENMP_SUPPORT)
    733         #pragma omp critical (MagickCore_CropImage)
    734 #endif
    735         proceed=SetImageProgress(image,CropImageTag,progress++,image->rows);
    736         if (proceed == MagickFalse)
    737           status=MagickFalse;
    738       }
    739   }
    740   crop_view=DestroyCacheView(crop_view);
    741   image_view=DestroyCacheView(image_view);
    742   crop_image->type=image->type;
    743   if (status == MagickFalse)
    744     crop_image=DestroyImage(crop_image);
    745   return(crop_image);
    746 }
    747 
    748 /*
    750 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    751 %                                                                             %
    752 %                                                                             %
    753 %                                                                             %
    754 %   C r o p I m a g e T o T i l e s                                           %
    755 %                                                                             %
    756 %                                                                             %
    757 %                                                                             %
    758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    759 %
    760 %  CropImageToTiles() crops a single image, into a possible list of tiles.
    761 %  This may include a single sub-region of the image.  This basically applies
    762 %  all the normal geometry flags for Crop.
    763 %
    764 %      Image *CropImageToTiles(const Image *image,
    765 %         const RectangleInfo *crop_geometry, ExceptionInfo *exception)
    766 %
    767 %  A description of each parameter follows:
    768 %
    769 %    o image: the image The transformed image is returned as this parameter.
    770 %
    771 %    o crop_geometry: A crop geometry string.
    772 %
    773 %    o exception: return any errors or warnings in this structure.
    774 %
    775 */
    776 
    777 static inline double MagickRound(double x)
    778 {
    779   /*
    780     Round the fraction to nearest integer.
    781   */
    782   if ((x-floor(x)) < (ceil(x)-x))
    783     return(floor(x));
    784   return(ceil(x));
    785 }
    786 
    787 MagickExport Image *CropImageToTiles(const Image *image,
    788   const char *crop_geometry,ExceptionInfo *exception)
    789 {
    790   Image
    791     *next,
    792     *crop_image;
    793 
    794   MagickStatusType
    795     flags;
    796 
    797   RectangleInfo
    798     geometry;
    799 
    800   assert(image != (Image *) NULL);
    801   assert(image->signature == MagickCoreSignature);
    802   if (image->debug != MagickFalse)
    803     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    804   crop_image=NewImageList();
    805   next=NewImageList();
    806   flags=ParseGravityGeometry(image,crop_geometry,&geometry,exception);
    807   if ((flags & AreaValue) != 0)
    808     {
    809       PointInfo
    810         delta,
    811         offset;
    812 
    813       RectangleInfo
    814         crop;
    815 
    816       size_t
    817         height,
    818         width;
    819 
    820       /*
    821         Crop into NxM tiles (@ flag).
    822       */
    823       width=image->columns;
    824       height=image->rows;
    825       if (geometry.width == 0)
    826         geometry.width=1;
    827       if (geometry.height == 0)
    828         geometry.height=1;
    829       if ((flags & AspectValue) == 0)
    830         {
    831           width-=(geometry.x < 0 ? -1 : 1)*geometry.x;
    832           height-=(geometry.y < 0 ? -1 : 1)*geometry.y;
    833         }
    834       else
    835         {
    836           width+=(geometry.x < 0 ? -1 : 1)*geometry.x;
    837           height+=(geometry.y < 0 ? -1 : 1)*geometry.y;
    838         }
    839       delta.x=(double) width/geometry.width;
    840       delta.y=(double) height/geometry.height;
    841       if (delta.x < 1.0)
    842         delta.x=1.0;
    843       if (delta.y < 1.0)
    844         delta.y=1.0;
    845       for (offset.y=0; offset.y < (double) height; )
    846       {
    847         if ((flags & AspectValue) == 0)
    848           {
    849             crop.y=(ssize_t) MagickRound((double) (offset.y-
    850               (geometry.y > 0 ? 0 : geometry.y)));
    851             offset.y+=delta.y;   /* increment now to find width */
    852             crop.height=(size_t) MagickRound((double) (offset.y+
    853               (geometry.y < 0 ? 0 : geometry.y)));
    854           }
    855         else
    856           {
    857             crop.y=(ssize_t) MagickRound((double) (offset.y-
    858               (geometry.y > 0 ? geometry.y : 0)));
    859             offset.y+=delta.y;  /* increment now to find width */
    860             crop.height=(size_t) MagickRound((double)
    861               (offset.y+(geometry.y < -1 ? geometry.y : 0)));
    862           }
    863         crop.height-=crop.y;
    864         crop.y+=image->page.y;
    865         for (offset.x=0; offset.x < (double) width; )
    866         {
    867           if ((flags & AspectValue) == 0)
    868             {
    869               crop.x=(ssize_t) MagickRound((double) (offset.x-
    870                 (geometry.x > 0 ? 0 : geometry.x)));
    871               offset.x+=delta.x;  /* increment now to find height */
    872               crop.width=(size_t) MagickRound((double) (offset.x+
    873                 (geometry.x < 0 ? 0 : geometry.x)));
    874             }
    875           else
    876             {
    877               crop.x=(ssize_t) MagickRound((double) (offset.x-
    878                 (geometry.x > 0 ? geometry.x : 0)));
    879               offset.x+=delta.x;  /* increment now to find height */
    880               crop.width=(size_t) MagickRound((double) (offset.x+
    881                 (geometry.x < 0 ? geometry.x : 0)));
    882             }
    883           crop.width-=crop.x;
    884           crop.x+=image->page.x;
    885           next=CropImage(image,&crop,exception);
    886           if (next != (Image *) NULL)
    887             AppendImageToList(&crop_image,next);
    888         }
    889       }
    890       ClearMagickException(exception);
    891       return(crop_image);
    892     }
    893   if (((geometry.width == 0) && (geometry.height == 0)) ||
    894       ((flags & XValue) != 0) || ((flags & YValue) != 0))
    895     {
    896       /*
    897         Crop a single region at +X+Y.
    898       */
    899       crop_image=CropImage(image,&geometry,exception);
    900       if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
    901         {
    902           crop_image->page.width=geometry.width;
    903           crop_image->page.height=geometry.height;
    904           crop_image->page.x-=geometry.x;
    905           crop_image->page.y-=geometry.y;
    906         }
    907       return(crop_image);
    908     }
    909   if ((image->columns > geometry.width) || (image->rows > geometry.height))
    910     {
    911       RectangleInfo
    912         page;
    913 
    914       size_t
    915         height,
    916         width;
    917 
    918       ssize_t
    919         x,
    920         y;
    921 
    922       /*
    923         Crop into tiles of fixed size WxH.
    924       */
    925       page=image->page;
    926       if (page.width == 0)
    927         page.width=image->columns;
    928       if (page.height == 0)
    929         page.height=image->rows;
    930       width=geometry.width;
    931       if (width == 0)
    932         width=page.width;
    933       height=geometry.height;
    934       if (height == 0)
    935         height=page.height;
    936       next=NewImageList();
    937       for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
    938       {
    939         for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
    940         {
    941           geometry.width=width;
    942           geometry.height=height;
    943           geometry.x=x;
    944           geometry.y=y;
    945           next=CropImage(image,&geometry,exception);
    946           if (next == (Image *) NULL)
    947             break;
    948           AppendImageToList(&crop_image,next);
    949         }
    950         if (next == (Image *) NULL)
    951           break;
    952       }
    953       return(crop_image);
    954     }
    955   return(CloneImage(image,0,0,MagickTrue,exception));
    956 }
    957 
    958 /*
    960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    961 %                                                                             %
    962 %                                                                             %
    963 %                                                                             %
    964 %   E x c e r p t I m a g e                                                   %
    965 %                                                                             %
    966 %                                                                             %
    967 %                                                                             %
    968 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    969 %
    970 %  ExcerptImage() returns a excerpt of the image as defined by the geometry.
    971 %
    972 %  The format of the ExcerptImage method is:
    973 %
    974 %      Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
    975 %        ExceptionInfo *exception)
    976 %
    977 %  A description of each parameter follows:
    978 %
    979 %    o image: the image.
    980 %
    981 %    o geometry: Define the region of the image to extend with members
    982 %      x, y, width, and height.
    983 %
    984 %    o exception: return any errors or warnings in this structure.
    985 %
    986 */
    987 MagickExport Image *ExcerptImage(const Image *image,
    988   const RectangleInfo *geometry,ExceptionInfo *exception)
    989 {
    990 #define ExcerptImageTag  "Excerpt/Image"
    991 
    992   CacheView
    993     *excerpt_view,
    994     *image_view;
    995 
    996   Image
    997     *excerpt_image;
    998 
    999   MagickBooleanType
   1000     status;
   1001 
   1002   MagickOffsetType
   1003     progress;
   1004 
   1005   ssize_t
   1006     y;
   1007 
   1008   /*
   1009     Allocate excerpt image.
   1010   */
   1011   assert(image != (const Image *) NULL);
   1012   assert(image->signature == MagickCoreSignature);
   1013   if (image->debug != MagickFalse)
   1014     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1015   assert(geometry != (const RectangleInfo *) NULL);
   1016   assert(exception != (ExceptionInfo *) NULL);
   1017   assert(exception->signature == MagickCoreSignature);
   1018   excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
   1019     exception);
   1020   if (excerpt_image == (Image *) NULL)
   1021     return((Image *) NULL);
   1022   /*
   1023     Excerpt each row.
   1024   */
   1025   status=MagickTrue;
   1026   progress=0;
   1027   image_view=AcquireVirtualCacheView(image,exception);
   1028   excerpt_view=AcquireAuthenticCacheView(excerpt_image,exception);
   1029 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1030   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   1031     magick_threads(image,excerpt_image,excerpt_image->rows,1)
   1032 #endif
   1033   for (y=0; y < (ssize_t) excerpt_image->rows; y++)
   1034   {
   1035     register const Quantum
   1036       *magick_restrict p;
   1037 
   1038     register Quantum
   1039       *magick_restrict q;
   1040 
   1041     register ssize_t
   1042       x;
   1043 
   1044     if (status == MagickFalse)
   1045       continue;
   1046     p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
   1047       geometry->width,1,exception);
   1048     q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
   1049       exception);
   1050     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   1051       {
   1052         status=MagickFalse;
   1053         continue;
   1054       }
   1055     for (x=0; x < (ssize_t) excerpt_image->columns; x++)
   1056     {
   1057       register ssize_t
   1058         i;
   1059 
   1060       if (GetPixelReadMask(image,p) == 0)
   1061         {
   1062           SetPixelBackgoundColor(excerpt_image,q);
   1063           p+=GetPixelChannels(image);
   1064           q+=GetPixelChannels(excerpt_image);
   1065           continue;
   1066         }
   1067       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1068       {
   1069         PixelChannel channel=GetPixelChannelChannel(image,i);
   1070         PixelTrait traits=GetPixelChannelTraits(image,channel);
   1071         PixelTrait excerpt_traits=GetPixelChannelTraits(excerpt_image,channel);
   1072         if ((traits == UndefinedPixelTrait) ||
   1073             (excerpt_traits == UndefinedPixelTrait))
   1074           continue;
   1075         SetPixelChannel(excerpt_image,channel,p[i],q);
   1076       }
   1077       p+=GetPixelChannels(image);
   1078       q+=GetPixelChannels(excerpt_image);
   1079     }
   1080     if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
   1081       status=MagickFalse;
   1082     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1083       {
   1084         MagickBooleanType
   1085           proceed;
   1086 
   1087 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1088         #pragma omp critical (MagickCore_ExcerptImage)
   1089 #endif
   1090         proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows);
   1091         if (proceed == MagickFalse)
   1092           status=MagickFalse;
   1093       }
   1094   }
   1095   excerpt_view=DestroyCacheView(excerpt_view);
   1096   image_view=DestroyCacheView(image_view);
   1097   excerpt_image->type=image->type;
   1098   if (status == MagickFalse)
   1099     excerpt_image=DestroyImage(excerpt_image);
   1100   return(excerpt_image);
   1101 }
   1102 
   1103 /*
   1105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1106 %                                                                             %
   1107 %                                                                             %
   1108 %                                                                             %
   1109 %   E x t e n t I m a g e                                                     %
   1110 %                                                                             %
   1111 %                                                                             %
   1112 %                                                                             %
   1113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1114 %
   1115 %  ExtentImage() extends the image as defined by the geometry, gravity, and
   1116 %  image background color.  Set the (x,y) offset of the geometry to move the
   1117 %  original image relative to the extended image.
   1118 %
   1119 %  The format of the ExtentImage method is:
   1120 %
   1121 %      Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
   1122 %        ExceptionInfo *exception)
   1123 %
   1124 %  A description of each parameter follows:
   1125 %
   1126 %    o image: the image.
   1127 %
   1128 %    o geometry: Define the region of the image to extend with members
   1129 %      x, y, width, and height.
   1130 %
   1131 %    o exception: return any errors or warnings in this structure.
   1132 %
   1133 */
   1134 MagickExport Image *ExtentImage(const Image *image,
   1135   const RectangleInfo *geometry,ExceptionInfo *exception)
   1136 {
   1137   Image
   1138     *extent_image;
   1139 
   1140   /*
   1141     Allocate extent image.
   1142   */
   1143   assert(image != (const Image *) NULL);
   1144   assert(image->signature == MagickCoreSignature);
   1145   if (image->debug != MagickFalse)
   1146     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1147   assert(geometry != (const RectangleInfo *) NULL);
   1148   assert(exception != (ExceptionInfo *) NULL);
   1149   assert(exception->signature == MagickCoreSignature);
   1150   if ((image->columns == geometry->width) &&
   1151       (image->rows == geometry->height) &&
   1152       (geometry->x == 0) && (geometry->y == 0))
   1153     return(CloneImage(image,0,0,MagickTrue,exception));
   1154   extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
   1155     exception);
   1156   if (extent_image == (Image *) NULL)
   1157     return((Image *) NULL);
   1158   (void) SetImageBackgroundColor(extent_image,exception);
   1159   (void) CompositeImage(extent_image,image,image->compose,MagickTrue,
   1160     -geometry->x,-geometry->y,exception);
   1161   return(extent_image);
   1162 }
   1163 
   1164 /*
   1166 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1167 %                                                                             %
   1168 %                                                                             %
   1169 %                                                                             %
   1170 %   F l i p I m a g e                                                         %
   1171 %                                                                             %
   1172 %                                                                             %
   1173 %                                                                             %
   1174 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1175 %
   1176 %  FlipImage() creates a vertical mirror image by reflecting the pixels
   1177 %  around the central x-axis.
   1178 %
   1179 %  The format of the FlipImage method is:
   1180 %
   1181 %      Image *FlipImage(const Image *image,ExceptionInfo *exception)
   1182 %
   1183 %  A description of each parameter follows:
   1184 %
   1185 %    o image: the image.
   1186 %
   1187 %    o exception: return any errors or warnings in this structure.
   1188 %
   1189 */
   1190 MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
   1191 {
   1192 #define FlipImageTag  "Flip/Image"
   1193 
   1194   CacheView
   1195     *flip_view,
   1196     *image_view;
   1197 
   1198   Image
   1199     *flip_image;
   1200 
   1201   MagickBooleanType
   1202     status;
   1203 
   1204   MagickOffsetType
   1205     progress;
   1206 
   1207   RectangleInfo
   1208     page;
   1209 
   1210   ssize_t
   1211     y;
   1212 
   1213   assert(image != (const Image *) NULL);
   1214   assert(image->signature == MagickCoreSignature);
   1215   if (image->debug != MagickFalse)
   1216     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1217   assert(exception != (ExceptionInfo *) NULL);
   1218   assert(exception->signature == MagickCoreSignature);
   1219   flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
   1220   if (flip_image == (Image *) NULL)
   1221     return((Image *) NULL);
   1222   /*
   1223     Flip image.
   1224   */
   1225   status=MagickTrue;
   1226   progress=0;
   1227   page=image->page;
   1228   image_view=AcquireVirtualCacheView(image,exception);
   1229   flip_view=AcquireAuthenticCacheView(flip_image,exception);
   1230 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1231   #pragma omp parallel for schedule(static,4) shared(status) \
   1232     magick_threads(image,flip_image,1,1)
   1233 #endif
   1234   for (y=0; y < (ssize_t) flip_image->rows; y++)
   1235   {
   1236     register const Quantum
   1237       *magick_restrict p;
   1238 
   1239     register Quantum
   1240       *magick_restrict q;
   1241 
   1242     register ssize_t
   1243       x;
   1244 
   1245     if (status == MagickFalse)
   1246       continue;
   1247     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   1248     q=QueueCacheViewAuthenticPixels(flip_view,0,(ssize_t) (flip_image->rows-y-
   1249       1),flip_image->columns,1,exception);
   1250     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   1251       {
   1252         status=MagickFalse;
   1253         continue;
   1254       }
   1255     for (x=0; x < (ssize_t) flip_image->columns; x++)
   1256     {
   1257       register ssize_t
   1258         i;
   1259 
   1260       if (GetPixelReadMask(image,p) == 0)
   1261         {
   1262           SetPixelBackgoundColor(flip_image,q);
   1263           p+=GetPixelChannels(image);
   1264           q+=GetPixelChannels(flip_image);
   1265           continue;
   1266         }
   1267       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1268       {
   1269         PixelChannel channel=GetPixelChannelChannel(image,i);
   1270         PixelTrait traits=GetPixelChannelTraits(image,channel);
   1271         PixelTrait flip_traits=GetPixelChannelTraits(flip_image,channel);
   1272         if ((traits == UndefinedPixelTrait) ||
   1273             (flip_traits == UndefinedPixelTrait))
   1274           continue;
   1275         SetPixelChannel(flip_image,channel,p[i],q);
   1276       }
   1277       p+=GetPixelChannels(image);
   1278       q+=GetPixelChannels(flip_image);
   1279     }
   1280     if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
   1281       status=MagickFalse;
   1282     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1283       {
   1284         MagickBooleanType
   1285           proceed;
   1286 
   1287 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1288         #pragma omp critical (MagickCore_FlipImage)
   1289 #endif
   1290         proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
   1291         if (proceed == MagickFalse)
   1292           status=MagickFalse;
   1293       }
   1294   }
   1295   flip_view=DestroyCacheView(flip_view);
   1296   image_view=DestroyCacheView(image_view);
   1297   flip_image->type=image->type;
   1298   if (page.height != 0)
   1299     page.y=(ssize_t) (page.height-flip_image->rows-page.y);
   1300   flip_image->page=page;
   1301   if (status == MagickFalse)
   1302     flip_image=DestroyImage(flip_image);
   1303   return(flip_image);
   1304 }
   1305 
   1306 /*
   1308 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1309 %                                                                             %
   1310 %                                                                             %
   1311 %                                                                             %
   1312 %   F l o p I m a g e                                                         %
   1313 %                                                                             %
   1314 %                                                                             %
   1315 %                                                                             %
   1316 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1317 %
   1318 %  FlopImage() creates a horizontal mirror image by reflecting the pixels
   1319 %  around the central y-axis.
   1320 %
   1321 %  The format of the FlopImage method is:
   1322 %
   1323 %      Image *FlopImage(const Image *image,ExceptionInfo *exception)
   1324 %
   1325 %  A description of each parameter follows:
   1326 %
   1327 %    o image: the image.
   1328 %
   1329 %    o exception: return any errors or warnings in this structure.
   1330 %
   1331 */
   1332 MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
   1333 {
   1334 #define FlopImageTag  "Flop/Image"
   1335 
   1336   CacheView
   1337     *flop_view,
   1338     *image_view;
   1339 
   1340   Image
   1341     *flop_image;
   1342 
   1343   MagickBooleanType
   1344     status;
   1345 
   1346   MagickOffsetType
   1347     progress;
   1348 
   1349   RectangleInfo
   1350     page;
   1351 
   1352   ssize_t
   1353     y;
   1354 
   1355   assert(image != (const Image *) NULL);
   1356   assert(image->signature == MagickCoreSignature);
   1357   if (image->debug != MagickFalse)
   1358     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1359   assert(exception != (ExceptionInfo *) NULL);
   1360   assert(exception->signature == MagickCoreSignature);
   1361   flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
   1362   if (flop_image == (Image *) NULL)
   1363     return((Image *) NULL);
   1364   /*
   1365     Flop each row.
   1366   */
   1367   status=MagickTrue;
   1368   progress=0;
   1369   page=image->page;
   1370   image_view=AcquireVirtualCacheView(image,exception);
   1371   flop_view=AcquireAuthenticCacheView(flop_image,exception);
   1372 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1373   #pragma omp parallel for schedule(static,4) shared(status) \
   1374     magick_threads(image,flop_image,1,1)
   1375 #endif
   1376   for (y=0; y < (ssize_t) flop_image->rows; y++)
   1377   {
   1378     register const Quantum
   1379       *magick_restrict p;
   1380 
   1381     register ssize_t
   1382       x;
   1383 
   1384     register Quantum
   1385       *magick_restrict q;
   1386 
   1387     if (status == MagickFalse)
   1388       continue;
   1389     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   1390     q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
   1391       exception);
   1392     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   1393       {
   1394         status=MagickFalse;
   1395         continue;
   1396       }
   1397     q+=GetPixelChannels(flop_image)*flop_image->columns;
   1398     for (x=0; x < (ssize_t) flop_image->columns; x++)
   1399     {
   1400       register ssize_t
   1401         i;
   1402 
   1403       q-=GetPixelChannels(flop_image);
   1404       if (GetPixelReadMask(image,p) == 0)
   1405         {
   1406           p+=GetPixelChannels(image);
   1407           continue;
   1408         }
   1409       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1410       {
   1411         PixelChannel channel=GetPixelChannelChannel(image,i);
   1412         PixelTrait traits=GetPixelChannelTraits(image,channel);
   1413         PixelTrait flop_traits=GetPixelChannelTraits(flop_image,channel);
   1414         if ((traits == UndefinedPixelTrait) ||
   1415             (flop_traits == UndefinedPixelTrait))
   1416           continue;
   1417         SetPixelChannel(flop_image,channel,p[i],q);
   1418       }
   1419       p+=GetPixelChannels(image);
   1420     }
   1421     if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
   1422       status=MagickFalse;
   1423     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1424       {
   1425         MagickBooleanType
   1426           proceed;
   1427 
   1428 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1429         #pragma omp critical (MagickCore_FlopImage)
   1430 #endif
   1431         proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
   1432         if (proceed == MagickFalse)
   1433           status=MagickFalse;
   1434       }
   1435   }
   1436   flop_view=DestroyCacheView(flop_view);
   1437   image_view=DestroyCacheView(image_view);
   1438   flop_image->type=image->type;
   1439   if (page.width != 0)
   1440     page.x=(ssize_t) (page.width-flop_image->columns-page.x);
   1441   flop_image->page=page;
   1442   if (status == MagickFalse)
   1443     flop_image=DestroyImage(flop_image);
   1444   return(flop_image);
   1445 }
   1446 
   1447 /*
   1449 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1450 %                                                                             %
   1451 %                                                                             %
   1452 %                                                                             %
   1453 %   R o l l I m a g e                                                         %
   1454 %                                                                             %
   1455 %                                                                             %
   1456 %                                                                             %
   1457 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1458 %
   1459 %  RollImage() offsets an image as defined by x_offset and y_offset.
   1460 %
   1461 %  The format of the RollImage method is:
   1462 %
   1463 %      Image *RollImage(const Image *image,const ssize_t x_offset,
   1464 %        const ssize_t y_offset,ExceptionInfo *exception)
   1465 %
   1466 %  A description of each parameter follows:
   1467 %
   1468 %    o image: the image.
   1469 %
   1470 %    o x_offset: the number of columns to roll in the horizontal direction.
   1471 %
   1472 %    o y_offset: the number of rows to roll in the vertical direction.
   1473 %
   1474 %    o exception: return any errors or warnings in this structure.
   1475 %
   1476 */
   1477 
   1478 static MagickBooleanType CopyImageRegion(Image *destination,const Image *source,  const size_t columns,const size_t rows,const ssize_t sx,const ssize_t sy,
   1479   const ssize_t dx,const ssize_t dy,ExceptionInfo *exception)
   1480 {
   1481   CacheView
   1482     *source_view,
   1483     *destination_view;
   1484 
   1485   MagickBooleanType
   1486     status;
   1487 
   1488   ssize_t
   1489     y;
   1490 
   1491   if (columns == 0)
   1492     return(MagickTrue);
   1493   status=MagickTrue;
   1494   source_view=AcquireVirtualCacheView(source,exception);
   1495   destination_view=AcquireAuthenticCacheView(destination,exception);
   1496 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1497   #pragma omp parallel for schedule(static,4) shared(status) \
   1498     magick_threads(source,destination,rows,1)
   1499 #endif
   1500   for (y=0; y < (ssize_t) rows; y++)
   1501   {
   1502     MagickBooleanType
   1503       sync;
   1504 
   1505     register const Quantum
   1506       *magick_restrict p;
   1507 
   1508     register Quantum
   1509       *magick_restrict q;
   1510 
   1511     register ssize_t
   1512       x;
   1513 
   1514     /*
   1515       Transfer scanline.
   1516     */
   1517     if (status == MagickFalse)
   1518       continue;
   1519     p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
   1520     q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
   1521     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   1522       {
   1523         status=MagickFalse;
   1524         continue;
   1525       }
   1526     for (x=0; x < (ssize_t) columns; x++)
   1527     {
   1528       register ssize_t
   1529         i;
   1530 
   1531       if (GetPixelReadMask(source,p) == 0)
   1532         {
   1533           SetPixelBackgoundColor(destination,q);
   1534           p+=GetPixelChannels(source);
   1535           q+=GetPixelChannels(destination);
   1536           continue;
   1537         }
   1538       for (i=0; i < (ssize_t) GetPixelChannels(source); i++)
   1539       {
   1540         PixelChannel channel=GetPixelChannelChannel(source,i);
   1541         PixelTrait source_traits=GetPixelChannelTraits(source,channel);
   1542         PixelTrait destination_traits=GetPixelChannelTraits(destination,
   1543           channel);
   1544         if ((source_traits == UndefinedPixelTrait) ||
   1545             (destination_traits == UndefinedPixelTrait))
   1546           continue;
   1547         SetPixelChannel(destination,channel,p[i],q);
   1548       }
   1549       p+=GetPixelChannels(source);
   1550       q+=GetPixelChannels(destination);
   1551     }
   1552     sync=SyncCacheViewAuthenticPixels(destination_view,exception);
   1553     if (sync == MagickFalse)
   1554       status=MagickFalse;
   1555   }
   1556   destination_view=DestroyCacheView(destination_view);
   1557   source_view=DestroyCacheView(source_view);
   1558   return(status);
   1559 }
   1560 
   1561 MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
   1562   const ssize_t y_offset,ExceptionInfo *exception)
   1563 {
   1564 #define RollImageTag  "Roll/Image"
   1565 
   1566   Image
   1567     *roll_image;
   1568 
   1569   MagickStatusType
   1570     status;
   1571 
   1572   RectangleInfo
   1573     offset;
   1574 
   1575   /*
   1576     Initialize roll image attributes.
   1577   */
   1578   assert(image != (const Image *) NULL);
   1579   assert(image->signature == MagickCoreSignature);
   1580   if (image->debug != MagickFalse)
   1581     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1582   assert(exception != (ExceptionInfo *) NULL);
   1583   assert(exception->signature == MagickCoreSignature);
   1584   roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
   1585   if (roll_image == (Image *) NULL)
   1586     return((Image *) NULL);
   1587   offset.x=x_offset;
   1588   offset.y=y_offset;
   1589   while (offset.x < 0)
   1590     offset.x+=(ssize_t) image->columns;
   1591   while (offset.x >= (ssize_t) image->columns)
   1592     offset.x-=(ssize_t) image->columns;
   1593   while (offset.y < 0)
   1594     offset.y+=(ssize_t) image->rows;
   1595   while (offset.y >= (ssize_t) image->rows)
   1596     offset.y-=(ssize_t) image->rows;
   1597   /*
   1598     Roll image.
   1599   */
   1600   status=CopyImageRegion(roll_image,image,(size_t) offset.x,
   1601     (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
   1602     offset.y,0,0,exception);
   1603   (void) SetImageProgress(image,RollImageTag,0,3);
   1604   status&=CopyImageRegion(roll_image,image,image->columns-offset.x,
   1605     (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0,
   1606     exception);
   1607   (void) SetImageProgress(image,RollImageTag,1,3);
   1608   status&=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows-
   1609     offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception);
   1610   (void) SetImageProgress(image,RollImageTag,2,3);
   1611   status&=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
   1612     offset.y,0,0,offset.x,offset.y,exception);
   1613   (void) SetImageProgress(image,RollImageTag,3,3);
   1614   roll_image->type=image->type;
   1615   if (status == MagickFalse)
   1616     roll_image=DestroyImage(roll_image);
   1617   return(roll_image);
   1618 }
   1619 
   1620 /*
   1622 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1623 %                                                                             %
   1624 %                                                                             %
   1625 %                                                                             %
   1626 %   S h a v e I m a g e                                                       %
   1627 %                                                                             %
   1628 %                                                                             %
   1629 %                                                                             %
   1630 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1631 %
   1632 %  ShaveImage() shaves pixels from the image edges.  It allocates the memory
   1633 %  necessary for the new Image structure and returns a pointer to the new
   1634 %  image.
   1635 %
   1636 %  The format of the ShaveImage method is:
   1637 %
   1638 %      Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
   1639 %        ExceptionInfo *exception)
   1640 %
   1641 %  A description of each parameter follows:
   1642 %
   1643 %    o shave_image: Method ShaveImage returns a pointer to the shaved
   1644 %      image.  A null image is returned if there is a memory shortage or
   1645 %      if the image width or height is zero.
   1646 %
   1647 %    o image: the image.
   1648 %
   1649 %    o shave_info: Specifies a pointer to a RectangleInfo which defines the
   1650 %      region of the image to crop.
   1651 %
   1652 %    o exception: return any errors or warnings in this structure.
   1653 %
   1654 */
   1655 MagickExport Image *ShaveImage(const Image *image,
   1656   const RectangleInfo *shave_info,ExceptionInfo *exception)
   1657 {
   1658   Image
   1659     *shave_image;
   1660 
   1661   RectangleInfo
   1662     geometry;
   1663 
   1664   assert(image != (const Image *) NULL);
   1665   assert(image->signature == MagickCoreSignature);
   1666   if (image->debug != MagickFalse)
   1667     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1668   if (((2*shave_info->width) >= image->columns) ||
   1669       ((2*shave_info->height) >= image->rows))
   1670     ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
   1671   SetGeometry(image,&geometry);
   1672   geometry.width-=2*shave_info->width;
   1673   geometry.height-=2*shave_info->height;
   1674   geometry.x=(ssize_t) shave_info->width+image->page.x;
   1675   geometry.y=(ssize_t) shave_info->height+image->page.y;
   1676   shave_image=CropImage(image,&geometry,exception);
   1677   if (shave_image == (Image *) NULL)
   1678     return((Image *) NULL);
   1679   shave_image->page.width-=2*shave_info->width;
   1680   shave_image->page.height-=2*shave_info->height;
   1681   shave_image->page.x-=(ssize_t) shave_info->width;
   1682   shave_image->page.y-=(ssize_t) shave_info->height;
   1683   return(shave_image);
   1684 }
   1685 
   1686 /*
   1688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1689 %                                                                             %
   1690 %                                                                             %
   1691 %                                                                             %
   1692 %   S p l i c e I m a g e                                                     %
   1693 %                                                                             %
   1694 %                                                                             %
   1695 %                                                                             %
   1696 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   1697 %
   1698 %  SpliceImage() splices a solid color into the image as defined by the
   1699 %  geometry.
   1700 %
   1701 %  The format of the SpliceImage method is:
   1702 %
   1703 %      Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
   1704 %        ExceptionInfo *exception)
   1705 %
   1706 %  A description of each parameter follows:
   1707 %
   1708 %    o image: the image.
   1709 %
   1710 %    o geometry: Define the region of the image to splice with members
   1711 %      x, y, width, and height.
   1712 %
   1713 %    o exception: return any errors or warnings in this structure.
   1714 %
   1715 */
   1716 MagickExport Image *SpliceImage(const Image *image,
   1717   const RectangleInfo *geometry,ExceptionInfo *exception)
   1718 {
   1719 #define SpliceImageTag  "Splice/Image"
   1720 
   1721   CacheView
   1722     *image_view,
   1723     *splice_view;
   1724 
   1725   Image
   1726     *splice_image;
   1727 
   1728   MagickBooleanType
   1729     status;
   1730 
   1731   MagickOffsetType
   1732     progress;
   1733 
   1734   RectangleInfo
   1735     splice_geometry;
   1736 
   1737   ssize_t
   1738     columns,
   1739     y;
   1740 
   1741   /*
   1742     Allocate splice image.
   1743   */
   1744   assert(image != (const Image *) NULL);
   1745   assert(image->signature == MagickCoreSignature);
   1746   if (image->debug != MagickFalse)
   1747     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   1748   assert(geometry != (const RectangleInfo *) NULL);
   1749   assert(exception != (ExceptionInfo *) NULL);
   1750   assert(exception->signature == MagickCoreSignature);
   1751   splice_geometry=(*geometry);
   1752   splice_image=CloneImage(image,image->columns+splice_geometry.width,
   1753     image->rows+splice_geometry.height,MagickTrue,exception);
   1754   if (splice_image == (Image *) NULL)
   1755     return((Image *) NULL);
   1756   if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse)
   1757     {
   1758       splice_image=DestroyImage(splice_image);
   1759       return((Image *) NULL);
   1760     }
   1761   if ((IsPixelInfoGray(&splice_image->background_color) == MagickFalse) &&
   1762       (IsGrayColorspace(splice_image->colorspace) != MagickFalse))
   1763     (void) SetImageColorspace(splice_image,sRGBColorspace,exception);
   1764   if ((splice_image->background_color.alpha_trait != UndefinedPixelTrait) &&
   1765       (splice_image->alpha_trait == UndefinedPixelTrait))
   1766     (void) SetImageAlpha(splice_image,OpaqueAlpha,exception);
   1767   (void) SetImageBackgroundColor(splice_image,exception);
   1768   /*
   1769     Respect image geometry.
   1770   */
   1771   switch (image->gravity)
   1772   {
   1773     default:
   1774     case UndefinedGravity:
   1775     case NorthWestGravity:
   1776       break;
   1777     case NorthGravity:
   1778     {
   1779       splice_geometry.x+=(ssize_t) splice_geometry.width/2;
   1780       break;
   1781     }
   1782     case NorthEastGravity:
   1783     {
   1784       splice_geometry.x+=(ssize_t) splice_geometry.width;
   1785       break;
   1786     }
   1787     case WestGravity:
   1788     {
   1789       splice_geometry.y+=(ssize_t) splice_geometry.width/2;
   1790       break;
   1791     }
   1792     case CenterGravity:
   1793     {
   1794       splice_geometry.x+=(ssize_t) splice_geometry.width/2;
   1795       splice_geometry.y+=(ssize_t) splice_geometry.height/2;
   1796       break;
   1797     }
   1798     case EastGravity:
   1799     {
   1800       splice_geometry.x+=(ssize_t) splice_geometry.width;
   1801       splice_geometry.y+=(ssize_t) splice_geometry.height/2;
   1802       break;
   1803     }
   1804     case SouthWestGravity:
   1805     {
   1806       splice_geometry.y+=(ssize_t) splice_geometry.height;
   1807       break;
   1808     }
   1809     case SouthGravity:
   1810     {
   1811       splice_geometry.x+=(ssize_t) splice_geometry.width/2;
   1812       splice_geometry.y+=(ssize_t) splice_geometry.height;
   1813       break;
   1814     }
   1815     case SouthEastGravity:
   1816     {
   1817       splice_geometry.x+=(ssize_t) splice_geometry.width;
   1818       splice_geometry.y+=(ssize_t) splice_geometry.height;
   1819       break;
   1820     }
   1821   }
   1822   /*
   1823     Splice image.
   1824   */
   1825   status=MagickTrue;
   1826   progress=0;
   1827   columns=MagickMin(splice_geometry.x,(ssize_t) splice_image->columns);
   1828   image_view=AcquireVirtualCacheView(image,exception);
   1829   splice_view=AcquireAuthenticCacheView(splice_image,exception);
   1830 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1831   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   1832     magick_threads(image,splice_image,1,1)
   1833 #endif
   1834   for (y=0; y < (ssize_t) splice_geometry.y; y++)
   1835   {
   1836     register const Quantum
   1837       *magick_restrict p;
   1838 
   1839     register ssize_t
   1840       x;
   1841 
   1842     register Quantum
   1843       *magick_restrict q;
   1844 
   1845     if (status == MagickFalse)
   1846       continue;
   1847     p=GetCacheViewVirtualPixels(image_view,0,y,splice_image->columns,1,
   1848       exception);
   1849     q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
   1850       exception);
   1851     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   1852       {
   1853         status=MagickFalse;
   1854         continue;
   1855       }
   1856     for (x=0; x < columns; x++)
   1857     {
   1858       register ssize_t
   1859         i;
   1860 
   1861       if (GetPixelReadMask(image,p) == 0)
   1862         {
   1863           SetPixelBackgoundColor(splice_image,q);
   1864           p+=GetPixelChannels(image);
   1865           q+=GetPixelChannels(splice_image);
   1866           continue;
   1867         }
   1868       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1869       {
   1870         PixelChannel channel=GetPixelChannelChannel(image,i);
   1871         PixelTrait traits=GetPixelChannelTraits(image,channel);
   1872         PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
   1873         if ((traits == UndefinedPixelTrait) ||
   1874             (splice_traits == UndefinedPixelTrait))
   1875           continue;
   1876         SetPixelChannel(splice_image,channel,p[i],q);
   1877       }
   1878       SetPixelRed(splice_image,GetPixelRed(image,p),q);
   1879       SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
   1880       SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
   1881       SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
   1882       p+=GetPixelChannels(image);
   1883       q+=GetPixelChannels(splice_image);
   1884     }
   1885     for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
   1886       q+=GetPixelChannels(splice_image);
   1887     for ( ; x < (ssize_t) splice_image->columns; x++)
   1888     {
   1889       register ssize_t
   1890         i;
   1891 
   1892       if (GetPixelReadMask(image,p) == 0)
   1893         {
   1894           SetPixelBackgoundColor(splice_image,q);
   1895           p+=GetPixelChannels(image);
   1896           q+=GetPixelChannels(splice_image);
   1897           continue;
   1898         }
   1899       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1900       {
   1901         PixelChannel channel=GetPixelChannelChannel(image,i);
   1902         PixelTrait traits=GetPixelChannelTraits(image,channel);
   1903         PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
   1904         if ((traits == UndefinedPixelTrait) ||
   1905             (splice_traits == UndefinedPixelTrait))
   1906           continue;
   1907         SetPixelChannel(splice_image,channel,p[i],q);
   1908       }
   1909       SetPixelRed(splice_image,GetPixelRed(image,p),q);
   1910       SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
   1911       SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
   1912       SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
   1913       p+=GetPixelChannels(image);
   1914       q+=GetPixelChannels(splice_image);
   1915     }
   1916     if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
   1917       status=MagickFalse;
   1918     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   1919       {
   1920         MagickBooleanType
   1921           proceed;
   1922 
   1923 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1924         #pragma omp critical (MagickCore_TransposeImage)
   1925 #endif
   1926         proceed=SetImageProgress(image,SpliceImageTag,progress++,
   1927           splice_image->rows);
   1928         if (proceed == MagickFalse)
   1929           status=MagickFalse;
   1930       }
   1931   }
   1932 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   1933   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   1934     magick_threads(image,splice_image,1,1)
   1935 #endif
   1936   for (y=(ssize_t) (splice_geometry.y+splice_geometry.height);
   1937        y < (ssize_t) splice_image->rows; y++)
   1938   {
   1939     register const Quantum
   1940       *magick_restrict p;
   1941 
   1942     register ssize_t
   1943       x;
   1944 
   1945     register Quantum
   1946       *magick_restrict q;
   1947 
   1948     if (status == MagickFalse)
   1949       continue;
   1950     if ((y < 0) || (y >= (ssize_t)splice_image->rows))
   1951       continue;
   1952     p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
   1953       splice_image->columns,1,exception);
   1954     q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
   1955       exception);
   1956     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   1957       {
   1958         status=MagickFalse;
   1959         continue;
   1960       }
   1961     for (x=0; x < columns; x++)
   1962     {
   1963       register ssize_t
   1964         i;
   1965 
   1966       if (GetPixelReadMask(image,q) == 0)
   1967         {
   1968           SetPixelBackgoundColor(splice_image,q);
   1969           p+=GetPixelChannels(image);
   1970           q+=GetPixelChannels(splice_image);
   1971           continue;
   1972         }
   1973       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   1974       {
   1975         PixelChannel channel=GetPixelChannelChannel(image,i);
   1976         PixelTrait traits=GetPixelChannelTraits(image,channel);
   1977         PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
   1978         if ((traits == UndefinedPixelTrait) ||
   1979             (splice_traits == UndefinedPixelTrait))
   1980           continue;
   1981         SetPixelChannel(splice_image,channel,p[i],q);
   1982       }
   1983       SetPixelRed(splice_image,GetPixelRed(image,p),q);
   1984       SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
   1985       SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
   1986       SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
   1987       p+=GetPixelChannels(image);
   1988       q+=GetPixelChannels(splice_image);
   1989     }
   1990     for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
   1991       q+=GetPixelChannels(splice_image);
   1992     for ( ; x < (ssize_t) splice_image->columns; x++)
   1993     {
   1994       register ssize_t
   1995         i;
   1996 
   1997       if (GetPixelReadMask(image,q) == 0)
   1998         {
   1999           SetPixelBackgoundColor(splice_image,q);
   2000           p+=GetPixelChannels(image);
   2001           q+=GetPixelChannels(splice_image);
   2002           continue;
   2003         }
   2004       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   2005       {
   2006         PixelChannel channel=GetPixelChannelChannel(image,i);
   2007         PixelTrait traits=GetPixelChannelTraits(image,channel);
   2008         PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
   2009         if ((traits == UndefinedPixelTrait) ||
   2010             (splice_traits == UndefinedPixelTrait))
   2011           continue;
   2012         SetPixelChannel(splice_image,channel,p[i],q);
   2013       }
   2014       SetPixelRed(splice_image,GetPixelRed(image,p),q);
   2015       SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
   2016       SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
   2017       SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
   2018       p+=GetPixelChannels(image);
   2019       q+=GetPixelChannels(splice_image);
   2020     }
   2021     if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
   2022       status=MagickFalse;
   2023     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2024       {
   2025         MagickBooleanType
   2026           proceed;
   2027 
   2028 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2029         #pragma omp critical (MagickCore_TransposeImage)
   2030 #endif
   2031         proceed=SetImageProgress(image,SpliceImageTag,progress++,
   2032           splice_image->rows);
   2033         if (proceed == MagickFalse)
   2034           status=MagickFalse;
   2035       }
   2036   }
   2037   splice_view=DestroyCacheView(splice_view);
   2038   image_view=DestroyCacheView(image_view);
   2039   if (status == MagickFalse)
   2040     splice_image=DestroyImage(splice_image);
   2041   return(splice_image);
   2042 }
   2043 
   2044 /*
   2046 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2047 %                                                                             %
   2048 %                                                                             %
   2049 %                                                                             %
   2050 %   T r a n s f o r m I m a g e                                               %
   2051 %                                                                             %
   2052 %                                                                             %
   2053 %                                                                             %
   2054 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2055 %
   2056 %  TransformImage() is a convenience method that behaves like ResizeImage() or
   2057 %  CropImage() but accepts scaling and/or cropping information as a region
   2058 %  geometry specification.  If the operation fails, the original image handle
   2059 %  is left as is.
   2060 %
   2061 %  This should only be used for single images.
   2062 %
   2063 %  This function destroys what it assumes to be a single image list.
   2064 %  If the input image is part of a larger list, all other images in that list
   2065 %  will be simply 'lost', not destroyed.
   2066 %
   2067 %  Also if the crop generates a list of images only the first image is resized.
   2068 %  And finally if the crop succeeds and the resize failed, you will get a
   2069 %  cropped image, as well as a 'false' or 'failed' report.
   2070 %
   2071 %  This function and should probably be deprecated in favor of direct calls
   2072 %  to CropImageToTiles() or ResizeImage(), as appropriate.
   2073 %
   2074 %  The format of the TransformImage method is:
   2075 %
   2076 %      MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
   2077 %        const char *image_geometry,ExceptionInfo *exception)
   2078 %
   2079 %  A description of each parameter follows:
   2080 %
   2081 %    o image: the image The transformed image is returned as this parameter.
   2082 %
   2083 %    o crop_geometry: A crop geometry string.  This geometry defines a
   2084 %      subregion of the image to crop.
   2085 %
   2086 %    o image_geometry: An image geometry string.  This geometry defines the
   2087 %      final size of the image.
   2088 %
   2089 %    o exception: return any errors or warnings in this structure.
   2090 %
   2091 */
   2092 MagickPrivate MagickBooleanType TransformImage(Image **image,
   2093   const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
   2094 {
   2095   Image
   2096     *resize_image,
   2097     *transform_image;
   2098 
   2099   RectangleInfo
   2100     geometry;
   2101 
   2102   assert(image != (Image **) NULL);
   2103   assert((*image)->signature == MagickCoreSignature);
   2104   if ((*image)->debug != MagickFalse)
   2105     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
   2106   transform_image=(*image);
   2107   if (crop_geometry != (const char *) NULL)
   2108     {
   2109       Image
   2110         *crop_image;
   2111 
   2112       /*
   2113         Crop image to a user specified size.
   2114       */
   2115       crop_image=CropImageToTiles(*image,crop_geometry,exception);
   2116       if (crop_image == (Image *) NULL)
   2117         transform_image=CloneImage(*image,0,0,MagickTrue,exception);
   2118       else
   2119         {
   2120           transform_image=DestroyImage(transform_image);
   2121           transform_image=GetFirstImageInList(crop_image);
   2122         }
   2123       *image=transform_image;
   2124     }
   2125   if (image_geometry == (const char *) NULL)
   2126     return(MagickTrue);
   2127 
   2128   /*
   2129     Scale image to a user specified size.
   2130   */
   2131   (void) ParseRegionGeometry(transform_image,image_geometry,&geometry,exception);
   2132   if ((transform_image->columns == geometry.width) &&
   2133       (transform_image->rows == geometry.height))
   2134     return(MagickTrue);
   2135   resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
   2136     transform_image->filter,exception);
   2137   if (resize_image == (Image *) NULL)
   2138     return(MagickFalse);
   2139   transform_image=DestroyImage(transform_image);
   2140   transform_image=resize_image;
   2141   *image=transform_image;
   2142   return(MagickTrue);
   2143 }
   2144 
   2145 /*
   2147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2148 %                                                                             %
   2149 %                                                                             %
   2150 %                                                                             %
   2151 %   T r a n s p o s e I m a g e                                               %
   2152 %                                                                             %
   2153 %                                                                             %
   2154 %                                                                             %
   2155 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2156 %
   2157 %  TransposeImage() creates a horizontal mirror image by reflecting the pixels
   2158 %  around the central y-axis while rotating them by 90 degrees.
   2159 %
   2160 %  The format of the TransposeImage method is:
   2161 %
   2162 %      Image *TransposeImage(const Image *image,ExceptionInfo *exception)
   2163 %
   2164 %  A description of each parameter follows:
   2165 %
   2166 %    o image: the image.
   2167 %
   2168 %    o exception: return any errors or warnings in this structure.
   2169 %
   2170 */
   2171 MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
   2172 {
   2173 #define TransposeImageTag  "Transpose/Image"
   2174 
   2175   CacheView
   2176     *image_view,
   2177     *transpose_view;
   2178 
   2179   Image
   2180     *transpose_image;
   2181 
   2182   MagickBooleanType
   2183     status;
   2184 
   2185   MagickOffsetType
   2186     progress;
   2187 
   2188   RectangleInfo
   2189     page;
   2190 
   2191   ssize_t
   2192     y;
   2193 
   2194   assert(image != (const Image *) NULL);
   2195   assert(image->signature == MagickCoreSignature);
   2196   if (image->debug != MagickFalse)
   2197     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2198   assert(exception != (ExceptionInfo *) NULL);
   2199   assert(exception->signature == MagickCoreSignature);
   2200   transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
   2201     exception);
   2202   if (transpose_image == (Image *) NULL)
   2203     return((Image *) NULL);
   2204   /*
   2205     Transpose image.
   2206   */
   2207   status=MagickTrue;
   2208   progress=0;
   2209   image_view=AcquireVirtualCacheView(image,exception);
   2210   transpose_view=AcquireAuthenticCacheView(transpose_image,exception);
   2211 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2212   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   2213     magick_threads(image,transpose_image,image->rows,1)
   2214 #endif
   2215   for (y=0; y < (ssize_t) image->rows; y++)
   2216   {
   2217     register const Quantum
   2218       *magick_restrict p;
   2219 
   2220     register Quantum
   2221       *magick_restrict q;
   2222 
   2223     register ssize_t
   2224       x;
   2225 
   2226     if (status == MagickFalse)
   2227       continue;
   2228     p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
   2229       image->columns,1,exception);
   2230     q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1),
   2231       0,1,transpose_image->rows,exception);
   2232     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   2233       {
   2234         status=MagickFalse;
   2235         continue;
   2236       }
   2237     for (x=0; x < (ssize_t) image->columns; x++)
   2238     {
   2239       register ssize_t
   2240         i;
   2241 
   2242       if (GetPixelReadMask(image,q) == 0)
   2243         {
   2244           SetPixelBackgoundColor(transpose_image,q);
   2245           p+=GetPixelChannels(image);
   2246           q+=GetPixelChannels(transpose_image);
   2247           continue;
   2248         }
   2249       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   2250       {
   2251         PixelChannel channel=GetPixelChannelChannel(image,i);
   2252         PixelTrait traits=GetPixelChannelTraits(image,channel);
   2253         PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image,
   2254           channel);
   2255         if ((traits == UndefinedPixelTrait) ||
   2256             (transpose_traits == UndefinedPixelTrait))
   2257           continue;
   2258         SetPixelChannel(transpose_image,channel,p[i],q);
   2259       }
   2260       p+=GetPixelChannels(image);
   2261       q+=GetPixelChannels(transpose_image);
   2262     }
   2263     if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
   2264       status=MagickFalse;
   2265     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2266       {
   2267         MagickBooleanType
   2268           proceed;
   2269 
   2270 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2271         #pragma omp critical (MagickCore_TransposeImage)
   2272 #endif
   2273         proceed=SetImageProgress(image,TransposeImageTag,progress++,
   2274           image->rows);
   2275         if (proceed == MagickFalse)
   2276           status=MagickFalse;
   2277       }
   2278   }
   2279   transpose_view=DestroyCacheView(transpose_view);
   2280   image_view=DestroyCacheView(image_view);
   2281   transpose_image->type=image->type;
   2282   page=transpose_image->page;
   2283   Swap(page.width,page.height);
   2284   Swap(page.x,page.y);
   2285   transpose_image->page=page;
   2286   if (status == MagickFalse)
   2287     transpose_image=DestroyImage(transpose_image);
   2288   return(transpose_image);
   2289 }
   2290 
   2291 /*
   2293 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2294 %                                                                             %
   2295 %                                                                             %
   2296 %                                                                             %
   2297 %   T r a n s v e r s e I m a g e                                             %
   2298 %                                                                             %
   2299 %                                                                             %
   2300 %                                                                             %
   2301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2302 %
   2303 %  TransverseImage() creates a vertical mirror image by reflecting the pixels
   2304 %  around the central x-axis while rotating them by 270 degrees.
   2305 %
   2306 %  The format of the TransverseImage method is:
   2307 %
   2308 %      Image *TransverseImage(const Image *image,ExceptionInfo *exception)
   2309 %
   2310 %  A description of each parameter follows:
   2311 %
   2312 %    o image: the image.
   2313 %
   2314 %    o exception: return any errors or warnings in this structure.
   2315 %
   2316 */
   2317 MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
   2318 {
   2319 #define TransverseImageTag  "Transverse/Image"
   2320 
   2321   CacheView
   2322     *image_view,
   2323     *transverse_view;
   2324 
   2325   Image
   2326     *transverse_image;
   2327 
   2328   MagickBooleanType
   2329     status;
   2330 
   2331   MagickOffsetType
   2332     progress;
   2333 
   2334   RectangleInfo
   2335     page;
   2336 
   2337   ssize_t
   2338     y;
   2339 
   2340   assert(image != (const Image *) NULL);
   2341   assert(image->signature == MagickCoreSignature);
   2342   if (image->debug != MagickFalse)
   2343     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2344   assert(exception != (ExceptionInfo *) NULL);
   2345   assert(exception->signature == MagickCoreSignature);
   2346   transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
   2347     exception);
   2348   if (transverse_image == (Image *) NULL)
   2349     return((Image *) NULL);
   2350   /*
   2351     Transverse image.
   2352   */
   2353   status=MagickTrue;
   2354   progress=0;
   2355   image_view=AcquireVirtualCacheView(image,exception);
   2356   transverse_view=AcquireAuthenticCacheView(transverse_image,exception);
   2357 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2358   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   2359     magick_threads(image,transverse_image,image->rows,1)
   2360 #endif
   2361   for (y=0; y < (ssize_t) image->rows; y++)
   2362   {
   2363     MagickBooleanType
   2364       sync;
   2365 
   2366     register const Quantum
   2367       *magick_restrict p;
   2368 
   2369     register Quantum
   2370       *magick_restrict q;
   2371 
   2372     register ssize_t
   2373       x;
   2374 
   2375     if (status == MagickFalse)
   2376       continue;
   2377     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
   2378     q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-1),
   2379       0,1,transverse_image->rows,exception);
   2380     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   2381       {
   2382         status=MagickFalse;
   2383         continue;
   2384       }
   2385     q+=GetPixelChannels(transverse_image)*image->columns;
   2386     for (x=0; x < (ssize_t) image->columns; x++)
   2387     {
   2388       register ssize_t
   2389         i;
   2390 
   2391       q-=GetPixelChannels(transverse_image);
   2392       if (GetPixelReadMask(image,p) == 0)
   2393         {
   2394           p+=GetPixelChannels(image);
   2395           continue;
   2396         }
   2397       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   2398       {
   2399         PixelChannel channel=GetPixelChannelChannel(image,i);
   2400         PixelTrait traits=GetPixelChannelTraits(image,channel);
   2401         PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image,
   2402           channel);
   2403         if ((traits == UndefinedPixelTrait) ||
   2404             (transverse_traits == UndefinedPixelTrait))
   2405           continue;
   2406         SetPixelChannel(transverse_image,channel,p[i],q);
   2407       }
   2408       p+=GetPixelChannels(image);
   2409     }
   2410     sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
   2411     if (sync == MagickFalse)
   2412       status=MagickFalse;
   2413     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2414       {
   2415         MagickBooleanType
   2416           proceed;
   2417 
   2418 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2419         #pragma omp critical (MagickCore_TransverseImage)
   2420 #endif
   2421         proceed=SetImageProgress(image,TransverseImageTag,progress++,
   2422           image->rows);
   2423         if (proceed == MagickFalse)
   2424           status=MagickFalse;
   2425       }
   2426   }
   2427   transverse_view=DestroyCacheView(transverse_view);
   2428   image_view=DestroyCacheView(image_view);
   2429   transverse_image->type=image->type;
   2430   page=transverse_image->page;
   2431   Swap(page.width,page.height);
   2432   Swap(page.x,page.y);
   2433   if (page.width != 0)
   2434     page.x=(ssize_t) (page.width-transverse_image->columns-page.x);
   2435   if (page.height != 0)
   2436     page.y=(ssize_t) (page.height-transverse_image->rows-page.y);
   2437   transverse_image->page=page;
   2438   if (status == MagickFalse)
   2439     transverse_image=DestroyImage(transverse_image);
   2440   return(transverse_image);
   2441 }
   2442 
   2443 /*
   2445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2446 %                                                                             %
   2447 %                                                                             %
   2448 %                                                                             %
   2449 %   T r i m I m a g e                                                         %
   2450 %                                                                             %
   2451 %                                                                             %
   2452 %                                                                             %
   2453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2454 %
   2455 %  TrimImage() trims pixels from the image edges.  It allocates the memory
   2456 %  necessary for the new Image structure and returns a pointer to the new
   2457 %  image.
   2458 %
   2459 %  The format of the TrimImage method is:
   2460 %
   2461 %      Image *TrimImage(const Image *image,ExceptionInfo *exception)
   2462 %
   2463 %  A description of each parameter follows:
   2464 %
   2465 %    o image: the image.
   2466 %
   2467 %    o exception: return any errors or warnings in this structure.
   2468 %
   2469 */
   2470 MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
   2471 {
   2472   RectangleInfo
   2473     geometry;
   2474 
   2475   assert(image != (const Image *) NULL);
   2476   assert(image->signature == MagickCoreSignature);
   2477   if (image->debug != MagickFalse)
   2478     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
   2479   geometry=GetImageBoundingBox(image,exception);
   2480   if ((geometry.width == 0) || (geometry.height == 0))
   2481     {
   2482       Image
   2483         *crop_image;
   2484 
   2485       crop_image=CloneImage(image,1,1,MagickTrue,exception);
   2486       if (crop_image == (Image *) NULL)
   2487         return((Image *) NULL);
   2488       crop_image->background_color.alpha=(Quantum) TransparentAlpha;
   2489       crop_image->alpha_trait=BlendPixelTrait;
   2490       (void) SetImageBackgroundColor(crop_image,exception);
   2491       crop_image->page=image->page;
   2492       crop_image->page.x=(-1);
   2493       crop_image->page.y=(-1);
   2494       return(crop_image);
   2495     }
   2496   geometry.x+=image->page.x;
   2497   geometry.y+=image->page.y;
   2498   return(CropImage(image,&geometry,exception));
   2499 }
   2500