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