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