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