MagickCore  7.0.8
Convert, Edit, Or Compose Bitmap Images
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-2018 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 critical (MagickCore_CompositeImage)
516 #endif
517  proceed=SetImageProgress(image,CompositeImageTag,progress++,
518  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  status=MagickTrue;
615  source_view=AcquireVirtualCacheView(source_image,exception);
616  image_view=AcquireAuthenticCacheView(image,exception);
617 #if defined(MAGICKCORE_OPENMP_SUPPORT)
618  #pragma omp parallel for schedule(static) shared(status) \
619  magick_number_threads(source_image,image,source_image->rows,1)
620 #endif
621  for (y=0; y < (ssize_t) source_image->rows; y++)
622  {
624  sync;
625 
626  register const Quantum
627  *p;
628 
629  register Quantum
630  *q;
631 
632  register ssize_t
633  x;
634 
635  if (status == MagickFalse)
636  continue;
637  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
638  exception);
639  q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
640  source_image->columns,1,exception);
641  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
642  {
643  status=MagickFalse;
644  continue;
645  }
646  for (x=0; x < (ssize_t) source_image->columns; x++)
647  {
648  register ssize_t
649  i;
650 
651  if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
652  {
653  p+=GetPixelChannels(source_image);
654  q+=GetPixelChannels(image);
655  continue;
656  }
657  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
658  {
659  PixelChannel channel = GetPixelChannelChannel(image,i);
660  PixelTrait traits = GetPixelChannelTraits(image,channel);
661  PixelTrait source_traits=GetPixelChannelTraits(source_image,
662  channel);
663  if (traits == UndefinedPixelTrait)
664  continue;
665  if (source_traits != UndefinedPixelTrait)
666  SetPixelChannel(image,channel,p[i],q);
667  else if (channel == AlphaPixelChannel)
668  SetPixelChannel(image,channel,OpaqueAlpha,q);
669  }
670  p+=GetPixelChannels(source_image);
671  q+=GetPixelChannels(image);
672  }
673  sync=SyncCacheViewAuthenticPixels(image_view,exception);
674  if (sync == MagickFalse)
675  status=MagickFalse;
676  if (image->progress_monitor != (MagickProgressMonitor) NULL)
677  {
679  proceed;
680 
681 #if defined(MAGICKCORE_OPENMP_SUPPORT)
682  #pragma omp critical (MagickCore_CompositeImage)
683 #endif
684  proceed=SetImageProgress(image,CompositeImageTag,
685  (MagickOffsetType) y,image->rows);
686  if (proceed == MagickFalse)
687  status=MagickFalse;
688  }
689  }
690  source_view=DestroyCacheView(source_view);
691  image_view=DestroyCacheView(image_view);
692  source_image=DestroyImage(source_image);
693  return(status);
694  }
696  {
697  if ((x_offset < 0) || (y_offset < 0))
698  break;
699  if ((x_offset+(ssize_t) source_image->columns) > (ssize_t) image->columns)
700  break;
701  if ((y_offset+(ssize_t) source_image->rows) > (ssize_t) image->rows)
702  break;
703  status=MagickTrue;
704  source_view=AcquireVirtualCacheView(source_image,exception);
705  image_view=AcquireAuthenticCacheView(image,exception);
706 #if defined(MAGICKCORE_OPENMP_SUPPORT)
707  #pragma omp parallel for schedule(static) shared(status) \
708  magick_number_threads(source_image,image,source_image->rows,1)
709 #endif
710  for (y=0; y < (ssize_t) source_image->rows; y++)
711  {
713  sync;
714 
715  register const Quantum
716  *p;
717 
718  register Quantum
719  *q;
720 
721  register ssize_t
722  x;
723 
724  if (status == MagickFalse)
725  continue;
726  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
727  exception);
728  q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
729  source_image->columns,1,exception);
730  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
731  {
732  status=MagickFalse;
733  continue;
734  }
735  for (x=0; x < (ssize_t) source_image->columns; x++)
736  {
737  if (GetPixelReadMask(source_image,p) <= (QuantumRange/2))
738  {
739  p+=GetPixelChannels(source_image);
740  q+=GetPixelChannels(image);
741  continue;
742  }
743  SetPixelAlpha(image,clamp != MagickFalse ?
744  ClampPixel(GetPixelIntensity(source_image,p)) :
745  ClampToQuantum(GetPixelIntensity(source_image,p)),q);
746  p+=GetPixelChannels(source_image);
747  q+=GetPixelChannels(image);
748  }
749  sync=SyncCacheViewAuthenticPixels(image_view,exception);
750  if (sync == MagickFalse)
751  status=MagickFalse;
752  if (image->progress_monitor != (MagickProgressMonitor) NULL)
753  {
755  proceed;
756 
757 #if defined(MAGICKCORE_OPENMP_SUPPORT)
758  #pragma omp critical (MagickCore_CompositeImage)
759 #endif
760  proceed=SetImageProgress(image,CompositeImageTag,
761  (MagickOffsetType) y,image->rows);
762  if (proceed == MagickFalse)
763  status=MagickFalse;
764  }
765  }
766  source_view=DestroyCacheView(source_view);
767  image_view=DestroyCacheView(image_view);
768  source_image=DestroyImage(source_image);
769  return(status);
770  }
773  {
774  /*
775  Modify canvas outside the overlaid region and require an alpha
776  channel to exist, to add transparency.
777  */
778  if (image->alpha_trait == UndefinedPixelTrait)
779  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
780  break;
781  }
782  case BlurCompositeOp:
783  {
784  CacheView
785  *canvas_view;
786 
788  angle_range,
789  angle_start,
790  height,
791  width;
792 
793  PixelInfo
794  pixel;
795 
797  *resample_filter;
798 
800  blur;
801 
802  /*
803  Blur Image by resampling.
804 
805  Blur Image dictated by an overlay gradient map: X = red_channel;
806  Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
807  */
808  canvas_image=CloneImage(image,0,0,MagickTrue,
809  exception);
810  if (canvas_image == (Image *) NULL)
811  {
812  source_image=DestroyImage(source_image);
813  return(MagickFalse);
814  }
815  /*
816  Gather the maximum blur sigma values from user.
817  */
818  flags=NoValue;
819  value=GetImageArtifact(image,"compose:args");
820  if (value != (const char *) NULL)
821  flags=ParseGeometry(value,&geometry_info);
822  if ((flags & WidthValue) == 0)
823  {
825  "InvalidSetting","'%s' '%s'","compose:args",value);
826  source_image=DestroyImage(source_image);
827  canvas_image=DestroyImage(canvas_image);
828  return(MagickFalse);
829  }
830  /*
831  Users input sigma now needs to be converted to the EWA ellipse size.
832  The filter defaults to a sigma of 0.5 so to make this match the
833  users input the ellipse size needs to be doubled.
834  */
835  width=height=geometry_info.rho*2.0;
836  if ((flags & HeightValue) != 0 )
837  height=geometry_info.sigma*2.0;
838  /*
839  Default the unrotated ellipse width and height axis vectors.
840  */
841  blur.x1=width;
842  blur.x2=0.0;
843  blur.y1=0.0;
844  blur.y2=height;
845  /* rotate vectors if a rotation angle is given */
846  if ((flags & XValue) != 0 )
847  {
849  angle;
850 
851  angle=DegreesToRadians(geometry_info.xi);
852  blur.x1=width*cos(angle);
853  blur.x2=width*sin(angle);
854  blur.y1=(-height*sin(angle));
855  blur.y2=height*cos(angle);
856  }
857  /* Otherwise lets set a angle range and calculate in the loop */
858  angle_start=0.0;
859  angle_range=0.0;
860  if ((flags & YValue) != 0 )
861  {
862  angle_start=DegreesToRadians(geometry_info.xi);
863  angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
864  }
865  /*
866  Set up a gaussian cylindrical filter for EWA Bluring.
867 
868  As the minimum ellipse radius of support*1.0 the EWA algorithm
869  can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
870  This means that even 'No Blur' will be still a little blurry!
871 
872  The solution (as well as the problem of preventing any user
873  expert filter settings, is to set our own user settings, then
874  restore them afterwards.
875  */
876  resample_filter=AcquireResampleFilter(image,exception);
877  SetResampleFilter(resample_filter,GaussianFilter);
878 
879  /* do the variable blurring of each pixel in image */
880  GetPixelInfo(image,&pixel);
881  source_view=AcquireVirtualCacheView(source_image,exception);
882  canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
883  for (y=0; y < (ssize_t) source_image->rows; y++)
884  {
886  sync;
887 
888  register const Quantum
889  *magick_restrict p;
890 
891  register Quantum
892  *magick_restrict q;
893 
894  register ssize_t
895  x;
896 
897  if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
898  continue;
899  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
900  exception);
901  q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
902  exception);
903  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
904  break;
905  for (x=0; x < (ssize_t) source_image->columns; x++)
906  {
907  if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
908  {
909  p+=GetPixelChannels(source_image);
910  continue;
911  }
912  if (fabs((double) angle_range) > MagickEpsilon)
913  {
915  angle;
916 
917  angle=angle_start+angle_range*QuantumScale*
918  GetPixelBlue(source_image,p);
919  blur.x1=width*cos(angle);
920  blur.x2=width*sin(angle);
921  blur.y1=(-height*sin(angle));
922  blur.y2=height*cos(angle);
923  }
924 #if 0
925  if ( x == 10 && y == 60 ) {
926  (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
927  blur.x2,blur.y1, blur.y2);
928  (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
930 #endif
931  ScaleResampleFilter(resample_filter,
932  blur.x1*QuantumScale*GetPixelRed(source_image,p),
933  blur.y1*QuantumScale*GetPixelGreen(source_image,p),
934  blur.x2*QuantumScale*GetPixelRed(source_image,p),
935  blur.y2*QuantumScale*GetPixelGreen(source_image,p) );
936  (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
937  (double) y_offset+y,&pixel,exception);
938  SetPixelViaPixelInfo(canvas_image,&pixel,q);
939  p+=GetPixelChannels(source_image);
940  q+=GetPixelChannels(canvas_image);
941  }
942  sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
943  if (sync == MagickFalse)
944  break;
945  }
946  resample_filter=DestroyResampleFilter(resample_filter);
947  source_view=DestroyCacheView(source_view);
948  canvas_view=DestroyCacheView(canvas_view);
949  source_image=DestroyImage(source_image);
950  source_image=canvas_image;
951  break;
952  }
953  case DisplaceCompositeOp:
954  case DistortCompositeOp:
955  {
956  CacheView
957  *canvas_view;
958 
960  horizontal_scale,
961  vertical_scale;
962 
963  PixelInfo
964  pixel;
965 
966  PointInfo
967  center,
968  offset;
969 
970  /*
971  Displace/Distort based on overlay gradient map:
972  X = red_channel; Y = green_channel;
973  compose:args = x_scale[,y_scale[,center.x,center.y]]
974  */
975  canvas_image=CloneImage(image,0,0,MagickTrue,
976  exception);
977  if (canvas_image == (Image *) NULL)
978  {
979  source_image=DestroyImage(source_image);
980  return(MagickFalse);
981  }
982  SetGeometryInfo(&geometry_info);
983  flags=NoValue;
984  value=GetImageArtifact(image,"compose:args");
985  if (value != (char *) NULL)
986  flags=ParseGeometry(value,&geometry_info);
987  if ((flags & (WidthValue | HeightValue)) == 0 )
988  {
989  if ((flags & AspectValue) == 0)
990  {
991  horizontal_scale=(MagickRealType) (source_image->columns-1)/2.0;
992  vertical_scale=(MagickRealType) (source_image->rows-1)/2.0;
993  }
994  else
995  {
996  horizontal_scale=(MagickRealType) (image->columns-1)/2.0;
997  vertical_scale=(MagickRealType) (image->rows-1)/2.0;
998  }
999  }
1000  else
1001  {
1002  horizontal_scale=geometry_info.rho;
1003  vertical_scale=geometry_info.sigma;
1004  if ((flags & PercentValue) != 0)
1005  {
1006  if ((flags & AspectValue) == 0)
1007  {
1008  horizontal_scale*=(source_image->columns-1)/200.0;
1009  vertical_scale*=(source_image->rows-1)/200.0;
1010  }
1011  else
1012  {
1013  horizontal_scale*=(image->columns-1)/200.0;
1014  vertical_scale*=(image->rows-1)/200.0;
1015  }
1016  }
1017  if ((flags & HeightValue) == 0)
1018  vertical_scale=horizontal_scale;
1019  }
1020  /*
1021  Determine fixed center point for absolute distortion map
1022  Absolute distort ==
1023  Displace offset relative to a fixed absolute point
1024  Select that point according to +X+Y user inputs.
1025  default = center of overlay image
1026  arg flag '!' = locations/percentage relative to background image
1027  */
1028  center.x=(MagickRealType) x_offset;
1029  center.y=(MagickRealType) y_offset;
1030  if (compose == DistortCompositeOp)
1031  {
1032  if ((flags & XValue) == 0)
1033  if ((flags & AspectValue) != 0)
1034  center.x=(MagickRealType) ((image->columns-1)/2.0);
1035  else
1036  center.x=(MagickRealType) (x_offset+(source_image->columns-1)/
1037  2.0);
1038  else
1039  if ((flags & AspectValue) != 0)
1040  center.x=geometry_info.xi;
1041  else
1042  center.x=(MagickRealType) (x_offset+geometry_info.xi);
1043  if ((flags & YValue) == 0)
1044  if ((flags & AspectValue) != 0)
1045  center.y=(MagickRealType) ((image->rows-1)/2.0);
1046  else
1047  center.y=(MagickRealType) (y_offset+(source_image->rows-1)/2.0);
1048  else
1049  if ((flags & AspectValue) != 0)
1050  center.y=geometry_info.psi;
1051  else
1052  center.y=(MagickRealType) (y_offset+geometry_info.psi);
1053  }
1054  /*
1055  Shift the pixel offset point as defined by the provided,
1056  displacement/distortion map. -- Like a lens...
1057  */
1058  GetPixelInfo(image,&pixel);
1059  image_view=AcquireVirtualCacheView(image,exception);
1060  source_view=AcquireVirtualCacheView(source_image,exception);
1061  canvas_view=AcquireAuthenticCacheView(canvas_image,exception);
1062  for (y=0; y < (ssize_t) source_image->rows; y++)
1063  {
1065  sync;
1066 
1067  register const Quantum
1068  *magick_restrict p;
1069 
1070  register Quantum
1071  *magick_restrict q;
1072 
1073  register ssize_t
1074  x;
1075 
1076  if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1077  continue;
1078  p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
1079  exception);
1080  q=QueueCacheViewAuthenticPixels(canvas_view,0,y,canvas_image->columns,1,
1081  exception);
1082  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1083  break;
1084  for (x=0; x < (ssize_t) source_image->columns; x++)
1085  {
1086  if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1087  {
1088  p+=GetPixelChannels(source_image);
1089  continue;
1090  }
1091  /*
1092  Displace the offset.
1093  */
1094  offset.x=(double) (horizontal_scale*(GetPixelRed(source_image,p)-
1095  (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1096  QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1097  x : 0);
1098  offset.y=(double) (vertical_scale*(GetPixelGreen(source_image,p)-
1099  (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1100  QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1101  y : 0);
1102  status=InterpolatePixelInfo(image,image_view,
1103  UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1104  &pixel,exception);
1105  if (status == MagickFalse)
1106  break;
1107  /*
1108  Mask with the 'invalid pixel mask' in alpha channel.
1109  */
1111  (QuantumScale*GetPixelAlpha(source_image,p));
1112  SetPixelViaPixelInfo(canvas_image,&pixel,q);
1113  p+=GetPixelChannels(source_image);
1114  q+=GetPixelChannels(canvas_image);
1115  }
1116  if (x < (ssize_t) source_image->columns)
1117  break;
1118  sync=SyncCacheViewAuthenticPixels(canvas_view,exception);
1119  if (sync == MagickFalse)
1120  break;
1121  }
1122  canvas_view=DestroyCacheView(canvas_view);
1123  source_view=DestroyCacheView(source_view);
1124  image_view=DestroyCacheView(image_view);
1125  source_image=DestroyImage(source_image);
1126  source_image=canvas_image;
1127  break;
1128  }
1129  case DissolveCompositeOp:
1130  {
1131  /*
1132  Geometry arguments to dissolve factors.
1133  */
1134  value=GetImageArtifact(image,"compose:args");
1135  if (value != (char *) NULL)
1136  {
1137  flags=ParseGeometry(value,&geometry_info);
1138  source_dissolve=geometry_info.rho/100.0;
1139  canvas_dissolve=1.0;
1140  if ((source_dissolve-MagickEpsilon) < 0.0)
1141  source_dissolve=0.0;
1142  if ((source_dissolve+MagickEpsilon) > 1.0)
1143  {
1144  canvas_dissolve=2.0-source_dissolve;
1145  source_dissolve=1.0;
1146  }
1147  if ((flags & SigmaValue) != 0)
1148  canvas_dissolve=geometry_info.sigma/100.0;
1149  if ((canvas_dissolve-MagickEpsilon) < 0.0)
1150  canvas_dissolve=0.0;
1151  }
1152  break;
1153  }
1154  case BlendCompositeOp:
1155  {
1156  value=GetImageArtifact(image,"compose:args");
1157  if (value != (char *) NULL)
1158  {
1159  flags=ParseGeometry(value,&geometry_info);
1160  source_dissolve=geometry_info.rho/100.0;
1161  canvas_dissolve=1.0-source_dissolve;
1162  if ((flags & SigmaValue) != 0)
1163  canvas_dissolve=geometry_info.sigma/100.0;
1164  }
1165  break;
1166  }
1168  {
1169  /*
1170  Just collect the values from "compose:args", setting.
1171  Unused values are set to zero automagically.
1172 
1173  Arguments are normally a comma separated list, so this probably should
1174  be changed to some 'general comma list' parser, (with a minimum
1175  number of values)
1176  */
1177  SetGeometryInfo(&geometry_info);
1178  value=GetImageArtifact(image,"compose:args");
1179  if (value != (char *) NULL)
1180  (void) ParseGeometry(value,&geometry_info);
1181  break;
1182  }
1183  case ModulateCompositeOp:
1184  {
1185  /*
1186  Determine the luma and chroma scale.
1187  */
1188  value=GetImageArtifact(image,"compose:args");
1189  if (value != (char *) NULL)
1190  {
1191  flags=ParseGeometry(value,&geometry_info);
1192  percent_luma=geometry_info.rho;
1193  if ((flags & SigmaValue) != 0)
1194  percent_chroma=geometry_info.sigma;
1195  }
1196  break;
1197  }
1198  case ThresholdCompositeOp:
1199  {
1200  /*
1201  Determine the amount and threshold.
1202  */
1203  value=GetImageArtifact(image,"compose:args");
1204  if (value != (char *) NULL)
1205  {
1206  flags=ParseGeometry(value,&geometry_info);
1207  amount=geometry_info.rho;
1208  threshold=geometry_info.sigma;
1209  if ((flags & SigmaValue) == 0)
1210  threshold=0.05f;
1211  }
1212  threshold*=QuantumRange;
1213  break;
1214  }
1215  default:
1216  break;
1217  }
1218  /*
1219  Composite image.
1220  */
1221  status=MagickTrue;
1222  progress=0;
1223  midpoint=((MagickRealType) QuantumRange+1.0)/2;
1224  source_view=AcquireVirtualCacheView(source_image,exception);
1225  image_view=AcquireAuthenticCacheView(image,exception);
1226 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1227  #pragma omp parallel for schedule(static) shared(progress,status) \
1228  magick_number_threads(source_image,image,image->rows,1)
1229 #endif
1230  for (y=0; y < (ssize_t) image->rows; y++)
1231  {
1232  const Quantum
1233  *pixels;
1234 
1236  blue,
1237  chroma,
1238  green,
1239  hue,
1240  luma,
1241  red;
1242 
1243  PixelInfo
1244  canvas_pixel,
1245  source_pixel;
1246 
1247  register const Quantum
1248  *magick_restrict p;
1249 
1250  register Quantum
1251  *magick_restrict q;
1252 
1253  register ssize_t
1254  x;
1255 
1256  if (status == MagickFalse)
1257  continue;
1258  if (clip_to_self != MagickFalse)
1259  {
1260  if (y < y_offset)
1261  continue;
1262  if ((y-y_offset) >= (ssize_t) source_image->rows)
1263  continue;
1264  }
1265  /*
1266  If pixels is NULL, y is outside overlay region.
1267  */
1268  pixels=(Quantum *) NULL;
1269  p=(Quantum *) NULL;
1270  if ((y >= y_offset) && ((y-y_offset) < (ssize_t) source_image->rows))
1271  {
1272  p=GetCacheViewVirtualPixels(source_view,0,y-y_offset,
1273  source_image->columns,1,exception);
1274  if (p == (const Quantum *) NULL)
1275  {
1276  status=MagickFalse;
1277  continue;
1278  }
1279  pixels=p;
1280  if (x_offset < 0)
1281  p-=x_offset*GetPixelChannels(source_image);
1282  }
1283  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1284  if (q == (Quantum *) NULL)
1285  {
1286  status=MagickFalse;
1287  continue;
1288  }
1289  hue=0.0;
1290  chroma=0.0;
1291  luma=0.0;
1292  GetPixelInfo(image,&canvas_pixel);
1293  GetPixelInfo(source_image,&source_pixel);
1294  for (x=0; x < (ssize_t) image->columns; x++)
1295  {
1296  double
1297  gamma;
1298 
1300  alpha,
1301  Da,
1302  Dc,
1303  Dca,
1304  DcaDa,
1305  Sa,
1306  SaSca,
1307  Sc,
1308  Sca;
1309 
1310  register ssize_t
1311  i;
1312 
1313  size_t
1314  channels;
1315 
1316  if (clip_to_self != MagickFalse)
1317  {
1318  if (x < x_offset)
1319  {
1320  q+=GetPixelChannels(image);
1321  continue;
1322  }
1323  if ((x-x_offset) >= (ssize_t) source_image->columns)
1324  break;
1325  }
1326  if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1327  ((x-x_offset) >= (ssize_t) source_image->columns))
1328  {
1329  Quantum
1330  source[MaxPixelChannels];
1331 
1332  /*
1333  Virtual composite:
1334  Sc: source color.
1335  Dc: canvas color.
1336  */
1337  (void) GetOneVirtualPixel(source_image,x-x_offset,y-y_offset,source,
1338  exception);
1339  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1340  {
1342  pixel;
1343 
1344  PixelChannel channel = GetPixelChannelChannel(image,i);
1345  PixelTrait traits = GetPixelChannelTraits(image,channel);
1346  PixelTrait source_traits=GetPixelChannelTraits(source_image,
1347  channel);
1348  if ((traits == UndefinedPixelTrait) ||
1349  (source_traits == UndefinedPixelTrait))
1350  continue;
1351  switch (compose)
1352  {
1353  case AlphaCompositeOp:
1354  case ChangeMaskCompositeOp:
1355  case CopyAlphaCompositeOp:
1356  case DstAtopCompositeOp:
1357  case DstInCompositeOp:
1358  case InCompositeOp:
1359  case OutCompositeOp:
1360  case SrcInCompositeOp:
1361  case SrcOutCompositeOp:
1362  {
1363  if (channel == AlphaPixelChannel)
1365  else
1366  pixel=(MagickRealType) q[i];
1367  break;
1368  }
1369  case ClearCompositeOp:
1370  case CopyCompositeOp:
1371  case ReplaceCompositeOp:
1372  case SrcCompositeOp:
1373  {
1374  if (channel == AlphaPixelChannel)
1376  else
1377  pixel=0.0;
1378  break;
1379  }
1380  case BlendCompositeOp:
1381  case DissolveCompositeOp:
1382  {
1383  if (channel == AlphaPixelChannel)
1384  pixel=canvas_dissolve*GetPixelAlpha(source_image,source);
1385  else
1386  pixel=(MagickRealType) source[channel];
1387  break;
1388  }
1389  default:
1390  {
1391  pixel=(MagickRealType) source[channel];
1392  break;
1393  }
1394  }
1395  q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1396  ClampToQuantum(pixel);
1397  }
1398  q+=GetPixelChannels(image);
1399  continue;
1400  }
1401  /*
1402  Authentic composite:
1403  Sa: normalized source alpha.
1404  Da: normalized canvas alpha.
1405  */
1406  Sa=QuantumScale*GetPixelAlpha(source_image,p);
1407  Da=QuantumScale*GetPixelAlpha(image,q);
1408  switch (compose)
1409  {
1410  case BumpmapCompositeOp:
1411  {
1412  alpha=GetPixelIntensity(source_image,p)*Sa;
1413  break;
1414  }
1415  case ColorBurnCompositeOp:
1416  case ColorDodgeCompositeOp:
1417  case DarkenCompositeOp:
1418  case DifferenceCompositeOp:
1419  case DivideDstCompositeOp:
1420  case DivideSrcCompositeOp:
1421  case ExclusionCompositeOp:
1422  case HardLightCompositeOp:
1423  case HardMixCompositeOp:
1424  case LinearBurnCompositeOp:
1427  case LightenCompositeOp:
1429  case MinusDstCompositeOp:
1430  case MinusSrcCompositeOp:
1431  case ModulusAddCompositeOp:
1433  case MultiplyCompositeOp:
1434  case OverlayCompositeOp:
1436  case PinLightCompositeOp:
1437  case ScreenCompositeOp:
1438  case SoftLightCompositeOp:
1439  case VividLightCompositeOp:
1440  {
1441  alpha=RoundToUnity(Sa+Da-Sa*Da);
1442  break;
1443  }
1444  case DstAtopCompositeOp:
1445  case DstInCompositeOp:
1446  case InCompositeOp:
1447  case SrcInCompositeOp:
1448  {
1449  alpha=Sa*Da;
1450  break;
1451  }
1452  case DissolveCompositeOp:
1453  {
1454  alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
1455  canvas_dissolve*Da;
1456  break;
1457  }
1458  case DstOverCompositeOp:
1459  case OverCompositeOp:
1460  case SrcOverCompositeOp:
1461  {
1462  alpha=Sa+Da-Sa*Da;
1463  break;
1464  }
1465  case DstOutCompositeOp:
1466  {
1467  alpha=Da*(1.0-Sa);
1468  break;
1469  }
1470  case OutCompositeOp:
1471  case SrcOutCompositeOp:
1472  {
1473  alpha=Sa*(1.0-Da);
1474  break;
1475  }
1476  case BlendCompositeOp:
1477  case PlusCompositeOp:
1478  {
1479  alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
1480  break;
1481  }
1482  case XorCompositeOp:
1483  {
1484  alpha=Sa+Da-2.0*Sa*Da;
1485  break;
1486  }
1487  default:
1488  {
1489  alpha=1.0;
1490  break;
1491  }
1492  }
1493  switch (compose)
1494  {
1495  case ColorizeCompositeOp:
1496  case HueCompositeOp:
1497  case LuminizeCompositeOp:
1498  case ModulateCompositeOp:
1499  case SaturateCompositeOp:
1500  {
1501  GetPixelInfoPixel(source_image,p,&source_pixel);
1502  GetPixelInfoPixel(image,q,&canvas_pixel);
1503  break;
1504  }
1505  default:
1506  break;
1507  }
1508  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1509  {
1511  pixel,
1512  sans;
1513 
1514  PixelChannel channel = GetPixelChannelChannel(image,i);
1515  PixelTrait traits = GetPixelChannelTraits(image,channel);
1516  PixelTrait source_traits = GetPixelChannelTraits(source_image,channel);
1517  if (traits == UndefinedPixelTrait)
1518  continue;
1519  if ((channel == AlphaPixelChannel) &&
1520  ((traits & UpdatePixelTrait) != 0))
1521  {
1522  /*
1523  Set alpha channel.
1524  */
1525  switch (compose)
1526  {
1527  case AlphaCompositeOp:
1528  {
1529  pixel=QuantumRange*Sa;
1530  break;
1531  }
1532  case AtopCompositeOp:
1533  case CopyBlackCompositeOp:
1534  case CopyBlueCompositeOp:
1535  case CopyCyanCompositeOp:
1536  case CopyGreenCompositeOp:
1538  case CopyRedCompositeOp:
1539  case CopyYellowCompositeOp:
1540  case SrcAtopCompositeOp:
1541  case DstCompositeOp:
1542  case NoCompositeOp:
1543  {
1544  pixel=QuantumRange*Da;
1545  break;
1546  }
1547  case ChangeMaskCompositeOp:
1548  {
1550  equivalent;
1551 
1552  if (Da < 0.5)
1553  {
1555  break;
1556  }
1557  equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1558  if (equivalent != MagickFalse)
1560  else
1561  pixel=(MagickRealType) OpaqueAlpha;
1562  break;
1563  }
1564  case ClearCompositeOp:
1565  {
1567  break;
1568  }
1569  case ColorizeCompositeOp:
1570  case HueCompositeOp:
1571  case LuminizeCompositeOp:
1572  case SaturateCompositeOp:
1573  {
1574  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1575  {
1576  pixel=QuantumRange*Da;
1577  break;
1578  }
1579  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1580  {
1581  pixel=QuantumRange*Sa;
1582  break;
1583  }
1584  if (Sa < Da)
1585  {
1586  pixel=QuantumRange*Da;
1587  break;
1588  }
1589  pixel=QuantumRange*Sa;
1590  break;
1591  }
1592  case CopyAlphaCompositeOp:
1593  {
1594  if (source_image->alpha_trait == UndefinedPixelTrait)
1595  pixel=GetPixelIntensity(source_image,p);
1596  else
1597  pixel=QuantumRange*Sa;
1598  break;
1599  }
1600  case CopyCompositeOp:
1601  case DisplaceCompositeOp:
1602  case DistortCompositeOp:
1603  case DstAtopCompositeOp:
1604  case ReplaceCompositeOp:
1605  case SrcCompositeOp:
1606  {
1607  pixel=QuantumRange*Sa;
1608  break;
1609  }
1611  {
1612  pixel=Sa*GetPixelIntensity(source_image,p) <
1613  Da*GetPixelIntensity(image,q) ? Sa : Da;
1614  break;
1615  }
1617  {
1618  pixel=Sa*GetPixelIntensity(source_image,p) >
1619  Da*GetPixelIntensity(image,q) ? Sa : Da;
1620  break;
1621  }
1622  case ModulateCompositeOp:
1623  {
1624  pixel=QuantumRange*Da;
1625  break;
1626  }
1627  case MultiplyCompositeOp:
1628  {
1629  pixel=QuantumRange*Sa*Da;
1630  break;
1631  }
1632  case StereoCompositeOp:
1633  {
1634  pixel=QuantumRange*(Sa+Da)/2;
1635  break;
1636  }
1637  default:
1638  {
1639  pixel=QuantumRange*alpha;
1640  break;
1641  }
1642  }
1643  q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1644  ClampToQuantum(pixel);
1645  continue;
1646  }
1647  if (source_traits == UndefinedPixelTrait)
1648  continue;
1649  /*
1650  Sc: source color.
1651  Dc: canvas color.
1652  */
1653  Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1654  Dc=(MagickRealType) q[i];
1655  if ((traits & CopyPixelTrait) != 0)
1656  {
1657  /*
1658  Copy channel.
1659  */
1660  q[i]=ClampToQuantum(Dc);
1661  continue;
1662  }
1663  /*
1664  Porter-Duff compositions:
1665  Sca: source normalized color multiplied by alpha.
1666  Dca: normalized canvas color multiplied by alpha.
1667  */
1668  Sca=QuantumScale*Sa*Sc;
1669  Dca=QuantumScale*Da*Dc;
1670  SaSca=Sa*PerceptibleReciprocal(Sca);
1671  DcaDa=Dca*PerceptibleReciprocal(Da);
1672  switch (compose)
1673  {
1674  case DarkenCompositeOp:
1675  case LightenCompositeOp:
1677  {
1678  gamma=PerceptibleReciprocal(1.0-alpha);
1679  break;
1680  }
1681  default:
1682  {
1683  gamma=PerceptibleReciprocal(alpha);
1684  break;
1685  }
1686  }
1687  pixel=Dc;
1688  switch (compose)
1689  {
1690  case AlphaCompositeOp:
1691  {
1692  pixel=QuantumRange*Sa;
1693  break;
1694  }
1695  case AtopCompositeOp:
1696  case SrcAtopCompositeOp:
1697  {
1698  pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1699  break;
1700  }
1701  case BlendCompositeOp:
1702  {
1703  pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1704  break;
1705  }
1706  case BlurCompositeOp:
1707  case CopyCompositeOp:
1708  case ReplaceCompositeOp:
1709  case SrcCompositeOp:
1710  {
1711  pixel=QuantumRange*Sca;
1712  break;
1713  }
1714  case DisplaceCompositeOp:
1715  case DistortCompositeOp:
1716  {
1717  pixel=Sc;
1718  break;
1719  }
1720  case BumpmapCompositeOp:
1721  {
1722  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1723  {
1724  pixel=Dc;
1725  break;
1726  }
1727  pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1728  break;
1729  }
1730  case ChangeMaskCompositeOp:
1731  {
1732  pixel=Dc;
1733  break;
1734  }
1735  case ClearCompositeOp:
1736  {
1737  pixel=0.0;
1738  break;
1739  }
1740  case ColorBurnCompositeOp:
1741  {
1742  if ((Sca == 0.0) && (Dca == Da))
1743  {
1744  pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1745  break;
1746  }
1747  if (Sca == 0.0)
1748  {
1749  pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1750  break;
1751  }
1752  pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-DcaDa)*
1753  SaSca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1754  break;
1755  }
1756  case ColorDodgeCompositeOp:
1757  {
1758  if ((Sca*Da+Dca*Sa) >= Sa*Da)
1759  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1760  else
1761  pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(Sa-Sca)+
1762  Sca*(1.0-Da)+Dca*(1.0-Sa));
1763  break;
1764  }
1765  case ColorizeCompositeOp:
1766  {
1767  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1768  {
1769  pixel=Dc;
1770  break;
1771  }
1772  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1773  {
1774  pixel=Sc;
1775  break;
1776  }
1777  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1778  &sans,&sans,&luma);
1779  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1780  &hue,&chroma,&sans);
1781  HCLComposite(hue,chroma,luma,&red,&green,&blue);
1782  switch (channel)
1783  {
1784  case RedPixelChannel: pixel=red; break;
1785  case GreenPixelChannel: pixel=green; break;
1786  case BluePixelChannel: pixel=blue; break;
1787  default: pixel=Dc; break;
1788  }
1789  break;
1790  }
1791  case CopyAlphaCompositeOp:
1792  {
1793  pixel=Dc;
1794  break;
1795  }
1796  case CopyBlackCompositeOp:
1797  {
1798  if (channel == BlackPixelChannel)
1799  pixel=(MagickRealType) (QuantumRange-
1800  GetPixelBlack(source_image,p));
1801  break;
1802  }
1803  case CopyBlueCompositeOp:
1804  case CopyYellowCompositeOp:
1805  {
1806  if (channel == BluePixelChannel)
1807  pixel=(MagickRealType) GetPixelBlue(source_image,p);
1808  break;
1809  }
1810  case CopyGreenCompositeOp:
1812  {
1813  if (channel == GreenPixelChannel)
1814  pixel=(MagickRealType) GetPixelGreen(source_image,p);
1815  break;
1816  }
1817  case CopyRedCompositeOp:
1818  case CopyCyanCompositeOp:
1819  {
1820  if (channel == RedPixelChannel)
1821  pixel=(MagickRealType) GetPixelRed(source_image,p);
1822  break;
1823  }
1824  case DarkenCompositeOp:
1825  {
1826  /*
1827  Darken is equivalent to a 'Minimum' method
1828  OR a greyscale version of a binary 'Or'
1829  OR the 'Intersection' of pixel sets.
1830  */
1831  if ((Sca*Da) < (Dca*Sa))
1832  {
1833  pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1834  break;
1835  }
1836  pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1837  break;
1838  }
1840  {
1841  pixel=Sa*GetPixelIntensity(source_image,p) <
1842  Da*GetPixelIntensity(image,q) ? Sc : Dc;
1843  break;
1844  }
1845  case DifferenceCompositeOp:
1846  {
1847  pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1848  break;
1849  }
1850  case DissolveCompositeOp:
1851  {
1852  pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1853  canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1854  break;
1855  }
1856  case DivideDstCompositeOp:
1857  {
1858  if ((fabs((double) Sca) < MagickEpsilon) &&
1859  (fabs((double) Dca) < MagickEpsilon))
1860  {
1861  pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1862  break;
1863  }
1864  if (fabs((double) Dca) < MagickEpsilon)
1865  {
1866  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1867  break;
1868  }
1869  pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1870  break;
1871  }
1872  case DivideSrcCompositeOp:
1873  {
1874  if ((fabs((double) Dca) < MagickEpsilon) &&
1875  (fabs((double) Sca) < MagickEpsilon))
1876  {
1877  pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1878  break;
1879  }
1880  if (fabs((double) Sca) < MagickEpsilon)
1881  {
1882  pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1883  break;
1884  }
1885  pixel=QuantumRange*gamma*(Dca*Sa*SaSca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1886  break;
1887  }
1888  case DstAtopCompositeOp:
1889  {
1890  pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1891  break;
1892  }
1893  case DstCompositeOp:
1894  case NoCompositeOp:
1895  {
1896  pixel=QuantumRange*Dca;
1897  break;
1898  }
1899  case DstInCompositeOp:
1900  {
1901  pixel=QuantumRange*(Dca*Sa);
1902  break;
1903  }
1904  case DstOutCompositeOp:
1905  {
1906  pixel=QuantumRange*(Dca*(1.0-Sa));
1907  break;
1908  }
1909  case DstOverCompositeOp:
1910  {
1911  pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1912  break;
1913  }
1914  case ExclusionCompositeOp:
1915  {
1916  pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1917  Dca*(1.0-Sa));
1918  break;
1919  }
1920  case HardLightCompositeOp:
1921  {
1922  if ((2.0*Sca) < Sa)
1923  {
1924  pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1925  Sa));
1926  break;
1927  }
1928  pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1929  Dca*(1.0-Sa));
1930  break;
1931  }
1932  case HardMixCompositeOp:
1933  {
1934  pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1935  break;
1936  }
1937  case HueCompositeOp:
1938  {
1939  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1940  {
1941  pixel=Dc;
1942  break;
1943  }
1944  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1945  {
1946  pixel=Sc;
1947  break;
1948  }
1949  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1950  &hue,&chroma,&luma);
1951  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1952  &hue,&sans,&sans);
1953  HCLComposite(hue,chroma,luma,&red,&green,&blue);
1954  switch (channel)
1955  {
1956  case RedPixelChannel: pixel=red; break;
1957  case GreenPixelChannel: pixel=green; break;
1958  case BluePixelChannel: pixel=blue; break;
1959  default: pixel=Dc; break;
1960  }
1961  break;
1962  }
1963  case InCompositeOp:
1964  case SrcInCompositeOp:
1965  {
1966  pixel=QuantumRange*(Sca*Da);
1967  break;
1968  }
1969  case LinearBurnCompositeOp:
1970  {
1971  /*
1972  LinearBurn: as defined by Abode Photoshop, according to
1973  http://www.simplefilter.de/en/basics/mixmods.html is:
1974 
1975  f(Sc,Dc) = Sc + Dc - 1
1976  */
1977  pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1978  break;
1979  }
1981  {
1982  pixel=gamma*(Sa*Sc+Da*Dc);
1983  break;
1984  }
1986  {
1987  /*
1988  LinearLight: as defined by Abode Photoshop, according to
1989  http://www.simplefilter.de/en/basics/mixmods.html is:
1990 
1991  f(Sc,Dc) = Dc + 2*Sc - 1
1992  */
1993  pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1994  break;
1995  }
1996  case LightenCompositeOp:
1997  {
1998  if ((Sca*Da) > (Dca*Sa))
1999  {
2000  pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2001  break;
2002  }
2003  pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2004  break;
2005  }
2007  {
2008  /*
2009  Lighten is equivalent to a 'Maximum' method
2010  OR a greyscale version of a binary 'And'
2011  OR the 'Union' of pixel sets.
2012  */
2013  pixel=Sa*GetPixelIntensity(source_image,p) >
2014  Da*GetPixelIntensity(image,q) ? Sc : Dc;
2015  break;
2016  }
2017  case LuminizeCompositeOp:
2018  {
2019  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2020  {
2021  pixel=Dc;
2022  break;
2023  }
2024  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2025  {
2026  pixel=Sc;
2027  break;
2028  }
2029  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2030  &hue,&chroma,&luma);
2031  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2032  &sans,&sans,&luma);
2033  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2034  switch (channel)
2035  {
2036  case RedPixelChannel: pixel=red; break;
2037  case GreenPixelChannel: pixel=green; break;
2038  case BluePixelChannel: pixel=blue; break;
2039  default: pixel=Dc; break;
2040  }
2041  break;
2042  }
2044  {
2045  /*
2046  'Mathematics' a free form user control mathematical composition
2047  is defined as...
2048 
2049  f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2050 
2051  Where the arguments A,B,C,D are (currently) passed to composite
2052  as a command separated 'geometry' string in "compose:args" image
2053  artifact.
2054 
2055  A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2056 
2057  Applying the SVG transparency formula (see above), we get...
2058 
2059  Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2060 
2061  Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2062  Dca*(1.0-Sa)
2063  */
2064  pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
2065  geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2066  geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2067  break;
2068  }
2069  case MinusDstCompositeOp:
2070  {
2071  pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2072  break;
2073  }
2074  case MinusSrcCompositeOp:
2075  {
2076  /*
2077  Minus source from canvas.
2078 
2079  f(Sc,Dc) = Sc - Dc
2080  */
2081  pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2082  break;
2083  }
2084  case ModulateCompositeOp:
2085  {
2086  ssize_t
2087  offset;
2088 
2089  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2090  {
2091  pixel=Dc;
2092  break;
2093  }
2094  offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2095  if (offset == 0)
2096  {
2097  pixel=Dc;
2098  break;
2099  }
2100  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2101  &hue,&chroma,&luma);
2102  luma+=(0.01*percent_luma*offset)/midpoint;
2103  chroma*=0.01*percent_chroma;
2104  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2105  switch (channel)
2106  {
2107  case RedPixelChannel: pixel=red; break;
2108  case GreenPixelChannel: pixel=green; break;
2109  case BluePixelChannel: pixel=blue; break;
2110  default: pixel=Dc; break;
2111  }
2112  break;
2113  }
2114  case ModulusAddCompositeOp:
2115  {
2116  pixel=Sc+Dc;
2117  while (pixel > QuantumRange)
2118  pixel-=QuantumRange;
2119  while (pixel < 0.0)
2120  pixel+=QuantumRange;
2121  pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2122  break;
2123  }
2125  {
2126  pixel=Sc-Dc;
2127  while (pixel > QuantumRange)
2128  pixel-=QuantumRange;
2129  while (pixel < 0.0)
2130  pixel+=QuantumRange;
2131  pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2132  break;
2133  }
2134  case MultiplyCompositeOp:
2135  {
2136  pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2137  break;
2138  }
2139  case OutCompositeOp:
2140  case SrcOutCompositeOp:
2141  {
2142  pixel=QuantumRange*(Sca*(1.0-Da));
2143  break;
2144  }
2145  case OverCompositeOp:
2146  case SrcOverCompositeOp:
2147  {
2148  pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
2149  break;
2150  }
2151  case OverlayCompositeOp:
2152  {
2153  if ((2.0*Dca) < Da)
2154  {
2155  pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-
2156  Da));
2157  break;
2158  }
2159  pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2160  Sca*(1.0-Da));
2161  break;
2162  }
2164  {
2165  /*
2166  PegTop: A Soft-Light alternative: A continuous version of the
2167  Softlight function, producing very similar results.
2168 
2169  f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2170 
2171  http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2172  */
2173  if (fabs((double) Da) < MagickEpsilon)
2174  {
2175  pixel=QuantumRange*gamma*(Sca);
2176  break;
2177  }
2178  pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2179  Da)+Dca*(1.0-Sa));
2180  break;
2181  }
2182  case PinLightCompositeOp:
2183  {
2184  /*
2185  PinLight: A Photoshop 7 composition method
2186  http://www.simplefilter.de/en/basics/mixmods.html
2187 
2188  f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2189  */
2190  if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2191  {
2192  pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2193  break;
2194  }
2195  if ((Dca*Sa) > (2.0*Sca*Da))
2196  {
2197  pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2198  break;
2199  }
2200  pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2201  break;
2202  }
2203  case PlusCompositeOp:
2204  {
2205  pixel=QuantumRange*(Sca+Dca);
2206  break;
2207  }
2208  case SaturateCompositeOp:
2209  {
2210  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2211  {
2212  pixel=Dc;
2213  break;
2214  }
2215  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2216  {
2217  pixel=Sc;
2218  break;
2219  }
2220  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2221  &hue,&chroma,&luma);
2222  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2223  &sans,&chroma,&sans);
2224  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2225  switch (channel)
2226  {
2227  case RedPixelChannel: pixel=red; break;
2228  case GreenPixelChannel: pixel=green; break;
2229  case BluePixelChannel: pixel=blue; break;
2230  default: pixel=Dc; break;
2231  }
2232  break;
2233  }
2234  case ScreenCompositeOp:
2235  {
2236  /*
2237  Screen: a negated multiply:
2238 
2239  f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2240  */
2241  pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2242  break;
2243  }
2244  case SoftLightCompositeOp:
2245  {
2246  if ((2.0*Sca) < Sa)
2247  {
2248  pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-DcaDa))+
2249  Sca*(1.0-Da)+Dca*(1.0-Sa));
2250  break;
2251  }
2252  if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2253  {
2254  pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*DcaDa*
2255  (4.0*DcaDa+1.0)*(DcaDa-1.0)+7.0*DcaDa)+Sca*(1.0-Da)+
2256  Dca*(1.0-Sa));
2257  break;
2258  }
2259  pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow(DcaDa,0.5)-
2260  DcaDa)+Sca*(1.0-Da)+Dca*(1.0-Sa));
2261  break;
2262  }
2263  case StereoCompositeOp:
2264  {
2265  if (channel == RedPixelChannel)
2266  pixel=(MagickRealType) GetPixelRed(source_image,p);
2267  break;
2268  }
2269  case ThresholdCompositeOp:
2270  {
2272  delta;
2273 
2274  delta=Sc-Dc;
2275  if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2276  {
2277  pixel=gamma*Dc;
2278  break;
2279  }
2280  pixel=gamma*(Dc+delta*amount);
2281  break;
2282  }
2283  case VividLightCompositeOp:
2284  {
2285  /*
2286  VividLight: A Photoshop 7 composition method. See
2287  http://www.simplefilter.de/en/basics/mixmods.html.
2288 
2289  f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2290  */
2291  if ((fabs((double) Sa) < MagickEpsilon) ||
2292  (fabs((double) (Sca-Sa)) < MagickEpsilon))
2293  {
2294  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2295  break;
2296  }
2297  if ((2.0*Sca) <= Sa)
2298  {
2299  pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)*
2300  PerceptibleReciprocal(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2301  break;
2302  }
2303  pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(2.0*
2304  (Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2305  break;
2306  }
2307  case XorCompositeOp:
2308  {
2309  pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2310  break;
2311  }
2312  default:
2313  {
2314  pixel=Sc;
2315  break;
2316  }
2317  }
2318  q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2319  }
2320  p+=GetPixelChannels(source_image);
2321  channels=GetPixelChannels(source_image);
2322  if (p >= (pixels+channels*source_image->columns))
2323  p=pixels;
2324  q+=GetPixelChannels(image);
2325  }
2326  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2327  status=MagickFalse;
2328  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2329  {
2331  proceed;
2332 
2333 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2334  #pragma omp critical (MagickCore_CompositeImage)
2335 #endif
2336  proceed=SetImageProgress(image,CompositeImageTag,progress++,
2337  image->rows);
2338  if (proceed == MagickFalse)
2339  status=MagickFalse;
2340  }
2341  }
2342  source_view=DestroyCacheView(source_view);
2343  image_view=DestroyCacheView(image_view);
2344  if (canvas_image != (Image * ) NULL)
2345  canvas_image=DestroyImage(canvas_image);
2346  else
2347  source_image=DestroyImage(source_image);
2348  return(status);
2349 }
2350 
2351 /*
2352 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2353 % %
2354 % %
2355 % %
2356 % T e x t u r e I m a g e %
2357 % %
2358 % %
2359 % %
2360 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2361 %
2362 % TextureImage() repeatedly tiles the texture image across and down the image
2363 % canvas.
2364 %
2365 % The format of the TextureImage method is:
2366 %
2367 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2368 % ExceptionInfo *exception)
2369 %
2370 % A description of each parameter follows:
2371 %
2372 % o image: the image.
2373 %
2374 % o texture_image: This image is the texture to layer on the background.
2375 %
2376 */
2378  ExceptionInfo *exception)
2379 {
2380 #define TextureImageTag "Texture/Image"
2381 
2382  CacheView
2383  *image_view,
2384  *texture_view;
2385 
2386  Image
2387  *texture_image;
2388 
2390  status;
2391 
2392  ssize_t
2393  y;
2394 
2395  assert(image != (Image *) NULL);
2396  if (image->debug != MagickFalse)
2397  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2398  assert(image->signature == MagickCoreSignature);
2399  if (texture == (const Image *) NULL)
2400  return(MagickFalse);
2401  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2402  return(MagickFalse);
2403  texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2404  if (texture_image == (const Image *) NULL)
2405  return(MagickFalse);
2406  (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2408  exception);
2409  status=MagickTrue;
2410  if ((image->compose != CopyCompositeOp) &&
2411  ((image->compose != OverCompositeOp) ||
2412  (image->alpha_trait != UndefinedPixelTrait) ||
2413  (texture_image->alpha_trait != UndefinedPixelTrait)))
2414  {
2415  /*
2416  Tile texture onto the image background.
2417  */
2418  for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2419  {
2420  register ssize_t
2421  x;
2422 
2423  if (status == MagickFalse)
2424  continue;
2425  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2426  {
2428  thread_status;
2429 
2430  thread_status=CompositeImage(image,texture_image,image->compose,
2431  MagickTrue,x+texture_image->tile_offset.x,y+
2432  texture_image->tile_offset.y,exception);
2433  if (thread_status == MagickFalse)
2434  {
2435  status=thread_status;
2436  break;
2437  }
2438  }
2439  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2440  {
2442  proceed;
2443 
2445  image->rows);
2446  if (proceed == MagickFalse)
2447  status=MagickFalse;
2448  }
2449  }
2451  image->rows,image->rows);
2452  texture_image=DestroyImage(texture_image);
2453  return(status);
2454  }
2455  /*
2456  Tile texture onto the image background (optimized).
2457  */
2458  status=MagickTrue;
2459  texture_view=AcquireVirtualCacheView(texture_image,exception);
2460  image_view=AcquireAuthenticCacheView(image,exception);
2461 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2462  #pragma omp parallel for schedule(static) shared(status) \
2463  magick_number_threads(texture_image,image,image->rows,1)
2464 #endif
2465  for (y=0; y < (ssize_t) image->rows; y++)
2466  {
2468  sync;
2469 
2470  register const Quantum
2471  *p,
2472  *pixels;
2473 
2474  register ssize_t
2475  x;
2476 
2477  register Quantum
2478  *q;
2479 
2480  size_t
2481  width;
2482 
2483  if (status == MagickFalse)
2484  continue;
2485  pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2486  (y+texture_image->tile_offset.y) % texture_image->rows,
2487  texture_image->columns,1,exception);
2488  q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2489  if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2490  {
2491  status=MagickFalse;
2492  continue;
2493  }
2494  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2495  {
2496  register ssize_t
2497  j;
2498 
2499  p=pixels;
2500  width=texture_image->columns;
2501  if ((x+(ssize_t) width) > (ssize_t) image->columns)
2502  width=image->columns-x;
2503  for (j=0; j < (ssize_t) width; j++)
2504  {
2505  register ssize_t
2506  i;
2507 
2508  for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2509  {
2510  PixelChannel channel = GetPixelChannelChannel(texture_image,i);
2511  PixelTrait traits = GetPixelChannelTraits(image,channel);
2512  PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2513  channel);
2514  if ((traits == UndefinedPixelTrait) ||
2515  (texture_traits == UndefinedPixelTrait))
2516  continue;
2517  SetPixelChannel(image,channel,p[i],q);
2518  }
2519  p+=GetPixelChannels(texture_image);
2520  q+=GetPixelChannels(image);
2521  }
2522  }
2523  sync=SyncCacheViewAuthenticPixels(image_view,exception);
2524  if (sync == MagickFalse)
2525  status=MagickFalse;
2526  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2527  {
2529  proceed;
2530 
2532  image->rows);
2533  if (proceed == MagickFalse)
2534  status=MagickFalse;
2535  }
2536  }
2537  texture_view=DestroyCacheView(texture_view);
2538  image_view=DestroyCacheView(image_view);
2539  texture_image=DestroyImage(texture_image);
2540  return(status);
2541 }
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:2377
MagickDoubleType MagickRealType
Definition: magick-type.h:118
MagickExport CacheView * DestroyCacheView(CacheView *cache_view)
Definition: cache-view.c:252
#define TransparentAlpha
Definition: image.h:26
double x2
Definition: image.h:107
static MagickBooleanType SetImageProgress(const Image *image, const char *tag, const MagickOffsetType offset, const MagickSizeType extent)
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:1325
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:1692
#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:191
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:1984
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:5476
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:191
#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:127
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:966
MagickBooleanType
Definition: magick-type.h:156
unsigned int MagickStatusType
Definition: magick-type.h:119
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:1505
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:191
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:3438
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:1064
MagickExport MagickBooleanType LogMagickEvent(const LogEventType type, const char *module, const char *function, const size_t line, const char *format,...)
Definition: log.c:1398
size_t signature
Definition: image.h:354
size_t columns
Definition: image.h:172
#define QuantumScale
Definition: magick-type.h:113
ssize_t x
Definition: geometry.h:134
MagickBooleanType(* MagickProgressMonitor)(const char *, const MagickOffsetType, const MagickSizeType, void *)
Definition: monitor.h:26
MagickExport ResampleFilter * DestroyResampleFilter(ResampleFilter *resample_filter)
Definition: resample.c:262
MagickExport MagickBooleanType SetImageStorageClass(Image *image, const ClassType storage_class, ExceptionInfo *exception)
Definition: image.c:2613
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:5949
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:1134
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:191
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:135
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:1177
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:794
ColorspaceType colorspace
Definition: image.h:157
#define QuantumRange
Definition: magick-type.h:83
MagickBooleanType debug
Definition: image.h:334