blob: 556cdad4165cece06fc1562169024281cf13cd83 [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% %
cristyfe676ee2013-11-18 13:03:38 +000020% Copyright 1999-2014 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"
cristy6a2180c2013-05-27 10:28:36 +000048#include "MagickCore/channel.h"
cristy4c08aed2011-07-01 19:47:50 +000049#include "MagickCore/client.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colorspace.h"
53#include "MagickCore/colorspace-private.h"
54#include "MagickCore/composite.h"
55#include "MagickCore/composite-private.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"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/monitor.h"
66#include "MagickCore/monitor-private.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/option.h"
69#include "MagickCore/pixel-accessor.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"
75#include "MagickCore/thread-private.h"
cristy191c0b72012-08-12 16:29:52 +000076#include "MagickCore/threshold.h"
cristy63a81872012-03-22 15:52:52 +000077#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000078#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000079#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000080#include "MagickCore/version.h"
81
82/*
83%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84% %
85% %
86% %
cristyf4ad9df2011-07-08 16:49:03 +000087% C o m p o s i t e I m a g e %
cristy4c08aed2011-07-01 19:47:50 +000088% %
89% %
90% %
91%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92%
cristyf4ad9df2011-07-08 16:49:03 +000093% CompositeImage() returns the second image composited onto the first
cristy4c08aed2011-07-01 19:47:50 +000094% at the specified offset, using the specified composite method.
95%
cristyf4ad9df2011-07-08 16:49:03 +000096% The format of the CompositeImage method is:
cristy4c08aed2011-07-01 19:47:50 +000097%
98% MagickBooleanType CompositeImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +000099% const Image *composite_image,const CompositeOperator compose,
100% const MagickBooleanType clip_to_self,const ssize_t x_offset,
101% const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000102%
103% A description of each parameter follows:
104%
105% o image: the destination image, modified by he composition
106%
cristyfeb3e962012-03-29 17:25:55 +0000107% o composite_image: the composite (source) image.
108%
cristy4c08aed2011-07-01 19:47:50 +0000109% 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%
cristyfeb3e962012-03-29 17:25:55 +0000113% o clip_to_self: set to MagickTrue to limit composition to area composed.
cristy4c08aed2011-07-01 19:47:50 +0000114%
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 'composite_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%
cristye941a752011-10-15 01:52:48 +0000129% o exception: return any errors or warnings in this structure.
130%
cristy4c08aed2011-07-01 19:47:50 +0000131*/
132
anthonyea068a52012-04-09 05:46:25 +0000133/*
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 destination 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 - QuantiumScale*alpha * QuantiumScale*beta;
157 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
158
159 The above SVG definitions also definate 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*/
cristy4c08aed2011-07-01 19:47:50 +0000180
cristy7159f662012-10-28 17:32:43 +0000181static inline MagickRealType MagickMin(const MagickRealType x,
182 const MagickRealType y)
cristye4a40472011-12-22 02:56:19 +0000183{
184 if (x < y)
185 return(x);
186 return(y);
187}
cristyddeeea22012-04-12 01:33:09 +0000188
cristy7159f662012-10-28 17:32:43 +0000189static inline MagickRealType MagickMax(const MagickRealType x,
190 const MagickRealType y)
cristye4a40472011-12-22 02:56:19 +0000191{
192 if (x > y)
193 return(x);
194 return(y);
195}
196
cristy7159f662012-10-28 17:32:43 +0000197static inline MagickRealType ConvertHueToRGB(MagickRealType m1,
198 MagickRealType m2,MagickRealType hue)
cristyd8f16f72012-08-13 12:49:50 +0000199{
200 if (hue < 0.0)
201 hue+=1.0;
202 if (hue > 1.0)
203 hue-=1.0;
204 if ((6.0*hue) < 1.0)
205 return(m1+6.0*(m2-m1)*hue);
206 if ((2.0*hue) < 1.0)
207 return(m2);
208 if ((3.0*hue) < 2.0)
209 return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
210 return(m1);
211}
212
cristy7159f662012-10-28 17:32:43 +0000213static void HCLComposite(const MagickRealType hue,const MagickRealType chroma,
214 const MagickRealType luma,MagickRealType *red,MagickRealType *green,
215 MagickRealType *blue)
cristyd8f16f72012-08-13 12:49:50 +0000216{
cristy7159f662012-10-28 17:32:43 +0000217 MagickRealType
cristyd8f16f72012-08-13 12:49:50 +0000218 b,
cristy7133e642012-08-14 11:04:11 +0000219 c,
cristyd8f16f72012-08-13 12:49:50 +0000220 g,
cristy7133e642012-08-14 11:04:11 +0000221 h,
222 m,
cristyd8f16f72012-08-13 12:49:50 +0000223 r,
cristy398fd022013-06-26 23:08:33 +0000224 x;
cristyd8f16f72012-08-13 12:49:50 +0000225
226 /*
cristy7133e642012-08-14 11:04:11 +0000227 Convert HCL to RGB colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000228 */
cristy7159f662012-10-28 17:32:43 +0000229 assert(red != (MagickRealType *) NULL);
230 assert(green != (MagickRealType *) NULL);
231 assert(blue != (MagickRealType *) NULL);
cristy7133e642012-08-14 11:04:11 +0000232 h=6.0*hue;
233 c=chroma;
234 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
235 r=0.0;
236 g=0.0;
237 b=0.0;
238 if ((0.0 <= h) && (h < 1.0))
cristyd8f16f72012-08-13 12:49:50 +0000239 {
cristye1715282012-08-15 13:10:55 +0000240 r=c;
241 g=x;
cristyd8f16f72012-08-13 12:49:50 +0000242 }
cristyd8f16f72012-08-13 12:49:50 +0000243 else
cristy7133e642012-08-14 11:04:11 +0000244 if ((1.0 <= h) && (h < 2.0))
245 {
cristye1715282012-08-15 13:10:55 +0000246 r=x;
247 g=c;
cristy7133e642012-08-14 11:04:11 +0000248 }
249 else
250 if ((2.0 <= h) && (h < 3.0))
251 {
cristye1715282012-08-15 13:10:55 +0000252 g=c;
253 b=x;
cristy7133e642012-08-14 11:04:11 +0000254 }
255 else
256 if ((3.0 <= h) && (h < 4.0))
257 {
cristye1715282012-08-15 13:10:55 +0000258 g=x;
259 b=c;
cristy7133e642012-08-14 11:04:11 +0000260 }
261 else
262 if ((4.0 <= h) && (h < 5.0))
263 {
cristye1715282012-08-15 13:10:55 +0000264 r=x;
265 b=c;
cristy7133e642012-08-14 11:04:11 +0000266 }
267 else
268 if ((5.0 <= h) && (h < 6.0))
269 {
cristye1715282012-08-15 13:10:55 +0000270 r=c;
271 b=x;
cristy7133e642012-08-14 11:04:11 +0000272 }
cristy9e2436a2013-05-02 20:35:59 +0000273 m=luma-(0.298839*r+0.586811*g+0.114350*b);
cristy398fd022013-06-26 23:08:33 +0000274 *red=QuantumRange*(r+m);
275 *green=QuantumRange*(g+m);
276 *blue=QuantumRange*(b+m);
cristyd8f16f72012-08-13 12:49:50 +0000277}
278
cristy7159f662012-10-28 17:32:43 +0000279static void CompositeHCL(const MagickRealType red,const MagickRealType green,
280 const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
281 MagickRealType *luma)
cristyd8f16f72012-08-13 12:49:50 +0000282{
cristy7159f662012-10-28 17:32:43 +0000283 MagickRealType
cristyd8f16f72012-08-13 12:49:50 +0000284 b,
cristy7133e642012-08-14 11:04:11 +0000285 c,
cristyd8f16f72012-08-13 12:49:50 +0000286 g,
cristy7133e642012-08-14 11:04:11 +0000287 h,
cristyd8f16f72012-08-13 12:49:50 +0000288 max,
cristyd8f16f72012-08-13 12:49:50 +0000289 r;
290
291 /*
cristy7133e642012-08-14 11:04:11 +0000292 Convert RGB to HCL colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000293 */
cristy7159f662012-10-28 17:32:43 +0000294 assert(hue != (MagickRealType *) NULL);
295 assert(chroma != (MagickRealType *) NULL);
296 assert(luma != (MagickRealType *) NULL);
cristy7133e642012-08-14 11:04:11 +0000297 r=red;
298 g=green;
299 b=blue;
cristyd8f16f72012-08-13 12:49:50 +0000300 max=MagickMax(r,MagickMax(g,b));
cristy7159f662012-10-28 17:32:43 +0000301 c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
cristy7133e642012-08-14 11:04:11 +0000302 h=0.0;
303 if (c == 0)
304 h=0.0;
cristyd8f16f72012-08-13 12:49:50 +0000305 else
cristy7133e642012-08-14 11:04:11 +0000306 if (red == max)
cristy9e2436a2013-05-02 20:35:59 +0000307 h=fmod((g-b)/c+6.0,6.0);
cristyd8f16f72012-08-13 12:49:50 +0000308 else
cristy7133e642012-08-14 11:04:11 +0000309 if (green == max)
310 h=((b-r)/c)+2.0;
311 else
312 if (blue == max)
313 h=((r-g)/c)+4.0;
314 *hue=(h/6.0);
315 *chroma=QuantumScale*c;
cristy9e2436a2013-05-02 20:35:59 +0000316 *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b);
cristyd8f16f72012-08-13 12:49:50 +0000317}
318
cristye4a40472011-12-22 02:56:19 +0000319static MagickBooleanType CompositeOverImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000320 const Image *composite_image,const MagickBooleanType clip_to_self,
321 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristye4a40472011-12-22 02:56:19 +0000322{
323#define CompositeImageTag "Composite/Image"
324
325 CacheView
326 *composite_view,
327 *image_view;
328
cristye4a40472011-12-22 02:56:19 +0000329 MagickBooleanType
cristye4a40472011-12-22 02:56:19 +0000330 status;
331
332 MagickOffsetType
333 progress;
334
335 ssize_t
336 y;
337
cristye4a40472011-12-22 02:56:19 +0000338 /*
cristye4a40472011-12-22 02:56:19 +0000339 Composite image.
340 */
341 status=MagickTrue;
342 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000343 composite_view=AcquireVirtualCacheView(composite_image,exception);
344 image_view=AcquireAuthenticCacheView(image,exception);
cristye4a40472011-12-22 02:56:19 +0000345#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000346 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000347 magick_threads(composite_image,image,image->rows,1)
cristye4a40472011-12-22 02:56:19 +0000348#endif
349 for (y=0; y < (ssize_t) image->rows; y++)
350 {
351 const Quantum
352 *pixels;
353
354 register const Quantum
355 *restrict p;
356
357 register Quantum
358 *restrict q;
359
360 register ssize_t
361 x;
362
cristy564a5692012-01-20 23:56:26 +0000363 size_t
364 channels;
365
cristye4a40472011-12-22 02:56:19 +0000366 if (status == MagickFalse)
367 continue;
cristyfeb3e962012-03-29 17:25:55 +0000368 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000369 {
370 if (y < y_offset)
371 continue;
372 if ((y-y_offset) >= (ssize_t) composite_image->rows)
373 continue;
374 }
375 /*
376 If pixels is NULL, y is outside overlay region.
377 */
378 pixels=(Quantum *) NULL;
379 p=(Quantum *) NULL;
380 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
381 {
382 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
383 composite_image->columns,1,exception);
384 if (p == (const Quantum *) NULL)
385 {
386 status=MagickFalse;
387 continue;
388 }
389 pixels=p;
390 if (x_offset < 0)
391 p-=x_offset*GetPixelChannels(composite_image);
392 }
393 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
394 if (q == (Quantum *) NULL)
395 {
396 status=MagickFalse;
397 continue;
398 }
399 for (x=0; x < (ssize_t) image->columns; x++)
400 {
cristy17028dc2013-01-24 12:28:39 +0000401 double
402 gamma;
403
cristy7159f662012-10-28 17:32:43 +0000404 MagickRealType
cristye4a40472011-12-22 02:56:19 +0000405 alpha,
406 Da,
407 Dc,
cristye4a40472011-12-22 02:56:19 +0000408 Sa,
409 Sc;
410
411 register ssize_t
412 i;
413
cristyfeb3e962012-03-29 17:25:55 +0000414 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000415 {
416 if (x < x_offset)
417 {
418 q+=GetPixelChannels(image);
419 continue;
420 }
421 if ((x-x_offset) >= (ssize_t) composite_image->columns)
422 break;
423 }
424 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
425 ((x-x_offset) >= (ssize_t) composite_image->columns))
426 {
427 Quantum
428 source[MaxPixelChannels];
429
430 /*
431 Virtual composite:
432 Sc: source color.
433 Dc: destination color.
434 */
cristy883fde12013-04-08 00:50:13 +0000435 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +0000436 {
437 q+=GetPixelChannels(image);
438 continue;
439 }
cristyc94ba6f2012-01-29 23:19:58 +0000440 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
441 source,exception);
cristye4a40472011-12-22 02:56:19 +0000442 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
443 {
cristy5a23c552013-02-13 14:34:28 +0000444 PixelChannel channel=GetPixelChannelChannel(image,i);
445 PixelTrait traits=GetPixelChannelTraits(image,channel);
446 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
447 channel);
cristye4a40472011-12-22 02:56:19 +0000448 if ((traits == UndefinedPixelTrait) ||
449 (composite_traits == UndefinedPixelTrait))
450 continue;
451 q[i]=source[channel];
452 }
453 q+=GetPixelChannels(image);
454 continue;
455 }
456 /*
457 Authentic composite:
458 Sa: normalized source alpha.
459 Da: normalized destination alpha.
460 */
cristy883fde12013-04-08 00:50:13 +0000461 if (GetPixelReadMask(composite_image,p) == 0)
cristyc94ba6f2012-01-29 23:19:58 +0000462 {
463 p+=GetPixelChannels(composite_image);
464 channels=GetPixelChannels(composite_image);
465 if (p >= (pixels+channels*composite_image->columns))
466 p=pixels;
467 q+=GetPixelChannels(image);
468 continue;
469 }
cristye4a40472011-12-22 02:56:19 +0000470 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
471 Da=QuantumScale*GetPixelAlpha(image,q);
472 alpha=Sa*(-Da)+Sa+Da;
473 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
474 {
cristy5a23c552013-02-13 14:34:28 +0000475 PixelChannel channel=GetPixelChannelChannel(image,i);
476 PixelTrait traits=GetPixelChannelTraits(image,channel);
477 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
478 channel);
cristye4a40472011-12-22 02:56:19 +0000479 if ((traits == UndefinedPixelTrait) ||
480 (composite_traits == UndefinedPixelTrait))
481 continue;
482 if ((traits & CopyPixelTrait) != 0)
483 {
484 if (channel != AlphaPixelChannel)
485 {
486 /*
487 Copy channel.
488 */
489 q[i]=GetPixelChannel(composite_image,channel,p);
490 continue;
491 }
492 /*
493 Set alpha channel.
494 */
495 q[i]=ClampToQuantum(QuantumRange*alpha);
496 continue;
497 }
498 /*
499 Sc: source color.
500 Dc: destination color.
501 */
cristy7159f662012-10-28 17:32:43 +0000502 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
503 Dc=(MagickRealType) q[i];
cristy3e3ec3a2012-11-03 23:11:06 +0000504 gamma=PerceptibleReciprocal(alpha);
cristye4a40472011-12-22 02:56:19 +0000505 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
506 }
507 p+=GetPixelChannels(composite_image);
508 channels=GetPixelChannels(composite_image);
509 if (p >= (pixels+channels*composite_image->columns))
510 p=pixels;
511 q+=GetPixelChannels(image);
512 }
513 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
514 status=MagickFalse;
515 if (image->progress_monitor != (MagickProgressMonitor) NULL)
516 {
517 MagickBooleanType
518 proceed;
519
520#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000521 #pragma omp critical (MagickCore_CompositeImage)
cristye4a40472011-12-22 02:56:19 +0000522#endif
523 proceed=SetImageProgress(image,CompositeImageTag,progress++,
524 image->rows);
525 if (proceed == MagickFalse)
526 status=MagickFalse;
527 }
528 }
529 composite_view=DestroyCacheView(composite_view);
530 image_view=DestroyCacheView(image_view);
531 return(status);
532}
533
cristy4c08aed2011-07-01 19:47:50 +0000534MagickExport MagickBooleanType CompositeImage(Image *image,
cristya865ccd2012-07-28 00:33:10 +0000535 const Image *composite,const CompositeOperator compose,
cristy44c98412012-03-31 18:25:40 +0000536 const MagickBooleanType clip_to_self,const ssize_t x_offset,
537 const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000538{
cristy4c08aed2011-07-01 19:47:50 +0000539#define CompositeImageTag "Composite/Image"
540
541 CacheView
542 *composite_view,
543 *image_view;
544
cristy4c08aed2011-07-01 19:47:50 +0000545 GeometryInfo
546 geometry_info;
547
548 Image
cristya865ccd2012-07-28 00:33:10 +0000549 *composite_image,
cristy4c08aed2011-07-01 19:47:50 +0000550 *destination_image;
551
552 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000553 status;
554
555 MagickOffsetType
556 progress;
557
cristy7159f662012-10-28 17:32:43 +0000558 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000559 amount,
560 destination_dissolve,
561 midpoint,
cristy7133e642012-08-14 11:04:11 +0000562 percent_luma,
563 percent_chroma,
cristy4c08aed2011-07-01 19:47:50 +0000564 source_dissolve,
565 threshold;
566
567 MagickStatusType
568 flags;
569
cristyd197cbb2012-01-13 02:14:12 +0000570 ssize_t
571 y;
572
cristy4c08aed2011-07-01 19:47:50 +0000573 assert(image != (Image *) NULL);
574 assert(image->signature == MagickSignature);
575 if (image->debug != MagickFalse)
576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristya865ccd2012-07-28 00:33:10 +0000577 assert(composite!= (Image *) NULL);
578 assert(composite->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000579 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000580 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +0000581 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
582 if (composite_image == (const Image *) NULL)
583 return(MagickFalse);
cristyde067e92013-04-18 00:47:50 +0000584 if (IsGrayColorspace(image->colorspace) != MagickFalse)
585 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristya4a22a22012-08-01 23:16:38 +0000586 (void) SetImageColorspace(composite_image,image->colorspace,exception);
cristy60dc90c2013-02-09 18:33:21 +0000587 if ((image->alpha_trait == BlendPixelTrait) &&
588 (composite_image->alpha_trait != BlendPixelTrait))
cristydc4bf872013-03-09 15:08:20 +0000589 (void) SetImageAlphaChannel(composite_image,SetAlphaChannel,exception);
cristye4a40472011-12-22 02:56:19 +0000590 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
591 {
cristyfeb3e962012-03-29 17:25:55 +0000592 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
593 y_offset,exception);
cristya865ccd2012-07-28 00:33:10 +0000594 composite_image=DestroyImage(composite_image);
cristye4a40472011-12-22 02:56:19 +0000595 return(status);
596 }
cristy4c08aed2011-07-01 19:47:50 +0000597 destination_image=(Image *) NULL;
598 amount=0.5;
599 destination_dissolve=1.0;
cristy7133e642012-08-14 11:04:11 +0000600 percent_luma=100.0;
601 percent_chroma=100.0;
cristy4c08aed2011-07-01 19:47:50 +0000602 source_dissolve=1.0;
603 threshold=0.05f;
604 switch (compose)
605 {
cristy4c08aed2011-07-01 19:47:50 +0000606 case CopyCompositeOp:
607 {
608 if ((x_offset < 0) || (y_offset < 0))
609 break;
610 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
611 break;
612 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
613 break;
614 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +0000615 composite_view=AcquireVirtualCacheView(composite_image,exception);
616 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000617#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000618 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +0000619 magick_threads(composite_image,image,composite_image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000620#endif
621 for (y=0; y < (ssize_t) composite_image->rows; y++)
622 {
623 MagickBooleanType
624 sync;
625
626 register const Quantum
627 *p;
628
629 register Quantum
630 *q;
631
632 register ssize_t
633 x;
634
635 if (status == MagickFalse)
636 continue;
637 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
638 1,exception);
639 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
640 composite_image->columns,1,exception);
641 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
642 {
643 status=MagickFalse;
644 continue;
645 }
646 for (x=0; x < (ssize_t) composite_image->columns; x++)
647 {
cristybdecccc2011-12-24 22:52:16 +0000648 register ssize_t
649 i;
650
cristy883fde12013-04-08 00:50:13 +0000651 if (GetPixelReadMask(composite_image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +0000652 {
653 p+=GetPixelChannels(composite_image);
654 q+=GetPixelChannels(image);
655 continue;
656 }
cristybdecccc2011-12-24 22:52:16 +0000657 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
658 {
cristy5a23c552013-02-13 14:34:28 +0000659 PixelChannel channel=GetPixelChannelChannel(composite_image,i);
660 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
661 channel);
662 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristybdecccc2011-12-24 22:52:16 +0000663 if ((traits == UndefinedPixelTrait) ||
664 (composite_traits == UndefinedPixelTrait))
665 continue;
666 SetPixelChannel(image,channel,p[i],q);
667 }
cristyed231572011-07-14 02:18:59 +0000668 p+=GetPixelChannels(composite_image);
669 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000670 }
671 sync=SyncCacheViewAuthenticPixels(image_view,exception);
672 if (sync == MagickFalse)
673 status=MagickFalse;
674 if (image->progress_monitor != (MagickProgressMonitor) NULL)
675 {
676 MagickBooleanType
677 proceed;
678
679#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000680 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000681#endif
682 proceed=SetImageProgress(image,CompositeImageTag,
683 (MagickOffsetType) y,image->rows);
684 if (proceed == MagickFalse)
685 status=MagickFalse;
686 }
687 }
688 composite_view=DestroyCacheView(composite_view);
689 image_view=DestroyCacheView(image_view);
cristy44886b92012-07-28 13:07:09 +0000690 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000691 return(status);
692 }
cristye4a40472011-12-22 02:56:19 +0000693 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000694 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000695 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000696 {
697 /*
698 Modify destination outside the overlaid region and require an alpha
699 channel to exist, to add transparency.
700 */
cristy8a46d822012-08-28 23:32:39 +0000701 if (image->alpha_trait != BlendPixelTrait)
cristy42c41de2012-05-05 18:36:31 +0000702 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000703 break;
704 }
705 case BlurCompositeOp:
706 {
707 CacheView
708 *composite_view,
709 *destination_view;
710
cristyfeb3e962012-03-29 17:25:55 +0000711 const char
712 *value;
713
cristy7159f662012-10-28 17:32:43 +0000714 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000715 angle_range,
716 angle_start,
717 height,
718 width;
719
cristybce4f4a2012-10-14 14:57:47 +0000720 PixelInfo
721 pixel;
722
cristy4c08aed2011-07-01 19:47:50 +0000723 ResampleFilter
724 *resample_filter;
725
726 SegmentInfo
727 blur;
728
729 /*
anthony9cb63cc2012-04-25 06:10:49 +0000730 Blur Image by resampling.
731
cristy4c08aed2011-07-01 19:47:50 +0000732 Blur Image dictated by an overlay gradient map: X = red_channel;
733 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
734 */
735 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000736 exception);
cristy4c08aed2011-07-01 19:47:50 +0000737 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000738 {
739 composite_image=DestroyImage(composite_image);
740 return(MagickFalse);
741 }
cristy4c08aed2011-07-01 19:47:50 +0000742 /*
anthony9cb63cc2012-04-25 06:10:49 +0000743 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000744 */
745 SetGeometryInfo(&geometry_info);
746 flags=NoValue;
cristy1a780952013-02-10 17:15:30 +0000747 value=GetImageArtifact(image,"compose:args");
748 if (value != (const char *) NULL)
cristy4c08aed2011-07-01 19:47:50 +0000749 flags=ParseGeometry(value,&geometry_info);
cristy1a780952013-02-10 17:15:30 +0000750 if ((flags & WidthValue) == 0)
751 {
752 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
753 "InvalidSetting","'%s' '%s'","compose:args",value);
cristy44886b92012-07-28 13:07:09 +0000754 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000755 destination_image=DestroyImage(destination_image);
756 return(MagickFalse);
757 }
anthony9cb63cc2012-04-25 06:10:49 +0000758 /*
759 Users input sigma now needs to be converted to the EWA ellipse size.
760 The filter defaults to a sigma of 0.5 so to make this match the
761 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000762 */
763 width=height=geometry_info.rho*2.0;
764 if ((flags & HeightValue) != 0 )
765 height=geometry_info.sigma*2.0;
cristy7159f662012-10-28 17:32:43 +0000766 /*
767 Default the unrotated ellipse width and height axis vectors.
768 */
anthonyd2923912012-04-23 13:06:53 +0000769 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000770 blur.x2=0.0;
771 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000772 blur.y2=height;
773 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000774 if ((flags & XValue) != 0 )
775 {
cristy7159f662012-10-28 17:32:43 +0000776 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000777 angle;
778
779 angle=DegreesToRadians(geometry_info.xi);
780 blur.x1=width*cos(angle);
781 blur.x2=width*sin(angle);
782 blur.y1=(-height*sin(angle));
783 blur.y2=height*cos(angle);
784 }
anthonyd2923912012-04-23 13:06:53 +0000785 /* Otherwise lets set a angle range and calculate in the loop */
786 angle_start=0.0;
787 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000788 if ((flags & YValue) != 0 )
789 {
790 angle_start=DegreesToRadians(geometry_info.xi);
791 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
792 }
793 /*
anthony9cb63cc2012-04-25 06:10:49 +0000794 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000795
anthony9cb63cc2012-04-25 06:10:49 +0000796 As the minimum ellipse radius of support*1.0 the EWA algorithm
797 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
798 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000799
anthony9cb63cc2012-04-25 06:10:49 +0000800 The solution (as well as the problem of preventing any user
801 expert filter settings, is to set our own user settings, then
802 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000803 */
cristy8a11cb12011-10-19 23:53:34 +0000804 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000805 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000806
807 /* do the variable blurring of each pixel in image */
808 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +0000809 composite_view=AcquireVirtualCacheView(composite_image,exception);
810 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000811 for (y=0; y < (ssize_t) composite_image->rows; y++)
812 {
813 MagickBooleanType
814 sync;
815
816 register const Quantum
817 *restrict p;
818
819 register Quantum
820 *restrict q;
821
822 register ssize_t
823 x;
824
825 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
826 continue;
827 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
828 1,exception);
829 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000830 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000831 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
832 break;
833 for (x=0; x < (ssize_t) composite_image->columns; x++)
834 {
835 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
836 {
cristyed231572011-07-14 02:18:59 +0000837 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000838 continue;
839 }
840 if (fabs(angle_range) > MagickEpsilon)
841 {
cristy7159f662012-10-28 17:32:43 +0000842 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000843 angle;
844
845 angle=angle_start+angle_range*QuantumScale*
846 GetPixelBlue(composite_image,p);
847 blur.x1=width*cos(angle);
848 blur.x2=width*sin(angle);
849 blur.y1=(-height*sin(angle));
850 blur.y2=height*cos(angle);
851 }
anthonyd2923912012-04-23 13:06:53 +0000852#if 0
853 if ( x == 10 && y == 60 ) {
cristy7159f662012-10-28 17:32:43 +0000854 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
855 blur.x2,blur.y1, blur.y2);
856 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
857 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
anthonyd2923912012-04-23 13:06:53 +0000858#endif
859 ScaleResampleFilter(resample_filter,
860 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
861 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
862 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
863 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000864 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000865 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000866 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000867 p+=GetPixelChannels(composite_image);
868 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000869 }
870 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
871 if (sync == MagickFalse)
872 break;
873 }
874 resample_filter=DestroyResampleFilter(resample_filter);
875 composite_view=DestroyCacheView(composite_view);
876 destination_view=DestroyCacheView(destination_view);
cristyf661c4d2012-07-28 12:57:17 +0000877 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000878 composite_image=destination_image;
879 break;
880 }
881 case DisplaceCompositeOp:
882 case DistortCompositeOp:
883 {
884 CacheView
885 *composite_view,
886 *destination_view,
887 *image_view;
888
cristyfeb3e962012-03-29 17:25:55 +0000889 const char
890 *value;
891
cristy4c08aed2011-07-01 19:47:50 +0000892 PixelInfo
893 pixel;
894
cristy7159f662012-10-28 17:32:43 +0000895 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000896 horizontal_scale,
897 vertical_scale;
898
899 PointInfo
900 center,
901 offset;
902
903 /*
904 Displace/Distort based on overlay gradient map:
905 X = red_channel; Y = green_channel;
906 compose:args = x_scale[,y_scale[,center.x,center.y]]
907 */
908 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000909 exception);
cristy4c08aed2011-07-01 19:47:50 +0000910 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000911 {
912 composite_image=DestroyImage(composite_image);
913 return(MagickFalse);
914 }
cristy4c08aed2011-07-01 19:47:50 +0000915 SetGeometryInfo(&geometry_info);
916 flags=NoValue;
917 value=GetImageArtifact(composite_image,"compose:args");
918 if (value != (char *) NULL)
919 flags=ParseGeometry(value,&geometry_info);
920 if ((flags & (WidthValue|HeightValue)) == 0 )
921 {
922 if ((flags & AspectValue) == 0)
923 {
cristy7159f662012-10-28 17:32:43 +0000924 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
cristy4c08aed2011-07-01 19:47:50 +0000925 2.0;
cristy7159f662012-10-28 17:32:43 +0000926 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000927 }
928 else
929 {
cristy7159f662012-10-28 17:32:43 +0000930 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
931 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000932 }
933 }
934 else
935 {
936 horizontal_scale=geometry_info.rho;
937 vertical_scale=geometry_info.sigma;
938 if ((flags & PercentValue) != 0)
939 {
940 if ((flags & AspectValue) == 0)
941 {
942 horizontal_scale*=(composite_image->columns-1.0)/200.0;
943 vertical_scale*=(composite_image->rows-1.0)/200.0;
944 }
945 else
946 {
947 horizontal_scale*=(image->columns-1.0)/200.0;
948 vertical_scale*=(image->rows-1.0)/200.0;
949 }
950 }
951 if ((flags & HeightValue) == 0)
952 vertical_scale=horizontal_scale;
953 }
954 /*
955 Determine fixed center point for absolute distortion map
956 Absolute distort ==
957 Displace offset relative to a fixed absolute point
958 Select that point according to +X+Y user inputs.
959 default = center of overlay image
960 arg flag '!' = locations/percentage relative to background image
961 */
cristy7159f662012-10-28 17:32:43 +0000962 center.x=(MagickRealType) x_offset;
963 center.y=(MagickRealType) y_offset;
cristy4c08aed2011-07-01 19:47:50 +0000964 if (compose == DistortCompositeOp)
965 {
966 if ((flags & XValue) == 0)
967 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000968 center.x=(MagickRealType) (x_offset+(composite_image->columns-1)/
969 2.0);
cristy4c08aed2011-07-01 19:47:50 +0000970 else
cristy7159f662012-10-28 17:32:43 +0000971 center.x=(MagickRealType) ((image->columns-1)/2);
cristy4c08aed2011-07-01 19:47:50 +0000972 else
973 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000974 center.x=(MagickRealType) x_offset+geometry_info.xi;
cristy4c08aed2011-07-01 19:47:50 +0000975 else
976 center.x=geometry_info.xi;
977 if ((flags & YValue) == 0)
978 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000979 center.y=(MagickRealType) (y_offset+(composite_image->rows-1)/
980 2.0);
cristy4c08aed2011-07-01 19:47:50 +0000981 else
cristy7159f662012-10-28 17:32:43 +0000982 center.y=(MagickRealType) ((image->rows-1)/2);
cristy4c08aed2011-07-01 19:47:50 +0000983 else
984 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000985 center.y=(MagickRealType) y_offset+geometry_info.psi;
cristy4c08aed2011-07-01 19:47:50 +0000986 else
987 center.y=geometry_info.psi;
988 }
989 /*
990 Shift the pixel offset point as defined by the provided,
991 displacement/distortion map. -- Like a lens...
992 */
cristye10859a2011-12-18 22:28:59 +0000993 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +0000994 image_view=AcquireVirtualCacheView(image,exception);
995 composite_view=AcquireVirtualCacheView(composite_image,exception);
996 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000997 for (y=0; y < (ssize_t) composite_image->rows; y++)
998 {
999 MagickBooleanType
1000 sync;
1001
1002 register const Quantum
1003 *restrict p;
1004
1005 register Quantum
1006 *restrict q;
1007
1008 register ssize_t
1009 x;
1010
1011 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1012 continue;
1013 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1014 1,exception);
1015 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001016 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001017 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1018 break;
1019 for (x=0; x < (ssize_t) composite_image->columns; x++)
1020 {
1021 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1022 {
cristyed231572011-07-14 02:18:59 +00001023 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001024 continue;
1025 }
1026 /*
1027 Displace the offset.
1028 */
cristy7159f662012-10-28 17:32:43 +00001029 offset.x=(double) (horizontal_scale*(GetPixelRed(composite_image,p)-
1030 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1031 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1032 x : 0);
1033 offset.y=(double) (vertical_scale*(GetPixelGreen(composite_image,p)-
1034 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1035 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1036 y : 0);
cristy4c08aed2011-07-01 19:47:50 +00001037 (void) InterpolatePixelInfo(image,image_view,
1038 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1039 &pixel,exception);
1040 /*
1041 Mask with the 'invalid pixel mask' in alpha channel.
1042 */
cristy7159f662012-10-28 17:32:43 +00001043 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1044 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001045 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001046 p+=GetPixelChannels(composite_image);
1047 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001048 }
1049 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1050 if (sync == MagickFalse)
1051 break;
1052 }
1053 destination_view=DestroyCacheView(destination_view);
1054 composite_view=DestroyCacheView(composite_view);
1055 image_view=DestroyCacheView(image_view);
cristyf661c4d2012-07-28 12:57:17 +00001056 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001057 composite_image=destination_image;
1058 break;
1059 }
1060 case DissolveCompositeOp:
1061 {
cristyfeb3e962012-03-29 17:25:55 +00001062 const char
1063 *value;
1064
cristy4c08aed2011-07-01 19:47:50 +00001065 /*
1066 Geometry arguments to dissolve factors.
1067 */
1068 value=GetImageArtifact(composite_image,"compose:args");
1069 if (value != (char *) NULL)
1070 {
1071 flags=ParseGeometry(value,&geometry_info);
1072 source_dissolve=geometry_info.rho/100.0;
1073 destination_dissolve=1.0;
1074 if ((source_dissolve-MagickEpsilon) < 0.0)
1075 source_dissolve=0.0;
1076 if ((source_dissolve+MagickEpsilon) > 1.0)
1077 {
1078 destination_dissolve=2.0-source_dissolve;
1079 source_dissolve=1.0;
1080 }
1081 if ((flags & SigmaValue) != 0)
1082 destination_dissolve=geometry_info.sigma/100.0;
1083 if ((destination_dissolve-MagickEpsilon) < 0.0)
1084 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001085 /* posible speed up? -- from IMv6 update
1086 clip_to_self=MagickFalse;
1087 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1088 {
1089 destination_dissolve=1.0;
1090 clip_to_self=MagickTrue;
1091 }
1092 */
cristy4c08aed2011-07-01 19:47:50 +00001093 }
1094 break;
1095 }
1096 case BlendCompositeOp:
1097 {
cristyfeb3e962012-03-29 17:25:55 +00001098 const char
1099 *value;
1100
cristy4c08aed2011-07-01 19:47:50 +00001101 value=GetImageArtifact(composite_image,"compose:args");
1102 if (value != (char *) NULL)
1103 {
1104 flags=ParseGeometry(value,&geometry_info);
1105 source_dissolve=geometry_info.rho/100.0;
1106 destination_dissolve=1.0-source_dissolve;
1107 if ((flags & SigmaValue) != 0)
1108 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001109 }
1110 break;
1111 }
1112 case MathematicsCompositeOp:
1113 {
cristyfeb3e962012-03-29 17:25:55 +00001114 const char
1115 *value;
1116
cristy4c08aed2011-07-01 19:47:50 +00001117 /*
1118 Just collect the values from "compose:args", setting.
1119 Unused values are set to zero automagically.
1120
1121 Arguments are normally a comma separated list, so this probably should
1122 be changed to some 'general comma list' parser, (with a minimum
1123 number of values)
1124 */
1125 SetGeometryInfo(&geometry_info);
1126 value=GetImageArtifact(composite_image,"compose:args");
1127 if (value != (char *) NULL)
1128 (void) ParseGeometry(value,&geometry_info);
1129 break;
1130 }
1131 case ModulateCompositeOp:
1132 {
cristyfeb3e962012-03-29 17:25:55 +00001133 const char
1134 *value;
1135
cristy4c08aed2011-07-01 19:47:50 +00001136 /*
cristy7133e642012-08-14 11:04:11 +00001137 Determine the luma and chroma scale.
cristy4c08aed2011-07-01 19:47:50 +00001138 */
1139 value=GetImageArtifact(composite_image,"compose:args");
1140 if (value != (char *) NULL)
1141 {
1142 flags=ParseGeometry(value,&geometry_info);
cristy7133e642012-08-14 11:04:11 +00001143 percent_luma=geometry_info.rho;
cristy4c08aed2011-07-01 19:47:50 +00001144 if ((flags & SigmaValue) != 0)
cristy7133e642012-08-14 11:04:11 +00001145 percent_chroma=geometry_info.sigma;
cristy4c08aed2011-07-01 19:47:50 +00001146 }
1147 break;
1148 }
1149 case ThresholdCompositeOp:
1150 {
cristyfeb3e962012-03-29 17:25:55 +00001151 const char
1152 *value;
1153
cristy4c08aed2011-07-01 19:47:50 +00001154 /*
1155 Determine the amount and threshold.
1156 */
1157 value=GetImageArtifact(composite_image,"compose:args");
1158 if (value != (char *) NULL)
1159 {
1160 flags=ParseGeometry(value,&geometry_info);
1161 amount=geometry_info.rho;
1162 threshold=geometry_info.sigma;
1163 if ((flags & SigmaValue) == 0)
1164 threshold=0.05f;
1165 }
1166 threshold*=QuantumRange;
1167 break;
1168 }
1169 default:
1170 break;
1171 }
cristy4c08aed2011-07-01 19:47:50 +00001172 /*
1173 Composite image.
1174 */
1175 status=MagickTrue;
1176 progress=0;
cristy7159f662012-10-28 17:32:43 +00001177 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy46ff2672012-12-14 15:32:26 +00001178 composite_view=AcquireVirtualCacheView(composite_image,exception);
1179 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001180#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001181 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001182 magick_threads(composite_image,image,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001183#endif
1184 for (y=0; y < (ssize_t) image->rows; y++)
1185 {
1186 const Quantum
1187 *pixels;
1188
cristy7159f662012-10-28 17:32:43 +00001189 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001190 blue,
cristy7133e642012-08-14 11:04:11 +00001191 luma,
cristye4a40472011-12-22 02:56:19 +00001192 green,
cristy4c08aed2011-07-01 19:47:50 +00001193 hue,
cristye4a40472011-12-22 02:56:19 +00001194 red,
cristy7133e642012-08-14 11:04:11 +00001195 chroma;
cristy4c08aed2011-07-01 19:47:50 +00001196
cristyddeeea22012-04-12 01:33:09 +00001197 PixelInfo
1198 destination_pixel,
1199 source_pixel;
1200
cristy4c08aed2011-07-01 19:47:50 +00001201 register const Quantum
1202 *restrict p;
1203
1204 register Quantum
1205 *restrict q;
1206
1207 register ssize_t
1208 x;
1209
1210 if (status == MagickFalse)
1211 continue;
cristyfeb3e962012-03-29 17:25:55 +00001212 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001213 {
1214 if (y < y_offset)
1215 continue;
1216 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1217 continue;
1218 }
1219 /*
1220 If pixels is NULL, y is outside overlay region.
1221 */
1222 pixels=(Quantum *) NULL;
1223 p=(Quantum *) NULL;
1224 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1225 {
1226 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1227 composite_image->columns,1,exception);
1228 if (p == (const Quantum *) NULL)
1229 {
1230 status=MagickFalse;
1231 continue;
1232 }
1233 pixels=p;
1234 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001235 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001236 }
1237 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001238 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001239 {
1240 status=MagickFalse;
1241 continue;
1242 }
cristy4c08aed2011-07-01 19:47:50 +00001243 hue=0.0;
cristy7133e642012-08-14 11:04:11 +00001244 chroma=0.0;
1245 luma=0.0;
cristyddeeea22012-04-12 01:33:09 +00001246 GetPixelInfo(image,&destination_pixel);
1247 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001248 for (x=0; x < (ssize_t) image->columns; x++)
1249 {
cristy17028dc2013-01-24 12:28:39 +00001250 double
1251 gamma;
1252
cristy7159f662012-10-28 17:32:43 +00001253 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001254 alpha,
1255 Da,
1256 Dc,
1257 Dca,
cristye4a40472011-12-22 02:56:19 +00001258 Sa,
1259 Sc,
1260 Sca;
1261
1262 register ssize_t
1263 i;
1264
cristy564a5692012-01-20 23:56:26 +00001265 size_t
1266 channels;
1267
cristyfeb3e962012-03-29 17:25:55 +00001268 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001269 {
1270 if (x < x_offset)
1271 {
cristyed231572011-07-14 02:18:59 +00001272 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001273 continue;
1274 }
1275 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1276 break;
1277 }
cristye4a40472011-12-22 02:56:19 +00001278 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1279 ((x-x_offset) >= (ssize_t) composite_image->columns))
1280 {
1281 Quantum
1282 source[MaxPixelChannels];
1283
1284 /*
1285 Virtual composite:
1286 Sc: source color.
1287 Dc: destination color.
1288 */
1289 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1290 source,exception);
cristy883fde12013-04-08 00:50:13 +00001291 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001292 {
1293 q+=GetPixelChannels(image);
1294 continue;
1295 }
cristye4a40472011-12-22 02:56:19 +00001296 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1297 {
cristy7159f662012-10-28 17:32:43 +00001298 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001299 pixel;
1300
cristy5a23c552013-02-13 14:34:28 +00001301 PixelChannel channel=GetPixelChannelChannel(image,i);
1302 PixelTrait traits=GetPixelChannelTraits(image,channel);
1303 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
1304 channel);
cristye4a40472011-12-22 02:56:19 +00001305 if ((traits == UndefinedPixelTrait) ||
1306 (composite_traits == UndefinedPixelTrait))
1307 continue;
1308 switch (compose)
1309 {
cristyc8d63672012-01-11 13:03:13 +00001310 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001311 case ChangeMaskCompositeOp:
1312 case CopyAlphaCompositeOp:
1313 case DstAtopCompositeOp:
1314 case DstInCompositeOp:
1315 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001316 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001317 case OutCompositeOp:
1318 case SrcInCompositeOp:
1319 case SrcOutCompositeOp:
1320 {
cristy7159f662012-10-28 17:32:43 +00001321 pixel=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001322 if (channel == AlphaPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001323 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001324 break;
1325 }
1326 case ClearCompositeOp:
1327 case CopyCompositeOp:
1328 case ReplaceCompositeOp:
1329 case SrcCompositeOp:
1330 {
1331 if (channel == AlphaPixelChannel)
1332 {
cristy7159f662012-10-28 17:32:43 +00001333 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001334 break;
1335 }
1336 pixel=0.0;
1337 break;
1338 }
cristy99abff32011-12-24 20:45:16 +00001339 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001340 case DissolveCompositeOp:
1341 {
1342 if (channel == AlphaPixelChannel)
1343 {
1344 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1345 source);
1346 break;
1347 }
cristy7159f662012-10-28 17:32:43 +00001348 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001349 break;
1350 }
1351 default:
1352 {
cristy7159f662012-10-28 17:32:43 +00001353 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001354 break;
1355 }
1356 }
1357 q[i]=ClampToQuantum(pixel);
1358 }
1359 q+=GetPixelChannels(image);
1360 continue;
1361 }
1362 /*
1363 Authentic composite:
1364 Sa: normalized source alpha.
1365 Da: normalized destination alpha.
1366 */
1367 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1368 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001369 switch (compose)
1370 {
cristye4a40472011-12-22 02:56:19 +00001371 case BumpmapCompositeOp:
1372 {
1373 alpha=GetPixelIntensity(composite_image,p)*Sa;
1374 break;
1375 }
cristycdc168f2011-12-21 15:24:39 +00001376 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001377 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001378 case DifferenceCompositeOp:
1379 case DivideDstCompositeOp:
1380 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001381 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001382 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001383 case LinearBurnCompositeOp:
1384 case LinearDodgeCompositeOp:
1385 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001386 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001387 case MinusDstCompositeOp:
1388 case MinusSrcCompositeOp:
1389 case ModulusAddCompositeOp:
1390 case ModulusSubtractCompositeOp:
1391 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001392 case OverlayCompositeOp:
1393 case PegtopLightCompositeOp:
1394 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001395 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001396 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001397 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001398 {
1399 alpha=RoundToUnity(Sa+Da-Sa*Da);
1400 break;
1401 }
1402 case DarkenCompositeOp:
1403 case DstAtopCompositeOp:
1404 case DstInCompositeOp:
1405 case InCompositeOp:
1406 case LightenCompositeOp:
1407 case SrcInCompositeOp:
1408 {
1409 alpha=Sa*Da;
1410 break;
1411 }
1412 case DissolveCompositeOp:
1413 {
1414 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1415 Sa+destination_dissolve*Da;
1416 break;
1417 }
1418 case DstOverCompositeOp:
1419 {
1420 alpha=Da*(-Sa)+Da+Sa;
1421 break;
1422 }
1423 case DstOutCompositeOp:
1424 {
1425 alpha=Da*(1.0-Sa);
1426 break;
1427 }
1428 case OutCompositeOp:
1429 case SrcOutCompositeOp:
1430 {
1431 alpha=Sa*(1.0-Da);
1432 break;
1433 }
1434 case OverCompositeOp:
1435 case SrcOverCompositeOp:
1436 {
1437 alpha=Sa*(-Da)+Sa+Da;
1438 break;
1439 }
cristy99abff32011-12-24 20:45:16 +00001440 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001441 case PlusCompositeOp:
1442 {
1443 alpha=RoundToUnity(Sa+Da);
1444 break;
1445 }
cristy4c08aed2011-07-01 19:47:50 +00001446 case XorCompositeOp:
1447 {
cristye4a40472011-12-22 02:56:19 +00001448 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001449 break;
1450 }
1451 default:
cristye4a40472011-12-22 02:56:19 +00001452 {
1453 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001454 break;
cristye4a40472011-12-22 02:56:19 +00001455 }
cristy4c08aed2011-07-01 19:47:50 +00001456 }
cristy883fde12013-04-08 00:50:13 +00001457 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001458 {
1459 p+=GetPixelChannels(composite_image);
1460 q+=GetPixelChannels(image);
1461 continue;
1462 }
cristy9d3d2792012-04-14 15:15:19 +00001463 switch (compose)
1464 {
1465 case ColorizeCompositeOp:
1466 case HueCompositeOp:
1467 case LuminizeCompositeOp:
1468 case ModulateCompositeOp:
1469 case SaturateCompositeOp:
1470 {
1471 GetPixelInfoPixel(composite_image,p,&source_pixel);
1472 GetPixelInfoPixel(image,q,&destination_pixel);
1473 break;
1474 }
1475 default:
1476 break;
1477 }
cristye4a40472011-12-22 02:56:19 +00001478 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1479 {
cristy7159f662012-10-28 17:32:43 +00001480 MagickRealType
1481 pixel,
cristy564a5692012-01-20 23:56:26 +00001482 sans;
1483
cristy5a23c552013-02-13 14:34:28 +00001484 PixelChannel channel=GetPixelChannelChannel(image,i);
1485 PixelTrait traits=GetPixelChannelTraits(image,channel);
1486 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
1487 channel);
cristy0cd1f212012-01-05 15:45:59 +00001488 if (traits == UndefinedPixelTrait)
1489 continue;
cristya7b07912012-01-11 20:01:32 +00001490 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001491 (composite_traits == UndefinedPixelTrait))
1492 continue;
1493 /*
1494 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001495 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001496 */
cristy7159f662012-10-28 17:32:43 +00001497 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
1498 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001499 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001500 {
cristye4a40472011-12-22 02:56:19 +00001501 if (channel != AlphaPixelChannel)
1502 {
1503 /*
1504 Copy channel.
1505 */
1506 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001507 continue;
cristye10859a2011-12-18 22:28:59 +00001508 }
cristye4a40472011-12-22 02:56:19 +00001509 /*
1510 Set alpha channel.
1511 */
cristye10859a2011-12-18 22:28:59 +00001512 switch (compose)
1513 {
cristyc8d63672012-01-11 13:03:13 +00001514 case AlphaCompositeOp:
1515 {
cristya7b07912012-01-11 20:01:32 +00001516 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001517 break;
1518 }
cristye4a40472011-12-22 02:56:19 +00001519 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001520 case CopyBlackCompositeOp:
1521 case CopyBlueCompositeOp:
1522 case CopyCyanCompositeOp:
1523 case CopyGreenCompositeOp:
1524 case CopyMagentaCompositeOp:
1525 case CopyRedCompositeOp:
1526 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001527 case SrcAtopCompositeOp:
1528 case DstCompositeOp:
1529 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001530 {
cristye4a40472011-12-22 02:56:19 +00001531 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001532 break;
1533 }
cristye10859a2011-12-18 22:28:59 +00001534 case ChangeMaskCompositeOp:
1535 {
cristye4a40472011-12-22 02:56:19 +00001536 MagickBooleanType
1537 equivalent;
1538
cristy7159f662012-10-28 17:32:43 +00001539 if (Da > ((MagickRealType) QuantumRange/2.0))
cristy99abff32011-12-24 20:45:16 +00001540 {
cristy7159f662012-10-28 17:32:43 +00001541 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001542 break;
1543 }
cristye4a40472011-12-22 02:56:19 +00001544 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001545 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001546 {
cristy7159f662012-10-28 17:32:43 +00001547 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001548 break;
1549 }
cristy7159f662012-10-28 17:32:43 +00001550 pixel=(MagickRealType) OpaqueAlpha;
cristye4a40472011-12-22 02:56:19 +00001551 break;
1552 }
cristy99abff32011-12-24 20:45:16 +00001553 case ClearCompositeOp:
1554 {
cristy7159f662012-10-28 17:32:43 +00001555 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001556 break;
1557 }
1558 case ColorizeCompositeOp:
1559 case HueCompositeOp:
1560 case LuminizeCompositeOp:
1561 case SaturateCompositeOp:
1562 {
1563 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1564 {
1565 pixel=QuantumRange*Da;
1566 break;
1567 }
1568 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1569 {
1570 pixel=QuantumRange*Sa;
1571 break;
1572 }
1573 if (Sa < Da)
1574 {
1575 pixel=QuantumRange*Da;
1576 break;
1577 }
1578 pixel=QuantumRange*Sa;
1579 break;
1580 }
cristy99abff32011-12-24 20:45:16 +00001581 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001582 {
1583 pixel=QuantumRange*Sa;
cristy1a941d72013-03-08 01:04:41 +00001584 if (composite_image->alpha_trait == BlendPixelTrait)
cristy24d5d722012-05-17 12:27:27 +00001585 pixel=GetPixelIntensity(composite_image,p);
1586 break;
1587 }
1588 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001589 case DisplaceCompositeOp:
1590 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001591 case DstAtopCompositeOp:
1592 case ReplaceCompositeOp:
1593 case SrcCompositeOp:
1594 {
1595 pixel=QuantumRange*Sa;
1596 break;
1597 }
1598 case DarkenIntensityCompositeOp:
1599 {
cristy99abff32011-12-24 20:45:16 +00001600 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1601 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001602 break;
1603 }
cristy98621462011-12-31 22:31:11 +00001604 case IntensityCompositeOp:
1605 {
cristyf13c5942012-08-08 23:50:11 +00001606 pixel=GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001607 break;
1608 }
cristye4a40472011-12-22 02:56:19 +00001609 case LightenIntensityCompositeOp:
1610 {
1611 pixel=Sa*GetPixelIntensity(composite_image,p) >
1612 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001613 break;
1614 }
cristy99abff32011-12-24 20:45:16 +00001615 case ModulateCompositeOp:
1616 {
1617 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1618 {
1619 pixel=QuantumRange*Da;
1620 break;
1621 }
1622 pixel=QuantumRange*Da;
1623 break;
1624 }
cristye10859a2011-12-18 22:28:59 +00001625 default:
1626 {
cristye4a40472011-12-22 02:56:19 +00001627 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001628 break;
1629 }
1630 }
cristye4a40472011-12-22 02:56:19 +00001631 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001632 continue;
1633 }
1634 /*
cristy99abff32011-12-24 20:45:16 +00001635 Porter-Duff compositions:
1636 Sca: source normalized color multiplied by alpha.
1637 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001638 */
cristy99abff32011-12-24 20:45:16 +00001639 Sca=QuantumScale*Sa*Sc;
1640 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001641 switch (compose)
1642 {
cristye10859a2011-12-18 22:28:59 +00001643 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001644 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001645 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001646 {
cristy99abff32011-12-24 20:45:16 +00001647 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001648 break;
1649 }
1650 default:
1651 break;
1652 }
cristy3e3ec3a2012-11-03 23:11:06 +00001653 gamma=PerceptibleReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001654 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001655 switch (compose)
1656 {
cristya7b07912012-01-11 20:01:32 +00001657 case AlphaCompositeOp:
1658 {
1659 pixel=QuantumRange*Sa;
1660 break;
1661 }
cristye4a40472011-12-22 02:56:19 +00001662 case AtopCompositeOp:
1663 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001664 {
cristye4a40472011-12-22 02:56:19 +00001665 pixel=Sc*Sa+Dc*(1.0-Sa);
1666 break;
cristye10859a2011-12-18 22:28:59 +00001667 }
cristye4a40472011-12-22 02:56:19 +00001668 case BlendCompositeOp:
1669 {
1670 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1671 break;
1672 }
1673 case BlurCompositeOp:
1674 case DisplaceCompositeOp:
1675 case DistortCompositeOp:
1676 case CopyCompositeOp:
1677 case ReplaceCompositeOp:
1678 case SrcCompositeOp:
1679 {
1680 pixel=Sc;
1681 break;
1682 }
1683 case BumpmapCompositeOp:
1684 {
cristy99abff32011-12-24 20:45:16 +00001685 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001686 {
1687 pixel=Dc;
1688 break;
1689 }
1690 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1691 break;
1692 }
cristy99abff32011-12-24 20:45:16 +00001693 case ChangeMaskCompositeOp:
1694 {
1695 pixel=Dc;
1696 break;
1697 }
1698 case ClearCompositeOp:
1699 {
1700 pixel=0.0;
1701 break;
1702 }
cristye4a40472011-12-22 02:56:19 +00001703 case ColorBurnCompositeOp:
1704 {
1705 /*
1706 Refer to the March 2009 SVG specification.
1707 */
1708 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1709 {
cristy99abff32011-12-24 20:45:16 +00001710 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001711 break;
1712 }
1713 if (Sca < MagickEpsilon)
1714 {
cristy99abff32011-12-24 20:45:16 +00001715 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001716 break;
1717 }
cristy99abff32011-12-24 20:45:16 +00001718 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1719 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001720 break;
1721 }
1722 case ColorDodgeCompositeOp:
1723 {
cristy98a39202013-07-09 13:25:52 +00001724 if ((Sca*Da+Dca*Sa) >= Sa*Da)
cristye4a40472011-12-22 02:56:19 +00001725 {
cristy31784be2013-07-09 13:55:34 +00001726 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001727 break;
1728 }
cristy99abff32011-12-24 20:45:16 +00001729 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001730 (1.0-Sa));
1731 break;
1732 }
1733 case ColorizeCompositeOp:
1734 {
cristy99abff32011-12-24 20:45:16 +00001735 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001736 {
1737 pixel=Dc;
1738 break;
1739 }
cristy99abff32011-12-24 20:45:16 +00001740 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001741 {
1742 pixel=Sc;
1743 break;
1744 }
cristy7133e642012-08-14 11:04:11 +00001745 CompositeHCL(destination_pixel.red,destination_pixel.green,
1746 destination_pixel.blue,&sans,&sans,&luma);
1747 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1748 &hue,&chroma,&sans);
1749 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001750 switch (channel)
1751 {
1752 case RedPixelChannel: pixel=red; break;
1753 case GreenPixelChannel: pixel=green; break;
1754 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001755 default: pixel=Dc; break;
1756 }
1757 break;
1758 }
cristye4a40472011-12-22 02:56:19 +00001759 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001760 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001761 {
cristy24d5d722012-05-17 12:27:27 +00001762 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001763 break;
1764 }
1765 case CopyBlackCompositeOp:
1766 {
cristyd197cbb2012-01-13 02:14:12 +00001767 if (channel == BlackPixelChannel)
cristy2fda00b2013-10-21 16:12:19 +00001768 pixel=(MagickRealType) (QuantumRange-
1769 GetPixelBlack(composite_image,p));
cristye4a40472011-12-22 02:56:19 +00001770 break;
1771 }
1772 case CopyBlueCompositeOp:
1773 case CopyYellowCompositeOp:
1774 {
cristyd197cbb2012-01-13 02:14:12 +00001775 if (channel == BluePixelChannel)
cristy7159f662012-10-28 17:32:43 +00001776 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001777 break;
1778 }
1779 case CopyGreenCompositeOp:
1780 case CopyMagentaCompositeOp:
1781 {
cristyd197cbb2012-01-13 02:14:12 +00001782 if (channel == GreenPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001783 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001784 break;
1785 }
1786 case CopyRedCompositeOp:
1787 case CopyCyanCompositeOp:
1788 {
cristyd197cbb2012-01-13 02:14:12 +00001789 if (channel == RedPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001790 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001791 break;
1792 }
cristy99abff32011-12-24 20:45:16 +00001793 case DarkenCompositeOp:
1794 {
1795 /*
1796 Darken is equivalent to a 'Minimum' method
1797 OR a greyscale version of a binary 'Or'
1798 OR the 'Intersection' of pixel sets.
1799 */
1800 if (Sc < Dc)
1801 {
1802 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1803 break;
1804 }
1805 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1806 break;
1807 }
cristye4a40472011-12-22 02:56:19 +00001808 case DarkenIntensityCompositeOp:
1809 {
cristy99abff32011-12-24 20:45:16 +00001810 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1811 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001812 break;
1813 }
1814 case DifferenceCompositeOp:
1815 {
1816 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1817 break;
1818 }
1819 case DissolveCompositeOp:
1820 {
1821 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1822 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1823 break;
1824 }
1825 case DivideDstCompositeOp:
1826 {
1827 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1828 {
cristy99abff32011-12-24 20:45:16 +00001829 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001830 break;
1831 }
1832 if (fabs(Dca) < MagickEpsilon)
1833 {
cristy99abff32011-12-24 20:45:16 +00001834 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001835 break;
1836 }
cristy99abff32011-12-24 20:45:16 +00001837 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001838 break;
1839 }
1840 case DivideSrcCompositeOp:
1841 {
1842 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1843 {
cristy99abff32011-12-24 20:45:16 +00001844 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001845 break;
1846 }
1847 if (fabs(Sca) < MagickEpsilon)
1848 {
cristy99abff32011-12-24 20:45:16 +00001849 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001850 break;
1851 }
cristy99abff32011-12-24 20:45:16 +00001852 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001853 break;
1854 }
1855 case DstAtopCompositeOp:
1856 {
1857 pixel=Dc*Da+Sc*(1.0-Da);
1858 break;
1859 }
1860 case DstCompositeOp:
1861 case NoCompositeOp:
1862 {
1863 pixel=Dc;
1864 break;
1865 }
1866 case DstInCompositeOp:
1867 {
1868 pixel=gamma*(Sa*Dc*Sa);
1869 break;
1870 }
1871 case DstOutCompositeOp:
1872 {
1873 pixel=gamma*(Da*Dc*(1.0-Sa));
1874 break;
1875 }
1876 case DstOverCompositeOp:
1877 {
1878 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1879 break;
1880 }
1881 case ExclusionCompositeOp:
1882 {
cristy99abff32011-12-24 20:45:16 +00001883 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1884 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001885 break;
1886 }
1887 case HardLightCompositeOp:
1888 {
1889 if ((2.0*Sca) < Sa)
1890 {
cristy99abff32011-12-24 20:45:16 +00001891 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001892 (1.0-Sa));
1893 break;
1894 }
cristy99abff32011-12-24 20:45:16 +00001895 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001896 Dca*(1.0-Sa));
1897 break;
1898 }
1899 case HueCompositeOp:
1900 {
cristy99abff32011-12-24 20:45:16 +00001901 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001902 {
1903 pixel=Dc;
1904 break;
1905 }
cristy99abff32011-12-24 20:45:16 +00001906 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001907 {
1908 pixel=Sc;
1909 break;
1910 }
cristy7133e642012-08-14 11:04:11 +00001911 CompositeHCL(destination_pixel.red,destination_pixel.green,
1912 destination_pixel.blue,&hue,&chroma,&luma);
1913 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001914 &hue,&sans,&sans);
cristy7133e642012-08-14 11:04:11 +00001915 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001916 switch (channel)
1917 {
1918 case RedPixelChannel: pixel=red; break;
1919 case GreenPixelChannel: pixel=green; break;
1920 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001921 default: pixel=Dc; break;
1922 }
1923 break;
1924 }
1925 case InCompositeOp:
1926 case SrcInCompositeOp:
1927 {
1928 pixel=gamma*(Da*Sc*Da);
1929 break;
1930 }
cristy99abff32011-12-24 20:45:16 +00001931 case LinearBurnCompositeOp:
1932 {
1933 /*
1934 LinearBurn: as defined by Abode Photoshop, according to
1935 http://www.simplefilter.de/en/basics/mixmods.html is:
1936
1937 f(Sc,Dc) = Sc + Dc - 1
1938 */
1939 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1940 break;
1941 }
1942 case LinearDodgeCompositeOp:
1943 {
1944 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1945 break;
1946 }
1947 case LinearLightCompositeOp:
1948 {
1949 /*
1950 LinearLight: as defined by Abode Photoshop, according to
1951 http://www.simplefilter.de/en/basics/mixmods.html is:
1952
1953 f(Sc,Dc) = Dc + 2*Sc - 1
1954 */
1955 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1956 break;
1957 }
1958 case LightenCompositeOp:
1959 {
1960 if (Sc > Dc)
1961 {
cristy24d5d722012-05-17 12:27:27 +00001962 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00001963 break;
1964 }
cristy24d5d722012-05-17 12:27:27 +00001965 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00001966 break;
1967 }
cristye4a40472011-12-22 02:56:19 +00001968 case LightenIntensityCompositeOp:
1969 {
1970 /*
1971 Lighten is equivalent to a 'Maximum' method
1972 OR a greyscale version of a binary 'And'
1973 OR the 'Union' of pixel sets.
1974 */
1975 pixel=Sa*GetPixelIntensity(composite_image,p) >
1976 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1977 break;
1978 }
cristye4a40472011-12-22 02:56:19 +00001979 case LuminizeCompositeOp:
1980 {
cristy99abff32011-12-24 20:45:16 +00001981 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001982 {
1983 pixel=Dc;
1984 break;
1985 }
cristy99abff32011-12-24 20:45:16 +00001986 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001987 {
1988 pixel=Sc;
1989 break;
1990 }
cristy7133e642012-08-14 11:04:11 +00001991 CompositeHCL(destination_pixel.red,destination_pixel.green,
1992 destination_pixel.blue,&hue,&chroma,&luma);
1993 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1994 &sans,&sans,&luma);
1995 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001996 switch (channel)
1997 {
1998 case RedPixelChannel: pixel=red; break;
1999 case GreenPixelChannel: pixel=green; break;
2000 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002001 default: pixel=Dc; break;
2002 }
2003 break;
2004 }
2005 case MathematicsCompositeOp:
2006 {
2007 /*
2008 'Mathematics' a free form user control mathematical composition
2009 is defined as...
2010
2011 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2012
2013 Where the arguments A,B,C,D are (currently) passed to composite
2014 as a command separated 'geometry' string in "compose:args" image
2015 artifact.
2016
2017 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2018
2019 Applying the SVG transparency formula (see above), we get...
2020
2021 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2022
2023 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2024 Dca*(1.0-Sa)
2025 */
2026 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2027 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2028 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2029 break;
2030 }
2031 case MinusDstCompositeOp:
2032 {
2033 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2034 break;
2035 }
2036 case MinusSrcCompositeOp:
2037 {
2038 /*
2039 Minus source from destination.
2040
2041 f(Sc,Dc) = Sc - Dc
2042 */
cristy99abff32011-12-24 20:45:16 +00002043 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002044 break;
2045 }
2046 case ModulateCompositeOp:
2047 {
2048 ssize_t
2049 offset;
2050
cristy99abff32011-12-24 20:45:16 +00002051 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002052 {
2053 pixel=Dc;
2054 break;
2055 }
2056 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2057 if (offset == 0)
2058 {
2059 pixel=Dc;
2060 break;
2061 }
cristy7133e642012-08-14 11:04:11 +00002062 CompositeHCL(destination_pixel.red,destination_pixel.green,
2063 destination_pixel.blue,&hue,&chroma,&luma);
2064 luma+=(0.01*percent_luma*offset)/midpoint;
2065 chroma*=0.01*percent_chroma;
2066 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002067 switch (channel)
2068 {
2069 case RedPixelChannel: pixel=red; break;
2070 case GreenPixelChannel: pixel=green; break;
2071 case BluePixelChannel: pixel=blue; break;
2072 default: pixel=Dc; break;
2073 }
2074 break;
2075 }
2076 case ModulusAddCompositeOp:
2077 {
2078 pixel=Sc+Dc;
2079 if (pixel > QuantumRange)
cristy516edc12013-10-27 10:53:42 +00002080 pixel-=QuantumRange;
cristy91de9ea2013-11-17 14:45:54 +00002081 pixel=gamma*(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002082 break;
2083 }
2084 case ModulusSubtractCompositeOp:
2085 {
cristy99abff32011-12-24 20:45:16 +00002086 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002087 if (pixel < 0.0)
cristy516edc12013-10-27 10:53:42 +00002088 pixel+=QuantumRange;
cristy91de9ea2013-11-17 14:45:54 +00002089 pixel=gamma*(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002090 break;
2091 }
2092 case MultiplyCompositeOp:
2093 {
cristy99abff32011-12-24 20:45:16 +00002094 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002095 break;
2096 }
2097 case OutCompositeOp:
2098 case SrcOutCompositeOp:
2099 {
2100 pixel=gamma*(Sa*Sc*(1.0-Da));
2101 break;
2102 }
2103 case OverCompositeOp:
2104 case SrcOverCompositeOp:
2105 {
cristy99abff32011-12-24 20:45:16 +00002106 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002107 break;
2108 }
2109 case OverlayCompositeOp:
2110 {
2111 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002112 {
2113 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2114 (1.0-Da));
2115 break;
2116 }
2117 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2118 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002119 break;
2120 }
2121 case PegtopLightCompositeOp:
2122 {
2123 /*
2124 PegTop: A Soft-Light alternative: A continuous version of the
2125 Softlight function, producing very similar results.
2126
2127 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2128
2129 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2130 */
2131 if (fabs(Da) < MagickEpsilon)
2132 {
cristy99abff32011-12-24 20:45:16 +00002133 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002134 break;
2135 }
cristy99abff32011-12-24 20:45:16 +00002136 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2137 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002138 break;
2139 }
2140 case PinLightCompositeOp:
2141 {
2142 /*
2143 PinLight: A Photoshop 7 composition method
2144 http://www.simplefilter.de/en/basics/mixmods.html
2145
2146 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2147 */
2148 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2149 {
cristy99abff32011-12-24 20:45:16 +00002150 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002151 break;
2152 }
2153 if ((Dca*Sa) > (2.0*Sca*Da))
2154 {
cristy99abff32011-12-24 20:45:16 +00002155 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002156 break;
2157 }
cristy99abff32011-12-24 20:45:16 +00002158 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002159 break;
2160 }
2161 case PlusCompositeOp:
2162 {
cristy24d5d722012-05-17 12:27:27 +00002163 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002164 break;
2165 }
2166 case SaturateCompositeOp:
2167 {
cristy99abff32011-12-24 20:45:16 +00002168 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002169 {
2170 pixel=Dc;
2171 break;
2172 }
cristy99abff32011-12-24 20:45:16 +00002173 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002174 {
2175 pixel=Sc;
2176 break;
2177 }
cristy7133e642012-08-14 11:04:11 +00002178 CompositeHCL(destination_pixel.red,destination_pixel.green,
2179 destination_pixel.blue,&hue,&chroma,&luma);
2180 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2181 &sans,&chroma,&sans);
2182 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002183 switch (channel)
2184 {
2185 case RedPixelChannel: pixel=red; break;
2186 case GreenPixelChannel: pixel=green; break;
2187 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002188 default: pixel=Dc; break;
2189 }
2190 break;
2191 }
2192 case ScreenCompositeOp:
2193 {
2194 /*
2195 Screen: a negated multiply:
2196
2197 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2198 */
cristy99abff32011-12-24 20:45:16 +00002199 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002200 break;
2201 }
2202 case SoftLightCompositeOp:
2203 {
2204 /*
2205 Refer to the March 2009 SVG specification.
2206 */
2207 if ((2.0*Sca) < Sa)
2208 {
cristy99abff32011-12-24 20:45:16 +00002209 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2210 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002211 break;
2212 }
2213 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2214 {
cristy99abff32011-12-24 20:45:16 +00002215 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2216 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2217 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002218 break;
2219 }
cristy99abff32011-12-24 20:45:16 +00002220 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2221 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002222 break;
2223 }
2224 case ThresholdCompositeOp:
2225 {
cristy7159f662012-10-28 17:32:43 +00002226 MagickRealType
cristye4a40472011-12-22 02:56:19 +00002227 delta;
2228
2229 delta=Sc-Dc;
cristy7159f662012-10-28 17:32:43 +00002230 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002231 {
2232 pixel=gamma*Dc;
2233 break;
2234 }
2235 pixel=gamma*(Dc+delta*amount);
2236 break;
2237 }
2238 case VividLightCompositeOp:
2239 {
2240 /*
2241 VividLight: A Photoshop 7 composition method. See
2242 http://www.simplefilter.de/en/basics/mixmods.html.
2243
2244 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2245 */
2246 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2247 {
cristy99abff32011-12-24 20:45:16 +00002248 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002249 break;
2250 }
2251 if ((2.0*Sca) <= Sa)
2252 {
cristy99abff32011-12-24 20:45:16 +00002253 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2254 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002255 break;
2256 }
cristy99abff32011-12-24 20:45:16 +00002257 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2258 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002259 break;
2260 }
2261 case XorCompositeOp:
2262 {
cristy99abff32011-12-24 20:45:16 +00002263 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002264 break;
2265 }
2266 default:
2267 {
2268 pixel=Sc;
2269 break;
2270 }
2271 }
2272 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002273 }
cristyed231572011-07-14 02:18:59 +00002274 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002275 channels=GetPixelChannels(composite_image);
2276 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002277 p=pixels;
cristyed231572011-07-14 02:18:59 +00002278 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002279 }
2280 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2281 status=MagickFalse;
2282 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2283 {
2284 MagickBooleanType
2285 proceed;
2286
2287#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002288 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002289#endif
2290 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2291 image->rows);
2292 if (proceed == MagickFalse)
2293 status=MagickFalse;
2294 }
2295 }
2296 composite_view=DestroyCacheView(composite_view);
2297 image_view=DestroyCacheView(image_view);
2298 if (destination_image != (Image * ) NULL)
2299 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002300 else
2301 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00002302 return(status);
2303}
2304
2305/*
2306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2307% %
2308% %
2309% %
2310% T e x t u r e I m a g e %
2311% %
2312% %
2313% %
2314%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2315%
2316% TextureImage() repeatedly tiles the texture image across and down the image
2317% canvas.
2318%
2319% The format of the TextureImage method is:
2320%
cristy30d8c942012-02-07 13:44:59 +00002321% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002322% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002323%
2324% A description of each parameter follows:
2325%
2326% o image: the image.
2327%
cristye6178502011-12-23 17:02:29 +00002328% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002329%
2330*/
cristy30d8c942012-02-07 13:44:59 +00002331MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2332 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002333{
2334#define TextureImageTag "Texture/Image"
2335
2336 CacheView
2337 *image_view,
2338 *texture_view;
2339
cristy30d8c942012-02-07 13:44:59 +00002340 Image
2341 *texture_image;
2342
cristy4c08aed2011-07-01 19:47:50 +00002343 MagickBooleanType
2344 status;
2345
2346 ssize_t
2347 y;
2348
2349 assert(image != (Image *) NULL);
2350 if (image->debug != MagickFalse)
2351 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2352 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002353 if (texture == (const Image *) NULL)
2354 return(MagickFalse);
2355 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2356 return(MagickFalse);
2357 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002358 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002359 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002360 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002361 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2362 exception);
cristy4c08aed2011-07-01 19:47:50 +00002363 status=MagickTrue;
2364 if ((image->compose != CopyCompositeOp) &&
cristy974a2a82013-04-23 12:57:16 +00002365 ((image->compose != OverCompositeOp) ||
2366 (image->alpha_trait == BlendPixelTrait) ||
cristy8a46d822012-08-28 23:32:39 +00002367 (texture_image->alpha_trait == BlendPixelTrait)))
cristy4c08aed2011-07-01 19:47:50 +00002368 {
2369 /*
2370 Tile texture onto the image background.
2371 */
cristye6178502011-12-23 17:02:29 +00002372 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002373 {
2374 register ssize_t
2375 x;
2376
2377 if (status == MagickFalse)
2378 continue;
cristye6178502011-12-23 17:02:29 +00002379 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002380 {
2381 MagickBooleanType
2382 thread_status;
2383
cristyfeb3e962012-03-29 17:25:55 +00002384 thread_status=CompositeImage(image,texture_image,image->compose,
2385 MagickFalse,x+texture_image->tile_offset.x,y+
2386 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002387 if (thread_status == MagickFalse)
2388 {
2389 status=thread_status;
2390 break;
2391 }
2392 }
2393 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2394 {
2395 MagickBooleanType
2396 proceed;
2397
cristy4c08aed2011-07-01 19:47:50 +00002398 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2399 y,image->rows);
2400 if (proceed == MagickFalse)
2401 status=MagickFalse;
2402 }
2403 }
2404 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2405 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002406 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002407 return(status);
2408 }
2409 /*
2410 Tile texture onto the image background (optimized).
2411 */
2412 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00002413 texture_view=AcquireVirtualCacheView(texture_image,exception);
2414 image_view=AcquireAuthenticCacheView(image,exception);
cristye8914392012-12-16 21:16:11 +00002415#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00002416 #pragma omp parallel for schedule(static,4) shared(status) \
2417 magick_threads(texture_image,image,1,1)
cristye8914392012-12-16 21:16:11 +00002418#endif
cristy4c08aed2011-07-01 19:47:50 +00002419 for (y=0; y < (ssize_t) image->rows; y++)
2420 {
2421 MagickBooleanType
2422 sync;
2423
2424 register const Quantum
2425 *p,
2426 *pixels;
2427
2428 register ssize_t
2429 x;
2430
2431 register Quantum
2432 *q;
2433
2434 size_t
2435 width;
2436
2437 if (status == MagickFalse)
2438 continue;
cristye6178502011-12-23 17:02:29 +00002439 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2440 (y+texture_image->tile_offset.y) % texture_image->rows,
2441 texture_image->columns,1,exception);
2442 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002443 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2444 {
2445 status=MagickFalse;
2446 continue;
2447 }
cristye6178502011-12-23 17:02:29 +00002448 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002449 {
2450 register ssize_t
cristye6178502011-12-23 17:02:29 +00002451 j;
cristy4c08aed2011-07-01 19:47:50 +00002452
2453 p=pixels;
cristye6178502011-12-23 17:02:29 +00002454 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002455 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2456 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002457 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002458 {
cristye6178502011-12-23 17:02:29 +00002459 register ssize_t
2460 i;
2461
cristy883fde12013-04-08 00:50:13 +00002462 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00002463 {
2464 p+=GetPixelChannels(texture_image);
2465 q+=GetPixelChannels(image);
2466 continue;
2467 }
cristye6178502011-12-23 17:02:29 +00002468 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2469 {
cristy5a23c552013-02-13 14:34:28 +00002470 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2471 PixelTrait traits=GetPixelChannelTraits(image,channel);
2472 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2473 channel);
cristye6178502011-12-23 17:02:29 +00002474 if ((traits == UndefinedPixelTrait) ||
2475 (texture_traits == UndefinedPixelTrait))
2476 continue;
2477 SetPixelChannel(image,channel,p[i],q);
2478 }
2479 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002480 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002481 }
2482 }
2483 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2484 if (sync == MagickFalse)
2485 status=MagickFalse;
2486 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2487 {
2488 MagickBooleanType
2489 proceed;
2490
cristy4c08aed2011-07-01 19:47:50 +00002491 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2492 image->rows);
2493 if (proceed == MagickFalse)
2494 status=MagickFalse;
2495 }
2496 }
2497 texture_view=DestroyCacheView(texture_view);
2498 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002499 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002500 return(status);
2501}