transform.c revision 8969a36f06b585ff612855f3808de07f1f5d57b0
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-2014 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=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 == 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 ((size_t) (page.x+page.width) > 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 ((size_t) (page.y+page.height) > 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            break;
882          AppendImageToList(&crop_image,next);
883        }
884        if (next == (Image *) NULL)
885          break;
886      }
887      ClearMagickException(exception);
888      return(crop_image);
889    }
890  if (((geometry.width == 0) && (geometry.height == 0)) ||
891      ((flags & XValue) != 0) || ((flags & YValue) != 0))
892    {
893      /*
894        Crop a single region at +X+Y.
895      */
896      crop_image=CropImage(image,&geometry,exception);
897      if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
898        {
899          crop_image->page.width=geometry.width;
900          crop_image->page.height=geometry.height;
901          crop_image->page.x-=geometry.x;
902          crop_image->page.y-=geometry.y;
903        }
904      return(crop_image);
905    }
906  if ((image->columns > geometry.width) || (image->rows > geometry.height))
907    {
908      RectangleInfo
909        page;
910
911      size_t
912        height,
913        width;
914
915      ssize_t
916        x,
917        y;
918
919      /*
920        Crop into tiles of fixed size WxH.
921      */
922      page=image->page;
923      if (page.width == 0)
924        page.width=image->columns;
925      if (page.height == 0)
926        page.height=image->rows;
927      width=geometry.width;
928      if (width == 0)
929        width=page.width;
930      height=geometry.height;
931      if (height == 0)
932        height=page.height;
933      next=NewImageList();
934      for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
935      {
936        for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
937        {
938          geometry.width=width;
939          geometry.height=height;
940          geometry.x=x;
941          geometry.y=y;
942          next=CropImage(image,&geometry,exception);
943          if (next == (Image *) NULL)
944            break;
945          AppendImageToList(&crop_image,next);
946        }
947        if (next == (Image *) NULL)
948          break;
949      }
950      return(crop_image);
951    }
952  return(CloneImage(image,0,0,MagickTrue,exception));
953}
954
955/*
956%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
957%                                                                             %
958%                                                                             %
959%                                                                             %
960%   E x c e r p t I m a g e                                                   %
961%                                                                             %
962%                                                                             %
963%                                                                             %
964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
965%
966%  ExcerptImage() returns a excerpt of the image as defined by the geometry.
967%
968%  The format of the ExcerptImage method is:
969%
970%      Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
971%        ExceptionInfo *exception)
972%
973%  A description of each parameter follows:
974%
975%    o image: the image.
976%
977%    o geometry: Define the region of the image to extend with members
978%      x, y, width, and height.
979%
980%    o exception: return any errors or warnings in this structure.
981%
982*/
983MagickExport Image *ExcerptImage(const Image *image,
984  const RectangleInfo *geometry,ExceptionInfo *exception)
985{
986#define ExcerptImageTag  "Excerpt/Image"
987
988  CacheView
989    *excerpt_view,
990    *image_view;
991
992  Image
993    *excerpt_image;
994
995  MagickBooleanType
996    status;
997
998  MagickOffsetType
999    progress;
1000
1001  ssize_t
1002    y;
1003
1004  /*
1005    Allocate excerpt image.
1006  */
1007  assert(image != (const Image *) NULL);
1008  assert(image->signature == MagickSignature);
1009  if (image->debug != MagickFalse)
1010    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1011  assert(geometry != (const RectangleInfo *) NULL);
1012  assert(exception != (ExceptionInfo *) NULL);
1013  assert(exception->signature == MagickSignature);
1014  excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1015    exception);
1016  if (excerpt_image == (Image *) NULL)
1017    return((Image *) NULL);
1018  /*
1019    Excerpt each row.
1020  */
1021  status=MagickTrue;
1022  progress=0;
1023  image_view=AcquireVirtualCacheView(image,exception);
1024  excerpt_view=AcquireAuthenticCacheView(excerpt_image,exception);
1025#if defined(MAGICKCORE_OPENMP_SUPPORT)
1026  #pragma omp parallel for schedule(static,4) shared(progress,status) \
1027    magick_threads(image,excerpt_image,excerpt_image->rows,1)
1028#endif
1029  for (y=0; y < (ssize_t) excerpt_image->rows; y++)
1030  {
1031    register const Quantum
1032      *restrict p;
1033
1034    register Quantum
1035      *restrict q;
1036
1037    register ssize_t
1038      x;
1039
1040    if (status == MagickFalse)
1041      continue;
1042    p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
1043      geometry->width,1,exception);
1044    q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
1045      exception);
1046    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1047      {
1048        status=MagickFalse;
1049        continue;
1050      }
1051    for (x=0; x < (ssize_t) excerpt_image->columns; x++)
1052    {
1053      register ssize_t
1054        i;
1055
1056      if (GetPixelReadMask(image,p) == 0)
1057        {
1058          SetPixelBackgoundColor(excerpt_image,q);
1059          p+=GetPixelChannels(image);
1060          q+=GetPixelChannels(excerpt_image);
1061          continue;
1062        }
1063      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1064      {
1065        PixelChannel channel=GetPixelChannelChannel(image,i);
1066        PixelTrait traits=GetPixelChannelTraits(image,channel);
1067        PixelTrait excerpt_traits=GetPixelChannelTraits(excerpt_image,channel);
1068        if ((traits == UndefinedPixelTrait) ||
1069            (excerpt_traits == UndefinedPixelTrait))
1070          continue;
1071        SetPixelChannel(excerpt_image,channel,p[i],q);
1072      }
1073      p+=GetPixelChannels(image);
1074      q+=GetPixelChannels(excerpt_image);
1075    }
1076    if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
1077      status=MagickFalse;
1078    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1079      {
1080        MagickBooleanType
1081          proceed;
1082
1083#if defined(MAGICKCORE_OPENMP_SUPPORT)
1084        #pragma omp critical (MagickCore_ExcerptImage)
1085#endif
1086        proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows);
1087        if (proceed == MagickFalse)
1088          status=MagickFalse;
1089      }
1090  }
1091  excerpt_view=DestroyCacheView(excerpt_view);
1092  image_view=DestroyCacheView(image_view);
1093  excerpt_image->type=image->type;
1094  if (status == MagickFalse)
1095    excerpt_image=DestroyImage(excerpt_image);
1096  return(excerpt_image);
1097}
1098
1099/*
1100%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1101%                                                                             %
1102%                                                                             %
1103%                                                                             %
1104%   E x t e n t I m a g e                                                     %
1105%                                                                             %
1106%                                                                             %
1107%                                                                             %
1108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1109%
1110%  ExtentImage() extends the image as defined by the geometry, gravity, and
1111%  image background color.  Set the (x,y) offset of the geometry to move the
1112%  original image relative to the extended image.
1113%
1114%  The format of the ExtentImage method is:
1115%
1116%      Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
1117%        ExceptionInfo *exception)
1118%
1119%  A description of each parameter follows:
1120%
1121%    o image: the image.
1122%
1123%    o geometry: Define the region of the image to extend with members
1124%      x, y, width, and height.
1125%
1126%    o exception: return any errors or warnings in this structure.
1127%
1128*/
1129MagickExport Image *ExtentImage(const Image *image,
1130  const RectangleInfo *geometry,ExceptionInfo *exception)
1131{
1132  Image
1133    *extent_image;
1134
1135  /*
1136    Allocate extent image.
1137  */
1138  assert(image != (const Image *) NULL);
1139  assert(image->signature == MagickSignature);
1140  if (image->debug != MagickFalse)
1141    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1142  assert(geometry != (const RectangleInfo *) NULL);
1143  assert(exception != (ExceptionInfo *) NULL);
1144  assert(exception->signature == MagickSignature);
1145  extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1146    exception);
1147  if (extent_image == (Image *) NULL)
1148    return((Image *) NULL);
1149  if (SetImageStorageClass(extent_image,DirectClass,exception) == MagickFalse)
1150    {
1151      extent_image=DestroyImage(extent_image);
1152      return((Image *) NULL);
1153    }
1154  if (extent_image->background_color.alpha != OpaqueAlpha)
1155    extent_image->alpha_trait=BlendPixelTrait;
1156  (void) SetImageBackgroundColor(extent_image,exception);
1157  (void) CompositeImage(extent_image,image,image->compose,MagickTrue,
1158    -geometry->x,-geometry->y,exception);
1159  return(extent_image);
1160}
1161
1162/*
1163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1164%                                                                             %
1165%                                                                             %
1166%                                                                             %
1167%   F l i p I m a g e                                                         %
1168%                                                                             %
1169%                                                                             %
1170%                                                                             %
1171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1172%
1173%  FlipImage() creates a vertical mirror image by reflecting the pixels
1174%  around the central x-axis.
1175%
1176%  The format of the FlipImage method is:
1177%
1178%      Image *FlipImage(const Image *image,ExceptionInfo *exception)
1179%
1180%  A description of each parameter follows:
1181%
1182%    o image: the image.
1183%
1184%    o exception: return any errors or warnings in this structure.
1185%
1186*/
1187MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1188{
1189#define FlipImageTag  "Flip/Image"
1190
1191  CacheView
1192    *flip_view,
1193    *image_view;
1194
1195  Image
1196    *flip_image;
1197
1198  MagickBooleanType
1199    status;
1200
1201  MagickOffsetType
1202    progress;
1203
1204  RectangleInfo
1205    page;
1206
1207  ssize_t
1208    y;
1209
1210  assert(image != (const Image *) NULL);
1211  assert(image->signature == MagickSignature);
1212  if (image->debug != MagickFalse)
1213    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1214  assert(exception != (ExceptionInfo *) NULL);
1215  assert(exception->signature == MagickSignature);
1216  flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1217  if (flip_image == (Image *) NULL)
1218    return((Image *) NULL);
1219  /*
1220    Flip image.
1221  */
1222  status=MagickTrue;
1223  progress=0;
1224  page=image->page;
1225  image_view=AcquireVirtualCacheView(image,exception);
1226  flip_view=AcquireAuthenticCacheView(flip_image,exception);
1227#if defined(MAGICKCORE_OPENMP_SUPPORT)
1228  #pragma omp parallel for schedule(static,4) shared(status) \
1229    magick_threads(image,flip_image,1,1)
1230#endif
1231  for (y=0; y < (ssize_t) flip_image->rows; y++)
1232  {
1233    register const Quantum
1234      *restrict p;
1235
1236    register Quantum
1237      *restrict q;
1238
1239    register ssize_t
1240      x;
1241
1242    if (status == MagickFalse)
1243      continue;
1244    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1245    q=QueueCacheViewAuthenticPixels(flip_view,0,(ssize_t) (flip_image->rows-y-
1246      1),flip_image->columns,1,exception);
1247    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1248      {
1249        status=MagickFalse;
1250        continue;
1251      }
1252    for (x=0; x < (ssize_t) flip_image->columns; x++)
1253    {
1254      register ssize_t
1255        i;
1256
1257      if (GetPixelReadMask(image,p) == 0)
1258        {
1259          SetPixelBackgoundColor(flip_image,q);
1260          p+=GetPixelChannels(image);
1261          q+=GetPixelChannels(flip_image);
1262          continue;
1263        }
1264      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1265      {
1266        PixelChannel channel=GetPixelChannelChannel(image,i);
1267        PixelTrait traits=GetPixelChannelTraits(image,channel);
1268        PixelTrait flip_traits=GetPixelChannelTraits(flip_image,channel);
1269        if ((traits == UndefinedPixelTrait) ||
1270            (flip_traits == UndefinedPixelTrait))
1271          continue;
1272        SetPixelChannel(flip_image,channel,p[i],q);
1273      }
1274      p+=GetPixelChannels(image);
1275      q+=GetPixelChannels(flip_image);
1276    }
1277    if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
1278      status=MagickFalse;
1279    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1280      {
1281        MagickBooleanType
1282          proceed;
1283
1284#if defined(MAGICKCORE_OPENMP_SUPPORT)
1285        #pragma omp critical (MagickCore_FlipImage)
1286#endif
1287        proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows);
1288        if (proceed == MagickFalse)
1289          status=MagickFalse;
1290      }
1291  }
1292  flip_view=DestroyCacheView(flip_view);
1293  image_view=DestroyCacheView(image_view);
1294  flip_image->type=image->type;
1295  if (page.height != 0)
1296    page.y=(ssize_t) (page.height-flip_image->rows-page.y);
1297  flip_image->page=page;
1298  if (status == MagickFalse)
1299    flip_image=DestroyImage(flip_image);
1300  return(flip_image);
1301}
1302
1303/*
1304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1305%                                                                             %
1306%                                                                             %
1307%                                                                             %
1308%   F l o p I m a g e                                                         %
1309%                                                                             %
1310%                                                                             %
1311%                                                                             %
1312%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1313%
1314%  FlopImage() creates a horizontal mirror image by reflecting the pixels
1315%  around the central y-axis.
1316%
1317%  The format of the FlopImage method is:
1318%
1319%      Image *FlopImage(const Image *image,ExceptionInfo *exception)
1320%
1321%  A description of each parameter follows:
1322%
1323%    o image: the image.
1324%
1325%    o exception: return any errors or warnings in this structure.
1326%
1327*/
1328MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1329{
1330#define FlopImageTag  "Flop/Image"
1331
1332  CacheView
1333    *flop_view,
1334    *image_view;
1335
1336  Image
1337    *flop_image;
1338
1339  MagickBooleanType
1340    status;
1341
1342  MagickOffsetType
1343    progress;
1344
1345  RectangleInfo
1346    page;
1347
1348  ssize_t
1349    y;
1350
1351  assert(image != (const Image *) NULL);
1352  assert(image->signature == MagickSignature);
1353  if (image->debug != MagickFalse)
1354    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1355  assert(exception != (ExceptionInfo *) NULL);
1356  assert(exception->signature == MagickSignature);
1357  flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1358  if (flop_image == (Image *) NULL)
1359    return((Image *) NULL);
1360  /*
1361    Flop each row.
1362  */
1363  status=MagickTrue;
1364  progress=0;
1365  page=image->page;
1366  image_view=AcquireVirtualCacheView(image,exception);
1367  flop_view=AcquireAuthenticCacheView(flop_image,exception);
1368#if defined(MAGICKCORE_OPENMP_SUPPORT)
1369  #pragma omp parallel for schedule(static,4) shared(status) \
1370    magick_threads(image,flop_image,1,1)
1371#endif
1372  for (y=0; y < (ssize_t) flop_image->rows; y++)
1373  {
1374    register const Quantum
1375      *restrict p;
1376
1377    register ssize_t
1378      x;
1379
1380    register Quantum
1381      *restrict q;
1382
1383    if (status == MagickFalse)
1384      continue;
1385    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1386    q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1387      exception);
1388    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1389      {
1390        status=MagickFalse;
1391        continue;
1392      }
1393    q+=GetPixelChannels(flop_image)*flop_image->columns;
1394    for (x=0; x < (ssize_t) flop_image->columns; x++)
1395    {
1396      register ssize_t
1397        i;
1398
1399      q-=GetPixelChannels(flop_image);
1400      if (GetPixelReadMask(image,p) == 0)
1401        {
1402          p+=GetPixelChannels(image);
1403          continue;
1404        }
1405      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1406      {
1407        PixelChannel channel=GetPixelChannelChannel(image,i);
1408        PixelTrait traits=GetPixelChannelTraits(image,channel);
1409        PixelTrait flop_traits=GetPixelChannelTraits(flop_image,channel);
1410        if ((traits == UndefinedPixelTrait) ||
1411            (flop_traits == UndefinedPixelTrait))
1412          continue;
1413        SetPixelChannel(flop_image,channel,p[i],q);
1414      }
1415      p+=GetPixelChannels(image);
1416    }
1417    if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1418      status=MagickFalse;
1419    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1420      {
1421        MagickBooleanType
1422          proceed;
1423
1424#if defined(MAGICKCORE_OPENMP_SUPPORT)
1425        #pragma omp critical (MagickCore_FlopImage)
1426#endif
1427        proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows);
1428        if (proceed == MagickFalse)
1429          status=MagickFalse;
1430      }
1431  }
1432  flop_view=DestroyCacheView(flop_view);
1433  image_view=DestroyCacheView(image_view);
1434  flop_image->type=image->type;
1435  if (page.width != 0)
1436    page.x=(ssize_t) (page.width-flop_image->columns-page.x);
1437  flop_image->page=page;
1438  if (status == MagickFalse)
1439    flop_image=DestroyImage(flop_image);
1440  return(flop_image);
1441}
1442
1443/*
1444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1445%                                                                             %
1446%                                                                             %
1447%                                                                             %
1448%   R o l l I m a g e                                                         %
1449%                                                                             %
1450%                                                                             %
1451%                                                                             %
1452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1453%
1454%  RollImage() offsets an image as defined by x_offset and y_offset.
1455%
1456%  The format of the RollImage method is:
1457%
1458%      Image *RollImage(const Image *image,const ssize_t x_offset,
1459%        const ssize_t y_offset,ExceptionInfo *exception)
1460%
1461%  A description of each parameter follows:
1462%
1463%    o image: the image.
1464%
1465%    o x_offset: the number of columns to roll in the horizontal direction.
1466%
1467%    o y_offset: the number of rows to roll in the vertical direction.
1468%
1469%    o exception: return any errors or warnings in this structure.
1470%
1471*/
1472
1473static inline MagickBooleanType CopyImageRegion(Image *destination,
1474  const Image *source,const size_t columns,const size_t rows,
1475  const ssize_t sx,const ssize_t sy,const ssize_t dx,const ssize_t dy,
1476  ExceptionInfo *exception)
1477{
1478  CacheView
1479    *source_view,
1480    *destination_view;
1481
1482  MagickBooleanType
1483    status;
1484
1485  ssize_t
1486    y;
1487
1488  if (columns == 0)
1489    return(MagickTrue);
1490  status=MagickTrue;
1491  source_view=AcquireVirtualCacheView(source,exception);
1492  destination_view=AcquireAuthenticCacheView(destination,exception);
1493#if defined(MAGICKCORE_OPENMP_SUPPORT)
1494  #pragma omp parallel for schedule(static,4) shared(status) \
1495    magick_threads(source,destination,rows,1)
1496#endif
1497  for (y=0; y < (ssize_t) rows; y++)
1498  {
1499    MagickBooleanType
1500      sync;
1501
1502    register const Quantum
1503      *restrict p;
1504
1505    register Quantum
1506      *restrict q;
1507
1508    register ssize_t
1509      x;
1510
1511    /*
1512      Transfer scanline.
1513    */
1514    if (status == MagickFalse)
1515      continue;
1516    p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1517    q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1518    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1519      {
1520        status=MagickFalse;
1521        continue;
1522      }
1523    for (x=0; x < (ssize_t) columns; x++)
1524    {
1525      register ssize_t
1526        i;
1527
1528      if (GetPixelReadMask(source,p) == 0)
1529        {
1530          SetPixelBackgoundColor(destination,q);
1531          p+=GetPixelChannels(source);
1532          q+=GetPixelChannels(destination);
1533          continue;
1534        }
1535      for (i=0; i < (ssize_t) GetPixelChannels(source); i++)
1536      {
1537        PixelChannel channel=GetPixelChannelChannel(source,i);
1538        PixelTrait source_traits=GetPixelChannelTraits(source,channel);
1539        PixelTrait destination_traits=GetPixelChannelTraits(destination,
1540          channel);
1541        if ((source_traits == UndefinedPixelTrait) ||
1542            (destination_traits == UndefinedPixelTrait))
1543          continue;
1544        SetPixelChannel(destination,channel,p[i],q);
1545      }
1546      p+=GetPixelChannels(source);
1547      q+=GetPixelChannels(destination);
1548    }
1549    sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1550    if (sync == MagickFalse)
1551      status=MagickFalse;
1552  }
1553  destination_view=DestroyCacheView(destination_view);
1554  source_view=DestroyCacheView(source_view);
1555  return(status);
1556}
1557
1558MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1559  const ssize_t y_offset,ExceptionInfo *exception)
1560{
1561#define RollImageTag  "Roll/Image"
1562
1563  Image
1564    *roll_image;
1565
1566  MagickStatusType
1567    status;
1568
1569  RectangleInfo
1570    offset;
1571
1572  /*
1573    Initialize roll image attributes.
1574  */
1575  assert(image != (const Image *) NULL);
1576  assert(image->signature == MagickSignature);
1577  if (image->debug != MagickFalse)
1578    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1579  assert(exception != (ExceptionInfo *) NULL);
1580  assert(exception->signature == MagickSignature);
1581  roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
1582  if (roll_image == (Image *) NULL)
1583    return((Image *) NULL);
1584  offset.x=x_offset;
1585  offset.y=y_offset;
1586  while (offset.x < 0)
1587    offset.x+=(ssize_t) image->columns;
1588  while (offset.x >= (ssize_t) image->columns)
1589    offset.x-=(ssize_t) image->columns;
1590  while (offset.y < 0)
1591    offset.y+=(ssize_t) image->rows;
1592  while (offset.y >= (ssize_t) image->rows)
1593    offset.y-=(ssize_t) image->rows;
1594  /*
1595    Roll image.
1596  */
1597  status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1598    (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
1599    offset.y,0,0,exception);
1600  (void) SetImageProgress(image,RollImageTag,0,3);
1601  status&=CopyImageRegion(roll_image,image,image->columns-offset.x,
1602    (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0,
1603    exception);
1604  (void) SetImageProgress(image,RollImageTag,1,3);
1605  status&=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows-
1606    offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception);
1607  (void) SetImageProgress(image,RollImageTag,2,3);
1608  status&=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows-
1609    offset.y,0,0,offset.x,offset.y,exception);
1610  (void) SetImageProgress(image,RollImageTag,3,3);
1611  roll_image->type=image->type;
1612  if (status == MagickFalse)
1613    roll_image=DestroyImage(roll_image);
1614  return(roll_image);
1615}
1616
1617/*
1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1619%                                                                             %
1620%                                                                             %
1621%                                                                             %
1622%   S h a v e I m a g e                                                       %
1623%                                                                             %
1624%                                                                             %
1625%                                                                             %
1626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1627%
1628%  ShaveImage() shaves pixels from the image edges.  It allocates the memory
1629%  necessary for the new Image structure and returns a pointer to the new
1630%  image.
1631%
1632%  The format of the ShaveImage method is:
1633%
1634%      Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1635%        ExceptionInfo *exception)
1636%
1637%  A description of each parameter follows:
1638%
1639%    o shave_image: Method ShaveImage returns a pointer to the shaved
1640%      image.  A null image is returned if there is a memory shortage or
1641%      if the image width or height is zero.
1642%
1643%    o image: the image.
1644%
1645%    o shave_info: Specifies a pointer to a RectangleInfo which defines the
1646%      region of the image to crop.
1647%
1648%    o exception: return any errors or warnings in this structure.
1649%
1650*/
1651MagickExport Image *ShaveImage(const Image *image,
1652  const RectangleInfo *shave_info,ExceptionInfo *exception)
1653{
1654  Image
1655    *shave_image;
1656
1657  RectangleInfo
1658    geometry;
1659
1660  assert(image != (const Image *) NULL);
1661  assert(image->signature == MagickSignature);
1662  if (image->debug != MagickFalse)
1663    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1664  if (((2*shave_info->width) >= image->columns) ||
1665      ((2*shave_info->height) >= image->rows))
1666    ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1667  SetGeometry(image,&geometry);
1668  geometry.width-=2*shave_info->width;
1669  geometry.height-=2*shave_info->height;
1670  geometry.x=(ssize_t) shave_info->width+image->page.x;
1671  geometry.y=(ssize_t) shave_info->height+image->page.y;
1672  shave_image=CropImage(image,&geometry,exception);
1673  if (shave_image == (Image *) NULL)
1674    return((Image *) NULL);
1675  shave_image->page.width-=2*shave_info->width;
1676  shave_image->page.height-=2*shave_info->height;
1677  shave_image->page.x-=(ssize_t) shave_info->width;
1678  shave_image->page.y-=(ssize_t) shave_info->height;
1679  return(shave_image);
1680}
1681
1682/*
1683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1684%                                                                             %
1685%                                                                             %
1686%                                                                             %
1687%   S p l i c e I m a g e                                                     %
1688%                                                                             %
1689%                                                                             %
1690%                                                                             %
1691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1692%
1693%  SpliceImage() splices a solid color into the image as defined by the
1694%  geometry.
1695%
1696%  The format of the SpliceImage method is:
1697%
1698%      Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1699%        ExceptionInfo *exception)
1700%
1701%  A description of each parameter follows:
1702%
1703%    o image: the image.
1704%
1705%    o geometry: Define the region of the image to splice with members
1706%      x, y, width, and height.
1707%
1708%    o exception: return any errors or warnings in this structure.
1709%
1710*/
1711MagickExport Image *SpliceImage(const Image *image,
1712  const RectangleInfo *geometry,ExceptionInfo *exception)
1713{
1714#define SpliceImageTag  "Splice/Image"
1715
1716  CacheView
1717    *image_view,
1718    *splice_view;
1719
1720  Image
1721    *splice_image;
1722
1723  MagickBooleanType
1724    status;
1725
1726  MagickOffsetType
1727    progress;
1728
1729  RectangleInfo
1730    splice_geometry;
1731
1732  ssize_t
1733    y;
1734
1735  /*
1736    Allocate splice image.
1737  */
1738  assert(image != (const Image *) NULL);
1739  assert(image->signature == MagickSignature);
1740  if (image->debug != MagickFalse)
1741    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1742  assert(geometry != (const RectangleInfo *) NULL);
1743  assert(exception != (ExceptionInfo *) NULL);
1744  assert(exception->signature == MagickSignature);
1745  splice_geometry=(*geometry);
1746  splice_image=CloneImage(image,image->columns+splice_geometry.width,
1747    image->rows+splice_geometry.height,MagickTrue,exception);
1748  if (splice_image == (Image *) NULL)
1749    return((Image *) NULL);
1750  if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse)
1751    {
1752      splice_image=DestroyImage(splice_image);
1753      return((Image *) NULL);
1754    }
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      p+=GetPixelChannels(image);
1865      q+=GetPixelChannels(splice_image);
1866    }
1867    for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
1868      q+=GetPixelChannels(splice_image);
1869    for ( ; x < (ssize_t) splice_image->columns; x++)
1870    {
1871      register ssize_t
1872        i;
1873
1874      if (GetPixelReadMask(image,p) == 0)
1875        {
1876          SetPixelBackgoundColor(splice_image,q);
1877          p+=GetPixelChannels(image);
1878          q+=GetPixelChannels(splice_image);
1879          continue;
1880        }
1881      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1882      {
1883        PixelChannel channel=GetPixelChannelChannel(image,i);
1884        PixelTrait traits=GetPixelChannelTraits(image,channel);
1885        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1886        if ((traits == UndefinedPixelTrait) ||
1887            (splice_traits == UndefinedPixelTrait))
1888          continue;
1889        SetPixelChannel(splice_image,channel,p[i],q);
1890      }
1891      p+=GetPixelChannels(image);
1892      q+=GetPixelChannels(splice_image);
1893    }
1894    if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1895      status=MagickFalse;
1896    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1897      {
1898        MagickBooleanType
1899          proceed;
1900
1901#if defined(MAGICKCORE_OPENMP_SUPPORT)
1902        #pragma omp critical (MagickCore_TransposeImage)
1903#endif
1904        proceed=SetImageProgress(image,SpliceImageTag,progress++,
1905          splice_image->rows);
1906        if (proceed == MagickFalse)
1907          status=MagickFalse;
1908      }
1909  }
1910#if defined(MAGICKCORE_OPENMP_SUPPORT)
1911  #pragma omp parallel for schedule(static,4) shared(progress,status) \
1912    magick_threads(image,splice_image,1,1)
1913#endif
1914  for (y=(ssize_t) (splice_geometry.y+splice_geometry.height);
1915       y < (ssize_t) splice_image->rows; y++)
1916  {
1917    register const Quantum
1918      *restrict p;
1919
1920    register ssize_t
1921      x;
1922
1923    register Quantum
1924      *restrict q;
1925
1926    if (status == MagickFalse)
1927      continue;
1928    p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
1929      image->columns,1,exception);
1930    if ((y < 0) || (y >= (ssize_t) splice_image->rows))
1931      continue;
1932    q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1933      exception);
1934    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1935      {
1936        status=MagickFalse;
1937        continue;
1938      }
1939    for (x=0; x < splice_geometry.x; x++)
1940    {
1941      register ssize_t
1942        i;
1943
1944      if (GetPixelReadMask(image,q) == 0)
1945        {
1946          SetPixelBackgoundColor(splice_image,q);
1947          p+=GetPixelChannels(image);
1948          q+=GetPixelChannels(splice_image);
1949          continue;
1950        }
1951      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1952      {
1953        PixelChannel channel=GetPixelChannelChannel(image,i);
1954        PixelTrait traits=GetPixelChannelTraits(image,channel);
1955        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1956        if ((traits == UndefinedPixelTrait) ||
1957            (splice_traits == UndefinedPixelTrait))
1958          continue;
1959        SetPixelChannel(splice_image,channel,p[i],q);
1960      }
1961      p+=GetPixelChannels(image);
1962      q+=GetPixelChannels(splice_image);
1963    }
1964    for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++)
1965      q+=GetPixelChannels(splice_image);
1966    for ( ; x < (ssize_t) splice_image->columns; x++)
1967    {
1968      register ssize_t
1969        i;
1970
1971      if (GetPixelReadMask(image,q) == 0)
1972        {
1973          SetPixelBackgoundColor(splice_image,q);
1974          p+=GetPixelChannels(image);
1975          q+=GetPixelChannels(splice_image);
1976          continue;
1977        }
1978      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1979      {
1980        PixelChannel channel=GetPixelChannelChannel(image,i);
1981        PixelTrait traits=GetPixelChannelTraits(image,channel);
1982        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1983        if ((traits == UndefinedPixelTrait) ||
1984            (splice_traits == UndefinedPixelTrait))
1985          continue;
1986        SetPixelChannel(splice_image,channel,p[i],q);
1987      }
1988      p+=GetPixelChannels(image);
1989      q+=GetPixelChannels(splice_image);
1990    }
1991    if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1992      status=MagickFalse;
1993    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1994      {
1995        MagickBooleanType
1996          proceed;
1997
1998#if defined(MAGICKCORE_OPENMP_SUPPORT)
1999        #pragma omp critical (MagickCore_TransposeImage)
2000#endif
2001        proceed=SetImageProgress(image,SpliceImageTag,progress++,
2002          splice_image->rows);
2003        if (proceed == MagickFalse)
2004          status=MagickFalse;
2005      }
2006  }
2007  splice_view=DestroyCacheView(splice_view);
2008  image_view=DestroyCacheView(image_view);
2009  if (status == MagickFalse)
2010    splice_image=DestroyImage(splice_image);
2011  return(splice_image);
2012}
2013
2014/*
2015%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2016%                                                                             %
2017%                                                                             %
2018%                                                                             %
2019%   T r a n s f o r m I m a g e                                               %
2020%                                                                             %
2021%                                                                             %
2022%                                                                             %
2023%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2024%
2025%  TransformImage() is a convenience method that behaves like ResizeImage() or
2026%  CropImage() but accepts scaling and/or cropping information as a region
2027%  geometry specification.  If the operation fails, the original image handle
2028%  is left as is.
2029%
2030%  This should only be used for single images.
2031%
2032%  This function destroys what it assumes to be a single image list.
2033%  If the input image is part of a larger list, all other images in that list
2034%  will be simply 'lost', not destroyed.
2035%
2036%  Also if the crop generates a list of images only the first image is resized.
2037%  And finally if the crop succeeds and the resize failed, you will get a
2038%  cropped image, as well as a 'false' or 'failed' report.
2039%
2040%  This function and should probably be depreciated in favor of direct calls
2041%  to CropImageToTiles() or ResizeImage(), as appropriate.
2042%
2043%  The format of the TransformImage method is:
2044%
2045%      MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
2046%        const char *image_geometry,ExceptionInfo *exception)
2047%
2048%  A description of each parameter follows:
2049%
2050%    o image: the image The transformed image is returned as this parameter.
2051%
2052%    o crop_geometry: A crop geometry string.  This geometry defines a
2053%      subregion of the image to crop.
2054%
2055%    o image_geometry: An image geometry string.  This geometry defines the
2056%      final size of the image.
2057%
2058%    o exception: return any errors or warnings in this structure.
2059%
2060*/
2061MagickExport MagickBooleanType TransformImage(Image **image,
2062  const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
2063{
2064  Image
2065    *resize_image,
2066    *transform_image;
2067
2068  MagickStatusType
2069    flags;
2070
2071  RectangleInfo
2072    geometry;
2073
2074  assert(image != (Image **) NULL);
2075  assert((*image)->signature == MagickSignature);
2076  if ((*image)->debug != MagickFalse)
2077    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
2078  transform_image=(*image);
2079  if (crop_geometry != (const char *) NULL)
2080    {
2081      Image
2082        *crop_image;
2083
2084      /*
2085        Crop image to a user specified size.
2086      */
2087      crop_image=CropImageToTiles(*image,crop_geometry,exception);
2088      if (crop_image == (Image *) NULL)
2089        transform_image=CloneImage(*image,0,0,MagickTrue,exception);
2090      else
2091        {
2092          transform_image=DestroyImage(transform_image);
2093          transform_image=GetFirstImageInList(crop_image);
2094        }
2095      *image=transform_image;
2096    }
2097  if (image_geometry == (const char *) NULL)
2098    return(MagickTrue);
2099
2100  /*
2101    Scale image to a user specified size.
2102  */
2103  flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,exception);
2104  (void) flags;
2105  if ((transform_image->columns == geometry.width) &&
2106      (transform_image->rows == geometry.height))
2107    return(MagickTrue);
2108  resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
2109    transform_image->filter,exception);
2110  if (resize_image == (Image *) NULL)
2111    return(MagickFalse);
2112  transform_image=DestroyImage(transform_image);
2113  transform_image=resize_image;
2114  *image=transform_image;
2115  return(MagickTrue);
2116}
2117
2118/*
2119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2120%                                                                             %
2121%                                                                             %
2122%                                                                             %
2123%   T r a n s f o r m I m a g e s                                             %
2124%                                                                             %
2125%                                                                             %
2126%                                                                             %
2127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2128%
2129%  TransformImages() calls TransformImage() on each image of a sequence.
2130%
2131%  The format of the TransformImage method is:
2132%
2133%      MagickBooleanType TransformImages(Image **image,
2134%        const char *crop_geometry,const char *image_geometry,
2135%        ExceptionInfo *exception)
2136%
2137%  A description of each parameter follows:
2138%
2139%    o image: the image The transformed image is returned as this parameter.
2140%
2141%    o crop_geometry: A crop geometry string.  This geometry defines a
2142%      subregion of the image to crop.
2143%
2144%    o image_geometry: An image geometry string.  This geometry defines the
2145%      final size of the image.
2146%
2147%    o exception: return any errors or warnings in this structure.
2148%
2149*/
2150MagickExport MagickBooleanType TransformImages(Image **images,
2151  const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
2152{
2153  Image
2154    *image,
2155    **image_list,
2156    *transform_images;
2157
2158  MagickStatusType
2159    status;
2160
2161  register ssize_t
2162    i;
2163
2164  assert(images != (Image **) NULL);
2165  assert((*images)->signature == MagickSignature);
2166  if ((*images)->debug != MagickFalse)
2167    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2168      (*images)->filename);
2169  image_list=ImageListToArray(*images,exception);
2170  if (image_list == (Image **) NULL)
2171    return(MagickFalse);
2172  status=MagickTrue;
2173  transform_images=NewImageList();
2174  for (i=0; image_list[i] != (Image *) NULL; i++)
2175  {
2176    image=image_list[i];
2177    status&=TransformImage(&image,crop_geometry,image_geometry,exception);
2178    AppendImageToList(&transform_images,image);
2179  }
2180  *images=transform_images;
2181  image_list=(Image **) RelinquishMagickMemory(image_list);
2182  return(status != 0 ? MagickTrue : MagickFalse);
2183}
2184
2185/*
2186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2187%                                                                             %
2188%                                                                             %
2189%                                                                             %
2190%   T r a n s p o s e I m a g e                                               %
2191%                                                                             %
2192%                                                                             %
2193%                                                                             %
2194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2195%
2196%  TransposeImage() creates a horizontal mirror image by reflecting the pixels
2197%  around the central y-axis while rotating them by 90 degrees.
2198%
2199%  The format of the TransposeImage method is:
2200%
2201%      Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2202%
2203%  A description of each parameter follows:
2204%
2205%    o image: the image.
2206%
2207%    o exception: return any errors or warnings in this structure.
2208%
2209*/
2210MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2211{
2212#define TransposeImageTag  "Transpose/Image"
2213
2214  CacheView
2215    *image_view,
2216    *transpose_view;
2217
2218  Image
2219    *transpose_image;
2220
2221  MagickBooleanType
2222    status;
2223
2224  MagickOffsetType
2225    progress;
2226
2227  RectangleInfo
2228    page;
2229
2230  ssize_t
2231    y;
2232
2233  assert(image != (const Image *) NULL);
2234  assert(image->signature == MagickSignature);
2235  if (image->debug != MagickFalse)
2236    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2237  assert(exception != (ExceptionInfo *) NULL);
2238  assert(exception->signature == MagickSignature);
2239  transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2240    exception);
2241  if (transpose_image == (Image *) NULL)
2242    return((Image *) NULL);
2243  /*
2244    Transpose image.
2245  */
2246  status=MagickTrue;
2247  progress=0;
2248  image_view=AcquireVirtualCacheView(image,exception);
2249  transpose_view=AcquireAuthenticCacheView(transpose_image,exception);
2250#if defined(MAGICKCORE_OPENMP_SUPPORT)
2251  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2252    magick_threads(image,transpose_image,image->rows,1)
2253#endif
2254  for (y=0; y < (ssize_t) image->rows; y++)
2255  {
2256    register const Quantum
2257      *restrict p;
2258
2259    register Quantum
2260      *restrict q;
2261
2262    register ssize_t
2263      x;
2264
2265    if (status == MagickFalse)
2266      continue;
2267    p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
2268      image->columns,1,exception);
2269    q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1),
2270      0,1,transpose_image->rows,exception);
2271    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2272      {
2273        status=MagickFalse;
2274        continue;
2275      }
2276    for (x=0; x < (ssize_t) image->columns; x++)
2277    {
2278      register ssize_t
2279        i;
2280
2281      if (GetPixelReadMask(image,q) == 0)
2282        {
2283          SetPixelBackgoundColor(transpose_image,q);
2284          p+=GetPixelChannels(image);
2285          q+=GetPixelChannels(transpose_image);
2286          continue;
2287        }
2288      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2289      {
2290        PixelChannel channel=GetPixelChannelChannel(image,i);
2291        PixelTrait traits=GetPixelChannelTraits(image,channel);
2292        PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image,
2293          channel);
2294        if ((traits == UndefinedPixelTrait) ||
2295            (transpose_traits == UndefinedPixelTrait))
2296          continue;
2297        SetPixelChannel(transpose_image,channel,p[i],q);
2298      }
2299      p+=GetPixelChannels(image);
2300      q+=GetPixelChannels(transpose_image);
2301    }
2302    if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2303      status=MagickFalse;
2304    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2305      {
2306        MagickBooleanType
2307          proceed;
2308
2309#if defined(MAGICKCORE_OPENMP_SUPPORT)
2310        #pragma omp critical (MagickCore_TransposeImage)
2311#endif
2312        proceed=SetImageProgress(image,TransposeImageTag,progress++,
2313          image->rows);
2314        if (proceed == MagickFalse)
2315          status=MagickFalse;
2316      }
2317  }
2318  transpose_view=DestroyCacheView(transpose_view);
2319  image_view=DestroyCacheView(image_view);
2320  transpose_image->type=image->type;
2321  page=transpose_image->page;
2322  Swap(page.width,page.height);
2323  Swap(page.x,page.y);
2324  transpose_image->page=page;
2325  if (status == MagickFalse)
2326    transpose_image=DestroyImage(transpose_image);
2327  return(transpose_image);
2328}
2329
2330/*
2331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2332%                                                                             %
2333%                                                                             %
2334%                                                                             %
2335%   T r a n s v e r s e I m a g e                                             %
2336%                                                                             %
2337%                                                                             %
2338%                                                                             %
2339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2340%
2341%  TransverseImage() creates a vertical mirror image by reflecting the pixels
2342%  around the central x-axis while rotating them by 270 degrees.
2343%
2344%  The format of the TransverseImage method is:
2345%
2346%      Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2347%
2348%  A description of each parameter follows:
2349%
2350%    o image: the image.
2351%
2352%    o exception: return any errors or warnings in this structure.
2353%
2354*/
2355MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2356{
2357#define TransverseImageTag  "Transverse/Image"
2358
2359  CacheView
2360    *image_view,
2361    *transverse_view;
2362
2363  Image
2364    *transverse_image;
2365
2366  MagickBooleanType
2367    status;
2368
2369  MagickOffsetType
2370    progress;
2371
2372  RectangleInfo
2373    page;
2374
2375  ssize_t
2376    y;
2377
2378  assert(image != (const Image *) NULL);
2379  assert(image->signature == MagickSignature);
2380  if (image->debug != MagickFalse)
2381    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2382  assert(exception != (ExceptionInfo *) NULL);
2383  assert(exception->signature == MagickSignature);
2384  transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2385    exception);
2386  if (transverse_image == (Image *) NULL)
2387    return((Image *) NULL);
2388  /*
2389    Transverse image.
2390  */
2391  status=MagickTrue;
2392  progress=0;
2393  image_view=AcquireVirtualCacheView(image,exception);
2394  transverse_view=AcquireAuthenticCacheView(transverse_image,exception);
2395#if defined(MAGICKCORE_OPENMP_SUPPORT)
2396  #pragma omp parallel for schedule(static,4) shared(progress,status) \
2397    magick_threads(image,transverse_image,image->rows,1)
2398#endif
2399  for (y=0; y < (ssize_t) image->rows; y++)
2400  {
2401    MagickBooleanType
2402      sync;
2403
2404    register const Quantum
2405      *restrict p;
2406
2407    register Quantum
2408      *restrict q;
2409
2410    register ssize_t
2411      x;
2412
2413    if (status == MagickFalse)
2414      continue;
2415    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2416    q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-1),
2417      0,1,transverse_image->rows,exception);
2418    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2419      {
2420        status=MagickFalse;
2421        continue;
2422      }
2423    q+=GetPixelChannels(transverse_image)*image->columns;
2424    for (x=0; x < (ssize_t) image->columns; x++)
2425    {
2426      register ssize_t
2427        i;
2428
2429      q-=GetPixelChannels(transverse_image);
2430      if (GetPixelReadMask(image,p) == 0)
2431        {
2432          p+=GetPixelChannels(image);
2433          continue;
2434        }
2435      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2436      {
2437        PixelChannel channel=GetPixelChannelChannel(image,i);
2438        PixelTrait traits=GetPixelChannelTraits(image,channel);
2439        PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image,
2440          channel);
2441        if ((traits == UndefinedPixelTrait) ||
2442            (transverse_traits == UndefinedPixelTrait))
2443          continue;
2444        SetPixelChannel(transverse_image,channel,p[i],q);
2445      }
2446      p+=GetPixelChannels(image);
2447    }
2448    sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2449    if (sync == MagickFalse)
2450      status=MagickFalse;
2451    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2452      {
2453        MagickBooleanType
2454          proceed;
2455
2456#if defined(MAGICKCORE_OPENMP_SUPPORT)
2457        #pragma omp critical (MagickCore_TransverseImage)
2458#endif
2459        proceed=SetImageProgress(image,TransverseImageTag,progress++,
2460          image->rows);
2461        if (proceed == MagickFalse)
2462          status=MagickFalse;
2463      }
2464  }
2465  transverse_view=DestroyCacheView(transverse_view);
2466  image_view=DestroyCacheView(image_view);
2467  transverse_image->type=image->type;
2468  page=transverse_image->page;
2469  Swap(page.width,page.height);
2470  Swap(page.x,page.y);
2471  if (page.width != 0)
2472    page.x=(ssize_t) (page.width-transverse_image->columns-page.x);
2473  if (page.height != 0)
2474    page.y=(ssize_t) (page.height-transverse_image->rows-page.y);
2475  transverse_image->page=page;
2476  if (status == MagickFalse)
2477    transverse_image=DestroyImage(transverse_image);
2478  return(transverse_image);
2479}
2480
2481/*
2482%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2483%                                                                             %
2484%                                                                             %
2485%                                                                             %
2486%   T r i m I m a g e                                                         %
2487%                                                                             %
2488%                                                                             %
2489%                                                                             %
2490%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2491%
2492%  TrimImage() trims pixels from the image edges.  It allocates the memory
2493%  necessary for the new Image structure and returns a pointer to the new
2494%  image.
2495%
2496%  The format of the TrimImage method is:
2497%
2498%      Image *TrimImage(const Image *image,ExceptionInfo *exception)
2499%
2500%  A description of each parameter follows:
2501%
2502%    o image: the image.
2503%
2504%    o exception: return any errors or warnings in this structure.
2505%
2506*/
2507MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2508{
2509  RectangleInfo
2510    geometry;
2511
2512  assert(image != (const Image *) NULL);
2513  assert(image->signature == MagickSignature);
2514  if (image->debug != MagickFalse)
2515    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2516  geometry=GetImageBoundingBox(image,exception);
2517  if ((geometry.width == 0) || (geometry.height == 0))
2518    {
2519      Image
2520        *crop_image;
2521
2522      crop_image=CloneImage(image,1,1,MagickTrue,exception);
2523      if (crop_image == (Image *) NULL)
2524        return((Image *) NULL);
2525      crop_image->background_color.alpha=(Quantum) TransparentAlpha;
2526      crop_image->alpha_trait=BlendPixelTrait;
2527      (void) SetImageBackgroundColor(crop_image,exception);
2528      crop_image->page=image->page;
2529      crop_image->page.x=(-1);
2530      crop_image->page.y=(-1);
2531      return(crop_image);
2532    }
2533  geometry.x+=image->page.x;
2534  geometry.y+=image->page.y;
2535  return(CropImage(image,&geometry,exception));
2536}
2537