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