Coverage Report

Created: 2026-06-30 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/MagickCore/composite.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%        CCCC   OOO   M   M  PPPP    OOO   SSSSS  IIIII  TTTTT  EEEEE         %
7
%       C      O   O  MM MM  P   P  O   O  SS       I      T    E             %
8
%       C      O   O  M M M  PPPP   O   O   SSS     I      T    EEE           %
9
%       C      O   O  M   M  P      O   O     SS    I      T    E             %
10
%        CCCC   OOO   M   M  P       OOO   SSSSS  IIIII    T    EEEEE         %
11
%                                                                             %
12
%                                                                             %
13
%                     MagickCore Image Composite Methods                      %
14
%                                                                             %
15
%                              Software Design                                %
16
%                                   Cristy                                    %
17
%                                 July 1992                                   %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  dedicated to making software imaging solutions freely available.           %
22
%                                                                             %
23
%  You may not use this file except in compliance with the License.  You may  %
24
%  obtain a copy of the License at                                            %
25
%                                                                             %
26
%    https://imagemagick.org/license/                                         %
27
%                                                                             %
28
%  Unless required by applicable law or agreed to in writing, software        %
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31
%  See the License for the specific language governing permissions and        %
32
%  limitations under the License.                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
%
38
*/
39

40
/*
41
  Include declarations.
42
*/
43
#include "MagickCore/studio.h"
44
#include "MagickCore/artifact.h"
45
#include "MagickCore/cache.h"
46
#include "MagickCore/cache-private.h"
47
#include "MagickCore/cache-view.h"
48
#include "MagickCore/channel.h"
49
#include "MagickCore/client.h"
50
#include "MagickCore/color.h"
51
#include "MagickCore/color-private.h"
52
#include "MagickCore/colorspace.h"
53
#include "MagickCore/colorspace-private.h"
54
#include "MagickCore/composite.h"
55
#include "MagickCore/composite-private.h"
56
#include "MagickCore/constitute.h"
57
#include "MagickCore/draw.h"
58
#include "MagickCore/exception-private.h"
59
#include "MagickCore/fx.h"
60
#include "MagickCore/gem.h"
61
#include "MagickCore/geometry.h"
62
#include "MagickCore/image.h"
63
#include "MagickCore/image-private.h"
64
#include "MagickCore/list.h"
65
#include "MagickCore/log.h"
66
#include "MagickCore/memory_.h"
67
#include "MagickCore/monitor.h"
68
#include "MagickCore/monitor-private.h"
69
#include "MagickCore/morphology.h"
70
#include "MagickCore/option.h"
71
#include "MagickCore/pixel-accessor.h"
72
#include "MagickCore/property.h"
73
#include "MagickCore/quantum.h"
74
#include "MagickCore/resample.h"
75
#include "MagickCore/resource_.h"
76
#include "MagickCore/string_.h"
77
#include "MagickCore/string-private.h"
78
#include "MagickCore/thread-private.h"
79
#include "MagickCore/threshold.h"
80
#include "MagickCore/token.h"
81
#include "MagickCore/transform.h"
82
#include "MagickCore/utility.h"
83
#include "MagickCore/utility-private.h"
84
#include "MagickCore/version.h"
85

86
/*
87
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
88
%                                                                             %
89
%                                                                             %
90
%                                                                             %
91
%   C o m p o s i t e I m a g e                                               %
92
%                                                                             %
93
%                                                                             %
94
%                                                                             %
95
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96
%
97
%  CompositeImage() returns the second image composited onto the first
98
%  at the specified offset, using the specified composite method.
99
%
100
%  The format of the CompositeImage method is:
101
%
102
%      MagickBooleanType CompositeImage(Image *image,
103
%        const Image *source_image,const CompositeOperator compose,
104
%        const MagickBooleanType clip_to_self,const ssize_t x_offset,
105
%        const ssize_t y_offset,ExceptionInfo *exception)
106
%
107
%  A description of each parameter follows:
108
%
109
%    o image: the canvas image, modified by he composition
110
%
111
%    o source_image: the source image.
112
%
113
%    o compose: This operator affects how the composite is applied to
114
%      the image.  The operators and how they are utilized are listed here
115
%      http://www.w3.org/TR/SVG12/#compositing.
116
%
117
%    o clip_to_self: set to MagickTrue to limit composition to area composed.
118
%
119
%    o x_offset: the column offset of the composited image.
120
%
121
%    o y_offset: the row offset of the composited image.
122
%
123
%  Extra Controls from Image meta-data in 'image' (artifacts)
124
%
125
%    o "compose:args"
126
%        A string containing extra numerical arguments for specific compose
127
%        methods, generally expressed as a 'geometry' or a comma separated list
128
%        of numbers.
129
%
130
%        Compose methods needing such arguments include "BlendCompositeOp" and
131
%        "DisplaceCompositeOp".
132
%
133
%    o exception: return any errors or warnings in this structure.
134
%
135
*/
136
137
/*
138
   Composition based on the SVG specification:
139
140
   A Composition is defined by...
141
      Color Function :  f(Sc,Dc)  where Sc and Dc are the normalized colors
142
      Blending areas :  X = 1     for area of overlap, ie: f(Sc,Dc)
143
                        Y = 1     for source preserved
144
                        Z = 1     for canvas preserved
145
146
   Conversion to transparency (then optimized)
147
      Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
148
      Da'  = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
149
150
   Where...
151
      Sca = Sc*Sa     normalized Source color divided by Source alpha
152
      Dca = Dc*Da     normalized Dest color divided by Dest alpha
153
      Dc' = Dca'/Da'  the desired color value for this channel.
154
155
   Da' in in the follow formula as 'gamma'  The resulting alpha value.
156
157
   Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
158
   the following optimizations...
159
      gamma = Sa+Da-Sa*Da;
160
      gamma = 1 - QuantumScale*alpha * QuantumScale*beta;
161
      opacity = QuantumScale*alpha*beta;  // over blend, optimized 1-Gamma
162
163
   The above SVG definitions also define that Mathematical Composition
164
   methods should use a 'Over' blending mode for Alpha Channel.
165
   It however was not applied for composition modes of 'Plus', 'Minus',
166
   the modulus versions of 'Add' and 'Subtract'.
167
168
   Mathematical operator changes to be applied from IM v6.7...
169
170
    1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
171
       'ModulusAdd' and 'ModulusSubtract' for clarity.
172
173
    2) All mathematical compositions work as per the SVG specification
174
       with regard to blending.  This now includes 'ModulusAdd' and
175
       'ModulusSubtract'.
176
177
    3) When the special channel flag 'sync' (synchronize channel updates)
178
       is turned off (enabled by default) then mathematical compositions are
179
       only performed on the channels specified, and are applied
180
       independently of each other.  In other words the mathematics is
181
       performed as 'pure' mathematical operations, rather than as image
182
       operations.
183
*/
184
185
static Image *BlendConvolveImage(const Image *image,const char *kernel,
186
  ExceptionInfo *exception)
187
0
{
188
0
  Image
189
0
    *clone_image,
190
0
    *convolve_image;
191
192
0
  KernelInfo
193
0
    *kernel_info;
194
195
  /*
196
    Convolve image with a kernel.
197
  */
198
0
  kernel_info=AcquireKernelInfo(kernel,exception);
199
0
  if (kernel_info == (KernelInfo *) NULL)
200
0
    return((Image *) NULL);
201
0
  clone_image=CloneImage(image,0,0,MagickTrue,exception);
202
0
  if (clone_image == (Image *) NULL)
203
0
    {
204
0
      kernel_info=DestroyKernelInfo(kernel_info);
205
0
      return((Image *) NULL);
206
0
    }
207
0
  (void) SetImageAlphaChannel(clone_image,OffAlphaChannel,exception);
208
0
  convolve_image=ConvolveImage(clone_image,kernel_info,exception);
209
0
  kernel_info=DestroyKernelInfo(kernel_info);
210
0
  clone_image=DestroyImage(clone_image);
211
0
  return(convolve_image);
212
0
}
213
214
static Image *BlendMagnitudeImage(const Image *dx_image,const Image *dy_image,
215
  ExceptionInfo *exception)
216
0
{
217
0
  CacheView
218
0
    *dx_view,
219
0
    *dy_view,
220
0
    *magnitude_view;
221
222
0
  Image
223
0
    *magnitude_image;
224
225
0
  MagickBooleanType
226
0
    status = MagickTrue;
227
228
0
  ssize_t
229
0
    y;
230
231
  /*
232
    Generate the magnitude between two images.
233
  */
234
0
  magnitude_image=CloneImage(dx_image,0,0,MagickTrue,exception);
235
0
  if (magnitude_image == (Image *) NULL)
236
0
    return(magnitude_image);
237
0
  dx_view=AcquireVirtualCacheView(dx_image,exception);
238
0
  dy_view=AcquireVirtualCacheView(dy_image,exception);
239
0
  magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
240
#if defined(MAGICKCORE_OPENMP_SUPPORT)
241
  #pragma omp parallel for schedule(static) shared(status) \
242
    magick_number_threads(dx_image,magnitude_image,dx_image->rows,1)
243
#endif
244
0
  for (y=0; y < (ssize_t) dx_image->rows; y++)
245
0
  {
246
0
    const Quantum
247
0
      *magick_restrict p,
248
0
      *magick_restrict q;
249
250
0
    Quantum
251
0
      *magick_restrict r;
252
253
0
    ssize_t
254
0
      x;
255
256
0
    if (status == MagickFalse)
257
0
      continue;
258
0
    p=GetCacheViewVirtualPixels(dx_view,0,y,dx_image->columns,1,exception);
259
0
    q=GetCacheViewVirtualPixels(dy_view,0,y,dx_image->columns,1,exception);
260
0
    r=GetCacheViewAuthenticPixels(magnitude_view,0,y,dx_image->columns,1,
261
0
      exception);
262
0
    if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL) ||
263
0
        (r == (Quantum *) NULL))
264
0
      {
265
0
        status=MagickFalse;
266
0
        continue;
267
0
      }
268
0
    for (x=0; x < (ssize_t) dx_image->columns; x++)
269
0
    {
270
0
      ssize_t
271
0
        i;
272
273
0
      for (i=0; i < (ssize_t) GetPixelChannels(dx_image); i++)
274
0
      {
275
0
        PixelChannel channel = GetPixelChannelChannel(dx_image,i);
276
0
        PixelTrait traits = GetPixelChannelTraits(dx_image,channel);
277
0
        PixelTrait dy_traits = GetPixelChannelTraits(dy_image,channel);
278
0
        if ((traits == UndefinedPixelTrait) ||
279
0
            (dy_traits == UndefinedPixelTrait) ||
280
0
            ((dy_traits & UpdatePixelTrait) == 0))
281
0
          continue;
282
0
        r[i]=ClampToQuantum(hypot((double) p[i],(double)
283
0
          GetPixelChannel(dy_image,channel,q)));
284
0
      }
285
0
      p+=(ptrdiff_t) GetPixelChannels(dx_image);
286
0
      q+=(ptrdiff_t) GetPixelChannels(dy_image);
287
0
      r+=(ptrdiff_t) GetPixelChannels(magnitude_image);
288
0
    }
289
0
    if (SyncCacheViewAuthenticPixels(magnitude_view,exception) == MagickFalse)
290
0
      status=MagickFalse;
291
0
  }
292
0
  magnitude_view=DestroyCacheView(magnitude_view);
293
0
  dy_view=DestroyCacheView(dy_view);
294
0
  dx_view=DestroyCacheView(dx_view);
295
0
  if (status == MagickFalse)
296
0
    magnitude_image=DestroyImage(magnitude_image);
297
0
  return(magnitude_image);
298
0
}
299
300
static Image *BlendMaxMagnitudeImage(const Image *alpha_image,
301
  const Image *beta_image,const Image *dx_image,const Image *dy_image,
302
  ExceptionInfo *exception)
303
0
{
304
0
  CacheView
305
0
    *alpha_view,
306
0
    *beta_view,
307
0
    *dx_view,
308
0
    *dy_view,
309
0
    *magnitude_view;
310
311
0
  Image
312
0
    *magnitude_image;
313
314
0
  MagickBooleanType
315
0
    status = MagickTrue;
316
317
0
  ssize_t
318
0
    y;
319
320
  /*
321
    Select the larger of two magnitudes.
322
  */
323
0
  magnitude_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
324
0
  if (magnitude_image == (Image *) NULL)
325
0
    return(magnitude_image);
326
0
  alpha_view=AcquireVirtualCacheView(alpha_image,exception);
327
0
  beta_view=AcquireVirtualCacheView(beta_image,exception);
328
0
  dx_view=AcquireVirtualCacheView(dx_image,exception);
329
0
  dy_view=AcquireVirtualCacheView(dy_image,exception);
330
0
  magnitude_view=AcquireAuthenticCacheView(magnitude_image,exception);
331
#if defined(MAGICKCORE_OPENMP_SUPPORT)
332
  #pragma omp parallel for schedule(static) shared(status) \
333
    magick_number_threads(alpha_image,magnitude_image,alpha_image->rows,1)
334
#endif
335
0
  for (y=0; y < (ssize_t) alpha_image->rows; y++)
336
0
  {
337
0
    const Quantum
338
0
      *magick_restrict p,
339
0
      *magick_restrict q,
340
0
      *magick_restrict r,
341
0
      *magick_restrict s;
342
343
0
    Quantum
344
0
      *magick_restrict t;
345
346
0
    ssize_t
347
0
      x;
348
349
0
    if (status == MagickFalse)
350
0
      continue;
351
0
    p=GetCacheViewVirtualPixels(alpha_view,0,y,alpha_image->columns,1,
352
0
      exception);
353
0
    q=GetCacheViewVirtualPixels(beta_view,0,y,alpha_image->columns,1,exception);
354
0
    r=GetCacheViewVirtualPixels(dx_view,0,y,alpha_image->columns,1,exception);
355
0
    s=GetCacheViewVirtualPixels(dy_view,0,y,alpha_image->columns,1,exception);
356
0
    t=GetCacheViewAuthenticPixels(magnitude_view,0,y,alpha_image->columns,1,
357
0
      exception);
358
0
    if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL) ||
359
0
        (r == (const Quantum *) NULL) || (s == (const Quantum *) NULL) ||
360
0
        (t == (Quantum *) NULL))
361
0
      {
362
0
        status=MagickFalse;
363
0
        continue;
364
0
      }
365
0
    for (x=0; x < (ssize_t) alpha_image->columns; x++)
366
0
    {
367
0
      ssize_t
368
0
        i;
369
370
0
      for (i=0; i < (ssize_t) GetPixelChannels(alpha_image); i++)
371
0
      {
372
0
        PixelChannel channel = GetPixelChannelChannel(alpha_image,i);
373
0
        PixelTrait traits = GetPixelChannelTraits(alpha_image,channel);
374
0
        PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
375
0
        if ((traits == UndefinedPixelTrait) ||
376
0
            (beta_traits == UndefinedPixelTrait) ||
377
0
            ((beta_traits & UpdatePixelTrait) == 0))
378
0
          continue;
379
0
        if (p[i] > GetPixelChannel(beta_image,channel,q))
380
0
          t[i]=GetPixelChannel(dx_image,channel,r);
381
0
        else
382
0
          t[i]=GetPixelChannel(dy_image,channel,s);
383
0
      }
384
0
      p+=(ptrdiff_t) GetPixelChannels(alpha_image);
385
0
      q+=(ptrdiff_t) GetPixelChannels(beta_image);
386
0
      r+=(ptrdiff_t) GetPixelChannels(dx_image);
387
0
      s+=(ptrdiff_t) GetPixelChannels(dy_image);
388
0
      t+=(ptrdiff_t) GetPixelChannels(magnitude_image);
389
0
    }
390
0
    if (SyncCacheViewAuthenticPixels(magnitude_view,exception) == MagickFalse)
391
0
      status=MagickFalse;
392
0
  }
393
0
  magnitude_view=DestroyCacheView(magnitude_view);
394
0
  dy_view=DestroyCacheView(dy_view);
395
0
  dx_view=DestroyCacheView(dx_view);
396
0
  beta_view=DestroyCacheView(beta_view);
397
0
  alpha_view=DestroyCacheView(alpha_view);
398
0
  if (status == MagickFalse)
399
0
    magnitude_image=DestroyImage(magnitude_image);
400
0
  return(magnitude_image);
401
0
}
402
403
static Image *BlendSumImage(const Image *alpha_image,const Image *beta_image,
404
  const double attenuate,const double sign,ExceptionInfo *exception)
405
0
{
406
0
  CacheView
407
0
    *alpha_view,
408
0
    *beta_view,
409
0
    *sum_view;
410
411
0
  Image
412
0
    *sum_image;
413
414
0
  MagickBooleanType
415
0
    status = MagickTrue;
416
417
0
  ssize_t
418
0
    y;
419
420
  /*
421
    Add or subtract and optionally attenuate two images.
422
  */
423
0
  sum_image=CloneImage(alpha_image,0,0,MagickTrue,exception);
424
0
  if (sum_image == (Image *) NULL)
425
0
    return(sum_image);
426
0
  alpha_view=AcquireVirtualCacheView(alpha_image,exception);
427
0
  beta_view=AcquireVirtualCacheView(beta_image,exception);
428
0
  sum_view=AcquireAuthenticCacheView(sum_image,exception);
429
#if defined(MAGICKCORE_OPENMP_SUPPORT)
430
  #pragma omp parallel for schedule(static) shared(status) \
431
    magick_number_threads(alpha_image,sum_image,alpha_image->rows,1)
432
#endif
433
0
  for (y=0; y < (ssize_t) alpha_image->rows; y++)
434
0
  {
435
0
    const Quantum
436
0
      *magick_restrict p,
437
0
      *magick_restrict q;
438
439
0
    Quantum
440
0
      *magick_restrict r;
441
442
0
    ssize_t
443
0
      x;
444
445
0
    if (status == MagickFalse)
446
0
      continue;
447
0
    p=GetCacheViewVirtualPixels(alpha_view,0,y,alpha_image->columns,1,
448
0
      exception);
449
0
    q=GetCacheViewVirtualPixels(beta_view,0,y,alpha_image->columns,1,exception);
450
0
    r=GetCacheViewAuthenticPixels(sum_view,0,y,alpha_image->columns,1,
451
0
      exception);
452
0
    if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL) ||
453
0
        (r == (Quantum *) NULL))
454
0
      {
455
0
        status=MagickFalse;
456
0
        continue;
457
0
      }
458
0
    for (x=0; x < (ssize_t) alpha_image->columns; x++)
459
0
    {
460
0
      ssize_t
461
0
        i;
462
463
0
      for (i=0; i < (ssize_t) GetPixelChannels(alpha_image); i++)
464
0
      {
465
0
        PixelChannel channel = GetPixelChannelChannel(alpha_image,i);
466
0
        PixelTrait traits = GetPixelChannelTraits(alpha_image,channel);
467
0
        PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
468
0
        if ((traits == UndefinedPixelTrait) ||
469
0
            (beta_traits == UndefinedPixelTrait) ||
470
0
            ((beta_traits & UpdatePixelTrait) == 0))
471
0
          continue;
472
0
        r[i]=ClampToQuantum(attenuate*((double) p[i]+sign*
473
0
          (double) GetPixelChannel(beta_image,channel,q)));
474
0
      }
475
0
      p+=(ptrdiff_t) GetPixelChannels(alpha_image);
476
0
      q+=(ptrdiff_t) GetPixelChannels(beta_image);
477
0
      r+=(ptrdiff_t) GetPixelChannels(sum_image);
478
0
    }
