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