MagickCore  7.0.10
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 FreezeCompositeOp:
1415  case HardLightCompositeOp:
1416  case HardMixCompositeOp:
1418  case LightenCompositeOp:
1419  case LinearBurnCompositeOp:
1423  case MinusDstCompositeOp:
1424  case MinusSrcCompositeOp:
1425  case MultiplyCompositeOp:
1426  case NegateCompositeOp:
1427  case OverlayCompositeOp:
1429  case PinLightCompositeOp:
1430  case ReflectCompositeOp:
1431  case ScreenCompositeOp:
1432  case SoftBurnCompositeOp:
1433  case SoftDodgeCompositeOp:
1434  case SoftLightCompositeOp:
1435  case StampCompositeOp:
1436  case VividLightCompositeOp:
1437  {
1438  alpha=RoundToUnity(Sa+Da-Sa*Da);
1439  break;
1440  }
1441  case DstAtopCompositeOp:
1442  case DstInCompositeOp:
1443  case InCompositeOp:
1444  case SrcInCompositeOp:
1445  {
1446  alpha=Sa*Da;
1447  break;
1448  }
1449  case DissolveCompositeOp:
1450  {
1451  alpha=source_dissolve*Sa*(-canvas_dissolve*Da)+source_dissolve*Sa+
1452  canvas_dissolve*Da;
1453  break;
1454  }
1455  case DstOverCompositeOp:
1456  case OverCompositeOp:
1457  case SrcOverCompositeOp:
1458  {
1459  alpha=Sa+Da-Sa*Da;
1460  break;
1461  }
1462  case DstOutCompositeOp:
1463  {
1464  alpha=Da*(1.0-Sa);
1465  break;
1466  }
1467  case OutCompositeOp:
1468  case SrcOutCompositeOp:
1469  {
1470  alpha=Sa*(1.0-Da);
1471  break;
1472  }
1473  case BlendCompositeOp:
1474  case PlusCompositeOp:
1475  {
1476  alpha=RoundToUnity(source_dissolve*Sa+canvas_dissolve*Da);
1477  break;
1478  }
1479  case XorCompositeOp:
1480  {
1481  alpha=Sa+Da-2.0*Sa*Da;
1482  break;
1483  }
1484  case ModulusAddCompositeOp:
1485  {
1486  if ((Sa+Da) <= 1.0)
1487  {
1488  alpha=(Sa+Da);
1489  break;
1490  }
1491  alpha=((Sa+Da)-1.0);
1492  break;
1493  }
1495  {
1496  if ((Sa-Da) >= 0.0)
1497  {
1498  alpha=(Sa-Da);
1499  break;
1500  }
1501  alpha=((Sa-Da)+1.0);
1502  break;
1503  }
1504  default:
1505  {
1506  alpha=1.0;
1507  break;
1508  }
1509  }
1510  switch (compose)
1511  {
1512  case ColorizeCompositeOp:
1513  case HueCompositeOp:
1514  case LuminizeCompositeOp:
1515  case ModulateCompositeOp:
1516  case SaturateCompositeOp:
1517  {
1518  GetPixelInfoPixel(source_image,p,&source_pixel);
1519  GetPixelInfoPixel(image,q,&canvas_pixel);
1520  break;
1521  }
1522  default:
1523  break;
1524  }
1525  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1526  {
1528  pixel,
1529  sans;
1530 
1531  PixelChannel channel = GetPixelChannelChannel(image,i);
1532  PixelTrait traits = GetPixelChannelTraits(image,channel);
1533  PixelTrait source_traits = GetPixelChannelTraits(source_image,channel);
1534  if (traits == UndefinedPixelTrait)
1535  continue;
1536  if ((channel == AlphaPixelChannel) &&
1537  ((traits & UpdatePixelTrait) != 0))
1538  {
1539  /*
1540  Set alpha channel.
1541  */
1542  switch (compose)
1543  {
1544  case AlphaCompositeOp:
1545  {
1546  pixel=QuantumRange*Sa;
1547  break;
1548  }
1549  case AtopCompositeOp:
1550  case CopyBlackCompositeOp:
1551  case CopyBlueCompositeOp:
1552  case CopyCyanCompositeOp:
1553  case CopyGreenCompositeOp:
1555  case CopyRedCompositeOp:
1556  case CopyYellowCompositeOp:
1557  case SrcAtopCompositeOp:
1558  case DstCompositeOp:
1559  case NoCompositeOp:
1560  {
1561  pixel=QuantumRange*Da;
1562  break;
1563  }
1564  case ChangeMaskCompositeOp:
1565  {
1567  equivalent;
1568 
1569  if (Da < 0.5)
1570  {
1572  break;
1573  }
1574  equivalent=IsFuzzyEquivalencePixel(source_image,p,image,q);
1575  if (equivalent != MagickFalse)
1577  else
1578  pixel=(MagickRealType) OpaqueAlpha;
1579  break;
1580  }
1581  case ClearCompositeOp:
1582  {
1584  break;
1585  }
1586  case ColorizeCompositeOp:
1587  case HueCompositeOp:
1588  case LuminizeCompositeOp:
1589  case SaturateCompositeOp:
1590  {
1591  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1592  {
1593  pixel=QuantumRange*Da;
1594  break;
1595  }
1596  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1597  {
1598  pixel=QuantumRange*Sa;
1599  break;
1600  }
1601  if (Sa < Da)
1602  {
1603  pixel=QuantumRange*Da;
1604  break;
1605  }
1606  pixel=QuantumRange*Sa;
1607  break;
1608  }
1609  case CopyAlphaCompositeOp:
1610  {
1611  if (source_image->alpha_trait == UndefinedPixelTrait)
1612  pixel=GetPixelIntensity(source_image,p);
1613  else
1614  pixel=QuantumRange*Sa;
1615  break;
1616  }
1617  case CopyCompositeOp:
1618  case DisplaceCompositeOp:
1619  case DistortCompositeOp:
1620  case DstAtopCompositeOp:
1621  case ReplaceCompositeOp:
1622  case SrcCompositeOp:
1623  {
1624  pixel=QuantumRange*Sa;
1625  break;
1626  }
1628  {
1629  pixel=Sa*GetPixelIntensity(source_image,p) <
1630  Da*GetPixelIntensity(image,q) ? Sa : Da;
1631  break;
1632  }
1633  case DifferenceCompositeOp:
1634  {
1635  pixel=QuantumRange*fabs(Sa-Da);
1636  break;
1637  }
1638  case FreezeCompositeOp:
1639  {
1640  pixel=QuantumRange*(1.0-(1.0-Sa)*(1.0-Sa)*
1641  PerceptibleReciprocal(Da));
1642  if (pixel < 0.0)
1643  pixel=0.0;
1644  break;
1645  }
1647  {
1648  pixel=QuantumRange*(0.5-0.25*cos(MagickPI*Sa)-0.25*
1649  cos(MagickPI*Da));
1650  break;
1651  }
1653  {
1654  pixel=Sa*GetPixelIntensity(source_image,p) >
1655  Da*GetPixelIntensity(image,q) ? Sa : Da;
1656  break;
1657  }
1658  case ModulateCompositeOp:
1659  {
1660  pixel=QuantumRange*Da;
1661  break;
1662  }
1663  case MultiplyCompositeOp:
1664  {
1665  pixel=QuantumRange*Sa*Da;
1666  break;
1667  }
1668  case NegateCompositeOp:
1669  {
1670  pixel=QuantumRange*((1.0-Sa-Da));
1671  break;
1672  }
1673  case ReflectCompositeOp:
1674  {
1675  pixel=QuantumRange*(Sa*Sa*PerceptibleReciprocal(1.0-Da));
1676  if (pixel > QuantumRange)
1677  pixel=QuantumRange;
1678  break;
1679  }
1680  case StampCompositeOp:
1681  {
1682  pixel=QuantumRange*(Sa+Da*Da-1.0);
1683  break;
1684  }
1685  case StereoCompositeOp:
1686  {
1687  pixel=QuantumRange*(Sa+Da)/2;
1688  break;
1689  }
1690  default:
1691  {
1692  pixel=QuantumRange*alpha;
1693  break;
1694  }
1695  }
1696  q[i]=clamp != MagickFalse ? ClampPixel(pixel) :
1697  ClampToQuantum(pixel);
1698  continue;
1699  }
1700  if (source_traits == UndefinedPixelTrait)
1701  continue;
1702  /*
1703  Sc: source color.
1704  Dc: canvas color.
1705  */
1706  Sc=(MagickRealType) GetPixelChannel(source_image,channel,p);
1707  Dc=(MagickRealType) q[i];
1708  if ((traits & CopyPixelTrait) != 0)
1709  {
1710  /*
1711  Copy channel.
1712  */
1713  q[i]=ClampToQuantum(Dc);
1714  continue;
1715  }
1716  /*
1717  Porter-Duff compositions:
1718  Sca: source normalized color multiplied by alpha.
1719  Dca: normalized canvas color multiplied by alpha.
1720  */
1721  Sca=QuantumScale*Sa*Sc;
1722  Dca=QuantumScale*Da*Dc;
1723  SaSca=Sa*PerceptibleReciprocal(Sca);
1724  DcaDa=Dca*PerceptibleReciprocal(Da);
1725  switch (compose)
1726  {
1727  case DarkenCompositeOp:
1728  case LightenCompositeOp:
1730  {
1731  gamma=PerceptibleReciprocal(1.0-alpha);
1732  break;
1733  }
1734  default:
1735  {
1736  gamma=PerceptibleReciprocal(alpha);
1737  break;
1738  }
1739  }
1740  pixel=Dc;
1741  switch (compose)
1742  {
1743  case AlphaCompositeOp:
1744  {
1745  pixel=QuantumRange*Sa;
1746  break;
1747  }
1748  case AtopCompositeOp:
1749  case SrcAtopCompositeOp:
1750  {
1751  pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
1752  break;
1753  }
1754  case BlendCompositeOp:
1755  {
1756  pixel=gamma*(source_dissolve*Sa*Sc+canvas_dissolve*Da*Dc);
1757  break;
1758  }
1759  case BlurCompositeOp:
1760  case CopyCompositeOp:
1761  case ReplaceCompositeOp:
1762  case SrcCompositeOp:
1763  {
1764  pixel=QuantumRange*Sca;
1765  break;
1766  }
1767  case DisplaceCompositeOp:
1768  case DistortCompositeOp:
1769  {
1770  pixel=Sc;
1771  break;
1772  }
1773  case BumpmapCompositeOp:
1774  {
1775  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1776  {
1777  pixel=Dc;
1778  break;
1779  }
1780  pixel=QuantumScale*GetPixelIntensity(source_image,p)*Dc;
1781  break;
1782  }
1783  case ChangeMaskCompositeOp:
1784  {
1785  pixel=Dc;
1786  break;
1787  }
1788  case ClearCompositeOp:
1789  {
1790  pixel=0.0;
1791  break;
1792  }
1793  case ColorBurnCompositeOp:
1794  {
1795  if ((Sca == 0.0) && (Dca == Da))
1796  {
1797  pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
1798  break;
1799  }
1800  if (Sca == 0.0)
1801  {
1802  pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1803  break;
1804  }
1805  pixel=QuantumRange*gamma*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-DcaDa)*
1806  SaSca)+Sca*(1.0-Da)+Dca*(1.0-Sa));
1807  break;
1808  }
1809  case ColorDodgeCompositeOp:
1810  {
1811  if ((Sca*Da+Dca*Sa) >= Sa*Da)
1812  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1813  else
1814  pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(Sa-Sca)+
1815  Sca*(1.0-Da)+Dca*(1.0-Sa));
1816  break;
1817  }
1818  case ColorizeCompositeOp:
1819  {
1820  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
1821  {
1822  pixel=Dc;
1823  break;
1824  }
1825  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
1826  {
1827  pixel=Sc;
1828  break;
1829  }
1830  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
1831  &sans,&sans,&luma);
1832  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1833  &hue,&chroma,&sans);
1834  HCLComposite(hue,chroma,luma,&red,&green,&blue);
1835  switch (channel)
1836  {
1837  case RedPixelChannel: pixel=red; break;
1838  case GreenPixelChannel: pixel=green; break;
1839  case BluePixelChannel: pixel=blue; break;
1840  default: pixel=Dc; break;
1841  }
1842  break;
1843  }
1844  case CopyAlphaCompositeOp:
1845  {
1846  pixel=Dc;
1847  break;
1848  }
1849  case CopyBlackCompositeOp:
1850  {
1851  if (channel == BlackPixelChannel)
1852  pixel=(MagickRealType) GetPixelBlack(source_image,p);
1853  break;
1854  }
1855  case CopyBlueCompositeOp:
1856  case CopyYellowCompositeOp:
1857  {
1858  if (channel == BluePixelChannel)
1859  pixel=(MagickRealType) GetPixelBlue(source_image,p);
1860  break;
1861  }
1862  case CopyGreenCompositeOp:
1864  {
1865  if (channel == GreenPixelChannel)
1866  pixel=(MagickRealType) GetPixelGreen(source_image,p);
1867  break;
1868  }
1869  case CopyRedCompositeOp:
1870  case CopyCyanCompositeOp:
1871  {
1872  if (channel == RedPixelChannel)
1873  pixel=(MagickRealType) GetPixelRed(source_image,p);
1874  break;
1875  }
1876  case DarkenCompositeOp:
1877  {
1878  /*
1879  Darken is equivalent to a 'Minimum' method
1880  OR a greyscale version of a binary 'Or'
1881  OR the 'Intersection' of pixel sets.
1882  */
1883  if ((Sca*Da) < (Dca*Sa))
1884  {
1885  pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
1886  break;
1887  }
1888  pixel=QuantumRange*(Dca+Sca*(1.0-Da));
1889  break;
1890  }
1892  {
1893  pixel=Sa*GetPixelIntensity(source_image,p) <
1894  Da*GetPixelIntensity(image,q) ? Sc : Dc;
1895  break;
1896  }
1897  case DifferenceCompositeOp:
1898  {
1899  pixel=QuantumRange*gamma*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
1900  break;
1901  }
1902  case DissolveCompositeOp:
1903  {
1904  pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1905  canvas_dissolve*Da*Dc+canvas_dissolve*Da*Dc);
1906  break;
1907  }
1908  case DivideDstCompositeOp:
1909  {
1910  if ((fabs((double) Sca) < MagickEpsilon) &&
1911  (fabs((double) Dca) < MagickEpsilon))
1912  {
1913  pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
1914  break;
1915  }
1916  if (fabs((double) Dca) < MagickEpsilon)
1917  {
1918  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
1919  break;
1920  }
1921  pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
1922  break;
1923  }
1924  case DivideSrcCompositeOp:
1925  {
1926  if ((fabs((double) Dca) < MagickEpsilon) &&
1927  (fabs((double) Sca) < MagickEpsilon))
1928  {
1929  pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
1930  break;
1931  }
1932  if (fabs((double) Sca) < MagickEpsilon)
1933  {
1934  pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
1935  break;
1936  }
1937  pixel=QuantumRange*gamma*(Dca*Sa*SaSca+Dca*(1.0-Sa)+Sca*(1.0-Da));
1938  break;
1939  }
1940  case DstAtopCompositeOp:
1941  {
1942  pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
1943  break;
1944  }
1945  case DstCompositeOp:
1946  case NoCompositeOp:
1947  {
1948  pixel=QuantumRange*Dca;
1949  break;
1950  }
1951  case DstInCompositeOp:
1952  {
1953  pixel=QuantumRange*gamma*(Dca*Sa);
1954  break;
1955  }
1956  case DstOutCompositeOp:
1957  {
1958  pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
1959  break;
1960  }
1961  case DstOverCompositeOp:
1962  {
1963  pixel=QuantumRange*gamma*(Dca+Sca*(1.0-Da));
1964  break;
1965  }
1966  case ExclusionCompositeOp:
1967  {
1968  pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1969  Dca*(1.0-Sa));
1970  break;
1971  }
1972  case FreezeCompositeOp:
1973  {
1974  pixel=QuantumRange*gamma*(1.0-(1.0-Sca)*(1.0-Sca)*
1975  PerceptibleReciprocal(Dca));
1976  if (pixel < 0.0)
1977  pixel=0.0;
1978  break;
1979  }
1980  case HardLightCompositeOp:
1981  {
1982  if ((2.0*Sca) < Sa)
1983  {
1984  pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-
1985  Sa));
1986  break;
1987  }
1988  pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
1989  Dca*(1.0-Sa));
1990  break;
1991  }
1992  case HardMixCompositeOp:
1993  {
1994  pixel=gamma*(((Sca+Dca) < 1.0) ? 0.0 : QuantumRange);
1995  break;
1996  }
1997  case HueCompositeOp:
1998  {
1999  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2000  {
2001  pixel=Dc;
2002  break;
2003  }
2004  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2005  {
2006  pixel=Sc;
2007  break;
2008  }
2009  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2010  &hue,&chroma,&luma);
2011  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2012  &hue,&sans,&sans);
2013  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2014  switch (channel)
2015  {
2016  case RedPixelChannel: pixel=red; break;
2017  case GreenPixelChannel: pixel=green; break;
2018  case BluePixelChannel: pixel=blue; break;
2019  default: pixel=Dc; break;
2020  }
2021  break;
2022  }
2023  case InCompositeOp:
2024  case SrcInCompositeOp:
2025  {
2026  pixel=QuantumRange*(Sca*Da);
2027  break;
2028  }
2030  {
2031  pixel=QuantumRange*(0.5-0.25*cos(MagickPI*Sca)-0.25*
2032  cos(MagickPI*Dca));
2033  break;
2034  }
2035  case LinearBurnCompositeOp:
2036  {
2037  /*
2038  LinearBurn: as defined by Abode Photoshop, according to
2039  http://www.simplefilter.de/en/basics/mixmods.html is:
2040 
2041  f(Sc,Dc) = Sc + Dc - 1
2042  */
2043  pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
2044  break;
2045  }
2047  {
2048  pixel=gamma*(Sa*Sc+Da*Dc);
2049  break;
2050  }
2052  {
2053  /*
2054  LinearLight: as defined by Abode Photoshop, according to
2055  http://www.simplefilter.de/en/basics/mixmods.html is:
2056 
2057  f(Sc,Dc) = Dc + 2*Sc - 1
2058  */
2059  pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
2060  break;
2061  }
2062  case LightenCompositeOp:
2063  {
2064  if ((Sca*Da) > (Dca*Sa))
2065  {
2066  pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
2067  break;
2068  }
2069  pixel=QuantumRange*(Dca+Sca*(1.0-Da));
2070  break;
2071  }
2073  {
2074  /*
2075  Lighten is equivalent to a 'Maximum' method
2076  OR a greyscale version of a binary 'And'
2077  OR the 'Union' of pixel sets.
2078  */
2079  pixel=Sa*GetPixelIntensity(source_image,p) >
2080  Da*GetPixelIntensity(image,q) ? Sc : Dc;
2081  break;
2082  }
2083  case LuminizeCompositeOp:
2084  {
2085  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2086  {
2087  pixel=Dc;
2088  break;
2089  }
2090  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2091  {
2092  pixel=Sc;
2093  break;
2094  }
2095  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2096  &hue,&chroma,&luma);
2097  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2098  &sans,&sans,&luma);
2099  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2100  switch (channel)
2101  {
2102  case RedPixelChannel: pixel=red; break;
2103  case GreenPixelChannel: pixel=green; break;
2104  case BluePixelChannel: pixel=blue; break;
2105  default: pixel=Dc; break;
2106  }
2107  break;
2108  }
2110  {
2111  /*
2112  'Mathematics' a free form user control mathematical composition
2113  is defined as...
2114 
2115  f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2116 
2117  Where the arguments A,B,C,D are (currently) passed to composite
2118  as a command separated 'geometry' string in "compose:args" image
2119  artifact.
2120 
2121  A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2122 
2123  Applying the SVG transparency formula (see above), we get...
2124 
2125  Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2126 
2127  Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2128  Dca*(1.0-Sa)
2129  */
2130  pixel=QuantumRange*gamma*(geometry_info.rho*Sca*Dca+
2131  geometry_info.sigma*Sca*Da+geometry_info.xi*Dca*Sa+
2132  geometry_info.psi*Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2133  break;
2134  }
2135  case MinusDstCompositeOp:
2136  {
2137  pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2138  break;
2139  }
2140  case MinusSrcCompositeOp:
2141  {
2142  /*
2143  Minus source from canvas.
2144 
2145  f(Sc,Dc) = Sc - Dc
2146  */
2147  pixel=gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
2148  break;
2149  }
2150  case ModulateCompositeOp:
2151  {
2152  ssize_t
2153  offset;
2154 
2155  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2156  {
2157  pixel=Dc;
2158  break;
2159  }
2160  offset=(ssize_t) (GetPixelIntensity(source_image,p)-midpoint);
2161  if (offset == 0)
2162  {
2163  pixel=Dc;
2164  break;
2165  }
2166  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2167  &hue,&chroma,&luma);
2168  luma+=(0.01*percent_luma*offset)/midpoint;
2169  chroma*=0.01*percent_chroma;
2170  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2171  switch (channel)
2172  {
2173  case RedPixelChannel: pixel=red; break;
2174  case GreenPixelChannel: pixel=green; break;
2175  case BluePixelChannel: pixel=blue; break;
2176  default: pixel=Dc; break;
2177  }
2178  break;
2179  }
2180  case ModulusAddCompositeOp:
2181  {
2182  if ((Sca+Dca) <= 1.0)
2183  {
2184  pixel=QuantumRange*(Sca+Dca);
2185  break;
2186  }
2187  pixel=QuantumRange*((Sca+Dca)-1.0);
2188  break;
2189  }
2191  {
2192  if ((Sca-Dca) >= 0.0)
2193  {
2194  pixel=QuantumRange*(Sca-Dca);
2195  break;
2196  }
2197  pixel=QuantumRange*((Sca-Dca)+1.0);
2198  break;
2199  }
2200  case MultiplyCompositeOp:
2201  {
2202  pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
2203  break;
2204  }
2205  case NegateCompositeOp:
2206  {
2207  pixel=QuantumRange*(1.0-fabs(1.0-Sca-Dca));
2208  break;
2209  }
2210  case OutCompositeOp:
2211  case SrcOutCompositeOp:
2212  {
2213  pixel=QuantumRange*(Sca*(1.0-Da));
2214  break;
2215  }
2216  case OverCompositeOp:
2217  case SrcOverCompositeOp:
2218  {
2219  pixel=QuantumRange*gamma*(Sca+Dca*(1.0-Sa));
2220  break;
2221  }
2222  case OverlayCompositeOp:
2223  {
2224  if ((2.0*Dca) < Da)
2225  {
2226  pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*(1.0-
2227  Da));
2228  break;
2229  }
2230  pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2231  Sca*(1.0-Da));
2232  break;
2233  }
2235  {
2236  /*
2237  PegTop: A Soft-Light alternative: A continuous version of the
2238  Softlight function, producing very similar results.
2239 
2240  f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2241 
2242  http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2243  */
2244  if (fabs((double) Da) < MagickEpsilon)
2245  {
2246  pixel=QuantumRange*gamma*Sca;
2247  break;
2248  }
2249  pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2250  Da)+Dca*(1.0-Sa));
2251  break;
2252  }
2253  case PinLightCompositeOp:
2254  {
2255  /*
2256  PinLight: A Photoshop 7 composition method
2257  http://www.simplefilter.de/en/basics/mixmods.html
2258 
2259  f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2260  */
2261  if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2262  {
2263  pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
2264  break;
2265  }
2266  if ((Dca*Sa) > (2.0*Sca*Da))
2267  {
2268  pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
2269  break;
2270  }
2271  pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
2272  break;
2273  }
2274  case PlusCompositeOp:
2275  {
2276  pixel=QuantumRange*(Sca+Dca);
2277  break;
2278  }
2279  case ReflectCompositeOp:
2280  {
2281  pixel=QuantumRange*gamma*(Sca*Sca*PerceptibleReciprocal(1.0-Dca));
2282  if (pixel > QuantumRange)
2283  pixel=QuantumRange;
2284  break;
2285  }
2286  case SaturateCompositeOp:
2287  {
2288  if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
2289  {
2290  pixel=Dc;
2291  break;
2292  }
2293  if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
2294  {
2295  pixel=Sc;
2296  break;
2297  }
2298  CompositeHCL(canvas_pixel.red,canvas_pixel.green,canvas_pixel.blue,
2299  &hue,&chroma,&luma);
2300  CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2301  &sans,&chroma,&sans);
2302  HCLComposite(hue,chroma,luma,&red,&green,&blue);
2303  switch (channel)
2304  {
2305  case RedPixelChannel: pixel=red; break;
2306  case GreenPixelChannel: pixel=green; break;
2307  case BluePixelChannel: pixel=blue; break;
2308  default: pixel=Dc; break;
2309  }
2310  break;
2311  }
2312  case ScreenCompositeOp:
2313  {
2314  /*
2315  Screen: a negated multiply:
2316 
2317  f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2318  */
2319  pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
2320  break;
2321  }
2322  case SoftBurnCompositeOp:
2323  {
2324  if ((Sca+Dca) < 1.0)
2325  pixel=QuantumRange*gamma*(0.5*Dca*PerceptibleReciprocal(1.0-Sca));
2326  else
2327  pixel=QuantumRange*gamma*(1.0-0.5*(1.0-Sca)*
2328  PerceptibleReciprocal(Dca));
2329  break;
2330  }
2331  case SoftDodgeCompositeOp:
2332  {
2333  if ((Sca+Dca) < 1.0)
2334  pixel=QuantumRange*gamma*(0.5*Sca*PerceptibleReciprocal(1.0-Dca));
2335  else
2336  pixel=QuantumRange*gamma*(1.0-0.5*(1.0-Dca)*
2337  PerceptibleReciprocal(Sca));
2338  break;
2339  }
2340  case SoftLightCompositeOp:
2341  {
2342  if ((2.0*Sca) < Sa)
2343  {
2344  pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-DcaDa))+
2345  Sca*(1.0-Da)+Dca*(1.0-Sa));
2346  break;
2347  }
2348  if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2349  {
2350  pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*DcaDa*
2351  (4.0*DcaDa+1.0)*(DcaDa-1.0)+7.0*DcaDa)+Sca*(1.0-Da)+
2352  Dca*(1.0-Sa));
2353  break;
2354  }
2355  pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow(DcaDa,0.5)-
2356  DcaDa)+Sca*(1.0-Da)+Dca*(1.0-Sa));
2357  break;
2358  }
2359  case StampCompositeOp:
2360  {
2361  pixel=QuantumRange*(Sca+Dca*Dca-1.0);
2362  break;
2363  }
2364  case StereoCompositeOp:
2365  {
2366  if (channel == RedPixelChannel)
2367  pixel=(MagickRealType) GetPixelRed(source_image,p);
2368  break;
2369  }
2370  case ThresholdCompositeOp:
2371  {
2373  delta;
2374 
2375  delta=Sc-Dc;
2376  if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2377  {
2378  pixel=gamma*Dc;
2379  break;
2380  }
2381  pixel=gamma*(Dc+delta*amount);
2382  break;
2383  }
2384  case VividLightCompositeOp:
2385  {
2386  /*
2387  VividLight: A Photoshop 7 composition method. See
2388  http://www.simplefilter.de/en/basics/mixmods.html.
2389 
2390  f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2391  */
2392  if ((fabs((double) Sa) < MagickEpsilon) ||
2393  (fabs((double) (Sca-Sa)) < MagickEpsilon))
2394  {
2395  pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
2396  break;
2397  }
2398  if ((2.0*Sca) <= Sa)
2399  {
2400  pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)*
2401  PerceptibleReciprocal(2.0*Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2402  break;
2403  }
2404  pixel=QuantumRange*gamma*(Dca*Sa*Sa*PerceptibleReciprocal(2.0*
2405  (Sa-Sca))+Sca*(1.0-Da)+Dca*(1.0-Sa));
2406  break;
2407  }
2408  case XorCompositeOp:
2409  {
2410  pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
2411  break;
2412  }
2413  default:
2414  {
2415  pixel=Sc;
2416  break;
2417  }
2418  }
2419  q[i]=clamp != MagickFalse ? ClampPixel(pixel) : ClampToQuantum(pixel);
2420  }
2421  p+=GetPixelChannels(source_image);
2422  channels=GetPixelChannels(source_image);
2423  if (p >= (pixels+channels*source_image->columns))
2424  p=pixels;
2425  q+=GetPixelChannels(image);
2426  }
2427  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2428  status=MagickFalse;
2429  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2430  {
2432  proceed;
2433 
2434 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2435  #pragma omp atomic
2436 #endif
2437  progress++;
2438  proceed=SetImageProgress(image,CompositeImageTag,progress,image->rows);
2439  if (proceed == MagickFalse)
2440  status=MagickFalse;
2441  }
2442  }
2443  source_view=DestroyCacheView(source_view);
2444  image_view=DestroyCacheView(image_view);
2445  if (canvas_image != (Image * ) NULL)
2446  canvas_image=DestroyImage(canvas_image);
2447  else
2448  source_image=DestroyImage(source_image);
2449  return(status);
2450 }
2451 
2452 /*
2453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2454 % %
2455 % %
2456 % %
2457 % T e x t u r e I m a g e %
2458 % %
2459 % %
2460 % %
2461 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2462 %
2463 % TextureImage() repeatedly tiles the texture image across and down the image
2464 % canvas.
2465 %
2466 % The format of the TextureImage method is:
2467 %
2468 % MagickBooleanType TextureImage(Image *image,const Image *texture,
2469 % ExceptionInfo *exception)
2470 %
2471 % A description of each parameter follows:
2472 %
2473 % o image: the image.
2474 %
2475 % o texture_image: This image is the texture to layer on the background.
2476 %
2477 */
2479  ExceptionInfo *exception)
2480 {
2481 #define TextureImageTag "Texture/Image"
2482 
2483  CacheView
2484  *image_view,
2485  *texture_view;
2486 
2487  Image
2488  *texture_image;
2489 
2491  status;
2492 
2493  ssize_t
2494  y;
2495 
2496  assert(image != (Image *) NULL);
2497  if (image->debug != MagickFalse)
2498  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2499  assert(image->signature == MagickCoreSignature);
2500  if (texture == (const Image *) NULL)
2501  return(MagickFalse);
2502  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2503  return(MagickFalse);
2504  texture_image=CloneImage(texture,0,0,MagickTrue,exception);
2505  if (texture_image == (const Image *) NULL)
2506  return(MagickFalse);
2507  (void) TransformImageColorspace(texture_image,image->colorspace,exception);
2509  exception);
2510  status=MagickTrue;
2511  if ((image->compose != CopyCompositeOp) &&
2512  ((image->compose != OverCompositeOp) ||
2513  (image->alpha_trait != UndefinedPixelTrait) ||
2514  (texture_image->alpha_trait != UndefinedPixelTrait)))
2515  {
2516  /*
2517  Tile texture onto the image background.
2518  */
2519  for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
2520  {
2521  register ssize_t
2522  x;
2523 
2524  if (status == MagickFalse)
2525  continue;
2526  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2527  {
2529  thread_status;
2530 
2531  thread_status=CompositeImage(image,texture_image,image->compose,
2532  MagickTrue,x+texture_image->tile_offset.x,y+
2533  texture_image->tile_offset.y,exception);
2534  if (thread_status == MagickFalse)
2535  {
2536  status=thread_status;
2537  break;
2538  }
2539  }
2540  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2541  {
2543  proceed;
2544 
2546  image->rows);
2547  if (proceed == MagickFalse)
2548  status=MagickFalse;
2549  }
2550  }
2552  image->rows,image->rows);
2553  texture_image=DestroyImage(texture_image);
2554  return(status);
2555  }
2556  /*
2557  Tile texture onto the image background (optimized).
2558  */
2559  status=MagickTrue;
2560  texture_view=AcquireVirtualCacheView(texture_image,exception);
2561  image_view=AcquireAuthenticCacheView(image,exception);
2562 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2563  #pragma omp parallel for schedule(static) shared(status) \
2564  magick_number_threads(texture_image,image,image->rows,1)
2565 #endif
2566  for (y=0; y < (ssize_t) image->rows; y++)
2567  {
2569  sync;
2570 
2571  register const Quantum
2572  *p,
2573  *pixels;
2574 
2575  register ssize_t
2576  x;
2577 
2578  register Quantum
2579  *q;
2580 
2581  size_t
2582  width;
2583 
2584  if (status == MagickFalse)
2585  continue;
2586  pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2587  (y+texture_image->tile_offset.y) % texture_image->rows,
2588  texture_image->columns,1,exception);
2589  q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2590  if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2591  {
2592  status=MagickFalse;
2593  continue;
2594  }
2595  for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
2596  {
2597  register ssize_t
2598  j;
2599 
2600  p=pixels;
2601  width=texture_image->columns;
2602  if ((x+(ssize_t) width) > (ssize_t) image->columns)
2603  width=image->columns-x;
2604  for (j=0; j < (ssize_t) width; j++)
2605  {
2606  register ssize_t
2607  i;
2608 
2609  for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2610  {
2611  PixelChannel channel = GetPixelChannelChannel(texture_image,i);
2612  PixelTrait traits = GetPixelChannelTraits(image,channel);
2613  PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2614  channel);
2615  if ((traits == UndefinedPixelTrait) ||
2616  (texture_traits == UndefinedPixelTrait))
2617  continue;
2618  SetPixelChannel(image,channel,p[i],q);
2619  }
2620  p+=GetPixelChannels(texture_image);
2621  q+=GetPixelChannels(image);
2622  }
2623  }
2624  sync=SyncCacheViewAuthenticPixels(image_view,exception);
2625  if (sync == MagickFalse)
2626  status=MagickFalse;
2627  if (image->progress_monitor != (MagickProgressMonitor) NULL)
2628  {
2630  proceed;
2631 
2633  image->rows);
2634  if (proceed == MagickFalse)
2635  status=MagickFalse;
2636  }
2637  }
2638  texture_view=DestroyCacheView(texture_view);
2639  image_view=DestroyCacheView(image_view);
2640  texture_image=DestroyImage(texture_image);
2641  return(status);
2642 }
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:2478
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:1503
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:1723
#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:1972
#define MagickPI
Definition: image-private.h:40
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:5483
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:53
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:1415
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:3477
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:2595
PixelChannel
Definition: pixel.h:67
static double RoundToUnity(const double value)
#define MagickMax(x, y)
Definition: image-private.h:36
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:5956
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:1312
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:37
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:1160
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:775
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