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