blob: 65cf75a26efe58c6431db0fcc05531f6b7cf431d [file] [log] [blame]
cristy0b3cd892012-08-01 23:13:50 +00001/*
cristy4c08aed2011-07-01 19:47:50 +00002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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% John Cristy %
17% July 1992 %
18% %
19% %
cristy45ef08f2012-12-07 13:13:34 +000020% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
cristy4c08aed2011-07-01 19:47:50 +000021% 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% http://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"
cristy52010022011-10-21 18:07:37 +000046#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/cache-view.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/colorspace-private.h"
53#include "MagickCore/composite.h"
54#include "MagickCore/composite-private.h"
55#include "MagickCore/constitute.h"
56#include "MagickCore/draw.h"
57#include "MagickCore/fx.h"
58#include "MagickCore/gem.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/image.h"
61#include "MagickCore/image-private.h"
62#include "MagickCore/list.h"
63#include "MagickCore/log.h"
64#include "MagickCore/monitor.h"
65#include "MagickCore/monitor-private.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/option.h"
68#include "MagickCore/pixel-accessor.h"
69#include "MagickCore/property.h"
70#include "MagickCore/quantum.h"
71#include "MagickCore/resample.h"
72#include "MagickCore/resource_.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/thread-private.h"
cristy191c0b72012-08-12 16:29:52 +000075#include "MagickCore/threshold.h"
cristy63a81872012-03-22 15:52:52 +000076#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000077#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000078#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000079#include "MagickCore/version.h"
80
81/*
82%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83% %
84% %
85% %
cristyf4ad9df2011-07-08 16:49:03 +000086% C o m p o s i t e I m a g e %
cristy4c08aed2011-07-01 19:47:50 +000087% %
88% %
89% %
90%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91%
cristyf4ad9df2011-07-08 16:49:03 +000092% CompositeImage() returns the second image composited onto the first
cristy4c08aed2011-07-01 19:47:50 +000093% at the specified offset, using the specified composite method.
94%
cristyf4ad9df2011-07-08 16:49:03 +000095% The format of the CompositeImage method is:
cristy4c08aed2011-07-01 19:47:50 +000096%
97% MagickBooleanType CompositeImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +000098% const Image *composite_image,const CompositeOperator compose,
99% const MagickBooleanType clip_to_self,const ssize_t x_offset,
100% const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000101%
102% A description of each parameter follows:
103%
104% o image: the destination image, modified by he composition
105%
cristyfeb3e962012-03-29 17:25:55 +0000106% o composite_image: the composite (source) image.
107%
cristy4c08aed2011-07-01 19:47:50 +0000108% o compose: This operator affects how the composite is applied to
109% the image. The operators and how they are utilized are listed here
110% http://www.w3.org/TR/SVG12/#compositing.
111%
cristyfeb3e962012-03-29 17:25:55 +0000112% o clip_to_self: set to MagickTrue to limit composition to area composed.
cristy4c08aed2011-07-01 19:47:50 +0000113%
114% o x_offset: the column offset of the composited image.
115%
116% o y_offset: the row offset of the composited image.
117%
118% Extra Controls from Image meta-data in 'composite_image' (artifacts)
119%
120% o "compose:args"
121% A string containing extra numerical arguments for specific compose
122% methods, generally expressed as a 'geometry' or a comma separated list
123% of numbers.
124%
125% Compose methods needing such arguments include "BlendCompositeOp" and
126% "DisplaceCompositeOp".
127%
cristye941a752011-10-15 01:52:48 +0000128% o exception: return any errors or warnings in this structure.
129%
cristy4c08aed2011-07-01 19:47:50 +0000130*/
131
anthonyea068a52012-04-09 05:46:25 +0000132/*
133 Composition based on the SVG specification:
134
135 A Composition is defined by...
136 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
137 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
138 Y = 1 for source preserved
139 Z = 1 for destination preserved
140
141 Conversion to transparency (then optimized)
142 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
143 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
144
145 Where...
146 Sca = Sc*Sa normalized Source color divided by Source alpha
147 Dca = Dc*Da normalized Dest color divided by Dest alpha
148 Dc' = Dca'/Da' the desired color value for this channel.
149
150 Da' in in the follow formula as 'gamma' The resulting alpla value.
151
152 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
153 the following optimizations...
154 gamma = Sa+Da-Sa*Da;
155 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
156 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
157
158 The above SVG definitions also definate that Mathematical Composition
159 methods should use a 'Over' blending mode for Alpha Channel.
160 It however was not applied for composition modes of 'Plus', 'Minus',
161 the modulus versions of 'Add' and 'Subtract'.
162
163 Mathematical operator changes to be applied from IM v6.7...
164
165 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
166 'ModulusAdd' and 'ModulusSubtract' for clarity.
167
168 2) All mathematical compositions work as per the SVG specification
169 with regard to blending. This now includes 'ModulusAdd' and
170 'ModulusSubtract'.
171
172 3) When the special channel flag 'sync' (syncronize channel updates)
173 is turned off (enabled by default) then mathematical compositions are
174 only performed on the channels specified, and are applied
175 independantally of each other. In other words the mathematics is
176 performed as 'pure' mathematical operations, rather than as image
177 operations.
178*/
cristy4c08aed2011-07-01 19:47:50 +0000179
cristy7159f662012-10-28 17:32:43 +0000180static inline MagickRealType MagickMin(const MagickRealType x,
181 const MagickRealType y)
cristye4a40472011-12-22 02:56:19 +0000182{
183 if (x < y)
184 return(x);
185 return(y);
186}
cristyddeeea22012-04-12 01:33:09 +0000187
cristy7159f662012-10-28 17:32:43 +0000188static inline MagickRealType MagickMax(const MagickRealType x,
189 const MagickRealType y)
cristye4a40472011-12-22 02:56:19 +0000190{
191 if (x > y)
192 return(x);
193 return(y);
194}
195
cristy7159f662012-10-28 17:32:43 +0000196static inline MagickRealType ConvertHueToRGB(MagickRealType m1,
197 MagickRealType m2,MagickRealType hue)
cristyd8f16f72012-08-13 12:49:50 +0000198{
199 if (hue < 0.0)
200 hue+=1.0;
201 if (hue > 1.0)
202 hue-=1.0;
203 if ((6.0*hue) < 1.0)
204 return(m1+6.0*(m2-m1)*hue);
205 if ((2.0*hue) < 1.0)
206 return(m2);
207 if ((3.0*hue) < 2.0)
208 return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
209 return(m1);
210}
211
cristy7159f662012-10-28 17:32:43 +0000212static void HCLComposite(const MagickRealType hue,const MagickRealType chroma,
213 const MagickRealType luma,MagickRealType *red,MagickRealType *green,
214 MagickRealType *blue)
cristyd8f16f72012-08-13 12:49:50 +0000215{
cristy7159f662012-10-28 17:32:43 +0000216 MagickRealType
cristyd8f16f72012-08-13 12:49:50 +0000217 b,
cristy7133e642012-08-14 11:04:11 +0000218 c,
cristyd8f16f72012-08-13 12:49:50 +0000219 g,
cristy7133e642012-08-14 11:04:11 +0000220 h,
221 m,
cristyd8f16f72012-08-13 12:49:50 +0000222 r,
cristyfbe79202013-02-12 18:08:53 +0000223 x;
cristyd8f16f72012-08-13 12:49:50 +0000224
225 /*
cristy7133e642012-08-14 11:04:11 +0000226 Convert HCL to RGB colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000227 */
cristy7159f662012-10-28 17:32:43 +0000228 assert(red != (MagickRealType *) NULL);
229 assert(green != (MagickRealType *) NULL);
230 assert(blue != (MagickRealType *) NULL);
cristy7133e642012-08-14 11:04:11 +0000231 h=6.0*hue;
232 c=chroma;
233 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
234 r=0.0;
235 g=0.0;
236 b=0.0;
237 if ((0.0 <= h) && (h < 1.0))
cristyd8f16f72012-08-13 12:49:50 +0000238 {
cristye1715282012-08-15 13:10:55 +0000239 r=c;
240 g=x;
cristyd8f16f72012-08-13 12:49:50 +0000241 }
cristyd8f16f72012-08-13 12:49:50 +0000242 else
cristy7133e642012-08-14 11:04:11 +0000243 if ((1.0 <= h) && (h < 2.0))
244 {
cristye1715282012-08-15 13:10:55 +0000245 r=x;
246 g=c;
cristy7133e642012-08-14 11:04:11 +0000247 }
248 else
249 if ((2.0 <= h) && (h < 3.0))
250 {
cristye1715282012-08-15 13:10:55 +0000251 g=c;
252 b=x;
cristy7133e642012-08-14 11:04:11 +0000253 }
254 else
255 if ((3.0 <= h) && (h < 4.0))
256 {
cristye1715282012-08-15 13:10:55 +0000257 g=x;
258 b=c;
cristy7133e642012-08-14 11:04:11 +0000259 }
260 else
261 if ((4.0 <= h) && (h < 5.0))
262 {
cristye1715282012-08-15 13:10:55 +0000263 r=x;
264 b=c;
cristy7133e642012-08-14 11:04:11 +0000265 }
266 else
267 if ((5.0 <= h) && (h < 6.0))
268 {
cristye1715282012-08-15 13:10:55 +0000269 r=c;
270 b=x;
cristy7133e642012-08-14 11:04:11 +0000271 }
cristya86a5cb2012-10-14 13:40:33 +0000272 m=luma-(0.298839f*r+0.586811f*g+0.114350f*b);
cristyfbe79202013-02-12 18:08:53 +0000273 *red=QuantumRange*(r+m);
274 *green=QuantumRange*(g+m);
275 *blue=QuantumRange*(b+m);
cristyd8f16f72012-08-13 12:49:50 +0000276}
277
cristy7159f662012-10-28 17:32:43 +0000278static void CompositeHCL(const MagickRealType red,const MagickRealType green,
279 const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
280 MagickRealType *luma)
cristyd8f16f72012-08-13 12:49:50 +0000281{
cristy7159f662012-10-28 17:32:43 +0000282 MagickRealType
cristyd8f16f72012-08-13 12:49:50 +0000283 b,
cristy7133e642012-08-14 11:04:11 +0000284 c,
cristyd8f16f72012-08-13 12:49:50 +0000285 g,
cristy7133e642012-08-14 11:04:11 +0000286 h,
cristyd8f16f72012-08-13 12:49:50 +0000287 max,
cristyd8f16f72012-08-13 12:49:50 +0000288 r;
289
290 /*
cristy7133e642012-08-14 11:04:11 +0000291 Convert RGB to HCL colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000292 */
cristy7159f662012-10-28 17:32:43 +0000293 assert(hue != (MagickRealType *) NULL);
294 assert(chroma != (MagickRealType *) NULL);
295 assert(luma != (MagickRealType *) NULL);
cristy7133e642012-08-14 11:04:11 +0000296 r=red;
297 g=green;
298 b=blue;
cristyd8f16f72012-08-13 12:49:50 +0000299 max=MagickMax(r,MagickMax(g,b));
cristy7159f662012-10-28 17:32:43 +0000300 c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
cristy7133e642012-08-14 11:04:11 +0000301 h=0.0;
302 if (c == 0)
303 h=0.0;
cristyd8f16f72012-08-13 12:49:50 +0000304 else
cristy7133e642012-08-14 11:04:11 +0000305 if (red == max)
cristyf2c5f4a2012-08-14 23:22:31 +0000306 h=fmod(6.0+(g-b)/c,6.0);
cristyd8f16f72012-08-13 12:49:50 +0000307 else
cristy7133e642012-08-14 11:04:11 +0000308 if (green == max)
309 h=((b-r)/c)+2.0;
310 else
311 if (blue == max)
312 h=((r-g)/c)+4.0;
313 *hue=(h/6.0);
314 *chroma=QuantumScale*c;
cristya86a5cb2012-10-14 13:40:33 +0000315 *luma=QuantumScale*(0.298839f*r+0.586811f*g+0.114350f*b);
cristyd8f16f72012-08-13 12:49:50 +0000316}
317
cristye4a40472011-12-22 02:56:19 +0000318static MagickBooleanType CompositeOverImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000319 const Image *composite_image,const MagickBooleanType clip_to_self,
320 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristye4a40472011-12-22 02:56:19 +0000321{
322#define CompositeImageTag "Composite/Image"
323
324 CacheView
325 *composite_view,
326 *image_view;
327
cristye4a40472011-12-22 02:56:19 +0000328 MagickBooleanType
cristye4a40472011-12-22 02:56:19 +0000329 status;
330
331 MagickOffsetType
332 progress;
333
334 ssize_t
335 y;
336
cristye4a40472011-12-22 02:56:19 +0000337 /*
cristye4a40472011-12-22 02:56:19 +0000338 Composite image.
339 */
340 status=MagickTrue;
341 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000342 composite_view=AcquireVirtualCacheView(composite_image,exception);
343 image_view=AcquireAuthenticCacheView(image,exception);
cristye4a40472011-12-22 02:56:19 +0000344#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000345 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000346 magick_threads(composite_image,image,image->rows,1)
cristye4a40472011-12-22 02:56:19 +0000347#endif
348 for (y=0; y < (ssize_t) image->rows; y++)
349 {
350 const Quantum
351 *pixels;
352
353 register const Quantum
354 *restrict p;
355
356 register Quantum
357 *restrict q;
358
359 register ssize_t
360 x;
361
cristy564a5692012-01-20 23:56:26 +0000362 size_t
363 channels;
364
cristye4a40472011-12-22 02:56:19 +0000365 if (status == MagickFalse)
366 continue;
cristyfeb3e962012-03-29 17:25:55 +0000367 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000368 {
369 if (y < y_offset)
370 continue;
371 if ((y-y_offset) >= (ssize_t) composite_image->rows)
372 continue;
373 }
374 /*
375 If pixels is NULL, y is outside overlay region.
376 */
377 pixels=(Quantum *) NULL;
378 p=(Quantum *) NULL;
379 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
380 {
381 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
382 composite_image->columns,1,exception);
383 if (p == (const Quantum *) NULL)
384 {
385 status=MagickFalse;
386 continue;
387 }
388 pixels=p;
389 if (x_offset < 0)
390 p-=x_offset*GetPixelChannels(composite_image);
391 }
392 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
393 if (q == (Quantum *) NULL)
394 {
395 status=MagickFalse;
396 continue;
397 }
398 for (x=0; x < (ssize_t) image->columns; x++)
399 {
cristy17028dc2013-01-24 12:28:39 +0000400 double
401 gamma;
402
cristy7159f662012-10-28 17:32:43 +0000403 MagickRealType
cristye4a40472011-12-22 02:56:19 +0000404 alpha,
405 Da,
406 Dc,
cristye4a40472011-12-22 02:56:19 +0000407 Sa,
408 Sc;
409
410 register ssize_t
411 i;
412
cristyfeb3e962012-03-29 17:25:55 +0000413 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000414 {
415 if (x < x_offset)
416 {
417 q+=GetPixelChannels(image);
418 continue;
419 }
420 if ((x-x_offset) >= (ssize_t) composite_image->columns)
421 break;
422 }
423 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
424 ((x-x_offset) >= (ssize_t) composite_image->columns))
425 {
426 Quantum
427 source[MaxPixelChannels];
428
429 /*
430 Virtual composite:
431 Sc: source color.
432 Dc: destination color.
433 */
cristy10a6c612012-01-29 21:41:05 +0000434 if (GetPixelMask(image,q) != 0)
435 {
436 q+=GetPixelChannels(image);
437 continue;
438 }
cristyc94ba6f2012-01-29 23:19:58 +0000439 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
440 source,exception);
cristye4a40472011-12-22 02:56:19 +0000441 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
442 {
cristy5a23c552013-02-13 14:34:28 +0000443 PixelChannel channel=GetPixelChannelChannel(image,i);
444 PixelTrait traits=GetPixelChannelTraits(image,channel);
445 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
446 channel);
cristye4a40472011-12-22 02:56:19 +0000447 if ((traits == UndefinedPixelTrait) ||
448 (composite_traits == UndefinedPixelTrait))
449 continue;
450 q[i]=source[channel];
451 }
452 q+=GetPixelChannels(image);
453 continue;
454 }
455 /*
456 Authentic composite:
457 Sa: normalized source alpha.
458 Da: normalized destination alpha.
459 */
cristyc94ba6f2012-01-29 23:19:58 +0000460 if (GetPixelMask(composite_image,p) != 0)
461 {
462 p+=GetPixelChannels(composite_image);
463 channels=GetPixelChannels(composite_image);
464 if (p >= (pixels+channels*composite_image->columns))
465 p=pixels;
466 q+=GetPixelChannels(image);
467 continue;
468 }
cristye4a40472011-12-22 02:56:19 +0000469 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
470 Da=QuantumScale*GetPixelAlpha(image,q);
471 alpha=Sa*(-Da)+Sa+Da;
472 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
473 {
cristy5a23c552013-02-13 14:34:28 +0000474 PixelChannel channel=GetPixelChannelChannel(image,i);
475 PixelTrait traits=GetPixelChannelTraits(image,channel);
476 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
477 channel);
cristye4a40472011-12-22 02:56:19 +0000478 if ((traits == UndefinedPixelTrait) ||
479 (composite_traits == UndefinedPixelTrait))
480 continue;
481 if ((traits & CopyPixelTrait) != 0)
482 {
483 if (channel != AlphaPixelChannel)
484 {
485 /*
486 Copy channel.
487 */
488 q[i]=GetPixelChannel(composite_image,channel,p);
489 continue;
490 }
491 /*
492 Set alpha channel.
493 */
494 q[i]=ClampToQuantum(QuantumRange*alpha);
495 continue;
496 }
497 /*
498 Sc: source color.
499 Dc: destination color.
500 */
cristy7159f662012-10-28 17:32:43 +0000501 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
502 Dc=(MagickRealType) q[i];
cristy3e3ec3a2012-11-03 23:11:06 +0000503 gamma=PerceptibleReciprocal(alpha);
cristye4a40472011-12-22 02:56:19 +0000504 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
505 }
506 p+=GetPixelChannels(composite_image);
507 channels=GetPixelChannels(composite_image);
508 if (p >= (pixels+channels*composite_image->columns))
509 p=pixels;
510 q+=GetPixelChannels(image);
511 }
512 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
513 status=MagickFalse;
514 if (image->progress_monitor != (MagickProgressMonitor) NULL)
515 {
516 MagickBooleanType
517 proceed;
518
519#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000520 #pragma omp critical (MagickCore_CompositeImage)
cristye4a40472011-12-22 02:56:19 +0000521#endif
522 proceed=SetImageProgress(image,CompositeImageTag,progress++,
523 image->rows);
524 if (proceed == MagickFalse)
525 status=MagickFalse;
526 }
527 }
528 composite_view=DestroyCacheView(composite_view);
529 image_view=DestroyCacheView(image_view);
530 return(status);
531}
532
cristy4c08aed2011-07-01 19:47:50 +0000533MagickExport MagickBooleanType CompositeImage(Image *image,
cristya865ccd2012-07-28 00:33:10 +0000534 const Image *composite,const CompositeOperator compose,
cristy44c98412012-03-31 18:25:40 +0000535 const MagickBooleanType clip_to_self,const ssize_t x_offset,
536 const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000537{
cristy4c08aed2011-07-01 19:47:50 +0000538#define CompositeImageTag "Composite/Image"
539
540 CacheView
541 *composite_view,
542 *image_view;
543
cristy4c08aed2011-07-01 19:47:50 +0000544 GeometryInfo
545 geometry_info;
546
547 Image
cristya865ccd2012-07-28 00:33:10 +0000548 *composite_image,
cristy4c08aed2011-07-01 19:47:50 +0000549 *destination_image;
550
551 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000552 status;
553
554 MagickOffsetType
555 progress;
556
cristy7159f662012-10-28 17:32:43 +0000557 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000558 amount,
559 destination_dissolve,
560 midpoint,
cristy7133e642012-08-14 11:04:11 +0000561 percent_luma,
562 percent_chroma,
cristy4c08aed2011-07-01 19:47:50 +0000563 source_dissolve,
564 threshold;
565
566 MagickStatusType
567 flags;
568
cristyd197cbb2012-01-13 02:14:12 +0000569 ssize_t
570 y;
571
cristy4c08aed2011-07-01 19:47:50 +0000572 assert(image != (Image *) NULL);
573 assert(image->signature == MagickSignature);
574 if (image->debug != MagickFalse)
575 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristya865ccd2012-07-28 00:33:10 +0000576 assert(composite!= (Image *) NULL);
577 assert(composite->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000578 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000579 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +0000580 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
581 if (composite_image == (const Image *) NULL)
582 return(MagickFalse);
cristyeb16d5e2012-09-12 20:11:53 +0000583 if (IsGrayColorspace(image->colorspace) != MagickFalse)
584 (void) SetImageColorspace(image,RGBColorspace,exception);
cristya4a22a22012-08-01 23:16:38 +0000585 (void) SetImageColorspace(composite_image,image->colorspace,exception);
cristy60dc90c2013-02-09 18:33:21 +0000586 if ((image->alpha_trait == BlendPixelTrait) &&
587 (composite_image->alpha_trait != BlendPixelTrait))
588 SetImageAlphaChannel(composite_image,SetAlphaChannel,exception);
cristye4a40472011-12-22 02:56:19 +0000589 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
590 {
cristyfeb3e962012-03-29 17:25:55 +0000591 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
592 y_offset,exception);
cristya865ccd2012-07-28 00:33:10 +0000593 composite_image=DestroyImage(composite_image);
cristye4a40472011-12-22 02:56:19 +0000594 return(status);
595 }
cristy4c08aed2011-07-01 19:47:50 +0000596 destination_image=(Image *) NULL;
597 amount=0.5;
598 destination_dissolve=1.0;
cristy7133e642012-08-14 11:04:11 +0000599 percent_luma=100.0;
600 percent_chroma=100.0;
cristy4c08aed2011-07-01 19:47:50 +0000601 source_dissolve=1.0;
602 threshold=0.05f;
603 switch (compose)
604 {
cristy4c08aed2011-07-01 19:47:50 +0000605 case CopyCompositeOp:
606 {
607 if ((x_offset < 0) || (y_offset < 0))
608 break;
609 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
610 break;
611 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
612 break;
613 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +0000614 composite_view=AcquireVirtualCacheView(composite_image,exception);
615 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000616#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000617 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +0000618 magick_threads(composite_image,image,composite_image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000619#endif
620 for (y=0; y < (ssize_t) composite_image->rows; y++)
621 {
622 MagickBooleanType
623 sync;
624
625 register const Quantum
626 *p;
627
628 register Quantum
629 *q;
630
631 register ssize_t
632 x;
633
634 if (status == MagickFalse)
635 continue;
636 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
637 1,exception);
638 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
639 composite_image->columns,1,exception);
640 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
641 {
642 status=MagickFalse;
643 continue;
644 }
645 for (x=0; x < (ssize_t) composite_image->columns; x++)
646 {
cristybdecccc2011-12-24 22:52:16 +0000647 register ssize_t
648 i;
649
cristy665e18f2012-05-17 12:39:54 +0000650 if (GetPixelMask(composite_image,p) != 0)
cristy10a6c612012-01-29 21:41:05 +0000651 {
652 p+=GetPixelChannels(composite_image);
653 q+=GetPixelChannels(image);
654 continue;
655 }
cristybdecccc2011-12-24 22:52:16 +0000656 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
657 {
cristy5a23c552013-02-13 14:34:28 +0000658 PixelChannel channel=GetPixelChannelChannel(composite_image,i);
659 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
660 channel);
661 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristybdecccc2011-12-24 22:52:16 +0000662 if ((traits == UndefinedPixelTrait) ||
663 (composite_traits == UndefinedPixelTrait))
664 continue;
665 SetPixelChannel(image,channel,p[i],q);
666 }
cristyed231572011-07-14 02:18:59 +0000667 p+=GetPixelChannels(composite_image);
668 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000669 }
670 sync=SyncCacheViewAuthenticPixels(image_view,exception);
671 if (sync == MagickFalse)
672 status=MagickFalse;
673 if (image->progress_monitor != (MagickProgressMonitor) NULL)
674 {
675 MagickBooleanType
676 proceed;
677
678#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000679 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000680#endif
681 proceed=SetImageProgress(image,CompositeImageTag,
682 (MagickOffsetType) y,image->rows);
683 if (proceed == MagickFalse)
684 status=MagickFalse;
685 }
686 }
687 composite_view=DestroyCacheView(composite_view);
688 image_view=DestroyCacheView(image_view);
cristy44886b92012-07-28 13:07:09 +0000689 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000690 return(status);
691 }
cristye4a40472011-12-22 02:56:19 +0000692 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000693 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000694 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000695 {
696 /*
697 Modify destination outside the overlaid region and require an alpha
698 channel to exist, to add transparency.
699 */
cristy8a46d822012-08-28 23:32:39 +0000700 if (image->alpha_trait != BlendPixelTrait)
cristy42c41de2012-05-05 18:36:31 +0000701 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000702 break;
703 }
704 case BlurCompositeOp:
705 {
706 CacheView
707 *composite_view,
708 *destination_view;
709
cristyfeb3e962012-03-29 17:25:55 +0000710 const char
711 *value;
712
cristy7159f662012-10-28 17:32:43 +0000713 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000714 angle_range,
715 angle_start,
716 height,
717 width;
718
cristybce4f4a2012-10-14 14:57:47 +0000719 PixelInfo
720 pixel;
721
cristy4c08aed2011-07-01 19:47:50 +0000722 ResampleFilter
723 *resample_filter;
724
725 SegmentInfo
726 blur;
727
728 /*
anthony9cb63cc2012-04-25 06:10:49 +0000729 Blur Image by resampling.
730
cristy4c08aed2011-07-01 19:47:50 +0000731 Blur Image dictated by an overlay gradient map: X = red_channel;
732 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
733 */
734 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000735 exception);
cristy4c08aed2011-07-01 19:47:50 +0000736 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000737 {
738 composite_image=DestroyImage(composite_image);
739 return(MagickFalse);
740 }
cristy4c08aed2011-07-01 19:47:50 +0000741 /*
anthony9cb63cc2012-04-25 06:10:49 +0000742 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000743 */
744 SetGeometryInfo(&geometry_info);
745 flags=NoValue;
cristy1a780952013-02-10 17:15:30 +0000746 value=GetImageArtifact(image,"compose:args");
747 if (value != (const char *) NULL)
cristy4c08aed2011-07-01 19:47:50 +0000748 flags=ParseGeometry(value,&geometry_info);
cristy1a780952013-02-10 17:15:30 +0000749 if ((flags & WidthValue) == 0)
750 {
751 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
752 "InvalidSetting","'%s' '%s'","compose:args",value);
cristy44886b92012-07-28 13:07:09 +0000753 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000754 destination_image=DestroyImage(destination_image);
755 return(MagickFalse);
756 }
anthony9cb63cc2012-04-25 06:10:49 +0000757 /*
758 Users input sigma now needs to be converted to the EWA ellipse size.
759 The filter defaults to a sigma of 0.5 so to make this match the
760 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000761 */
762 width=height=geometry_info.rho*2.0;
763 if ((flags & HeightValue) != 0 )
764 height=geometry_info.sigma*2.0;
cristy7159f662012-10-28 17:32:43 +0000765 /*
766 Default the unrotated ellipse width and height axis vectors.
767 */
anthonyd2923912012-04-23 13:06:53 +0000768 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000769 blur.x2=0.0;
770 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000771 blur.y2=height;
772 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000773 if ((flags & XValue) != 0 )
774 {
cristy7159f662012-10-28 17:32:43 +0000775 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000776 angle;
777
778 angle=DegreesToRadians(geometry_info.xi);
779 blur.x1=width*cos(angle);
780 blur.x2=width*sin(angle);
781 blur.y1=(-height*sin(angle));
782 blur.y2=height*cos(angle);
783 }
anthonyd2923912012-04-23 13:06:53 +0000784 /* Otherwise lets set a angle range and calculate in the loop */
785 angle_start=0.0;
786 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000787 if ((flags & YValue) != 0 )
788 {
789 angle_start=DegreesToRadians(geometry_info.xi);
790 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
791 }
792 /*
anthony9cb63cc2012-04-25 06:10:49 +0000793 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000794
anthony9cb63cc2012-04-25 06:10:49 +0000795 As the minimum ellipse radius of support*1.0 the EWA algorithm
796 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
797 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000798
anthony9cb63cc2012-04-25 06:10:49 +0000799 The solution (as well as the problem of preventing any user
800 expert filter settings, is to set our own user settings, then
801 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000802 */
cristy8a11cb12011-10-19 23:53:34 +0000803 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000804 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000805
806 /* do the variable blurring of each pixel in image */
807 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +0000808 composite_view=AcquireVirtualCacheView(composite_image,exception);
809 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000810 for (y=0; y < (ssize_t) composite_image->rows; y++)
811 {
812 MagickBooleanType
813 sync;
814
815 register const Quantum
816 *restrict p;
817
818 register Quantum
819 *restrict q;
820
821 register ssize_t
822 x;
823
824 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
825 continue;
826 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
827 1,exception);
828 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000829 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000830 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
831 break;
832 for (x=0; x < (ssize_t) composite_image->columns; x++)
833 {
834 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
835 {
cristyed231572011-07-14 02:18:59 +0000836 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000837 continue;
838 }
839 if (fabs(angle_range) > MagickEpsilon)
840 {
cristy7159f662012-10-28 17:32:43 +0000841 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000842 angle;
843
844 angle=angle_start+angle_range*QuantumScale*
845 GetPixelBlue(composite_image,p);
846 blur.x1=width*cos(angle);
847 blur.x2=width*sin(angle);
848 blur.y1=(-height*sin(angle));
849 blur.y2=height*cos(angle);
850 }
anthonyd2923912012-04-23 13:06:53 +0000851#if 0
852 if ( x == 10 && y == 60 ) {
cristy7159f662012-10-28 17:32:43 +0000853 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
854 blur.x2,blur.y1, blur.y2);
855 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
856 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
anthonyd2923912012-04-23 13:06:53 +0000857#endif
858 ScaleResampleFilter(resample_filter,
859 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
860 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
861 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
862 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000863 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000864 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000865 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000866 p+=GetPixelChannels(composite_image);
867 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000868 }
869 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
870 if (sync == MagickFalse)
871 break;
872 }
873 resample_filter=DestroyResampleFilter(resample_filter);
874 composite_view=DestroyCacheView(composite_view);
875 destination_view=DestroyCacheView(destination_view);
cristyf661c4d2012-07-28 12:57:17 +0000876 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000877 composite_image=destination_image;
878 break;
879 }
880 case DisplaceCompositeOp:
881 case DistortCompositeOp:
882 {
883 CacheView
884 *composite_view,
885 *destination_view,
886 *image_view;
887
cristyfeb3e962012-03-29 17:25:55 +0000888 const char
889 *value;
890
cristy4c08aed2011-07-01 19:47:50 +0000891 PixelInfo
892 pixel;
893
cristy7159f662012-10-28 17:32:43 +0000894 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000895 horizontal_scale,
896 vertical_scale;
897
898 PointInfo
899 center,
900 offset;
901
902 /*
903 Displace/Distort based on overlay gradient map:
904 X = red_channel; Y = green_channel;
905 compose:args = x_scale[,y_scale[,center.x,center.y]]
906 */
907 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000908 exception);
cristy4c08aed2011-07-01 19:47:50 +0000909 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000910 {
911 composite_image=DestroyImage(composite_image);
912 return(MagickFalse);
913 }
cristy4c08aed2011-07-01 19:47:50 +0000914 SetGeometryInfo(&geometry_info);
915 flags=NoValue;
916 value=GetImageArtifact(composite_image,"compose:args");
917 if (value != (char *) NULL)
918 flags=ParseGeometry(value,&geometry_info);
919 if ((flags & (WidthValue|HeightValue)) == 0 )
920 {
921 if ((flags & AspectValue) == 0)
922 {
cristy7159f662012-10-28 17:32:43 +0000923 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
cristy4c08aed2011-07-01 19:47:50 +0000924 2.0;
cristy7159f662012-10-28 17:32:43 +0000925 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000926 }
927 else
928 {
cristy7159f662012-10-28 17:32:43 +0000929 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
930 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000931 }
932 }
933 else
934 {
935 horizontal_scale=geometry_info.rho;
936 vertical_scale=geometry_info.sigma;
937 if ((flags & PercentValue) != 0)
938 {
939 if ((flags & AspectValue) == 0)
940 {
941 horizontal_scale*=(composite_image->columns-1.0)/200.0;
942 vertical_scale*=(composite_image->rows-1.0)/200.0;
943 }
944 else
945 {
946 horizontal_scale*=(image->columns-1.0)/200.0;
947 vertical_scale*=(image->rows-1.0)/200.0;
948 }
949 }
950 if ((flags & HeightValue) == 0)
951 vertical_scale=horizontal_scale;
952 }
953 /*
954 Determine fixed center point for absolute distortion map
955 Absolute distort ==
956 Displace offset relative to a fixed absolute point
957 Select that point according to +X+Y user inputs.
958 default = center of overlay image
959 arg flag '!' = locations/percentage relative to background image
960 */
cristy7159f662012-10-28 17:32:43 +0000961 center.x=(MagickRealType) x_offset;
962 center.y=(MagickRealType) y_offset;
cristy4c08aed2011-07-01 19:47:50 +0000963 if (compose == DistortCompositeOp)
964 {
965 if ((flags & XValue) == 0)
966 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000967 center.x=(MagickRealType) (x_offset+(composite_image->columns-1)/
968 2.0);
cristy4c08aed2011-07-01 19:47:50 +0000969 else
cristy7159f662012-10-28 17:32:43 +0000970 center.x=(MagickRealType) ((image->columns-1)/2);
cristy4c08aed2011-07-01 19:47:50 +0000971 else
972 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000973 center.x=(MagickRealType) x_offset+geometry_info.xi;
cristy4c08aed2011-07-01 19:47:50 +0000974 else
975 center.x=geometry_info.xi;
976 if ((flags & YValue) == 0)
977 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000978 center.y=(MagickRealType) (y_offset+(composite_image->rows-1)/
979 2.0);
cristy4c08aed2011-07-01 19:47:50 +0000980 else
cristy7159f662012-10-28 17:32:43 +0000981 center.y=(MagickRealType) ((image->rows-1)/2);
cristy4c08aed2011-07-01 19:47:50 +0000982 else
983 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000984 center.y=(MagickRealType) y_offset+geometry_info.psi;
cristy4c08aed2011-07-01 19:47:50 +0000985 else
986 center.y=geometry_info.psi;
987 }
988 /*
989 Shift the pixel offset point as defined by the provided,
990 displacement/distortion map. -- Like a lens...
991 */
cristye10859a2011-12-18 22:28:59 +0000992 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +0000993 image_view=AcquireVirtualCacheView(image,exception);
994 composite_view=AcquireVirtualCacheView(composite_image,exception);
995 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000996 for (y=0; y < (ssize_t) composite_image->rows; y++)
997 {
998 MagickBooleanType
999 sync;
1000
1001 register const Quantum
1002 *restrict p;
1003
1004 register Quantum
1005 *restrict q;
1006
1007 register ssize_t
1008 x;
1009
1010 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1011 continue;
1012 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1013 1,exception);
1014 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001015 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001016 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1017 break;
1018 for (x=0; x < (ssize_t) composite_image->columns; x++)
1019 {
1020 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1021 {
cristyed231572011-07-14 02:18:59 +00001022 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001023 continue;
1024 }
1025 /*
1026 Displace the offset.
1027 */
cristy7159f662012-10-28 17:32:43 +00001028 offset.x=(double) (horizontal_scale*(GetPixelRed(composite_image,p)-
1029 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1030 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1031 x : 0);
1032 offset.y=(double) (vertical_scale*(GetPixelGreen(composite_image,p)-
1033 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1034 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1035 y : 0);
cristy4c08aed2011-07-01 19:47:50 +00001036 (void) InterpolatePixelInfo(image,image_view,
1037 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1038 &pixel,exception);
1039 /*
1040 Mask with the 'invalid pixel mask' in alpha channel.
1041 */
cristy7159f662012-10-28 17:32:43 +00001042 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1043 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001044 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001045 p+=GetPixelChannels(composite_image);
1046 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001047 }
1048 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1049 if (sync == MagickFalse)
1050 break;
1051 }
1052 destination_view=DestroyCacheView(destination_view);
1053 composite_view=DestroyCacheView(composite_view);
1054 image_view=DestroyCacheView(image_view);
cristyf661c4d2012-07-28 12:57:17 +00001055 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001056 composite_image=destination_image;
1057 break;
1058 }
1059 case DissolveCompositeOp:
1060 {
cristyfeb3e962012-03-29 17:25:55 +00001061 const char
1062 *value;
1063
cristy4c08aed2011-07-01 19:47:50 +00001064 /*
1065 Geometry arguments to dissolve factors.
1066 */
1067 value=GetImageArtifact(composite_image,"compose:args");
1068 if (value != (char *) NULL)
1069 {
1070 flags=ParseGeometry(value,&geometry_info);
1071 source_dissolve=geometry_info.rho/100.0;
1072 destination_dissolve=1.0;
1073 if ((source_dissolve-MagickEpsilon) < 0.0)
1074 source_dissolve=0.0;
1075 if ((source_dissolve+MagickEpsilon) > 1.0)
1076 {
1077 destination_dissolve=2.0-source_dissolve;
1078 source_dissolve=1.0;
1079 }
1080 if ((flags & SigmaValue) != 0)
1081 destination_dissolve=geometry_info.sigma/100.0;
1082 if ((destination_dissolve-MagickEpsilon) < 0.0)
1083 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001084 /* posible speed up? -- from IMv6 update
1085 clip_to_self=MagickFalse;
1086 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1087 {
1088 destination_dissolve=1.0;
1089 clip_to_self=MagickTrue;
1090 }
1091 */
cristy4c08aed2011-07-01 19:47:50 +00001092 }
1093 break;
1094 }
1095 case BlendCompositeOp:
1096 {
cristyfeb3e962012-03-29 17:25:55 +00001097 const char
1098 *value;
1099
cristy4c08aed2011-07-01 19:47:50 +00001100 value=GetImageArtifact(composite_image,"compose:args");
1101 if (value != (char *) NULL)
1102 {
1103 flags=ParseGeometry(value,&geometry_info);
1104 source_dissolve=geometry_info.rho/100.0;
1105 destination_dissolve=1.0-source_dissolve;
1106 if ((flags & SigmaValue) != 0)
1107 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001108 }
1109 break;
1110 }
1111 case MathematicsCompositeOp:
1112 {
cristyfeb3e962012-03-29 17:25:55 +00001113 const char
1114 *value;
1115
cristy4c08aed2011-07-01 19:47:50 +00001116 /*
1117 Just collect the values from "compose:args", setting.
1118 Unused values are set to zero automagically.
1119
1120 Arguments are normally a comma separated list, so this probably should
1121 be changed to some 'general comma list' parser, (with a minimum
1122 number of values)
1123 */
1124 SetGeometryInfo(&geometry_info);
1125 value=GetImageArtifact(composite_image,"compose:args");
1126 if (value != (char *) NULL)
1127 (void) ParseGeometry(value,&geometry_info);
1128 break;
1129 }
1130 case ModulateCompositeOp:
1131 {
cristyfeb3e962012-03-29 17:25:55 +00001132 const char
1133 *value;
1134
cristy4c08aed2011-07-01 19:47:50 +00001135 /*
cristy7133e642012-08-14 11:04:11 +00001136 Determine the luma and chroma scale.
cristy4c08aed2011-07-01 19:47:50 +00001137 */
1138 value=GetImageArtifact(composite_image,"compose:args");
1139 if (value != (char *) NULL)
1140 {
1141 flags=ParseGeometry(value,&geometry_info);
cristy7133e642012-08-14 11:04:11 +00001142 percent_luma=geometry_info.rho;
cristy4c08aed2011-07-01 19:47:50 +00001143 if ((flags & SigmaValue) != 0)
cristy7133e642012-08-14 11:04:11 +00001144 percent_chroma=geometry_info.sigma;
cristy4c08aed2011-07-01 19:47:50 +00001145 }
1146 break;
1147 }
1148 case ThresholdCompositeOp:
1149 {
cristyfeb3e962012-03-29 17:25:55 +00001150 const char
1151 *value;
1152
cristy4c08aed2011-07-01 19:47:50 +00001153 /*
1154 Determine the amount and threshold.
1155 */
1156 value=GetImageArtifact(composite_image,"compose:args");
1157 if (value != (char *) NULL)
1158 {
1159 flags=ParseGeometry(value,&geometry_info);
1160 amount=geometry_info.rho;
1161 threshold=geometry_info.sigma;
1162 if ((flags & SigmaValue) == 0)
1163 threshold=0.05f;
1164 }
1165 threshold*=QuantumRange;
1166 break;
1167 }
1168 default:
1169 break;
1170 }
cristy4c08aed2011-07-01 19:47:50 +00001171 /*
1172 Composite image.
1173 */
1174 status=MagickTrue;
1175 progress=0;
cristy7159f662012-10-28 17:32:43 +00001176 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy46ff2672012-12-14 15:32:26 +00001177 composite_view=AcquireVirtualCacheView(composite_image,exception);
1178 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001179#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001180 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001181 magick_threads(composite_image,image,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001182#endif
1183 for (y=0; y < (ssize_t) image->rows; y++)
1184 {
1185 const Quantum
1186 *pixels;
1187
cristy7159f662012-10-28 17:32:43 +00001188 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001189 blue,
cristy7133e642012-08-14 11:04:11 +00001190 luma,
cristye4a40472011-12-22 02:56:19 +00001191 green,
cristy4c08aed2011-07-01 19:47:50 +00001192 hue,
cristye4a40472011-12-22 02:56:19 +00001193 red,
cristy7133e642012-08-14 11:04:11 +00001194 chroma;
cristy4c08aed2011-07-01 19:47:50 +00001195
cristyddeeea22012-04-12 01:33:09 +00001196 PixelInfo
1197 destination_pixel,
1198 source_pixel;
1199
cristy4c08aed2011-07-01 19:47:50 +00001200 register const Quantum
1201 *restrict p;
1202
1203 register Quantum
1204 *restrict q;
1205
1206 register ssize_t
1207 x;
1208
1209 if (status == MagickFalse)
1210 continue;
cristyfeb3e962012-03-29 17:25:55 +00001211 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001212 {
1213 if (y < y_offset)
1214 continue;
1215 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1216 continue;
1217 }
1218 /*
1219 If pixels is NULL, y is outside overlay region.
1220 */
1221 pixels=(Quantum *) NULL;
1222 p=(Quantum *) NULL;
1223 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1224 {
1225 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1226 composite_image->columns,1,exception);
1227 if (p == (const Quantum *) NULL)
1228 {
1229 status=MagickFalse;
1230 continue;
1231 }
1232 pixels=p;
1233 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001234 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001235 }
1236 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001237 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001238 {
1239 status=MagickFalse;
1240 continue;
1241 }
cristy4c08aed2011-07-01 19:47:50 +00001242 hue=0.0;
cristy7133e642012-08-14 11:04:11 +00001243 chroma=0.0;
1244 luma=0.0;
cristyddeeea22012-04-12 01:33:09 +00001245 GetPixelInfo(image,&destination_pixel);
1246 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001247 for (x=0; x < (ssize_t) image->columns; x++)
1248 {
cristy17028dc2013-01-24 12:28:39 +00001249 double
1250 gamma;
1251
cristy7159f662012-10-28 17:32:43 +00001252 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001253 alpha,
1254 Da,
1255 Dc,
1256 Dca,
cristye4a40472011-12-22 02:56:19 +00001257 Sa,
1258 Sc,
1259 Sca;
1260
1261 register ssize_t
1262 i;
1263
cristy564a5692012-01-20 23:56:26 +00001264 size_t
1265 channels;
1266
cristyfeb3e962012-03-29 17:25:55 +00001267 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001268 {
1269 if (x < x_offset)
1270 {
cristyed231572011-07-14 02:18:59 +00001271 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001272 continue;
1273 }
1274 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1275 break;
1276 }
cristye4a40472011-12-22 02:56:19 +00001277 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1278 ((x-x_offset) >= (ssize_t) composite_image->columns))
1279 {
1280 Quantum
1281 source[MaxPixelChannels];
1282
1283 /*
1284 Virtual composite:
1285 Sc: source color.
1286 Dc: destination color.
1287 */
1288 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1289 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001290 if (GetPixelMask(image,q) != 0)
1291 {
1292 q+=GetPixelChannels(image);
1293 continue;
1294 }
cristye4a40472011-12-22 02:56:19 +00001295 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1296 {
cristy7159f662012-10-28 17:32:43 +00001297 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001298 pixel;
1299
cristy5a23c552013-02-13 14:34:28 +00001300 PixelChannel channel=GetPixelChannelChannel(image,i);
1301 PixelTrait traits=GetPixelChannelTraits(image,channel);
1302 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
1303 channel);
cristye4a40472011-12-22 02:56:19 +00001304 if ((traits == UndefinedPixelTrait) ||
1305 (composite_traits == UndefinedPixelTrait))
1306 continue;
1307 switch (compose)
1308 {
cristyc8d63672012-01-11 13:03:13 +00001309 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001310 case ChangeMaskCompositeOp:
1311 case CopyAlphaCompositeOp:
1312 case DstAtopCompositeOp:
1313 case DstInCompositeOp:
1314 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001315 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001316 case OutCompositeOp:
1317 case SrcInCompositeOp:
1318 case SrcOutCompositeOp:
1319 {
cristy7159f662012-10-28 17:32:43 +00001320 pixel=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001321 if (channel == AlphaPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001322 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001323 break;
1324 }
1325 case ClearCompositeOp:
1326 case CopyCompositeOp:
1327 case ReplaceCompositeOp:
1328 case SrcCompositeOp:
1329 {
1330 if (channel == AlphaPixelChannel)
1331 {
cristy7159f662012-10-28 17:32:43 +00001332 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001333 break;
1334 }
1335 pixel=0.0;
1336 break;
1337 }
cristy99abff32011-12-24 20:45:16 +00001338 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001339 case DissolveCompositeOp:
1340 {
1341 if (channel == AlphaPixelChannel)
1342 {
1343 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1344 source);
1345 break;
1346 }
cristy7159f662012-10-28 17:32:43 +00001347 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001348 break;
1349 }
1350 default:
1351 {
cristy7159f662012-10-28 17:32:43 +00001352 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001353 break;
1354 }
1355 }
1356 q[i]=ClampToQuantum(pixel);
1357 }
1358 q+=GetPixelChannels(image);
1359 continue;
1360 }
1361 /*
1362 Authentic composite:
1363 Sa: normalized source alpha.
1364 Da: normalized destination alpha.
1365 */
1366 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1367 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001368 switch (compose)
1369 {
cristye4a40472011-12-22 02:56:19 +00001370 case BumpmapCompositeOp:
1371 {
1372 alpha=GetPixelIntensity(composite_image,p)*Sa;
1373 break;
1374 }
cristycdc168f2011-12-21 15:24:39 +00001375 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001376 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001377 case DifferenceCompositeOp:
1378 case DivideDstCompositeOp:
1379 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001380 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001381 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001382 case LinearBurnCompositeOp:
1383 case LinearDodgeCompositeOp:
1384 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001385 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001386 case MinusDstCompositeOp:
1387 case MinusSrcCompositeOp:
1388 case ModulusAddCompositeOp:
1389 case ModulusSubtractCompositeOp:
1390 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001391 case OverlayCompositeOp:
1392 case PegtopLightCompositeOp:
1393 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001394 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001395 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001396 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001397 {
1398 alpha=RoundToUnity(Sa+Da-Sa*Da);
1399 break;
1400 }
1401 case DarkenCompositeOp:
1402 case DstAtopCompositeOp:
1403 case DstInCompositeOp:
1404 case InCompositeOp:
1405 case LightenCompositeOp:
1406 case SrcInCompositeOp:
1407 {
1408 alpha=Sa*Da;
1409 break;
1410 }
1411 case DissolveCompositeOp:
1412 {
1413 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1414 Sa+destination_dissolve*Da;
1415 break;
1416 }
1417 case DstOverCompositeOp:
1418 {
1419 alpha=Da*(-Sa)+Da+Sa;
1420 break;
1421 }
1422 case DstOutCompositeOp:
1423 {
1424 alpha=Da*(1.0-Sa);
1425 break;
1426 }
1427 case OutCompositeOp:
1428 case SrcOutCompositeOp:
1429 {
1430 alpha=Sa*(1.0-Da);
1431 break;
1432 }
1433 case OverCompositeOp:
1434 case SrcOverCompositeOp:
1435 {
1436 alpha=Sa*(-Da)+Sa+Da;
1437 break;
1438 }
cristy99abff32011-12-24 20:45:16 +00001439 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001440 case PlusCompositeOp:
1441 {
1442 alpha=RoundToUnity(Sa+Da);
1443 break;
1444 }
cristy4c08aed2011-07-01 19:47:50 +00001445 case XorCompositeOp:
1446 {
cristye4a40472011-12-22 02:56:19 +00001447 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001448 break;
1449 }
1450 default:
cristye4a40472011-12-22 02:56:19 +00001451 {
1452 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001453 break;
cristye4a40472011-12-22 02:56:19 +00001454 }
cristy4c08aed2011-07-01 19:47:50 +00001455 }
cristyd03b8fa2013-02-10 18:59:28 +00001456 if (GetPixelMask(image,q) != 0)
cristy10a6c612012-01-29 21:41:05 +00001457 {
1458 p+=GetPixelChannels(composite_image);
1459 q+=GetPixelChannels(image);
1460 continue;
1461 }
cristy9d3d2792012-04-14 15:15:19 +00001462 switch (compose)
1463 {
1464 case ColorizeCompositeOp:
1465 case HueCompositeOp:
1466 case LuminizeCompositeOp:
1467 case ModulateCompositeOp:
1468 case SaturateCompositeOp:
1469 {
1470 GetPixelInfoPixel(composite_image,p,&source_pixel);
1471 GetPixelInfoPixel(image,q,&destination_pixel);
1472 break;
1473 }
1474 default:
1475 break;
1476 }
cristye4a40472011-12-22 02:56:19 +00001477 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1478 {
cristy7159f662012-10-28 17:32:43 +00001479 MagickRealType
1480 pixel,
cristy564a5692012-01-20 23:56:26 +00001481 sans;
1482
cristy5a23c552013-02-13 14:34:28 +00001483 PixelChannel channel=GetPixelChannelChannel(image,i);
1484 PixelTrait traits=GetPixelChannelTraits(image,channel);
1485 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
1486 channel);
cristy0cd1f212012-01-05 15:45:59 +00001487 if (traits == UndefinedPixelTrait)
1488 continue;
cristya7b07912012-01-11 20:01:32 +00001489 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001490 (composite_traits == UndefinedPixelTrait))
1491 continue;
1492 /*
1493 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001494 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001495 */
cristy7159f662012-10-28 17:32:43 +00001496 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
1497 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001498 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001499 {
cristye4a40472011-12-22 02:56:19 +00001500 if (channel != AlphaPixelChannel)
1501 {
1502 /*
1503 Copy channel.
1504 */
1505 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001506 continue;
cristye10859a2011-12-18 22:28:59 +00001507 }
cristye4a40472011-12-22 02:56:19 +00001508 /*
1509 Set alpha channel.
1510 */
cristye10859a2011-12-18 22:28:59 +00001511 switch (compose)
1512 {
cristyc8d63672012-01-11 13:03:13 +00001513 case AlphaCompositeOp:
1514 {
cristya7b07912012-01-11 20:01:32 +00001515 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001516 break;
1517 }
cristye4a40472011-12-22 02:56:19 +00001518 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001519 case CopyBlackCompositeOp:
1520 case CopyBlueCompositeOp:
1521 case CopyCyanCompositeOp:
1522 case CopyGreenCompositeOp:
1523 case CopyMagentaCompositeOp:
1524 case CopyRedCompositeOp:
1525 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001526 case SrcAtopCompositeOp:
1527 case DstCompositeOp:
1528 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001529 {
cristye4a40472011-12-22 02:56:19 +00001530 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001531 break;
1532 }
cristye10859a2011-12-18 22:28:59 +00001533 case ChangeMaskCompositeOp:
1534 {
cristye4a40472011-12-22 02:56:19 +00001535 MagickBooleanType
1536 equivalent;
1537
cristy7159f662012-10-28 17:32:43 +00001538 if (Da > ((MagickRealType) QuantumRange/2.0))
cristy99abff32011-12-24 20:45:16 +00001539 {
cristy7159f662012-10-28 17:32:43 +00001540 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001541 break;
1542 }
cristye4a40472011-12-22 02:56:19 +00001543 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001544 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001545 {
cristy7159f662012-10-28 17:32:43 +00001546 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001547 break;
1548 }
cristy7159f662012-10-28 17:32:43 +00001549 pixel=(MagickRealType) OpaqueAlpha;
cristye4a40472011-12-22 02:56:19 +00001550 break;
1551 }
cristy99abff32011-12-24 20:45:16 +00001552 case ClearCompositeOp:
1553 {
cristy7159f662012-10-28 17:32:43 +00001554 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001555 break;
1556 }
1557 case ColorizeCompositeOp:
1558 case HueCompositeOp:
1559 case LuminizeCompositeOp:
1560 case SaturateCompositeOp:
1561 {
1562 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1563 {
1564 pixel=QuantumRange*Da;
1565 break;
1566 }
1567 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1568 {
1569 pixel=QuantumRange*Sa;
1570 break;
1571 }
1572 if (Sa < Da)
1573 {
1574 pixel=QuantumRange*Da;
1575 break;
1576 }
1577 pixel=QuantumRange*Sa;
1578 break;
1579 }
cristy99abff32011-12-24 20:45:16 +00001580 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001581 {
1582 pixel=QuantumRange*Sa;
cristy1a941d72013-03-08 01:04:41 +00001583 if (composite_image->alpha_trait == BlendPixelTrait)
cristy24d5d722012-05-17 12:27:27 +00001584 pixel=GetPixelIntensity(composite_image,p);
1585 break;
1586 }
1587 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001588 case DisplaceCompositeOp:
1589 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001590 case DstAtopCompositeOp:
1591 case ReplaceCompositeOp:
1592 case SrcCompositeOp:
1593 {
1594 pixel=QuantumRange*Sa;
1595 break;
1596 }
1597 case DarkenIntensityCompositeOp:
1598 {
cristy99abff32011-12-24 20:45:16 +00001599 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1600 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001601 break;
1602 }
cristy98621462011-12-31 22:31:11 +00001603 case IntensityCompositeOp:
1604 {
cristyf13c5942012-08-08 23:50:11 +00001605 pixel=GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001606 break;
1607 }
cristye4a40472011-12-22 02:56:19 +00001608 case LightenIntensityCompositeOp:
1609 {
1610 pixel=Sa*GetPixelIntensity(composite_image,p) >
1611 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001612 break;
1613 }
cristy99abff32011-12-24 20:45:16 +00001614 case ModulateCompositeOp:
1615 {
1616 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1617 {
1618 pixel=QuantumRange*Da;
1619 break;
1620 }
1621 pixel=QuantumRange*Da;
1622 break;
1623 }
cristye10859a2011-12-18 22:28:59 +00001624 default:
1625 {
cristye4a40472011-12-22 02:56:19 +00001626 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001627 break;
1628 }
1629 }
cristye4a40472011-12-22 02:56:19 +00001630 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001631 continue;
1632 }
1633 /*
cristy99abff32011-12-24 20:45:16 +00001634 Porter-Duff compositions:
1635 Sca: source normalized color multiplied by alpha.
1636 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001637 */
cristy99abff32011-12-24 20:45:16 +00001638 Sca=QuantumScale*Sa*Sc;
1639 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001640 switch (compose)
1641 {
cristye10859a2011-12-18 22:28:59 +00001642 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001643 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001644 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001645 {
cristy99abff32011-12-24 20:45:16 +00001646 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001647 break;
1648 }
1649 default:
1650 break;
1651 }
cristy3e3ec3a2012-11-03 23:11:06 +00001652 gamma=PerceptibleReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001653 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001654 switch (compose)
1655 {
cristya7b07912012-01-11 20:01:32 +00001656 case AlphaCompositeOp:
1657 {
1658 pixel=QuantumRange*Sa;
1659 break;
1660 }
cristye4a40472011-12-22 02:56:19 +00001661 case AtopCompositeOp:
1662 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001663 {
cristye4a40472011-12-22 02:56:19 +00001664 pixel=Sc*Sa+Dc*(1.0-Sa);
1665 break;
cristye10859a2011-12-18 22:28:59 +00001666 }
cristye4a40472011-12-22 02:56:19 +00001667 case BlendCompositeOp:
1668 {
1669 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1670 break;
1671 }
1672 case BlurCompositeOp:
1673 case DisplaceCompositeOp:
1674 case DistortCompositeOp:
1675 case CopyCompositeOp:
1676 case ReplaceCompositeOp:
1677 case SrcCompositeOp:
1678 {
1679 pixel=Sc;
1680 break;
1681 }
1682 case BumpmapCompositeOp:
1683 {
cristy99abff32011-12-24 20:45:16 +00001684 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001685 {
1686 pixel=Dc;
1687 break;
1688 }
1689 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1690 break;
1691 }
cristy99abff32011-12-24 20:45:16 +00001692 case ChangeMaskCompositeOp:
1693 {
1694 pixel=Dc;
1695 break;
1696 }
1697 case ClearCompositeOp:
1698 {
1699 pixel=0.0;
1700 break;
1701 }
cristye4a40472011-12-22 02:56:19 +00001702 case ColorBurnCompositeOp:
1703 {
1704 /*
1705 Refer to the March 2009 SVG specification.
1706 */
1707 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1708 {
cristy99abff32011-12-24 20:45:16 +00001709 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001710 break;
1711 }
1712 if (Sca < MagickEpsilon)
1713 {
cristy99abff32011-12-24 20:45:16 +00001714 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001715 break;
1716 }
cristy99abff32011-12-24 20:45:16 +00001717 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1718 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001719 break;
1720 }
1721 case ColorDodgeCompositeOp:
1722 {
1723 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1724 {
cristy99abff32011-12-24 20:45:16 +00001725 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001726 break;
1727 }
1728 if (fabs(Sca-Sa) < MagickEpsilon)
1729 {
cristy99abff32011-12-24 20:45:16 +00001730 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001731 break;
1732 }
cristy99abff32011-12-24 20:45:16 +00001733 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001734 (1.0-Sa));
1735 break;
1736 }
1737 case ColorizeCompositeOp:
1738 {
cristy99abff32011-12-24 20:45:16 +00001739 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001740 {
1741 pixel=Dc;
1742 break;
1743 }
cristy99abff32011-12-24 20:45:16 +00001744 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001745 {
1746 pixel=Sc;
1747 break;
1748 }
cristy7133e642012-08-14 11:04:11 +00001749 CompositeHCL(destination_pixel.red,destination_pixel.green,
1750 destination_pixel.blue,&sans,&sans,&luma);
1751 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1752 &hue,&chroma,&sans);
1753 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001754 switch (channel)
1755 {
1756 case RedPixelChannel: pixel=red; break;
1757 case GreenPixelChannel: pixel=green; break;
1758 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001759 default: pixel=Dc; break;
1760 }
1761 break;
1762 }
cristye4a40472011-12-22 02:56:19 +00001763 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001764 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001765 {
cristy24d5d722012-05-17 12:27:27 +00001766 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001767 break;
1768 }
1769 case CopyBlackCompositeOp:
1770 {
cristyd197cbb2012-01-13 02:14:12 +00001771 if (channel == BlackPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001772 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001773 break;
1774 }
1775 case CopyBlueCompositeOp:
1776 case CopyYellowCompositeOp:
1777 {
cristyd197cbb2012-01-13 02:14:12 +00001778 if (channel == BluePixelChannel)
cristy7159f662012-10-28 17:32:43 +00001779 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001780 break;
1781 }
1782 case CopyGreenCompositeOp:
1783 case CopyMagentaCompositeOp:
1784 {
cristyd197cbb2012-01-13 02:14:12 +00001785 if (channel == GreenPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001786 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001787 break;
1788 }
1789 case CopyRedCompositeOp:
1790 case CopyCyanCompositeOp:
1791 {
cristyd197cbb2012-01-13 02:14:12 +00001792 if (channel == RedPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001793 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001794 break;
1795 }
cristy99abff32011-12-24 20:45:16 +00001796 case DarkenCompositeOp:
1797 {
1798 /*
1799 Darken is equivalent to a 'Minimum' method
1800 OR a greyscale version of a binary 'Or'
1801 OR the 'Intersection' of pixel sets.
1802 */
1803 if (Sc < Dc)
1804 {
1805 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1806 break;
1807 }
1808 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1809 break;
1810 }
cristye4a40472011-12-22 02:56:19 +00001811 case DarkenIntensityCompositeOp:
1812 {
cristy99abff32011-12-24 20:45:16 +00001813 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1814 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001815 break;
1816 }
1817 case DifferenceCompositeOp:
1818 {
1819 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1820 break;
1821 }
1822 case DissolveCompositeOp:
1823 {
1824 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1825 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1826 break;
1827 }
1828 case DivideDstCompositeOp:
1829 {
1830 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1831 {
cristy99abff32011-12-24 20:45:16 +00001832 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001833 break;
1834 }
1835 if (fabs(Dca) < MagickEpsilon)
1836 {
cristy99abff32011-12-24 20:45:16 +00001837 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001838 break;
1839 }
cristy99abff32011-12-24 20:45:16 +00001840 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001841 break;
1842 }
1843 case DivideSrcCompositeOp:
1844 {
1845 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1846 {
cristy99abff32011-12-24 20:45:16 +00001847 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001848 break;
1849 }
1850 if (fabs(Sca) < MagickEpsilon)
1851 {
cristy99abff32011-12-24 20:45:16 +00001852 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001853 break;
1854 }
cristy99abff32011-12-24 20:45:16 +00001855 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001856 break;
1857 }
1858 case DstAtopCompositeOp:
1859 {
1860 pixel=Dc*Da+Sc*(1.0-Da);
1861 break;
1862 }
1863 case DstCompositeOp:
1864 case NoCompositeOp:
1865 {
1866 pixel=Dc;
1867 break;
1868 }
1869 case DstInCompositeOp:
1870 {
1871 pixel=gamma*(Sa*Dc*Sa);
1872 break;
1873 }
1874 case DstOutCompositeOp:
1875 {
1876 pixel=gamma*(Da*Dc*(1.0-Sa));
1877 break;
1878 }
1879 case DstOverCompositeOp:
1880 {
1881 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1882 break;
1883 }
1884 case ExclusionCompositeOp:
1885 {
cristy99abff32011-12-24 20:45:16 +00001886 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1887 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001888 break;
1889 }
1890 case HardLightCompositeOp:
1891 {
1892 if ((2.0*Sca) < Sa)
1893 {
cristy99abff32011-12-24 20:45:16 +00001894 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001895 (1.0-Sa));
1896 break;
1897 }
cristy99abff32011-12-24 20:45:16 +00001898 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001899 Dca*(1.0-Sa));
1900 break;
1901 }
1902 case HueCompositeOp:
1903 {
cristy99abff32011-12-24 20:45:16 +00001904 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001905 {
1906 pixel=Dc;
1907 break;
1908 }
cristy99abff32011-12-24 20:45:16 +00001909 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001910 {
1911 pixel=Sc;
1912 break;
1913 }
cristy7133e642012-08-14 11:04:11 +00001914 CompositeHCL(destination_pixel.red,destination_pixel.green,
1915 destination_pixel.blue,&hue,&chroma,&luma);
1916 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001917 &hue,&sans,&sans);
cristy7133e642012-08-14 11:04:11 +00001918 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001919 switch (channel)
1920 {
1921 case RedPixelChannel: pixel=red; break;
1922 case GreenPixelChannel: pixel=green; break;
1923 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001924 default: pixel=Dc; break;
1925 }
1926 break;
1927 }
1928 case InCompositeOp:
1929 case SrcInCompositeOp:
1930 {
1931 pixel=gamma*(Da*Sc*Da);
1932 break;
1933 }
cristy99abff32011-12-24 20:45:16 +00001934 case LinearBurnCompositeOp:
1935 {
1936 /*
1937 LinearBurn: as defined by Abode Photoshop, according to
1938 http://www.simplefilter.de/en/basics/mixmods.html is:
1939
1940 f(Sc,Dc) = Sc + Dc - 1
1941 */
1942 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1943 break;
1944 }
1945 case LinearDodgeCompositeOp:
1946 {
1947 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1948 break;
1949 }
1950 case LinearLightCompositeOp:
1951 {
1952 /*
1953 LinearLight: as defined by Abode Photoshop, according to
1954 http://www.simplefilter.de/en/basics/mixmods.html is:
1955
1956 f(Sc,Dc) = Dc + 2*Sc - 1
1957 */
1958 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1959 break;
1960 }
1961 case LightenCompositeOp:
1962 {
1963 if (Sc > Dc)
1964 {
cristy24d5d722012-05-17 12:27:27 +00001965 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00001966 break;
1967 }
cristy24d5d722012-05-17 12:27:27 +00001968 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00001969 break;
1970 }
cristye4a40472011-12-22 02:56:19 +00001971 case LightenIntensityCompositeOp:
1972 {
1973 /*
1974 Lighten is equivalent to a 'Maximum' method
1975 OR a greyscale version of a binary 'And'
1976 OR the 'Union' of pixel sets.
1977 */
1978 pixel=Sa*GetPixelIntensity(composite_image,p) >
1979 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1980 break;
1981 }
cristye4a40472011-12-22 02:56:19 +00001982 case LuminizeCompositeOp:
1983 {
cristy99abff32011-12-24 20:45:16 +00001984 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001985 {
1986 pixel=Dc;
1987 break;
1988 }
cristy99abff32011-12-24 20:45:16 +00001989 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001990 {
1991 pixel=Sc;
1992 break;
1993 }
cristy7133e642012-08-14 11:04:11 +00001994 CompositeHCL(destination_pixel.red,destination_pixel.green,
1995 destination_pixel.blue,&hue,&chroma,&luma);
1996 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1997 &sans,&sans,&luma);
1998 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001999 switch (channel)
2000 {
2001 case RedPixelChannel: pixel=red; break;
2002 case GreenPixelChannel: pixel=green; break;
2003 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002004 default: pixel=Dc; break;
2005 }
2006 break;
2007 }
2008 case MathematicsCompositeOp:
2009 {
2010 /*
2011 'Mathematics' a free form user control mathematical composition
2012 is defined as...
2013
2014 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2015
2016 Where the arguments A,B,C,D are (currently) passed to composite
2017 as a command separated 'geometry' string in "compose:args" image
2018 artifact.
2019
2020 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2021
2022 Applying the SVG transparency formula (see above), we get...
2023
2024 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2025
2026 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2027 Dca*(1.0-Sa)
2028 */
2029 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2030 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2031 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2032 break;
2033 }
2034 case MinusDstCompositeOp:
2035 {
2036 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2037 break;
2038 }
2039 case MinusSrcCompositeOp:
2040 {
2041 /*
2042 Minus source from destination.
2043
2044 f(Sc,Dc) = Sc - Dc
2045 */
cristy99abff32011-12-24 20:45:16 +00002046 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002047 break;
2048 }
2049 case ModulateCompositeOp:
2050 {
2051 ssize_t
2052 offset;
2053
cristy99abff32011-12-24 20:45:16 +00002054 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002055 {
2056 pixel=Dc;
2057 break;
2058 }
2059 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2060 if (offset == 0)
2061 {
2062 pixel=Dc;
2063 break;
2064 }
cristy7133e642012-08-14 11:04:11 +00002065 CompositeHCL(destination_pixel.red,destination_pixel.green,
2066 destination_pixel.blue,&hue,&chroma,&luma);
2067 luma+=(0.01*percent_luma*offset)/midpoint;
2068 chroma*=0.01*percent_chroma;
2069 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002070 switch (channel)
2071 {
2072 case RedPixelChannel: pixel=red; break;
2073 case GreenPixelChannel: pixel=green; break;
2074 case BluePixelChannel: pixel=blue; break;
2075 default: pixel=Dc; break;
2076 }
2077 break;
2078 }
2079 case ModulusAddCompositeOp:
2080 {
2081 pixel=Sc+Dc;
2082 if (pixel > QuantumRange)
2083 pixel-=(QuantumRange+1.0);
2084 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2085 break;
2086 }
2087 case ModulusSubtractCompositeOp:
2088 {
cristy99abff32011-12-24 20:45:16 +00002089 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002090 if (pixel < 0.0)
2091 pixel+=(QuantumRange+1.0);
2092 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2093 break;
2094 }
2095 case MultiplyCompositeOp:
2096 {
cristy99abff32011-12-24 20:45:16 +00002097 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002098 break;
2099 }
2100 case OutCompositeOp:
2101 case SrcOutCompositeOp:
2102 {
2103 pixel=gamma*(Sa*Sc*(1.0-Da));
2104 break;
2105 }
2106 case OverCompositeOp:
2107 case SrcOverCompositeOp:
2108 {
cristy99abff32011-12-24 20:45:16 +00002109 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002110 break;
2111 }
2112 case OverlayCompositeOp:
2113 {
2114 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002115 {
2116 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2117 (1.0-Da));
2118 break;
2119 }
2120 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2121 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002122 break;
2123 }
2124 case PegtopLightCompositeOp:
2125 {
2126 /*
2127 PegTop: A Soft-Light alternative: A continuous version of the
2128 Softlight function, producing very similar results.
2129
2130 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2131
2132 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2133 */
2134 if (fabs(Da) < MagickEpsilon)
2135 {
cristy99abff32011-12-24 20:45:16 +00002136 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002137 break;
2138 }
cristy99abff32011-12-24 20:45:16 +00002139 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2140 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002141 break;
2142 }
2143 case PinLightCompositeOp:
2144 {
2145 /*
2146 PinLight: A Photoshop 7 composition method
2147 http://www.simplefilter.de/en/basics/mixmods.html
2148
2149 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2150 */
2151 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2152 {
cristy99abff32011-12-24 20:45:16 +00002153 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002154 break;
2155 }
2156 if ((Dca*Sa) > (2.0*Sca*Da))
2157 {
cristy99abff32011-12-24 20:45:16 +00002158 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002159 break;
2160 }
cristy99abff32011-12-24 20:45:16 +00002161 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002162 break;
2163 }
2164 case PlusCompositeOp:
2165 {
cristy24d5d722012-05-17 12:27:27 +00002166 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002167 break;
2168 }
2169 case SaturateCompositeOp:
2170 {
cristy99abff32011-12-24 20:45:16 +00002171 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002172 {
2173 pixel=Dc;
2174 break;
2175 }
cristy99abff32011-12-24 20:45:16 +00002176 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002177 {
2178 pixel=Sc;
2179 break;
2180 }
cristy7133e642012-08-14 11:04:11 +00002181 CompositeHCL(destination_pixel.red,destination_pixel.green,
2182 destination_pixel.blue,&hue,&chroma,&luma);
2183 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2184 &sans,&chroma,&sans);
2185 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002186 switch (channel)
2187 {
2188 case RedPixelChannel: pixel=red; break;
2189 case GreenPixelChannel: pixel=green; break;
2190 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002191 default: pixel=Dc; break;
2192 }
2193 break;
2194 }
2195 case ScreenCompositeOp:
2196 {
2197 /*
2198 Screen: a negated multiply:
2199
2200 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2201 */
cristy99abff32011-12-24 20:45:16 +00002202 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002203 break;
2204 }
2205 case SoftLightCompositeOp:
2206 {
2207 /*
2208 Refer to the March 2009 SVG specification.
2209 */
2210 if ((2.0*Sca) < Sa)
2211 {
cristy99abff32011-12-24 20:45:16 +00002212 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2213 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002214 break;
2215 }
2216 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2217 {
cristy99abff32011-12-24 20:45:16 +00002218 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2219 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2220 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002221 break;
2222 }
cristy99abff32011-12-24 20:45:16 +00002223 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2224 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002225 break;
2226 }
2227 case ThresholdCompositeOp:
2228 {
cristy7159f662012-10-28 17:32:43 +00002229 MagickRealType
cristye4a40472011-12-22 02:56:19 +00002230 delta;
2231
2232 delta=Sc-Dc;
cristy7159f662012-10-28 17:32:43 +00002233 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002234 {
2235 pixel=gamma*Dc;
2236 break;
2237 }
2238 pixel=gamma*(Dc+delta*amount);
2239 break;
2240 }
2241 case VividLightCompositeOp:
2242 {
2243 /*
2244 VividLight: A Photoshop 7 composition method. See
2245 http://www.simplefilter.de/en/basics/mixmods.html.
2246
2247 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2248 */
2249 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2250 {
cristy99abff32011-12-24 20:45:16 +00002251 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002252 break;
2253 }
2254 if ((2.0*Sca) <= Sa)
2255 {
cristy99abff32011-12-24 20:45:16 +00002256 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2257 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002258 break;
2259 }
cristy99abff32011-12-24 20:45:16 +00002260 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2261 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002262 break;
2263 }
2264 case XorCompositeOp:
2265 {
cristy99abff32011-12-24 20:45:16 +00002266 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002267 break;
2268 }
2269 default:
2270 {
2271 pixel=Sc;
2272 break;
2273 }
2274 }
2275 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002276 }
cristyed231572011-07-14 02:18:59 +00002277 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002278 channels=GetPixelChannels(composite_image);
2279 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002280 p=pixels;
cristyed231572011-07-14 02:18:59 +00002281 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002282 }
2283 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2284 status=MagickFalse;
2285 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2286 {
2287 MagickBooleanType
2288 proceed;
2289
2290#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002291 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002292#endif
2293 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2294 image->rows);
2295 if (proceed == MagickFalse)
2296 status=MagickFalse;
2297 }
2298 }
2299 composite_view=DestroyCacheView(composite_view);
2300 image_view=DestroyCacheView(image_view);
2301 if (destination_image != (Image * ) NULL)
2302 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002303 else
2304 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00002305 return(status);
2306}
2307
2308/*
2309%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2310% %
2311% %
2312% %
2313% T e x t u r e I m a g e %
2314% %
2315% %
2316% %
2317%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2318%
2319% TextureImage() repeatedly tiles the texture image across and down the image
2320% canvas.
2321%
2322% The format of the TextureImage method is:
2323%
cristy30d8c942012-02-07 13:44:59 +00002324% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002325% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002326%
2327% A description of each parameter follows:
2328%
2329% o image: the image.
2330%
cristye6178502011-12-23 17:02:29 +00002331% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002332%
2333*/
cristy30d8c942012-02-07 13:44:59 +00002334MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2335 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002336{
2337#define TextureImageTag "Texture/Image"
2338
2339 CacheView
2340 *image_view,
2341 *texture_view;
2342
cristy30d8c942012-02-07 13:44:59 +00002343 Image
2344 *texture_image;
2345
cristy4c08aed2011-07-01 19:47:50 +00002346 MagickBooleanType
2347 status;
2348
2349 ssize_t
2350 y;
2351
2352 assert(image != (Image *) NULL);
2353 if (image->debug != MagickFalse)
2354 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2355 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002356 if (texture == (const Image *) NULL)
2357 return(MagickFalse);
2358 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2359 return(MagickFalse);
2360 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002361 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002362 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002363 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002364 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2365 exception);
cristy4c08aed2011-07-01 19:47:50 +00002366 status=MagickTrue;
2367 if ((image->compose != CopyCompositeOp) &&
cristy8a46d822012-08-28 23:32:39 +00002368 ((image->compose != OverCompositeOp) || (image->alpha_trait == BlendPixelTrait) ||
2369 (texture_image->alpha_trait == BlendPixelTrait)))
cristy4c08aed2011-07-01 19:47:50 +00002370 {
2371 /*
2372 Tile texture onto the image background.
2373 */
cristye6178502011-12-23 17:02:29 +00002374 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002375 {
2376 register ssize_t
2377 x;
2378
2379 if (status == MagickFalse)
2380 continue;
cristye6178502011-12-23 17:02:29 +00002381 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002382 {
2383 MagickBooleanType
2384 thread_status;
2385
cristyfeb3e962012-03-29 17:25:55 +00002386 thread_status=CompositeImage(image,texture_image,image->compose,
2387 MagickFalse,x+texture_image->tile_offset.x,y+
2388 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002389 if (thread_status == MagickFalse)
2390 {
2391 status=thread_status;
2392 break;
2393 }
2394 }
2395 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2396 {
2397 MagickBooleanType
2398 proceed;
2399
cristy4c08aed2011-07-01 19:47:50 +00002400 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2401 y,image->rows);
2402 if (proceed == MagickFalse)
2403 status=MagickFalse;
2404 }
2405 }
2406 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2407 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002408 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002409 return(status);
2410 }
2411 /*
2412 Tile texture onto the image background (optimized).
2413 */
2414 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00002415 texture_view=AcquireVirtualCacheView(texture_image,exception);
2416 image_view=AcquireAuthenticCacheView(image,exception);
cristye8914392012-12-16 21:16:11 +00002417#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00002418 #pragma omp parallel for schedule(static,4) shared(status) \
2419 magick_threads(texture_image,image,1,1)
cristye8914392012-12-16 21:16:11 +00002420#endif
cristy4c08aed2011-07-01 19:47:50 +00002421 for (y=0; y < (ssize_t) image->rows; y++)
2422 {
2423 MagickBooleanType
2424 sync;
2425
2426 register const Quantum
2427 *p,
2428 *pixels;
2429
2430 register ssize_t
2431 x;
2432
2433 register Quantum
2434 *q;
2435
2436 size_t
2437 width;
2438
2439 if (status == MagickFalse)
2440 continue;
cristye6178502011-12-23 17:02:29 +00002441 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2442 (y+texture_image->tile_offset.y) % texture_image->rows,
2443 texture_image->columns,1,exception);
2444 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002445 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2446 {
2447 status=MagickFalse;
2448 continue;
2449 }
cristye6178502011-12-23 17:02:29 +00002450 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002451 {
2452 register ssize_t
cristye6178502011-12-23 17:02:29 +00002453 j;
cristy4c08aed2011-07-01 19:47:50 +00002454
2455 p=pixels;
cristye6178502011-12-23 17:02:29 +00002456 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002457 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2458 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002459 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002460 {
cristye6178502011-12-23 17:02:29 +00002461 register ssize_t
2462 i;
2463
cristyd03b8fa2013-02-10 18:59:28 +00002464 if (GetPixelMask(image,q) != 0)
cristy10a6c612012-01-29 21:41:05 +00002465 {
2466 p+=GetPixelChannels(texture_image);
2467 q+=GetPixelChannels(image);
2468 continue;
2469 }
cristye6178502011-12-23 17:02:29 +00002470 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2471 {
cristy5a23c552013-02-13 14:34:28 +00002472 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2473 PixelTrait traits=GetPixelChannelTraits(image,channel);
2474 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2475 channel);
cristye6178502011-12-23 17:02:29 +00002476 if ((traits == UndefinedPixelTrait) ||
2477 (texture_traits == UndefinedPixelTrait))
2478 continue;
2479 SetPixelChannel(image,channel,p[i],q);
2480 }
2481 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002482 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002483 }
2484 }
2485 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2486 if (sync == MagickFalse)
2487 status=MagickFalse;
2488 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2489 {
2490 MagickBooleanType
2491 proceed;
2492
cristy4c08aed2011-07-01 19:47:50 +00002493 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2494 image->rows);
2495 if (proceed == MagickFalse)
2496 status=MagickFalse;
2497 }
2498 }
2499 texture_view=DestroyCacheView(texture_view);
2500 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002501 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002502 return(status);
2503}