479
0
    if (SyncCacheViewAuthenticPixels(sum_view,exception) == MagickFalse)
480
0
      status=MagickFalse;
481
0
  }
482
0
  sum_view=DestroyCacheView(sum_view);
483
0
  beta_view=DestroyCacheView(beta_view);
484
0
  alpha_view=DestroyCacheView(alpha_view);
485
0
  if (status == MagickFalse)
486
0
    sum_image=DestroyImage(sum_image);
487
0
  return(sum_image);
488
0
}
489
490
static Image *BlendDivergentImage(const Image *alpha_image,
491
  const Image *beta_image,ExceptionInfo *exception)
492
0
{
493
0
#define FreeDivergentResources() \
494
0
{ \
495
0
  if (dy_image != (Image *) NULL) \
496
0
    dy_image=DestroyImage(dy_image); \
497
0
  if (dx_image != (Image *) NULL) \
498
0
    dx_image=DestroyImage(dx_image); \
499
0
  if (magnitude_beta != (Image *) NULL) \
500
0
    magnitude_beta=DestroyImage(magnitude_beta); \
501
0
  if (dy_beta != (Image *) NULL) \
502
0
    dy_beta=DestroyImage(dy_beta); \
503
0
  if (dx_beta != (Image *) NULL) \
504
0
    dx_beta=DestroyImage(dx_beta); \
505
0
  if (magnitude_alpha != (Image *) NULL) \
506
0
    magnitude_alpha=DestroyImage(magnitude_alpha); \
507
0
  if (dy_alpha != (Image *) NULL) \
508
0
    dy_alpha=DestroyImage(dy_alpha); \
509
0
  if (dx_alpha != (Image *) NULL) \
510
0
    dx_alpha=DestroyImage(dx_alpha); \
511
0
}
512
513
0
  Image
514
0
    *divergent_image = (Image *) NULL,
515
0
    *dx_alpha = (Image *) NULL,
516
0
    *dx_beta = (Image *) NULL,
517
0
    *dx_divergent = (Image *) NULL,
518
0
    *dx_image = (Image *) NULL,
519
0
    *dy_alpha = (Image *) NULL,
520
0
    *dy_beta = (Image *) NULL,
521
0
    *dy_divergent = (Image *) NULL,
522
0
    *dy_image = (Image *) NULL,
523
0
    *magnitude_alpha = (Image *) NULL,
524
0
    *magnitude_beta = (Image *) NULL;
525
526
  /*
527
    Create X and Y gradient images for alpha image and the magnitude.
528
  */
529
0
  dx_alpha=BlendConvolveImage(alpha_image,"3x1:-0.5,0.0,0.5",exception);
530
0
  if (dx_alpha == (Image *) NULL)
531
0
    {
532
0
      FreeDivergentResources();
533
0
      return((Image *) NULL);
534
0
    }
535
0
  dy_alpha=BlendConvolveImage(alpha_image,"1x3:-0.5,0.0,0.5",exception);
536
0
  if (dy_alpha == (Image *) NULL)
537
0
    {
538
0
      FreeDivergentResources();
539
0
      return((Image *) NULL);
540
0
    }
541
0
  magnitude_alpha=BlendMagnitudeImage(dx_alpha,dy_alpha,exception);
542
0
  if (magnitude_alpha == (Image *) NULL)
543
0
    {
544
0
      FreeDivergentResources();
545
0
      return((Image *) NULL);
546
0
    }
547
  /*
548
    Create X and Y gradient images for beta and the magnitude.
549
  */
550
0
  dx_beta=BlendConvolveImage(beta_image,"3x1:-0.5,0.0,0.5",exception);
551
0
  if (dx_beta == (Image *) NULL)
552
0
    {
553
0
      FreeDivergentResources();
554
0
      return((Image *) NULL);
555
0
    }
556
0
  dy_beta=BlendConvolveImage(beta_image,"1x3:-0.5,0.0,0.5",exception);
557
0
  if (dy_beta == (Image *) NULL)
558
0
    {
559
0
      FreeDivergentResources();
560
0
      return((Image *) NULL);
561
0
    }
562
0
  magnitude_beta=BlendMagnitudeImage(dx_beta,dy_beta,exception);
563
0
  if (magnitude_beta == (Image *) NULL)
564
0
    {
565
0
      FreeDivergentResources();
566
0
      return((Image *) NULL);
567
0
    }
568
  /*
569
    Select alpha or beta gradient for larger of two magnitudes.
570
  */
571
0
  dx_image=BlendMaxMagnitudeImage(magnitude_alpha,magnitude_beta,dx_alpha,
572
0
    dx_beta,exception);
573
0
  if (dx_image == (Image *) NULL)
574
0
    {
575
0
      FreeDivergentResources();
576
0
      return((Image *) NULL);
577
0
    }
578
0
  dy_image=BlendMaxMagnitudeImage(magnitude_alpha,magnitude_beta,dy_alpha,
579
0
    dy_beta,exception);
580
0
  if (dy_image == (Image *) NULL)
581
0
    {
582
0
      FreeDivergentResources();
583
0
      return((Image *) NULL);
584
0
    }
585
0
  dx_beta=DestroyImage(dx_beta);
586
0
  dx_alpha=DestroyImage(dx_alpha);
587
0
  magnitude_beta=DestroyImage(magnitude_beta);
588
0
  magnitude_alpha=DestroyImage(magnitude_alpha);
589
  /*
590
    Create divergence of gradients dx and dy and divide by 4 as guide image.
591
  */
592
0
  dx_divergent=BlendConvolveImage(dx_image,"3x1:-0.5,0.0,0.5",exception);
593
0
  if (dx_divergent == (Image *) NULL)
594
0
    {
595
0
      FreeDivergentResources();
596
0
      return((Image *) NULL);
597
0
    }
598
0
  dy_divergent=BlendConvolveImage(dy_image,"1x3:-0.5,0.0,0.5",exception);
599
0
  if (dy_divergent == (Image *) NULL)
600
0
    {
601
0
      FreeDivergentResources();
602
0
      return((Image *) NULL);
603
0
    }
604
0
  divergent_image=BlendSumImage(dx_divergent,dy_divergent,0.25,1.0,exception);
605
0
  dy_divergent=DestroyImage(dy_divergent);
606
0
  dx_divergent=DestroyImage(dx_divergent);
607
0
  if (divergent_image == (Image *) NULL)
608
0
    {
609
0
      FreeDivergentResources();
610
0
      return((Image *) NULL);
611
0
    }
612
0
  FreeDivergentResources();
613
0
  return(divergent_image);
614
0
}
615
616
static MagickBooleanType BlendMaskAlphaChannel(Image *image,
617
  const Image *mask_image,ExceptionInfo *exception)
618
0
{
619
0
  CacheView
620
0
    *image_view,
621
0
    *mask_view;
622
623
0
  MagickBooleanType
624
0
    status = MagickTrue;
625
626
0
  ssize_t
627
0
    y;
628
629
  /*
630
    Threshold the alpha channel.
631
  */
632
0
  if (SetImageAlpha(image,OpaqueAlpha,exception) == MagickFalse)
633
0
    return(MagickFalse);
634
0
  image_view=AcquireAuthenticCacheView(image,exception);
635
0
  mask_view=AcquireVirtualCacheView(mask_image,exception);
636
#if defined(MAGICKCORE_OPENMP_SUPPORT)
637
  #pragma omp parallel for schedule(static) shared(status) \
638
    magick_number_threads(image,image,image->rows,2)
639
#endif
640
0
  for (y=0; y < (ssize_t) image->rows; y++)
641
0
  {
642
0
    const Quantum
643
0
      *magick_restrict p;
644
645
0
    Quantum
646
0
      *magick_restrict q;
647
648
0
    ssize_t
649
0
      x;
650
651
0
    if (status == MagickFalse)
652
0
      continue;
653
0
    p=GetCacheViewVirtualPixels(mask_view,0,y,image->columns,1,exception);
654
0
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
655
0
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
656
0
      {
657
0
        status=MagickFalse;
658
0
        continue;
659
0
      }
660
0
    for (x=0; x < (ssize_t) image->columns; x++)
661
0
    {
662
0
      Quantum
663
0
        alpha = GetPixelAlpha(mask_image,p);
664
665
0
      ssize_t
666
0
        i = GetPixelChannelOffset(image,AlphaPixelChannel);
667
668
0
      if (fabs((double) alpha) >= MagickEpsilon)
669
0
        q[i]=(Quantum) 0;
670
0
      p+=(ptrdiff_t) GetPixelChannels(mask_image);
671
0
      q+=(ptrdiff_t) GetPixelChannels(image);
672
0
    }
673
0
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
674
0
      status=MagickFalse;
675
0
  }
676
0
  mask_view=DestroyCacheView(mask_view);
677
0
  image_view=DestroyCacheView(image_view);
678
0
  return(status);
679
0
}
680
681
static Image *BlendMeanImage(Image *image,const Image *mask_image,
682
  ExceptionInfo *exception)
683
0
{
684
0
  CacheView
685
0
    *alpha_view,
686
0
    *mask_view,
687
0
    *mean_view;
688
689
0
  double
690
0
    mean[MaxPixelChannels];
691
692
0
  Image
693
0
    *mean_image;
694
695
0
  MagickBooleanType
696
0
    status = MagickTrue;
697
698
0
  ssize_t
699
0
    j,
700
0
    y;
701
702
  /*
703
    Compute the mean of the image.
704
  */
705
0
  (void) memset(mean,0,MaxPixelChannels*sizeof(*mean));
706
0
  alpha_view=AcquireVirtualCacheView(image,exception);
707
0
  for (y=0; y < (ssize_t) image->rows; y++)
708
0
  {
709
0
    const Quantum
710
0
      *magick_restrict p;
711
712
0
    ssize_t
713
0
      x;
714
715
0
    p=GetCacheViewVirtualPixels(alpha_view,0,y,image->columns,1,
716
0
      exception);
717
0
    if (p == (const Quantum *) NULL)
718
0
      break;
719
0
    for (x=0; x < (ssize_t) image->columns; x++)
720
0
    {
721
0
      ssize_t
722
0
        i;
723
724
0
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
725
0
      {
726
0
        PixelChannel channel = GetPixelChannelChannel(image,i);
727
0
        PixelTrait traits = GetPixelChannelTraits(image,channel);
728
0
        if (traits == UndefinedPixelTrait)
729
0
          continue;
730
0
        mean[i]+=QuantumScale*(double) p[i];
731
0
      }
732
0
      p+=(ptrdiff_t) GetPixelChannels(image);
733
0
    }
734
0
  }
735
0
  alpha_view=DestroyCacheView(alpha_view);
736
0
  if (y < (ssize_t) image->rows)
737
0
    return((Image *) NULL);
738
0
  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
739
0
    mean[j]=(double) QuantumRange*mean[j]/image->columns/
740
0
      image->rows;
741
  /*
742
    Replace any unmasked pixels with the mean pixel.
743
  */
744
0
  mean_image=CloneImage(image,0,0,MagickTrue,exception);
745
0
  if (mean_image == (Image *) NULL)
746
0
    return(mean_image);
747
0
  mask_view=AcquireVirtualCacheView(mask_image,exception);
748
0
  mean_view=AcquireAuthenticCacheView(mean_image,exception);
749
#if defined(MAGICKCORE_OPENMP_SUPPORT)
750
  #pragma omp parallel for schedule(static) shared(status) \
751
    magick_number_threads(mask_image,mean_image,mean_image->rows,4)
752
#endif
753
0
  for (y=0; y < (ssize_t) mean_image->rows; y++)
754
0
  {
755
0
    const Quantum
756
0
      *magick_restrict p;
757
758
0
    Quantum
759
0
      *magick_restrict q;
760
761
0
    ssize_t
762
0
      x;
763
764
0
    if (status == MagickFalse)
765
0
      continue;
766
0
    p=GetCacheViewVirtualPixels(mask_view,0,y,mean_image->columns,1,exception);
767
0
    q=GetCacheViewAuthenticPixels(mean_view,0,y,mean_image->columns,1,
768
0
      exception);
769
0
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
770
0
      {
771
0
        status=MagickFalse;
772
0
        continue;
773
0
      }
774
0
    for (x=0; x < (ssize_t) mean_image->columns; x++)
775
0
    {
776
0
      Quantum
777
0
        alpha = GetPixelAlpha(mask_image,p),
778
0
        mask = GetPixelReadMask(mask_image,p);
779
780
0
      ssize_t
781
0
        i;
782
783
0
      for (i=0; i < (ssize_t) GetPixelChannels(mean_image); i++)
784
0
      {
785
0
        PixelChannel channel = GetPixelChannelChannel(mean_image,i);
786
0
        PixelTrait traits = GetPixelChannelTraits(mean_image,channel);
787
0
        if (traits == UndefinedPixelTrait)
788
0
          continue;
789
0
        if (mask <= (QuantumRange/2))
790
0
          q[i]=(Quantum) 0;
791
0
        else
792
0
          if (fabs((double) alpha) >= MagickEpsilon)
793
0
            q[i]=ClampToQuantum(mean[i]);
794
0
      }
795
0
      p+=(ptrdiff_t) GetPixelChannels(mask_image);
796
0
      q+=(ptrdiff_t) GetPixelChannels(mean_image);
797
0
    }
798
0
    if (SyncCacheViewAuthenticPixels(mean_view,exception) == MagickFalse)
799
0
      status=MagickFalse;
800
0
  }
801
0
  mask_view=DestroyCacheView(mask_view);
802
0
  mean_view=DestroyCacheView(mean_view);
803
0
  if (status == MagickFalse)
804
0
    mean_image=DestroyImage(mean_image);
805
0
  return(mean_image);
806
0
}
807
808
static MagickBooleanType BlendRMSEResidual(const Image *alpha_image,
809
  const Image *beta_image,double *residual,ExceptionInfo *exception)
