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-2021 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/fx.h"
59 #include "MagickCore/gem.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/image.h"
62 #include "MagickCore/image-private.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/monitor.h"
66 #include "MagickCore/monitor-private.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/option.h"
69 #include "MagickCore/pixel-accessor.h"
70 #include "MagickCore/property.h"
71 #include "MagickCore/quantum.h"
72 #include "MagickCore/resample.h"
73 #include "MagickCore/resource_.h"
74 #include "MagickCore/string_.h"
75 #include "MagickCore/thread-private.h"
76 #include "MagickCore/threshold.h"
77 #include "MagickCore/token.h"
78 #include "MagickCore/utility.h"
79 #include "MagickCore/utility-private.h"
80 #include "MagickCore/version.h"
81
82 /*
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 % %
85 % %
86 % %
87 % C o m p o s i t e I m a g e %
88 % %
89 % %
90 % %
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 %
93 % CompositeImage() returns the second image composited onto the first
94 % at the specified offset, using the specified composite method.
95 %
96 % The format of the CompositeImage method is:
97 %
98 % MagickBooleanType CompositeImage(Image *image,
99 % const Image *source_image,const CompositeOperator compose,
100 % const MagickBooleanType clip_to_self,const ssize_t x_offset,
101 % const ssize_t y_offset,ExceptionInfo *exception)
102 %
103 % A description of each parameter follows:
104 %
105 % o image: the canvas image, modified by he composition
106 %
107 % o source_image: the source image.
108 %
109 % o compose: This operator affects how the composite is applied to
110 % the image. The operators and how they are utilized are listed here
111 % http://www.w3.org/TR/SVG12/#compositing.
112 %
113 % o clip_to_self: set to MagickTrue to limit composition to area composed.
114 %
115 % o x_offset: the column offset of the composited image.
116 %
117 % o y_offset: the row offset of the composited image.
118 %
119 % Extra Controls from Image meta-data in 'image' (artifacts)
120 %
121 % o "compose:args"
122 % A string containing extra numerical arguments for specific compose
123 % methods, generally expressed as a 'geometry' or a comma separated list
124 % of numbers.
125 %
126 % Compose methods needing such arguments include "BlendCompositeOp" and
127 % "DisplaceCompositeOp".
128 %
129 % o exception: return any errors or warnings in this structure.
130 %
131 */
132
133 /*
134 Composition based on the SVG specification:
135
136 A Composition is defined by...
137 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
138 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
139 Y = 1 for source preserved
140 Z = 1 for canvas preserved
141
142 Conversion to transparency (then optimized)
143 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
144 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
145
146 Where...
147 Sca = Sc*Sa normalized Source color divided by Source alpha
148 Dca = Dc*Da normalized Dest color divided by Dest alpha
149 Dc' = Dca'/Da' the desired color value for this channel.
150
151 Da' in in the follow formula as 'gamma' The resulting alpla value.
152
153 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
154 the following optimizations...
155 gamma = Sa+Da-Sa*Da;
156 gamma = 1 - QuantumScale*alpha * QuantumScale*beta;
157 opacity = QuantumScale*alpha*beta; // over blend, optimized 1-Gamma
158
159 The above SVG definitions also define that Mathematical Composition
160 methods should use a 'Over' blending mode for Alpha Channel.
161 It however was not applied for composition modes of 'Plus', 'Minus',
162 the modulus versions of 'Add' and 'Subtract'.
163
164 Mathematical operator changes to be applied from IM v6.7...
165
166 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
167 'ModulusAdd' and 'ModulusSubtract' for clarity.
168
169 2) All mathematical compositions work as per the SVG specification
170 with regard to blending. This now includes 'ModulusAdd' and
171 'ModulusSubtract'.
172
173 3) When the special channel flag 'sync' (syncronize channel updates)
174 is turned off (enabled by default) then mathematical compositions are
175 only performed on the channels specified, and are applied
176 independantally of each other. In other words the mathematics is
177 performed as 'pure' mathematical operations, rather than as image
178 operations.
179 */
180
HCLComposite(const MagickRealType hue,const MagickRealType chroma,const MagickRealType luma,MagickRealType * red,MagickRealType * green,MagickRealType * blue)181 static void HCLComposite(const MagickRealType hue,const MagickRealType chroma,
182 const MagickRealType luma,MagickRealType *red,MagickRealType *green,
183 MagickRealType *blue)
184 {
185 MagickRealType
186 b,
187 c,
188 g,
189 h,
190 m,
191 r,
192 x;
193
194 /*
195 Convert HCL to RGB colorspace.
196 */
197 assert(red != (MagickRealType *) NULL);
198 assert(green != (MagickRealType *) NULL);
199 assert(blue != (MagickRealType *) NULL);
200 h=6.0*hue;
201 c=chroma;
202 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
203 r=0.0;
204 g=0.0;
205 b=0.0;
206 if ((0.0 <= h) && (h < 1.0))
207 {
208 r=c;
209 g=x;
210 }
211 else
212 if ((1.0 <= h) && (h < 2.0))
213 {
214 r=x;
215 g=c;
216 }
217 else
218 if ((2.0 <= h) && (h < 3.0))
219 {
220 g=c;
221 b=x;
222 }
223 else
224 if ((3.0 <= h) && (h < 4.0))
225 {
226 g=x;
227 b=c;
228 }
229 else
230 if ((4.0 <= h) && (h < 5.0))
231 {
232 r=x;
233 b=c;
234 }
235 else
236 if ((5.0 <= h) && (h < 6.0))
237 {
238 r=c;
239 b=x;
240 }
241 m=luma-(0.298839*r+0.586811*g+0.114350*b);
242 *red=QuantumRange*(r+m);
243 *green=QuantumRange*(g+m);
244 *blue=QuantumRange*(b+m);
245 }
246
CompositeHCL(const MagickRealType red,const MagickRealType green,const MagickRealType blue,MagickRealType * hue,MagickRealType * chroma,MagickRealType * luma)247 static void CompositeHCL(const MagickRealType red,const MagickRealType green,
248 const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
249 MagickRealType *luma)
250 {
251 MagickRealType
252 b,
253 c,
254 g,
255 h,
256 max,
257 r;
258
259 /*
260 Convert RGB to HCL colorspace.
261 */
262 assert(hue != (MagickRealType *) NULL);
263 assert(chroma != (MagickRealType *) NULL);
264 assert(luma != (MagickRealType *) NULL);
265 r=red;
266 g=green;
267 b=blue;
268 max=MagickMax(r,MagickMax(g,b));
269 c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
270 h=0.0;
271 if (c == 0)
272 h=0.0;
273 else
274 if (red == max)
275 h=fmod((g-b)/c+6.0,6.0);
276 else
277 if (green == max)
278 h=((b-r)/c)+2.0;
279 else
280 if (blue == max)
281 h=((r-g)/c)+4.0;
282 *hue=(h/6.0);
283 *chroma=QuantumScale*c;
284 *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b);
285 }
286
CompositeOverImage(Image * image,const Image * source_image,const MagickBooleanType clip_to_self,const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo * exception)287 static MagickBooleanType CompositeOverImage(Image *image,
288 const Image *source_image,const MagickBooleanType clip_to_self,
289 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
290 {
291 #define CompositeImageTag "Composite/Image"
292
293 CacheView
294 *image_view,
295 *source_view;
296
297 const char
298 *value;
299
300 MagickBooleanType
301 clamp,
302 status;
303
304 MagickOffsetType
305 progress;
306
307 ssize_t
308 y;
309
310 /*
311 Composite image.
312 */
313 status=MagickTrue;
314 progress=0;
315 clamp=MagickTrue;
316 value=GetImageArtifact(image,"compose:clamp");
317 if (value != (const char *) NULL)
318 clamp=IsStringTrue(value);
319 status=MagickTrue;
320 progress=0;
321 source_view=AcquireVirtualCacheView(source_image,exception);
322 image_view=AcquireAuthenticCacheView(image,exception);
323 #if defined(MAGICKCORE_OPENMP_SUPPORT)
324 #pragma omp parallel for schedule(static) shared(progress,status) \
325 magick_number_threads(source_image,image,image->rows,1)
326 #endif
327 for (y=0; y < (ssize_t) image->rows; y++)
328 {
329 const Quantum
330 *pixels;
331
332 PixelInfo
333 canvas_pixel,
334 source_pixel;
335
336 const Quantum
337 *magick_restrict p;
338
339 Quantum
340 *magick_restrict q;
341
342 ssize_t
343 x;
344
345 if (status == MagickFalse)
346 continue;
347 if (clip_to_self != MagickFalse)
348 {
349 if (y < y_offset)
350 continue;
351 if ((y-y_offset) >= (ssize_t) source_image->rows)
352 continue;
353 }
354 /*
355 If pixels is NULL, y is outside overlay region.
356 */
357 pixels=(Quantum *) NULL;
358 p=(Quantum *) NULL;
359 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
360 {
361 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
362 source_image->columns,1,exception);
363 if (p == (const Quantum *) NULL)
364 {
365 status=MagickFalse;
366 continue;
367 }
368 pixels=p;
369 if (x_offset < 0)
370 p-=x_offset*(ssize_t) GetPixelChannels(source_image);
371 }
372 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
373 if (q == (Quantum *) NULL)
374 {
375 status=MagickFalse;
376 continue;
377 }
378 GetPixelInfo(image,&canvas_pixel);
379 GetPixelInfo(source_image,&source_pixel);
380 for (x=0; x < (ssize_t) image->columns; x++)
381 {
382 double
383 gamma;
384
385 MagickRealType
386 alpha,
387 Da,
388 Dc,
389 Dca,
390 Sa,
391 Sc,
392 Sca;
393
394 ssize_t
395 i;
396
397 size_t
398 channels;
399
400 if (clip_to_self != MagickFalse)
401 {
402 if (x < x_offset)
403 {
404 q+=GetPixelChannels(image);
405 continue;
406 }
407 if ((x-x_offset) >= (ssize_t) source_image->columns)
408 break;
409 }
410 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
411 ((x-x_offset) >= (ssize_t) source_image->columns))
412 {
413 Quantum
414 source[MaxPixelChannels];
415
416 /*
417 Virtual composite:
418 Sc: source color.
419 Dc: canvas color.
420 */
421 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
422 exception);
423 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
424 {
425 MagickRealType
426 pixel;
427
428 PixelChannel channel = GetPixelChannelChannel(image,i);
429 PixelTrait traits = GetPixelChannelTraits(image,channel);
430 PixelTrait source_traits=GetPixelChannelTraits(source_image,
431 channel);
432 if ((traits == UndefinedPixelTrait) ||
433 (source_traits == UndefinedPixelTrait))
434 continue;
435 if (channel == AlphaPixelChannel)
436 pixel=(MagickRealType) TransparentAlpha;
437 else
438 pixel=(MagickRealType) q[i];
439 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
440 ClampToQuantum(pixel);
441 }
442 q+=GetPixelChannels(image);
443 continue;
444 }
445 /*
446 Authentic composite:
447 Sa: normalized source alpha.
448 Da: normalized canvas alpha.
449 */
450 Sa=QuantumScale*GetPixelAlpha(source_image,p);
451 Da=QuantumScale*GetPixelAlpha(image,q);
452 alpha=Sa+Da-Sa*Da;
453 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
454 {
455 MagickRealType
456 pixel;
457
458 PixelChannel channel = GetPixelChannelChannel(image,i);
459 PixelTrait traits = GetPixelChannelTraits(image,channel);
460 PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
461 if (traits == UndefinedPixelTrait)
462 continue;
463 if ((source_traits == UndefinedPixelTrait) &&
464 (channel != AlphaPixelChannel))
465 continue;
466 if (channel == AlphaPixelChannel)
467 {
468 /*
469 Set alpha channel.
470 */
471 pixel=QuantumRange*alpha;
472 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
473 ClampToQuantum(pixel);
474 continue;
475 }
476 /*
477 Sc: source color.
478 Dc: canvas color.
479 */
480 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
481 Dc=(MagickRealType) q[i];
482 if ((traits & CopyPixelTrait) != 0)
483 {
484 /*
485 Copy channel.
486 */
487 q[i]=ClampToQuantum(Sc);
488 continue;
489 }
490 /*
491 Porter-Duff compositions:
492 Sca: source normalized color multiplied by alpha.
493 Dca: normalized canvas color multiplied by alpha.
494 */
495 Sca=QuantumScale*Sa*Sc;
496 Dca=QuantumScale*Da*Dc;
497 gamma=PerceptibleReciprocal(alpha);
498 pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
499 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
500 }
501 p+=GetPixelChannels(source_image);
502 channels=GetPixelChannels(source_image);
503 if (p >= (pixels+channels*source_image->columns))
504 p=pixels;
505 q+=GetPixelChannels(image);
506 }
507 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
508 status=MagickFalse;
509 if (image->progress_monitor != (MagickProgressMonitor) NULL)
510 {
511 MagickBooleanType
512 proceed;
513
514 #if defined(MAGICKCORE_OPENMP_SUPPORT)
515 #pragma omp atomic
516 #endif
517 progress++;
518 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
519 if (proceed == MagickFalse)
520 status=MagickFalse;
521 }
522 }
523 source_view=DestroyCacheView(source_view);
524 image_view=DestroyCacheView(image_view);
525 return(status);
526 }
527
CompositeImage(Image * image,const Image * composite,const CompositeOperator compose,const MagickBooleanType clip_to_self,const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo * exception)528 MagickExport MagickBooleanType CompositeImage(Image *image,
529 const Image *composite,const CompositeOperator compose,
530 const MagickBooleanType clip_to_self,const ssize_t x_offset,
531 const ssize_t y_offset,ExceptionInfo *exception)
532 {
533 #define CompositeImageTag "Composite/Image"
534
535 CacheView
536 *source_view,
537 *image_view;
538
539 const char
540 *value;
541
542 GeometryInfo
543 geometry_info;
544
545 Image
546 *canvas_image,
547 *source_image;
548
549 MagickBooleanType
550 clamp,
551 status;
552
553 MagickOffsetType
554 progress;
555
556 MagickRealType
557 amount,
558 canvas_dissolve,
559 midpoint,
560 percent_luma,
561 percent_chroma,
562 source_dissolve,
563 threshold;
564
565 MagickStatusType
566 flags;
567
568 ssize_t
569 y;
570
571 assert(image != (Image *) NULL);
572 assert(image->signature == MagickCoreSignature);
573 if (image->debug != MagickFalse)
574 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
575 assert(composite != (Image *) NULL);
576 assert(composite->signature == MagickCoreSignature);
577 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
578 return(MagickFalse);
579 source_image=CloneImage(composite,0,0,MagickTrue,exception);
580 if (source_image == (const Image *) NULL)
581 return(MagickFalse);
582 (void) SetImageColorspace(source_image,image->colorspace,exception);
583 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
584 {
585 status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
586 y_offset,exception);
587 source_image=DestroyImage(source_image);
588 return(status);
589 }
590 amount=0.5;
591 canvas_image=(Image *) NULL;
592 canvas_dissolve=1.0;
593 clamp=MagickTrue;
594 value=GetImageArtifact(image,"compose:clamp");
595 if (value != (const char *) NULL)
596 clamp=IsStringTrue(value);
597 SetGeometryInfo(&geometry_info);
598 percent_luma=100.0;
599 percent_chroma=100.0;
600 source_dissolve=1.0;
601 threshold=0.05f;
602 switch (compose)
603 {
604 case CopyCompositeOp:
605 {
606 if ((x_offset < 0) || (y_offset < 0))
607 break;
608 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
609 break;
610 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
611 break;
612 if ((source_image->alpha_trait == UndefinedPixelTrait) &&
613 (image->alpha_trait != UndefinedPixelTrait))
614 (void) SetImageAlphaChannel(source_image,OpaqueAlphaChannel,exception);
615 status=MagickTrue;
616 source_view=AcquireVirtualCacheView(source_image,exception);
617 image_view=AcquireAuthenticCacheView(image,exception);
618 #if defined(MAGICKCORE_OPENMP_SUPPORT)
619 #pragma omp parallel for schedule(static) shared(status) \
620 magick_number_threads(source_image,image,source_image->rows,1)
621 #endif
622 for (y=0; y < (ssize_t) source_image->rows; y++)
623 {
624 MagickBooleanType
625 sync;
626
627 const Quantum
628 *p;
629
630 Quantum
631 *q;
632
633 ssize_t
634 x;
635
636 if (status == MagickFalse)
637 continue;
638 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
639 exception);
640 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
641 source_image->columns,1,exception);
642 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
643 {
644 status=MagickFalse;
645 continue;
646 }
647 for (x=0; x < (ssize_t) source_image->columns; x++)
648 {
649 ssize_t
650 i;
651
652 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
653 {
654 p+=GetPixelChannels(source_image);
655 q+=GetPixelChannels(image);
656 continue;
657 }
658 for (i=0; i < (ssize_t) GetPixelChannels(source_image); i++)
659 {
660 PixelChannel channel = GetPixelChannelChannel(source_image,i);
661 PixelTrait source_traits = GetPixelChannelTraits(source_image,
662 channel);
663 PixelTrait traits = GetPixelChannelTraits(image,channel);
664 if ((source_traits == UndefinedPixelTrait) ||
665 (traits == UndefinedPixelTrait))
666 continue;
667 SetPixelChannel(image,channel,p[i],q);
668 }
669 p+=GetPixelChannels(source_image);
670 q+=GetPixelChannels(image);
671 }
672 sync=SyncCacheViewAuthenticPixels(image_view,exception);
673 if (sync == MagickFalse)
674 status=MagickFalse;
675 if (image->progress_monitor != (MagickProgressMonitor) NULL)
676 {
677 MagickBooleanType
678 proceed;
679
680 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
681 y,image->rows);
682 if (proceed == MagickFalse)
683 status=MagickFalse;
684 }
685 }
686 source_view=DestroyCacheView(source_view);
687 image_view=DestroyCacheView(image_view);
688 source_image=DestroyImage(source_image);
689 return(status);
690 }
691 case IntensityCompositeOp:
692 {
693 if ((x_offset < 0) || (y_offset < 0))
694 break;
695 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
696 break;
697 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
698 break;
699 status=MagickTrue;
700 source_view=AcquireVirtualCacheView(source_image,exception);
701 image_view=AcquireAuthenticCacheView(image,exception);
702 #if defined(MAGICKCORE_OPENMP_SUPPORT)
703 #pragma omp parallel for schedule(static) shared(status) \
704 magick_number_threads(source_image,image,source_image->rows,1)
705 #endif
706 for (y=0; y < (ssize_t) source_image->rows; y++)
707 {
708 MagickBooleanType
709 sync;
710
711 const Quantum
712 *p;
713
714 Quantum
715 *q;
716
717 ssize_t
718 x;
719
720 if (status == MagickFalse)
721 continue;
722 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
723 exception);
724 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
725 source_image->columns,1,exception);
726 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
727 {
728 status=MagickFalse;
729 continue;
730 }
731 for (x=0; x < (ssize_t) source_image->columns; x++)
732 {
733 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
734 {
735 p+=GetPixelChannels(source_image);
736 q+=GetPixelChannels(image);
737 continue;
738 }
739 SetPixelAlpha(image,clamp != MagickFalse ?
740 ClampPixel(GetPixelIntensity(source_image,p)) :
741 ClampToQuantum(GetPixelIntensity(source_image,p)),q);
742 p+=GetPixelChannels(source_image);
743 q+=GetPixelChannels(image);
744 }
745 sync=SyncCacheViewAuthenticPixels(image_view,exception);
746 if (sync == MagickFalse)
747 status=MagickFalse;
748 if (image->progress_monitor != (MagickProgressMonitor) NULL)
749 {
750 MagickBooleanType
751 proceed;
752
753 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
754 y,image->rows);
755 if (proceed == MagickFalse)
756 status=MagickFalse;
757 }
758 }
759 source_view=DestroyCacheView(source_view);
760 image_view=DestroyCacheView(image_view);
761 source_image=DestroyImage(source_image);
762 return(status);
763 }
764 case CopyAlphaCompositeOp:
765 case ChangeMaskCompositeOp:
766 {
767 /*
768 Modify canvas outside the overlaid region and require an alpha
769 channel to exist, to add transparency.
770 */
771 if (image->alpha_trait == UndefinedPixelTrait)
772 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
773 break;
774 }
775 case BlurCompositeOp:
776 {
777 CacheView
778 *canvas_view;
779
780 double
781 angle_range,
782 angle_start,
783 height,
784 width;
785
786 PixelInfo
787 pixel;
788
789 ResampleFilter
790 *resample_filter;
791
792 SegmentInfo
793 blur;
794
795 /*
796 Blur Image by resampling dictated by an overlay gradient map:
797 X = red_channel; Y = green_channel; compose:args =
798 x_scale[,y_scale[,angle]].
799 */
800 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
801 if (canvas_image == (Image *) NULL)
802 {
803 source_image=DestroyImage(source_image);
804 return(MagickFalse);
805 }
806 /*
807 Gather the maximum blur sigma values from user.
808 */
809 flags=NoValue;
810 value=GetImageArtifact(image,"compose:args");
811 if (value != (const char *) NULL)
812 flags=ParseGeometry(value,&geometry_info);
813 if ((flags & WidthValue) == 0)
814 {
815 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
816 "InvalidSetting","'%s' '%s'","compose:args",value);
817 source_image=DestroyImage(source_image);
818 canvas_image=DestroyImage(canvas_image);
819 return(MagickFalse);
820 }
821 /*
822 Users input sigma now needs to be converted to the EWA ellipse size.
823 The filter defaults to a sigma of 0.5 so to make this match the users
824 input the ellipse size needs to be doubled.
825 */
826 width=2.0*geometry_info.rho;
827 height=width;
828 if ((flags & HeightValue) != 0)
829 height=2.0*geometry_info.sigma;
830 /*
831 Default the unrotated ellipse width and height axis vectors.
832 */
833 blur.x1=width;
834 blur.x2=0.0;
835 blur.y1=0.0;
836 blur.y2=height;
837 if ((flags & XValue) != 0 )
838 {
839 MagickRealType
840 angle;
841
842 /*
843 Rotate vectors if a rotation angle is given.
844 */
845 angle=DegreesToRadians(geometry_info.xi);
846 blur.x1=width*cos(angle);
847 blur.x2=width*sin(angle);
848 blur.y1=(-height*sin(angle));
849 blur.y2=height*cos(angle);
850 }
851 angle_start=0.0;
852 angle_range=0.0;
853 if ((flags & YValue) != 0 )
854 {
855 /*
856 Lets set a angle range and calculate in the loop.
857 */
858 angle_start=DegreesToRadians(geometry_info.xi);
859 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
860 }
861 /*
862 Set up a gaussian cylindrical filter for EWA Bluring.
863
864 As the minimum ellipse radius of support*1.0 the EWA algorithm
865 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
866 This means that even 'No Blur' will be still a little blurry! The
867 solution (as well as the problem of preventing any user expert filter
868 settings, is to set our own user settings, restore them afterwards.
869 */
870 resample_filter=AcquireResampleFilter(image,exception);
871 SetResampleFilter(resample_filter,GaussianFilter);
872 /*
873 Perform the variable blurring of each pixel in image.
874 */
875 GetPixelInfo(image,&pixel);
876 source_view=AcquireVirtualCacheView(source_image,exception);
877 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
878 for (y=0; y < (ssize_t) source_image->rows; y++)
879 {
880 MagickBooleanType
881 sync;
882
883 const Quantum
884 *magick_restrict p;
885
886 Quantum
887 *magick_restrict q;
888
889 ssize_t
890 x;
891
892 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
893 continue;
894 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
895 exception);
896 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
897 exception);
898 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
899 break;
900 for (x=0; x < (ssize_t) source_image->columns; x++)
901 {
902 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
903 {
904 p+=GetPixelChannels(source_image);
905 continue;
906 }
907 if (fabs(angle_range) > MagickEpsilon)
908 {
909 MagickRealType
910 angle;
911
912 angle=angle_start+angle_range*QuantumScale*
913 GetPixelBlue(source_image,p);
914 blur.x1=width*cos(angle);
915 blur.x2=width*sin(angle);
916 blur.y1=(-height*sin(angle));
917 blur.y2=height*cos(angle);
918 }
919 ScaleResampleFilter(resample_filter,
920 blur.x1*QuantumScale*GetPixelRed(source_image,p),
921 blur.y1*QuantumScale*GetPixelGreen(source_image,p),
922 blur.x2*QuantumScale*GetPixelRed(source_image,p),
923 blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
924 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
925 (double) y_offset+y,&pixel,exception);
926 SetPixelViaPixelInfo(canvas_image,&pixel,q);
927 p+=GetPixelChannels(source_image);
928 q+=GetPixelChannels(canvas_image);
929 }
930 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
931 if (sync == MagickFalse)
932 break;
933 }
934 resample_filter=DestroyResampleFilter(resample_filter);
935 source_view=DestroyCacheView(source_view);
936 canvas_view=DestroyCacheView(canvas_view);
937 source_image=DestroyImage(source_image);
938 source_image=canvas_image;
939 break;
940 }
941 case DisplaceCompositeOp:
942 case DistortCompositeOp:
943 {
944 CacheView
945 *canvas_view;
946
947 MagickRealType
948 horizontal_scale,
949 vertical_scale;
950
951 PixelInfo
952 pixel;
953
954 PointInfo
955 center,
956 offset;
957
958 /*
959 Displace/Distort based on overlay gradient map:
960 X = red_channel; Y = green_channel;
961 compose:args = x_scale[,y_scale[,center.x,center.y]]
962 */
963 canvas_image=CloneImage(image,0,0,MagickTrue,exception);
964 if (canvas_image == (Image *) NULL)
965 {
966 source_image=DestroyImage(source_image);
967 return(MagickFalse);
968 }
969 SetGeometryInfo(&geometry_info);
970 flags=NoValue;
971 value=GetImageArtifact(image,"compose:args");
972 if (value != (char *) NULL)
973 flags=ParseGeometry(value,&geometry_info);
974 if ((flags & (WidthValue | HeightValue)) == 0 )
975 {
976 if ((flags & AspectValue) == 0)
977 {
978 horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
979 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
980 }
981 else
982 {
983 horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
984 vertical_scale=(MagickRealType) (image->rows-1)/2.0;
985 }
986 }
987 else
988 {
989 horizontal_scale=geometry_info.rho;
990 vertical_scale=geometry_info.sigma;
991 if ((flags & PercentValue) != 0)
992 {
993 if ((flags & AspectValue) == 0)
994 {
995 horizontal_scale*=(source_image->columns-1)/200.0;
996 vertical_scale*=(source_image->rows-1)/200.0;
997 }
998 else
999 {
1000 horizontal_scale*=(image->columns-1)/200.0;
1001 vertical_scale*=(image->rows-1)/200.0;
1002 }
1003 }
1004 if ((flags & HeightValue) == 0)
1005 vertical_scale=horizontal_scale;
1006 }
1007 /*
1008 Determine fixed center point for absolute distortion map
1009 Absolute distort ==
1010 Displace offset relative to a fixed absolute point
1011 Select that point according to +X+Y user inputs.
1012 default = center of overlay image
1013 arg flag '!' = locations/percentage relative to background image
1014 */
1015 center.x=(MagickRealType) x_offset;
1016 center.y=(MagickRealType) y_offset;
1017 if (compose == DistortCompositeOp)
1018 {
1019 if ((flags & XValue) == 0)
1020 if ((flags & AspectValue) != 0)
1021 center.x=(MagickRealType) ((image->columns-1)/2.0);
1022 else
1023 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1024 2.0);
1025 else
1026 if ((flags & AspectValue) != 0)
1027 center.x=geometry_info.xi;
1028 else
1029 center.x=(MagickRealType) (x_offset+geometry_info.xi);
1030 if ((flags & YValue) == 0)
1031 if ((flags & AspectValue) != 0)
1032 center.y=(MagickRealType) ((image->rows-1)/2.0);
1033 else
1034 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1035 else
1036 if ((flags & AspectValue) != 0)
1037 center.y=geometry_info.psi;
1038 else
1039 center.y=(MagickRealType) (y_offset+geometry_info.psi);
1040 }
1041 /*
1042 Shift the pixel offset point as defined by the provided,
1043 displacement/distortion map. -- Like a lens...
1044 */
1045 GetPixelInfo(image,&pixel);
1046 image_view=AcquireVirtualCacheView(image,exception);
1047 source_view=AcquireVirtualCacheView(source_image,exception);
1048 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1049 for (y=0; y < (ssize_t) source_image->rows; y++)
1050 {
1051 MagickBooleanType
1052 sync;
1053
1054 const Quantum
1055 *magick_restrict p;
1056
1057 Quantum
1058 *magick_restrict q;
1059
1060 ssize_t
1061 x;
1062
1063 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1064 continue;
1065 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1066 exception);
1067 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1068 exception);
1069 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1070 break;
1071 for (x=0; x < (ssize_t) source_image->columns; x++)
1072 {
1073 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1074 {
1075 p+=GetPixelChannels(source_image);
1076 continue;
1077 }
1078 /*
1079 Displace the offset.
1080 */
1081 offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1082 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1083 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1084 x : 0);
1085 offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1086 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1087 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1088 y : 0);
1089 status=InterpolatePixelInfo(image,image_view,
1090 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1091 &pixel,exception);
1092 if (status == MagickFalse)
1093 break;
1094 /*
1095 Mask with the 'invalid pixel mask' in alpha channel.
1096 */
1097 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
1098 (QuantumScale*GetPixelAlpha(source_image,p));
1099 SetPixelViaPixelInfo(canvas_image,&pixel,q);
1100 p+=GetPixelChannels(source_image);
1101 q+=GetPixelChannels(canvas_image);
1102 }
1103 if (x < (ssize_t) source_image->columns)
1104 break;
1105 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1106 if (sync == MagickFalse)
1107 break;
1108 }
1109 canvas_view=DestroyCacheView(canvas_view);
1110 source_view=DestroyCacheView(source_view);
1111 image_view=DestroyCacheView(image_view);
1112 source_image=DestroyImage(source_image);
1113 source_image=canvas_image;
1114 break;
1115 }
1116 case DissolveCompositeOp:
1117 {
1118 /*
1119 Geometry arguments to dissolve factors.
1120 */
1121 value=GetImageArtifact(image,"compose:args");
1122 if (value != (char *) NULL)
1123 {
1124 flags=ParseGeometry(value,&geometry_info);
1125 source_dissolve=geometry_info.rho/100.0;
1126 canvas_dissolve=1.0;
1127 if ((source_dissolve-MagickEpsilon) < 0.0)
1128 source_dissolve=0.0;
1129 if ((source_dissolve+MagickEpsilon) > 1.0)
1130 {
1131 canvas_dissolve=2.0-source_dissolve;
1132 source_dissolve=1.0;
1133 }
1134 if ((flags & SigmaValue) != 0)
1135 canvas_dissolve=geometry_info.sigma/100.0;
1136 if ((canvas_dissolve-MagickEpsilon) < 0.0)
1137 canvas_dissolve=0.0;
1138 }
1139 break;
1140 }
1141 case BlendCompositeOp:
1142 {
1143 value=GetImageArtifact(image,"compose:args");
1144 if (value != (char *) NULL)
1145 {
1146 flags=ParseGeometry(value,&geometry_info);
1147 source_dissolve=geometry_info.rho/100.0;
1148 canvas_dissolve=1.0-source_dissolve;
1149 if ((flags & SigmaValue) != 0)
1150 canvas_dissolve=geometry_info.sigma/100.0;
1151 }
1152 break;
1153 }
1154 case MathematicsCompositeOp:
1155 {
1156 /*
1157 Just collect the values from "compose:args", setting.
1158 Unused values are set to zero automagically.
1159
1160 Arguments are normally a comma separated list, so this probably should
1161 be changed to some 'general comma list' parser, (with a minimum
1162 number of values)
1163 */
1164 SetGeometryInfo(&geometry_info);
1165 value=GetImageArtifact(image,"compose:args");
1166 if (value != (char *) NULL)
1167 (void) ParseGeometry(value,&geometry_info);
1168 break;
1169 }
1170 case ModulateCompositeOp:
1171 {
1172 /*
1173 Determine the luma and chroma scale.
1174 */
1175 value=GetImageArtifact(image,"compose:args");
1176 if (value != (char *) NULL)
1177 {
1178 flags=ParseGeometry(value,&geometry_info);
1179 percent_luma=geometry_info.rho;
1180 if ((flags & SigmaValue) != 0)
1181 percent_chroma=geometry_info.sigma;
1182 }
1183 break;
1184 }
1185 case ThresholdCompositeOp:
1186 {
1187 /*
1188 Determine the amount and threshold.
1189 */
1190 value=GetImageArtifact(image,"compose:args");
1191 if (value != (char *) NULL)
1192 {
1193 flags=ParseGeometry(value,&geometry_info);
1194 amount=geometry_info.rho;
1195 threshold=geometry_info.sigma;
1196 if ((flags & SigmaValue) == 0)
1197 threshold=0.05f;
1198 }
1199 threshold*=QuantumRange;
1200 break;
1201 }
1202 default:
1203 break;
1204 }
1205 /*
1206 Composite image.
1207 */
1208 status=MagickTrue;
1209 progress=0;
1210 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1211 source_view=AcquireVirtualCacheView(source_image,exception);
1212 image_view=AcquireAuthenticCacheView(image,exception);
1213 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1214 #pragma omp parallel for schedule(static) shared(progress,status) \
1215 magick_number_threads(source_image,image,image->rows,1)
1216 #endif
1217 for (y=0; y < (ssize_t) image->rows; y++)
1218 {
1219 const Quantum
1220 *pixels;
1221
1222 MagickRealType
1223 blue,
1224 chroma,
1225 green,
1226 hue,
1227 luma,
1228 red;
1229
1230 PixelInfo
1231 canvas_pixel,
1232 source_pixel;
1233
1234 const Quantum
1235 *magick_restrict p;
1236
1237 Quantum
1238 *magick_restrict q;
1239
1240 ssize_t
1241 x;
1242
1243 if (status == MagickFalse)
1244 continue;
1245 if (clip_to_self != MagickFalse)
1246 {
1247 if (y < y_offset)
1248 continue;
1249 if ((y-y_offset) >= (ssize_t) source_image->rows)
1250 continue;
1251 }
1252 /*
1253 If pixels is NULL, y is outside overlay region.
1254 */
1255 pixels=(Quantum *) NULL;
1256 p=(Quantum *) NULL;
1257 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1258 {
1259 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1260 source_image->columns,1,exception);
1261 if (p == (const Quantum *) NULL)
1262 {
1263 status=MagickFalse;
1264 continue;
1265 }
1266 pixels=p;
1267 if (x_offset < 0)
1268 p-=x_offset*(ssize_t) GetPixelChannels(source_image);
1269 }
1270 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1271 if (q == (Quantum *) NULL)
1272 {
1273 status=MagickFalse;
1274 continue;
1275 }
1276 hue=0.0;
1277 chroma=0.0;
1278 luma=0.0;
1279 GetPixelInfo(image,&canvas_pixel);
1280 GetPixelInfo(source_image,&source_pixel);
1281 for (x=0; x < (ssize_t) image->columns; x++)
1282 {
1283 double
1284 gamma;
1285
1286 MagickRealType
1287 alpha,
1288 Da,
1289 Dc,
1290 Dca,
1291 DcaDa,
1292 Sa,
1293 SaSca,
1294 Sc,
1295 Sca;
1296
1297 ssize_t
1298 i;
1299
1300 size_t
1301 channels;
1302
1303 if (clip_to_self != MagickFalse)
1304 {
1305 if (x < x_offset)
1306 {
1307 q+=GetPixelChannels(image);
1308 continue;
1309 }
1310 if ((x-x_offset) >= (ssize_t) source_image->columns)
1311 break;
1312 }
1313 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1314 ((x-x_offset) >= (ssize_t) source_image->columns))
1315 {
1316 Quantum
1317 source[MaxPixelChannels];
1318
1319 /*
1320 Virtual composite:
1321 Sc: source color.
1322 Dc: canvas color.
1323 */
1324 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
1325 exception);
1326 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1327 {
1328 MagickRealType
1329 pixel;
1330
1331 PixelChannel channel = GetPixelChannelChannel(image,i);
1332 PixelTrait traits = GetPixelChannelTraits(image,channel);
1333 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1334 channel);
1335 if ((traits == UndefinedPixelTrait) ||
1336 (source_traits == UndefinedPixelTrait))
1337 continue;
1338 switch (compose)
1339 {
1340 case AlphaCompositeOp:
1341 case ChangeMaskCompositeOp:
1342 case CopyAlphaCompositeOp:
1343 case DstAtopCompositeOp:
1344 case DstInCompositeOp:
1345 case InCompositeOp:
1346 case OutCompositeOp:
1347 case SrcInCompositeOp:
1348 case SrcOutCompositeOp:
1349 {
1350 if (channel == AlphaPixelChannel)
1351 pixel=(MagickRealType) TransparentAlpha;
1352 else
1353 pixel=(MagickRealType) q[i];
1354 break;
1355 }
1356 case ClearCompositeOp:
1357 case CopyCompositeOp:
1358 case ReplaceCompositeOp:
1359 case SrcCompositeOp:
1360 {
1361 if (channel == AlphaPixelChannel)
1362 pixel=(MagickRealType) TransparentAlpha;
1363 else
1364 pixel=0.0;
1365 break;
1366 }
1367 case BlendCompositeOp:
1368 case DissolveCompositeOp:
1369 {
1370 if (channel == AlphaPixelChannel)
1371 pixel=canvas_dissolve*GetPixelAlpha(source_image,source);
1372 else
1373 pixel=(MagickRealType) source[channel];
1374 break;
1375 }
1376 default:
1377 {
1378 pixel=(MagickRealType) source[channel];
1379 break;
1380 }
1381 }
1382 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1383 ClampToQuantum(pixel);
1384 }
1385 q+=GetPixelChannels(image);
1386 continue;
1387 }
1388 /*
1389 Authentic composite:
1390 Sa: normalized source alpha.
1391 Da: normalized canvas alpha.
1392 */
1393 Sa=QuantumScale*GetPixelAlpha(source_image,p);
1394 Da=QuantumScale*GetPixelAlpha(image,q);
1395 switch (compose)
1396 {
1397 case BumpmapCompositeOp:
1398 {
1399 alpha=GetPixelIntensity(source_image,p)*Sa;
1400 break;
1401 }
1402 case ColorBurnCompositeOp:
1403 case ColorDodgeCompositeOp:
1404 case DarkenCompositeOp:
1405 case DifferenceCompositeOp:
1406 case DivideDstCompositeOp:
1407 case DivideSrcCompositeOp:
1408 case ExclusionCompositeOp:
1409 case FreezeCompositeOp:
1410 case HardLightCompositeOp:
1411 case HardMixCompositeOp:
1412 case InterpolateCompositeOp:
1413 case LightenCompositeOp:
1414 case LinearBurnCompositeOp:
1415 case LinearDodgeCompositeOp:
1416 case LinearLightCompositeOp:
1417 case MathematicsCompositeOp:
1418 case MinusDstCompositeOp:
1419 case MinusSrcCompositeOp:
1420 case MultiplyCompositeOp:
1421 case NegateCompositeOp:
1422 case OverlayCompositeOp:
1423 case PegtopLightCompositeOp:
1424 case PinLightCompositeOp:
1425 case ReflectCompositeOp:
1426 case ScreenCompositeOp:
1427 case SoftBurnCompositeOp:
1428 case SoftDodgeCompositeOp:
1429 case SoftLightCompositeOp:
1430 case StampCompositeOp:
1431 case VividLightCompositeOp:
1432 {
1433 alpha=RoundToUnity(Sa+Da-Sa*Da);
1434 break;
1435 }
1436 case DstAtopCompositeOp:
1437 case DstInCompositeOp:
1438 case InCompositeOp:
1439 case SrcInCompositeOp:
1440 {
1441 alpha=Sa*Da;
1442 break;
1443 }
1444 case DissolveCompositeOp:
1445 {
1446 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
1447 canvas_dissolve*Da;
1448 break;
1449 }
1450 case DstOverCompositeOp:
1451 case OverCompositeOp:
1452 case SrcOverCompositeOp:
1453 {
1454 alpha=Sa+Da-Sa*Da;
1455 break;
1456 }
1457 case DstOutCompositeOp:
1458 {
1459 alpha=Da*(1.0-Sa);
1460 break;
1461 }
1462 case OutCompositeOp:
1463 case SrcOutCompositeOp:
1464 {
1465 alpha=Sa*(1.0-Da);
1466 break;
1467 }
1468 case BlendCompositeOp:
1469 case PlusCompositeOp:
1470 {
1471 alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
1472 break;
1473 }
1474 case XorCompositeOp:
1475 {
1476 alpha=Sa+Da-2.0*Sa*Da;
1477 break;
1478 }
1479 case ModulusAddCompositeOp:
1480 {
1481 if ((Sa+Da) <= 1.0)
1482 {
1483 alpha=(Sa+Da);
1484 break;
1485 }
1486 alpha=((Sa+Da)-1.0);
1487 break;
1488 }
1489 case ModulusSubtractCompositeOp:
1490 {
1491 if ((Sa-Da) >= 0.0)
1492 {
1493 alpha=(Sa-Da);
1494 break;
1495 }
1496 alpha=((Sa-Da)+1.0);
1497 break;
1498 }
1499 default:
1500 {
1501 alpha=1.0;
1502 break;
1503 }
1504 }
1505 switch (compose)
1506 {
1507 case ColorizeCompositeOp:
1508 case HueCompositeOp:
1509 case LuminizeCompositeOp:
1510 case ModulateCompositeOp:
1511 case RMSECompositeOp:
1512 case SaturateCompositeOp:
1513 {
1514 GetPixelInfoPixel(source_image,p,&source_pixel);
1515 GetPixelInfoPixel(image,q,&canvas_pixel);
1516 break;
1517 }
1518 default:
1519 break;
1520 }
1521 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1522 {
1523 MagickRealType
1524 pixel,
1525 sans;
1526
1527 PixelChannel channel = GetPixelChannelChannel(image,i);
1528 PixelTrait traits = GetPixelChannelTraits(image,channel);
1529 PixelTrait source_traits = GetPixelChannelTraits(source_image,channel);
1530 if (traits == UndefinedPixelTrait)
1531 continue;
1532 if ((channel == AlphaPixelChannel) &&
1533 ((traits & UpdatePixelTrait) != 0))
1534 {
1535 /*
1536 Set alpha channel.
1537 */
1538 switch (compose)
1539 {
1540 case AlphaCompositeOp:
1541 {
1542 pixel=QuantumRange*Sa;
1543 break;
1544 }
1545 case AtopCompositeOp:
1546 case CopyBlackCompositeOp:
1547 case CopyBlueCompositeOp:
1548 case CopyCyanCompositeOp:
1549 case CopyGreenCompositeOp:
1550 case CopyMagentaCompositeOp:
1551 case CopyRedCompositeOp:
1552 case CopyYellowCompositeOp:
1553 case SrcAtopCompositeOp:
1554 case DstCompositeOp:
1555 case NoCompositeOp:
1556 {
1557 pixel=QuantumRange*Da;
1558 break;
1559 }
1560 case ChangeMaskCompositeOp:
1561 {
1562 MagickBooleanType
1563 equivalent;
1564
1565 if (Da < 0.5)
1566 {
1567 pixel=(MagickRealType) TransparentAlpha;
1568 break;
1569 }
1570 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1571 if (equivalent != MagickFalse)
1572 pixel=(MagickRealType) TransparentAlpha;
1573 else
1574 pixel=(MagickRealType) OpaqueAlpha;
1575 break;
1576 }
1577 case ClearCompositeOp:
1578 {
1579 pixel=(MagickRealType) TransparentAlpha;
1580 break;
1581 }
1582 case ColorizeCompositeOp:
1583 case HueCompositeOp:
1584 case LuminizeCompositeOp:
1585 case RMSECompositeOp:
1586 case SaturateCompositeOp:
1587 {
1588 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1589 {
1590 pixel=QuantumRange*Da;
1591 break;
1592 }
1593 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1594 {
1595 pixel=QuantumRange*Sa;
1596 break;
1597 }
1598 if (Sa < Da)
1599 {
1600 pixel=QuantumRange*Da;
1601 break;
1602 }
1603 pixel=QuantumRange*Sa;
1604 break;
1605 }
1606 case CopyAlphaCompositeOp:
1607 {
1608 if (source_image->alpha_trait == UndefinedPixelTrait)
1609 pixel=GetPixelIntensity(source_image,p);
1610 else
1611 pixel=QuantumRange*Sa;
1612 break;
1613 }
1614 case BlurCompositeOp:
1615 case CopyCompositeOp:
1616 case DisplaceCompositeOp:
1617 case DistortCompositeOp:
1618 case DstAtopCompositeOp:
1619 case ReplaceCompositeOp:
1620 case SrcCompositeOp:
1621 {
1622 pixel=QuantumRange*Sa;
1623 break;
1624 }
1625 case DarkenIntensityCompositeOp:
1626 {
1627 pixel=Sa*GetPixelIntensity(source_image,p) <
1628 Da*GetPixelIntensity(image,q) ? Sa : Da;
1629 break;
1630 }
1631 case DifferenceCompositeOp:
1632 {
1633 pixel=QuantumRange*fabs((double) (Sa-Da));
1634 break;
1635 }
1636 case FreezeCompositeOp:
1637 {
1638 pixel=QuantumRange*(1.0-(1.0-Sa)*(1.0-Sa)*
1639 PerceptibleReciprocal(Da));
1640 if (pixel < 0.0)
1641 pixel=0.0;
1642 break;
1643 }
1644 case InterpolateCompositeOp:
1645 {
1646 pixel=QuantumRange*(0.5-0.25*cos(MagickPI*Sa)-0.25*
1647 cos(MagickPI*Da));
1648 break;
1649 }
1650 case LightenIntensityCompositeOp:
1651 {
1652 pixel=Sa*GetPixelIntensity(source_image,p) >
1653 Da*GetPixelIntensity(image,q) ? Sa : Da;
1654 break;
1655 }
1656 case ModulateCompositeOp:
1657 {
1658 pixel=QuantumRange*Da;
1659 break;
1660 }
1661 case MultiplyCompositeOp:
1662 {
1663 pixel=QuantumRange*Sa*Da;
1664 break;
1665 }
1666 case NegateCompositeOp:
1667 {
1668 pixel=QuantumRange*((1.0-Sa-Da));
1669 break;
1670 }
1671 case ReflectCompositeOp:
1672 {
1673 pixel=QuantumRange*(Sa*Sa*PerceptibleReciprocal(1.0-Da));
1674 if (pixel > QuantumRange)
1675 pixel=QuantumRange;
1676 break;
1677 }
1678 case StampCompositeOp:
1679 {
1680 pixel=QuantumRange*(Sa+Da*Da-1.0);
1681 break;
1682 }
1683 case StereoCompositeOp:
1684 {
1685 pixel=QuantumRange*(Sa+Da)/2;
1686 break;
1687 }
1688 default:
1689 {
1690 pixel=QuantumRange*alpha;
1691 break;
1692 }
1693 }
1694 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1695 ClampToQuantum(pixel);
1696 continue;
1697 }
1698 if (source_traits == UndefinedPixelTrait)
1699 continue;
1700 /*
1701 Sc: source color.
1702 Dc: canvas color.
1703 */
1704 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1705 Dc=(MagickRealType) q[i];
1706 if ((traits & CopyPixelTrait) != 0)
1707 {
1708 /*
1709 Copy channel.
1710 */
1711 q[i]=ClampToQuantum(Dc);
1712 continue;
1713 }
1714 /*
1715 Porter-Duff compositions:
1716 Sca: source normalized color multiplied by alpha.
1717 Dca: normalized canvas color multiplied by alpha.
1718 */
1719 Sca=QuantumScale*Sa*Sc;
1720 Dca=QuantumScale*Da*Dc;
1721 SaSca=Sa*PerceptibleReciprocal(Sca);
1722 DcaDa=Dca*PerceptibleReciprocal(Da);
1723 switch (compose)
1724 {
1725 case DarkenCompositeOp:
1726 case LightenCompositeOp:
1727 case ModulusSubtractCompositeOp:
1728 {
1729 gamma=PerceptibleReciprocal(1.0-alpha);
1730 break;
1731 }
1732 default:
1733 {
1734 gamma=PerceptibleReciprocal(alpha);
1735 break;
1736 }
1737 }
1738 pixel=Dc;
1739 switch (compose)
1740 {
1741 case AlphaCompositeOp:
1742 {
1743 pixel=QuantumRange*Sa;
1744 break;
1745 }
1746 case AtopCompositeOp:
1747 case SrcAtopCompositeOp:
1748 {
1749 pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1750 break;
1751 }
1752 case BlendCompositeOp:
1753 {
1754 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1755 break;
1756 }
1757 case CopyCompositeOp:
1758 case ReplaceCompositeOp:
1759 case SrcCompositeOp:
1760 {
1761 pixel=QuantumRange*Sca;
1762 break;
1763 }
1764 case BlurCompositeOp:
1765 case DisplaceCompositeOp:
1766 case DistortCompositeOp:
1767 {
1768 pixel=Sc;
1769 break;
1770 }
1771 case BumpmapCompositeOp:
1772 {
1773 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1774 {
1775 pixel=Dc;
1776 break;
1777 }
1778 pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1779 break;
1780 }
1781 case ChangeMaskCompositeOp:
1782 {
1783 pixel=Dc;
1784 break;
1785 }
1786 case ClearCompositeOp:
1787 {
1788 pixel=0.0;
1789 break;
1790 }
1791 case ColorBurnCompositeOp:
1792 {
1793 if ((Sca == 0.0) && (Dca == Da))
1794 {
1795 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1796 break;
1797 }
1798 if (Sca == 0.0)
1799 {
1800 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1801 break;
1802 }
1803 pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-DcaDa)*
1804 SaSca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1805 break;
1806 }
1807 case ColorDodgeCompositeOp:
1808 {
1809 if ((Sca*Da+Dca*Sa) >= Sa*Da)
1810 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1811 else
1812 pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(Sa-Sca)+
1813 Sca*(1.0-Da)+Dca*(1.0-Sa));
1814 break;
1815 }
1816 case ColorizeCompositeOp:
1817 {
1818 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1819 {
1820 pixel=Dc;
1821 break;
1822 }
1823 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1824 {
1825 pixel=Sc;
1826 break;
1827 }
1828 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1829 &sans,&sans,&luma);
1830 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1831 &hue,&chroma,&sans);
1832 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1833 switch (channel)
1834 {
1835 case RedPixelChannel: pixel=red; break;
1836 case GreenPixelChannel: pixel=green; break;
1837 case BluePixelChannel: pixel=blue; break;
1838 default: pixel=Dc; break;
1839 }
1840 break;
1841 }
1842 case CopyAlphaCompositeOp:
1843 {
1844 pixel=Dc;
1845 break;
1846 }
1847 case CopyBlackCompositeOp:
1848 {
1849 if (channel == BlackPixelChannel)
1850 pixel=(MagickRealType) GetPixelBlack(source_image,p);
1851 break;
1852 }
1853 case CopyBlueCompositeOp:
1854 case CopyYellowCompositeOp:
1855 {
1856 if (channel == BluePixelChannel)
1857 pixel=(MagickRealType) GetPixelBlue(source_image,p);
1858 break;
1859 }
1860 case CopyGreenCompositeOp:
1861 case CopyMagentaCompositeOp:
1862 {
1863 if (channel == GreenPixelChannel)
1864 pixel=(MagickRealType) GetPixelGreen(source_image,p);
1865 break;
1866 }
1867 case CopyRedCompositeOp:
1868 case CopyCyanCompositeOp:
1869 {
1870 if (channel == RedPixelChannel)
1871 pixel=(MagickRealType) GetPixelRed(source_image,p);
1872 break;
1873 }
1874 case DarkenCompositeOp:
1875 {
1876 /*
1877 Darken is equivalent to a 'Minimum' method
1878 OR a greyscale version of a binary 'Or'
1879 OR the 'Intersection' of pixel sets.
1880 */
1881 if ((Sca*Da) < (Dca*Sa))
1882 {
1883 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1884 break;
1885 }
1886 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1887 break;
1888 }
1889 case DarkenIntensityCompositeOp:
1890 {
1891 pixel=Sa*GetPixelIntensity(source_image,p) <
1892 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1893 break;
1894 }
1895 case DifferenceCompositeOp:
1896 {
1897 pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1898 break;
1899 }
1900 case DissolveCompositeOp:
1901 {
1902 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1903 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1904 break;
1905 }
1906 case DivideDstCompositeOp:
1907 {
1908 if ((fabs((double) Sca) < MagickEpsilon) &&
1909 (fabs((double) Dca) < MagickEpsilon))
1910 {
1911 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1912 break;
1913 }
1914 if (fabs((double) Dca) < MagickEpsilon)
1915 {
1916 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1917 break;
1918 }
1919 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1920 break;
1921 }
1922 case DivideSrcCompositeOp:
1923 {
1924 if ((fabs((double) Dca) < MagickEpsilon) &&
1925 (fabs((double) Sca) < MagickEpsilon))
1926 {
1927 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1928 break;
1929 }
1930 if (fabs((double) Sca) < MagickEpsilon)
1931 {
1932 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1933 break;
1934 }
1935 pixel=QuantumRange*gamma*(Dca*Sa*SaSca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1936 break;
1937 }
1938 case DstAtopCompositeOp:
1939 {
1940 pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1941 break;
1942 }
1943 case DstCompositeOp:
1944 case NoCompositeOp:
1945 {
1946 pixel=QuantumRange*Dca;
1947 break;
1948 }
1949 case DstInCompositeOp:
1950 {
1951 pixel=QuantumRange*gamma*(Dca*Sa);
1952 break;
1953 }
1954 case DstOutCompositeOp:
1955 {
1956 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1957 break;
1958 }
1959 case DstOverCompositeOp:
1960 {
1961 pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1962 break;
1963 }
1964 case ExclusionCompositeOp:
1965 {
1966 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1967 Dca*(1.0-Sa));
1968 break;
1969 }
1970 case FreezeCompositeOp:
1971 {
1972 pixel=QuantumRange*gamma*(1.0-(1.0-Sca)*(1.0-Sca)*
1973 PerceptibleReciprocal(Dca));
1974 if (pixel < 0.0)
1975 pixel=0.0;
1976 break;
1977 }
1978 case HardLightCompositeOp:
1979 {
1980 if ((2.0*Sca) < Sa)
1981 {
1982 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1983 Sa));
1984 break;
1985 }
1986 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1987 Dca*(1.0-Sa));
1988 break;
1989 }
1990 case HardMixCompositeOp:
1991 {
1992 pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1993 break;
1994 }
1995 case HueCompositeOp:
1996 {
1997 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1998 {
1999 pixel=Dc;
2000 break;
2001 }
2002 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2003 {
2004 pixel=Sc;
2005 break;
2006 }
2007 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2008 &hue,&chroma,&luma);
2009 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2010 &hue,&sans,&sans);
2011 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2012 switch (channel)
2013 {
2014 case RedPixelChannel: pixel=red; break;
2015 case GreenPixelChannel: pixel=green; break;
2016 case BluePixelChannel: pixel=blue; break;
2017 default: pixel=Dc; break;
2018 }
2019 break;
2020 }
2021 case InCompositeOp:
2022 case SrcInCompositeOp:
2023 {
2024 pixel=QuantumRange*(Sca*Da);
2025 break;
2026 }
2027 case InterpolateCompositeOp:
2028 {
2029 pixel=QuantumRange*(0.5-0.25*cos(MagickPI*Sca)-0.25*
2030 cos(MagickPI*Dca));
2031 break;
2032 }
2033 case LinearBurnCompositeOp:
2034 {
2035 /*
2036 LinearBurn: as defined by Abode Photoshop, according to
2037 http://www.simplefilter.de/en/basics/mixmods.html is:
2038
2039 f(Sc,Dc) = Sc + Dc - 1
2040 */
2041 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
2042 break;
2043 }
2044 case LinearDodgeCompositeOp:
2045 {
2046 pixel=gamma*(Sa*Sc+Da*Dc);
2047 break;
2048 }
2049 case LinearLightCompositeOp:
2050 {
2051 /*
2052 LinearLight: as defined by Abode Photoshop, according to
2053 http://www.simplefilter.de/en/basics/mixmods.html is:
2054
2055 f(Sc,Dc) = Dc + 2*Sc - 1
2056 */
2057 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
2058 break;
2059 }
2060 case LightenCompositeOp:
2061 {
2062 if ((Sca*Da) > (Dca*Sa))
2063 {
2064 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2065 break;
2066 }
2067 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2068 break;
2069 }
2070 case LightenIntensityCompositeOp:
2071 {
2072 /*
2073 Lighten is equivalent to a 'Maximum' method
2074 OR a greyscale version of a binary 'And'
2075 OR the 'Union' of pixel sets.
2076 */
2077 pixel=Sa*GetPixelIntensity(source_image,p) >
2078 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2079 break;
2080 }
2081 case LuminizeCompositeOp:
2082 {
2083 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2084 {
2085 pixel=Dc;
2086 break;
2087 }
2088 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2089 {
2090 pixel=Sc;
2091 break;
2092 }
2093 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2094 &hue,&chroma,&luma);
2095 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2096 &sans,&sans,&luma);
2097 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2098 switch (channel)
2099 {
2100 case RedPixelChannel: pixel=red; break;
2101 case GreenPixelChannel: pixel=green; break;
2102 case BluePixelChannel: pixel=blue; break;
2103 default: pixel=Dc; break;
2104 }
2105 break;
2106 }
2107 case MathematicsCompositeOp:
2108 {
2109 /*
2110 'Mathematics' a free form user control mathematical composition
2111 is defined as...
2112
2113 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2114
2115 Where the arguments A,B,C,D are (currently) passed to composite
2116 as a command separated 'geometry' string in "compose:args" image
2117 artifact.
2118
2119 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2120
2121 Applying the SVG transparency formula (see above), we get...
2122
2123 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2124
2125 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2126 Dca*(1.0-Sa)
2127 */
2128 pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
2129 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2130 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2131 break;
2132 }
2133 case MinusDstCompositeOp:
2134 {
2135 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2136 break;
2137 }
2138 case MinusSrcCompositeOp:
2139 {
2140 /*
2141 Minus source from canvas.
2142
2143 f(Sc,Dc) = Sc - Dc
2144 */
2145 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2146 break;
2147 }
2148 case ModulateCompositeOp:
2149 {
2150 ssize_t
2151 offset;
2152
2153 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2154 {
2155 pixel=Dc;
2156 break;
2157 }
2158 offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2159 if (offset == 0)
2160 {
2161 pixel=Dc;
2162 break;
2163 }
2164 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2165 &hue,&chroma,&luma);
2166 luma+=(0.01*percent_luma*offset)/midpoint;
2167 chroma*=0.01*percent_chroma;
2168 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2169 switch (channel)
2170 {
2171 case RedPixelChannel: pixel=red; break;
2172 case GreenPixelChannel: pixel=green; break;
2173 case BluePixelChannel: pixel=blue; break;
2174 default: pixel=Dc; break;
2175 }
2176 break;
2177 }
2178 case ModulusAddCompositeOp:
2179 {
2180 if ((Sca+Dca) <= 1.0)
2181 {
2182 pixel=QuantumRange*(Sca+Dca);
2183 break;
2184 }
2185 pixel=QuantumRange*((Sca+Dca)-1.0);
2186 break;
2187 }
2188 case ModulusSubtractCompositeOp:
2189 {
2190 if ((Sca-Dca) >= 0.0)
2191 {
2192 pixel=QuantumRange*(Sca-Dca);
2193 break;
2194 }
2195 pixel=QuantumRange*((Sca-Dca)+1.0);
2196 break;
2197 }
2198 case MultiplyCompositeOp:
2199 {
2200 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2201 break;
2202 }
2203 case NegateCompositeOp:
2204 {
2205 pixel=QuantumRange*(1.0-fabs(1.0-Sca-Dca));
2206 break;
2207 }
2208 case OutCompositeOp:
2209 case SrcOutCompositeOp:
2210 {
2211 pixel=QuantumRange*(Sca*(1.0-Da));
2212 break;
2213 }
2214 case OverCompositeOp:
2215 case SrcOverCompositeOp:
2216 {
2217 pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
2218 break;
2219 }
2220 case OverlayCompositeOp:
2221 {
2222 if ((2.0*Dca) < Da)
2223 {
2224 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-
2225 Da));
2226 break;
2227 }
2228 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2229 Sca*(1.0-Da));
2230 break;
2231 }
2232 case PegtopLightCompositeOp:
2233 {
2234 /*
2235 PegTop: A Soft-Light alternative: A continuous version of the
2236 Softlight function, producing very similar results.
2237
2238 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2239
2240 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2241 */
2242 if (fabs((double) Da) < MagickEpsilon)
2243 {
2244 pixel=QuantumRange*gamma*Sca;
2245 break;
2246 }
2247 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2248 Da)+Dca*(1.0-Sa));
2249 break;
2250 }
2251 case PinLightCompositeOp:
2252 {
2253 /*
2254 PinLight: A Photoshop 7 composition method
2255 http://www.simplefilter.de/en/basics/mixmods.html
2256
2257 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2258 */
2259 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2260 {
2261 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2262 break;
2263 }
2264 if ((Dca*Sa) > (2.0*Sca*Da))
2265 {
2266 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2267 break;
2268 }
2269 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2270 break;
2271 }
2272 case PlusCompositeOp:
2273 {
2274 pixel=QuantumRange*(Sca+Dca);
2275 break;
2276 }
2277 case ReflectCompositeOp:
2278 {
2279 pixel=QuantumRange*gamma*(Sca*Sca*PerceptibleReciprocal(1.0-Dca));
2280 if (pixel > QuantumRange)
2281 pixel=QuantumRange;
2282 break;
2283 }
2284 case RMSECompositeOp:
2285 {
2286 double
2287 gray;
2288
2289 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2290 {
2291 pixel=Dc;
2292 break;
2293 }
2294 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2295 {
2296 pixel=Sc;
2297 break;
2298 }
2299 gray=sqrt(
2300 (canvas_pixel.red-source_pixel.red)*
2301 (canvas_pixel.red-source_pixel.red)+
2302 (canvas_pixel.green-source_pixel.green)*
2303 (canvas_pixel.green-source_pixel.green)+
2304 (canvas_pixel.blue-source_pixel.blue)*
2305 (canvas_pixel.blue-source_pixel.blue)/3.0);
2306 switch (channel)
2307 {
2308 case RedPixelChannel: pixel=gray; break;
2309 case GreenPixelChannel: pixel=gray; break;
2310 case BluePixelChannel: pixel=gray; break;
2311 default: pixel=Dc; break;
2312 }
2313 break;
2314 }
2315 case SaturateCompositeOp:
2316 {
2317 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2318 {
2319 pixel=Dc;
2320 break;
2321 }
2322 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2323 {
2324 pixel=Sc;
2325 break;
2326 }
2327 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2328 &hue,&chroma,&luma);
2329 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2330 &sans,&chroma,&sans);
2331 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2332 switch (channel)
2333 {
2334 case RedPixelChannel: pixel=red; break;
2335 case GreenPixelChannel: pixel=green; break;
2336 case BluePixelChannel: pixel=blue; break;
2337 default: pixel=Dc; break;
2338 }
2339 break;
2340 }
2341 case ScreenCompositeOp:
2342 {
2343 /*
2344 Screen: a negated multiply:
2345
2346 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2347 */
2348 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2349 break;
2350 }
2351 case SoftBurnCompositeOp:
2352 {
2353 if ((Sca+Dca) < 1.0)
2354 pixel=QuantumRange*gamma*(0.5*Dca*PerceptibleReciprocal(1.0-Sca));
2355 else
2356 pixel=QuantumRange*gamma*(1.0-0.5*(1.0-Sca)*
2357 PerceptibleReciprocal(Dca));
2358 break;
2359 }
2360 case SoftDodgeCompositeOp:
2361 {
2362 if ((Sca+Dca) < 1.0)
2363 pixel=QuantumRange*gamma*(0.5*Sca*PerceptibleReciprocal(1.0-Dca));
2364 else
2365 pixel=QuantumRange*gamma*(1.0-0.5*(1.0-Dca)*
2366 PerceptibleReciprocal(Sca));
2367 break;
2368 }
2369 case SoftLightCompositeOp:
2370 {
2371 if ((2.0*Sca) < Sa)
2372 {
2373 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-DcaDa))+
2374 Sca*(1.0-Da)+Dca*(1.0-Sa));
2375 break;
2376 }
2377 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2378 {
2379 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*DcaDa*
2380 (4.0*DcaDa+1.0)*(DcaDa-1.0)+7.0*DcaDa)+Sca*(1.0-Da)+
2381 Dca*(1.0-Sa));
2382 break;
2383 }
2384 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow(DcaDa,0.5)-
2385 DcaDa)+Sca*(1.0-Da)+Dca*(1.0-Sa));
2386 break;
2387 }
2388 case StampCompositeOp:
2389 {
2390 pixel=QuantumRange*(Sca+Dca*Dca-1.0);
2391 break;
2392 }
2393 case StereoCompositeOp:
2394 {
2395 if (channel == RedPixelChannel)
2396 pixel=(MagickRealType) GetPixelRed(source_image,p);
2397 break;
2398 }
2399 case ThresholdCompositeOp:
2400 {
2401 MagickRealType
2402 delta;
2403
2404 delta=Sc-Dc;
2405 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2406 {
2407 pixel=gamma*Dc;
2408 break;
2409 }
2410 pixel=gamma*(Dc+delta*amount);
2411 break;
2412 }
2413 case VividLightCompositeOp:
2414 {
2415 /*
2416 VividLight: A Photoshop 7 composition method. See
2417 http://www.simplefilter.de/en/basics/mixmods.html.
2418
2419 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2420 */
2421 if ((fabs((double) Sa) < MagickEpsilon) ||
2422 (fabs((double) (Sca-Sa)) < MagickEpsilon))
2423 {
2424 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2425 break;
2426 }
2427 if ((2.0*Sca) <= Sa)
2428 {
2429 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)*
2430 PerceptibleReciprocal(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2431 break;
2432 }
2433 pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(2.0*
2434 (Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2435 break;
2436 }
2437 case XorCompositeOp:
2438 {
2439 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2440 break;
2441 }
2442 default:
2443 {
2444 pixel=Sc;
2445 break;
2446 }
2447 }
2448 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2449 }
2450 p+=GetPixelChannels(source_image);
2451 channels=GetPixelChannels(source_image);
2452 if (p >= (pixels+channels*source_image->columns))
2453 p=pixels;
2454 q+=GetPixelChannels(image);
2455 }
2456 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2457 status=MagickFalse;
2458 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2459 {
2460 MagickBooleanType
2461 proceed;
2462
2463 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2464 #pragma omp atomic
2465 #endif
2466 progress++;
2467 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
2468 if (proceed == MagickFalse)
2469 status=MagickFalse;
2470 }
2471 }
2472 source_view=DestroyCacheView(source_view);
2473 image_view=DestroyCacheView(image_view);
2474 if (canvas_image != (Image * ) NULL)
2475 canvas_image=DestroyImage(canvas_image);
2476 else
2477 source_image=DestroyImage(source_image);
2478 return(status);
2479 }
2480
2481 /*
2482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2483 % %
2484 % %
2485 % %
2486 % T e x t u r e I m a g e %
2487 % %
2488 % %
2489 % %
2490 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2491 %
2492 % TextureImage() repeatedly tiles the texture image across and down the image
2493 % canvas.
2494 %
2495 % The format of the TextureImage method is:
2496 %
2497 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2498 % ExceptionInfo *exception)
2499 %
2500 % A description of each parameter follows:
2501 %
2502 % o image: the image.
2503 %
2504 % o texture_image: This image is the texture to layer on the background.
2505 %
2506 */
TextureImage(Image * image,const Image * texture,ExceptionInfo * exception)2507 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2508 ExceptionInfo *exception)
2509 {
2510 #define TextureImageTag "Texture/Image"
2511
2512 CacheView
2513 *image_view,
2514 *texture_view;
2515
2516 Image
2517 *texture_image;
2518
2519 MagickBooleanType
2520 status;
2521
2522 ssize_t
2523 y;
2524
2525 assert(image != (Image *) NULL);
2526 if (image->debug != MagickFalse)
2527 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2528 assert(image->signature == MagickCoreSignature);
2529 if (texture == (const Image *) NULL)
2530 return(MagickFalse);
2531 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2532 return(MagickFalse);
2533 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2534 if (texture_image == (const Image *) NULL)
2535 return(MagickFalse);
2536 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2537 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2538 exception);
2539 status=MagickTrue;
2540 if ((image->compose != CopyCompositeOp) &&
2541 ((image->compose != OverCompositeOp) ||
2542 (image->alpha_trait != UndefinedPixelTrait) ||
2543 (texture_image->alpha_trait != UndefinedPixelTrait)))
2544 {
2545 /*
2546 Tile texture onto the image background.
2547 */
2548 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2549 {
2550 ssize_t
2551 x;
2552
2553 if (status == MagickFalse)
2554 continue;
2555 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2556 {
2557 MagickBooleanType
2558 thread_status;
2559
2560 thread_status=CompositeImage(image,texture_image,image->compose,
2561 MagickTrue,x+texture_image->tile_offset.x,y+
2562 texture_image->tile_offset.y,exception);
2563 if (thread_status == MagickFalse)
2564 {
2565 status=thread_status;
2566 break;
2567 }
2568 }
2569 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2570 {
2571 MagickBooleanType
2572 proceed;
2573
2574 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2575 image->rows);
2576 if (proceed == MagickFalse)
2577 status=MagickFalse;
2578 }
2579 }
2580 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2581 image->rows,image->rows);
2582 texture_image=DestroyImage(texture_image);
2583 return(status);
2584 }
2585 /*
2586 Tile texture onto the image background (optimized).
2587 */
2588 status=MagickTrue;
2589 texture_view=AcquireVirtualCacheView(texture_image,exception);
2590 image_view=AcquireAuthenticCacheView(image,exception);
2591 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2592 #pragma omp parallel for schedule(static) shared(status) \
2593 magick_number_threads(texture_image,image,image->rows,1)
2594 #endif
2595 for (y=0; y < (ssize_t) image->rows; y++)
2596 {
2597 MagickBooleanType
2598 sync;
2599
2600 const Quantum
2601 *p,
2602 *pixels;
2603
2604 ssize_t
2605 x;
2606
2607 Quantum
2608 *q;
2609
2610 size_t
2611 width;
2612
2613 if (status == MagickFalse)
2614 continue;
2615 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2616 (y+texture_image->tile_offset.y) % texture_image->rows,
2617 texture_image->columns,1,exception);
2618 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2619 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2620 {
2621 status=MagickFalse;
2622 continue;
2623 }
2624 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2625 {
2626 ssize_t
2627 j;
2628
2629 p=pixels;
2630 width=texture_image->columns;
2631 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2632 width=image->columns-x;
2633 for (j=0; j < (ssize_t) width; j++)
2634 {
2635 ssize_t
2636 i;
2637
2638 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2639 {
2640 PixelChannel channel = GetPixelChannelChannel(texture_image,i);
2641 PixelTrait traits = GetPixelChannelTraits(image,channel);
2642 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2643 channel);
2644 if ((traits == UndefinedPixelTrait) ||
2645 (texture_traits == UndefinedPixelTrait))
2646 continue;
2647 SetPixelChannel(image,channel,p[i],q);
2648 }
2649 p+=GetPixelChannels(texture_image);
2650 q+=GetPixelChannels(image);
2651 }
2652 }
2653 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2654 if (sync == MagickFalse)
2655 status=MagickFalse;
2656 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2657 {
2658 MagickBooleanType
2659 proceed;
2660
2661 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2662 image->rows);
2663 if (proceed == MagickFalse)
2664 status=MagickFalse;
2665 }
2666 }
2667 texture_view=DestroyCacheView(texture_view);
2668 image_view=DestroyCacheView(image_view);
2669 texture_image=DestroyImage(texture_image);
2670 return(status);
2671 }
2672