Coverage Report

Created: 2025-12-31 07:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/composite.c
Line
Count
Source
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
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
34.6M
{
80
34.6M
  *destination=update_pixels[i];
81
34.6M
  if (!update_image->matte)
82
460
    destination->opacity=OpaqueOpacity;
83
34.6M
  else
84
34.6M
    if (update_image->colorspace == CMYKColorspace)
85
0
      destination->opacity=update_indexes[i];
86
34.6M
}
87
88
89
/*
90
  Build a PixelPacket representing the update pixel.
91
*/
92
static inline void
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
34.6M
{
99
34.6M
  *source=source_pixels[i];
100
34.6M
  if (!source_image->matte)
101
0
    source->opacity=OpaqueOpacity;
102
34.6M
  else
103
34.6M
    if (source_image->colorspace == CMYKColorspace)
104
0
      source->opacity=source_indexes[i];
105
34.6M
}
106
107
108
/*
109
  Apply composition updates to the canvas image.
110
*/
111
static inline void
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
34.6M
{
119
34.6M
  if (update_image->colorspace != CMYKColorspace)
120
34.6M
    {
121
      /*
122
        RGB stores opacity in 'opacity'.
123
      */
124
34.6M
      update_pixels[i]=*composite;
125
34.6M
    }
126
0
  else
127
0
    {
128
      /*
129
        CMYK(A) stores K in 'opacity' and A in the indexes.
130
      */
131
0
      update_pixels[i].red=composite->red;
132
0
      update_pixels[i].green=composite->green;
133
0
      update_pixels[i].blue=composite->blue;
134
0
      update_indexes[i]=composite->opacity; /* opacity */
135
0
    }
136
34.6M
}
137
138
139
static MagickPassFail
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
39.4k
{
152
39.4k
  register long
153
39.4k
    i;
154
155
39.4k
  PixelPacket
156
39.4k
    destination,
157
39.4k
    source;
158
159
39.4k
  ARG_NOT_USED(mutable_data);
160
39.4k
  ARG_NOT_USED(immutable_data);
161
39.4k
  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
34.7M
  for (i=0; i < npixels; i++)
169
34.6M
    {
170
34.6M
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
171
34.6M
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
172
173
34.6M
      AlphaCompositePixel(&destination,&source,source.opacity,&destination,destination.opacity);
174
175
34.6M
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
176
34.6M
    }
177
178
39.4k
  return MagickPass;
179
39.4k
}
180
181
182
static MagickPassFail
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
0
{
195
0
  register long
196
0
    i;
197
198
0
  PixelPacket
199
0
    destination,
200
0
    source;
201
202
0
  ARG_NOT_USED(mutable_data);
203
0
  ARG_NOT_USED(immutable_data);
204
0
  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
0
  for (i=0; i < npixels; i++)
212
0
    {
213
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
214
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
215
216
0
      if (source.opacity == TransparentOpacity)
217
0
        {
218
0
          destination=source;
219
0
        }
220
0
      else if (destination.opacity == TransparentOpacity)
221
0
        {
222
0
        }
223
0
      else
224
0
        {
225
0
          double
226
0
            opacity;
227
228
0
          opacity=(double)
229
0
            (((double) MaxRGBDouble-source.opacity)*
230
0
             (MaxRGBDouble-destination.opacity)/MaxRGBDouble);
231
232
0
          destination.red=(Quantum)
233
0
            (((double) MaxRGBDouble-source.opacity)*
234
0
             (MaxRGBDouble-destination.opacity)*source.red/MaxRGBDouble/opacity+0.5);
235
236
0
          destination.green=(Quantum)
237
0
            (((double) MaxRGBDouble-source.opacity)*
238
0
             (MaxRGBDouble-destination.opacity)*source.green/MaxRGBDouble/opacity+0.5);
239
240
0
          destination.blue=(Quantum)
241
0
            (((double) MaxRGBDouble-source.opacity)*
242
0
             (MaxRGBDouble-destination.opacity)*source.blue/MaxRGBDouble/opacity+0.5);
243
244
0
          destination.opacity=(Quantum) (MaxRGBDouble-opacity+0.5);
245
0
        }
246
247
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
248
0
    }
249
250
0
  return MagickPass;
251
0
}
252
253
254
static MagickPassFail
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
0
{
267
0
  register long
268
0
    i;
269
270
0
  PixelPacket
271
0
    destination,
272
0
    source;
273
274
0
  ARG_NOT_USED(mutable_data);
275
0
  ARG_NOT_USED(immutable_data);
276
0
  ARG_NOT_USED(exception);
277
278
  /*
279
    The resulting image is change-image with the shape of
280
    base-image cut out.
281
  */
282
0
  for (i=0; i < npixels; i++)
283
0
    {
284
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
285
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
286
287
0
      if (source.opacity == TransparentOpacity)
288
0
        {
289
0
          destination=source;
290
0
        }
291
0
      else if (destination.opacity == OpaqueOpacity)
292
0
        {
293
0
          destination.opacity=TransparentOpacity;
294
0
        }
295
0
      else
296
0
        {
297
0
          double
298
0
            opacity;
299
300
0
          opacity=(double)
301
0
            (MaxRGBDouble-source.opacity)*destination.opacity/MaxRGBDouble;
302
303
0
          destination.red=(Quantum)
304
0
            (((double) MaxRGBDouble-source.opacity)*
305
0
             destination.opacity*source.red/MaxRGBDouble/opacity+0.5);
306
307
0
          destination.green=(Quantum)
308
0
            (((double) MaxRGBDouble-source.opacity)*
309
0
             destination.opacity*source.green/MaxRGBDouble/opacity+0.5);
310
311
0
          destination.blue=(Quantum)
312
0
            (((double) MaxRGBDouble-source.opacity)*
313
0
             destination.opacity*source.blue/MaxRGBDouble/opacity+0.5);
314
315
0
          destination.opacity=(Quantum) (MaxRGBDouble-opacity+0.5);
316
0
        }
317
318
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
319
0
    }
320
321
0
  return MagickPass;
322
0
}
323
324
static MagickPassFail
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
0
{
337
0
  register long
338
0
    i;
339
340
0
  PixelPacket
341
0
    destination,
342
0
    source;
343
344
0
  ARG_NOT_USED(mutable_data);
345
0
  ARG_NOT_USED(immutable_data);
346
0
  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
0
  for (i=0; i < npixels; i++)
356
0
    {
357
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
358
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
359
360
0
      AtopCompositePixel(&destination,&destination,&source);
361
362
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
363
0
    }
364
365
0
  return MagickPass;
366
0
}
367
368
369
static MagickPassFail
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
0
{
382
0
  register long
383
0
    i;
384
385
0
  PixelPacket
386
0
    destination,
387
0
    source;
388
389
0
  ARG_NOT_USED(mutable_data);
390
0
  ARG_NOT_USED(immutable_data);
391
0
  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
0
  for (i=0; i < npixels; i++)
399
0
    {
400
0
      double gamma;
401
0
      double source_alpha;
402
0
      double dest_alpha;
403
0
      double composite;
404
405
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
406
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
407
408
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
409
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
410
411
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
412
0
        2.0*(1.0-source_alpha)*(1.0-dest_alpha);
413
414
0
      composite=MaxRGBDouble*(1.0-gamma);
415
0
      destination.opacity=RoundDoubleToQuantum(composite);
416
417
0
      gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
418
419
0
      composite=((1.0-source_alpha)*source.red*dest_alpha+
420
0
                 (1.0-dest_alpha)*destination.red*source_alpha)*gamma;
421
0
      destination.red=RoundDoubleToQuantum(composite);
422
423
0
      composite=((1.0-source_alpha)*source.green*dest_alpha+
424
0
                 (1.0-dest_alpha)*destination.green*source_alpha)*gamma;
425
0
      destination.green=RoundDoubleToQuantum(composite);
426
427
0
      composite=((1.0-source_alpha)*source.blue*dest_alpha+
428
0
                 (1.0-dest_alpha)*destination.blue*source_alpha)*gamma;
429
0
      destination.blue=RoundDoubleToQuantum(composite);
430
431
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
432
0
    }
433
434
0
  return MagickPass;
435
0
}
436
437
438
static MagickPassFail
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
0
{
451
0
  register long
452
0
    i;
453
454
0
  PixelPacket
455
0
    destination,
456
0
    source;
457
458
0
  ARG_NOT_USED(mutable_data);
459
0
  ARG_NOT_USED(immutable_data);
460
0
  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
0
  for (i=0; i < npixels; i++)
468
0
    {
469
0
      double
470
0
        value;
471
472
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
473
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
474
475
0
      value=((double) (MaxRGBDouble-source.opacity)*source.red+(double)
476
0
                 (MaxRGBDouble-destination.opacity)*destination.red)/MaxRGBDouble;
477
0
      destination.red=RoundDoubleToQuantum(value);
478
479
0
      value=((double) (MaxRGBDouble-source.opacity)*source.green+(double)
480
0
                   (MaxRGBDouble-destination.opacity)*destination.green)/MaxRGBDouble;
481
0
      destination.green=RoundDoubleToQuantum(value);
482
483
0
      value=((double) (MaxRGBDouble-source.opacity)*source.blue+(double)
484
0
                  (MaxRGBDouble-destination.opacity)*destination.blue)/MaxRGBDouble;
485
0
      destination.blue=RoundDoubleToQuantum(value);
486
487
0
      value=((double) (MaxRGBDouble-source.opacity)+
488
0
                     (double) (MaxRGBDouble-destination.opacity))/MaxRGBDouble;
489
0
      destination.opacity=MaxRGB-RoundDoubleToQuantum(value);
490
491
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
492
0
    }
493
494
0
  return MagickPass;
495
0
}
496
497
498
static MagickPassFail
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
0
{
511
0
  register long
512
0
    i;
513
514
0
  PixelPacket
515
0
    destination,
516
0
    source;
517
518
0
  ARG_NOT_USED(mutable_data);
519
0
  ARG_NOT_USED(immutable_data);
520
0
  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
0
  for (i=0; i < npixels; i++)
527
0
    {
528
0
      double
529
0
        value;
530
531
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
532
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
533
534
0
      value=((double) (MaxRGBDouble-destination.opacity)*destination.red-
535
0
             (double) (MaxRGBDouble-source.opacity)*source.red)/MaxRGBDouble;
536
0
      destination.red=RoundDoubleToQuantum(value);
537
538
0
      value=((double) (MaxRGBDouble-destination.opacity)*destination.green-
539
0
             (double) (MaxRGBDouble-source.opacity)*source.green)/MaxRGBDouble;
540
0
      destination.green=RoundDoubleToQuantum(value);
541
542
0
      value=((double) (MaxRGBDouble-destination.opacity)*destination.blue-
543
0
             (double) (MaxRGBDouble-source.opacity)*source.blue)/MaxRGBDouble;
544
0
      destination.blue=RoundDoubleToQuantum(value);
545
546
0
      value=((double) (MaxRGBDouble-destination.opacity)-
547
0
             (double) (MaxRGBDouble-source.opacity))/MaxRGBDouble;
548
0
      destination.opacity=MaxRGB-RoundDoubleToQuantum(value);
549
550
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
551
0
    }
552
553
0
  return MagickPass;
554
0
}
555
556
557
static MagickPassFail
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
0
{
570
0
  register long
571
0
    i;
572
573
0
  PixelPacket
574
0
    destination,
575
0
    source;
576
577
0
  ARG_NOT_USED(mutable_data);
578
0
  ARG_NOT_USED(immutable_data);
579
0
  ARG_NOT_USED(exception);
580
581
  /*
582
    The result of change-image + base-image, with overflow wrapping
583
    around (mod MaxRGB+1).
584
  */
585
0
  for (i=0; i < npixels; i++)
586
0
    {
587
0
      double
588
0
        value;
589
590
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
591
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
592
593
0
      value=(double) source.red+destination.red;
594
0
      if (value > MaxRGBDouble) value -= ((double) MaxRGBDouble+1.0);
595
0
      destination.red=RoundDoubleToQuantum(value);
596
597
0
      value=(double) source.green+destination.green;
598
0
      if (value > MaxRGBDouble) value -= ((double) MaxRGBDouble+1.0);
599
0
      destination.green=RoundDoubleToQuantum(value);
600
601
0
      value=(double) source.blue+destination.blue;
602
0
      if (value > MaxRGBDouble) value -= ((double) MaxRGBDouble+1.0);
603
0
      destination.blue=RoundDoubleToQuantum(value);
604
605
0
      destination.opacity=OpaqueOpacity;
606
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
607
0
    }
608
609
0
  return MagickPass;
610
0
}
611
612
613
static MagickPassFail
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
0
{
626
0
  register long
627
0
    i;
628
629
0
  PixelPacket
630
0
    destination,
631
0
    source;
632
633
0
  ARG_NOT_USED(mutable_data);
634
0
  ARG_NOT_USED(immutable_data);
635
0
  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
0
  for (i=0; i < npixels; i++)
643
0
    {
644
0
      double
645
0
        value;
646
647
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
648
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
649
650
0
      value=(double) source.red-destination.red;
651
0
      if (value < 0) value += ((double) MaxRGBDouble+1.0);
652
0
      destination.red=RoundDoubleToQuantum(value);
653
654
0
      value=(double) source.green-destination.green;
655
0
      if (value < 0) value += ((double) MaxRGBDouble+1.0);
656
0
      destination.green=RoundDoubleToQuantum(value);
657
658
0
      value=(double) source.blue-destination.blue;
659
0
      if (value < 0) value += ((double) MaxRGBDouble+1.0);
660
0
      destination.blue=RoundDoubleToQuantum(value);
661
662
0
      destination.opacity=OpaqueOpacity;
663
664
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
665
0
    }
666
667
0
  return MagickPass;
668
0
}
669
670
671
static MagickPassFail
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
0
{
684
0
  register long
685
0
    i;
686
687
0
  PixelPacket
688
0
    destination,
689
0
    source;
690
691
0
  ARG_NOT_USED(mutable_data);
692
0
  ARG_NOT_USED(immutable_data);
693
0
  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
0
  for (i=0; i < npixels; i++)
700
0
    {
701
0
      double gamma;
702
0
      double source_alpha;
703
0
      double dest_alpha;
704
0
      double composite;
705
706
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
707
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
708
709
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
710
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
711
712
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
713
0
        (1.0-source_alpha)*(1.0-dest_alpha);
714
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
715
716
0
      composite=MaxRGBDouble*(1.0-gamma);
717
0
      destination.opacity=RoundDoubleToQuantum(composite);
718
719
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
720
721
0
      composite=(fabs((double)source.red - (double)destination.red)*
722
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
723
0
                 source.red*(1.0-source_alpha)*dest_alpha+
724
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
725
0
      destination.red=RoundDoubleToQuantum(composite);
726
727
0
      composite=(fabs((double)source.green - (double)destination.green)*
728
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
729
0
                 source.green*(1.0-source_alpha)*dest_alpha+
730
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
731
0
      destination.green=RoundDoubleToQuantum(composite);
732
733
0
      composite=(fabs((double)source.blue - (double)destination.blue)*
734
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
735
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
736
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
737
0
      destination.blue=RoundDoubleToQuantum(composite);
738
739
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
740
0
    }
741
742
0
  return MagickPass;
743
0
}
744
745
746
static MagickPassFail
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
0
{
759
0
  register long
760
0
    i;
761
762
0
  PixelPacket
763
0
    destination,
764
0
    source;
765
766
0
  ARG_NOT_USED(mutable_data);
767
0
  ARG_NOT_USED(immutable_data);
768
0
  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
0
  for (i=0; i < npixels; i++)
777
0
    {
778
0
      double gamma;
779
0
      double source_alpha;
780
0
      double dest_alpha;
781
0
      double composite;
782
783
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
784
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
785
786
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
787
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
788
789
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
790
0
        (1.0-source_alpha)*(1.0-dest_alpha);
791
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
792
793
0
      composite=MaxRGBDouble*(1.0-gamma);
794
0
      destination.opacity=RoundDoubleToQuantum(composite);
795
796
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
797
798
0
      composite=(((double)source.red*(1.0-source_alpha)*
799
0
                  (double)destination.red*(1.0-dest_alpha))/MaxRGBDouble+
800
0
                 source.red*(1.0-source_alpha)*dest_alpha+
801
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
802
0
      destination.red=RoundDoubleToQuantum(composite);
803
804
0
      composite=(((double)source.green*(1.0-source_alpha)*
805
0
                  (double)destination.green*(1.0-dest_alpha))/MaxRGBDouble+
806
0
                 source.green*(1.0-source_alpha)*dest_alpha+
807
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
808
0
      destination.green=RoundDoubleToQuantum(composite);
809
810
0
      composite=(((double)source.blue*(1.0-source_alpha)*
811
0
                  (double)destination.blue*(1.0-dest_alpha))/MaxRGBDouble+
812
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
813
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
814
0
      destination.blue=RoundDoubleToQuantum(composite);
815
816
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
817
0
    }
818
819
0
  return MagickPass;
820
0
}
821
822
823
static MagickPassFail
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
0
{
836
0
  register long
837
0
    i;
838
839
0
  PixelPacket
840
0
    destination,
841
0
    source;
842
843
0
  ARG_NOT_USED(mutable_data);
844
0
  ARG_NOT_USED(immutable_data);
845
0
  ARG_NOT_USED(exception);
846
847
  /*
848
    The result base-image shaded by change-image.
849
  */
850
0
  for (i=0; i < npixels; i++)
851
0
    {
852
0
      double value;
853
0
      double source_intensity;
854
855
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
856
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
857
858
0
      source_intensity=(double) PixelIntensity(&source)/MaxRGBDouble;
859
860
0
      value=source_intensity*destination.red;
861
0
      destination.red=RoundDoubleToQuantum(value);
862
863
0
      value=source_intensity*destination.green;
864
0
      destination.green=RoundDoubleToQuantum(value);
865
866
0
      value=source_intensity*destination.blue;
867
0
      destination.blue=RoundDoubleToQuantum(value);
868
869
0
      value=source_intensity*destination.opacity;
870
0
      destination.opacity=RoundDoubleToQuantum(value);
871
872
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
873
0
    }
874
875
0
  return MagickPass;
876
0
}
877
878
879
880
881
static MagickPassFail
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
1.01M
{
894
1.01M
  ARG_NOT_USED(mutable_data);
895
1.01M
  ARG_NOT_USED(immutable_data);
896
1.01M
  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
1.01M
  if ((update_image->colorspace == CMYKColorspace) &&
903
0
      (update_image->matte))
904
0
    {
905
0
      if (source_image->matte)
906
0
        {
907
0
          (void) memcpy(update_pixels,source_pixels,npixels*sizeof(PixelPacket));
908
0
          (void) memcpy(update_indexes,source_indexes,npixels*sizeof(IndexPacket));
909
0
        }
910
0
      else
911
0
        {
912
0
          (void) memcpy(update_pixels,source_pixels,npixels*sizeof(PixelPacket));
913
0
          (void) memset(update_indexes,OpaqueOpacity,npixels*sizeof(IndexPacket));
914
0
        }
915
0
    }
916
1.01M
  else
917
1.01M
    {
918
1.01M
      (void) memcpy(update_pixels,source_pixels,npixels*sizeof(PixelPacket));
919
1.01M
    }
920
921
1.01M
  return MagickPass;
922
1.01M
}
923
924
static MagickPassFail
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
0
{
937
0
  register long
938
0
    i;
939
940
0
  ARG_NOT_USED(mutable_data);
941
0
  ARG_NOT_USED(immutable_data);
942
0
  ARG_NOT_USED(source_image);
943
0
  ARG_NOT_USED(source_indexes);
944
0
  ARG_NOT_USED(update_image);
945
0
  ARG_NOT_USED(update_indexes);
946
0
  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
0
  for (i=0; i < npixels; i++)
954
0
    {
955
0
      update_pixels[i].red = source_pixels[i].red;
956
0
    }
957
958
0
  return MagickPass;
959
0
}
960
961
static MagickPassFail
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
0
{
974
0
  register long
975
0
    i;
976
977
0
  ARG_NOT_USED(mutable_data);
978
0
  ARG_NOT_USED(immutable_data);
979
0
  ARG_NOT_USED(source_image);
980
0
  ARG_NOT_USED(source_indexes);
981
0
  ARG_NOT_USED(update_image);
982
0
  ARG_NOT_USED(update_indexes);
983
0
  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
0
  for (i=0; i < npixels; i++)
991
0
    {
992
0
      update_pixels[i].green = source_pixels[i].green;
993
0
    }
994
995
0
  return MagickPass;
996
0
}
997
998
999
static MagickPassFail
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
0
{
1012
0
  register long
1013
0
    i;
1014
1015
0
  ARG_NOT_USED(mutable_data);
1016
0
  ARG_NOT_USED(immutable_data);
1017
0
  ARG_NOT_USED(source_image);
1018
0
  ARG_NOT_USED(source_indexes);
1019
0
  ARG_NOT_USED(update_image);
1020
0
  ARG_NOT_USED(update_indexes);
1021
0
  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
0
  for (i=0; i < npixels; i++)
1029
0
    {
1030
0
      update_pixels[i].blue = source_pixels[i].blue;
1031
0
    }
1032
1033
0
  return MagickPass;
1034
0
}
1035
1036
static MagickPassFail
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
0
{
1049
0
  register long
1050
0
    i;
1051
1052
0
  ARG_NOT_USED(mutable_data);
1053
0
  ARG_NOT_USED(immutable_data);
1054
0
  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
0
  if (update_image->colorspace == CMYKColorspace)
1062
0
    {
1063
0
      if (!source_image->matte)
1064
0
        {
1065
0
          for (i=0; i < npixels; i++)
1066
0
            {
1067
0
              update_indexes[i] =
1068
0
                (Quantum) (MaxRGB-PixelIntensityToQuantum(&source_pixels[i]));
1069
0
            }
1070
0
        }
1071
0
      else
1072
0
        {
1073
0
          for (i=0; i < npixels; i++)
1074
0
            {
1075
0
              update_indexes[i] = source_indexes[i];
1076
0
            }
1077
0
        }
1078
0
    }
1079
0
  else
1080
0
    {
1081
0
      if (!source_image->matte)
1082
0
        {
1083
0
          for (i=0; i < npixels; i++)
1084
0
            {
1085
0
              update_pixels[i].opacity =
1086
0
                (Quantum) (MaxRGB-PixelIntensityToQuantum(&source_pixels[i]));
1087
0
            }
1088
0
        }
1089
0
      else
1090
0
        {
1091
0
          for (i=0; i < npixels; i++)
1092
0
            {
1093
0
              update_pixels[i].opacity = source_pixels[i].opacity;
1094
0
            }
1095
0
        }
1096
0
    }
1097
1098
0
  return MagickPass;
1099
0
}
1100
1101
static MagickPassFail
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
0
{
1114
0
  register long
1115
0
    i;
1116
1117
0
  ARG_NOT_USED(mutable_data);
1118
0
  ARG_NOT_USED(immutable_data);
1119
0
  ARG_NOT_USED(source_image);
1120
0
  ARG_NOT_USED(source_pixels);
1121
0
  ARG_NOT_USED(source_indexes);
1122
0
  ARG_NOT_USED(exception);
1123
1124
  /*
1125
    Set destination pixels to transparent.
1126
  */
1127
0
  if (update_image->colorspace == CMYKColorspace)
1128
0
    {
1129
0
      update_image->matte=MagickTrue;
1130
0
      for (i=0; i < npixels; i++)
1131
0
        {
1132
0
          update_indexes[i] = TransparentOpacity;
1133
0
        }
1134
0
    }
1135
0
  else
1136
0
    {
1137
0
      for (i=0; i < npixels; i++)
1138
0
        {
1139
0
          update_pixels[i].opacity = TransparentOpacity;
1140
0
        }
1141
0
    }
1142
1143
0
  return MagickPass;
1144
0
}
1145
1146
1147
1148
static MagickPassFail
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
0
{
1161
0
  register long
1162
0
    i;
1163
1164
0
  PixelPacket
1165
0
    destination,
1166
0
    source;
1167
1168
0
  ARG_NOT_USED(mutable_data);
1169
0
  ARG_NOT_USED(immutable_data);
1170
0
  ARG_NOT_USED(exception);
1171
1172
0
  for (i=0; i < npixels; i++)
1173
0
    {
1174
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1175
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1176
1177
0
      destination.red=(Quantum)
1178
0
        (((double) source.opacity*source.red+
1179
0
          (MaxRGBDouble-source.opacity)*destination.red)/MaxRGBDouble+0.5);
1180
0
      destination.green=(Quantum)
1181
0
        (((double) source.opacity*source.green+
1182
0
          (MaxRGBDouble-source.opacity)*destination.green)/MaxRGBDouble+0.5);
1183
0
      destination.blue=(Quantum)
1184
0
        (((double) source.opacity*source.blue+
1185
0
          (MaxRGBDouble-source.opacity)*destination.blue)/MaxRGBDouble+0.5);
1186
0
      destination.opacity=OpaqueOpacity;
1187
1188
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1189
0
    }
1190
1191
0
  return MagickPass;
1192
0
}
1193
1194
1195
static MagickPassFail
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
0
{
1208
0
  const CompositeOptions_t
1209
0
    *options = (const CompositeOptions_t *) immutable_data;
1210
1211
0
  const double
1212
0
    percent_brightness = options->percent_brightness;
1213
1214
1215
0
  double
1216
0
    midpoint;
1217
1218
0
  register long
1219
0
    i;
1220
1221
0
  PixelPacket
1222
0
    destination,
1223
0
    source;
1224
1225
0
  ARG_NOT_USED(mutable_data);
1226
0
  ARG_NOT_USED(exception);
1227
1228
0
  midpoint=((double) MaxRGB+1.0)/2;
1229
0
  for (i=0; i < npixels; i++)
1230
0
    {
1231
0
      double
1232
0
        offset;
1233
1234
0
      double
1235
0
        brightness,
1236
0
        hue,
1237
0
        saturation;
1238
1239
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1240
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1241
1242
0
      offset=(long) (PixelIntensityToQuantum(&source)-midpoint);
1243
0
      if (offset == 0)
1244
0
        break;
1245
0
      TransformHSL(destination.red,destination.green,destination.blue,
1246
0
                   &hue,&saturation,&brightness);
1247
0
      brightness+=(percent_brightness*offset)/midpoint;
1248
0
      if (brightness < 0.0)
1249
0
        brightness=0.0;
1250
0
      else
1251
0
        if (brightness > 1.0)
1252
0
          brightness=1.0;
1253
0
      HSLTransform(hue,saturation,brightness,&destination.red,
1254
0
                   &destination.green,&destination.blue);
1255
1256
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1257
0
    }
1258
1259
0
  return MagickPass;
1260
0
}
1261
1262
1263
static MagickPassFail
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
0
{
1276
0
  const CompositeOptions_t
1277
0
    *options = (const CompositeOptions_t *) immutable_data;
1278
1279
0
  const double
1280
0
    amount = options->amount,
1281
0
    threshold = options->threshold;
1282
1283
0
  register long
1284
0
    i;
1285
1286
0
  PixelPacket
1287
0
    destination,
1288
0
    source;
1289
1290
0
  ARG_NOT_USED(mutable_data);
1291
0
  ARG_NOT_USED(exception);
1292
1293
0
  for (i=0; i < npixels; i++)
1294
0
    {
1295
0
      double
1296
0
        value;
1297
1298
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1299
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1300
1301
0
      value=destination.red-(double) source.red;
1302
0
      if (fabs(2.0*value) < threshold)
1303
0
        value=destination.red;
1304
0
      else
1305
0
        value=destination.red+(value*amount);
1306
0
      destination.red=RoundDoubleToQuantum(value);
1307
1308
0
      value=destination.green-(double) source.green;
1309
0
      if (fabs(2.0*value) < threshold)
1310
0
        value=destination.green;
1311
0
      else
1312
0
        value=destination.green+(value*amount);
1313
0
      destination.green=RoundDoubleToQuantum(value);
1314
1315
0
      value=destination.blue-(double) source.blue;
1316
0
      if (fabs(2.0*value) < threshold)
1317
0
        value=destination.blue;
1318
0
      else
1319
0
        value=destination.blue+(value*amount);
1320
0
      destination.blue=RoundDoubleToQuantum(value);
1321
1322
0
      value=destination.opacity-(double) source.opacity;
1323
0
      if (fabs(2.0*value) < threshold)
1324
0
        value=destination.opacity;
1325
0
      else
1326
0
        value=destination.opacity+(value*amount);
1327
0
      destination.opacity=RoundDoubleToQuantum(value);
1328
1329
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1330
0
    }
1331
1332
0
  return MagickPass;
1333
0
}
1334
1335
1336
static MagickPassFail
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
0
{
1349
0
  register long
1350
0
    i;
1351
1352
0
  PixelPacket
1353
0
    destination,
1354
0
    source;
1355
1356
0
  ARG_NOT_USED(mutable_data);
1357
0
  ARG_NOT_USED(immutable_data);
1358
0
  ARG_NOT_USED(exception);
1359
1360
0
  for (i=0; i < npixels; i++)
1361
0
    {
1362
0
      double gamma;
1363
0
      double source_alpha;
1364
0
      double dest_alpha;
1365
0
      double composite;
1366
1367
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1368
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1369
1370
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
1371
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
1372
1373
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
1374
0
        (1.0-source_alpha)*(1.0-dest_alpha);
1375
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
1376
1377
0
      composite=MaxRGBDouble*(1.0-gamma);
1378
0
      destination.opacity=RoundDoubleToQuantum(composite);
1379
1380
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
1381
1382
0
      composite=(MagickFmin((double)source.red,(double)destination.red)*
1383
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
1384
0
                 source.red*(1.0-source_alpha)*dest_alpha+
1385
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
1386
0
      destination.red=RoundDoubleToQuantum(composite);
1387
1388
0
      composite=(MagickFmin((double)source.green,(double)destination.green)*
1389
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
1390
0
                 source.green*(1.0-source_alpha)*dest_alpha+
1391
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
1392
0
      destination.green=RoundDoubleToQuantum(composite);
1393
1394
0
      composite=(MagickFmin((double)source.blue,(double)destination.blue)*
1395
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
1396
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
1397
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
1398
0
      destination.blue=RoundDoubleToQuantum(composite);
1399
1400
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1401
0
    }
1402
1403
0
  return MagickPass;
1404
0
}
1405
1406
1407
static MagickPassFail
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
0
{
1420
0
  register long
1421
0
    i;
1422
1423
0
  PixelPacket
1424
0
    destination,
1425
0
    source;
1426
1427
0
  ARG_NOT_USED(mutable_data);
1428
0
  ARG_NOT_USED(immutable_data);
1429
0
  ARG_NOT_USED(exception);
1430
1431
0
  for (i=0; i < npixels; i++)
1432
0
    {
1433
0
      double gamma;
1434
0
      double source_alpha;
1435
0
      double dest_alpha;
1436
0
      double composite;
1437
1438
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1439
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1440
1441
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
1442
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
1443
1444
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
1445
0
        (1.0-source_alpha)*(1.0-dest_alpha);
1446
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
1447
1448
0
      composite=MaxRGBDouble*(1.0-gamma);
1449
0
      destination.opacity=RoundDoubleToQuantum(composite);
1450
1451
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
1452
1453
0
      composite=(MagickFmax((double)source.red,(double)destination.red)*
1454
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
1455
0
                 source.red*(1.0-source_alpha)*dest_alpha+
1456
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
1457
0
      destination.red=RoundDoubleToQuantum(composite);
1458
1459
0
      composite=(MagickFmax((double)source.green,(double)destination.green)*
1460
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
1461
0
                 source.green*(1.0-source_alpha)*dest_alpha+
1462
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
1463
0
      destination.green=RoundDoubleToQuantum(composite);
1464
1465
0
      composite=(MagickFmax((double)source.blue,(double)destination.blue)*
1466
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
1467
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
1468
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
1469
0
      destination.blue=RoundDoubleToQuantum(composite);
1470
1471
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1472
0
    }
1473
1474
0
  return MagickPass;
1475
0
}
1476
1477
1478
static MagickPassFail
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
0
{
1491
0
  register long
1492
0
    i;
1493
1494
0
  PixelPacket
1495
0
    destination,
1496
0
    source;
1497
1498
0
  ARG_NOT_USED(mutable_data);
1499
0
  ARG_NOT_USED(immutable_data);
1500
0
  ARG_NOT_USED(exception);
1501
1502
0
  for (i=0; i < npixels; i++)
1503
0
    {
1504
0
      double
1505
0
        brightness,
1506
0
        hue,
1507
0
        saturation,
1508
0
        sans;
1509
1510
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1511
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1512
1513
0
      if (source.opacity == TransparentOpacity)
1514
0
        {
1515
0
        }
1516
0
      else if (destination.opacity == TransparentOpacity)
1517
0
        {
1518
0
          destination=source;
1519
0
        }
1520
0
      else
1521
0
        {
1522
0
          TransformHSL(destination.red,destination.green,destination.blue,
1523
0
                       &hue,&saturation,&brightness);
1524
0
          TransformHSL(source.red,source.green,source.blue,&hue,&sans,&sans);
1525
0
          HSLTransform(hue,saturation,brightness,&destination.red,
1526
0
                       &destination.green,&destination.blue);
1527
0
          if (source.opacity < destination.opacity)
1528
0
            destination.opacity=source.opacity;
1529
0
        }
1530
1531
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1532
0
    }
1533
1534
0
  return MagickPass;
1535
0
}
1536
1537
1538
static MagickPassFail
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
0
{
1551
0
  register long
1552
0
    i;
1553
1554
0
  PixelPacket
1555
0
    destination,
1556
0
    source;
1557
1558
0
  ARG_NOT_USED(mutable_data);
1559
0
  ARG_NOT_USED(immutable_data);
1560
0
  ARG_NOT_USED(exception);
1561
1562
0
  for (i=0; i < npixels; i++)
1563
0
    {
1564
0
      double
1565
0
        brightness,
1566
0
        hue,
1567
0
        saturation,
1568
0
        sans;
1569
1570
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1571
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1572
1573
0
      if (source.opacity == TransparentOpacity)
1574
0
        {
1575
0
        }
1576
0
      else if (destination.opacity == TransparentOpacity)
1577
0
        {
1578
0
          destination=source;
1579
0
        }
1580
0
      else
1581
0
        {
1582
0
          TransformHSL(destination.red,destination.green,destination.blue,
1583
0
                       &hue,&saturation,&brightness);
1584
0
          TransformHSL(source.red,source.green,source.blue,&sans,&saturation,
1585
0
                       &sans);
1586
0
          HSLTransform(hue,saturation,brightness,&destination.red,
1587
0
                       &destination.green,&destination.blue);
1588
0
          if (source.opacity < destination.opacity)
1589
0
            destination.opacity=source.opacity;
1590
0
        }
1591
1592
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1593
0
    }
1594
1595
0
  return MagickPass;
1596
0
}
1597
1598
1599
static MagickPassFail
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
0
{
1612
0
  register long
1613
0
    i;
1614
1615
0
  PixelPacket
1616
0
    destination,
1617
0
    source;
1618
1619
0
  ARG_NOT_USED(mutable_data);
1620
0
  ARG_NOT_USED(immutable_data);
1621
0
  ARG_NOT_USED(exception);
1622
1623
0
  for (i=0; i < npixels; i++)
1624
0
    {
1625
0
      double
1626
0
        brightness,
1627
0
        hue,
1628
0
        saturation,
1629
0
        sans;
1630
1631
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1632
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1633
1634
0
      if (source.opacity == TransparentOpacity)
1635
0
        {
1636
0
        }
1637
0
      else if (destination.opacity == TransparentOpacity)
1638
0
        {
1639
0
          destination=source;
1640
0
        }
1641
0
      else
1642
0
        {
1643
0
          TransformHSL(destination.red,destination.green,destination.blue,
1644
0
                       &sans,&sans,&brightness);
1645
0
          TransformHSL(source.red,source.green,source.blue,&hue,&saturation,
1646
0
                       &sans);
1647
0
          HSLTransform(hue,saturation,brightness,&destination.red,
1648
0
                       &destination.green,&destination.blue);
1649
0
          if (source.opacity < destination.opacity)
1650
0
            destination.opacity=source.opacity;
1651
0
        }
1652
1653
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1654
0
    }
1655
1656
0
  return MagickPass;
1657
0
}
1658
1659
1660
static MagickPassFail
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
0
{
1673
0
  register long
1674
0
    i;
1675
1676
0
  PixelPacket
1677
0
    destination,
1678
0
    source;
1679
1680
0
  ARG_NOT_USED(mutable_data);
1681
0
  ARG_NOT_USED(immutable_data);
1682
0
  ARG_NOT_USED(exception);
1683
1684
0
  for (i=0; i < npixels; i++)
1685
0
    {
1686
0
      double
1687
0
        brightness,
1688
0
        hue,
1689
0
        saturation,
1690
0
        sans;
1691
1692
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1693
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1694
1695
0
      if (source.opacity == TransparentOpacity)
1696
0
        {
1697
0
        }
1698
0
      else if (destination.opacity == TransparentOpacity)
1699
0
        {
1700
0
          destination=source;
1701
0
        }
1702
0
      else
1703
0
        {
1704
0
          TransformHSL(destination.red,destination.green,destination.blue,
1705
0
                       &hue,&saturation,&brightness);
1706
0
          TransformHSL(source.red,source.green,source.blue,&sans,&sans,
1707
0
                       &brightness);
1708
0
          HSLTransform(hue,saturation,brightness,&destination.red,
1709
0
                       &destination.green,&destination.blue);
1710
0
          if (source.opacity < destination.opacity)
1711
0
            destination.opacity=source.opacity;
1712
0
        }
1713
1714
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1715
0
    }
1716
1717
0
  return MagickPass;
1718
0
}
1719
1720
1721
static MagickPassFail
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
0
{
1734
0
  register long
1735
0
    i;
1736
1737
0
  PixelPacket
1738
0
    destination,
1739
0
    source;
1740
1741
0
  ARG_NOT_USED(mutable_data);
1742
0
  ARG_NOT_USED(immutable_data);
1743
0
  ARG_NOT_USED(exception);
1744
1745
  /*
1746
    Input colors are complimented and multiplied, then the product is complimented again.
1747
  */
1748
1749
1750
0
  for (i=0; i < npixels; i++)
1751
0
    {
1752
0
      double gamma;
1753
0
      double source_alpha;
1754
0
      double dest_alpha;
1755
0
      double composite;
1756
1757
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1758
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1759
1760
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
1761
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
1762
1763
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
1764
0
        (1.0-source_alpha)*(1.0-dest_alpha);
1765
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
1766
1767
0
      composite=MaxRGBDouble*(1.0-gamma);
1768
0
      destination.opacity=RoundDoubleToQuantum(composite);
1769
1770
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
1771
1772
0
      composite=(((double)source.red+(double)destination.red-
1773
0
                  ((double)source.red*(double)destination.red)/MaxRGBDouble)*
1774
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
1775
0
                 source.red*(1.0-source_alpha)*dest_alpha+
1776
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
1777
0
      destination.red=RoundDoubleToQuantum(composite);
1778
1779
0
      composite=(((double)source.green+(double)destination.green-
1780
0
                  ((double)source.green*(double)destination.green)/MaxRGBDouble)*
1781
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
1782
0
                 source.green*(1.0-source_alpha)*dest_alpha+
1783
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
1784
0
      destination.green=RoundDoubleToQuantum(composite);
1785
1786
0
      composite=(((double)source.blue+(double)destination.blue-
1787
0
                  ((double)source.blue*(double)destination.blue)/MaxRGBDouble)*
1788
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
1789
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
1790
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
1791
0
      destination.blue=RoundDoubleToQuantum(composite);
1792
1793
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1794
0
    }
1795
1796
0
  return MagickPass;
1797
0
}
1798
1799
1800
static MagickPassFail
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
0
{
1813
0
  register long
1814
0
    i;
1815
1816
0
  PixelPacket
1817
0
    destination,
1818
0
    source;
1819
1820
0
  ARG_NOT_USED(mutable_data);
1821
0
  ARG_NOT_USED(immutable_data);
1822
0
  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
0
  for (i=0; i < npixels; i++)
1830
0
    {
1831
0
      double gamma;
1832
0
      double source_alpha;
1833
0
      double dest_alpha;
1834
0
      double composite;
1835
1836
0
      double
1837
0
        value;
1838
1839
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1840
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1841
1842
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
1843
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
1844
1845
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
1846
0
            (1.0-source_alpha)*(1.0-dest_alpha);
1847
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
1848
1849
0
      composite=MaxRGBDouble*(1.0-gamma);
1850
0
      destination.opacity=RoundDoubleToQuantum(composite);
1851
1852
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
1853
1854
0
      if(destination.red < (0.5*MaxRGBDouble))
1855
0
        value=((double) source.red*destination.red*2.0)/MaxRGBDouble;
1856
0
      else
1857
0
        value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.red/MaxRGBDouble) *
1858
0
                               (1.0-(double)destination.red/MaxRGBDouble));
1859
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
1860
0
                 source.red*(1.0-source_alpha)*dest_alpha+
1861
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
1862
0
      destination.red=RoundDoubleToQuantum(composite);
1863
1864
0
      if(destination.green < (0.5*MaxRGBDouble))
1865
0
        value=((double) source.green*destination.green*2.0)/MaxRGBDouble;
1866
0
      else
1867
0
        value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.green/MaxRGBDouble) *
1868
0
                               (1.0-(double)destination.green/MaxRGBDouble));
1869
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
1870
0
                 source.green*(1.0-source_alpha)*dest_alpha+
1871
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
1872
0
      destination.green=RoundDoubleToQuantum(composite);
1873
1874
0
      if(destination.blue < (0.5*MaxRGBDouble))
1875
0
        value=((double) source.blue*destination.blue*2.0)/MaxRGBDouble;
1876
0
      else
1877
0
        value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.blue/MaxRGBDouble) *
1878
0
                               (1.0-(double)destination.blue/MaxRGBDouble));
1879
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
1880
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
1881
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
1882
0
      destination.blue=RoundDoubleToQuantum(composite);
1883
1884
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1885
0
    }
1886
1887
0
  return MagickPass;
1888
0
}
1889
1890
1891
static MagickPassFail
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
0
{
1904
0
  register long
1905
0
    i;
1906
1907
0
  ARG_NOT_USED(mutable_data);
1908
0
  ARG_NOT_USED(immutable_data);
1909
0
  ARG_NOT_USED(source_indexes);
1910
0
  ARG_NOT_USED(update_indexes);
1911
0
  ARG_NOT_USED(exception);
1912
1913
  /*
1914
    Copy the CMYK Black (K) channel into the image.
1915
  */
1916
0
  if ((update_image->colorspace == CMYKColorspace) &&
1917
0
      (source_image->colorspace == CMYKColorspace))
1918
0
    {
1919
0
      for (i=0; i < npixels; i++)
1920
0
        {
1921
0
          update_pixels[i].opacity=source_pixels[i].opacity;
1922
0
        }
1923
0
    }
1924
0
  else
1925
0
    {
1926
0
      for (i=0; i < npixels; i++)
1927
0
        {
1928
0
          update_pixels[i].opacity = PixelIntensityToQuantum(&source_pixels[i]);
1929
0
        }
1930
0
    }
1931
1932
0
  return MagickPass;
1933
0
}
1934
1935
1936
static MagickPassFail
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
0
{
1949
0
  register long
1950
0
    i;
1951
1952
0
  PixelPacket
1953
0
    destination,
1954
0
    source;
1955
1956
0
  ARG_NOT_USED(mutable_data);
1957
0
  ARG_NOT_USED(immutable_data);
1958
0
  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
0
  for (i=0; i < npixels; i++)
1967
0
    {
1968
0
      double
1969
0
        composite,
1970
0
        divisor;
1971
1972
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
1973
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
1974
1975
      /* Avoid division by zero error, use value near zero instead */
1976
0
      divisor=((destination.red != 0.0) ? destination.red : 1.0/MaxRGBDouble);
1977
0
      composite=((double) (source.red*MaxRGBDouble)/divisor);
1978
0
      destination.red=RoundDoubleToQuantum(composite);
1979
1980
0
      divisor=((destination.green != 0.0) ? destination.green : 1.0/MaxRGBDouble);
1981
0
      composite=((double) (source.green*MaxRGBDouble)/divisor);
1982
0
      destination.green=RoundDoubleToQuantum(composite);
1983
1984
0
      divisor=((destination.blue != 0.0) ? destination.blue : 1.0/MaxRGBDouble);
1985
0
      composite=((double) (source.blue*MaxRGBDouble)/divisor);
1986
0
      destination.blue=RoundDoubleToQuantum(composite);
1987
1988
0
      divisor=((destination.opacity != 0.0) ? destination.opacity : 1.0/MaxRGBDouble);
1989
0
      composite=((double) (source.opacity*MaxRGBDouble)/divisor);
1990
0
      destination.opacity=RoundDoubleToQuantum(composite);
1991
1992
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
1993
0
    }
1994
1995
0
  return MagickPass;
1996
0
}
1997
1998
1999
static MagickPassFail
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
0
{
2012
0
  register long
2013
0
    i;
2014
2015
0
  PixelPacket
2016
0
    destination,
2017
0
    source;
2018
2019
0
  ARG_NOT_USED(mutable_data);
2020
0
  ARG_NOT_USED(immutable_data);
2021
0
  ARG_NOT_USED(exception);
2022
2023
  /*
2024
    The result of base-image gets lighting effects by change-image.
2025
  */
2026
2027
0
  for (i=0; i < npixels; i++)
2028
0
    {
2029
0
      double gamma;
2030
0
      double source_alpha;
2031
0
      double dest_alpha;
2032
0
      double composite;
2033
2034
0
      double
2035
0
        value;
2036
2037
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2038
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2039
2040
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2041
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2042
2043
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2044
0
            (1.0-source_alpha)*(1.0-dest_alpha);
2045
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2046
2047
0
      composite=MaxRGBDouble*(1.0-gamma);
2048
0
      destination.opacity=RoundDoubleToQuantum(composite);
2049
2050
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2051
2052
0
      if(source.red <= (0.5*MaxRGBDouble))
2053
0
        value=((double) source.red*destination.red*2.0)/MaxRGBDouble;
2054
0
      else
2055
0
        value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.red/MaxRGBDouble) *
2056
0
                               (1.0-(double)destination.red/MaxRGBDouble));
2057
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2058
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2059
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2060
0
      destination.red=RoundDoubleToQuantum(composite);
2061
2062
0
      if(source.green <= (0.5*MaxRGBDouble))
2063
0
        value=((double) source.green*destination.green*2.0)/MaxRGBDouble;
2064
0
      else
2065
0
        value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.green/MaxRGBDouble) *
2066
0
                               (1.0-(double)destination.green/MaxRGBDouble));
2067
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2068
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2069
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2070
0
      destination.green=RoundDoubleToQuantum(composite);
2071
2072
0
      if(source.blue <= (0.5*MaxRGBDouble))
2073
0
        value=((double) source.blue*destination.blue*2.0)/MaxRGBDouble;
2074
0
      else
2075
0
        value= MaxRGBDouble * (1.0 - 2.0 * (1.0-(double) source.blue/MaxRGBDouble) *
2076
0
                               (1.0-(double)destination.blue/MaxRGBDouble));
2077
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2078
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2079
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2080
0
      destination.blue=RoundDoubleToQuantum(composite);
2081
2082
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2083
0
    }
2084
2085
0
  return MagickPass;
2086
0
}
2087
2088
2089
static MagickPassFail
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
0
{
2102
0
  register long
2103
0
    i;
2104
2105
0
  PixelPacket
2106
0
    destination,
2107
0
    source;
2108
2109
0
  ARG_NOT_USED(mutable_data);
2110
0
  ARG_NOT_USED(immutable_data);
2111
0
  ARG_NOT_USED(exception);
2112
2113
  /*
2114
   A similar effect to Difference, but lower in contrast.
2115
  */
2116
2117
2118
0
  for (i=0; i < npixels; i++)
2119
0
    {
2120
0
      double gamma;
2121
0
      double source_alpha;
2122
0
      double dest_alpha;
2123
0
      double composite;
2124
2125
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2126
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2127
2128
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2129
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2130
2131
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2132
0
        (1.0-source_alpha)*(1.0-dest_alpha);
2133
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2134
2135
0
      composite=MaxRGBDouble*(1.0-gamma);
2136
0
      destination.opacity=RoundDoubleToQuantum(composite);
2137
2138
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2139
2140
0
      composite=(((double)source.red+(double)destination.red-
2141
0
                  2*((double)source.red*(double)destination.red)/MaxRGBDouble)*
2142
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
2143
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2144
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2145
0
      destination.red=RoundDoubleToQuantum(composite);
2146
2147
0
      composite=(((double)source.green+(double)destination.green-
2148
0
                  2*((double)source.green*(double)destination.green)/MaxRGBDouble)*
2149
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
2150
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2151
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2152
0
      destination.green=RoundDoubleToQuantum(composite);
2153
2154
0
      composite=(((double)source.blue+(double)destination.blue-
2155
0
                  2*((double)source.blue*(double)destination.blue)/MaxRGBDouble)*
2156
0
                 (1.0-source_alpha)*(1.0-dest_alpha)+
2157
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2158
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2159
0
      destination.blue=RoundDoubleToQuantum(composite);
2160
2161
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2162
0
    }
2163
2164
0
  return MagickPass;
2165
0
}
2166
2167
2168
static MagickPassFail
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
0
{
2181
0
  register long
2182
0
    i;
2183
2184
0
  PixelPacket
2185
0
    destination,
2186
0
    source;
2187
2188
0
  ARG_NOT_USED(mutable_data);
2189
0
  ARG_NOT_USED(immutable_data);
2190
0
  ARG_NOT_USED(exception);
2191
2192
  /*
2193
    Brightens the destination color by an amount depending on the source color
2194
  */
2195
2196
0
  for (i=0; i < npixels; i++)
2197
0
    {
2198
0
      double gamma;
2199
0
      double source_alpha;
2200
0
      double dest_alpha;
2201
0
      double composite;
2202
2203
0
      double
2204
0
        value;
2205
2206
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2207
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2208
2209
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2210
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2211
2212
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2213
0
            (1.0-source_alpha)*(1.0-dest_alpha);
2214
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2215
2216
0
      composite=MaxRGBDouble*(1.0-gamma);
2217
0
      destination.opacity=RoundDoubleToQuantum(composite);
2218
2219
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2220
2221
0
      if(source.red == MaxRGB)
2222
0
        value = MaxRGBDouble;
2223
0
      else
2224
0
        value=MagickFmin(MaxRGBDouble,(double)destination.red/(1.0-(double) source.red/MaxRGBDouble));
2225
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2226
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2227
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2228
0
      destination.red=RoundDoubleToQuantum(composite);
2229
2230
0
      if(source.green == MaxRGB)
2231
0
        value = MaxRGBDouble;
2232
0
      else
2233
0
        value=MagickFmin(MaxRGBDouble,(double)destination.green/(1.0-(double) source.green/MaxRGBDouble));
2234
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2235
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2236
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2237
0
      destination.green=RoundDoubleToQuantum(composite);
2238
2239
0
      if(source.blue == MaxRGB)
2240
0
        value = MaxRGBDouble;
2241
0
      else
2242
0
        value=MagickFmin(MaxRGBDouble,(double)destination.blue/(1.0-(double) source.blue/MaxRGBDouble));
2243
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2244
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2245
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2246
0
      destination.blue=RoundDoubleToQuantum(composite);
2247
2248
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2249
0
    }
2250
2251
0
  return MagickPass;
2252
0
}
2253
2254
2255
static MagickPassFail
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
0
{
2268
0
  register long
2269
0
    i;
2270
2271
0
  PixelPacket
2272
0
    destination,
2273
0
    source;
2274
2275
0
  ARG_NOT_USED(mutable_data);
2276
0
  ARG_NOT_USED(immutable_data);
2277
0
  ARG_NOT_USED(exception);
2278
2279
  /*
2280
    Darkens the destination color by an amount depending on the source color
2281
  */
2282
2283
0
  for (i=0; i < npixels; i++)
2284
0
    {
2285
0
      double gamma;
2286
0
      double source_alpha;
2287
0
      double dest_alpha;
2288
0
      double composite;
2289
2290
0
      double
2291
0
        value;
2292
2293
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2294
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2295
2296
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2297
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2298
2299
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2300
0
            (1.0-source_alpha)*(1.0-dest_alpha);
2301
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2302
2303
0
      composite=MaxRGBDouble*(1.0-gamma);
2304
0
      destination.opacity=RoundDoubleToQuantum(composite);
2305
2306
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2307
2308
0
      if(source.red == 0)
2309
0
        value=0;
2310
0
      else
2311
0
        value = MaxRGBDouble-MagickFmin(MaxRGBDouble,(MaxRGBDouble-(double)destination.red)/((double) source.red/MaxRGBDouble));
2312
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2313
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2314
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2315
0
      destination.red=RoundDoubleToQuantum(composite);
2316
2317
0
      if(source.green == 0)
2318
0
        value=0;
2319
0
      else
2320
0
        value = MaxRGBDouble-MagickFmin(MaxRGBDouble,(MaxRGBDouble-(double)destination.green)/((double) source.green/MaxRGBDouble));
2321
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2322
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2323
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2324
0
      destination.green=RoundDoubleToQuantum(composite);
2325
2326
0
      if(source.blue == 0)
2327
0
        value=0;
2328
0
      else
2329
0
        value = MaxRGBDouble-MagickFmin(MaxRGBDouble,(MaxRGBDouble-(double)destination.blue)/((double) source.blue/MaxRGBDouble));
2330
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2331
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2332
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2333
0
      destination.blue=RoundDoubleToQuantum(composite);
2334
2335
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2336
0
    }
2337
2338
0
  return MagickPass;
2339
0
}
2340
2341
2342
static MagickPassFail
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
0
{
2355
0
  register long
2356
0
    i;
2357
2358
0
  PixelPacket
2359
0
    destination,
2360
0
    source;
2361
2362
0
  ARG_NOT_USED(mutable_data);
2363
0
  ARG_NOT_USED(immutable_data);
2364
0
  ARG_NOT_USED(exception);
2365
2366
  /*
2367
    Darkens or lightens, depending on the source color
2368
  */
2369
2370
0
  for (i=0; i < npixels; i++)
2371
0
    {
2372
0
      double gamma;
2373
0
      double source_alpha;
2374
0
      double dest_alpha;
2375
0
      double composite;
2376
0
      double ramp;
2377
2378
0
      double
2379
0
        value;
2380
2381
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2382
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2383
2384
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2385
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2386
2387
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2388
0
            (1.0-source_alpha)*(1.0-dest_alpha);
2389
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2390
2391
0
      composite=MaxRGBDouble*(1.0-gamma);
2392
0
      destination.opacity=RoundDoubleToQuantum(composite);
2393
2394
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2395
2396
2397
0
      if(source.red <= (0.5*MaxRGBDouble))
2398
0
        value=destination.red*(1.0 - (1.0-(double)destination.red/MaxRGBDouble)*(1.0-2.0*(double)source.red/MaxRGBDouble));
2399
0
      else
2400
0
      {
2401
0
        if(destination.red <= (0.25*MaxRGBDouble))
2402
0
          ramp = ((16.0*((double)destination.red/MaxRGBDouble)-12.0)*((double)destination.red/MaxRGBDouble)+4.0)*(double)destination.red/MaxRGBDouble;
2403
0
        else
2404
0
          ramp = sqrt((double)destination.red/MaxRGBDouble);
2405
0
        value=destination.red + ((2.0*source.red)-MaxRGBDouble)*(ramp-(double)destination.red/MaxRGBDouble);
2406
0
      }
2407
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2408
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2409
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2410
0
      destination.red=RoundDoubleToQuantum(composite);
2411
2412
0
      if(source.green <= (0.5*MaxRGBDouble))
2413
0
        value=destination.green*(1.0 - (1.0-(double)destination.green/MaxRGBDouble)*(1.0-2.0*(double)source.green/MaxRGBDouble));
2414
0
      else
2415
0
      {
2416
0
        if(destination.green <= (0.25*MaxRGBDouble))
2417
0
          ramp = ((16.0*((double)destination.green/MaxRGBDouble)-12.0)*((double)destination.green/MaxRGBDouble)+4.0)*(double)destination.green/MaxRGBDouble;
2418
0
        else
2419
0
          ramp = sqrt((double)destination.green/MaxRGBDouble);
2420
0
        value=destination.green + ((2.0*source.green)-MaxRGBDouble)*(ramp-(double)destination.green/MaxRGBDouble);
2421
0
      }
2422
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2423
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2424
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2425
0
      destination.green=RoundDoubleToQuantum(composite);
2426
2427
0
      if(source.blue <= (0.5*MaxRGBDouble))
2428
0
        value=destination.blue*(1.0 - (1.0-(double)destination.blue/MaxRGBDouble)*(1.0-2.0*(double)source.blue/MaxRGBDouble));
2429
0
      else
2430
0
      {
2431
0
        if(destination.blue <= (0.25*MaxRGBDouble))
2432
0
          ramp = ((16.0*((double)destination.blue/MaxRGBDouble)-12.0)*((double)destination.blue/MaxRGBDouble)+4.0)*(double)destination.blue/MaxRGBDouble;
2433
0
        else
2434
0
          ramp = sqrt((double)destination.blue/MaxRGBDouble);
2435
0
        value=destination.blue + ((2.0*source.blue)-MaxRGBDouble)*(ramp-(double)destination.blue/MaxRGBDouble);
2436
0
      }
2437
0
      composite=(value*(1.0-source_alpha)*(1.0-dest_alpha)+
2438
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2439
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2440
0
      destination.blue=RoundDoubleToQuantum(composite);
2441
2442
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2443
0
    }
2444
2445
0
  return MagickPass;
2446
0
}
2447
2448
2449
static MagickPassFail
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
0
{
2462
0
  register long
2463
0
    i;
2464
2465
0
  PixelPacket
2466
0
    destination,
2467
0
    source;
2468
2469
0
  ARG_NOT_USED(mutable_data);
2470
0
  ARG_NOT_USED(immutable_data);
2471
0
  ARG_NOT_USED(exception);
2472
2473
  /*
2474
    Inverts the sum of the inverted images
2475
  */
2476
2477
2478
0
  for (i=0; i < npixels; i++)
2479
0
    {
2480
0
      double gamma;
2481
0
      double source_alpha;
2482
0
      double dest_alpha;
2483
0
      double composite;
2484
0
      double value;
2485
2486
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2487
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2488
2489
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2490
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2491
2492
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2493
0
        (1.0-source_alpha)*(1.0-dest_alpha);
2494
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2495
2496
0
      composite=MaxRGBDouble*(1.0-gamma);
2497
0
      destination.opacity=RoundDoubleToQuantum(composite);
2498
2499
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2500
2501
0
      value = MagickFmax(0.0,(double)source.red+(double)destination.red-MaxRGBDouble);
2502
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2503
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2504
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2505
0
      destination.red=RoundDoubleToQuantum(composite);
2506
2507
0
      value = MagickFmax(0.0,(double)source.green+(double)destination.green-MaxRGBDouble);
2508
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2509
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2510
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2511
0
      destination.green=RoundDoubleToQuantum(composite);
2512
2513
0
      value = MagickFmax(0.0,(double)source.blue+(double)destination.blue-MaxRGBDouble);
2514
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2515
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2516
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2517
0
      destination.blue=RoundDoubleToQuantum(composite);
2518
2519
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2520
0
    }
2521
2522
0
  return MagickPass;
2523
0
}
2524
2525
2526
static MagickPassFail
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
0
{
2539
0
  register long
2540
0
    i;
2541
2542
0
  PixelPacket
2543
0
    destination,
2544
0
    source;
2545
2546
0
  ARG_NOT_USED(mutable_data);
2547
0
  ARG_NOT_USED(immutable_data);
2548
0
  ARG_NOT_USED(exception);
2549
2550
  /*
2551
    A simple alpha-blended sum of the images
2552
  */
2553
2554
2555
0
  for (i=0; i < npixels; i++)
2556
0
    {
2557
0
      double gamma;
2558
0
      double source_alpha;
2559
0
      double dest_alpha;
2560
0
      double composite;
2561
0
      double value;
2562
2563
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2564
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2565
2566
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2567
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2568
2569
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2570
0
        (1.0-source_alpha)*(1.0-dest_alpha);
2571
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2572
2573
0
      composite=MaxRGBDouble*(1.0-gamma);
2574
0
      destination.opacity=RoundDoubleToQuantum(composite);
2575
2576
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2577
2578
0
      value = MagickFmin(MaxRGBDouble,(double)source.red+(double)destination.red);
2579
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2580
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2581
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2582
0
      destination.red=RoundDoubleToQuantum(composite);
2583
2584
0
      value = MagickFmin(MaxRGBDouble,(double)source.green+(double)destination.green);
2585
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2586
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2587
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2588
0
      destination.green=RoundDoubleToQuantum(composite);
2589
2590
0
      value = MagickFmin(MaxRGBDouble,(double)source.blue+(double)destination.blue);
2591
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2592
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2593
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2594
0
      destination.blue=RoundDoubleToQuantum(composite);
2595
2596
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2597
0
    }
2598
2599
0
  return MagickPass;
2600
0
}
2601
2602
2603
static MagickPassFail
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
0
{
2616
0
  register long
2617
0
    i;
2618
2619
0
  PixelPacket
2620
0
    destination,
2621
0
    source;
2622
2623
0
  ARG_NOT_USED(mutable_data);
2624
0
  ARG_NOT_USED(immutable_data);
2625
0
  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
0
  for (i=0; i < npixels; i++)
2633
0
    {
2634
0
      double gamma;
2635
0
      double source_alpha;
2636
0
      double dest_alpha;
2637
0
      double composite;
2638
0
      double value;
2639
2640
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2641
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2642
2643
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2644
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2645
2646
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2647
0
        (1.0-source_alpha)*(1.0-dest_alpha);
2648
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2649
2650
0
      composite=MaxRGBDouble*(1.0-gamma);
2651
0
      destination.opacity=RoundDoubleToQuantum(composite);
2652
2653
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2654
2655
0
      value = MagickFmin(MaxRGBDouble,MagickFmax(0.0,2.0*(double)source.red+(double)destination.red-MaxRGBDouble));
2656
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2657
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2658
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2659
0
      destination.red=RoundDoubleToQuantum(composite);
2660
2661
0
      value = MagickFmin(MaxRGBDouble,MagickFmax(0.0,2.0*(double)source.green+(double)destination.green-MaxRGBDouble));
2662
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2663
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2664
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2665
0
      destination.green=RoundDoubleToQuantum(composite);
2666
2667
0
      value = MagickFmin(MaxRGBDouble,MagickFmax(0.0,2.0*(double)source.blue+(double)destination.blue-MaxRGBDouble));
2668
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2669
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2670
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2671
0
      destination.blue=RoundDoubleToQuantum(composite);
2672
2673
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2674
0
    }
2675
2676
0
  return MagickPass;
2677
0
}
2678
2679
2680
static MagickPassFail
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
0
{
2693
0
  register long
2694
0
    i;
2695
2696
0
  PixelPacket
2697
0
    destination,
2698
0
    source;
2699
2700
0
  ARG_NOT_USED(mutable_data);
2701
0
  ARG_NOT_USED(immutable_data);
2702
0
  ARG_NOT_USED(exception);
2703
2704
  /*
2705
    Acts like ColorDodge for bright source pixels, ColorBurn for dark source pixels
2706
  */
2707
2708
2709
0
  for (i=0; i < npixels; i++)
2710
0
    {
2711
0
      double gamma;
2712
0
      double source_alpha;
2713
0
      double dest_alpha;
2714
0
      double composite;
2715
0
      double value;
2716
2717
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2718
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2719
2720
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2721
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2722
2723
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2724
0
        (1.0-source_alpha)*(1.0-dest_alpha);
2725
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2726
2727
0
      composite=MaxRGBDouble*(1.0-gamma);
2728
0
      destination.opacity=RoundDoubleToQuantum(composite);
2729
2730
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2731
2732
0
      if(source.red==MaxRGB)
2733
0
        value = MaxRGBDouble;
2734
0
      else if(source.red==0)
2735
0
        value = 0.;
2736
0
      else if(source.red>=(0.5*MaxRGBDouble))
2737
0
        value = MagickFmin(MaxRGBDouble,destination.red/(2.0-(2.0*(double)source.red/MaxRGBDouble)));
2738
0
      else
2739
0
        value = MagickFmax(0.0,((double)destination.red+2.0*source.red-MaxRGBDouble)/(2.0*(double)source.red/MaxRGBDouble));
2740
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2741
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2742
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2743
0
      destination.red=RoundDoubleToQuantum(composite);
2744
2745
0
      if(source.green==MaxRGB)
2746
0
        value = MaxRGBDouble;
2747
0
      else if(source.green==0)
2748
0
        value = 0.;
2749
0
      else if(source.green>=(0.5*MaxRGBDouble))
2750
0
        value = MagickFmin(MaxRGBDouble,destination.green/(2.0-(2.0*(double)source.green/MaxRGBDouble)));
2751
0
      else
2752
0
        value = MagickFmax(0.0,((double)destination.green+2.0*source.green-MaxRGBDouble)/(2.0*(double)source.green/MaxRGBDouble));
2753
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2754
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2755
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2756
0
      destination.green=RoundDoubleToQuantum(composite);
2757
2758
0
      if(source.blue==MaxRGB)
2759
0
        value = MaxRGBDouble;
2760
0
      else if(source.blue==0)
2761
0
        value = 0.;
2762
0
      else if(source.blue>=(0.5*MaxRGBDouble))
2763
0
        value = MagickFmin(MaxRGBDouble,destination.blue/(2.0-(2.0*(double)source.blue/MaxRGBDouble)));
2764
0
      else
2765
0
        value = MagickFmax(0.0,((double)destination.blue+2.0*source.blue-MaxRGBDouble)/(2.0*(double)source.blue/MaxRGBDouble));
2766
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2767
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2768
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2769
0
      destination.blue=RoundDoubleToQuantum(composite);
2770
2771
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2772
0
    }
2773
2774
0
  return MagickPass;
2775
0
}
2776
2777
2778
static MagickPassFail
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
0
{
2791
0
  register long
2792
0
    i;
2793
2794
0
  PixelPacket
2795
0
    destination,
2796
0
    source;
2797
2798
0
  ARG_NOT_USED(mutable_data);
2799
0
  ARG_NOT_USED(immutable_data);
2800
0
  ARG_NOT_USED(exception);
2801
2802
  /*
2803
    Acts like Lighten for bright source pixels, Darken for dark source pixels
2804
  */
2805
2806
2807
0
  for (i=0; i < npixels; i++)
2808
0
    {
2809
0
      double gamma;
2810
0
      double source_alpha;
2811
0
      double dest_alpha;
2812
0
      double composite;
2813
0
      double value;
2814
2815
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2816
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2817
2818
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2819
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2820
2821
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2822
0
        (1.0-source_alpha)*(1.0-dest_alpha);
2823
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2824
2825
0
      composite=MaxRGBDouble*(1.0-gamma);
2826
0
      destination.opacity=RoundDoubleToQuantum(composite);
2827
2828
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2829
2830
0
      if(source.red>=(0.5*MaxRGBDouble))
2831
0
        value = MagickFmax((double)destination.red,2.0*((double)source.red-0.5*MaxRGBDouble));
2832
0
      else
2833
0
        value = MagickFmin((double)destination.red,2.0*(double)source.red);
2834
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2835
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2836
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2837
0
      destination.red=RoundDoubleToQuantum(composite);
2838
2839
0
      if(source.green>=(0.5*MaxRGBDouble))
2840
0
        value = MagickFmax((double)destination.green,2.0*((double)source.green-0.5*MaxRGBDouble));
2841
0
      else
2842
0
        value = MagickFmin((double)destination.green,2.0*(double)source.green);
2843
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2844
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2845
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2846
0
      destination.green=RoundDoubleToQuantum(composite);
2847
2848
0
      if(source.blue>=(0.5*MaxRGBDouble))
2849
0
        value = MagickFmax((double)destination.blue,2.0*((double)source.blue-0.5*MaxRGBDouble));
2850
0
      else
2851
0
        value = MagickFmin((double)destination.blue,2.0*(double)source.blue);
2852
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2853
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2854
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2855
0
      destination.blue=RoundDoubleToQuantum(composite);
2856
2857
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2858
0
    }
2859
2860
0
  return MagickPass;
2861
0
}
2862
2863
2864
static MagickPassFail
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
0
{
2877
0
  register long
2878
0
    i;
2879
2880
0
  PixelPacket
2881
0
    destination,
2882
0
    source;
2883
2884
0
  ARG_NOT_USED(mutable_data);
2885
0
  ARG_NOT_USED(immutable_data);
2886
0
  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
0
  for (i=0; i < npixels; i++)
2896
0
    {
2897
0
      double gamma;
2898
0
      double source_alpha;
2899
0
      double dest_alpha;
2900
0
      double composite;
2901
0
      double value;
2902
2903
0
      PrepareSourcePacket(&source,source_pixels,source_image,source_indexes,i);
2904
0
      PrepareDestinationPacket(&destination,update_pixels,update_image,update_indexes,i);
2905
2906
0
      source_alpha=(double) source.opacity/MaxRGBDouble;
2907
0
      dest_alpha=(double) destination.opacity/MaxRGBDouble;
2908
2909
0
      gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
2910
0
        (1.0-source_alpha)*(1.0-dest_alpha);
2911
0
      gamma=gamma < 0.0 ? 0.0 : (gamma > 1.0) ? 1.0 : gamma;
2912
2913
0
      composite=MaxRGBDouble*(1.0-gamma);
2914
0
      destination.opacity=RoundDoubleToQuantum(composite);
2915
2916
0
      gamma=1.0/(fabs(gamma) < MagickEpsilon ? MagickEpsilon : gamma);
2917
2918
0
      if(source.red + destination.red < MaxRGB)
2919
0
        value = 0.0;
2920
0
      else
2921
0
        value = MaxRGBDouble;
2922
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2923
0
                 source.red*(1.0-source_alpha)*dest_alpha+
2924
0
                 destination.red*(1.0-dest_alpha)*source_alpha)*gamma;
2925
0
      destination.red=RoundDoubleToQuantum(composite);
2926
2927
0
      if(source.green + destination.green < MaxRGB)
2928
0
        value = 0.0;
2929
0
      else
2930
0
        value = MaxRGBDouble;
2931
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2932
0
                 source.green*(1.0-source_alpha)*dest_alpha+
2933
0
                 destination.green*(1.0-dest_alpha)*source_alpha)*gamma;
2934
0
      destination.green=RoundDoubleToQuantum(composite);
2935
2936
0
      if(source.blue + destination.blue < MaxRGB)
2937
0
        value = 0.0;
2938
0
      else
2939
0
        value = MaxRGBDouble;
2940
0
      composite=((value*(1.0-source_alpha)*(1.0-dest_alpha))+
2941
0
                 source.blue*(1.0-source_alpha)*dest_alpha+
2942
0
                 destination.blue*(1.0-dest_alpha)*source_alpha)*gamma;
2943
0
      destination.blue=RoundDoubleToQuantum(composite);
2944
2945
0
      ApplyPacketUpdates(update_pixels,update_indexes,update_image,&destination,i);
2946
0
    }
2947
2948
0
  return MagickPass;
2949
0
}
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
2998
GetCompositionPixelIteratorCallback(const CompositeOperator compose,
2999
                                    const MagickBool canvas_matte,
3000
                                    const MagickBool change_matte,
3001
                                    MagickBool *clear)
3002
98.3k
{
3003
98.3k
  PixelIteratorDualModifyCallback
3004
98.3k
    call_back = (PixelIteratorDualModifyCallback) NULL;
3005
3006
98.3k
  MagickBool
3007
98.3k
    clear_flag=MagickFalse;
3008
3009
98.3k
  assert(clear != (MagickBool *) NULL);
3010
3011
98.3k
  switch (compose)
3012
98.3k
    {
3013
0
    case UndefinedCompositeOp:
3014
      /* Does nothing */
3015
0
      break;
3016
6.84k
    case OverCompositeOp:
3017
6.84k
      if (canvas_matte || change_matte)
3018
6.21k
        call_back=OverCompositePixels;
3019
627
      else
3020
627
        call_back=CopyCompositePixels;
3021
6.84k
      break;
3022
0
    case InCompositeOp:
3023
0
      call_back=InCompositePixels;
3024
0
      break;
3025
0
    case OutCompositeOp:
3026
0
      call_back=OutCompositePixels;
3027
0
      break;
3028
0
    case AtopCompositeOp:
3029
0
      if (canvas_matte || change_matte)
3030
0
        call_back=AtopCompositePixels;
3031
0
      else
3032
0
        call_back=CopyCompositePixels;
3033
0
      break;
3034
0
    case XorCompositeOp:
3035
0
      call_back=XorCompositePixels;
3036
0
      break;
3037
0
    case PlusCompositeOp:
3038
0
      call_back=PlusCompositePixels;
3039
0
      break;
3040
0
    case MinusCompositeOp:
3041
0
      call_back=MinusCompositePixels;
3042
0
      break;
3043
0
    case AddCompositeOp:
3044
0
      call_back=AddCompositePixels;
3045
0
      break;
3046
0
    case SubtractCompositeOp:
3047
0
      call_back=SubtractCompositePixels;
3048
0
      break;
3049
0
    case DifferenceCompositeOp:
3050
0
      call_back=DifferenceCompositePixels;
3051
0
      break;
3052
0
    case MultiplyCompositeOp:
3053
0
      call_back=MultiplyCompositePixels;
3054
0
      break;
3055
0
    case BumpmapCompositeOp:
3056
0
      call_back=BumpmapCompositePixels;
3057
0
      break;
3058
91.4k
    case CopyCompositeOp:
3059
91.4k
      call_back=CopyCompositePixels;
3060
91.4k
      break;
3061
0
    case CopyRedCompositeOp:
3062
0
      call_back=CopyRedCompositePixels;
3063
0
      break;
3064
0
    case CopyGreenCompositeOp:
3065
0
      call_back=CopyGreenCompositePixels;
3066
0
      break;
3067
0
    case CopyBlueCompositeOp:
3068
0
      call_back=CopyBlueCompositePixels;
3069
0
      break;
3070
0
    case CopyOpacityCompositeOp:
3071
0
      call_back=CopyOpacityCompositePixels;
3072
0
      break;
3073
0
    case ClearCompositeOp:
3074
0
      call_back=ClearCompositePixels;
3075
0
      break;
3076
0
    case DissolveCompositeOp:
3077
0
      call_back=DissolveCompositePixels;
3078
0
      break;
3079
0
    case DisplaceCompositeOp:
3080
0
      call_back=CopyCompositePixels;
3081
0
      break;
3082
0
    case ModulateCompositeOp:
3083
0
      call_back=ModulateCompositePixels;
3084
0
      break;
3085
0
    case ThresholdCompositeOp:
3086
0
      call_back=ThresholdCompositePixels;
3087
0
      break;
3088
0
    case NoCompositeOp:
3089
0
      break;
3090
0
    case DarkenCompositeOp:
3091
0
      call_back=DarkenCompositePixels;
3092
0
      break;
3093
0
    case LightenCompositeOp:
3094
0
      call_back=LightenCompositePixels;
3095
0
      break;
3096
0
    case HueCompositeOp:
3097
0
      call_back=HueCompositePixels;
3098
0
      break;
3099
0
    case SaturateCompositeOp:
3100
0
      call_back=SaturateCompositePixels;
3101
0
      break;
3102
0
    case ColorizeCompositeOp:
3103
0
      call_back=ColorizeCompositePixels;
3104
0
      break;
3105
0
    case LuminizeCompositeOp:
3106
0
      call_back=LuminizeCompositePixels;
3107
0
      break;
3108
0
    case ScreenCompositeOp:
3109
0
      call_back=ScreenCompositePixels;
3110
0
      break;
3111
0
    case OverlayCompositeOp:
3112
0
      call_back=OverlayCompositePixels;
3113
0
      break;
3114
0
    case CopyCyanCompositeOp:
3115
0
      call_back=CopyRedCompositePixels;
3116
0
      break;
3117
0
    case CopyMagentaCompositeOp:
3118
0
      call_back=CopyGreenCompositePixels;
3119
0
      break;
3120
0
    case CopyYellowCompositeOp:
3121
0
      call_back=CopyBlueCompositePixels;
3122
0
      break;
3123
0
    case CopyBlackCompositeOp:
3124
0
      call_back=CopyBlackCompositePixels;
3125
0
      break;
3126
0
    case DivideCompositeOp:
3127
0
      call_back=DivideCompositePixels;
3128
0
      break;
3129
0
    case HardLightCompositeOp:
3130
0
      call_back=HardLightCompositePixels;
3131
0
      break;
3132
0
    case ExclusionCompositeOp:
3133
0
      call_back=ExclusionCompositePixels;
3134
0
      break;
3135
0
    case ColorDodgeCompositeOp:
3136
0
      call_back=ColorDodgeCompositePixels;
3137
0
      break;
3138
0
    case ColorBurnCompositeOp:
3139
0
      call_back=ColorBurnCompositePixels;
3140
0
      break;
3141
0
    case SoftLightCompositeOp:
3142
0
      call_back=SoftLightCompositePixels;
3143
0
      break;
3144
0
    case LinearBurnCompositeOp:
3145
0
      call_back=LinearBurnCompositePixels;
3146
0
      break;
3147
0
    case LinearDodgeCompositeOp:
3148
0
      call_back=LinearDodgeCompositePixels;
3149
0
      break;
3150
0
    case LinearLightCompositeOp:
3151
0
      call_back=LinearLightCompositePixels;
3152
0
      break;
3153
0
    case VividLightCompositeOp:
3154
0
      call_back=VividLightCompositePixels;
3155
0
      break;
3156
0
    case PinLightCompositeOp:
3157
0
      call_back=PinLightCompositePixels;
3158
0
      break;
3159
0
    case HardMixCompositeOp:
3160
0
      call_back=HardMixCompositePixels;
3161
0
      break;
3162
0
    default:
3163
0
      {
3164
0
        break;
3165
0
      }
3166
98.3k
    }
3167
3168
98.3k
  if ((CopyCompositePixels == call_back) ||
3169
6.21k
      (ClearCompositePixels == call_back))
3170
92.0k
    clear_flag=MagickTrue;
3171
3172
98.3k
  *clear=clear_flag;
3173
98.3k
  return call_back;
3174
98.3k
}
3175
MagickExport MagickPassFail
3176
CompositeImage(Image *canvas_image,
3177
               const CompositeOperator compose,
3178
               const Image *update_image,
3179
               const long x_offset,const long y_offset)
3180
58.2k
{
3181
58.2k
  CompositeOptions_t
3182
58.2k
    options;
3183
3184
58.2k
  Image
3185
58.2k
    *change_image;
3186
3187
58.2k
  double
3188
58.2k
    amount=0.0,
3189
58.2k
    percent_brightness=0.0,
3190
58.2k
    percent_saturation=0.0,
3191
58.2k
    threshold=0.0;
3192
3193
58.2k
  long
3194
58.2k
    y;
3195
3196
58.2k
  register const PixelPacket
3197
58.2k
    *p;
3198
3199
58.2k
  register long
3200
58.2k
    x;
3201
3202
58.2k
  register PixelPacket
3203
58.2k
    *q;
3204
3205
58.2k
  MagickPassFail
3206
58.2k
    status=MagickPass;
3207
3208
  /*
3209
    Prepare composite image.
3210
  */
3211
58.2k
  assert(canvas_image != (Image *) NULL);
3212
58.2k
  assert(canvas_image->signature == MagickSignature);
3213
58.2k
  assert(update_image != (Image *) NULL);
3214
58.2k
  assert(update_image->signature == MagickSignature);
3215
58.2k
  if (compose == NoCompositeOp)
3216
0
    return(MagickPass);
3217
3218
  /*
3219
    Clone composite image so that we can modify it if need be.
3220
  */
3221
58.2k
  change_image=CloneImage(update_image,0,0,True,&canvas_image->exception);
3222
58.2k
  if (change_image == (Image *) NULL)
3223
0
    return(MagickFail);
3224
3225
58.2k
  canvas_image->storage_class=DirectClass;
3226
58.2k
  switch (compose)
3227
58.2k
    {
3228
0
    case CopyCyanCompositeOp:
3229
0
    case CopyMagentaCompositeOp:
3230
0
    case CopyYellowCompositeOp:
3231
0
    case CopyBlackCompositeOp:
3232
0
      {
3233
0
        canvas_image->colorspace=CMYKColorspace;
3234
0
        break;
3235
0
      }
3236
0
    case CopyOpacityCompositeOp:
3237
0
      {
3238
0
        canvas_image->matte=MagickTrue;
3239
0
        break;
3240
0
      }
3241
0
    case DisplaceCompositeOp:
3242
0
      {
3243
0
        double
3244
0
          x_displace,
3245
0
          y_displace;
3246
3247
0
        double
3248
0
          horizontal_scale,
3249
0
          vertical_scale;
3250
3251
0
        register PixelPacket
3252
0
          *r;
3253
3254
0
        horizontal_scale=20.0;
3255
0
        vertical_scale=20.0;
3256
0
        if (update_image->geometry != (char *) NULL)
3257
0
          {
3258
0
            int
3259
0
              count;
3260
3261
            /*
3262
              Determine the horizontal and vertical displacement scale.
3263
            */
3264
0
            count=GetMagickDimension(update_image->geometry,
3265
0
                                     &horizontal_scale,&vertical_scale,NULL,NULL);
3266
0
            if (count == 1)
3267
0
              vertical_scale=horizontal_scale;
3268
0
          }
3269
        /*
3270
          Shift image pixels as defined by a displacement map.
3271
        */
3272
0
        for (y=0; y < (long) update_image->rows; y++)
3273
0
          {
3274
0
            if (((y+y_offset) < 0) || ((y+y_offset) >= (long) canvas_image->rows))
3275
0
              continue;
3276
0
            p=AcquireImagePixels(update_image,0,y,update_image->columns,1,
3277
0
                                 &canvas_image->exception);
3278
0
            q=GetImagePixels(canvas_image,0,y+y_offset,canvas_image->columns,1);
3279
0
            r=GetImagePixels(change_image,0,y,change_image->columns,1);
3280
0
            if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
3281
0
                (r == (PixelPacket *) NULL))
3282
0
              {
3283
0
                status=MagickFail;
3284
0
                break;
3285
0
              }
3286
0
            q+=x_offset;
3287
0
            for (x=0; x < (long) update_image->columns; x++)
3288
0
              {
3289
0
                if (((x_offset+x) < 0) || ((x_offset+x) >= (long) canvas_image->columns))
3290
0
                  {
3291
0
                    p++;
3292
0
                    q++;
3293
0
                    continue;
3294
0
                  }
3295
0
                x_displace=(horizontal_scale*(PixelIntensityToQuantum(p)-
3296
0
                                              (((double) MaxRGB+1.0)/2)))/(((double) MaxRGB+1.0)/2);
3297
0
                y_displace=x_displace;
3298
0
                if (update_image->matte)
3299
0
                  y_displace=(vertical_scale*(p->opacity-
3300
0
                                              (((double) MaxRGB+1.0)/2)))/(((double) MaxRGB+1.0)/2);
3301
0
                if (InterpolateViewColor(AccessDefaultCacheView(canvas_image),r,
3302
0
                                         x_offset+(size_t) x+x_displace,y_offset+(size_t) y+y_displace,
3303
0
                                         &canvas_image->exception) == MagickFail)
3304
0
                  {
3305
0
                    status=MagickFail;
3306
0
                    break;
3307
0
                  }
3308
0
                p++;
3309
0
                q++;
3310
0
                r++;
3311
0
              }
3312
0
            if (status != MagickFail)
3313
0
              if (!SyncImagePixels(change_image))
3314
0
                {
3315
0
                  status=MagickFail;
3316
0
                  break;
3317
0
                }
3318
0
          }
3319
0
        break;
3320
0
      }
3321
0
    case ModulateCompositeOp:
3322
0
      {
3323
0
        percent_saturation=50.0;
3324
0
        percent_brightness=50.0;
3325
0
        if (update_image->geometry != (char *) NULL)
3326
0
          {
3327
0
            int
3328
0
              count;
3329
3330
            /*
3331
              Determine the brightness and saturation scale.
3332
            */
3333
0
            count=GetMagickDimension(update_image->geometry,
3334
0
                                     &percent_brightness,&percent_saturation,NULL,NULL);
3335
0
            if (count == 1)
3336
0
              percent_saturation=percent_brightness;
3337
0
          }
3338
0
        percent_brightness/=100.0;
3339
0
        percent_saturation/=100.0;
3340
0
        break;
3341
0
      }
3342
0
    case ThresholdCompositeOp:
3343
0
      {
3344
        /*
3345
          Determine the amount and threshold.
3346
        */
3347
0
        amount=0.5;
3348
0
        threshold=0.05;
3349
0
        if (update_image->geometry != (char *) NULL)
3350
0
          (void) GetMagickDimension(update_image->geometry,&amount,&threshold,NULL,NULL);
3351
0
        threshold*=MaxRGB;
3352
0
        break;
3353
0
      }
3354
58.2k
    default:
3355
58.2k
      break;
3356
58.2k
    }
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
58.2k
  switch (compose)
3363
58.2k
    {
3364
0
    case CopyRedCompositeOp:
3365
0
    case CopyGreenCompositeOp:
3366
0
    case CopyBlueCompositeOp:
3367
0
    case CopyCyanCompositeOp:
3368
0
    case CopyMagentaCompositeOp:
3369
0
    case CopyYellowCompositeOp:
3370
0
    case CopyBlackCompositeOp:
3371
0
      {
3372
        /*
3373
          Assume that the user is right for channel copies.
3374
        */
3375
0
        break;
3376
0
      }
3377
58.2k
    default:
3378
58.2k
      {
3379
58.2k
        if (IsRGBColorspace(canvas_image->colorspace))
3380
58.2k
          {
3381
58.2k
            if (!IsRGBColorspace(change_image->colorspace))
3382
7.81k
              TransformColorspace(change_image,RGBColorspace);
3383
58.2k
          }
3384
0
        else if (IsYCbCrColorspace(canvas_image->colorspace))
3385
0
          {
3386
0
            if (canvas_image->colorspace != change_image->colorspace)
3387
0
              TransformColorspace(change_image,canvas_image->colorspace);
3388
0
          }
3389
0
        else if (IsCMYKColorspace(canvas_image->colorspace))
3390
0
          {
3391
0
            if (!IsCMYKColorspace(change_image->colorspace))
3392
0
              TransformColorspace(change_image,canvas_image->colorspace);
3393
0
          }
3394
0
        else
3395
0
          {
3396
0
            TransformColorspace(change_image,canvas_image->colorspace);
3397
0
          }
3398
58.2k
        break;
3399
0
      }
3400
58.2k
    }
3401
3402
  /*
3403
    Composite image.
3404
  */
3405
58.2k
  options.percent_brightness=percent_brightness;
3406
58.2k
  options.amount=amount;
3407
58.2k
  options.threshold=threshold;
3408
3409
58.2k
  {
3410
58.2k
    unsigned long
3411
58.2k
      columns,
3412
58.2k
      rows;
3413
3414
58.2k
    long
3415
58.2k
      composite_x,
3416
58.2k
      composite_y,
3417
58.2k
      canvas_x,
3418
58.2k
      canvas_y;
3419
3420
58.2k
    columns=change_image->columns;
3421
58.2k
    rows=change_image->rows;
3422
3423
58.2k
    composite_x=0;
3424
58.2k
    composite_y=0;
3425
58.2k
    canvas_x=x_offset;
3426
58.2k
    canvas_y=y_offset;
3427
3428
58.2k
    if (x_offset < 0)
3429
61
      composite_x += -x_offset;
3430
58.2k
    if (y_offset < 0)
3431
95
      composite_y += -y_offset;
3432
3433
58.2k
    columns -= composite_x;
3434
58.2k
    rows -= composite_y;
3435
3436
58.2k
    if (canvas_x < 0)
3437
61
      canvas_x=0;
3438
58.2k
    if (canvas_y < 0)
3439
95
      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
58.2k
    if (((unsigned long) canvas_x < canvas_image->columns) &&
3454
51.1k
        ((unsigned long) canvas_y < canvas_image->rows) &&
3455
47.2k
        ((unsigned long) composite_x < change_image->columns) &&
3456
47.1k
        ((unsigned long) composite_y < change_image->rows))
3457
47.1k
      {
3458
47.1k
        PixelIteratorDualModifyCallback
3459
47.1k
          call_back = (PixelIteratorDualModifyCallback) NULL;
3460
3461
47.1k
        MagickBool
3462
47.1k
          clear_pixels = MagickFalse;
3463
3464
47.1k
        columns = Min(canvas_image->columns - canvas_x,
3465
47.1k
                      change_image->columns - composite_x);
3466
47.1k
        rows = Min(canvas_image->rows - canvas_y,
3467
47.1k
                   change_image->rows - composite_y);
3468
3469
47.1k
        call_back=GetCompositionPixelIteratorCallback(compose,
3470
47.1k
                                                      canvas_image->matte,
3471
47.1k
                                                      change_image->matte,
3472
47.1k
                                                      &clear_pixels);
3473
47.1k
        if (call_back != (PixelIteratorDualModifyCallback) NULL)
3474
47.1k
          {
3475
47.1k
            char
3476
47.1k
              description[MaxTextExtent];
3477
3478
47.1k
            FormatString(description,"[%%s] Composite %s image pixels ...",
3479
47.1k
                         CompositeOperatorToString(compose));
3480
3481
47.1k
            if (clear_pixels)
3482
40.9k
              {
3483
                /*
3484
                  We don't care about existing pixels in the region.
3485
                */
3486
40.9k
                status=PixelIterateDualNew(call_back,              /* Callback */
3487
40.9k
                                           NULL,
3488
40.9k
                                           description,            /* Description */
3489
40.9k
                                           NULL,
3490
40.9k
                                           &options,               /* Options */
3491
40.9k
                                           columns,                /* Number of columns */
3492
40.9k
                                           rows,                   /* Number of rows */
3493
40.9k
                                           change_image,           /* Composite image */
3494
40.9k
                                           composite_x,            /* Composite x offset */
3495
40.9k
                                           composite_y,            /* Composite y offset */
3496
40.9k
                                           canvas_image,           /* Canvas image */
3497
40.9k
                                           canvas_x,               /* Canvas x offset */
3498
40.9k
                                           canvas_y,               /* Canvas y offset */
3499
40.9k
                                           &canvas_image->exception); /* Exception */
3500
40.9k
              }
3501
6.21k
            else
3502
6.21k
              {
3503
                /*
3504
                  Blend with existing pixels in the region.
3505
                */
3506
6.21k
                status=PixelIterateDualModify(call_back,              /* Callback */
3507
6.21k
                                              NULL,
3508
6.21k
                                              description,            /* Description */
3509
6.21k
                                              NULL,
3510
6.21k
                                              &options,               /* Options */
3511
6.21k
                                              columns,                /* Number of columns */
3512
6.21k
                                              rows,                   /* Number of rows */
3513
6.21k
                                              change_image,           /* Composite image */
3514
6.21k
                                              composite_x,            /* Composite x offset */
3515
6.21k
                                              composite_y,            /* Composite y offset */
3516
6.21k
                                              canvas_image,           /* Canvas image */
3517
6.21k
                                              canvas_x,               /* Canvas x offset */
3518
6.21k
                                              canvas_y,               /* Canvas y offset */
3519
6.21k
                                              &canvas_image->exception); /* Exception */
3520
6.21k
              }
3521
47.1k
          }
3522
0
        else
3523
0
          {
3524
0
            status=MagickFail;
3525
0
          }
3526
47.1k
      }
3527
58.2k
  }
3528
3529
58.2k
  DestroyImage(change_image);
3530
58.2k
  change_image=(Image *) NULL;
3531
3532
58.2k
  return(status);
3533
58.2k
}
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
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
51.1k
{
3621
51.1k
  PixelIteratorDualModifyCallback
3622
51.1k
    call_back = (PixelIteratorDualModifyCallback) NULL;
3623
3624
51.1k
  MagickBool
3625
51.1k
    clear_pixels = MagickFalse;
3626
3627
51.1k
  MagickPassFail
3628
51.1k
    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
51.1k
  if (compose == NoCompositeOp)
3634
0
    return(MagickPass);
3635
3636
51.1k
  canvas_image->storage_class=DirectClass;
3637
3638
51.1k
  call_back=GetCompositionPixelIteratorCallback(compose,
3639
51.1k
                                                canvas_image->matte,
3640
51.1k
                                                update_image->matte,
3641
51.1k
                                                &clear_pixels);
3642
51.1k
  if (call_back != (PixelIteratorDualModifyCallback) NULL)
3643
51.1k
    {
3644
51.1k
      const char
3645
51.1k
        *description = "[%s] Composite image pixels ...";
3646
3647
51.1k
      unsigned long
3648
51.1k
        columns=arg_columns,
3649
51.1k
        rows=arg_rows;
3650
3651
51.1k
      long
3652
51.1k
        update_x=arg_update_x,
3653
51.1k
        update_y=arg_update_y,
3654
51.1k
        canvas_x=arg_canvas_x,
3655
51.1k
        canvas_y=arg_canvas_y;
3656
3657
      /*
3658
        FIXME: The area logic is not implemented yet.
3659
      */
3660
3661
51.1k
      if ((update_x >= (long) update_image->columns) ||
3662
51.1k
          (update_y >= (long) update_image->rows) ||
3663
51.1k
          (canvas_x >= (long) canvas_image->columns) ||
3664
35.1k
          (canvas_y >= (long) canvas_image->rows))
3665
44.0k
        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
51.1k
      if ((status == MagickPass) &&
3676
7.14k
          ((unsigned long) canvas_x < canvas_image->columns) &&
3677
7.14k
          ((unsigned long) canvas_y < canvas_image->rows) &&
3678
7.14k
          ((unsigned long) update_x < update_image->columns) &&
3679
7.14k
          ((unsigned long) update_y < update_image->rows) &&
3680
7.14k
          (columns != 0) && (rows != 0))
3681
7.14k
        {
3682
7.14k
          if (clear_pixels)
3683
7.14k
            {
3684
              /*
3685
                We don't care about existing pixels in the region.
3686
              */
3687
7.14k
              status=PixelIterateDualNew(call_back,              /* Callback */
3688
7.14k
                                         NULL,
3689
7.14k
                                         description,            /* Description */
3690
7.14k
                                         NULL,
3691
7.14k
                                         options,                /* Options */
3692
7.14k
                                         columns,                /* Number of columns */
3693
7.14k
                                         rows,                   /* Number of rows */
3694
7.14k
                                         update_image,           /* Composite image */
3695
7.14k
                                         update_x,               /* Composite x offset */
3696
7.14k
                                         update_y,               /* Composite y offset */
3697
7.14k
                                         canvas_image,           /* Canvas image */
3698
7.14k
                                         canvas_x,               /* Canvas x offset */
3699
7.14k
                                         canvas_y,               /* Canvas y offset */
3700
7.14k
                                         exception);             /* Exception */
3701
7.14k
            }
3702
0
          else
3703
0
            {
3704
              /*
3705
                Blend with existing pixels in the region.
3706
              */
3707
0
              status=PixelIterateDualModify(call_back,              /* Callback */
3708
0
                                            NULL,
3709
0
                                            description,            /* Description */
3710
0
                                            NULL,
3711
0
                                            options,                /* Options */
3712
0
                                            columns,                /* Number of columns */
3713
0
                                            rows,                   /* Number of rows */
3714
0
                                            update_image,           /* Composite image */
3715
0
                                            update_x,               /* Composite x offset */
3716
0
                                            update_y,               /* Composite y offset */
3717
0
                                            canvas_image,           /* Canvas image */
3718
0
                                            canvas_x,               /* Canvas x offset */
3719
0
                                            canvas_y,               /* Canvas y offset */
3720
0
                                            exception);             /* Exception */
3721
0
            }
3722
7.14k
        }
3723
51.1k
    }
3724
0
  else
3725
0
    {
3726
0
      status=MagickFail;
3727
0
    }
3728
3729
51.1k
  return status;
3730
51.1k
}
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
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
0
{
3771
0
  const PixelPacket
3772
0
    * restrict background_color = (const PixelPacket *) immutable_data;
3773
3774
0
  register long
3775
0
    i;
3776
3777
0
  ARG_NOT_USED(mutable_data);
3778
0
  ARG_NOT_USED(image);
3779
0
  ARG_NOT_USED(indexes);
3780
0
  ARG_NOT_USED(exception);
3781
3782
0
  for (i=0; i < npixels; i++)
3783
0
    {
3784
0
      AlphaCompositePixel(&pixels[i],&pixels[i],pixels[i].opacity,background_color,
3785
0
                          background_color->opacity);
3786
0
      pixels[i].opacity=OpaqueOpacity;
3787
0
    }
3788
3789
0
  return MagickPass;
3790
0
}
3791
3792
MagickExport MagickPassFail
3793
MagickCompositeImageUnderColor(Image *image,const PixelPacket *undercolor,
3794
                               ExceptionInfo *exception)
3795
0
{
3796
0
  MagickPassFail
3797
0
    status;
3798
3799
0
  image->storage_class=DirectClass;
3800
0
  status=PixelIterateMonoModify(MagickCompositeImageUnderColorPixels,
3801
0
                                NULL,
3802
0
                                "[%s] Applying undercolor...",
3803
0
                                NULL,undercolor,
3804
0
                                0,0,image->columns,image->rows,
3805
0
                                image,
3806
0
                                exception);
3807
0
  image->matte=MagickFalse;
3808
3809
0
  return status;
3810
0
}