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