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