~ubuntu-branches/ubuntu/intrepid/graphicsmagick/intrepid

« back to all changes in this revision

Viewing changes to magick/composite.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Kobras
  • Date: 2006-05-06 16:28:08 UTC
  • Revision ID: james.westby@ubuntu.com-20060506162808-vt2ni3r5nytcszms
Tags: upstream-1.1.7
ImportĀ upstreamĀ versionĀ 1.1.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
% Copyright (C) 2003 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
%                                                                             %
 
27
%                                                                             %
 
28
%                                                                             %
 
29
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
30
%
 
31
%
 
32
%
 
33
*/
 
34
 
 
35
/*
 
36
  Include declarations.
 
37
*/
 
38
#include "magick/studio.h"
 
39
#include "magick/cache.h"
 
40
#include "magick/composite.h"
 
41
#include "magick/gem.h"
 
42
#include "magick/utility.h"
 
43
 
 
44
/*
 
45
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
46
%                                                                             %
 
47
%                                                                             %
 
48
%                                                                             %
 
49
%   C o m p o s i t e I m a g e                                               %
 
50
%                                                                             %
 
51
%                                                                             %
 
52
%                                                                             %
 
53
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
54
%
 
55
%  CompositeImage() returns the second image (composite_image) composited
 
56
%  onto the first (canvas_image) at the specified offsets.
 
57
%
 
58
%  The format of the CompositeImage method is:
 
59
%
 
60
%      unsigned int CompositeImage(Image *canvas_image,
 
61
%        const CompositeOperator compose,const Image *composite_image,
 
62
%        const long x_offset,const long y_offset)
 
63
%
 
64
%  A description of each parameter follows:
 
65
%
 
66
%    o canvas_image: The image to be updated.
 
67
%
 
68
%    o compose: This operator affects how the composite is applied to
 
69
%      the image.  The default is Over.  Choose from one of these
 
70
%      operators: OverCompositeOp, InCompositeOp, OutCompositeOP,
 
71
%      AtopCompositeOP, XorCompositeOP, PlusCompositeOP, MinusCompositeOP,
 
72
%      AddCompositeOP, SubtractCompositeOP, DifferenceCompositeOP,
 
73
%      BumpmapCompositeOP, CopyCompositeOP, CopyRedCompositeOP,
 
74
%      CopyGreenCompositeOP, CopyBlueCompositeOP, CopyOpacityCompositeOP.
 
75
%
 
76
%    o composite_image: The composite image.
 
77
%
 
78
%    o x_offset: The column offset of the composited image.
 
79
%
 
80
%    o y_offset: The row offset of the composited image.
 
81
%
 
82
%
 
83
*/
 
84
 
 
85
static inline PixelPacket AlphaComposite(const PixelPacket *p,
 
86
  const double alpha,const PixelPacket *q,const double beta)
 
87
{
 
88
  PixelPacket
 
89
    composite;
 
90
 
 
91
  double
 
92
    MaxRGB_alpha,
 
93
    MaxRGB_beta;
 
94
 
 
95
  MaxRGB_alpha=MaxRGB-alpha;
 
96
  MaxRGB_beta=MaxRGB-beta;
 
97
  composite.red=(Quantum)
 
98
    ((MaxRGB_alpha*p->red+alpha*MaxRGB_beta*q->red/MaxRGB)/MaxRGB+0.5);
 
99
  composite.green=(Quantum)
 
100
    ((MaxRGB_alpha*p->green+alpha*MaxRGB_beta*q->green/MaxRGB)/MaxRGB+0.5);
 
101
  composite.blue=(Quantum)
 
102
    ((MaxRGB_alpha*p->blue+alpha*MaxRGB_beta*q->blue/MaxRGB)/MaxRGB+0.5);
 
103
  composite.opacity=(Quantum)
 
104
    (MaxRGB-(MaxRGB_alpha+alpha*MaxRGB_beta/MaxRGB)+0.5);
 
105
  return(composite);
 
106
}
 
107
 
 
108
MagickExport unsigned int CompositeImage(Image *canvas_image,
 
109
  const CompositeOperator compose,const Image *composite_image,
 
110
  const long x_offset,const long y_offset)
 