810
0
{
811
0
  CacheView
812
0
    *alpha_view,
813
0
    *beta_view;
814
815
0
  double
816
0
    area = 0.0,
817
0
    channel_residual = 0.0;
818
819
0
  MagickBooleanType
820
0
    status = MagickTrue;
821
822
0
  size_t
823
0
    columns = MagickMax(alpha_image->columns,beta_image->columns),
824
0
    rows = MagickMax(alpha_image->rows,beta_image->rows);
825
826
0
  ssize_t
827
0
    y;
828
829
0
  alpha_view=AcquireVirtualCacheView(alpha_image,exception);
830
0
  beta_view=AcquireVirtualCacheView(beta_image,exception);
831
#if defined(MAGICKCORE_OPENMP_SUPPORT)
832
  #pragma omp parallel for schedule(static) shared(status) \
833
    reduction(+:area) reduction(+:channel_residual) \
834
    magick_number_threads(alpha_image,alpha_image,rows,1)
835
#endif
836
0
  for (y=0; y < (ssize_t) rows; y++)
837
0
  {
838
0
    const Quantum
839
0
      *magick_restrict p,
840
0
      *magick_restrict q;
841
842
0
    ssize_t
843
0
      x;
844
845
0
    if (status == MagickFalse)
846
0
      continue;
847
0
    p=GetCacheViewVirtualPixels(alpha_view,0,y,columns,1,exception);
848
0
    q=GetCacheViewVirtualPixels(beta_view,0,y,columns,1,exception);
849
0
    if ((p == (const Quantum *) NULL) || (q == (const Quantum *) NULL))
850
0
      {
851
0
        status=MagickFalse;
852
0
        continue;
853
0
      }
854
0
    channel_residual=0.0;
855
0
    for (x=0; x < (ssize_t) columns; x++)
856
0
    {
857
0
      double
858
0
        Da,
859
0
        Sa;
860
861
0
      ssize_t
862
0
        i;
863
864
0
      if ((GetPixelReadMask(alpha_image,p) <= (QuantumRange/2)) ||
865
0
          (GetPixelReadMask(beta_image,q) <= (QuantumRange/2)))
866
0
        {
867
0
          p+=(ptrdiff_t) GetPixelChannels(alpha_image);
868
0
          q+=(ptrdiff_t) GetPixelChannels(beta_image);
869
0
          continue;
870
0
        }
871
0
      Sa=QuantumScale*(double) GetPixelAlpha(alpha_image,p);
872
0
      Da=QuantumScale*(double) GetPixelAlpha(beta_image,q);
873
0
      for (i=0; i < (ssize_t) GetPixelChannels(alpha_image); i++)
874
0
      {
875
0
        double
876
0
          distance;
877
878
0
        PixelChannel channel = GetPixelChannelChannel(alpha_image,i);
879
0
        PixelTrait traits = GetPixelChannelTraits(alpha_image,channel);
880
0
        PixelTrait beta_traits = GetPixelChannelTraits(beta_image,channel);
881
0
        if ((traits == UndefinedPixelTrait) ||
882
0
            (beta_traits == UndefinedPixelTrait) ||
883
0
            ((beta_traits & UpdatePixelTrait) == 0))
884
0
          continue;
885
0
        if (channel == AlphaPixelChannel)
886
0
          distance=QuantumScale*((double) p[i]-(double) GetPixelChannel(
887
0
            beta_image,channel,q));
888
0
        else
889
0
          distance=QuantumScale*(Sa*(double) p[i]-Da*(double) GetPixelChannel(
890
0
            beta_image,channel,q));
891
0
        channel_residual+=distance*distance;
892
0
      }
893
0
      area++;
894
0
      p+=(ptrdiff_t) GetPixelChannels(alpha_image);
895
0
      q+=(ptrdiff_t) GetPixelChannels(beta_image);
896
0
    }
897
0
  }
898
0
  beta_view=DestroyCacheView(beta_view);
899
0
  alpha_view=DestroyCacheView(alpha_view);
900
0
  area=MagickSafeReciprocal(area);
901
0
  *residual=sqrt(area*channel_residual/(double) GetImageChannels(alpha_image));
902
0
  return(status);
903
0
}
904
905
static MagickBooleanType CompositeOverImage(Image *image,
906
  const Image *source_image,const MagickBooleanType clip_to_self,
907
  const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
908
17.2k
{
909
17.2k
#define CompositeImageTag  "Composite/Image"
910
911
17.2k
  CacheView
912
17.2k
    *image_view,
913
17.2k
    *source_view;
914
915
17.2k
  const char
916
17.2k
    *value;
917
918
17.2k
  MagickBooleanType
919
17.2k
    clamp,
920
17.2k
    status;
921
922
17.2k
  MagickOffsetType
923
17.2k
    progress;
924
925
17.2k
  ssize_t
926
17.2k
    y;
927
928
  /*
929
    Composite image.
930
  */
931
17.2k
  status=MagickTrue;
932
17.2k
  progress=0;
933
17.2k
  clamp=MagickTrue;
934
17.2k
  value=GetImageArtifact(image,"compose:clamp");
935
17.2k
  if (value != (const char *) NULL)
936
0
    clamp=IsStringTrue(value);
937
17.2k
  status=MagickTrue;
938
17.2k
  progress=0;
939
17.2k
  source_view=AcquireVirtualCacheView(source_image,exception);
940
17.2k
  image_view=AcquireAuthenticCacheView(image,exception);
941
#if defined(MAGICKCORE_OPENMP_SUPPORT)
942
  #pragma omp parallel for schedule(static) shared(progress,status) \
943
    magick_number_threads(source_image,image,image->rows,1)
944
#endif
945
3.15M
  for (y=0; y < (ssize_t) image->rows; y++)
946
3.13M
  {
947
3.13M
    const Quantum
948
3.13M
      *pixels;
949
950
3.13M
    PixelInfo
951
3.13M
      canvas_pixel,
952
3.13M
      source_pixel;
953
954
3.13M
    const Quantum
955
3.13M
      *magick_restrict p;
956
957
3.13M
    Quantum
958
3.13M
      *magick_restrict q;
959
960
3.13M
    ssize_t
961
3.13M
      x;
962
963
3.13M
    if (status == MagickFalse)
964
0
      continue;
965
3.13M
    if (clip_to_self != MagickFalse)
966
3.13M
      {
967
3.13M
        if (y < y_offset)
968
243k
          continue;
969
2.89M
        if ((y-y_offset) >= (ssize_t) source_image->rows)
970
1.12M
          continue;
971
2.89M
      }
972
    /*
973
      If pixels is NULL, y is outside overlay region.
974
    */
975
1.77M
    pixels=(Quantum *) NULL;
976
1.77M
    p=(Quantum *) NULL;
977
1.77M
    if ((y >= y_offset) &&
978
1.77M
        ((y-y_offset) < (ssize_t) source_image->rows))
979
1.77M
      {
980
1.77M
        p=GetCacheViewVirtualPixels(source_view,0,CastDoubleToSsizeT((double) y-
981
1.77M
          y_offset),source_image->columns,1,exception);
982
1.77M
        if (p == (const Quantum *) NULL)
983
0
          {
984
0
            status=MagickFalse;
985
0
            continue;
986
0
          }
987
1.77M
        pixels=p;
988
1.77M
        if (x_offset < 0)
989
118
          p-=(ptrdiff_t) CastDoubleToSsizeT((double) x_offset*
990
118
            GetPixelChannels(source_image));
991
1.77M
      }
992
1.77M
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
993
1.77M
    if (q == (Quantum *) NULL)
994
0
      {
995
0
        status=MagickFalse;
996
0
        continue;
997
0
      }
998
1.77M
    GetPixelInfo(image,&canvas_pixel);
999
1.77M
    GetPixelInfo(source_image,&source_pixel);
1000
232M
    for (x=0; x < (ssize_t) image->columns; x++)
1001
232M
    {
1002
232M
      double
1003
232M
        gamma;
1004
1005
232M
      MagickRealType
1006
232M
        alpha,
1007
232M
        Da,
1008
232M
        Dc,
1009
232M
        Dca,
1010
232M
        Sa,
1011
232M
        Sc,
1012
232M
        Sca;
1013
1014
232M
      ssize_t
1015
232M
        i;
1016
1017
232M
      size_t
1018
232M
        channels;
1019
1020
232M
      if (clip_to_self != MagickFalse)
1021
232M
        {
1022
232M
          if (x < x_offset)
1023
27.0M
            {
1024
27.0M
              q+=(ptrdiff_t) GetPixelChannels(image);
1025
27.0M
              continue;
1026
27.0M
            }
1027
205M
          if ((x-x_offset) >= (ssize_t) source_image->columns)
1028
1.63M
            break;
1029
205M
        }
1030
203M
      if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1031
203M
          ((x-x_offset) >= (ssize_t) source_image->columns))
1032
0
        {
1033
0
          Quantum
1034
0
            source[MaxPixelChannels];
1035
1036
          /*
1037
            Virtual composite:
1038
              Sc: source color.
1039
              Dc: canvas color.
1040
          */
1041
0
          (void) GetOneVirtualPixel(source_image,
1042
0
            CastDoubleToSsizeT((double) x-x_offset),
1043
0
            CastDoubleToSsizeT((double) y-y_offset),source,exception);
1044
0
          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1045
0
          {
1046
0
            MagickRealType
1047
0
              pixel;
1048
1049
0
            PixelChannel channel = GetPixelChannelChannel(image,i);
1050
0
            PixelTrait traits = GetPixelChannelTraits(image,channel);
1051
0
            PixelTrait source_traits=GetPixelChannelTraits(source_image,
1052
0
              channel);
1053
0
            if ((traits == UndefinedPixelTrait) ||
1054
0
                (source_traits == UndefinedPixelTrait))
1055
0
              continue;
1056
0
            if (channel == AlphaPixelChannel)
1057
0
              pixel=(MagickRealType) TransparentAlpha;
1058
0
            else
1059
0
              pixel=(MagickRealType) q[i];
1060
0
            q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1061
0
              ClampToQuantum(pixel);
1062
0
          }
1063
0
          q+=(ptrdiff_t) GetPixelChannels(image);
1064
0
          continue;
1065
0
        }
1066
      /*
1067
        Authentic composite:
1068
          Sa:  normalized source alpha.
1069
          Da:  normalized canvas alpha.
1070
      */
1071
203M
      Sa=QuantumScale*(double) GetPixelAlpha(source_image,p);
1072
203M
      Da=QuantumScale*(double) GetPixelAlpha(image,q);
1073
203M
      alpha=Sa+Da-Sa*Da;
1074
764M
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1075
561M
      {
1076
561M
        MagickRealType
1077
561M
          pixel;
1078
1079
561M
        PixelChannel channel = GetPixelChannelChannel(image,i);
1080
561M
        PixelTrait traits = GetPixelChannelTraits(image,channel);
1081
561M
        PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
1082
561M
        if (traits == UndefinedPixelTrait)
1083
0
          continue;
1084
561M
        if ((source_traits == UndefinedPixelTrait) &&
1085
310
            (channel != AlphaPixelChannel))
1086
76
            continue;
1087
561M
        if (channel == AlphaPixelChannel)
1088
50.1M
          {
1089
            /*
1090
              Set alpha channel.
1091
            */
1092
50.1M
            pixel=(double) QuantumRange*alpha;
1093
50.1M
            q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1094
50.1M
              ClampToQuantum(pixel);
1095
50.1M
            continue;
1096
50.1M
          }
1097
        /*
1098
          Sc: source color.
1099
          Dc: canvas color.
1100
        */
1101
511M
        Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1102
511M
        Dc=(MagickRealType) q[i];
1103
511M
        if ((traits & CopyPixelTrait) != 0)
1104
107k
          {
1105
            /*
1106
              Copy channel.
1107
            */
1108
107k
            q[i]=ClampToQuantum(Sc);
1109
107k
            continue;
1110
107k
          }
1111
        /*
1112
          Porter-Duff compositions:
1113
            Sca: source normalized color multiplied by alpha.
1114
            Dca: normalized canvas color multiplied by alpha.
1115
        */
1116
511M
        Sca=QuantumScale*Sa*Sc;
1117
511M
        Dca=QuantumScale*Da*Dc;
1118
511M
        gamma=MagickSafeReciprocal(alpha);
1119
511M
        pixel=(double) QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
1120
511M
        q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
1121
511M
      }
1122
203M
      p+=(ptrdiff_t) GetPixelChannels(source_image);
1123
203M
      channels=GetPixelChannels(source_image);
1124
203M
      if (p >= (pixels+channels*source_image->columns))
1125
1.77M
        p=pixels;
1126
203M
      q+=(ptrdiff_t) GetPixelChannels(image);
1127
203M
    }
1128
1.77M
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1129
0
      status=MagickFalse;
1130
1.77M
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1131
0
      {
1132
0
        MagickBooleanType
1133
0
          proceed;
1134
1135
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1136
        #pragma omp atomic
1137
#endif
1138
0
        progress++;
1139
0
        proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
1140
0
        if (proceed == MagickFalse)
1141
0
          status=MagickFalse;
1142
0
      }
1143
1.77M
  }
1144
17.2k
  source_view=DestroyCacheView(source_view);
1145
17.2k
  image_view=DestroyCacheView(image_view);
1146
17.2k
  return(status);
1147
17.2k
}
1148
1149
static MagickBooleanType SaliencyBlendImage(Image *image,
1150
  const Image *source_image,const ssize_t x_offset,const ssize_t y_offset,
1151
  const double iterations,const double residual_threshold,const size_t tick,
1152
  ExceptionInfo *exception)
1153
0
{
1154
0
  Image
1155
0
    *crop_image,
1156
0
    *divergent_image,
1157
0
    *relax_image,
1158
0
    *residual_image = (Image *) NULL;
1159
1160
0
  KernelInfo
1161
0
    *kernel_info;
1162
1163
0
  MagickBooleanType
1164
0
    status = MagickTrue,
1165
0
    verbose = MagickFalse;
1166
1167
0
  RectangleInfo
1168
0
    crop_info = {
1169
0
      source_image->columns,
1170
0
      source_image->rows,
1171
0
      x_offset,
1172
0
      y_offset
1173
0
    };
1174
1175
0
  ssize_t
1176
0
    i;
1177
1178
  /*
1179
    Saliency blend composite operator.
1180
  */
1181
0
  crop_image=CropImage(image,&crop_info,exception);
1182
0
  if (crop_image == (Image *) NULL)
1183
0
    return(MagickFalse);
1184
0
  DisableCompositeClampUnlessSpecified(crop_image);
1185
0
  divergent_image=BlendDivergentImage(crop_image,source_image,exception);
1186
0
  if (divergent_image == (Image *) NULL)
1187
0
    {
1188
0
      crop_image=DestroyImage(crop_image);
1189
0
      return(MagickFalse);
1190
0
    }
1191
0
  (void) ResetImagePage(crop_image,"0x0+0+0");
1192
0
  relax_image=BlendMeanImage(crop_image,source_image,exception);
1193
0
  if (relax_image == (Image *) NULL)
1194
0
    {
1195
0
      crop_image=DestroyImage(crop_image);
1196
0
      divergent_image=DestroyImage(divergent_image);
1197
0
      return(MagickFalse);
1198
0
    }
1199
0
  status=BlendMaskAlphaChannel(crop_image,source_image,exception);
1200
0
  if (status == MagickFalse)
1201
0
    {
1202
0
      crop_image=DestroyImage(crop_image);
1203
0
      divergent_image=DestroyImage(divergent_image);
1204
0
      return(MagickFalse);
1205
0
    }
1206
0
  residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1207
0
  if (residual_image == (Image *) NULL)
1208
0
    {
1209
0
      crop_image=DestroyImage(crop_image);
1210
0
      relax_image=DestroyImage(relax_image);
1211
0
      return(MagickFalse);
1212
0
    }
1213
  /*
1214
    Convolve relaxed image and blur area of interest.
1215
  */
1216
0
  kernel_info=AcquireKernelInfo("3x3:0,0.25,0,0.25,0,0.25,0,0.25,0",exception);
1217
0
  if (kernel_info == (KernelInfo *) NULL)
1218
0
    {
1219
0
      crop_image=DestroyImage(crop_image);
1220
0
      residual_image=DestroyImage(residual_image);
1221
0
      relax_image=DestroyImage(relax_image);
1222
0
      return(MagickFalse);
1223
0
    }
1224
0
  verbose=IsStringTrue(GetImageArtifact(image,"verbose"));
1225
0
  if (verbose != MagickFalse)
1226
0
    (void) FormatLocaleFile(stderr,"saliency blending:\n");
1227
0
  for (i=0; i < (ssize_t) iterations; i++)
1228
0
  {
1229
0
    double
1230
0
      residual = 1.0;
1231
1232
0
    Image
1233
0
      *convolve_image,
1234
0
      *sum_image;
1235
1236
0
    convolve_image=ConvolveImage(relax_image,kernel_info,exception);
1237
0
    if (convolve_image == (Image *) NULL)
1238
0
      break;
1239
0
    relax_image=DestroyImage(relax_image);
1240
0
    relax_image=convolve_image;
1241
0
    sum_image=BlendSumImage(relax_image,divergent_image,1.0,-1.0,exception);
1242
0
    if (sum_image == (Image *) NULL)
1243
0
      break;
1244
0
    relax_image=DestroyImage(relax_image);
1245
0
    relax_image=sum_image;
1246
0
    status=CompositeOverImage(relax_image,crop_image,MagickTrue,0,0,exception);
1247
0
    if (status == MagickFalse)
1248
0
      break;
1249
0
    status=BlendRMSEResidual(relax_image,residual_image,&residual,exception);
1250
0
    if (status == MagickFalse)
1251
0
      break;
1252
0
    if ((verbose != MagickFalse) && ((i % MagickMax(tick,1)) == 0))
1253
0
      (void) FormatLocaleFile(stderr,"  %g: %g\n",(double) i,(double) residual);
1254
0
    if (residual < residual_threshold)
1255
0
      {
1256
0
        if (verbose != MagickFalse)
1257
0
          (void) FormatLocaleFile(stderr,"  %g: %g\n",(double) i,(double)
1258
0
            residual);
1259
0
        break;
1260
0
      }
1261
0
    residual_image=DestroyImage(residual_image);
1262
0
    residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1263
0
    if (residual_image == (Image *) NULL)
1264
0
      break;
1265
0
  }
1266
0
  kernel_info=DestroyKernelInfo(kernel_info);
1267
0
  crop_image=DestroyImage(crop_image);
1268
0
  divergent_image=DestroyImage(divergent_image);
1269
0
  residual_image=DestroyImage(residual_image);
1270
  /*
1271
    Composite relaxed over the background image.
1272
  */
1273
0
  status=CompositeOverImage(image,relax_image,MagickTrue,x_offset,y_offset,
1274
0
    exception);
1275
0
  relax_image=DestroyImage(relax_image);
1276
0
  return(status);
1277
0
}
1278
1279
static MagickBooleanType SeamlessBlendImage(Image *image,
1280
  const Image *source_image,const ssize_t x_offset,const ssize_t y_offset,
1281
  const double iterations,const double residual_threshold,const size_t tick,
1282
  ExceptionInfo *exception)
1283
0
{
1284
0
  Image
1285
0
    *crop_image,
1286
0
    *foreground_image,
1287
0
    *mean_image,
1288
0
    *relax_image,
1289
0
    *residual_image,
1290
0
    *sum_image;
1291
1292
0
  KernelInfo
1293
0
    *kernel_info;
1294
1295
0
  MagickBooleanType
1296
0
    status = MagickTrue,
1297
0
    verbose = MagickFalse;
1298
1299
0
  RectangleInfo
1300
0
    crop_info = {
1301
0
      source_image->columns,
1302
0
      source_image->rows,
1303
0
      x_offset,
1304
0
      y_offset
1305
0
    };
1306
1307
0
  ssize_t
1308
0
    i;
1309
1310
  /*
1311
    Seamless blend composite operator.
1312
  */
1313
0
  crop_image=CropImage(image,&crop_info,exception);
1314
0
  if (crop_image == (Image *) NULL)
1315
0
    return(MagickFalse);
1316
0
  DisableCompositeClampUnlessSpecified(crop_image);
1317
0
  (void) ResetImagePage(crop_image,"0x0+0+0");
1318
0
  sum_image=BlendSumImage(crop_image,source_image,1.0,-1.0,exception);
1319
0
  crop_image=DestroyImage(crop_image);
1320
0
  if (sum_image == (Image *) NULL)
1321
0
    return(MagickFalse);
1322
0
  mean_image=BlendMeanImage(sum_image,source_image,exception);
1323
0
  sum_image=DestroyImage(sum_image);
1324
0
  if (mean_image == (Image *) NULL)
1325
0
    return(MagickFalse);
1326
0
  relax_image=CloneImage(mean_image,0,0,MagickTrue,exception);
1327
0
  if (relax_image == (Image *) NULL)
1328
0
    {
1329
0
      mean_image=DestroyImage(mean_image);
1330
0
      return(MagickFalse);
1331
0
    }
1332
0
  status=BlendMaskAlphaChannel(mean_image,source_image,exception);
1333
0
  if (status == MagickFalse)
1334
0
    {
1335
0
      relax_image=DestroyImage(relax_image);
1336
0
      mean_image=DestroyImage(mean_image);
1337
0
      return(MagickFalse);
1338
0
    }
1339
0
  residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1340
0
  if (residual_image == (Image *) NULL)
1341
0
    {
1342
0
      relax_image=DestroyImage(relax_image);
1343
0
      mean_image=DestroyImage(mean_image);
1344
0
      return(MagickFalse);
1345
0
    }
1346
  /*
1347
    Convolve relaxed image and blur area of interest.
1348
  */
1349
0
  kernel_info=AcquireKernelInfo("3x3:0,0.25,0,0.25,0,0.25,0,0.25,0",exception);
1350
0
  if (kernel_info == (KernelInfo *) NULL)
1351
0
    {
1352
0
      residual_image=DestroyImage(residual_image);
1353
0
      relax_image=DestroyImage(relax_image);
1354
0
      mean_image=DestroyImage(mean_image);
1355
0
      return(MagickFalse);
1356
0
    }
1357
0
  verbose=IsStringTrue(GetImageArtifact(image,"verbose"));
1358
0
  if (verbose != MagickFalse)
1359
0
    (void) FormatLocaleFile(stderr,"seamless blending:\n");
1360
0
  for (i=0; i < (ssize_t) iterations; i++)
1361
0
  {
1362
0
    double
1363
0
      residual = 1.0;
1364
1365
0
    Image
1366
0
      *convolve_image;
1367
1368
0
    convolve_image=ConvolveImage(relax_image,kernel_info,exception);
1369
0
    if (convolve_image == (Image *) NULL)
1370
0
      break;
1371
0
    relax_image=DestroyImage(relax_image);
1372
0
    relax_image=convolve_image;
1373
0
    status=CompositeOverImage(relax_image,mean_image,MagickTrue,0,0,exception);
1374
0
    if (status == MagickFalse)
1375
0
      break;
1376
0
    status=BlendRMSEResidual(relax_image,residual_image,&residual,exception);
1377
0
    if (status == MagickFalse)
1378
0
      break;
1379
0
    if ((verbose != MagickFalse) && ((i % MagickMax(tick,1)) == 0))
1380
0
      (void) FormatLocaleFile(stderr,"  %g: %g\n",(double) i,(double) residual);
1381
0
    if (residual < residual_threshold)
1382
0
      {
1383
0
        if (verbose != MagickFalse)
1384
0
          (void) FormatLocaleFile(stderr,"  %g: %g\n",(double) i,(double)
1385
0
            residual);
1386
0
        break;
1387
0
      }
1388
0
    if (residual_image != (Image *) NULL)
1389
0
      residual_image=DestroyImage(residual_image);
1390
0
    residual_image=CloneImage(relax_image,0,0,MagickTrue,exception);
1391
0
    if (residual_image == (Image *) NULL)
1392
0
      break;
1393
0
  }
1394
0
  kernel_info=DestroyKernelInfo(kernel_info);
1395
0
  mean_image=DestroyImage(mean_image);
1396
0
  residual_image=DestroyImage(residual_image);
1397
  /*
1398
    Composite the foreground image over the background image.
1399
  */
1400
0
  foreground_image=BlendSumImage(source_image,relax_image,1.0,1.0,exception);
1401
0
  relax_image=DestroyImage(relax_image);
1402
0
  if (foreground_image == (Image *) NULL)
1403
0
    return(MagickFalse);
1404
0
  (void) SetImageMask(foreground_image,ReadPixelMask,(const Image *) NULL,
1405
0
    exception);
1406
0
  status=CompositeOverImage(image,foreground_image,MagickTrue,x_offset,y_offset,
1407
0
    exception);
1408
0
  foreground_image=DestroyImage(foreground_image);
1409
0
  return(status);
1410
0
}
1411
1412
MagickExport MagickBooleanType CompositeImage(Image *image,
1413
  const Image *composite,const CompositeOperator compose,
1414
  const MagickBooleanType clip_to_self,const ssize_t x_offset,
1415
  const ssize_t y_offset,ExceptionInfo *exception)
