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