111
{
 
112
  const PixelPacket
 
113
    *pixels;
 
114
 
 
115
  double
 
116
    amount,
 
117
    brightness,
 
118
    hue,
 
119
    midpoint,
 
120
    percent_brightness,
 
121
    percent_saturation,
 
122
    sans,
 
123
    saturation,
 
124
    threshold;
 
125
 
 
126
  DoublePixelPacket
 
127
    pixel;
 
128
 
 
129
  IndexPacket
 
130
    *composite_indexes,
 
131
    *indexes;
 
132
 
 
133
  long
 
134
    y;
 
135
 
 
136
  PixelPacket
 
137
    destination,
 
138
    source;
 
139
 
 
140
  register const PixelPacket
 
141
    *p;
 
142
 
 
143
  register long
 
144
    x;
 
145
 
 
146
  register PixelPacket
 
147
    *q;
 
148
 
 
149
  /*
 
150
    Prepare composite image.
 
151
  */
 
152
  assert(canvas_image != (Image *) NULL);
 
153
  assert(canvas_image->signature == MagickSignature);
 
154
  assert(composite_image != (Image *) NULL);
 
155
  assert(composite_image->signature == MagickSignature);
 
156
  if (compose == NoCompositeOp)
 
157
    return(True);
 
158
  SetImageType(canvas_image,TrueColorType);
 
159
  switch (compose)
 
160
  {
 
161
    case DisplaceCompositeOp:
 
162
    {
 
163
      double
 
164
        x_displace,
 
165
        y_displace;
 
166
 
 
167
      double
 
168
        horizontal_scale,
 
169
        vertical_scale;
 
170
 
 
171
      Image
 
172
        *displace_image;
 
173
 
 
174
      register PixelPacket
 
175
        *r;
 
176
 
 
177
      /*
 
178
        Allocate the displace image.
 
179
      */
 
180
      displace_image=CloneImage(composite_image,0,0,True,&canvas_image->exception);
 
181
      if (displace_image == (Image *) NULL)
 
182
        return(False);
 
183
      horizontal_scale=20.0;
 
184
      vertical_scale=20.0;
 
185
      if (composite_image->geometry != (char *) NULL)
 
186
        {
 
187
          int
 
188
            count;
 
189
 
 
190
          /*
 
191
            Determine the horizontal and vertical displacement scale.
 
192
          */
 
193
          count=GetMagickDimension(composite_image->geometry,
 
194
            &horizontal_scale,&vertical_scale);
 
195
          if (count == 1)
 
196
            vertical_scale=horizontal_scale;
 
197
        }
 
198
      /*
 
199
        Shift image pixels as defined by a displacement map.
 
200
      */
 
201
      for (y=0; y < (long) composite_image->rows; y++)
 
202
      {
 
203
        if (((y+y_offset) < 0) || ((y+y_offset) >= (long) canvas_image->rows))
 
204
          continue;
 
205
        p=AcquireImagePixels(composite_image,0,y,composite_image->columns,1,
 
206
          &canvas_image->exception);
 
207
        q=GetImagePixels(canvas_image,0,y+y_offset,canvas_image->columns,1);
 
208
        r=GetImagePixels(displace_image,0,y,displace_image->columns,1);
 
209
        if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL) ||
 
210
            (r == (PixelPacket *) NULL))
 
211
          break;
 
212
        q+=x_offset;
 
213
        for (x=0; x < (long) composite_image->columns; x++)
 
214
        {
 
215
          if (((x_offset+x) < 0) || ((x_offset+x) >= (long) canvas_image->columns))
 
216
            {
 
217
              p++;
 
218
              q++;
 
219
              continue;
 
220
            }
 
221
          x_displace=(horizontal_scale*(PixelIntensityToQuantum(p)-
 
222
            (((double) MaxRGB+1.0)/2)))/(((double) MaxRGB+1.0)/2);
 
223
          y_displace=x_displace;
 
224
          if (composite_image->matte)
 
225
            y_displace=(vertical_scale*(p->opacity-
 
226
              (((double) MaxRGB+1.0)/2)))/(((double) MaxRGB+1.0)/2);
 
227
          *r=InterpolateColor(canvas_image,x_offset+x+x_displace,y_offset+y+y_displace,
 
228
            &canvas_image->exception);
 
229
          p++;
 
230
          q++;
 
231
          r++;
 
232
        }
 
233
        if (!SyncImagePixels(displace_image))
 
234
          break;
 
235
      }
 
236
      composite_image=displace_image;
 
237
      break;
 
238
    }
 
239
    case ModulateCompositeOp:
 
240
    {
 
241
      percent_saturation=50.0;
 
242
      percent_brightness=50.0;
 
243
      if (composite_image->geometry != (char *) NULL)
 
244
        {
 
245
          int
 
246
            count;
 
247
 
 
248
          /*
 
249
            Determine the brightness and saturation scale.
 
250
          */
 
251
          count=GetMagickDimension(composite_image->geometry,
 
252
            &percent_brightness,&percent_saturation);
 
253
          if (count == 1)
 
254
            percent_saturation=percent_brightness;
 
255
        }
 
256
      percent_brightness/=100.0;
 
257
      percent_saturation/=100.0;
 
258
      break;
 
259
    }
 