1416
35.8k
{
1417
35.8k
#define CompositeImageTag  "Composite/Image"
1418
1419
35.8k
  CacheView
1420
35.8k
    *source_view,
1421
35.8k
    *image_view;
1422
1423
35.8k
  ColorspaceType
1424
35.8k
    colorspace = HCLColorspace;
1425
1426
35.8k
  const char
1427
35.8k
    *artifact;
1428
1429
35.8k
  double
1430
35.8k
    white_luminance = 10000.0;
1431
1432
35.8k
  GeometryInfo
1433
35.8k
    geometry_info;
1434
1435
35.8k
  IlluminantType
1436
35.8k
    illuminant = D65Illuminant;
1437
1438
35.8k
  Image
1439
35.8k
    *canvas_image,
1440
35.8k
    *source_image;
1441
1442
35.8k
  MagickBooleanType
1443
35.8k
    clamp,
1444
35.8k
    compose_sync,
1445
35.8k
    status;
1446
1447
35.8k
  MagickOffsetType
1448
35.8k
    progress;
1449
1450
35.8k
  MagickRealType
1451
35.8k
    amount,
1452
35.8k
    canvas_dissolve,
1453
35.8k
    midpoint,
1454
35.8k
    percent_luma,
1455
35.8k
    percent_chroma,
1456
35.8k
    source_dissolve,
1457
35.8k
    threshold;
1458
1459
35.8k
  MagickStatusType
1460
35.8k
    flags;
1461
1462
35.8k
  ssize_t
1463
35.8k
    y;
1464
1465
35.8k
  assert(image != (Image *) NULL);
1466
35.8k
  assert(image->signature == MagickCoreSignature);
1467
35.8k
  assert(composite != (Image *) NULL);
1468
35.8k
  assert(composite->signature == MagickCoreSignature);
1469
35.8k
  if (IsEventLogging() != MagickFalse)
1470
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1471
35.8k
  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1472
92
    return(MagickFalse);
1473
35.7k
  source_image=CloneImage(composite,0,0,MagickTrue,exception);
1474
35.7k
  if (source_image == (const Image *) NULL)
1475
116
    return(MagickFalse);
1476
35.6k
  (void) SetImageColorspace(source_image,image->colorspace,exception);
1477
35.6k
  if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
1478
17.2k
    {
1479
17.2k
      status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
1480
17.2k
        y_offset,exception);
1481
17.2k
      source_image=DestroyImage(source_image);
1482
17.2k
      return(status);
1483
17.2k
    }
1484
18.4k
  amount=0.5;
1485
18.4k
  canvas_image=(Image *) NULL;
1486
18.4k
  canvas_dissolve=1.0;
1487
18.4k
  white_luminance=10000.0;
1488
18.4k
  artifact=GetImageArtifact(image,"compose:white-luminance");
1489
18.4k
  if (artifact != (const char *) NULL)
1490
0
    white_luminance=StringToDouble(artifact,(char **) NULL);
1491
18.4k
  artifact=GetImageArtifact(image,"compose:illuminant");
1492
18.4k
  if (artifact != (const char *) NULL)
1493
0
    {
1494
0
      ssize_t
1495
0
        illuminant_type;
1496
1497
0
      illuminant_type=ParseCommandOption(MagickIlluminantOptions,MagickFalse,
1498
0
        artifact);
1499
0
      if (illuminant_type < 0)
1500
0
        illuminant=UndefinedIlluminant;
1501
0
      else
1502
0
        illuminant=(IlluminantType) illuminant_type;
1503
0
    }
1504
18.4k
  artifact=GetImageArtifact(image,"compose:colorspace");
1505
18.4k
  if (artifact != (const char *) NULL)
1506
0
    {
1507
0
      ssize_t
1508
0
        colorspace_type;
1509
1510
0
      colorspace_type=ParseCommandOption(MagickColorspaceOptions,MagickFalse,
1511
0
        artifact);
1512
0
      if (colorspace_type < 0)
1513
0
        colorspace=UndefinedColorspace;
1514
0
      else
1515
0
        colorspace=(ColorspaceType) colorspace_type;
1516
0
    }
1517
18.4k
  clamp=MagickTrue;
1518
18.4k
  artifact=GetImageArtifact(image,"compose:clamp");
1519
18.4k
  if (artifact != (const char *) NULL)
1520
0
    clamp=IsStringTrue(artifact);
1521
18.4k
  compose_sync=MagickTrue;
1522
18.4k
  artifact=GetImageArtifact(image,"compose:sync");
1523
18.4k
  if (artifact != (const char *) NULL)
1524
0
    compose_sync=IsStringTrue(artifact);
1525
18.4k
  SetGeometryInfo(&geometry_info);
1526
18.4k
  percent_luma=100.0;
1527
18.4k
  percent_chroma=100.0;
1528
18.4k
  source_dissolve=1.0;
1529
18.4k
  threshold=0.05f;
1530
18.4k
  switch (compose)
1531
18.4k
  {
1532
18.3k
    case CopyCompositeOp:
1533
18.3k
    {
1534
18.3k
      if ((x_offset < 0) || (y_offset < 0))
1535
1.16k
        break;
1536
17.1k
      if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
1537
3.73k
        break;
1538
13.4k
      if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
1539
5.51k
        break;
1540
7.94k
      if ((source_image->alpha_trait == UndefinedPixelTrait) &&
1541
4.20k
          (image->alpha_trait != UndefinedPixelTrait))
1542
75
        (void) SetImageAlphaChannel(source_image,OpaqueAlphaChannel,exception);
1543
7.94k
      status=MagickTrue;
1544
7.94k
      source_view=AcquireVirtualCacheView(source_image,exception);
1545
7.94k
      image_view=AcquireAuthenticCacheView(image,exception);
1546
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1547
      #pragma omp parallel for schedule(static) shared(status) \
1548
        magick_number_threads(source_image,image,source_image->rows,4)
1549
#endif
1550
728k
      for (y=0; y < (ssize_t) source_image->rows; y++)
1551
720k
      {
1552
720k
        MagickBooleanType
1553
720k
          sync;
1554
1555
720k
        const Quantum
1556
720k
          *p;
1557
1558
720k
        Quantum
1559
720k
          *q;
1560
1561
720k
        ssize_t
1562
720k
          x;
1563
1564
720k
        if (status == MagickFalse)
1565
0
          continue;
1566
720k
        p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1567
720k
          exception);
1568
720k
        q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1569
720k
          source_image->columns,1,exception);
1570
720k
        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1571
0
          {
1572
0
            status=MagickFalse;
1573
0
            continue;
1574
0
          }
1575
250M
        for (x=0; x < (ssize_t) source_image->columns; x++)
1576
249M
        {
1577
249M
          ssize_t
1578
249M
            i;
1579
1580
249M
          if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
1581
0
            {
1582
0
              p+=(ptrdiff_t) GetPixelChannels(source_image);
1583
0
              q+=(ptrdiff_t) GetPixelChannels(image);
1584
0
              continue;
1585
0
            }
1586
1.03G
          for (i=0; i < (ssize_t) GetPixelChannels(source_image); i++)
1587
783M
          {
1588
783M
            PixelChannel channel = GetPixelChannelChannel(source_image,i);
1589
783M
            PixelTrait source_traits = GetPixelChannelTraits(source_image,
1590
783M
              channel);
1591
783M
            PixelTrait traits = GetPixelChannelTraits(image,channel);
1592
783M
            if ((source_traits == UndefinedPixelTrait) ||
1593
783M
                (traits == UndefinedPixelTrait))
1594
85.4M
              continue;
1595
697M
            SetPixelChannel(image,channel,p[i],q);
1596
697M
          }
1597
249M
          p+=(ptrdiff_t) GetPixelChannels(source_image);
1598
249M
          q+=(ptrdiff_t) GetPixelChannels(image);
1599
249M
        }
1600
720k
        sync=SyncCacheViewAuthenticPixels(image_view,exception);
1601
720k
        if (sync == MagickFalse)
1602
0
          status=MagickFalse;
1603
720k
        if (image->progress_monitor != (MagickProgressMonitor) NULL)
1604
0
          {
1605
0
            MagickBooleanType
1606
0
              proceed;
1607
1608
0
            proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
1609
0
              y,image->rows);
1610
0
            if (proceed == MagickFalse)
1611
0
              status=MagickFalse;
1612
0
          }
1613
720k
      }
1614
7.94k
      source_view=DestroyCacheView(source_view);
1615
7.94k
      image_view=DestroyCacheView(image_view);
1616
7.94k
      source_image=DestroyImage(source_image);
1617
7.94k
      return(status);
1618
13.4k
    }
1619
0
    case IntensityCompositeOp:
1620
0
    {
1621
0
      if ((x_offset < 0) || (y_offset < 0))
1622
0
        break;
1623
0
      if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
1624
0
        break;
1625
0
      if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
1626
0
        break;
1627
0
      status=MagickTrue;
1628
0
      source_view=AcquireVirtualCacheView(source_image,exception);
1629
0
      image_view=AcquireAuthenticCacheView(image,exception);
1630
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1631
      #pragma omp parallel for schedule(static) shared(status) \
1632
        magick_number_threads(source_image,image,source_image->rows,4)
1633
#endif
1634
0
      for (y=0; y < (ssize_t) source_image->rows; y++)
1635
0
      {
1636
0
        MagickBooleanType
1637
0
          sync;
1638
1639
0
        const Quantum
1640
0
          *p;
1641
1642
0
        Quantum
1643
0
          *q;
1644
1645
0
        ssize_t
1646
0
          x;
1647
1648
0
        if (status == MagickFalse)
1649
0
          continue;
1650
0
        p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1651
0
          exception);
1652
0
        q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
1653
0
          source_image->columns,1,exception);
1654
0
        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1655
0
          {
1656
0
            status=MagickFalse;
1657
0
            continue;
1658
0
          }
1659
0
        for (x=0; x < (ssize_t) source_image->columns; x++)
1660
0
        {
1661
0
          if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
1662
0
            {
1663
0
              p+=(ptrdiff_t) GetPixelChannels(source_image);
1664
0
              q+=(ptrdiff_t) GetPixelChannels(image);
1665
0
              continue;
1666
0
            }
1667
0
          SetPixelAlpha(image,clamp != MagickFalse ?
1668
0
            ClampPixel(GetPixelIntensity(source_image,p)) :
1669
0
            ClampToQuantum(GetPixelIntensity(source_image,p)),q);
1670
0
          p+=(ptrdiff_t) GetPixelChannels(source_image);
1671
0
          q+=(ptrdiff_t) GetPixelChannels(image);
1672
0
        }
1673
0
        sync=SyncCacheViewAuthenticPixels(image_view,exception);
1674
0
        if (sync == MagickFalse)
1675
0
          status=MagickFalse;
1676
0
        if (image->progress_monitor != (MagickProgressMonitor) NULL)
1677
0
          {
1678
0
            MagickBooleanType
1679
0
              proceed;
1680
1681
0
            proceed=SetImageProgress(image,CompositeImageTag,(MagickOffsetType)
1682
0
              y,image->rows);
1683
0
            if (proceed == MagickFalse)
1684
0
              status=MagickFalse;
1685
0
          }
1686
0
      }
1687
0
      source_view=DestroyCacheView(source_view);
1688
0
      image_view=DestroyCacheView(image_view);
1689
0
      source_image=DestroyImage(source_image);
1690
0
      return(status);
1691
0
    }
1692
0
    case CopyAlphaCompositeOp:
1693
0
    case ChangeMaskCompositeOp:
1694
0
    {
1695
      /*
1696
        Modify canvas outside the overlaid region and require an alpha
1697
        channel to exist, to add transparency.
1698
      */
1699
0
      if ((image->alpha_trait & BlendPixelTrait) == 0)
1700
0
        (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1701
0
      break;
1702
0
    }
1703
0
    case BlurCompositeOp:
1704
0
    {
1705
0
      CacheView
1706
0
        *canvas_view;
1707
1708
0
      double
1709
0
        angle_range,
1710
0
        angle_start,
1711
0
        height,
1712
0
        width;
1713
1714
0
      PixelInfo
1715
0
        pixel;
1716
1717
0
      ResampleFilter
1718
0
        *resample_filter;
1719
1720
0
      SegmentInfo
1721
0
        blur;
1722
1723
      /*
1724
        Blur Image by resampling dictated by an overlay gradient map:
1725
          X = red_channel; Y = green_channel; compose:args =
1726
          x_scale[,y_scale[,angle]].
1727
      */
1728
0
      canvas_image=CloneImage(image,0,0,MagickTrue,exception);
1729
0
      if (canvas_image == (Image *) NULL)
1730
0
        {
1731
0
          source_image=DestroyImage(source_image);
1732
0
          return(MagickFalse);
1733
0
        }
1734
      /*
1735
        Gather the maximum blur sigma values from user.
1736
      */
1737
0
      flags=NoValue;
1738
0
      artifact=GetImageArtifact(image,"compose:args");
1739
0
      if (artifact != (const char *) NULL)
1740
0
        flags=ParseGeometry(artifact,&geometry_info);
1741
0
      if ((flags & WidthValue) == 0)
1742
0
        {
1743
0
          (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
1744
0
            "InvalidSetting","'%s' '%s'","compose:args",artifact);
1745
0
          source_image=DestroyImage(source_image);
1746
0
          canvas_image=DestroyImage(canvas_image);
1747
0
          return(MagickFalse);
1748
0
        }
1749
      /*
1750
        Users input sigma now needs to be converted to the EWA ellipse size.
1751
        The filter defaults to a sigma of 0.5 so to make this match the users
1752
        input the ellipse size needs to be doubled.
1753
      */
1754
0
      width=2.0*geometry_info.rho;
1755
0
      height=width;
1756
0
      if ((flags & HeightValue) != 0)
1757
0
        height=2.0*geometry_info.sigma;
1758
      /*
1759
        Default the unrotated ellipse width and height axis vectors.
1760
      */
1761
0
      blur.x1=width;
1762
0
      blur.x2=0.0;
1763
0
      blur.y1=0.0;
1764
0
      blur.y2=height;
1765
0
      if ((flags & XValue) != 0 )
1766
0
        {
1767
0
          MagickRealType
1768
0
            angle;
1769
1770
          /*
1771
            Rotate vectors if a rotation angle is given.
1772
          */
1773
0
          angle=DegreesToRadians(geometry_info.xi);
1774
0
          blur.x1=width*cos(angle);
1775
0
          blur.x2=width*sin(angle);
1776
0
          blur.y1=(-height*sin(angle));
1777
0
          blur.y2=height*cos(angle);
1778
0
        }
1779
0
      angle_start=0.0;
1780
0
      angle_range=0.0;
1781
0
      if ((flags & YValue) != 0 )
1782
0
        {
1783
          /*
1784
            Lets set a angle range and calculate in the loop.
1785
          */
1786
0
          angle_start=DegreesToRadians(geometry_info.xi);
1787
0
          angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
1788
0
        }
1789
      /*
1790
        Set up a gaussian cylindrical filter for EWA Blurring.
1791
1792
        As the minimum ellipse radius of support*1.0 the EWA algorithm
1793
        can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
1794
        This means that even 'No Blur' will be still a little blurry! The
1795
        solution (as well as the problem of preventing any user expert filter
1796
        settings, is to set our own user settings, restore them afterwards.
1797
      */
1798
0
      resample_filter=AcquireResampleFilter(image,exception);
1799
0
      SetResampleFilter(resample_filter,GaussianFilter);
1800
      /*
1801
        Perform the variable blurring of each pixel in image.
1802
      */
1803
0
      GetPixelInfo(image,&pixel);
1804
0
      source_view=AcquireVirtualCacheView(source_image,exception);
1805
0
      canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1806
0
      for (y=0; y < (ssize_t) source_image->rows; y++)
1807
0
      {
1808
0
        MagickBooleanType
1809
0
          sync;
1810
1811
0
        const Quantum
1812
0
          *magick_restrict p;
1813
1814
0
        Quantum
1815
0
          *magick_restrict q;
1816
1817
0
        ssize_t
1818
0
          x;
1819
1820
0
        if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1821
0
          continue;
1822
0
        p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1823
0
          exception);
1824
0
        q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1825
0
          exception);
1826
0
        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1827
0
          break;
1828
0
        for (x=0; x < (ssize_t) source_image->columns; x++)
1829
0
        {
1830
0
          if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1831
0
            {
1832
0
              p+=(ptrdiff_t) GetPixelChannels(source_image);
1833
0
              continue;
1834
0
            }
1835
0
          if (fabs(angle_range) > MagickEpsilon)
1836
0
            {
1837
0
              MagickRealType
1838
0
                angle;
1839
1840
0
              angle=angle_start+angle_range*QuantumScale*(double) 
1841
0
                GetPixelBlue(source_image,p);
1842
0
              blur.x1=width*cos(angle);
1843
0
              blur.x2=width*sin(angle);
1844
0
              blur.y1=(-height*sin(angle));
1845
0
              blur.y2=height*cos(angle);
1846
0
            }
1847
0
          ScaleResampleFilter(resample_filter,
1848
0
            blur.x1*QuantumScale*(double) GetPixelRed(source_image,p),
1849
0
            blur.y1*QuantumScale*(double) GetPixelGreen(source_image,p),
1850
0
            blur.x2*QuantumScale*(double) GetPixelRed(source_image,p),
1851
0
            blur.y2*QuantumScale*(double) GetPixelGreen(source_image,p) );
1852
0
          (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
1853
0
            (double) y_offset+y,&pixel,exception);
1854
0
          SetPixelViaPixelInfo(canvas_image,&pixel,q);
1855
0
          p+=(ptrdiff_t) GetPixelChannels(source_image);
1856
0
          q+=(ptrdiff_t) GetPixelChannels(canvas_image);
1857
0
        }
1858
0
        sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1859
0
        if (sync == MagickFalse)
1860
0
          break;
1861
0
      }
1862
0
      resample_filter=DestroyResampleFilter(resample_filter);
1863
0
      source_view=DestroyCacheView(source_view);
1864
0
      canvas_view=DestroyCacheView(canvas_view);
1865
0
      source_image=DestroyImage(source_image);
1866
0
      source_image=canvas_image;
1867
0
      break;
1868
0
    }
1869
0
    case DisplaceCompositeOp:
1870
0
    case DistortCompositeOp:
