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