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