1871
0
    {
1872
0
      CacheView
1873
0
        *canvas_view;
1874
1875
0
      MagickRealType
1876
0
        horizontal_scale,
1877
0
        vertical_scale;
1878
1879
0
      PixelInfo
1880
0
        pixel;
1881
1882
0
      PointInfo
1883
0
        center,
1884
0
        offset;
1885
1886
      /*
1887
        Displace/Distort based on overlay gradient map:
1888
          X = red_channel;  Y = green_channel;
1889
          compose:args = x_scale[,y_scale[,center.x,center.y]]
1890
      */
1891
0
      canvas_image=CloneImage(image,0,0,MagickTrue,exception);
1892
0
      if (canvas_image == (Image *) NULL)
1893
0
        {
1894
0
          source_image=DestroyImage(source_image);
1895
0
          return(MagickFalse);
1896
0
        }
1897
0
      SetGeometryInfo(&geometry_info);
1898
0
      flags=NoValue;
1899
0
      artifact=GetImageArtifact(image,"compose:args");
1900
0
      if (artifact != (char *) NULL)
1901
0
        flags=ParseGeometry(artifact,&geometry_info);
1902
0
      if ((flags & (WidthValue | HeightValue)) == 0 )
1903
0
        {
1904
0
          if ((flags & AspectValue) == 0)
1905
0
            {
1906
0
              horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
1907
0
              vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
1908
0
            }
1909
0
          else
1910
0
            {
1911
0
              horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
1912
0
              vertical_scale=(MagickRealType) (image->rows-1)/2.0;
1913
0
            }
1914
0
        }
1915
0
      else
1916
0
        {
1917
0
          horizontal_scale=geometry_info.rho;
1918
0
          vertical_scale=geometry_info.sigma;
1919
0
          if ((flags & PercentValue) != 0)
1920
0
            {
1921
0
              if ((flags & AspectValue) == 0)
1922
0
                {
1923
0
                  horizontal_scale*=(source_image->columns-1)/200.0;
1924
0
                  vertical_scale*=(source_image->rows-1)/200.0;
1925
0
                }
1926
0
              else
1927
0
                {
1928
0
                  horizontal_scale*=(image->columns-1)/200.0;
1929
0
                  vertical_scale*=(image->rows-1)/200.0;
1930
0
                }
1931
0
            }
1932
0
          if ((flags & HeightValue) == 0)
1933
0
            vertical_scale=horizontal_scale;
1934
0
        }
1935
      /*
1936
        Determine fixed center point for absolute distortion map
1937
         Absolute distort ==
1938
           Displace offset relative to a fixed absolute point
1939
           Select that point according to +X+Y user inputs.
1940
           default = center of overlay image
1941
           arg flag '!' = locations/percentage relative to background image
1942
      */
1943
0
      center.x=(MagickRealType) x_offset;
1944
0
      center.y=(MagickRealType) y_offset;
1945
0
      if (compose == DistortCompositeOp)
1946
0
        {
1947
0
          if ((flags & XValue) == 0)
1948
0
            if ((flags & AspectValue) != 0)
1949
0
              center.x=(MagickRealType) ((image->columns-1)/2.0);
1950
0
            else
1951
0
              center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1952
0
                2.0);
1953
0
          else
1954
0
            if ((flags & AspectValue) != 0)
1955
0
              center.x=geometry_info.xi;
1956
0
            else
1957
0
              center.x=(MagickRealType) (x_offset+geometry_info.xi);
1958
0
          if ((flags & YValue) == 0)
1959
0
            if ((flags & AspectValue) != 0)
1960
0
              center.y=(MagickRealType) ((image->rows-1)/2.0);
1961
0
            else
1962
0
              center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1963
0
          else
1964
0
            if ((flags & AspectValue) != 0)
1965
0
              center.y=geometry_info.psi;
1966
0
            else
1967
0
              center.y=(MagickRealType) (y_offset+geometry_info.psi);
1968
0
        }
1969
      /*
1970
        Shift the pixel offset point as defined by the provided,
1971
        displacement/distortion map.  -- Like a lens...
1972
      */
1973
0
      GetPixelInfo(image,&pixel);
1974
0
      image_view=AcquireVirtualCacheView(image,exception);
1975
0
      source_view=AcquireVirtualCacheView(source_image,exception);
1976
0
      canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1977
0
      for (y=0; y < (ssize_t) source_image->rows; y++)
1978
0
      {
1979
0
        MagickBooleanType
1980
0
          sync;
1981
1982
0
        const Quantum
1983
0
          *magick_restrict p;
1984
1985
0
        Quantum
1986
0
          *magick_restrict q;
1987
1988
0
        ssize_t
1989
0
          x;
1990
1991
0
        if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1992
0
          continue;
1993
0
        p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1994
0
          exception);
1995
0
        q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1996
0
          exception);
1997
0
        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1998
0
          break;
1999
0
        for (x=0; x < (ssize_t) source_image->columns; x++)
2000
0
        {
2001
0
          if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
2002
0
            {
2003
0
              p+=(ptrdiff_t) GetPixelChannels(source_image);
2004
0
              continue;
2005
0
            }
2006
          /*
2007
            Displace the offset.
2008
          */
2009
0
          offset.x=(double) (horizontal_scale*((double) GetPixelRed(
2010
0
            source_image,p)-(((MagickRealType) QuantumRange+1.0)/2.0)))/
2011
0
            (((MagickRealType) QuantumRange+1.0)/2.0)+center.x+
2012
0
            ((compose == DisplaceCompositeOp) ? x : 0);
2013
0
          offset.y=(double) (vertical_scale*((double) GetPixelGreen(
2014
0
            source_image,p)-(((MagickRealType) QuantumRange+1.0)/2.0)))/
2015
0
            (((MagickRealType) QuantumRange+1.0)/2.0)+center.y+
2016
0
            ((compose == DisplaceCompositeOp) ? y : 0);
2017
0
          status=InterpolatePixelInfo(image,image_view,
2018
0
            UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
2019
0
            &pixel,exception);
2020
0
          if (status == MagickFalse)
2021
0
            break;
2022
          /*
2023
            Mask with the 'invalid pixel mask' in alpha channel.
2024
          */
2025
0
          pixel.alpha=(MagickRealType) QuantumRange*(QuantumScale*pixel.alpha)*
2026
0
            (QuantumScale*(double) GetPixelAlpha(source_image,p));
2027
0
          SetPixelViaPixelInfo(canvas_image,&pixel,q);
2028
0
          p+=(ptrdiff_t) GetPixelChannels(source_image);
2029
0
          q+=(ptrdiff_t) GetPixelChannels(canvas_image);
2030
0
        }
2031
0
        if (x < (ssize_t) source_image->columns)
2032
0
          break;
2033
0
        sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
2034
0
        if (sync == MagickFalse)
2035
0
          break;
2036
0
      }
2037
0
      canvas_view=DestroyCacheView(canvas_view);
2038
0
      source_view=DestroyCacheView(source_view);
2039
0
      image_view=DestroyCacheView(image_view);
2040
0
      source_image=DestroyImage(source_image);
2041
0
      source_image=canvas_image;
2042
0
      break;
2043
0
    }
2044
0
    case DissolveCompositeOp:
2045
0
    {
2046
      /*
2047
        Geometry arguments to dissolve factors.
2048
      */
2049
0
      artifact=GetImageArtifact(image,"compose:args");
2050
0
      if (artifact != (char *) NULL)
2051
0
        {
2052
0
          flags=ParseGeometry(artifact,&geometry_info);
2053
0
          source_dissolve=geometry_info.rho/100.0;
2054
0
          canvas_dissolve=1.0;
2055
0
          if ((source_dissolve-MagickEpsilon) < 0.0)
2056
0
            source_dissolve=0.0;
2057
0
          if ((source_dissolve+MagickEpsilon) > 1.0)
2058
0
            {
2059
0
              canvas_dissolve=2.0-source_dissolve;
2060
0
              source_dissolve=1.0;
2061
0
            }
2062
0
          if ((flags & SigmaValue) != 0)
2063
0
            canvas_dissolve=geometry_info.sigma/100.0;
2064
0
          if ((canvas_dissolve-MagickEpsilon) < 0.0)
2065
0
            canvas_dissolve=0.0;
2066
0
          if ((canvas_dissolve+MagickEpsilon) > 1.0)
2067
0
            canvas_dissolve=1.0;
2068
0
        }
2069
0
      break;
2070
0
    }
2071
0
    case BlendCompositeOp:
2072
0
    {
2073
0
      artifact=GetImageArtifact(image,"compose:args");
2074
0
      if (artifact != (char *) NULL)
2075
0
        {
2076
0
          flags=ParseGeometry(artifact,&geometry_info);
2077
0
          source_dissolve=geometry_info.rho/100.0;
2078
0
          canvas_dissolve=1.0-source_dissolve;
2079
0
          if ((flags & SigmaValue) != 0)
2080
0
            canvas_dissolve=geometry_info.sigma/100.0;
2081
0
        }
2082
0
      break;
2083
0
    }
2084
0
    case SaliencyBlendCompositeOp:
2085
0
    {
2086
0
      double
2087
0
        residual_threshold = 0.0002,
2088
0
        iterations = 400.0;
2089
2090
0
      size_t
2091
0
        tick = 100;
2092
2093
0
      artifact=GetImageArtifact(image,"compose:args");
2094
0
      if (artifact != (char *) NULL)
2095
0
        {
2096
0
          flags=ParseGeometry(artifact,&geometry_info);
2097
0
          iterations=geometry_info.rho;
2098
0
          if ((flags & SigmaValue) != 0)
2099
0
            residual_threshold=geometry_info.sigma;
2100
0
          if ((flags & XiValue) != 0)
2101
0
            tick=(size_t) geometry_info.xi;
2102
0
        }
2103
0
      status=SaliencyBlendImage(image,composite,x_offset,y_offset,iterations,
2104
0
        residual_threshold,tick,exception);
2105
0
      source_image=DestroyImage(source_image);
2106
0
      return(status);
2107
0
    }
2108
0
    case SeamlessBlendCompositeOp:
2109
0
    {
2110
0
      double
2111
0
        residual_threshold = 0.0002,
2112
0
        iterations = 400.0;
2113
2114
0
      size_t
2115
0
        tick = 100;
2116
2117
0
      artifact=GetImageArtifact(image,"compose:args");
2118
0
      if (artifact != (char *) NULL)
2119
0
        {
2120
0
          flags=ParseGeometry(artifact,&geometry_info);
2121
0
          iterations=geometry_info.rho;
2122
0
          if ((flags & SigmaValue) != 0)
2123
0
            residual_threshold=geometry_info.sigma;
2124
0
          if ((flags & XiValue) != 0)
2125
0
            tick=(size_t) geometry_info.xi;
2126
0
        }
2127
0
      status=SeamlessBlendImage(image,composite,x_offset,y_offset,iterations,
2128
0
        residual_threshold,tick,exception);
2129
0
      source_image=DestroyImage(source_image);
2130
0
      return(status);
2131
0
    }
2132
0
    case MathematicsCompositeOp:
2133
0
    {
2134
      /*
2135
        Just collect the values from "compose:args", setting.
2136
        Unused values are set to zero automagically.
2137
2138
        Arguments are normally a comma separated list, so this probably should
2139
        be changed to some 'general comma list' parser, (with a minimum
2140
        number of values)
2141
      */
2142
0
      SetGeometryInfo(&geometry_info);
2143
0
      artifact=GetImageArtifact(image,"compose:args");
2144
0
      if (artifact != (char *) NULL)
2145
0
        {
2146
0
          flags=ParseGeometry(artifact,&geometry_info);
2147
0
          if (flags == NoValue)
2148
0
            (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
2149
0
              "InvalidGeometry","`%s'",artifact);
2150
0
        }
2151
0
      break;
2152
0
    }
2153
0
    case ModulateCompositeOp:
2154
0
    {
2155
      /*
2156
        Determine the luma and chroma scale.
2157
      */
2158
0
      artifact=GetImageArtifact(image,"compose:args");
2159
0
      if (artifact != (char *) NULL)
2160
0
        {
2161
0
          flags=ParseGeometry(artifact,&geometry_info);
2162
0
          percent_luma=geometry_info.rho;
2163
0
          if ((flags & SigmaValue) != 0)
2164
0
            percent_chroma=geometry_info.sigma;
2165
0
        }
2166
0
      break;
2167
0
    }
2168
0
    case ThresholdCompositeOp:
2169
0
    {
2170
      /*
2171
        Determine the amount and threshold.
2172
      */
2173
0
      artifact=GetImageArtifact(image,"compose:args");
2174
0
      if (artifact != (char *) NULL)
2175
0
        {
2176
0
          flags=ParseGeometry(artifact,&geometry_info);
2177
0
          amount=geometry_info.rho;
2178
0
          threshold=geometry_info.sigma;
2179
0
          if ((flags & SigmaValue) == 0)
2180
0
            threshold=0.05f;
2181
0
        }
2182
0
      threshold*=(double) QuantumRange;
2183
0
      break;
2184
0
    }
2185
82
    default:
2186
82
      break;
2187
18.4k
  }
2188
  /*
2189
    Composite image.
2190
  */
2191
10.4k
  status=MagickTrue;
2192
10.4k
  progress=0;
2193
10.4k
  midpoint=((MagickRealType) QuantumRange+1.0)/2;
2194
10.4k
  source_view=AcquireVirtualCacheView(source_image,exception);
2195
10.4k
  image_view=AcquireAuthenticCacheView(image,exception);
2196
#if defined(MAGICKCORE_OPENMP_SUPPORT)
2197
  #pragma omp parallel for schedule(static) shared(progress,status) \
2198
    magick_number_threads(source_image,image,image->rows,1)
2199
#endif
2200
4.49M
  for (y=0; y < (ssize_t) image->rows; y++)
