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