1 /*
2 % Copyright (C) 2003 - 2018 GraphicsMagick Group
3 % Copyright (C) 2002 ImageMagick Studio
4 %
5 % This program is covered by multiple licenses, which are described in
6 % Copyright.txt. You should have received a copy of Copyright.txt with this
7 % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
8 %
9 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10 %                                                                             %
11 %                                                                             %
12 %                                                                             %
13 %        CCCC   OOO  M   M  PPPP    OOO   SSSSS  IIIII  TTTTT  EEEEE          %
14 %       C      O   O MM MM  P   P  O   O  SS       I      T    E              %
15 %       C      O   O M M M  PPPP   O   O   SSS     I      T    EEE            %
16 %       C      O   O M   M  P      O   O     SS    I      T    E              %
17 %        CCCC   OOO  M   M  P       OOO   SSSSS  IIIII    T    EEEEE          %
18 %                                                                             %
19 %                                                                             %
20 %                   GraphicsMagick Image Composition Methods                  %
21 %                                                                             %
22 %                                                                             %
23 %                              Software Design                                %
24 %                                John Cristy                                  %
25 %                                 July 1992                                   %
26 %                            Re-design/Re-write                               %
27 %                              Bob Friesenhahn                                %
28 %                                  2008                                       %
29 %                                                                             %
30 %                                                                             %
31 %                                                                             %
32 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
33 %
34 %
35 %
36 */
37 
38 /*
39   Include declarations.
40 */
41 #include "magick/studio.h"
42 #include "magick/alpha_composite.h"
43 #include "magick/composite.h"
44 #include "magick/enum_strings.h"
45 #include "magick/gem.h"
46 #include "magick/pixel_cache.h"
47 #include "magick/pixel_iterator.h"
48 #include "magick/utility.h"
49 
50 
51 /*
52   Structure to pass any necessary options to composition callbacks.
53 */
54 #if 0
55 typedef struct _CompositeOptions_t
56 {
57   /* Composition operator */
58   /* CompositeOperator compose; */
59 
60   /* ModulateComposite */
61   double            percent_brightness;
62 
63   /* ThresholdComposite */
64   double            amount;
65   double            threshold;
66 } CompositeOptions_t;
67 #endif
68 
69 
70 /*
71   Build a PixelPacket representing the canvas pixel.
72 */
73 static inline void
PrepareDestinationPacket(PixelPacket * destination,const PixelPacket * restrict update_pixels,const Image * restrict update_image,const IndexPacket * restrict update_indexes,const long i)74 PrepareDestinationPacket(PixelPacket *destination,
75                          const PixelPacket * restrict update_pixels,
76                          const Image * restrict update_image,
77                          const IndexPacket * restrict update_indexes,
78                          const long i)
79 {
80   *destination=update_pixels[i];
81   if (!update_image->matte)
82     destination->opacity=OpaqueOpacity;
83   else
84     if (update_image->colorspace == CMYKColorspace)
85       destination->opacity=update_indexes[i];
86 }
87 
88 
89 /*
90   Build a PixelPacket representing the update pixel.
91 */
92 static inline void
PrepareSourcePacket(PixelPacket * source,const PixelPacket * restrict source_pixels,const Image * restrict source_image,const IndexPacket * restrict source_indexes,const long i)93 PrepareSourcePacket(PixelPacket *source,
94                     const PixelPacket * restrict source_pixels,
95                     const Image * restrict source_image,
96                     const IndexPacket * restrict source_indexes,
97                     const long i)
98 {
99   *source=source_pixels[i];
100   if (!source_image->matte)
101     source->opacity=OpaqueOpacity;
102   else
103     if (source_image->colorspace == CMYKColorspace)
104       source->opacity=source_indexes[i];
105 }
106 
107 
108 /*
109   Apply composition updates to the canvas image.
110 */
111 static inline void
ApplyPacketUpdates(PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const Image * restrict update_image,const PixelPacket * restrict composite,const long i)112 ApplyPacketUpdates(PixelPacket * restrict update_pixels,
113                    IndexPacket * restrict update_indexes,
114                    const Image * restrict update_image,
115                    const PixelPacket * restrict composite,
116                    const long i
117                    )
118 {
119   if (update_image->colorspace != CMYKColorspace)
120     {
121       /*
122         RGB stores opacity in 'opacity'.
123       */
124       update_pixels[i]=*composite;
125     }
126   else
127     {
128       /*
129         CMYK(A) stores K in 'opacity' and A in the indexes.
130       */
131       update_pixels[i].red=composite->red;
132       update_pixels[i].green=composite->green;
133       update_pixels[i].blue=composite->blue;
134       update_indexes[i]=composite->opacity; /* opacity */
135     }
136 }
137 
138 
139 static MagickPassFail
OverCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)140 OverCompositePixels(void *mutable_data,                /* User provided mutable data */
141                     const void *immutable_data,        /* User provided immutable data */
142                     const Image * restrict source_image,         /* Source image */
143                     const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
144                     const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
145                     Image * restrict update_image,               /* Update image */
146                     PixelPacket * restrict update_pixels,        /* Pixel row in update image */
147                     IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
148                     const long npixels,                /* Number of pixels in row */
149                     ExceptionInfo *exception           /* Exception report */
150                     )
151 {
152   register long
153     i;
154 
155   PixelPacket
156     destination,
157     source;
158 
159   ARG_NOT_USED(mutable_data);
160   ARG_NOT_USED(immutable_data);
161   ARG_NOT_USED(exception);
162 
163   /*
164     The result will be the union of the two image shapes, with
165     opaque areas of change-image obscuring base-image in the
166     region of overlap.
167   */
168   for (i=0; i < npixels; i++)
169     {
170       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
171       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
172 
173       AlphaCompositePixel(&destination,&source,source.opacity,&destination,destination.opacity);
174 
175       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
176     }
177 
178   return MagickPass;
179 }
180 
181 
182 static MagickPassFail
InCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)183 InCompositePixels(void *mutable_data,                /* User provided mutable data */
184                   const void *immutable_data,        /* User provided immutable data */
185                   const Image * restrict source_image,         /* Source image */
186                   const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
187                   const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
188                   Image * restrict update_image,               /* Update image */
189                   PixelPacket * restrict update_pixels,        /* Pixel row in update image */
190                   IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
191                   const long npixels,                /* Number of pixels in row */
192                   ExceptionInfo *exception           /* Exception report */
193                   )
194 {
195   register long
196     i;
197 
198   PixelPacket
199     destination,
200     source;
201 
202   ARG_NOT_USED(mutable_data);
203   ARG_NOT_USED(immutable_data);
204   ARG_NOT_USED(exception);
205 
206   /*
207     The result is simply change-image cut by the shape of
208     base-image. None of the image data of base-image will be
209     in the result.
210   */
211   for (i=0; i < npixels; i++)
212     {
213       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
214       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
215 
216       if (source.opacity == TransparentOpacity)
217         {
218           destination=source;
219         }
220       else if (destination.opacity == TransparentOpacity)
221         {
222         }
223       else
224         {
225           double
226             opacity;
227 
228           opacity=(double)
229             (((double) MaxRGBDouble-source.opacity)*
230              (MaxRGBDouble-destination.opacity)/MaxRGBDouble);
231 
232           destination.red=(Quantum)
233             (((double) MaxRGBDouble-source.opacity)*
234              (MaxRGBDouble-destination.opacity)*source.red/MaxRGBDouble/opacity+0.5);
235 
236           destination.green=(Quantum)
237             (((double) MaxRGBDouble-source.opacity)*
238              (MaxRGBDouble-destination.opacity)*source.green/MaxRGBDouble/opacity+0.5);
239 
240           destination.blue=(Quantum)
241             (((double) MaxRGBDouble-source.opacity)*
242              (MaxRGBDouble-destination.opacity)*source.blue/MaxRGBDouble/opacity+0.5);
243 
244           destination.opacity=(Quantum) (MaxRGBDouble-opacity+0.5);
245         }
246 
247       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
248     }
249 
250   return MagickPass;
251 }
252 
253 
254 static MagickPassFail
OutCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)255 OutCompositePixels(void *mutable_data,                /* User provided mutable data */
256                    const void *immutable_data,        /* User provided immutable data */
257                    const Image * restrict source_image,         /* Source image */
258                    const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
259                    const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
260                    Image * restrict update_image,               /* Update image */
261                    PixelPacket * restrict update_pixels,        /* Pixel row in update image */
262                    IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
263                    const long npixels,                /* Number of pixels in row */
264                    ExceptionInfo *exception           /* Exception report */
265                    )
266 {
267   register long
268     i;
269 
270   PixelPacket
271     destination,
272     source;
273 
274   ARG_NOT_USED(mutable_data);
275   ARG_NOT_USED(immutable_data);
276   ARG_NOT_USED(exception);
277 
278   /*
279     The resulting image is change-image with the shape of
280     base-image cut out.
281   */
282   for (i=0; i < npixels; i++)
283     {
284       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
285       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
286 
287       if (source.opacity == TransparentOpacity)
288         {
289           destination=source;
290         }
291       else if (destination.opacity == OpaqueOpacity)
292         {
293           destination.opacity=TransparentOpacity;
294         }
295       else
296         {
297           double
298             opacity;
299 
300           opacity=(double)
301             (MaxRGBDouble-source.opacity)*destination.opacity/MaxRGBDouble;
302 
303           destination.red=(Quantum)
304             (((double) MaxRGBDouble-source.opacity)*
305              destination.opacity*source.red/MaxRGBDouble/opacity+0.5);
306 
307           destination.green=(Quantum)
308             (((double) MaxRGBDouble-source.opacity)*
309              destination.opacity*source.green/MaxRGBDouble/opacity+0.5);
310 
311           destination.blue=(Quantum)
312             (((double) MaxRGBDouble-source.opacity)*
313              destination.opacity*source.blue/MaxRGBDouble/opacity+0.5);
314 
315           destination.opacity=(Quantum) (MaxRGBDouble-opacity+0.5);
316         }
317 
318       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
319     }
320 
321   return MagickPass;
322 }
323 
324 static MagickPassFail
AtopCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)325 AtopCompositePixels(void *mutable_data,                /* User provided mutable data */
326                     const void *immutable_data,        /* User provided immutable data */
327                     const Image * restrict source_image,         /* Source image */
328                     const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
329                     const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
330                     Image * restrict update_image,               /* Update image */
331                     PixelPacket * restrict update_pixels,        /* Pixel row in update image */
332                     IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
333                     const long npixels,                /* Number of pixels in row */
334                     ExceptionInfo *exception           /* Exception report */
335                     )
336 {
337   register long
338     i;
339 
340   PixelPacket
341     destination,
342     source;
343 
344   ARG_NOT_USED(mutable_data);
345   ARG_NOT_USED(immutable_data);
346   ARG_NOT_USED(exception);
347 
348   /*
349     The result is the same shape as base-image, with
350     change-image obscuring base-image where the image shapes
351     overlap. Note this differs from over because the portion
352     of change-image outside base-image's shape does not appear
353     in the result.
354   */
355   for (i=0; i < npixels; i++)
356     {
357       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
358       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
359 
360       AtopCompositePixel(&destination,&destination,&source);
361 
362       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
363     }
364 
365   return MagickPass;
366 }
367 
368 
369 static MagickPassFail
XorCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)370 XorCompositePixels(void *mutable_data,                /* User provided mutable data */
371                    const void *immutable_data,        /* User provided immutable data */
372                    const Image * restrict source_image,         /* Source image */
373                    const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
374                    const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
375                    Image * restrict update_image,               /* Update image */
376                    PixelPacket * restrict update_pixels,        /* Pixel row in update image */
377                    IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
378                    const long npixels,                /* Number of pixels in row */
379                    ExceptionInfo *exception           /* Exception report */
380                    )
381 {
382   register long
383     i;
384 
385   PixelPacket
386     destination,
387     source;
388 
389   ARG_NOT_USED(mutable_data);
390   ARG_NOT_USED(immutable_data);
391   ARG_NOT_USED(exception);
392 
393   /*
394     The result is the image data from both change-image and
395     base-image that is outside the overlap region. The overlap
396     region will be blank.
397   */
398   for (i=0; i < npixels; i++)
399     {
400       double gamma;
401       double source_alpha;
402       double dest_alpha;
403       double composite;
404 
405       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
406       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
407 
408       source_alpha=(double) source.opacity/MaxRGBDouble;
409       dest_alpha=(double) destination.opacity/MaxRGBDouble;
410 
411       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
412         2.0*(1.0-source_alpha)*(1.0-dest_alpha);
413 
414       composite=MaxRGBDouble*(1.0-gamma);
415       destination.opacity=RoundDoubleToQuantum(composite);
416 
417       gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
418 
419       composite=((1.0-source_alpha)*source.red*dest_alpha+
420                  (1.0-dest_alpha)*destination.red*source_alpha)*gamma;
421       destination.red=RoundDoubleToQuantum(composite);
422 
423       composite=((1.0-source_alpha)*source.green*dest_alpha+
424                  (1.0-dest_alpha)*destination.green*source_alpha)*gamma;
425       destination.green=RoundDoubleToQuantum(composite);
426 
427       composite=((1.0-source_alpha)*source.blue*dest_alpha+
428                  (1.0-dest_alpha)*destination.blue*source_alpha)*gamma;
429       destination.blue=RoundDoubleToQuantum(composite);
430 
431       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
432     }
433 
434   return MagickPass;
435 }
436 
437 
438 static MagickPassFail
PlusCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)439 PlusCompositePixels(void *mutable_data,                /* User provided mutable data */
440                     const void *immutable_data,        /* User provided immutable data */
441                     const Image * restrict source_image,         /* Source image */
442                     const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
443                     const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
444                     Image * restrict update_image,               /* Update image */
445                     PixelPacket * restrict update_pixels,        /* Pixel row in update image */
446                     IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
447                     const long npixels,                /* Number of pixels in row */
448                     ExceptionInfo *exception           /* Exception report */
449                     )
450 {
451   register long
452     i;
453 
454   PixelPacket
455     destination,
456     source;
457 
458   ARG_NOT_USED(mutable_data);
459   ARG_NOT_USED(immutable_data);
460   ARG_NOT_USED(exception);
461 
462   /*
463     The result is just the sum of the image data. Output values are
464     cropped to MaxRGB (no overflow). This operation is independent of
465     the matte channels.
466   */
467   for (i=0; i < npixels; i++)
468     {
469       double
470         value;
471 
472       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
473       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
474 
475       value=((double) (MaxRGBDouble-source.opacity)*source.red+(double)
476                  (MaxRGBDouble-destination.opacity)*destination.red)/MaxRGBDouble;
477       destination.red=RoundDoubleToQuantum(value);
478 
479       value=((double) (MaxRGBDouble-source.opacity)*source.green+(double)
480                    (MaxRGBDouble-destination.opacity)*destination.green)/MaxRGBDouble;
481       destination.green=RoundDoubleToQuantum(value);
482 
483       value=((double) (MaxRGBDouble-source.opacity)*source.blue+(double)
484                   (MaxRGBDouble-destination.opacity)*destination.blue)/MaxRGBDouble;
485       destination.blue=RoundDoubleToQuantum(value);
486 
487       value=((double) (MaxRGBDouble-source.opacity)+
488                      (double) (MaxRGBDouble-destination.opacity))/MaxRGBDouble;
489       destination.opacity=MaxRGB-RoundDoubleToQuantum(value);
490 
491       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
492     }
493 
494   return MagickPass;
495 }
496 
497 
498 static MagickPassFail
MinusCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)499 MinusCompositePixels(void *mutable_data,                /* User provided mutable data */
500                      const void *immutable_data,        /* User provided immutable data */
501                      const Image * restrict source_image,         /* Source image */
502                      const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
503                      const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
504                      Image * restrict update_image,               /* Update image */
505                      PixelPacket * restrict update_pixels,        /* Pixel row in update image */
506                      IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
507                      const long npixels,                /* Number of pixels in row */
508                      ExceptionInfo *exception           /* Exception report */
509                      )
510 {
511   register long
512     i;
513 
514   PixelPacket
515     destination,
516     source;
517 
518   ARG_NOT_USED(mutable_data);
519   ARG_NOT_USED(immutable_data);
520   ARG_NOT_USED(exception);
521 
522   /*
523     The result of change-image - base-image, with underflow cropped to
524     zero. The matte channel is ignored (set to opaque, full coverage).
525   */
526   for (i=0; i < npixels; i++)
527     {
528       double
529         value;
530 
531       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
532       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
533 
534       value=((double) (MaxRGBDouble-destination.opacity)*destination.red-
535              (double) (MaxRGBDouble-source.opacity)*source.red)/MaxRGBDouble;
536       destination.red=RoundDoubleToQuantum(value);
537 
538       value=((double) (MaxRGBDouble-destination.opacity)*destination.green-
539              (double) (MaxRGBDouble-source.opacity)*source.green)/MaxRGBDouble;
540       destination.green=RoundDoubleToQuantum(value);
541 
542       value=((double) (MaxRGBDouble-destination.opacity)*destination.blue-
543              (double) (MaxRGBDouble-source.opacity)*source.blue)/MaxRGBDouble;
544       destination.blue=RoundDoubleToQuantum(value);
545 
546       value=((double) (MaxRGBDouble-destination.opacity)-
547              (double) (MaxRGBDouble-source.opacity))/MaxRGBDouble;
548       destination.opacity=MaxRGB-RoundDoubleToQuantum(value);
549 
550       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
551     }
552 
553   return MagickPass;
554 }
555 
556 
557 static MagickPassFail
AddCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)558 AddCompositePixels(void *mutable_data,                /* User provided mutable data */
559                    const void *immutable_data,        /* User provided immutable data */
560                    const Image * restrict source_image,         /* Source image */
561                    const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
562                    const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
563                    Image * restrict update_image,               /* Update image */
564                    PixelPacket * restrict update_pixels,        /* Pixel row in update image */
565                    IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
566                    const long npixels,                /* Number of pixels in row */
567                    ExceptionInfo *exception           /* Exception report */
568                    )
569 {
570   register long
571     i;
572 
573   PixelPacket
574     destination,
575     source;
576 
577   ARG_NOT_USED(mutable_data);
578   ARG_NOT_USED(immutable_data);
579   ARG_NOT_USED(exception);
580 
581   /*
582     The result of change-image + base-image, with overflow wrapping
583     around (mod MaxRGB+1).
584   */
585   for (i=0; i < npixels; i++)
586     {
587       double
588         value;
589 
590       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
591       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
592 
593       value=(double) source.red+destination.red;
594       if (value > MaxRGBDouble) value -= ((double) MaxRGBDouble+1.0);
595       destination.red=RoundDoubleToQuantum(value);
596 
597       value=(double) source.green+destination.green;
598       if (value > MaxRGBDouble) value -= ((double) MaxRGBDouble+1.0);
599       destination.green=RoundDoubleToQuantum(value);
600 
601       value=(double) source.blue+destination.blue;
602       if (value > MaxRGBDouble) value -= ((double) MaxRGBDouble+1.0);
603       destination.blue=RoundDoubleToQuantum(value);
604 
605       destination.opacity=OpaqueOpacity;
606       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
607     }
608 
609   return MagickPass;
610 }
611 
612 
613 static MagickPassFail
SubtractCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)614 SubtractCompositePixels(void *mutable_data,                /* User provided mutable data */
615                         const void *immutable_data,        /* User provided immutable data */
616                         const Image * restrict source_image,         /* Source image */
617                         const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
618                         const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
619                         Image * restrict update_image,               /* Update image */
620                         PixelPacket * restrict update_pixels,        /* Pixel row in update image */
621                         IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
622                         const long npixels,                /* Number of pixels in row */
623                         ExceptionInfo *exception           /* Exception report */
624                         )
625 {
626   register long
627     i;
628 
629   PixelPacket
630     destination,
631     source;
632 
633   ARG_NOT_USED(mutable_data);
634   ARG_NOT_USED(immutable_data);
635   ARG_NOT_USED(exception);
636 
637   /*
638     The result of change-image - base-image, with underflow wrapping
639     around (mod MaxRGB+1). The add and subtract operators can be used
640     to perform reversible transformations.
641   */
642   for (i=0; i < npixels; i++)
643     {
644       double
645         value;
646 
647       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
648       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
649 
650       value=(double) source.red-destination.red;
651       if (value < 0) value += ((double) MaxRGBDouble+1.0);
652       destination.red=RoundDoubleToQuantum(value);
653 
654       value=(double) source.green-destination.green;
655       if (value < 0) value += ((double) MaxRGBDouble+1.0);
656       destination.green=RoundDoubleToQuantum(value);
657 
658       value=(double) source.blue-destination.blue;
659       if (value < 0) value += ((double) MaxRGBDouble+1.0);
660       destination.blue=RoundDoubleToQuantum(value);
661 
662       destination.opacity=OpaqueOpacity;
663 
664       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
665     }
666 
667   return MagickPass;
668 }
669 
670 
671 static MagickPassFail
DifferenceCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)672 DifferenceCompositePixels(void *mutable_data,                /* User provided mutable data */
673                           const void *immutable_data,        /* User provided immutable data */
674                           const Image * restrict source_image,         /* Source image */
675                           const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
676                           const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
677                           Image * restrict update_image,               /* Update image */
678                           PixelPacket * restrict update_pixels,        /* Pixel row in update image */
679                           IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
680                           const long npixels,                /* Number of pixels in row */
681                           ExceptionInfo *exception           /* Exception report */
682                           )
683 {
684   register long
685     i;
686 
687   PixelPacket
688     destination,
689     source;
690 
691   ARG_NOT_USED(mutable_data);
692   ARG_NOT_USED(immutable_data);
693   ARG_NOT_USED(exception);
694 
695   /*
696     The result of abs(change-image - base-image). This is useful for
697     comparing two very similar images.
698   */
699   for (i=0; i < npixels; i++)
700     {
701       double gamma;
702       double source_alpha;
703       double dest_alpha;
704       double composite;
705 
706       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
707       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
708 
709       source_alpha=(double) source.opacity/MaxRGBDouble;
710       dest_alpha=(double) destination.opacity/MaxRGBDouble;
711 
712       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
713         (1.0-source_alpha)*(1.0-dest_alpha);
714       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
715 
716       composite=MaxRGBDouble*(1.0-gamma);
717       destination.opacity=RoundDoubleToQuantum(composite);
718 
719       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
720 
721       composite=(fabs((double)source.red - (double)destination.red)*
722                  (1.0-source_alpha)*(1.0-dest_alpha)+
723                  source.red*(1.0-source_alpha)*dest_alpha+
724                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
725       destination.red=RoundDoubleToQuantum(composite);
726 
727       composite=(fabs((double)source.green - (double)destination.green)*
728                  (1.0-source_alpha)*(1.0-dest_alpha)+
729                  source.green*(1.0-source_alpha)*dest_alpha+
730                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
731       destination.green=RoundDoubleToQuantum(composite);
732 
733       composite=(fabs((double)source.blue - (double)destination.blue)*
734                  (1.0-source_alpha)*(1.0-dest_alpha)+
735                  source.blue*(1.0-source_alpha)*dest_alpha+
736                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
737       destination.blue=RoundDoubleToQuantum(composite);
738 
739       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
740     }
741 
742   return MagickPass;
743 }
744 
745 
746 static MagickPassFail
MultiplyCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)747 MultiplyCompositePixels(void *mutable_data,                /* User provided mutable data */
748                         const void *immutable_data,        /* User provided immutable data */
749                         const Image * restrict source_image,         /* Source image */
750                         const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
751                         const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
752                         Image * restrict update_image,               /* Update image */
753                         PixelPacket * restrict update_pixels,        /* Pixel row in update image */
754                         IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
755                         const long npixels,                /* Number of pixels in row */
756                         ExceptionInfo *exception           /* Exception report */
757                         )
758 {
759   register long
760     i;
761 
762   PixelPacket
763     destination,
764     source;
765 
766   ARG_NOT_USED(mutable_data);
767   ARG_NOT_USED(immutable_data);
768   ARG_NOT_USED(exception);
769 
770   /*
771     The result of change-image * base-image. This is useful for the
772     creation of drop-shadows.
773   */
774 
775 
776   for (i=0; i < npixels; i++)
777     {
778       double gamma;
779       double source_alpha;
780       double dest_alpha;
781       double composite;
782 
783       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
784       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
785 
786       source_alpha=(double) source.opacity/MaxRGBDouble;
787       dest_alpha=(double) destination.opacity/MaxRGBDouble;
788 
789       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
790         (1.0-source_alpha)*(1.0-dest_alpha);
791       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
792 
793       composite=MaxRGBDouble*(1.0-gamma);
794       destination.opacity=RoundDoubleToQuantum(composite);
795 
796       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
797 
798       composite=(((double)source.red*(1.0-source_alpha)*
799                   (double)destination.red*(1.0-dest_alpha))/MaxRGBDouble+
800                  source.red*(1.0-source_alpha)*dest_alpha+
801                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
802       destination.red=RoundDoubleToQuantum(composite);
803 
804       composite=(((double)source.green*(1.0-source_alpha)*
805                   (double)destination.green*(1.0-dest_alpha))/MaxRGBDouble+
806                  source.green*(1.0-source_alpha)*dest_alpha+
807                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
808       destination.green=RoundDoubleToQuantum(composite);
809 
810       composite=(((double)source.blue*(1.0-source_alpha)*
811                   (double)destination.blue*(1.0-dest_alpha))/MaxRGBDouble+
812                  source.blue*(1.0-source_alpha)*dest_alpha+
813                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
814       destination.blue=RoundDoubleToQuantum(composite);
815 
816       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
817     }
818 
819   return MagickPass;
820 }
821 
822 
823 static MagickPassFail
BumpmapCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)824 BumpmapCompositePixels(void *mutable_data,                /* User provided mutable data */
825                        const void *immutable_data,        /* User provided immutable data */
826                        const Image * restrict source_image,         /* Source image */
827                        const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
828                        const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
829                        Image * restrict update_image,               /* Update image */
830                        PixelPacket * restrict update_pixels,        /* Pixel row in update image */
831                        IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
832                        const long npixels,                /* Number of pixels in row */
833                        ExceptionInfo *exception           /* Exception report */
834                        )
835 {
836   register long
837     i;
838 
839   PixelPacket
840     destination,
841     source;
842 
843   ARG_NOT_USED(mutable_data);
844   ARG_NOT_USED(immutable_data);
845   ARG_NOT_USED(exception);
846 
847   /*
848     The result base-image shaded by change-image.
849   */
850   for (i=0; i < npixels; i++)
851     {
852       double value;
853       double source_intensity;
854 
855       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
856       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
857 
858       source_intensity=(double) PixelIntensity(&source)/MaxRGBDouble;
859 
860       value=source_intensity*destination.red;
861       destination.red=RoundDoubleToQuantum(value);
862 
863       value=source_intensity*destination.green;
864       destination.green=RoundDoubleToQuantum(value);
865 
866       value=source_intensity*destination.blue;
867       destination.blue=RoundDoubleToQuantum(value);
868 
869       value=source_intensity*destination.opacity;
870       destination.opacity=RoundDoubleToQuantum(value);
871 
872       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
873     }
874 
875   return MagickPass;
876 }
877 
878 
879 
880 
881 static MagickPassFail
CopyCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)882 CopyCompositePixels(void *mutable_data,                /* User provided mutable data */
883                     const void *immutable_data,        /* User provided immutable data */
884                     const Image * restrict source_image,         /* Source image */
885                     const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
886                     const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
887                     Image * restrict update_image,               /* Update image */
888                     PixelPacket * restrict update_pixels,        /* Pixel row in update image */
889                     IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
890                     const long npixels,                /* Number of pixels in row */
891                     ExceptionInfo *exception           /* Exception report */
892                     )
893 {
894   ARG_NOT_USED(mutable_data);
895   ARG_NOT_USED(immutable_data);
896   ARG_NOT_USED(exception);
897 
898   /*
899     The resulting image is base-image replaced with change-image. Here
900     the matte information is ignored.
901   */
902   if ((update_image->colorspace == CMYKColorspace) &&
903       (update_image->matte))
904     {
905       if (source_image->matte)
906         {
907           (void) memcpy(update_pixels,source_pixels,npixels*sizeof(PixelPacket));
908           (void) memcpy(update_indexes,source_indexes,npixels*sizeof(IndexPacket));
909         }
910       else
911         {
912           (void) memcpy(update_pixels,source_pixels,npixels*sizeof(PixelPacket));
913           (void) memset(update_indexes,OpaqueOpacity,npixels*sizeof(IndexPacket));
914         }
915     }
916   else
917     {
918       (void) memcpy(update_pixels,source_pixels,npixels*sizeof(PixelPacket));
919     }
920 
921   return MagickPass;
922 }
923 
924 static MagickPassFail
CopyRedCompositePixels(void * mutable_data,const void * immutable_data,const Image * source_image,const PixelPacket * source_pixels,const IndexPacket * source_indexes,Image * update_image,PixelPacket * update_pixels,IndexPacket * update_indexes,const long npixels,ExceptionInfo * exception)925 CopyRedCompositePixels(void *mutable_data,                /* User provided mutable data */
926                        const void *immutable_data,        /* User provided immutable data */
927                        const Image *source_image,         /* Source image */
928                        const PixelPacket *source_pixels,  /* Pixel row in source image */
929                        const IndexPacket *source_indexes, /* Pixel row indexes in source image */
930                        Image *update_image,               /* Update image */
931                        PixelPacket *update_pixels,        /* Pixel row in update image */
932                        IndexPacket *update_indexes,       /* Pixel row indexes in update image */
933                        const long npixels,                /* Number of pixels in row */
934                        ExceptionInfo *exception           /* Exception report */
935                        )
936 {
937   register long
938     i;
939 
940   ARG_NOT_USED(mutable_data);
941   ARG_NOT_USED(immutable_data);
942   ARG_NOT_USED(source_image);
943   ARG_NOT_USED(source_indexes);
944   ARG_NOT_USED(update_image);
945   ARG_NOT_USED(update_indexes);
946   ARG_NOT_USED(exception);
947 
948   /*
949     The resulting image is the red channel in base-image replaced with
950     the red channel in change-image. The other channels are copied
951     untouched.
952   */
953   for (i=0; i < npixels; i++)
954     {
955       update_pixels[i].red = source_pixels[i].red;
956     }
957 
958   return MagickPass;
959 }
960 
961 static MagickPassFail
CopyGreenCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)962 CopyGreenCompositePixels(void *mutable_data,                /* User provided mutable data */
963                          const void *immutable_data,        /* User provided immutable data */
964                          const Image * restrict source_image,         /* Source image */
965                          const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
966                          const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
967                          Image * restrict update_image,               /* Update image */
968                          PixelPacket * restrict update_pixels,        /* Pixel row in update image */
969                          IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
970                          const long npixels,                /* Number of pixels in row */
971                          ExceptionInfo *exception           /* Exception report */
972                          )
973 {
974   register long
975     i;
976 
977   ARG_NOT_USED(mutable_data);
978   ARG_NOT_USED(immutable_data);
979   ARG_NOT_USED(source_image);
980   ARG_NOT_USED(source_indexes);
981   ARG_NOT_USED(update_image);
982   ARG_NOT_USED(update_indexes);
983   ARG_NOT_USED(exception);
984 
985   /*
986     The resulting image is the green channel in base-image replaced
987     with the green channel in change-image. The other channels are
988     copied untouched.
989   */
990   for (i=0; i < npixels; i++)
991     {
992       update_pixels[i].green = source_pixels[i].green;
993     }
994 
995   return MagickPass;
996 }
997 
998 
999 static MagickPassFail
CopyBlueCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1000 CopyBlueCompositePixels(void *mutable_data,                /* User provided mutable data */
1001                         const void *immutable_data,        /* User provided immutable data */
1002                         const Image * restrict source_image,         /* Source image */
1003                         const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1004                         const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1005                         Image * restrict update_image,               /* Update image */
1006                         PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1007                         IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1008                         const long npixels,                /* Number of pixels in row */
1009                         ExceptionInfo *exception           /* Exception report */
1010                         )
1011 {
1012   register long
1013     i;
1014 
1015   ARG_NOT_USED(mutable_data);
1016   ARG_NOT_USED(immutable_data);
1017   ARG_NOT_USED(source_image);
1018   ARG_NOT_USED(source_indexes);
1019   ARG_NOT_USED(update_image);
1020   ARG_NOT_USED(update_indexes);
1021   ARG_NOT_USED(exception);
1022 
1023   /*
1024     The resulting image is the blue channel in base-image replaced
1025     with the blue channel in change-image. The other channels are
1026     copied untouched.
1027   */
1028   for (i=0; i < npixels; i++)
1029     {
1030       update_pixels[i].blue = source_pixels[i].blue;
1031     }
1032 
1033   return MagickPass;
1034 }
1035 
1036 static MagickPassFail
CopyOpacityCompositePixels(void * mutable_data,const void * immutable_data,const Image * source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1037 CopyOpacityCompositePixels(void *mutable_data,                /* User provided mutable data */
1038                            const void *immutable_data,        /* User provided immutable data */
1039                            const Image *source_image,         /* Source image */
1040                            const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1041                            const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1042                            Image *update_image,               /* Update image */
1043                            PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1044                            IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1045                            const long npixels,                /* Number of pixels in row */
1046                            ExceptionInfo *exception           /* Exception report */
1047                            )
1048 {
1049   register long
1050     i;
1051 
1052   ARG_NOT_USED(mutable_data);
1053   ARG_NOT_USED(immutable_data);
1054   ARG_NOT_USED(exception);
1055 
1056   /*
1057     The resulting image is the opacity channel in base-image replaced
1058     with the opacity channel in change-image.  The other channels are
1059     copied untouched.
1060   */
1061   if (update_image->colorspace == CMYKColorspace)
1062     {
1063       if (!source_image->matte)
1064         {
1065           for (i=0; i < npixels; i++)
1066             {
1067               update_indexes[i] =
1068                 (Quantum) (MaxRGB-PixelIntensityToQuantum(&source_pixels[i]));
1069             }
1070         }
1071       else
1072         {
1073           for (i=0; i < npixels; i++)
1074             {
1075               update_indexes[i] = source_indexes[i];
1076             }
1077         }
1078     }
1079   else
1080     {
1081       if (!source_image->matte)
1082         {
1083           for (i=0; i < npixels; i++)
1084             {
1085               update_pixels[i].opacity =
1086                 (Quantum) (MaxRGB-PixelIntensityToQuantum(&source_pixels[i]));
1087             }
1088         }
1089       else
1090         {
1091           for (i=0; i < npixels; i++)
1092             {
1093               update_pixels[i].opacity = source_pixels[i].opacity;
1094             }
1095         }
1096     }
1097 
1098   return MagickPass;
1099 }
1100 
1101 static MagickPassFail
ClearCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1102 ClearCompositePixels(void *mutable_data,                /* User provided mutable data */
1103                      const void *immutable_data,        /* User provided immutable data */
1104                      const Image * restrict source_image,         /* Source image */
1105                      const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1106                      const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1107                      Image * restrict update_image,               /* Update image */
1108                      PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1109                      IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1110                      const long npixels,                /* Number of pixels in row */
1111                      ExceptionInfo *exception           /* Exception report */
1112                      )
1113 {
1114   register long
1115     i;
1116 
1117   ARG_NOT_USED(mutable_data);
1118   ARG_NOT_USED(immutable_data);
1119   ARG_NOT_USED(source_image);
1120   ARG_NOT_USED(source_pixels);
1121   ARG_NOT_USED(source_indexes);
1122   ARG_NOT_USED(exception);
1123 
1124   /*
1125     Set destination pixels to transparent.
1126   */
1127   if (update_image->colorspace == CMYKColorspace)
1128     {
1129       update_image->matte=MagickTrue;
1130       for (i=0; i < npixels; i++)
1131         {
1132           update_indexes[i] = TransparentOpacity;
1133         }
1134     }
1135   else
1136     {
1137       for (i=0; i < npixels; i++)
1138         {
1139           update_pixels[i].opacity = TransparentOpacity;
1140         }
1141     }
1142 
1143   return MagickPass;
1144 }
1145 
1146 
1147 
1148 static MagickPassFail
DissolveCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1149 DissolveCompositePixels(void *mutable_data,                /* User provided mutable data */
1150                         const void *immutable_data,        /* User provided immutable data */
1151                         const Image * restrict source_image,         /* Source image */
1152                         const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1153                         const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1154                         Image * restrict update_image,               /* Update image */
1155                         PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1156                         IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1157                         const long npixels,                /* Number of pixels in row */
1158                         ExceptionInfo *exception           /* Exception report */
1159                         )
1160 {
1161   register long
1162     i;
1163 
1164   PixelPacket
1165     destination,
1166     source;
1167 
1168   ARG_NOT_USED(mutable_data);
1169   ARG_NOT_USED(immutable_data);
1170   ARG_NOT_USED(exception);
1171 
1172   for (i=0; i < npixels; i++)
1173     {
1174       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1175       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1176 
1177       destination.red=(Quantum)
1178         (((double) source.opacity*source.red+
1179           (MaxRGBDouble-source.opacity)*destination.red)/MaxRGBDouble+0.5);
1180       destination.green=(Quantum)
1181         (((double) source.opacity*source.green+
1182           (MaxRGBDouble-source.opacity)*destination.green)/MaxRGBDouble+0.5);
1183       destination.blue=(Quantum)
1184         (((double) source.opacity*source.blue+
1185           (MaxRGBDouble-source.opacity)*destination.blue)/MaxRGBDouble+0.5);
1186       destination.opacity=OpaqueOpacity;
1187 
1188       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1189     }
1190 
1191   return MagickPass;
1192 }
1193 
1194 
1195 static MagickPassFail
ModulateCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1196 ModulateCompositePixels(void *mutable_data,                /* User provided mutable data */
1197                         const void *immutable_data,        /* User provided immutable data */
1198                         const Image * restrict source_image,         /* Source image */
1199                         const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1200                         const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1201                         Image * restrict update_image,               /* Update image */
1202                         PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1203                         IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1204                         const long npixels,                /* Number of pixels in row */
1205                         ExceptionInfo *exception           /* Exception report */
1206                         )
1207 {
1208   const CompositeOptions_t
1209     *options = (const CompositeOptions_t *) immutable_data;
1210 
1211   const double
1212     percent_brightness = options->percent_brightness;
1213 
1214 
1215   double
1216     midpoint;
1217 
1218   register long
1219     i;
1220 
1221   PixelPacket
1222     destination,
1223     source;
1224 
1225   ARG_NOT_USED(mutable_data);
1226   ARG_NOT_USED(exception);
1227 
1228   midpoint=((double) MaxRGB+1.0)/2;
1229   for (i=0; i < npixels; i++)
1230     {
1231       double
1232         offset;
1233 
1234       double
1235         brightness,
1236         hue,
1237         saturation;
1238 
1239       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1240       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1241 
1242       offset=(long) (PixelIntensityToQuantum(&source)-midpoint);
1243       if (offset == 0)
1244         break;
1245       TransformHSL(destination.red,destination.green,destination.blue,
1246                    &hue,&saturation,&brightness);
1247       brightness+=(percent_brightness*offset)/midpoint;
1248       if (brightness < 0.0)
1249         brightness=0.0;
1250       else
1251         if (brightness > 1.0)
1252           brightness=1.0;
1253       HSLTransform(hue,saturation,brightness,&destination.red,
1254                    &destination.green,&destination.blue);
1255 
1256       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1257     }
1258 
1259   return MagickPass;
1260 }
1261 
1262 
1263 static MagickPassFail
ThresholdCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1264 ThresholdCompositePixels(void *mutable_data,                /* User provided mutable data */
1265                          const void *immutable_data,        /* User provided immutable data */
1266                          const Image * restrict source_image,         /* Source image */
1267                          const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1268                          const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1269                          Image * restrict update_image,               /* Update image */
1270                          PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1271                          IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1272                          const long npixels,                /* Number of pixels in row */
1273                          ExceptionInfo *exception           /* Exception report */
1274                          )
1275 {
1276   const CompositeOptions_t
1277     *options = (const CompositeOptions_t *) immutable_data;
1278 
1279   const double
1280     amount = options->amount,
1281     threshold = options->threshold;
1282 
1283   register long
1284     i;
1285 
1286   PixelPacket
1287     destination,
1288     source;
1289 
1290   ARG_NOT_USED(mutable_data);
1291   ARG_NOT_USED(exception);
1292 
1293   for (i=0; i < npixels; i++)
1294     {
1295       double
1296         value;
1297 
1298       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1299       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1300 
1301       value=destination.red-(double) source.red;
1302       if (fabs(2.0*value) < threshold)
1303         value=destination.red;
1304       else
1305         value=destination.red+(value*amount);
1306       destination.red=RoundDoubleToQuantum(value);
1307 
1308       value=destination.green-(double) source.green;
1309       if (fabs(2.0*value) < threshold)
1310         value=destination.green;
1311       else
1312         value=destination.green+(value*amount);
1313       destination.green=RoundDoubleToQuantum(value);
1314 
1315       value=destination.blue-(double) source.blue;
1316       if (fabs(2.0*value) < threshold)
1317         value=destination.blue;
1318       else
1319         value=destination.blue+(value*amount);
1320       destination.blue=RoundDoubleToQuantum(value);
1321 
1322       value=destination.opacity-(double) source.opacity;
1323       if (fabs(2.0*value) < threshold)
1324         value=destination.opacity;
1325       else
1326         value=destination.opacity+(value*amount);
1327       destination.opacity=RoundDoubleToQuantum(value);
1328 
1329       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1330     }
1331 
1332   return MagickPass;
1333 }
1334 
1335 
1336 static MagickPassFail
DarkenCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1337 DarkenCompositePixels(void *mutable_data,                /* User provided mutable data */
1338                       const void *immutable_data,        /* User provided immutable data */
1339                       const Image * restrict source_image,         /* Source image */
1340                       const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1341                       const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1342                       Image * restrict update_image,               /* Update image */
1343                       PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1344                       IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1345                       const long npixels,                /* Number of pixels in row */
1346                       ExceptionInfo *exception           /* Exception report */
1347                       )
1348 {
1349   register long
1350     i;
1351 
1352   PixelPacket
1353     destination,
1354     source;
1355 
1356   ARG_NOT_USED(mutable_data);
1357   ARG_NOT_USED(immutable_data);
1358   ARG_NOT_USED(exception);
1359 
1360   for (i=0; i < npixels; i++)
1361     {
1362       double gamma;
1363       double source_alpha;
1364       double dest_alpha;
1365       double composite;
1366 
1367       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1368       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1369 
1370       source_alpha=(double) source.opacity/MaxRGBDouble;
1371       dest_alpha=(double) destination.opacity/MaxRGBDouble;
1372 
1373       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
1374         (1.0-source_alpha)*(1.0-dest_alpha);
1375       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
1376 
1377       composite=MaxRGBDouble*(1.0-gamma);
1378       destination.opacity=RoundDoubleToQuantum(composite);
1379 
1380       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
1381 
1382       composite=(MagickFmin((double)source.red,(double)destination.red)*
1383                  (1.0-source_alpha)*(1.0-dest_alpha)+
1384                  source.red*(1.0-source_alpha)*dest_alpha+
1385                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
1386       destination.red=RoundDoubleToQuantum(composite);
1387 
1388       composite=(MagickFmin((double)source.green,(double)destination.green)*
1389                  (1.0-source_alpha)*(1.0-dest_alpha)+
1390                  source.green*(1.0-source_alpha)*dest_alpha+
1391                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
1392       destination.green=RoundDoubleToQuantum(composite);
1393 
1394       composite=(MagickFmin((double)source.blue,(double)destination.blue)*
1395                  (1.0-source_alpha)*(1.0-dest_alpha)+
1396                  source.blue*(1.0-source_alpha)*dest_alpha+
1397                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
1398       destination.blue=RoundDoubleToQuantum(composite);
1399 
1400       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1401     }
1402 
1403   return MagickPass;
1404 }
1405 
1406 
1407 static MagickPassFail
LightenCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1408 LightenCompositePixels(void *mutable_data,                /* User provided mutable data */
1409                        const void *immutable_data,        /* User provided immutable data */
1410                        const Image * restrict source_image,         /* Source image */
1411                        const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1412                        const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1413                        Image * restrict update_image,               /* Update image */
1414                        PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1415                        IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1416                        const long npixels,                /* Number of pixels in row */
1417                        ExceptionInfo *exception           /* Exception report */
1418                        )
1419 {
1420   register long
1421     i;
1422 
1423   PixelPacket
1424     destination,
1425     source;
1426 
1427   ARG_NOT_USED(mutable_data);
1428   ARG_NOT_USED(immutable_data);
1429   ARG_NOT_USED(exception);
1430 
1431   for (i=0; i < npixels; i++)
1432     {
1433       double gamma;
1434       double source_alpha;
1435       double dest_alpha;
1436       double composite;
1437 
1438       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1439       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1440 
1441       source_alpha=(double) source.opacity/MaxRGBDouble;
1442       dest_alpha=(double) destination.opacity/MaxRGBDouble;
1443 
1444       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
1445         (1.0-source_alpha)*(1.0-dest_alpha);
1446       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
1447 
1448       composite=MaxRGBDouble*(1.0-gamma);
1449       destination.opacity=RoundDoubleToQuantum(composite);
1450 
1451       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
1452 
1453       composite=(MagickFmax((double)source.red,(double)destination.red)*
1454                  (1.0-source_alpha)*(1.0-dest_alpha)+
1455                  source.red*(1.0-source_alpha)*dest_alpha+
1456                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
1457       destination.red=RoundDoubleToQuantum(composite);
1458 
1459       composite=(MagickFmax((double)source.green,(double)destination.green)*
1460                  (1.0-source_alpha)*(1.0-dest_alpha)+
1461                  source.green*(1.0-source_alpha)*dest_alpha+
1462                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
1463       destination.green=RoundDoubleToQuantum(composite);
1464 
1465       composite=(MagickFmax((double)source.blue,(double)destination.blue)*
1466                  (1.0-source_alpha)*(1.0-dest_alpha)+
1467                  source.blue*(1.0-source_alpha)*dest_alpha+
1468                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
1469       destination.blue=RoundDoubleToQuantum(composite);
1470 
1471       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1472     }
1473 
1474   return MagickPass;
1475 }
1476 
1477 
1478 static MagickPassFail
HueCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1479 HueCompositePixels(void *mutable_data,                /* User provided mutable data */
1480                    const void *immutable_data,        /* User provided immutable data */
1481                    const Image * restrict source_image,         /* Source image */
1482                    const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1483                    const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1484                    Image * restrict update_image,               /* Update image */
1485                    PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1486                    IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1487                    const long npixels,                /* Number of pixels in row */
1488                    ExceptionInfo *exception           /* Exception report */
1489                    )
1490 {
1491   register long
1492     i;
1493 
1494   PixelPacket
1495     destination,
1496     source;
1497 
1498   ARG_NOT_USED(mutable_data);
1499   ARG_NOT_USED(immutable_data);
1500   ARG_NOT_USED(exception);
1501 
1502   for (i=0; i < npixels; i++)
1503     {
1504       double
1505         brightness,
1506         hue,
1507         saturation,
1508         sans;
1509 
1510       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1511       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1512 
1513       if (source.opacity == TransparentOpacity)
1514         {
1515         }
1516       else if (destination.opacity == TransparentOpacity)
1517         {
1518           destination=source;
1519         }
1520       else
1521         {
1522           TransformHSL(destination.red,destination.green,destination.blue,
1523                        &hue,&saturation,&brightness);
1524           TransformHSL(source.red,source.green,source.blue,&hue,&sans,&sans);
1525           HSLTransform(hue,saturation,brightness,&destination.red,
1526                        &destination.green,&destination.blue);
1527           if (source.opacity < destination.opacity)
1528             destination.opacity=source.opacity;
1529         }
1530 
1531       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1532     }
1533 
1534   return MagickPass;
1535 }
1536 
1537 
1538 static MagickPassFail
SaturateCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1539 SaturateCompositePixels(void *mutable_data,                /* User provided mutable data */
1540                         const void *immutable_data,        /* User provided immutable data */
1541                         const Image * restrict source_image,         /* Source image */
1542                         const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1543                         const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1544                         Image * restrict update_image,               /* Update image */
1545                         PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1546                         IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1547                         const long npixels,                /* Number of pixels in row */
1548                         ExceptionInfo *exception           /* Exception report */
1549                         )
1550 {
1551   register long
1552     i;
1553 
1554   PixelPacket
1555     destination,
1556     source;
1557 
1558   ARG_NOT_USED(mutable_data);
1559   ARG_NOT_USED(immutable_data);
1560   ARG_NOT_USED(exception);
1561 
1562   for (i=0; i < npixels; i++)
1563     {
1564       double
1565         brightness,
1566         hue,
1567         saturation,
1568         sans;
1569 
1570       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1571       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1572 
1573       if (source.opacity == TransparentOpacity)
1574         {
1575         }
1576       else if (destination.opacity == TransparentOpacity)
1577         {
1578           destination=source;
1579         }
1580       else
1581         {
1582           TransformHSL(destination.red,destination.green,destination.blue,
1583                        &hue,&saturation,&brightness);
1584           TransformHSL(source.red,source.green,source.blue,&sans,&saturation,
1585                        &sans);
1586           HSLTransform(hue,saturation,brightness,&destination.red,
1587                        &destination.green,&destination.blue);
1588           if (source.opacity < destination.opacity)
1589             destination.opacity=source.opacity;
1590         }
1591 
1592       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1593     }
1594 
1595   return MagickPass;
1596 }
1597 
1598 
1599 static MagickPassFail
ColorizeCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1600 ColorizeCompositePixels(void *mutable_data,                /* User provided mutable data */
1601                         const void *immutable_data,        /* User provided immutable data */
1602                         const Image * restrict source_image,         /* Source image */
1603                         const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1604                         const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1605                         Image * restrict update_image,               /* Update image */
1606                         PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1607                         IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1608                         const long npixels,                /* Number of pixels in row */
1609                         ExceptionInfo *exception           /* Exception report */
1610                         )
1611 {
1612   register long
1613     i;
1614 
1615   PixelPacket
1616     destination,
1617     source;
1618 
1619   ARG_NOT_USED(mutable_data);
1620   ARG_NOT_USED(immutable_data);
1621   ARG_NOT_USED(exception);
1622 
1623   for (i=0; i < npixels; i++)
1624     {
1625       double
1626         brightness,
1627         hue,
1628         saturation,
1629         sans;
1630 
1631       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1632       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1633 
1634       if (source.opacity == TransparentOpacity)
1635         {
1636         }
1637       else if (destination.opacity == TransparentOpacity)
1638         {
1639           destination=source;
1640         }
1641       else
1642         {
1643           TransformHSL(destination.red,destination.green,destination.blue,
1644                        &sans,&sans,&brightness);
1645           TransformHSL(source.red,source.green,source.blue,&hue,&saturation,
1646                        &sans);
1647           HSLTransform(hue,saturation,brightness,&destination.red,
1648                        &destination.green,&destination.blue);
1649           if (source.opacity < destination.opacity)
1650             destination.opacity=source.opacity;
1651         }
1652 
1653       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1654     }
1655 
1656   return MagickPass;
1657 }
1658 
1659 
1660 static MagickPassFail
LuminizeCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1661 LuminizeCompositePixels(void *mutable_data,                /* User provided mutable data */
1662                         const void *immutable_data,        /* User provided immutable data */
1663                         const Image * restrict source_image,         /* Source image */
1664                         const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1665                         const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1666                         Image * restrict update_image,               /* Update image */
1667                         PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1668                         IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1669                         const long npixels,                /* Number of pixels in row */
1670                         ExceptionInfo *exception           /* Exception report */
1671                         )
1672 {
1673   register long
1674     i;
1675 
1676   PixelPacket
1677     destination,
1678     source;
1679 
1680   ARG_NOT_USED(mutable_data);
1681   ARG_NOT_USED(immutable_data);
1682   ARG_NOT_USED(exception);
1683 
1684   for (i=0; i < npixels; i++)
1685     {
1686       double
1687         brightness,
1688         hue,
1689         saturation,
1690         sans;
1691 
1692       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1693       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1694 
1695       if (source.opacity == TransparentOpacity)
1696         {
1697         }
1698       else if (destination.opacity == TransparentOpacity)
1699         {
1700           destination=source;
1701         }
1702       else
1703         {
1704           TransformHSL(destination.red,destination.green,destination.blue,
1705                        &hue,&saturation,&brightness);
1706           TransformHSL(source.red,source.green,source.blue,&sans,&sans,
1707                        &brightness);
1708           HSLTransform(hue,saturation,brightness,&destination.red,
1709                        &destination.green,&destination.blue);
1710           if (source.opacity < destination.opacity)
1711             destination.opacity=source.opacity;
1712         }
1713 
1714       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1715     }
1716 
1717   return MagickPass;
1718 }
1719 
1720 
1721 static MagickPassFail
ScreenCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1722 ScreenCompositePixels(void *mutable_data,                /* User provided mutable data */
1723                       const void *immutable_data,        /* User provided immutable data */
1724                       const Image * restrict source_image,         /* Source image */
1725                       const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1726                       const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1727                       Image * restrict update_image,               /* Update image */
1728                       PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1729                       IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1730                       const long npixels,                /* Number of pixels in row */
1731                       ExceptionInfo *exception           /* Exception report */
1732                       )
1733 {
1734   register long
1735     i;
1736 
1737   PixelPacket
1738     destination,
1739     source;
1740 
1741   ARG_NOT_USED(mutable_data);
1742   ARG_NOT_USED(immutable_data);
1743   ARG_NOT_USED(exception);
1744 
1745   /*
1746     Input colors are complimented and multiplied, then the product is complimented again.
1747   */
1748 
1749 
1750   for (i=0; i < npixels; i++)
1751     {
1752       double gamma;
1753       double source_alpha;
1754       double dest_alpha;
1755       double composite;
1756 
1757       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1758       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1759 
1760       source_alpha=(double) source.opacity/MaxRGBDouble;
1761       dest_alpha=(double) destination.opacity/MaxRGBDouble;
1762 
1763       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
1764         (1.0-source_alpha)*(1.0-dest_alpha);
1765       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
1766 
1767       composite=MaxRGBDouble*(1.0-gamma);
1768       destination.opacity=RoundDoubleToQuantum(composite);
1769 
1770       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
1771 
1772       composite=(((double)source.red+(double)destination.red-
1773                   ((double)source.red*(double)destination.red)/MaxRGBDouble)*
1774                  (1.0-source_alpha)*(1.0-dest_alpha)+
1775                  source.red*(1.0-source_alpha)*dest_alpha+
1776                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
1777       destination.red=RoundDoubleToQuantum(composite);
1778 
1779       composite=(((double)source.green+(double)destination.green-
1780                   ((double)source.green*(double)destination.green)/MaxRGBDouble)*
1781                  (1.0-source_alpha)*(1.0-dest_alpha)+
1782                  source.green*(1.0-source_alpha)*dest_alpha+
1783                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
1784       destination.green=RoundDoubleToQuantum(composite);
1785 
1786       composite=(((double)source.blue+(double)destination.blue-
1787                   ((double)source.blue*(double)destination.blue)/MaxRGBDouble)*
1788                  (1.0-source_alpha)*(1.0-dest_alpha)+
1789                  source.blue*(1.0-source_alpha)*dest_alpha+
1790                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
1791       destination.blue=RoundDoubleToQuantum(composite);
1792 
1793       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1794     }
1795 
1796   return MagickPass;
1797 }
1798 
1799 
1800 static MagickPassFail
OverlayCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1801 OverlayCompositePixels(void *mutable_data,               /* User provided mutable data */
1802                        const void *immutable_data,        /* User provided immutable data */
1803                        const Image * restrict source_image,         /* Source image */
1804                        const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1805                        const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1806                        Image * restrict update_image,               /* Update image */
1807                        PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1808                        IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1809                        const long npixels,                /* Number of pixels in row */
1810                        ExceptionInfo *exception           /* Exception report */
1811                        )
1812 {
1813   register long
1814     i;
1815 
1816   PixelPacket
1817     destination,
1818     source;
1819 
1820   ARG_NOT_USED(mutable_data);
1821   ARG_NOT_USED(immutable_data);
1822   ARG_NOT_USED(exception);
1823 
1824   /*
1825     Multiplies or screens, depending on the destination colour.
1826     Overlay(a,b) = HardLight(b,a)
1827   */
1828 
1829   for (i=0; i < npixels; i++)
1830     {
1831       double gamma;
1832       double source_alpha;
1833       double dest_alpha;
1834       double composite;
1835 
1836       double
1837         value;
1838 
1839       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1840       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1841 
1842       source_alpha=(double) source.opacity/MaxRGBDouble;
1843       dest_alpha=(double) destination.opacity/MaxRGBDouble;
1844 
1845       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
1846             (1.0-source_alpha)*(1.0-dest_alpha);
1847       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
1848 
1849       composite=MaxRGBDouble*(1.0-gamma);
1850       destination.opacity=RoundDoubleToQuantum(composite);
1851 
1852       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
1853 
1854       if(destination.red < (0.5*MaxRGBDouble))
1855         value=((double) source.red*destination.red*2.0)/MaxRGBDouble;
1856       else
1857         value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.red/MaxRGBDouble) *
1858                                (1.0-(double)destination.red/MaxRGBDouble));
1859       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
1860                  source.red*(1.0-source_alpha)*dest_alpha+
1861                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
1862       destination.red=RoundDoubleToQuantum(composite);
1863 
1864       if(destination.green < (0.5*MaxRGBDouble))
1865         value=((double) source.green*destination.green*2.0)/MaxRGBDouble;
1866       else
1867         value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.green/MaxRGBDouble) *
1868                                (1.0-(double)destination.green/MaxRGBDouble));
1869       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
1870                  source.green*(1.0-source_alpha)*dest_alpha+
1871                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
1872       destination.green=RoundDoubleToQuantum(composite);
1873 
1874       if(destination.blue < (0.5*MaxRGBDouble))
1875         value=((double) source.blue*destination.blue*2.0)/MaxRGBDouble;
1876       else
1877         value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.blue/MaxRGBDouble) *
1878                                (1.0-(double)destination.blue/MaxRGBDouble));
1879       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
1880                  source.blue*(1.0-source_alpha)*dest_alpha+
1881                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
1882       destination.blue=RoundDoubleToQuantum(composite);
1883 
1884       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1885     }
1886 
1887   return MagickPass;
1888 }
1889 
1890 
1891 static MagickPassFail
CopyBlackCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1892 CopyBlackCompositePixels(void *mutable_data,                /* User provided mutable data */
1893                          const void *immutable_data,        /* User provided immutable data */
1894                          const Image * restrict source_image,         /* Source image */
1895                          const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1896                          const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1897                          Image * restrict update_image,               /* Update image */
1898                          PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1899                          IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1900                          const long npixels,                /* Number of pixels in row */
1901                          ExceptionInfo *exception           /* Exception report */
1902                          )
1903 {
1904   register long
1905     i;
1906 
1907   ARG_NOT_USED(mutable_data);
1908   ARG_NOT_USED(immutable_data);
1909   ARG_NOT_USED(source_indexes);
1910   ARG_NOT_USED(update_indexes);
1911   ARG_NOT_USED(exception);
1912 
1913   /*
1914     Copy the CMYK Black (K) channel into the image.
1915   */
1916   if ((update_image->colorspace == CMYKColorspace) &&
1917       (source_image->colorspace == CMYKColorspace))
1918     {
1919       for (i=0; i < npixels; i++)
1920         {
1921           update_pixels[i].opacity=source_pixels[i].opacity;
1922         }
1923     }
1924   else
1925     {
1926       for (i=0; i < npixels; i++)
1927         {
1928           update_pixels[i].opacity = PixelIntensityToQuantum(&source_pixels[i]);
1929         }
1930     }
1931 
1932   return MagickPass;
1933 }
1934 
1935 
1936 static MagickPassFail
DivideCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)1937 DivideCompositePixels(void *mutable_data,                /* User provided mutable data */
1938                       const void *immutable_data,        /* User provided immutable data */
1939                       const Image * restrict source_image,         /* Source image */
1940                       const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
1941                       const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
1942                       Image * restrict update_image,               /* Update image */
1943                       PixelPacket * restrict update_pixels,        /* Pixel row in update image */
1944                       IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
1945                       const long npixels,                /* Number of pixels in row */
1946                       ExceptionInfo *exception           /* Exception report */
1947                       )
1948 {
1949   register long
1950     i;
1951 
1952   PixelPacket
1953     destination,
1954     source;
1955 
1956   ARG_NOT_USED(mutable_data);
1957   ARG_NOT_USED(immutable_data);
1958   ARG_NOT_USED(exception);
1959 
1960   /*
1961     The result of change-image / base-image. This is useful for
1962     improving the readability of text on unevenly illuminated photos.
1963     (by using a gaussian blurred copy of change-image as base-image)
1964   */
1965 
1966   for (i=0; i < npixels; i++)
1967     {
1968       double
1969         composite,
1970         divisor;
1971 
1972       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1973       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1974 
1975       /* Avoid division by zero error, use value near zero instead */
1976       divisor=((destination.red != 0.0) ? destination.red : 1.0/MaxRGBDouble);
1977       composite=((double) (source.red*MaxRGBDouble)/divisor);
1978       destination.red=RoundDoubleToQuantum(composite);
1979 
1980       divisor=((destination.green != 0.0) ? destination.green : 1.0/MaxRGBDouble);
1981       composite=((double) (source.green*MaxRGBDouble)/divisor);
1982       destination.green=RoundDoubleToQuantum(composite);
1983 
1984       divisor=((destination.blue != 0.0) ? destination.blue : 1.0/MaxRGBDouble);
1985       composite=((double) (source.blue*MaxRGBDouble)/divisor);
1986       destination.blue=RoundDoubleToQuantum(composite);
1987 
1988       divisor=((destination.opacity != 0.0) ? destination.opacity : 1.0/MaxRGBDouble);
1989       composite=((double) (source.opacity*MaxRGBDouble)/divisor);
1990       destination.opacity=RoundDoubleToQuantum(composite);
1991 
1992       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1993     }
1994 
1995   return MagickPass;
1996 }
1997 
1998 
1999 static MagickPassFail
HardLightCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2000 HardLightCompositePixels(void *mutable_data,               /* User provided mutable data */
2001                          const void *immutable_data,        /* User provided immutable data */
2002                          const Image * restrict source_image,         /* Source image */
2003                          const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2004                          const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2005                          Image * restrict update_image,               /* Update image */
2006                          PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2007                          IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2008                          const long npixels,                /* Number of pixels in row */
2009                          ExceptionInfo *exception           /* Exception report */
2010                          )
2011 {
2012   register long
2013     i;
2014 
2015   PixelPacket
2016     destination,
2017     source;
2018 
2019   ARG_NOT_USED(mutable_data);
2020   ARG_NOT_USED(immutable_data);
2021   ARG_NOT_USED(exception);
2022 
2023   /*
2024     The result of base-image gets lighting effects by change-image.
2025   */
2026 
2027   for (i=0; i < npixels; i++)
2028     {
2029       double gamma;
2030       double source_alpha;
2031       double dest_alpha;
2032       double composite;
2033 
2034       double
2035         value;
2036 
2037       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2038       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2039 
2040       source_alpha=(double) source.opacity/MaxRGBDouble;
2041       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2042 
2043       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2044             (1.0-source_alpha)*(1.0-dest_alpha);
2045       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2046 
2047       composite=MaxRGBDouble*(1.0-gamma);
2048       destination.opacity=RoundDoubleToQuantum(composite);
2049 
2050       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2051 
2052       if(source.red <= (0.5*MaxRGBDouble))
2053         value=((double) source.red*destination.red*2.0)/MaxRGBDouble;
2054       else
2055         value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.red/MaxRGBDouble) *
2056                                (1.0-(double)destination.red/MaxRGBDouble));
2057       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2058                  source.red*(1.0-source_alpha)*dest_alpha+
2059                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2060       destination.red=RoundDoubleToQuantum(composite);
2061 
2062       if(source.green <= (0.5*MaxRGBDouble))
2063         value=((double) source.green*destination.green*2.0)/MaxRGBDouble;
2064       else
2065         value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.green/MaxRGBDouble) *
2066                                (1.0-(double)destination.green/MaxRGBDouble));
2067       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2068                  source.green*(1.0-source_alpha)*dest_alpha+
2069                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2070       destination.green=RoundDoubleToQuantum(composite);
2071 
2072       if(source.blue <= (0.5*MaxRGBDouble))
2073         value=((double) source.blue*destination.blue*2.0)/MaxRGBDouble;
2074       else
2075         value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.blue/MaxRGBDouble) *
2076                                (1.0-(double)destination.blue/MaxRGBDouble));
2077       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2078                  source.blue*(1.0-source_alpha)*dest_alpha+
2079                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2080       destination.blue=RoundDoubleToQuantum(composite);
2081 
2082       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2083     }
2084 
2085   return MagickPass;
2086 }
2087 
2088 
2089 static MagickPassFail
ExclusionCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2090 ExclusionCompositePixels(void *mutable_data,                /* User provided mutable data */
2091                          const void *immutable_data,        /* User provided immutable data */
2092                          const Image * restrict source_image,         /* Source image */
2093                          const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2094                          const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2095                          Image * restrict update_image,               /* Update image */
2096                          PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2097                          IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2098                          const long npixels,                /* Number of pixels in row */
2099                          ExceptionInfo *exception           /* Exception report */
2100                          )
2101 {
2102   register long
2103     i;
2104 
2105   PixelPacket
2106     destination,
2107     source;
2108 
2109   ARG_NOT_USED(mutable_data);
2110   ARG_NOT_USED(immutable_data);
2111   ARG_NOT_USED(exception);
2112 
2113   /*
2114    A similar effect to Difference, but lower in contrast.
2115   */
2116 
2117 
2118   for (i=0; i < npixels; i++)
2119     {
2120       double gamma;
2121       double source_alpha;
2122       double dest_alpha;
2123       double composite;
2124 
2125       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2126       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2127 
2128       source_alpha=(double) source.opacity/MaxRGBDouble;
2129       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2130 
2131       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2132         (1.0-source_alpha)*(1.0-dest_alpha);
2133       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2134 
2135       composite=MaxRGBDouble*(1.0-gamma);
2136       destination.opacity=RoundDoubleToQuantum(composite);
2137 
2138       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2139 
2140       composite=(((double)source.red+(double)destination.red-
2141                   2*((double)source.red*(double)destination.red)/MaxRGBDouble)*
2142                  (1.0-source_alpha)*(1.0-dest_alpha)+
2143                  source.red*(1.0-source_alpha)*dest_alpha+
2144                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2145       destination.red=RoundDoubleToQuantum(composite);
2146 
2147       composite=(((double)source.green+(double)destination.green-
2148                   2*((double)source.green*(double)destination.green)/MaxRGBDouble)*
2149                  (1.0-source_alpha)*(1.0-dest_alpha)+
2150                  source.green*(1.0-source_alpha)*dest_alpha+
2151                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2152       destination.green=RoundDoubleToQuantum(composite);
2153 
2154       composite=(((double)source.blue+(double)destination.blue-
2155                   2*((double)source.blue*(double)destination.blue)/MaxRGBDouble)*
2156                  (1.0-source_alpha)*(1.0-dest_alpha)+
2157                  source.blue*(1.0-source_alpha)*dest_alpha+
2158                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2159       destination.blue=RoundDoubleToQuantum(composite);
2160 
2161       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2162     }
2163 
2164   return MagickPass;
2165 }
2166 
2167 
2168 static MagickPassFail
ColorDodgeCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2169 ColorDodgeCompositePixels(void *mutable_data,               /* User provided mutable data */
2170                           const void *immutable_data,        /* User provided immutable data */
2171                           const Image * restrict source_image,         /* Source image */
2172                           const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2173                           const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2174                           Image * restrict update_image,               /* Update image */
2175                           PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2176                           IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2177                           const long npixels,                /* Number of pixels in row */
2178                           ExceptionInfo *exception           /* Exception report */
2179                           )
2180 {
2181   register long
2182     i;
2183 
2184   PixelPacket
2185     destination,
2186     source;
2187 
2188   ARG_NOT_USED(mutable_data);
2189   ARG_NOT_USED(immutable_data);
2190   ARG_NOT_USED(exception);
2191 
2192   /*
2193     Brightens the destination color by an amount depending on the source color
2194   */
2195 
2196   for (i=0; i < npixels; i++)
2197     {
2198       double gamma;
2199       double source_alpha;
2200       double dest_alpha;
2201       double composite;
2202 
2203       double
2204         value;
2205 
2206       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2207       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2208 
2209       source_alpha=(double) source.opacity/MaxRGBDouble;
2210       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2211 
2212       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2213             (1.0-source_alpha)*(1.0-dest_alpha);
2214       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2215 
2216       composite=MaxRGBDouble*(1.0-gamma);
2217       destination.opacity=RoundDoubleToQuantum(composite);
2218 
2219       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2220 
2221       if(source.red == MaxRGB)
2222         value = MaxRGBDouble;
2223       else
2224         value=MagickFmin(MaxRGBDouble,(double)destination.red/(1.0-(double) source.red/MaxRGBDouble));
2225       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2226                  source.red*(1.0-source_alpha)*dest_alpha+
2227                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2228       destination.red=RoundDoubleToQuantum(composite);
2229 
2230       if(source.green == MaxRGB)
2231         value = MaxRGBDouble;
2232       else
2233         value=MagickFmin(MaxRGBDouble,(double)destination.green/(1.0-(double) source.green/MaxRGBDouble));
2234       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2235                  source.green*(1.0-source_alpha)*dest_alpha+
2236                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2237       destination.green=RoundDoubleToQuantum(composite);
2238 
2239       if(source.blue == MaxRGB)
2240         value = MaxRGBDouble;
2241       else
2242         value=MagickFmin(MaxRGBDouble,(double)destination.blue/(1.0-(double) source.blue/MaxRGBDouble));
2243       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2244                  source.blue*(1.0-source_alpha)*dest_alpha+
2245                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2246       destination.blue=RoundDoubleToQuantum(composite);
2247 
2248       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2249     }
2250 
2251   return MagickPass;
2252 }
2253 
2254 
2255 static MagickPassFail
ColorBurnCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2256 ColorBurnCompositePixels(void *mutable_data,               /* User provided mutable data */
2257                          const void *immutable_data,        /* User provided immutable data */
2258                          const Image * restrict source_image,         /* Source image */
2259                          const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2260                          const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2261                          Image * restrict update_image,               /* Update image */
2262                          PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2263                          IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2264                          const long npixels,                /* Number of pixels in row */
2265                          ExceptionInfo *exception           /* Exception report */
2266                          )
2267 {
2268   register long
2269     i;
2270 
2271   PixelPacket
2272     destination,
2273     source;
2274 
2275   ARG_NOT_USED(mutable_data);
2276   ARG_NOT_USED(immutable_data);
2277   ARG_NOT_USED(exception);
2278 
2279   /*
2280     Darkens the destination color by an amount depending on the source color
2281   */
2282 
2283   for (i=0; i < npixels; i++)
2284     {
2285       double gamma;
2286       double source_alpha;
2287       double dest_alpha;
2288       double composite;
2289 
2290       double
2291         value;
2292 
2293       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2294       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2295 
2296       source_alpha=(double) source.opacity/MaxRGBDouble;
2297       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2298 
2299       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2300             (1.0-source_alpha)*(1.0-dest_alpha);
2301       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2302 
2303       composite=MaxRGBDouble*(1.0-gamma);
2304       destination.opacity=RoundDoubleToQuantum(composite);
2305 
2306       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2307 
2308       if(source.red == 0)
2309         value=0;
2310       else
2311         value = MaxRGBDouble-MagickFmin(MaxRGBDouble,(MaxRGBDouble-(double)destination.red)/((double) source.red/MaxRGBDouble));
2312       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2313                  source.red*(1.0-source_alpha)*dest_alpha+
2314                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2315       destination.red=RoundDoubleToQuantum(composite);
2316 
2317       if(source.green == 0)
2318         value=0;
2319       else
2320         value = MaxRGBDouble-MagickFmin(MaxRGBDouble,(MaxRGBDouble-(double)destination.green)/((double) source.green/MaxRGBDouble));
2321       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2322                  source.green*(1.0-source_alpha)*dest_alpha+
2323                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2324       destination.green=RoundDoubleToQuantum(composite);
2325 
2326       if(source.blue == 0)
2327         value=0;
2328       else
2329         value = MaxRGBDouble-MagickFmin(MaxRGBDouble,(MaxRGBDouble-(double)destination.blue)/((double) source.blue/MaxRGBDouble));
2330       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2331                  source.blue*(1.0-source_alpha)*dest_alpha+
2332                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2333       destination.blue=RoundDoubleToQuantum(composite);
2334 
2335       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2336     }
2337 
2338   return MagickPass;
2339 }
2340 
2341 
2342 static MagickPassFail
SoftLightCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2343 SoftLightCompositePixels(void *mutable_data,               /* User provided mutable data */
2344                          const void *immutable_data,        /* User provided immutable data */
2345                          const Image * restrict source_image,         /* Source image */
2346                          const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2347                          const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2348                          Image * restrict update_image,               /* Update image */
2349                          PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2350                          IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2351                          const long npixels,                /* Number of pixels in row */
2352                          ExceptionInfo *exception           /* Exception report */
2353                          )
2354 {
2355   register long
2356     i;
2357 
2358   PixelPacket
2359     destination,
2360     source;
2361 
2362   ARG_NOT_USED(mutable_data);
2363   ARG_NOT_USED(immutable_data);
2364   ARG_NOT_USED(exception);
2365 
2366   /*
2367     Darkens or lightens, depending on the source color
2368   */
2369 
2370   for (i=0; i < npixels; i++)
2371     {
2372       double gamma;
2373       double source_alpha;
2374       double dest_alpha;
2375       double composite;
2376       double ramp;
2377 
2378       double
2379         value;
2380 
2381       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2382       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2383 
2384       source_alpha=(double) source.opacity/MaxRGBDouble;
2385       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2386 
2387       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2388             (1.0-source_alpha)*(1.0-dest_alpha);
2389       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2390 
2391       composite=MaxRGBDouble*(1.0-gamma);
2392       destination.opacity=RoundDoubleToQuantum(composite);
2393 
2394       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2395 
2396 
2397       if(source.red <= (0.5*MaxRGBDouble))
2398         value=destination.red*(1.0 - (1.0-(double)destination.red/MaxRGBDouble)*(1.0-2.0*(double)source.red/MaxRGBDouble));
2399       else
2400       {
2401         if(destination.red <= (0.25*MaxRGBDouble))
2402           ramp = ((16.0*((double)destination.red/MaxRGBDouble)-12.0)*((double)destination.red/MaxRGBDouble)+4.0)*(double)destination.red/MaxRGBDouble;
2403         else
2404           ramp = sqrt((double)destination.red/MaxRGBDouble);
2405         value=destination.red + ((2.0*source.red)-MaxRGBDouble)*(ramp-(double)destination.red/MaxRGBDouble);
2406       }
2407       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2408                  source.red*(1.0-source_alpha)*dest_alpha+
2409                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2410       destination.red=RoundDoubleToQuantum(composite);
2411 
2412       if(source.green <= (0.5*MaxRGBDouble))
2413         value=destination.green*(1.0 - (1.0-(double)destination.green/MaxRGBDouble)*(1.0-2.0*(double)source.green/MaxRGBDouble));
2414       else
2415       {
2416         if(destination.green <= (0.25*MaxRGBDouble))
2417           ramp = ((16.0*((double)destination.green/MaxRGBDouble)-12.0)*((double)destination.green/MaxRGBDouble)+4.0)*(double)destination.green/MaxRGBDouble;
2418         else
2419           ramp = sqrt((double)destination.green/MaxRGBDouble);
2420         value=destination.green + ((2.0*source.green)-MaxRGBDouble)*(ramp-(double)destination.green/MaxRGBDouble);
2421       }
2422       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2423                  source.green*(1.0-source_alpha)*dest_alpha+
2424                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2425       destination.green=RoundDoubleToQuantum(composite);
2426 
2427       if(source.blue <= (0.5*MaxRGBDouble))
2428         value=destination.blue*(1.0 - (1.0-(double)destination.blue/MaxRGBDouble)*(1.0-2.0*(double)source.blue/MaxRGBDouble));
2429       else
2430       {
2431         if(destination.blue <= (0.25*MaxRGBDouble))
2432           ramp = ((16.0*((double)destination.blue/MaxRGBDouble)-12.0)*((double)destination.blue/MaxRGBDouble)+4.0)*(double)destination.blue/MaxRGBDouble;
2433         else
2434           ramp = sqrt((double)destination.blue/MaxRGBDouble);
2435         value=destination.blue + ((2.0*source.blue)-MaxRGBDouble)*(ramp-(double)destination.blue/MaxRGBDouble);
2436       }
2437       composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2438                  source.blue*(1.0-source_alpha)*dest_alpha+
2439                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2440       destination.blue=RoundDoubleToQuantum(composite);
2441 
2442       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2443     }
2444 
2445   return MagickPass;
2446 }
2447 
2448 
2449 static MagickPassFail
LinearBurnCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2450 LinearBurnCompositePixels(void *mutable_data,                /* User provided mutable data */
2451                           const void *immutable_data,        /* User provided immutable data */
2452                           const Image * restrict source_image,         /* Source image */
2453                           const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2454                           const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2455                           Image * restrict update_image,               /* Update image */
2456                           PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2457                           IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2458                           const long npixels,                /* Number of pixels in row */
2459                           ExceptionInfo *exception           /* Exception report */
2460                           )
2461 {
2462   register long
2463     i;
2464 
2465   PixelPacket
2466     destination,
2467     source;
2468 
2469   ARG_NOT_USED(mutable_data);
2470   ARG_NOT_USED(immutable_data);
2471   ARG_NOT_USED(exception);
2472 
2473   /*
2474     Inverts the sum of the inverted images
2475   */
2476 
2477 
2478   for (i=0; i < npixels; i++)
2479     {
2480       double gamma;
2481       double source_alpha;
2482       double dest_alpha;
2483       double composite;
2484       double value;
2485 
2486       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2487       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2488 
2489       source_alpha=(double) source.opacity/MaxRGBDouble;
2490       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2491 
2492       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2493         (1.0-source_alpha)*(1.0-dest_alpha);
2494       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2495 
2496       composite=MaxRGBDouble*(1.0-gamma);
2497       destination.opacity=RoundDoubleToQuantum(composite);
2498 
2499       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2500 
2501       value = MagickFmax(0.0,(double)source.red+(double)destination.red-MaxRGBDouble);
2502       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2503                  source.red*(1.0-source_alpha)*dest_alpha+
2504                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2505       destination.red=RoundDoubleToQuantum(composite);
2506 
2507       value = MagickFmax(0.0,(double)source.green+(double)destination.green-MaxRGBDouble);
2508       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2509                  source.green*(1.0-source_alpha)*dest_alpha+
2510                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2511       destination.green=RoundDoubleToQuantum(composite);
2512 
2513       value = MagickFmax(0.0,(double)source.blue+(double)destination.blue-MaxRGBDouble);
2514       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2515                  source.blue*(1.0-source_alpha)*dest_alpha+
2516                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2517       destination.blue=RoundDoubleToQuantum(composite);
2518 
2519       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2520     }
2521 
2522   return MagickPass;
2523 }
2524 
2525 
2526 static MagickPassFail
LinearDodgeCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2527 LinearDodgeCompositePixels(void *mutable_data,                /* User provided mutable data */
2528                            const void *immutable_data,        /* User provided immutable data */
2529                            const Image * restrict source_image,         /* Source image */
2530                            const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2531                            const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2532                            Image * restrict update_image,               /* Update image */
2533                            PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2534                            IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2535                            const long npixels,                /* Number of pixels in row */
2536                            ExceptionInfo *exception           /* Exception report */
2537                            )
2538 {
2539   register long
2540     i;
2541 
2542   PixelPacket
2543     destination,
2544     source;
2545 
2546   ARG_NOT_USED(mutable_data);
2547   ARG_NOT_USED(immutable_data);
2548   ARG_NOT_USED(exception);
2549 
2550   /*
2551     A simple alpha-blended sum of the images
2552   */
2553 
2554 
2555   for (i=0; i < npixels; i++)
2556     {
2557       double gamma;
2558       double source_alpha;
2559       double dest_alpha;
2560       double composite;
2561       double value;
2562 
2563       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2564       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2565 
2566       source_alpha=(double) source.opacity/MaxRGBDouble;
2567       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2568 
2569       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2570         (1.0-source_alpha)*(1.0-dest_alpha);
2571       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2572 
2573       composite=MaxRGBDouble*(1.0-gamma);
2574       destination.opacity=RoundDoubleToQuantum(composite);
2575 
2576       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2577 
2578       value = MagickFmin(MaxRGBDouble,(double)source.red+(double)destination.red);
2579       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2580                  source.red*(1.0-source_alpha)*dest_alpha+
2581                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2582       destination.red=RoundDoubleToQuantum(composite);
2583 
2584       value = MagickFmin(MaxRGBDouble,(double)source.green+(double)destination.green);
2585       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2586                  source.green*(1.0-source_alpha)*dest_alpha+
2587                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2588       destination.green=RoundDoubleToQuantum(composite);
2589 
2590       value = MagickFmin(MaxRGBDouble,(double)source.blue+(double)destination.blue);
2591       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2592                  source.blue*(1.0-source_alpha)*dest_alpha+
2593                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2594       destination.blue=RoundDoubleToQuantum(composite);
2595 
2596       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2597     }
2598 
2599   return MagickPass;
2600 }
2601 
2602 
2603 static MagickPassFail
LinearLightCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2604 LinearLightCompositePixels(void *mutable_data,                /* User provided mutable data */
2605                            const void *immutable_data,        /* User provided immutable data */
2606                            const Image * restrict source_image,         /* Source image */
2607                            const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2608                            const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2609                            Image * restrict update_image,               /* Update image */
2610                            PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2611                            IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2612                            const long npixels,                /* Number of pixels in row */
2613                            ExceptionInfo *exception           /* Exception report */
2614                            )
2615 {
2616   register long
2617     i;
2618 
2619   PixelPacket
2620     destination,
2621     source;
2622 
2623   ARG_NOT_USED(mutable_data);
2624   ARG_NOT_USED(immutable_data);
2625   ARG_NOT_USED(exception);
2626 
2627   /*
2628     Acts like LinearDodge (sum) for bright source pixels, LinearBurn (inverted sum) for dark source pixels
2629   */
2630 
2631 
2632   for (i=0; i < npixels; i++)
2633     {
2634       double gamma;
2635       double source_alpha;
2636       double dest_alpha;
2637       double composite;
2638       double value;
2639 
2640       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2641       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2642 
2643       source_alpha=(double) source.opacity/MaxRGBDouble;
2644       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2645 
2646       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2647         (1.0-source_alpha)*(1.0-dest_alpha);
2648       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2649 
2650       composite=MaxRGBDouble*(1.0-gamma);
2651       destination.opacity=RoundDoubleToQuantum(composite);
2652 
2653       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2654 
2655       value = MagickFmin(MaxRGBDouble,MagickFmax(0.0,2.0*(double)source.red+(double)destination.red-MaxRGBDouble));
2656       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2657                  source.red*(1.0-source_alpha)*dest_alpha+
2658                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2659       destination.red=RoundDoubleToQuantum(composite);
2660 
2661       value = MagickFmin(MaxRGBDouble,MagickFmax(0.0,2.0*(double)source.green+(double)destination.green-MaxRGBDouble));
2662       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2663                  source.green*(1.0-source_alpha)*dest_alpha+
2664                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2665       destination.green=RoundDoubleToQuantum(composite);
2666 
2667       value = MagickFmin(MaxRGBDouble,MagickFmax(0.0,2.0*(double)source.blue+(double)destination.blue-MaxRGBDouble));
2668       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2669                  source.blue*(1.0-source_alpha)*dest_alpha+
2670                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2671       destination.blue=RoundDoubleToQuantum(composite);
2672 
2673       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2674     }
2675 
2676   return MagickPass;
2677 }
2678 
2679 
2680 static MagickPassFail
VividLightCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2681 VividLightCompositePixels(void *mutable_data,                /* User provided mutable data */
2682                           const void *immutable_data,        /* User provided immutable data */
2683                           const Image * restrict source_image,         /* Source image */
2684                           const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2685                           const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2686                           Image * restrict update_image,               /* Update image */
2687                           PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2688                           IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2689                           const long npixels,                /* Number of pixels in row */
2690                           ExceptionInfo *exception           /* Exception report */
2691                           )
2692 {
2693   register long
2694     i;
2695 
2696   PixelPacket
2697     destination,
2698     source;
2699 
2700   ARG_NOT_USED(mutable_data);
2701   ARG_NOT_USED(immutable_data);
2702   ARG_NOT_USED(exception);
2703 
2704   /*
2705     Acts like ColorDodge for bright source pixels, ColorBurn for dark source pixels
2706   */
2707 
2708 
2709   for (i=0; i < npixels; i++)
2710     {
2711       double gamma;
2712       double source_alpha;
2713       double dest_alpha;
2714       double composite;
2715       double value;
2716 
2717       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2718       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2719 
2720       source_alpha=(double) source.opacity/MaxRGBDouble;
2721       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2722 
2723       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2724         (1.0-source_alpha)*(1.0-dest_alpha);
2725       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2726 
2727       composite=MaxRGBDouble*(1.0-gamma);
2728       destination.opacity=RoundDoubleToQuantum(composite);
2729 
2730       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2731 
2732       if(source.red==MaxRGB)
2733         value = MaxRGBDouble;
2734       else if(source.red==0)
2735         value = 0.;
2736       else if(source.red>=(0.5*MaxRGBDouble))
2737         value = MagickFmin(MaxRGBDouble,destination.red/(2.0-(2.0*(double)source.red/MaxRGBDouble)));
2738       else
2739         value = MagickFmax(0.0,((double)destination.red+2.0*source.red-MaxRGBDouble)/(2.0*(double)source.red/MaxRGBDouble));
2740       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2741                  source.red*(1.0-source_alpha)*dest_alpha+
2742                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2743       destination.red=RoundDoubleToQuantum(composite);
2744 
2745       if(source.green==MaxRGB)
2746         value = MaxRGBDouble;
2747       else if(source.green==0)
2748         value = 0.;
2749       else if(source.green>=(0.5*MaxRGBDouble))
2750         value = MagickFmin(MaxRGBDouble,destination.green/(2.0-(2.0*(double)source.green/MaxRGBDouble)));
2751       else
2752         value = MagickFmax(0.0,((double)destination.green+2.0*source.green-MaxRGBDouble)/(2.0*(double)source.green/MaxRGBDouble));
2753       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2754                  source.green*(1.0-source_alpha)*dest_alpha+
2755                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2756       destination.green=RoundDoubleToQuantum(composite);
2757 
2758       if(source.blue==MaxRGB)
2759         value = MaxRGBDouble;
2760       else if(source.blue==0)
2761         value = 0.;
2762       else if(source.blue>=(0.5*MaxRGBDouble))
2763         value = MagickFmin(MaxRGBDouble,destination.blue/(2.0-(2.0*(double)source.blue/MaxRGBDouble)));
2764       else
2765         value = MagickFmax(0.0,((double)destination.blue+2.0*source.blue-MaxRGBDouble)/(2.0*(double)source.blue/MaxRGBDouble));
2766       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2767                  source.blue*(1.0-source_alpha)*dest_alpha+
2768                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2769       destination.blue=RoundDoubleToQuantum(composite);
2770 
2771       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2772     }
2773 
2774   return MagickPass;
2775 }
2776 
2777 
2778 static MagickPassFail
PinLightCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2779 PinLightCompositePixels(void *mutable_data,                /* User provided mutable data */
2780                         const void *immutable_data,        /* User provided immutable data */
2781                         const Image * restrict source_image,         /* Source image */
2782                         const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2783                         const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2784                         Image * restrict update_image,               /* Update image */
2785                         PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2786                         IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2787                         const long npixels,                /* Number of pixels in row */
2788                         ExceptionInfo *exception           /* Exception report */
2789                         )
2790 {
2791   register long
2792     i;
2793 
2794   PixelPacket
2795     destination,
2796     source;
2797 
2798   ARG_NOT_USED(mutable_data);
2799   ARG_NOT_USED(immutable_data);
2800   ARG_NOT_USED(exception);
2801 
2802   /*
2803     Acts like Lighten for bright source pixels, Darken for dark source pixels
2804   */
2805 
2806 
2807   for (i=0; i < npixels; i++)
2808     {
2809       double gamma;
2810       double source_alpha;
2811       double dest_alpha;
2812       double composite;
2813       double value;
2814 
2815       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2816       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2817 
2818       source_alpha=(double) source.opacity/MaxRGBDouble;
2819       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2820 
2821       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2822         (1.0-source_alpha)*(1.0-dest_alpha);
2823       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2824 
2825       composite=MaxRGBDouble*(1.0-gamma);
2826       destination.opacity=RoundDoubleToQuantum(composite);
2827 
2828       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2829 
2830       if(source.red>=(0.5*MaxRGBDouble))
2831         value = MagickFmax((double)destination.red,2.0*((double)source.red-0.5*MaxRGBDouble));
2832       else
2833         value = MagickFmin((double)destination.red,2.0*(double)source.red);
2834       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2835                  source.red*(1.0-source_alpha)*dest_alpha+
2836                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2837       destination.red=RoundDoubleToQuantum(composite);
2838 
2839       if(source.green>=(0.5*MaxRGBDouble))
2840         value = MagickFmax((double)destination.green,2.0*((double)source.green-0.5*MaxRGBDouble));
2841       else
2842         value = MagickFmin((double)destination.green,2.0*(double)source.green);
2843       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2844                  source.green*(1.0-source_alpha)*dest_alpha+
2845                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2846       destination.green=RoundDoubleToQuantum(composite);
2847 
2848       if(source.blue>=(0.5*MaxRGBDouble))
2849         value = MagickFmax((double)destination.blue,2.0*((double)source.blue-0.5*MaxRGBDouble));
2850       else
2851         value = MagickFmin((double)destination.blue,2.0*(double)source.blue);
2852       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2853                  source.blue*(1.0-source_alpha)*dest_alpha+
2854                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2855       destination.blue=RoundDoubleToQuantum(composite);
2856 
2857       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2858     }
2859 
2860   return MagickPass;
2861 }
2862 
2863 
2864 static MagickPassFail
HardMixCompositePixels(void * mutable_data,const void * immutable_data,const Image * restrict source_image,const PixelPacket * restrict source_pixels,const IndexPacket * restrict source_indexes,Image * restrict update_image,PixelPacket * restrict update_pixels,IndexPacket * restrict update_indexes,const long npixels,ExceptionInfo * exception)2865 HardMixCompositePixels(void *mutable_data,                /* User provided mutable data */
2866                        const void *immutable_data,        /* User provided immutable data */
2867                        const Image * restrict source_image,         /* Source image */
2868                        const PixelPacket * restrict source_pixels,  /* Pixel row in source image */
2869                        const IndexPacket * restrict source_indexes, /* Pixel row indexes in source image */
2870                        Image * restrict update_image,               /* Update image */
2871                        PixelPacket * restrict update_pixels,        /* Pixel row in update image */
2872                        IndexPacket * restrict update_indexes,       /* Pixel row indexes in update image */
2873                        const long npixels,                /* Number of pixels in row */
2874                        ExceptionInfo *exception           /* Exception report */
2875                        )
2876 {
2877   register long
2878     i;
2879 
2880   PixelPacket
2881     destination,
2882     source;
2883 
2884   ARG_NOT_USED(mutable_data);
2885   ARG_NOT_USED(immutable_data);
2886   ARG_NOT_USED(exception);
2887 
2888   /*
2889     Averages each channel, then thresholds at half-value;
2890     i.e. sets to zero if the average value is less than one half,
2891     sets to full if above half.
2892   */
2893 
2894 
2895   for (i=0; i < npixels; i++)
2896     {
2897       double gamma;
2898       double source_alpha;
2899       double dest_alpha;
2900       double composite;
2901       double value;
2902 
2903       PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2904       PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2905 
2906       source_alpha=(double) source.opacity/MaxRGBDouble;
2907       dest_alpha=(double) destination.opacity/MaxRGBDouble;
2908 
2909       gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2910         (1.0-source_alpha)*(1.0-dest_alpha);
2911       gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2912 
2913       composite=MaxRGBDouble*(1.0-gamma);
2914       destination.opacity=RoundDoubleToQuantum(composite);
2915 
2916       gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2917 
2918       if(source.red + destination.red < MaxRGB)
2919         value = 0.0;
2920       else
2921         value = MaxRGBDouble;
2922       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2923                  source.red*(1.0-source_alpha)*dest_alpha+
2924                  destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2925       destination.red=RoundDoubleToQuantum(composite);
2926 
2927       if(source.green + destination.green < MaxRGB)
2928         value = 0.0;
2929       else
2930         value = MaxRGBDouble;
2931       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2932                  source.green*(1.0-source_alpha)*dest_alpha+
2933                  destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2934       destination.green=RoundDoubleToQuantum(composite);
2935 
2936       if(source.blue + destination.blue < MaxRGB)
2937         value = 0.0;
2938       else
2939         value = MaxRGBDouble;
2940       composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2941                  source.blue*(1.0-source_alpha)*dest_alpha+
2942                  destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2943       destination.blue=RoundDoubleToQuantum(composite);
2944 
2945       ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2946     }
2947 
2948   return MagickPass;
2949 }
2950 
2951 /*
2952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2953 %                                                                             %
2954 %                                                                             %
2955 %                                                                             %
2956 %   C o m p o s i t e I m a g e                                               %
2957 %                                                                             %
2958 %                                                                             %
2959 %                                                                             %
2960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2961 %
2962 %  CompositeImage() composites the second image (composite_image) onto the
2963 %  first (canvas_image) at the specified offsets.
2964 %
2965 %  The format of the CompositeImage method is:
2966 %
2967 %      MagickPassFail CompositeImage(Image *canvas_image,
2968 %        const CompositeOperator compose,const Image *composite_image,
2969 %        const long x_offset,const long y_offset)
2970 %
2971 %  A description of each parameter follows:
2972 %
2973 %    o canvas_image: The image to be updated.
2974 %
2975 %    o compose: This operator affects how the composite is applied to
2976 %      the image.  Choose from one of these operators: AddCompositeOp,
2977 %      AtopCompositeOp, BumpmapCompositeOp, ClearCompositeOp,
2978 %      ColorizeCompositeOp, CopyBlackCompositeOp, CopyBlueCompositeOp,
2979 %      CopyCompositeOp, CopyCyanCompositeOp,CopyGreenCompositeOp,
2980 %      CopyMagentaCompositeOp, CopyOpacityCompositeOp, CopyRedCompositeOp,
2981 %      CopyYellowCompositeOp, DarkenCompositeOp, DifferenceCompositeOp,
2982 %      DisplaceCompositeOp, DissolveCompositeOp, DivideCompositeOp,
2983 %      HueCompositeOp, InCompositeOp, LightenCompositeOp, LuminizeCompositeOp,
2984 %      MinusCompositeOp, ModulateCompositeOp, MultiplyCompositeOp,
2985 %      NoCompositeOp, OutCompositeOp, OverlayCompositeOp, PlusCompositeOp,
2986 %      SaturateCompositeOp, ScreenCompositeOp, SubtractCompositeOp,
2987 %      ThresholdCompositeOp, XorCompositeOp, HardLightCompositeOp.
2988 %
2989 %    o composite_image: The composite image.
2990 %
2991 %    o x_offset: The column offset of the composited image.
2992 %
2993 %    o y_offset: The row offset of the composited image.
2994 %
2995 %
2996 */
2997 static PixelIteratorDualModifyCallback
GetCompositionPixelIteratorCallback(const CompositeOperator compose,const MagickBool canvas_matte,const MagickBool change_matte,MagickBool * clear)2998 GetCompositionPixelIteratorCallback(const CompositeOperator compose,
2999                                     const MagickBool canvas_matte,
3000                                     const MagickBool change_matte,
3001                                     MagickBool *clear)
3002 {
3003   PixelIteratorDualModifyCallback
3004     call_back = (PixelIteratorDualModifyCallback) NULL;
3005 
3006   MagickBool
3007     clear_flag=MagickFalse;
3008 
3009   assert(clear != (MagickBool *) NULL);
3010 
3011   switch (compose)
3012     {
3013     case UndefinedCompositeOp:
3014       /* Does nothing */
3015       break;
3016     case OverCompositeOp:
3017       if (canvas_matte || change_matte)
3018         call_back=OverCompositePixels;
3019       else
3020         call_back=CopyCompositePixels;
3021       break;
3022     case InCompositeOp:
3023       call_back=InCompositePixels;
3024       break;
3025     case OutCompositeOp:
3026       call_back=OutCompositePixels;
3027       break;
3028     case AtopCompositeOp:
3029       if (canvas_matte || change_matte)
3030         call_back=AtopCompositePixels;
3031       else
3032         call_back=CopyCompositePixels;
3033       break;
3034     case XorCompositeOp:
3035       call_back=XorCompositePixels;
3036       break;
3037     case PlusCompositeOp:
3038       call_back=PlusCompositePixels;
3039       break;
3040     case MinusCompositeOp:
3041       call_back=MinusCompositePixels;
3042       break;
3043     case AddCompositeOp:
3044       call_back=AddCompositePixels;
3045       break;
3046     case SubtractCompositeOp:
3047       call_back=SubtractCompositePixels;
3048       break;
3049     case DifferenceCompositeOp:
3050       call_back=DifferenceCompositePixels;
3051       break;
3052     case MultiplyCompositeOp:
3053       call_back=MultiplyCompositePixels;
3054       break;
3055     case BumpmapCompositeOp:
3056       call_back=BumpmapCompositePixels;
3057       break;
3058     case CopyCompositeOp:
3059       call_back=CopyCompositePixels;
3060       break;
3061     case CopyRedCompositeOp:
3062       call_back=CopyRedCompositePixels;
3063       break;
3064     case CopyGreenCompositeOp:
3065       call_back=CopyGreenCompositePixels;
3066       break;
3067     case CopyBlueCompositeOp:
3068       call_back=CopyBlueCompositePixels;
3069       break;
3070     case CopyOpacityCompositeOp:
3071       call_back=CopyOpacityCompositePixels;
3072       break;
3073     case ClearCompositeOp:
3074       call_back=ClearCompositePixels;
3075       break;
3076     case DissolveCompositeOp:
3077       call_back=DissolveCompositePixels;
3078       break;
3079     case DisplaceCompositeOp:
3080       call_back=CopyCompositePixels;
3081       break;
3082     case ModulateCompositeOp:
3083       call_back=ModulateCompositePixels;
3084       break;
3085     case ThresholdCompositeOp:
3086       call_back=ThresholdCompositePixels;
3087       break;
3088     case NoCompositeOp:
3089       break;
3090     case DarkenCompositeOp:
3091       call_back=DarkenCompositePixels;
3092       break;
3093     case LightenCompositeOp:
3094       call_back=LightenCompositePixels;
3095       break;
3096     case HueCompositeOp:
3097       call_back=HueCompositePixels;
3098       break;
3099     case SaturateCompositeOp:
3100       call_back=SaturateCompositePixels;
3101       break;
3102     case ColorizeCompositeOp:
3103       call_back=ColorizeCompositePixels;
3104       break;
3105     case LuminizeCompositeOp:
3106       call_back=LuminizeCompositePixels;
3107       break;
3108     case ScreenCompositeOp:
3109       call_back=ScreenCompositePixels;
3110       break;
3111     case OverlayCompositeOp:
3112       call_back=OverlayCompositePixels;
3113       break;
3114     case CopyCyanCompositeOp:
3115       call_back=CopyRedCompositePixels;
3116       break;
3117     case CopyMagentaCompositeOp:
3118       call_back=CopyGreenCompositePixels;
3119       break;
3120     case CopyYellowCompositeOp:
3121       call_back=CopyBlueCompositePixels;
3122       break;
3123     case CopyBlackCompositeOp:
3124       call_back=CopyBlackCompositePixels;
3125       break;
3126     case DivideCompositeOp:
3127       call_back=DivideCompositePixels;
3128       break;
3129     case HardLightCompositeOp:
3130       call_back=HardLightCompositePixels;
3131       break;
3132     case ExclusionCompositeOp:
3133       call_back=ExclusionCompositePixels;
3134       break;
3135     case ColorDodgeCompositeOp:
3136       call_back=ColorDodgeCompositePixels;
3137       break;
3138     case ColorBurnCompositeOp:
3139       call_back=ColorBurnCompositePixels;
3140       break;
3141     case SoftLightCompositeOp:
3142       call_back=SoftLightCompositePixels;
3143       break;
3144     case LinearBurnCompositeOp:
3145       call_back=LinearBurnCompositePixels;
3146       break;
3147     case LinearDodgeCompositeOp:
3148       call_back=LinearDodgeCompositePixels;
3149       break;
3150     case LinearLightCompositeOp:
3151       call_back=LinearLightCompositePixels;
3152       break;
3153     case VividLightCompositeOp:
3154       call_back=VividLightCompositePixels;
3155       break;
3156     case PinLightCompositeOp:
3157       call_back=PinLightCompositePixels;
3158       break;
3159     case HardMixCompositeOp:
3160       call_back=HardMixCompositePixels;
3161       break;
3162     default:
3163       {
3164         break;
3165       }
3166     }
3167 
3168   if ((CopyCompositePixels == call_back) ||
3169       (ClearCompositePixels == call_back))
3170     clear_flag=MagickTrue;
3171 
3172   *clear=clear_flag;
3173   return call_back;
3174 }
3175 MagickExport MagickPassFail
CompositeImage(Image * canvas_image,const CompositeOperator compose,const Image * update_image,const long x_offset,const long y_offset)3176 CompositeImage(Image *canvas_image,
3177                const CompositeOperator compose,
3178                const Image *update_image,
3179                const long x_offset,const long y_offset)
3180 {
3181   CompositeOptions_t
3182     options;
3183 
3184   Image
3185     *change_image;
3186 
3187   double
3188     amount=0.0,
3189     percent_brightness=0.0,
3190     percent_saturation=0.0,
3191     threshold=0.0;
3192 
3193   long
3194     y;
3195 
3196   register const PixelPacket
3197     *p;
3198 
3199   register long
3200     x;
3201 
3202   register PixelPacket
3203     *q;
3204 
3205   MagickPassFail
3206     status=MagickPass;
3207 
3208   /*
3209     Prepare composite image.
3210   */
3211   assert(canvas_image != (Image *) NULL);
3212   assert(canvas_image->signature == MagickSignature);
3213   assert(update_image != (Image *) NULL);
3214   assert(update_image->signature == MagickSignature);
3215   if (compose == NoCompositeOp)
3216     return(MagickPass);
3217 
3218   /*
3219     Clone composite image so that we can modify it if need be.
3220   */
3221   change_image=CloneImage(update_image,0,0,True,&canvas_image->exception);
3222   if (change_image == (Image *) NULL)
3223     return(MagickFail);
3224 
3225   canvas_image->storage_class=DirectClass;
3226   switch (compose)
3227     {
3228     case CopyCyanCompositeOp:
3229     case CopyMagentaCompositeOp:
3230     case CopyYellowCompositeOp:
3231     case CopyBlackCompositeOp:
3232       {
3233         canvas_image->colorspace=CMYKColorspace;
3234         break;
3235       }
3236     case CopyOpacityCompositeOp:
3237       {
3238         canvas_image->matte=MagickTrue;
3239         break;
3240       }
3241     case DisplaceCompositeOp:
3242       {
3243         double
3244           x_displace,
3245           y_displace;
3246 
3247         double
3248           horizontal_scale,
3249           vertical_scale;
3250 
3251         register PixelPacket
3252           *r;
3253 
3254         horizontal_scale=20.0;
3255         vertical_scale=20.0;
3256         if (update_image->geometry != (char *) NULL)
3257           {
3258             int
3259               count;
3260 
3261             /*
3262               Determine the horizontal and vertical displacement scale.
3263             */
3264             count=GetMagickDimension(update_image->geometry,
3265                                      &horizontal_scale,&vertical_scale,NULL,NULL);
3266             if (count == 1)
3267               vertical_scale=horizontal_scale;
3268           }
3269         /*
3270           Shift image pixels as defined by a displacement map.
3271         */
3272         for (y=0; y < (long) update_image->rows; y++)
3273           {
3274             if (((y+y_offset) < 0) || ((y+y_offset) >= (long) canvas_image->rows))
3275               continue;
3276             p=AcquireImagePixels(update_image,0,y,update_image->columns,1,
3277                                  &canvas_image->exception);
3278             q=GetImagePixels(canvas_image,0,y+y_offset,canvas_image->columns,1);
3279             r=GetImagePixels(change_image,0,y,change_image->columns,1);
3280             if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
3281                 (r == (PixelPacket *) NULL))
3282               {
3283                 status=MagickFail;
3284                 break;
3285               }
3286             q+=x_offset;
3287             for (x=0; x < (long) update_image->columns; x++)
3288               {
3289                 if (((x_offset+x) < 0) || ((x_offset+x) >= (long) canvas_image->columns))
3290                   {
3291                     p++;
3292                     q++;
3293                     continue;
3294                   }
3295                 x_displace=(horizontal_scale*(PixelIntensityToQuantum(p)-
3296                                               (((double) MaxRGB+1.0)/2)))/(((double) MaxRGB+1.0)/2);
3297                 y_displace=x_displace;
3298                 if (update_image->matte)
3299                   y_displace=(vertical_scale*(p->opacity-
3300                                               (((double) MaxRGB+1.0)/2)))/(((double) MaxRGB+1.0)/2);
3301                 if (InterpolateViewColor(AccessDefaultCacheView(canvas_image),r,
3302                                          x_offset+(size_t) x+x_displace,y_offset+(size_t) y+y_displace,
3303                                          &canvas_image->exception) == MagickFail)
3304                   {
3305                     status=MagickFail;
3306                     break;
3307                   }
3308                 p++;
3309                 q++;
3310                 r++;
3311               }
3312             if (status != MagickFail)
3313               if (!SyncImagePixels(change_image))
3314                 {
3315                   status=MagickFail;
3316                   break;
3317                 }
3318           }
3319         break;
3320       }
3321     case ModulateCompositeOp:
3322       {
3323         percent_saturation=50.0;
3324         percent_brightness=50.0;
3325         if (update_image->geometry != (char *) NULL)
3326           {
3327             int
3328               count;
3329 
3330             /*
3331               Determine the brightness and saturation scale.
3332             */
3333             count=GetMagickDimension(update_image->geometry,
3334                                      &percent_brightness,&percent_saturation,NULL,NULL);
3335             if (count == 1)
3336               percent_saturation=percent_brightness;
3337           }
3338         percent_brightness/=100.0;
3339         percent_saturation/=100.0;
3340         break;
3341       }
3342     case ThresholdCompositeOp:
3343       {
3344         /*
3345           Determine the amount and threshold.
3346         */
3347         amount=0.5;
3348         threshold=0.05;
3349         if (update_image->geometry != (char *) NULL)
3350           (void) GetMagickDimension(update_image->geometry,&amount,&threshold,NULL,NULL);
3351         threshold*=MaxRGB;
3352         break;
3353       }
3354     default:
3355       break;
3356     }
3357 
3358   /*
3359     Make sure that the composite image is in a colorspace which is
3360     compatible (as need be) with the canvas image.
3361   */
3362   switch (compose)
3363     {
3364     case CopyRedCompositeOp:
3365     case CopyGreenCompositeOp:
3366     case CopyBlueCompositeOp:
3367     case CopyCyanCompositeOp:
3368     case CopyMagentaCompositeOp:
3369     case CopyYellowCompositeOp:
3370     case CopyBlackCompositeOp:
3371       {
3372         /*
3373           Assume that the user is right for channel copies.
3374         */
3375         break;
3376       }
3377     default:
3378       {
3379         if (IsRGBColorspace(canvas_image->colorspace))
3380           {
3381             if (!IsRGBColorspace(change_image->colorspace))
3382               TransformColorspace(change_image,RGBColorspace);
3383           }
3384         else if (IsYCbCrColorspace(canvas_image->colorspace))
3385           {
3386             if (canvas_image->colorspace != change_image->colorspace)
3387               TransformColorspace(change_image,canvas_image->colorspace);
3388           }
3389         else if (IsCMYKColorspace(canvas_image->colorspace))
3390           {
3391             if (!IsCMYKColorspace(change_image->colorspace))
3392               TransformColorspace(change_image,canvas_image->colorspace);
3393           }
3394         else
3395           {
3396             TransformColorspace(change_image,canvas_image->colorspace);
3397           }
3398         break;
3399       }
3400     }
3401 
3402   /*
3403     Composite image.
3404   */
3405   options.percent_brightness=percent_brightness;
3406   options.amount=amount;
3407   options.threshold=threshold;
3408 
3409   {
3410     unsigned long
3411       columns,
3412       rows;
3413 
3414     long
3415       composite_x,
3416       composite_y,
3417       canvas_x,
3418       canvas_y;
3419 
3420     columns=change_image->columns;
3421     rows=change_image->rows;
3422 
3423     composite_x=0;
3424     composite_y=0;
3425     canvas_x=x_offset;
3426     canvas_y=y_offset;
3427 
3428     if (x_offset < 0)
3429       composite_x += -x_offset;
3430     if (y_offset < 0)
3431       composite_y += -y_offset;
3432 
3433     columns -= composite_x;
3434     rows -= composite_y;
3435 
3436     if (canvas_x < 0)
3437       canvas_x=0;
3438     if (canvas_y < 0)
3439       canvas_y=0;
3440 
3441 #if 0
3442     fprintf(stderr,
3443             "Parameters: canvas=%lux%lu | composite=%lux%lu | offset x=%ld y=%ld\n"
3444             "Overlap:    canvas x=%ld y=%ld | composite x=%ld y=%ld | size=%ldx%ld\n",
3445            canvas_image->columns,canvas_image->rows,
3446            change_image->columns,change_image->rows,
3447            x_offset,y_offset,
3448            canvas_x,canvas_y,
3449            composite_x,composite_y,
3450            columns,rows);
3451 #endif
3452 
3453     if (((unsigned long) canvas_x < canvas_image->columns) &&
3454         ((unsigned long) canvas_y < canvas_image->rows) &&
3455         ((unsigned long) composite_x < change_image->columns) &&
3456         ((unsigned long) composite_y < change_image->rows))
3457       {
3458         PixelIteratorDualModifyCallback
3459           call_back = (PixelIteratorDualModifyCallback) NULL;
3460 
3461         MagickBool
3462           clear_pixels = MagickFalse;
3463 
3464         columns = Min(canvas_image->columns - canvas_x,
3465                       change_image->columns - composite_x);
3466         rows = Min(canvas_image->rows - canvas_y,
3467                    change_image->rows - composite_y);
3468 
3469         call_back=GetCompositionPixelIteratorCallback(compose,
3470                                                       canvas_image->matte,
3471                                                       change_image->matte,
3472                                                       &clear_pixels);
3473         if (call_back != (PixelIteratorDualModifyCallback) NULL)
3474           {
3475             char
3476               description[MaxTextExtent];
3477 
3478             FormatString(description,"[%%s] Composite %s image pixels ...",
3479                          CompositeOperatorToString(compose));
3480 
3481             if (clear_pixels)
3482               {
3483                 /*
3484                   We don't care about existing pixels in the region.
3485                 */
3486                 status=PixelIterateDualNew(call_back,              /* Callback */
3487                                            NULL,
3488                                            description,            /* Description */
3489                                            NULL,
3490                                            &options,               /* Options */
3491                                            columns,                /* Number of columns */
3492                                            rows,                   /* Number of rows */
3493                                            change_image,           /* Composite image */
3494                                            composite_x,            /* Composite x offset */
3495                                            composite_y,            /* Composite y offset */
3496                                            canvas_image,           /* Canvas image */
3497                                            canvas_x,               /* Canvas x offset */
3498                                            canvas_y,               /* Canvas y offset */
3499                                            &canvas_image->exception); /* Exception */
3500               }
3501             else
3502               {
3503                 /*
3504                   Blend with existing pixels in the region.
3505                 */
3506                 status=PixelIterateDualModify(call_back,              /* Callback */
3507                                               NULL,
3508                                               description,            /* Description */
3509                                               NULL,
3510                                               &options,               /* Options */
3511                                               columns,                /* Number of columns */
3512                                               rows,                   /* Number of rows */
3513                                               change_image,           /* Composite image */
3514                                               composite_x,            /* Composite x offset */
3515                                               composite_y,            /* Composite y offset */
3516                                               canvas_image,           /* Canvas image */
3517                                               canvas_x,               /* Canvas x offset */
3518                                               canvas_y,               /* Canvas y offset */
3519                                               &canvas_image->exception); /* Exception */
3520               }
3521           }
3522         else
3523           {
3524             status=MagickFail;
3525           }
3526       }
3527   }
3528 
3529   DestroyImage(change_image);
3530   change_image=(Image *) NULL;
3531 
3532   return(status);
3533 }
3534 
3535 /*
3536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3537 %                                                                             %
3538 %                                                                             %
3539 %                                                                             %
3540 +   C o m p o s i t e I m a g e R e g i o n                                   %
3541 %                                                                             %
3542 %                                                                             %
3543 %                                                                             %
3544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3545 %
3546 %  CompositeImageRegion() composites the update image on the canvas image
3547 %  using a specified composition operation.  The offset and dimensions of
3548 %  the region in update image to use are specified.  The offset to
3549 %  composite on the canvas image is specified.  These parameters are
3550 %  adjusted as needed so that only the portions which overlap (according
3551 %  to the user's specification and the image sizes) are actually composited.
3552 %  If there is no overlap at all, then no work is performed and MagickFail
3553 %  is returned.
3554 %
3555 %  The format of the CompositeImage method is:
3556 %
3557 %      MagickPassFail CompositeImageRegion(const CompositeOperator compose,
3558 %                                          const CompositeOptions_t *options,
3559 %                                          const unsigned long columns,
3560 %                                          const unsigned long rows,
3561 %                                          const Image *update_image,
3562 %                                          const long update_x,
3563 %                                          const long update_y,
3564 %                                          Image *canvas_image,
3565 %                                          const long canvas_x,
3566 %                                          const long canvas_y,
3567 %                                          ExceptionInfo *exception)
3568 %
3569 %  A description of each parameter follows:
3570 %
3571 %    o compose: This operator affects how the composite is applied to
3572 %      the image.  Choose from one of these operators: AddCompositeOp,
3573 %      AtopCompositeOp, BumpmapCompositeOp, ClearCompositeOp,
3574 %      ColorizeCompositeOp, CopyBlackCompositeOp, CopyBlueCompositeOp,
3575 %      CopyCompositeOp, CopyCyanCompositeOp,CopyGreenCompositeOp,
3576 %      CopyMagentaCompositeOp, CopyOpacityCompositeOp, CopyRedCompositeOp,
3577 %      CopyYellowCompositeOp, DarkenCompositeOp, DifferenceCompositeOp,
3578 %      DisplaceCompositeOp, DissolveCompositeOp, DivideCompositeOp,
3579 %      HueCompositeOp, InCompositeOp, LightenCompositeOp, LuminizeCompositeOp,
3580 %      MinusCompositeOp, ModulateCompositeOp, MultiplyCompositeOp,
3581 %      NoCompositeOp, OutCompositeOp, OverlayCompositeOp, PlusCompositeOp,
3582 %      SaturateCompositeOp, ScreenCompositeOp, SubtractCompositeOp,
3583 %      ThresholdCompositeOp, XorCompositeOp, HardLightCompositeOp.
3584 %
3585 %    o options: This optional structure passes options required by
3586 %        ModulateComposite and ThresholdComposite and NULL may be
3587 %        passed if it is not otherwise needed.
3588 %
3589 %    o columns: Width of update region.
3590 %
3591 %    o rows: Height of update region.
3592 %
3593 %    o update_image: Image to composite on canvas image.
3594 %
3595 %    o update_x: X ordinate of region to composite.
3596 %
3597 %    o update_y: Y ordinate of region to composite.
3598 %
3599 %    o canvas_image: Image to update.
3600 %
3601 %    o canvas_x: X ordinate of canvas region to composite on.
3602 %
3603 %    o canvas_y: Y ordinate of canvas region to composite on.
3604 %
3605 %    o exception: Details of any error are reported here.
3606 %
3607 */
3608 MagickExport MagickPassFail
CompositeImageRegion(const CompositeOperator compose,const CompositeOptions_t * options,const unsigned long arg_columns,const unsigned long arg_rows,const Image * update_image,const long arg_update_x,const long arg_update_y,Image * canvas_image,const long arg_canvas_x,const long arg_canvas_y,ExceptionInfo * exception)3609 CompositeImageRegion(const CompositeOperator compose,
3610                      const CompositeOptions_t *options,
3611                      const unsigned long arg_columns,
3612                      const unsigned long arg_rows,
3613                      const Image *update_image,
3614                      const long arg_update_x,
3615                      const long arg_update_y,
3616                      Image *canvas_image,
3617                      const long arg_canvas_x,
3618                      const long arg_canvas_y,
3619                      ExceptionInfo *exception)
3620 {
3621   PixelIteratorDualModifyCallback
3622     call_back = (PixelIteratorDualModifyCallback) NULL;
3623 
3624   MagickBool
3625     clear_pixels = MagickFalse;
3626 
3627   MagickPassFail
3628     status=MagickPass;
3629 
3630   /*   printf("columns=%lu rows=%lu update_x=%ld update_y=%ld canvas_x=%ld canvas_y=%ld\n", */
3631   /*          columns,rows,update_x,update_y,canvas_x,canvas_y); */
3632 
3633   if (compose == NoCompositeOp)
3634     return(MagickPass);
3635 
3636   canvas_image->storage_class=DirectClass;
3637 
3638   call_back=GetCompositionPixelIteratorCallback(compose,
3639                                                 canvas_image->matte,
3640                                                 update_image->matte,
3641                                                 &clear_pixels);
3642   if (call_back != (PixelIteratorDualModifyCallback) NULL)
3643     {
3644       const char
3645         *description = "[%s] Composite image pixels ...";
3646 
3647       unsigned long
3648         columns=arg_columns,
3649         rows=arg_rows;
3650 
3651       long
3652         update_x=arg_update_x,
3653         update_y=arg_update_y,
3654         canvas_x=arg_canvas_x,
3655         canvas_y=arg_canvas_y;
3656 
3657       /*
3658         FIXME: The area logic is not implemented yet.
3659       */
3660 
3661       if ((update_x >= (long) update_image->columns) ||
3662           (update_y >= (long) update_image->rows) ||
3663           (canvas_x >= (long) canvas_image->columns) ||
3664           (canvas_y >= (long) canvas_image->rows))
3665         status = MagickFail;
3666 
3667 #if 0
3668       printf("canvas_image=%lux%lu update_image=%lux%lu update_region=%lux%lu+%ld+%ld canvas_region=%lux%lu+%ld+%ld \n",
3669              canvas_image->columns,canvas_image->rows,
3670              update_image->columns,update_image->rows,
3671              columns,rows,update_x,update_y,
3672              columns,rows,canvas_x,canvas_y);
3673 #endif
3674 
3675       if ((status == MagickPass) &&
3676           ((unsigned long) canvas_x < canvas_image->columns) &&
3677           ((unsigned long) canvas_y < canvas_image->rows) &&
3678           ((unsigned long) update_x < update_image->columns) &&
3679           ((unsigned long) update_y < update_image->rows) &&
3680           (columns != 0) && (rows != 0))
3681         {
3682           if (clear_pixels)
3683             {
3684               /*
3685                 We don't care about existing pixels in the region.
3686               */
3687               status=PixelIterateDualNew(call_back,              /* Callback */
3688                                          NULL,
3689                                          description,            /* Description */
3690                                          NULL,
3691                                          options,                /* Options */
3692                                          columns,                /* Number of columns */
3693                                          rows,                   /* Number of rows */
3694                                          update_image,           /* Composite image */
3695                                          update_x,               /* Composite x offset */
3696                                          update_y,               /* Composite y offset */
3697                                          canvas_image,           /* Canvas image */
3698                                          canvas_x,               /* Canvas x offset */
3699                                          canvas_y,               /* Canvas y offset */
3700                                          exception);             /* Exception */
3701             }
3702           else
3703             {
3704               /*
3705                 Blend with existing pixels in the region.
3706               */
3707               status=PixelIterateDualModify(call_back,              /* Callback */
3708                                             NULL,
3709                                             description,            /* Description */
3710                                             NULL,
3711                                             options,                /* Options */
3712                                             columns,                /* Number of columns */
3713                                             rows,                   /* Number of rows */
3714                                             update_image,           /* Composite image */
3715                                             update_x,               /* Composite x offset */
3716                                             update_y,               /* Composite y offset */
3717                                             canvas_image,           /* Canvas image */
3718                                             canvas_x,               /* Canvas x offset */
3719                                             canvas_y,               /* Canvas y offset */
3720                                             exception);             /* Exception */
3721             }
3722         }
3723     }
3724   else
3725     {
3726       status=MagickFail;
3727     }
3728 
3729   return status;
3730 }
3731 
3732 
3733 /*
3734 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3735 %                                                                             %
3736 %                                                                             %
3737 %                                                                             %
3738 +   M a g i c k C o m p o s i t e I m a g e U n d e r C o l o r               %
3739 %                                                                             %
3740 %                                                                             %
3741 %                                                                             %
3742 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3743 %
3744 %  MagickCompositeImageUnderColor() composites a color underneath an image,
3745 %  removing any existing opacity.
3746 %
3747 %  The format of the MagickCompositeImageUnderColor method is:
3748 %
3749 %      MagickPassFail MagickCompositeImageUnderColor(Image *image,
3750 %                                              PixelPacket *undercolor,
3751 %                                              ExceptionInfo *exception)
3752 %
3753 %  A description of each parameter follows:
3754 %
3755 %    o image: Image to modify.
3756 %
3757 %    o undercolor: Background color to apply.
3758 %
3759 %    o exception: Details of any error are reported here.
3760 %
3761 */
3762 static MagickPassFail
MagickCompositeImageUnderColorPixels(void * mutable_data,const void * immutable_data,Image * restrict image,PixelPacket * restrict pixels,IndexPacket * restrict indexes,const long npixels,ExceptionInfo * exception)3763 MagickCompositeImageUnderColorPixels(void *mutable_data,             /* User provided mutable data */
3764                                      const void *immutable_data,     /* User provided immutable data */
3765                                      Image * restrict image,                   /* Modify image */
3766                                      PixelPacket * restrict pixels,  /* Pixel row */
3767                                      IndexPacket * restrict indexes, /* Pixel row indexes */
3768                                      const long npixels,             /* Number of pixels in row */
3769                                      ExceptionInfo *exception)       /* Exception report */
3770 {
3771   const PixelPacket
3772     * restrict background_color = (const PixelPacket *) immutable_data;
3773 
3774   register long
3775     i;
3776 
3777   ARG_NOT_USED(mutable_data);
3778   ARG_NOT_USED(image);
3779   ARG_NOT_USED(indexes);
3780   ARG_NOT_USED(exception);
3781 
3782   for (i=0; i < npixels; i++)
3783     {
3784       AlphaCompositePixel(&pixels[i],&pixels[i],pixels[i].opacity,background_color,
3785                           background_color->opacity);
3786       pixels[i].opacity=OpaqueOpacity;
3787     }
3788 
3789   return MagickPass;
3790 }
3791 
3792 MagickExport MagickPassFail
MagickCompositeImageUnderColor(Image * image,const PixelPacket * undercolor,ExceptionInfo * exception)3793 MagickCompositeImageUnderColor(Image *image,const PixelPacket *undercolor,
3794                                ExceptionInfo *exception)
3795 {
3796   MagickPassFail
3797     status;
3798 
3799   image->storage_class=DirectClass;
3800   status=PixelIterateMonoModify(MagickCompositeImageUnderColorPixels,
3801                                 NULL,
3802                                 "[%s] Applying undercolor...",
3803                                 NULL,undercolor,
3804                                 0,0,image->columns,image->rows,
3805                                 image,
3806                                 exception);
3807   image->matte=MagickFalse;
3808 
3809   return status;
3810 }
3811