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