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