MagickCore  7.0.3
composite.c
Go to the documentation of this file.
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-2019 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"
47 #include "MagickCore/cache-view.h"
48 #include "MagickCore/channel.h"
49 #include "MagickCore/client.h"
50 #include "MagickCore/color.h"
52 #include "MagickCore/colorspace.h"
54 #include "MagickCore/composite.h"
56 #include "MagickCore/constitute.h"
57 #include "MagickCore/draw.h"
58 #include "MagickCore/fx.h"
59 #include "MagickCore/gem.h"
60 #include "MagickCore/geometry.h"
61 #include "MagickCore/image.h"
63 #include "MagickCore/list.h"
64 #include "MagickCore/log.h"
65 #include "MagickCore/monitor.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/option.h"
70 #include "MagickCore/property.h"
71 #include "MagickCore/quantum.h"
72 #include "MagickCore/resample.h"
73 #include "MagickCore/resource_.h"
74 #include "MagickCore/string_.h"
76 #include "MagickCore/threshold.h"
77 #include "MagickCore/token.h"
78 #include "MagickCore/utility.h"
80 #include "MagickCore/version.h"
81 
82 /*
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 % %
85 % %
86 % %
87 % C o m p o s i t e I m a g e %
88 % %
89 % %
90 % %
91 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92 %
93 % CompositeImage() returns the second image composited onto the first
94 % at the specified offset, using the specified composite method.
95 %
96 % The format of the CompositeImage method is:
97 %
98 % MagickBooleanType CompositeImage(Image *image,
99 % const Image *source_image,const CompositeOperator compose,
100 % const MagickBooleanType clip_to_self,const ssize_t x_offset,
101 % const ssize_t y_offset,ExceptionInfo *exception)
102 %
103 % A description of each parameter follows:
104 %
105 % o image: the canvas image, modified by he composition
106 %
107 % o source_image: the source image.
108 %
109 % o compose: This operator affects how the composite is applied to
110 % the image. The operators and how they are utilized are listed here
111 % http://www.w3.org/TR/SVG12/#compositing.
112 %
113 % o clip_to_self: set to MagickTrue to limit composition to area composed.
114 %
115 % o x_offset: the column offset of the composited image.
116 %
117 % o y_offset: the row offset of the composited image.
118 %
119 % Extra Controls from Image meta-data in 'image' (artifacts)
120 %
121 % o "compose:args"
122 % A string containing extra numerical arguments for specific compose
123 % methods, generally expressed as a 'geometry' or a comma separated list
124 % of numbers.
125 %
126 % Compose methods needing such arguments include "BlendCompositeOp" and
127 % "DisplaceCompositeOp".
128 %
129 % o exception: return any errors or warnings in this structure.
130 %
131 */
132 
133 /*
134  Composition based on the SVG specification:
135 
136  A Composition is defined by...
137  Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
138  Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
139  Y = 1 for source preserved
140  Z = 1 for canvas preserved
141 
142  Conversion to transparency (then optimized)
143  Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
144  Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
145 
146  Where...
147  Sca = Sc*Sa normalized Source color divided by Source alpha
148  Dca = Dc*Da normalized Dest color divided by Dest alpha
149  Dc' = Dca'/Da' the desired color value for this channel.
150 
151  Da' in in the follow formula as 'gamma' The resulting alpla value.
152 
153  Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
154  the following optimizations...
155  gamma = Sa+Da-Sa*Da;
156  gamma = 1 - QuantumScale*alpha * QuantumScale*beta;
157  opacity = QuantumScale*alpha*beta; // over blend, optimized 1-Gamma
158 
159  The above SVG definitions also define that Mathematical Composition
160  methods should use a 'Over' blending mode for Alpha Channel.
161  It however was not applied for composition modes of 'Plus', 'Minus',
162  the modulus versions of 'Add' and 'Subtract'.
163 
164  Mathematical operator changes to be applied from IM v6.7...
165 
166  1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
167  'ModulusAdd' and 'ModulusSubtract' for clarity.
168 
169  2) All mathematical compositions work as per the SVG specification
170  with regard to blending. This now includes 'ModulusAdd' and
171  'ModulusSubtract'.
172 
173  3) When the special channel flag 'sync' (syncronize channel updates)
174  is turned off (enabled by default) then mathematical compositions are
175  only performed on the channels specified, and are applied
176  independantally of each other. In other words the mathematics is
177  performed as 'pure' mathematical operations, rather than as image
178  operations.
179 */
180 
181 static void HCLComposite(const MagickRealType hue,const MagickRealType chroma,
182  const MagickRealType luma,MagickRealType *red,MagickRealType *green,
183  MagickRealType *blue)
184 {
186  b,
187  c,
188  g,
189  h,
190  m,
191  r,
192  x;
193 
194  /*
195  Convert HCL to RGB colorspace.
196  */
197  assert(red != (MagickRealType *) NULL);
198  assert(green != (MagickRealType *) NULL);
199  assert(blue != (MagickRealType *) NULL);
200  h=6.0*hue;
201  c=chroma;
202  x=c*(1.0-fabs(fmod(h,2.0)-1.0));
203  r=0.0;
204  g=0.0;
205  b=0.0;
206  if ((0.0 <= h) && (h < 1.0))
207  {
208  r=c;
209  g=x;
210  }
211  else
212  if ((1.0 <= h) && (h < 2.0))
213  {
214  r=x;
215  g=c;
216  }
217  else
218  if ((2.0 <= h) && (h < 3.0))
219  {
220  g=c;
221  b=x;
222  }
223  else
224  if ((3.0 <= h) && (h < 4.0))
225  {
226  g=x;
227  b=c;
228  }
229  else
230  if ((4.0 <= h) && (h < 5.0))
231  {
232  r=x;
233  b=c;
234  }
235  else
236  if ((5.0 <= h) && (h < 6.0))
237  {
238  r=c;
239  b=x;
240  }
241  m=luma-(0.298839*r+0.586811*g+0.114350*b);
242  *red=QuantumRange*(r+m);
243  *green=QuantumRange*(g+m);
244  *blue=QuantumRange*(b+m);
245 }
246 
247 static void CompositeHCL(const MagickRealType red,const MagickRealType green,
248  const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
249  MagickRealType *luma)
250 {
252  b,
253  c,
254  g,
255  h,
256  max,
257  r;
258 
259  /*
260  Convert RGB to HCL colorspace.
261  */
262  assert(hue != (MagickRealType *) NULL);
263  assert(chroma != (MagickRealType *) NULL);
264  assert(luma != (MagickRealType *) NULL);
265  r=red;
266  g=green;
267  b=blue;
268  max=MagickMax(r,MagickMax(g,b));
269  c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
270  h=0.0;
271  if (c == 0)
272  h=0.0;
273  else
274  if (red == max)
275  h=fmod((g-b)/c+6.0,6.0);
276  else
277  if (green == max)
278  h=((b-r)/c)+2.0;
279  else
280  if (blue == max)
281  h=((r-g)/c)+4.0;
282  *hue=(h/6.0);
283  *chroma=QuantumScale*c;
284  *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b);
285 }
286 
288  const Image *source_image,const MagickBooleanType clip_to_self,
289  const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
290 {
291 #define CompositeImageTag "Composite/Image"
292 
293  CacheView
294  *image_view,
295  *source_view;
296 
297  const char
298  *value;
299 
301  clamp,
302  status;
303 
305  progress;
306 
307  ssize_t
308  y;
309 
310  /*
311  Composite image.
312  */
313  status=MagickTrue;
314  progress=0;
315  clamp=MagickTrue;
316  value=GetImageArtifact(image,"compose:clamp");
317  if (value != (const char *) NULL)
318  clamp=IsStringTrue(value);
319  status=MagickTrue;
320  progress=0;
321  source_view=AcquireVirtualCacheView(source_image,exception);
322  image_view=AcquireAuthenticCacheView(image,exception);
323 #if defined(MAGICKCORE_OPENMP_SUPPORT)
324  #pragma omp parallel for schedule(static) shared(progress,status) \
325  magick_number_threads(source_image,image,image->rows,1)
326 #endif
327  for (y=0; y < (ssize_t) image->rows; y++)
328  {
329  const Quantum
330  *pixels;
331 
332  PixelInfo
333  canvas_pixel,
334  source_pixel;
335 
336  register const Quantum
337  *magick_restrict p;
338 
339  register Quantum
340  *magick_restrict q;
341 
342  register ssize_t
343  x;
344 
345  if (status == MagickFalse)
346  continue;
347  if (clip_to_self != MagickFalse)
348  {
349  if (y < y_offset)
350  continue;
351  if ((y-y_offset) >= (ssize_t) source_image->rows)
352  continue;
353  }
354  /*
355  If pixels is NULL, y is outside overlay region.
356  */
357  pixels=(Quantum *) NULL;
358  p=(Quantum *) NULL;
359  if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
360  {
361  p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
362  source_image->columns,1,exception);
363  if (p == (const Quantum *) NULL)
364  {
365  status=MagickFalse;
366  continue;
367  }
368  pixels=p;
369  if (x_offset < 0)
370  p-=x_offset*GetPixelChannels(source_image);
371  }
372  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
373  if (q == (Quantum *) NULL)
374  {
375  status=MagickFalse;
376  continue;
377  }
378  GetPixelInfo(image,&canvas_pixel);
379  GetPixelInfo(source_image,&source_pixel);
380  for (x=0; x < (ssize_t) image->columns; x++)
381  {
382  double
383  gamma;
384 
386  alpha,
387  Da,
388  Dc,
389  Dca,
390  Sa,
391  Sc,
392  Sca;
393 
394  register ssize_t
395  i;
396 
397  size_t
398  channels;
399 
400  if (clip_to_self != MagickFalse)
401  {
402  if (x < x_offset)
403  {
404  q+=GetPixelChannels(image);
405  continue;
406  }
407  if ((x-x_offset) >= (ssize_t) source_image->columns)
408  break;
409  }
410  if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
411  ((x-x_offset) >= (ssize_t) source_image->columns))
412  {
413  Quantum
414  source[MaxPixelChannels];
415 
416  /*
417  Virtual composite:
418  Sc: source color.
419  Dc: canvas color.
420  */
421  (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
422  exception);
423  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
424  {
426  pixel;
427 
428  PixelChannel channel = GetPixelChannelChannel(image,i);
429  PixelTrait traits = GetPixelChannelTraits(image,channel);
430  PixelTrait source_traits=GetPixelChannelTraits(source_image,
431  channel);
432  if ((traits == UndefinedPixelTrait) ||
433  (source_traits == UndefinedPixelTrait))
434  continue;
435  if (channel == AlphaPixelChannel)
437  else
438  pixel=(MagickRealType) q[i];
439  q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
440  ClampToQuantum(pixel);
441  }
442  q+=GetPixelChannels(image);
443  continue;
444  }
445  /*
446  Authentic composite:
447  Sa: normalized source alpha.
448  Da: normalized canvas alpha.
449  */
450  Sa=QuantumScale*GetPixelAlpha(source_image,p);
451  Da=QuantumScale*GetPixelAlpha(image,q);
452  alpha=Sa+Da-Sa*Da;
453  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
454  {
456  pixel;
457 
458  PixelChannel channel = GetPixelChannelChannel(image,i);
459  PixelTrait traits = GetPixelChannelTraits(image,channel);
460  PixelTrait source_traits=GetPixelChannelTraits(source_image,channel);
461  if (traits == UndefinedPixelTrait)
462  continue;
463  if ((source_traits == UndefinedPixelTrait) &&
464  (channel != AlphaPixelChannel))
465  continue;
466  if (channel == AlphaPixelChannel)
467  {
468  /*
469  Set alpha channel.
470  */
471  pixel=QuantumRange*alpha;
472  q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
473  ClampToQuantum(pixel);
474  continue;
475  }
476  /*
477  Sc: source color.
478  Dc: canvas color.
479  */
480  Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
481  Dc=(MagickRealType) q[i];
482  if ((traits & CopyPixelTrait) != 0)
483  {
484  /*
485  Copy channel.
486  */
487  q[i]=ClampToQuantum(Sc);
488  continue;
489  }
490  /*
491  Porter-Duff compositions:
492  Sca: source normalized color multiplied by alpha.
493  Dca: normalized canvas color multiplied by alpha.
494  */
495  Sca=QuantumScale*Sa*Sc;
496  Dca=QuantumScale*Da*Dc;
497  gamma=PerceptibleReciprocal(alpha);
498  pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
499  q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
500  }
501  p+=GetPixelChannels(source_image);
502  channels=GetPixelChannels(source_image);
503  if (p >= (pixels+channels*source_image->columns))
504  p=pixels;
505  q+=GetPixelChannels(image);
506  }
507  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
508  status=MagickFalse;
509  if (image->progress_monitor != (MagickProgressMonitor) NULL)
510  {
512  proceed;
513 
514 #if defined(MAGICKCORE_OPENMP_SUPPORT)
515  #pragma omp atomic
516 #endif
517  progress++;
518  proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
519  if (proceed == MagickFalse)
520  status=MagickFalse;
521  }
522  }
523  source_view=DestroyCacheView(source_view);
524  image_view=DestroyCacheView(image_view);
525  return(status);
526 }
527 
529  const Image *composite,const CompositeOperator compose,
530  const MagickBooleanType clip_to_self,const ssize_t x_offset,
531  const ssize_t y_offset,ExceptionInfo *exception)
532 {
533 #define CompositeImageTag "Composite/Image"
534 
535  CacheView
536  *source_view,
537  *image_view;
538 
539  const char
540  *value;
541 
543  geometry_info;
544 
545  Image
546  *canvas_image,
547  *source_image;
548 
550  clamp,
551  status;
552 
554  progress;
555 
557  amount,
558  canvas_dissolve,
559  midpoint,
560  percent_luma,
561  percent_chroma,
562  source_dissolve,
563  threshold;
564 
566  flags;
567 
568  ssize_t
569  y;
570 
571  assert(image != (Image *) NULL);
572  assert(image->signature == MagickCoreSignature);
573  if (image->debug != MagickFalse)
574  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
575  assert(composite != (Image *) NULL);
576  assert(composite->signature == MagickCoreSignature);
577  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
578  return(MagickFalse);
579  source_image=CloneImage(composite,0,0,MagickTrue,exception);
580  if (source_image == (const Image *) NULL)
581  return(MagickFalse);
582  if (IsGrayColorspace(image->colorspace) == MagickFalse)
583  (void) SetImageColorspace(image,sRGBColorspace,exception);
584  (void) SetImageColorspace(source_image,image->colorspace,exception);
585  if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
586  {
587  status=CompositeOverImage(image,source_image,clip_to_self,x_offset,
588  y_offset,exception);
589  source_image=DestroyImage(source_image);
590  return(status);
591  }
592  amount=0.5;
593  canvas_image=(Image *) NULL;
594  canvas_dissolve=1.0;
595  clamp=MagickTrue;
596  value=GetImageArtifact(image,"compose:clamp");
597  if (value != (const char *) NULL)
598  clamp=IsStringTrue(value);
599  SetGeometryInfo(&geometry_info);
600  percent_luma=100.0;
601  percent_chroma=100.0;
602  source_dissolve=1.0;
603  threshold=0.05f;
604  switch (compose)
605  {
606  case CopyCompositeOp:
607  {
608  if ((x_offset < 0) || (y_offset < 0))
609  break;
610  if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
611  break;
612  if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
613  break;
614  if ((source_image->alpha_trait == UndefinedPixelTrait) &&
615  (image->alpha_trait != UndefinedPixelTrait))
616  (void) SetImageAlphaChannel(source_image,OpaqueAlphaChannel,exception);
617  status=MagickTrue;
618  source_view=AcquireVirtualCacheView(source_image,exception);
619  image_view=AcquireAuthenticCacheView(image,exception);
620 #if defined(MAGICKCORE_OPENMP_SUPPORT)
621  #pragma omp parallel for schedule(static) shared(status) \
622  magick_number_threads(source_image,image,source_image->rows,1)
623 #endif
624  for (y=0; y < (ssize_t) source_image->rows; y++)
625  {
627  sync;
628 
629  register const Quantum
630  *p;
631 
632  register Quantum
633  *q;
634 
635  register ssize_t
636  x;
637 
638  if (status == MagickFalse)
639  continue;
640  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
641  exception);
642  q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
643  source_image->columns,1,exception);
644  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
645  {
646  status=MagickFalse;
647  continue;
648  }
649  for (x=0; x < (ssize_t) source_image->columns; x++)
650  {
651  register ssize_t
652  i;
653 
654  if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
655  {
656  p+=GetPixelChannels(source_image);
657  q+=GetPixelChannels(image);
658  continue;
659  }
660  for (i=0; i < (ssize_t) GetPixelChannels(source_image); i++)
661  {
662  PixelChannel channel = GetPixelChannelChannel(source_image,i);
663  PixelTrait source_traits = GetPixelChannelTraits(source_image,
664  channel);
665  PixelTrait traits = GetPixelChannelTraits(image,channel);
666  if ((source_traits == UndefinedPixelTrait) ||
667  (traits == UndefinedPixelTrait))
668  continue;
669  SetPixelChannel(image,channel,p[i],q);
670  }
671  p+=GetPixelChannels(source_image);
672  q+=GetPixelChannels(image);
673  }
674  sync=SyncCacheViewAuthenticPixels(image_view,exception);
675  if (sync == MagickFalse)
676  status=MagickFalse;
677  if (image->progress_monitor != (MagickProgressMonitor) NULL)
678  {
680  proceed;
681 
683  y,image->rows);
684  if (proceed == MagickFalse)
685  status=MagickFalse;
686  }
687  }
688  source_view=DestroyCacheView(source_view);
689  image_view=DestroyCacheView(image_view);
690  source_image=DestroyImage(source_image);
691  return(status);
692  }
694  {
695  if ((x_offset < 0) || (y_offset < 0))
696  break;
697  if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
698  break;
699  if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
700  break;
701  status=MagickTrue;
702  source_view=AcquireVirtualCacheView(source_image,exception);
703  image_view=AcquireAuthenticCacheView(image,exception);
704 #if defined(MAGICKCORE_OPENMP_SUPPORT)
705  #pragma omp parallel for schedule(static) shared(status) \
706  magick_number_threads(source_image,image,source_image->rows,1)
707 #endif
708  for (y=0; y < (ssize_t) source_image->rows; y++)
709  {
711  sync;
712 
713  register const Quantum
714  *p;
715 
716  register Quantum
717  *q;
718 
719  register ssize_t
720  x;
721 
722  if (status == MagickFalse)
723  continue;
724  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
725  exception);
726  q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
727  source_image->columns,1,exception);
728  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
729  {
730  status=MagickFalse;
731  continue;
732  }
733  for (x=0; x < (ssize_t) source_image->columns; x++)
734  {
735  if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
736  {
737  p+=GetPixelChannels(source_image);
738  q+=GetPixelChannels(image);
739  continue;
740  }
741  SetPixelAlpha(image,clamp != MagickFalse ?
742  ClampPixel(GetPixelIntensity(source_image,p)) :
743  ClampToQuantum(GetPixelIntensity(source_image,p)),q);
744  p+=GetPixelChannels(source_image);
745  q+=GetPixelChannels(image);
746  }
747  sync=SyncCacheViewAuthenticPixels(image_view,exception);
748  if (sync == MagickFalse)
749  status=MagickFalse;
750  if (image->progress_monitor != (MagickProgressMonitor) NULL)
751  {
753  proceed;
754 
756  y,image->rows);
757  if (proceed == MagickFalse)
758  status=MagickFalse;
759  }
760  }
761  source_view=DestroyCacheView(source_view);
762  image_view=DestroyCacheView(image_view);
763  source_image=DestroyImage(source_image);
764  return(status);
765  }
768  {
769  /*
770  Modify canvas outside the overlaid region and require an alpha
771  channel to exist, to add transparency.
772  */
773  if (image->alpha_trait == UndefinedPixelTrait)
774  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
775  break;
776  }
777  case BlurCompositeOp:
778  {
779  CacheView
780  *canvas_view;
781 
783  angle_range,
784  angle_start,
785  height,
786  width;
787 
788  PixelInfo
789  pixel;
790 
792  *resample_filter;
793 
795  blur;
796 
797  /*
798  Blur Image by resampling.
799 
800  Blur Image dictated by an overlay gradient map: X = red_channel;
801  Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
802  */
803  canvas_image=CloneImage(image,0,0,MagickTrue,
804  exception);
805  if (canvas_image == (Image *) NULL)
806  {
807  source_image=DestroyImage(source_image);
808  return(MagickFalse);
809  }
810  /*
811  Gather the maximum blur sigma values from user.
812  */
813  flags=NoValue;
814  value=GetImageArtifact(image,"compose:args");
815  if (value != (const char *) NULL)
816  flags=ParseGeometry(value,&geometry_info);
817  if ((flags & WidthValue) == 0)
818  {
820  "InvalidSetting","'%s' '%s'","compose:args",value);
821  source_image=DestroyImage(source_image);
822  canvas_image=DestroyImage(canvas_image);
823  return(MagickFalse);
824  }
825  /*
826  Users input sigma now needs to be converted to the EWA ellipse size.
827  The filter defaults to a sigma of 0.5 so to make this match the
828  users input the ellipse size needs to be doubled.
829  */
830  width=height=geometry_info.rho*2.0;
831  if ((flags & HeightValue) != 0 )
832  height=geometry_info.sigma*2.0;
833  /*
834  Default the unrotated ellipse width and height axis vectors.
835  */
836  blur.x1=width;
837  blur.x2=0.0;
838  blur.y1=0.0;
839  blur.y2=height;
840  /* rotate vectors if a rotation angle is given */
841  if ((flags & XValue) != 0 )
842  {
844  angle;
845 
846  angle=DegreesToRadians(geometry_info.xi);
847  blur.x1=width*cos(angle);
848  blur.x2=width*sin(angle);
849  blur.y1=(-height*sin(angle));
850  blur.y2=height*cos(angle);
851  }
852  /* Otherwise lets set a angle range and calculate in the loop */
853  angle_start=0.0;
854  angle_range=0.0;
855  if ((flags & YValue) != 0 )
856  {
857  angle_start=DegreesToRadians(geometry_info.xi);
858  angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
859  }
860  /*
861  Set up a gaussian cylindrical filter for EWA Bluring.
862 
863  As the minimum ellipse radius of support*1.0 the EWA algorithm
864  can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
865  This means that even 'No Blur' will be still a little blurry!
866 
867  The solution (as well as the problem of preventing any user
868  expert filter settings, is to set our own user settings, then
869  restore them afterwards.
870  */
871  resample_filter=AcquireResampleFilter(image,exception);
872  SetResampleFilter(resample_filter,GaussianFilter);
873 
874  /* do the variable blurring of each pixel in image */
875  GetPixelInfo(image,&pixel);
876  source_view=AcquireVirtualCacheView(source_image,exception);
877  canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
878  for (y=0; y < (ssize_t) source_image->rows; y++)
879  {
881  sync;
882 
883  register const Quantum
884  *magick_restrict p;
885 
886  register Quantum
887  *magick_restrict q;
888 
889  register ssize_t
890  x;
891 
892  if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
893  continue;
894  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
895  exception);
896  q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
897  exception);
898  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
899  break;
900  for (x=0; x < (ssize_t) source_image->columns; x++)
901  {
902  if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
903  {
904  p+=GetPixelChannels(source_image);
905  continue;
906  }
907  if (fabs((double) angle_range) > MagickEpsilon)
908  {
910  angle;
911 
912  angle=angle_start+angle_range*QuantumScale*
913  GetPixelBlue(source_image,p);
914  blur.x1=width*cos(angle);
915  blur.x2=width*sin(angle);
916  blur.y1=(-height*sin(angle));
917  blur.y2=height*cos(angle);
918  }
919 #if 0
920  if ( x == 10 && y == 60 ) {
921  (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
922  blur.x2,blur.y1, blur.y2);
923  (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
925 #endif
926  ScaleResampleFilter(resample_filter,
927  blur.x1*QuantumScale*GetPixelRed(source_image,p),
928  blur.y1*QuantumScale*GetPixelGreen(source_image,p),
929  blur.x2*QuantumScale*GetPixelRed(source_image,p),
930  blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
931  (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
932  (double) y_offset+y,&pixel,exception);
933  SetPixelViaPixelInfo(canvas_image,&pixel,q);
934  p+=GetPixelChannels(source_image);
935  q+=GetPixelChannels(canvas_image);
936  }
937  sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
938  if (sync == MagickFalse)
939  break;
940  }
941  resample_filter=DestroyResampleFilter(resample_filter);
942  source_view=DestroyCacheView(source_view);
943  canvas_view=DestroyCacheView(canvas_view);
944  source_image=DestroyImage(source_image);
945  source_image=canvas_image;
946  break;
947  }
948  case DisplaceCompositeOp:
949  case DistortCompositeOp:
950  {
951  CacheView
952  *canvas_view;
953 
955  horizontal_scale,
956  vertical_scale;
957 
958  PixelInfo
959  pixel;
960 
961  PointInfo
962  center,
963  offset;
964 
965  /*
966  Displace/Distort based on overlay gradient map:
967  X = red_channel; Y = green_channel;
968  compose:args = x_scale[,y_scale[,center.x,center.y]]
969  */
970  canvas_image=CloneImage(image,0,0,MagickTrue,
971  exception);
972  if (canvas_image == (Image *) NULL)
973  {
974  source_image=DestroyImage(source_image);
975  return(MagickFalse);
976  }
977  SetGeometryInfo(&geometry_info);
978  flags=NoValue;
979  value=GetImageArtifact(image,"compose:args");
980  if (value != (char *) NULL)
981  flags=ParseGeometry(value,&geometry_info);
982  if ((flags & (WidthValue | HeightValue)) == 0 )
983  {
984  if ((flags & AspectValue) == 0)
985  {
986  horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
987  vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
988  }
989  else
990  {
991  horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
992  vertical_scale=(MagickRealType) (image->rows-1)/2.0;
993  }
994  }
995  else
996  {
997  horizontal_scale=geometry_info.rho;
998  vertical_scale=geometry_info.sigma;
999  if ((flags & PercentValue) != 0)
1000  {
1001  if ((flags & AspectValue) == 0)
1002  {
1003  horizontal_scale*=(source_image->columns-1)/200.0;
1004  vertical_scale*=(source_image->rows-1)/200.0;
1005  }
1006  else
1007  {
1008  horizontal_scale*=(image->columns-1)/200.0;
1009  vertical_scale*=(image->rows-1)/200.0;
1010  }
1011  }
1012  if ((flags & HeightValue) == 0)
1013  vertical_scale=horizontal_scale;
1014  }
1015  /*
1016  Determine fixed center point for absolute distortion map
1017  Absolute distort ==
1018  Displace offset relative to a fixed absolute point
1019  Select that point according to +X+Y user inputs.
1020  default = center of overlay image
1021  arg flag '!' = locations/percentage relative to background image
1022  */
1023  center.x=(MagickRealType) x_offset;
1024  center.y=(MagickRealType) y_offset;
1025  if (compose == DistortCompositeOp)
1026  {
1027  if ((flags & XValue) == 0)
1028  if ((flags & AspectValue) != 0)
1029  center.x=(MagickRealType) ((image->columns-1)/2.0);
1030  else
1031  center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1032  2.0);
1033  else
1034  if ((flags & AspectValue) != 0)
1035  center.x=geometry_info.xi;
1036  else
1037  center.x=(MagickRealType) (x_offset+geometry_info.xi);
1038  if ((flags & YValue) == 0)
1039  if ((flags & AspectValue) != 0)
1040  center.y=(MagickRealType) ((image->rows-1)/2.0);
1041  else
1042  center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1043  else
1044  if ((flags & AspectValue) != 0)
1045  center.y=geometry_info.psi;
1046  else
1047  center.y=(MagickRealType) (y_offset+geometry_info.psi);
1048  }
1049  /*
1050  Shift the pixel offset point as defined by the provided,
1051  displacement/distortion map. -- Like a lens...
1052  */
1053  GetPixelInfo(image,&pixel);
1054  image_view=AcquireVirtualCacheView(image,exception);
1055  source_view=AcquireVirtualCacheView(source_image,exception);
1056  canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1057  for (y=0; y < (ssize_t) source_image->rows; y++)
1058  {
1060  sync;
1061 
1062  register const Quantum
1063  *magick_restrict p;
1064 
1065  register Quantum
1066  *magick_restrict q;
1067 
1068  register ssize_t
1069  x;
1070 
1071  if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1072  continue;
1073  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1074  exception);
1075  q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1076  exception);
1077  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1078  break;
1079  for (x=0; x < (ssize_t) source_image->columns; x++)
1080  {
1081  if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1082  {
1083  p+=GetPixelChannels(source_image);
1084  continue;
1085  }
1086  /*
1087  Displace the offset.
1088  */
1089  offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1090  (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1091  QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1092  x : 0);
1093  offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1094  (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1095  QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1096  y : 0);
1097  status=InterpolatePixelInfo(image,image_view,
1098  UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1099  &pixel,exception);
1100  if (status == MagickFalse)
1101  break;
1102  /*
1103  Mask with the 'invalid pixel mask' in alpha channel.
1104  */
1106  (QuantumScale*GetPixelAlpha(source_image,p));
1107  SetPixelViaPixelInfo(canvas_image,&pixel,q);
1108  p+=GetPixelChannels(source_image);
1109  q+=GetPixelChannels(canvas_image);
1110  }
1111  if (x < (ssize_t) source_image->columns)
1112  break;
1113  sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1114  if (sync == MagickFalse)
1115  break;
1116  }
1117  canvas_view=DestroyCacheView(canvas_view);
1118  source_view=DestroyCacheView(source_view);
1119  image_view=DestroyCacheView(image_view);
1120  source_image=DestroyImage(source_image);
1121  source_image=canvas_image;
1122  break;
1123  }
1124  case DissolveCompositeOp:
1125  {
1126  /*
1127  Geometry arguments to dissolve factors.
1128  */
1129  value=GetImageArtifact(image,"compose:args");
1130  if (value != (char *) NULL)
1131  {
1132  flags=ParseGeometry(value,&geometry_info);
1133  source_dissolve=geometry_info.rho/100.0;
1134  canvas_dissolve=1.0;
1135  if ((source_dissolve-MagickEpsilon) < 0.0)
1136  source_dissolve=0.0;
1137  if ((source_dissolve+MagickEpsilon) > 1.0)
1138  {
1139  canvas_dissolve=2.0-source_dissolve;
1140  source_dissolve=1.0;
1141  }
1142  if ((flags & SigmaValue) != 0)
1143  canvas_dissolve=geometry_info.sigma/100.0;
1144  if ((canvas_dissolve-MagickEpsilon) < 0.0)
1145  canvas_dissolve=0.0;
1146  }
1147  break;
1148  }
1149  case BlendCompositeOp:
1150  {
1151  value=GetImageArtifact(image,"compose:args");
1152  if (value != (char *) NULL)
1153  {
1154  flags=ParseGeometry(value,&geometry_info);
1155  source_dissolve=geometry_info.rho/100.0;
1156  canvas_dissolve=1.0-source_dissolve;
1157  if ((flags & SigmaValue) != 0)
1158  canvas_dissolve=geometry_info.sigma/100.0;
1159  }
1160  break;
1161  }
1163  {
1164  /*
1165  Just collect the values from "compose:args", setting.
1166  Unused values are set to zero automagically.
1167 
1168  Arguments are normally a comma separated list, so this probably should
1169  be changed to some 'general comma list' parser, (with a minimum
1170  number of values)
1171  */
1172  SetGeometryInfo(&geometry_info);
1173  value=GetImageArtifact(image,"compose:args");
1174  if (value != (char *) NULL)
1175  (void) ParseGeometry(value,&geometry_info);
1176  break;
1177  }
1178  case ModulateCompositeOp:
1179  {
1180  /*
1181  Determine the luma and chroma scale.
1182  */
1183  value=GetImageArtifact(image,"compose:args");
1184  if (value != (char *) NULL)
1185  {
1186  flags=ParseGeometry(value,&geometry_info);
1187  percent_luma=geometry_info.rho;
1188  if ((flags & SigmaValue) != 0)
1189  percent_chroma=geometry_info.sigma;
1190  }
1191  break;
1192  }
1193  case ThresholdCompositeOp:
1194  {
1195  /*
1196  Determine the amount and threshold.
1197  */
1198  value=GetImageArtifact(image,"compose:args");
1199  if (value != (char *) NULL)
1200  {
1201  flags=ParseGeometry(value,&geometry_info);
1202  amount=geometry_info.rho;
1203  threshold=geometry_info.sigma;
1204  if ((flags & SigmaValue) == 0)
1205  threshold=0.05f;
1206  }
1207  threshold*=QuantumRange;
1208  break;
1209  }
1210  default:
1211  break;
1212  }
1213  /*
1214  Composite image.
1215  */
1216  status=MagickTrue;
1217  progress=0;
1218  midpoint=((MagickRealType) QuantumRange+1.0)/2;
1219  source_view=AcquireVirtualCacheView(source_image,exception);
1220  image_view=AcquireAuthenticCacheView(image,exception);
1221 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1222  #pragma omp parallel for schedule(static) shared(progress,status) \
1223  magick_number_threads(source_image,image,image->rows,1)
1224 #endif
1225  for (y=0; y < (ssize_t) image->rows; y++)
1226  {
1227  const Quantum
1228  *pixels;
1229 
1231  blue,
1232  chroma,
1233  green,
1234  hue,
1235  luma,
1236  red;
1237 
1238  PixelInfo
1239  canvas_pixel,
1240  source_pixel;
1241 
1242  register const Quantum
1243  *magick_restrict p;
1244 
1245  register Quantum
1246  *magick_restrict q;
1247 
1248  register ssize_t
1249  x;
1250 
1251  if (status == MagickFalse)
1252  continue;
1253  if (clip_to_self != MagickFalse)
1254  {
1255  if (y < y_offset)
1256  continue;
1257  if ((y-y_offset) >= (ssize_t) source_image->rows)
1258  continue;
1259  }
1260  /*
1261  If pixels is NULL, y is outside overlay region.
1262  */
1263  pixels=(Quantum *) NULL;
1264  p=(Quantum *) NULL;
1265  if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1266  {
1267  p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1268  source_image->columns,1,exception);
1269  if (p == (const Quantum *) NULL)
1270  {
1271  status=MagickFalse;
1272  continue;
1273  }
1274  pixels=p;
1275  if (x_offset < 0)
1276  p-=x_offset*GetPixelChannels(source_image);
1277  }
1278  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1279  if (q == (Quantum *) NULL)
1280  {
1281  status=MagickFalse;
1282  continue;
1283  }
1284  hue=0.0;
1285  chroma=0.0;
1286  luma=0.0;
1287  GetPixelInfo(image,&canvas_pixel);
1288  GetPixelInfo(source_image,&source_pixel);
1289  for (x=0; x < (ssize_t) image->columns; x++)
1290  {
1291  double
1292  gamma;
1293 
1295  alpha,
1296  Da,
1297  Dc,
1298  Dca,
1299  DcaDa,
1300  Sa,
1301  SaSca,
1302  Sc,
1303  Sca;
1304 
1305  register ssize_t
1306  i;
1307 
1308  size_t
1309  channels;
1310 
1311  if (clip_to_self != MagickFalse)
1312  {
1313  if (x < x_offset)
1314  {
1315  q+=GetPixelChannels(image);
1316  continue;
1317  }
1318  if ((x-x_offset) >= (ssize_t) source_image->columns)
1319  break;
1320  }
1321  if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1322  ((x-x_offset) >= (ssize_t) source_image->columns))
1323  {
1324  Quantum
1325  source[MaxPixelChannels];
1326 
1327  /*
1328  Virtual composite:
1329  Sc: source color.
1330  Dc: canvas color.
1331  */
1332  (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
1333  exception);
1334  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1335  {
1337  pixel;
1338 
1339  PixelChannel channel = GetPixelChannelChannel(image,i);
1340  PixelTrait traits = GetPixelChannelTraits(image,channel);
1341  PixelTrait source_traits=GetPixelChannelTraits(source_image,
1342  channel);
1343  if ((traits == UndefinedPixelTrait) ||
1344  (source_traits == UndefinedPixelTrait))
1345  continue;
1346  switch (compose)
1347  {
1348  case AlphaCompositeOp:
1349  case ChangeMaskCompositeOp:
1350  case CopyAlphaCompositeOp:
1351  case DstAtopCompositeOp:
1352  case DstInCompositeOp:
1353  case InCompositeOp:
1354  case OutCompositeOp:
1355  case SrcInCompositeOp:
1356  case SrcOutCompositeOp:
1357  {
1358  if (channel == AlphaPixelChannel)
1360  else
1361  pixel=(MagickRealType) q[i];
1362  break;
1363  }
1364  case ClearCompositeOp:
1365  case CopyCompositeOp:
1366  case ReplaceCompositeOp:
1367  case SrcCompositeOp:
1368  {
1369  if (channel == AlphaPixelChannel)
1371  else
1372  pixel=0.0;
1373  break;
1374  }
1375  case BlendCompositeOp:
1376  case DissolveCompositeOp:
1377  {
1378  if (channel == AlphaPixelChannel)
1379  pixel=canvas_dissolve*GetPixelAlpha(source_image,source);
1380  else
1381  pixel=(MagickRealType) source[channel];
1382  break;
1383  }
1384  default:
1385  {
1386  pixel=(MagickRealType) source[channel];
1387  break;
1388  }
1389  }
1390  q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1391  ClampToQuantum(pixel);
1392  }
1393  q+=GetPixelChannels(image);
1394  continue;
1395  }
1396  /*
1397  Authentic composite:
1398  Sa: normalized source alpha.
1399  Da: normalized canvas alpha.
1400  */
1401  Sa=QuantumScale*GetPixelAlpha(source_image,p);
1402  Da=QuantumScale*GetPixelAlpha(image,q);
1403  switch (compose)
1404  {
1405  case BumpmapCompositeOp:
1406  {
1407  alpha=GetPixelIntensity(source_image,p)*Sa;
1408  break;
1409  }
1410  case ColorBurnCompositeOp:
1411  case ColorDodgeCompositeOp:
1412  case DarkenCompositeOp:
1413  case DifferenceCompositeOp:
1414  case DivideDstCompositeOp:
1415  case DivideSrcCompositeOp:
1416  case ExclusionCompositeOp:
1417  case HardLightCompositeOp:
1418  case HardMixCompositeOp:
1419  case LinearBurnCompositeOp:
1422  case LightenCompositeOp:
1424  case MinusDstCompositeOp:
1425  case MinusSrcCompositeOp:
1426  case MultiplyCompositeOp:
1427  case OverlayCompositeOp:
1429  case PinLightCompositeOp:
1430  case ScreenCompositeOp:
1431  case SoftLightCompositeOp:
1432  case VividLightCompositeOp:
1433  {
1434  alpha=RoundToUnity(Sa+Da-Sa*Da);
1435  break;
1436  }
1437  case DstAtopCompositeOp:
1438  case DstInCompositeOp:
1439  case InCompositeOp:
1440  case SrcInCompositeOp:
1441  {
1442  alpha=Sa*Da;
1443  break;
1444  }
1445  case DissolveCompositeOp:
1446  {
1447  alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
1448  canvas_dissolve*Da;
1449  break;
1450  }
1451  case DstOverCompositeOp:
1452  case OverCompositeOp:
1453  case SrcOverCompositeOp:
1454  {
1455  alpha=Sa+Da-Sa*Da;
1456  break;
1457  }
1458  case DstOutCompositeOp:
1459  {
1460  alpha=Da*(1.0-Sa);
1461  break;
1462  }
1463  case OutCompositeOp:
1464  case SrcOutCompositeOp:
1465  {
1466  alpha=Sa*(1.0-Da);
1467  break;
1468  }
1469  case BlendCompositeOp:
1470  case PlusCompositeOp:
1471  {
1472  alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
1473  break;
1474  }
1475  case XorCompositeOp:
1476  {
1477  alpha=Sa+Da-2.0*Sa*Da;
1478  break;
1479  }
1480  case ModulusAddCompositeOp:
1481  {
1482  if ((Sa+Da) <= 1.0)
1483  {
1484  alpha=(Sa+Da);
1485  break;
1486  }
1487  alpha=((Sa+Da)-1.0);
1488  break;
1489  }
1491  {
1492  if ((Sa-Da) >= 0.0)
1493  {
1494  alpha=(Sa-Da);
1495  break;
1496  }
1497  alpha=((Sa-Da)+1.0);
1498  break;
1499  }
1500  default:
1501  {
1502  alpha=1.0;
1503  break;
1504  }
1505  }
1506  switch (compose)
1507  {
1508  case ColorizeCompositeOp:
1509  case HueCompositeOp:
1510  case LuminizeCompositeOp:
1511  case ModulateCompositeOp:
1512  case SaturateCompositeOp:
1513  {
1514  GetPixelInfoPixel(source_image,p,&source_pixel);
1515  GetPixelInfoPixel(image,q,&canvas_pixel);
1516  break;
1517  }
1518  default:
1519  break;
1520  }
1521  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1522  {
1524  pixel,
1525  sans;
1526 
1527  PixelChannel channel = GetPixelChannelChannel(image,i);
1528  PixelTrait traits = GetPixelChannelTraits(image,channel);
1529  PixelTrait source_traits = GetPixelChannelTraits(source_image,channel);
1530  if (traits == UndefinedPixelTrait)
1531  continue;
1532  if ((channel == AlphaPixelChannel) &&
1533  ((traits & UpdatePixelTrait) != 0))
1534  {
1535  /*
1536  Set alpha channel.
1537  */
1538  switch (compose)
1539  {
1540  case AlphaCompositeOp:
1541  {
1542  pixel=QuantumRange*Sa;
1543  break;
1544  }
1545  case AtopCompositeOp:
1546  case CopyBlackCompositeOp:
1547  case CopyBlueCompositeOp:
1548  case CopyCyanCompositeOp:
1549  case CopyGreenCompositeOp:
1551  case CopyRedCompositeOp:
1552  case CopyYellowCompositeOp:
1553  case SrcAtopCompositeOp:
1554  case DstCompositeOp:
1555  case NoCompositeOp:
1556  {
1557  pixel=QuantumRange*Da;
1558  break;
1559  }
1560  case ChangeMaskCompositeOp:
1561  {
1563  equivalent;
1564 
1565  if (Da < 0.5)
1566  {
1568  break;
1569  }
1570  equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1571  if (equivalent != MagickFalse)
1573  else
1574  pixel=(MagickRealType) OpaqueAlpha;
1575  break;
1576  }
1577  case ClearCompositeOp:
1578  {
1580  break;
1581  }
1582  case ColorizeCompositeOp:
1583  case HueCompositeOp:
1584  case LuminizeCompositeOp:
1585  case SaturateCompositeOp:
1586  {
1587  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1588  {
1589  pixel=QuantumRange*Da;
1590  break;
1591  }
1592  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1593  {
1594  pixel=QuantumRange*Sa;
1595  break;
1596  }
1597  if (Sa < Da)
1598  {
1599  pixel=QuantumRange*Da;
1600  break;
1601  }
1602  pixel=QuantumRange*Sa;
1603  break;
1604  }
1605  case CopyAlphaCompositeOp:
1606  {
1607  if (source_image->alpha_trait == UndefinedPixelTrait)
1608  pixel=GetPixelIntensity(source_image,p);
1609  else
1610  pixel=QuantumRange*Sa;
1611  break;
1612  }
1613  case CopyCompositeOp:
1614  case DisplaceCompositeOp:
1615  case DistortCompositeOp:
1616  case DstAtopCompositeOp:
1617  case ReplaceCompositeOp:
1618  case SrcCompositeOp:
1619  {
1620  pixel=QuantumRange*Sa;
1621  break;
1622  }
1624  {
1625  pixel=Sa*GetPixelIntensity(source_image,p) <
1626  Da*GetPixelIntensity(image,q) ? Sa : Da;
1627  break;
1628  }
1629  case DifferenceCompositeOp:
1630  {
1631  pixel=QuantumRange*fabs(Sa-Da);
1632  break;
1633  }
1635  {
1636  pixel=Sa*GetPixelIntensity(source_image,p) >
1637  Da*GetPixelIntensity(image,q) ? Sa : Da;
1638  break;
1639  }
1640  case ModulateCompositeOp:
1641  {
1642  pixel=QuantumRange*Da;
1643  break;
1644  }
1645  case MultiplyCompositeOp:
1646  {
1647  pixel=QuantumRange*Sa*Da;
1648  break;
1649  }
1650  case StereoCompositeOp:
1651  {
1652  pixel=QuantumRange*(Sa+Da)/2;
1653  break;
1654  }
1655  default:
1656  {
1657  pixel=QuantumRange*alpha;
1658  break;
1659  }
1660  }
1661  q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1662  ClampToQuantum(pixel);
1663  continue;
1664  }
1665  if (source_traits == UndefinedPixelTrait)
1666  continue;
1667  /*
1668  Sc: source color.
1669  Dc: canvas color.
1670  */
1671  Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1672  Dc=(MagickRealType) q[i];
1673  if ((traits & CopyPixelTrait) != 0)
1674  {
1675  /*
1676  Copy channel.
1677  */
1678  q[i]=ClampToQuantum(Dc);
1679  continue;
1680  }
1681  /*
1682  Porter-Duff compositions:
1683  Sca: source normalized color multiplied by alpha.
1684  Dca: normalized canvas color multiplied by alpha.
1685  */
1686  Sca=QuantumScale*Sa*Sc;
1687  Dca=QuantumScale*Da*Dc;
1688  SaSca=Sa*PerceptibleReciprocal(Sca);
1689  DcaDa=Dca*PerceptibleReciprocal(Da);
1690  switch (compose)
1691  {
1692  case DarkenCompositeOp:
1693  case LightenCompositeOp:
1695  {
1696  gamma=PerceptibleReciprocal(1.0-alpha);
1697  break;
1698  }
1699  default:
1700  {
1701  gamma=PerceptibleReciprocal(alpha);
1702  break;
1703  }
1704  }
1705  pixel=Dc;
1706  switch (compose)
1707  {
1708  case AlphaCompositeOp:
1709  {
1710  pixel=QuantumRange*Sa;
1711  break;
1712  }
1713  case AtopCompositeOp:
1714  case SrcAtopCompositeOp:
1715  {
1716  pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1717  break;
1718  }
1719  case BlendCompositeOp:
1720  {
1721  pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1722  break;
1723  }
1724  case BlurCompositeOp:
1725  case CopyCompositeOp:
1726  case ReplaceCompositeOp:
1727  case SrcCompositeOp:
1728  {
1729  pixel=QuantumRange*Sca;
1730  break;
1731  }
1732  case DisplaceCompositeOp:
1733  case DistortCompositeOp:
1734  {
1735  pixel=Sc;
1736  break;
1737  }
1738  case BumpmapCompositeOp:
1739  {
1740  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1741  {
1742  pixel=Dc;
1743  break;
1744  }
1745  pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1746  break;
1747  }
1748  case ChangeMaskCompositeOp:
1749  {
1750  pixel=Dc;
1751  break;
1752  }
1753  case ClearCompositeOp:
1754  {
1755  pixel=0.0;
1756  break;
1757  }
1758  case ColorBurnCompositeOp:
1759  {
1760  if ((Sca == 0.0) && (Dca == Da))
1761  {
1762  pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1763  break;
1764  }
1765  if (Sca == 0.0)
1766  {
1767  pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1768  break;
1769  }
1770  pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-DcaDa)*
1771  SaSca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1772  break;
1773  }
1774  case ColorDodgeCompositeOp:
1775  {
1776  if ((Sca*Da+Dca*Sa) >= Sa*Da)
1777  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1778  else
1779  pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(Sa-Sca)+
1780  Sca*(1.0-Da)+Dca*(1.0-Sa));
1781  break;
1782  }
1783  case ColorizeCompositeOp:
1784  {
1785  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1786  {
1787  pixel=Dc;
1788  break;
1789  }
1790  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1791  {
1792  pixel=Sc;
1793  break;
1794  }
1795  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1796  &sans,&sans,&luma);
1797  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1798  &hue,&chroma,&sans);
1799  HCLComposite(hue,chroma,luma,&red,&green,&blue);
1800  switch (channel)
1801  {
1802  case RedPixelChannel: pixel=red; break;
1803  case GreenPixelChannel: pixel=green; break;
1804  case BluePixelChannel: pixel=blue; break;
1805  default: pixel=Dc; break;
1806  }
1807  break;
1808  }
1809  case CopyAlphaCompositeOp:
1810  {
1811  pixel=Dc;
1812  break;
1813  }
1814  case CopyBlackCompositeOp:
1815  {
1816  if (channel == BlackPixelChannel)
1817  pixel=(MagickRealType) GetPixelBlack(source_image,p);
1818  break;
1819  }
1820  case CopyBlueCompositeOp:
1821  case CopyYellowCompositeOp:
1822  {
1823  if (channel == BluePixelChannel)
1824  pixel=(MagickRealType) GetPixelBlue(source_image,p);
1825  break;
1826  }
1827  case CopyGreenCompositeOp:
1829  {
1830  if (channel == GreenPixelChannel)
1831  pixel=(MagickRealType) GetPixelGreen(source_image,p);
1832  break;
1833  }
1834  case CopyRedCompositeOp:
1835  case CopyCyanCompositeOp:
1836  {
1837  if (channel == RedPixelChannel)
1838  pixel=(MagickRealType) GetPixelRed(source_image,p);
1839  break;
1840  }
1841  case DarkenCompositeOp:
1842  {
1843  /*
1844  Darken is equivalent to a 'Minimum' method
1845  OR a greyscale version of a binary 'Or'
1846  OR the 'Intersection' of pixel sets.
1847  */
1848  if ((Sca*Da) < (Dca*Sa))
1849  {
1850  pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1851  break;
1852  }
1853  pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1854  break;
1855  }
1857  {
1858  pixel=Sa*GetPixelIntensity(source_image,p) <
1859  Da*GetPixelIntensity(image,q) ? Sc : Dc;
1860  break;
1861  }
1862  case DifferenceCompositeOp:
1863  {
1864  pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1865  break;
1866  }
1867  case DissolveCompositeOp:
1868  {
1869  pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1870  canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1871  break;
1872  }
1873  case DivideDstCompositeOp:
1874  {
1875  if ((fabs((double) Sca) < MagickEpsilon) &&
1876  (fabs((double) Dca) < MagickEpsilon))
1877  {
1878  pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1879  break;
1880  }
1881  if (fabs((double) Dca) < MagickEpsilon)
1882  {
1883  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1884  break;
1885  }
1886  pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1887  break;
1888  }
1889  case DivideSrcCompositeOp:
1890  {
1891  if ((fabs((double) Dca) < MagickEpsilon) &&
1892  (fabs((double) Sca) < MagickEpsilon))
1893  {
1894  pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1895  break;
1896  }
1897  if (fabs((double) Sca) < MagickEpsilon)
1898  {
1899  pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1900  break;
1901  }
1902  pixel=QuantumRange*gamma*(Dca*Sa*SaSca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1903  break;
1904  }
1905  case DstAtopCompositeOp:
1906  {
1907  pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1908  break;
1909  }
1910  case DstCompositeOp:
1911  case NoCompositeOp:
1912  {
1913  pixel=QuantumRange*Dca;
1914  break;
1915  }
1916  case DstInCompositeOp:
1917  {
1918  pixel=QuantumRange*(Dca*Sa);
1919  break;
1920  }
1921  case DstOutCompositeOp:
1922  {
1923  pixel=QuantumRange*(Dca*(1.0-Sa));
1924  break;
1925  }
1926  case DstOverCompositeOp:
1927  {
1928  pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1929  break;
1930  }
1931  case ExclusionCompositeOp:
1932  {
1933  pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1934  Dca*(1.0-Sa));
1935  break;
1936  }
1937  case HardLightCompositeOp:
1938  {
1939  if ((2.0*Sca) < Sa)
1940  {
1941  pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1942  Sa));
1943  break;
1944  }
1945  pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1946  Dca*(1.0-Sa));
1947  break;
1948  }
1949  case HardMixCompositeOp:
1950  {
1951  pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1952  break;
1953  }
1954  case HueCompositeOp:
1955  {
1956  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1957  {
1958  pixel=Dc;
1959  break;
1960  }
1961  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1962  {
1963  pixel=Sc;
1964  break;
1965  }
1966  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1967  &hue,&chroma,&luma);
1968  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1969  &hue,&sans,&sans);
1970  HCLComposite(hue,chroma,luma,&red,&green,&blue);
1971  switch (channel)
1972  {
1973  case RedPixelChannel: pixel=red; break;
1974  case GreenPixelChannel: pixel=green; break;
1975  case BluePixelChannel: pixel=blue; break;
1976  default: pixel=Dc; break;
1977  }
1978  break;
1979  }
1980  case InCompositeOp:
1981  case SrcInCompositeOp:
1982  {
1983  pixel=QuantumRange*(Sca*Da);
1984  break;
1985  }
1986  case LinearBurnCompositeOp:
1987  {
1988  /*
1989  LinearBurn: as defined by Abode Photoshop, according to
1990  http://www.simplefilter.de/en/basics/mixmods.html is:
1991 
1992  f(Sc,Dc) = Sc + Dc - 1
1993  */
1994  pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1995  break;
1996  }
1998  {
1999  pixel=gamma*(Sa*Sc+Da*Dc);
2000  break;
2001  }
2003  {
2004  /*
2005  LinearLight: as defined by Abode Photoshop, according to
2006  http://www.simplefilter.de/en/basics/mixmods.html is:
2007 
2008  f(Sc,Dc) = Dc + 2*Sc - 1
2009  */
2010  pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
2011  break;
2012  }
2013  case LightenCompositeOp:
2014  {
2015  if ((Sca*Da) > (Dca*Sa))
2016  {
2017  pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2018  break;
2019  }
2020  pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2021  break;
2022  }
2024  {
2025  /*
2026  Lighten is equivalent to a 'Maximum' method
2027  OR a greyscale version of a binary 'And'
2028  OR the 'Union' of pixel sets.
2029  */
2030  pixel=Sa*GetPixelIntensity(source_image,p) >
2031  Da*GetPixelIntensity(image,q) ? Sc : Dc;
2032  break;
2033  }
2034  case LuminizeCompositeOp:
2035  {
2036  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2037  {
2038  pixel=Dc;
2039  break;
2040  }
2041  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2042  {
2043  pixel=Sc;
2044  break;
2045  }
2046  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2047  &hue,&chroma,&luma);
2048  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2049  &sans,&sans,&luma);
2050  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2051  switch (channel)
2052  {
2053  case RedPixelChannel: pixel=red; break;
2054  case GreenPixelChannel: pixel=green; break;
2055  case BluePixelChannel: pixel=blue; break;
2056  default: pixel=Dc; break;
2057  }
2058  break;
2059  }
2061  {
2062  /*
2063  'Mathematics' a free form user control mathematical composition
2064  is defined as...
2065 
2066  f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2067 
2068  Where the arguments A,B,C,D are (currently) passed to composite
2069  as a command separated 'geometry' string in "compose:args" image
2070  artifact.
2071 
2072  A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2073 
2074  Applying the SVG transparency formula (see above), we get...
2075 
2076  Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2077 
2078  Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2079  Dca*(1.0-Sa)
2080  */
2081  pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
2082  geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2083  geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2084  break;
2085  }
2086  case MinusDstCompositeOp:
2087  {
2088  pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2089  break;
2090  }
2091  case MinusSrcCompositeOp:
2092  {
2093  /*
2094  Minus source from canvas.
2095 
2096  f(Sc,Dc) = Sc - Dc
2097  */
2098  pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2099  break;
2100  }
2101  case ModulateCompositeOp:
2102  {
2103  ssize_t
2104  offset;
2105 
2106  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2107  {
2108  pixel=Dc;
2109  break;
2110  }
2111  offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2112  if (offset == 0)
2113  {
2114  pixel=Dc;
2115  break;
2116  }
2117  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2118  &hue,&chroma,&luma);
2119  luma+=(0.01*percent_luma*offset)/midpoint;
2120  chroma*=0.01*percent_chroma;
2121  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2122  switch (channel)
2123  {
2124  case RedPixelChannel: pixel=red; break;
2125  case GreenPixelChannel: pixel=green; break;
2126  case BluePixelChannel: pixel=blue; break;
2127  default: pixel=Dc; break;
2128  }
2129  break;
2130  }
2131  case ModulusAddCompositeOp:
2132  {
2133  if ((Sca+Dca) <= 1.0)
2134  {
2135  pixel=QuantumRange*(Sca+Dca);
2136  break;
2137  }
2138  pixel=QuantumRange*((Sca+Dca)-1.0);
2139  break;
2140  }
2142  {
2143  if ((Sca-Dca) >= 0.0)
2144  {
2145  pixel=QuantumRange*(Sca-Dca);
2146  break;
2147  }
2148  pixel=QuantumRange*((Sca-Dca)+1.0);
2149  break;
2150  }
2151  case MultiplyCompositeOp:
2152  {
2153  pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2154  break;
2155  }
2156  case OutCompositeOp:
2157  case SrcOutCompositeOp:
2158  {
2159  pixel=QuantumRange*(Sca*(1.0-Da));
2160  break;
2161  }
2162  case OverCompositeOp:
2163  case SrcOverCompositeOp:
2164  {
2165  pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
2166  break;
2167  }
2168  case OverlayCompositeOp:
2169  {
2170  if ((2.0*Dca) < Da)
2171  {
2172  pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-
2173  Da));
2174  break;
2175  }
2176  pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2177  Sca*(1.0-Da));
2178  break;
2179  }
2181  {
2182  /*
2183  PegTop: A Soft-Light alternative: A continuous version of the
2184  Softlight function, producing very similar results.
2185 
2186  f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2187 
2188  http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2189  */
2190  if (fabs((double) Da) < MagickEpsilon)
2191  {
2192  pixel=QuantumRange*gamma*(Sca);
2193  break;
2194  }
2195  pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2196  Da)+Dca*(1.0-Sa));
2197  break;
2198  }
2199  case PinLightCompositeOp:
2200  {
2201  /*
2202  PinLight: A Photoshop 7 composition method
2203  http://www.simplefilter.de/en/basics/mixmods.html
2204 
2205  f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2206  */
2207  if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2208  {
2209  pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2210  break;
2211  }
2212  if ((Dca*Sa) > (2.0*Sca*Da))
2213  {
2214  pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2215  break;
2216  }
2217  pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2218  break;
2219  }
2220  case PlusCompositeOp:
2221  {
2222  pixel=QuantumRange*(Sca+Dca);
2223  break;
2224  }
2225  case SaturateCompositeOp:
2226  {
2227  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2228  {
2229  pixel=Dc;
2230  break;
2231  }
2232  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2233  {
2234  pixel=Sc;
2235  break;
2236  }
2237  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2238  &hue,&chroma,&luma);
2239  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2240  &sans,&chroma,&sans);
2241  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2242  switch (channel)
2243  {
2244  case RedPixelChannel: pixel=red; break;
2245  case GreenPixelChannel: pixel=green; break;
2246  case BluePixelChannel: pixel=blue; break;
2247  default: pixel=Dc; break;
2248  }
2249  break;
2250  }
2251  case ScreenCompositeOp:
2252  {
2253  /*
2254  Screen: a negated multiply:
2255 
2256  f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2257  */
2258  pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2259  break;
2260  }
2261  case SoftLightCompositeOp:
2262  {
2263  if ((2.0*Sca) < Sa)
2264  {
2265  pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-DcaDa))+
2266  Sca*(1.0-Da)+Dca*(1.0-Sa));
2267  break;
2268  }
2269  if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2270  {
2271  pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*DcaDa*
2272  (4.0*DcaDa+1.0)*(DcaDa-1.0)+7.0*DcaDa)+Sca*(1.0-Da)+
2273  Dca*(1.0-Sa));
2274  break;
2275  }
2276  pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow(DcaDa,0.5)-
2277  DcaDa)+Sca*(1.0-Da)+Dca*(1.0-Sa));
2278  break;
2279  }
2280  case StereoCompositeOp:
2281  {
2282  if (channel == RedPixelChannel)
2283  pixel=(MagickRealType) GetPixelRed(source_image,p);
2284  break;
2285  }
2286  case ThresholdCompositeOp:
2287  {
2289  delta;
2290 
2291  delta=Sc-Dc;
2292  if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2293  {
2294  pixel=gamma*Dc;
2295  break;
2296  }
2297  pixel=gamma*(Dc+delta*amount);
2298  break;
2299  }
2300  case VividLightCompositeOp:
2301  {
2302  /*
2303  VividLight: A Photoshop 7 composition method. See
2304  http://www.simplefilter.de/en/basics/mixmods.html.
2305 
2306  f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2307  */
2308  if ((fabs((double) Sa) < MagickEpsilon) ||
2309  (fabs((double) (Sca-Sa)) < MagickEpsilon))
2310  {
2311  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2312  break;
2313  }
2314  if ((2.0*Sca) <= Sa)
2315  {
2316  pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)*
2317  PerceptibleReciprocal(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2318  break;
2319  }
2320  pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(2.0*
2321  (Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2322  break;
2323  }
2324  case XorCompositeOp:
2325  {
2326  pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2327  break;
2328  }
2329  default:
2330  {
2331  pixel=Sc;
2332  break;
2333  }
2334  }
2335  q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2336  }
2337  p+=GetPixelChannels(source_image);
2338  channels=GetPixelChannels(source_image);
2339  if (p >= (pixels+channels*source_image->columns))
2340  p=pixels;
2341  q+=GetPixelChannels(image);
2342  }
2343  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2344  status=MagickFalse;
2345  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2346  {
2348  proceed;
2349 
2350 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2351  #pragma omp atomic
2352 #endif
2353  progress++;
2354  proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
2355  if (proceed == MagickFalse)
2356  status=MagickFalse;
2357  }
2358  }
2359  source_view=DestroyCacheView(source_view);
2360  image_view=DestroyCacheView(image_view);
2361  if (canvas_image != (Image * ) NULL)
2362  canvas_image=DestroyImage(canvas_image);
2363  else
2364  source_image=DestroyImage(source_image);
2365  return(status);
2366 }
2367 
2368 /*
2369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2370 % %
2371 % %
2372 % %
2373 % T e x t u r e I m a g e %
2374 % %
2375 % %
2376 % %
2377 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2378 %
2379 % TextureImage() repeatedly tiles the texture image across and down the image
2380 % canvas.
2381 %
2382 % The format of the TextureImage method is:
2383 %
2384 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2385 % ExceptionInfo *exception)
2386 %
2387 % A description of each parameter follows:
2388 %
2389 % o image: the image.
2390 %
2391 % o texture_image: This image is the texture to layer on the background.
2392 %
2393 */
2395  ExceptionInfo *exception)
2396 {
2397 #define TextureImageTag "Texture/Image"
2398 
2399  CacheView
2400  *image_view,
2401  *texture_view;
2402 
2403  Image
2404  *texture_image;
2405 
2407  status;
2408 
2409  ssize_t
2410  y;
2411 
2412  assert(image != (Image *) NULL);
2413  if (image->debug != MagickFalse)
2414  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2415  assert(image->signature == MagickCoreSignature);
2416  if (texture == (const Image *) NULL)
2417  return(MagickFalse);
2418  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2419  return(MagickFalse);
2420  texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2421  if (texture_image == (const Image *) NULL)
2422  return(MagickFalse);
2423  (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2425  exception);
2426  status=MagickTrue;
2427  if ((image->compose != CopyCompositeOp) &&
2428  ((image->compose != OverCompositeOp) ||
2429  (image->alpha_trait != UndefinedPixelTrait) ||
2430  (texture_image->alpha_trait != UndefinedPixelTrait)))
2431  {
2432  /*
2433  Tile texture onto the image background.
2434  */
2435  for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2436  {
2437  register ssize_t
2438  x;
2439 
2440  if (status == MagickFalse)
2441  continue;
2442  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2443  {
2445  thread_status;
2446 
2447  thread_status=CompositeImage(image,texture_image,image->compose,
2448  MagickTrue,x+texture_image->tile_offset.x,y+
2449  texture_image->tile_offset.y,exception);
2450  if (thread_status == MagickFalse)
2451  {
2452  status=thread_status;
2453  break;
2454  }
2455  }
2456  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2457  {
2459  proceed;
2460 
2462  image->rows);
2463  if (proceed == MagickFalse)
2464  status=MagickFalse;
2465  }
2466  }
2468  image->rows,image->rows);
2469  texture_image=DestroyImage(texture_image);
2470  return(status);
2471  }
2472  /*
2473  Tile texture onto the image background (optimized).
2474  */
2475  status=MagickTrue;
2476  texture_view=AcquireVirtualCacheView(texture_image,exception);
2477  image_view=AcquireAuthenticCacheView(image,exception);
2478 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2479  #pragma omp parallel for schedule(static) shared(status) \
2480  magick_number_threads(texture_image,image,image->rows,1)
2481 #endif
2482  for (y=0; y < (ssize_t) image->rows; y++)
2483  {
2485  sync;
2486 
2487  register const Quantum
2488  *p,
2489  *pixels;
2490 
2491  register ssize_t
2492  x;
2493 
2494  register Quantum
2495  *q;
2496 
2497  size_t
2498  width;
2499 
2500  if (status == MagickFalse)
2501  continue;
2502  pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2503  (y+texture_image->tile_offset.y) % texture_image->rows,
2504  texture_image->columns,1,exception);
2505  q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2506  if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2507  {
2508  status=MagickFalse;
2509  continue;
2510  }
2511  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2512  {
2513  register ssize_t
2514  j;
2515 
2516  p=pixels;
2517  width=texture_image->columns;
2518  if ((x+(ssize_t) width) > (ssize_t) image->columns)
2519  width=image->columns-x;
2520  for (j=0; j < (ssize_t) width; j++)
2521  {
2522  register ssize_t
2523  i;
2524 
2525  for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2526  {
2527  PixelChannel channel = GetPixelChannelChannel(texture_image,i);
2528  PixelTrait traits = GetPixelChannelTraits(image,channel);
2529  PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2530  channel);
2531  if ((traits == UndefinedPixelTrait) ||
2532  (texture_traits == UndefinedPixelTrait))
2533  continue;
2534  SetPixelChannel(image,channel,p[i],q);
2535  }
2536  p+=GetPixelChannels(texture_image);
2537  q+=GetPixelChannels(image);
2538  }
2539  }
2540  sync=SyncCacheViewAuthenticPixels(image_view,exception);
2541  if (sync == MagickFalse)
2542  status=MagickFalse;
2543  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2544  {
2546  proceed;
2547 
2549  image->rows);
2550  if (proceed == MagickFalse)
2551  status=MagickFalse;
2552  }
2553  }
2554  texture_view=DestroyCacheView(texture_view);
2555  image_view=DestroyCacheView(image_view);
2556  texture_image=DestroyImage(texture_image);
2557  return(status);
2558 }
double psi
Definition: geometry.h:106
size_t rows
Definition: image.h:172
#define magick_restrict
Definition: MagickCore.h:41
MagickExport MagickBooleanType TextureImage(Image *image, const Image *texture, ExceptionInfo *exception)
Definition: composite.c:2394
MagickDoubleType MagickRealType
Definition: magick-type.h:120
MagickExport CacheView * DestroyCacheView(CacheView *cache_view)
Definition: cache-view.c:252
#define TransparentAlpha
Definition: image.h:26
double x2
Definition: image.h:107
static void HCLComposite(const MagickRealType hue, const MagickRealType chroma, const MagickRealType luma, MagickRealType *red, MagickRealType *green, MagickRealType *blue)
Definition: composite.c:181
MagickProgressMonitor progress_monitor
Definition: image.h:303
static Quantum GetPixelAlpha(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport MagickBooleanType TransformImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1387
static Quantum GetPixelRed(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport MagickBooleanType ResamplePixelColor(ResampleFilter *resample_filter, const double u0, const double v0, PixelInfo *pixel, ExceptionInfo *exception)
Definition: resample.c:315
double rho
Definition: geometry.h:106
MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
Definition: geometry.c:1717
#define OpaqueAlpha
Definition: image.h:25
static Quantum GetPixelChannel(const Image *magick_restrict image, const PixelChannel channel, const Quantum *magick_restrict pixel)
MagickExport const char * GetImageArtifact(const Image *image, const char *artifact)
Definition: artifact.c:273
MagickRealType red
Definition: pixel.h:190
static PixelTrait GetPixelChannelTraits(const Image *magick_restrict image, const PixelChannel channel)
MagickExport MagickBooleanType GetOneVirtualPixel(const Image *image, const ssize_t x, const ssize_t y, Quantum *pixel, ExceptionInfo *exception)
Definition: cache.c:1970
MagickExport MagickBooleanType InterpolatePixelInfo(const Image *image, const CacheView_ *image_view, const PixelInterpolateMethod method, const double x, const double y, PixelInfo *pixel, ExceptionInfo *exception)
Definition: pixel.c:5481
static void SetPixelViaPixelInfo(const Image *magick_restrict image, const PixelInfo *magick_restrict pixel_info, Quantum *magick_restrict pixel)
static MagickBooleanType IsGrayColorspace(const ColorspaceType colorspace)
static Quantum GetPixelReadMask(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport const Quantum * GetCacheViewVirtualPixels(const CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:651
MagickRealType alpha
Definition: pixel.h:190
#define MagickEpsilon
Definition: magick-type.h:110
double sigma
Definition: geometry.h:106
MagickExport MagickBooleanType CompositeImage(Image *image, const Image *composite, const CompositeOperator compose, const MagickBooleanType clip_to_self, const ssize_t x_offset, const ssize_t y_offset, ExceptionInfo *exception)
Definition: composite.c:528
RectangleInfo tile_offset
Definition: image.h:261
MagickExport ResampleFilter * AcquireResampleFilter(const Image *image, ExceptionInfo *exception)
Definition: resample.c:208
Definition: log.h:52
ssize_t MagickOffsetType
Definition: magick-type.h:129
MagickExport void GetPixelInfo(const Image *image, PixelInfo *pixel)
Definition: pixel.c:2170
Definition: image.h:151
double x
Definition: geometry.h:123
#define MagickCoreSignature
MagickExport Quantum * GetCacheViewAuthenticPixels(CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:299
static Quantum ClampPixel(const MagickRealType pixel)
MagickExport MagickBooleanType SetImageAlphaChannel(Image *image, const AlphaChannelOption alpha_type, ExceptionInfo *exception)
Definition: channel.c:974
MagickBooleanType
Definition: magick-type.h:158
unsigned int MagickStatusType
Definition: magick-type.h:121
static double PerceptibleReciprocal(const double x)
static void CompositeHCL(const MagickRealType red, const MagickRealType green, const MagickRealType blue, MagickRealType *hue, MagickRealType *chroma, MagickRealType *luma)
Definition: composite.c:247
double x1
Definition: image.h:107
static double DegreesToRadians(const double degrees)
Definition: image-private.h:56
double y
Definition: geometry.h:123
static Quantum GetPixelGreen(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport MagickBooleanType IsStringTrue(const char *value)
Definition: string.c:1425
static void GetPixelInfoPixel(const Image *magick_restrict image, const Quantum *magick_restrict pixel, PixelInfo *magick_restrict pixel_info)
PixelTrait alpha_trait
Definition: image.h:280
MagickRealType blue
Definition: pixel.h:190
static Quantum GetPixelBlack(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
MagickExport Quantum * QueueCacheViewAuthenticPixels(CacheView *cache_view, const ssize_t x, const ssize_t y, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: cache-view.c:977
MagickExport VirtualPixelMethod SetImageVirtualPixelMethod(Image *image, const VirtualPixelMethod virtual_pixel_method, ExceptionInfo *exception)
Definition: image.c:3490
double y2
Definition: image.h:107
MagickExport MagickBooleanType ThrowMagickException(ExceptionInfo *exception, const char *module, const char *function, const size_t line, const ExceptionType severity, const char *tag, const char *format,...)
Definition: exception.c:1145
MagickExport MagickBooleanType LogMagickEvent(const LogEventType type, const char *module, const char *function, const size_t line, const char *format,...)
Definition: log.c:1413
size_t signature
Definition: image.h:354
size_t columns
Definition: image.h:172
#define QuantumScale
Definition: magick-type.h:115
ssize_t x
Definition: geometry.h:134
MagickExport ResampleFilter * DestroyResampleFilter(ResampleFilter *resample_filter)
Definition: resample.c:262
MagickExport MagickBooleanType SetImageStorageClass(Image *image, const ClassType storage_class, ExceptionInfo *exception)
Definition: image.c:2615
PixelChannel
Definition: pixel.h:67
static double RoundToUnity(const double value)
#define MagickMax(x, y)
Definition: image-private.h:26
MagickExport void SetResampleFilter(ResampleFilter *resample_filter, const FilterType filter)
Definition: resample.c:1241
MagickExport void ScaleResampleFilter(ResampleFilter *resample_filter, const double dux, const double duy, const double dvx, const double dvy)
Definition: resample.c:1038
static size_t GetPixelChannels(const Image *magick_restrict image)
char filename[MagickPathExtent]
Definition: image.h:319
#define GetMagickModule()
Definition: log.h:28
MagickExport MagickBooleanType IsFuzzyEquivalencePixel(const Image *source, const Quantum *p, const Image *destination, const Quantum *q)
Definition: pixel.c:5954
static Quantum ClampToQuantum(const MagickRealType value)
Definition: quantum.h:84
static PixelChannel GetPixelChannelChannel(const Image *magick_restrict image, const ssize_t offset)
MagickExport CacheView * AcquireVirtualCacheView(const Image *image, ExceptionInfo *exception)
Definition: cache-view.c:149
unsigned short Quantum
Definition: magick-type.h:82
double xi
Definition: geometry.h:106
MagickExport MagickBooleanType SetImageColorspace(Image *image, const ColorspaceType colorspace, ExceptionInfo *exception)
Definition: colorspace.c:1196
MagickExport MagickStatusType ParseGeometry(const char *geometry, GeometryInfo *geometry_info)
Definition: geometry.c:853
static void SetPixelChannel(const Image *magick_restrict image, const PixelChannel channel, const Quantum quantum, Quantum *magick_restrict pixel)
#define MagickMin(x, y)
Definition: image-private.h:27
static void SetPixelAlpha(const Image *magick_restrict image, const Quantum alpha, Quantum *magick_restrict pixel)
#define CompositeImageTag
#define MaxPixelChannels
Definition: pixel.h:27
MagickRealType green
Definition: pixel.h:190
MagickBooleanType(* MagickProgressMonitor)(const char *, const MagickOffsetType, const MagickSizeType, void *)
Definition: monitor.h:26
CompositeOperator compose
Definition: image.h:234
#define TextureImageTag
static MagickBooleanType CompositeOverImage(Image *image, const Image *source_image, const MagickBooleanType clip_to_self, const ssize_t x_offset, const ssize_t y_offset, ExceptionInfo *exception)
Definition: composite.c:287
CompositeOperator
Definition: composite.h:25
#define MagickExport
MagickExport MagickBooleanType SyncCacheViewAuthenticPixels(CacheView *magick_restrict cache_view, ExceptionInfo *exception)
Definition: cache-view.c:1100
ssize_t y
Definition: geometry.h:134
MagickExport CacheView * AcquireAuthenticCacheView(const Image *image, ExceptionInfo *exception)
Definition: cache-view.c:112
double y1
Definition: image.h:107
static Quantum GetPixelBlue(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
PixelTrait
Definition: pixel.h:134
MagickExport MagickRealType GetPixelIntensity(const Image *magick_restrict image, const Quantum *magick_restrict pixel)
Definition: pixel.c:2358
MagickExport Image * DestroyImage(Image *image)
Definition: image.c:1181
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:796
ColorspaceType colorspace
Definition: image.h:157
#define QuantumRange
Definition: magick-type.h:83
MagickExport MagickBooleanType SetImageProgress(const Image *image, const char *tag, const MagickOffsetType offset, const MagickSizeType extent)
Definition: monitor.c:136
MagickBooleanType debug
Definition: image.h:334