2201
4.48M
  {
2202
4.48M
    const Quantum
2203
4.48M
      *magick_restrict p,
2204
4.48M
      *pixels;
2205
2206
4.48M
    MagickRealType
2207
4.48M
      blue = 0.0,
2208
4.48M
      chroma = 0.0,
2209
4.48M
      green = 0.0,
2210
4.48M
      hue = 0.0,
2211
4.48M
      luma = 0.0,
2212
4.48M
      red = 0.0;
2213
2214
4.48M
    PixelInfo
2215
4.48M
      canvas_pixel,
2216
4.48M
      source_pixel;
2217
2218
4.48M
    Quantum
2219
4.48M
      *magick_restrict q;
2220
2221
4.48M
    ssize_t
2222
4.48M
      x;
2223
2224
4.48M
    if (status == MagickFalse)
2225
602
      continue;
2226
4.48M
    if (clip_to_self != MagickFalse)
2227
4.48M
      {
2228
4.48M
        if (y < y_offset)
2229
1.70M
          continue;
2230
2.78M
        if ((y-y_offset) >= (ssize_t) source_image->rows)
2231
1.36M
          continue;
2232
2.78M
      }
2233
    /*
2234
      If pixels is NULL, y is outside overlay region.
2235
    */
2236
1.41M
    pixels=(Quantum *) NULL;
2237
1.41M
    p=(Quantum *) NULL;
2238
1.41M
    if ((y >= y_offset) &&
2239
1.41M
        ((y-y_offset) < (ssize_t) source_image->rows))
2240
1.41M
      {
2241
1.41M
        p=GetCacheViewVirtualPixels(source_view,0,
2242
1.41M
          CastDoubleToSsizeT((double) y-y_offset),source_image->columns,1,
2243
1.41M
          exception);
2244
1.41M
        if (p == (const Quantum *) NULL)
2245
9
          {
2246
9
            status=MagickFalse;
2247
9
            continue;
2248
9
          }
2249
1.41M
        pixels=p;
2250
1.41M
        if (x_offset < 0)
2251
164
          p-=(ptrdiff_t) CastDoubleToSsizeT((double) x_offset*
2252
164
            GetPixelChannels(source_image));
2253
1.41M
      }
2254
1.41M
    q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2255
1.41M
    if (q == (Quantum *) NULL)
2256
0
      {
2257
0
        status=MagickFalse;
2258
0
        continue;
2259
0
      }
2260
1.41M
    GetPixelInfo(image,&canvas_pixel);
2261
1.41M
    GetPixelInfo(source_image,&source_pixel);
2262
584M
    for (x=0; x < (ssize_t) image->columns; x++)
2263
583M
    {
2264
583M
      double
2265
583M
        gamma = 0.0;
2266
2267
583M
      MagickRealType
2268
583M
        alpha = 0.0,
2269
583M
        blend = 0.0,
2270
583M
        D = 0.0,
2271
583M
        Da = 0.0,
2272
583M
        Dc = 0.0,
2273
583M
        Dca = 0.0,
2274
583M
        Di = 0.0,
2275
583M
        S = 0.0,
2276
583M
        Sa = 0.0,
2277
583M
        Sc = 0.0,
2278
583M
        Sca = 0.0,
2279
583M
        Si = 0.0;
2280
2281
583M
      size_t
2282
583M
        channels;
2283
2284
583M
      ssize_t
2285
583M
        i;
2286
2287
583M
      if (clip_to_self != MagickFalse)
2288
583M
        {
2289
583M
          if (x < x_offset)
2290
39.3M
            {
2291
39.3M
              q+=(ptrdiff_t) GetPixelChannels(image);
2292
39.3M
              continue;
2293
39.3M
            }
2294
544M
          if ((x-x_offset) >= (ssize_t) source_image->columns)
2295
720k
            break;
2296
544M
        }
2297
543M
      if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
2298
543M
          ((x-x_offset) >= (ssize_t) source_image->columns))
2299
0
        {
2300
0
          Quantum
2301
0
            source[MaxPixelChannels];
2302
2303
          /*
2304
            Virtual composite:
2305
              Sc: source color.
2306
              Dc: canvas color.
2307
          */
2308
0
          (void) GetOneVirtualPixel(source_image,
2309
0
            CastDoubleToSsizeT((double) x-x_offset),
2310
0
            CastDoubleToSsizeT((double) y-y_offset),source,exception);
2311
0
          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2312
0
          {
2313
0
            MagickRealType
2314
0
              pixel = 0.0;
2315
2316
0
            PixelChannel channel = GetPixelChannelChannel(image,i);
2317
0
            PixelTrait traits = GetPixelChannelTraits(image,channel);
2318
0
            PixelTrait source_traits = GetPixelChannelTraits(source_image,
2319
0
              channel);
2320
0
            if ((traits == UndefinedPixelTrait) ||
2321
0
                (source_traits == UndefinedPixelTrait))
2322
0
              continue;
2323
0
            switch (compose)
2324
0
            {
2325
0
              case AlphaCompositeOp:
2326
0
              case ChangeMaskCompositeOp:
2327
0
              case CopyAlphaCompositeOp:
2328
0
              case DstAtopCompositeOp:
2329
0
              case DstInCompositeOp:
2330
0
              case InCompositeOp:
2331
0
              case OutCompositeOp:
2332
0
              case SrcInCompositeOp:
2333
0
              case SrcOutCompositeOp:
2334
0
              {
2335
0
                if (channel == AlphaPixelChannel)
2336
0
                  pixel=(MagickRealType) TransparentAlpha;
2337
0
                else
2338
0
                  pixel=(MagickRealType) q[i];
2339
0
                break;
2340
0
              }
2341
0
              case ClearCompositeOp:
2342
0
              case CopyCompositeOp:
2343
0
              case ReplaceCompositeOp:
2344
0
              {
2345
0
                if (channel == AlphaPixelChannel)
2346
0
                  pixel=(MagickRealType) TransparentAlpha;
2347
0
                else
2348
0
                  pixel=0.0;
2349
0
                break;
2350
0
              }
2351
0
              case BlendCompositeOp:
2352
0
              case DissolveCompositeOp:
2353
0
              {
2354
0
                if (channel == AlphaPixelChannel)
2355
0
                  pixel=canvas_dissolve*(double) GetPixelAlpha(source_image,
2356
0
                    source);
2357
0
                else
2358
0
                  pixel=(MagickRealType) source[channel];
2359
0
                break;
2360
0
              }
2361
0
              default:
2362
0
              {
2363
0
                pixel=(MagickRealType) source[channel];
2364
0
                break;
2365
0
              }
2366
0
            }
2367
0
            q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
2368
0
              ClampToQuantum(pixel);
2369
0
          }
2370
0
          q+=(ptrdiff_t) GetPixelChannels(image);
2371
0
          continue;
2372
0
        }
2373
      /*
2374
        Authentic composite:
2375
          Sa:  normalized source alpha.
2376
          Da:  normalized canvas alpha.
2377
      */
2378
543M
      Sa=QuantumScale*(double) GetPixelAlpha(source_image,p);
2379
543M
      Da=QuantumScale*(double) GetPixelAlpha(image,q);
2380
543M
      switch (compose)
2381
543M
      {
2382
0
        case BumpmapCompositeOp:
2383
0
        case ColorBurnCompositeOp:
2384
0
        case ColorDodgeCompositeOp:
2385
0
        case DarkenCompositeOp:
2386
0
        case DifferenceCompositeOp:
2387
0
        case DivideDstCompositeOp:
2388
0
        case DivideSrcCompositeOp:
2389
0
        case ExclusionCompositeOp:
2390
0
        case FreezeCompositeOp:
2391
0
        case HardLightCompositeOp:
2392
0
        case HardMixCompositeOp:
2393
0
        case InterpolateCompositeOp:
2394
0
        case LightenCompositeOp:
2395
0
        case LinearBurnCompositeOp:
2396
0
        case LinearDodgeCompositeOp:
2397
0
        case LinearLightCompositeOp:
2398
0
        case MathematicsCompositeOp:
2399
0
        case MinusDstCompositeOp:
2400
0
        case MinusSrcCompositeOp:
2401
0
        case MultiplyCompositeOp:
2402
0
        case NegateCompositeOp:
2403
0
        case OverlayCompositeOp:
2404
0
        case PegtopLightCompositeOp:
2405
0
        case PinLightCompositeOp:
2406
0
        case ReflectCompositeOp:
2407
0
        case ScreenCompositeOp:
2408
0
        case SoftBurnCompositeOp:
2409
0
        case SoftDodgeCompositeOp:
2410
0
        case SoftLightCompositeOp:
2411
0
        case StampCompositeOp:
2412
0
        case VividLightCompositeOp:
2413
0
        {
2414
0
          alpha=RoundToUnity(Sa+Da-Sa*Da);
2415
0
          break;
2416
0
        }
2417
0
        case DstAtopCompositeOp:
2418
0
        case DstInCompositeOp:
2419
0
        case InCompositeOp:
2420
0
        case SrcInCompositeOp:
2421
0
        {
2422
0
          alpha=Sa*Da;
2423
0
          break;
2424
0
        }
2425
0
        case DissolveCompositeOp:
2426
0
        {
2427
0
          alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
2428
0
            canvas_dissolve*Da;
2429
0
          break;
2430
0
        }
2431
0
        case DstOverCompositeOp:
2432
0
        case OverCompositeOp:
2433
0
        case SrcOverCompositeOp:
2434
0
        {
2435
0
          alpha=Sa+Da-Sa*Da;
2436
0
          break;
2437
0
        }
2438
0
        case DstOutCompositeOp:
2439
0
        {
2440
0
          alpha=Da*(1.0-Sa);
2441
0
          break;
2442
0
        }
2443
0
        case OutCompositeOp:
2444
0
        case SrcOutCompositeOp:
2445
0
        {
2446
0
          alpha=Sa*(1.0-Da);
2447
0
          break;
2448
0
        }
2449
0
        case BlendCompositeOp:
2450
0
        case PlusCompositeOp:
2451
0
        {
2452
0
          alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
2453
0
          break;
2454
0
        }
2455
0
        case XorCompositeOp:
2456
0
        {
2457
0
          alpha=Sa+Da-2.0*Sa*Da;
2458
0
          break;
2459
0
        }
2460
0
        case ModulusAddCompositeOp:
2461
0
        {
2462
0
          if ((Sa+Da) <= 1.0)
2463
0
            {
2464
0
              alpha=(Sa+Da);
2465
0
              break;
2466
0
            }
2467
0
          alpha=((Sa+Da)-1.0);
2468
0
          break;
2469
0
        }
2470
0
        case ModulusSubtractCompositeOp:
2471
0
        {
2472
0
          if ((Sa-Da) >= 0.0)
2473
0
            {
2474
0
              alpha=(Sa-Da);
2475
0
              break;
2476
0
            }
2477
0
          alpha=((Sa-Da)+1.0);
2478
0
          break;
2479
0
        }
2480
543M
        default:
2481
543M
        {
2482
543M
          alpha=1.0;
2483
543M
          break;
2484
0
        }
2485
543M
      }
2486
543M
      switch (compose)
2487
543M
      {
2488
0
        case ColorizeCompositeOp:
2489
0
        case HueCompositeOp:
2490
0
        case LuminizeCompositeOp:
2491
0
        case ModulateCompositeOp:
2492
0
        case RMSECompositeOp:
2493
0
        case SaturateCompositeOp:
2494
0
        {
2495
0
          Si=GetPixelIntensity(source_image,p);
2496
0
          GetPixelInfoPixel(source_image,p,&source_pixel);
2497
0
          GetPixelInfoPixel(image,q,&canvas_pixel);
2498
0
          break;
2499
0
        }
2500
0
        case BumpmapCompositeOp:
2501
0
        case CopyAlphaCompositeOp:
2502
0
        case DarkenIntensityCompositeOp:
2503
0
        case LightenIntensityCompositeOp:
2504
0
        {
2505
0
          Si=GetPixelIntensity(source_image,p);
2506
0
          Di=GetPixelIntensity(image,q);
2507
0
          break;
2508
0
        }
2509
543M
        default:
2510
543M
          break;
2511
543M
      }
2512
1.88G
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2513
1.34G
      {
2514
1.34G
        MagickRealType
2515
1.34G
          pixel = 0.0,
2516
1.34G
          sans = 0.0;
2517
2518
1.34G
        PixelChannel channel = GetPixelChannelChannel(image,i);
2519
1.34G
        PixelTrait traits = GetPixelChannelTraits(image,channel);
2520
1.34G
        PixelTrait source_traits = GetPixelChannelTraits(source_image,channel);
2521
1.34G
        if (traits == UndefinedPixelTrait)
2522
0
          continue;
2523
1.34G
        if ((channel == AlphaPixelChannel) &&
2524
317k
            ((traits & UpdatePixelTrait) != 0))
2525
317k
          {
2526
            /*
2527
              Set alpha channel.
2528
            */
2529
317k
            switch (compose)
2530
317k
            {
2531
0
              case AlphaCompositeOp:
2532
0
              {
2533
0
                pixel=(double) QuantumRange*Sa;
2534
0
                break;
2535
0
              }
2536
0
              case AtopCompositeOp:
2537
0
              case CopyBlackCompositeOp:
2538
0
              case CopyBlueCompositeOp:
2539
0
              case CopyCyanCompositeOp:
2540
0
              case CopyGreenCompositeOp:
2541
0
              case CopyMagentaCompositeOp:
2542
0
              case CopyRedCompositeOp:
2543
0
              case CopyYellowCompositeOp:
2544
0
              case SrcAtopCompositeOp:
2545
0
              case DstCompositeOp:
2546
1.25k
              case NoCompositeOp:
2547
1.25k
              {
2548
1.25k
                pixel=(double) QuantumRange*Da;
2549
1.25k
                break;
2550
0
              }
2551
0
              case BumpmapCompositeOp:
2552
0
              {
2553
0
                pixel=Si*Da;
2554
0
                break;
2555
0
              }
2556
0
              case ChangeMaskCompositeOp:
2557
0
              {
2558
0
                if (IsFuzzyEquivalencePixel(source_image,p,image,q) != MagickFalse)
2559
0
                  pixel=(MagickRealType) TransparentAlpha;
2560
0
                else
2561
0
                  pixel=(double) QuantumRange*Da;
2562
0
                break;
2563
0
              }
2564
0
              case ClearCompositeOp:
2565
0
              {
2566
0
                pixel=(MagickRealType) TransparentAlpha;
2567
0
                break;
2568
0
              }
2569
0
              case ColorizeCompositeOp:
2570
0
              case HueCompositeOp:
2571
0
              case LuminizeCompositeOp:
2572
0
              case RMSECompositeOp:
2573
0
              case SaturateCompositeOp:
2574
0
              {
2575
0
                if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2576
0
                  {
2577
0
                    pixel=(double) QuantumRange*Da;
2578
0
                    break;
2579
0
                  }
2580
0
                if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
2581
0
                  {
2582
0
                    pixel=(double) QuantumRange*Sa;
2583
0
                    break;
2584
0
                  }
2585
0
                if (Sa < Da)
2586
0
                  {
2587
0
                    pixel=(double) QuantumRange*Da;
2588
0
                    break;
2589
0
                  }
2590
0
                pixel=(double) QuantumRange*Sa;
2591
0
                break;
2592
0
              }
2593
0
              case CopyAlphaCompositeOp:
2594
0
              {
2595
0
                if (source_image->alpha_trait == UndefinedPixelTrait)
2596
0
                  pixel=Si;
2597
0
                else
2598
0
                  pixel=(double) QuantumRange*Sa;
2599
0
                break;
2600
0
              }
2601
0
              case BlurCompositeOp:
2602
316k
              case CopyCompositeOp:
2603
316k
              case DisplaceCompositeOp:
2604
316k
              case DistortCompositeOp:
2605
316k
              case DstAtopCompositeOp:
2606
316k
              case ReplaceCompositeOp:
2607
316k
              case SrcCompositeOp:
2608
316k
              {
2609
316k
                pixel=(double) QuantumRange*Sa;
2610
316k
                break;
2611
316k
              }
2612
0
              case DarkenIntensityCompositeOp:
2613
0
              {
2614
0
                if (compose_sync == MagickFalse)
2615
0
                  {
2616
0
                    pixel=Si < Di ? Sa : Da;
2617
0
                    break;
2618
0
                  }
2619
0
                pixel=Sa*Si < Da*Di ? Sa : Da;
2620
0
                break;
2621
0
              }
2622
0
              case DifferenceCompositeOp:
2623
0
              {
2624
0
                pixel=(double) QuantumRange*fabs((double) (Sa-Da));
2625
0
                break;
2626
0
              }
2627
0
              case FreezeCompositeOp:
2628
0
              {
2629
0
                pixel=(double) QuantumRange*(1.0-(1.0-Sa)*(1.0-Sa)*
2630
0
                  MagickSafeReciprocal(Da));
2631
0
                if (pixel < 0.0)
2632
0
                  pixel=0.0;
2633
0
                break;
2634
0
              }
2635
0
              case InterpolateCompositeOp:
2636
0
              {
2637
0
                pixel=(double) QuantumRange*(0.5-0.25*cos(MagickPI*Sa)-0.25*
2638
0
                  cos(MagickPI*Da));
2639
0
                break;
2640
0
              }
2641
0
              case LightenIntensityCompositeOp:
2642
0
              {
2643
0
                if (compose_sync == MagickFalse)
2644
0
                  {
2645
0
                    pixel=Si > Di ? Sa : Da;
2646
0
                    break;
2647
0
                  }
2648
0
                pixel=Sa*Si > Da*Di ? Sa : Da;
2649
0
                break;
2650
0
              }
2651
0
              case ModulateCompositeOp:
2652
0
              {
2653
0
                pixel=(double) QuantumRange*Da;
2654
0
                break;
2655
0
              }
2656
0
              case MultiplyCompositeOp:
2657
0
              {
2658
0
                if (compose_sync == MagickFalse)
2659
0
                  {
2660
0
                    pixel=(double) QuantumRange*Sa*Da;
2661
0
                    break;
2662
0
                  }
2663
0
                pixel=(double) QuantumRange*alpha;
2664
0
                break;
2665
0
              }
2666
0
              case NegateCompositeOp:
2667
0
              {
2668
0
                pixel=(double) QuantumRange*((1.0-Sa-Da));
2669
0
                break;
2670
0
              }
2671
0
              case ReflectCompositeOp:
2672
0
              {
2673
0
                pixel=(double) QuantumRange*(Sa*Sa*
2674
0
                  MagickSafeReciprocal(1.0-Da));
2675
0
                if (pixel > (double) QuantumRange)
2676
0
                  pixel=(double) QuantumRange;
2677
0
                break;
2678
0
              }
2679
0
              case StampCompositeOp:
2680
0
              {
2681
0
                pixel=(double) QuantumRange*(Sa+Da*Da-1.0);
2682
0
                break;
2683
0
              }
2684
0
              case StereoCompositeOp:
2685
0
              {
2686
0
                pixel=(double) QuantumRange*(Sa+Da)/2;
2687
0
                break;
2688
0
              }
2689
0
              default:
2690
0
              {
2691
0
                pixel=(double) QuantumRange*alpha;
2692
0
                break;
2693
0
              }
2694
317k
            }
2695
317k
            q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
2696
317k
              ClampToQuantum(pixel);
2697
317k
            continue;
2698
317k
          }
2699
1.34G
        if (source_traits == UndefinedPixelTrait)
2700
7.70k
          continue;
2701
        /*
2702
          Sc: source color.
2703
          Dc: canvas color.
2704
        */
2705
1.34G
        Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
2706
1.34G
        Dc=(MagickRealType) q[i];
2707
1.34G
        if ((traits & CopyPixelTrait) != 0)
2708
0
          {
2709
            /*
2710
              Copy channel.
2711
            */
2712
0
            q[i]=ClampToQuantum(Dc);
2713
0
            continue;
2714
0
          }
2715
        /*
2716
          Porter-Duff compositions:
2717
            Sca: source normalized color multiplied by alpha.
2718
            Dca: normalized canvas color multiplied by alpha.
2719
        */
2720
1.34G
        Sca=QuantumScale*Sa*Sc;
2721
1.34G
        Dca=QuantumScale*Da*Dc;
2722
1.34G
        switch (compose)
2723
1.34G
        {
2724
0
          case DarkenCompositeOp:
2725
0
          case LightenCompositeOp:
2726
0
          case ModulusSubtractCompositeOp:
2727
0
          {
2728
0
            gamma=MagickSafeReciprocal(1.0-alpha);
2729
0
            break;
2730
0
          }
2731
1.34G
          default:
2732
1.34G
          {
2733
1.34G
            gamma=MagickSafeReciprocal(alpha);
2734
1.34G
            break;
2735
0
          }
2736
1.34G
        }
2737
1.34G
        pixel=Dc;
2738
1.34G
        switch (compose)
2739
1.34G
        {
2740
0
          case AlphaCompositeOp:
2741
0
          {
2742
0
            pixel=(double) QuantumRange*Sa;
2743
0
            break;
2744
0
          }
2745
0
          case AtopCompositeOp:
2746
0
          case SrcAtopCompositeOp:
2747
0
          {
2748
0
            pixel=(double) QuantumRange*(Sca*Da+Dca*(1.0-Sa));
2749
0
            break;
2750
0
          }
2751
0
          case BlendCompositeOp:
2752
0
          {
2753
0
            pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
2754
0
            break;
2755
0
          }
2756
1.34G
          case CopyCompositeOp:
2757
1.34G
          case ReplaceCompositeOp:
2758
1.34G
          {
2759
1.34G
            pixel=(double) QuantumRange*Sca;
2760
1.34G
            break;
2761
1.34G
          }
2762
0
          case BlurCompositeOp:
2763
0
          case DisplaceCompositeOp:
2764
0
          case DistortCompositeOp:
2765
0
          case SrcCompositeOp:
2766
0
          {
2767
0
            pixel=Sc;
2768
0
            break;
2769
0
          }
2770
0
          case BumpmapCompositeOp:
2771
0
          {
2772
0
            if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2773
0
              {
2774
0
                pixel=Dc;
2775
0
                break;
2776
0
              }
2777
0
            pixel=(QuantumScale*Si)*Dc;
2778
0
            break;
2779
0
          }
2780
0
          case ChangeMaskCompositeOp:
2781
0
          {
2782
0
            pixel=Dc;
2783
0
            break;
2784
0
          }
2785
0
          case ClearCompositeOp:
2786
0
          {
2787
0
            pixel=0.0;
2788
0
            break;
2789
0
          }
2790
0
          case ColorBurnCompositeOp:
2791
0
          {
2792
0
            D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
2793
0
            S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
2794
0
            if (S <= 0.0)
2795
0
              blend=0.0;
2796
0
            else
2797
0
              blend=1.0-(1.0-D)/S;
2798
0
            pixel=(double) QuantumRange*gamma*RoundToUnity(Sa*Da*
2799
0
              RoundToUnity(blend)+Sa*(1.0-Da)*S+Da*(1.0-Sa)*D);
2800
0
            break;
2801
0
          }
2802
0
          case ColorDodgeCompositeOp:
2803
0
          {
2804
0
            if (Sa > 0.0)
2805
0
              S=RoundToUnity(Sca/Sa);
2806
0
            else
2807
0
              S=0.0;
2808
0
            if (Da > 0.0)
2809
0
              D=RoundToUnity(Dca/Da);
2810
0
            else
2811
0
              D=0.0;
2812
0
            if (S >= 1.0)
2813
0
              blend=1.0;
2814
0
            else
2815
0
              if (D <= 0.0)
2816
0
                blend=0.0;
2817
0
              else
2818
0
                {
2819
0
                  if ((1.0-S) <= 0.0)
2820
0
                    blend=1.0;
2821
0
                  else
2822
0
                    blend=MagickMin(1.0,D/(1.0-S));
2823
0
                }
2824
0
            pixel=(double) QuantumRange*gamma*(Sa*Da*blend+Sca*(1.0-Da)+Dca*
2825
0
              (1.0-Sa));
2826
0
            break;
2827
0
          }
2828
0
          case ColorizeCompositeOp:
2829
0
          {
2830
0
            if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
2831
0
              {
2832
0
                pixel=Dc;
2833
0
                break;
2834
0
              }
2835
0
            if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
2836
0
              {
2837
0
                pixel=Sc;
2838
0
                break;
2839
0
              }
2840
0
            ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
2841
0
              (double) canvas_pixel.green,(double) canvas_pixel.blue,
2842
0
              white_luminance,illuminant,&sans,&sans,&luma);
2843
0
            ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
2844
0
              (double) source_pixel.green,(double) source_pixel.blue,
2845
0
              white_luminance,illuminant,&hue,&chroma,&sans);
2846
0
            ConvertGenericToRGB(colorspace,hue,chroma,luma,
2847
0
              white_luminance,illuminant,&red,&green,&blue);
2848
0
            switch (channel)
2849
0
            {
2850
0
              case RedPixelChannel: pixel=red; break;
2851
0
              case GreenPixelChannel: pixel=green; break;
2852
0
              case BluePixelChannel: pixel=blue; break;
2853
0
              default: pixel=Dc; break;
2854
0
            }
2855
0
            break;
2856
0
          }
2857
0
          case CopyAlphaCompositeOp:
2858
0
          case DstCompositeOp:
2859
0
          {
2860
0
            pixel=Dc;
2861
0
            break;
2862
0
          }
2863
0
          case CopyBlackCompositeOp:
2864
0
          {
2865
0
            if (channel == BlackPixelChannel)
2866
0
              pixel=(MagickRealType) GetPixelBlack(source_image,p);
2867
0
            break;
2868
0
          }
2869
0
          case CopyBlueCompositeOp:
2870
0
          case CopyYellowCompositeOp:
2871
0
          {
2872
0
            if (channel == BluePixelChannel)
2873
0
              pixel=(MagickRealType) GetPixelBlue(source_image,p);
2874
0
            break;
2875
0
          }
2876
0
          case CopyGreenCompositeOp:
2877
0
          case CopyMagentaCompositeOp:
2878
0
          {
2879
0
            if (channel == GreenPixelChannel)
2880
0
              pixel=(MagickRealType) GetPixelGreen(source_image,p);
2881
0
            break;
2882
0
          }
2883
0
          case CopyRedCompositeOp:
2884
0
          case CopyCyanCompositeOp:
2885
0
          {
2886
0
            if (channel == RedPixelChannel)
2887
0
              pixel=(MagickRealType) GetPixelRed(source_image,p);
2888
0
            break;
2889
0
          }
2890
0
          case DarkenCompositeOp:
2891
0
          {
2892
0
            if (compose_sync == MagickFalse)
2893
0
              {
2894
0
                pixel=RoundToUnity(MagickMin(Sca,Dca)+Sca*(1.0-Da)+Dca*
2895
0
                  (1.0-Sa));
2896
0
                break;
2897
0
              }
2898
0
            pixel=(double) QuantumRange*RoundToUnity(MagickMin(Sca,Dca)+Sca*
2899
0
              (1.0-Da)+Dca*(1.0-Sa));
2900
0
            break;
2901
0
          }
2902
0
          case DarkenIntensityCompositeOp:
2903
0
          {
2904
0
            if (compose_sync == MagickFalse)
2905
0
              {
2906
0
                pixel=RoundToUnity(MagickMin(Sca,Dca)+Sca*(1.0-Di)+Dca*
2907
0
                  (1.0-Si));
2908
0
                break;
2909
0
              }
2910
0
            pixel=(double) QuantumRange*RoundToUnity(MagickMin(Sca,Dca)+Sca*
2911
0
              (1.0-Di)+Dca*(1.0-Si));
2912
0
            break;
2913
0
          }
2914
0
          case DifferenceCompositeOp:
2915
0
          {
2916
0
            if (compose_sync == MagickFalse)
2917
0
              {
2918
0
                 pixel=(double) QuantumRange*RoundToUnity(fabs((double) Sc-
2919
0
                  (double) Dc));
2920
0
                 break;
2921
0
              }
2922
0
            S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
2923
0
            D=(Da > 0.0) ? (Dca/Da) : 0.0;
2924
0
            pixel=(double) QuantumRange*RoundToUnity(fabs(S-D));
2925
0
            break;
2926
0
          }
2927
0
          case DissolveCompositeOp:
2928
0
          {
2929
0
            pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
2930
0
              canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
2931
0
            break;
2932
0
          }
2933
0
          case DivideDstCompositeOp:
2934
0
          {
2935
0
            S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
2936
0
            D=(Da > 0.0) ? (Dca/Da) : 0.0;
2937
0
            if (S <= 0.0)
2938
0
              blend=1.0;
2939
0
            else
2940
0
              blend=MagickMin(1.0,D/S);
2941
0
            pixel=(double) QuantumRange*RoundToUnity(Sca*(1.0-Da)+Dca*(1.0-Sa)+
2942
0
              Sa*Da*blend);
2943
0
            break;
2944
0
          }
2945
0
          case DivideSrcCompositeOp:
2946
0
          {
2947
0
            S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
2948
0
            D=(Da > 0.0) ? (Dca/Da) : 0.0;
2949
0
            if (D <= 0.0)
2950
0
              blend=1.0;
2951
0
            else
2952
0
              blend=MagickMin(1.0,S/D);
2953
0
            pixel=(double) QuantumRange*RoundToUnity(Sca*(1.0-Da)+Dca*(1.0-Sa)+
2954
0
              Sa*Da*blend);
2955
0
            break;
2956
0
          }
2957
0
          case DstAtopCompositeOp:
2958
0
          {
2959
0
            pixel=(double) QuantumRange*(Dca*Sa+Sca*(1.0-Da));
2960
0
            break;
2961
0
          }
2962
0
          case DstInCompositeOp:
2963
0
          {
2964
0
            pixel=(double) QuantumRange*gamma*(Dca*Sa);
2965
0
            break;
2966
0
          }
2967
0
          case DstOutCompositeOp:
2968
0
          {
2969
0
            pixel=(double) QuantumRange*gamma*(Dca*(1.0-Sa));
2970
0
            break;
2971
0
          }
2972
0
          case DstOverCompositeOp:
2973
0
          {
2974
0
            pixel=(double) QuantumRange*gamma*(Dca+Sca*(1.0-Da));
2975
0
            break;
2976
0
          }
2977
0
          case ExclusionCompositeOp:
2978
0
          {
2979
0
            S=(Sa > 0.0) ? Sca/Sa : 0.0;
2980
0
            D=(Da > 0.0) ? Dca/Da : 0.0;
2981
0
            blend=RoundToUnity(S+D-2.0*S*D);
2982
0
            pixel=(double) QuantumRange*RoundToUnity((blend*Sa+D*(1.0-Sa))*
2983
0
              (Sa+Da-Sa*Da));
2984
0
            break;
2985
0
          }
2986
0
          case FreezeCompositeOp:
2987
0
          {          
2988
0
            if (Dca != 0.0)
2989
0
              blend=1.0-(1.0-Sca)*(1.0-Sca)/Dca;
2990
0
            else
2991
0
              blend=0.0;
2992
0
            pixel=(double) QuantumRange*gamma*RoundToUnity(blend);
2993
0
            break;
2994
0
          }
2995
0
          case HardLightCompositeOp:
2996
0
          {
2997
0
            D=(Da > 0.0) ? (Dca/Da) : 0.0;
2998
0
            S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
2999
0
            if (S <= 0.5)
3000
0
              blend=2.0*S*D;
3001
0
            else
3002
0
              blend=1.0-2.0*(1.0-S)*(1.0-D);
3003
0
            pixel=(double) QuantumRange*gamma*RoundToUnity((1.0-Da)*Sca+
3004
0
              (1.0-Sa)*Dca+blend*Sa*Da);
3005
0
            break;
3006
0
          }
3007
0
          case HardMixCompositeOp:
3008
0
          {
3009
0
            if (Sca < 0.5)
3010
0
              {
3011
0
                if ((2.0*Sca) == 0.0)
3012
0
                  blend=0.0;
3013
0
                else
3014
0
                  blend=1.0-(1.0-Dca)/(2.0*Sca);
3015
0
              }
3016
0
            else
3017
0
              {
3018
0
                if ((1.0-((2.0*Sca)-1.0)) == 0.0)
3019
0
                  blend=1.0;
3020
0
                else
3021
0
                  blend=Dca/(1.0-(2.0*Sca-1.0));
3022
0
              }
3023
0
            pixel=gamma*((blend < 0.5) ? 0.0 : (double) QuantumRange);
3024
0
            break;
3025
0
          }
3026
0
          case HueCompositeOp:
3027
0
          {
3028
0
            if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3029
0
              {
3030
0
                pixel=Dc;
3031
0
                break;
3032
0
              }
3033
0
            if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3034
0
              {
3035
0
                pixel=Sc;
3036
0
                break;
3037
0
              }
3038
0
            ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3039
0
              (double) canvas_pixel.green,(double) canvas_pixel.blue,
3040
0
              white_luminance,illuminant,&hue,&chroma,&luma);
3041
0
            ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3042
0
              (double) source_pixel.green,(double) source_pixel.blue,
3043
0
              white_luminance,illuminant,&hue,&sans,&sans);
