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-2019 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 register const Quantum
337 *magick_restrict p;
338
339 register Quantum
340 *magick_restrict q;
341
342 register 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*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 register 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 if (IsGrayColorspace(image->colorspace) != MagickFalse)
583 (void) SetImageColorspace(image,sRGBColorspace,exception);
584 (void) SetImageColorspace(source_image,image->colorspace,exception);
585 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
586 {
587 status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
588 y_offset,exception);
589 source_image=DestroyImage(source_image);
590 return(status);
591 }
592 amount=0.5;
593 canvas_image=(Image *) NULL;
594 canvas_dissolve=1.0;
595 clamp=MagickTrue;
596 value=GetImageArtifact(image,"compose:clamp");
597 if (value != (const char *) NULL)
598 clamp=IsStringTrue(value);
599 SetGeometryInfo(&geometry_info);
600 percent_luma=100.0;
601 percent_chroma=100.0;
602 source_dissolve=1.0;
603 threshold=0.05f;
604 switch (compose)
605 {
606 case CopyCompositeOp:
607 {
608 if ((x_offset < 0) || (y_offset < 0))
609 break;
610 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
611 break;
612 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
613 break;
614 status=MagickTrue;
615 source_view=AcquireVirtualCacheView(source_image,exception);
616 image_view=AcquireAuthenticCacheView(image,exception);
617 #if defined(MAGICKCORE_OPENMP_SUPPORT)
618 #pragma omp parallel for schedule(static) shared(status) \
619 magick_number_threads(source_image,image,source_image->rows,1)
620 #endif
621 for (y=0; y < (ssize_t) source_image->rows; y++)
622 {
623 MagickBooleanType
624 sync;
625
626 register const Quantum
627 *p;
628
629 register Quantum
630 *q;
631
632 register ssize_t
633 x;
634
635 if (status == MagickFalse)
636 continue;
637 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
638 exception);
639 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
640 source_image->columns,1,exception);
641 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
642 {
643 status=MagickFalse;
644 continue;
645 }
646 for (x=0; x < (ssize_t) source_image->columns; x++)
647 {
648 register ssize_t
649 i;
650
651 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
652 {
653 p+=GetPixelChannels(source_image);
654 q+=GetPixelChannels(image);
655 continue;
656 }
657 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
658 {
659 PixelChannel channel = GetPixelChannelChannel(image,i);
660 PixelTrait traits = GetPixelChannelTraits(image,channel);
661 PixelTrait source_traits=GetPixelChannelTraits(source_image,
662 channel);
663 if (traits == UndefinedPixelTrait)
664 continue;
665 if (source_traits != UndefinedPixelTrait)
666 SetPixelChannel(image,channel,p[i],q);
667 else if (channel == AlphaPixelChannel)
668 SetPixelChannel(image,channel,OpaqueAlpha,q);
669 }
670 p+=GetPixelChannels(source_image);
671 q+=GetPixelChannels(image);
672 }
673 sync=SyncCacheViewAuthenticPixels(image_view,exception);
674 if (sync == MagickFalse)
675 status=MagickFalse;
676 if (image->progress_monitor != (MagickProgressMonitor) NULL)
677 {
678 MagickBooleanType
679 proceed;
680
681 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
682 y,image->rows);
683 if (proceed == MagickFalse)
684 status=MagickFalse;
685 }
686 }
687 source_view=DestroyCacheView(source_view);
688 image_view=DestroyCacheView(image_view);
689 source_image=DestroyImage(source_image);
690 return(status);
691 }
692 case IntensityCompositeOp:
693 {
694 if ((x_offset < 0) || (y_offset < 0))
695 break;
696 if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
697 break;
698 if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
699 break;
700 status=MagickTrue;
701 source_view=AcquireVirtualCacheView(source_image,exception);
702 image_view=AcquireAuthenticCacheView(image,exception);
703 #if defined(MAGICKCORE_OPENMP_SUPPORT)
704 #pragma omp parallel for schedule(static) shared(status) \
705 magick_number_threads(source_image,image,source_image->rows,1)
706 #endif
707 for (y=0; y < (ssize_t) source_image->rows; y++)
708 {
709 MagickBooleanType
710 sync;
711
712 register const Quantum
713 *p;
714
715 register Quantum
716 *q;
717
718 register ssize_t
719 x;
720
721 if (status == MagickFalse)
722 continue;
723 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
724 exception);
725 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
726 source_image->columns,1,exception);
727 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
728 {
729 status=MagickFalse;
730 continue;
731 }
732 for (x=0; x < (ssize_t) source_image->columns; x++)
733 {
734 if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
735 {
736 p+=GetPixelChannels(source_image);
737 q+=GetPixelChannels(image);
738 continue;
739 }
740 SetPixelAlpha(image,clamp != MagickFalse ?
741 ClampPixel(GetPixelIntensity(source_image,p)) :
742 ClampToQuantum(GetPixelIntensity(source_image,p)),q);
743 p+=GetPixelChannels(source_image);
744 q+=GetPixelChannels(image);
745 }
746 sync=SyncCacheViewAuthenticPixels(image_view,exception);
747 if (sync == MagickFalse)
748 status=MagickFalse;
749 if (image->progress_monitor != (MagickProgressMonitor) NULL)
750 {
751 MagickBooleanType
752 proceed;
753
754 proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
755 y,image->rows);
756 if (proceed == MagickFalse)
757 status=MagickFalse;
758 }
759 }
760 source_view=DestroyCacheView(source_view);
761 image_view=DestroyCacheView(image_view);
762 source_image=DestroyImage(source_image);
763 return(status);
764 }
765 case CopyAlphaCompositeOp:
766 case ChangeMaskCompositeOp:
767 {
768 /*
769 Modify canvas outside the overlaid region and require an alpha
770 channel to exist, to add transparency.
771 */
772 if (image->alpha_trait == UndefinedPixelTrait)
773 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
774 break;
775 }
776 case BlurCompositeOp:
777 {
778 CacheView
779 *canvas_view;
780
781 MagickRealType
782 angle_range,
783 angle_start,
784 height,
785 width;
786
787 PixelInfo
788 pixel;
789
790 ResampleFilter
791 *resample_filter;
792
793 SegmentInfo
794 blur;
795
796 /*
797 Blur Image by resampling.
798
799 Blur Image dictated by an overlay gradient map: X = red_channel;
800 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
801 */
802 canvas_image=CloneImage(image,0,0,MagickTrue,
803 exception);
804 if (canvas_image == (Image *) NULL)
805 {
806 source_image=DestroyImage(source_image);
807 return(MagickFalse);
808 }
809 /*
810 Gather the maximum blur sigma values from user.
811 */
812 flags=NoValue;
813 value=GetImageArtifact(image,"compose:args");
814 if (value != (const char *) NULL)
815 flags=ParseGeometry(value,&geometry_info);
816 if ((flags & WidthValue) == 0)
817 {
818 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
819 "InvalidSetting","'%s' '%s'","compose:args",value);
820 source_image=DestroyImage(source_image);
821 canvas_image=DestroyImage(canvas_image);
822 return(MagickFalse);
823 }
824 /*
825 Users input sigma now needs to be converted to the EWA ellipse size.
826 The filter defaults to a sigma of 0.5 so to make this match the
827 users input the ellipse size needs to be doubled.
828 */
829 width=height=geometry_info.rho*2.0;
830 if ((flags & HeightValue) != 0 )
831 height=geometry_info.sigma*2.0;
832 /*
833 Default the unrotated ellipse width and height axis vectors.
834 */
835 blur.x1=width;
836 blur.x2=0.0;
837 blur.y1=0.0;
838 blur.y2=height;
839 /* rotate vectors if a rotation angle is given */
840 if ((flags & XValue) != 0 )
841 {
842 MagickRealType
843 angle;
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 /* Otherwise lets set a angle range and calculate in the loop */
852 angle_start=0.0;
853 angle_range=0.0;
854 if ((flags & YValue) != 0 )
855 {
856 angle_start=DegreesToRadians(geometry_info.xi);
857 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
858 }
859 /*
860 Set up a gaussian cylindrical filter for EWA Bluring.
861
862 As the minimum ellipse radius of support*1.0 the EWA algorithm
863 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
864 This means that even 'No Blur' will be still a little blurry!
865
866 The solution (as well as the problem of preventing any user
867 expert filter settings, is to set our own user settings, then
868 restore them afterwards.
869 */
870 resample_filter=AcquireResampleFilter(image,exception);
871 SetResampleFilter(resample_filter,GaussianFilter);
872
873 /* do the variable blurring of each pixel in image */
874 GetPixelInfo(image,&pixel);
875 source_view=AcquireVirtualCacheView(source_image,exception);
876 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
877 for (y=0; y < (ssize_t) source_image->rows; y++)
878 {
879 MagickBooleanType
880 sync;
881
882 register const Quantum
883 *magick_restrict p;
884
885 register Quantum
886 *magick_restrict q;
887
888 register ssize_t
889 x;
890
891 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
892 continue;
893 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
894 exception);
895 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
896 exception);
897 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
898 break;
899 for (x=0; x < (ssize_t) source_image->columns; x++)
900 {
901 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
902 {
903 p+=GetPixelChannels(source_image);
904 continue;
905 }
906 if (fabs((double) angle_range) > MagickEpsilon)
907 {
908 MagickRealType
909 angle;
910
911 angle=angle_start+angle_range*QuantumScale*
912 GetPixelBlue(source_image,p);
913 blur.x1=width*cos(angle);
914 blur.x2=width*sin(angle);
915 blur.y1=(-height*sin(angle));
916 blur.y2=height*cos(angle);
917 }
918 #if 0
919 if ( x == 10 && y == 60 ) {
920 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
921 blur.x2,blur.y1, blur.y2);
922 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
923 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
924 #endif
925 ScaleResampleFilter(resample_filter,
926 blur.x1*QuantumScale*GetPixelRed(source_image,p),
927 blur.y1*QuantumScale*GetPixelGreen(source_image,p),
928 blur.x2*QuantumScale*GetPixelRed(source_image,p),
929 blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
930 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
931 (double) y_offset+y,&pixel,exception);
932 SetPixelViaPixelInfo(canvas_image,&pixel,q);
933 p+=GetPixelChannels(source_image);
934 q+=GetPixelChannels(canvas_image);
935 }
936 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
937 if (sync == MagickFalse)
938 break;
939 }
940 resample_filter=DestroyResampleFilter(resample_filter);
941 source_view=DestroyCacheView(source_view);
942 canvas_view=DestroyCacheView(canvas_view);
943 source_image=DestroyImage(source_image);
944 source_image=canvas_image;
945 break;
946 }
947 case DisplaceCompositeOp:
948 case DistortCompositeOp:
949 {
950 CacheView
951 *canvas_view;
952
953 MagickRealType
954 horizontal_scale,
955 vertical_scale;
956
957 PixelInfo
958 pixel;
959
960 PointInfo
961 center,
962 offset;
963
964 /*
965 Displace/Distort based on overlay gradient map:
966 X = red_channel; Y = green_channel;
967 compose:args = x_scale[,y_scale[,center.x,center.y]]
968 */
969 canvas_image=CloneImage(image,0,0,MagickTrue,
970 exception);
971 if (canvas_image == (Image *) NULL)
972 {
973 source_image=DestroyImage(source_image);
974 return(MagickFalse);
975 }
976 SetGeometryInfo(&geometry_info);
977 flags=NoValue;
978 value=GetImageArtifact(image,"compose:args");
979 if (value != (char *) NULL)
980 flags=ParseGeometry(value,&geometry_info);
981 if ((flags & (WidthValue | HeightValue)) == 0 )
982 {
983 if ((flags & AspectValue) == 0)
984 {
985 horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
986 vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
987 }
988 else
989 {
990 horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
991 vertical_scale=(MagickRealType) (image->rows-1)/2.0;
992 }
993 }
994 else
995 {
996 horizontal_scale=geometry_info.rho;
997 vertical_scale=geometry_info.sigma;
998 if ((flags & PercentValue) != 0)
999 {
1000 if ((flags & AspectValue) == 0)
1001 {
1002 horizontal_scale*=(source_image->columns-1)/200.0;
1003 vertical_scale*=(source_image->rows-1)/200.0;
1004 }
1005 else
1006 {
1007 horizontal_scale*=(image->columns-1)/200.0;
1008 vertical_scale*=(image->rows-1)/200.0;
1009 }
1010 }
1011 if ((flags & HeightValue) == 0)
1012 vertical_scale=horizontal_scale;
1013 }
1014 /*
1015 Determine fixed center point for absolute distortion map
1016 Absolute distort ==
1017 Displace offset relative to a fixed absolute point
1018 Select that point according to +X+Y user inputs.
1019 default = center of overlay image
1020 arg flag '!' = locations/percentage relative to background image
1021 */
1022 center.x=(MagickRealType) x_offset;
1023 center.y=(MagickRealType) y_offset;
1024 if (compose == DistortCompositeOp)
1025 {
1026 if ((flags & XValue) == 0)
1027 if ((flags & AspectValue) != 0)
1028 center.x=(MagickRealType) ((image->columns-1)/2.0);
1029 else
1030 center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1031 2.0);
1032 else
1033 if ((flags & AspectValue) != 0)
1034 center.x=geometry_info.xi;
1035 else
1036 center.x=(MagickRealType) (x_offset+geometry_info.xi);
1037 if ((flags & YValue) == 0)
1038 if ((flags & AspectValue) != 0)
1039 center.y=(MagickRealType) ((image->rows-1)/2.0);
1040 else
1041 center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1042 else
1043 if ((flags & AspectValue) != 0)
1044 center.y=geometry_info.psi;
1045 else
1046 center.y=(MagickRealType) (y_offset+geometry_info.psi);
1047 }
1048 /*
1049 Shift the pixel offset point as defined by the provided,
1050 displacement/distortion map. -- Like a lens...
1051 */
1052 GetPixelInfo(image,&pixel);
1053 image_view=AcquireVirtualCacheView(image,exception);
1054 source_view=AcquireVirtualCacheView(source_image,exception);
1055 canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1056 for (y=0; y < (ssize_t) source_image->rows; y++)
1057 {
1058 MagickBooleanType
1059 sync;
1060
1061 register const Quantum
1062 *magick_restrict p;
1063
1064 register Quantum
1065 *magick_restrict q;
1066
1067 register ssize_t
1068 x;
1069
1070 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1071 continue;
1072 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1073 exception);
1074 q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1075 exception);
1076 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1077 break;
1078 for (x=0; x < (ssize_t) source_image->columns; x++)
1079 {
1080 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1081 {
1082 p+=GetPixelChannels(source_image);
1083 continue;
1084 }
1085 /*
1086 Displace the offset.
1087 */
1088 offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1089 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1090 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1091 x : 0);
1092 offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1093 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1094 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1095 y : 0);
1096 status=InterpolatePixelInfo(image,image_view,
1097 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1098 &pixel,exception);
1099 if (status == MagickFalse)
1100 break;
1101 /*
1102 Mask with the 'invalid pixel mask' in alpha channel.
1103 */
1104 pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
1105 (QuantumScale*GetPixelAlpha(source_image,p));
1106 SetPixelViaPixelInfo(canvas_image,&pixel,q);
1107 p+=GetPixelChannels(source_image);
1108 q+=GetPixelChannels(canvas_image);
1109 }
1110 if (x < (ssize_t) source_image->columns)
1111 break;
1112 sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1113 if (sync == MagickFalse)
1114 break;
1115 }
1116 canvas_view=DestroyCacheView(canvas_view);
1117 source_view=DestroyCacheView(source_view);
1118 image_view=DestroyCacheView(image_view);
1119 source_image=DestroyImage(source_image);
1120 source_image=canvas_image;
1121 break;
1122 }
1123 case DissolveCompositeOp:
1124 {
1125 /*
1126 Geometry arguments to dissolve factors.
1127 */
1128 value=GetImageArtifact(image,"compose:args");
1129 if (value != (char *) NULL)
1130 {
1131 flags=ParseGeometry(value,&geometry_info);
1132 source_dissolve=geometry_info.rho/100.0;
1133 canvas_dissolve=1.0;
1134 if ((source_dissolve-MagickEpsilon) < 0.0)
1135 source_dissolve=0.0;
1136 if ((source_dissolve+MagickEpsilon) > 1.0)
1137 {
1138 canvas_dissolve=2.0-source_dissolve;
1139 source_dissolve=1.0;
1140 }
1141 if ((flags & SigmaValue) != 0)
1142 canvas_dissolve=geometry_info.sigma/100.0;
1143 if ((canvas_dissolve-MagickEpsilon) < 0.0)
1144 canvas_dissolve=0.0;
1145 }
1146 break;
1147 }
1148 case BlendCompositeOp:
1149 {
1150 value=GetImageArtifact(image,"compose:args");
1151 if (value != (char *) NULL)
1152 {
1153 flags=ParseGeometry(value,&geometry_info);
1154 source_dissolve=geometry_info.rho/100.0;
1155 canvas_dissolve=1.0-source_dissolve;
1156 if ((flags & SigmaValue) != 0)
1157 canvas_dissolve=geometry_info.sigma/100.0;
1158 }
1159 break;
1160 }
1161 case MathematicsCompositeOp:
1162 {
1163 /*
1164 Just collect the values from "compose:args", setting.
1165 Unused values are set to zero automagically.
1166
1167 Arguments are normally a comma separated list, so this probably should
1168 be changed to some 'general comma list' parser, (with a minimum
1169 number of values)
1170 */
1171 SetGeometryInfo(&geometry_info);
1172 value=GetImageArtifact(image,"compose:args");
1173 if (value != (char *) NULL)
1174 (void) ParseGeometry(value,&geometry_info);
1175 break;
1176 }
1177 case ModulateCompositeOp:
1178 {
1179 /*
1180 Determine the luma and chroma scale.
1181 */
1182 value=GetImageArtifact(image,"compose:args");
1183 if (value != (char *) NULL)
1184 {
1185 flags=ParseGeometry(value,&geometry_info);
1186 percent_luma=geometry_info.rho;
1187 if ((flags & SigmaValue) != 0)
1188 percent_chroma=geometry_info.sigma;
1189 }
1190 break;
1191 }
1192 case ThresholdCompositeOp:
1193 {
1194 /*
1195 Determine the amount and threshold.
1196 */
1197 value=GetImageArtifact(image,"compose:args");
1198 if (value != (char *) NULL)
1199 {
1200 flags=ParseGeometry(value,&geometry_info);
1201 amount=geometry_info.rho;
1202 threshold=geometry_info.sigma;
1203 if ((flags & SigmaValue) == 0)
1204 threshold=0.05f;
1205 }
1206 threshold*=QuantumRange;
1207 break;
1208 }
1209 default:
1210 break;
1211 }
1212 /*
1213 Composite image.
1214 */
1215 status=MagickTrue;
1216 progress=0;
1217 midpoint=((MagickRealType) QuantumRange+1.0)/2;
1218 source_view=AcquireVirtualCacheView(source_image,exception);
1219 image_view=AcquireAuthenticCacheView(image,exception);
1220 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1221 #pragma omp parallel for schedule(static) shared(progress,status) \
1222 magick_number_threads(source_image,image,image->rows,1)
1223 #endif
1224 for (y=0; y < (ssize_t) image->rows; y++)
1225 {
1226 const Quantum
1227 *pixels;
1228
1229 MagickRealType
1230 blue,
1231 chroma,
1232 green,
1233 hue,
1234 luma,
1235 red;
1236
1237 PixelInfo
1238 canvas_pixel,
1239 source_pixel;
1240
1241 register const Quantum
1242 *magick_restrict p;
1243
1244 register Quantum
1245 *magick_restrict q;
1246
1247 register ssize_t
1248 x;
1249
1250 if (status == MagickFalse)
1251 continue;
1252 if (clip_to_self != MagickFalse)
1253 {
1254 if (y < y_offset)
1255 continue;
1256 if ((y-y_offset) >= (ssize_t) source_image->rows)
1257 continue;
1258 }
1259 /*
1260 If pixels is NULL, y is outside overlay region.
1261 */
1262 pixels=(Quantum *) NULL;
1263 p=(Quantum *) NULL;
1264 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1265 {
1266 p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1267 source_image->columns,1,exception);
1268 if (p == (const Quantum *) NULL)
1269 {
1270 status=MagickFalse;
1271 continue;
1272 }
1273 pixels=p;
1274 if (x_offset < 0)
1275 p-=x_offset*GetPixelChannels(source_image);
1276 }
1277 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1278 if (q == (Quantum *) NULL)
1279 {
1280 status=MagickFalse;
1281 continue;
1282 }
1283 hue=0.0;
1284 chroma=0.0;
1285 luma=0.0;
1286 GetPixelInfo(image,&canvas_pixel);
1287 GetPixelInfo(source_image,&source_pixel);
1288 for (x=0; x < (ssize_t) image->columns; x++)
1289 {
1290 double
1291 gamma;
1292
1293 MagickRealType
1294 alpha,
1295 Da,
1296 Dc,
1297 Dca,
1298 DcaDa,
1299 Sa,
1300 SaSca,
1301 Sc,
1302 Sca;
1303
1304 register ssize_t
1305 i;
1306
1307 size_t
1308 channels;
1309
1310 if (clip_to_self != MagickFalse)
1311 {
1312 if (x < x_offset)
1313 {
1314 q+=GetPixelChannels(image);
1315 continue;
1316 }
1317 if ((x-x_offset) >= (ssize_t) source_image->columns)
1318 break;
1319 }
1320 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1321 ((x-x_offset) >= (ssize_t) source_image->columns))
1322 {
1323 Quantum
1324 source[MaxPixelChannels];
1325
1326 /*
1327 Virtual composite:
1328 Sc: source color.
1329 Dc: canvas color.
1330 */
1331 (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
1332 exception);
1333 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1334 {
1335 MagickRealType
1336 pixel;
1337
1338 PixelChannel channel = GetPixelChannelChannel(image,i);
1339 PixelTrait traits = GetPixelChannelTraits(image,channel);
1340 PixelTrait source_traits=GetPixelChannelTraits(source_image,
1341 channel);
1342 if ((traits == UndefinedPixelTrait) ||
1343 (source_traits == UndefinedPixelTrait))
1344 continue;
1345 switch (compose)
1346 {
1347 case AlphaCompositeOp:
1348 case ChangeMaskCompositeOp:
1349 case CopyAlphaCompositeOp:
1350 case DstAtopCompositeOp:
1351 case DstInCompositeOp:
1352 case InCompositeOp:
1353 case OutCompositeOp:
1354 case SrcInCompositeOp:
1355 case SrcOutCompositeOp:
1356 {
1357 if (channel == AlphaPixelChannel)
1358 pixel=(MagickRealType) TransparentAlpha;
1359 else
1360 pixel=(MagickRealType) q[i];
1361 break;
1362 }
1363 case ClearCompositeOp:
1364 case CopyCompositeOp:
1365 case ReplaceCompositeOp:
1366 case SrcCompositeOp:
1367 {
1368 if (channel == AlphaPixelChannel)
1369 pixel=(MagickRealType) TransparentAlpha;
1370 else
1371 pixel=0.0;
1372 break;
1373 }
1374 case BlendCompositeOp:
1375 case DissolveCompositeOp:
1376 {
1377 if (channel == AlphaPixelChannel)
1378 pixel=canvas_dissolve*GetPixelAlpha(source_image,source);
1379 else
1380 pixel=(MagickRealType) source[channel];
1381 break;
1382 }
1383 default:
1384 {
1385 pixel=(MagickRealType) source[channel];
1386 break;
1387 }
1388 }
1389 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1390 ClampToQuantum(pixel);
1391 }
1392 q+=GetPixelChannels(image);
1393 continue;
1394 }
1395 /*
1396 Authentic composite:
1397 Sa: normalized source alpha.
1398 Da: normalized canvas alpha.
1399 */
1400 Sa=QuantumScale*GetPixelAlpha(source_image,p);
1401 Da=QuantumScale*GetPixelAlpha(image,q);
1402 switch (compose)
1403 {
1404 case BumpmapCompositeOp:
1405 {
1406 alpha=GetPixelIntensity(source_image,p)*Sa;
1407 break;
1408 }
1409 case ColorBurnCompositeOp:
1410 case ColorDodgeCompositeOp:
1411 case DarkenCompositeOp:
1412 case DifferenceCompositeOp:
1413 case DivideDstCompositeOp:
1414 case DivideSrcCompositeOp:
1415 case ExclusionCompositeOp:
1416 case HardLightCompositeOp:
1417 case HardMixCompositeOp:
1418 case LinearBurnCompositeOp:
1419 case LinearDodgeCompositeOp:
1420 case LinearLightCompositeOp:
1421 case LightenCompositeOp:
1422 case MathematicsCompositeOp:
1423 case MinusDstCompositeOp:
1424 case MinusSrcCompositeOp:
1425 case ModulusAddCompositeOp:
1426 case ModulusSubtractCompositeOp:
1427 case MultiplyCompositeOp:
1428 case OverlayCompositeOp:
1429 case PegtopLightCompositeOp:
1430 case PinLightCompositeOp:
1431 case ScreenCompositeOp:
1432 case SoftLightCompositeOp:
1433 case VividLightCompositeOp:
1434 {
1435 alpha=RoundToUnity(Sa+Da-Sa*Da);
1436 break;
1437 }
1438 case DstAtopCompositeOp:
1439 case DstInCompositeOp:
1440 case InCompositeOp:
1441 case SrcInCompositeOp:
1442 {
1443 alpha=Sa*Da;
1444 break;
1445 }
1446 case DissolveCompositeOp:
1447 {
1448 alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
1449 canvas_dissolve*Da;
1450 break;
1451 }
1452 case DstOverCompositeOp:
1453 case OverCompositeOp:
1454 case SrcOverCompositeOp:
1455 {
1456 alpha=Sa+Da-Sa*Da;
1457 break;
1458 }
1459 case DstOutCompositeOp:
1460 {
1461 alpha=Da*(1.0-Sa);
1462 break;
1463 }
1464 case OutCompositeOp:
1465 case SrcOutCompositeOp:
1466 {
1467 alpha=Sa*(1.0-Da);
1468 break;
1469 }
1470 case BlendCompositeOp:
1471 case PlusCompositeOp:
1472 {
1473 alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
1474 break;
1475 }
1476 case XorCompositeOp:
1477 {
1478 alpha=Sa+Da-2.0*Sa*Da;
1479 break;
1480 }
1481 default:
1482 {
1483 alpha=1.0;
1484 break;
1485 }
1486 }
1487 switch (compose)
1488 {
1489 case ColorizeCompositeOp:
1490 case HueCompositeOp:
1491 case LuminizeCompositeOp:
1492 case ModulateCompositeOp:
1493 case SaturateCompositeOp:
1494 {
1495 GetPixelInfoPixel(source_image,p,&source_pixel);
1496 GetPixelInfoPixel(image,q,&canvas_pixel);
1497 break;
1498 }
1499 default:
1500 break;
1501 }
1502 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1503 {
1504 MagickRealType
1505 pixel,
1506 sans;
1507
1508 PixelChannel channel = GetPixelChannelChannel(image,i);
1509 PixelTrait traits = GetPixelChannelTraits(image,channel);
1510 PixelTrait source_traits = GetPixelChannelTraits(source_image,channel);
1511 if (traits == UndefinedPixelTrait)
1512 continue;
1513 if ((channel == AlphaPixelChannel) &&
1514 ((traits & UpdatePixelTrait) != 0))
1515 {
1516 /*
1517 Set alpha channel.
1518 */
1519 switch (compose)
1520 {
1521 case AlphaCompositeOp:
1522 {
1523 pixel=QuantumRange*Sa;
1524 break;
1525 }
1526 case AtopCompositeOp:
1527 case CopyBlackCompositeOp:
1528 case CopyBlueCompositeOp:
1529 case CopyCyanCompositeOp:
1530 case CopyGreenCompositeOp:
1531 case CopyMagentaCompositeOp:
1532 case CopyRedCompositeOp:
1533 case CopyYellowCompositeOp:
1534 case SrcAtopCompositeOp:
1535 case DstCompositeOp:
1536 case NoCompositeOp:
1537 {
1538 pixel=QuantumRange*Da;
1539 break;
1540 }
1541 case ChangeMaskCompositeOp:
1542 {
1543 MagickBooleanType
1544 equivalent;
1545
1546 if (Da < 0.5)
1547 {
1548 pixel=(MagickRealType) TransparentAlpha;
1549 break;
1550 }
1551 equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1552 if (equivalent != MagickFalse)
1553 pixel=(MagickRealType) TransparentAlpha;
1554 else
1555 pixel=(MagickRealType) OpaqueAlpha;
1556 break;
1557 }
1558 case ClearCompositeOp:
1559 {
1560 pixel=(MagickRealType) TransparentAlpha;
1561 break;
1562 }
1563 case ColorizeCompositeOp:
1564 case HueCompositeOp:
1565 case LuminizeCompositeOp:
1566 case SaturateCompositeOp:
1567 {
1568 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1569 {
1570 pixel=QuantumRange*Da;
1571 break;
1572 }
1573 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1574 {
1575 pixel=QuantumRange*Sa;
1576 break;
1577 }
1578 if (Sa < Da)
1579 {
1580 pixel=QuantumRange*Da;
1581 break;
1582 }
1583 pixel=QuantumRange*Sa;
1584 break;
1585 }
1586 case CopyAlphaCompositeOp:
1587 {
1588 if (source_image->alpha_trait == UndefinedPixelTrait)
1589 pixel=GetPixelIntensity(source_image,p);
1590 else
1591 pixel=QuantumRange*Sa;
1592 break;
1593 }
1594 case CopyCompositeOp:
1595 case DisplaceCompositeOp:
1596 case DistortCompositeOp:
1597 case DstAtopCompositeOp:
1598 case ReplaceCompositeOp:
1599 case SrcCompositeOp:
1600 {
1601 pixel=QuantumRange*Sa;
1602 break;
1603 }
1604 case DarkenIntensityCompositeOp:
1605 {
1606 pixel=Sa*GetPixelIntensity(source_image,p) <
1607 Da*GetPixelIntensity(image,q) ? Sa : Da;
1608 break;
1609 }
1610 case DifferenceCompositeOp:
1611 {
1612 pixel=QuantumRange*fabs(Sa-Da);
1613 break;
1614 }
1615 case LightenIntensityCompositeOp:
1616 {
1617 pixel=Sa*GetPixelIntensity(source_image,p) >
1618 Da*GetPixelIntensity(image,q) ? Sa : Da;
1619 break;
1620 }
1621 case ModulateCompositeOp:
1622 {
1623 pixel=QuantumRange*Da;
1624 break;
1625 }
1626 case MultiplyCompositeOp:
1627 {
1628 pixel=QuantumRange*Sa*Da;
1629 break;
1630 }
1631 case StereoCompositeOp:
1632 {
1633 pixel=QuantumRange*(Sa+Da)/2;
1634 break;
1635 }
1636 default:
1637 {
1638 pixel=QuantumRange*alpha;
1639 break;
1640 }
1641 }
1642 q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1643 ClampToQuantum(pixel);
1644 continue;
1645 }
1646 if (source_traits == UndefinedPixelTrait)
1647 continue;
1648 /*
1649 Sc: source color.
1650 Dc: canvas color.
1651 */
1652 Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1653 Dc=(MagickRealType) q[i];
1654 if ((traits & CopyPixelTrait) != 0)
1655 {
1656 /*
1657 Copy channel.
1658 */
1659 q[i]=ClampToQuantum(Dc);
1660 continue;
1661 }
1662 /*
1663 Porter-Duff compositions:
1664 Sca: source normalized color multiplied by alpha.
1665 Dca: normalized canvas color multiplied by alpha.
1666 */
1667 Sca=QuantumScale*Sa*Sc;
1668 Dca=QuantumScale*Da*Dc;
1669 SaSca=Sa*PerceptibleReciprocal(Sca);
1670 DcaDa=Dca*PerceptibleReciprocal(Da);
1671 switch (compose)
1672 {
1673 case DarkenCompositeOp:
1674 case LightenCompositeOp:
1675 case ModulusSubtractCompositeOp:
1676 {
1677 gamma=PerceptibleReciprocal(1.0-alpha);
1678 break;
1679 }
1680 default:
1681 {
1682 gamma=PerceptibleReciprocal(alpha);
1683 break;
1684 }
1685 }
1686 pixel=Dc;
1687 switch (compose)
1688 {
1689 case AlphaCompositeOp:
1690 {
1691 pixel=QuantumRange*Sa;
1692 break;
1693 }
1694 case AtopCompositeOp:
1695 case SrcAtopCompositeOp:
1696 {
1697 pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1698 break;
1699 }
1700 case BlendCompositeOp:
1701 {
1702 pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1703 break;
1704 }
1705 case BlurCompositeOp:
1706 case CopyCompositeOp:
1707 case ReplaceCompositeOp:
1708 case SrcCompositeOp:
1709 {
1710 pixel=QuantumRange*Sca;
1711 break;
1712 }
1713 case DisplaceCompositeOp:
1714 case DistortCompositeOp:
1715 {
1716 pixel=Sc;
1717 break;
1718 }
1719 case BumpmapCompositeOp:
1720 {
1721 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1722 {
1723 pixel=Dc;
1724 break;
1725 }
1726 pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1727 break;
1728 }
1729 case ChangeMaskCompositeOp:
1730 {
1731 pixel=Dc;
1732 break;
1733 }
1734 case ClearCompositeOp:
1735 {
1736 pixel=0.0;
1737 break;
1738 }
1739 case ColorBurnCompositeOp:
1740 {
1741 if ((Sca == 0.0) && (Dca == Da))
1742 {
1743 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1744 break;
1745 }
1746 if (Sca == 0.0)
1747 {
1748 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1749 break;
1750 }
1751 pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-DcaDa)*
1752 SaSca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1753 break;
1754 }
1755 case ColorDodgeCompositeOp:
1756 {
1757 if ((Sca*Da+Dca*Sa) >= Sa*Da)
1758 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1759 else
1760 pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(Sa-Sca)+
1761 Sca*(1.0-Da)+Dca*(1.0-Sa));
1762 break;
1763 }
1764 case ColorizeCompositeOp:
1765 {
1766 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1767 {
1768 pixel=Dc;
1769 break;
1770 }
1771 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1772 {
1773 pixel=Sc;
1774 break;
1775 }
1776 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1777 &sans,&sans,&luma);
1778 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1779 &hue,&chroma,&sans);
1780 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1781 switch (channel)
1782 {
1783 case RedPixelChannel: pixel=red; break;
1784 case GreenPixelChannel: pixel=green; break;
1785 case BluePixelChannel: pixel=blue; break;
1786 default: pixel=Dc; break;
1787 }
1788 break;
1789 }
1790 case CopyAlphaCompositeOp:
1791 {
1792 pixel=Dc;
1793 break;
1794 }
1795 case CopyBlackCompositeOp:
1796 {
1797 if (channel == BlackPixelChannel)
1798 pixel=(MagickRealType) (QuantumRange-
1799 GetPixelBlack(source_image,p));
1800 break;
1801 }
1802 case CopyBlueCompositeOp:
1803 case CopyYellowCompositeOp:
1804 {
1805 if (channel == BluePixelChannel)
1806 pixel=(MagickRealType) GetPixelBlue(source_image,p);
1807 break;
1808 }
1809 case CopyGreenCompositeOp:
1810 case CopyMagentaCompositeOp:
1811 {
1812 if (channel == GreenPixelChannel)
1813 pixel=(MagickRealType) GetPixelGreen(source_image,p);
1814 break;
1815 }
1816 case CopyRedCompositeOp:
1817 case CopyCyanCompositeOp:
1818 {
1819 if (channel == RedPixelChannel)
1820 pixel=(MagickRealType) GetPixelRed(source_image,p);
1821 break;
1822 }
1823 case DarkenCompositeOp:
1824 {
1825 /*
1826 Darken is equivalent to a 'Minimum' method
1827 OR a greyscale version of a binary 'Or'
1828 OR the 'Intersection' of pixel sets.
1829 */
1830 if ((Sca*Da) < (Dca*Sa))
1831 {
1832 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1833 break;
1834 }
1835 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1836 break;
1837 }
1838 case DarkenIntensityCompositeOp:
1839 {
1840 pixel=Sa*GetPixelIntensity(source_image,p) <
1841 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1842 break;
1843 }
1844 case DifferenceCompositeOp:
1845 {
1846 pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1847 break;
1848 }
1849 case DissolveCompositeOp:
1850 {
1851 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1852 canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1853 break;
1854 }
1855 case DivideDstCompositeOp:
1856 {
1857 if ((fabs((double) Sca) < MagickEpsilon) &&
1858 (fabs((double) Dca) < MagickEpsilon))
1859 {
1860 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1861 break;
1862 }
1863 if (fabs((double) Dca) < MagickEpsilon)
1864 {
1865 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1866 break;
1867 }
1868 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1869 break;
1870 }
1871 case DivideSrcCompositeOp:
1872 {
1873 if ((fabs((double) Dca) < MagickEpsilon) &&
1874 (fabs((double) Sca) < MagickEpsilon))
1875 {
1876 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1877 break;
1878 }
1879 if (fabs((double) Sca) < MagickEpsilon)
1880 {
1881 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1882 break;
1883 }
1884 pixel=QuantumRange*gamma*(Dca*Sa*SaSca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1885 break;
1886 }
1887 case DstAtopCompositeOp:
1888 {
1889 pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1890 break;
1891 }
1892 case DstCompositeOp:
1893 case NoCompositeOp:
1894 {
1895 pixel=QuantumRange*Dca;
1896 break;
1897 }
1898 case DstInCompositeOp:
1899 {
1900 pixel=QuantumRange*(Dca*Sa);
1901 break;
1902 }
1903 case DstOutCompositeOp:
1904 {
1905 pixel=QuantumRange*(Dca*(1.0-Sa));
1906 break;
1907 }
1908 case DstOverCompositeOp:
1909 {
1910 pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1911 break;
1912 }
1913 case ExclusionCompositeOp:
1914 {
1915 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1916 Dca*(1.0-Sa));
1917 break;
1918 }
1919 case HardLightCompositeOp:
1920 {
1921 if ((2.0*Sca) < Sa)
1922 {
1923 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1924 Sa));
1925 break;
1926 }
1927 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1928 Dca*(1.0-Sa));
1929 break;
1930 }
1931 case HardMixCompositeOp:
1932 {
1933 pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1934 break;
1935 }
1936 case HueCompositeOp:
1937 {
1938 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1939 {
1940 pixel=Dc;
1941 break;
1942 }
1943 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1944 {
1945 pixel=Sc;
1946 break;
1947 }
1948 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1949 &hue,&chroma,&luma);
1950 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1951 &hue,&sans,&sans);
1952 HCLComposite(hue,chroma,luma,&red,&green,&blue);
1953 switch (channel)
1954 {
1955 case RedPixelChannel: pixel=red; break;
1956 case GreenPixelChannel: pixel=green; break;
1957 case BluePixelChannel: pixel=blue; break;
1958 default: pixel=Dc; break;
1959 }
1960 break;
1961 }
1962 case InCompositeOp:
1963 case SrcInCompositeOp:
1964 {
1965 pixel=QuantumRange*(Sca*Da);
1966 break;
1967 }
1968 case LinearBurnCompositeOp:
1969 {
1970 /*
1971 LinearBurn: as defined by Abode Photoshop, according to
1972 http://www.simplefilter.de/en/basics/mixmods.html is:
1973
1974 f(Sc,Dc) = Sc + Dc - 1
1975 */
1976 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1977 break;
1978 }
1979 case LinearDodgeCompositeOp:
1980 {
1981 pixel=gamma*(Sa*Sc+Da*Dc);
1982 break;
1983 }
1984 case LinearLightCompositeOp:
1985 {
1986 /*
1987 LinearLight: as defined by Abode Photoshop, according to
1988 http://www.simplefilter.de/en/basics/mixmods.html is:
1989
1990 f(Sc,Dc) = Dc + 2*Sc - 1
1991 */
1992 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1993 break;
1994 }
1995 case LightenCompositeOp:
1996 {
1997 if ((Sca*Da) > (Dca*Sa))
1998 {
1999 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2000 break;
2001 }
2002 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2003 break;
2004 }
2005 case LightenIntensityCompositeOp:
2006 {
2007 /*
2008 Lighten is equivalent to a 'Maximum' method
2009 OR a greyscale version of a binary 'And'
2010 OR the 'Union' of pixel sets.
2011 */
2012 pixel=Sa*GetPixelIntensity(source_image,p) >
2013 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2014 break;
2015 }
2016 case LuminizeCompositeOp:
2017 {
2018 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2019 {
2020 pixel=Dc;
2021 break;
2022 }
2023 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2024 {
2025 pixel=Sc;
2026 break;
2027 }
2028 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2029 &hue,&chroma,&luma);
2030 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2031 &sans,&sans,&luma);
2032 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2033 switch (channel)
2034 {
2035 case RedPixelChannel: pixel=red; break;
2036 case GreenPixelChannel: pixel=green; break;
2037 case BluePixelChannel: pixel=blue; break;
2038 default: pixel=Dc; break;
2039 }
2040 break;
2041 }
2042 case MathematicsCompositeOp:
2043 {
2044 /*
2045 'Mathematics' a free form user control mathematical composition
2046 is defined as...
2047
2048 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2049
2050 Where the arguments A,B,C,D are (currently) passed to composite
2051 as a command separated 'geometry' string in "compose:args" image
2052 artifact.
2053
2054 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2055
2056 Applying the SVG transparency formula (see above), we get...
2057
2058 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2059
2060 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2061 Dca*(1.0-Sa)
2062 */
2063 pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
2064 geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2065 geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2066 break;
2067 }
2068 case MinusDstCompositeOp:
2069 {
2070 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2071 break;
2072 }
2073 case MinusSrcCompositeOp:
2074 {
2075 /*
2076 Minus source from canvas.
2077
2078 f(Sc,Dc) = Sc - Dc
2079 */
2080 pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2081 break;
2082 }
2083 case ModulateCompositeOp:
2084 {
2085 ssize_t
2086 offset;
2087
2088 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2089 {
2090 pixel=Dc;
2091 break;
2092 }
2093 offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2094 if (offset == 0)
2095 {
2096 pixel=Dc;
2097 break;
2098 }
2099 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2100 &hue,&chroma,&luma);
2101 luma+=(0.01*percent_luma*offset)/midpoint;
2102 chroma*=0.01*percent_chroma;
2103 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2104 switch (channel)
2105 {
2106 case RedPixelChannel: pixel=red; break;
2107 case GreenPixelChannel: pixel=green; break;
2108 case BluePixelChannel: pixel=blue; break;
2109 default: pixel=Dc; break;
2110 }
2111 break;
2112 }
2113 case ModulusAddCompositeOp:
2114 {
2115 pixel=Sc+Dc;
2116 while (pixel > QuantumRange)
2117 pixel-=QuantumRange;
2118 while (pixel < 0.0)
2119 pixel+=QuantumRange;
2120 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2121 break;
2122 }
2123 case ModulusSubtractCompositeOp:
2124 {
2125 pixel=Sc-Dc;
2126 while (pixel > QuantumRange)
2127 pixel-=QuantumRange;
2128 while (pixel < 0.0)
2129 pixel+=QuantumRange;
2130 pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2131 break;
2132 }
2133 case MultiplyCompositeOp:
2134 {
2135 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2136 break;
2137 }
2138 case OutCompositeOp:
2139 case SrcOutCompositeOp:
2140 {
2141 pixel=QuantumRange*(Sca*(1.0-Da));
2142 break;
2143 }
2144 case OverCompositeOp:
2145 case SrcOverCompositeOp:
2146 {
2147 pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
2148 break;
2149 }
2150 case OverlayCompositeOp:
2151 {
2152 if ((2.0*Dca) < Da)
2153 {
2154 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-
2155 Da));
2156 break;
2157 }
2158 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2159 Sca*(1.0-Da));
2160 break;
2161 }
2162 case PegtopLightCompositeOp:
2163 {
2164 /*
2165 PegTop: A Soft-Light alternative: A continuous version of the
2166 Softlight function, producing very similar results.
2167
2168 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2169
2170 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2171 */
2172 if (fabs((double) Da) < MagickEpsilon)
2173 {
2174 pixel=QuantumRange*gamma*(Sca);
2175 break;
2176 }
2177 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2178 Da)+Dca*(1.0-Sa));
2179 break;
2180 }
2181 case PinLightCompositeOp:
2182 {
2183 /*
2184 PinLight: A Photoshop 7 composition method
2185 http://www.simplefilter.de/en/basics/mixmods.html
2186
2187 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2188 */
2189 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2190 {
2191 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2192 break;
2193 }
2194 if ((Dca*Sa) > (2.0*Sca*Da))
2195 {
2196 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2197 break;
2198 }
2199 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2200 break;
2201 }
2202 case PlusCompositeOp:
2203 {
2204 pixel=QuantumRange*(Sca+Dca);
2205 break;
2206 }
2207 case SaturateCompositeOp:
2208 {
2209 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2210 {
2211 pixel=Dc;
2212 break;
2213 }
2214 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2215 {
2216 pixel=Sc;
2217 break;
2218 }
2219 CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2220 &hue,&chroma,&luma);
2221 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2222 &sans,&chroma,&sans);
2223 HCLComposite(hue,chroma,luma,&red,&green,&blue);
2224 switch (channel)
2225 {
2226 case RedPixelChannel: pixel=red; break;
2227 case GreenPixelChannel: pixel=green; break;
2228 case BluePixelChannel: pixel=blue; break;
2229 default: pixel=Dc; break;
2230 }
2231 break;
2232 }
2233 case ScreenCompositeOp:
2234 {
2235 /*
2236 Screen: a negated multiply:
2237
2238 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2239 */
2240 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2241 break;
2242 }
2243 case SoftLightCompositeOp:
2244 {
2245 if ((2.0*Sca) < Sa)
2246 {
2247 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-DcaDa))+
2248 Sca*(1.0-Da)+Dca*(1.0-Sa));
2249 break;
2250 }
2251 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2252 {
2253 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*DcaDa*
2254 (4.0*DcaDa+1.0)*(DcaDa-1.0)+7.0*DcaDa)+Sca*(1.0-Da)+
2255 Dca*(1.0-Sa));
2256 break;
2257 }
2258 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow(DcaDa,0.5)-
2259 DcaDa)+Sca*(1.0-Da)+Dca*(1.0-Sa));
2260 break;
2261 }
2262 case StereoCompositeOp:
2263 {
2264 if (channel == RedPixelChannel)
2265 pixel=(MagickRealType) GetPixelRed(source_image,p);
2266 break;
2267 }
2268 case ThresholdCompositeOp:
2269 {
2270 MagickRealType
2271 delta;
2272
2273 delta=Sc-Dc;
2274 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2275 {
2276 pixel=gamma*Dc;
2277 break;
2278 }
2279 pixel=gamma*(Dc+delta*amount);
2280 break;
2281 }
2282 case VividLightCompositeOp:
2283 {
2284 /*
2285 VividLight: A Photoshop 7 composition method. See
2286 http://www.simplefilter.de/en/basics/mixmods.html.
2287
2288 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2289 */
2290 if ((fabs((double) Sa) < MagickEpsilon) ||
2291 (fabs((double) (Sca-Sa)) < MagickEpsilon))
2292 {
2293 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2294 break;
2295 }
2296 if ((2.0*Sca) <= Sa)
2297 {
2298 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)*
2299 PerceptibleReciprocal(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2300 break;
2301 }
2302 pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(2.0*
2303 (Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2304 break;
2305 }
2306 case XorCompositeOp:
2307 {
2308 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2309 break;
2310 }
2311 default:
2312 {
2313 pixel=Sc;
2314 break;
2315 }
2316 }
2317 q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2318 }
2319 p+=GetPixelChannels(source_image);
2320 channels=GetPixelChannels(source_image);
2321 if (p >= (pixels+channels*source_image->columns))
2322 p=pixels;
2323 q+=GetPixelChannels(image);
2324 }
2325 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2326 status=MagickFalse;
2327 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2328 {
2329 MagickBooleanType
2330 proceed;
2331
2332 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2333 #pragma omp atomic
2334 #endif
2335 progress++;
2336 proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
2337 if (proceed == MagickFalse)
2338 status=MagickFalse;
2339 }
2340 }
2341 source_view=DestroyCacheView(source_view);
2342 image_view=DestroyCacheView(image_view);
2343 if (canvas_image != (Image * ) NULL)
2344 canvas_image=DestroyImage(canvas_image);
2345 else
2346 source_image=DestroyImage(source_image);
2347 return(status);
2348 }
2349
2350 /*
2351 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2352 % %
2353 % %
2354 % %
2355 % T e x t u r e I m a g e %
2356 % %
2357 % %
2358 % %
2359 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2360 %
2361 % TextureImage() repeatedly tiles the texture image across and down the image
2362 % canvas.
2363 %
2364 % The format of the TextureImage method is:
2365 %
2366 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2367 % ExceptionInfo *exception)
2368 %
2369 % A description of each parameter follows:
2370 %
2371 % o image: the image.
2372 %
2373 % o texture_image: This image is the texture to layer on the background.
2374 %
2375 */
2376 MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2377 ExceptionInfo *exception)
2378 {
2379 #define TextureImageTag "Texture/Image"
2380
2381 CacheView
2382 *image_view,
2383 *texture_view;
2384
2385 Image
2386 *texture_image;
2387
2388 MagickBooleanType
2389 status;
2390
2391 ssize_t
2392 y;
2393
2394 assert(image != (Image *) NULL);
2395 if (image->debug != MagickFalse)
2396 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2397 assert(image->signature == MagickCoreSignature);
2398 if (texture == (const Image *) NULL)
2399 return(MagickFalse);
2400 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2401 return(MagickFalse);
2402 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2403 if (texture_image == (const Image *) NULL)
2404 return(MagickFalse);
2405 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2406 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2407 exception);
2408 status=MagickTrue;
2409 if ((image->compose != CopyCompositeOp) &&
2410 ((image->compose != OverCompositeOp) ||
2411 (image->alpha_trait != UndefinedPixelTrait) ||
2412 (texture_image->alpha_trait != UndefinedPixelTrait)))
2413 {
2414 /*
2415 Tile texture onto the image background.
2416 */
2417 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2418 {
2419 register ssize_t
2420 x;
2421
2422 if (status == MagickFalse)
2423 continue;
2424 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2425 {
2426 MagickBooleanType
2427 thread_status;
2428
2429 thread_status=CompositeImage(image,texture_image,image->compose,
2430 MagickTrue,x+texture_image->tile_offset.x,y+
2431 texture_image->tile_offset.y,exception);
2432 if (thread_status == MagickFalse)
2433 {
2434 status=thread_status;
2435 break;
2436 }
2437 }
2438 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2439 {
2440 MagickBooleanType
2441 proceed;
2442
2443 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2444 image->rows);
2445 if (proceed == MagickFalse)
2446 status=MagickFalse;
2447 }
2448 }
2449 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2450 image->rows,image->rows);
2451 texture_image=DestroyImage(texture_image);
2452 return(status);
2453 }
2454 /*
2455 Tile texture onto the image background (optimized).
2456 */
2457 status=MagickTrue;
2458 texture_view=AcquireVirtualCacheView(texture_image,exception);
2459 image_view=AcquireAuthenticCacheView(image,exception);
2460 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2461 #pragma omp parallel for schedule(static) shared(status) \
2462 magick_number_threads(texture_image,image,image->rows,1)
2463 #endif
2464 for (y=0; y < (ssize_t) image->rows; y++)
2465 {
2466 MagickBooleanType
2467 sync;
2468
2469 register const Quantum
2470 *p,
2471 *pixels;
2472
2473 register ssize_t
2474 x;
2475
2476 register Quantum
2477 *q;
2478
2479 size_t
2480 width;
2481
2482 if (status == MagickFalse)
2483 continue;
2484 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2485 (y+texture_image->tile_offset.y) % texture_image->rows,
2486 texture_image->columns,1,exception);
2487 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2488 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2489 {
2490 status=MagickFalse;
2491 continue;
2492 }
2493 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2494 {
2495 register ssize_t
2496 j;
2497
2498 p=pixels;
2499 width=texture_image->columns;
2500 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2501 width=image->columns-x;
2502 for (j=0; j < (ssize_t) width; j++)
2503 {
2504 register ssize_t
2505 i;
2506
2507 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2508 {
2509 PixelChannel channel = GetPixelChannelChannel(texture_image,i);
2510 PixelTrait traits = GetPixelChannelTraits(image,channel);
2511 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2512 channel);
2513 if ((traits == UndefinedPixelTrait) ||
2514 (texture_traits == UndefinedPixelTrait))
2515 continue;
2516 SetPixelChannel(image,channel,p[i],q);
2517 }
2518 p+=GetPixelChannels(texture_image);
2519 q+=GetPixelChannels(image);
2520 }
2521 }
2522 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2523 if (sync == MagickFalse)
2524 status=MagickFalse;
2525 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2526 {
2527 MagickBooleanType
2528 proceed;
2529
2530 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2531 image->rows);
2532 if (proceed == MagickFalse)
2533 status=MagickFalse;
2534 }
2535 }
2536 texture_view=DestroyCacheView(texture_view);
2537 image_view=DestroyCacheView(image_view);
2538 texture_image=DestroyImage(texture_image);
2539 return(status);
2540 }
2541