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