260
    case ThresholdCompositeOp:
 
261
    {
 
262
      /*
 
263
        Determine the amount and threshold.
 
264
      */
 
265
      amount=0.5;
 
266
      threshold=0.05;
 
267
      if (composite_image->geometry != (char *) NULL)
 
268
        (void) GetMagickDimension(composite_image->geometry,&amount,&threshold);
 
269
      threshold*=MaxRGB;
 
270
      break;
 
271
    }
 
272
    default:
 
273
      break;
 
274
  }
 
275
  /*
 
276
    Composite image.
 
277
  */
 
278
  midpoint=((double) MaxRGB+1.0)/2;
 
279
  for (y=0; y < (long) canvas_image->rows; y++)
 
280
  {
 
281
    if (y < y_offset)
 
282
      continue;
 
283
    if ((y-y_offset) >= (long) composite_image->rows)
 
284
      break;
 
285
    p=AcquireImagePixels(composite_image,0,y-y_offset,composite_image->columns,
 
286
      1,&canvas_image->exception);
 
287
    q=GetImagePixels(canvas_image,0,y,canvas_image->columns,1);
 
288
    if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
 
289
      break;
 
290
    pixels=p;
 
291
    if (x_offset < 0)
 
292
      p-=x_offset;
 
293
    indexes=GetIndexes(canvas_image);
 
294
    composite_indexes=GetIndexes(composite_image);
 
295
    for (x=0; x < (long) canvas_image->columns; x++)
 
296
    {
 
297
      if (x < x_offset)
 
298
        {
 
299
          q++;
 
300
          continue;
 
301
        }
 
302
      if ((x-x_offset) >= (long) composite_image->columns)
 
303
        break;
 
304
      source=(*p);
 
305
      if (!composite_image->matte)
 
306
        source.opacity=OpaqueOpacity;
 
307
      else
 
308
        if (composite_image->colorspace == CMYKColorspace)
 
309
          source.opacity=composite_indexes[x];
 
310
      destination=(*q);
 
311
      if (!canvas_image->matte)
 
312
        destination.opacity=OpaqueOpacity;
 
313
      else
 
314
        if (canvas_image->colorspace == CMYKColorspace)
 
315
          destination.opacity=indexes[x];
 
316
      switch (compose)
 
317
      {
 
318
        case OverCompositeOp:
 
319
        {
 
320
          /*
 
321
            The result will be the union of the two image shapes, with
 
322
            opaque areas of change-image obscuring base-image in the
 
323
            region of overlap.
 
324
          */
 
325
          destination=AlphaComposite(&source,source.opacity,&destination,
 
326
            destination.opacity);
 
327
          break;
 
328
        }
 
329
        case InCompositeOp:
 
330
        {
 
331
          /*
 
332
            The result is simply change-image cut by the shape of
 
333
            base-image. None of the image data of base-image will be
 
334
            in the result.
 
335
          */
 
336
          if (source.opacity == TransparentOpacity)
 
337
            {
 
338
              destination=source;
 
339
              break;
 
340
            }
 
341
          if (destination.opacity == TransparentOpacity)
 
342
            break;
 
343
 
 
344
          pixel.opacity=(double) (((double) MaxRGB-source.opacity)*
 
345
            (MaxRGB-destination.opacity)/MaxRGB);
 
346
 
 
347
          destination.red=(Quantum) (((double) MaxRGB-source.opacity)*
 
348
            (MaxRGB-destination.opacity)*source.red/MaxRGB/pixel.opacity+0.5);
 
349
 
 
350
          destination.green=(Quantum) (((double) MaxRGB-source.opacity)*
 
351
            (MaxRGB-destination.opacity)*source.green/MaxRGB/pixel.opacity+0.5);
 
352
 
 
353
          destination.blue=(Quantum) (((double) MaxRGB-source.opacity)*
 
354
            (MaxRGB-destination.opacity)*source.blue/MaxRGB/pixel.opacity+0.5);
 
355
 
 
356
          destination.opacity=(Quantum) (MaxRGB-pixel.opacity+0.5);
 
357
          break;
 
358
        }
 
359
        case OutCompositeOp:
 
360
        {
 
361
          /*
 
362
            The resulting image is change-image with the shape of
 
363
            base-image cut out.
 
364
          */
 
365
          if (source.opacity == TransparentOpacity)
 
366
            {
 
367
              destination=source;
 
368
              break;
 
369
            }
 
370
          if (destination.opacity == OpaqueOpacity)
 
371
            {
 
372
              destination.opacity=TransparentOpacity;
 
373
              break;
 
374
            }
 
375
          pixel.opacity=(double)
 
376
            (MaxRGB-source.opacity)*destination.opacity/MaxRGB;
 
377
 
 
378
          destination.red=(Quantum) (((double) MaxRGB-source.opacity)*
 
379
            destination.opacity*source.red/MaxRGB/pixel.opacity+0.5);
 
380
 
 
381
          destination.green=(Quantum) (((double) MaxRGB-source.opacity)*
 
382
            destination.opacity*source.green/MaxRGB/pixel.opacity+0.5);
 
383
 
 
384
          destination.blue=(Quantum) (((double) MaxRGB-source.opacity)*
 
385
            destination.opacity*source.blue/MaxRGB/pixel.opacity+0.5);
 
386
 
 
387
          destination.opacity=(Quantum) (MaxRGB-pixel.opacity+0.5);
 
388
          break;
 
389
        }
 
390
        case AtopCompositeOp:
 
391
        {
 
392
          /*
 
393
            The result is the same shape as base-image, with
 
394
            change-image obscuring base-image where the image shapes
 
395
            overlap. Note this differs from over because the portion
 
396
            of change-image outside base-image's shape does not appear
 
397
            in the result.
 
398
          */
 
399
          pixel.opacity=((double)(MaxRGB-source.opacity)*
 
400
            (MaxRGB-destination.opacity)+(double) source.opacity*
 
401
            (MaxRGB-destination.opacity))/MaxRGB;
 
402
 
 
403
          pixel.red=((double) (MaxRGB-source.opacity)*(MaxRGB-
 
404
            destination.opacity)*source.red/MaxRGB+(double)
 
405
            source.opacity*(MaxRGB-destination.opacity)*
 
406
            destination.red/MaxRGB)/pixel.opacity;
 
407
          destination.red=RoundToQuantum(pixel.red);
 
408
 
 
409
          pixel.green=((double) (MaxRGB-source.opacity)*(MaxRGB-
 
410
            destination.opacity)*source.green/MaxRGB+(double)
 
411
            source.opacity*(MaxRGB-destination.opacity)*
 
412
            destination.green/MaxRGB)/pixel.opacity;
 
413
          destination.green=RoundToQuantum(pixel.green);
 
414
 
 
415
          pixel.blue=((double) (MaxRGB-source.opacity)*(MaxRGB-
 
416
            destination.opacity)*source.blue/MaxRGB+(double)
 
417
            source.opacity*(MaxRGB-destination.opacity)*
 
418
            destination.blue/MaxRGB)/pixel.opacity;
 
419
          destination.blue=RoundToQuantum(pixel.blue);
 
420
 
 
421
          destination.opacity=MaxRGB-RoundToQuantum(pixel.opacity);
 
422
          break;
 
423
        }
 
424
        case XorCompositeOp:
 
425
        {
 
426
          /*
 
427
            The result is the image data from both change-image and
 
428
            base-image that is outside the overlap region. The overlap
 
429
            region will be blank.
 
430
          */
 
431
          double gamma;
 
432
          double source_alpha;
 
433
          double dest_alpha;
 
434
          double composite;
 
435
          source_alpha=(double) source.opacity/MaxRGB;
 
436
          dest_alpha=(double) destination.opacity/MaxRGB;
 
437
          
 
438
          gamma=(1.0-source_alpha)+(1.0-dest_alpha)-
 
439
            2.0*(1.0-source_alpha)*(1.0-dest_alpha);
 
440
          
 
441
          composite=MaxRGB*(1.0-gamma);
 
442
          destination.opacity=RoundToQuantum(composite);
 
443
          
 
444
          gamma=1.0/(gamma <= MagickEpsilon ? 1.0 : gamma);
 
445
          
 
446
          composite=((1.0-source_alpha)*source.red*dest_alpha+
 
447
                     (1.0-dest_alpha)*destination.red*source_alpha)*gamma;
 
448
          destination.red=RoundToQuantum(composite);
 
449
          
 
450
          composite=((1.0-source_alpha)*source.green*dest_alpha+
 
451
                     (1.0-dest_alpha)*destination.green*source_alpha)*gamma;
 
452
          destination.green=RoundToQuantum(composite);
 
453
          
 
454
          composite=((1.0-source_alpha)*source.blue*dest_alpha+
 
455
                     (1.0-dest_alpha)*destination.blue*source_alpha)*gamma;
 
456
          destination.blue=RoundToQuantum(composite);
 
457
          break;
 
458
        }
 
459
        case PlusCompositeOp:
 
460
        {
 
461
          /*
 
462
            The result is just the sum of the image data. Output
 
463
            values are cropped to MaxRGB (no overflow). This operation
 
464
            is independent of the matte channels.
 
465
          */
 
466
          pixel.red=((double) (MaxRGB-source.opacity)*source.red+(double)
 
467
            (MaxRGB-destination.opacity)*destination.red)/MaxRGB;
 
468
          destination.red=RoundSignedToQuantum(pixel.red);
 
469
 
 
470
          pixel.green=((double) (MaxRGB-source.opacity)*source.green+(double)
 
471
            (MaxRGB-destination.opacity)*destination.green)/MaxRGB;
 
472
          destination.green=RoundSignedToQuantum(pixel.green);
 
473
 
 
474
          pixel.blue=((double) (MaxRGB-source.opacity)*source.blue+(double)
 
475
            (MaxRGB-destination.opacity)*destination.blue)/MaxRGB;
 
476
          destination.blue=RoundSignedToQuantum(pixel.blue);
 
477
 
 
478
          pixel.opacity=((double) (MaxRGB-source.opacity)+
 
479
            (double) (MaxRGB-destination.opacity))/MaxRGB;
 
480
          destination.opacity=MaxRGB-RoundSignedToQuantum(pixel.opacity);
 
481
          break;
 
482
        }
 
483
        case MinusCompositeOp:
 
484
        {
 
485
          /*
 
486
            The result of change-image - base-image, with underflow
 
487
            cropped to zero. The matte channel is ignored (set to
 
488
            opaque, full coverage).
 
489
          */
 
490
          double composite;
 
491
 
 
492
          composite=((double) (MaxRGB-destination.opacity)*destination.red-
 
493
            (double) (MaxRGB-source.opacity)*source.red)/MaxRGB;
 
494
          destination.red=RoundSignedToQuantum(composite);
 
495
 
 
496
          composite=((double) (MaxRGB-destination.opacity)*destination.green-
 
497
            (double) (MaxRGB-source.opacity)*source.green)/MaxRGB;
 
498
          destination.green=RoundSignedToQuantum(composite);
 
499
 
 
500
          composite=((double) (MaxRGB-destination.opacity)*destination.blue-
 
501
            (double) (MaxRGB-source.opacity)*source.blue)/MaxRGB;
 
502
          destination.blue=RoundSignedToQuantum(composite);
 
503
 
 
504
          composite=((double) (MaxRGB-destination.opacity)-
 
505
            (double) (MaxRGB-source.opacity))/MaxRGB;
 
506
          destination.opacity=MaxRGB-RoundSignedToQuantum(composite);
 
507
          break;
 
508
        }
 
509
        case AddCompositeOp:
 
510
        {
 
511
          /*
 
512
            The result of change-image + base-image, with overflow
 
513
            wrapping around (mod MaxRGB+1).
 
514
          */
 
515
          double composite;
 
516
 
 
517
          composite=(double) source.red+destination.red;
 
518
          if (composite > MaxRGB) composite -= ((double) MaxRGB+1.0);
 
519
          destination.red=RoundToQuantum(composite);
 
520
 
 
521
          composite=(double) source.green+destination.green;
 
522
          if (composite > MaxRGB) composite -= ((double) MaxRGB+1.0);
 
523
          destination.green=RoundToQuantum(composite);
 
524
 
 
525
          composite=(double) source.blue+destination.blue;
 
526
          if (composite > MaxRGB) composite -= ((double) MaxRGB+1.0);
 
527
          destination.blue=RoundToQuantum(composite);
 
528
 
 
529
          destination.opacity=OpaqueOpacity;
 
530
          break;
 
531
        }
 
532
        case SubtractCompositeOp:
 
533
        {
 
534
          /*
 
535
            The result of change-image - base-image, with underflow
 
536
            wrapping around (mod MaxRGB+1). The add and subtract
 
537
            operators can be used to perform reversible
 
538
            transformations.
 
539
          */
 
540
          double composite;
 
541
 
 
542
          composite=(double) source.red-destination.red;
 
543
          if (composite < 0) composite += ((double) MaxRGB+1.0);
 
544
          destination.red=RoundToQuantum(composite);
 
545
 
 
546
          composite=(double) source.green-destination.green;
 
547
          if (composite < 0) composite += ((double) MaxRGB+1.0);
 
548
          destination.green=RoundToQuantum(composite);
 
549
 
 
550
          composite=(double) source.blue-destination.blue;
 
551
          if (composite < 0) composite += ((double) MaxRGB+1.0);
 
552
          destination.blue=RoundToQuantum(composite);
 
553
 
 
554
          destination.opacity=OpaqueOpacity;
 
555
          break;
 
556
        }
 
557
        case MultiplyCompositeOp:
 
558
        {
 
559
          /*
 
560
            The result of change-image * base-image. This is useful
 
561
            for the creation of drop-shadows.
 
562
          */
 
563
          double composite;
 
564
 
 
565
          composite=((double) source.red*destination.red)/MaxRGB;
 
566
          destination.red=RoundToQuantum(composite);
 
567
 
 
568
          composite=((double) source.green*destination.green)/MaxRGB;
 
569
          destination.green=RoundToQuantum(composite);
 
570
 
 
571
          composite=((double) source.blue*destination.blue)/MaxRGB;
 
572
          destination.blue=RoundToQuantum(composite);
 
573
 
 
574
          composite=((double) source.opacity*destination.opacity)/MaxRGB;
 
575
          destination.opacity=RoundToQuantum(composite);
 
576
          break;
 
577
        }
 
578
        case DifferenceCompositeOp:
 
579
        {
 
580
          /*
 
581
            The result of abs(change-image - base-image). This is
 
582
            useful for comparing two very similar images.
 
583
          */
 
584
          double composite;
 
585
 
 
586
          composite=source.red-(double) destination.red;
 
587
          destination.red=(Quantum) AbsoluteValue(composite);
 
588
 
 
589
          composite=source.green-(double) destination.green;
 
590
          destination.green=(Quantum) AbsoluteValue(composite);
 
591
 
 
592
          composite=source.blue-(double) destination.blue;
 
593
          destination.blue=(Quantum) AbsoluteValue(composite);
 
594
 
 
595
          composite=source.opacity-(double) destination.opacity;
 
596
          destination.opacity=(Quantum) AbsoluteValue(composite);
 
597
          break;
 
598
        }
 
599
        case BumpmapCompositeOp:
 
600
        {
 
601
          /*
 
602
            The result base-image shaded by change-image.
 
603
          */
 
604
          double composite;
 
605
          double source_intensity;
 
606
 
 
607
          source_intensity=(double) PixelIntensity(&source)/MaxRGB;
 
608
 
 
609
          composite=source_intensity*destination.red;
 
610
          destination.red=RoundToQuantum(composite);
 
611
 
 
612
          composite=source_intensity*destination.green;
 
613
          destination.green=RoundToQuantum(composite);
 
614
 
 
615
          composite=source_intensity*destination.blue;
 
616
          destination.blue=RoundToQuantum(composite);
 
617
 
 
618
          composite=source_intensity*destination.opacity;
 
619
          destination.opacity=RoundToQuantum(composite);
 
620
 
 
621
          break;
 
622
        }
 
623
        case CopyCompositeOp:
 
624
        {
 
625
          /*
 
626
            The resulting image is base-image replaced with
 
627
            change-image. Here the matte information is ignored.
 
628
          */
 
629
          destination=source;
 
630
          break;
 
631
        }
 
632
        case CopyRedCompositeOp:
 
633
        case CopyCyanCompositeOp:
 
634
        {
 
635
          /*
 
636
            The resulting image is the red channel in base-image
 
637
            replaced with the red channel in change-image. The other
 
638
            channels are copied untouched.
 
639
          */
 
640
          destination.red=source.red;
 
641
          break;
 
642
        }
 
643
        case CopyGreenCompositeOp:
 
644
        case CopyMagentaCompositeOp:
 
645
        {
 
646
          /*
 
647
            The resulting image is the green channel in base-image
 
648
            replaced with the green channel in change-image. The other
 
649
            channels are copied untouched.
 
650
          */
 
651
          destination.green=source.green;
 
652
          break;
 
653
        }
 
654
        case CopyBlueCompositeOp:
 
655
        case CopyYellowCompositeOp:
 
656
        {
 
657
          /*
 
658
            The resulting image is the blue channel in base-image
 
659
            replaced with the blue channel in change-image. The other
 
660
            channels are copied untouched.
 
661
          */
 
662
          destination.blue=source.blue;
 
663
          break;
 
664
        }
 
665
        case CopyOpacityCompositeOp:
 
666
        default:
 
667
        {
 
668
          /*
 
669
            The resulting image is the opacity channel in base-image
 
670
            replaced with the opacity channel in change-image.  The
 
671
            other channels are copied untouched.
 
672
          */
 
673
          if (!composite_image->matte)
 
674
            {
 
675
              destination.opacity=(Quantum)
 
676
                (MaxRGB-PixelIntensityToQuantum(&source));
 
677
              break;
 
678
            }
 
679
          destination.opacity=source.opacity;
 
680
          break;
 
681
        }
 
682
        case CopyBlackCompositeOp:
 
683
        {
 
684
          /*
 
685
            Copy the CMYK Black (K) channel into the image.
 
686
          */
 
687
          if ((canvas_image->colorspace == CMYKColorspace) &&
 
688
              (composite_image->colorspace == CMYKColorspace))
 
689
            indexes[x]=(*composite_indexes++);
 
690
          break;
 
691
        }
 
692
        case ClearCompositeOp:
 
693
        {
 
694
          /*
 
695
            Set destination pixels to transparent.
 
696
          */
 
697
          destination.opacity=TransparentOpacity;
 
698
          break;
 
699
        }
 
700
        case DissolveCompositeOp:
 
701
        {
 
702
          destination.red=(Quantum) (((double) source.opacity*source.red+
 
703
            (MaxRGB-source.opacity)*destination.red)/MaxRGB+0.5);
 
704
          destination.green=(Quantum) (((double) source.opacity*source.green+
 
705
            (MaxRGB-source.opacity)*destination.green)/MaxRGB+0.5);
 
706
          destination.blue=(Quantum) (((double) source.opacity*source.blue+
 
707
            (MaxRGB-source.opacity)*destination.blue)/MaxRGB+0.5);
 
708
          destination.opacity=OpaqueOpacity;
 
709
          break;
 
710
        }
 
711
        case DisplaceCompositeOp:
 
712
        {
 
713
          destination=source;
 
714
          break;
 
715
        }
 
716
        case ThresholdCompositeOp:
 
717
        {
 
718
          pixel.red=destination.red-(double) source.red;
 
719
          if (fabs(2.0*pixel.red) < threshold)
 
720
            pixel.red=destination.red;
 
721
          else
 
722
            pixel.red=destination.red+(pixel.red*amount);
 
723
          pixel.green=destination.green-(double) source.green;
 
724
          if (fabs(2.0*pixel.green) < threshold)
 
725
            pixel.green=destination.green;
 
726
          else
 
727
            pixel.green=destination.green+(pixel.green*amount);
 
728
          pixel.blue=destination.blue-(double) source.blue;
 
729
          if (fabs(2.0*pixel.blue) < threshold)
 
730
            pixel.blue=destination.blue;
 
731
          else
 
732
            pixel.blue=destination.blue+(pixel.blue*amount);
 
733
          pixel.opacity=destination.opacity-(double) source.opacity;
 
734
          if (fabs(2.0*pixel.opacity) < threshold)
 
735
            pixel.opacity=destination.opacity;
 
736
          else
 
737
            pixel.opacity=destination.opacity+(pixel.opacity*amount);
 
738
          destination.red=(Quantum) ((pixel.red < 0.0) ? 0 :
 
739
            (pixel.red > MaxRGB) ? MaxRGB : pixel.red+0.5);
 
740
          destination.green=(Quantum) ((pixel.green < 0.0) ? 0 :
 
741
            (pixel.green > MaxRGB) ? MaxRGB : pixel.green+0.5);
 
742
          destination.blue=(Quantum) ((pixel.blue < 0) ? 0 :
 
743
            (pixel.blue > MaxRGB) ? MaxRGB : pixel.blue+0.5);
 
744
          destination.opacity=(Quantum) ((pixel.opacity < 0.0) ? 0 :
 
745
            (pixel.opacity > MaxRGB) ? MaxRGB : pixel.opacity+0.5);
 
746
          break;
 
747
        }
 
748
        case ModulateCompositeOp:
 
749
        {
 
750
          long
 
751
            offset;
 
752
 
 
753
          offset=(long) (PixelIntensityToQuantum(&source)-midpoint);
 
754
          if (offset == 0)
 
755
            break;
 
756
          TransformHSL(destination.red,destination.green,destination.blue,
 
757
            &hue,&saturation,&brightness);
 
758
          brightness+=(percent_brightness*offset)/midpoint;
 
759
          if (brightness < 0.0)
 
760
            brightness=0.0;
 
761
          else
 
762
            if (brightness > 1.0)
 
763
              brightness=1.0;
 
764
          HSLTransform(hue,saturation,brightness,&destination.red,
 
765
            &destination.green,&destination.blue);
 
766
          break;
 
767
        }
 
768
        case DarkenCompositeOp:
 
769
        {
 
770
          if (source.opacity == TransparentOpacity)
 
771
            break;
 
772
          if (destination.opacity == TransparentOpacity)
 
773
            {
 
774
              destination=source;
 
775
              break;
 
776
            }
 
777
          if (source.red < destination.red)
 
778
            destination.red=source.red;
 
779
          if (source.green < destination.green)
 
780
            destination.green=source.green;
 
781
          if (source.blue < destination.blue)
 
782
            destination.blue=source.blue;
 
783
          if (source.opacity < destination.opacity)
 
784
            destination.opacity=source.opacity;
 
785
          break;
 
786
        }
 
787
        case LightenCompositeOp:
 
788
        {
 
789
          if (source.opacity == TransparentOpacity)
 
790
            break;
 
791
          if (destination.opacity == TransparentOpacity)
 
792
            {
 
793
              destination=source;
 
794
              break;
 
795
            }
 
796
          if (source.red > destination.red)
 
797
            destination.red=source.red;
 
798
          if (source.green > destination.green)
 
799
            destination.green=source.green;
 
800
          if (source.blue > destination.blue)
 
801
            destination.blue=source.blue;
 
802
          if (source.opacity > destination.opacity)
 
803
            destination.opacity=source.opacity;
 
804
          break;
 
805
        }
 
806
        case HueCompositeOp:
 
807
        {
 
808
          if (source.opacity == TransparentOpacity)
 
809
            break;
 
810
          if (destination.opacity == TransparentOpacity)
 
811
            {
 
812
              destination=source;
 
813
              break;
 
814
            }
 
815
          TransformHSL(destination.red,destination.green,destination.blue,
 
816
            &hue,&saturation,&brightness);
 
817
          TransformHSL(source.red,source.green,source.blue,&hue,&sans,&sans);
 
818
          HSLTransform(hue,saturation,brightness,&destination.red,
 
819
            &destination.green,&destination.blue);
 
820
          if (source.opacity < destination.opacity)
 
821
            destination.opacity=source.opacity;
 
822
          break;
 
823
        }
 
824
        case SaturateCompositeOp:
 
825
        {
 
826
          if (source.opacity == TransparentOpacity)
 
827
            break;
 
828
          if (destination.opacity == TransparentOpacity)
 
829
            {
 
830
              destination=source;
 
831
              break;
 
832
            }
 
833
          TransformHSL(destination.red,destination.green,destination.blue,
 
834
            &hue,&saturation,&brightness);
 
835
          TransformHSL(source.red,source.green,source.blue,&sans,&saturation,
 
836
            &sans);
 
837
          HSLTransform(hue,saturation,brightness,&destination.red,
 
838
            &destination.green,&destination.blue);
 
839
          if (source.opacity < destination.opacity)
 
840
            destination.opacity=source.opacity;
 
841
          break;
 
842
        }
 
843
        case LuminizeCompositeOp:
 
844
        {
 
845
          if (source.opacity == TransparentOpacity)
 
846
            break;
 
847
          if (destination.opacity == TransparentOpacity)
 
848
            {
 
849
              destination=source;
 
850
              break;
 
851
            }
 
852
          TransformHSL(destination.red,destination.green,destination.blue,
 
853
            &hue,&saturation,&brightness);
 
854
          TransformHSL(source.red,source.green,source.blue,&sans,&sans,
 
855
            &brightness);
 
856
          HSLTransform(hue,saturation,brightness,&destination.red,
 
857
            &destination.green,&destination.blue);
 
858
          if (source.opacity < destination.opacity)
 
859
            destination.opacity=source.opacity;
 
860
          break;
 
861
        }
 
862
        case ColorizeCompositeOp:
 
863
        {
 
864
          if (source.opacity == TransparentOpacity)
 
865
            break;
 
866
          if (destination.opacity == TransparentOpacity)
 
867
            {
 
868
              destination=source;
 
869
              break;
 
870
            }
 
871
          TransformHSL(destination.red,destination.green,destination.blue,
 
872
            &sans,&sans,&brightness);
 
873
          TransformHSL(source.red,source.green,source.blue,&hue,&saturation,
 
874
            &sans);
 
875
          HSLTransform(hue,saturation,brightness,&destination.red,
 
876
            &destination.green,&destination.blue);
 
877
          if (source.opacity < destination.opacity)
 
878
            destination.opacity=source.opacity;
 
879
          break;
 
880
        }
 
881
      }
 
882
      q->red=destination.red;
 
883
      q->green=destination.green;
 
884
      q->blue=destination.blue;
 
885
      if (canvas_image->colorspace != CMYKColorspace)
 
886
        q->opacity=destination.opacity;
 
887
      else
 
888
        {
 
889
          q->opacity=p->opacity;
 
890
          indexes[x]=destination.opacity;
 
891
        }
 
892
      p++;
 
893
      if (p >= (pixels+composite_image->columns))
 
894
        p=pixels;
 
895
      q++;
 
896
    }
 
897
    if (!SyncImagePixels(canvas_image))
 
898
      break;
 
899
  }
 
900
  return(True);
 
901
}