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