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