1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%       TTTTT  RRRR    AAA   N   N  SSSSS  FFFFF   OOO   RRRR   M   M         %
7%         T    R   R  A   A  NN  N  SS     F      O   O  R   R  MM MM         %
8%         T    RRRR   AAAAA  N N N   SSS   FFF    O   O  RRRR   M M M         %
9%         T    R R    A   A  N  NN     SS  F      O   O  R R    M   M         %
10%         T    R  R   A   A  N   N  SSSSS  F       OOO   R  R   M   M         %
11%                                                                             %
12%                                                                             %
13%                    MagickCore Image Transform Methods                       %
14%                                                                             %
15%                              Software Design                                %
16%                                   Cristy                                    %
17%                                 July 1992                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
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 == MagickCoreSignature);
106  assert(exception != (ExceptionInfo *) NULL);
107  assert(exception->signature == MagickCoreSignature);
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=RotateImage(image,90.0,exception);
141      break;
142    }
143    case RightBottomOrientation:
144    {
145      orient_image=TransverseImage(image,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 == MagickCoreSignature);
216  if (image->debug != MagickFalse)
217    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
218  assert(exception != (ExceptionInfo *) NULL);
219  assert(exception->signature == MagickCoreSignature);
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      *magick_restrict p;
260
261    register ssize_t
262      x;
263
264    register Quantum
265      *magick_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      *magick_restrict p;
324
325    register ssize_t
326      x;
327
328    register Quantum
329      *magick_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 == MagickCoreSignature);
433  if (images->debug != MagickFalse)
434    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
435  assert(exception != (ExceptionInfo *) NULL);
436  assert(exception->signature == MagickCoreSignature);
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          *magick_restrict p;
459
460        register ssize_t
461          x;
462
463        register Quantum
464          *magick_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 == MagickCoreSignature);
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 == MagickCoreSignature);
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      *magick_restrict p;
677
678    register Quantum
679      *magick_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 == MagickCoreSignature);
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 == MagickCoreSignature);
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 == MagickCoreSignature);
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      *magick_restrict p;
1030
1031    register Quantum
1032      *magick_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 == MagickCoreSignature);
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 == MagickCoreSignature);
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 == MagickCoreSignature);
1206  if (image->debug != MagickFalse)
1207    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1208  assert(exception != (ExceptionInfo *) NULL);
1209  assert(exception->signature == MagickCoreSignature);
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      *magick_restrict p;
1229
1230    register Quantum
1231      *magick_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 == MagickCoreSignature);
1347  if (image->debug != MagickFalse)
1348    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1349  assert(exception != (ExceptionInfo *) NULL);
1350  assert(exception->signature == MagickCoreSignature);
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      *magick_restrict p;
1370
1371    register ssize_t
1372      x;
1373
1374    register Quantum
1375      *magick_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 MagickBooleanType CopyImageRegion(Image *destination,const Image *source,  const size_t columns,const size_t rows,const ssize_t sx,const ssize_t sy,
1468  const ssize_t dx,const ssize_t dy,ExceptionInfo *exception)
1469{
1470  CacheView
1471    *source_view,
1472    *destination_view;
1473
1474  MagickBooleanType
1475    status;
1476
1477  ssize_t
1478    y;
1479
1480  if (columns == 0)
1481    return(MagickTrue);
1482  status=MagickTrue;
1483  source_view=AcquireVirtualCacheView(source,exception);
1484  destination_view=AcquireAuthenticCacheView(destination,exception);
1485#if defined(MAGICKCORE_OPENMP_SUPPORT)
1486  #pragma omp parallel for schedule(static,4) shared(status) \
1487    magick_threads(source,destination,rows,1)
1488#endif
1489  for (y=0; y < (ssize_t) rows; y++)
1490  {
1491    MagickBooleanType
1492      sync;
1493
1494    register const Quantum
1495      *magick_restrict p;
1496
1497    register Quantum
1498      *magick_restrict q;
1499
1500    register ssize_t
1501      x;
1502
1503    /*
1504      Transfer scanline.
1505    */
1506    if (status == MagickFalse)
1507      continue;
1508    p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1509    q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1510    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1511      {
1512        status=MagickFalse;
1513        continue;
1514      }
1515    for (x=0; x < (ssize_t) columns; x++)
1516    {
1517      register ssize_t
1518        i;
1519
1520      if (GetPixelReadMask(source,p) == 0)
1521        {
1522          SetPixelBackgoundColor(destination,q);
1523          p+=GetPixelChannels(source);
1524          q+=GetPixelChannels(destination);
1525          continue;
1526        }
1527      for (i=0; i < (ssize_t) GetPixelChannels(source); i++)
1528      {
1529        PixelChannel channel=GetPixelChannelChannel(source,i);
1530        PixelTrait source_traits=GetPixelChannelTraits(source,channel);
1531        PixelTrait destination_traits=GetPixelChannelTraits(destination,
1532          channel);
1533        if ((source_traits == UndefinedPixelTrait) ||
1534            (destination_traits == UndefinedPixelTrait))
1535          continue;
1536        SetPixelChannel(destination,channel,p[i],q);
1537      }
1538      p+=GetPixelChannels(source);
1539      q+=GetPixelChannels(destination);
1540    }
1541    sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1542    if (sync == MagickFalse)
1543      status=MagickFalse;
1544  }
1545  destination_view=DestroyCacheView(destination_view);
1546  source_view=DestroyCacheView(source_view);
1547  return(status);
1548}
1549
1550MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1551  const ssize_t y_offset,ExceptionInfo *exception)
1552{
1553#define RollImageTag  "Roll/Image"
1554
1555  Image
1556    *roll_image;
1557
1558  MagickStatusType
1559    status;
1560
1561  RectangleInfo
1562    offset;
1563
1564  /*
1565    Initialize roll image attributes.
1566  */
1567  assert(image != (const Image *) NULL);
1568  assert(image->signature == MagickCoreSignature);
1569  if (image->debug != MagickFalse)
1570    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1571  assert(exception != (ExceptionInfo *) NULL);
1572  assert(exception->signature == MagickCoreSignature);
1573  roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1574  if (roll_image == (Image *) NULL)
1575    return((Image *) NULL);
1576  offset.x=x_offset;
1577  offset.y=y_offset;
1578  while (offset.x < 0)
1579    offset.x+=(ssize_t) image->columns;
1580  while (offset.x >= (ssize_t) image->columns)
1581    offset.x-=(ssize_t) image->columns;
1582  while (offset.y < 0)
1583    offset.y+=(ssize_t) image->rows;
1584  while (offset.y >= (ssize_t) image->rows)
1585    offset.y-=(ssize_t) image->rows;
1586  /*
1587    Roll image.
1588  */
1589  status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1590    (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
1591    offset.y,0,0,exception);
1592  (void) SetImageProgress(image,RollImageTag,0,3);
1593  status&=CopyImageRegion(roll_image,image,image->columns-offset.x,
1594    (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0,
1595    exception);
1596  (void) SetImageProgress(image,RollImageTag,1,3);
1597  status&=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows-
1598    offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception);
1599  (void) SetImageProgress(image,RollImageTag,2,3);
1600  status&=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
1601    offset.y,0,0,offset.x,offset.y,exception);
1602  (void) SetImageProgress(image,RollImageTag,3,3);
1603  roll_image->type=image->type;
1604  if (status == MagickFalse)
1605    roll_image=DestroyImage(roll_image);
1606  return(roll_image);
1607}
1608
1609/*
1610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1611%                                                                             %
1612%                                                                             %
1613%                                                                             %
1614%   S h a v e I m a g e                                                       %
1615%                                                                             %
1616%                                                                             %
1617%                                                                             %
1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619%
1620%  ShaveImage() shaves pixels from the image edges.  It allocates the memory
1621%  necessary for the new Image structure and returns a pointer to the new
1622%  image.
1623%
1624%  The format of the ShaveImage method is:
1625%
1626%      Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1627%        ExceptionInfo *exception)
1628%
1629%  A description of each parameter follows:
1630%
1631%    o shave_image: Method ShaveImage returns a pointer to the shaved
1632%      image.  A null image is returned if there is a memory shortage or
1633%      if the image width or height is zero.
1634%
1635%    o image: the image.
1636%
1637%    o shave_info: Specifies a pointer to a RectangleInfo which defines the
1638%      region of the image to crop.
1639%
1640%    o exception: return any errors or warnings in this structure.
1641%
1642*/
1643MagickExport Image *ShaveImage(const Image *image,
1644  const RectangleInfo *shave_info,ExceptionInfo *exception)
1645{
1646  Image
1647    *shave_image;
1648
1649  RectangleInfo
1650    geometry;
1651
1652  assert(image != (const Image *) NULL);
1653  assert(image->signature == MagickCoreSignature);
1654  if (image->debug != MagickFalse)
1655    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1656  if (((2*shave_info->width) >= image->columns) ||
1657      ((2*shave_info->height) >= image->rows))
1658    ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1659  SetGeometry(image,&geometry);
1660  geometry.width-=2*shave_info->width;
1661  geometry.height-=2*shave_info->height;
1662  geometry.x=(ssize_t) shave_info->width+image->page.x;
1663  geometry.y=(ssize_t) shave_info->height+image->page.y;
1664  shave_image=CropImage(image,&geometry,exception);
1665  if (shave_image == (Image *) NULL)
1666    return((Image *) NULL);
1667  shave_image->page.width-=2*shave_info->width;
1668  shave_image->page.height-=2*shave_info->height;
1669  shave_image->page.x-=(ssize_t) shave_info->width;
1670  shave_image->page.y-=(ssize_t) shave_info->height;
1671  return(shave_image);
1672}
1673
1674/*
1675%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1676%                                                                             %
1677%                                                                             %
1678%                                                                             %
1679%   S p l i c e I m a g e                                                     %
1680%                                                                             %
1681%                                                                             %
1682%                                                                             %
1683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1684%
1685%  SpliceImage() splices a solid color into the image as defined by the
1686%  geometry.
1687%
1688%  The format of the SpliceImage method is:
1689%
1690%      Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1691%        ExceptionInfo *exception)
1692%
1693%  A description of each parameter follows:
1694%
1695%    o image: the image.
1696%
1697%    o geometry: Define the region of the image to splice with members
1698%      x, y, width, and height.
1699%
1700%    o exception: return any errors or warnings in this structure.
1701%
1702*/
1703MagickExport Image *SpliceImage(const Image *image,
1704  const RectangleInfo *geometry,ExceptionInfo *exception)
1705{
1706#define SpliceImageTag  "Splice/Image"
1707
1708  CacheView
1709    *image_view,
1710    *splice_view;
1711
1712  Image
1713    *splice_image;
1714
1715  MagickBooleanType
1716    status;
1717
1718  MagickOffsetType
1719    progress;
1720
1721  RectangleInfo
1722    splice_geometry;
1723
1724  ssize_t
1725    columns,
1726    y;
1727
1728  /*
1729    Allocate splice image.
1730  */
1731  assert(image != (const Image *) NULL);
1732  assert(image->signature == MagickCoreSignature);
1733  if (image->debug != MagickFalse)
1734    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1735  assert(geometry != (const RectangleInfo *) NULL);
1736  assert(exception != (ExceptionInfo *) NULL);
1737  assert(exception->signature == MagickCoreSignature);
1738  splice_geometry=(*geometry);
1739  splice_image=CloneImage(image,image->columns+splice_geometry.width,
1740    image->rows+splice_geometry.height,MagickTrue,exception);
1741  if (splice_image == (Image *) NULL)
1742    return((Image *) NULL);
1743  if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse)
1744    {
1745      splice_image=DestroyImage(splice_image);
1746      return((Image *) NULL);
1747    }
1748  if ((IsPixelInfoGray(&splice_image->background_color) == MagickFalse) &&
1749      (IsGrayColorspace(splice_image->colorspace) != MagickFalse))
1750    (void) SetImageColorspace(splice_image,sRGBColorspace,exception);
1751  if ((splice_image->background_color.alpha_trait != UndefinedPixelTrait) &&
1752      (splice_image->alpha_trait == UndefinedPixelTrait))
1753    (void) SetImageAlpha(splice_image,OpaqueAlpha,exception);
1754  (void) SetImageBackgroundColor(splice_image,exception);
1755  /*
1756    Respect image geometry.
1757  */
1758  switch (image->gravity)
1759  {
1760    default:
1761    case UndefinedGravity:
1762    case NorthWestGravity:
1763      break;
1764    case NorthGravity:
1765    {
1766      splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1767      break;
1768    }
1769    case NorthEastGravity:
1770    {
1771      splice_geometry.x+=(ssize_t) splice_geometry.width;
1772      break;
1773    }
1774    case WestGravity:
1775    {
1776      splice_geometry.y+=(ssize_t) splice_geometry.width/2;
1777      break;
1778    }
1779    case CenterGravity:
1780    {
1781      splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1782      splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1783      break;
1784    }
1785    case EastGravity:
1786    {
1787      splice_geometry.x+=(ssize_t) splice_geometry.width;
1788      splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1789      break;
1790    }
1791    case SouthWestGravity:
1792    {
1793      splice_geometry.y+=(ssize_t) splice_geometry.height;
1794      break;
1795    }
1796    case SouthGravity:
1797    {
1798      splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1799      splice_geometry.y+=(ssize_t) splice_geometry.height;
1800      break;
1801    }
1802    case SouthEastGravity:
1803    {
1804      splice_geometry.x+=(ssize_t) splice_geometry.width;
1805      splice_geometry.y+=(ssize_t) splice_geometry.height;
1806      break;
1807    }
1808  }
1809  /*
1810    Splice image.
1811  */
1812  status=MagickTrue;
1813  progress=0;
1814  columns=MagickMin(splice_geometry.x,(ssize_t) splice_image->columns);
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      *magick_restrict p;
1825
1826    register ssize_t
1827      x;
1828
1829    register Quantum
1830      *magick_restrict q;
1831
1832    if (status == MagickFalse)
1833      continue;
1834    p=GetCacheViewVirtualPixels(image_view,0,y,splice_image->columns,1,
1835      exception);
1836    q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1837      exception);
1838    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1839      {
1840        status=MagickFalse;
1841        continue;
1842      }
1843    for (x=0; x < columns; x++)
1844    {
1845      register ssize_t
1846        i;
1847
1848      if (GetPixelReadMask(image,p) == 0)
1849        {
1850          SetPixelBackgoundColor(splice_image,q);
1851          p+=GetPixelChannels(image);
1852          q+=GetPixelChannels(splice_image);
1853          continue;
1854        }
1855      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1856      {
1857        PixelChannel channel=GetPixelChannelChannel(image,i);
1858        PixelTrait traits=GetPixelChannelTraits(image,channel);
1859        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1860        if ((traits == UndefinedPixelTrait) ||
1861            (splice_traits == UndefinedPixelTrait))
1862          continue;
1863        SetPixelChannel(splice_image,channel,p[i],q);
1864      }
1865      SetPixelRed(splice_image,GetPixelRed(image,p),q);
1866      SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1867      SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1868      SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1869      p+=GetPixelChannels(image);
1870      q+=GetPixelChannels(splice_image);
1871    }
1872    for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
1873      q+=GetPixelChannels(splice_image);
1874    for ( ; x < (ssize_t) splice_image->columns; x++)
1875    {
1876      register ssize_t
1877        i;
1878
1879      if (GetPixelReadMask(image,p) == 0)
1880        {
1881          SetPixelBackgoundColor(splice_image,q);
1882          p+=GetPixelChannels(image);
1883          q+=GetPixelChannels(splice_image);
1884          continue;
1885        }
1886      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1887      {
1888        PixelChannel channel=GetPixelChannelChannel(image,i);
1889        PixelTrait traits=GetPixelChannelTraits(image,channel);
1890        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1891        if ((traits == UndefinedPixelTrait) ||
1892            (splice_traits == UndefinedPixelTrait))
1893          continue;
1894        SetPixelChannel(splice_image,channel,p[i],q);
1895      }
1896      SetPixelRed(splice_image,GetPixelRed(image,p),q);
1897      SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1898      SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1899      SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1900      p+=GetPixelChannels(image);
1901      q+=GetPixelChannels(splice_image);
1902    }
1903    if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1904      status=MagickFalse;
1905    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1906      {
1907        MagickBooleanType
1908          proceed;
1909
1910#if defined(MAGICKCORE_OPENMP_SUPPORT)
1911        #pragma omp critical (MagickCore_TransposeImage)
1912#endif
1913        proceed=SetImageProgress(image,SpliceImageTag,progress++,
1914          splice_image->rows);
1915        if (proceed == MagickFalse)
1916          status=MagickFalse;
1917      }
1918  }
1919#if defined(MAGICKCORE_OPENMP_SUPPORT)
1920  #pragma omp parallel for schedule(static,4) shared(progress,status) \
1921    magick_threads(image,splice_image,1,1)
1922#endif
1923  for (y=(ssize_t) (splice_geometry.y+splice_geometry.height);
1924       y < (ssize_t) splice_image->rows; y++)
1925  {
1926    register const Quantum
1927      *magick_restrict p;
1928
1929    register ssize_t
1930      x;
1931
1932    register Quantum
1933      *magick_restrict q;
1934
1935    if (status == MagickFalse)
1936      continue;
1937    if ((y < 0) || (y >= (ssize_t)splice_image->rows))
1938      continue;
1939    p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
1940      splice_image->columns,1,exception);
1941    q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1942      exception);
1943    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1944      {
1945        status=MagickFalse;
1946        continue;
1947      }
1948    for (x=0; x < columns; x++)
1949    {
1950      register ssize_t
1951        i;
1952
1953      if (GetPixelReadMask(image,q) == 0)
1954        {
1955          SetPixelBackgoundColor(splice_image,q);
1956          p+=GetPixelChannels(image);
1957          q+=GetPixelChannels(splice_image);
1958          continue;
1959        }
1960      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1961      {
1962        PixelChannel channel=GetPixelChannelChannel(image,i);
1963        PixelTrait traits=GetPixelChannelTraits(image,channel);
1964        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1965        if ((traits == UndefinedPixelTrait) ||
1966            (splice_traits == UndefinedPixelTrait))
1967          continue;
1968        SetPixelChannel(splice_image,channel,p[i],q);
1969      }
1970      SetPixelRed(splice_image,GetPixelRed(image,p),q);
1971      SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1972      SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1973      SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1974      p+=GetPixelChannels(image);
1975      q+=GetPixelChannels(splice_image);
1976    }
1977    for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
1978      q+=GetPixelChannels(splice_image);
1979    for ( ; x < (ssize_t) splice_image->columns; x++)
1980    {
1981      register ssize_t
1982        i;
1983
1984      if (GetPixelReadMask(image,q) == 0)
1985        {
1986          SetPixelBackgoundColor(splice_image,q);
1987          p+=GetPixelChannels(image);
1988          q+=GetPixelChannels(splice_image);
1989          continue;
1990        }
1991      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1992      {
1993        PixelChannel channel=GetPixelChannelChannel(image,i);
1994        PixelTrait traits=GetPixelChannelTraits(image,channel);
1995        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1996        if ((traits == UndefinedPixelTrait) ||
1997            (splice_traits == UndefinedPixelTrait))
1998          continue;
1999        SetPixelChannel(splice_image,channel,p[i],q);
2000      }
2001      SetPixelRed(splice_image,GetPixelRed(image,p),q);
2002      SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
2003      SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
2004      SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
2005      p+=GetPixelChannels(image);
2006      q+=GetPixelChannels(splice_image);
2007    }
2008    if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
2009      status=MagickFalse;
2010    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2011      {
2012        MagickBooleanType
2013          proceed;
2014
2015#if defined(MAGICKCORE_OPENMP_SUPPORT)
2016        #pragma omp critical (MagickCore_TransposeImage)
2017#endif
2018        proceed=SetImageProgress(image,SpliceImageTag,progress++,
2019          splice_image->rows);
2020        if (proceed == MagickFalse)
2021          status=MagickFalse;
2022      }
2023  }
2024  splice_view=DestroyCacheView(splice_view);
2025  image_view=DestroyCacheView(image_view);
2026  if (status == MagickFalse)
2027    splice_image=DestroyImage(splice_image);
2028  return(splice_image);
2029}
2030
2031/*
2032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2033%                                                                             %
2034%                                                                             %
2035%                                                                             %
2036%   T r a n s f o r m I m a g e                                               %
2037%                                                                             %
2038%                                                                             %
2039%                                                                             %
2040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2041%
2042%  TransformImage() is a convenience method that behaves like ResizeImage() or
2043%  CropImage() but accepts scaling and/or cropping information as a region
2044%  geometry specification.  If the operation fails, the original image handle
2045%  is left as is.
2046%
2047%  This should only be used for single images.
2048%
2049%  This function destroys what it assumes to be a single image list.
2050%  If the input image is part of a larger list, all other images in that list
2051%  will be simply 'lost', not destroyed.
2052%
2053%  Also if the crop generates a list of images only the first image is resized.
2054%  And finally if the crop succeeds and the resize failed, you will get a
2055%  cropped image, as well as a 'false' or 'failed' report.
2056%
2057%  This function and should probably be deprecated in favor of direct calls
2058%  to CropImageToTiles() or ResizeImage(), as appropriate.
2059%
2060%  The format of the TransformImage method is:
2061%
2062%      MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
2063%        const char *image_geometry,ExceptionInfo *exception)
2064%
2065%  A description of each parameter follows:
2066%
2067%    o image: the image The transformed image is returned as this parameter.
2068%
2069%    o crop_geometry: A crop geometry string.  This geometry defines a
2070%      subregion of the image to crop.
2071%
2072%    o image_geometry: An image geometry string.  This geometry defines the
2073%      final size of the image.
2074%
2075%    o exception: return any errors or warnings in this structure.
2076%
2077*/
2078MagickPrivate MagickBooleanType TransformImage(Image **image,
2079  const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
2080{
2081  Image
2082    *resize_image,
2083    *transform_image;
2084
2085  RectangleInfo
2086    geometry;
2087
2088  assert(image != (Image **) NULL);
2089  assert((*image)->signature == MagickCoreSignature);
2090  if ((*image)->debug != MagickFalse)
2091    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
2092  transform_image=(*image);
2093  if (crop_geometry != (const char *) NULL)
2094    {
2095      Image
2096        *crop_image;
2097
2098      /*
2099        Crop image to a user specified size.
2100      */
2101      crop_image=CropImageToTiles(*image,crop_geometry,exception);
2102      if (crop_image == (Image *) NULL)
2103        transform_image=CloneImage(*image,0,0,MagickTrue,exception);
2104      else
2105        {
2106          transform_image=DestroyImage(transform_image);
2107          transform_image=GetFirstImageInList(crop_image);
2108        }
2109      *image=transform_image;
2110    }
2111  if (image_geometry == (const char *) NULL)
2112    return(MagickTrue);
2113
2114  /*
2115    Scale image to a user specified size.
2116  */
2117  (void) ParseRegionGeometry(transform_image,image_geometry,&geometry,exception);
2118  if ((transform_image->columns == geometry.width) &&
2119      (transform_image->rows == geometry.height))
2120    return(MagickTrue);
2121  resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
2122    transform_image->filter,exception);
2123  if (resize_image == (Image *) NULL)
2124    return(MagickFalse);
2125  transform_image=DestroyImage(transform_image);
2126  transform_image=resize_image;
2127  *image=transform_image;
2128  return(MagickTrue);
2129}
2130
2131/*
2132%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2133%                                                                             %
2134%                                                                             %
2135%                                                                             %
2136%   T r a n s p o s e I m a g e                                               %
2137%                                                                             %
2138%                                                                             %
2139%                                                                             %
2140%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2141%
2142%  TransposeImage() creates a horizontal mirror image by reflecting the pixels
2143%  around the central y-axis while rotating them by 90 degrees.
2144%
2145%  The format of the TransposeImage method is:
2146%
2147%      Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2148%
2149%  A description of each parameter follows:
2150%
2151%    o image: the image.
2152%
2153%    o exception: return any errors or warnings in this structure.
2154%
2155*/
2156MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2157{
2158#define TransposeImageTag  "Transpose/Image"
2159
2160  CacheView
2161    *image_view,
2162    *transpose_view;
2163
2164  Image
2165    *transpose_image;
2166
2167  MagickBooleanType
2168    status;
2169
2170  MagickOffsetType
2171    progress;
2172
2173  RectangleInfo
2174    page;
2175
2176  ssize_t
2177    y;
2178
2179  assert(image != (const Image *) NULL);
2180  assert(image->signature == MagickCoreSignature);
2181  if (image->debug != MagickFalse)
2182    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2183  assert(exception != (ExceptionInfo *) NULL);
2184  assert(exception->signature == MagickCoreSignature);
2185  transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2186    exception);
2187  if (transpose_image == (Image *) NULL)
2188    return((Image *) NULL);
2189  /*
2190    Transpose image.
2191  */
2192  status=MagickTrue;
2193  progress=0;
2194  image_view=AcquireVirtualCacheView(image,exception);
2195  transpose_view=AcquireAuthenticCacheView(transpose_image,exception);
2196#if defined(MAGICKCORE_OPENMP_SUPPORT)
2197  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2198    magick_threads(image,transpose_image,image->rows,1)
2199#endif
2200  for (y=0; y < (ssize_t) image->rows; y++)
2201  {
2202    register const Quantum
2203      *magick_restrict p;
2204
2205    register Quantum
2206      *magick_restrict q;
2207
2208    register ssize_t
2209      x;
2210
2211    if (status == MagickFalse)
2212      continue;
2213    p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
2214      image->columns,1,exception);
2215    q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1),
2216      0,1,transpose_image->rows,exception);
2217    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2218      {
2219        status=MagickFalse;
2220        continue;
2221      }
2222    for (x=0; x < (ssize_t) image->columns; x++)
2223    {
2224      register ssize_t
2225        i;
2226
2227      if (GetPixelReadMask(image,q) == 0)
2228        {
2229          SetPixelBackgoundColor(transpose_image,q);
2230          p+=GetPixelChannels(image);
2231          q+=GetPixelChannels(transpose_image);
2232          continue;
2233        }
2234      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2235      {
2236        PixelChannel channel=GetPixelChannelChannel(image,i);
2237        PixelTrait traits=GetPixelChannelTraits(image,channel);
2238        PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image,
2239          channel);
2240        if ((traits == UndefinedPixelTrait) ||
2241            (transpose_traits == UndefinedPixelTrait))
2242          continue;
2243        SetPixelChannel(transpose_image,channel,p[i],q);
2244      }
2245      p+=GetPixelChannels(image);
2246      q+=GetPixelChannels(transpose_image);
2247    }
2248    if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2249      status=MagickFalse;
2250    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2251      {
2252        MagickBooleanType
2253          proceed;
2254
2255#if defined(MAGICKCORE_OPENMP_SUPPORT)
2256        #pragma omp critical (MagickCore_TransposeImage)
2257#endif
2258        proceed=SetImageProgress(image,TransposeImageTag,progress++,
2259          image->rows);
2260        if (proceed == MagickFalse)
2261          status=MagickFalse;
2262      }
2263  }
2264  transpose_view=DestroyCacheView(transpose_view);
2265  image_view=DestroyCacheView(image_view);
2266  transpose_image->type=image->type;
2267  page=transpose_image->page;
2268  Swap(page.width,page.height);
2269  Swap(page.x,page.y);
2270  transpose_image->page=page;
2271  if (status == MagickFalse)
2272    transpose_image=DestroyImage(transpose_image);
2273  return(transpose_image);
2274}
2275
2276/*
2277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2278%                                                                             %
2279%                                                                             %
2280%                                                                             %
2281%   T r a n s v e r s e I m a g e                                             %
2282%                                                                             %
2283%                                                                             %
2284%                                                                             %
2285%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2286%
2287%  TransverseImage() creates a vertical mirror image by reflecting the pixels
2288%  around the central x-axis while rotating them by 270 degrees.
2289%
2290%  The format of the TransverseImage method is:
2291%
2292%      Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2293%
2294%  A description of each parameter follows:
2295%
2296%    o image: the image.
2297%
2298%    o exception: return any errors or warnings in this structure.
2299%
2300*/
2301MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2302{
2303#define TransverseImageTag  "Transverse/Image"
2304
2305  CacheView
2306    *image_view,
2307    *transverse_view;
2308
2309  Image
2310    *transverse_image;
2311
2312  MagickBooleanType
2313    status;
2314
2315  MagickOffsetType
2316    progress;
2317
2318  RectangleInfo
2319    page;
2320
2321  ssize_t
2322    y;
2323
2324  assert(image != (const Image *) NULL);
2325  assert(image->signature == MagickCoreSignature);
2326  if (image->debug != MagickFalse)
2327    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2328  assert(exception != (ExceptionInfo *) NULL);
2329  assert(exception->signature == MagickCoreSignature);
2330  transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2331    exception);
2332  if (transverse_image == (Image *) NULL)
2333    return((Image *) NULL);
2334  /*
2335    Transverse image.
2336  */
2337  status=MagickTrue;
2338  progress=0;
2339  image_view=AcquireVirtualCacheView(image,exception);
2340  transverse_view=AcquireAuthenticCacheView(transverse_image,exception);
2341#if defined(MAGICKCORE_OPENMP_SUPPORT)
2342  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2343    magick_threads(image,transverse_image,image->rows,1)
2344#endif
2345  for (y=0; y < (ssize_t) image->rows; y++)
2346  {
2347    MagickBooleanType
2348      sync;
2349
2350    register const Quantum
2351      *magick_restrict p;
2352
2353    register Quantum
2354      *magick_restrict q;
2355
2356    register ssize_t
2357      x;
2358
2359    if (status == MagickFalse)
2360      continue;
2361    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2362    q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-1),
2363      0,1,transverse_image->rows,exception);
2364    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2365      {
2366        status=MagickFalse;
2367        continue;
2368      }
2369    q+=GetPixelChannels(transverse_image)*image->columns;
2370    for (x=0; x < (ssize_t) image->columns; x++)
2371    {
2372      register ssize_t
2373        i;
2374
2375      q-=GetPixelChannels(transverse_image);
2376      if (GetPixelReadMask(image,p) == 0)
2377        {
2378          p+=GetPixelChannels(image);
2379          continue;
2380        }
2381      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2382      {
2383        PixelChannel channel=GetPixelChannelChannel(image,i);
2384        PixelTrait traits=GetPixelChannelTraits(image,channel);
2385        PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image,
2386          channel);
2387        if ((traits == UndefinedPixelTrait) ||
2388            (transverse_traits == UndefinedPixelTrait))
2389          continue;
2390        SetPixelChannel(transverse_image,channel,p[i],q);
2391      }
2392      p+=GetPixelChannels(image);
2393    }
2394    sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2395    if (sync == MagickFalse)
2396      status=MagickFalse;
2397    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2398      {
2399        MagickBooleanType
2400          proceed;
2401
2402#if defined(MAGICKCORE_OPENMP_SUPPORT)
2403        #pragma omp critical (MagickCore_TransverseImage)
2404#endif
2405        proceed=SetImageProgress(image,TransverseImageTag,progress++,
2406          image->rows);
2407        if (proceed == MagickFalse)
2408          status=MagickFalse;
2409      }
2410  }
2411  transverse_view=DestroyCacheView(transverse_view);
2412  image_view=DestroyCacheView(image_view);
2413  transverse_image->type=image->type;
2414  page=transverse_image->page;
2415  Swap(page.width,page.height);
2416  Swap(page.x,page.y);
2417  if (page.width != 0)
2418    page.x=(ssize_t) (page.width-transverse_image->columns-page.x);
2419  if (page.height != 0)
2420    page.y=(ssize_t) (page.height-transverse_image->rows-page.y);
2421  transverse_image->page=page;
2422  if (status == MagickFalse)
2423    transverse_image=DestroyImage(transverse_image);
2424  return(transverse_image);
2425}
2426
2427/*
2428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2429%                                                                             %
2430%                                                                             %
2431%                                                                             %
2432%   T r i m I m a g e                                                         %
2433%                                                                             %
2434%                                                                             %
2435%                                                                             %
2436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2437%
2438%  TrimImage() trims pixels from the image edges.  It allocates the memory
2439%  necessary for the new Image structure and returns a pointer to the new
2440%  image.
2441%
2442%  The format of the TrimImage method is:
2443%
2444%      Image *TrimImage(const Image *image,ExceptionInfo *exception)
2445%
2446%  A description of each parameter follows:
2447%
2448%    o image: the image.
2449%
2450%    o exception: return any errors or warnings in this structure.
2451%
2452*/
2453MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2454{
2455  RectangleInfo
2456    geometry;
2457
2458  assert(image != (const Image *) NULL);
2459  assert(image->signature == MagickCoreSignature);
2460  if (image->debug != MagickFalse)
2461    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2462  geometry=GetImageBoundingBox(image,exception);
2463  if ((geometry.width == 0) || (geometry.height == 0))
2464    {
2465      Image
2466        *crop_image;
2467
2468      crop_image=CloneImage(image,1,1,MagickTrue,exception);
2469      if (crop_image == (Image *) NULL)
2470        return((Image *) NULL);
2471      crop_image->background_color.alpha=(Quantum) TransparentAlpha;
2472      crop_image->alpha_trait=BlendPixelTrait;
2473      (void) SetImageBackgroundColor(crop_image,exception);
2474      crop_image->page=image->page;
2475      crop_image->page.x=(-1);
2476      crop_image->page.y=(-1);
2477      return(crop_image);
2478    }
2479  geometry.x+=image->page.x;
2480  geometry.y+=image->page.y;
2481  return(CropImage(image,&geometry,exception));
2482}
2483