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