blob: 145cfbdc997529376dd207c2a5e0807488d29ef4 [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 {
cristy98a39202013-07-09 13:25:52 +00001724 if ((Sca*Da+Dca*Sa) >= Sa*Da)
cristye4a40472011-12-22 02:56:19 +00001725 {
cristy98a39202013-07-09 13:25:52 +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)
cristy7159f662012-10-28 17:32:43 +00001768 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001769 break;
1770 }
1771 case CopyBlueCompositeOp:
1772 case CopyYellowCompositeOp:
1773 {
cristyd197cbb2012-01-13 02:14:12 +00001774 if (channel == BluePixelChannel)
cristy7159f662012-10-28 17:32:43 +00001775 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001776 break;
1777 }
1778 case CopyGreenCompositeOp:
1779 case CopyMagentaCompositeOp:
1780 {
cristyd197cbb2012-01-13 02:14:12 +00001781 if (channel == GreenPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001782 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001783 break;
1784 }
1785 case CopyRedCompositeOp:
1786 case CopyCyanCompositeOp:
1787 {
cristyd197cbb2012-01-13 02:14:12 +00001788 if (channel == RedPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001789 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001790 break;
1791 }
cristy99abff32011-12-24 20:45:16 +00001792 case DarkenCompositeOp:
1793 {
1794 /*
1795 Darken is equivalent to a 'Minimum' method
1796 OR a greyscale version of a binary 'Or'
1797 OR the 'Intersection' of pixel sets.
1798 */
1799 if (Sc < Dc)
1800 {
1801 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1802 break;
1803 }
1804 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1805 break;
1806 }
cristye4a40472011-12-22 02:56:19 +00001807 case DarkenIntensityCompositeOp:
1808 {
cristy99abff32011-12-24 20:45:16 +00001809 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1810 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001811 break;
1812 }
1813 case DifferenceCompositeOp:
1814 {
1815 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1816 break;
1817 }
1818 case DissolveCompositeOp:
1819 {
1820 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1821 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1822 break;
1823 }
1824 case DivideDstCompositeOp:
1825 {
1826 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1827 {
cristy99abff32011-12-24 20:45:16 +00001828 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001829 break;
1830 }
1831 if (fabs(Dca) < MagickEpsilon)
1832 {
cristy99abff32011-12-24 20:45:16 +00001833 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001834 break;
1835 }
cristy99abff32011-12-24 20:45:16 +00001836 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001837 break;
1838 }
1839 case DivideSrcCompositeOp:
1840 {
1841 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1842 {
cristy99abff32011-12-24 20:45:16 +00001843 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001844 break;
1845 }
1846 if (fabs(Sca) < MagickEpsilon)
1847 {
cristy99abff32011-12-24 20:45:16 +00001848 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001849 break;
1850 }
cristy99abff32011-12-24 20:45:16 +00001851 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001852 break;
1853 }
1854 case DstAtopCompositeOp:
1855 {
1856 pixel=Dc*Da+Sc*(1.0-Da);
1857 break;
1858 }
1859 case DstCompositeOp:
1860 case NoCompositeOp:
1861 {
1862 pixel=Dc;
1863 break;
1864 }
1865 case DstInCompositeOp:
1866 {
1867 pixel=gamma*(Sa*Dc*Sa);
1868 break;
1869 }
1870 case DstOutCompositeOp:
1871 {
1872 pixel=gamma*(Da*Dc*(1.0-Sa));
1873 break;
1874 }
1875 case DstOverCompositeOp:
1876 {
1877 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1878 break;
1879 }
1880 case ExclusionCompositeOp:
1881 {
cristy99abff32011-12-24 20:45:16 +00001882 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1883 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001884 break;
1885 }
1886 case HardLightCompositeOp:
1887 {
1888 if ((2.0*Sca) < Sa)
1889 {
cristy99abff32011-12-24 20:45:16 +00001890 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001891 (1.0-Sa));
1892 break;
1893 }
cristy99abff32011-12-24 20:45:16 +00001894 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001895 Dca*(1.0-Sa));
1896 break;
1897 }
1898 case HueCompositeOp:
1899 {
cristy99abff32011-12-24 20:45:16 +00001900 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001901 {
1902 pixel=Dc;
1903 break;
1904 }
cristy99abff32011-12-24 20:45:16 +00001905 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001906 {
1907 pixel=Sc;
1908 break;
1909 }
cristy7133e642012-08-14 11:04:11 +00001910 CompositeHCL(destination_pixel.red,destination_pixel.green,
1911 destination_pixel.blue,&hue,&chroma,&luma);
1912 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001913 &hue,&sans,&sans);
cristy7133e642012-08-14 11:04:11 +00001914 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001915 switch (channel)
1916 {
1917 case RedPixelChannel: pixel=red; break;
1918 case GreenPixelChannel: pixel=green; break;
1919 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001920 default: pixel=Dc; break;
1921 }
1922 break;
1923 }
1924 case InCompositeOp:
1925 case SrcInCompositeOp:
1926 {
1927 pixel=gamma*(Da*Sc*Da);
1928 break;
1929 }
cristy99abff32011-12-24 20:45:16 +00001930 case LinearBurnCompositeOp:
1931 {
1932 /*
1933 LinearBurn: as defined by Abode Photoshop, according to
1934 http://www.simplefilter.de/en/basics/mixmods.html is:
1935
1936 f(Sc,Dc) = Sc + Dc - 1
1937 */
1938 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1939 break;
1940 }
1941 case LinearDodgeCompositeOp:
1942 {
1943 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1944 break;
1945 }
1946 case LinearLightCompositeOp:
1947 {
1948 /*
1949 LinearLight: as defined by Abode Photoshop, according to
1950 http://www.simplefilter.de/en/basics/mixmods.html is:
1951
1952 f(Sc,Dc) = Dc + 2*Sc - 1
1953 */
1954 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1955 break;
1956 }
1957 case LightenCompositeOp:
1958 {
1959 if (Sc > Dc)
1960 {
cristy24d5d722012-05-17 12:27:27 +00001961 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00001962 break;
1963 }
cristy24d5d722012-05-17 12:27:27 +00001964 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00001965 break;
1966 }
cristye4a40472011-12-22 02:56:19 +00001967 case LightenIntensityCompositeOp:
1968 {
1969 /*
1970 Lighten is equivalent to a 'Maximum' method
1971 OR a greyscale version of a binary 'And'
1972 OR the 'Union' of pixel sets.
1973 */
1974 pixel=Sa*GetPixelIntensity(composite_image,p) >
1975 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1976 break;
1977 }
cristye4a40472011-12-22 02:56:19 +00001978 case LuminizeCompositeOp:
1979 {
cristy99abff32011-12-24 20:45:16 +00001980 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001981 {
1982 pixel=Dc;
1983 break;
1984 }
cristy99abff32011-12-24 20:45:16 +00001985 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001986 {
1987 pixel=Sc;
1988 break;
1989 }
cristy7133e642012-08-14 11:04:11 +00001990 CompositeHCL(destination_pixel.red,destination_pixel.green,
1991 destination_pixel.blue,&hue,&chroma,&luma);
1992 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1993 &sans,&sans,&luma);
1994 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001995 switch (channel)
1996 {
1997 case RedPixelChannel: pixel=red; break;
1998 case GreenPixelChannel: pixel=green; break;
1999 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002000 default: pixel=Dc; break;
2001 }
2002 break;
2003 }
2004 case MathematicsCompositeOp:
2005 {
2006 /*
2007 'Mathematics' a free form user control mathematical composition
2008 is defined as...
2009
2010 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2011
2012 Where the arguments A,B,C,D are (currently) passed to composite
2013 as a command separated 'geometry' string in "compose:args" image
2014 artifact.
2015
2016 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2017
2018 Applying the SVG transparency formula (see above), we get...
2019
2020 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2021
2022 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2023 Dca*(1.0-Sa)
2024 */
2025 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2026 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2027 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2028 break;
2029 }
2030 case MinusDstCompositeOp:
2031 {
2032 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2033 break;
2034 }
2035 case MinusSrcCompositeOp:
2036 {
2037 /*
2038 Minus source from destination.
2039
2040 f(Sc,Dc) = Sc - Dc
2041 */
cristy99abff32011-12-24 20:45:16 +00002042 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002043 break;
2044 }
2045 case ModulateCompositeOp:
2046 {
2047 ssize_t
2048 offset;
2049
cristy99abff32011-12-24 20:45:16 +00002050 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002051 {
2052 pixel=Dc;
2053 break;
2054 }
2055 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2056 if (offset == 0)
2057 {
2058 pixel=Dc;
2059 break;
2060 }
cristy7133e642012-08-14 11:04:11 +00002061 CompositeHCL(destination_pixel.red,destination_pixel.green,
2062 destination_pixel.blue,&hue,&chroma,&luma);
2063 luma+=(0.01*percent_luma*offset)/midpoint;
2064 chroma*=0.01*percent_chroma;
2065 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002066 switch (channel)
2067 {
2068 case RedPixelChannel: pixel=red; break;
2069 case GreenPixelChannel: pixel=green; break;
2070 case BluePixelChannel: pixel=blue; break;
2071 default: pixel=Dc; break;
2072 }
2073 break;
2074 }
2075 case ModulusAddCompositeOp:
2076 {
2077 pixel=Sc+Dc;
2078 if (pixel > QuantumRange)
2079 pixel-=(QuantumRange+1.0);
2080 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2081 break;
2082 }
2083 case ModulusSubtractCompositeOp:
2084 {
cristy99abff32011-12-24 20:45:16 +00002085 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002086 if (pixel < 0.0)
2087 pixel+=(QuantumRange+1.0);
2088 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2089 break;
2090 }
2091 case MultiplyCompositeOp:
2092 {
cristy99abff32011-12-24 20:45:16 +00002093 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002094 break;
2095 }
2096 case OutCompositeOp:
2097 case SrcOutCompositeOp:
2098 {
2099 pixel=gamma*(Sa*Sc*(1.0-Da));
2100 break;
2101 }
2102 case OverCompositeOp:
2103 case SrcOverCompositeOp:
2104 {
cristy99abff32011-12-24 20:45:16 +00002105 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002106 break;
2107 }
2108 case OverlayCompositeOp:
2109 {
2110 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002111 {
2112 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2113 (1.0-Da));
2114 break;
2115 }
2116 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2117 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002118 break;
2119 }
2120 case PegtopLightCompositeOp:
2121 {
2122 /*
2123 PegTop: A Soft-Light alternative: A continuous version of the
2124 Softlight function, producing very similar results.
2125
2126 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2127
2128 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2129 */
2130 if (fabs(Da) < MagickEpsilon)
2131 {
cristy99abff32011-12-24 20:45:16 +00002132 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002133 break;
2134 }
cristy99abff32011-12-24 20:45:16 +00002135 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2136 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002137 break;
2138 }
2139 case PinLightCompositeOp:
2140 {
2141 /*
2142 PinLight: A Photoshop 7 composition method
2143 http://www.simplefilter.de/en/basics/mixmods.html
2144
2145 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2146 */
2147 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2148 {
cristy99abff32011-12-24 20:45:16 +00002149 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002150 break;
2151 }
2152 if ((Dca*Sa) > (2.0*Sca*Da))
2153 {
cristy99abff32011-12-24 20:45:16 +00002154 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002155 break;
2156 }
cristy99abff32011-12-24 20:45:16 +00002157 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002158 break;
2159 }
2160 case PlusCompositeOp:
2161 {
cristy24d5d722012-05-17 12:27:27 +00002162 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002163 break;
2164 }
2165 case SaturateCompositeOp:
2166 {
cristy99abff32011-12-24 20:45:16 +00002167 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002168 {
2169 pixel=Dc;
2170 break;
2171 }
cristy99abff32011-12-24 20:45:16 +00002172 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002173 {
2174 pixel=Sc;
2175 break;
2176 }
cristy7133e642012-08-14 11:04:11 +00002177 CompositeHCL(destination_pixel.red,destination_pixel.green,
2178 destination_pixel.blue,&hue,&chroma,&luma);
2179 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2180 &sans,&chroma,&sans);
2181 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002182 switch (channel)
2183 {
2184 case RedPixelChannel: pixel=red; break;
2185 case GreenPixelChannel: pixel=green; break;
2186 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002187 default: pixel=Dc; break;
2188 }
2189 break;
2190 }
2191 case ScreenCompositeOp:
2192 {
2193 /*
2194 Screen: a negated multiply:
2195
2196 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2197 */
cristy99abff32011-12-24 20:45:16 +00002198 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002199 break;
2200 }
2201 case SoftLightCompositeOp:
2202 {
2203 /*
2204 Refer to the March 2009 SVG specification.
2205 */
2206 if ((2.0*Sca) < Sa)
2207 {
cristy99abff32011-12-24 20:45:16 +00002208 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2209 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002210 break;
2211 }
2212 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2213 {
cristy99abff32011-12-24 20:45:16 +00002214 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2215 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2216 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002217 break;
2218 }
cristy99abff32011-12-24 20:45:16 +00002219 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2220 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002221 break;
2222 }
2223 case ThresholdCompositeOp:
2224 {
cristy7159f662012-10-28 17:32:43 +00002225 MagickRealType
cristye4a40472011-12-22 02:56:19 +00002226 delta;
2227
2228 delta=Sc-Dc;
cristy7159f662012-10-28 17:32:43 +00002229 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002230 {
2231 pixel=gamma*Dc;
2232 break;
2233 }
2234 pixel=gamma*(Dc+delta*amount);
2235 break;
2236 }
2237 case VividLightCompositeOp:
2238 {
2239 /*
2240 VividLight: A Photoshop 7 composition method. See
2241 http://www.simplefilter.de/en/basics/mixmods.html.
2242
2243 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2244 */
2245 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2246 {
cristy99abff32011-12-24 20:45:16 +00002247 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002248 break;
2249 }
2250 if ((2.0*Sca) <= Sa)
2251 {
cristy99abff32011-12-24 20:45:16 +00002252 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2253 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002254 break;
2255 }
cristy99abff32011-12-24 20:45:16 +00002256 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2257 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002258 break;
2259 }
2260 case XorCompositeOp:
2261 {
cristy99abff32011-12-24 20:45:16 +00002262 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002263 break;
2264 }
2265 default:
2266 {
2267 pixel=Sc;
2268 break;
2269 }
2270 }
2271 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002272 }
cristyed231572011-07-14 02:18:59 +00002273 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002274 channels=GetPixelChannels(composite_image);
2275 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002276 p=pixels;
cristyed231572011-07-14 02:18:59 +00002277 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002278 }
2279 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2280 status=MagickFalse;
2281 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2282 {
2283 MagickBooleanType
2284 proceed;
2285
2286#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002287 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002288#endif
2289 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2290 image->rows);
2291 if (proceed == MagickFalse)
2292 status=MagickFalse;
2293 }
2294 }
2295 composite_view=DestroyCacheView(composite_view);
2296 image_view=DestroyCacheView(image_view);
2297 if (destination_image != (Image * ) NULL)
2298 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002299 else
2300 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00002301 return(status);
2302}
2303
2304/*
2305%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2306% %
2307% %
2308% %
2309% T e x t u r e I m a g e %
2310% %
2311% %
2312% %
2313%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2314%
2315% TextureImage() repeatedly tiles the texture image across and down the image
2316% canvas.
2317%
2318% The format of the TextureImage method is:
2319%
cristy30d8c942012-02-07 13:44:59 +00002320% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002321% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002322%
2323% A description of each parameter follows:
2324%
2325% o image: the image.
2326%
cristye6178502011-12-23 17:02:29 +00002327% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002328%
2329*/
cristy30d8c942012-02-07 13:44:59 +00002330MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2331 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002332{
2333#define TextureImageTag "Texture/Image"
2334
2335 CacheView
2336 *image_view,
2337 *texture_view;
2338
cristy30d8c942012-02-07 13:44:59 +00002339 Image
2340 *texture_image;
2341
cristy4c08aed2011-07-01 19:47:50 +00002342 MagickBooleanType
2343 status;
2344
2345 ssize_t
2346 y;
2347
2348 assert(image != (Image *) NULL);
2349 if (image->debug != MagickFalse)
2350 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2351 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002352 if (texture == (const Image *) NULL)
2353 return(MagickFalse);
2354 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2355 return(MagickFalse);
2356 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002357 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002358 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002359 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002360 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2361 exception);
cristy4c08aed2011-07-01 19:47:50 +00002362 status=MagickTrue;
2363 if ((image->compose != CopyCompositeOp) &&
cristy974a2a82013-04-23 12:57:16 +00002364 ((image->compose != OverCompositeOp) ||
2365 (image->alpha_trait == BlendPixelTrait) ||
cristy8a46d822012-08-28 23:32:39 +00002366 (texture_image->alpha_trait == BlendPixelTrait)))
cristy4c08aed2011-07-01 19:47:50 +00002367 {
2368 /*
2369 Tile texture onto the image background.
2370 */
cristye6178502011-12-23 17:02:29 +00002371 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002372 {
2373 register ssize_t
2374 x;
2375
2376 if (status == MagickFalse)
2377 continue;
cristye6178502011-12-23 17:02:29 +00002378 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002379 {
2380 MagickBooleanType
2381 thread_status;
2382
cristyfeb3e962012-03-29 17:25:55 +00002383 thread_status=CompositeImage(image,texture_image,image->compose,
2384 MagickFalse,x+texture_image->tile_offset.x,y+
2385 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002386 if (thread_status == MagickFalse)
2387 {
2388 status=thread_status;
2389 break;
2390 }
2391 }
2392 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2393 {
2394 MagickBooleanType
2395 proceed;
2396
cristy4c08aed2011-07-01 19:47:50 +00002397 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2398 y,image->rows);
2399 if (proceed == MagickFalse)
2400 status=MagickFalse;
2401 }
2402 }
2403 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2404 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002405 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002406 return(status);
2407 }
2408 /*
2409 Tile texture onto the image background (optimized).
2410 */
2411 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00002412 texture_view=AcquireVirtualCacheView(texture_image,exception);
2413 image_view=AcquireAuthenticCacheView(image,exception);
cristye8914392012-12-16 21:16:11 +00002414#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00002415 #pragma omp parallel for schedule(static,4) shared(status) \
2416 magick_threads(texture_image,image,1,1)
cristye8914392012-12-16 21:16:11 +00002417#endif
cristy4c08aed2011-07-01 19:47:50 +00002418 for (y=0; y < (ssize_t) image->rows; y++)
2419 {
2420 MagickBooleanType
2421 sync;
2422
2423 register const Quantum
2424 *p,
2425 *pixels;
2426
2427 register ssize_t
2428 x;
2429
2430 register Quantum
2431 *q;
2432
2433 size_t
2434 width;
2435
2436 if (status == MagickFalse)
2437 continue;
cristye6178502011-12-23 17:02:29 +00002438 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2439 (y+texture_image->tile_offset.y) % texture_image->rows,
2440 texture_image->columns,1,exception);
2441 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002442 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2443 {
2444 status=MagickFalse;
2445 continue;
2446 }
cristye6178502011-12-23 17:02:29 +00002447 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002448 {
2449 register ssize_t
cristye6178502011-12-23 17:02:29 +00002450 j;
cristy4c08aed2011-07-01 19:47:50 +00002451
2452 p=pixels;
cristye6178502011-12-23 17:02:29 +00002453 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002454 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2455 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002456 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002457 {
cristye6178502011-12-23 17:02:29 +00002458 register ssize_t
2459 i;
2460
cristy883fde12013-04-08 00:50:13 +00002461 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00002462 {
2463 p+=GetPixelChannels(texture_image);
2464 q+=GetPixelChannels(image);
2465 continue;
2466 }
cristye6178502011-12-23 17:02:29 +00002467 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2468 {
cristy5a23c552013-02-13 14:34:28 +00002469 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2470 PixelTrait traits=GetPixelChannelTraits(image,channel);
2471 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2472 channel);
cristye6178502011-12-23 17:02:29 +00002473 if ((traits == UndefinedPixelTrait) ||
2474 (texture_traits == UndefinedPixelTrait))
2475 continue;
2476 SetPixelChannel(image,channel,p[i],q);
2477 }
2478 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002479 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002480 }
2481 }
2482 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2483 if (sync == MagickFalse)
2484 status=MagickFalse;
2485 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2486 {
2487 MagickBooleanType
2488 proceed;
2489
cristy4c08aed2011-07-01 19:47:50 +00002490 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2491 image->rows);
2492 if (proceed == MagickFalse)
2493 status=MagickFalse;
2494 }
2495 }
2496 texture_view=DestroyCacheView(texture_view);
2497 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002498 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002499 return(status);
2500}