MagickCore  6.9.12-67
Convert, Edit, Or Compose Bitmap Images
 All Data Structures
visual-effects.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % V V IIIII SSSSS U U AAA L %
7 % V V I SS U U A A L %
8 % V V I SSS U U AAAAA L %
9 % V V I SS U U A A L %
10 % V IIIII SSSSS UUU A A LLLLL %
11 % %
12 % EEEEE FFFFF FFFFF EEEEE CCCC TTTTT SSSSS %
13 % E F F E C T SS %
14 % EEE FFF FFF EEE C T SSS %
15 % E F F E C T SS %
16 % EEEEE F F EEEEE CCCC T SSSSS %
17 % %
18 % %
19 % MagickCore Image Special Effects Methods %
20 % %
21 % Software Design %
22 % Cristy %
23 % October 1996 %
24 % %
25 % %
26 % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
27 % dedicated to making software imaging solutions freely available. %
28 % %
29 % You may not use this file except in compliance with the License. You may %
30 % obtain a copy of the License at %
31 % %
32 % https://imagemagick.org/script/license.php %
33 % %
34 % Unless required by applicable law or agreed to in writing, software %
35 % distributed under the License is distributed on an "AS IS" BASIS, %
36 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
37 % See the License for the specific language governing permissions and %
38 % limitations under the License. %
39 % %
40 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
41 %
42 %
43 %
44 */
45 
46 /*
47  Include declarations.
48 */
49 #include "magick/studio.h"
50 #include "magick/accelerate-private.h"
51 #include "magick/annotate.h"
52 #include "magick/artifact.h"
53 #include "magick/attribute.h"
54 #include "magick/cache.h"
55 #include "magick/cache-view.h"
56 #include "magick/channel.h"
57 #include "magick/color.h"
58 #include "magick/color-private.h"
59 #include "magick/colorspace.h"
60 #include "magick/colorspace-private.h"
61 #include "magick/composite.h"
62 #include "magick/decorate.h"
63 #include "magick/distort.h"
64 #include "magick/draw.h"
65 #include "magick/effect.h"
66 #include "magick/enhance.h"
67 #include "magick/exception.h"
68 #include "magick/exception-private.h"
69 #include "magick/gem.h"
70 #include "magick/geometry.h"
71 #include "magick/layer.h"
72 #include "magick/list.h"
73 #include "magick/log.h"
74 #include "magick/image.h"
75 #include "magick/image-private.h"
76 #include "magick/magick.h"
77 #include "magick/memory_.h"
78 #include "magick/memory-private.h"
79 #include "magick/monitor.h"
80 #include "magick/monitor-private.h"
81 #include "magick/opencl-private.h"
82 #include "magick/option.h"
83 #include "magick/pixel-accessor.h"
84 #include "magick/pixel-private.h"
85 #include "magick/property.h"
86 #include "magick/quantum.h"
87 #include "magick/quantum-private.h"
88 #include "magick/random_.h"
89 #include "magick/random-private.h"
90 #include "magick/resample.h"
91 #include "magick/resample-private.h"
92 #include "magick/resize.h"
93 #include "magick/resource_.h"
94 #include "magick/splay-tree.h"
95 #include "magick/statistic.h"
96 #include "magick/string_.h"
97 #include "magick/string-private.h"
98 #include "magick/thread-private.h"
99 #include "magick/threshold.h"
100 #include "magick/transform.h"
101 #include "magick/utility.h"
102 #include "magick/visual-effects.h"
103 
104 /*
105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106 % %
107 % %
108 % %
109 % A d d N o i s e I m a g e %
110 % %
111 % %
112 % %
113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114 %
115 % AddNoiseImage() adds random noise to the image.
116 %
117 % The format of the AddNoiseImage method is:
118 %
119 % Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
120 % ExceptionInfo *exception)
121 % Image *AddNoiseImageChannel(const Image *image,const ChannelType channel,
122 % const NoiseType noise_type,ExceptionInfo *exception)
123 %
124 % A description of each parameter follows:
125 %
126 % o image: the image.
127 %
128 % o channel: the channel type.
129 %
130 % o noise_type: The type of noise: Uniform, Gaussian, Multiplicative,
131 % Impulse, Laplacian, or Poisson.
132 %
133 % o exception: return any errors or warnings in this structure.
134 %
135 */
136 MagickExport Image *AddNoiseImage(const Image *image,const NoiseType noise_type,
137  ExceptionInfo *exception)
138 {
139  Image
140  *noise_image;
141 
142  noise_image=AddNoiseImageChannel(image,DefaultChannels,noise_type,exception);
143  return(noise_image);
144 }
145 
146 MagickExport Image *AddNoiseImageChannel(const Image *image,
147  const ChannelType channel,const NoiseType noise_type,ExceptionInfo *exception)
148 {
149 #define AddNoiseImageTag "AddNoise/Image"
150 
151  CacheView
152  *image_view,
153  *noise_view;
154 
155  const char
156  *option;
157 
158  double
159  attenuate;
160 
161  Image
162  *noise_image;
163 
164  MagickBooleanType
165  status;
166 
167  MagickOffsetType
168  progress;
169 
170  RandomInfo
171  **magick_restrict random_info;
172 
173  ssize_t
174  y;
175 
176 #if defined(MAGICKCORE_OPENMP_SUPPORT)
177  unsigned long
178  key;
179 #endif
180 
181  /*
182  Initialize noise image attributes.
183  */
184  assert(image != (const Image *) NULL);
185  assert(image->signature == MagickCoreSignature);
186  assert(exception != (ExceptionInfo *) NULL);
187  assert(exception->signature == MagickCoreSignature);
188  if (IsEventLogging() != MagickFalse)
189  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
190 #if defined(MAGICKCORE_OPENCL_SUPPORT)
191  noise_image=AccelerateAddNoiseImage(image,channel,noise_type,exception);
192  if (noise_image != (Image *) NULL)
193  return(noise_image);
194 #endif
195  noise_image=CloneImage(image,0,0,MagickTrue,exception);
196  if (noise_image == (Image *) NULL)
197  return((Image *) NULL);
198  if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
199  {
200  InheritException(exception,&noise_image->exception);
201  noise_image=DestroyImage(noise_image);
202  return((Image *) NULL);
203  }
204  /*
205  Add noise in each row.
206  */
207  attenuate=1.0;
208  option=GetImageArtifact(image,"attenuate");
209  if (option != (char *) NULL)
210  attenuate=StringToDouble(option,(char **) NULL);
211  status=MagickTrue;
212  progress=0;
213  random_info=AcquireRandomInfoTLS();
214  image_view=AcquireVirtualCacheView(image,exception);
215  noise_view=AcquireAuthenticCacheView(noise_image,exception);
216 #if defined(MAGICKCORE_OPENMP_SUPPORT)
217  key=GetRandomSecretKey(random_info[0]);
218  #pragma omp parallel for schedule(static) shared(progress,status) \
219  magick_number_threads(image,noise_image,image->rows,key == ~0UL)
220 #endif
221  for (y=0; y < (ssize_t) image->rows; y++)
222  {
223  const int
224  id = GetOpenMPThreadId();
225 
226  MagickBooleanType
227  sync;
228 
229  const IndexPacket
230  *magick_restrict indexes;
231 
232  const PixelPacket
233  *magick_restrict p;
234 
235  IndexPacket
236  *magick_restrict noise_indexes;
237 
238  ssize_t
239  x;
240 
242  *magick_restrict q;
243 
244  if (status == MagickFalse)
245  continue;
246  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
247  q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
248  exception);
249  if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
250  {
251  status=MagickFalse;
252  continue;
253  }
254  indexes=GetCacheViewVirtualIndexQueue(image_view);
255  noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
256  for (x=0; x < (ssize_t) image->columns; x++)
257  {
258  if ((channel & RedChannel) != 0)
259  SetPixelRed(q,ClampToQuantum(GenerateDifferentialNoise(random_info[id],
260  GetPixelRed(p),noise_type,attenuate)));
261  if (IsGrayColorspace(image->colorspace) != MagickFalse)
262  {
263  SetPixelGreen(q,GetPixelRed(q));
264  SetPixelBlue(q,GetPixelRed(q));
265  }
266  else
267  {
268  if ((channel & GreenChannel) != 0)
269  SetPixelGreen(q,ClampToQuantum(GenerateDifferentialNoise(
270  random_info[id],GetPixelGreen(p),noise_type,attenuate)));
271  if ((channel & BlueChannel) != 0)
272  SetPixelBlue(q,ClampToQuantum(GenerateDifferentialNoise(
273  random_info[id],GetPixelBlue(p),noise_type,attenuate)));
274  }
275  if ((channel & OpacityChannel) != 0)
276  SetPixelOpacity(q,ClampToQuantum(GenerateDifferentialNoise(
277  random_info[id],GetPixelOpacity(p),noise_type,attenuate)));
278  if (((channel & IndexChannel) != 0) &&
279  (image->colorspace == CMYKColorspace))
280  SetPixelIndex(noise_indexes+x,ClampToQuantum(
281  GenerateDifferentialNoise(random_info[id],GetPixelIndex(
282  indexes+x),noise_type,attenuate)));
283  p++;
284  q++;
285  }
286  sync=SyncCacheViewAuthenticPixels(noise_view,exception);
287  if (sync == MagickFalse)
288  status=MagickFalse;
289  if (image->progress_monitor != (MagickProgressMonitor) NULL)
290  {
291  MagickBooleanType
292  proceed;
293 
294 #if defined(MAGICKCORE_OPENMP_SUPPORT)
295  #pragma omp atomic
296 #endif
297  progress++;
298  proceed=SetImageProgress(image,AddNoiseImageTag,progress,image->rows);
299  if (proceed == MagickFalse)
300  status=MagickFalse;
301  }
302  }
303  noise_view=DestroyCacheView(noise_view);
304  image_view=DestroyCacheView(image_view);
305  random_info=DestroyRandomInfoTLS(random_info);
306  if (status == MagickFalse)
307  noise_image=DestroyImage(noise_image);
308  return(noise_image);
309 }
310 
311 /*
312 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
313 % %
314 % %
315 % %
316 % B l u e S h i f t I m a g e %
317 % %
318 % %
319 % %
320 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
321 %
322 % BlueShiftImage() mutes the colors of the image to simulate a scene at
323 % nighttime in the moonlight.
324 %
325 % The format of the BlueShiftImage method is:
326 %
327 % Image *BlueShiftImage(const Image *image,const double factor,
328 % ExceptionInfo *exception)
329 %
330 % A description of each parameter follows:
331 %
332 % o image: the image.
333 %
334 % o factor: the shift factor.
335 %
336 % o exception: return any errors or warnings in this structure.
337 %
338 */
339 MagickExport Image *BlueShiftImage(const Image *image,const double factor,
340  ExceptionInfo *exception)
341 {
342 #define BlueShiftImageTag "BlueShift/Image"
343 
344  CacheView
345  *image_view,
346  *shift_view;
347 
348  Image
349  *shift_image;
350 
351  MagickBooleanType
352  status;
353 
354  MagickOffsetType
355  progress;
356 
357  ssize_t
358  y;
359 
360  /*
361  Allocate blue shift image.
362  */
363  assert(image != (const Image *) NULL);
364  assert(image->signature == MagickCoreSignature);
365  assert(exception != (ExceptionInfo *) NULL);
366  assert(exception->signature == MagickCoreSignature);
367  if (IsEventLogging() != MagickFalse)
368  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
369  shift_image=CloneImage(image,0,0,MagickTrue,exception);
370  if (shift_image == (Image *) NULL)
371  return((Image *) NULL);
372  if (SetImageStorageClass(shift_image,DirectClass) == MagickFalse)
373  {
374  InheritException(exception,&shift_image->exception);
375  shift_image=DestroyImage(shift_image);
376  return((Image *) NULL);
377  }
378  /*
379  Blue-shift DirectClass image.
380  */
381  status=MagickTrue;
382  progress=0;
383  image_view=AcquireVirtualCacheView(image,exception);
384  shift_view=AcquireAuthenticCacheView(shift_image,exception);
385 #if defined(MAGICKCORE_OPENMP_SUPPORT)
386  #pragma omp parallel for schedule(static) shared(progress,status) \
387  magick_number_threads(image,shift_image,image->rows,1)
388 #endif
389  for (y=0; y < (ssize_t) image->rows; y++)
390  {
391  MagickBooleanType
392  sync;
393 
395  pixel;
396 
397  Quantum
398  quantum;
399 
400  const PixelPacket
401  *magick_restrict p;
402 
403  ssize_t
404  x;
405 
407  *magick_restrict q;
408 
409  if (status == MagickFalse)
410  continue;
411  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
412  q=QueueCacheViewAuthenticPixels(shift_view,0,y,shift_image->columns,1,
413  exception);
414  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
415  {
416  status=MagickFalse;
417  continue;
418  }
419  for (x=0; x < (ssize_t) image->columns; x++)
420  {
421  quantum=GetPixelRed(p);
422  if (GetPixelGreen(p) < quantum)
423  quantum=GetPixelGreen(p);
424  if (GetPixelBlue(p) < quantum)
425  quantum=GetPixelBlue(p);
426  pixel.red=0.5*(GetPixelRed(p)+factor*quantum);
427  pixel.green=0.5*(GetPixelGreen(p)+factor*quantum);
428  pixel.blue=0.5*(GetPixelBlue(p)+factor*quantum);
429  quantum=GetPixelRed(p);
430  if (GetPixelGreen(p) > quantum)
431  quantum=GetPixelGreen(p);
432  if (GetPixelBlue(p) > quantum)
433  quantum=GetPixelBlue(p);
434  pixel.red=0.5*(pixel.red+factor*quantum);
435  pixel.green=0.5*(pixel.green+factor*quantum);
436  pixel.blue=0.5*(pixel.blue+factor*quantum);
437  SetPixelRed(q,ClampToQuantum(pixel.red));
438  SetPixelGreen(q,ClampToQuantum(pixel.green));
439  SetPixelBlue(q,ClampToQuantum(pixel.blue));
440  p++;
441  q++;
442  }
443  sync=SyncCacheViewAuthenticPixels(shift_view,exception);
444  if (sync == MagickFalse)
445  status=MagickFalse;
446  if (image->progress_monitor != (MagickProgressMonitor) NULL)
447  {
448  MagickBooleanType
449  proceed;
450 
451 #if defined(MAGICKCORE_OPENMP_SUPPORT)
452  #pragma omp atomic
453 #endif
454  progress++;
455  proceed=SetImageProgress(image,BlueShiftImageTag,progress,image->rows);
456  if (proceed == MagickFalse)
457  status=MagickFalse;
458  }
459  }
460  image_view=DestroyCacheView(image_view);
461  shift_view=DestroyCacheView(shift_view);
462  if (status == MagickFalse)
463  shift_image=DestroyImage(shift_image);
464  return(shift_image);
465 }
466 
467 /*
468 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469 % %
470 % %
471 % %
472 % C h a r c o a l I m a g e %
473 % %
474 % %
475 % %
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477 %
478 % CharcoalImage() creates a new image that is a copy of an existing one with
479 % the edge highlighted. It allocates the memory necessary for the new Image
480 % structure and returns a pointer to the new image.
481 %
482 % The format of the CharcoalImage method is:
483 %
484 % Image *CharcoalImage(const Image *image,const double radius,
485 % const double sigma,ExceptionInfo *exception)
486 %
487 % A description of each parameter follows:
488 %
489 % o image: the image.
490 %
491 % o radius: the radius of the pixel neighborhood.
492 %
493 % o sigma: the standard deviation of the Gaussian, in pixels.
494 %
495 % o exception: return any errors or warnings in this structure.
496 %
497 */
498 MagickExport Image *CharcoalImage(const Image *image,const double radius,
499  const double sigma,ExceptionInfo *exception)
500 {
501  Image
502  *charcoal_image,
503  *edge_image;
504 
505  MagickBooleanType
506  status;
507 
508  assert(image != (Image *) NULL);
509  assert(image->signature == MagickCoreSignature);
510  assert(exception != (ExceptionInfo *) NULL);
511  assert(exception->signature == MagickCoreSignature);
512  if (IsEventLogging() != MagickFalse)
513  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
514  edge_image=EdgeImage(image,radius,exception);
515  if (edge_image == (Image *) NULL)
516  return((Image *) NULL);
517  charcoal_image=(Image *) NULL;
518  status=ClampImage(edge_image);
519  if (status != MagickFalse)
520  charcoal_image=BlurImage(edge_image,radius,sigma,exception);
521  edge_image=DestroyImage(edge_image);
522  if (charcoal_image == (Image *) NULL)
523  return((Image *) NULL);
524  status=NormalizeImage(charcoal_image);
525  if (status != MagickFalse)
526  status=NegateImage(charcoal_image,MagickFalse);
527  if (status != MagickFalse)
528  status=GrayscaleImage(charcoal_image,image->intensity);
529  if (status == MagickFalse)
530  charcoal_image=DestroyImage(charcoal_image);
531  return(charcoal_image);
532 }
533 
534 /*
535 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
536 % %
537 % %
538 % %
539 % C o l o r i z e I m a g e %
540 % %
541 % %
542 % %
543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
544 %
545 % ColorizeImage() blends the fill color with each pixel in the image.
546 % A percentage blend is specified with opacity. Control the application
547 % of different color components by specifying a different percentage for
548 % each component (e.g. 90/100/10 is 90% red, 100% green, and 10% blue).
549 %
550 % The format of the ColorizeImage method is:
551 %
552 % Image *ColorizeImage(const Image *image,const char *opacity,
553 % const PixelPacket colorize,ExceptionInfo *exception)
554 %
555 % A description of each parameter follows:
556 %
557 % o image: the image.
558 %
559 % o opacity: A character string indicating the level of opacity as a
560 % percentage.
561 %
562 % o colorize: A color value.
563 %
564 % o exception: return any errors or warnings in this structure.
565 %
566 */
567 MagickExport Image *ColorizeImage(const Image *image,const char *opacity,
568  const PixelPacket colorize,ExceptionInfo *exception)
569 {
570 #define ColorizeImageTag "Colorize/Image"
571 
572  CacheView
573  *colorize_view,
574  *image_view;
575 
577  geometry_info;
578 
579  Image
580  *colorize_image;
581 
582  MagickBooleanType
583  status;
584 
585  MagickOffsetType
586  progress;
587 
589  pixel;
590 
591  MagickStatusType
592  flags;
593 
594  ssize_t
595  y;
596 
597  /*
598  Allocate colorized image.
599  */
600  assert(image != (const Image *) NULL);
601  assert(image->signature == MagickCoreSignature);
602  assert(exception != (ExceptionInfo *) NULL);
603  assert(exception->signature == MagickCoreSignature);
604  if (IsEventLogging() != MagickFalse)
605  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
606  colorize_image=CloneImage(image,0,0,MagickTrue,exception);
607  if (colorize_image == (Image *) NULL)
608  return((Image *) NULL);
609  if (SetImageStorageClass(colorize_image,DirectClass) == MagickFalse)
610  {
611  InheritException(exception,&colorize_image->exception);
612  colorize_image=DestroyImage(colorize_image);
613  return((Image *) NULL);
614  }
615  if ((IsGrayColorspace(image->colorspace) != MagickFalse) ||
616  (IsPixelGray(&colorize) != MagickFalse))
617  (void) SetImageColorspace(colorize_image,sRGBColorspace);
618  if ((colorize_image->matte == MagickFalse) &&
619  (colorize.opacity != OpaqueOpacity))
620  (void) SetImageAlphaChannel(colorize_image,OpaqueAlphaChannel);
621  if (opacity == (const char *) NULL)
622  return(colorize_image);
623  /*
624  Determine RGB values of the pen color.
625  */
626  flags=ParseGeometry(opacity,&geometry_info);
627  pixel.red=geometry_info.rho;
628  pixel.green=geometry_info.rho;
629  pixel.blue=geometry_info.rho;
630  pixel.opacity=(MagickRealType) OpaqueOpacity;
631  if ((flags & SigmaValue) != 0)
632  pixel.green=geometry_info.sigma;
633  if ((flags & XiValue) != 0)
634  pixel.blue=geometry_info.xi;
635  if ((flags & PsiValue) != 0)
636  pixel.opacity=geometry_info.psi;
637  /*
638  Colorize DirectClass image.
639  */
640  status=MagickTrue;
641  progress=0;
642  image_view=AcquireVirtualCacheView(image,exception);
643  colorize_view=AcquireAuthenticCacheView(colorize_image,exception);
644 #if defined(MAGICKCORE_OPENMP_SUPPORT)
645  #pragma omp parallel for schedule(static) shared(progress,status) \
646  magick_number_threads(image,colorize_image,image->rows,1)
647 #endif
648  for (y=0; y < (ssize_t) image->rows; y++)
649  {
650  MagickBooleanType
651  sync;
652 
653  const PixelPacket
654  *magick_restrict p;
655 
656  ssize_t
657  x;
658 
660  *magick_restrict q;
661 
662  if (status == MagickFalse)
663  continue;
664  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
665  q=QueueCacheViewAuthenticPixels(colorize_view,0,y,colorize_image->columns,1,
666  exception);
667  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
668  {
669  status=MagickFalse;
670  continue;
671  }
672  for (x=0; x < (ssize_t) image->columns; x++)
673  {
674  SetPixelRed(q,((GetPixelRed(p)*(100.0-pixel.red)+
675  colorize.red*pixel.red)/100.0));
676  SetPixelGreen(q,((GetPixelGreen(p)*(100.0-pixel.green)+
677  colorize.green*pixel.green)/100.0));
678  SetPixelBlue(q,((GetPixelBlue(p)*(100.0-pixel.blue)+
679  colorize.blue*pixel.blue)/100.0));
680  if (colorize_image->matte == MagickFalse)
681  SetPixelOpacity(q,GetPixelOpacity(p));
682  else
683  SetPixelOpacity(q,((GetPixelOpacity(p)*(100.0-pixel.opacity)+
684  colorize.opacity*pixel.opacity)/100.0));
685  p++;
686  q++;
687  }
688  sync=SyncCacheViewAuthenticPixels(colorize_view,exception);
689  if (sync == MagickFalse)
690  status=MagickFalse;
691  if (image->progress_monitor != (MagickProgressMonitor) NULL)
692  {
693  MagickBooleanType
694  proceed;
695 
696 #if defined(MAGICKCORE_OPENMP_SUPPORT)
697  #pragma omp atomic
698 #endif
699  progress++;
700  proceed=SetImageProgress(image,ColorizeImageTag,progress,image->rows);
701  if (proceed == MagickFalse)
702  status=MagickFalse;
703  }
704  }
705  image_view=DestroyCacheView(image_view);
706  colorize_view=DestroyCacheView(colorize_view);
707  if (status == MagickFalse)
708  colorize_image=DestroyImage(colorize_image);
709  return(colorize_image);
710 }
711 
712 /*
713 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
714 % %
715 % %
716 % %
717 % C o l o r M a t r i x I m a g e %
718 % %
719 % %
720 % %
721 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
722 %
723 % ColorMatrixImage() applies color transformation to an image. This method
724 % permits saturation changes, hue rotation, luminance to alpha, and various
725 % other effects. Although variable-sized transformation matrices can be used,
726 % typically one uses a 5x5 matrix for an RGBA image and a 6x6 for CMYKA
727 % (or RGBA with offsets). The matrix is similar to those used by Adobe Flash
728 % except offsets are in column 6 rather than 5 (in support of CMYKA images)
729 % and offsets are normalized (divide Flash offset by 255).
730 %
731 % The format of the ColorMatrixImage method is:
732 %
733 % Image *ColorMatrixImage(const Image *image,
734 % const KernelInfo *color_matrix,ExceptionInfo *exception)
735 %
736 % A description of each parameter follows:
737 %
738 % o image: the image.
739 %
740 % o color_matrix: the color matrix.
741 %
742 % o exception: return any errors or warnings in this structure.
743 %
744 */
745 MagickExport Image *ColorMatrixImage(const Image *image,
746  const KernelInfo *color_matrix,ExceptionInfo *exception)
747 {
748 #define ColorMatrixImageTag "ColorMatrix/Image"
749 
750  CacheView
751  *color_view,
752  *image_view;
753 
754  double
755  ColorMatrix[6][6] =
756  {
757  { 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 },
758  { 0.0, 1.0, 0.0, 0.0, 0.0, 0.0 },
759  { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 },
760  { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
761  { 0.0, 0.0, 0.0, 0.0, 1.0, 0.0 },
762  { 0.0, 0.0, 0.0, 0.0, 0.0, 1.0 }
763  };
764 
765  Image
766  *color_image;
767 
768  MagickBooleanType
769  status;
770 
771  MagickOffsetType
772  progress;
773 
774  ssize_t
775  i;
776 
777  ssize_t
778  u,
779  v,
780  y;
781 
782  /*
783  Create color matrix.
784  */
785  assert(image != (Image *) NULL);
786  assert(image->signature == MagickCoreSignature);
787  assert(exception != (ExceptionInfo *) NULL);
788  assert(exception->signature == MagickCoreSignature);
789  if (IsEventLogging() != MagickFalse)
790  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
791  i=0;
792  for (v=0; v < (ssize_t) color_matrix->height; v++)
793  for (u=0; u < (ssize_t) color_matrix->width; u++)
794  {
795  if ((v < 6) && (u < 6))
796  ColorMatrix[v][u]=color_matrix->values[i];
797  i++;
798  }
799  /*
800  Initialize color image.
801  */
802  color_image=CloneImage(image,0,0,MagickTrue,exception);
803  if (color_image == (Image *) NULL)
804  return((Image *) NULL);
805  if (SetImageStorageClass(color_image,DirectClass) == MagickFalse)
806  {
807  InheritException(exception,&color_image->exception);
808  color_image=DestroyImage(color_image);
809  return((Image *) NULL);
810  }
811  if (image->debug != MagickFalse)
812  {
813  char
814  format[MaxTextExtent],
815  *message;
816 
817  (void) LogMagickEvent(TransformEvent,GetMagickModule(),
818  " ColorMatrix image with color matrix:");
819  message=AcquireString("");
820  for (v=0; v < 6; v++)
821  {
822  *message='\0';
823  (void) FormatLocaleString(format,MaxTextExtent,"%.20g: ",(double) v);
824  (void) ConcatenateString(&message,format);
825  for (u=0; u < 6; u++)
826  {
827  (void) FormatLocaleString(format,MaxTextExtent,"%+f ",
828  ColorMatrix[v][u]);
829  (void) ConcatenateString(&message,format);
830  }
831  (void) LogMagickEvent(TransformEvent,GetMagickModule(),"%s",message);
832  }
833  message=DestroyString(message);
834  }
835  /*
836  ColorMatrix image.
837  */
838  status=MagickTrue;
839  progress=0;
840  image_view=AcquireVirtualCacheView(image,exception);
841  color_view=AcquireAuthenticCacheView(color_image,exception);
842 #if defined(MAGICKCORE_OPENMP_SUPPORT)
843  #pragma omp parallel for schedule(static) shared(progress,status) \
844  magick_number_threads(image,color_image,image->rows,1)
845 #endif
846  for (y=0; y < (ssize_t) image->rows; y++)
847  {
848  MagickRealType
849  pixel;
850 
851  const IndexPacket
852  *magick_restrict indexes;
853 
854  const PixelPacket
855  *magick_restrict p;
856 
857  ssize_t
858  x;
859 
860  IndexPacket
861  *magick_restrict color_indexes;
862 
864  *magick_restrict q;
865 
866  if (status == MagickFalse)
867  continue;
868  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
869  q=GetCacheViewAuthenticPixels(color_view,0,y,color_image->columns,1,
870  exception);
871  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
872  {
873  status=MagickFalse;
874  continue;
875  }
876  indexes=GetCacheViewVirtualIndexQueue(image_view);
877  color_indexes=GetCacheViewAuthenticIndexQueue(color_view);
878  for (x=0; x < (ssize_t) image->columns; x++)
879  {
880  ssize_t
881  v;
882 
883  size_t
884  height;
885 
886  height=color_matrix->height > 6 ? 6UL : color_matrix->height;
887  for (v=0; v < (ssize_t) height; v++)
888  {
889  pixel=ColorMatrix[v][0]*GetPixelRed(p)+ColorMatrix[v][1]*
890  GetPixelGreen(p)+ColorMatrix[v][2]*GetPixelBlue(p);
891  if (image->matte != MagickFalse)
892  pixel+=ColorMatrix[v][3]*(QuantumRange-GetPixelOpacity(p));
893  if (image->colorspace == CMYKColorspace)
894  pixel+=ColorMatrix[v][4]*GetPixelIndex(indexes+x);
895  pixel+=QuantumRange*ColorMatrix[v][5];
896  switch (v)
897  {
898  case 0: SetPixelRed(q,ClampToQuantum(pixel)); break;
899  case 1: SetPixelGreen(q,ClampToQuantum(pixel)); break;
900  case 2: SetPixelBlue(q,ClampToQuantum(pixel)); break;
901  case 3:
902  {
903  if (image->matte != MagickFalse)
904  SetPixelAlpha(q,ClampToQuantum(pixel));
905  break;
906  }
907  case 4:
908  {
909  if (image->colorspace == CMYKColorspace)
910  SetPixelIndex(color_indexes+x,ClampToQuantum(pixel));
911  break;
912  }
913  }
914  }
915  p++;
916  q++;
917  }
918  if (SyncCacheViewAuthenticPixels(color_view,exception) == MagickFalse)
919  status=MagickFalse;
920  if (image->progress_monitor != (MagickProgressMonitor) NULL)
921  {
922  MagickBooleanType
923  proceed;
924 
925 #if defined(MAGICKCORE_OPENMP_SUPPORT)
926  #pragma omp atomic
927 #endif
928  progress++;
929  proceed=SetImageProgress(image,ColorMatrixImageTag,progress,
930  image->rows);
931  if (proceed == MagickFalse)
932  status=MagickFalse;
933  }
934  }
935  color_view=DestroyCacheView(color_view);
936  image_view=DestroyCacheView(image_view);
937  if (status == MagickFalse)
938  color_image=DestroyImage(color_image);
939  return(color_image);
940 }
941 
942 /*
943 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
944 % %
945 % %
946 % %
947 % I m p l o d e I m a g e %
948 % %
949 % %
950 % %
951 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
952 %
953 % ImplodeImage() creates a new image that is a copy of an existing
954 % one with the image pixels "implode" by the specified percentage. It
955 % allocates the memory necessary for the new Image structure and returns a
956 % pointer to the new image.
957 %
958 % The format of the ImplodeImage method is:
959 %
960 % Image *ImplodeImage(const Image *image,const double amount,
961 % ExceptionInfo *exception)
962 %
963 % A description of each parameter follows:
964 %
965 % o implode_image: Method ImplodeImage returns a pointer to the image
966 % after it is implode. A null image is returned if there is a memory
967 % shortage.
968 %
969 % o image: the image.
970 %
971 % o amount: Define the extent of the implosion.
972 %
973 % o exception: return any errors or warnings in this structure.
974 %
975 */
976 MagickExport Image *ImplodeImage(const Image *image,const double amount,
977  ExceptionInfo *exception)
978 {
979 #define ImplodeImageTag "Implode/Image"
980 
981  CacheView
982  *image_view,
983  *implode_view;
984 
985  double
986  radius;
987 
988  Image
989  *implode_image;
990 
991  MagickBooleanType
992  status;
993 
994  MagickOffsetType
995  progress;
996 
998  zero;
999 
1000  PointInfo
1001  center,
1002  scale;
1003 
1004  ssize_t
1005  y;
1006 
1007  /*
1008  Initialize implode image attributes.
1009  */
1010  assert(image != (Image *) NULL);
1011  assert(image->signature == MagickCoreSignature);
1012  assert(exception != (ExceptionInfo *) NULL);
1013  assert(exception->signature == MagickCoreSignature);
1014  if (IsEventLogging() != MagickFalse)
1015  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1016  implode_image=CloneImage(image,0,0,MagickTrue,exception);
1017  if (implode_image == (Image *) NULL)
1018  return((Image *) NULL);
1019  if (SetImageStorageClass(implode_image,DirectClass) == MagickFalse)
1020  {
1021  InheritException(exception,&implode_image->exception);
1022  implode_image=DestroyImage(implode_image);
1023  return((Image *) NULL);
1024  }
1025  if (implode_image->background_color.opacity != OpaqueOpacity)
1026  implode_image->matte=MagickTrue;
1027  /*
1028  Compute scaling factor.
1029  */
1030  scale.x=1.0;
1031  scale.y=1.0;
1032  center.x=0.5*image->columns;
1033  center.y=0.5*image->rows;
1034  radius=center.x;
1035  if (image->columns > image->rows)
1036  scale.y=(double) image->columns*PerceptibleReciprocal((double)
1037  image->rows);
1038  else
1039  if (image->columns < image->rows)
1040  {
1041  scale.x=(double) image->rows*PerceptibleReciprocal((double)
1042  image->columns);
1043  radius=center.y;
1044  }
1045  /*
1046  Implode image.
1047  */
1048  status=MagickTrue;
1049  progress=0;
1050  GetMagickPixelPacket(implode_image,&zero);
1051  image_view=AcquireVirtualCacheView(image,exception);
1052  implode_view=AcquireAuthenticCacheView(implode_image,exception);
1053 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1054  #pragma omp parallel for schedule(static) shared(progress,status) \
1055  magick_number_threads(image,implode_image,image->rows,1)
1056 #endif
1057  for (y=0; y < (ssize_t) image->rows; y++)
1058  {
1059  double
1060  distance;
1061 
1063  pixel;
1064 
1065  PointInfo
1066  delta;
1067 
1068  IndexPacket
1069  *magick_restrict implode_indexes;
1070 
1071  ssize_t
1072  x;
1073 
1074  PixelPacket
1075  *magick_restrict q;
1076 
1077  if (status == MagickFalse)
1078  continue;
1079  q=GetCacheViewAuthenticPixels(implode_view,0,y,implode_image->columns,1,
1080  exception);
1081  if (q == (PixelPacket *) NULL)
1082  {
1083  status=MagickFalse;
1084  continue;
1085  }
1086  implode_indexes=GetCacheViewAuthenticIndexQueue(implode_view);
1087  delta.y=scale.y*(double) (y-center.y);
1088  pixel=zero;
1089  for (x=0; x < (ssize_t) image->columns; x++)
1090  {
1091  /*
1092  Determine if the pixel is within an ellipse.
1093  */
1094  delta.x=scale.x*(double) (x-center.x);
1095  distance=delta.x*delta.x+delta.y*delta.y;
1096  if (distance < (radius*radius))
1097  {
1098  double
1099  factor;
1100 
1101  /*
1102  Implode the pixel.
1103  */
1104  factor=1.0;
1105  if (distance > 0.0)
1106  factor=pow(sin((double) (MagickPI*sqrt((double) distance)*
1107  PerceptibleReciprocal(radius)/2)),-amount);
1108  status=InterpolateMagickPixelPacket(image,image_view,
1109  UndefinedInterpolatePixel,(double) (factor*delta.x*PerceptibleReciprocal(scale.x)+
1110  center.x),(double) (factor*delta.y*PerceptibleReciprocal(scale.y)+center.y),&pixel,
1111  exception);
1112  if (status == MagickFalse)
1113  break;
1114  SetPixelPacket(implode_image,&pixel,q,implode_indexes+x);
1115  }
1116  q++;
1117  }
1118  if (SyncCacheViewAuthenticPixels(implode_view,exception) == MagickFalse)
1119  status=MagickFalse;
1120  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1121  {
1122  MagickBooleanType
1123  proceed;
1124 
1125 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1126  #pragma omp atomic
1127 #endif
1128  progress++;
1129  proceed=SetImageProgress(image,ImplodeImageTag,progress,image->rows);
1130  if (proceed == MagickFalse)
1131  status=MagickFalse;
1132  }
1133  }
1134  implode_view=DestroyCacheView(implode_view);
1135  image_view=DestroyCacheView(image_view);
1136  if (status == MagickFalse)
1137  implode_image=DestroyImage(implode_image);
1138  return(implode_image);
1139 }
1140 
1141 /*
1142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1143 % %
1144 % %
1145 % %
1146 % M o r p h I m a g e s %
1147 % %
1148 % %
1149 % %
1150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1151 %
1152 % The MorphImages() method requires a minimum of two images. The first
1153 % image is transformed into the second by a number of intervening images
1154 % as specified by frames.
1155 %
1156 % The format of the MorphImage method is:
1157 %
1158 % Image *MorphImages(const Image *image,const size_t number_frames,
1159 % ExceptionInfo *exception)
1160 %
1161 % A description of each parameter follows:
1162 %
1163 % o image: the image.
1164 %
1165 % o number_frames: Define the number of in-between image to generate.
1166 % The more in-between frames, the smoother the morph.
1167 %
1168 % o exception: return any errors or warnings in this structure.
1169 %
1170 */
1171 MagickExport Image *MorphImages(const Image *image,
1172  const size_t number_frames,ExceptionInfo *exception)
1173 {
1174 #define MorphImageTag "Morph/Image"
1175 
1176  double
1177  alpha,
1178  beta;
1179 
1180  Image
1181  *morph_image,
1182  *morph_images;
1183 
1184  MagickBooleanType
1185  status;
1186 
1187  MagickOffsetType
1188  scene;
1189 
1190  const Image
1191  *next;
1192 
1193  ssize_t
1194  i;
1195 
1196  ssize_t
1197  y;
1198 
1199  /*
1200  Clone first frame in sequence.
1201  */
1202  assert(image != (Image *) NULL);
1203  assert(image->signature == MagickCoreSignature);
1204  assert(exception != (ExceptionInfo *) NULL);
1205  assert(exception->signature == MagickCoreSignature);
1206  if (IsEventLogging() != MagickFalse)
1207  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1208  morph_images=CloneImage(image,0,0,MagickTrue,exception);
1209  if (morph_images == (Image *) NULL)
1210  return((Image *) NULL);
1211  if (GetNextImageInList(image) == (Image *) NULL)
1212  {
1213  /*
1214  Morph single image.
1215  */
1216  for (i=1; i < (ssize_t) number_frames; i++)
1217  {
1218  morph_image=CloneImage(image,0,0,MagickTrue,exception);
1219  if (morph_image == (Image *) NULL)
1220  {
1221  morph_images=DestroyImageList(morph_images);
1222  return((Image *) NULL);
1223  }
1224  AppendImageToList(&morph_images,morph_image);
1225  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1226  {
1227  MagickBooleanType
1228  proceed;
1229 
1230  proceed=SetImageProgress(image,MorphImageTag,(MagickOffsetType) i,
1231  number_frames);
1232  if (proceed == MagickFalse)
1233  status=MagickFalse;
1234  }
1235  }
1236  return(GetFirstImageInList(morph_images));
1237  }
1238  /*
1239  Morph image sequence.
1240  */
1241  status=MagickTrue;
1242  scene=0;
1243  next=image;
1244  for ( ; GetNextImageInList(next) != (Image *) NULL; next=GetNextImageInList(next))
1245  {
1246  for (i=0; i < (ssize_t) number_frames; i++)
1247  {
1248  CacheView
1249  *image_view,
1250  *morph_view;
1251 
1252  beta=(double) (i+1.0)/(double) (number_frames+1.0);
1253  alpha=1.0-beta;
1254  morph_image=ResizeImage(next,(size_t) (alpha*next->columns+beta*
1255  GetNextImageInList(next)->columns+0.5),(size_t) (alpha*
1256  next->rows+beta*GetNextImageInList(next)->rows+0.5),
1257  next->filter,next->blur,exception);
1258  if (morph_image == (Image *) NULL)
1259  {
1260  morph_images=DestroyImageList(morph_images);
1261  return((Image *) NULL);
1262  }
1263  if (SetImageStorageClass(morph_image,DirectClass) == MagickFalse)
1264  {
1265  InheritException(exception,&morph_image->exception);
1266  morph_image=DestroyImage(morph_image);
1267  return((Image *) NULL);
1268  }
1269  AppendImageToList(&morph_images,morph_image);
1270  morph_images=GetLastImageInList(morph_images);
1271  morph_image=ResizeImage(GetNextImageInList(next),morph_images->columns,
1272  morph_images->rows,GetNextImageInList(next)->filter,
1273  GetNextImageInList(next)->blur,exception);
1274  if (morph_image == (Image *) NULL)
1275  {
1276  morph_images=DestroyImageList(morph_images);
1277  return((Image *) NULL);
1278  }
1279  image_view=AcquireVirtualCacheView(morph_image,exception);
1280  morph_view=AcquireAuthenticCacheView(morph_images,exception);
1281 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1282  #pragma omp parallel for schedule(static) shared(status) \
1283  magick_number_threads(morph_image,morph_image,morph_image->rows,1)
1284 #endif
1285  for (y=0; y < (ssize_t) morph_images->rows; y++)
1286  {
1287  MagickBooleanType
1288  sync;
1289 
1290  const PixelPacket
1291  *magick_restrict p;
1292 
1293  ssize_t
1294  x;
1295 
1296  PixelPacket
1297  *magick_restrict q;
1298 
1299  if (status == MagickFalse)
1300  continue;
1301  p=GetCacheViewVirtualPixels(image_view,0,y,morph_image->columns,1,
1302  exception);
1303  q=GetCacheViewAuthenticPixels(morph_view,0,y,morph_images->columns,1,
1304  exception);
1305  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1306  {
1307  status=MagickFalse;
1308  continue;
1309  }
1310  for (x=0; x < (ssize_t) morph_images->columns; x++)
1311  {
1312  SetPixelRed(q,ClampToQuantum(alpha*
1313  GetPixelRed(q)+beta*GetPixelRed(p)));
1314  SetPixelGreen(q,ClampToQuantum(alpha*
1315  GetPixelGreen(q)+beta*GetPixelGreen(p)));
1316  SetPixelBlue(q,ClampToQuantum(alpha*
1317  GetPixelBlue(q)+beta*GetPixelBlue(p)));
1318  SetPixelOpacity(q,ClampToQuantum(alpha*
1319  GetPixelOpacity(q)+beta*GetPixelOpacity(p)));
1320  p++;
1321  q++;
1322  }
1323  sync=SyncCacheViewAuthenticPixels(morph_view,exception);
1324  if (sync == MagickFalse)
1325  status=MagickFalse;
1326  }
1327  morph_view=DestroyCacheView(morph_view);
1328  image_view=DestroyCacheView(image_view);
1329  morph_image=DestroyImage(morph_image);
1330  }
1331  if (i < (ssize_t) number_frames)
1332  break;
1333  /*
1334  Clone last frame in sequence.
1335  */
1336  morph_image=CloneImage(GetNextImageInList(next),0,0,MagickTrue,exception);
1337  if (morph_image == (Image *) NULL)
1338  {
1339  morph_images=DestroyImageList(morph_images);
1340  return((Image *) NULL);
1341  }
1342  AppendImageToList(&morph_images,morph_image);
1343  morph_images=GetLastImageInList(morph_images);
1344  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1345  {
1346  MagickBooleanType
1347  proceed;
1348 
1349  proceed=SetImageProgress(image,MorphImageTag,scene,
1350  GetImageListLength(image));
1351  if (proceed == MagickFalse)
1352  status=MagickFalse;
1353  }
1354  scene++;
1355  }
1356  if (GetNextImageInList(next) != (Image *) NULL)
1357  {
1358  morph_images=DestroyImageList(morph_images);
1359  return((Image *) NULL);
1360  }
1361  return(GetFirstImageInList(morph_images));
1362 }
1363 
1364 /*
1365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1366 % %
1367 % %
1368 % %
1369 % P l a s m a I m a g e %
1370 % %
1371 % %
1372 % %
1373 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1374 %
1375 % PlasmaImage() initializes an image with plasma fractal values. The image
1376 % must be initialized with a base color and the random number generator
1377 % seeded before this method is called.
1378 %
1379 % The format of the PlasmaImage method is:
1380 %
1381 % MagickBooleanType PlasmaImage(Image *image,const SegmentInfo *segment,
1382 % size_t attenuate,size_t depth)
1383 %
1384 % A description of each parameter follows:
1385 %
1386 % o image: the image.
1387 %
1388 % o segment: Define the region to apply plasma fractals values.
1389 %
1390 % o attenuate: Define the plasma attenuation factor.
1391 %
1392 % o depth: Limit the plasma recursion depth.
1393 %
1394 */
1395 
1396 static inline Quantum PlasmaPixel(RandomInfo *magick_restrict random_info,
1397  const MagickRealType pixel,const double noise)
1398 {
1399  MagickRealType
1400  plasma;
1401 
1402  plasma=pixel+noise*GetPseudoRandomValue(random_info)-noise/2.0;
1403  return(ClampToQuantum(plasma));
1404 }
1405 
1406 MagickExport MagickBooleanType PlasmaImageProxy(Image *image,
1407  CacheView *image_view,CacheView *u_view,CacheView *v_view,
1408  RandomInfo *magick_restrict random_info,
1409  const SegmentInfo *magick_restrict segment,size_t attenuate,size_t depth)
1410 {
1412  *exception;
1413 
1414  double
1415  plasma;
1416 
1417  MagickStatusType
1418  status;
1419 
1420  PixelPacket
1421  u,
1422  v;
1423 
1424  ssize_t
1425  x,
1426  x_mid,
1427  y,
1428  y_mid;
1429 
1430  if ((fabs(segment->x2-segment->x1) < MagickEpsilon) &&
1431  (fabs(segment->y2-segment->y1) < MagickEpsilon))
1432  return(MagickTrue);
1433  if (depth != 0)
1434  {
1435  SegmentInfo
1436  local_info;
1437 
1438  /*
1439  Divide the area into quadrants and recurse.
1440  */
1441  depth--;
1442  attenuate++;
1443  x_mid=CastDoubleToLong(ceil((segment->x1+segment->x2)/2-0.5));
1444  y_mid=CastDoubleToLong(ceil((segment->y1+segment->y2)/2-0.5));
1445  local_info=(*segment);
1446  local_info.x2=(double) x_mid;
1447  local_info.y2=(double) y_mid;
1448  status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
1449  &local_info,attenuate,depth);
1450  local_info=(*segment);
1451  local_info.y1=(double) y_mid;
1452  local_info.x2=(double) x_mid;
1453  status&=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
1454  &local_info,attenuate,depth);
1455  local_info=(*segment);
1456  local_info.x1=(double) x_mid;
1457  local_info.y2=(double) y_mid;
1458  status&=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
1459  &local_info,attenuate,depth);
1460  local_info=(*segment);
1461  local_info.x1=(double) x_mid;
1462  local_info.y1=(double) y_mid;
1463  status&=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,
1464  &local_info,attenuate,depth);
1465  return(status == 0 ? MagickFalse : MagickTrue);
1466  }
1467  x_mid=CastDoubleToLong(ceil((segment->x1+segment->x2)/2-0.5));
1468  y_mid=CastDoubleToLong(ceil((segment->y1+segment->y2)/2-0.5));
1469  if ((fabs(segment->x1-x_mid) < MagickEpsilon) &&
1470  (fabs(segment->x2-x_mid) < MagickEpsilon) &&
1471  (fabs(segment->y1-y_mid) < MagickEpsilon) &&
1472  (fabs(segment->y2-y_mid) < MagickEpsilon))
1473  return(MagickFalse);
1474  /*
1475  Average pixels and apply plasma.
1476  */
1477  status=MagickTrue;
1478  exception=(&image->exception);
1479  plasma=(double) QuantumRange/(2.0*attenuate);
1480  if ((fabs(segment->x1-x_mid) >= MagickEpsilon) ||
1481  (fabs(segment->x2-x_mid) >= MagickEpsilon))
1482  {
1483  PixelPacket
1484  *magick_restrict q;
1485 
1486  /*
1487  Left pixel.
1488  */
1489  x=CastDoubleToLong(ceil(segment->x1-0.5));
1490  (void) GetOneCacheViewVirtualPixel(u_view,x,CastDoubleToLong(ceil(
1491  segment->y1-0.5)),&u,exception);
1492  (void) GetOneCacheViewVirtualPixel(v_view,x,CastDoubleToLong(ceil(
1493  segment->y2-0.5)),&v,exception);
1494  q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
1495  if (q == (PixelPacket *) NULL)
1496  return(MagickTrue);
1497  SetPixelRed(q,PlasmaPixel(random_info,((MagickRealType) u.red+
1498  v.red)/2.0,plasma));
1499  SetPixelGreen(q,PlasmaPixel(random_info,((MagickRealType) u.green+
1500  v.green)/2.0,plasma));
1501  SetPixelBlue(q,PlasmaPixel(random_info,((MagickRealType) u.blue+
1502  v.blue)/2.0,plasma));
1503  status=SyncCacheViewAuthenticPixels(image_view,exception);
1504  if (fabs(segment->x1-segment->x2) >= MagickEpsilon)
1505  {
1506  /*
1507  Right pixel.
1508  */
1509  x=CastDoubleToLong(ceil(segment->x2-0.5));
1510  (void) GetOneCacheViewVirtualPixel(u_view,x,CastDoubleToLong(ceil(
1511  segment->y1-0.5)),&u,exception);
1512  (void) GetOneCacheViewVirtualPixel(v_view,x,CastDoubleToLong(ceil(
1513  segment->y2-0.5)),&v,exception);
1514  q=QueueCacheViewAuthenticPixels(image_view,x,y_mid,1,1,exception);
1515  if (q == (PixelPacket *) NULL)
1516  return(MagickFalse);
1517  SetPixelRed(q,PlasmaPixel(random_info,((MagickRealType) u.red+
1518  v.red)/2.0,plasma));
1519  SetPixelGreen(q,PlasmaPixel(random_info,((MagickRealType) u.green+
1520  v.green)/2.0,plasma));
1521  SetPixelBlue(q,PlasmaPixel(random_info,((MagickRealType) u.blue+
1522  v.blue)/2.0,plasma));
1523  status=SyncCacheViewAuthenticPixels(image_view,exception);
1524  }
1525  }
1526  if ((fabs(segment->y1-y_mid) >= MagickEpsilon) ||
1527  (fabs(segment->y2-y_mid) >= MagickEpsilon))
1528  {
1529  if ((fabs(segment->x1-x_mid) >= MagickEpsilon) ||
1530  (fabs(segment->y2-y_mid) >= MagickEpsilon))
1531  {
1532  PixelPacket
1533  *magick_restrict q;
1534 
1535  /*
1536  Bottom pixel.
1537  */
1538  y=CastDoubleToLong(ceil(segment->y2-0.5));
1539  (void) GetOneCacheViewVirtualPixel(u_view,CastDoubleToLong(ceil(
1540  segment->x1-0.5)),y,&u,exception);
1541  (void) GetOneCacheViewVirtualPixel(v_view,CastDoubleToLong(ceil(
1542  segment->x2-0.5)),y,&v,exception);
1543  q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
1544  if (q == (PixelPacket *) NULL)
1545  return(MagickTrue);
1546  SetPixelRed(q,PlasmaPixel(random_info,((MagickRealType) u.red+
1547  v.red)/2.0,plasma));
1548  SetPixelGreen(q,PlasmaPixel(random_info,((MagickRealType) u.green+
1549  v.green)/2.0,plasma));
1550  SetPixelBlue(q,PlasmaPixel(random_info,((MagickRealType) u.blue+
1551  v.blue)/2.0,plasma));
1552  status=SyncCacheViewAuthenticPixels(image_view,exception);
1553  }
1554  if (fabs(segment->y1-segment->y2) >= MagickEpsilon)
1555  {
1556  PixelPacket
1557  *magick_restrict q;
1558 
1559  /*
1560  Top pixel.
1561  */
1562  y=CastDoubleToLong(ceil(segment->y1-0.5));
1563  (void) GetOneCacheViewVirtualPixel(u_view,CastDoubleToLong(ceil(
1564  segment->x1-0.5)),y,&u,exception);
1565  (void) GetOneCacheViewVirtualPixel(v_view,CastDoubleToLong(ceil(
1566  segment->x2-0.5)),y,&v,exception);
1567  q=QueueCacheViewAuthenticPixels(image_view,x_mid,y,1,1,exception);
1568  if (q == (PixelPacket *) NULL)
1569  return(MagickTrue);
1570  SetPixelRed(q,PlasmaPixel(random_info,((MagickRealType) u.red+
1571  v.red)/2.0,plasma));
1572  SetPixelGreen(q,PlasmaPixel(random_info,((MagickRealType) u.green+
1573  v.green)/2.0,plasma));
1574  SetPixelBlue(q,PlasmaPixel(random_info,((MagickRealType) u.blue+
1575  v.blue)/2.0,plasma));
1576  status=SyncCacheViewAuthenticPixels(image_view,exception);
1577  }
1578  }
1579  if ((fabs(segment->x1-segment->x2) >= MagickEpsilon) ||
1580  (fabs(segment->y1-segment->y2) >= MagickEpsilon))
1581  {
1582  PixelPacket
1583  *magick_restrict q;
1584 
1585  /*
1586  Middle pixel.
1587  */
1588  x=CastDoubleToLong(ceil(segment->x1-0.5));
1589  y=CastDoubleToLong(ceil(segment->y1-0.5));
1590  (void) GetOneCacheViewVirtualPixel(u_view,x,y,&u,exception);
1591  x=CastDoubleToLong(ceil(segment->x2-0.5));
1592  y=CastDoubleToLong(ceil(segment->y2-0.5));
1593  (void) GetOneCacheViewVirtualPixel(v_view,x,y,&v,exception);
1594  q=QueueCacheViewAuthenticPixels(image_view,x_mid,y_mid,1,1,exception);
1595  if (q == (PixelPacket *) NULL)
1596  return(MagickTrue);
1597  SetPixelRed(q,PlasmaPixel(random_info,((MagickRealType) u.red+
1598  v.red)/2.0,plasma));
1599  SetPixelGreen(q,PlasmaPixel(random_info,((MagickRealType) u.green+
1600  v.green)/2.0,plasma));
1601  SetPixelBlue(q,PlasmaPixel(random_info,((MagickRealType) u.blue+
1602  v.blue)/2.0,plasma));
1603  status=SyncCacheViewAuthenticPixels(image_view,exception);
1604  }
1605  if ((fabs(segment->x2-segment->x1) < 3.0) &&
1606  (fabs(segment->y2-segment->y1) < 3.0))
1607  return(status == 0 ? MagickFalse : MagickTrue);
1608  return(MagickFalse);
1609 }
1610 
1611 MagickExport MagickBooleanType PlasmaImage(Image *image,
1612  const SegmentInfo *segment,size_t attenuate,size_t depth)
1613 {
1614  CacheView
1615  *image_view,
1616  *u_view,
1617  *v_view;
1618 
1619  MagickBooleanType
1620  status;
1621 
1622  RandomInfo
1623  *random_info;
1624 
1625  assert(image != (Image *) NULL);
1626  assert(image->signature == MagickCoreSignature);
1627  if (IsEventLogging() != MagickFalse)
1628  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1629  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1630  return(MagickFalse);
1631  image_view=AcquireAuthenticCacheView(image,&image->exception);
1632  u_view=AcquireVirtualCacheView(image,&image->exception);
1633  v_view=AcquireVirtualCacheView(image,&image->exception);
1634  random_info=AcquireRandomInfo();
1635  status=PlasmaImageProxy(image,image_view,u_view,v_view,random_info,segment,
1636  attenuate,depth);
1637  random_info=DestroyRandomInfo(random_info);
1638  v_view=DestroyCacheView(v_view);
1639  u_view=DestroyCacheView(u_view);
1640  image_view=DestroyCacheView(image_view);
1641  return(status);
1642 }
1643 
1644 /*
1645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1646 % %
1647 % %
1648 % %
1649 % P o l a r o i d I m a g e %
1650 % %
1651 % %
1652 % %
1653 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1654 %
1655 % PolaroidImage() simulates a Polaroid picture.
1656 %
1657 % The format of the AnnotateImage method is:
1658 %
1659 % Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
1660 % const double angle,ExceptionInfo exception)
1661 %
1662 % A description of each parameter follows:
1663 %
1664 % o image: the image.
1665 %
1666 % o draw_info: the draw info.
1667 %
1668 % o angle: Apply the effect along this angle.
1669 %
1670 % o exception: return any errors or warnings in this structure.
1671 %
1672 */
1673 MagickExport Image *PolaroidImage(const Image *image,const DrawInfo *draw_info,
1674  const double angle,ExceptionInfo *exception)
1675 {
1676  const char
1677  *value;
1678 
1679  Image
1680  *bend_image,
1681  *caption_image,
1682  *flop_image,
1683  *picture_image,
1684  *polaroid_image,
1685  *rotate_image,
1686  *trim_image;
1687 
1688  size_t
1689  height;
1690 
1691  ssize_t
1692  quantum;
1693 
1694  /*
1695  Simulate a Polaroid picture.
1696  */
1697  assert(image != (Image *) NULL);
1698  assert(image->signature == MagickCoreSignature);
1699  assert(exception != (ExceptionInfo *) NULL);
1700  assert(exception->signature == MagickCoreSignature);
1701  if (IsEventLogging() != MagickFalse)
1702  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1703  quantum=(ssize_t) MagickMax(MagickMax((double) image->columns,(double)
1704  image->rows)/25.0,10.0);
1705  height=image->rows+2*quantum;
1706  caption_image=(Image *) NULL;
1707  value=GetImageProperty(image,"Caption");
1708  if (value != (const char *) NULL)
1709  {
1710  char
1711  *caption;
1712 
1713  /*
1714  Generate caption image.
1715  */
1716  caption_image=CloneImage(image,image->columns,1,MagickTrue,exception);
1717  if (caption_image == (Image *) NULL)
1718  return((Image *) NULL);
1719  caption=InterpretImageProperties((ImageInfo *) NULL,(Image *) image,
1720  value);
1721  if (caption != (char *) NULL)
1722  {
1723  char
1724  geometry[MaxTextExtent];
1725 
1726  DrawInfo
1727  *annotate_info;
1728 
1729  MagickBooleanType
1730  status;
1731 
1732  ssize_t
1733  count;
1734 
1735  TypeMetric
1736  metrics;
1737 
1738  annotate_info=CloneDrawInfo((const ImageInfo *) NULL,draw_info);
1739  (void) CloneString(&annotate_info->text,caption);
1740  count=FormatMagickCaption(caption_image,annotate_info,MagickTrue,
1741  &metrics,&caption);
1742  status=SetImageExtent(caption_image,image->columns,(size_t)
1743  ((count+1)*(metrics.ascent-metrics.descent)+0.5));
1744  if (status == MagickFalse)
1745  caption_image=DestroyImage(caption_image);
1746  else
1747  {
1748  caption_image->background_color=image->border_color;
1749  (void) SetImageBackgroundColor(caption_image);
1750  (void) CloneString(&annotate_info->text,caption);
1751  (void) FormatLocaleString(geometry,MaxTextExtent,"+0+%.20g",
1752  metrics.ascent);
1753  if (annotate_info->gravity == UndefinedGravity)
1754  (void) CloneString(&annotate_info->geometry,AcquireString(
1755  geometry));
1756  (void) AnnotateImage(caption_image,annotate_info);
1757  height+=caption_image->rows;
1758  }
1759  annotate_info=DestroyDrawInfo(annotate_info);
1760  caption=DestroyString(caption);
1761  }
1762  }
1763  picture_image=CloneImage(image,image->columns+2*quantum,height,MagickTrue,
1764  exception);
1765  if (picture_image == (Image *) NULL)
1766  {
1767  if (caption_image != (Image *) NULL)
1768  caption_image=DestroyImage(caption_image);
1769  return((Image *) NULL);
1770  }
1771  picture_image->background_color=image->border_color;
1772  (void) SetImageBackgroundColor(picture_image);
1773  (void) CompositeImage(picture_image,OverCompositeOp,image,quantum,quantum);
1774  if (caption_image != (Image *) NULL)
1775  {
1776  (void) CompositeImage(picture_image,OverCompositeOp,caption_image,
1777  quantum,(ssize_t) (image->rows+3*quantum/2));
1778  caption_image=DestroyImage(caption_image);
1779  }
1780  (void) QueryColorDatabase("none",&picture_image->background_color,exception);
1781  (void) SetImageAlphaChannel(picture_image,OpaqueAlphaChannel);
1782  rotate_image=RotateImage(picture_image,90.0,exception);
1783  picture_image=DestroyImage(picture_image);
1784  if (rotate_image == (Image *) NULL)
1785  return((Image *) NULL);
1786  picture_image=rotate_image;
1787  bend_image=WaveImage(picture_image,0.01*picture_image->rows,2.0*
1788  picture_image->columns,exception);
1789  picture_image=DestroyImage(picture_image);
1790  if (bend_image == (Image *) NULL)
1791  return((Image *) NULL);
1792  InheritException(&bend_image->exception,exception);
1793  picture_image=bend_image;
1794  rotate_image=RotateImage(picture_image,-90.0,exception);
1795  picture_image=DestroyImage(picture_image);
1796  if (rotate_image == (Image *) NULL)
1797  return((Image *) NULL);
1798  picture_image=rotate_image;
1799  picture_image->background_color=image->background_color;
1800  polaroid_image=ShadowImage(picture_image,80.0,2.0,quantum/3,quantum/3,
1801  exception);
1802  if (polaroid_image == (Image *) NULL)
1803  {
1804  picture_image=DestroyImage(picture_image);
1805  return(picture_image);
1806  }
1807  flop_image=FlopImage(polaroid_image,exception);
1808  polaroid_image=DestroyImage(polaroid_image);
1809  if (flop_image == (Image *) NULL)
1810  {
1811  picture_image=DestroyImage(picture_image);
1812  return(picture_image);
1813  }
1814  polaroid_image=flop_image;
1815  (void) CompositeImage(polaroid_image,OverCompositeOp,picture_image,
1816  (ssize_t) (-0.01*picture_image->columns/2.0),0L);
1817  picture_image=DestroyImage(picture_image);
1818  (void) QueryColorDatabase("none",&polaroid_image->background_color,exception);
1819  rotate_image=RotateImage(polaroid_image,angle,exception);
1820  polaroid_image=DestroyImage(polaroid_image);
1821  if (rotate_image == (Image *) NULL)
1822  return((Image *) NULL);
1823  polaroid_image=rotate_image;
1824  trim_image=TrimImage(polaroid_image,exception);
1825  polaroid_image=DestroyImage(polaroid_image);
1826  if (trim_image == (Image *) NULL)
1827  return((Image *) NULL);
1828  polaroid_image=trim_image;
1829  return(polaroid_image);
1830 }
1831 
1832 /*
1833 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1834 % %
1835 % %
1836 % %
1837 % S e p i a T o n e I m a g e %
1838 % %
1839 % %
1840 % %
1841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1842 %
1843 % MagickSepiaToneImage() applies a special effect to the image, similar to the
1844 % effect achieved in a photo darkroom by sepia toning. Threshold ranges from
1845 % 0 to QuantumRange and is a measure of the extent of the sepia toning. A
1846 % threshold of 80% is a good starting point for a reasonable tone.
1847 %
1848 % The format of the SepiaToneImage method is:
1849 %
1850 % Image *SepiaToneImage(const Image *image,const double threshold,
1851 % ExceptionInfo *exception)
1852 %
1853 % A description of each parameter follows:
1854 %
1855 % o image: the image.
1856 %
1857 % o threshold: the tone threshold.
1858 %
1859 % o exception: return any errors or warnings in this structure.
1860 %
1861 */
1862 MagickExport Image *SepiaToneImage(const Image *image,const double threshold,
1863  ExceptionInfo *exception)
1864 {
1865 #define SepiaToneImageTag "SepiaTone/Image"
1866 
1867  CacheView
1868  *image_view,
1869  *sepia_view;
1870 
1871  Image
1872  *sepia_image;
1873 
1874  MagickBooleanType
1875  status;
1876 
1877  MagickOffsetType
1878  progress;
1879 
1880  ssize_t
1881  y;
1882 
1883  /*
1884  Initialize sepia-toned image attributes.
1885  */
1886  assert(image != (const Image *) NULL);
1887  assert(image->signature == MagickCoreSignature);
1888  assert(exception != (ExceptionInfo *) NULL);
1889  assert(exception->signature == MagickCoreSignature);
1890  if (IsEventLogging() != MagickFalse)
1891  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1892  sepia_image=CloneImage(image,0,0,MagickTrue,exception);
1893  if (sepia_image == (Image *) NULL)
1894  return((Image *) NULL);
1895  if (SetImageStorageClass(sepia_image,DirectClass) == MagickFalse)
1896  {
1897  InheritException(exception,&sepia_image->exception);
1898  sepia_image=DestroyImage(sepia_image);
1899  return((Image *) NULL);
1900  }
1901  /*
1902  Tone each row of the image.
1903  */
1904  status=MagickTrue;
1905  progress=0;
1906  image_view=AcquireVirtualCacheView(image,exception);
1907  sepia_view=AcquireAuthenticCacheView(sepia_image,exception);
1908 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1909  #pragma omp parallel for schedule(static) shared(progress,status) \
1910  magick_number_threads(image,sepia_image,image->rows,1)
1911 #endif
1912  for (y=0; y < (ssize_t) image->rows; y++)
1913  {
1914  const PixelPacket
1915  *magick_restrict p;
1916 
1917  ssize_t
1918  x;
1919 
1920  PixelPacket
1921  *magick_restrict q;
1922 
1923  if (status == MagickFalse)
1924  continue;
1925  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1926  q=QueueCacheViewAuthenticPixels(sepia_view,0,y,sepia_image->columns,1,
1927  exception);
1928  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1929  {
1930  status=MagickFalse;
1931  continue;
1932  }
1933  for (x=0; x < (ssize_t) image->columns; x++)
1934  {
1935  double
1936  intensity,
1937  tone;
1938 
1939  intensity=GetPixelIntensity(image,p);
1940  tone=intensity > threshold ? (double) QuantumRange : intensity+
1941  (double) QuantumRange-threshold;
1942  SetPixelRed(q,ClampToQuantum(tone));
1943  tone=intensity > (7.0*threshold/6.0) ? (double) QuantumRange :
1944  intensity+(double) QuantumRange-7.0*threshold/6.0;
1945  SetPixelGreen(q,ClampToQuantum(tone));
1946  tone=intensity < (threshold/6.0) ? 0 : intensity-threshold/6.0;
1947  SetPixelBlue(q,ClampToQuantum(tone));
1948  tone=threshold/7.0;
1949  if ((double) GetPixelGreen(q) < tone)
1950  SetPixelGreen(q,ClampToQuantum(tone));
1951  if ((double) GetPixelBlue(q) < tone)
1952  SetPixelBlue(q,ClampToQuantum(tone));
1953  SetPixelOpacity(q,GetPixelOpacity(p));
1954  p++;
1955  q++;
1956  }
1957  if (SyncCacheViewAuthenticPixels(sepia_view,exception) == MagickFalse)
1958  status=MagickFalse;
1959  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1960  {
1961  MagickBooleanType
1962  proceed;
1963 
1964 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1965  #pragma omp atomic
1966 #endif
1967  progress++;
1968  proceed=SetImageProgress(image,SepiaToneImageTag,progress,image->rows);
1969  if (proceed == MagickFalse)
1970  status=MagickFalse;
1971  }
1972  }
1973  sepia_view=DestroyCacheView(sepia_view);
1974  image_view=DestroyCacheView(image_view);
1975  (void) NormalizeImage(sepia_image);
1976  (void) ContrastImage(sepia_image,MagickTrue);
1977  if (status == MagickFalse)
1978  sepia_image=DestroyImage(sepia_image);
1979  return(sepia_image);
1980 }
1981 
1982 /*
1983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1984 % %
1985 % %
1986 % %
1987 % S h a d o w I m a g e %
1988 % %
1989 % %
1990 % %
1991 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1992 %
1993 % ShadowImage() simulates a shadow from the specified image and returns it.
1994 %
1995 % The format of the ShadowImage method is:
1996 %
1997 % Image *ShadowImage(const Image *image,const double opacity,
1998 % const double sigma,const ssize_t x_offset,const ssize_t y_offset,
1999 % ExceptionInfo *exception)
2000 %
2001 % A description of each parameter follows:
2002 %
2003 % o image: the image.
2004 %
2005 % o opacity: percentage transparency.
2006 %
2007 % o sigma: the standard deviation of the Gaussian, in pixels.
2008 %
2009 % o x_offset: the shadow x-offset.
2010 %
2011 % o y_offset: the shadow y-offset.
2012 %
2013 % o exception: return any errors or warnings in this structure.
2014 %
2015 */
2016 MagickExport Image *ShadowImage(const Image *image,const double opacity,
2017  const double sigma,const ssize_t x_offset,const ssize_t y_offset,
2018  ExceptionInfo *exception)
2019 {
2020 #define ShadowImageTag "Shadow/Image"
2021 
2022  CacheView
2023  *image_view;
2024 
2025  Image
2026  *border_image,
2027  *clone_image,
2028  *shadow_image;
2029 
2030  MagickBooleanType
2031  status;
2032 
2033  MagickOffsetType
2034  progress;
2035 
2037  border_info;
2038 
2039  ssize_t
2040  y;
2041 
2042  assert(image != (Image *) NULL);
2043  assert(image->signature == MagickCoreSignature);
2044  assert(exception != (ExceptionInfo *) NULL);
2045  assert(exception->signature == MagickCoreSignature);
2046  if (IsEventLogging() != MagickFalse)
2047  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2048  clone_image=CloneImage(image,0,0,MagickTrue,exception);
2049  if (clone_image == (Image *) NULL)
2050  return((Image *) NULL);
2051  if (IsGrayColorspace(image->colorspace) != MagickFalse)
2052  (void) SetImageColorspace(clone_image,sRGBColorspace);
2053  (void) SetImageVirtualPixelMethod(clone_image,EdgeVirtualPixelMethod);
2054  clone_image->compose=OverCompositeOp;
2055  border_info.width=(size_t) floor(2.0*sigma+0.5);
2056  border_info.height=(size_t) floor(2.0*sigma+0.5);
2057  border_info.x=0;
2058  border_info.y=0;
2059  (void) QueryColorDatabase("none",&clone_image->border_color,exception);
2060  border_image=BorderImage(clone_image,&border_info,exception);
2061  clone_image=DestroyImage(clone_image);
2062  if (border_image == (Image *) NULL)
2063  return((Image *) NULL);
2064  if (border_image->matte == MagickFalse)
2065  (void) SetImageAlphaChannel(border_image,OpaqueAlphaChannel);
2066  /*
2067  Shadow image.
2068  */
2069  status=MagickTrue;
2070  progress=0;
2071  image_view=AcquireAuthenticCacheView(border_image,exception);
2072 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2073  #pragma omp parallel for schedule(static) shared(progress,status) \
2074  magick_number_threads(border_image,border_image,border_image->rows,1)
2075 #endif
2076  for (y=0; y < (ssize_t) border_image->rows; y++)
2077  {
2078  PixelPacket
2079  *magick_restrict q;
2080 
2081  ssize_t
2082  x;
2083 
2084  if (status == MagickFalse)
2085  continue;
2086  q=GetCacheViewAuthenticPixels(image_view,0,y,border_image->columns,1,
2087  exception);
2088  if (q == (PixelPacket *) NULL)
2089  {
2090  status=MagickFalse;
2091  continue;
2092  }
2093  for (x=0; x < (ssize_t) border_image->columns; x++)
2094  {
2095  SetPixelRed(q,border_image->background_color.red);
2096  SetPixelGreen(q,border_image->background_color.green);
2097  SetPixelBlue(q,border_image->background_color.blue);
2098  if (border_image->matte == MagickFalse)
2099  SetPixelOpacity(q,border_image->background_color.opacity);
2100  else
2101  SetPixelOpacity(q,ClampToQuantum((double) (QuantumRange-
2102  GetPixelAlpha(q)*opacity/100.0)));
2103  q++;
2104  }
2105  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2106  status=MagickFalse;
2107  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2108  {
2109  MagickBooleanType
2110  proceed;
2111 
2112 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2113  #pragma omp atomic
2114 #endif
2115  progress++;
2116  proceed=SetImageProgress(image,ShadowImageTag,progress,
2117  border_image->rows);
2118  if (proceed == MagickFalse)
2119  status=MagickFalse;
2120  }
2121  }
2122  image_view=DestroyCacheView(image_view);
2123  shadow_image=BlurImageChannel(border_image,AlphaChannel,0.0,sigma,exception);
2124  border_image=DestroyImage(border_image);
2125  if (shadow_image == (Image *) NULL)
2126  return((Image *) NULL);
2127  if (shadow_image->page.width == 0)
2128  shadow_image->page.width=shadow_image->columns;
2129  if (shadow_image->page.height == 0)
2130  shadow_image->page.height=shadow_image->rows;
2131  shadow_image->page.width+=x_offset-(ssize_t) border_info.width;
2132  shadow_image->page.height+=y_offset-(ssize_t) border_info.height;
2133  shadow_image->page.x+=x_offset-(ssize_t) border_info.width;
2134  shadow_image->page.y+=y_offset-(ssize_t) border_info.height;
2135  return(shadow_image);
2136 }
2137 
2138 /*
2139 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2140 % %
2141 % %
2142 % %
2143 % S k e t c h I m a g e %
2144 % %
2145 % %
2146 % %
2147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2148 %
2149 % SketchImage() simulates a pencil sketch. We convolve the image with a
2150 % Gaussian operator of the given radius and standard deviation (sigma). For
2151 % reasonable results, radius should be larger than sigma. Use a radius of 0
2152 % and SketchImage() selects a suitable radius for you. Angle gives the angle
2153 % of the sketch.
2154 %
2155 % The format of the SketchImage method is:
2156 %
2157 % Image *SketchImage(const Image *image,const double radius,
2158 % const double sigma,const double angle,ExceptionInfo *exception)
2159 %
2160 % A description of each parameter follows:
2161 %
2162 % o image: the image.
2163 %
2164 % o radius: the radius of the Gaussian, in pixels, not counting
2165 % the center pixel.
2166 %
2167 % o sigma: the standard deviation of the Gaussian, in pixels.
2168 %
2169 % o angle: Apply the effect along this angle.
2170 %
2171 % o exception: return any errors or warnings in this structure.
2172 %
2173 */
2174 MagickExport Image *SketchImage(const Image *image,const double radius,
2175  const double sigma,const double angle,ExceptionInfo *exception)
2176 {
2177  CacheView
2178  *random_view;
2179 
2180  Image
2181  *blend_image,
2182  *blur_image,
2183  *dodge_image,
2184  *random_image,
2185  *sketch_image;
2186 
2187  MagickBooleanType
2188  status;
2189 
2191  zero;
2192 
2193  RandomInfo
2194  **magick_restrict random_info;
2195 
2196  ssize_t
2197  y;
2198 
2199 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2200  unsigned long
2201  key;
2202 #endif
2203 
2204  /*
2205  Sketch image.
2206  */
2207  random_image=CloneImage(image,image->columns << 1,image->rows << 1,
2208  MagickTrue,exception);
2209  if (random_image == (Image *) NULL)
2210  return((Image *) NULL);
2211  status=MagickTrue;
2212  GetMagickPixelPacket(random_image,&zero);
2213  random_info=AcquireRandomInfoTLS();
2214  random_view=AcquireAuthenticCacheView(random_image,exception);
2215 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2216  key=GetRandomSecretKey(random_info[0]);
2217 #pragma omp parallel for schedule(static) shared(status) \
2218 magick_number_threads(random_image,random_image,random_image->rows,key == ~0UL)
2219 #endif
2220  for (y=0; y < (ssize_t) random_image->rows; y++)
2221  {
2222  const int
2223  id = GetOpenMPThreadId();
2224 
2226  pixel;
2227 
2228  IndexPacket
2229  *magick_restrict indexes;
2230 
2231  ssize_t
2232  x;
2233 
2234  PixelPacket
2235  *magick_restrict q;
2236 
2237  if (status == MagickFalse)
2238  continue;
2239  q=QueueCacheViewAuthenticPixels(random_view,0,y,random_image->columns,1,
2240  exception);
2241  if (q == (PixelPacket *) NULL)
2242  {
2243  status=MagickFalse;
2244  continue;
2245  }
2246  indexes=GetCacheViewAuthenticIndexQueue(random_view);
2247  pixel=zero;
2248  for (x=0; x < (ssize_t) random_image->columns; x++)
2249  {
2250  pixel.red=(MagickRealType) (QuantumRange*
2251  GetPseudoRandomValue(random_info[id]));
2252  pixel.green=pixel.red;
2253  pixel.blue=pixel.red;
2254  if (image->colorspace == CMYKColorspace)
2255  pixel.index=pixel.red;
2256  SetPixelPacket(random_image,&pixel,q,indexes+x);
2257  q++;
2258  }
2259  if (SyncCacheViewAuthenticPixels(random_view,exception) == MagickFalse)
2260  status=MagickFalse;
2261  }
2262  random_info=DestroyRandomInfoTLS(random_info);
2263  if (status == MagickFalse)
2264  {
2265  random_view=DestroyCacheView(random_view);
2266  random_image=DestroyImage(random_image);
2267  return(random_image);
2268  }
2269  random_view=DestroyCacheView(random_view);
2270 
2271  blur_image=MotionBlurImage(random_image,radius,sigma,angle,exception);
2272  random_image=DestroyImage(random_image);
2273  if (blur_image == (Image *) NULL)
2274  return((Image *) NULL);
2275  dodge_image=EdgeImage(blur_image,radius,exception);
2276  blur_image=DestroyImage(blur_image);
2277  if (dodge_image == (Image *) NULL)
2278  return((Image *) NULL);
2279  status=ClampImage(dodge_image);
2280  if (status != MagickFalse)
2281  status=NormalizeImage(dodge_image);
2282  if (status != MagickFalse)
2283  status=NegateImage(dodge_image,MagickFalse);
2284  if (status != MagickFalse)
2285  status=TransformImage(&dodge_image,(char *) NULL,"50%");
2286  sketch_image=CloneImage(image,0,0,MagickTrue,exception);
2287  if (sketch_image == (Image *) NULL)
2288  {
2289  dodge_image=DestroyImage(dodge_image);
2290  return((Image *) NULL);
2291  }
2292  (void) CompositeImage(sketch_image,ColorDodgeCompositeOp,dodge_image,0,0);
2293  dodge_image=DestroyImage(dodge_image);
2294  blend_image=CloneImage(image,0,0,MagickTrue,exception);
2295  if (blend_image == (Image *) NULL)
2296  {
2297  sketch_image=DestroyImage(sketch_image);
2298  return((Image *) NULL);
2299  }
2300  (void) SetImageArtifact(blend_image,"compose:args","20x80");
2301  (void) CompositeImage(sketch_image,BlendCompositeOp,blend_image,0,0);
2302  blend_image=DestroyImage(blend_image);
2303  return(sketch_image);
2304 }
2305 
2306 /*
2307 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2308 % %
2309 % %
2310 % %
2311 % S o l a r i z e I m a g e %
2312 % %
2313 % %
2314 % %
2315 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2316 %
2317 % SolarizeImage() applies a special effect to the image, similar to the effect
2318 % achieved in a photo darkroom by selectively exposing areas of photo
2319 % sensitive paper to light. Threshold ranges from 0 to QuantumRange and is a
2320 % measure of the extent of the solarization.
2321 %
2322 % The format of the SolarizeImage method is:
2323 %
2324 % MagickBooleanType SolarizeImage(Image *image,const double threshold)
2325 % MagickBooleanType SolarizeImageChannel(Image *image,
2326 % const ChannelType channel,const double threshold,
2327 % ExceptionInfo *exception)
2328 %
2329 % A description of each parameter follows:
2330 %
2331 % o image: the image.
2332 %
2333 % o channel: the channel type.
2334 %
2335 % o threshold: Define the extent of the solarization.
2336 %
2337 % o exception: return any errors or warnings in this structure.
2338 %
2339 */
2340 MagickExport MagickBooleanType SolarizeImage(Image *image,
2341  const double threshold)
2342 {
2343  MagickBooleanType
2344  status;
2345 
2346  status=SolarizeImageChannel(image,DefaultChannels,threshold,
2347  &image->exception);
2348  return(status);
2349 }
2350 
2351 MagickExport MagickBooleanType SolarizeImageChannel(Image *image,
2352  const ChannelType channel,const double threshold,ExceptionInfo *exception)
2353 {
2354 #define SolarizeImageTag "Solarize/Image"
2355 
2356  CacheView
2357  *image_view;
2358 
2359  MagickBooleanType
2360  status;
2361 
2362  MagickOffsetType
2363  progress;
2364 
2365  ssize_t
2366  y;
2367 
2368  assert(image != (Image *) NULL);
2369  assert(image->signature == MagickCoreSignature);
2370  if (IsEventLogging() != MagickFalse)
2371  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2372  if (IsGrayColorspace(image->colorspace) != MagickFalse)
2373  (void) SetImageColorspace(image,sRGBColorspace);
2374  if (image->storage_class == PseudoClass)
2375  {
2376  ssize_t
2377  i;
2378 
2379  /*
2380  Solarize colormap.
2381  */
2382  for (i=0; i < (ssize_t) image->colors; i++)
2383  {
2384  if ((channel & RedChannel) != 0)
2385  if ((double) image->colormap[i].red > threshold)
2386  image->colormap[i].red=QuantumRange-image->colormap[i].red;
2387  if ((channel & GreenChannel) != 0)
2388  if ((double) image->colormap[i].green > threshold)
2389  image->colormap[i].green=QuantumRange-image->colormap[i].green;
2390  if ((channel & BlueChannel) != 0)
2391  if ((double) image->colormap[i].blue > threshold)
2392  image->colormap[i].blue=QuantumRange-image->colormap[i].blue;
2393  }
2394  }
2395  /*
2396  Solarize image.
2397  */
2398  status=MagickTrue;
2399  progress=0;
2400  image_view=AcquireAuthenticCacheView(image,exception);
2401 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2402  #pragma omp parallel for schedule(static) shared(progress,status) \
2403  magick_number_threads(image,image,image->rows,1)
2404 #endif
2405  for (y=0; y < (ssize_t) image->rows; y++)
2406  {
2407  ssize_t
2408  x;
2409 
2410  PixelPacket
2411  *magick_restrict q;
2412 
2413  if (status == MagickFalse)
2414  continue;
2415  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2416  exception);
2417  if (q == (PixelPacket *) NULL)
2418  {
2419  status=MagickFalse;
2420  continue;
2421  }
2422  for (x=0; x < (ssize_t) image->columns; x++)
2423  {
2424  if ((channel & RedChannel) != 0)
2425  if ((double) GetPixelRed(q) > threshold)
2426  SetPixelRed(q,QuantumRange-GetPixelRed(q));
2427  if ((channel & GreenChannel) != 0)
2428  if ((double) GetPixelGreen(q) > threshold)
2429  SetPixelGreen(q,QuantumRange-GetPixelGreen(q));
2430  if ((channel & BlueChannel) != 0)
2431  if ((double) GetPixelBlue(q) > threshold)
2432  SetPixelBlue(q,QuantumRange-GetPixelBlue(q));
2433  q++;
2434  }
2435  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2436  status=MagickFalse;
2437  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2438  {
2439  MagickBooleanType
2440  proceed;
2441 
2442 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2443  #pragma omp atomic
2444 #endif
2445  progress++;
2446  proceed=SetImageProgress(image,SolarizeImageTag,progress,image->rows);
2447  if (proceed == MagickFalse)
2448  status=MagickFalse;
2449  }
2450  }
2451  image_view=DestroyCacheView(image_view);
2452  return(status);
2453 }
2454 
2455 /*
2456 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2457 % %
2458 % %
2459 % %
2460 % S t e g a n o I m a g e %
2461 % %
2462 % %
2463 % %
2464 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2465 %
2466 % SteganoImage() hides a digital watermark within the image. Recover
2467 % the hidden watermark later to prove that the authenticity of an image.
2468 % Offset defines the start position within the image to hide the watermark.
2469 %
2470 % The format of the SteganoImage method is:
2471 %
2472 % Image *SteganoImage(const Image *image,Image *watermark,
2473 % ExceptionInfo *exception)
2474 %
2475 % A description of each parameter follows:
2476 %
2477 % o image: the image.
2478 %
2479 % o watermark: the watermark image.
2480 %
2481 % o exception: return any errors or warnings in this structure.
2482 %
2483 */
2484 MagickExport Image *SteganoImage(const Image *image,const Image *watermark,
2485  ExceptionInfo *exception)
2486 {
2487 #define GetBit(alpha,i) ((((size_t) (alpha) >> (size_t) (i)) & 0x01) != 0)
2488 #define SetBit(alpha,i,set) (alpha)=(Quantum) ((set) != 0 ? (size_t) (alpha) \
2489  | (one << (size_t) (i)) : (size_t) (alpha) & ~(one << (size_t) (i)))
2490 #define SteganoImageTag "Stegano/Image"
2491 
2492  CacheView
2493  *stegano_view,
2494  *watermark_view;
2495 
2496  Image
2497  *stegano_image;
2498 
2499  int
2500  c;
2501 
2502  MagickBooleanType
2503  status;
2504 
2505  PixelPacket
2506  pixel;
2507 
2508  PixelPacket
2509  *q;
2510 
2511  ssize_t
2512  x;
2513 
2514  size_t
2515  depth,
2516  one;
2517 
2518  ssize_t
2519  i,
2520  j,
2521  k,
2522  y;
2523 
2524  /*
2525  Initialize steganographic image attributes.
2526  */
2527  assert(image != (const Image *) NULL);
2528  assert(image->signature == MagickCoreSignature);
2529  assert(watermark != (const Image *) NULL);
2530  assert(watermark->signature == MagickCoreSignature);
2531  assert(exception != (ExceptionInfo *) NULL);
2532  assert(exception->signature == MagickCoreSignature);
2533  if (IsEventLogging() != MagickFalse)
2534  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2535  one=1UL;
2536  stegano_image=CloneImage(image,0,0,MagickTrue,exception);
2537  if (stegano_image == (Image *) NULL)
2538  return((Image *) NULL);
2539  if (SetImageStorageClass(stegano_image,DirectClass) == MagickFalse)
2540  {
2541  InheritException(exception,&stegano_image->exception);
2542  stegano_image=DestroyImage(stegano_image);
2543  return((Image *) NULL);
2544  }
2545  stegano_image->depth=MAGICKCORE_QUANTUM_DEPTH;
2546  /*
2547  Hide watermark in low-order bits of image.
2548  */
2549  c=0;
2550  i=0;
2551  j=0;
2552  depth=stegano_image->depth;
2553  k=image->offset;
2554  status=MagickTrue;
2555  watermark_view=AcquireVirtualCacheView(watermark,exception);
2556  stegano_view=AcquireAuthenticCacheView(stegano_image,exception);
2557  for (i=(ssize_t) depth-1; (i >= 0) && (j < (ssize_t) depth); i--)
2558  {
2559  for (y=0; (y < (ssize_t) watermark->rows) && (j < (ssize_t) depth); y++)
2560  {
2561  for (x=0; (x < (ssize_t) watermark->columns) && (j < (ssize_t) depth); x++)
2562  {
2563  (void) GetOneCacheViewVirtualPixel(watermark_view,x,y,&pixel,exception);
2564  if ((k/(ssize_t) stegano_image->columns) >= (ssize_t) stegano_image->rows)
2565  break;
2566  q=GetCacheViewAuthenticPixels(stegano_view,k % (ssize_t)
2567  stegano_image->columns,k/(ssize_t) stegano_image->columns,1,1,
2568  exception);
2569  if (q == (PixelPacket *) NULL)
2570  break;
2571  switch (c)
2572  {
2573  case 0:
2574  {
2575  SetBit(GetPixelRed(q),j,GetBit(ClampToQuantum(GetPixelIntensity(
2576  image,&pixel)),i));
2577  break;
2578  }
2579  case 1:
2580  {
2581  SetBit(GetPixelGreen(q),j,GetBit(ClampToQuantum(GetPixelIntensity(
2582  image,&pixel)),i));
2583  break;
2584  }
2585  case 2:
2586  {
2587  SetBit(GetPixelBlue(q),j,GetBit(ClampToQuantum(GetPixelIntensity(
2588  image,&pixel)),i));
2589  break;
2590  }
2591  }
2592  if (SyncCacheViewAuthenticPixels(stegano_view,exception) == MagickFalse)
2593  break;
2594  c++;
2595  if (c == 3)
2596  c=0;
2597  k++;
2598  if (k == (ssize_t) (stegano_image->columns*stegano_image->columns))
2599  k=0;
2600  if (k == image->offset)
2601  j++;
2602  }
2603  }
2604  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2605  {
2606  MagickBooleanType
2607  proceed;
2608 
2609  proceed=SetImageProgress(image,SteganoImageTag,(MagickOffsetType)
2610  (depth-i),depth);
2611  if (proceed == MagickFalse)
2612  status=MagickFalse;
2613  }
2614  }
2615  stegano_view=DestroyCacheView(stegano_view);
2616  watermark_view=DestroyCacheView(watermark_view);
2617  if (stegano_image->storage_class == PseudoClass)
2618  (void) SyncImage(stegano_image);
2619  if (status == MagickFalse)
2620  stegano_image=DestroyImage(stegano_image);
2621  return(stegano_image);
2622 }
2623 
2624 /*
2625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2626 % %
2627 % %
2628 % %
2629 % S t e r e o A n a g l y p h I m a g e %
2630 % %
2631 % %
2632 % %
2633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2634 %
2635 % StereoAnaglyphImage() combines two images and produces a single image that
2636 % is the composite of a left and right image of a stereo pair. Special
2637 % red-green stereo glasses are required to view this effect.
2638 %
2639 % The format of the StereoAnaglyphImage method is:
2640 %
2641 % Image *StereoImage(const Image *left_image,const Image *right_image,
2642 % ExceptionInfo *exception)
2643 % Image *StereoAnaglyphImage(const Image *left_image,
2644 % const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
2645 % ExceptionInfo *exception)
2646 %
2647 % A description of each parameter follows:
2648 %
2649 % o left_image: the left image.
2650 %
2651 % o right_image: the right image.
2652 %
2653 % o exception: return any errors or warnings in this structure.
2654 %
2655 % o x_offset: amount, in pixels, by which the left image is offset to the
2656 % right of the right image.
2657 %
2658 % o y_offset: amount, in pixels, by which the left image is offset to the
2659 % bottom of the right image.
2660 %
2661 %
2662 */
2663 MagickExport Image *StereoImage(const Image *left_image,
2664  const Image *right_image,ExceptionInfo *exception)
2665 {
2666  return(StereoAnaglyphImage(left_image,right_image,0,0,exception));
2667 }
2668 
2669 MagickExport Image *StereoAnaglyphImage(const Image *left_image,
2670  const Image *right_image,const ssize_t x_offset,const ssize_t y_offset,
2671  ExceptionInfo *exception)
2672 {
2673 #define StereoImageTag "Stereo/Image"
2674 
2675  const Image
2676  *image;
2677 
2678  Image
2679  *stereo_image;
2680 
2681  MagickBooleanType
2682  status;
2683 
2684  ssize_t
2685  y;
2686 
2687  assert(left_image != (const Image *) NULL);
2688  assert(left_image->signature == MagickCoreSignature);
2689  assert(right_image != (const Image *) NULL);
2690  assert(right_image->signature == MagickCoreSignature);
2691  assert(exception != (ExceptionInfo *) NULL);
2692  assert(exception->signature == MagickCoreSignature);
2693  if (IsEventLogging() != MagickFalse)
2694  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
2695  left_image->filename);
2696  image=left_image;
2697  if ((left_image->columns != right_image->columns) ||
2698  (left_image->rows != right_image->rows))
2699  ThrowImageException(ImageError,"LeftAndRightImageSizesDiffer");
2700  /*
2701  Initialize stereo image attributes.
2702  */
2703  stereo_image=CloneImage(left_image,left_image->columns,left_image->rows,
2704  MagickTrue,exception);
2705  if (stereo_image == (Image *) NULL)
2706  return((Image *) NULL);
2707  if (SetImageStorageClass(stereo_image,DirectClass) == MagickFalse)
2708  {
2709  InheritException(exception,&stereo_image->exception);
2710  stereo_image=DestroyImage(stereo_image);
2711  return((Image *) NULL);
2712  }
2713  (void) SetImageColorspace(stereo_image,sRGBColorspace);
2714  /*
2715  Copy left image to red channel and right image to blue channel.
2716  */
2717  status=MagickTrue;
2718  for (y=0; y < (ssize_t) stereo_image->rows; y++)
2719  {
2720  const PixelPacket
2721  *magick_restrict p,
2722  *magick_restrict q;
2723 
2724  ssize_t
2725  x;
2726 
2727  PixelPacket
2728  *magick_restrict r;
2729 
2730  p=GetVirtualPixels(left_image,-x_offset,y-y_offset,image->columns,1,
2731  exception);
2732  q=GetVirtualPixels(right_image,0,y,right_image->columns,1,exception);
2733  r=QueueAuthenticPixels(stereo_image,0,y,stereo_image->columns,1,exception);
2734  if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
2735  (r == (PixelPacket *) NULL))
2736  break;
2737  for (x=0; x < (ssize_t) stereo_image->columns; x++)
2738  {
2739  SetPixelRed(r,GetPixelRed(p));
2740  SetPixelGreen(r,GetPixelGreen(q));
2741  SetPixelBlue(r,GetPixelBlue(q));
2742  SetPixelOpacity(r,(GetPixelOpacity(p)+q->opacity)/2);
2743  p++;
2744  q++;
2745  r++;
2746  }
2747  if (SyncAuthenticPixels(stereo_image,exception) == MagickFalse)
2748  break;
2749  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2750  {
2751  MagickBooleanType
2752  proceed;
2753 
2754  proceed=SetImageProgress(image,StereoImageTag,(MagickOffsetType) y,
2755  stereo_image->rows);
2756  if (proceed == MagickFalse)
2757  status=MagickFalse;
2758  }
2759  }
2760  if (status == MagickFalse)
2761  stereo_image=DestroyImage(stereo_image);
2762  return(stereo_image);
2763 }
2764 
2765 /*
2766 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2767 % %
2768 % %
2769 % %
2770 % S w i r l I m a g e %
2771 % %
2772 % %
2773 % %
2774 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2775 %
2776 % SwirlImage() swirls the pixels about the center of the image, where
2777 % degrees indicates the sweep of the arc through which each pixel is moved.
2778 % You get a more dramatic effect as the degrees move from 1 to 360.
2779 %
2780 % The format of the SwirlImage method is:
2781 %
2782 % Image *SwirlImage(const Image *image,double degrees,
2783 % ExceptionInfo *exception)
2784 %
2785 % A description of each parameter follows:
2786 %
2787 % o image: the image.
2788 %
2789 % o degrees: Define the tightness of the swirling effect.
2790 %
2791 % o exception: return any errors or warnings in this structure.
2792 %
2793 */
2794 MagickExport Image *SwirlImage(const Image *image,double degrees,
2795  ExceptionInfo *exception)
2796 {
2797 #define SwirlImageTag "Swirl/Image"
2798 
2799  CacheView
2800  *image_view,
2801  *swirl_view;
2802 
2803  double
2804  radius;
2805 
2806  Image
2807  *swirl_image;
2808 
2809  MagickBooleanType
2810  status;
2811 
2812  MagickOffsetType
2813  progress;
2814 
2816  zero;
2817 
2818  PointInfo
2819  center,
2820  scale;
2821 
2822  ssize_t
2823  y;
2824 
2825  /*
2826  Initialize swirl image attributes.
2827  */
2828  assert(image != (const Image *) NULL);
2829  assert(image->signature == MagickCoreSignature);
2830  assert(exception != (ExceptionInfo *) NULL);
2831  assert(exception->signature == MagickCoreSignature);
2832  if (IsEventLogging() != MagickFalse)
2833  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2834  swirl_image=CloneImage(image,0,0,MagickTrue,exception);
2835  if (swirl_image == (Image *) NULL)
2836  return((Image *) NULL);
2837  if (SetImageStorageClass(swirl_image,DirectClass) == MagickFalse)
2838  {
2839  InheritException(exception,&swirl_image->exception);
2840  swirl_image=DestroyImage(swirl_image);
2841  return((Image *) NULL);
2842  }
2843  if (swirl_image->background_color.opacity != OpaqueOpacity)
2844  swirl_image->matte=MagickTrue;
2845  /*
2846  Compute scaling factor.
2847  */
2848  center.x=(double) image->columns/2.0;
2849  center.y=(double) image->rows/2.0;
2850  radius=MagickMax(center.x,center.y);
2851  scale.x=1.0;
2852  scale.y=1.0;
2853  if (image->columns > image->rows)
2854  scale.y=(double) image->columns/(double) image->rows;
2855  else
2856  if (image->columns < image->rows)
2857  scale.x=(double) image->rows/(double) image->columns;
2858  degrees=(double) DegreesToRadians(degrees);
2859  /*
2860  Swirl image.
2861  */
2862  status=MagickTrue;
2863  progress=0;
2864  GetMagickPixelPacket(swirl_image,&zero);
2865  image_view=AcquireVirtualCacheView(image,exception);
2866  swirl_view=AcquireAuthenticCacheView(swirl_image,exception);
2867 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2868  #pragma omp parallel for schedule(static) shared(progress,status) \
2869  magick_number_threads(image,swirl_image,image->rows,1)
2870 #endif
2871  for (y=0; y < (ssize_t) image->rows; y++)
2872  {
2873  double
2874  distance;
2875 
2877  pixel;
2878 
2879  PointInfo
2880  delta;
2881 
2882  IndexPacket
2883  *magick_restrict swirl_indexes;
2884 
2885  ssize_t
2886  x;
2887 
2888  PixelPacket
2889  *magick_restrict q;
2890 
2891  if (status == MagickFalse)
2892  continue;
2893  q=GetCacheViewAuthenticPixels(swirl_view,0,y,swirl_image->columns,1,
2894  exception);
2895  if (q == (PixelPacket *) NULL)
2896  {
2897  status=MagickFalse;
2898  continue;
2899  }
2900  swirl_indexes=GetCacheViewAuthenticIndexQueue(swirl_view);
2901  delta.y=scale.y*(double) (y-center.y);
2902  pixel=zero;
2903  for (x=0; x < (ssize_t) image->columns; x++)
2904  {
2905  /*
2906  Determine if the pixel is within an ellipse.
2907  */
2908  delta.x=scale.x*(double) (x-center.x);
2909  distance=delta.x*delta.x+delta.y*delta.y;
2910  if (distance < (radius*radius))
2911  {
2912  double
2913  cosine,
2914  factor,
2915  sine;
2916 
2917  /*
2918  Swirl the pixel.
2919  */
2920  factor=1.0-sqrt(distance)/radius;
2921  sine=sin((double) (degrees*factor*factor));
2922  cosine=cos((double) (degrees*factor*factor));
2923  status=InterpolateMagickPixelPacket(image,image_view,
2924  UndefinedInterpolatePixel,(double) ((cosine*delta.x-sine*delta.y)/
2925  scale.x+center.x),(double) ((sine*delta.x+cosine*delta.y)/scale.y+
2926  center.y),&pixel,exception);
2927  if (status == MagickFalse)
2928  break;
2929  SetPixelPacket(swirl_image,&pixel,q,swirl_indexes+x);
2930  }
2931  q++;
2932  }
2933  if (SyncCacheViewAuthenticPixels(swirl_view,exception) == MagickFalse)
2934  status=MagickFalse;
2935  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2936  {
2937  MagickBooleanType
2938  proceed;
2939 
2940 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2941  #pragma omp atomic
2942 #endif
2943  progress++;
2944  proceed=SetImageProgress(image,SwirlImageTag,progress,image->rows);
2945  if (proceed == MagickFalse)
2946  status=MagickFalse;
2947  }
2948  }
2949  swirl_view=DestroyCacheView(swirl_view);
2950  image_view=DestroyCacheView(image_view);
2951  if (status == MagickFalse)
2952  swirl_image=DestroyImage(swirl_image);
2953  return(swirl_image);
2954 }
2955 
2956 /*
2957 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2958 % %
2959 % %
2960 % %
2961 % T i n t I m a g e %
2962 % %
2963 % %
2964 % %
2965 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2966 %
2967 % TintImage() applies a color vector to each pixel in the image. The length
2968 % of the vector is 0 for black and white and at its maximum for the midtones.
2969 % The vector weighting function is f(x)=(1-(4.0*((x-0.5)*(x-0.5))))
2970 %
2971 % The format of the TintImage method is:
2972 %
2973 % Image *TintImage(const Image *image,const char *opacity,
2974 % const PixelPacket tint,ExceptionInfo *exception)
2975 %
2976 % A description of each parameter follows:
2977 %
2978 % o image: the image.
2979 %
2980 % o opacity: A color value used for tinting.
2981 %
2982 % o tint: A color value used for tinting.
2983 %
2984 % o exception: return any errors or warnings in this structure.
2985 %
2986 */
2987 MagickExport Image *TintImage(const Image *image,const char *opacity,
2988  const PixelPacket tint,ExceptionInfo *exception)
2989 {
2990 #define TintImageTag "Tint/Image"
2991 
2992  CacheView
2993  *image_view,
2994  *tint_view;
2995 
2996  GeometryInfo
2997  geometry_info;
2998 
2999  Image
3000  *tint_image;
3001 
3002  MagickBooleanType
3003  status;
3004 
3005  MagickOffsetType
3006  progress;
3007 
3009  color_vector,
3010  pixel;
3011 
3012  MagickStatusType
3013  flags;
3014 
3015  ssize_t
3016  y;
3017 
3018  /*
3019  Allocate tint image.
3020  */
3021  assert(image != (const Image *) NULL);
3022  assert(image->signature == MagickCoreSignature);
3023  assert(exception != (ExceptionInfo *) NULL);
3024  assert(exception->signature == MagickCoreSignature);
3025  if (IsEventLogging() != MagickFalse)
3026  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3027  tint_image=CloneImage(image,0,0,MagickTrue,exception);
3028  if (tint_image == (Image *) NULL)
3029  return((Image *) NULL);
3030  if (SetImageStorageClass(tint_image,DirectClass) == MagickFalse)
3031  {
3032  InheritException(exception,&tint_image->exception);
3033  tint_image=DestroyImage(tint_image);
3034  return((Image *) NULL);
3035  }
3036  if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
3037  (IsPixelGray(&tint) == MagickFalse))
3038  (void) SetImageColorspace(tint_image,sRGBColorspace);
3039  if (opacity == (const char *) NULL)
3040  return(tint_image);
3041  /*
3042  Determine RGB values of the tint color.
3043  */
3044  flags=ParseGeometry(opacity,&geometry_info);
3045  pixel.red=geometry_info.rho;
3046  pixel.green=geometry_info.rho;
3047  pixel.blue=geometry_info.rho;
3048  pixel.opacity=(MagickRealType) OpaqueOpacity;
3049  if ((flags & SigmaValue) != 0)
3050  pixel.green=geometry_info.sigma;
3051  if ((flags & XiValue) != 0)
3052  pixel.blue=geometry_info.xi;
3053  if ((flags & PsiValue) != 0)
3054  pixel.opacity=geometry_info.psi;
3055  color_vector.red=(MagickRealType) (pixel.red*tint.red/100.0-
3056  PixelPacketIntensity(&tint));
3057  color_vector.green=(MagickRealType) (pixel.green*tint.green/100.0-
3058  PixelPacketIntensity(&tint));
3059  color_vector.blue=(MagickRealType) (pixel.blue*tint.blue/100.0-
3060  PixelPacketIntensity(&tint));
3061  /*
3062  Tint image.
3063  */
3064  status=MagickTrue;
3065  progress=0;
3066  image_view=AcquireVirtualCacheView(image,exception);
3067  tint_view=AcquireAuthenticCacheView(tint_image,exception);
3068 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3069  #pragma omp parallel for schedule(static) shared(progress,status) \
3070  magick_number_threads(image,tint_image,image->rows,1)
3071 #endif
3072  for (y=0; y < (ssize_t) image->rows; y++)
3073  {
3074  const PixelPacket
3075  *magick_restrict p;
3076 
3077  PixelPacket
3078  *magick_restrict q;
3079 
3080  ssize_t
3081  x;
3082 
3083  if (status == MagickFalse)
3084  continue;
3085  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
3086  q=QueueCacheViewAuthenticPixels(tint_view,0,y,tint_image->columns,1,
3087  exception);
3088  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
3089  {
3090  status=MagickFalse;
3091  continue;
3092  }
3093  for (x=0; x < (ssize_t) image->columns; x++)
3094  {
3095  double
3096  weight;
3097 
3099  pixel;
3100 
3101  weight=QuantumScale*GetPixelRed(p)-0.5;
3102  pixel.red=(MagickRealType) GetPixelRed(p)+color_vector.red*(1.0-(4.0*
3103  (weight*weight)));
3104  SetPixelRed(q,ClampToQuantum(pixel.red));
3105  weight=QuantumScale*GetPixelGreen(p)-0.5;
3106  pixel.green=(MagickRealType) GetPixelGreen(p)+color_vector.green*(1.0-
3107  (4.0*(weight*weight)));
3108  SetPixelGreen(q,ClampToQuantum(pixel.green));
3109  weight=QuantumScale*GetPixelBlue(p)-0.5;
3110  pixel.blue=(MagickRealType) GetPixelBlue(p)+color_vector.blue*(1.0-(4.0*
3111  (weight*weight)));
3112  SetPixelBlue(q,ClampToQuantum(pixel.blue));
3113  SetPixelOpacity(q,GetPixelOpacity(p));
3114  p++;
3115  q++;
3116  }
3117  if (SyncCacheViewAuthenticPixels(tint_view,exception) == MagickFalse)
3118  status=MagickFalse;
3119  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3120  {
3121  MagickBooleanType
3122  proceed;
3123 
3124 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3125  #pragma omp atomic
3126 #endif
3127  progress++;
3128  proceed=SetImageProgress(image,TintImageTag,progress,image->rows);
3129  if (proceed == MagickFalse)
3130  status=MagickFalse;
3131  }
3132  }
3133  tint_view=DestroyCacheView(tint_view);
3134  image_view=DestroyCacheView(image_view);
3135  if (status == MagickFalse)
3136  tint_image=DestroyImage(tint_image);
3137  return(tint_image);
3138 }
3139 
3140 /*
3141 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3142 % %
3143 % %
3144 % %
3145 % V i g n e t t e I m a g e %
3146 % %
3147 % %
3148 % %
3149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3150 %
3151 % VignetteImage() softens the edges of the image in vignette style.
3152 %
3153 % The format of the VignetteImage method is:
3154 %
3155 % Image *VignetteImage(const Image *image,const double radius,
3156 % const double sigma,const ssize_t x,const ssize_t y,
3157 % ExceptionInfo *exception)
3158 %
3159 % A description of each parameter follows:
3160 %
3161 % o image: the image.
3162 %
3163 % o radius: the radius of the pixel neighborhood.
3164 %
3165 % o sigma: the standard deviation of the Gaussian, in pixels.
3166 %
3167 % o x, y: Define the x and y ellipse offset.
3168 %
3169 % o exception: return any errors or warnings in this structure.
3170 %
3171 */
3172 MagickExport Image *VignetteImage(const Image *image,const double radius,
3173  const double sigma,const ssize_t x,const ssize_t y,ExceptionInfo *exception)
3174 {
3175  char
3176  ellipse[MaxTextExtent];
3177 
3178  DrawInfo
3179  *draw_info;
3180 
3181  Image
3182  *blur_image,
3183  *canvas_image,
3184  *oval_image,
3185  *vignette_image;
3186 
3187  assert(image != (Image *) NULL);
3188  assert(image->signature == MagickCoreSignature);
3189  assert(exception != (ExceptionInfo *) NULL);
3190  assert(exception->signature == MagickCoreSignature);
3191  if (IsEventLogging() != MagickFalse)
3192  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3193  canvas_image=CloneImage(image,0,0,MagickTrue,exception);
3194  if (canvas_image == (Image *) NULL)
3195  return((Image *) NULL);
3196  if (SetImageStorageClass(canvas_image,DirectClass) == MagickFalse)
3197  {
3198  InheritException(exception,&canvas_image->exception);
3199  canvas_image=DestroyImage(canvas_image);
3200  return((Image *) NULL);
3201  }
3202  canvas_image->matte=MagickTrue;
3203  oval_image=CloneImage(canvas_image,canvas_image->columns,canvas_image->rows,
3204  MagickTrue,exception);
3205  if (oval_image == (Image *) NULL)
3206  {
3207  canvas_image=DestroyImage(canvas_image);
3208  return((Image *) NULL);
3209  }
3210  (void) QueryColorDatabase("#000000",&oval_image->background_color,exception);
3211  (void) SetImageBackgroundColor(oval_image);
3212  draw_info=CloneDrawInfo((const ImageInfo *) NULL,(const DrawInfo *) NULL);
3213  (void) QueryColorDatabase("#ffffff",&draw_info->fill,exception);
3214  (void) QueryColorDatabase("#ffffff",&draw_info->stroke,exception);
3215  (void) FormatLocaleString(ellipse,MaxTextExtent,
3216  "ellipse %g,%g,%g,%g,0.0,360.0",image->columns/2.0,
3217  image->rows/2.0,image->columns/2.0-x,image->rows/2.0-y);
3218  draw_info->primitive=AcquireString(ellipse);
3219  (void) DrawImage(oval_image,draw_info);
3220  draw_info=DestroyDrawInfo(draw_info);
3221  blur_image=BlurImage(oval_image,radius,sigma,exception);
3222  oval_image=DestroyImage(oval_image);
3223  if (blur_image == (Image *) NULL)
3224  {
3225  canvas_image=DestroyImage(canvas_image);
3226  return((Image *) NULL);
3227  }
3228  blur_image->matte=MagickFalse;
3229  (void) CompositeImage(canvas_image,CopyOpacityCompositeOp,blur_image,0,0);
3230  blur_image=DestroyImage(blur_image);
3231  vignette_image=MergeImageLayers(canvas_image,FlattenLayer,exception);
3232  canvas_image=DestroyImage(canvas_image);
3233  if (vignette_image != (Image *) NULL)
3234  (void) TransformImageColorspace(vignette_image,image->colorspace);
3235  return(vignette_image);
3236 }
3237 
3238 /*
3239 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3240 % %
3241 % %
3242 % %
3243 % W a v e I m a g e %
3244 % %
3245 % %
3246 % %
3247 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3248 %
3249 % WaveImage() creates a "ripple" effect in the image by shifting the pixels
3250 % vertically along a sine wave whose amplitude and wavelength is specified
3251 % by the given parameters.
3252 %
3253 % The format of the WaveImage method is:
3254 %
3255 % Image *WaveImage(const Image *image,const double amplitude,
3256 % const double wave_length,ExceptionInfo *exception)
3257 %
3258 % A description of each parameter follows:
3259 %
3260 % o image: the image.
3261 %
3262 % o amplitude, wave_length: Define the amplitude and wave length of the
3263 % sine wave.
3264 %
3265 % o exception: return any errors or warnings in this structure.
3266 %
3267 */
3268 MagickExport Image *WaveImage(const Image *image,const double amplitude,
3269  const double wave_length,ExceptionInfo *exception)
3270 {
3271 #define WaveImageTag "Wave/Image"
3272 
3273  CacheView
3274  *image_view,
3275  *wave_view;
3276 
3277  float
3278  *sine_map;
3279 
3280  Image
3281  *wave_image;
3282 
3283  MagickBooleanType
3284  status;
3285 
3286  MagickOffsetType
3287  progress;
3288 
3290  zero;
3291 
3292  ssize_t
3293  i;
3294 
3295  ssize_t
3296  y;
3297 
3298  /*
3299  Initialize wave image attributes.
3300  */
3301  assert(image != (Image *) NULL);
3302  assert(image->signature == MagickCoreSignature);
3303  assert(exception != (ExceptionInfo *) NULL);
3304  assert(exception->signature == MagickCoreSignature);
3305  if (IsEventLogging() != MagickFalse)
3306  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3307  wave_image=CloneImage(image,image->columns,(size_t) (image->rows+2.0*
3308  fabs(amplitude)),MagickTrue,exception);
3309  if (wave_image == (Image *) NULL)
3310  return((Image *) NULL);
3311  if (SetImageStorageClass(wave_image,DirectClass) == MagickFalse)
3312  {
3313  InheritException(exception,&wave_image->exception);
3314  wave_image=DestroyImage(wave_image);
3315  return((Image *) NULL);
3316  }
3317  if (wave_image->background_color.opacity != OpaqueOpacity)
3318  wave_image->matte=MagickTrue;
3319  /*
3320  Allocate sine map.
3321  */
3322  sine_map=(float *) AcquireQuantumMemory((size_t) wave_image->columns,
3323  sizeof(*sine_map));
3324  if (sine_map == (float *) NULL)
3325  {
3326  wave_image=DestroyImage(wave_image);
3327  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3328  }
3329  for (i=0; i < (ssize_t) wave_image->columns; i++)
3330  sine_map[i]=(float) fabs(amplitude)+amplitude*sin((double)
3331  ((2.0*MagickPI*i)*PerceptibleReciprocal(wave_length)));
3332  /*
3333  Wave image.
3334  */
3335  status=MagickTrue;
3336  progress=0;
3337  GetMagickPixelPacket(wave_image,&zero);
3338  image_view=AcquireVirtualCacheView(image,exception);
3339  wave_view=AcquireAuthenticCacheView(wave_image,exception);
3340  (void) SetCacheViewVirtualPixelMethod(image_view,
3341  BackgroundVirtualPixelMethod);
3342 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3343  #pragma omp parallel for schedule(static) shared(progress,status) \
3344  magick_number_threads(image,wave_image,wave_image->rows,1)
3345 #endif
3346  for (y=0; y < (ssize_t) wave_image->rows; y++)
3347  {
3349  pixel;
3350 
3351  IndexPacket
3352  *magick_restrict indexes;
3353 
3354  PixelPacket
3355  *magick_restrict q;
3356 
3357  ssize_t
3358  x;
3359 
3360  if (status == MagickFalse)
3361  continue;
3362  q=QueueCacheViewAuthenticPixels(wave_view,0,y,wave_image->columns,1,
3363  exception);
3364  if (q == (PixelPacket *) NULL)
3365  {
3366  status=MagickFalse;
3367  continue;
3368  }
3369  indexes=GetCacheViewAuthenticIndexQueue(wave_view);
3370  pixel=zero;
3371  for (x=0; x < (ssize_t) wave_image->columns; x++)
3372  {
3373  status=InterpolateMagickPixelPacket(image,image_view,
3374  UndefinedInterpolatePixel,(double) x,(double) (y-sine_map[x]),&pixel,
3375  exception);
3376  if (status == MagickFalse)
3377  break;
3378  SetPixelPacket(wave_image,&pixel,q,indexes+x);
3379  q++;
3380  }
3381  if (SyncCacheViewAuthenticPixels(wave_view,exception) == MagickFalse)
3382  status=MagickFalse;
3383  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3384  {
3385  MagickBooleanType
3386  proceed;
3387 
3388 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3389  #pragma omp atomic
3390 #endif
3391  progress++;
3392  proceed=SetImageProgress(image,WaveImageTag,progress,image->rows);
3393  if (proceed == MagickFalse)
3394  status=MagickFalse;
3395  }
3396  }
3397  wave_view=DestroyCacheView(wave_view);
3398  image_view=DestroyCacheView(image_view);
3399  sine_map=(float *) RelinquishMagickMemory(sine_map);
3400  if (status == MagickFalse)
3401  wave_image=DestroyImage(wave_image);
3402  return(wave_image);
3403 }
3404 
3405 /*
3406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3407 % %
3408 % %
3409 % %
3410 % W a v e l e t D e n o i s e I m a g e %
3411 % %
3412 % %
3413 % %
3414 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3415 %
3416 % WaveletDenoiseImage() removes noise from the image using a wavelet
3417 % transform. The wavelet transform is a fast hierarchical scheme for
3418 % processing an image using a set of consecutive lowpass and high_pass filters,
3419 % followed by a decimation. This results in a decomposition into different
3420 % scales which can be regarded as different “frequency bands”, determined by
3421 % the mother wavelet. Adapted from dcraw.c by David Coffin.
3422 %
3423 % The format of the WaveletDenoiseImage method is:
3424 %
3425 % Image *WaveletDenoiseImage(const Image *image,const double threshold,
3426 % const double softness,ExceptionInfo *exception)
3427 %
3428 % A description of each parameter follows:
3429 %
3430 % o image: the image.
3431 %
3432 % o threshold: set the threshold for smoothing.
3433 %
3434 % o softness: attenuate the smoothing threshold.
3435 %
3436 % o exception: return any errors or warnings in this structure.
3437 %
3438 */
3439 
3440 static inline void HatTransform(const float *magick_restrict pixels,
3441  const size_t stride,const size_t extent,const size_t scale,float *kernel)
3442 {
3443  const float
3444  *magick_restrict p,
3445  *magick_restrict q,
3446  *magick_restrict r;
3447 
3448  ssize_t
3449  i;
3450 
3451  p=pixels;
3452  q=pixels+scale*stride,
3453  r=pixels+scale*stride;
3454  for (i=0; i < (ssize_t) scale; i++)
3455  {
3456  kernel[i]=0.25f*(*p+(*p)+(*q)+(*r));
3457  p+=stride;
3458  q-=stride;
3459  r+=stride;
3460  }
3461  for ( ; i < (ssize_t) (extent-scale); i++)
3462  {
3463  kernel[i]=0.25f*(2.0f*(*p)+*(p-scale*stride)+*(p+scale*stride));
3464  p+=stride;
3465  }
3466  q=p-scale*stride;
3467  r=pixels+stride*(extent-2);
3468  for ( ; i < (ssize_t) extent; i++)
3469  {
3470  kernel[i]=0.25f*(*p+(*p)+(*q)+(*r));
3471  p+=stride;
3472  q+=stride;
3473  r-=stride;
3474  }
3475 }
3476 
3477 MagickExport Image *WaveletDenoiseImage(const Image *image,
3478  const double threshold,const double softness,ExceptionInfo *exception)
3479 {
3480  CacheView
3481  *image_view,
3482  *noise_view;
3483 
3484  float
3485  *kernel,
3486  *pixels;
3487 
3488  Image
3489  *noise_image;
3490 
3491  MagickBooleanType
3492  status;
3493 
3494  MagickSizeType
3495  number_pixels;
3496 
3497  MemoryInfo
3498  *pixels_info;
3499 
3500  size_t
3501  max_channels;
3502 
3503  ssize_t
3504  channel;
3505 
3506  static const double
3507  noise_levels[]= {
3508  0.8002, 0.2735, 0.1202, 0.0585, 0.0291, 0.0152, 0.0080, 0.0044 };
3509 
3510  /*
3511  Initialize noise image attributes.
3512  */
3513  assert(image != (const Image *) NULL);
3514  assert(image->signature == MagickCoreSignature);
3515  assert(exception != (ExceptionInfo *) NULL);
3516  assert(exception->signature == MagickCoreSignature);
3517  if (IsEventLogging() != MagickFalse)
3518  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
3519  noise_image=(Image *) NULL;
3520 #if defined(MAGICKCORE_OPENCL_SUPPORT)
3521  noise_image=AccelerateWaveletDenoiseImage(image,threshold,exception);
3522  if (noise_image != (Image *) NULL)
3523  return(noise_image);
3524 #endif
3525  noise_image=CloneImage(image,0,0,MagickTrue,exception);
3526  if (noise_image == (Image *) NULL)
3527  return((Image *) NULL);
3528  if (SetImageStorageClass(noise_image,DirectClass) == MagickFalse)
3529  {
3530  noise_image=DestroyImage(noise_image);
3531  return((Image *) NULL);
3532  }
3533  if (AcquireMagickResource(WidthResource,3*image->columns) == MagickFalse)
3534  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3535  pixels_info=AcquireVirtualMemory(3*image->columns,image->rows*
3536  sizeof(*pixels));
3537  kernel=(float *) AcquireQuantumMemory(MagickMax(image->rows,image->columns)+1,
3538  GetOpenMPMaximumThreads()*sizeof(*kernel));
3539  if ((pixels_info == (MemoryInfo *) NULL) || (kernel == (float *) NULL))
3540  {
3541  if (kernel != (float *) NULL)
3542  kernel=(float *) RelinquishMagickMemory(kernel);
3543  if (pixels_info != (MemoryInfo *) NULL)
3544  pixels_info=RelinquishVirtualMemory(pixels_info);
3545  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
3546  }
3547  pixels=(float *) GetVirtualMemoryBlob(pixels_info);
3548  status=MagickTrue;
3549  number_pixels=image->columns*image->rows;
3550  max_channels=(size_t) (image->colorspace == CMYKColorspace ? 4 : 3);
3551  image_view=AcquireAuthenticCacheView(image,exception);
3552  noise_view=AcquireAuthenticCacheView(noise_image,exception);
3553  for (channel=0; channel < (ssize_t) max_channels; channel++)
3554  {
3555  ssize_t
3556  i;
3557 
3558  size_t
3559  high_pass,
3560  low_pass;
3561 
3562  ssize_t
3563  level,
3564  y;
3565 
3566  if (status == MagickFalse)
3567  continue;
3568  /*
3569  Copy channel from image to wavelet pixel array.
3570  */
3571  i=0;
3572  for (y=0; y < (ssize_t) image->rows; y++)
3573  {
3574  const IndexPacket
3575  *magick_restrict indexes;
3576 
3577  const PixelPacket
3578  *magick_restrict p;
3579 
3580  ssize_t
3581  x;
3582 
3583  p=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3584  if (p == (const PixelPacket *) NULL)
3585  {
3586  status=MagickFalse;
3587  break;
3588  }
3589  indexes=GetCacheViewVirtualIndexQueue(image_view);
3590  for (x=0; x < (ssize_t) image->columns; x++)
3591  {
3592  switch (channel)
3593  {
3594  case 0: pixels[i]=(float) GetPixelRed(p); break;
3595  case 1: pixels[i]=(float) GetPixelGreen(p); break;
3596  case 2: pixels[i]=(float) GetPixelBlue(p); break;
3597  case 3: pixels[i]=(float) indexes[x]; break;
3598  default: break;
3599  }
3600  i++;
3601  p++;
3602  }
3603  }
3604  /*
3605  Low pass filter outputs are called approximation kernel & high pass
3606  filters are referred to as detail kernel. The detail kernel
3607  have high values in the noisy parts of the signal.
3608  */
3609  high_pass=0;
3610  for (level=0; level < 5; level++)
3611  {
3612  double
3613  magnitude;
3614 
3615  ssize_t
3616  x,
3617  y;
3618 
3619  low_pass=(size_t) (number_pixels*((level & 0x01)+1));
3620 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3621  #pragma omp parallel for schedule(static,1) \
3622  magick_number_threads(image,image,image->rows,1)
3623 #endif
3624  for (y=0; y < (ssize_t) image->rows; y++)
3625  {
3626  const int
3627  id = GetOpenMPThreadId();
3628 
3629  float
3630  *magick_restrict p,
3631  *magick_restrict q;
3632 
3633  ssize_t
3634  x;
3635 
3636  p=kernel+id*image->columns;
3637  q=pixels+y*image->columns;
3638  HatTransform(q+high_pass,1,image->columns,(size_t) (1UL << level),p);
3639  q+=low_pass;
3640  for (x=0; x < (ssize_t) image->columns; x++)
3641  *q++=(*p++);
3642  }
3643 #if defined(MAGICKCORE_OPENMP_SUPPORT)
3644  #pragma omp parallel for schedule(static,1) \
3645  magick_number_threads(image,image,image->columns,1)
3646 #endif
3647  for (x=0; x < (ssize_t) image->columns; x++)
3648  {
3649  const int
3650  id = GetOpenMPThreadId();
3651 
3652  float
3653  *magick_restrict p,
3654  *magick_restrict q;
3655 
3656  ssize_t
3657  y;
3658 
3659  p=kernel+id*image->rows;
3660  q=pixels+x+low_pass;
3661  HatTransform(q,image->columns,image->rows,(size_t) (1UL << level),p);
3662  for (y=0; y < (ssize_t) image->rows; y++)
3663  {
3664  *q=(*p++);
3665  q+=image->columns;
3666  }
3667  }
3668  /*
3669  To threshold, each coefficient is compared to a threshold value and
3670  attenuated / shrunk by some factor.
3671  */
3672  magnitude=threshold*noise_levels[level];
3673  for (i=0; i < (ssize_t) number_pixels; ++i)
3674  {
3675  pixels[high_pass+i]-=pixels[low_pass+i];
3676  if (pixels[high_pass+i] < -magnitude)
3677  pixels[high_pass+i]+=magnitude-softness*magnitude;
3678  else
3679  if (pixels[high_pass+i] > magnitude)
3680  pixels[high_pass+i]-=magnitude-softness*magnitude;
3681  else
3682  pixels[high_pass+i]*=softness;
3683  if (high_pass != 0)
3684  pixels[i]+=pixels[high_pass+i];
3685  }
3686  high_pass=low_pass;
3687  }
3688  /*
3689  Reconstruct image from the thresholded wavelet kernel.
3690  */
3691  i=0;
3692  for (y=0; y < (ssize_t) image->rows; y++)
3693  {
3694  MagickBooleanType
3695  sync;
3696 
3697  IndexPacket
3698  *magick_restrict noise_indexes;
3699 
3700  PixelPacket
3701  *magick_restrict q;
3702 
3703  ssize_t
3704  x;
3705 
3706  q=GetCacheViewAuthenticPixels(noise_view,0,y,noise_image->columns,1,
3707  exception);
3708  if (q == (PixelPacket *) NULL)
3709  {
3710  status=MagickFalse;
3711  break;
3712  }
3713  noise_indexes=GetCacheViewAuthenticIndexQueue(noise_view);
3714  for (x=0; x < (ssize_t) image->columns; x++)
3715  {
3716  float
3717  pixel;
3718 
3719  pixel=pixels[i]+pixels[low_pass+i];
3720  switch (channel)
3721  {
3722  case 0: SetPixelRed(q,ClampToQuantum(pixel)); break;
3723  case 1: SetPixelGreen(q,ClampToQuantum(pixel)); break;
3724  case 2: SetPixelBlue(q,ClampToQuantum(pixel)); break;
3725  case 3: SetPixelIndex(noise_indexes+x,ClampToQuantum(pixel)); break;
3726  default: break;
3727  }
3728  i++;
3729  q++;
3730  }
3731  sync=SyncCacheViewAuthenticPixels(noise_view,exception);
3732  if (sync == MagickFalse)
3733  status=MagickFalse;
3734  }
3735  if (image->progress_monitor != (MagickProgressMonitor) NULL)
3736  {
3737  MagickBooleanType
3738  proceed;
3739 
3740  proceed=SetImageProgress(image,AddNoiseImageTag,(MagickOffsetType)
3741  channel,max_channels);
3742  if (proceed == MagickFalse)
3743  status=MagickFalse;
3744  }
3745  }
3746  noise_view=DestroyCacheView(noise_view);
3747  image_view=DestroyCacheView(image_view);
3748  kernel=(float *) RelinquishMagickMemory(kernel);
3749  pixels_info=RelinquishVirtualMemory(pixels_info);
3750  return(noise_image);
3751 }
Definition: image.h:152