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  }
1616  case DifferenceCompositeOp:
1617  {
1618  pixel=QuantumRange*fabs(Sa-Da);
1619  break;
1620  }
1622  {
1623  pixel=Sa*GetPixelIntensity(source_image,p) >
1624  Da*GetPixelIntensity(image,q) ? Sa : Da;
1625  break;
1626  }
1627  case ModulateCompositeOp:
1628  {
1629  pixel=QuantumRange*Da;
1630  break;
1631  }
1632  case MultiplyCompositeOp:
1633  {
1634  pixel=QuantumRange*Sa*Da;
1635  break;
1636  }
1637  case StereoCompositeOp:
1638  {
1639  pixel=QuantumRange*(Sa+Da)/2;
1640  break;
1641  }
1642  default:
1643  {
1644  pixel=QuantumRange*alpha;
1645  break;
1646  }
1647  }
1648  q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1649  ClampToQuantum(pixel);
1650  continue;
1651  }
1652  if (source_traits == UndefinedPixelTrait)
1653  continue;
1654  /*
1655  Sc: source color.
1656  Dc: canvas color.
1657  */
1658  Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1659  Dc=(MagickRealType) q[i];
1660  if ((traits & CopyPixelTrait) != 0)
1661  {
1662  /*
1663  Copy channel.
1664  */
1665  q[i]=ClampToQuantum(Dc);
1666  continue;
1667  }
1668  /*
1669  Porter-Duff compositions:
1670  Sca: source normalized color multiplied by alpha.
1671  Dca: normalized canvas color multiplied by alpha.
1672  */
1673  Sca=QuantumScale*Sa*Sc;
1674  Dca=QuantumScale*Da*Dc;
1675  SaSca=Sa*PerceptibleReciprocal(Sca);
1676  DcaDa=Dca*PerceptibleReciprocal(Da);
1677  switch (compose)
1678  {
1679  case DarkenCompositeOp:
1680  case LightenCompositeOp:
1682  {
1683  gamma=PerceptibleReciprocal(1.0-alpha);
1684  break;
1685  }
1686  default:
1687  {
1688  gamma=PerceptibleReciprocal(alpha);
1689  break;
1690  }
1691  }
1692  pixel=Dc;
1693  switch (compose)
1694  {
1695  case AlphaCompositeOp:
1696  {
1697  pixel=QuantumRange*Sa;
1698  break;
1699  }
1700  case AtopCompositeOp:
1701  case SrcAtopCompositeOp:
1702  {
1703  pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1704  break;
1705  }
1706  case BlendCompositeOp:
1707  {
1708  pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1709  break;
1710  }
1711  case BlurCompositeOp:
1712  case CopyCompositeOp:
1713  case ReplaceCompositeOp:
1714  case SrcCompositeOp:
1715  {
1716  pixel=QuantumRange*Sca;
1717  break;
1718  }
1719  case DisplaceCompositeOp:
1720  case DistortCompositeOp:
1721  {
1722  pixel=Sc;
1723  break;
1724  }
1725  case BumpmapCompositeOp:
1726  {
1727  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1728  {
1729  pixel=Dc;
1730  break;
1731  }
1732  pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1733  break;
1734  }
1735  case ChangeMaskCompositeOp:
1736  {
1737  pixel=Dc;
1738  break;
1739  }
1740  case ClearCompositeOp:
1741  {
1742  pixel=0.0;
1743  break;
1744  }
1745  case ColorBurnCompositeOp:
1746  {
1747  if ((Sca == 0.0) && (Dca == Da))
1748  {
1749  pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1750  break;
1751  }
1752  if (Sca == 0.0)
1753  {
1754  pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1755  break;
1756  }
1757  pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-DcaDa)*
1758  SaSca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1759  break;
1760  }
1761  case ColorDodgeCompositeOp:
1762  {
1763  if ((Sca*Da+Dca*Sa) >= Sa*Da)
1764  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1765  else
1766  pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(Sa-Sca)+
1767  Sca*(1.0-Da)+Dca*(1.0-Sa));
1768  break;
1769  }
1770  case ColorizeCompositeOp:
1771  {
1772  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1773  {
1774  pixel=Dc;
1775  break;
1776  }
1777  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1778  {
1779  pixel=Sc;
1780  break;
1781  }
1782  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1783  &sans,&sans,&luma);
1784  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1785  &hue,&chroma,&sans);
1786  HCLComposite(hue,chroma,luma,&red,&green,&blue);
1787  switch (channel)
1788  {
1789  case RedPixelChannel: pixel=red; break;
1790  case GreenPixelChannel: pixel=green; break;
1791  case BluePixelChannel: pixel=blue; break;
1792  default: pixel=Dc; break;
1793  }
1794  break;
1795  }
1796  case CopyAlphaCompositeOp:
1797  {
1798  pixel=Dc;
1799  break;
1800  }
1801  case CopyBlackCompositeOp:
1802  {
1803  if (channel == BlackPixelChannel)
1804  pixel=(MagickRealType) (QuantumRange-
1805  GetPixelBlack(source_image,p));
1806  break;
1807  }
1808  case CopyBlueCompositeOp:
1809  case CopyYellowCompositeOp:
1810  {
1811  if (channel == BluePixelChannel)
1812  pixel=(MagickRealType) GetPixelBlue(source_image,p);
1813  break;
1814  }
1815  case CopyGreenCompositeOp:
1817  {
1818  if (channel == GreenPixelChannel)
1819  pixel=(MagickRealType) GetPixelGreen(source_image,p);
1820  break;
1821  }
1822  case CopyRedCompositeOp:
1823  case CopyCyanCompositeOp:
1824  {
1825  if (channel == RedPixelChannel)
1826  pixel=(MagickRealType) GetPixelRed(source_image,p);
1827  break;
1828  }
1829  case DarkenCompositeOp:
1830  {
1831  /*
1832  Darken is equivalent to a 'Minimum' method
1833  OR a greyscale version of a binary 'Or'
1834  OR the 'Intersection' of pixel sets.
1835  */
1836  if ((Sca*Da) < (Dca*Sa))
1837  {
1838  pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1839  break;
1840  }
1841  pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1842  break;
1843  }
1845  {
1846  pixel=Sa*GetPixelIntensity(source_image,p) <
1847  Da*GetPixelIntensity(image,q) ? Sc : Dc;
1848  break;
1849  }
1850  case DifferenceCompositeOp:
1851  {
1852  pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1853  break;
1854  }
1855  case DissolveCompositeOp:
1856  {
1857  pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1858  canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1859  break;
1860  }
1861  case DivideDstCompositeOp:
1862  {
1863  if ((fabs((double) Sca) < MagickEpsilon) &&
1864  (fabs((double) Dca) < MagickEpsilon))
1865  {
1866  pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1867  break;
1868  }
1869  if (fabs((double) Dca) < MagickEpsilon)
1870  {
1871  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1872  break;
1873  }
1874  pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1875  break;
1876  }
1877  case DivideSrcCompositeOp:
1878  {
1879  if ((fabs((double) Dca) < MagickEpsilon) &&
1880  (fabs((double) Sca) < MagickEpsilon))
1881  {
1882  pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1883  break;
1884  }
1885  if (fabs((double) Sca) < MagickEpsilon)
1886  {
1887  pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1888  break;
1889  }
1890  pixel=QuantumRange*gamma*(Dca*Sa*SaSca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1891  break;
1892  }
1893  case DstAtopCompositeOp:
1894  {
1895  pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1896  break;
1897  }
1898  case DstCompositeOp:
1899  case NoCompositeOp:
1900  {
1901  pixel=QuantumRange*Dca;
1902  break;
1903  }
1904  case DstInCompositeOp:
1905  {
1906  pixel=QuantumRange*(Dca*Sa);
1907  break;
1908  }
1909  case DstOutCompositeOp:
1910  {
1911  pixel=QuantumRange*(Dca*(1.0-Sa));
1912  break;
1913  }
1914  case DstOverCompositeOp:
1915  {
1916  pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1917  break;
1918  }
1919  case ExclusionCompositeOp:
1920  {
1921  pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1922  Dca*(1.0-Sa));
1923  break;
1924  }
1925  case HardLightCompositeOp:
1926  {
1927  if ((2.0*Sca) < Sa)
1928  {
1929  pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1930  Sa));
1931  break;
1932  }
1933  pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1934  Dca*(1.0-Sa));
1935  break;
1936  }
1937  case HardMixCompositeOp:
1938  {
1939  pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1940  break;
1941  }
1942  case HueCompositeOp:
1943  {
1944  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1945  {
1946  pixel=Dc;
1947  break;
1948  }
1949  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1950  {
1951  pixel=Sc;
1952  break;
1953  }
1954  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1955  &hue,&chroma,&luma);
1956  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1957  &hue,&sans,&sans);
1958  HCLComposite(hue,chroma,luma,&red,&green,&blue);
1959  switch (channel)
1960  {
1961  case RedPixelChannel: pixel=red; break;
1962  case GreenPixelChannel: pixel=green; break;
1963  case BluePixelChannel: pixel=blue; break;
1964  default: pixel=Dc; break;
1965  }
1966  break;
1967  }
1968  case InCompositeOp:
1969  case SrcInCompositeOp:
1970  {
1971  pixel=QuantumRange*(Sca*Da);
1972  break;
1973  }
1974  case LinearBurnCompositeOp:
1975  {
1976  /*
1977  LinearBurn: as defined by Abode Photoshop, according to
1978  http://www.simplefilter.de/en/basics/mixmods.html is:
1979 
1980  f(Sc,Dc) = Sc + Dc - 1
1981  */
1982  pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1983  break;
1984  }
1986  {
1987  pixel=gamma*(Sa*Sc+Da*Dc);
1988  break;
1989  }
1991  {
1992  /*
1993  LinearLight: as defined by Abode Photoshop, according to
1994  http://www.simplefilter.de/en/basics/mixmods.html is:
1995 
1996  f(Sc,Dc) = Dc + 2*Sc - 1
1997  */
1998  pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1999  break;
2000  }
2001  case LightenCompositeOp:
2002  {
2003  if ((Sca*Da) > (Dca*Sa))
2004  {
2005  pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2006  break;
2007  }
2008  pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2009  break;
2010  }
2012  {
2013  /*
2014  Lighten is equivalent to a 'Maximum' method
2015  OR a greyscale version of a binary 'And'
2016  OR the 'Union' of pixel sets.
2017  */
2018  pixel=Sa*GetPixelIntensity(source_image,p) >
2019  Da*GetPixelIntensity(image,q) ? Sc : Dc;
2020  break;
2021  }
2022  case LuminizeCompositeOp:
2023  {
2024  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2025  {
2026  pixel=Dc;
2027  break;
2028  }
2029  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2030  {
2031  pixel=Sc;
2032  break;
2033  }
2034  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2035  &hue,&chroma,&luma);
2036  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2037  &sans,&sans,&luma);
2038  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2039  switch (channel)
2040  {
2041  case RedPixelChannel: pixel=red; break;
2042  case GreenPixelChannel: pixel=green; break;
2043  case BluePixelChannel: pixel=blue; break;
2044  default: pixel=Dc; break;
2045  }
2046  break;
2047  }
2049  {
2050  /*
2051  'Mathematics' a free form user control mathematical composition
2052  is defined as...
2053 
2054  f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2055 
2056  Where the arguments A,B,C,D are (currently) passed to composite
2057  as a command separated 'geometry' string in "compose:args" image
2058  artifact.
2059 
2060  A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2061 
2062  Applying the SVG transparency formula (see above), we get...
2063 
2064  Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2065 
2066  Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2067  Dca*(1.0-Sa)
2068  */
2069  pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
2070  geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2071  geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2072  break;
2073  }
2074  case MinusDstCompositeOp:
2075  {
2076  pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2077  break;
2078  }
2079  case MinusSrcCompositeOp:
2080  {
2081  /*
2082  Minus source from canvas.
2083 
2084  f(Sc,Dc) = Sc - Dc
2085  */
2086  pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2087  break;
2088  }
2089  case ModulateCompositeOp:
2090  {
2091  ssize_t
2092  offset;
2093 
2094  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2095  {
2096  pixel=Dc;
2097  break;
2098  }
2099  offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2100  if (offset == 0)
2101  {
2102  pixel=Dc;
2103  break;
2104  }
2105  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2106  &hue,&chroma,&luma);
2107  luma+=(0.01*percent_luma*offset)/midpoint;
2108  chroma*=0.01*percent_chroma;
2109  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2110  switch (channel)
2111  {
2112  case RedPixelChannel: pixel=red; break;
2113  case GreenPixelChannel: pixel=green; break;
2114  case BluePixelChannel: pixel=blue; break;
2115  default: pixel=Dc; break;
2116  }
2117  break;
2118  }
2119  case ModulusAddCompositeOp:
2120  {
2121  pixel=Sc+Dc;
2122  while (pixel > QuantumRange)
2123  pixel-=QuantumRange;
2124  while (pixel < 0.0)
2125  pixel+=QuantumRange;
2126  pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2127  break;
2128  }
2130  {
2131  pixel=Sc-Dc;
2132  while (pixel > QuantumRange)
2133  pixel-=QuantumRange;
2134  while (pixel < 0.0)
2135  pixel+=QuantumRange;
2136  pixel=(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2137  break;
2138  }
2139  case MultiplyCompositeOp:
2140  {
2141  pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2142  break;
2143  }
2144  case OutCompositeOp:
2145  case SrcOutCompositeOp:
2146  {
2147  pixel=QuantumRange*(Sca*(1.0-Da));
2148  break;
2149  }
2150  case OverCompositeOp:
2151  case SrcOverCompositeOp:
2152  {
2153  pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
2154  break;
2155  }
2156  case OverlayCompositeOp:
2157  {
2158  if ((2.0*Dca) < Da)
2159  {
2160  pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-
2161  Da));
2162  break;
2163  }
2164  pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2165  Sca*(1.0-Da));
2166  break;
2167  }
2169  {
2170  /*
2171  PegTop: A Soft-Light alternative: A continuous version of the
2172  Softlight function, producing very similar results.
2173 
2174  f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2175 
2176  http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2177  */
2178  if (fabs((double) Da) < MagickEpsilon)
2179  {
2180  pixel=QuantumRange*gamma*(Sca);
2181  break;
2182  }
2183  pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2184  Da)+Dca*(1.0-Sa));
2185  break;
2186  }
2187  case PinLightCompositeOp:
2188  {
2189  /*
2190  PinLight: A Photoshop 7 composition method
2191  http://www.simplefilter.de/en/basics/mixmods.html
2192 
2193  f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2194  */
2195  if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2196  {
2197  pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2198  break;
2199  }
2200  if ((Dca*Sa) > (2.0*Sca*Da))
2201  {
2202  pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2203  break;
2204  }
2205  pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2206  break;
2207  }
2208  case PlusCompositeOp:
2209  {
2210  pixel=QuantumRange*(Sca+Dca);
2211  break;
2212  }
2213  case SaturateCompositeOp:
2214  {
2215  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2216  {
2217  pixel=Dc;
2218  break;
2219  }
2220  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2221  {
2222  pixel=Sc;
2223  break;
2224  }
2225  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2226  &hue,&chroma,&luma);
2227  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2228  &sans,&chroma,&sans);
2229  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2230  switch (channel)
2231  {
2232  case RedPixelChannel: pixel=red; break;
2233  case GreenPixelChannel: pixel=green; break;
2234  case BluePixelChannel: pixel=blue; break;
2235  default: pixel=Dc; break;
2236  }
2237  break;
2238  }
2239  case ScreenCompositeOp:
2240  {
2241  /*
2242  Screen: a negated multiply:
2243 
2244  f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2245  */
2246  pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2247  break;
2248  }
2249  case SoftLightCompositeOp:
2250  {
2251  if ((2.0*Sca) < Sa)
2252  {
2253  pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-DcaDa))+
2254  Sca*(1.0-Da)+Dca*(1.0-Sa));
2255  break;
2256  }
2257  if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2258  {
2259  pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*DcaDa*
2260  (4.0*DcaDa+1.0)*(DcaDa-1.0)+7.0*DcaDa)+Sca*(1.0-Da)+
2261  Dca*(1.0-Sa));
2262  break;
2263  }
2264  pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow(DcaDa,0.5)-
2265  DcaDa)+Sca*(1.0-Da)+Dca*(1.0-Sa));
2266  break;
2267  }
2268  case StereoCompositeOp:
2269  {
2270  if (channel == RedPixelChannel)
2271  pixel=(MagickRealType) GetPixelRed(source_image,p);
2272  break;
2273  }
2274  case ThresholdCompositeOp:
2275  {
2277  delta;
2278 
2279  delta=Sc-Dc;
2280  if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2281  {
2282  pixel=gamma*Dc;
2283  break;
2284  }
2285  pixel=gamma*(Dc+delta*amount);
2286  break;
2287  }
2288  case VividLightCompositeOp:
2289  {
2290  /*
2291  VividLight: A Photoshop 7 composition method. See
2292  http://www.simplefilter.de/en/basics/mixmods.html.
2293 
2294  f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2295  */
2296  if ((fabs((double) Sa) < MagickEpsilon) ||
2297  (fabs((double) (Sca-Sa)) < MagickEpsilon))
2298  {
2299  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2300  break;
2301  }
2302  if ((2.0*Sca) <= Sa)
2303  {
2304  pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)*
2305  PerceptibleReciprocal(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2306  break;
2307  }
2308  pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(2.0*
2309  (Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2310  break;
2311  }
2312  case XorCompositeOp:
2313  {
2314  pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2315  break;
2316  }
2317  default:
2318  {
2319  pixel=Sc;
2320  break;
2321  }
2322  }
2323  q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2324  }
2325  p+=GetPixelChannels(source_image);
2326  channels=GetPixelChannels(source_image);
2327  if (p >= (pixels+channels*source_image->columns))
2328  p=pixels;
2329  q+=GetPixelChannels(image);
2330  }
2331  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2332  status=MagickFalse;
2333  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2334  {
2336  proceed;
2337 
2338 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2339  #pragma omp critical (MagickCore_CompositeImage)
2340 #endif
2341  proceed=SetImageProgress(image,CompositeImageTag,progress++,
2342  image->rows);
2343  if (proceed == MagickFalse)
2344  status=MagickFalse;
2345  }
2346  }
2347  source_view=DestroyCacheView(source_view);
2348  image_view=DestroyCacheView(image_view);
2349  if (canvas_image != (Image * ) NULL)
2350  canvas_image=DestroyImage(canvas_image);
2351  else
2352  source_image=DestroyImage(source_image);
2353  return(status);
2354 }
2355 
2356 /*
2357 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2358 % %
2359 % %
2360 % %
2361 % T e x t u r e I m a g e %
2362 % %
2363 % %
2364 % %
2365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2366 %
2367 % TextureImage() repeatedly tiles the texture image across and down the image
2368 % canvas.
2369 %
2370 % The format of the TextureImage method is:
2371 %
2372 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2373 % ExceptionInfo *exception)
2374 %
2375 % A description of each parameter follows:
2376 %
2377 % o image: the image.
2378 %
2379 % o texture_image: This image is the texture to layer on the background.
2380 %
2381 */
2383  ExceptionInfo *exception)
2384 {
2385 #define TextureImageTag "Texture/Image"
2386 
2387  CacheView
2388  *image_view,
2389  *texture_view;
2390 
2391  Image
2392  *texture_image;
2393 
2395  status;
2396 
2397  ssize_t
2398  y;
2399 
2400  assert(image != (Image *) NULL);
2401  if (image->debug != MagickFalse)
2402  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2403  assert(image->signature == MagickCoreSignature);
2404  if (texture == (const Image *) NULL)
2405  return(MagickFalse);
2406  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2407  return(MagickFalse);
2408  texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2409  if (texture_image == (const Image *) NULL)
2410  return(MagickFalse);
2411  (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2413  exception);
2414  status=MagickTrue;
2415  if ((image->compose != CopyCompositeOp) &&
2416  ((image->compose != OverCompositeOp) ||
2417  (image->alpha_trait != UndefinedPixelTrait) ||
2418  (texture_image->alpha_trait != UndefinedPixelTrait)))
2419  {
2420  /*
2421  Tile texture onto the image background.
2422  */
2423  for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2424  {
2425  register ssize_t
2426  x;
2427 
2428  if (status == MagickFalse)
2429  continue;
2430  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2431  {
2433  thread_status;
2434 
2435  thread_status=CompositeImage(image,texture_image,image->compose,
2436  MagickTrue,x+texture_image->tile_offset.x,y+
2437  texture_image->tile_offset.y,exception);
2438  if (thread_status == MagickFalse)
2439  {
2440  status=thread_status;
2441  break;
2442  }
2443  }
2444  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2445  {
2447  proceed;
2448 
2450  image->rows);
2451  if (proceed == MagickFalse)
2452  status=MagickFalse;
2453  }
2454  }
2456  image->rows,image->rows);
2457  texture_image=DestroyImage(texture_image);
2458  return(status);
2459  }
2460  /*
2461  Tile texture onto the image background (optimized).
2462  */
2463  status=MagickTrue;
2464  texture_view=AcquireVirtualCacheView(texture_image,exception);
2465  image_view=AcquireAuthenticCacheView(image,exception);
2466 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2467  #pragma omp parallel for schedule(static) shared(status) \
2468  magick_number_threads(texture_image,image,image->rows,1)
2469 #endif
2470  for (y=0; y < (ssize_t) image->rows; y++)
2471  {
2473  sync;
2474 
2475  register const Quantum
2476  *p,
2477  *pixels;
2478 
2479  register ssize_t
2480  x;
2481 
2482  register Quantum
2483  *q;
2484 
2485  size_t
2486  width;
2487 
2488  if (status == MagickFalse)
2489  continue;
2490  pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2491  (y+texture_image->tile_offset.y) % texture_image->rows,
2492  texture_image->columns,1,exception);
2493  q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2494  if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2495  {
2496  status=MagickFalse;
2497  continue;
2498  }
2499  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2500  {
2501  register ssize_t
2502  j;
2503 
2504  p=pixels;
2505  width=texture_image->columns;
2506  if ((x+(ssize_t) width) > (ssize_t) image->columns)
2507  width=image->columns-x;
2508  for (j=0; j < (ssize_t) width; j++)
2509  {
2510  register ssize_t
2511  i;
2512 
2513  for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2514  {
2515  PixelChannel channel = GetPixelChannelChannel(texture_image,i);
2516  PixelTrait traits = GetPixelChannelTraits(image,channel);
2517  PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2518  channel);
2519  if ((traits == UndefinedPixelTrait) ||
2520  (texture_traits == UndefinedPixelTrait))
2521  continue;
2522  SetPixelChannel(image,channel,p[i],q);
2523  }
2524  p+=GetPixelChannels(texture_image);
2525  q+=GetPixelChannels(image);
2526  }
2527  }
2528  sync=SyncCacheViewAuthenticPixels(image_view,exception);
2529  if (sync == MagickFalse)
2530  status=MagickFalse;
2531  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2532  {
2534  proceed;
2535 
2537  image->rows);
2538  if (proceed == MagickFalse)
2539  status=MagickFalse;
2540  }
2541  }
2542  texture_view=DestroyCacheView(texture_view);
2543  image_view=DestroyCacheView(image_view);
2544  texture_image=DestroyImage(texture_image);
2545  return(status);
2546 }
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:2382
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 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: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:969
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: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:3485
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:115
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