blob: 3a8d0b8014338b5d720f188fe729c5c413982b2c [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"
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 {
1724 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1725 {
cristy99abff32011-12-24 20:45:16 +00001726 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001727 break;
1728 }
1729 if (fabs(Sca-Sa) < MagickEpsilon)
1730 {
cristy99abff32011-12-24 20:45:16 +00001731 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001732 break;
1733 }
cristy99abff32011-12-24 20:45:16 +00001734 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001735 (1.0-Sa));
1736 break;
1737 }
1738 case ColorizeCompositeOp:
1739 {
cristy99abff32011-12-24 20:45:16 +00001740 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001741 {
1742 pixel=Dc;
1743 break;
1744 }
cristy99abff32011-12-24 20:45:16 +00001745 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001746 {
1747 pixel=Sc;
1748 break;
1749 }
cristy7133e642012-08-14 11:04:11 +00001750 CompositeHCL(destination_pixel.red,destination_pixel.green,
1751 destination_pixel.blue,&sans,&sans,&luma);
1752 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1753 &hue,&chroma,&sans);
1754 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001755 switch (channel)
1756 {
1757 case RedPixelChannel: pixel=red; break;
1758 case GreenPixelChannel: pixel=green; break;
1759 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001760 default: pixel=Dc; break;
1761 }
1762 break;
1763 }
cristye4a40472011-12-22 02:56:19 +00001764 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001765 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001766 {
cristy24d5d722012-05-17 12:27:27 +00001767 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001768 break;
1769 }
1770 case CopyBlackCompositeOp:
1771 {
cristyd197cbb2012-01-13 02:14:12 +00001772 if (channel == BlackPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001773 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001774 break;
1775 }
1776 case CopyBlueCompositeOp:
1777 case CopyYellowCompositeOp:
1778 {
cristyd197cbb2012-01-13 02:14:12 +00001779 if (channel == BluePixelChannel)
cristy7159f662012-10-28 17:32:43 +00001780 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001781 break;
1782 }
1783 case CopyGreenCompositeOp:
1784 case CopyMagentaCompositeOp:
1785 {
cristyd197cbb2012-01-13 02:14:12 +00001786 if (channel == GreenPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001787 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001788 break;
1789 }
1790 case CopyRedCompositeOp:
1791 case CopyCyanCompositeOp:
1792 {
cristyd197cbb2012-01-13 02:14:12 +00001793 if (channel == RedPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001794 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001795 break;
1796 }
cristy99abff32011-12-24 20:45:16 +00001797 case DarkenCompositeOp:
1798 {
1799 /*
1800 Darken is equivalent to a 'Minimum' method
1801 OR a greyscale version of a binary 'Or'
1802 OR the 'Intersection' of pixel sets.
1803 */
1804 if (Sc < Dc)
1805 {
1806 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1807 break;
1808 }
1809 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1810 break;
1811 }
cristye4a40472011-12-22 02:56:19 +00001812 case DarkenIntensityCompositeOp:
1813 {
cristy99abff32011-12-24 20:45:16 +00001814 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1815 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001816 break;
1817 }
1818 case DifferenceCompositeOp:
1819 {
1820 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1821 break;
1822 }
1823 case DissolveCompositeOp:
1824 {
1825 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1826 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1827 break;
1828 }
1829 case DivideDstCompositeOp:
1830 {
1831 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1832 {
cristy99abff32011-12-24 20:45:16 +00001833 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001834 break;
1835 }
1836 if (fabs(Dca) < MagickEpsilon)
1837 {
cristy99abff32011-12-24 20:45:16 +00001838 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001839 break;
1840 }
cristy99abff32011-12-24 20:45:16 +00001841 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001842 break;
1843 }
1844 case DivideSrcCompositeOp:
1845 {
1846 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1847 {
cristy99abff32011-12-24 20:45:16 +00001848 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001849 break;
1850 }
1851 if (fabs(Sca) < MagickEpsilon)
1852 {
cristy99abff32011-12-24 20:45:16 +00001853 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001854 break;
1855 }
cristy99abff32011-12-24 20:45:16 +00001856 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001857 break;
1858 }
1859 case DstAtopCompositeOp:
1860 {
1861 pixel=Dc*Da+Sc*(1.0-Da);
1862 break;
1863 }
1864 case DstCompositeOp:
1865 case NoCompositeOp:
1866 {
1867 pixel=Dc;
1868 break;
1869 }
1870 case DstInCompositeOp:
1871 {
1872 pixel=gamma*(Sa*Dc*Sa);
1873 break;
1874 }
1875 case DstOutCompositeOp:
1876 {
1877 pixel=gamma*(Da*Dc*(1.0-Sa));
1878 break;
1879 }
1880 case DstOverCompositeOp:
1881 {
1882 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1883 break;
1884 }
1885 case ExclusionCompositeOp:
1886 {
cristy99abff32011-12-24 20:45:16 +00001887 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1888 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001889 break;
1890 }
1891 case HardLightCompositeOp:
1892 {
1893 if ((2.0*Sca) < Sa)
1894 {
cristy99abff32011-12-24 20:45:16 +00001895 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001896 (1.0-Sa));
1897 break;
1898 }
cristy99abff32011-12-24 20:45:16 +00001899 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001900 Dca*(1.0-Sa));
1901 break;
1902 }
1903 case HueCompositeOp:
1904 {
cristy99abff32011-12-24 20:45:16 +00001905 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001906 {
1907 pixel=Dc;
1908 break;
1909 }
cristy99abff32011-12-24 20:45:16 +00001910 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001911 {
1912 pixel=Sc;
1913 break;
1914 }
cristy7133e642012-08-14 11:04:11 +00001915 CompositeHCL(destination_pixel.red,destination_pixel.green,
1916 destination_pixel.blue,&hue,&chroma,&luma);
1917 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001918 &hue,&sans,&sans);
cristy7133e642012-08-14 11:04:11 +00001919 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001920 switch (channel)
1921 {
1922 case RedPixelChannel: pixel=red; break;
1923 case GreenPixelChannel: pixel=green; break;
1924 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001925 default: pixel=Dc; break;
1926 }
1927 break;
1928 }
1929 case InCompositeOp:
1930 case SrcInCompositeOp:
1931 {
1932 pixel=gamma*(Da*Sc*Da);
1933 break;
1934 }
cristy99abff32011-12-24 20:45:16 +00001935 case LinearBurnCompositeOp:
1936 {
1937 /*
1938 LinearBurn: as defined by Abode Photoshop, according to
1939 http://www.simplefilter.de/en/basics/mixmods.html is:
1940
1941 f(Sc,Dc) = Sc + Dc - 1
1942 */
1943 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1944 break;
1945 }
1946 case LinearDodgeCompositeOp:
1947 {
1948 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1949 break;
1950 }
1951 case LinearLightCompositeOp:
1952 {
1953 /*
1954 LinearLight: as defined by Abode Photoshop, according to
1955 http://www.simplefilter.de/en/basics/mixmods.html is:
1956
1957 f(Sc,Dc) = Dc + 2*Sc - 1
1958 */
1959 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1960 break;
1961 }
1962 case LightenCompositeOp:
1963 {
1964 if (Sc > Dc)
1965 {
cristy24d5d722012-05-17 12:27:27 +00001966 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00001967 break;
1968 }
cristy24d5d722012-05-17 12:27:27 +00001969 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00001970 break;
1971 }
cristye4a40472011-12-22 02:56:19 +00001972 case LightenIntensityCompositeOp:
1973 {
1974 /*
1975 Lighten is equivalent to a 'Maximum' method
1976 OR a greyscale version of a binary 'And'
1977 OR the 'Union' of pixel sets.
1978 */
1979 pixel=Sa*GetPixelIntensity(composite_image,p) >
1980 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1981 break;
1982 }
cristye4a40472011-12-22 02:56:19 +00001983 case LuminizeCompositeOp:
1984 {
cristy99abff32011-12-24 20:45:16 +00001985 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001986 {
1987 pixel=Dc;
1988 break;
1989 }
cristy99abff32011-12-24 20:45:16 +00001990 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001991 {
1992 pixel=Sc;
1993 break;
1994 }
cristy7133e642012-08-14 11:04:11 +00001995 CompositeHCL(destination_pixel.red,destination_pixel.green,
1996 destination_pixel.blue,&hue,&chroma,&luma);
1997 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1998 &sans,&sans,&luma);
1999 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002000 switch (channel)
2001 {
2002 case RedPixelChannel: pixel=red; break;
2003 case GreenPixelChannel: pixel=green; break;
2004 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002005 default: pixel=Dc; break;
2006 }
2007 break;
2008 }
2009 case MathematicsCompositeOp:
2010 {
2011 /*
2012 'Mathematics' a free form user control mathematical composition
2013 is defined as...
2014
2015 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2016
2017 Where the arguments A,B,C,D are (currently) passed to composite
2018 as a command separated 'geometry' string in "compose:args" image
2019 artifact.
2020
2021 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2022
2023 Applying the SVG transparency formula (see above), we get...
2024
2025 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2026
2027 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2028 Dca*(1.0-Sa)
2029 */
2030 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2031 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2032 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2033 break;
2034 }
2035 case MinusDstCompositeOp:
2036 {
2037 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2038 break;
2039 }
2040 case MinusSrcCompositeOp:
2041 {
2042 /*
2043 Minus source from destination.
2044
2045 f(Sc,Dc) = Sc - Dc
2046 */
cristy99abff32011-12-24 20:45:16 +00002047 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002048 break;
2049 }
2050 case ModulateCompositeOp:
2051 {
2052 ssize_t
2053 offset;
2054
cristy99abff32011-12-24 20:45:16 +00002055 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002056 {
2057 pixel=Dc;
2058 break;
2059 }
2060 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2061 if (offset == 0)
2062 {
2063 pixel=Dc;
2064 break;
2065 }
cristy7133e642012-08-14 11:04:11 +00002066 CompositeHCL(destination_pixel.red,destination_pixel.green,
2067 destination_pixel.blue,&hue,&chroma,&luma);
2068 luma+=(0.01*percent_luma*offset)/midpoint;
2069 chroma*=0.01*percent_chroma;
2070 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002071 switch (channel)
2072 {
2073 case RedPixelChannel: pixel=red; break;
2074 case GreenPixelChannel: pixel=green; break;
2075 case BluePixelChannel: pixel=blue; break;
2076 default: pixel=Dc; break;
2077 }
2078 break;
2079 }
2080 case ModulusAddCompositeOp:
2081 {
2082 pixel=Sc+Dc;
2083 if (pixel > QuantumRange)
2084 pixel-=(QuantumRange+1.0);
2085 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2086 break;
2087 }
2088 case ModulusSubtractCompositeOp:
2089 {
cristy99abff32011-12-24 20:45:16 +00002090 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002091 if (pixel < 0.0)
2092 pixel+=(QuantumRange+1.0);
2093 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2094 break;
2095 }
2096 case MultiplyCompositeOp:
2097 {
cristy99abff32011-12-24 20:45:16 +00002098 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002099 break;
2100 }
2101 case OutCompositeOp:
2102 case SrcOutCompositeOp:
2103 {
2104 pixel=gamma*(Sa*Sc*(1.0-Da));
2105 break;
2106 }
2107 case OverCompositeOp:
2108 case SrcOverCompositeOp:
2109 {
cristy99abff32011-12-24 20:45:16 +00002110 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002111 break;
2112 }
2113 case OverlayCompositeOp:
2114 {
2115 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002116 {
2117 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2118 (1.0-Da));
2119 break;
2120 }
2121 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2122 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002123 break;
2124 }
2125 case PegtopLightCompositeOp:
2126 {
2127 /*
2128 PegTop: A Soft-Light alternative: A continuous version of the
2129 Softlight function, producing very similar results.
2130
2131 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2132
2133 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2134 */
2135 if (fabs(Da) < MagickEpsilon)
2136 {
cristy99abff32011-12-24 20:45:16 +00002137 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002138 break;
2139 }
cristy99abff32011-12-24 20:45:16 +00002140 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2141 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002142 break;
2143 }
2144 case PinLightCompositeOp:
2145 {
2146 /*
2147 PinLight: A Photoshop 7 composition method
2148 http://www.simplefilter.de/en/basics/mixmods.html
2149
2150 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2151 */
2152 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2153 {
cristy99abff32011-12-24 20:45:16 +00002154 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002155 break;
2156 }
2157 if ((Dca*Sa) > (2.0*Sca*Da))
2158 {
cristy99abff32011-12-24 20:45:16 +00002159 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002160 break;
2161 }
cristy99abff32011-12-24 20:45:16 +00002162 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002163 break;
2164 }
2165 case PlusCompositeOp:
2166 {
cristy24d5d722012-05-17 12:27:27 +00002167 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002168 break;
2169 }
2170 case SaturateCompositeOp:
2171 {
cristy99abff32011-12-24 20:45:16 +00002172 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002173 {
2174 pixel=Dc;
2175 break;
2176 }
cristy99abff32011-12-24 20:45:16 +00002177 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002178 {
2179 pixel=Sc;
2180 break;
2181 }
cristy7133e642012-08-14 11:04:11 +00002182 CompositeHCL(destination_pixel.red,destination_pixel.green,
2183 destination_pixel.blue,&hue,&chroma,&luma);
2184 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2185 &sans,&chroma,&sans);
2186 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002187 switch (channel)
2188 {
2189 case RedPixelChannel: pixel=red; break;
2190 case GreenPixelChannel: pixel=green; break;
2191 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002192 default: pixel=Dc; break;
2193 }
2194 break;
2195 }
2196 case ScreenCompositeOp:
2197 {
2198 /*
2199 Screen: a negated multiply:
2200
2201 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2202 */
cristy99abff32011-12-24 20:45:16 +00002203 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002204 break;
2205 }
2206 case SoftLightCompositeOp:
2207 {
2208 /*
2209 Refer to the March 2009 SVG specification.
2210 */
2211 if ((2.0*Sca) < Sa)
2212 {
cristy99abff32011-12-24 20:45:16 +00002213 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2214 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002215 break;
2216 }
2217 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2218 {
cristy99abff32011-12-24 20:45:16 +00002219 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2220 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2221 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002222 break;
2223 }
cristy99abff32011-12-24 20:45:16 +00002224 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2225 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002226 break;
2227 }
2228 case ThresholdCompositeOp:
2229 {
cristy7159f662012-10-28 17:32:43 +00002230 MagickRealType
cristye4a40472011-12-22 02:56:19 +00002231 delta;
2232
2233 delta=Sc-Dc;
cristy7159f662012-10-28 17:32:43 +00002234 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002235 {
2236 pixel=gamma*Dc;
2237 break;
2238 }
2239 pixel=gamma*(Dc+delta*amount);
2240 break;
2241 }
2242 case VividLightCompositeOp:
2243 {
2244 /*
2245 VividLight: A Photoshop 7 composition method. See
2246 http://www.simplefilter.de/en/basics/mixmods.html.
2247
2248 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2249 */
2250 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2251 {
cristy99abff32011-12-24 20:45:16 +00002252 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002253 break;
2254 }
2255 if ((2.0*Sca) <= Sa)
2256 {
cristy99abff32011-12-24 20:45:16 +00002257 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2258 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002259 break;
2260 }
cristy99abff32011-12-24 20:45:16 +00002261 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2262 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002263 break;
2264 }
2265 case XorCompositeOp:
2266 {
cristy99abff32011-12-24 20:45:16 +00002267 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002268 break;
2269 }
2270 default:
2271 {
2272 pixel=Sc;
2273 break;
2274 }
2275 }
2276 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002277 }
cristyed231572011-07-14 02:18:59 +00002278 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002279 channels=GetPixelChannels(composite_image);
2280 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002281 p=pixels;
cristyed231572011-07-14 02:18:59 +00002282 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002283 }
2284 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2285 status=MagickFalse;
2286 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2287 {
2288 MagickBooleanType
2289 proceed;
2290
2291#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002292 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002293#endif
2294 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2295 image->rows);
2296 if (proceed == MagickFalse)
2297 status=MagickFalse;
2298 }
2299 }
2300 composite_view=DestroyCacheView(composite_view);
2301 image_view=DestroyCacheView(image_view);
2302 if (destination_image != (Image * ) NULL)
2303 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002304 else
2305 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00002306 return(status);
2307}
2308
2309/*
2310%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2311% %
2312% %
2313% %
2314% T e x t u r e I m a g e %
2315% %
2316% %
2317% %
2318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2319%
2320% TextureImage() repeatedly tiles the texture image across and down the image
2321% canvas.
2322%
2323% The format of the TextureImage method is:
2324%
cristy30d8c942012-02-07 13:44:59 +00002325% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002326% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002327%
2328% A description of each parameter follows:
2329%
2330% o image: the image.
2331%
cristye6178502011-12-23 17:02:29 +00002332% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002333%
2334*/
cristy30d8c942012-02-07 13:44:59 +00002335MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2336 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002337{
2338#define TextureImageTag "Texture/Image"
2339
2340 CacheView
2341 *image_view,
2342 *texture_view;
2343
cristy30d8c942012-02-07 13:44:59 +00002344 Image
2345 *texture_image;
2346
cristy4c08aed2011-07-01 19:47:50 +00002347 MagickBooleanType
2348 status;
2349
2350 ssize_t
2351 y;
2352
2353 assert(image != (Image *) NULL);
2354 if (image->debug != MagickFalse)
2355 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2356 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002357 if (texture == (const Image *) NULL)
2358 return(MagickFalse);
2359 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2360 return(MagickFalse);
2361 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002362 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002363 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002364 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002365 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2366 exception);
cristy4c08aed2011-07-01 19:47:50 +00002367 status=MagickTrue;
2368 if ((image->compose != CopyCompositeOp) &&
cristy974a2a82013-04-23 12:57:16 +00002369 ((image->compose != OverCompositeOp) ||
2370 (image->alpha_trait == BlendPixelTrait) ||
cristy8a46d822012-08-28 23:32:39 +00002371 (texture_image->alpha_trait == BlendPixelTrait)))
cristy4c08aed2011-07-01 19:47:50 +00002372 {
2373 /*
2374 Tile texture onto the image background.
2375 */
cristye6178502011-12-23 17:02:29 +00002376 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002377 {
2378 register ssize_t
2379 x;
2380
2381 if (status == MagickFalse)
2382 continue;
cristye6178502011-12-23 17:02:29 +00002383 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002384 {
2385 MagickBooleanType
2386 thread_status;
2387
cristyfeb3e962012-03-29 17:25:55 +00002388 thread_status=CompositeImage(image,texture_image,image->compose,
2389 MagickFalse,x+texture_image->tile_offset.x,y+
2390 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002391 if (thread_status == MagickFalse)
2392 {
2393 status=thread_status;
2394 break;
2395 }
2396 }
2397 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2398 {
2399 MagickBooleanType
2400 proceed;
2401
cristy4c08aed2011-07-01 19:47:50 +00002402 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2403 y,image->rows);
2404 if (proceed == MagickFalse)
2405 status=MagickFalse;
2406 }
2407 }
2408 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2409 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002410 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002411 return(status);
2412 }
2413 /*
2414 Tile texture onto the image background (optimized).
2415 */
2416 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00002417 texture_view=AcquireVirtualCacheView(texture_image,exception);
2418 image_view=AcquireAuthenticCacheView(image,exception);
cristye8914392012-12-16 21:16:11 +00002419#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00002420 #pragma omp parallel for schedule(static,4) shared(status) \
2421 magick_threads(texture_image,image,1,1)
cristye8914392012-12-16 21:16:11 +00002422#endif
cristy4c08aed2011-07-01 19:47:50 +00002423 for (y=0; y < (ssize_t) image->rows; y++)
2424 {
2425 MagickBooleanType
2426 sync;
2427
2428 register const Quantum
2429 *p,
2430 *pixels;
2431
2432 register ssize_t
2433 x;
2434
2435 register Quantum
2436 *q;
2437
2438 size_t
2439 width;
2440
2441 if (status == MagickFalse)
2442 continue;
cristye6178502011-12-23 17:02:29 +00002443 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2444 (y+texture_image->tile_offset.y) % texture_image->rows,
2445 texture_image->columns,1,exception);
2446 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002447 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2448 {
2449 status=MagickFalse;
2450 continue;
2451 }
cristye6178502011-12-23 17:02:29 +00002452 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002453 {
2454 register ssize_t
cristye6178502011-12-23 17:02:29 +00002455 j;
cristy4c08aed2011-07-01 19:47:50 +00002456
2457 p=pixels;
cristye6178502011-12-23 17:02:29 +00002458 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002459 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2460 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002461 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002462 {
cristye6178502011-12-23 17:02:29 +00002463 register ssize_t
2464 i;
2465
cristy883fde12013-04-08 00:50:13 +00002466 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00002467 {
2468 p+=GetPixelChannels(texture_image);
2469 q+=GetPixelChannels(image);
2470 continue;
2471 }
cristye6178502011-12-23 17:02:29 +00002472 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2473 {
cristy5a23c552013-02-13 14:34:28 +00002474 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2475 PixelTrait traits=GetPixelChannelTraits(image,channel);
2476 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2477 channel);
cristye6178502011-12-23 17:02:29 +00002478 if ((traits == UndefinedPixelTrait) ||
2479 (texture_traits == UndefinedPixelTrait))
2480 continue;
2481 SetPixelChannel(image,channel,p[i],q);
2482 }
2483 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002484 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002485 }
2486 }
2487 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2488 if (sync == MagickFalse)
2489 status=MagickFalse;
2490 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2491 {
2492 MagickBooleanType
2493 proceed;
2494
cristy4c08aed2011-07-01 19:47:50 +00002495 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2496 image->rows);
2497 if (proceed == MagickFalse)
2498 status=MagickFalse;
2499 }
2500 }
2501 texture_view=DestroyCacheView(texture_view);
2502 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002503 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002504 return(status);
2505}