3044
0
            ConvertGenericToRGB(colorspace,hue,chroma,luma,
3045
0
              white_luminance,illuminant,&red,&green,&blue);
3046
0
            switch (channel)
3047
0
            {
3048
0
              case RedPixelChannel: pixel=red; break;
3049
0
              case GreenPixelChannel: pixel=green; break;
3050
0
              case BluePixelChannel: pixel=blue; break;
3051
0
              default: pixel=Dc; break;
3052
0
            }
3053
0
            break;
3054
0
          }
3055
0
          case InCompositeOp:
3056
0
          case SrcInCompositeOp:
3057
0
          {
3058
0
            pixel=(double) QuantumRange*(Sca*Da);
3059
0
            break;
3060
0
          }
3061
0
          case InterpolateCompositeOp:
3062
0
          {
3063
0
            pixel=(double) QuantumRange*(0.5-0.25*cos(MagickPI*Sca)-0.25*
3064
0
              cos(MagickPI*Dca));
3065
0
            break;
3066
0
          }
3067
0
          case LinearBurnCompositeOp:
3068
0
          {
3069
            /*
3070
              LinearBurn: as defined by Abode Photoshop, according to
3071
              http://www.simplefilter.de/en/basics/mixmods.html is:
3072
3073
                f(Sc,Dc) = Sc + Dc - 1
3074
            */
3075
0
            pixel=(double) QuantumRange*(Sca+Dca-Sa*Da);
3076
0
            break;
3077
0
          }
3078
0
          case LinearDodgeCompositeOp:
3079
0
          {
3080
0
            pixel=gamma*(Sa*Sc+Da*Dc);
3081
0
            break;
3082
0
          }
3083
0
          case LinearLightCompositeOp:
3084
0
          {
3085
            /*
3086
              Linear Light (Adobe standard):
3087
                f(Sc, Dc) = Dc + 2*Sc - 1
3088
              Applied in linear (HDRI) space with clamping.
3089
            */
3090
0
            D=(Da > 0.0) ? Dca/Da : 0.0,
3091
0
            S=(Sa > 0.0) ? Sca/Sa : 0.0;
3092
0
            pixel=(double) QuantumRange*gamma*(RoundToUnity(D+2.0*S-1.0)*Da);
3093
0
            break;
3094
0
          }
3095
0
          case LightenCompositeOp:
3096
0
          {
3097
0
            if (compose_sync == MagickFalse)
3098
0
              {
3099
0
                pixel=MagickMax(Sc,Dc);
3100
0
                break;
3101
0
              }
3102
0
            S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
3103
0
            D=(Da > 0.0) ? (Dca/Da) : 0.0;
3104
0
            pixel=(double) QuantumRange*RoundToUnity(Sca*(1.0-Da)+Dca*(1.0-Sa)+
3105
0
              MagickMax(S,D)*Sa*Da);
3106
0
            break;
3107
0
          }
3108
0
          case LightenIntensityCompositeOp:
3109
0
          {
3110
0
            pixel=Si > Di ? Sc : Dc;
3111
0
            break;
3112
0
          }
3113
0
          case LuminizeCompositeOp:
3114
0
          {
3115
0
            if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3116
0
              {
3117
0
                pixel=Dc;
3118
0
                break;
3119
0
              }
3120
0
            if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3121
0
              {
3122
0
                pixel=Sc;
3123
0
                break;
3124
0
              }
3125
0
            ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3126
0
              (double) canvas_pixel.green,(double) canvas_pixel.blue,
3127
0
              white_luminance,illuminant,&hue,&chroma,&luma);
3128
0
            ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3129
0
              (double) source_pixel.green,(double) source_pixel.blue,
3130
0
              white_luminance,illuminant,&sans,&sans,&luma);
3131
0
            ConvertGenericToRGB(colorspace,hue,chroma,luma,
3132
0
              white_luminance,illuminant,&red,&green,&blue);
3133
0
            switch (channel)
3134
0
            {
3135
0
              case RedPixelChannel: pixel=red; break;
3136
0
              case GreenPixelChannel: pixel=green; break;
3137
0
              case BluePixelChannel: pixel=blue; break;
3138
0
              default: pixel=Dc; break;
3139
0
            }
3140
0
            break;
3141
0
          }
3142
0
          case MathematicsCompositeOp:
3143
0
          {
3144
            /*
3145
              'Mathematics' a free form user control mathematical composition
3146
              is defined as...
3147
3148
                f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
3149
3150
              Where the arguments A,B,C,D are (currently) passed to composite
3151
              as a command separated 'geometry' string in "compose:args" image
3152
              artifact.
3153
3154
                 A = a->rho,   B = a->sigma,  C = a->xi,  D = a->psi
3155
3156
              Applying the SVG transparency formula (see above), we get...
3157
3158
               Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
3159
3160
               Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
3161
                 Dca*(1.0-Sa)
3162
            */
3163
0
            if (compose_sync == MagickFalse)
3164
0
              {
3165
0
                pixel=geometry_info.rho*Sc*Dc+geometry_info.sigma*Sc+
3166
0
                  geometry_info.xi*Dc+geometry_info.psi;
3167
0
                break;
3168
0
              }
3169
0
            pixel=(double) QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
3170
0
              geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
3171
0
              geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
3172
0
            break;
3173
0
          }
3174
0
          case MinusDstCompositeOp:
3175
0
          {
3176
0
           if (compose_sync == MagickFalse)
3177
0
              {
3178
0
                pixel=Dc-Sc;
3179
0
                break;
3180
0
              }
3181
0
            pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
3182
0
            break;
3183
0
          }
3184
0
          case MinusSrcCompositeOp:
3185
0
          {
3186
0
            if (compose_sync == MagickFalse)
3187
0
              {
3188
0
                pixel=Sc-Dc;
3189
0
                break;
3190
0
              }
3191
0
            pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
3192
0
            break;
3193
0
          }
3194
0
          case ModulateCompositeOp:
3195
0
          {
3196
0
            ssize_t
3197
0
              offset;
3198
3199
0
            if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3200
0
              {
3201
0
                pixel=Dc;
3202
0
                break;
3203
0
              }
3204
0
            offset=(ssize_t) (Si-midpoint);
3205
0
            if (offset == 0)
3206
0
              {
3207
0
                pixel=Dc;
3208
0
                break;
3209
0
              }
3210
0
            ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3211
0
              (double) canvas_pixel.green,(double) canvas_pixel.blue,
3212
0
              white_luminance,illuminant,&hue,&chroma,&luma);
3213
0
            luma+=(0.01*percent_luma*offset)/midpoint;
3214
0
            chroma*=0.01*percent_chroma;
3215
0
            ConvertGenericToRGB(colorspace,hue,chroma,luma,
3216
0
              white_luminance,illuminant,&red,&green,&blue);
3217
0
            switch (channel)
3218
0
            {
3219
0
              case RedPixelChannel: pixel=red; break;
3220
0
              case GreenPixelChannel: pixel=green; break;
3221
0
              case BluePixelChannel: pixel=blue; break;
3222
0
              default: pixel=Dc; break;
3223
0
            }
3224
0
            break;
3225
0
          }
3226
0
          case ModulusAddCompositeOp:
3227
0
          {
3228
0
            if (compose_sync == MagickFalse)
3229
0
              {
3230
0
                pixel=(Quantum) QuantumRange*((Sc+Dc)-floor(Sc+Dc));
3231
0
                break;
3232
0
              }
3233
0
            pixel=(Quantum) QuantumRange*((Sca+Dca)-floor(Sca+Dca));
3234
0
            break;
3235
0
          }
3236
0
          case ModulusSubtractCompositeOp:
3237
0
          {
3238
0
            if (compose_sync == MagickFalse)
3239
0
              {
3240
0
                pixel=(Quantum) QuantumRange*((Sc-Dc)-floor(Sc-Dc));
3241
0
                break;
3242
0
              }
3243
0
            pixel=(Quantum) QuantumRange*((Sca-Dca)-floor(Sca-Dca));
3244
0
            break;
3245
0
          }
3246
0
          case MultiplyCompositeOp:
3247
0
          {
3248
0
            if (compose_sync == MagickFalse)
3249
0
              {
3250
0
                pixel=(double) QuantumRange*Sc*Dc;
3251
0
                break;
3252
0
              }
3253
0
            pixel=(double) QuantumRange*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
3254
0
            break;
3255
0
          }
3256
0
          case NegateCompositeOp:
3257
0
          {
3258
0
            D=(Da > 0.0) ? Dca/Da : 0.0;
3259
0
            S=(Sa > 0.0) ? Sca/Sa : 0.0;
3260
0
            pixel=(double) QuantumRange*((1.0-fabs(1.0-S-D))*Da);
3261
0
            break;
3262
0
          }
3263
1.25k
          case NoCompositeOp:
3264
1.25k
          {
3265
1.25k
            pixel=(double) QuantumRange*Dca;
3266
1.25k
            break;
3267
0
          }
3268
0
          case OutCompositeOp:
3269
0
          case SrcOutCompositeOp:
3270
0
          {
3271
0
            pixel=(double) QuantumRange*(Sca*(1.0-Da));
3272
0
            break;
3273
0
          }
3274
0
          case OverCompositeOp:
3275
0
          case SrcOverCompositeOp:
3276
0
          {
3277
0
            if ((Sa+Da*(1.0-Sa)) <= MagickEpsilon)
3278
0
              pixel=0.0;
3279
0
            else
3280
0
              pixel=(double) QuantumRange*gamma*((Sca+Dca*(1.0-Sa))/
3281
0
                (Sa+Da*(1.0-Sa)));
3282
0
            break;
3283
0
          }
3284
0
          case OverlayCompositeOp:
3285
0
          {
3286
0
            if ((2.0*Dca) < Da)
3287
0
              {
3288
0
                pixel=(double) QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+
3289
0
                  Sca*(1.0-Da));
3290
0
                break;
3291
0
              }
3292
0
            pixel=(double) QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*
3293
0
              (1.0-Sa)+Sca*(1.0-Da));
3294
0
            break;
3295
0
          }
3296
0
          case PegtopLightCompositeOp:
3297
0
          {
3298
0
            if (fabs((double) Da) < MagickEpsilon)
3299
0
              {
3300
0
                pixel=(double) QuantumRange*gamma*Sca;
3301
0
                break;
3302
0
              }
3303
0
            if (RoundToUnity(Sca) <= 0.5)
3304
0
              blend=RoundToUnity(Dca/Da)-(1.0-2.0*RoundToUnity(Sca))*
3305
0
                RoundToUnity(Dca/Da)*(1.0-RoundToUnity(Dca/Da));
3306
0
            else
3307
0
              {
3308
0
                if (RoundToUnity(Dca/Da) <= 0.25)
3309
0
                  blend=((16.0*RoundToUnity(Dca/Da)-12.0)*RoundToUnity(Dca/Da)+
3310
0
                    4.0)*RoundToUnity(Dca/Da);
3311
0
                else
3312
0
                  blend=sqrt(RoundToUnity(Dca/Da));
3313
0
                blend=RoundToUnity(Dca/Da)+(2.0*RoundToUnity(Sca)-1.0)*
3314
0
                  (blend-RoundToUnity(Dca/Da));
3315
0
              }
3316
0
            pixel=(double) QuantumRange*gamma*(RoundToUnity(blend)*Da*Sa+Dca*
3317
0
              (1.0-Sa));
3318
0
            break;
3319
0
          }
