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