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