3320
0
          case PinLightCompositeOp:
3321
0
          {
3322
            /*
3323
              Adobe Pin Light (colors in [0,1]):
3324
3325
                if (Cs <= 0.5)
3326
                  f = min(Cd, 2*Cs);
3327
                else
3328
                  f = max(Cd, 2*Cs - 1);
3329
            */
3330
0
            D=(Da > 0.0) ? RoundToUnity(Dca/Da) : 0.0;
3331
0
            S=(Sa > 0.0) ? RoundToUnity(Sca/Sa) : 0.0;
3332
0
            if (S <= 0.5)
3333
0
              blend=MagickMin(D,2.0*S);
3334
0
            else
3335
0
              blend=MagickMax(D,2.0*S-1.0);
3336
0
            pixel=(double) QuantumRange*gamma*(blend*RoundToUnity(Sa+Da-Sa*Da));
3337
0
            break;
3338
0
          }
3339
0
          case PlusCompositeOp:
3340
0
          {
3341
0
            D=(Da > 0.0) ? Dca/Da : 0.0;
3342
0
            S=(Sa > 0.0) ? Sca/Sa : 0.0;
3343
0
            pixel=(double) QuantumRange*(RoundToUnity(Sa+Da-Sa*Da)*
3344
0
              RoundToUnity(S+D));
3345
0
            break;
3346
0
          }
3347
0
          case ReflectCompositeOp:
3348
0
          {
3349
0
            if (compose_sync == MagickFalse)
3350
0
              {
3351
0
                if (Dc < 1.0)
3352
0
                  blend=(Sc*Sc)/(1.0-Dc);
3353
0
                else
3354
0
                  blend=1.0;
3355
0
                pixel=(double) QuantumRange*RoundToUnity(blend);
3356
0
                break;
3357
0
              }
3358
0
            if (Sa > 0.0)
3359
0
              S=Sca/Sa;
3360
0
            else
3361
0
              S=0.0;
3362
0
            if (Da > 0.0)
3363
0
              D=Dca/Da;
3364
0
            else
3365
0
              D=0.0;
3366
0
            if (D < 1.0)
3367
0
              blend=(S*S)/(1.0-D);
3368
0
            else
3369
0
              blend=1.0;
3370
0
            pixel=(double) QuantumRange*RoundToUnity((Sa+Da-Sa*Da)*blend);
3371
0
            break;
3372
0
          }
3373
0
          case RMSECompositeOp:
3374
0
          {
3375
0
            double
3376
0
              gray;
3377
3378
0
            if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3379
0
              {
3380
0
                pixel=Dc;
3381
0
                break;
3382
0
              }
3383
0
            if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3384
0
              {
3385
0
                pixel=Sc;
3386
0
                break;
3387
0
              }
3388
0
            gray=sqrt(
3389
0
              (canvas_pixel.red-source_pixel.red)*
3390
0
              (canvas_pixel.red-source_pixel.red)+
3391
0
              (canvas_pixel.green-source_pixel.green)*
3392
0
              (canvas_pixel.green-source_pixel.green)+
3393
0
              (canvas_pixel.blue-source_pixel.blue)*
3394
0
              (canvas_pixel.blue-source_pixel.blue)/3.0);
3395
0
            switch (channel)
3396
0
            {
3397
0
              case RedPixelChannel: pixel=gray; break;
3398
0
              case GreenPixelChannel: pixel=gray; break;
3399
0
              case BluePixelChannel: pixel=gray; break;
3400
0
              default: pixel=Dc; break;
3401
0
            }
3402
0
            break;
3403
0
          }
3404
0
          case SaturateCompositeOp:
3405
0
          {
3406
0
            if (fabs((double) QuantumRange*Sa-(double) TransparentAlpha) < MagickEpsilon)
3407
0
              {
3408
0
                pixel=Dc;
3409
0
                break;
3410
0
              }
3411
0
            if (fabs((double) QuantumRange*Da-(double) TransparentAlpha) < MagickEpsilon)
3412
0
              {
3413
0
                pixel=Sc;
3414
0
                break;
3415
0
              }
3416
0
            ConvertRGBToGeneric(colorspace,(double) canvas_pixel.red,
3417
0
              (double) canvas_pixel.green,(double) canvas_pixel.blue,
3418
0
              white_luminance,illuminant,&hue,&chroma,&luma);
3419
0
            ConvertRGBToGeneric(colorspace,(double) source_pixel.red,
3420
0
              (double) source_pixel.green,(double) source_pixel.blue,
3421
0
              white_luminance,illuminant,&sans,&chroma,&sans);
3422
0
            ConvertGenericToRGB(colorspace,hue,chroma,luma,
3423
0
              white_luminance,illuminant,&red,&green,&blue);
3424
0
            switch (channel)
3425
0
            {
3426
0
              case RedPixelChannel: pixel=red; break;
3427
0
              case GreenPixelChannel: pixel=green; break;
3428
0
              case BluePixelChannel: pixel=blue; break;
3429
0
              default: pixel=Dc; break;
3430
0
            }
3431
0
            break;
3432
0
          }
3433
0
          case ScreenCompositeOp:
3434
0
          {
3435
0
            if (compose_sync == MagickFalse)
3436
0
              {
3437
0
                pixel=(double) QuantumRange*RoundToUnity(Sc+Dc-Sc*Dc);
3438
0
                break;
3439
0
              }
3440
0
            if (Sa > 0.0)
3441
0
              S=Sca/Sa;
3442
0
            else
3443
0
              S=0.0;
3444
0
            if (Da > 0.0)
3445
0
              D=Dca/Da;
3446
0
            else
3447
0
              D=0.0;
3448
0
            if ((Sa+Da-Sa*Da) > 0.0)
3449
0
              pixel=(double) QuantumRange*RoundToUnity((Sa+Da-Sa*Da)*(S+D-S*D));
3450
0
            else
3451
0
              pixel=0.0;
3452
0
            break;
3453
0
          }
3454
0
          case SoftBurnCompositeOp:
3455
0
          {
3456
0
            if (RoundToUnity(Dca) <= 0.0)
3457
0
              blend = 0.0;
3458
0
            else
3459
0
              if (RoundToUnity(Sca) >= 1.0)
3460
0
                blend = 1.0;
3461
0
              else
3462
0
                blend=1.0-MagickMin(1.0,(1.0-RoundToUnity(Dca))/
3463
0
                  RoundToUnity(Sca));
3464
0
            pixel=(double) QuantumRange*gamma*RoundToUnity(blend);
3465
0
            break;
3466
0
          }
3467
0
          case SoftDodgeCompositeOp:
3468
0
          {
3469
0
            if (RoundToUnity(Sca) <= 0.0)
3470
0
              blend=RoundToUnity(Dca);
3471
0
            else
3472
0
              if (RoundToUnity(Dca) >= 1.0)
3473
0
                blend=1.0;
3474
0
              else
3475
0
                blend=MagickMin(1.0,RoundToUnity(Dca)/(1.0-RoundToUnity(Sca)));
3476
0
            pixel=(double) QuantumRange*gamma*RoundToUnity(blend);
3477
0
            break;
3478
0
          }
3479
0
          case SoftLightCompositeOp:
3480
0
          {
3481
0
            if (RoundToUnity(Sca) <= 0.5)
3482
0
              {
3483
0
                pixel=(double) QuantumRange*gamma*(RoundToUnity(Dca)*Sa+Da*
3484
0
                 (RoundToUnity(Dca)-(1.0-2.0*RoundToUnity(Sca))*
3485
0
                 RoundToUnity(Dca)*(1.0-RoundToUnity(Dca)))+RoundToUnity(Sca)*
3486
0
                 (1.0-Da)+RoundToUnity(Dca)*(1.0-Sa));
3487
0
                break;
3488
0
              }
3489
0
            if (RoundToUnity(Dca) > 0.25)
3490
0
              blend=sqrt(RoundToUnity(Dca));
3491
0
            else
3492
0
              blend=((16.0*RoundToUnity(Dca)-12.0)*RoundToUnity(Dca)+4.0)*
3493
0
                RoundToUnity(Dca);
3494
0
            pixel=(double) QuantumRange*gamma*(RoundToUnity(Dca)*Sa+Da*
3495
0
              (RoundToUnity(Dca)+(2.0*RoundToUnity(Sca)-1.0)*
3496
0
              (RoundToUnity(blend)-RoundToUnity(Dca)))+RoundToUnity(Sca)*
3497
0
              (1.0-Da)+RoundToUnity(Dca)*(1.0-Sa));
3498
0
            break;
3499
0
          }
3500
0
          case StampCompositeOp:
3501
0
          {
3502
0
            pixel=(double) QuantumRange*RoundToUnity(Sca+Dca-1.0);
3503
0
            break;
3504
0
          }
3505
0
          case StereoCompositeOp:
3506
0
          {
3507
0
            if (channel == RedPixelChannel)
3508
0
              pixel=(MagickRealType) GetPixelRed(source_image,p);
3509
0
            break;
3510
0
          }
3511
0
          case ThresholdCompositeOp:
3512
0
          {
3513
0
            S=(Sa > 0.0) ? (Sca/Sa) : 0.0;
3514
0
            D=(Da > 0.0) ? (Dca/Da) : 0.0;
3515
0
            if (fabs(2.0*(S-D)) < threshold)
3516
0
              blend=D;
3517
0
            else
3518
0
              blend=D+(S-D)*amount;
3519
0
            pixel=(double) QuantumRange*(Sa*Da*RoundToUnity(blend)+Sca*
3520
0
              (1.0-Da)+Dca*(1.0-Sa));
3521
0
            break;
3522
0
          }
3523
0
          case VividLightCompositeOp:
3524
0
          {
3525
0
            if ((fabs((double) Sa) < MagickEpsilon) ||
3526
0
                (fabs((double) Da) < MagickEpsilon))
3527
0
              {
3528
0
                pixel=(double) QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*
3529
0
                  (1.0-Sa));
3530
0
                break;
3531
0
              }
3532
0
            if (RoundToUnity(Sca/Sa) <= 0.0)
3533
0
              blend=0.0;
3534
0
            else
3535
0
              if (RoundToUnity(Sca/Sa) < 0.5)
3536
0
                blend=1.0-(1.0-RoundToUnity(Dca/Da))/(2.0*RoundToUnity(Sca/Sa));
3537
0
            else
3538
0
              if (RoundToUnity(Sca/Sa) < 1.0)
3539
0
                blend=RoundToUnity(Dca/Da)/(2.0*(1.0-RoundToUnity(Sca/Sa)));
3540
0
              else
3541
0
                blend=1.0;
3542
0
            pixel=(double) QuantumRange*gamma*(Sa*Da*RoundToUnity(blend)+Sca*
3543
0
              (1.0-Da)+Dca*(1.0-Sa));
3544
0
            break;
3545
0
          }
3546
0
          case XorCompositeOp:
3547
0
          {
3548
0
            pixel=(double) QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
3549
0
            break;
3550
0
          }
3551
0
          default:
3552
0
          {
3553
0
            pixel=Sc;
3554
0
            break;
3555
0
          }
3556
1.34G
        }
3557
1.34G
        q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
3558
1.34G
      }
3559
543M
      p+=(ptrdiff_t) GetPixelChannels(source_image);
3560
543M
      channels=GetPixelChannels(source_image);
3561
543M
      if (p >= (pixels+channels*source_image->columns))
3562
932k
        p=pixels;
3563
543M
      q+=(ptrdiff_t) GetPixelChannels(image);
3564
543M
    }
3565
1.41M
    if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
3566
0
      status=MagickFalse;
3567
1.41M
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3568
0
      {
3569
0
        MagickBooleanType
3570
0
          proceed;
3571
3572
#if defined(MAGICKCORE_OPENMP_SUPPORT)
3573
        #pragma omp atomic
3574
#endif
3575
0
        progress++;
3576
0
        proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
3577
0
        if (proceed == MagickFalse)
3578
0
          status=MagickFalse;
3579
0
      }
3580
1.41M
  }
3581
10.4k
  source_view=DestroyCacheView(source_view);
3582
10.4k
  image_view=DestroyCacheView(image_view);
3583
10.4k
  if (canvas_image != (Image * ) NULL)
3584
0
    canvas_image=DestroyImage(canvas_image);
3585
10.4k
  else
3586
10.4k
    source_image=DestroyImage(source_image);
3587
10.4k
  return(status);
3588
10.4k
}
3589

3590
/*
3591
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3592
%                                                                             %
3593
%                                                                             %
3594
%                                                                             %
3595
%     T e x t u r e I m a g e                                                 %
3596
%                                                                             %
3597
%                                                                             %
3598
%                                                                             %
3599
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3600
%
3601
%  TextureImage() repeatedly tiles the texture image across and down the image
3602
%  canvas.
3603
%
3604
%  The format of the TextureImage method is:
3605
%
3606
%      MagickBooleanType TextureImage(Image *image,const Image *texture,
3607
%        ExceptionInfo *exception)
3608
%
3609
%  A description of each parameter follows:
3610
%
3611
%    o image: the image.
3612
%
3613
%    o texture_image: This image is the texture to layer on the background.
3614
%
3615
*/
3616
MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
3617
  ExceptionInfo *exception)
3618
0
{
3619
0
#define TextureImageTag  "Texture/Image"
3620
3621
0
  CacheView
3622
0
    *image_view,
3623
0
    *texture_view;
3624
3625
0
  Image
3626
0
    *texture_image;
3627
3628
0
  MagickBooleanType
3629
0
    status;
3630
3631
0
  ssize_t
3632
0
    y;
3633
3634
0
  assert(image != (Image *) NULL);
3635
0
  assert(image->signature == MagickCoreSignature);
3636
0
  if (IsEventLogging() != MagickFalse)
3637
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
3638
0
  if (texture == (const Image *) NULL)
3639
0
    return(MagickFalse);
3640
0
  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
3641
0
    return(MagickFalse);
3642
0
  texture_image=CloneImage(texture,0,0,MagickTrue,exception);
3643
0
  if (texture_image == (const Image *) NULL)
3644
0
    return(MagickFalse);
3645
0
  (void) TransformImageColorspace(texture_image,image->colorspace,exception);
3646
0
  (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
3647
0
    exception);
3648
0
  status=MagickTrue;
3649
0
  if ((image->compose != CopyCompositeOp) &&
3650
0
      ((image->compose != OverCompositeOp) ||
3651
0
       (image->alpha_trait != UndefinedPixelTrait) ||
3652
0
       (texture_image->alpha_trait != UndefinedPixelTrait)))
3653
0
    {
3654
      /*
3655
        Tile texture onto the image background.
3656
      */
3657
0
      for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
3658
0
      {
3659
0
        ssize_t
3660
0
          x;
3661
3662
0
        if (status == MagickFalse)
3663
0
          continue;
3664
0
        for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
3665
0
        {
3666
0
          MagickBooleanType
3667
0
            thread_status;
3668
3669
0
          thread_status=CompositeImage(image,texture_image,image->compose,
3670
0
            MagickTrue,x+texture_image->tile_offset.x,y+
3671
0
            texture_image->tile_offset.y,exception);
3672
0
          if (thread_status == MagickFalse)
3673
0
            {
3674
0
              status=thread_status;
3675
0
              break;
3676
0
            }
3677
0
        }
3678
0
        if (image->progress_monitor != (MagickProgressMonitor) NULL)
3679
0
          {
3680
0
            MagickBooleanType
3681
0
              proceed;
3682
3683
0
            proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3684
0
              image->rows);
3685
0
            if (proceed == MagickFalse)
3686
0
              status=MagickFalse;
3687
0
          }
3688
0
      }
3689
0
      (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
3690
0
        image->rows,image->rows);
3691
0
      texture_image=DestroyImage(texture_image);
3692
0
      return(status);
3693
0
    }
3694
  /*
3695
    Tile texture onto the image background (optimized).
3696
  */
3697
0
  status=MagickTrue;
3698
0
  texture_view=AcquireVirtualCacheView(texture_image,exception);
3699
0
  image_view=AcquireAuthenticCacheView(image,exception);
3700
#if defined(MAGICKCORE_OPENMP_SUPPORT)
3701
  #pragma omp parallel for schedule(static) shared(status) \
3702
    magick_number_threads(texture_image,image,image->rows,2)
3703
#endif
3704
0
  for (y=0; y < (ssize_t) image->rows; y++)
3705
0
  {
3706
0
    MagickBooleanType
3707
0
      sync;
3708
3709
0
    const Quantum
3710
0
      *p,
3711
0
      *pixels;
3712
3713
0
    ssize_t
3714
0
      x;
3715
3716
0
    Quantum
3717
0
      *q;
3718
3719
0
    size_t
3720
0
      width;
3721
3722
0
    if (status == MagickFalse)
3723
0
      continue;
3724
0
    pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
3725
0
      (y+texture_image->tile_offset.y) % (ssize_t) texture_image->rows,
3726
0
      texture_image->columns,1,exception);
3727
0
    q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
3728
0
    if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3729
0
      {
3730
0
        status=MagickFalse;
3731
0
        continue;
3732
0
      }
3733
0
    for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
3734
0
    {
3735
0
      ssize_t
3736
0
        j;
3737
3738
0
      p=pixels;
3739
0
      width=texture_image->columns;
3740
0
      if ((x+(ssize_t) width) > (ssize_t) image->columns)
3741
0
        width=image->columns-(size_t) x;
3742
0
      for (j=0; j < (ssize_t) width; j++)
3743
0
      {
3744
0
        ssize_t
3745
0
          i;
3746
3747
0
        for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
3748
0
        {
3749
0
          PixelChannel channel = GetPixelChannelChannel(texture_image,i);
3750
0
          PixelTrait traits = GetPixelChannelTraits(image,channel);
3751
0
          PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
3752
0
            channel);
3753
0
          if ((traits == UndefinedPixelTrait) ||
3754
0
              (texture_traits == UndefinedPixelTrait))
3755
0
            continue;
3756
0
          SetPixelChannel(image,channel,p[i],q);
3757
0
        }
3758
0
        p+=(ptrdiff_t) GetPixelChannels(texture_image);
3759
0
        q+=(ptrdiff_t) GetPixelChannels(image);
3760
0
      }
3761
0
    }
3762
0
    sync=SyncCacheViewAuthenticPixels(image_view,exception);
3763
0
    if (sync == MagickFalse)
3764
0
      status=MagickFalse;
3765
0
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3766
0
      {
3767
0
        MagickBooleanType
3768
0
          proceed;
3769
3770
0
        proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
3771
0
          image->rows);
3772
0
        if (proceed == MagickFalse)
3773
0
          status=MagickFalse;
3774
0
      }
3775
0
  }
3776
0
  texture_view=DestroyCacheView(texture_view);
3777
0
  image_view=DestroyCacheView(image_view);
3778
0
  texture_image=DestroyImage(texture_image);
3779
0
  return(status);
3780
0
}