MagickCore 7.1.2-16
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
composite.c
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7% C O O MM MM P P O O SS I T E %
8% C O O M M M PPPP O O SSS I T EEE %
9% C O O M M P O O SS I T E %
10% CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
11% %
12% %
13% MagickCore Image Composite Methods %
14% %
15% Software Design %
16% Cristy %
17% July 1992 %
18% %
19% %
20% Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
21% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% https://imagemagick.org/license/ %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/cache-private.h"
47#include "MagickCore/cache-view.h"
48#include "MagickCore/channel.h"
49#include "MagickCore/client.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colorspace.h"
53#include "MagickCore/colorspace-private.h"
54#include "MagickCore/composite.h"
55#include "MagickCore/composite-private.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/draw.h"
58#include "MagickCore/exception-private.h"
59#include "MagickCore/fx.h"
60#include "MagickCore/gem.h"
61#include "MagickCore/geometry.h"
62#include "MagickCore/image.h"
63#include "MagickCore/image-private.h"
64#include "MagickCore/list.h"
65#include "MagickCore/log.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/monitor.h"
68#include "MagickCore/monitor-private.h"
69#include "MagickCore/morphology.h"
70#include "MagickCore/option.h"
71#include "MagickCore/pixel-accessor.h"
72#include "MagickCore/property.h"
73#include "MagickCore/quantum.h"
74#include "MagickCore/random_.h"
75#include "MagickCore/random-private.h"
76#include "MagickCore/resample.h"
77#include "MagickCore/resource_.h"
78#include "MagickCore/string_.h"
79#include "MagickCore/string-private.h"
80#include "MagickCore/thread-private.h"
81#include "MagickCore/threshold.h"
82#include "MagickCore/token.h"
83#include "MagickCore/transform.h"
84#include "MagickCore/utility.h"
85#include "MagickCore/utility-private.h"
86#include "MagickCore/version.h"
87
88/*
89%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90% %
91% %
92% %
93% C o m p o s i t e I m a g e %
94% %
95% %
96% %
97%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
98%
99% CompositeImage() returns the second image composited onto the first
100% at the specified offset, using the specified composite method.
101%
102% The format of the CompositeImage method is:
103%
104% MagickBooleanType CompositeImage(Image *image,
105% const Image *source_image,const CompositeOperator compose,
106% const MagickBooleanType clip_to_self,const ssize_t x_offset,
107% const ssize_t y_offset,ExceptionInfo *exception)
108%
109% A description of each parameter follows:
110%
111% o image: the canvas image, modified by he composition
112%
113% o source_image: the source image.
114%
115% o compose: This operator affects how the composite is applied to
116% the image. The operators and how they are utilized are listed here
117% http://www.w3.org/TR/SVG12/#compositing.
118%
119% o clip_to_self: set to MagickTrue to limit composition to area composed.
120%
121% o x_offset: the column offset of the composited image.
122%
123% o y_offset: the row offset of the composited image.
124%
125% Extra Controls from Image meta-data in 'image' (artifacts)
126%
127% o "compose:args"
128% A string containing extra numerical arguments for specific compose
129% methods, generally expressed as a 'geometry' or a comma separated list
130% of numbers.
131%
132% Compose methods needing such arguments include "BlendCompositeOp" and
133% "DisplaceCompositeOp".
134%
135% o exception: return any errors or warnings in this structure.
136%
137*/
138
139/*
140 Composition based on the SVG specification:
141
142 A Composition is defined by...
143 Color Function : f(Sc,Dc) where Sc and Dc are the normalized colors
144 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
145 Y = 1 for source preserved
146 Z = 1 for canvas preserved
147
148 Conversion to transparency (then optimized)
149 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
150 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
151
152 Where...
153 Sca = Sc*Sa normalized Source color divided by Source alpha
154 Dca = Dc*Da normalized Dest color divided by Dest alpha
155 Dc' = Dca'/Da' the desired color value for this channel.
156
157 Da' in in the follow formula as 'gamma' The resulting alpha value.
158
159 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
160 the following optimizations...
161 gamma = Sa+Da-Sa*Da;
162 gamma = 1 - QuantumScale*alpha * QuantumScale*beta;
163 opacity = QuantumScale*alpha*beta; // over blend, optimized 1-Gamma
164
165 The above SVG definitions also define that Mathematical Composition
166 methods should use a 'Over' blending mode for Alpha Channel.
167 It however was not applied for composition modes of 'Plus', 'Minus',
168 the modulus versions of 'Add' and 'Subtract'.
169
170 Mathematical operator changes to be applied from IM v6.7...
171
172 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
173 'ModulusAdd' and 'ModulusSubtract' for clarity.
174
175 2) All mathematical compositions work as per the SVG specification
176 with regard to blending. This now includes 'ModulusAdd' and
177 'ModulusSubtract'.
178
179 3) When the special channel flag 'sync' (synchronize channel updates)
180 is turned off (enabled by default) then mathematical compositions are
181 only performed on the channels specified, and are applied
182 independently of each other. In other words the mathematics is
183 performed as 'pure' mathematical operations, rather than as image
184 operations.
185*/
186
187static Image *BlendConvolveImage(const Image *image,const char *kernel,
188 ExceptionInfo *exception)
189{
190 Image
191 *clone_image,
192 *convolve_image;
193
194 KernelInfo
195 *kernel_info;
196
197 /*
198 Convolve image with a kernel.
199 */
200 kernel_info=AcquireKernelInfo(kernel,exception);
201 if (kernel_info == (KernelInfo *) NULL)
202 return((Image *) NULL);
203 clone_image=CloneImage(image,0,0,MagickTrue,exception);
204 if (clone_image == (Image *) NULL)
205 {
206 kernel_info=DestroyKernelInfo(kernel_info);
207 return((Image *) NULL);
208 }
209 (void) SetImageAlphaChannel(clone_image,OffAlphaChannel,exception);
210 convolve_image=ConvolveImage(clone_image,kernel_info,exception);
211 kernel_info=DestroyKernelInfo(kernel_info);
212 clone_image=DestroyImage(clone_image);
213 return(convolve_image);
214}
215
216static Image *BlendMagnitudeImage(const Image *dx_image,const Image *dy_image,
217 ExceptionInfo *exception)
218{
219 CacheView
220 *dx_view,
221 *dy_view,
222 *magnitude_view;
223
224 Image
225 *magnitude_image;
226
227 MagickBooleanType
228 status = MagickTrue;
229
230 ssize_t
231 y;
232
233 /*
234 Generate the magnitude between two images.
235 */
236 magnitude_image=CloneImage(dx_image,0,0,MagickTrue,exception);
237 if (magnitude_image == (Image *) NULL)
238 return(magnitude_image);
239 dx_view=AcquireVirtualCacheView(dx_image,exception);
240 dy_view=AcquireVirtualCacheView(dy_image,exception);
241 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
242#if defined(MAGICKCORE_OPENMP_SUPPORT)
243 #pragma omp parallel for schedule(static) shared(status) \
244 magick_number_threads(dx_image,magnitude_image,dx_image->rows,1)
245#endif
246 for (y=0; y < (ssize_t) dx_image->rows; y++)
247 {
248 const Quantum
249 *magick_restrict p,
250 *magick_restrict q;
251
252 Quantum
253 *magick_restrict r;
254
255 ssize_t
256 x;
257
258 if (status == MagickFalse)
259 continue;
260 p=GetCacheViewVirtualPixels(dx_view,0,y,dx_image->columns,1,exception);
261 q=GetCacheViewVirtualPixels(dy_view,0,y,dx_image->columns,1,exception);
262 r=GetCacheViewAuthenticPixels(magnitude_view,0,y,dx_image->columns,1,
263 exception);
264 if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL) ||
265 (r == (Quantum *) NULL))
266 {
267 status=MagickFalse;
268 continue;
269 }
270 for (x=0; x < (ssize_t) dx_image->columns; x++)
271 {
272 ssize_t
273 i;
274
275 for (i=0; i < (ssize_t) GetPixelChannels(dx_image); i++)
276 {
277 PixelChannel channel = GetPixelChannelChannel(dx_image,i);
278 PixelTrait traits = GetPixelChannelTraits(dx_image,channel);
279 PixelTrait dy_traits = GetPixelChannelTraits(dy_image,channel);
280 if ((traits == UndefinedPixelTrait) ||
281 (dy_traits == UndefinedPixelTrait) ||
282 ((dy_traits & UpdatePixelTrait) == 0))
283 continue;
284 r[i]=ClampToQuantum(hypot((double) p[i],(double)
285 GetPixelChannel(dy_image,channel,q)));
286 }
287 p+=(ptrdiff_t) GetPixelChannels(dx_image);
288 q+=(ptrdiff_t) GetPixelChannels(dy_image);
289 r+=(ptrdiff_t) GetPixelChannels(magnitude_image);
290 }
291 if (SyncCacheViewAuthenticPixels(magnitude_view,exception) == MagickFalse)
292 status=MagickFalse;
293 }
294 magnitude_view=DestroyCacheView(magnitude_view);
295 dy_view=DestroyCacheView(dy_view);
296 dx_view=DestroyCacheView(dx_view);
297 if (status == MagickFalse)
298 magnitude_image=DestroyImage(magnitude_image);
299 return(magnitude_image);
300}
301
302static Image *BlendMaxMagnitudeImage(const Image *alpha_image,
303 const Image *beta_image,const Image *dx_image,const Image *dy_image,
304 ExceptionInfo *exception)
305{
306 CacheView
307 *alpha_view,
308 *beta_view,
309 *dx_view,
310 *dy_view,
311 *magnitude_view;
312
313 Image
314 *magnitude_image;
315
316 MagickBooleanType
317 status = MagickTrue;
318
319 ssize_t
320 y;
321
322 /*
323 Select the larger of two magnitudes.
324 */
325 magnitude_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
326 if (magnitude_image == (Image *) NULL)
327 return(magnitude_image);
328 alpha_view=AcquireVirtualCacheView(alpha_image,exception);
329 beta_view=AcquireVirtualCacheView(beta_image,exception);
330 dx_view=AcquireVirtualCacheView(dx_image,exception);
331 dy_view=AcquireVirtualCacheView(dy_image,exception);
332 magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
333#if defined(MAGICKCORE_OPENMP_SUPPORT)
334 #pragma omp parallel for schedule(static) shared(status) \
335 magick_number_threads(alpha_image,magnitude_image,alpha_image->rows,1)
336#endif
337 for (y=0; y < (ssize_t) alpha_image->rows; y++)
338 {
339 const Quantum
340 *magick_restrict p,
341 *magick_restrict q,
342 *magick_restrict r,
343 *magick_restrict s;
344
345 Quantum
346 *magick_restrict t;
347
348 ssize_t
349 x;
350
351 if (status == MagickFalse)
352 continue;
353 p=GetCacheViewVirtualPixels(alpha_view,0,y,alpha_image->columns,1,
354 exception);
355 q=GetCacheViewVirtualPixels(beta_view,0,y,alpha_image->columns,1,exception);
356 r=GetCacheViewVirtualPixels(dx_view,0,y,alpha_image->columns,1,exception);
357 s=GetCacheViewVirtualPixels(dy_view,0,y,alpha_image->columns,1,exception);
358 t=GetCacheViewAuthenticPixels(magnitude_view,0,y,alpha_image->columns,1,
359 exception);
360 if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL) ||
361 (r == (const Quantum *) NULL) || (s == (const Quantum *) NULL) ||
362 (t == (Quantum *) NULL))
363 {
364 status=MagickFalse;
365 continue;
366 }
367 for (x=0; x < (ssize_t) alpha_image->columns; x++)
368 {
369 ssize_t
370 i;
371
372 for (i=0; i < (ssize_t) GetPixelChannels(alpha_image); i++)
373 {
374 PixelChannel channel = GetPixelChannelChannel(alpha_image,i);
375 PixelTrait traits = GetPixelChannelTraits(alpha_image,channel);
376 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
377 if ((traits == UndefinedPixelTrait) ||
378 (beta_traits == UndefinedPixelTrait) ||
379 ((beta_traits & UpdatePixelTrait) == 0))
380 continue;
381 if (p[i] > GetPixelChannel(beta_image,channel,q))
382 t[i]=GetPixelChannel(dx_image,channel,r);
383 else
384 t[i]=GetPixelChannel(dy_image,channel,s);
385 }
386 p+=(ptrdiff_t) GetPixelChannels(alpha_image);
387 q+=(ptrdiff_t) GetPixelChannels(beta_image);
388 r+=(ptrdiff_t) GetPixelChannels(dx_image);
389 s+=(ptrdiff_t) GetPixelChannels(dy_image);
390 t+=(ptrdiff_t) GetPixelChannels(magnitude_image);
391 }
392 if (SyncCacheViewAuthenticPixels(magnitude_view,exception) == MagickFalse)
393 status=MagickFalse;
394 }
395 magnitude_view=DestroyCacheView(magnitude_view);
396 dy_view=DestroyCacheView(dy_view);
397 dx_view=DestroyCacheView(dx_view);
398 beta_view=DestroyCacheView(beta_view);
399 alpha_view=DestroyCacheView(alpha_view);
400 if (status == MagickFalse)
401 magnitude_image=DestroyImage(magnitude_image);
402 return(magnitude_image);
403}
404
405static Image *BlendSumImage(const Image *alpha_image,const Image *beta_image,
406 const double attenuate,const double sign,ExceptionInfo *exception)
407{
408 CacheView
409 *alpha_view,
410 *beta_view,
411 *sum_view;
412
413 Image
414 *sum_image;
415
416 MagickBooleanType
417 status = MagickTrue;
418
419 ssize_t
420 y;
421
422 /*
423 Add or subtract and optionally attenuate two images.
424 */
425 sum_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
426 if (sum_image == (Image *) NULL)
427 return(sum_image);
428 alpha_view=AcquireVirtualCacheView(alpha_image,exception);
429 beta_view=AcquireVirtualCacheView(beta_image,exception);
430 sum_view=AcquireAuthenticCacheView(sum_image,exception);
431#if defined(MAGICKCORE_OPENMP_SUPPORT)
432 #pragma omp parallel for schedule(static) shared(status) \
433 magick_number_threads(alpha_image,sum_image,alpha_image->rows,1)
434#endif
435 for (y=0; y < (ssize_t) alpha_image->rows; y++)
436 {
437 const Quantum
438 *magick_restrict p,
439 *magick_restrict q;
440
441 Quantum
442 *magick_restrict r;
443
444 ssize_t
445 x;
446
447 if (status == MagickFalse)
448 continue;
449 p=GetCacheViewVirtualPixels(alpha_view,0,y,alpha_image->columns,1,
450 exception);
451 q=GetCacheViewVirtualPixels(beta_view,0,y,alpha_image->columns,1,exception);
452 r=GetCacheViewAuthenticPixels(sum_view,0,y,alpha_image->columns,1,
453 exception);
454 if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL) ||
455 (r == (Quantum *) NULL))
456 {
457 status=MagickFalse;
458 continue;
459 }
460 for (x=0; x < (ssize_t) alpha_image->columns; x++)
461 {
462 ssize_t
463 i;
464
465 for (i=0; i < (ssize_t) GetPixelChannels(alpha_image); i++)
466 {
467 PixelChannel channel = GetPixelChannelChannel(alpha_image,i);
468 PixelTrait traits = GetPixelChannelTraits(alpha_image,channel);
469 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
470 if ((traits == UndefinedPixelTrait) ||
471 (beta_traits == UndefinedPixelTrait) ||
472 ((beta_traits & UpdatePixelTrait) == 0))
473 continue;
474 r[i]=ClampToQuantum(attenuate*((double) p[i]+sign*
475 (double) GetPixelChannel(beta_image,channel,q)));
476 }
477 p+=(ptrdiff_t) GetPixelChannels(alpha_image);
478 q+=(ptrdiff_t) GetPixelChannels(beta_image);
479 r+=(ptrdiff_t) GetPixelChannels(sum_image);
480 }
481 if (SyncCacheViewAuthenticPixels(sum_view,exception) == MagickFalse)
482 status=MagickFalse;
483 }
484 sum_view=DestroyCacheView(sum_view);
485 beta_view=DestroyCacheView(beta_view);
486 alpha_view=DestroyCacheView(alpha_view);
487 if (status == MagickFalse)
488 sum_image=DestroyImage(sum_image);
489 return(sum_image);
490}
491
492static Image *BlendDivergentImage(const Image *alpha_image,
493 const Image *beta_image,ExceptionInfo *exception)
494{
495#define FreeDivergentResources() \
496{ \
497 if (dy_image != (Image *) NULL) \
498 dy_image=DestroyImage(dy_image); \
499 if (dx_image != (Image *) NULL) \
500 dx_image=DestroyImage(dx_image); \
501 if (magnitude_beta != (Image *) NULL) \
502 magnitude_beta=DestroyImage(magnitude_beta); \
503 if (dy_beta != (Image *) NULL) \
504 dy_beta=DestroyImage(dy_beta); \
505 if (dx_beta != (Image *) NULL) \
506 dx_beta=DestroyImage(dx_beta); \
507 if (magnitude_alpha != (Image *) NULL) \
508 magnitude_alpha=DestroyImage(magnitude_alpha); \
509 if (dy_alpha != (Image *) NULL) \
510 dy_alpha=DestroyImage(dy_alpha); \
511 if (dx_alpha != (Image *) NULL) \
512 dx_alpha=DestroyImage(dx_alpha); \
513}
514
515 Image
516 *divergent_image = (Image *) NULL,
517 *dx_alpha = (Image *) NULL,
518 *dx_beta = (Image *) NULL,
519 *dx_divergent = (Image *) NULL,
520 *dx_image = (Image *) NULL,
521 *dy_alpha = (Image *) NULL,
522 *dy_beta = (Image *) NULL,
523 *dy_divergent = (Image *) NULL,
524 *dy_image = (Image *) NULL,
525 *magnitude_alpha = (Image *) NULL,
526 *magnitude_beta = (Image *) NULL;
527
528 /*
529 Create X and Y gradient images for alpha image and the magnitude.
530 */
531 dx_alpha=BlendConvolveImage(alpha_image,"3x1:-0.5,0.0,0.5",exception);
532 if (dx_alpha == (Image *) NULL)
533 {
534 FreeDivergentResources();
535 return((Image *) NULL);
536 }
537 dy_alpha=BlendConvolveImage(alpha_image,"1x3:-0.5,0.0,0.5",exception);
538 if (dy_alpha == (Image *) NULL)
539 {
540 FreeDivergentResources();
541 return((Image *) NULL);
542 }
543 magnitude_alpha=BlendMagnitudeImage(dx_alpha,dy_alpha,exception);
544 if (magnitude_alpha == (Image *) NULL)
545 {
546 FreeDivergentResources();
547 return((Image *) NULL);
548 }
549 /*
550 Create X and Y gradient images for beta and the magnitude.
551 */
552 dx_beta=BlendConvolveImage(beta_image,"3x1:-0.5,0.0,0.5",exception);
553 if (dx_beta == (Image *) NULL)
554 {
555 FreeDivergentResources();
556 return((Image *) NULL);
557 }
558 dy_beta=BlendConvolveImage(beta_image,"1x3:-0.5,0.0,0.5",exception);
559 if (dy_beta == (Image *) NULL)
560 {
561 FreeDivergentResources();
562 return((Image *) NULL);
563 }
564 magnitude_beta=BlendMagnitudeImage(dx_beta,dy_beta,exception);
565 if (magnitude_beta == (Image *) NULL)
566 {
567 FreeDivergentResources();
568 return((Image *) NULL);
569 }
570 /*
571 Select alpha or beta gradient for larger of two magnitudes.
572 */
573 dx_image=BlendMaxMagnitudeImage(magnitude_alpha,magnitude_beta,dx_alpha,
574 dx_beta,exception);
575 if (dx_image == (Image *) NULL)
576 {
577 FreeDivergentResources();
578 return((Image *) NULL);
579 }
580 dy_image=BlendMaxMagnitudeImage(magnitude_alpha,magnitude_beta,dy_alpha,
581 dy_beta,exception);
582 if (dy_image == (Image *) NULL)
583 {
584 FreeDivergentResources();
585 return((Image *) NULL);
586 }
587 dx_beta=DestroyImage(dx_beta);
588 dx_alpha=DestroyImage(dx_alpha);
589 magnitude_beta=DestroyImage(magnitude_beta);
590 magnitude_alpha=DestroyImage(magnitude_alpha);
591 /*
592 Create divergence of gradients dx and dy and divide by 4 as guide image.
593 */
594 dx_divergent=BlendConvolveImage(dx_image,"3x1:-0.5,0.0,0.5",exception);
595 if (dx_divergent == (Image *) NULL)
596 {
597 FreeDivergentResources();
598 return((Image *) NULL);
599 }
600 dy_divergent=BlendConvolveImage(dy_image,"1x3:-0.5,0.0,0.5",exception);
601 if (dy_divergent == (Image *) NULL)
602 {
603 FreeDivergentResources();
604 return((Image *) NULL);
605 }
606 divergent_image=BlendSumImage(dx_divergent,dy_divergent,0.25,1.0,exception);
607 dy_divergent=DestroyImage(dy_divergent);
608 dx_divergent=DestroyImage(dx_divergent);
609 if (divergent_image == (Image *) NULL)
610 {
611 FreeDivergentResources();
612 return((Image *) NULL);
613 }
614 FreeDivergentResources();
615 return(divergent_image);
616}
617
618static MagickBooleanType BlendMaskAlphaChannel(Image *image,
619 const Image *mask_image,ExceptionInfo *exception)
620{
621 CacheView
622 *image_view,
623 *mask_view;
624
625 MagickBooleanType
626 status = MagickTrue;
627
628 ssize_t
629 y;
630
631 /*
632 Threshold the alpha channel.
633 */
634 if (SetImageAlpha(image,OpaqueAlpha,exception) == MagickFalse)
635 return(MagickFalse);
636 image_view=AcquireAuthenticCacheView(image,exception);
637 mask_view=AcquireVirtualCacheView(mask_image,exception);
638#if defined(MAGICKCORE_OPENMP_SUPPORT)
639 #pragma omp parallel for schedule(static) shared(status) \
640 magick_number_threads(image,image,image->rows,2)
641#endif
642 for (y=0; y < (ssize_t) image->rows; y++)
643 {
644 const Quantum
645 *magick_restrict p;
646
647 Quantum
648 *magick_restrict q;
649
650 ssize_t
651 x;
652
653 if (status == MagickFalse)
654 continue;
655 p=GetCacheViewVirtualPixels(mask_view,0,y,image->columns,1,exception);
656 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
657 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
658 {
659 status=MagickFalse;
660 continue;
661 }
662 for (x=0; x < (ssize_t) image->columns; x++)
663 {
664 Quantum
665 alpha = GetPixelAlpha(mask_image,p);
666
667 ssize_t
668 i = GetPixelChannelOffset(image,AlphaPixelChannel);
669
670 if (fabs((double) alpha) >= MagickEpsilon)
671 q[i]=(Quantum) 0;
672 p+=(ptrdiff_t) GetPixelChannels(mask_image);
673 q+=(ptrdiff_t) GetPixelChannels(image);
674 }
675 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
676 status=MagickFalse;
677 }
678 mask_view=DestroyCacheView(mask_view);
679 image_view=DestroyCacheView(image_view);
680 return(status);
681}
682
683static Image *BlendMeanImage(Image *image,const Image *mask_image,
684 ExceptionInfo *exception)
685{
686 CacheView
687 *alpha_view,
688 *mask_view,
689 *mean_view;
690
691 double
692 mean[MaxPixelChannels];
693
694 Image
695 *mean_image;
696
697 MagickBooleanType
698 status = MagickTrue;
699
700 ssize_t
701 j,
702 y;
703
704 /*
705 Compute the mean of the image.
706 */
707 (void) memset(mean,0,MaxPixelChannels*sizeof(*mean));
708 alpha_view=AcquireVirtualCacheView(image,exception);
709 for (y=0; y < (ssize_t) image->rows; y++)
710 {
711 const Quantum
712 *magick_restrict p;
713
714 ssize_t
715 x;
716
717 p=GetCacheViewVirtualPixels(alpha_view,0,y,image->columns,1,
718 exception);
719 if (p == (const Quantum *) NULL)
720 break;
721 for (x=0; x < (ssize_t) image->columns; x++)
722 {
723 ssize_t
724 i;
725
726 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
727 {
728 PixelChannel channel = GetPixelChannelChannel(image,i);
729 PixelTrait traits = GetPixelChannelTraits(image,channel);
730 if (traits == UndefinedPixelTrait)
731 continue;
732 mean[i]+=QuantumScale*(double) p[i];
733 }
734 p+=(ptrdiff_t) GetPixelChannels(image);
735 }
736 }
737 alpha_view=DestroyCacheView(alpha_view);
738 if (y < (ssize_t) image->rows)
739 return((Image *) NULL);
740 for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
741 mean[j]=(double) QuantumRange*mean[j]/image->columns/
742 image->rows;
743 /*
744 Replace any unmasked pixels with the mean pixel.
745 */
746 mean_image=CloneImage(image,0,0,MagickTrue,exception);
747 if (mean_image == (Image *) NULL)
748 return(mean_image);
749 mask_view=AcquireVirtualCacheView(mask_image,exception);
750 mean_view=AcquireAuthenticCacheView(mean_image,exception);
751#if defined(MAGICKCORE_OPENMP_SUPPORT)
752 #pragma omp parallel for schedule(static) shared(status) \
753 magick_number_threads(mask_image,mean_image,mean_image->rows,4)
754#endif
755 for (y=0; y < (ssize_t) mean_image->rows; y++)
756 {
757 const Quantum
758 *magick_restrict p;
759
760 Quantum
761 *magick_restrict q;
762
763 ssize_t
764 x;
765
766 if (status == MagickFalse)
767 continue;
768 p=GetCacheViewVirtualPixels(mask_view,0,y,mean_image->columns,1,exception);
769 q=GetCacheViewAuthenticPixels(mean_view,0,y,mean_image->columns,1,
770 exception);
771 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
772 {
773 status=MagickFalse;
774 continue;
775 }
776 for (x=0; x < (ssize_t) mean_image->columns; x++)
777 {
778 Quantum
779 alpha = GetPixelAlpha(mask_image,p),
780 mask = GetPixelReadMask(mask_image,p);
781
782 ssize_t
783 i;
784
785 for (i=0; i < (ssize_t) GetPixelChannels(mean_image); i++)
786 {
787 PixelChannel channel = GetPixelChannelChannel(mean_image,i);
788 PixelTrait traits = GetPixelChannelTraits(mean_image,channel);
789 if (traits == UndefinedPixelTrait)
790 continue;
791 if (mask <= (QuantumRange/2))
792 q[i]=(Quantum) 0;
793 else
794 if (fabs((double) alpha) >= MagickEpsilon)
795 q[i]=ClampToQuantum(mean[i]);
796 }
797 p+=(ptrdiff_t) GetPixelChannels(mask_image);
798 q+=(ptrdiff_t) GetPixelChannels(mean_image);
799 }
800 if (SyncCacheViewAuthenticPixels(mean_view,exception) == MagickFalse)
801 status=MagickFalse;
802 }
803 mask_view=DestroyCacheView(mask_view);
804 mean_view=DestroyCacheView(mean_view);
805 if (status == MagickFalse)
806 mean_image=DestroyImage(mean_image);
807 return(mean_image);
808}
809
810static MagickBooleanType BlendRMSEResidual(const Image *alpha_image,
811 const Image *beta_image,double *residual,ExceptionInfo *exception)
812{
813 CacheView
814 *alpha_view,
815 *beta_view;
816
817 double
818 area = 0.0,
819 channel_residual = 0.0;
820
821 MagickBooleanType
822 status = MagickTrue;
823
824 size_t
825 columns = MagickMax(alpha_image->columns,beta_image->columns),
826 rows = MagickMax(alpha_image->rows,beta_image->rows);
827
828 ssize_t
829 y;
830
831 alpha_view=AcquireVirtualCacheView(alpha_image,exception);
832 beta_view=AcquireVirtualCacheView(beta_image,exception);
833#if defined(MAGICKCORE_OPENMP_SUPPORT)
834 #pragma omp parallel for schedule(static) shared(status) \
835 reduction(+:area) reduction(+:channel_residual) \
836 magick_number_threads(alpha_image,alpha_image,rows,1)
837#endif
838 for (y=0; y < (ssize_t) rows; y++)
839 {
840 const Quantum
841 *magick_restrict p,
842 *magick_restrict q;
843
844 ssize_t
845 x;
846
847 if (status == MagickFalse)
848 continue;
849 p=GetCacheViewVirtualPixels(alpha_view,0,y,columns,1,exception);
850 q=GetCacheViewVirtualPixels(beta_view,0,y,columns,1,exception);
851 if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL))
852 {
853 status=MagickFalse;
854 continue;
855 }
856 channel_residual=0.0;
857 for (x=0; x < (ssize_t) columns; x++)
858 {
859 double
860 Da,
861 Sa;
862
863 ssize_t
864 i;
865
866 if ((GetPixelReadMask(alpha_image,p) <= (QuantumRange/2)) ||
867 (GetPixelReadMask(beta_image,q) <= (QuantumRange/2)))
868 {
869 p+=(ptrdiff_t) GetPixelChannels(alpha_image);
870 q+=(ptrdiff_t) GetPixelChannels(beta_image);
871 continue;
872 }
873 Sa=QuantumScale*(double) GetPixelAlpha(alpha_image,p);
874 Da=QuantumScale*(double) GetPixelAlpha(beta_image,q);
875 for (i=0; i < (ssize_t) GetPixelChannels(alpha_image); i++)
876 {
877 double
878 distance;
879
880 PixelChannel channel = GetPixelChannelChannel(alpha_image,i);
881 PixelTrait traits = GetPixelChannelTraits(alpha_image,channel);
882 PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
883 if ((traits == UndefinedPixelTrait) ||
884 (beta_traits == UndefinedPixelTrait) ||
885 ((beta_traits & UpdatePixelTrait) == 0))
886 continue;
887 if (channel == AlphaPixelChannel)
888 distance=QuantumScale*((double) p[i]-(double) GetPixelChannel(
889 beta_image,channel,q));
890 else
891 distance=QuantumScale*(Sa*(double) p[i]-Da*(double) GetPixelChannel(
892 beta_image,channel,q));
893 channel_residual+=distance*distance;
894 }
895 area++;
896 p+=(ptrdiff_t) GetPixelChannels(alpha_image);
897 q+=(ptrdiff_t) GetPixelChannels(beta_image);
898 }
899 }
900 beta_view=DestroyCacheView(beta_view);
901 alpha_view=DestroyCacheView(alpha_view);
902 area=MagickSafeReciprocal(area);
903 *residual=sqrt(area*channel_residual/(double) GetImageChannels(alpha_image));
904 return(status);
905}
906
907static MagickBooleanType CompositeOverImage(Image *image,
908 const Image *source_image,const MagickBooleanType clip_to_self,
909 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
910{
911#define CompositeImageTag "Composite/Image"
912
913 CacheView
914 *image_view,
915 *source_view;
916
917 const char
918 *value;
919
920 MagickBooleanType
921 clamp,
922 status;
923
924 MagickOffsetType
925 progress;
926
927 ssize_t
928 y;
929
930 /*
931 Composite image.
932 */
933 status=MagickTrue;
934 progress=0;
935 clamp=MagickTrue;
936 value=GetImageArtifact(image,"compose:clamp");
937 if (value != (const char *) NULL)
938 clamp=IsStringTrue(value);
939 status=MagickTrue;
940 progress=0;
941 source_view=AcquireVirtualCacheView(source_image,exception);
942 image_view=AcquireAuthenticCacheView(image,exception);
943#if defined(MAGICKCORE_OPENMP_SUPPORT)
944 #pragma omp parallel for schedule(static) shared(progress,status) \
945 magick_number_threads(source_image,image,image->rows,1)
946#endif
947 for (y=0; y < (ssize_t) image->rows; y++)
948 {
949 const Quantum
950 *pixels;
951
952 PixelInfo
953 canvas_pixel,
954 source_pixel;
955
956 const Quantum
957 *magick_restrict p;
958
959 Quantum
960 *magick_restrict q;
961
962 ssize_t
963 x;
964
965 if (status == MagickFalse)
966 continue;
967 if (clip_to_self != MagickFalse)
968 {
969 if (y < y_offset)
970 continue;
971 if ((y-y_offset) >= (ssize_t) source_image->rows)
972 continue;
973 }
974 /*
975 If pixels is NULL, y is outside overlay region.
976 */
977 pixels=(Quantum *) NULL;
978 p=(Quantum *) NULL;
979 if ((y >= y_offset) &&
980 ((y-y_offset) < (ssize_t) source_image->rows))
981 {
982 p=GetCacheViewVirtualPixels(source_view,0,CastDoubleToSsizeT((double) y-
983 y_offset),source_image->columns,1,exception);
984 if (p == (const Quantum *) NULL)
985 {
986 status=MagickFalse;
987 continue;
988 }
989 pixels=p;
990 if (x_offset < 0)
991 p-=(ptrdiff_t) CastDoubleToSsizeT((double) x_offset*
992 GetPixelChannels(source_image));
993 }
994 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
995 if (q == (Quantum *) NULL)
996 {
997 status=MagickFalse;
998 continue;
999 }
1000 GetPixelInfo(image,&canvas_pixel);
1001 GetPixelInfo(source_image,&source_pixel);
1002 for (x=0; x < (ssize_t) image->columns; x++)
1003 {
1004 double
1005 gamma;
1006
1007 MagickRealType
1008 alpha,
1009 Da,
1010 Dc,
1011 Dca,
1012 Sa,
1013 Sc,
1014 Sca;
1015
1016 ssize_t
1017 i;
1018
1019 size_t
1020 channels;
1021
1022 if (clip_to_self != MagickFalse)
1023 {
1024 if (x < x_offset)
1025 {
1026 q+=(ptrdiff_t) GetPixelChannels(image);
1027 continue;
1028 }
1029 if ((x-x_offset) >= (ssize_t) source_image->columns)
1030 break;
1031 }
1032 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1033 ((x-x_offset) >= (ssize_t) source_image->columns))
1034 {
1035 Quantum
1036 source[MaxPixelChannels];
1037
1038 /*
1039 Virtual composite:
1040 Sc: source color.
1041 Dc: canvas color.
1042 */
1043 (void) GetOneVirtualPixel(source_image,
1044 CastDoubleToSsizeT((double) x-x_offset),
1045 CastDoubleToSsizeT((double) y-y_offset),source,exception);
1046 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1047 {
1048 MagickRealType
1049 pixel;
1050
1051 PixelChannel channel = GetPixelChannelChannel(image,i);
1052 PixelTrait traits = GetPixelChannelTraits(image,channel);
1053 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1054 channel);
1055 if ((traits == UndefinedPixelTrait) ||
1056 (source_traits == UndefinedPixelTrait))
1057 continue;
1058 if (channel == AlphaPixelChannel)
1059 pixel=(MagickRealType) TransparentAlpha;
1060 else
1061 pixel=(MagickRealType) q[i];
1062 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1063 ClampToQuantum(pixel);
1064 }
1065 q+=(ptrdiff_t) GetPixelChannels(image);
1066 continue;
1067 }
1068 /*
1069 Authentic composite:
1070 Sa: normalized source alpha.
1071 Da: normalized canvas alpha.
1072 */
1073 Sa=QuantumScale*(double) GetPixelAlpha(source_image,p);
1074 Da=QuantumScale*(double) GetPixelAlpha(image,q);
1075 alpha=Sa+Da-Sa*Da;
1076 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1077 {
1078 MagickRealType
1079 pixel;
1080
1081 PixelChannel channel = GetPixelChannelChannel(image,i);
1082 PixelTrait traits = GetPixelChannelTraits(image,channel);
1083 PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
1084 if (traits == UndefinedPixelTrait)
1085 continue;
1086 if ((source_traits == UndefinedPixelTrait) &&
1087 (channel != AlphaPixelChannel))
1088 continue;
1089 if (channel == AlphaPixelChannel)
1090 {
1091 /*
1092 Set alpha channel.
1093 */
1094 pixel=(double) QuantumRange*alpha;
1095 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1096 ClampToQuantum(pixel);
1097 continue;
1098 }
1099 /*
1100 Sc: source color.
1101 Dc: canvas color.
1102 */
1103 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1104 Dc=(MagickRealType) q[i];
1105 if ((traits & CopyPixelTrait) != 0)
1106 {
1107 /*
1108 Copy channel.
1109 */
1110 q[i]=ClampToQuantum(Sc);
1111 continue;
1112 }
1113 /*
1114 Porter-Duff compositions:
1115 Sca: source normalized color multiplied by alpha.
1116 Dca: normalized canvas color multiplied by alpha.
1117 */
1118 Sca=QuantumScale*Sa*Sc;
1119 Dca=QuantumScale*Da*Dc;
1120 gamma=MagickSafeReciprocal(alpha);
1121 pixel=(double) QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
1122 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
1123 }
1124 p+=(ptrdiff_t) GetPixelChannels(source_image);
1125 channels=GetPixelChannels(source_image);
1126 if (p >= (pixels+channels*source_image->columns))
1127 p=pixels;
1128 q+=(ptrdiff_t) GetPixelChannels(image);
1129 }
1130 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1131 status=MagickFalse;
1132 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1133 {
1134 MagickBooleanType
1135 proceed;
1136
1137#if defined(MAGICKCORE_OPENMP_SUPPORT)
1138 #pragma omp atomic
1139#endif
1140 progress++;
1141 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
1142 if (proceed == MagickFalse)
1143 status=MagickFalse;
1144 }
1145 }
1146 source_view=DestroyCacheView(source_view);
1147 image_view=DestroyCacheView(image_view);
1148 return(status);
1149}
1150
1151static MagickBooleanType SaliencyBlendImage(Image *image,
1152 const Image *source_image,const ssize_t x_offset,const ssize_t y_offset,
1153 const double iterations,const double residual_threshold,const size_t tick,
1154 ExceptionInfo *exception)
1155{
1156 Image
1157 *crop_image,
1158 *divergent_image,
1159 *relax_image,
1160 *residual_image = (Image *) NULL;
1161
1162 KernelInfo
1163 *kernel_info;
1164
1165 MagickBooleanType
1166 status = MagickTrue,
1167 verbose = MagickFalse;
1168
1169 RectangleInfo
1170 crop_info = {
1171 source_image->columns,
1172 source_image->rows,
1173 x_offset,
1174 y_offset
1175 };
1176
1177 ssize_t
1178 i;
1179
1180 /*
1181 Saliency blend composite operator.
1182 */
1183 crop_image=CropImage(image,&crop_info,exception);
1184 if (crop_image == (Image *) NULL)
1185 return(MagickFalse);
1186 DisableCompositeClampUnlessSpecified(crop_image);
1187 divergent_image=BlendDivergentImage(crop_image,source_image,exception);
1188 if (divergent_image == (Image *) NULL)
1189 {
1190 crop_image=DestroyImage(crop_image);
1191 return(MagickFalse);
1192 }
1193 (void) ResetImagePage(crop_image,"0x0+0+0");
1194 relax_image=BlendMeanImage(crop_image,source_image,exception);
1195 if (relax_image == (Image *) NULL)
1196 {
1197 crop_image=DestroyImage(crop_image);
1198 divergent_image=DestroyImage(divergent_image);
1199 return(MagickFalse);
1200 }
1201 status=BlendMaskAlphaChannel(crop_image,source_image,exception);
1202 if (status == MagickFalse)
1203 {
1204 crop_image=DestroyImage(crop_image);
1205 divergent_image=DestroyImage(divergent_image);
1206 return(MagickFalse);
1207 }
1208 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1209 if (residual_image == (Image *) NULL)
1210 {
1211 crop_image=DestroyImage(crop_image);
1212 relax_image=DestroyImage(relax_image);
1213 return(MagickFalse);
1214 }
1215 /*
1216 Convolve relaxed image and blur area of interest.
1217 */
1218 kernel_info=AcquireKernelInfo("3x3:0,0.25,0,0.25,0,0.25,0,0.25,0",exception);
1219 if (kernel_info == (KernelInfo *) NULL)
1220 {
1221 crop_image=DestroyImage(crop_image);
1222 residual_image=DestroyImage(residual_image);
1223 relax_image=DestroyImage(relax_image);
1224 return(MagickFalse);
1225 }
1226 verbose=IsStringTrue(GetImageArtifact(image,"verbose"));
1227 if (verbose != MagickFalse)
1228 (void) FormatLocaleFile(stderr,"saliency blending:\n");
1229 for (i=0; i < (ssize_t) iterations; i++)
1230 {
1231 double
1232 residual = 1.0;
1233
1234 Image
1235 *convolve_image,
1236 *sum_image;
1237
1238 convolve_image=ConvolveImage(relax_image,kernel_info,exception);
1239 if (convolve_image == (Image *) NULL)
1240 break;
1241 relax_image=DestroyImage(relax_image);
1242 relax_image=convolve_image;
1243 sum_image=BlendSumImage(relax_image,divergent_image,1.0,-1.0,exception);
1244 if (sum_image == (Image *) NULL)
1245 break;
1246 relax_image=DestroyImage(relax_image);
1247 relax_image=sum_image;
1248 status=CompositeOverImage(relax_image,crop_image,MagickTrue,0,0,exception);
1249 if (status == MagickFalse)
1250 break;
1251 status=BlendRMSEResidual(relax_image,residual_image,&residual,exception);
1252 if (status == MagickFalse)
1253 break;
1254 if ((verbose != MagickFalse) && ((i % MagickMax(tick,1)) == 0))
1255 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double) residual);
1256 if (residual < residual_threshold)
1257 {
1258 if (verbose != MagickFalse)
1259 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double)
1260 residual);
1261 break;
1262 }
1263 residual_image=DestroyImage(residual_image);
1264 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1265 if (residual_image == (Image *) NULL)
1266 break;
1267 }
1268 kernel_info=DestroyKernelInfo(kernel_info);
1269 crop_image=DestroyImage(crop_image);
1270 divergent_image=DestroyImage(divergent_image);
1271 residual_image=DestroyImage(residual_image);
1272 /*
1273 Composite relaxed over the background image.
1274 */
1275 status=CompositeOverImage(image,relax_image,MagickTrue,x_offset,y_offset,
1276 exception);
1277 relax_image=DestroyImage(relax_image);
1278 return(status);
1279}
1280
1281static MagickBooleanType SeamlessBlendImage(Image *image,
1282 const Image *source_image,const ssize_t x_offset,const ssize_t y_offset,
1283 const double iterations,const double residual_threshold,const size_t tick,
1284 ExceptionInfo *exception)
1285{
1286 Image
1287 *crop_image,
1288 *foreground_image,
1289 *mean_image,
1290 *relax_image,
1291 *residual_image,
1292 *sum_image;
1293
1294 KernelInfo
1295 *kernel_info;
1296
1297 MagickBooleanType
1298 status = MagickTrue,
1299 verbose = MagickFalse;
1300
1301 RectangleInfo
1302 crop_info = {
1303 source_image->columns,
1304 source_image->rows,
1305 x_offset,
1306 y_offset
1307 };
1308
1309 ssize_t
1310 i;
1311
1312 /*
1313 Seamless blend composite operator.
1314 */
1315 crop_image=CropImage(image,&crop_info,exception);
1316 if (crop_image == (Image *) NULL)
1317 return(MagickFalse);
1318 DisableCompositeClampUnlessSpecified(crop_image);
1319 (void) ResetImagePage(crop_image,"0x0+0+0");
1320 sum_image=BlendSumImage(crop_image,source_image,1.0,-1.0,exception);
1321 crop_image=DestroyImage(crop_image);
1322 if (sum_image == (Image *) NULL)
1323 return(MagickFalse);
1324 mean_image=BlendMeanImage(sum_image,source_image,exception);
1325 sum_image=DestroyImage(sum_image);
1326 if (mean_image == (Image *) NULL)
1327 return(MagickFalse);
1328 relax_image=CloneImage(mean_image,0,0,MagickTrue,exception);
1329 if (relax_image == (Image *) NULL)
1330 {
1331 mean_image=DestroyImage(mean_image);
1332 return(MagickFalse);
1333 }
1334 status=BlendMaskAlphaChannel(mean_image,source_image,exception);
1335 if (status == MagickFalse)
1336 {
1337 relax_image=DestroyImage(relax_image);
1338 mean_image=DestroyImage(mean_image);
1339 return(MagickFalse);
1340 }
1341 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1342 if (residual_image == (Image *) NULL)
1343 {
1344 relax_image=DestroyImage(relax_image);
1345 mean_image=DestroyImage(mean_image);
1346 return(MagickFalse);
1347 }
1348 /*
1349 Convolve relaxed image and blur area of interest.
1350 */
1351 kernel_info=AcquireKernelInfo("3x3:0,0.25,0,0.25,0,0.25,0,0.25,0",exception);
1352 if (kernel_info == (KernelInfo *) NULL)
1353 {
1354 residual_image=DestroyImage(residual_image);
1355 relax_image=DestroyImage(relax_image);
1356 mean_image=DestroyImage(mean_image);
1357 return(MagickFalse);
1358 }
1359 verbose=IsStringTrue(GetImageArtifact(image,"verbose"));
1360 if (verbose != MagickFalse)
1361 (void) FormatLocaleFile(stderr,"seamless blending:\n");
1362 for (i=0; i < (ssize_t) iterations; i++)
1363 {
1364 double
1365 residual = 1.0;
1366
1367 Image
1368 *convolve_image;
1369
1370 convolve_image=ConvolveImage(relax_image,kernel_info,exception);
1371 if (convolve_image == (Image *) NULL)
1372 break;
1373 relax_image=DestroyImage(relax_image);
1374 relax_image=convolve_image;
1375 status=CompositeOverImage(relax_image,mean_image,MagickTrue,0,0,exception);
1376 if (status == MagickFalse)
1377 break;
1378 status=BlendRMSEResidual(relax_image,residual_image,&residual,exception);
1379 if (status == MagickFalse)
1380 break;
1381 if ((verbose != MagickFalse) && ((i % MagickMax(tick,1)) == 0))
1382 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double) residual);
1383 if (residual < residual_threshold)
1384 {
1385 if (verbose != MagickFalse)
1386 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double)
1387 residual);
1388 break;
1389 }
1390 if (residual_image != (Image *) NULL)
1391 residual_image=DestroyImage(residual_image);
1392 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1393 if (residual_image == (Image *) NULL)
1394 break;
1395 }
1396 kernel_info=DestroyKernelInfo(kernel_info);
1397 mean_image=DestroyImage(mean_image);
1398 residual_image=DestroyImage(residual_image);
1399 /*
1400 Composite the foreground image over the background image.
1401 */
1402 foreground_image=BlendSumImage(source_image,relax_image,1.0,1.0,exception);
1403 relax_image=DestroyImage(relax_image);
1404 if (foreground_image == (Image *) NULL)
1405 return(MagickFalse);
1406 (void) SetImageMask(foreground_image,ReadPixelMask,(const Image *) NULL,
1407 exception);
1408 status=CompositeOverImage(image,foreground_image,MagickTrue,x_offset,y_offset,
1409 exception);
1410 foreground_image=DestroyImage(foreground_image);
1411 return(status);
1412}
1413
1414MagickExport MagickBooleanType CompositeImage(Image *image,
1415 const Image *composite,const CompositeOperator compose,
1416 const MagickBooleanType clip_to_self,const ssize_t x_offset,
1417 const ssize_t y_offset,ExceptionInfo *exception)
1418{
1419#define CompositeImageTag "Composite/Image"
1420
1421 CacheView
1422 *source_view,
1423 *image_view;
1424
1425 ColorspaceType
1426 colorspace = HCLColorspace;
1427
1428 const char
1429 *artifact;
1430
1431 double
1432 white_luminance = 10000.0;
1433
1434 GeometryInfo
1435 geometry_info;
1436
1437 IlluminantType
1438 illuminant = D65Illuminant;
1439
1440 Image
1441 *canvas_image,
1442 *source_image;
1443
1444 MagickBooleanType
1445 clamp,
1446 compose_sync,
1447 status;
1448
1449 MagickOffsetType
1450 progress;
1451
1452 MagickRealType
1453 amount,
1454 canvas_dissolve,
1455 midpoint,
1456 percent_luma,
1457 percent_chroma,
1458 source_dissolve,
1459 threshold;
1460
1461 MagickStatusType
1462 flags;
1463
1464 RandomInfo
1465 *random_info;
1466
1467 ssize_t
1468 y;
1469
1470 assert(image != (Image *) NULL);
1471 assert(image->signature == MagickCoreSignature);
1472 assert(composite != (Image *) NULL);
1473 assert(composite->signature == MagickCoreSignature);
1474 if (IsEventLogging() != MagickFalse)
1475 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1476 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1477 return(MagickFalse);
1478 source_image=CloneImage(composite,0,0,MagickTrue,exception);
1479 if (source_image == (const Image *) NULL)
1480 return(MagickFalse);
1481 (void) SetImageColorspace(source_image,image->colorspace,exception);
1482 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
1483 {
1484 status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
1485 y_offset,exception);
1486 source_image=DestroyImage(source_image);
1487 return(status);
1488 }
1489 amount=0.5;
1490 canvas_image=(Image *) NULL;
1491 canvas_dissolve=1.0;
1492 white_luminance=10000.0;
1493 artifact=GetImageArtifact(image,"compose:white-luminance");
1494 if (artifact != (const char *) NULL)
1495 white_luminance=StringToDouble(artifact,(char **) NULL);
1496 artifact=GetImageArtifact(image,"compose:illuminant");
1497 if (artifact != (const char *) NULL)
1498 {
1499 ssize_t
1500 illuminant_type;
1501
1502 illuminant_type=ParseCommandOption(MagickIlluminantOptions,MagickFalse,
1503 artifact);
1504 if (illuminant_type < 0)
1505 illuminant=UndefinedIlluminant;
1506 else
1507 illuminant=(IlluminantType) illuminant_type;
1508 }
1509 artifact=GetImageArtifact(image,"compose:colorspace");
1510 if (artifact != (const char *) NULL)
1511 {
1512 ssize_t
1513 colorspace_type;
1514
1515 colorspace_type=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
1516 artifact);
1517 if (colorspace_type < 0)
1518 colorspace=UndefinedColorspace;
1519 else
1520 colorspace=(ColorspaceType) colorspace_type;
1521 }
1522 clamp=MagickTrue;
1523 artifact=GetImageArtifact(image,"compose:clamp");
1524 if (artifact != (const char *) NULL)
1525 clamp=IsStringTrue(artifact);
1526 compose_sync=MagickTrue;
1527 artifact=GetImageArtifact(image,"compose:sync");
1528 if (artifact != (const char *) NULL)
1529 compose_sync=IsStringTrue(artifact);
1530 SetGeometryInfo(&geometry_info);
1531 percent_luma=100.0;
1532 percent_chroma=100.0;
1533 source_dissolve=1.0;
1534 threshold=0.05f;
1535 switch (compose)
1536 {
1537 case CopyCompositeOp:
1538 {
1539 if ((x_offset < 0) || (y_offset < 0))
1540 break;
1541 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
1542 break;
1543 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
1544 break;
1545 if ((source_image->alpha_trait == UndefinedPixelTrait) &&
1546 (image->alpha_trait != UndefinedPixelTrait))
1547 (void) SetImageAlphaChannel(source_image,OpaqueAlphaChannel,exception);
1548 status=MagickTrue;
1549 source_view=AcquireVirtualCacheView(source_image,exception);
1550 image_view=AcquireAuthenticCacheView(image,exception);
1551#if defined(MAGICKCORE_OPENMP_SUPPORT)
1552 #pragma omp parallel for schedule(static) shared(status) \
1553 magick_number_threads(source_image,image,source_image->rows,4)
1554#endif
1555 for (y=0; y < (ssize_t) source_image->rows; y++)
1556 {
1557 MagickBooleanType
1558 sync;
1559
1560 const Quantum
1561 *p;
1562
1563 Quantum
1564 *q;
1565
1566 ssize_t
1567 x;
1568
1569 if (status == MagickFalse)
1570 continue;
1571 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1572 exception);
1573 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1574 source_image->columns,1,exception);
1575 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1576 {
1577 status=MagickFalse;
1578 continue;
1579 }
1580 for (x=0; x < (ssize_t) source_image->columns; x++)
1581 {
1582 ssize_t
1583 i;
1584
1585 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
1586 {
1587 p+=(ptrdiff_t) GetPixelChannels(source_image);
1588 q+=(ptrdiff_t) GetPixelChannels(image);
1589 continue;
1590 }
1591 for (i=0; i < (ssize_t) GetPixelChannels(source_image); i++)
1592 {
1593 PixelChannel channel = GetPixelChannelChannel(source_image,i);
1594 PixelTrait source_traits = GetPixelChannelTraits(source_image,
1595 channel);
1596 PixelTrait traits = GetPixelChannelTraits(image,channel);
1597 if ((source_traits == UndefinedPixelTrait) ||
1598 (traits == UndefinedPixelTrait))
1599 continue;
1600 SetPixelChannel(image,channel,p[i],q);
1601 }
1602 p+=(ptrdiff_t) GetPixelChannels(source_image);
1603 q+=(ptrdiff_t) GetPixelChannels(image);
1604 }
1605 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1606 if (sync == MagickFalse)
1607 status=MagickFalse;
1608 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1609 {
1610 MagickBooleanType
1611 proceed;
1612
1613 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
1614 y,image->rows);
1615 if (proceed == MagickFalse)
1616 status=MagickFalse;
1617 }
1618 }
1619 source_view=DestroyCacheView(source_view);
1620 image_view=DestroyCacheView(image_view);
1621 source_image=DestroyImage(source_image);
1622 return(status);
1623 }
1624 case IntensityCompositeOp:
1625 {
1626 if ((x_offset < 0) || (y_offset < 0))
1627 break;
1628 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
1629 break;
1630 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
1631 break;
1632 status=MagickTrue;
1633 source_view=AcquireVirtualCacheView(source_image,exception);
1634 image_view=AcquireAuthenticCacheView(image,exception);
1635#if defined(MAGICKCORE_OPENMP_SUPPORT)
1636 #pragma omp parallel for schedule(static) shared(status) \
1637 magick_number_threads(source_image,image,source_image->rows,4)
1638#endif
1639 for (y=0; y < (ssize_t) source_image->rows; y++)
1640 {
1641 MagickBooleanType
1642 sync;
1643
1644 const Quantum
1645 *p;
1646
1647 Quantum
1648 *q;
1649
1650 ssize_t
1651 x;
1652
1653 if (status == MagickFalse)
1654 continue;
1655 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1656 exception);
1657 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1658 source_image->columns,1,exception);
1659 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1660 {
1661 status=MagickFalse;
1662 continue;
1663 }
1664 for (x=0; x < (ssize_t) source_image->columns; x++)
1665 {
1666 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
1667 {
1668 p+=(ptrdiff_t) GetPixelChannels(source_image);
1669 q+=(ptrdiff_t) GetPixelChannels(image);
1670 continue;
1671 }
1672 SetPixelAlpha(image,clamp != MagickFalse ?
1673 ClampPixel(GetPixelIntensity(source_image,p)) :
1674 ClampToQuantum(GetPixelIntensity(source_image,p)),q);
1675 p+=(ptrdiff_t) GetPixelChannels(source_image);
1676 q+=(ptrdiff_t) GetPixelChannels(image);
1677 }
1678 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1679 if (sync == MagickFalse)
1680 status=MagickFalse;
1681 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1682 {
1683 MagickBooleanType
1684 proceed;
1685
1686 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
1687 y,image->rows);
1688 if (proceed == MagickFalse)
1689 status=MagickFalse;
1690 }
1691 }
1692 source_view=DestroyCacheView(source_view);
1693 image_view=DestroyCacheView(image_view);
1694 source_image=DestroyImage(source_image);
1695 return(status);
1696 }
1697 case CopyAlphaCompositeOp:
1698 case ChangeMaskCompositeOp:
1699 {
1700 /*
1701 Modify canvas outside the overlaid region and require an alpha
1702 channel to exist, to add transparency.
1703 */
1704 if ((image->alpha_trait & BlendPixelTrait) == 0)
1705 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1706 break;
1707 }
1708 case BlurCompositeOp:
1709 {
1710 CacheView
1711 *canvas_view;
1712
1713 double
1714 angle_range,
1715 angle_start,
1716 height,
1717 width;
1718
1719 PixelInfo
1720 pixel;
1721
1722 ResampleFilter
1723 *resample_filter;
1724
1725 SegmentInfo
1726 blur;
1727
1728 /*
1729 Blur Image by resampling dictated by an overlay gradient map:
1730 X = red_channel; Y = green_channel; compose:args =
1731 x_scale[,y_scale[,angle]].
1732 */
1733 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
1734 if (canvas_image == (Image *) NULL)
1735 {
1736 source_image=DestroyImage(source_image);
1737 return(MagickFalse);
1738 }
1739 /*
1740 Gather the maximum blur sigma values from user.
1741 */
1742 flags=NoValue;
1743 artifact=GetImageArtifact(image,"compose:args");
1744 if (artifact != (const char *) NULL)
1745 flags=ParseGeometry(artifact,&geometry_info);
1746 if ((flags & WidthValue) == 0)
1747 {
1748 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1749 "InvalidSetting","'%s' '%s'","compose:args",artifact);
1750 source_image=DestroyImage(source_image);
1751 canvas_image=DestroyImage(canvas_image);
1752 return(MagickFalse);
1753 }
1754 /*
1755 Users input sigma now needs to be converted to the EWA ellipse size.
1756 The filter defaults to a sigma of 0.5 so to make this match the users
1757 input the ellipse size needs to be doubled.
1758 */
1759 width=2.0*geometry_info.rho;
1760 height=width;
1761 if ((flags & HeightValue) != 0)
1762 height=2.0*geometry_info.sigma;
1763 /*
1764 Default the unrotated ellipse width and height axis vectors.
1765 */
1766 blur.x1=width;
1767 blur.x2=0.0;
1768 blur.y1=0.0;
1769 blur.y2=height;
1770 if ((flags & XValue) != 0 )
1771 {
1772 MagickRealType
1773 angle;
1774
1775 /*
1776 Rotate vectors if a rotation angle is given.
1777 */
1778 angle=DegreesToRadians(geometry_info.xi);
1779 blur.x1=width*cos(angle);
1780 blur.x2=width*sin(angle);
1781 blur.y1=(-height*sin(angle));
1782 blur.y2=height*cos(angle);
1783 }
1784 angle_start=0.0;
1785 angle_range=0.0;
1786 if ((flags & YValue) != 0 )
1787 {
1788 /*
1789 Lets set a angle range and calculate in the loop.
1790 */
1791 angle_start=DegreesToRadians(geometry_info.xi);
1792 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1793 }
1794 /*
1795 Set up a gaussian cylindrical filter for EWA Blurring.
1796
1797 As the minimum ellipse radius of support*1.0 the EWA algorithm
1798 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
1799 This means that even 'No Blur' will be still a little blurry! The
1800 solution (as well as the problem of preventing any user expert filter
1801 settings, is to set our own user settings, restore them afterwards.
1802 */
1803 resample_filter=AcquireResampleFilter(image,exception);
1804 SetResampleFilter(resample_filter,GaussianFilter);
1805 /*
1806 Perform the variable blurring of each pixel in image.
1807 */
1808 GetPixelInfo(image,&pixel);
1809 source_view=AcquireVirtualCacheView(source_image,exception);
1810 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1811 for (y=0; y < (ssize_t) source_image->rows; y++)
1812 {
1813 MagickBooleanType
1814 sync;
1815
1816 const Quantum
1817 *magick_restrict p;
1818
1819 Quantum
1820 *magick_restrict q;
1821
1822 ssize_t
1823 x;
1824
1825 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1826 continue;
1827 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1828 exception);
1829 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1830 exception);
1831 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1832 break;
1833 for (x=0; x < (ssize_t) source_image->columns; x++)
1834 {
1835 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1836 {
1837 p+=(ptrdiff_t) GetPixelChannels(source_image);
1838 continue;
1839 }
1840 if (fabs(angle_range) > MagickEpsilon)
1841 {
1842 MagickRealType
1843 angle;
1844
1845 angle=angle_start+angle_range*QuantumScale*(double)
1846 GetPixelBlue(source_image,p);
1847 blur.x1=width*cos(angle);
1848 blur.x2=width*sin(angle);
1849 blur.y1=(-height*sin(angle));
1850 blur.y2=height*cos(angle);
1851 }
1852 ScaleResampleFilter(resample_filter,
1853 blur.x1*QuantumScale*(double) GetPixelRed(source_image,p),
1854 blur.y1*QuantumScale*(double) GetPixelGreen(source_image,p),
1855 blur.x2*QuantumScale*(double) GetPixelRed(source_image,p),
1856 blur.y2*QuantumScale*(double) GetPixelGreen(source_image,p) );
1857 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1858 (double) y_offset+y,&pixel,exception);
1859 SetPixelViaPixelInfo(canvas_image,&pixel,q);
1860 p+=(ptrdiff_t) GetPixelChannels(source_image);
1861 q+=(ptrdiff_t) GetPixelChannels(canvas_image);
1862 }
1863 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1864 if (sync == MagickFalse)
1865 break;
1866 }
1867 resample_filter=DestroyResampleFilter(resample_filter);
1868 source_view=DestroyCacheView(source_view);
1869 canvas_view=DestroyCacheView(canvas_view);
1870 source_image=DestroyImage(source_image);
1871 source_image=canvas_image;
1872 break;
1873 }
1874 case DisplaceCompositeOp:
1875 case DistortCompositeOp:
1876 {
1877 CacheView
1878 *canvas_view;
1879
1880 MagickRealType
1881 horizontal_scale,
1882 vertical_scale;
1883
1884 PixelInfo
1885 pixel;
1886
1887 PointInfo
1888 center,
1889 offset;
1890
1891 /*
1892 Displace/Distort based on overlay gradient map:
1893 X = red_channel; Y = green_channel;
1894 compose:args = x_scale[,y_scale[,center.x,center.y]]
1895 */
1896 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
1897 if (canvas_image == (Image *) NULL)
1898 {
1899 source_image=DestroyImage(source_image);
1900 return(MagickFalse);
1901 }
1902 SetGeometryInfo(&geometry_info);
1903 flags=NoValue;
1904 artifact=GetImageArtifact(image,"compose:args");
1905 if (artifact != (char *) NULL)
1906 flags=ParseGeometry(artifact,&geometry_info);
1907 if ((flags & (WidthValue | HeightValue)) == 0 )
1908 {
1909 if ((flags & AspectValue) == 0)
1910 {
1911 horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
1912 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
1913 }
1914 else
1915 {
1916 horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
1917 vertical_scale=(MagickRealType) (image->rows-1)/2.0;
1918 }
1919 }
1920 else
1921 {
1922 horizontal_scale=geometry_info.rho;
1923 vertical_scale=geometry_info.sigma;
1924 if ((flags & PercentValue) != 0)
1925 {
1926 if ((flags & AspectValue) == 0)
1927 {
1928 horizontal_scale*=(source_image->columns-1)/200.0;
1929 vertical_scale*=(source_image->rows-1)/200.0;
1930 }
1931 else
1932 {
1933 horizontal_scale*=(image->columns-1)/200.0;
1934 vertical_scale*=(image->rows-1)/200.0;
1935 }
1936 }
1937 if ((flags & HeightValue) == 0)
1938 vertical_scale=horizontal_scale;
1939 }
1940 /*
1941 Determine fixed center point for absolute distortion map
1942 Absolute distort ==
1943 Displace offset relative to a fixed absolute point
1944 Select that point according to +X+Y user inputs.
1945 default = center of overlay image
1946 arg flag '!' = locations/percentage relative to background image
1947 */
1948 center.x=(MagickRealType) x_offset;
1949 center.y=(MagickRealType) y_offset;
1950 if (compose == DistortCompositeOp)
1951 {
1952 if ((flags & XValue) == 0)
1953 if ((flags & AspectValue) != 0)
1954 center.x=(MagickRealType) ((image->columns-1)/2.0);
1955 else
1956 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1957 2.0);
1958 else
1959 if ((flags & AspectValue) != 0)
1960 center.x=geometry_info.xi;
1961 else
1962 center.x=(MagickRealType) (x_offset+geometry_info.xi);
1963 if ((flags & YValue) == 0)
1964 if ((flags & AspectValue) != 0)
1965 center.y=(MagickRealType) ((image->rows-1)/2.0);
1966 else
1967 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1968 else
1969 if ((flags & AspectValue) != 0)
1970 center.y=geometry_info.psi;
1971 else
1972 center.y=(MagickRealType) (y_offset+geometry_info.psi);
1973 }
1974 /*
1975 Shift the pixel offset point as defined by the provided,
1976 displacement/distortion map. -- Like a lens...
1977 */
1978 GetPixelInfo(image,&pixel);
1979 image_view=AcquireVirtualCacheView(image,exception);
1980 source_view=AcquireVirtualCacheView(source_image,exception);
1981 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1982 for (y=0; y < (ssize_t) source_image->rows; y++)
1983 {
1984 MagickBooleanType
1985 sync;
1986
1987 const Quantum
1988 *magick_restrict p;
1989
1990 Quantum
1991 *magick_restrict q;
1992
1993 ssize_t
1994 x;
1995
1996 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1997 continue;
1998 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1999 exception);
2000 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
2001 exception);
2002 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2003 break;
2004 for (x=0; x < (ssize_t) source_image->columns; x++)
2005 {
2006 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
2007 {
2008 p+=(ptrdiff_t) GetPixelChannels(source_image);
2009 continue;
2010 }
2011 /*
2012 Displace the offset.
2013 */
2014 offset.x=(double) (horizontal_scale*((double) GetPixelRed(
2015 source_image,p)-(((MagickRealType) QuantumRange+1.0)/2.0)))/
2016 (((MagickRealType) QuantumRange+1.0)/2.0)+center.x+
2017 ((compose == DisplaceCompositeOp) ? x : 0);
2018 offset.y=(double) (vertical_scale*((double) GetPixelGreen(
2019 source_image,p)-(((MagickRealType) QuantumRange+1.0)/2.0)))/
2020 (((MagickRealType) QuantumRange+1.0)/2.0)+center.y+
2021 ((compose == DisplaceCompositeOp) ? y : 0);
2022 status=InterpolatePixelInfo(image,image_view,
2023 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2024 &pixel,exception);
2025 if (status == MagickFalse)
2026 break;
2027 /*
2028 Mask with the 'invalid pixel mask' in alpha channel.
2029 */
2030 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
2031 (QuantumScale*(double) GetPixelAlpha(source_image,p));
2032 SetPixelViaPixelInfo(canvas_image,&pixel,q);
2033 p+=(ptrdiff_t) GetPixelChannels(source_image);
2034 q+=(ptrdiff_t) GetPixelChannels(canvas_image);
2035 }
2036 if (x < (ssize_t) source_image->columns)
2037 break;
2038 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
2039 if (sync == MagickFalse)
2040 break;
2041 }
2042 canvas_view=DestroyCacheView(canvas_view);
2043 source_view=DestroyCacheView(source_view);
2044 image_view=DestroyCacheView(image_view);
2045 source_image=DestroyImage(source_image);
2046 source_image=canvas_image;
2047 break;
2048 }
2049 case DissolveCompositeOp:
2050 {
2051 /*
2052 Geometry arguments to dissolve factors.
2053 */
2054 artifact=GetImageArtifact(image,"compose:args");
2055 if (artifact != (char *) NULL)
2056 {
2057 flags=ParseGeometry(artifact,&geometry_info);
2058 source_dissolve=geometry_info.rho/100.0;
2059 canvas_dissolve=1.0;
2060 if ((source_dissolve-MagickEpsilon) < 0.0)
2061 source_dissolve=0.0;
2062 if ((source_dissolve+MagickEpsilon) > 1.0)
2063 {
2064 canvas_dissolve=2.0-source_dissolve;
2065 source_dissolve=1.0;
2066 }
2067 if ((flags & SigmaValue) != 0)
2068 canvas_dissolve=geometry_info.sigma/100.0;
2069 if ((canvas_dissolve-MagickEpsilon) < 0.0)
2070 canvas_dissolve=0.0;
2071 if ((canvas_dissolve+MagickEpsilon) > 1.0)
2072 canvas_dissolve=1.0;
2073 }
2074 break;
2075 }
2076 case BlendCompositeOp:
2077 {
2078 artifact=GetImageArtifact(image,"compose:args");
2079 if (artifact != (char *) NULL)
2080 {
2081 flags=ParseGeometry(artifact,&geometry_info);
2082 source_dissolve=geometry_info.rho/100.0;
2083 canvas_dissolve=1.0-source_dissolve;
2084 if ((flags & SigmaValue) != 0)
2085 canvas_dissolve=geometry_info.sigma/100.0;
2086 }
2087 break;
2088 }
2089 case SaliencyBlendCompositeOp:
2090 {
2091 double
2092 residual_threshold = 0.0002,
2093 iterations = 400.0;
2094
2095 size_t
2096 tick = 100;
2097
2098 artifact=GetImageArtifact(image,"compose:args");
2099 if (artifact != (char *) NULL)
2100 {
2101 flags=ParseGeometry(artifact,&geometry_info);
2102 iterations=geometry_info.rho;
2103 if ((flags & SigmaValue) != 0)
2104 residual_threshold=geometry_info.sigma;
2105 if ((flags & XiValue) != 0)
2106 tick=(size_t) geometry_info.xi;
2107 }
2108 status=SaliencyBlendImage(image,composite,x_offset,y_offset,iterations,
2109 residual_threshold,tick,exception);
2110 source_image=DestroyImage(source_image);
2111 return(status);
2112 }
2113 case SeamlessBlendCompositeOp:
2114 {
2115 double
2116 residual_threshold = 0.0002,
2117 iterations = 400.0;
2118
2119 size_t
2120 tick = 100;
2121
2122 artifact=GetImageArtifact(image,"compose:args");
2123 if (artifact != (char *) NULL)
2124 {
2125 flags=ParseGeometry(artifact,&geometry_info);
2126 iterations=geometry_info.rho;
2127 if ((flags & SigmaValue) != 0)
2128 residual_threshold=geometry_info.sigma;
2129 if ((flags & XiValue) != 0)
2130 tick=(size_t) geometry_info.xi;
2131 }
2132 status=SeamlessBlendImage(image,composite,x_offset,y_offset,iterations,
2133 residual_threshold,tick,exception);
2134 source_image=DestroyImage(source_image);
2135 return(status);
2136 }
2137 case MathematicsCompositeOp:
2138 {
2139 /*
2140 Just collect the values from "compose:args", setting.
2141 Unused values are set to zero automagically.
2142
2143 Arguments are normally a comma separated list, so this probably should
2144 be changed to some 'general comma list' parser, (with a minimum
2145 number of values)
2146 */
2147 SetGeometryInfo(&geometry_info);
2148 artifact=GetImageArtifact(image,"compose:args");
2149 if (artifact != (char *) NULL)
2150 {
2151 flags=ParseGeometry(artifact,&geometry_info);
2152 if (flags == NoValue)
2153 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2154 "InvalidGeometry","`%s'",artifact);
2155 }
2156 break;
2157 }
2158 case ModulateCompositeOp:
2159 {
2160 /*
2161 Determine the luma and chroma scale.
2162 */
2163 artifact=GetImageArtifact(image,"compose:args");
2164 if (artifact != (char *) NULL)
2165 {
2166 flags=ParseGeometry(artifact,&geometry_info);
2167 percent_luma=geometry_info.rho;
2168 if ((flags & SigmaValue) != 0)
2169 percent_chroma=geometry_info.sigma;
2170 }
2171 break;
2172 }
2173 case ThresholdCompositeOp:
2174 {
2175 /*
2176 Determine the amount and threshold.
2177 */
2178 artifact=GetImageArtifact(image,"compose:args");
2179 if (artifact != (char *) NULL)
2180 {
2181 flags=ParseGeometry(artifact,&geometry_info);
2182 amount=geometry_info.rho;
2183 threshold=geometry_info.sigma;
2184 if ((flags & SigmaValue) == 0)
2185 threshold=0.05f;
2186 }
2187 threshold*=(double) QuantumRange;
2188 break;
2189 }
2190 default:
2191 break;
2192 }
2193 /*
2194 Composite image.
2195 */
2196 status=MagickTrue;
2197 progress=0;
2198 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2199 random_info=AcquireRandomInfo();
2200 source_view=AcquireVirtualCacheView(source_image,exception);
2201 image_view=AcquireAuthenticCacheView(image,exception);
2202#if defined(MAGICKCORE_OPENMP_SUPPORT)
2203 #pragma omp parallel for schedule(static) shared(progress,status) \
2204 magick_number_threads(source_image,image,image->rows,1)
2205#endif
2206 for (y=0; y < (ssize_t) image->rows; y++)
2207 {
2208 const Quantum
2209 *magick_restrict p,
2210 *pixels;
2211
2212 MagickRealType
2213 blue = 0.0,
2214 chroma = 0.0,
2215 green = 0.0,
2216 hue = 0.0,
2217 luma = 0.0,
2218 red = 0.0;
2219
2220 PixelInfo
2221 canvas_pixel,
2222 source_pixel;
2223
2224 Quantum
2225 *magick_restrict q;
2226
2227 ssize_t
2228 x;
2229
2230 if (status == MagickFalse)
2231 continue;
2232 if (clip_to_self != MagickFalse)
2233 {
2234 if (y < y_offset)
2235 continue;
2236 if ((y-y_offset) >= (ssize_t) source_image->rows)
2237 continue;
2238 }
2239 /*
2240 If pixels is NULL, y is outside overlay region.
2241 */
2242 pixels=(Quantum *) NULL;
2243 p=(Quantum *) NULL;
2244 if ((y >= y_offset) &&
2245 ((y-y_offset) < (ssize_t) source_image->rows))
2246 {
2247 p=GetCacheViewVirtualPixels(source_view,0,
2248 CastDoubleToSsizeT((double) y-y_offset),source_image->columns,1,
2249 exception);
2250 if (p == (const Quantum *) NULL)
2251 {
2252 status=MagickFalse;
2253 continue;
2254 }
2255 pixels=p;
2256 if (x_offset < 0)
2257 p-=(ptrdiff_t) CastDoubleToSsizeT((double) x_offset*
2258 GetPixelChannels(source_image));
2259 }
2260 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2261 if (q == (Quantum *) NULL)
2262 {
2263 status=MagickFalse;
2264 continue;
2265 }
2266 GetPixelInfo(image,&canvas_pixel);
2267 GetPixelInfo(source_image,&source_pixel);
2268 for (x=0; x < (ssize_t) image->columns; x++)
2269 {
2270 double
2271 gamma = 0.0;
2272
2273 MagickRealType
2274 alpha = 0.0,
2275 blend = 0.0,
2276 D = 0.0,
2277 Da = 0.0,
2278 Dc = 0.0,
2279 Dca = 0.0,
2280 Di = 0.0,
2281 S = 0.0,
2282 Sa = 0.0,
2283 Sc = 0.0,
2284 Sca = 0.0,
2285 Si = 0.0;
2286
2287 size_t
2288 channels;
2289
2290 ssize_t
2291 i;
2292
2293 if (clip_to_self != MagickFalse)
2294 {
2295 if (x < x_offset)
2296 {
2297 q+=(ptrdiff_t) GetPixelChannels(image);
2298 continue;
2299 }
2300 if ((x-x_offset) >= (ssize_t) source_image->columns)
2301 break;
2302 }
2303 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2304 ((x-x_offset) >= (ssize_t) source_image->columns))
2305 {
2306 Quantum
2307 source[MaxPixelChannels];
2308
2309 /*
2310 Virtual composite:
2311 Sc: source color.
2312 Dc: canvas color.
2313 */
2314 (void) GetOneVirtualPixel(source_image,
2315 CastDoubleToSsizeT((double) x-x_offset),
2316 CastDoubleToSsizeT((double) y-y_offset),source,exception);
2317 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2318 {
2319 MagickRealType
2320 pixel = 0.0;
2321
2322 PixelChannel channel = GetPixelChannelChannel(image,i);
2323 PixelTrait traits = GetPixelChannelTraits(image,channel);
2324 PixelTrait source_traits = GetPixelChannelTraits(source_image,
2325 channel);
2326 if ((traits == UndefinedPixelTrait) ||
2327 (source_traits == UndefinedPixelTrait))
2328 continue;
2329 switch (compose)
2330 {
2331 case AlphaCompositeOp:
2332 case ChangeMaskCompositeOp:
2333 case CopyAlphaCompositeOp:
2334 case DstAtopCompositeOp:
2335 case DstInCompositeOp:
2336 case InCompositeOp:
2337 case OutCompositeOp:
2338 case SrcInCompositeOp:
2339 case SrcOutCompositeOp:
2340 {
2341 if (channel == AlphaPixelChannel)
2342 pixel=(MagickRealType) TransparentAlpha;
2343 else
2344 pixel=(MagickRealType) q[i];
2345 break;
2346 }
2347 case ClearCompositeOp:
2348 case CopyCompositeOp:
2349 case ReplaceCompositeOp:
2350 {
2351 if (channel == AlphaPixelChannel)
2352 pixel=(MagickRealType) TransparentAlpha;
2353 else
2354 pixel=0.0;
2355 break;
2356 }
2357 case BlendCompositeOp:
2358 case DissolveCompositeOp:
2359 {
2360 if (channel == AlphaPixelChannel)
2361 pixel=canvas_dissolve*(double) GetPixelAlpha(source_image,
2362 source);
2363 else
2364 pixel=(MagickRealType) source[channel];
2365 break;
2366 }
2367 default:
2368 {
2369 pixel=(MagickRealType) source[channel];
2370 break;
2371 }
2372 }
2373 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
2374 ClampToQuantum(pixel);
2375 }
2376 q+=(ptrdiff_t) GetPixelChannels(image);
2377 continue;
2378 }
2379 /*
2380 Authentic composite:
2381 Sa: normalized source alpha.
2382 Da: normalized canvas alpha.
2383 */
2384 Sa=QuantumScale*(double) GetPixelAlpha(source_image,p);
2385 Da=QuantumScale*(double) GetPixelAlpha(image,q);
2386 switch (compose)
2387 {
2388 case BumpmapCompositeOp:
2389 case ColorBurnCompositeOp:
2390 case ColorDodgeCompositeOp:
2391 case DarkenCompositeOp:
2392 case DifferenceCompositeOp:
2393 case DivideDstCompositeOp:
2394 case DivideSrcCompositeOp:
2395 case ExclusionCompositeOp:
2396 case FreezeCompositeOp:
2397 case HardLightCompositeOp:
2398 case HardMixCompositeOp:
2399 case InterpolateCompositeOp:
2400 case LightenCompositeOp:
2401 case LinearBurnCompositeOp:
2402 case LinearDodgeCompositeOp:
2403 case LinearLightCompositeOp:
2404 case MathematicsCompositeOp:
2405 case MinusDstCompositeOp:
2406 case MinusSrcCompositeOp:
2407 case MultiplyCompositeOp:
2408 case NegateCompositeOp:
2409 case OverlayCompositeOp:
2410 case PegtopLightCompositeOp:
2411 case PinLightCompositeOp:
2412 case ReflectCompositeOp:
2413 case ScreenCompositeOp:
2414 case SoftBurnCompositeOp:
2415 case SoftDodgeCompositeOp:
2416 case SoftLightCompositeOp:
2417 case StampCompositeOp:
2418 case VividLightCompositeOp:
2419 {
2420 alpha=RoundToUnity(Sa+Da-Sa*Da);
2421 break;
2422 }
2423 case DstAtopCompositeOp:
2424 case DstInCompositeOp:
2425 case InCompositeOp:
2426 case SrcInCompositeOp:
2427 {
2428 alpha=Sa*Da;
2429 break;
2430 }
2431 case DissolveCompositeOp:
2432 {
2433 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
2434 canvas_dissolve*Da;
2435 break;
2436 }
2437 case DstOverCompositeOp:
2438 case OverCompositeOp:
2439 case SrcOverCompositeOp:
2440 {
2441 alpha=Sa+Da-Sa*Da;
2442 break;
2443 }
2444 case DstOutCompositeOp:
2445 {
2446 alpha=Da*(1.0-Sa);
2447 break;
2448 }
2449 case OutCompositeOp:
2450 case SrcOutCompositeOp:
2451 {
2452 alpha=Sa*(1.0-Da);
2453 break;
2454 }
2455 case BlendCompositeOp:
2456 case PlusCompositeOp:
2457 {
2458 alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
2459 break;
2460 }
2461 case XorCompositeOp:
2462 {
2463 alpha=Sa+Da-2.0*Sa*Da;
2464 break;
2465 }
2466 case ModulusAddCompositeOp:
2467 {
2468 if ((Sa+Da) <= 1.0)
2469 {
2470 alpha=(Sa+Da);
2471 break;
2472 }
2473 alpha=((Sa+Da)-1.0);
2474 break;
2475 }
2476 case ModulusSubtractCompositeOp:
2477 {
2478 if ((Sa-Da) >= 0.0)
2479 {
2480 alpha=(Sa-Da);
2481 break;
2482 }
2483 alpha=((Sa-Da)+1.0);
2484 break;
2485 }
2486 default:
2487 {
2488 alpha=1.0;
2489 break;
2490 }
2491 }
2492 switch (compose)
2493 {
2494 case ColorizeCompositeOp:
2495 case HueCompositeOp:
2496 case LuminizeCompositeOp:
2497 case ModulateCompositeOp:
2498 case RMSECompositeOp:
2499 case SaturateCompositeOp:
2500 {
2501 Si=GetPixelIntensity(source_image,p);
2502 GetPixelInfoPixel(source_image,p,&source_pixel);
2503 GetPixelInfoPixel(image,q,&canvas_pixel);
2504 break;
2505 }
2506 case BumpmapCompositeOp:
2507 case CopyAlphaCompositeOp:
2508 case DarkenIntensityCompositeOp:
2509 case LightenIntensityCompositeOp:
2510 {
2511 Si=GetPixelIntensity(source_image,p);
2512 Di=GetPixelIntensity(image,q);
2513 break;
2514 }
2515 default:
2516 break;
2517 }
2518 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2519 {
2520 MagickRealType
2521 pixel = 0.0,
2522 sans = 0.0;
2523
2524 PixelChannel channel = GetPixelChannelChannel(image,i);
2525 PixelTrait traits = GetPixelChannelTraits(image,channel);
2526 PixelTrait source_traits = GetPixelChannelTraits(source_image,channel);
2527 if (traits == UndefinedPixelTrait)
2528 continue;
2529 if ((channel == AlphaPixelChannel) &&
2530 ((traits & UpdatePixelTrait) != 0))
2531 {
2532 /*
2533 Set alpha channel.
2534 */
2535 switch (compose)
2536 {
2537 case AlphaCompositeOp:
2538 {
2539 pixel=(double) QuantumRange*Sa;
2540 break;
2541 }
2542 case AtopCompositeOp:
2543 case CopyBlackCompositeOp:
2544 case CopyBlueCompositeOp:
2545 case CopyCyanCompositeOp:
2546 case CopyGreenCompositeOp:
2547 case CopyMagentaCompositeOp:
2548 case CopyRedCompositeOp:
2549 case CopyYellowCompositeOp:
2550 case SrcAtopCompositeOp:
2551 case DstCompositeOp:
2552 case NoCompositeOp:
2553 {
2554 pixel=(double) QuantumRange*Da;
2555 break;
2556 }
2557 case BumpmapCompositeOp:
2558 {
2559 pixel=Si*Da;
2560 break;
2561 }
2562 case ChangeMaskCompositeOp:
2563 {
2564 if (IsFuzzyEquivalencePixel(source_image,p,image,q) != MagickFalse)
2565 pixel=(MagickRealType) TransparentAlpha;
2566 else
2567 pixel=(double) QuantumRange*Da;
2568 break;
2569 }
2570 case ClearCompositeOp:
2571 {
2572 pixel=(MagickRealType) TransparentAlpha;
2573 break;
2574 }
2575 case ColorizeCompositeOp:
2576 case HueCompositeOp:
2577 case LuminizeCompositeOp:
2578 case RMSECompositeOp:
2579 case SaturateCompositeOp:
2580 {
2581 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2582 {
2583 pixel=(double) QuantumRange*Da;
2584 break;
2585 }
2586 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
2587 {
2588 pixel=(double) QuantumRange*Sa;
2589 break;
2590 }
2591 if (Sa < Da)
2592 {
2593 pixel=(double) QuantumRange*Da;
2594 break;
2595 }
2596 pixel=(double) QuantumRange*Sa;
2597 break;
2598 }
2599 case CopyAlphaCompositeOp:
2600 {
2601 pixel=Si;
2602 break;
2603 }
2604 case BlurCompositeOp:
2605 case CopyCompositeOp:
2606 case DisplaceCompositeOp:
2607 case DistortCompositeOp:
2608 case DstAtopCompositeOp:
2609 case ReplaceCompositeOp:
2610 case SrcCompositeOp:
2611 {
2612 pixel=(double) QuantumRange*Sa;
2613 break;
2614 }
2615 case DarkenIntensityCompositeOp:
2616 {
2617 if (compose_sync == MagickFalse)
2618 {
2619 pixel=Si < Di ? Sa : Da;
2620 break;
2621 }
2622 pixel=Sa*Si < Da*Di ? Sa : Da;
2623 break;
2624 }
2625 case DifferenceCompositeOp:
2626 {
2627 pixel=(double) QuantumRange*fabs((double) (Sa-Da));
2628 break;
2629 }
2630 case FreezeCompositeOp:
2631 {
2632 pixel=(double) QuantumRange*(1.0-(1.0-Sa)*(1.0-Sa)*
2633 MagickSafeReciprocal(Da));
2634 if (pixel < 0.0)
2635 pixel=0.0;
2636 break;
2637 }
2638 case InterpolateCompositeOp:
2639 {
2640 pixel=(double) QuantumRange*(0.5-0.25*cos(MagickPI*Sa)-0.25*
2641 cos(MagickPI*Da));
2642 break;
2643 }
2644 case LightenIntensityCompositeOp:
2645 {
2646 if (compose_sync == MagickFalse)
2647 {
2648 pixel=Si > Di ? Sa : Da;
2649 break;
2650 }
2651 pixel=Sa*Si > Da*Di ? Sa : Da;
2652 break;
2653 }
2654 case ModulateCompositeOp:
2655 {
2656 pixel=(double) QuantumRange*Da;
2657 break;
2658 }
2659 case MultiplyCompositeOp:
2660 {
2661 if (compose_sync == MagickFalse)
2662 {
2663 pixel=(double) QuantumRange*Sa*Da;
2664 break;
2665 }
2666 pixel=(double) QuantumRange*alpha;
2667 break;
2668 }
2669 case NegateCompositeOp:
2670 {
2671 pixel=(double) QuantumRange*((1.0-Sa-Da));
2672 break;
2673 }
2674 case ReflectCompositeOp:
2675 {
2676 pixel=(double) QuantumRange*(Sa*Sa*
2677 MagickSafeReciprocal(1.0-Da));
2678 if (pixel > (double) QuantumRange)
2679 pixel=(double) QuantumRange;
2680 break;
2681 }
2682 case StampCompositeOp:
2683 {
2684 pixel=(double) QuantumRange*(Sa+Da*Da-1.0);
2685 break;
2686 }
2687 case StereoCompositeOp:
2688 {
2689 pixel=(double) QuantumRange*(Sa+Da)/2;
2690 break;
2691 }
2692 default:
2693 {
2694 pixel=(double) QuantumRange*alpha;
2695 break;
2696 }
2697 }
2698 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
2699 ClampToQuantum(pixel);
2700 continue;
2701 }
2702 if (source_traits == UndefinedPixelTrait)
2703 continue;
2704 /*
2705 Sc: source color.
2706 Dc: canvas color.
2707 */
2708 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
2709 Dc=(MagickRealType) q[i];
2710 if ((traits & CopyPixelTrait) != 0)
2711 {
2712 /*
2713 Copy channel.
2714 */
2715 q[i]=ClampToQuantum(Dc);
2716 continue;
2717 }
2718 /*
2719 Porter-Duff compositions:
2720 Sca: source normalized color multiplied by alpha.
2721 Dca: normalized canvas color multiplied by alpha.
2722 */
2723 Sca=QuantumScale*Sa*Sc;
2724 Dca=QuantumScale*Da*Dc;
2725 switch (compose)
2726 {
2727 case DarkenCompositeOp:
2728 case LightenCompositeOp:
2729 case ModulusSubtractCompositeOp:
2730 {
2731 gamma=MagickSafeReciprocal(1.0-alpha);
2732 break;
2733 }
2734 default:
2735 {
2736 gamma=MagickSafeReciprocal(alpha);
2737 break;
2738 }
2739 }
2740 pixel=Dc;
2741 switch (compose)
2742 {
2743 case AlphaCompositeOp:
2744 {
2745 pixel=(double) QuantumRange*Sa;
2746 break;
2747 }
2748 case AtopCompositeOp:
2749 case SrcAtopCompositeOp:
2750 {
2751 pixel=(double) QuantumRange*(Sca*Da+Dca*(1.0-Sa));
2752 break;
2753 }
2754 case BlendCompositeOp:
2755 {
2756 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
2757 break;
2758 }
2759 case CopyCompositeOp:
2760 case ReplaceCompositeOp:
2761 {
2762 pixel=(double) QuantumRange*Sca;
2763 break;
2764 }
2765 case BlurCompositeOp:
2766 case DisplaceCompositeOp:
2767 case DistortCompositeOp:
2768 case SrcCompositeOp:
2769 {
2770 pixel=Sc;
2771 break;
2772 }
2773 case BumpmapCompositeOp:
2774 {
2775 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2776 {
2777 pixel=Dc;
2778 break;
2779 }
2780 pixel=(QuantumScale*Si)*Dc;
2781 break;
2782 }
2783 case ChangeMaskCompositeOp:
2784 {
2785 pixel=Dc;
2786 break;
2787 }
2788 case ClearCompositeOp:
2789 {
2790 pixel=0.0;
2791 break;
2792 }
2793 case ColorBurnCompositeOp:
2794 {
2795 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
2796 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
2797 if (S <= 0.0)
2798 blend=0.0;
2799 else
2800 blend=1.0-(1.0-D)/S;
2801 pixel=(double) QuantumRange*gamma*RoundToUnity(Sa*Da*
2802 RoundToUnity(blend)+Sa*(1.0-Da)*S+Da*(1.0-Sa)*D);
2803 break;
2804 }
2805 case ColorDodgeCompositeOp:
2806 {
2807 if (Sa > 0.0)
2808 S=RoundToUnity(Sca/Sa);
2809 else
2810 S=0.0;
2811 if (Da > 0.0)
2812 D=RoundToUnity(Dca/Da);
2813 else
2814 D=0.0;
2815 if (S >= 1.0)
2816 blend=1.0;
2817 else
2818 if (D <= 0.0)
2819 blend=0.0;
2820 else
2821 {
2822 if ((1.0-S) <= 0.0)
2823 blend=1.0;
2824 else
2825 blend=MagickMin(1.0,D/(1.0-S));
2826 }
2827 pixel=(double) QuantumRange*gamma*(Sa*Da*blend+Sca*(1.0-Da)+Dca*
2828 (1.0-Sa));
2829 break;
2830 }
2831 case ColorizeCompositeOp:
2832 {
2833 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2834 {
2835 pixel=Dc;
2836 break;
2837 }
2838 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
2839 {
2840 pixel=Sc;
2841 break;
2842 }
2843 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
2844 (double) canvas_pixel.green,(double) canvas_pixel.blue,
2845 white_luminance,illuminant,&sans,&sans,&luma);
2846 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
2847 (double) source_pixel.green,(double) source_pixel.blue,
2848 white_luminance,illuminant,&hue,&chroma,&sans);
2849 ConvertGenericToRGB(colorspace,hue,chroma,luma,
2850 white_luminance,illuminant,&red,&green,&blue);
2851 switch (channel)
2852 {
2853 case RedPixelChannel: pixel=red; break;
2854 case GreenPixelChannel: pixel=green; break;
2855 case BluePixelChannel: pixel=blue; break;
2856 default: pixel=Dc; break;
2857 }
2858 break;
2859 }
2860 case CopyAlphaCompositeOp:
2861 case DstCompositeOp:
2862 {
2863 pixel=Dc;
2864 break;
2865 }
2866 case CopyBlackCompositeOp:
2867 {
2868 if (channel == BlackPixelChannel)
2869 pixel=(MagickRealType) GetPixelBlack(source_image,p);
2870 break;
2871 }
2872 case CopyBlueCompositeOp:
2873 case CopyYellowCompositeOp:
2874 {
2875 if (channel == BluePixelChannel)
2876 pixel=(MagickRealType) GetPixelBlue(source_image,p);
2877 break;
2878 }
2879 case CopyGreenCompositeOp:
2880 case CopyMagentaCompositeOp:
2881 {
2882 if (channel == GreenPixelChannel)
2883 pixel=(MagickRealType) GetPixelGreen(source_image,p);
2884 break;
2885 }
2886 case CopyRedCompositeOp:
2887 case CopyCyanCompositeOp:
2888 {
2889 if (channel == RedPixelChannel)
2890 pixel=(MagickRealType) GetPixelRed(source_image,p);
2891 break;
2892 }
2893 case DarkenCompositeOp:
2894 {
2895 if (compose_sync == MagickFalse)
2896 {
2897 pixel=RoundToUnity(MagickMin(Sca,Dca)+Sca*(1.0-Da)+Dca*
2898 (1.0-Sa));
2899 break;
2900 }
2901 pixel=(double) QuantumRange*RoundToUnity(MagickMin(Sca,Dca)+Sca*
2902 (1.0-Da)+Dca*(1.0-Sa));
2903 break;
2904 }
2905 case DarkenIntensityCompositeOp:
2906 {
2907 if (compose_sync == MagickFalse)
2908 {
2909 pixel=RoundToUnity(MagickMin(Sca,Dca)+Sca*(1.0-Di)+Dca*
2910 (1.0-Si));
2911 break;
2912 }
2913 pixel=(double) QuantumRange*RoundToUnity(MagickMin(Sca,Dca)+Sca*
2914 (1.0-Di)+Dca*(1.0-Si));
2915 break;
2916 }
2917 case DifferenceCompositeOp:
2918 {
2919 if (compose_sync == MagickFalse)
2920 {
2921 pixel=(double) QuantumRange*RoundToUnity(fabs((double) Sc-
2922 (double) Dc));
2923 break;
2924 }
2925 S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
2926 D=(Da > 0.0) ? (Dca/Da) : 0.0;
2927 pixel=(double) QuantumRange*RoundToUnity(fabs(S-D));
2928 break;
2929 }
2930 case DissolveCompositeOp:
2931 {
2932 if (GetPseudoRandomValue(random_info) < (source_dissolve*Sa))
2933 pixel=Sc;
2934 else
2935 pixel=Dc;
2936 break;
2937 }
2938 case DivideDstCompositeOp:
2939 {
2940 S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
2941 D=(Da > 0.0) ? (Dca/Da) : 0.0;
2942 if (S <= 0.0)
2943 blend=1.0;
2944 else
2945 blend=MagickMin(1.0,D/S);
2946 pixel=(double) QuantumRange*RoundToUnity(Sca*(1.0-Da)+Dca*(1.0-Sa)+
2947 Sa*Da*blend);
2948 break;
2949 }
2950 case DivideSrcCompositeOp:
2951 {
2952 S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
2953 D=(Da > 0.0) ? (Dca/Da) : 0.0;
2954 if (D <= 0.0)
2955 blend=1.0;
2956 else
2957 blend=MagickMin(1.0,S/D);
2958 pixel=(double) QuantumRange*RoundToUnity(Sca*(1.0-Da)+Dca*(1.0-Sa)+
2959 Sa*Da*blend);
2960 break;
2961 }
2962 case DstAtopCompositeOp:
2963 {
2964 pixel=(double) QuantumRange*(Dca*Sa+Sca*(1.0-Da));
2965 break;
2966 }
2967 case DstInCompositeOp:
2968 {
2969 pixel=(double) QuantumRange*gamma*(Dca*Sa);
2970 break;
2971 }
2972 case DstOutCompositeOp:
2973 {
2974 pixel=(double) QuantumRange*gamma*(Dca*(1.0-Sa));
2975 break;
2976 }
2977 case DstOverCompositeOp:
2978 {
2979 pixel=(double) QuantumRange*gamma*(Dca+Sca*(1.0-Da));
2980 break;
2981 }
2982 case ExclusionCompositeOp:
2983 {
2984 S=(Sa > 0.0) ? Sca/Sa : 0.0;
2985 D=(Da > 0.0) ? Dca/Da : 0.0;
2986 blend=RoundToUnity(S+D-2.0*S*D);
2987 pixel=(double) QuantumRange*RoundToUnity((blend*Sa+D*(1.0-Sa))*
2988 (Sa+Da-Sa*Da));
2989 break;
2990 }
2991 case FreezeCompositeOp:
2992 {
2993 if (Dca != 0.0)
2994 blend=1.0-(1.0-Sca)*(1.0-Sca)/Dca;
2995 else
2996 blend=0.0;
2997 pixel=(double) QuantumRange*gamma*RoundToUnity(blend);
2998 break;
2999 }
3000 case HardLightCompositeOp:
3001 {
3002 D=(Da > 0.0) ? (Dca/Da) : 0.0;
3003 S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
3004 if (S <= 0.5)
3005 blend=2.0*S*D;
3006 else
3007 blend=1.0-2.0*(1.0-S)*(1.0-D);
3008 pixel=(double) QuantumRange*gamma*RoundToUnity((1.0-Da)*Sca+
3009 (1.0-Sa)*Dca+blend*Sa*Da);
3010 break;
3011 }
3012 case HardMixCompositeOp:
3013 {
3014 if (Sca < 0.5)
3015 {
3016 if ((2.0*Sca) == 0.0)
3017 blend=0.0;
3018 else
3019 blend=1.0-(1.0-Dca)/(2.0*Sca);
3020 }
3021 else
3022 {
3023 if ((1.0-((2.0*Sca)-1.0)) == 0.0)
3024 blend=1.0;
3025 else
3026 blend=Dca/(1.0-(2.0*Sca-1.0));
3027 }
3028 pixel=gamma*((blend < 0.5) ? 0.0 : (double) QuantumRange);
3029 break;
3030 }
3031 case HueCompositeOp:
3032 {
3033 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3034 {
3035 pixel=Dc;
3036 break;
3037 }
3038 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3039 {
3040 pixel=Sc;
3041 break;
3042 }
3043 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3044 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3045 white_luminance,illuminant,&hue,&chroma,&luma);
3046 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3047 (double) source_pixel.green,(double) source_pixel.blue,
3048 white_luminance,illuminant,&hue,&sans,&sans);
3049 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3050 white_luminance,illuminant,&red,&green,&blue);
3051 switch (channel)
3052 {
3053 case RedPixelChannel: pixel=red; break;
3054 case GreenPixelChannel: pixel=green; break;
3055 case BluePixelChannel: pixel=blue; break;
3056 default: pixel=Dc; break;
3057 }
3058 break;
3059 }
3060 case InCompositeOp:
3061 case SrcInCompositeOp:
3062 {
3063 pixel=(double) QuantumRange*(Sca*Da);
3064 break;
3065 }
3066 case InterpolateCompositeOp:
3067 {
3068 pixel=(double) QuantumRange*(0.5-0.25*cos(MagickPI*Sca)-0.25*
3069 cos(MagickPI*Dca));
3070 break;
3071 }
3072 case LinearBurnCompositeOp:
3073 {
3074 /*
3075 LinearBurn: as defined by Abode Photoshop, according to
3076 http://www.simplefilter.de/en/basics/mixmods.html is:
3077
3078 f(Sc,Dc) = Sc + Dc - 1
3079 */
3080 pixel=(double) QuantumRange*(Sca+Dca-Sa*Da);
3081 break;
3082 }
3083 case LinearDodgeCompositeOp:
3084 {
3085 pixel=gamma*(Sa*Sc+Da*Dc);
3086 break;
3087 }
3088 case LinearLightCompositeOp:
3089 {
3090 /*
3091 Linear Light (Adobe standard):
3092 f(Sc, Dc) = Dc + 2*Sc - 1
3093 Applied in linear (HDRI) space with clamping.
3094 */
3095 D=(Da > 0.0) ? Dca/Da : 0.0,
3096 S=(Sa > 0.0) ? Sca/Sa : 0.0;
3097 pixel=(double) QuantumRange*gamma*(RoundToUnity(D+2.0*S-1.0)*Da);
3098 break;
3099 }
3100 case LightenCompositeOp:
3101 {
3102 if (compose_sync == MagickFalse)
3103 {
3104 pixel=MagickMax(Sc,Dc);
3105 break;
3106 }
3107 S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
3108 D=(Da > 0.0) ? (Dca/Da) : 0.0;
3109 pixel=(double) QuantumRange*RoundToUnity(Sca*(1.0-Da)+Dca*(1.0-Sa)+
3110 MagickMax(S,D)*Sa*Da);
3111 break;
3112 }
3113 case LightenIntensityCompositeOp:
3114 {
3115 pixel=Si > Di ? Sc : Dc;
3116 break;
3117 }
3118 case LuminizeCompositeOp:
3119 {
3120 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3121 {
3122 pixel=Dc;
3123 break;
3124 }
3125 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3126 {
3127 pixel=Sc;
3128 break;
3129 }
3130 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3131 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3132 white_luminance,illuminant,&hue,&chroma,&luma);
3133 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3134 (double) source_pixel.green,(double) source_pixel.blue,
3135 white_luminance,illuminant,&sans,&sans,&luma);
3136 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3137 white_luminance,illuminant,&red,&green,&blue);
3138 switch (channel)
3139 {
3140 case RedPixelChannel: pixel=red; break;
3141 case GreenPixelChannel: pixel=green; break;
3142 case BluePixelChannel: pixel=blue; break;
3143 default: pixel=Dc; break;
3144 }
3145 break;
3146 }
3147 case MathematicsCompositeOp:
3148 {
3149 /*
3150 'Mathematics' a free form user control mathematical composition
3151 is defined as...
3152
3153 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
3154
3155 Where the arguments A,B,C,D are (currently) passed to composite
3156 as a command separated 'geometry' string in "compose:args" image
3157 artifact.
3158
3159 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
3160
3161 Applying the SVG transparency formula (see above), we get...
3162
3163 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
3164
3165 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
3166 Dca*(1.0-Sa)
3167 */
3168 if (compose_sync == MagickFalse)
3169 {
3170 pixel=geometry_info.rho*Sc*Dc+geometry_info.sigma*Sc+
3171 geometry_info.xi*Dc+geometry_info.psi;
3172 break;
3173 }
3174 pixel=(double) QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
3175 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
3176 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
3177 break;
3178 }
3179 case MinusDstCompositeOp:
3180 {
3181 if (compose_sync == MagickFalse)
3182 {
3183 pixel=Dc-Sc;
3184 break;
3185 }
3186 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
3187 break;
3188 }
3189 case MinusSrcCompositeOp:
3190 {
3191 if (compose_sync == MagickFalse)
3192 {
3193 pixel=Sc-Dc;
3194 break;
3195 }
3196 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
3197 break;
3198 }
3199 case ModulateCompositeOp:
3200 {
3201 ssize_t
3202 offset;
3203
3204 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3205 {
3206 pixel=Dc;
3207 break;
3208 }
3209 offset=(ssize_t) (Si-midpoint);
3210 if (offset == 0)
3211 {
3212 pixel=Dc;
3213 break;
3214 }
3215 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3216 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3217 white_luminance,illuminant,&hue,&chroma,&luma);
3218 luma+=(0.01*percent_luma*offset)/midpoint;
3219 chroma*=0.01*percent_chroma;
3220 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3221 white_luminance,illuminant,&red,&green,&blue);
3222 switch (channel)
3223 {
3224 case RedPixelChannel: pixel=red; break;
3225 case GreenPixelChannel: pixel=green; break;
3226 case BluePixelChannel: pixel=blue; break;
3227 default: pixel=Dc; break;
3228 }
3229 break;
3230 }
3231 case ModulusAddCompositeOp:
3232 {
3233 if (compose_sync == MagickFalse)
3234 {
3235 pixel=(Quantum) QuantumRange*((Sc+Dc)-floor(Sc+Dc));
3236 break;
3237 }
3238 pixel=(Quantum) QuantumRange*((Sca+Dca)-floor(Sca+Dca));
3239 break;
3240 }
3241 case ModulusSubtractCompositeOp:
3242 {
3243 if (compose_sync == MagickFalse)
3244 {
3245 pixel=(Quantum) QuantumRange*((Sc-Dc)-floor(Sc-Dc));
3246 break;
3247 }
3248 pixel=(Quantum) QuantumRange*((Sca-Dca)-floor(Sca-Dca));
3249 break;
3250 }
3251 case MultiplyCompositeOp:
3252 {
3253 if (compose_sync == MagickFalse)
3254 {
3255 pixel=(double) QuantumRange*Sc*Dc;
3256 break;
3257 }
3258 pixel=(double) QuantumRange*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
3259 break;
3260 }
3261 case NegateCompositeOp:
3262 {
3263 D=(Da > 0.0) ? Dca/Da : 0.0;
3264 S=(Sa > 0.0) ? Sca/Sa : 0.0;
3265 pixel=(double) QuantumRange*((1.0-fabs(1.0-S-D))*Da);
3266 break;
3267 }
3268 case NoCompositeOp:
3269 {
3270 pixel=(double) QuantumRange*Dca;
3271 break;
3272 }
3273 case OutCompositeOp:
3274 case SrcOutCompositeOp:
3275 {
3276 pixel=(double) QuantumRange*(Sca*(1.0-Da));
3277 break;
3278 }
3279 case OverCompositeOp:
3280 case SrcOverCompositeOp:
3281 {
3282 if ((Sa+Da*(1.0-Sa)) <= MagickEpsilon)
3283 pixel=0.0;
3284 else
3285 pixel=(double) QuantumRange*gamma*((Sca+Dca*(1.0-Sa))/
3286 (Sa+Da*(1.0-Sa)));
3287 break;
3288 }
3289 case OverlayCompositeOp:
3290 {
3291 if ((2.0*Dca) < Da)
3292 {
3293 pixel=(double) QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+
3294 Sca*(1.0-Da));
3295 break;
3296 }
3297 pixel=(double) QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*
3298 (1.0-Sa)+Sca*(1.0-Da));
3299 break;
3300 }
3301 case PegtopLightCompositeOp:
3302 {
3303 if (fabs((double) Da) < MagickEpsilon)
3304 {
3305 pixel=(double) QuantumRange*gamma*Sca;
3306 break;
3307 }
3308 if (RoundToUnity(Sca) <= 0.5)
3309 blend=RoundToUnity(Dca/Da)-(1.0-2.0*RoundToUnity(Sca))*
3310 RoundToUnity(Dca/Da)*(1.0-RoundToUnity(Dca/Da));
3311 else
3312 {
3313 if (RoundToUnity(Dca/Da) <= 0.25)
3314 blend=((16.0*RoundToUnity(Dca/Da)-12.0)*RoundToUnity(Dca/Da)+
3315 4.0)*RoundToUnity(Dca/Da);
3316 else
3317 blend=sqrt(RoundToUnity(Dca/Da));
3318 blend=RoundToUnity(Dca/Da)+(2.0*RoundToUnity(Sca)-1.0)*
3319 (blend-RoundToUnity(Dca/Da));
3320 }
3321 pixel=(double) QuantumRange*gamma*(RoundToUnity(blend)*Da*Sa+Dca*
3322 (1.0-Sa));
3323 break;
3324 }
3325 case PinLightCompositeOp:
3326 {
3327 /*
3328 Adobe Pin Light (colors in [0,1]):
3329
3330 if (Cs <= 0.5)
3331 f = min(Cd, 2*Cs);
3332 else
3333 f = max(Cd, 2*Cs - 1);
3334 */
3335 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3336 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3337 if (S <= 0.5)
3338 blend=MagickMin(D,2.0*S);
3339 else
3340 blend=MagickMax(D,2.0*S-1.0);
3341 pixel=(double) QuantumRange*gamma*(blend*RoundToUnity(Sa+Da-Sa*Da));
3342 break;
3343 }
3344 case PlusCompositeOp:
3345 {
3346 D=(Da > 0.0) ? Dca/Da : 0.0;
3347 S=(Sa > 0.0) ? Sca/Sa : 0.0;
3348 pixel=(double) QuantumRange*(RoundToUnity(Sa+Da-Sa*Da)*
3349 RoundToUnity(S+D));
3350 break;
3351 }
3352 case ReflectCompositeOp:
3353 {
3354 if (compose_sync == MagickFalse)
3355 {
3356 if (Dc < 1.0)
3357 blend=(Sc*Sc)/(1.0-Dc);
3358 else
3359 blend=1.0;
3360 pixel=(double) QuantumRange*RoundToUnity(blend);
3361 break;
3362 }
3363 if (Sa > 0.0)
3364 S=Sca/Sa;
3365 else
3366 S=0.0;
3367 if (Da > 0.0)
3368 D=Dca/Da;
3369 else
3370 D=0.0;
3371 if (D < 1.0)
3372 blend=(S*S)/(1.0-D);
3373 else
3374 blend=1.0;
3375 pixel=(double) QuantumRange*RoundToUnity((Sa+Da-Sa*Da)*blend);
3376 break;
3377 }
3378 case RMSECompositeOp:
3379 {
3380 double
3381 gray;
3382
3383 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3384 {
3385 pixel=Dc;
3386 break;
3387 }
3388 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3389 {
3390 pixel=Sc;
3391 break;
3392 }
3393 gray=sqrt(
3394 (canvas_pixel.red-source_pixel.red)*
3395 (canvas_pixel.red-source_pixel.red)+
3396 (canvas_pixel.green-source_pixel.green)*
3397 (canvas_pixel.green-source_pixel.green)+
3398 (canvas_pixel.blue-source_pixel.blue)*
3399 (canvas_pixel.blue-source_pixel.blue)/3.0);
3400 switch (channel)
3401 {
3402 case RedPixelChannel: pixel=gray; break;
3403 case GreenPixelChannel: pixel=gray; break;
3404 case BluePixelChannel: pixel=gray; break;
3405 default: pixel=Dc; break;
3406 }
3407 break;
3408 }
3409 case SaturateCompositeOp:
3410 {
3411 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3412 {
3413 pixel=Dc;
3414 break;
3415 }
3416 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3417 {
3418 pixel=Sc;
3419 break;
3420 }
3421 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3422 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3423 white_luminance,illuminant,&hue,&chroma,&luma);
3424 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3425 (double) source_pixel.green,(double) source_pixel.blue,
3426 white_luminance,illuminant,&sans,&chroma,&sans);
3427 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3428 white_luminance,illuminant,&red,&green,&blue);
3429 switch (channel)
3430 {
3431 case RedPixelChannel: pixel=red; break;
3432 case GreenPixelChannel: pixel=green; break;
3433 case BluePixelChannel: pixel=blue; break;
3434 default: pixel=Dc; break;
3435 }
3436 break;
3437 }
3438 case ScreenCompositeOp:
3439 {
3440 if (compose_sync == MagickFalse)
3441 {
3442 pixel=(double) QuantumRange*RoundToUnity(Sc+Dc-Sc*Dc);
3443 break;
3444 }
3445 if (Sa > 0.0)
3446 S=Sca/Sa;
3447 else
3448 S=0.0;
3449 if (Da > 0.0)
3450 D=Dca/Da;
3451 else
3452 D=0.0;
3453 if ((Sa+Da-Sa*Da) > 0.0)
3454 pixel=(double) QuantumRange*RoundToUnity((Sa+Da-Sa*Da)*(S+D-S*D));
3455 else
3456 pixel=0.0;
3457 break;
3458 }
3459 case SoftBurnCompositeOp:
3460 {
3461 if (RoundToUnity(Dca) <= 0.0)
3462 blend = 0.0;
3463 else
3464 if (RoundToUnity(Sca) >= 1.0)
3465 blend = 1.0;
3466 else
3467 blend=1.0-MagickMin(1.0,(1.0-RoundToUnity(Dca))/
3468 RoundToUnity(Sca));
3469 pixel=(double) QuantumRange*gamma*RoundToUnity(blend);
3470 break;
3471 }
3472 case SoftDodgeCompositeOp:
3473 {
3474 if (RoundToUnity(Sca) <= 0.0)
3475 blend=RoundToUnity(Dca);
3476 else
3477 if (RoundToUnity(Dca) >= 1.0)
3478 blend=1.0;
3479 else
3480 blend=MagickMin(1.0,RoundToUnity(Dca)/(1.0-RoundToUnity(Sca)));
3481 pixel=(double) QuantumRange*gamma*RoundToUnity(blend);
3482 break;
3483 }
3484 case SoftLightCompositeOp:
3485 {
3486 if (RoundToUnity(Sca) <= 0.5)
3487 {
3488 pixel=(double) QuantumRange*gamma*(RoundToUnity(Dca)*Sa+Da*
3489 (RoundToUnity(Dca)-(1.0-2.0*RoundToUnity(Sca))*
3490 RoundToUnity(Dca)*(1.0-RoundToUnity(Dca)))+RoundToUnity(Sca)*
3491 (1.0-Da)+RoundToUnity(Dca)*(1.0-Sa));
3492 break;
3493 }
3494 if (RoundToUnity(Dca) > 0.25)
3495 blend=sqrt(RoundToUnity(Dca));
3496 else
3497 blend=((16.0*RoundToUnity(Dca)-12.0)*RoundToUnity(Dca)+4.0)*
3498 RoundToUnity(Dca);
3499 pixel=(double) QuantumRange*gamma*(RoundToUnity(Dca)*Sa+Da*
3500 (RoundToUnity(Dca)+(2.0*RoundToUnity(Sca)-1.0)*
3501 (RoundToUnity(blend)-RoundToUnity(Dca)))+RoundToUnity(Sca)*
3502 (1.0-Da)+RoundToUnity(Dca)*(1.0-Sa));
3503 break;
3504 }
3505 case StampCompositeOp:
3506 {
3507 pixel=(double) QuantumRange*RoundToUnity(Sca+Dca-1.0);
3508 break;
3509 }
3510 case StereoCompositeOp:
3511 {
3512 if (channel == RedPixelChannel)
3513 pixel=(MagickRealType) GetPixelRed(source_image,p);
3514 break;
3515 }
3516 case ThresholdCompositeOp:
3517 {
3518 S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
3519 D=(Da > 0.0) ? (Dca/Da) : 0.0;
3520 if (fabs(2.0*(S-D)) < threshold)
3521 blend=D;
3522 else
3523 blend=D+(S-D)*amount;
3524 pixel=(double) QuantumRange*(Sa*Da*RoundToUnity(blend)+Sca*
3525 (1.0-Da)+Dca*(1.0-Sa));
3526 break;
3527 }
3528 case VividLightCompositeOp:
3529 {
3530 if ((fabs((double) Sa) < MagickEpsilon) ||
3531 (fabs((double) Da) < MagickEpsilon))
3532 {
3533 pixel=(double) QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*
3534 (1.0-Sa));
3535 break;
3536 }
3537 if (RoundToUnity(Sca/Sa) <= 0.0)
3538 blend=0.0;
3539 else
3540 if (RoundToUnity(Sca/Sa) < 0.5)
3541 blend=1.0-(1.0-RoundToUnity(Dca/Da))/(2.0*RoundToUnity(Sca/Sa));
3542 else
3543 if (RoundToUnity(Sca/Sa) < 1.0)
3544 blend=RoundToUnity(Dca/Da)/(2.0*(1.0-RoundToUnity(Sca/Sa)));
3545 else
3546 blend=1.0;
3547 pixel=(double) QuantumRange*gamma*(Sa*Da*RoundToUnity(blend)+Sca*
3548 (1.0-Da)+Dca*(1.0-Sa));
3549 break;
3550 }
3551 case XorCompositeOp:
3552 {
3553 pixel=(double) QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
3554 break;
3555 }
3556 default:
3557 {
3558 pixel=Sc;
3559 break;
3560 }
3561 }
3562 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
3563 }
3564 p+=(ptrdiff_t) GetPixelChannels(source_image);
3565 channels=GetPixelChannels(source_image);
3566 if (p >= (pixels+channels*source_image->columns))
3567 p=pixels;
3568 q+=(ptrdiff_t) GetPixelChannels(image);
3569 }
3570 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3571 status=MagickFalse;
3572 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3573 {
3574 MagickBooleanType
3575 proceed;
3576
3577#if defined(MAGICKCORE_OPENMP_SUPPORT)
3578 #pragma omp atomic
3579#endif
3580 progress++;
3581 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
3582 if (proceed == MagickFalse)
3583 status=MagickFalse;
3584 }
3585 }
3586 source_view=DestroyCacheView(source_view);
3587 image_view=DestroyCacheView(image_view);
3588 random_info=DestroyRandomInfo(random_info);
3589 if (canvas_image != (Image * ) NULL)
3590 canvas_image=DestroyImage(canvas_image);
3591 else
3592 source_image=DestroyImage(source_image);
3593 return(status);
3594}
3595
3596/*
3597%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3598% %
3599% %
3600% %
3601% T e x t u r e I m a g e %
3602% %
3603% %
3604% %
3605%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3606%
3607% TextureImage() repeatedly tiles the texture image across and down the image
3608% canvas.
3609%
3610% The format of the TextureImage method is:
3611%
3612% MagickBooleanType TextureImage(Image *image,const Image *texture,
3613% ExceptionInfo *exception)
3614%
3615% A description of each parameter follows:
3616%
3617% o image: the image.
3618%
3619% o texture_image: This image is the texture to layer on the background.
3620%
3621*/
3622MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
3623 ExceptionInfo *exception)
3624{
3625#define TextureImageTag "Texture/Image"
3626
3627 CacheView
3628 *image_view,
3629 *texture_view;
3630
3631 Image
3632 *texture_image;
3633
3634 MagickBooleanType
3635 status;
3636
3637 ssize_t
3638 y;
3639
3640 assert(image != (Image *) NULL);
3641 assert(image->signature == MagickCoreSignature);
3642 if (IsEventLogging() != MagickFalse)
3643 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3644 if (texture == (const Image *) NULL)
3645 return(MagickFalse);
3646 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
3647 return(MagickFalse);
3648 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
3649 if (texture_image == (const Image *) NULL)
3650 return(MagickFalse);
3651 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
3652 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
3653 exception);
3654 status=MagickTrue;
3655 if ((image->compose != CopyCompositeOp) &&
3656 ((image->compose != OverCompositeOp) ||
3657 (image->alpha_trait != UndefinedPixelTrait) ||
3658 (texture_image->alpha_trait != UndefinedPixelTrait)))
3659 {
3660 /*
3661 Tile texture onto the image background.
3662 */
3663 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
3664 {
3665 ssize_t
3666 x;
3667
3668 if (status == MagickFalse)
3669 continue;
3670 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
3671 {
3672 MagickBooleanType
3673 thread_status;
3674
3675 thread_status=CompositeImage(image,texture_image,image->compose,
3676 MagickTrue,x+texture_image->tile_offset.x,y+
3677 texture_image->tile_offset.y,exception);
3678 if (thread_status == MagickFalse)
3679 {
3680 status=thread_status;
3681 break;
3682 }
3683 }
3684 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3685 {
3686 MagickBooleanType
3687 proceed;
3688
3689 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3690 image->rows);
3691 if (proceed == MagickFalse)
3692 status=MagickFalse;
3693 }
3694 }
3695 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
3696 image->rows,image->rows);
3697 texture_image=DestroyImage(texture_image);
3698 return(status);
3699 }
3700 /*
3701 Tile texture onto the image background (optimized).
3702 */
3703 status=MagickTrue;
3704 texture_view=AcquireVirtualCacheView(texture_image,exception);
3705 image_view=AcquireAuthenticCacheView(image,exception);
3706#if defined(MAGICKCORE_OPENMP_SUPPORT)
3707 #pragma omp parallel for schedule(static) shared(status) \
3708 magick_number_threads(texture_image,image,image->rows,2)
3709#endif
3710 for (y=0; y < (ssize_t) image->rows; y++)
3711 {
3712 MagickBooleanType
3713 sync;
3714
3715 const Quantum
3716 *p,
3717 *pixels;
3718
3719 ssize_t
3720 x;
3721
3722 Quantum
3723 *q;
3724
3725 size_t
3726 width;
3727
3728 if (status == MagickFalse)
3729 continue;
3730 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
3731 (y+texture_image->tile_offset.y) % (ssize_t) texture_image->rows,
3732 texture_image->columns,1,exception);
3733 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3734 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3735 {
3736 status=MagickFalse;
3737 continue;
3738 }
3739 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
3740 {
3741 ssize_t
3742 j;
3743
3744 p=pixels;
3745 width=texture_image->columns;
3746 if ((x+(ssize_t) width) > (ssize_t) image->columns)
3747 width=image->columns-(size_t) x;
3748 for (j=0; j < (ssize_t) width; j++)
3749 {
3750 ssize_t
3751 i;
3752
3753 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
3754 {
3755 PixelChannel channel = GetPixelChannelChannel(texture_image,i);
3756 PixelTrait traits = GetPixelChannelTraits(image,channel);
3757 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
3758 channel);
3759 if ((traits == UndefinedPixelTrait) ||
3760 (texture_traits == UndefinedPixelTrait))
3761 continue;
3762 SetPixelChannel(image,channel,p[i],q);
3763 }
3764 p+=(ptrdiff_t) GetPixelChannels(texture_image);
3765 q+=(ptrdiff_t) GetPixelChannels(image);
3766 }
3767 }
3768 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3769 if (sync == MagickFalse)
3770 status=MagickFalse;
3771 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3772 {
3773 MagickBooleanType
3774 proceed;
3775
3776 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3777 image->rows);
3778 if (proceed == MagickFalse)
3779 status=MagickFalse;
3780 }
3781 }
3782 texture_view=DestroyCacheView(texture_view);
3783 image_view=DestroyCacheView(image_view);
3784 texture_image=DestroyImage(texture_image);
3785 return(status);
3786}