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