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