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