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