MagickCore 7.1.2-28
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 pixel=(double) QuantumRange*alpha;
1093 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1094 ClampToQuantum(pixel);
1095 continue;
1096 }
1097 /*
1098 Sc: source color.
1099 Dc: canvas color.
1100 */
1101 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1102 Dc=(MagickRealType) q[i];
1103 if ((traits & CopyPixelTrait) != 0)
1104 {
1105 /*
1106 Copy channel.
1107 */
1108 q[i]=ClampToQuantum(Sc);
1109 continue;
1110 }
1111 /*
1112 Porter-Duff compositions:
1113 Sca: source normalized color multiplied by alpha.
1114 Dca: normalized canvas color multiplied by alpha.
1115 */
1116 Sca=QuantumScale*Sa*Sc;
1117 Dca=QuantumScale*Da*Dc;
1118 gamma=MagickSafeReciprocal(alpha);
1119 pixel=(double) QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
1120 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
1121 }
1122 p+=(ptrdiff_t) GetPixelChannels(source_image);
1123 channels=GetPixelChannels(source_image);
1124 if (p >= (pixels+channels*source_image->columns))
1125 p=pixels;
1126 q+=(ptrdiff_t) GetPixelChannels(image);
1127 }
1128 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1129 status=MagickFalse;
1130 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1131 {
1132 MagickBooleanType
1133 proceed;
1134
1135#if defined(MAGICKCORE_OPENMP_SUPPORT)
1136 #pragma omp atomic
1137#endif
1138 progress++;
1139 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
1140 if (proceed == MagickFalse)
1141 status=MagickFalse;
1142 }
1143 }
1144 source_view=DestroyCacheView(source_view);
1145 image_view=DestroyCacheView(image_view);
1146 return(status);
1147}
1148
1149static MagickBooleanType SaliencyBlendImage(Image *image,
1150 const Image *source_image,const ssize_t x_offset,const ssize_t y_offset,
1151 const double iterations,const double residual_threshold,const size_t tick,
1152 ExceptionInfo *exception)
1153{
1154 Image
1155 *crop_image,
1156 *divergent_image,
1157 *relax_image,
1158 *residual_image = (Image *) NULL;
1159
1160 KernelInfo
1161 *kernel_info;
1162
1163 MagickBooleanType
1164 status = MagickTrue,
1165 verbose = MagickFalse;
1166
1167 RectangleInfo
1168 crop_info = {
1169 source_image->columns,
1170 source_image->rows,
1171 x_offset,
1172 y_offset
1173 };
1174
1175 ssize_t
1176 i;
1177
1178 /*
1179 Saliency blend composite operator.
1180 */
1181 crop_image=CropImage(image,&crop_info,exception);
1182 if (crop_image == (Image *) NULL)
1183 return(MagickFalse);
1184 DisableCompositeClampUnlessSpecified(crop_image);
1185 divergent_image=BlendDivergentImage(crop_image,source_image,exception);
1186 if (divergent_image == (Image *) NULL)
1187 {
1188 crop_image=DestroyImage(crop_image);
1189 return(MagickFalse);
1190 }
1191 (void) ResetImagePage(crop_image,"0x0+0+0");
1192 relax_image=BlendMeanImage(crop_image,source_image,exception);
1193 if (relax_image == (Image *) NULL)
1194 {
1195 crop_image=DestroyImage(crop_image);
1196 divergent_image=DestroyImage(divergent_image);
1197 return(MagickFalse);
1198 }
1199 status=BlendMaskAlphaChannel(crop_image,source_image,exception);
1200 if (status == MagickFalse)
1201 {
1202 crop_image=DestroyImage(crop_image);
1203 divergent_image=DestroyImage(divergent_image);
1204 return(MagickFalse);
1205 }
1206 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1207 if (residual_image == (Image *) NULL)
1208 {
1209 crop_image=DestroyImage(crop_image);
1210 relax_image=DestroyImage(relax_image);
1211 return(MagickFalse);
1212 }
1213 /*
1214 Convolve relaxed image and blur area of interest.
1215 */
1216 kernel_info=AcquireKernelInfo("3x3:0,0.25,0,0.25,0,0.25,0,0.25,0",exception);
1217 if (kernel_info == (KernelInfo *) NULL)
1218 {
1219 crop_image=DestroyImage(crop_image);
1220 residual_image=DestroyImage(residual_image);
1221 relax_image=DestroyImage(relax_image);
1222 return(MagickFalse);
1223 }
1224 verbose=IsStringTrue(GetImageArtifact(image,"verbose"));
1225 if (verbose != MagickFalse)
1226 (void) FormatLocaleFile(stderr,"saliency blending:\n");
1227 for (i=0; i < (ssize_t) iterations; i++)
1228 {
1229 double
1230 residual = 1.0;
1231
1232 Image
1233 *convolve_image,
1234 *sum_image;
1235
1236 convolve_image=ConvolveImage(relax_image,kernel_info,exception);
1237 if (convolve_image == (Image *) NULL)
1238 break;
1239 relax_image=DestroyImage(relax_image);
1240 relax_image=convolve_image;
1241 sum_image=BlendSumImage(relax_image,divergent_image,1.0,-1.0,exception);
1242 if (sum_image == (Image *) NULL)
1243 break;
1244 relax_image=DestroyImage(relax_image);
1245 relax_image=sum_image;
1246 status=CompositeOverImage(relax_image,crop_image,MagickTrue,0,0,exception);
1247 if (status == MagickFalse)
1248 break;
1249 status=BlendRMSEResidual(relax_image,residual_image,&residual,exception);
1250 if (status == MagickFalse)
1251 break;
1252 if ((verbose != MagickFalse) && ((i % MagickMax(tick,1)) == 0))
1253 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double) residual);
1254 if (residual < residual_threshold)
1255 {
1256 if (verbose != MagickFalse)
1257 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double)
1258 residual);
1259 break;
1260 }
1261 residual_image=DestroyImage(residual_image);
1262 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1263 if (residual_image == (Image *) NULL)
1264 break;
1265 }
1266 kernel_info=DestroyKernelInfo(kernel_info);
1267 crop_image=DestroyImage(crop_image);
1268 divergent_image=DestroyImage(divergent_image);
1269 residual_image=DestroyImage(residual_image);
1270 /*
1271 Composite relaxed over the background image.
1272 */
1273 status=CompositeOverImage(image,relax_image,MagickTrue,x_offset,y_offset,
1274 exception);
1275 relax_image=DestroyImage(relax_image);
1276 return(status);
1277}
1278
1279static MagickBooleanType SeamlessBlendImage(Image *image,
1280 const Image *source_image,const ssize_t x_offset,const ssize_t y_offset,
1281 const double iterations,const double residual_threshold,const size_t tick,
1282 ExceptionInfo *exception)
1283{
1284 Image
1285 *crop_image,
1286 *foreground_image,
1287 *mean_image,
1288 *relax_image,
1289 *residual_image,
1290 *sum_image;
1291
1292 KernelInfo
1293 *kernel_info;
1294
1295 MagickBooleanType
1296 status = MagickTrue,
1297 verbose = MagickFalse;
1298
1299 RectangleInfo
1300 crop_info = {
1301 source_image->columns,
1302 source_image->rows,
1303 x_offset,
1304 y_offset
1305 };
1306
1307 ssize_t
1308 i;
1309
1310 /*
1311 Seamless blend composite operator.
1312 */
1313 crop_image=CropImage(image,&crop_info,exception);
1314 if (crop_image == (Image *) NULL)
1315 return(MagickFalse);
1316 DisableCompositeClampUnlessSpecified(crop_image);
1317 (void) ResetImagePage(crop_image,"0x0+0+0");
1318 sum_image=BlendSumImage(crop_image,source_image,1.0,-1.0,exception);
1319 crop_image=DestroyImage(crop_image);
1320 if (sum_image == (Image *) NULL)
1321 return(MagickFalse);
1322 mean_image=BlendMeanImage(sum_image,source_image,exception);
1323 sum_image=DestroyImage(sum_image);
1324 if (mean_image == (Image *) NULL)
1325 return(MagickFalse);
1326 relax_image=CloneImage(mean_image,0,0,MagickTrue,exception);
1327 if (relax_image == (Image *) NULL)
1328 {
1329 mean_image=DestroyImage(mean_image);
1330 return(MagickFalse);
1331 }
1332 status=BlendMaskAlphaChannel(mean_image,source_image,exception);
1333 if (status == MagickFalse)
1334 {
1335 relax_image=DestroyImage(relax_image);
1336 mean_image=DestroyImage(mean_image);
1337 return(MagickFalse);
1338 }
1339 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1340 if (residual_image == (Image *) NULL)
1341 {
1342 relax_image=DestroyImage(relax_image);
1343 mean_image=DestroyImage(mean_image);
1344 return(MagickFalse);
1345 }
1346 /*
1347 Convolve relaxed image and blur area of interest.
1348 */
1349 kernel_info=AcquireKernelInfo("3x3:0,0.25,0,0.25,0,0.25,0,0.25,0",exception);
1350 if (kernel_info == (KernelInfo *) NULL)
1351 {
1352 residual_image=DestroyImage(residual_image);
1353 relax_image=DestroyImage(relax_image);
1354 mean_image=DestroyImage(mean_image);
1355 return(MagickFalse);
1356 }
1357 verbose=IsStringTrue(GetImageArtifact(image,"verbose"));
1358 if (verbose != MagickFalse)
1359 (void) FormatLocaleFile(stderr,"seamless blending:\n");
1360 for (i=0; i < (ssize_t) iterations; i++)
1361 {
1362 double
1363 residual = 1.0;
1364
1365 Image
1366 *convolve_image;
1367
1368 convolve_image=ConvolveImage(relax_image,kernel_info,exception);
1369 if (convolve_image == (Image *) NULL)
1370 break;
1371 relax_image=DestroyImage(relax_image);
1372 relax_image=convolve_image;
1373 status=CompositeOverImage(relax_image,mean_image,MagickTrue,0,0,exception);
1374 if (status == MagickFalse)
1375 break;
1376 status=BlendRMSEResidual(relax_image,residual_image,&residual,exception);
1377 if (status == MagickFalse)
1378 break;
1379 if ((verbose != MagickFalse) && ((i % MagickMax(tick,1)) == 0))
1380 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double) residual);
1381 if (residual < residual_threshold)
1382 {
1383 if (verbose != MagickFalse)
1384 (void) FormatLocaleFile(stderr," %g: %g\n",(double) i,(double)
1385 residual);
1386 break;
1387 }
1388 if (residual_image != (Image *) NULL)
1389 residual_image=DestroyImage(residual_image);
1390 residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1391 if (residual_image == (Image *) NULL)
1392 break;
1393 }
1394 kernel_info=DestroyKernelInfo(kernel_info);
1395 mean_image=DestroyImage(mean_image);
1396 residual_image=DestroyImage(residual_image);
1397 /*
1398 Composite the foreground image over the background image.
1399 */
1400 foreground_image=BlendSumImage(source_image,relax_image,1.0,1.0,exception);
1401 relax_image=DestroyImage(relax_image);
1402 if (foreground_image == (Image *) NULL)
1403 return(MagickFalse);
1404 (void) SetImageMask(foreground_image,ReadPixelMask,(const Image *) NULL,
1405 exception);
1406 status=CompositeOverImage(image,foreground_image,MagickTrue,x_offset,y_offset,
1407 exception);
1408 foreground_image=DestroyImage(foreground_image);
1409 return(status);
1410}
1411
1412MagickExport MagickBooleanType CompositeImage(Image *image,
1413 const Image *composite,const CompositeOperator compose,
1414 const MagickBooleanType clip_to_self,const ssize_t x_offset,
1415 const ssize_t y_offset,ExceptionInfo *exception)
1416{
1417#define CompositeImageTag "Composite/Image"
1418
1419 CacheView
1420 *source_view,
1421 *image_view;
1422
1423 ColorspaceType
1424 colorspace = HCLColorspace;
1425
1426 const char
1427 *artifact;
1428
1429 double
1430 white_luminance = 10000.0;
1431
1432 GeometryInfo
1433 geometry_info;
1434
1435 IlluminantType
1436 illuminant = D65Illuminant;
1437
1438 Image
1439 *canvas_image,
1440 *source_image;
1441
1442 MagickBooleanType
1443 clamp,
1444 compose_sync,
1445 status;
1446
1447 MagickOffsetType
1448 progress;
1449
1450 MagickRealType
1451 amount,
1452 canvas_dissolve,
1453 midpoint,
1454 percent_luma,
1455 percent_chroma,
1456 source_dissolve,
1457 threshold;
1458
1459 MagickStatusType
1460 flags;
1461
1462 ssize_t
1463 y;
1464
1465 assert(image != (Image *) NULL);
1466 assert(image->signature == MagickCoreSignature);
1467 assert(composite != (Image *) NULL);
1468 assert(composite->signature == MagickCoreSignature);
1469 if (IsEventLogging() != MagickFalse)
1470 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1471 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1472 return(MagickFalse);
1473 source_image=CloneImage(composite,0,0,MagickTrue,exception);
1474 if (source_image == (const Image *) NULL)
1475 return(MagickFalse);
1476 (void) SetImageColorspace(source_image,image->colorspace,exception);
1477 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
1478 {
1479 status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
1480 y_offset,exception);
1481 source_image=DestroyImage(source_image);
1482 return(status);
1483 }
1484 amount=0.5;
1485 canvas_image=(Image *) NULL;
1486 canvas_dissolve=1.0;
1487 white_luminance=10000.0;
1488 artifact=GetImageArtifact(image,"compose:white-luminance");
1489 if (artifact != (const char *) NULL)
1490 white_luminance=StringToDouble(artifact,(char **) NULL);
1491 artifact=GetImageArtifact(image,"compose:illuminant");
1492 if (artifact != (const char *) NULL)
1493 {
1494 ssize_t
1495 illuminant_type;
1496
1497 illuminant_type=ParseCommandOption(MagickIlluminantOptions,MagickFalse,
1498 artifact);
1499 if (illuminant_type < 0)
1500 illuminant=UndefinedIlluminant;
1501 else
1502 illuminant=(IlluminantType) illuminant_type;
1503 }
1504 artifact=GetImageArtifact(image,"compose:colorspace");
1505 if (artifact != (const char *) NULL)
1506 {
1507 ssize_t
1508 colorspace_type;
1509
1510 colorspace_type=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
1511 artifact);
1512 if (colorspace_type < 0)
1513 colorspace=UndefinedColorspace;
1514 else
1515 colorspace=(ColorspaceType) colorspace_type;
1516 }
1517 clamp=MagickTrue;
1518 artifact=GetImageArtifact(image,"compose:clamp");
1519 if (artifact != (const char *) NULL)
1520 clamp=IsStringTrue(artifact);
1521 compose_sync=MagickTrue;
1522 artifact=GetImageArtifact(image,"compose:sync");
1523 if (artifact != (const char *) NULL)
1524 compose_sync=IsStringTrue(artifact);
1525 SetGeometryInfo(&geometry_info);
1526 percent_luma=100.0;
1527 percent_chroma=100.0;
1528 source_dissolve=1.0;
1529 threshold=0.05f;
1530 switch (compose)
1531 {
1532 case CopyCompositeOp:
1533 {
1534 if ((x_offset < 0) || (y_offset < 0))
1535 break;
1536 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
1537 break;
1538 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
1539 break;
1540 if ((source_image->alpha_trait == UndefinedPixelTrait) &&
1541 (image->alpha_trait != UndefinedPixelTrait))
1542 (void) SetImageAlphaChannel(source_image,OpaqueAlphaChannel,exception);
1543 status=MagickTrue;
1544 source_view=AcquireVirtualCacheView(source_image,exception);
1545 image_view=AcquireAuthenticCacheView(image,exception);
1546#if defined(MAGICKCORE_OPENMP_SUPPORT)
1547 #pragma omp parallel for schedule(static) shared(status) \
1548 magick_number_threads(source_image,image,source_image->rows,4)
1549#endif
1550 for (y=0; y < (ssize_t) source_image->rows; y++)
1551 {
1552 MagickBooleanType
1553 sync;
1554
1555 const Quantum
1556 *p;
1557
1558 Quantum
1559 *q;
1560
1561 ssize_t
1562 x;
1563
1564 if (status == MagickFalse)
1565 continue;
1566 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1567 exception);
1568 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1569 source_image->columns,1,exception);
1570 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1571 {
1572 status=MagickFalse;
1573 continue;
1574 }
1575 for (x=0; x < (ssize_t) source_image->columns; x++)
1576 {
1577 ssize_t
1578 i;
1579
1580 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
1581 {
1582 p+=(ptrdiff_t) GetPixelChannels(source_image);
1583 q+=(ptrdiff_t) GetPixelChannels(image);
1584 continue;
1585 }
1586 for (i=0; i < (ssize_t) GetPixelChannels(source_image); i++)
1587 {
1588 PixelChannel channel = GetPixelChannelChannel(source_image,i);
1589 PixelTrait source_traits = GetPixelChannelTraits(source_image,
1590 channel);
1591 PixelTrait traits = GetPixelChannelTraits(image,channel);
1592 if ((source_traits == UndefinedPixelTrait) ||
1593 (traits == UndefinedPixelTrait))
1594 continue;
1595 SetPixelChannel(image,channel,p[i],q);
1596 }
1597 p+=(ptrdiff_t) GetPixelChannels(source_image);
1598 q+=(ptrdiff_t) GetPixelChannels(image);
1599 }
1600 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1601 if (sync == MagickFalse)
1602 status=MagickFalse;
1603 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1604 {
1605 MagickBooleanType
1606 proceed;
1607
1608 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
1609 y,image->rows);
1610 if (proceed == MagickFalse)
1611 status=MagickFalse;
1612 }
1613 }
1614 source_view=DestroyCacheView(source_view);
1615 image_view=DestroyCacheView(image_view);
1616 source_image=DestroyImage(source_image);
1617 return(status);
1618 }
1619 case IntensityCompositeOp:
1620 {
1621 if ((x_offset < 0) || (y_offset < 0))
1622 break;
1623 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
1624 break;
1625 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
1626 break;
1627 status=MagickTrue;
1628 source_view=AcquireVirtualCacheView(source_image,exception);
1629 image_view=AcquireAuthenticCacheView(image,exception);
1630#if defined(MAGICKCORE_OPENMP_SUPPORT)
1631 #pragma omp parallel for schedule(static) shared(status) \
1632 magick_number_threads(source_image,image,source_image->rows,4)
1633#endif
1634 for (y=0; y < (ssize_t) source_image->rows; y++)
1635 {
1636 MagickBooleanType
1637 sync;
1638
1639 const Quantum
1640 *p;
1641
1642 Quantum
1643 *q;
1644
1645 ssize_t
1646 x;
1647
1648 if (status == MagickFalse)
1649 continue;
1650 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1651 exception);
1652 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1653 source_image->columns,1,exception);
1654 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1655 {
1656 status=MagickFalse;
1657 continue;
1658 }
1659 for (x=0; x < (ssize_t) source_image->columns; x++)
1660 {
1661 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
1662 {
1663 p+=(ptrdiff_t) GetPixelChannels(source_image);
1664 q+=(ptrdiff_t) GetPixelChannels(image);
1665 continue;
1666 }
1667 SetPixelAlpha(image,clamp != MagickFalse ?
1668 ClampPixel(GetPixelIntensity(source_image,p)) :
1669 ClampToQuantum(GetPixelIntensity(source_image,p)),q);
1670 p+=(ptrdiff_t) GetPixelChannels(source_image);
1671 q+=(ptrdiff_t) GetPixelChannels(image);
1672 }
1673 sync=SyncCacheViewAuthenticPixels(image_view,exception);
1674 if (sync == MagickFalse)
1675 status=MagickFalse;
1676 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1677 {
1678 MagickBooleanType
1679 proceed;
1680
1681 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
1682 y,image->rows);
1683 if (proceed == MagickFalse)
1684 status=MagickFalse;
1685 }
1686 }
1687 source_view=DestroyCacheView(source_view);
1688 image_view=DestroyCacheView(image_view);
1689 source_image=DestroyImage(source_image);
1690 return(status);
1691 }
1692 case CopyAlphaCompositeOp:
1693 case ChangeMaskCompositeOp:
1694 {
1695 /*
1696 Modify canvas outside the overlaid region and require an alpha
1697 channel to exist, to add transparency.
1698 */
1699 if ((image->alpha_trait & BlendPixelTrait) == 0)
1700 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1701 break;
1702 }
1703 case BlurCompositeOp:
1704 {
1705 CacheView
1706 *canvas_view;
1707
1708 double
1709 angle_range,
1710 angle_start,
1711 height,
1712 width;
1713
1714 PixelInfo
1715 pixel;
1716
1717 ResampleFilter
1718 *resample_filter;
1719
1720 SegmentInfo
1721 blur;
1722
1723 /*
1724 Blur Image by resampling dictated by an overlay gradient map:
1725 X = red_channel; Y = green_channel; compose:args =
1726 x_scale[,y_scale[,angle]].
1727 */
1728 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
1729 if (canvas_image == (Image *) NULL)
1730 {
1731 source_image=DestroyImage(source_image);
1732 return(MagickFalse);
1733 }
1734 /*
1735 Gather the maximum blur sigma values from user.
1736 */
1737 flags=NoValue;
1738 artifact=GetImageArtifact(image,"compose:args");
1739 if (artifact != (const char *) NULL)
1740 flags=ParseGeometry(artifact,&geometry_info);
1741 if ((flags & WidthValue) == 0)
1742 {
1743 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1744 "InvalidSetting","'%s' '%s'","compose:args",artifact);
1745 source_image=DestroyImage(source_image);
1746 canvas_image=DestroyImage(canvas_image);
1747 return(MagickFalse);
1748 }
1749 /*
1750 Users input sigma now needs to be converted to the EWA ellipse size.
1751 The filter defaults to a sigma of 0.5 so to make this match the users
1752 input the ellipse size needs to be doubled.
1753 */
1754 width=2.0*geometry_info.rho;
1755 height=width;
1756 if ((flags & HeightValue) != 0)
1757 height=2.0*geometry_info.sigma;
1758 /*
1759 Default the unrotated ellipse width and height axis vectors.
1760 */
1761 blur.x1=width;
1762 blur.x2=0.0;
1763 blur.y1=0.0;
1764 blur.y2=height;
1765 if ((flags & XValue) != 0 )
1766 {
1767 MagickRealType
1768 angle;
1769
1770 /*
1771 Rotate vectors if a rotation angle is given.
1772 */
1773 angle=DegreesToRadians(geometry_info.xi);
1774 blur.x1=width*cos(angle);
1775 blur.x2=width*sin(angle);
1776 blur.y1=(-height*sin(angle));
1777 blur.y2=height*cos(angle);
1778 }
1779 angle_start=0.0;
1780 angle_range=0.0;
1781 if ((flags & YValue) != 0 )
1782 {
1783 /*
1784 Lets set a angle range and calculate in the loop.
1785 */
1786 angle_start=DegreesToRadians(geometry_info.xi);
1787 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1788 }
1789 /*
1790 Set up a gaussian cylindrical filter for EWA Blurring.
1791
1792 As the minimum ellipse radius of support*1.0 the EWA algorithm
1793 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
1794 This means that even 'No Blur' will be still a little blurry! The
1795 solution (as well as the problem of preventing any user expert filter
1796 settings, is to set our own user settings, restore them afterwards.
1797 */
1798 resample_filter=AcquireResampleFilter(image,exception);
1799 SetResampleFilter(resample_filter,GaussianFilter);
1800 /*
1801 Perform the variable blurring of each pixel in image.
1802 */
1803 GetPixelInfo(image,&pixel);
1804 source_view=AcquireVirtualCacheView(source_image,exception);
1805 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1806 for (y=0; y < (ssize_t) source_image->rows; y++)
1807 {
1808 MagickBooleanType
1809 sync;
1810
1811 const Quantum
1812 *magick_restrict p;
1813
1814 Quantum
1815 *magick_restrict q;
1816
1817 ssize_t
1818 x;
1819
1820 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1821 continue;
1822 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1823 exception);
1824 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1825 exception);
1826 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1827 break;
1828 for (x=0; x < (ssize_t) source_image->columns; x++)
1829 {
1830 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1831 {
1832 p+=(ptrdiff_t) GetPixelChannels(source_image);
1833 continue;
1834 }
1835 if (fabs(angle_range) > MagickEpsilon)
1836 {
1837 MagickRealType
1838 angle;
1839
1840 angle=angle_start+angle_range*QuantumScale*(double)
1841 GetPixelBlue(source_image,p);
1842 blur.x1=width*cos(angle);
1843 blur.x2=width*sin(angle);
1844 blur.y1=(-height*sin(angle));
1845 blur.y2=height*cos(angle);
1846 }
1847 ScaleResampleFilter(resample_filter,
1848 blur.x1*QuantumScale*(double) GetPixelRed(source_image,p),
1849 blur.y1*QuantumScale*(double) GetPixelGreen(source_image,p),
1850 blur.x2*QuantumScale*(double) GetPixelRed(source_image,p),
1851 blur.y2*QuantumScale*(double) GetPixelGreen(source_image,p) );
1852 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1853 (double) y_offset+y,&pixel,exception);
1854 SetPixelViaPixelInfo(canvas_image,&pixel,q);
1855 p+=(ptrdiff_t) GetPixelChannels(source_image);
1856 q+=(ptrdiff_t) GetPixelChannels(canvas_image);
1857 }
1858 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1859 if (sync == MagickFalse)
1860 break;
1861 }
1862 resample_filter=DestroyResampleFilter(resample_filter);
1863 source_view=DestroyCacheView(source_view);
1864 canvas_view=DestroyCacheView(canvas_view);
1865 source_image=DestroyImage(source_image);
1866 source_image=canvas_image;
1867 break;
1868 }
1869 case DisplaceCompositeOp:
1870 case DistortCompositeOp:
1871 {
1872 CacheView
1873 *canvas_view;
1874
1875 MagickRealType
1876 horizontal_scale,
1877 vertical_scale;
1878
1879 PixelInfo
1880 pixel;
1881
1882 PointInfo
1883 center,
1884 offset;
1885
1886 /*
1887 Displace/Distort based on overlay gradient map:
1888 X = red_channel; Y = green_channel;
1889 compose:args = x_scale[,y_scale[,center.x,center.y]]
1890 */
1891 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
1892 if (canvas_image == (Image *) NULL)
1893 {
1894 source_image=DestroyImage(source_image);
1895 return(MagickFalse);
1896 }
1897 SetGeometryInfo(&geometry_info);
1898 flags=NoValue;
1899 artifact=GetImageArtifact(image,"compose:args");
1900 if (artifact != (char *) NULL)
1901 flags=ParseGeometry(artifact,&geometry_info);
1902 if ((flags & (WidthValue | HeightValue)) == 0 )
1903 {
1904 if ((flags & AspectValue) == 0)
1905 {
1906 horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
1907 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
1908 }
1909 else
1910 {
1911 horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
1912 vertical_scale=(MagickRealType) (image->rows-1)/2.0;
1913 }
1914 }
1915 else
1916 {
1917 horizontal_scale=geometry_info.rho;
1918 vertical_scale=geometry_info.sigma;
1919 if ((flags & PercentValue) != 0)
1920 {
1921 if ((flags & AspectValue) == 0)
1922 {
1923 horizontal_scale*=(source_image->columns-1)/200.0;
1924 vertical_scale*=(source_image->rows-1)/200.0;
1925 }
1926 else
1927 {
1928 horizontal_scale*=(image->columns-1)/200.0;
1929 vertical_scale*=(image->rows-1)/200.0;
1930 }
1931 }
1932 if ((flags & HeightValue) == 0)
1933 vertical_scale=horizontal_scale;
1934 }
1935 /*
1936 Determine fixed center point for absolute distortion map
1937 Absolute distort ==
1938 Displace offset relative to a fixed absolute point
1939 Select that point according to +X+Y user inputs.
1940 default = center of overlay image
1941 arg flag '!' = locations/percentage relative to background image
1942 */
1943 center.x=(MagickRealType) x_offset;
1944 center.y=(MagickRealType) y_offset;
1945 if (compose == DistortCompositeOp)
1946 {
1947 if ((flags & XValue) == 0)
1948 if ((flags & AspectValue) != 0)
1949 center.x=(MagickRealType) ((image->columns-1)/2.0);
1950 else
1951 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1952 2.0);
1953 else
1954 if ((flags & AspectValue) != 0)
1955 center.x=geometry_info.xi;
1956 else
1957 center.x=(MagickRealType) (x_offset+geometry_info.xi);
1958 if ((flags & YValue) == 0)
1959 if ((flags & AspectValue) != 0)
1960 center.y=(MagickRealType) ((image->rows-1)/2.0);
1961 else
1962 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1963 else
1964 if ((flags & AspectValue) != 0)
1965 center.y=geometry_info.psi;
1966 else
1967 center.y=(MagickRealType) (y_offset+geometry_info.psi);
1968 }
1969 /*
1970 Shift the pixel offset point as defined by the provided,
1971 displacement/distortion map. -- Like a lens...
1972 */
1973 GetPixelInfo(image,&pixel);
1974 image_view=AcquireVirtualCacheView(image,exception);
1975 source_view=AcquireVirtualCacheView(source_image,exception);
1976 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1977 for (y=0; y < (ssize_t) source_image->rows; y++)
1978 {
1979 MagickBooleanType
1980 sync;
1981
1982 const Quantum
1983 *magick_restrict p;
1984
1985 Quantum
1986 *magick_restrict q;
1987
1988 ssize_t
1989 x;
1990
1991 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1992 continue;
1993 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1994 exception);
1995 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1996 exception);
1997 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1998 break;
1999 for (x=0; x < (ssize_t) source_image->columns; x++)
2000 {
2001 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
2002 {
2003 p+=(ptrdiff_t) GetPixelChannels(source_image);
2004 continue;
2005 }
2006 /*
2007 Displace the offset.
2008 */
2009 offset.x=(double) (horizontal_scale*((double) GetPixelRed(
2010 source_image,p)-(((MagickRealType) QuantumRange+1.0)/2.0)))/
2011 (((MagickRealType) QuantumRange+1.0)/2.0)+center.x+
2012 ((compose == DisplaceCompositeOp) ? x : 0);
2013 offset.y=(double) (vertical_scale*((double) GetPixelGreen(
2014 source_image,p)-(((MagickRealType) QuantumRange+1.0)/2.0)))/
2015 (((MagickRealType) QuantumRange+1.0)/2.0)+center.y+
2016 ((compose == DisplaceCompositeOp) ? y : 0);
2017 status=InterpolatePixelInfo(image,image_view,
2018 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2019 &pixel,exception);
2020 if (status == MagickFalse)
2021 break;
2022 /*
2023 Mask with the 'invalid pixel mask' in alpha channel.
2024 */
2025 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
2026 (QuantumScale*(double) GetPixelAlpha(source_image,p));
2027 SetPixelViaPixelInfo(canvas_image,&pixel,q);
2028 p+=(ptrdiff_t) GetPixelChannels(source_image);
2029 q+=(ptrdiff_t) GetPixelChannels(canvas_image);
2030 }
2031 if (x < (ssize_t) source_image->columns)
2032 break;
2033 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
2034 if (sync == MagickFalse)
2035 break;
2036 }
2037 canvas_view=DestroyCacheView(canvas_view);
2038 source_view=DestroyCacheView(source_view);
2039 image_view=DestroyCacheView(image_view);
2040 source_image=DestroyImage(source_image);
2041 source_image=canvas_image;
2042 break;
2043 }
2044 case DissolveCompositeOp:
2045 {
2046 /*
2047 Geometry arguments to dissolve factors.
2048 */
2049 artifact=GetImageArtifact(image,"compose:args");
2050 if (artifact != (char *) NULL)
2051 {
2052 flags=ParseGeometry(artifact,&geometry_info);
2053 source_dissolve=geometry_info.rho/100.0;
2054 canvas_dissolve=1.0;
2055 if ((source_dissolve-MagickEpsilon) < 0.0)
2056 source_dissolve=0.0;
2057 if ((source_dissolve+MagickEpsilon) > 1.0)
2058 {
2059 canvas_dissolve=2.0-source_dissolve;
2060 source_dissolve=1.0;
2061 }
2062 if ((flags & SigmaValue) != 0)
2063 canvas_dissolve=geometry_info.sigma/100.0;
2064 if ((canvas_dissolve-MagickEpsilon) < 0.0)
2065 canvas_dissolve=0.0;
2066 if ((canvas_dissolve+MagickEpsilon) > 1.0)
2067 canvas_dissolve=1.0;
2068 }
2069 break;
2070 }
2071 case BlendCompositeOp:
2072 {
2073 artifact=GetImageArtifact(image,"compose:args");
2074 if (artifact != (char *) NULL)
2075 {
2076 flags=ParseGeometry(artifact,&geometry_info);
2077 source_dissolve=geometry_info.rho/100.0;
2078 canvas_dissolve=1.0-source_dissolve;
2079 if ((flags & SigmaValue) != 0)
2080 canvas_dissolve=geometry_info.sigma/100.0;
2081 }
2082 break;
2083 }
2084 case SaliencyBlendCompositeOp:
2085 {
2086 double
2087 residual_threshold = 0.0002,
2088 iterations = 400.0;
2089
2090 size_t
2091 tick = 100;
2092
2093 artifact=GetImageArtifact(image,"compose:args");
2094 if (artifact != (char *) NULL)
2095 {
2096 flags=ParseGeometry(artifact,&geometry_info);
2097 iterations=geometry_info.rho;
2098 if ((flags & SigmaValue) != 0)
2099 residual_threshold=geometry_info.sigma;
2100 if ((flags & XiValue) != 0)
2101 tick=(size_t) geometry_info.xi;
2102 }
2103 status=SaliencyBlendImage(image,composite,x_offset,y_offset,iterations,
2104 residual_threshold,tick,exception);
2105 source_image=DestroyImage(source_image);
2106 return(status);
2107 }
2108 case SeamlessBlendCompositeOp:
2109 {
2110 double
2111 residual_threshold = 0.0002,
2112 iterations = 400.0;
2113
2114 size_t
2115 tick = 100;
2116
2117 artifact=GetImageArtifact(image,"compose:args");
2118 if (artifact != (char *) NULL)
2119 {
2120 flags=ParseGeometry(artifact,&geometry_info);
2121 iterations=geometry_info.rho;
2122 if ((flags & SigmaValue) != 0)
2123 residual_threshold=geometry_info.sigma;
2124 if ((flags & XiValue) != 0)
2125 tick=(size_t) geometry_info.xi;
2126 }
2127 status=SeamlessBlendImage(image,composite,x_offset,y_offset,iterations,
2128 residual_threshold,tick,exception);
2129 source_image=DestroyImage(source_image);
2130 return(status);
2131 }
2132 case MathematicsCompositeOp:
2133 {
2134 /*
2135 Just collect the values from "compose:args", setting.
2136 Unused values are set to zero automagically.
2137
2138 Arguments are normally a comma separated list, so this probably should
2139 be changed to some 'general comma list' parser, (with a minimum
2140 number of values)
2141 */
2142 SetGeometryInfo(&geometry_info);
2143 artifact=GetImageArtifact(image,"compose:args");
2144 if (artifact != (char *) NULL)
2145 {
2146 flags=ParseGeometry(artifact,&geometry_info);
2147 if (flags == NoValue)
2148 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2149 "InvalidGeometry","`%s'",artifact);
2150 }
2151 break;
2152 }
2153 case ModulateCompositeOp:
2154 {
2155 /*
2156 Determine the luma and chroma scale.
2157 */
2158 artifact=GetImageArtifact(image,"compose:args");
2159 if (artifact != (char *) NULL)
2160 {
2161 flags=ParseGeometry(artifact,&geometry_info);
2162 percent_luma=geometry_info.rho;
2163 if ((flags & SigmaValue) != 0)
2164 percent_chroma=geometry_info.sigma;
2165 }
2166 break;
2167 }
2168 case ThresholdCompositeOp:
2169 {
2170 /*
2171 Determine the amount and threshold.
2172 */
2173 artifact=GetImageArtifact(image,"compose:args");
2174 if (artifact != (char *) NULL)
2175 {
2176 flags=ParseGeometry(artifact,&geometry_info);
2177 amount=geometry_info.rho;
2178 threshold=geometry_info.sigma;
2179 if ((flags & SigmaValue) == 0)
2180 threshold=0.05f;
2181 }
2182 threshold*=(double) QuantumRange;
2183 break;
2184 }
2185 default:
2186 break;
2187 }
2188 /*
2189 Composite image.
2190 */
2191 status=MagickTrue;
2192 progress=0;
2193 midpoint=((MagickRealType) QuantumRange+1.0)/2;
2194 source_view=AcquireVirtualCacheView(source_image,exception);
2195 image_view=AcquireAuthenticCacheView(image,exception);
2196#if defined(MAGICKCORE_OPENMP_SUPPORT)
2197 #pragma omp parallel for schedule(static) shared(progress,status) \
2198 magick_number_threads(source_image,image,image->rows,1)
2199#endif
2200 for (y=0; y < (ssize_t) image->rows; y++)
2201 {
2202 const Quantum
2203 *magick_restrict p,
2204 *pixels;
2205
2206 MagickRealType
2207 blue = 0.0,
2208 chroma = 0.0,
2209 green = 0.0,
2210 hue = 0.0,
2211 luma = 0.0,
2212 red = 0.0;
2213
2214 PixelInfo
2215 canvas_pixel,
2216 source_pixel;
2217
2218 Quantum
2219 *magick_restrict q;
2220
2221 ssize_t
2222 x;
2223
2224 if (status == MagickFalse)
2225 continue;
2226 if (clip_to_self != MagickFalse)
2227 {
2228 if (y < y_offset)
2229 continue;
2230 if ((y-y_offset) >= (ssize_t) source_image->rows)
2231 continue;
2232 }
2233 /*
2234 If pixels is NULL, y is outside overlay region.
2235 */
2236 pixels=(Quantum *) NULL;
2237 p=(Quantum *) NULL;
2238 if ((y >= y_offset) &&
2239 ((y-y_offset) < (ssize_t) source_image->rows))
2240 {
2241 p=GetCacheViewVirtualPixels(source_view,0,
2242 CastDoubleToSsizeT((double) y-y_offset),source_image->columns,1,
2243 exception);
2244 if (p == (const Quantum *) NULL)
2245 {
2246 status=MagickFalse;
2247 continue;
2248 }
2249 pixels=p;
2250 if (x_offset < 0)
2251 p-=(ptrdiff_t) CastDoubleToSsizeT((double) x_offset*
2252 GetPixelChannels(source_image));
2253 }
2254 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2255 if (q == (Quantum *) NULL)
2256 {
2257 status=MagickFalse;
2258 continue;
2259 }
2260 GetPixelInfo(image,&canvas_pixel);
2261 GetPixelInfo(source_image,&source_pixel);
2262 for (x=0; x < (ssize_t) image->columns; x++)
2263 {
2264 double
2265 gamma = 0.0;
2266
2267 MagickRealType
2268 alpha = 0.0,
2269 blend = 0.0,
2270 D = 0.0,
2271 Da = 0.0,
2272 Dc = 0.0,
2273 Dca = 0.0,
2274 Di = 0.0,
2275 S = 0.0,
2276 Sa = 0.0,
2277 Sc = 0.0,
2278 Sca = 0.0,
2279 Si = 0.0;
2280
2281 size_t
2282 channels;
2283
2284 ssize_t
2285 i;
2286
2287 if (clip_to_self != MagickFalse)
2288 {
2289 if (x < x_offset)
2290 {
2291 q+=(ptrdiff_t) GetPixelChannels(image);
2292 continue;
2293 }
2294 if ((x-x_offset) >= (ssize_t) source_image->columns)
2295 break;
2296 }
2297 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2298 ((x-x_offset) >= (ssize_t) source_image->columns))
2299 {
2300 Quantum
2301 source[MaxPixelChannels];
2302
2303 /*
2304 Virtual composite:
2305 Sc: source color.
2306 Dc: canvas color.
2307 */
2308 (void) GetOneVirtualPixel(source_image,
2309 CastDoubleToSsizeT((double) x-x_offset),
2310 CastDoubleToSsizeT((double) y-y_offset),source,exception);
2311 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2312 {
2313 MagickRealType
2314 pixel = 0.0;
2315
2316 PixelChannel channel = GetPixelChannelChannel(image,i);
2317 PixelTrait traits = GetPixelChannelTraits(image,channel);
2318 PixelTrait source_traits = GetPixelChannelTraits(source_image,
2319 channel);
2320 if ((traits == UndefinedPixelTrait) ||
2321 (source_traits == UndefinedPixelTrait))
2322 continue;
2323 switch (compose)
2324 {
2325 case AlphaCompositeOp:
2326 case ChangeMaskCompositeOp:
2327 case CopyAlphaCompositeOp:
2328 case DstAtopCompositeOp:
2329 case DstInCompositeOp:
2330 case InCompositeOp:
2331 case OutCompositeOp:
2332 case SrcInCompositeOp:
2333 case SrcOutCompositeOp:
2334 {
2335 if (channel == AlphaPixelChannel)
2336 pixel=(MagickRealType) TransparentAlpha;
2337 else
2338 pixel=(MagickRealType) q[i];
2339 break;
2340 }
2341 case ClearCompositeOp:
2342 case CopyCompositeOp:
2343 case ReplaceCompositeOp:
2344 {
2345 if (channel == AlphaPixelChannel)
2346 pixel=(MagickRealType) TransparentAlpha;
2347 else
2348 pixel=0.0;
2349 break;
2350 }
2351 case BlendCompositeOp:
2352 case DissolveCompositeOp:
2353 {
2354 if (channel == AlphaPixelChannel)
2355 pixel=canvas_dissolve*(double) GetPixelAlpha(source_image,
2356 source);
2357 else
2358 pixel=(MagickRealType) source[channel];
2359 break;
2360 }
2361 default:
2362 {
2363 pixel=(MagickRealType) source[channel];
2364 break;
2365 }
2366 }
2367 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
2368 ClampToQuantum(pixel);
2369 }
2370 q+=(ptrdiff_t) GetPixelChannels(image);
2371 continue;
2372 }
2373 /*
2374 Authentic composite:
2375 Sa: normalized source alpha.
2376 Da: normalized canvas alpha.
2377 */
2378 Sa=QuantumScale*(double) GetPixelAlpha(source_image,p);
2379 Da=QuantumScale*(double) GetPixelAlpha(image,q);
2380 switch (compose)
2381 {
2382 case BumpmapCompositeOp:
2383 case ColorBurnCompositeOp:
2384 case ColorDodgeCompositeOp:
2385 case DarkenCompositeOp:
2386 case DifferenceCompositeOp:
2387 case DivideCompositeOp:
2388 case DivideDstCompositeOp:
2389 case DivideSrcCompositeOp:
2390 case ExclusionCompositeOp:
2391 case FreezeCompositeOp:
2392 case HardLightCompositeOp:
2393 case HardMixCompositeOp:
2394 case InterpolateCompositeOp:
2395 case LightenCompositeOp:
2396 case LinearBurnCompositeOp:
2397 case LinearDodgeCompositeOp:
2398 case LinearLightCompositeOp:
2399 case MathematicsCompositeOp:
2400 case MinusDstCompositeOp:
2401 case MinusSrcCompositeOp:
2402 case MultiplyCompositeOp:
2403 case NegateCompositeOp:
2404 case OverlayCompositeOp:
2405 case PegtopLightCompositeOp:
2406 case PinLightCompositeOp:
2407 case ReflectCompositeOp:
2408 case ScreenCompositeOp:
2409 case SoftBurnCompositeOp:
2410 case SoftDodgeCompositeOp:
2411 case SoftLightCompositeOp:
2412 case StampCompositeOp:
2413 case VividLightCompositeOp:
2414 {
2415 alpha=RoundToUnity(Sa+Da-Sa*Da);
2416 break;
2417 }
2418 case DstAtopCompositeOp:
2419 case DstInCompositeOp:
2420 case InCompositeOp:
2421 case SrcInCompositeOp:
2422 {
2423 alpha=Sa*Da;
2424 break;
2425 }
2426 case DissolveCompositeOp:
2427 {
2428 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
2429 canvas_dissolve*Da;
2430 break;
2431 }
2432 case DstOverCompositeOp:
2433 case OverCompositeOp:
2434 case SrcOverCompositeOp:
2435 {
2436 alpha=Sa+Da-Sa*Da;
2437 break;
2438 }
2439 case DstOutCompositeOp:
2440 {
2441 alpha=Da*(1.0-Sa);
2442 break;
2443 }
2444 case OutCompositeOp:
2445 case SrcOutCompositeOp:
2446 {
2447 alpha=Sa*(1.0-Da);
2448 break;
2449 }
2450 case BlendCompositeOp:
2451 case PlusCompositeOp:
2452 {
2453 alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
2454 break;
2455 }
2456 case XorCompositeOp:
2457 {
2458 alpha=Sa+Da-2.0*Sa*Da;
2459 break;
2460 }
2461 case ModulusAddCompositeOp:
2462 {
2463 if ((Sa+Da) <= 1.0)
2464 {
2465 alpha=(Sa+Da);
2466 break;
2467 }
2468 alpha=((Sa+Da)-1.0);
2469 break;
2470 }
2471 case ModulusSubtractCompositeOp:
2472 {
2473 if ((Sa-Da) >= 0.0)
2474 {
2475 alpha=(Sa-Da);
2476 break;
2477 }
2478 alpha=((Sa-Da)+1.0);
2479 break;
2480 }
2481 default:
2482 {
2483 alpha=1.0;
2484 break;
2485 }
2486 }
2487 switch (compose)
2488 {
2489 case ColorizeCompositeOp:
2490 case HueCompositeOp:
2491 case LuminizeCompositeOp:
2492 case ModulateCompositeOp:
2493 case RMSECompositeOp:
2494 case SaturateCompositeOp:
2495 {
2496 Si=GetPixelIntensity(source_image,p);
2497 GetPixelInfoPixel(source_image,p,&source_pixel);
2498 GetPixelInfoPixel(image,q,&canvas_pixel);
2499 break;
2500 }
2501 case BumpmapCompositeOp:
2502 case CopyAlphaCompositeOp:
2503 case DarkenIntensityCompositeOp:
2504 case LightenIntensityCompositeOp:
2505 {
2506 Si=GetPixelIntensity(source_image,p);
2507 Di=GetPixelIntensity(image,q);
2508 break;
2509 }
2510 default:
2511 break;
2512 }
2513 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2514 {
2515 MagickRealType
2516 pixel = 0.0,
2517 sans = 0.0;
2518
2519 PixelChannel channel = GetPixelChannelChannel(image,i);
2520 PixelTrait traits = GetPixelChannelTraits(image,channel);
2521 PixelTrait source_traits = GetPixelChannelTraits(source_image,channel);
2522 if (traits == UndefinedPixelTrait)
2523 continue;
2524 if ((channel == AlphaPixelChannel) &&
2525 ((traits & UpdatePixelTrait) != 0))
2526 {
2527 /*
2528 Set alpha channel.
2529 */
2530 switch (compose)
2531 {
2532 case AlphaCompositeOp:
2533 {
2534 pixel=(double) QuantumRange*Sa;
2535 break;
2536 }
2537 case AtopCompositeOp:
2538 case CopyBlackCompositeOp:
2539 case CopyBlueCompositeOp:
2540 case CopyCyanCompositeOp:
2541 case CopyGreenCompositeOp:
2542 case CopyMagentaCompositeOp:
2543 case CopyRedCompositeOp:
2544 case CopyYellowCompositeOp:
2545 case SrcAtopCompositeOp:
2546 case DstCompositeOp:
2547 case NoCompositeOp:
2548 {
2549 pixel=(double) QuantumRange*Da;
2550 break;
2551 }
2552 case BumpmapCompositeOp:
2553 {
2554 pixel=Si*Da;
2555 break;
2556 }
2557 case ChangeMaskCompositeOp:
2558 {
2559 if (IsFuzzyEquivalencePixel(source_image,p,image,q) != MagickFalse)
2560 pixel=(MagickRealType) TransparentAlpha;
2561 else
2562 pixel=(double) QuantumRange*Da;
2563 break;
2564 }
2565 case ClearCompositeOp:
2566 {
2567 pixel=(MagickRealType) TransparentAlpha;
2568 break;
2569 }
2570 case ColorizeCompositeOp:
2571 case HueCompositeOp:
2572 case LuminizeCompositeOp:
2573 case RMSECompositeOp:
2574 case SaturateCompositeOp:
2575 {
2576 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2577 {
2578 pixel=(double) QuantumRange*Da;
2579 break;
2580 }
2581 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
2582 {
2583 pixel=(double) QuantumRange*Sa;
2584 break;
2585 }
2586 if (Sa < Da)
2587 {
2588 pixel=(double) QuantumRange*Da;
2589 break;
2590 }
2591 pixel=(double) QuantumRange*Sa;
2592 break;
2593 }
2594 case CopyAlphaCompositeOp:
2595 {
2596 if (source_image->alpha_trait == UndefinedPixelTrait)
2597 pixel=Si;
2598 else
2599 pixel=(double) QuantumRange*Sa;
2600 break;
2601 }
2602 case BlurCompositeOp:
2603 case CopyCompositeOp:
2604 case DisplaceCompositeOp:
2605 case DistortCompositeOp:
2606 case DstAtopCompositeOp:
2607 case ReplaceCompositeOp:
2608 case SrcCompositeOp:
2609 {
2610 pixel=(double) QuantumRange*Sa;
2611 break;
2612 }
2613 case DarkenIntensityCompositeOp:
2614 {
2615 if (compose_sync == MagickFalse)
2616 {
2617 pixel=Si < Di ? Sa : Da;
2618 break;
2619 }
2620 pixel=Sa*Si < Da*Di ? Sa : Da;
2621 break;
2622 }
2623 case DifferenceCompositeOp:
2624 {
2625 pixel=(double) QuantumRange*fabs((double) (Sa-Da));
2626 break;
2627 }
2628 case FreezeCompositeOp:
2629 {
2630 pixel=(double) QuantumRange*(1.0-(1.0-Sa)*(1.0-Sa)*
2631 MagickSafeReciprocal(Da));
2632 if (pixel < 0.0)
2633 pixel=0.0;
2634 break;
2635 }
2636 case InterpolateCompositeOp:
2637 {
2638 pixel=(double) QuantumRange*(0.5-0.25*cos(MagickPI*Sa)-0.25*
2639 cos(MagickPI*Da));
2640 break;
2641 }
2642 case LightenIntensityCompositeOp:
2643 {
2644 if (compose_sync == MagickFalse)
2645 {
2646 pixel=Si > Di ? Sa : Da;
2647 break;
2648 }
2649 pixel=Sa*Si > Da*Di ? Sa : Da;
2650 break;
2651 }
2652 case ModulateCompositeOp:
2653 {
2654 pixel=(double) QuantumRange*Da;
2655 break;
2656 }
2657 case MultiplyCompositeOp:
2658 {
2659 if (compose_sync == MagickFalse)
2660 {
2661 pixel=(double) QuantumRange*Sa*Da;
2662 break;
2663 }
2664 pixel=(double) QuantumRange*alpha;
2665 break;
2666 }
2667 case NegateCompositeOp:
2668 {
2669 pixel=(double) QuantumRange*((1.0-Sa-Da));
2670 break;
2671 }
2672 case ReflectCompositeOp:
2673 {
2674 pixel=(double) QuantumRange*(Sa*Sa*
2675 MagickSafeReciprocal(1.0-Da));
2676 if (pixel > (double) QuantumRange)
2677 pixel=(double) QuantumRange;
2678 break;
2679 }
2680 case StampCompositeOp:
2681 {
2682 pixel=(double) QuantumRange*(Sa+Da*Da-1.0);
2683 break;
2684 }
2685 case StereoCompositeOp:
2686 {
2687 pixel=(double) QuantumRange*(Sa+Da)/2;
2688 break;
2689 }
2690 default:
2691 {
2692 pixel=(double) QuantumRange*alpha;
2693 break;
2694 }
2695 }
2696 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
2697 ClampToQuantum(pixel);
2698 continue;
2699 }
2700 if (source_traits == UndefinedPixelTrait)
2701 continue;
2702 /*
2703 Sc: source color.
2704 Dc: canvas color.
2705 */
2706 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
2707 Dc=(MagickRealType) q[i];
2708 if ((traits & CopyPixelTrait) != 0)
2709 {
2710 /*
2711 Copy channel.
2712 */
2713 q[i]=ClampToQuantum(Dc);
2714 continue;
2715 }
2716 /*
2717 Porter-Duff compositions:
2718 Sca: source normalized color multiplied by alpha.
2719 Dca: normalized canvas color multiplied by alpha.
2720 */
2721 Sca=QuantumScale*Sa*Sc;
2722 Dca=QuantumScale*Da*Dc;
2723 switch (compose)
2724 {
2725 case DarkenCompositeOp:
2726 case LightenCompositeOp:
2727 case ModulusSubtractCompositeOp:
2728 {
2729 gamma=MagickSafeReciprocal(1.0-alpha);
2730 break;
2731 }
2732 default:
2733 {
2734 gamma=MagickSafeReciprocal(alpha);
2735 break;
2736 }
2737 }
2738 pixel=Dc;
2739 switch (compose)
2740 {
2741 case AlphaCompositeOp:
2742 {
2743 pixel=(double) QuantumRange*Sa;
2744 break;
2745 }
2746 case AtopCompositeOp:
2747 case SrcAtopCompositeOp:
2748 {
2749 pixel=(double) QuantumRange*(Sca*Da+Dca*(1.0-Sa));
2750 break;
2751 }
2752 case BlendCompositeOp:
2753 {
2754 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
2755 break;
2756 }
2757 case CopyCompositeOp:
2758 case ReplaceCompositeOp:
2759 {
2760 pixel=(double) QuantumRange*Sca;
2761 break;
2762 }
2763 case BlurCompositeOp:
2764 case DisplaceCompositeOp:
2765 case DistortCompositeOp:
2766 case SrcCompositeOp:
2767 {
2768 pixel=Sc;
2769 break;
2770 }
2771 case BumpmapCompositeOp:
2772 {
2773 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2774 {
2775 pixel=Dc;
2776 break;
2777 }
2778 pixel=(QuantumScale*Si)*Dc;
2779 break;
2780 }
2781 case ChangeMaskCompositeOp:
2782 {
2783 pixel=Dc;
2784 break;
2785 }
2786 case ClearCompositeOp:
2787 {
2788 pixel=0.0;
2789 break;
2790 }
2791 case ColorBurnCompositeOp:
2792 {
2793 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
2794 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
2795 if (fabs(S) < MagickEpsilon)
2796 blend=0.0;
2797 else
2798 blend=MagickMin(1.0,MagickMax(0.0,1.0-(1.0-D)/S));
2799 pixel=(double) QuantumRange*gamma*RoundToUnity(Sa*Da*blend+Sa*
2800 (1.0-Da)*S+Da*(1.0-Sa)*D);
2801 break;
2802 }
2803 case ColorDodgeCompositeOp:
2804 {
2805 if (Sa > 0.0)
2806 S=RoundToUnity(Sca/Sa);
2807 else
2808 S=0.0;
2809 if (Da > 0.0)
2810 D=RoundToUnity(Dca/Da);
2811 else
2812 D=0.0;
2813 if (S >= 1.0)
2814 blend=1.0;
2815 else
2816 if (D <= 0.0)
2817 blend=0.0;
2818 else
2819 {
2820 if ((1.0-S) <= 0.0)
2821 blend=1.0;
2822 else
2823 blend=MagickMin(1.0,D/(1.0-S));
2824 }
2825 pixel=(double) QuantumRange*gamma*(Sa*Da*blend+Sca*(1.0-Da)+Dca*
2826 (1.0-Sa));
2827 break;
2828 }
2829 case ColorizeCompositeOp:
2830 {
2831 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2832 {
2833 pixel=Dc;
2834 break;
2835 }
2836 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
2837 {
2838 pixel=Sc;
2839 break;
2840 }
2841 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
2842 (double) canvas_pixel.green,(double) canvas_pixel.blue,
2843 white_luminance,illuminant,&sans,&sans,&luma);
2844 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
2845 (double) source_pixel.green,(double) source_pixel.blue,
2846 white_luminance,illuminant,&hue,&chroma,&sans);
2847 ConvertGenericToRGB(colorspace,hue,chroma,luma,
2848 white_luminance,illuminant,&red,&green,&blue);
2849 switch (channel)
2850 {
2851 case RedPixelChannel: pixel=red; break;
2852 case GreenPixelChannel: pixel=green; break;
2853 case BluePixelChannel: pixel=blue; break;
2854 default: pixel=Dc; break;
2855 }
2856 break;
2857 }
2858 case CopyAlphaCompositeOp:
2859 case DstCompositeOp:
2860 {
2861 pixel=Dc;
2862 break;
2863 }
2864 case CopyBlackCompositeOp:
2865 {
2866 if (channel == BlackPixelChannel)
2867 pixel=(MagickRealType) GetPixelBlack(source_image,p);
2868 break;
2869 }
2870 case CopyBlueCompositeOp:
2871 case CopyYellowCompositeOp:
2872 {
2873 if (channel == BluePixelChannel)
2874 pixel=(MagickRealType) GetPixelBlue(source_image,p);
2875 break;
2876 }
2877 case CopyGreenCompositeOp:
2878 case CopyMagentaCompositeOp:
2879 {
2880 if (channel == GreenPixelChannel)
2881 pixel=(MagickRealType) GetPixelGreen(source_image,p);
2882 break;
2883 }
2884 case CopyRedCompositeOp:
2885 case CopyCyanCompositeOp:
2886 {
2887 if (channel == RedPixelChannel)
2888 pixel=(MagickRealType) GetPixelRed(source_image,p);
2889 break;
2890 }
2891 case DarkenCompositeOp:
2892 {
2893 if (compose_sync == MagickFalse)
2894 {
2895 pixel=RoundToUnity(MagickMin(Sca,Dca)+Sca*(1.0-Da)+Dca*
2896 (1.0-Sa));
2897 break;
2898 }
2899 pixel=(double) QuantumRange*RoundToUnity(MagickMin(Sca,Dca)+Sca*
2900 (1.0-Da)+Dca*(1.0-Sa));
2901 break;
2902 }
2903 case DarkenIntensityCompositeOp:
2904 {
2905 if (compose_sync == MagickFalse)
2906 {
2907 pixel=RoundToUnity(MagickMin(Sca,Dca)+Sca*(1.0-Di)+Dca*
2908 (1.0-Si));
2909 break;
2910 }
2911 pixel=(double) QuantumRange*RoundToUnity(MagickMin(Sca,Dca)+Sca*
2912 (1.0-Di)+Dca*(1.0-Si));
2913 break;
2914 }
2915 case DifferenceCompositeOp:
2916 {
2917 if (compose_sync == MagickFalse)
2918 {
2919 pixel=(double) QuantumRange*RoundToUnity(fabs((double) Sc-
2920 (double) Dc));
2921 break;
2922 }
2923 S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
2924 D=(Da > 0.0) ? (Dca/Da) : 0.0;
2925 pixel=(double) QuantumRange*RoundToUnity(fabs(S-D));
2926 break;
2927 }
2928 case DissolveCompositeOp:
2929 {
2930 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
2931 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
2932 break;
2933 }
2934 case DivideCompositeOp:
2935 {
2936 D=(Da > 0.0) ? (Dca/Da) : 0.0;
2937 S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
2938 if (fabs(S) < MagickEpsilon)
2939 blend=1.0;
2940 else
2941 blend=RoundToUnity(D/S);
2942 pixel=(double) QuantumRange*RoundToUnity(Sca*(1.0-Da)+Dca*(1.0-Sa)+
2943 Sa*Da*blend);
2944 break;
2945 }
2946 case DivideDstCompositeOp:
2947 {
2948 if (compose_sync == MagickFalse)
2949 {
2950 if (fabs(Dc) < MagickEpsilon)
2951 pixel=(double) QuantumRange;
2952 else
2953 pixel=(double) QuantumRange*(Sc/Dc);
2954 break;
2955 }
2956 if (fabs(Dca) < MagickEpsilon)
2957 pixel=(double) QuantumRange*(Sca+(1.0-Sa));
2958 else
2959 pixel=(double) QuantumRange*(Sca/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2960 break;
2961 }
2962 case DivideSrcCompositeOp:
2963 {
2964 if (compose_sync == MagickFalse)
2965 {
2966 if (fabs(Sc) < MagickEpsilon)
2967 pixel=(double) QuantumRange;
2968 else
2969 pixel=(double) QuantumRange*(Dc/Sc);
2970 break;
2971 }
2972 if (fabs(Dca) < MagickEpsilon)
2973 pixel=(double) QuantumRange*(Dca+(1.0-Da));
2974 else
2975 pixel=(double) QuantumRange*(Dca/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
2976 break;
2977 }
2978 case DstAtopCompositeOp:
2979 {
2980 pixel=(double) QuantumRange*(Dca*Sa+Sca*(1.0-Da));
2981 break;
2982 }
2983 case DstInCompositeOp:
2984 {
2985 pixel=(double) QuantumRange*gamma*(Dca*Sa);
2986 break;
2987 }
2988 case DstOutCompositeOp:
2989 {
2990 pixel=(double) QuantumRange*gamma*(Dca*(1.0-Sa));
2991 break;
2992 }
2993 case DstOverCompositeOp:
2994 {
2995 pixel=(double) QuantumRange*gamma*(Dca+Sca*(1.0-Da));
2996 break;
2997 }
2998 case ExclusionCompositeOp:
2999 {
3000 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3001 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3002 blend=RoundToUnity(S+D-2.0*S*D);
3003 pixel=(double) QuantumRange*RoundToUnity((blend*Sa+D*(1.0-Sa))*
3004 (Sa+Da-Sa*Da));
3005 break;
3006 }
3007 case FreezeCompositeOp:
3008 {
3009 if (Dca != 0.0)
3010 blend=1.0-(1.0-Sca)*(1.0-Sca)/Dca;
3011 else
3012 blend=0.0;
3013 pixel=(double) QuantumRange*gamma*RoundToUnity(blend);
3014 break;
3015 }
3016 case HardLightCompositeOp:
3017 {
3018 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3019 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3020 if (S <= 0.5)
3021 blend=2.0*S*D;
3022 else
3023 blend=1.0-2.0*(1.0-S)*(1.0-D);
3024 pixel=(double) QuantumRange*gamma*RoundToUnity((1.0-Da)*Sca+
3025 (1.0-Sa)*Dca+blend*Sa*Da);
3026 break;
3027 }
3028 case HardMixCompositeOp:
3029 {
3030 double
3031 hardmix,
3032 vivid;
3033
3034 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3035 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3036 if (S <= 0.5)
3037 {
3038 if (S <= MagickEpsilon)
3039 vivid=0.0;
3040 else
3041 vivid=1.0-(1.0-D)/(2.0*S);
3042 }
3043 else
3044 {
3045 if ((1.0-(2.0*S-1.0)) <= MagickEpsilon)
3046 vivid=1.0;
3047 else
3048 vivid=D/(1.0-(2.0*S-1.0));
3049 }
3050 hardmix=(vivid < 0.5) ? 0.0 : 1.0;
3051 pixel=(double) QuantumRange*gamma*RoundToUnity(Sa*Da*hardmix+Sca*
3052 (1.0-Da)+Dca*(1.0-Sa));
3053 break;
3054 }
3055 case HueCompositeOp:
3056 {
3057 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3058 {
3059 pixel=Dc;
3060 break;
3061 }
3062 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3063 {
3064 pixel=Sc;
3065 break;
3066 }
3067 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3068 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3069 white_luminance,illuminant,&hue,&chroma,&luma);
3070 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3071 (double) source_pixel.green,(double) source_pixel.blue,
3072 white_luminance,illuminant,&hue,&sans,&sans);
3073 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3074 white_luminance,illuminant,&red,&green,&blue);
3075 switch (channel)
3076 {
3077 case RedPixelChannel: pixel=red; break;
3078 case GreenPixelChannel: pixel=green; break;
3079 case BluePixelChannel: pixel=blue; break;
3080 default: pixel=Dc; break;
3081 }
3082 break;
3083 }
3084 case InCompositeOp:
3085 case SrcInCompositeOp:
3086 {
3087 pixel=(double) QuantumRange*(Sca*Da);
3088 break;
3089 }
3090 case InterpolateCompositeOp:
3091 {
3092 pixel=(double) QuantumRange*(0.5-0.25*cos(MagickPI*Sca)-0.25*
3093 cos(MagickPI*Dca));
3094 break;
3095 }
3096 case LinearBurnCompositeOp:
3097 {
3098 /*
3099 LinearBurn: as defined by Abode Photoshop, according to
3100 http://www.simplefilter.de/en/basics/mixmods.html is:
3101
3102 f(Sc,Dc) = Sc + Dc - 1
3103 */
3104 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3105 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3106 blend=RoundToUnity(S+D-1.0);
3107 pixel=(double) QuantumRange*RoundToUnity(Sa*Da*blend+Sca*(1.0-Da)+
3108 Dca*(1.0-Sa));
3109 break;
3110 }
3111 case LinearDodgeCompositeOp:
3112 {
3113 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3114 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3115 blend=RoundToUnity(S+D);
3116 pixel=(double) QuantumRange*gamma*RoundToUnity(Sa*Da*blend+
3117 Sca*(1.0-Da)+Dca*(1.0-Sa));
3118 break;
3119 }
3120 case LinearLightCompositeOp:
3121 {
3122 /*
3123 Linear Light (Adobe standard):
3124 f(Sc, Dc) = Dc + 2*Sc - 1
3125 Applied in linear (HDRI) space with clamping.
3126 */
3127 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3128 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3129 blend=RoundToUnity(D+2.0*S-1.0);
3130 pixel=(double) QuantumRange*gamma*RoundToUnity(Sa*Da*blend+Sca*
3131 (1.0-Da)+Dca*(1.0-Sa));
3132 break;
3133 }
3134 case LightenCompositeOp:
3135 {
3136 if (compose_sync == MagickFalse)
3137 {
3138 pixel=MagickMax(Sc,Dc);
3139 break;
3140 }
3141 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3142 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3143 pixel=(double) QuantumRange*RoundToUnity(Sca*(1.0-Da)+Dca*(1.0-Sa)+
3144 MagickMax(S,D)*Sa*Da);
3145 break;
3146 }
3147 case LightenIntensityCompositeOp:
3148 {
3149 pixel=Si > Di ? Sc : Dc;
3150 break;
3151 }
3152 case LuminizeCompositeOp:
3153 {
3154 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3155 {
3156 pixel=Dc;
3157 break;
3158 }
3159 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3160 {
3161 pixel=Sc;
3162 break;
3163 }
3164 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3165 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3166 white_luminance,illuminant,&hue,&chroma,&luma);
3167 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3168 (double) source_pixel.green,(double) source_pixel.blue,
3169 white_luminance,illuminant,&sans,&sans,&luma);
3170 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3171 white_luminance,illuminant,&red,&green,&blue);
3172 switch (channel)
3173 {
3174 case RedPixelChannel: pixel=red; break;
3175 case GreenPixelChannel: pixel=green; break;
3176 case BluePixelChannel: pixel=blue; break;
3177 default: pixel=Dc; break;
3178 }
3179 break;
3180 }
3181 case MathematicsCompositeOp:
3182 {
3183 /*
3184 'Mathematics' a free form user control mathematical composition
3185 is defined as...
3186
3187 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
3188
3189 Where the arguments A,B,C,D are (currently) passed to composite
3190 as a command separated 'geometry' string in "compose:args" image
3191 artifact.
3192
3193 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
3194
3195 Applying the SVG transparency formula (see above), we get...
3196
3197 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
3198
3199 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
3200 Dca*(1.0-Sa)
3201 */
3202 if (compose_sync == MagickFalse)
3203 {
3204 pixel=geometry_info.rho*Sc*Dc+geometry_info.sigma*Sc+
3205 geometry_info.xi*Dc+geometry_info.psi;
3206 break;
3207 }
3208 pixel=(double) QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
3209 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
3210 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
3211 break;
3212 }
3213 case MinusDstCompositeOp:
3214 {
3215 if (compose_sync == MagickFalse)
3216 {
3217 pixel=Dc-Sc;
3218 break;
3219 }
3220 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
3221 break;
3222 }
3223 case MinusSrcCompositeOp:
3224 {
3225 if (compose_sync == MagickFalse)
3226 {
3227 pixel=Sc-Dc;
3228 break;
3229 }
3230 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
3231 break;
3232 }
3233 case ModulateCompositeOp:
3234 {
3235 ssize_t
3236 offset;
3237
3238 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3239 {
3240 pixel=Dc;
3241 break;
3242 }
3243 offset=(ssize_t) (Si-midpoint);
3244 if (offset == 0)
3245 {
3246 pixel=Dc;
3247 break;
3248 }
3249 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3250 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3251 white_luminance,illuminant,&hue,&chroma,&luma);
3252 luma+=(0.01*percent_luma*offset)/midpoint;
3253 chroma*=0.01*percent_chroma;
3254 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3255 white_luminance,illuminant,&red,&green,&blue);
3256 switch (channel)
3257 {
3258 case RedPixelChannel: pixel=red; break;
3259 case GreenPixelChannel: pixel=green; break;
3260 case BluePixelChannel: pixel=blue; break;
3261 default: pixel=Dc; break;
3262 }
3263 break;
3264 }
3265 case ModulusAddCompositeOp:
3266 {
3267 if (compose_sync == MagickFalse)
3268 {
3269 pixel=(Quantum) QuantumRange*((Sc+Dc)-floor(Sc+Dc));
3270 break;
3271 }
3272 pixel=(Quantum) QuantumRange*((Sca+Dca)-floor(Sca+Dca));
3273 break;
3274 }
3275 case ModulusSubtractCompositeOp:
3276 {
3277 if (compose_sync == MagickFalse)
3278 {
3279 pixel=(Quantum) QuantumRange*((Sc-Dc)-floor(Sc-Dc));
3280 break;
3281 }
3282 pixel=(Quantum) QuantumRange*((Sca-Dca)-floor(Sca-Dca));
3283 break;
3284 }
3285 case MultiplyCompositeOp:
3286 {
3287 if (compose_sync == MagickFalse)
3288 {
3289 pixel=(double) QuantumRange*Sc*Dc;
3290 break;
3291 }
3292 pixel=(double) QuantumRange*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
3293 break;
3294 }
3295 case NegateCompositeOp:
3296 {
3297 D=(Da > 0.0) ? Dca/Da : 0.0;
3298 S=(Sa > 0.0) ? Sca/Sa : 0.0;
3299 pixel=(double) QuantumRange*((1.0-fabs(1.0-S-D))*Da);
3300 break;
3301 }
3302 case NoCompositeOp:
3303 {
3304 pixel=(double) QuantumRange*Dca;
3305 break;
3306 }
3307 case OutCompositeOp:
3308 case SrcOutCompositeOp:
3309 {
3310 pixel=(double) QuantumRange*(Sca*(1.0-Da));
3311 break;
3312 }
3313 case OverCompositeOp:
3314 case SrcOverCompositeOp:
3315 {
3316 if ((Sa+Da*(1.0-Sa)) <= MagickEpsilon)
3317 pixel=0.0;
3318 else
3319 pixel=(double) QuantumRange*gamma*((Sca+Dca*(1.0-Sa))/
3320 (Sa+Da*(1.0-Sa)));
3321 break;
3322 }
3323 case OverlayCompositeOp:
3324 {
3325 if ((2.0*Dca) < Da)
3326 {
3327 pixel=(double) QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+
3328 Sca*(1.0-Da));
3329 break;
3330 }
3331 pixel=(double) QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*
3332 (1.0-Sa)+Sca*(1.0-Da));
3333 break;
3334 }
3335 case PegtopLightCompositeOp:
3336 {
3337 if (fabs((double) Da) < MagickEpsilon)
3338 {
3339 pixel=(double) QuantumRange*gamma*Sca;
3340 break;
3341 }
3342 if (RoundToUnity(Sca) <= 0.5)
3343 blend=RoundToUnity(Dca/Da)-(1.0-2.0*RoundToUnity(Sca))*
3344 RoundToUnity(Dca/Da)*(1.0-RoundToUnity(Dca/Da));
3345 else
3346 {
3347 if (RoundToUnity(Dca/Da) <= 0.25)
3348 blend=((16.0*RoundToUnity(Dca/Da)-12.0)*RoundToUnity(Dca/Da)+
3349 4.0)*RoundToUnity(Dca/Da);
3350 else
3351 blend=sqrt(RoundToUnity(Dca/Da));
3352 blend=RoundToUnity(Dca/Da)+(2.0*RoundToUnity(Sca)-1.0)*
3353 (blend-RoundToUnity(Dca/Da));
3354 }
3355 pixel=(double) QuantumRange*gamma*(RoundToUnity(blend)*Da*Sa+Dca*
3356 (1.0-Sa));
3357 break;
3358 }
3359 case PinLightCompositeOp:
3360 {
3361 /*
3362 Adobe Pin Light (colors in [0,1]):
3363
3364 if (Cs <= 0.5)
3365 f = min(Cd, 2*Cs);
3366 else
3367 f = max(Cd, 2*Cs - 1);
3368 */
3369 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3370 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3371 if (S <= 0.5)
3372 blend=MagickMin(D,2.0*S);
3373 else
3374 blend=MagickMax(D,2.0*S-1.0);
3375 pixel=(double) QuantumRange*gamma*RoundToUnity(Sa*Da*blend+Sca*
3376 (1.0-Da)+Dca*(1.0-Sa));
3377 break;
3378 }
3379 case PlusCompositeOp:
3380 {
3381 if (compose_sync == MagickFalse)
3382 {
3383 pixel=(double) QuantumRange*(Sc+Dc);
3384 break;
3385 }
3386 pixel=(double) QuantumRange*(Sca+Dca);
3387 break;
3388 }
3389 case ReflectCompositeOp:
3390 {
3391 if (compose_sync == MagickFalse)
3392 {
3393 if (Dc < 1.0)
3394 blend=(Sc*Sc)/(1.0-Dc);
3395 else
3396 blend=1.0;
3397 pixel=(double) QuantumRange*RoundToUnity(blend);
3398 break;
3399 }
3400 if (Sa > 0.0)
3401 S=Sca/Sa;
3402 else
3403 S=0.0;
3404 if (Da > 0.0)
3405 D=Dca/Da;
3406 else
3407 D=0.0;
3408 if (D < 1.0)
3409 blend=(S*S)/(1.0-D);
3410 else
3411 blend=1.0;
3412 pixel=(double) QuantumRange*RoundToUnity((Sa+Da-Sa*Da)*blend);
3413 break;
3414 }
3415 case RMSECompositeOp:
3416 {
3417 double
3418 gray;
3419
3420 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3421 {
3422 pixel=Dc;
3423 break;
3424 }
3425 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3426 {
3427 pixel=Sc;
3428 break;
3429 }
3430 gray=sqrt(
3431 (canvas_pixel.red-source_pixel.red)*
3432 (canvas_pixel.red-source_pixel.red)+
3433 (canvas_pixel.green-source_pixel.green)*
3434 (canvas_pixel.green-source_pixel.green)+
3435 (canvas_pixel.blue-source_pixel.blue)*
3436 (canvas_pixel.blue-source_pixel.blue)/3.0);
3437 switch (channel)
3438 {
3439 case RedPixelChannel: pixel=gray; break;
3440 case GreenPixelChannel: pixel=gray; break;
3441 case BluePixelChannel: pixel=gray; break;
3442 default: pixel=Dc; break;
3443 }
3444 break;
3445 }
3446 case SaturateCompositeOp:
3447 {
3448 if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3449 {
3450 pixel=Dc;
3451 break;
3452 }
3453 if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3454 {
3455 pixel=Sc;
3456 break;
3457 }
3458 ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3459 (double) canvas_pixel.green,(double) canvas_pixel.blue,
3460 white_luminance,illuminant,&hue,&chroma,&luma);
3461 ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3462 (double) source_pixel.green,(double) source_pixel.blue,
3463 white_luminance,illuminant,&sans,&chroma,&sans);
3464 ConvertGenericToRGB(colorspace,hue,chroma,luma,
3465 white_luminance,illuminant,&red,&green,&blue);
3466 switch (channel)
3467 {
3468 case RedPixelChannel: pixel=red; break;
3469 case GreenPixelChannel: pixel=green; break;
3470 case BluePixelChannel: pixel=blue; break;
3471 default: pixel=Dc; break;
3472 }
3473 break;
3474 }
3475 case ScreenCompositeOp:
3476 {
3477 if (compose_sync == MagickFalse)
3478 {
3479 pixel=(double) QuantumRange*RoundToUnity(Sc+Dc-Sc*Dc);
3480 break;
3481 }
3482 S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3483 D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3484 blend=RoundToUnity(S+D-S*D);
3485 pixel=(double) QuantumRange*RoundToUnity(Sa*Da*blend+Sca*(1.0-Da)+
3486 Dca*(1.0-Sa));
3487 break;
3488 }
3489 case SoftBurnCompositeOp:
3490 {
3491 if (RoundToUnity(Dca) <= 0.0)
3492 blend = 0.0;
3493 else
3494 if (RoundToUnity(Sca) >= 1.0)
3495 blend = 1.0;
3496 else
3497 blend=1.0-MagickMin(1.0,(1.0-RoundToUnity(Dca))/
3498 RoundToUnity(Sca));
3499 pixel=(double) QuantumRange*gamma*RoundToUnity(blend);
3500 break;
3501 }
3502 case SoftDodgeCompositeOp:
3503 {
3504 if (RoundToUnity(Sca) <= 0.0)
3505 blend=RoundToUnity(Dca);
3506 else
3507 if (RoundToUnity(Dca) >= 1.0)
3508 blend=1.0;
3509 else
3510 blend=MagickMin(1.0,RoundToUnity(Dca)/(1.0-RoundToUnity(Sca)));
3511 pixel=(double) QuantumRange*gamma*RoundToUnity(blend);
3512 break;
3513 }
3514 case SoftLightCompositeOp:
3515 {
3516 if (RoundToUnity(Sca) <= 0.5)
3517 {
3518 pixel=(double) QuantumRange*gamma*(RoundToUnity(Dca)*Sa+Da*
3519 (RoundToUnity(Dca)-(1.0-2.0*RoundToUnity(Sca))*
3520 RoundToUnity(Dca)*(1.0-RoundToUnity(Dca)))+RoundToUnity(Sca)*
3521 (1.0-Da)+RoundToUnity(Dca)*(1.0-Sa));
3522 break;
3523 }
3524 if (RoundToUnity(Dca) > 0.25)
3525 blend=sqrt(RoundToUnity(Dca));
3526 else
3527 blend=((16.0*RoundToUnity(Dca)-12.0)*RoundToUnity(Dca)+4.0)*
3528 RoundToUnity(Dca);
3529 pixel=(double) QuantumRange*gamma*(RoundToUnity(Dca)*Sa+Da*
3530 (RoundToUnity(Dca)+(2.0*RoundToUnity(Sca)-1.0)*
3531 (RoundToUnity(blend)-RoundToUnity(Dca)))+RoundToUnity(Sca)*
3532 (1.0-Da)+RoundToUnity(Dca)*(1.0-Sa));
3533 break;
3534 }
3535 case StampCompositeOp:
3536 {
3537 pixel=(double) QuantumRange*RoundToUnity(Sca+Dca-1.0);
3538 break;
3539 }
3540 case StereoCompositeOp:
3541 {
3542 if (channel == RedPixelChannel)
3543 pixel=(MagickRealType) GetPixelRed(source_image,p);
3544 break;
3545 }
3546 case ThresholdCompositeOp:
3547 {
3548 S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
3549 D=(Da > 0.0) ? (Dca/Da) : 0.0;
3550 if (fabs(2.0*(S-D)) < threshold)
3551 blend=D;
3552 else
3553 blend=D+(S-D)*amount;
3554 pixel=(double) QuantumRange*(Sa*Da*RoundToUnity(blend)+Sca*
3555 (1.0-Da)+Dca*(1.0-Sa));
3556 break;
3557 }
3558 case VividLightCompositeOp:
3559 {
3560 if ((fabs((double) Sa) < MagickEpsilon) ||
3561 (fabs((double) Da) < MagickEpsilon))
3562 {
3563 pixel=(double) QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*
3564 (1.0-Sa));
3565 break;
3566 }
3567 if (RoundToUnity(Sca/Sa) <= 0.0)
3568 blend=0.0;
3569 else
3570 if (RoundToUnity(Sca/Sa) < 0.5)
3571 blend=1.0-(1.0-RoundToUnity(Dca/Da))/(2.0*RoundToUnity(Sca/Sa));
3572 else
3573 if (RoundToUnity(Sca/Sa) < 1.0)
3574 blend=RoundToUnity(Dca/Da)/(2.0*(1.0-RoundToUnity(Sca/Sa)));
3575 else
3576 blend=1.0;
3577 pixel=(double) QuantumRange*gamma*(Sa*Da*RoundToUnity(blend)+Sca*
3578 (1.0-Da)+Dca*(1.0-Sa));
3579 break;
3580 }
3581 case XorCompositeOp:
3582 {
3583 pixel=(double) QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
3584 break;
3585 }
3586 default:
3587 {
3588 pixel=Sc;
3589 break;
3590 }
3591 }
3592 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
3593 }
3594 p+=(ptrdiff_t) GetPixelChannels(source_image);
3595 channels=GetPixelChannels(source_image);
3596 if (p >= (pixels+channels*source_image->columns))
3597 p=pixels;
3598 q+=(ptrdiff_t) GetPixelChannels(image);
3599 }
3600 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3601 status=MagickFalse;
3602 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3603 {
3604 MagickBooleanType
3605 proceed;
3606
3607#if defined(MAGICKCORE_OPENMP_SUPPORT)
3608 #pragma omp atomic
3609#endif
3610 progress++;
3611 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
3612 if (proceed == MagickFalse)
3613 status=MagickFalse;
3614 }
3615 }
3616 source_view=DestroyCacheView(source_view);
3617 image_view=DestroyCacheView(image_view);
3618 if (canvas_image != (Image * ) NULL)
3619 canvas_image=DestroyImage(canvas_image);
3620 else
3621 source_image=DestroyImage(source_image);
3622 return(status);
3623}
3624
3625/*
3626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3627% %
3628% %
3629% %
3630% T e x t u r e I m a g e %
3631% %
3632% %
3633% %
3634%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3635%
3636% TextureImage() repeatedly tiles the texture image across and down the image
3637% canvas.
3638%
3639% The format of the TextureImage method is:
3640%
3641% MagickBooleanType TextureImage(Image *image,const Image *texture,
3642% ExceptionInfo *exception)
3643%
3644% A description of each parameter follows:
3645%
3646% o image: the image.
3647%
3648% o texture_image: This image is the texture to layer on the background.
3649%
3650*/
3651MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
3652 ExceptionInfo *exception)
3653{
3654#define TextureImageTag "Texture/Image"
3655
3656 CacheView
3657 *image_view,
3658 *texture_view;
3659
3660 Image
3661 *texture_image;
3662
3663 MagickBooleanType
3664 status;
3665
3666 ssize_t
3667 y;
3668
3669 assert(image != (Image *) NULL);
3670 assert(image->signature == MagickCoreSignature);
3671 if (IsEventLogging() != MagickFalse)
3672 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3673 if (texture == (const Image *) NULL)
3674 return(MagickFalse);
3675 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
3676 return(MagickFalse);
3677 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
3678 if (texture_image == (const Image *) NULL)
3679 return(MagickFalse);
3680 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
3681 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
3682 exception);
3683 status=MagickTrue;
3684 if ((image->compose != CopyCompositeOp) &&
3685 ((image->compose != OverCompositeOp) ||
3686 (image->alpha_trait != UndefinedPixelTrait) ||
3687 (texture_image->alpha_trait != UndefinedPixelTrait)))
3688 {
3689 /*
3690 Tile texture onto the image background.
3691 */
3692 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
3693 {
3694 ssize_t
3695 x;
3696
3697 if (status == MagickFalse)
3698 continue;
3699 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
3700 {
3701 MagickBooleanType
3702 thread_status;
3703
3704 thread_status=CompositeImage(image,texture_image,image->compose,
3705 MagickTrue,x+texture_image->tile_offset.x,y+
3706 texture_image->tile_offset.y,exception);
3707 if (thread_status == MagickFalse)
3708 {
3709 status=thread_status;
3710 break;
3711 }
3712 }
3713 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3714 {
3715 MagickBooleanType
3716 proceed;
3717
3718 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3719 image->rows);
3720 if (proceed == MagickFalse)
3721 status=MagickFalse;
3722 }
3723 }
3724 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
3725 image->rows,image->rows);
3726 texture_image=DestroyImage(texture_image);
3727 return(status);
3728 }
3729 /*
3730 Tile texture onto the image background (optimized).
3731 */
3732 status=MagickTrue;
3733 texture_view=AcquireVirtualCacheView(texture_image,exception);
3734 image_view=AcquireAuthenticCacheView(image,exception);
3735#if defined(MAGICKCORE_OPENMP_SUPPORT)
3736 #pragma omp parallel for schedule(static) shared(status) \
3737 magick_number_threads(texture_image,image,image->rows,2)
3738#endif
3739 for (y=0; y < (ssize_t) image->rows; y++)
3740 {
3741 MagickBooleanType
3742 sync;
3743
3744 const Quantum
3745 *p,
3746 *pixels;
3747
3748 ssize_t
3749 x;
3750
3751 Quantum
3752 *q;
3753
3754 size_t
3755 width;
3756
3757 if (status == MagickFalse)
3758 continue;
3759 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
3760 (y+texture_image->tile_offset.y) % (ssize_t) texture_image->rows,
3761 texture_image->columns,1,exception);
3762 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3763 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3764 {
3765 status=MagickFalse;
3766 continue;
3767 }
3768 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
3769 {
3770 ssize_t
3771 j;
3772
3773 p=pixels;
3774 width=texture_image->columns;
3775 if ((x+(ssize_t) width) > (ssize_t) image->columns)
3776 width=image->columns-(size_t) x;
3777 for (j=0; j < (ssize_t) width; j++)
3778 {
3779 ssize_t
3780 i;
3781
3782 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
3783 {
3784 PixelChannel channel = GetPixelChannelChannel(texture_image,i);
3785 PixelTrait traits = GetPixelChannelTraits(image,channel);
3786 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
3787 channel);
3788 if ((traits == UndefinedPixelTrait) ||
3789 (texture_traits == UndefinedPixelTrait))
3790 continue;
3791 SetPixelChannel(image,channel,p[i],q);
3792 }
3793 p+=(ptrdiff_t) GetPixelChannels(texture_image);
3794 q+=(ptrdiff_t) GetPixelChannels(image);
3795 }
3796 }
3797 sync=SyncCacheViewAuthenticPixels(image_view,exception);
3798 if (sync == MagickFalse)
3799 status=MagickFalse;
3800 if (image->progress_monitor != (MagickProgressMonitor) NULL)
3801 {
3802 MagickBooleanType
3803 proceed;
3804
3805 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3806 image->rows);
3807 if (proceed == MagickFalse)
3808 status=MagickFalse;
3809 }
3810 }
3811 texture_view=DestroyCacheView(texture_view);
3812 image_view=DestroyCacheView(image_view);
3813 texture_image=DestroyImage(texture_image);
3814 return(status);
3815}