blob: 65b9f9f4baef126bcefce7bfc3c911dbe576d361 [file] [log] [blame]
cristy0b3cd892012-08-01 23:13:50 +00001/*
cristy4c08aed2011-07-01 19:47:50 +00002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7% C O O MM MM P P O O SS I T E %
8% C O O M M M PPPP O O SSS I T EEE %
9% C O O M M P O O SS I T E %
10% CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
11% %
12% %
13% MagickCore Image Composite Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy45ef08f2012-12-07 13:13:34 +000020% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
cristy4c08aed2011-07-01 19:47:50 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/cache.h"
cristy52010022011-10-21 18:07:37 +000046#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/cache-view.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/colorspace-private.h"
53#include "MagickCore/composite.h"
54#include "MagickCore/composite-private.h"
55#include "MagickCore/constitute.h"
56#include "MagickCore/draw.h"
57#include "MagickCore/fx.h"
58#include "MagickCore/gem.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/image.h"
61#include "MagickCore/image-private.h"
62#include "MagickCore/list.h"
63#include "MagickCore/log.h"
64#include "MagickCore/monitor.h"
65#include "MagickCore/monitor-private.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/option.h"
68#include "MagickCore/pixel-accessor.h"
69#include "MagickCore/property.h"
70#include "MagickCore/quantum.h"
71#include "MagickCore/resample.h"
72#include "MagickCore/resource_.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/thread-private.h"
cristy191c0b72012-08-12 16:29:52 +000075#include "MagickCore/threshold.h"
cristy63a81872012-03-22 15:52:52 +000076#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000077#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000078#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000079#include "MagickCore/version.h"
80
81/*
82%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83% %
84% %
85% %
cristyf4ad9df2011-07-08 16:49:03 +000086% C o m p o s i t e I m a g e %
cristy4c08aed2011-07-01 19:47:50 +000087% %
88% %
89% %
90%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91%
cristyf4ad9df2011-07-08 16:49:03 +000092% CompositeImage() returns the second image composited onto the first
cristy4c08aed2011-07-01 19:47:50 +000093% at the specified offset, using the specified composite method.
94%
cristyf4ad9df2011-07-08 16:49:03 +000095% The format of the CompositeImage method is:
cristy4c08aed2011-07-01 19:47:50 +000096%
97% MagickBooleanType CompositeImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +000098% const Image *composite_image,const CompositeOperator compose,
99% const MagickBooleanType clip_to_self,const ssize_t x_offset,
100% const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000101%
102% A description of each parameter follows:
103%
104% o image: the destination image, modified by he composition
105%
cristyfeb3e962012-03-29 17:25:55 +0000106% o composite_image: the composite (source) image.
107%
cristy4c08aed2011-07-01 19:47:50 +0000108% o compose: This operator affects how the composite is applied to
109% the image. The operators and how they are utilized are listed here
110% http://www.w3.org/TR/SVG12/#compositing.
111%
cristyfeb3e962012-03-29 17:25:55 +0000112% o clip_to_self: set to MagickTrue to limit composition to area composed.
cristy4c08aed2011-07-01 19:47:50 +0000113%
114% o x_offset: the column offset of the composited image.
115%
116% o y_offset: the row offset of the composited image.
117%
118% Extra Controls from Image meta-data in 'composite_image' (artifacts)
119%
120% o "compose:args"
121% A string containing extra numerical arguments for specific compose
122% methods, generally expressed as a 'geometry' or a comma separated list
123% of numbers.
124%
125% Compose methods needing such arguments include "BlendCompositeOp" and
126% "DisplaceCompositeOp".
127%
cristye941a752011-10-15 01:52:48 +0000128% o exception: return any errors or warnings in this structure.
129%
cristy4c08aed2011-07-01 19:47:50 +0000130*/
131
anthonyea068a52012-04-09 05:46:25 +0000132/*
133 Composition based on the SVG specification:
134
135 A Composition is defined by...
136 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
137 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
138 Y = 1 for source preserved
139 Z = 1 for destination preserved
140
141 Conversion to transparency (then optimized)
142 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
143 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
144
145 Where...
146 Sca = Sc*Sa normalized Source color divided by Source alpha
147 Dca = Dc*Da normalized Dest color divided by Dest alpha
148 Dc' = Dca'/Da' the desired color value for this channel.
149
150 Da' in in the follow formula as 'gamma' The resulting alpla value.
151
152 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
153 the following optimizations...
154 gamma = Sa+Da-Sa*Da;
155 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
156 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
157
158 The above SVG definitions also definate that Mathematical Composition
159 methods should use a 'Over' blending mode for Alpha Channel.
160 It however was not applied for composition modes of 'Plus', 'Minus',
161 the modulus versions of 'Add' and 'Subtract'.
162
163 Mathematical operator changes to be applied from IM v6.7...
164
165 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
166 'ModulusAdd' and 'ModulusSubtract' for clarity.
167
168 2) All mathematical compositions work as per the SVG specification
169 with regard to blending. This now includes 'ModulusAdd' and
170 'ModulusSubtract'.
171
172 3) When the special channel flag 'sync' (syncronize channel updates)
173 is turned off (enabled by default) then mathematical compositions are
174 only performed on the channels specified, and are applied
175 independantally of each other. In other words the mathematics is
176 performed as 'pure' mathematical operations, rather than as image
177 operations.
178*/
cristy4c08aed2011-07-01 19:47:50 +0000179
cristy7159f662012-10-28 17:32:43 +0000180static inline MagickRealType MagickMin(const MagickRealType x,
181 const MagickRealType y)
cristye4a40472011-12-22 02:56:19 +0000182{
183 if (x < y)
184 return(x);
185 return(y);
186}
cristyddeeea22012-04-12 01:33:09 +0000187
cristy7159f662012-10-28 17:32:43 +0000188static inline MagickRealType MagickMax(const MagickRealType x,
189 const MagickRealType y)
cristye4a40472011-12-22 02:56:19 +0000190{
191 if (x > y)
192 return(x);
193 return(y);
194}
195
cristy7159f662012-10-28 17:32:43 +0000196static inline MagickRealType ConvertHueToRGB(MagickRealType m1,
197 MagickRealType m2,MagickRealType hue)
cristyd8f16f72012-08-13 12:49:50 +0000198{
199 if (hue < 0.0)
200 hue+=1.0;
201 if (hue > 1.0)
202 hue-=1.0;
203 if ((6.0*hue) < 1.0)
204 return(m1+6.0*(m2-m1)*hue);
205 if ((2.0*hue) < 1.0)
206 return(m2);
207 if ((3.0*hue) < 2.0)
208 return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
209 return(m1);
210}
211
cristy7159f662012-10-28 17:32:43 +0000212static void HCLComposite(const MagickRealType hue,const MagickRealType chroma,
213 const MagickRealType luma,MagickRealType *red,MagickRealType *green,
214 MagickRealType *blue)
cristyd8f16f72012-08-13 12:49:50 +0000215{
cristy7159f662012-10-28 17:32:43 +0000216 MagickRealType
cristyd8f16f72012-08-13 12:49:50 +0000217 b,
cristy7133e642012-08-14 11:04:11 +0000218 c,
cristyd8f16f72012-08-13 12:49:50 +0000219 g,
cristy7133e642012-08-14 11:04:11 +0000220 h,
221 m,
cristyd8f16f72012-08-13 12:49:50 +0000222 r,
cristyfbe79202013-02-12 18:08:53 +0000223 x;
cristyd8f16f72012-08-13 12:49:50 +0000224
225 /*
cristy7133e642012-08-14 11:04:11 +0000226 Convert HCL to RGB colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000227 */
cristy7159f662012-10-28 17:32:43 +0000228 assert(red != (MagickRealType *) NULL);
229 assert(green != (MagickRealType *) NULL);
230 assert(blue != (MagickRealType *) NULL);
cristy7133e642012-08-14 11:04:11 +0000231 h=6.0*hue;
232 c=chroma;
233 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
234 r=0.0;
235 g=0.0;
236 b=0.0;
237 if ((0.0 <= h) && (h < 1.0))
cristyd8f16f72012-08-13 12:49:50 +0000238 {
cristye1715282012-08-15 13:10:55 +0000239 r=c;
240 g=x;
cristyd8f16f72012-08-13 12:49:50 +0000241 }
cristyd8f16f72012-08-13 12:49:50 +0000242 else
cristy7133e642012-08-14 11:04:11 +0000243 if ((1.0 <= h) && (h < 2.0))
244 {
cristye1715282012-08-15 13:10:55 +0000245 r=x;
246 g=c;
cristy7133e642012-08-14 11:04:11 +0000247 }
248 else
249 if ((2.0 <= h) && (h < 3.0))
250 {
cristye1715282012-08-15 13:10:55 +0000251 g=c;
252 b=x;
cristy7133e642012-08-14 11:04:11 +0000253 }
254 else
255 if ((3.0 <= h) && (h < 4.0))
256 {
cristye1715282012-08-15 13:10:55 +0000257 g=x;
258 b=c;
cristy7133e642012-08-14 11:04:11 +0000259 }
260 else
261 if ((4.0 <= h) && (h < 5.0))
262 {
cristye1715282012-08-15 13:10:55 +0000263 r=x;
264 b=c;
cristy7133e642012-08-14 11:04:11 +0000265 }
266 else
267 if ((5.0 <= h) && (h < 6.0))
268 {
cristye1715282012-08-15 13:10:55 +0000269 r=c;
270 b=x;
cristy7133e642012-08-14 11:04:11 +0000271 }
cristya86a5cb2012-10-14 13:40:33 +0000272 m=luma-(0.298839f*r+0.586811f*g+0.114350f*b);
cristyfbe79202013-02-12 18:08:53 +0000273 *red=QuantumRange*(r+m);
274 *green=QuantumRange*(g+m);
275 *blue=QuantumRange*(b+m);
cristyd8f16f72012-08-13 12:49:50 +0000276}
277
cristy7159f662012-10-28 17:32:43 +0000278static void CompositeHCL(const MagickRealType red,const MagickRealType green,
279 const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
280 MagickRealType *luma)
cristyd8f16f72012-08-13 12:49:50 +0000281{
cristy7159f662012-10-28 17:32:43 +0000282 MagickRealType
cristyd8f16f72012-08-13 12:49:50 +0000283 b,
cristy7133e642012-08-14 11:04:11 +0000284 c,
cristyd8f16f72012-08-13 12:49:50 +0000285 g,
cristy7133e642012-08-14 11:04:11 +0000286 h,
cristyd8f16f72012-08-13 12:49:50 +0000287 max,
cristyd8f16f72012-08-13 12:49:50 +0000288 r;
289
290 /*
cristy7133e642012-08-14 11:04:11 +0000291 Convert RGB to HCL colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000292 */
cristy7159f662012-10-28 17:32:43 +0000293 assert(hue != (MagickRealType *) NULL);
294 assert(chroma != (MagickRealType *) NULL);
295 assert(luma != (MagickRealType *) NULL);
cristy7133e642012-08-14 11:04:11 +0000296 r=red;
297 g=green;
298 b=blue;
cristyd8f16f72012-08-13 12:49:50 +0000299 max=MagickMax(r,MagickMax(g,b));
cristy7159f662012-10-28 17:32:43 +0000300 c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
cristy7133e642012-08-14 11:04:11 +0000301 h=0.0;
302 if (c == 0)
303 h=0.0;
cristyd8f16f72012-08-13 12:49:50 +0000304 else
cristy7133e642012-08-14 11:04:11 +0000305 if (red == max)
cristyf2c5f4a2012-08-14 23:22:31 +0000306 h=fmod(6.0+(g-b)/c,6.0);
cristyd8f16f72012-08-13 12:49:50 +0000307 else
cristy7133e642012-08-14 11:04:11 +0000308 if (green == max)
309 h=((b-r)/c)+2.0;
310 else
311 if (blue == max)
312 h=((r-g)/c)+4.0;
313 *hue=(h/6.0);
314 *chroma=QuantumScale*c;
cristya86a5cb2012-10-14 13:40:33 +0000315 *luma=QuantumScale*(0.298839f*r+0.586811f*g+0.114350f*b);
cristyd8f16f72012-08-13 12:49:50 +0000316}
317
cristye4a40472011-12-22 02:56:19 +0000318static MagickBooleanType CompositeOverImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000319 const Image *composite_image,const MagickBooleanType clip_to_self,
320 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristye4a40472011-12-22 02:56:19 +0000321{
322#define CompositeImageTag "Composite/Image"
323
324 CacheView
325 *composite_view,
326 *image_view;
327
cristye4a40472011-12-22 02:56:19 +0000328 MagickBooleanType
cristye4a40472011-12-22 02:56:19 +0000329 status;
330
331 MagickOffsetType
332 progress;
333
334 ssize_t
335 y;
336
cristye4a40472011-12-22 02:56:19 +0000337 /*
cristye4a40472011-12-22 02:56:19 +0000338 Composite image.
339 */
340 status=MagickTrue;
341 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000342 composite_view=AcquireVirtualCacheView(composite_image,exception);
343 image_view=AcquireAuthenticCacheView(image,exception);
cristye4a40472011-12-22 02:56:19 +0000344#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000345 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000346 magick_threads(composite_image,image,image->rows,1)
cristye4a40472011-12-22 02:56:19 +0000347#endif
348 for (y=0; y < (ssize_t) image->rows; y++)
349 {
350 const Quantum
351 *pixels;
352
353 register const Quantum
354 *restrict p;
355
356 register Quantum
357 *restrict q;
358
359 register ssize_t
360 x;
361
cristy564a5692012-01-20 23:56:26 +0000362 size_t
363 channels;
364
cristye4a40472011-12-22 02:56:19 +0000365 if (status == MagickFalse)
366 continue;
cristyfeb3e962012-03-29 17:25:55 +0000367 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000368 {
369 if (y < y_offset)
370 continue;
371 if ((y-y_offset) >= (ssize_t) composite_image->rows)
372 continue;
373 }
374 /*
375 If pixels is NULL, y is outside overlay region.
376 */
377 pixels=(Quantum *) NULL;
378 p=(Quantum *) NULL;
379 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
380 {
381 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
382 composite_image->columns,1,exception);
383 if (p == (const Quantum *) NULL)
384 {
385 status=MagickFalse;
386 continue;
387 }
388 pixels=p;
389 if (x_offset < 0)
390 p-=x_offset*GetPixelChannels(composite_image);
391 }
392 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
393 if (q == (Quantum *) NULL)
394 {
395 status=MagickFalse;
396 continue;
397 }
398 for (x=0; x < (ssize_t) image->columns; x++)
399 {
cristy17028dc2013-01-24 12:28:39 +0000400 double
401 gamma;
402
cristy7159f662012-10-28 17:32:43 +0000403 MagickRealType
cristye4a40472011-12-22 02:56:19 +0000404 alpha,
405 Da,
406 Dc,
cristye4a40472011-12-22 02:56:19 +0000407 Sa,
408 Sc;
409
410 register ssize_t
411 i;
412
cristyfeb3e962012-03-29 17:25:55 +0000413 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000414 {
415 if (x < x_offset)
416 {
417 q+=GetPixelChannels(image);
418 continue;
419 }
420 if ((x-x_offset) >= (ssize_t) composite_image->columns)
421 break;
422 }
423 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
424 ((x-x_offset) >= (ssize_t) composite_image->columns))
425 {
426 Quantum
427 source[MaxPixelChannels];
428
429 /*
430 Virtual composite:
431 Sc: source color.
432 Dc: destination color.
433 */
cristy10a6c612012-01-29 21:41:05 +0000434 if (GetPixelMask(image,q) != 0)
435 {
436 q+=GetPixelChannels(image);
437 continue;
438 }
cristyc94ba6f2012-01-29 23:19:58 +0000439 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
440 source,exception);
cristye4a40472011-12-22 02:56:19 +0000441 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
442 {
443 PixelChannel
444 channel;
445
446 PixelTrait
447 composite_traits,
448 traits;
449
cristycf1296e2012-08-26 23:40:49 +0000450 channel=GetPixelChannelChannel(image,i);
451 traits=GetPixelChannelTraits(image,channel);
452 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000453 if ((traits == UndefinedPixelTrait) ||
454 (composite_traits == UndefinedPixelTrait))
455 continue;
456 q[i]=source[channel];
457 }
458 q+=GetPixelChannels(image);
459 continue;
460 }
461 /*
462 Authentic composite:
463 Sa: normalized source alpha.
464 Da: normalized destination alpha.
465 */
cristyc94ba6f2012-01-29 23:19:58 +0000466 if (GetPixelMask(composite_image,p) != 0)
467 {
468 p+=GetPixelChannels(composite_image);
469 channels=GetPixelChannels(composite_image);
470 if (p >= (pixels+channels*composite_image->columns))
471 p=pixels;
472 q+=GetPixelChannels(image);
473 continue;
474 }
cristye4a40472011-12-22 02:56:19 +0000475 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
476 Da=QuantumScale*GetPixelAlpha(image,q);
477 alpha=Sa*(-Da)+Sa+Da;
478 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
479 {
480 PixelChannel
481 channel;
482
483 PixelTrait
484 composite_traits,
485 traits;
486
cristycf1296e2012-08-26 23:40:49 +0000487 channel=GetPixelChannelChannel(image,i);
488 traits=GetPixelChannelTraits(image,channel);
489 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000490 if ((traits == UndefinedPixelTrait) ||
491 (composite_traits == UndefinedPixelTrait))
492 continue;
493 if ((traits & CopyPixelTrait) != 0)
494 {
495 if (channel != AlphaPixelChannel)
496 {
497 /*
498 Copy channel.
499 */
500 q[i]=GetPixelChannel(composite_image,channel,p);
501 continue;
502 }
503 /*
504 Set alpha channel.
505 */
506 q[i]=ClampToQuantum(QuantumRange*alpha);
507 continue;
508 }
509 /*
510 Sc: source color.
511 Dc: destination color.
512 */
cristy7159f662012-10-28 17:32:43 +0000513 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
514 Dc=(MagickRealType) q[i];
cristy3e3ec3a2012-11-03 23:11:06 +0000515 gamma=PerceptibleReciprocal(alpha);
cristye4a40472011-12-22 02:56:19 +0000516 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
517 }
518 p+=GetPixelChannels(composite_image);
519 channels=GetPixelChannels(composite_image);
520 if (p >= (pixels+channels*composite_image->columns))
521 p=pixels;
522 q+=GetPixelChannels(image);
523 }
524 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
525 status=MagickFalse;
526 if (image->progress_monitor != (MagickProgressMonitor) NULL)
527 {
528 MagickBooleanType
529 proceed;
530
531#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000532 #pragma omp critical (MagickCore_CompositeImage)
cristye4a40472011-12-22 02:56:19 +0000533#endif
534 proceed=SetImageProgress(image,CompositeImageTag,progress++,
535 image->rows);
536 if (proceed == MagickFalse)
537 status=MagickFalse;
538 }
539 }
540 composite_view=DestroyCacheView(composite_view);
541 image_view=DestroyCacheView(image_view);
542 return(status);
543}
544
cristy4c08aed2011-07-01 19:47:50 +0000545MagickExport MagickBooleanType CompositeImage(Image *image,
cristya865ccd2012-07-28 00:33:10 +0000546 const Image *composite,const CompositeOperator compose,
cristy44c98412012-03-31 18:25:40 +0000547 const MagickBooleanType clip_to_self,const ssize_t x_offset,
548 const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000549{
cristy4c08aed2011-07-01 19:47:50 +0000550#define CompositeImageTag "Composite/Image"
551
552 CacheView
553 *composite_view,
554 *image_view;
555
cristy4c08aed2011-07-01 19:47:50 +0000556 GeometryInfo
557 geometry_info;
558
559 Image
cristya865ccd2012-07-28 00:33:10 +0000560 *composite_image,
cristy4c08aed2011-07-01 19:47:50 +0000561 *destination_image;
562
563 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000564 status;
565
566 MagickOffsetType
567 progress;
568
cristy7159f662012-10-28 17:32:43 +0000569 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000570 amount,
571 destination_dissolve,
572 midpoint,
cristy7133e642012-08-14 11:04:11 +0000573 percent_luma,
574 percent_chroma,
cristy4c08aed2011-07-01 19:47:50 +0000575 source_dissolve,
576 threshold;
577
578 MagickStatusType
579 flags;
580
cristyd197cbb2012-01-13 02:14:12 +0000581 ssize_t
582 y;
583
cristy4c08aed2011-07-01 19:47:50 +0000584 assert(image != (Image *) NULL);
585 assert(image->signature == MagickSignature);
586 if (image->debug != MagickFalse)
587 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristya865ccd2012-07-28 00:33:10 +0000588 assert(composite!= (Image *) NULL);
589 assert(composite->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000590 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000591 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +0000592 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
593 if (composite_image == (const Image *) NULL)
594 return(MagickFalse);
cristyeb16d5e2012-09-12 20:11:53 +0000595 if (IsGrayColorspace(image->colorspace) != MagickFalse)
596 (void) SetImageColorspace(image,RGBColorspace,exception);
cristya4a22a22012-08-01 23:16:38 +0000597 (void) SetImageColorspace(composite_image,image->colorspace,exception);
cristy60dc90c2013-02-09 18:33:21 +0000598 if ((image->alpha_trait == BlendPixelTrait) &&
599 (composite_image->alpha_trait != BlendPixelTrait))
600 SetImageAlphaChannel(composite_image,SetAlphaChannel,exception);
cristye4a40472011-12-22 02:56:19 +0000601 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
602 {
cristyfeb3e962012-03-29 17:25:55 +0000603 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
604 y_offset,exception);
cristya865ccd2012-07-28 00:33:10 +0000605 composite_image=DestroyImage(composite_image);
cristye4a40472011-12-22 02:56:19 +0000606 return(status);
607 }
cristy4c08aed2011-07-01 19:47:50 +0000608 destination_image=(Image *) NULL;
609 amount=0.5;
610 destination_dissolve=1.0;
cristy7133e642012-08-14 11:04:11 +0000611 percent_luma=100.0;
612 percent_chroma=100.0;
cristy4c08aed2011-07-01 19:47:50 +0000613 source_dissolve=1.0;
614 threshold=0.05f;
615 switch (compose)
616 {
cristy4c08aed2011-07-01 19:47:50 +0000617 case CopyCompositeOp:
618 {
619 if ((x_offset < 0) || (y_offset < 0))
620 break;
621 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
622 break;
623 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
624 break;
625 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +0000626 composite_view=AcquireVirtualCacheView(composite_image,exception);
627 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000628#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000629 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +0000630 magick_threads(composite_image,image,composite_image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000631#endif
632 for (y=0; y < (ssize_t) composite_image->rows; y++)
633 {
634 MagickBooleanType
635 sync;
636
637 register const Quantum
638 *p;
639
640 register Quantum
641 *q;
642
643 register ssize_t
644 x;
645
646 if (status == MagickFalse)
647 continue;
648 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
649 1,exception);
650 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
651 composite_image->columns,1,exception);
652 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
653 {
654 status=MagickFalse;
655 continue;
656 }
657 for (x=0; x < (ssize_t) composite_image->columns; x++)
658 {
cristybdecccc2011-12-24 22:52:16 +0000659 register ssize_t
660 i;
661
cristy665e18f2012-05-17 12:39:54 +0000662 if (GetPixelMask(composite_image,p) != 0)
cristy10a6c612012-01-29 21:41:05 +0000663 {
664 p+=GetPixelChannels(composite_image);
665 q+=GetPixelChannels(image);
666 continue;
667 }
cristybdecccc2011-12-24 22:52:16 +0000668 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
669 {
670 PixelChannel
671 channel;
672
673 PixelTrait
674 composite_traits,
675 traits;
676
cristycf1296e2012-08-26 23:40:49 +0000677 channel=GetPixelChannelChannel(composite_image,i);
678 composite_traits=GetPixelChannelTraits(composite_image,channel);
679 traits=GetPixelChannelTraits(image,channel);
cristybdecccc2011-12-24 22:52:16 +0000680 if ((traits == UndefinedPixelTrait) ||
681 (composite_traits == UndefinedPixelTrait))
682 continue;
683 SetPixelChannel(image,channel,p[i],q);
684 }
cristyed231572011-07-14 02:18:59 +0000685 p+=GetPixelChannels(composite_image);
686 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000687 }
688 sync=SyncCacheViewAuthenticPixels(image_view,exception);
689 if (sync == MagickFalse)
690 status=MagickFalse;
691 if (image->progress_monitor != (MagickProgressMonitor) NULL)
692 {
693 MagickBooleanType
694 proceed;
695
696#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000697 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000698#endif
699 proceed=SetImageProgress(image,CompositeImageTag,
700 (MagickOffsetType) y,image->rows);
701 if (proceed == MagickFalse)
702 status=MagickFalse;
703 }
704 }
705 composite_view=DestroyCacheView(composite_view);
706 image_view=DestroyCacheView(image_view);
cristy44886b92012-07-28 13:07:09 +0000707 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000708 return(status);
709 }
cristye4a40472011-12-22 02:56:19 +0000710 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000711 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000712 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000713 {
714 /*
715 Modify destination outside the overlaid region and require an alpha
716 channel to exist, to add transparency.
717 */
cristy8a46d822012-08-28 23:32:39 +0000718 if (image->alpha_trait != BlendPixelTrait)
cristy42c41de2012-05-05 18:36:31 +0000719 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000720 break;
721 }
722 case BlurCompositeOp:
723 {
724 CacheView
725 *composite_view,
726 *destination_view;
727
cristyfeb3e962012-03-29 17:25:55 +0000728 const char
729 *value;
730
cristy7159f662012-10-28 17:32:43 +0000731 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000732 angle_range,
733 angle_start,
734 height,
735 width;
736
cristybce4f4a2012-10-14 14:57:47 +0000737 PixelInfo
738 pixel;
739
cristy4c08aed2011-07-01 19:47:50 +0000740 ResampleFilter
741 *resample_filter;
742
743 SegmentInfo
744 blur;
745
746 /*
anthony9cb63cc2012-04-25 06:10:49 +0000747 Blur Image by resampling.
748
cristy4c08aed2011-07-01 19:47:50 +0000749 Blur Image dictated by an overlay gradient map: X = red_channel;
750 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
751 */
752 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000753 exception);
cristy4c08aed2011-07-01 19:47:50 +0000754 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000755 {
756 composite_image=DestroyImage(composite_image);
757 return(MagickFalse);
758 }
cristy4c08aed2011-07-01 19:47:50 +0000759 /*
anthony9cb63cc2012-04-25 06:10:49 +0000760 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000761 */
762 SetGeometryInfo(&geometry_info);
763 flags=NoValue;
cristy1a780952013-02-10 17:15:30 +0000764 value=GetImageArtifact(image,"compose:args");
765 if (value != (const char *) NULL)
cristy4c08aed2011-07-01 19:47:50 +0000766 flags=ParseGeometry(value,&geometry_info);
cristy1a780952013-02-10 17:15:30 +0000767 if ((flags & WidthValue) == 0)
768 {
769 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
770 "InvalidSetting","'%s' '%s'","compose:args",value);
cristy44886b92012-07-28 13:07:09 +0000771 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000772 destination_image=DestroyImage(destination_image);
773 return(MagickFalse);
774 }
anthony9cb63cc2012-04-25 06:10:49 +0000775 /*
776 Users input sigma now needs to be converted to the EWA ellipse size.
777 The filter defaults to a sigma of 0.5 so to make this match the
778 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000779 */
780 width=height=geometry_info.rho*2.0;
781 if ((flags & HeightValue) != 0 )
782 height=geometry_info.sigma*2.0;
cristy7159f662012-10-28 17:32:43 +0000783 /*
784 Default the unrotated ellipse width and height axis vectors.
785 */
anthonyd2923912012-04-23 13:06:53 +0000786 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000787 blur.x2=0.0;
788 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000789 blur.y2=height;
790 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000791 if ((flags & XValue) != 0 )
792 {
cristy7159f662012-10-28 17:32:43 +0000793 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000794 angle;
795
796 angle=DegreesToRadians(geometry_info.xi);
797 blur.x1=width*cos(angle);
798 blur.x2=width*sin(angle);
799 blur.y1=(-height*sin(angle));
800 blur.y2=height*cos(angle);
801 }
anthonyd2923912012-04-23 13:06:53 +0000802 /* Otherwise lets set a angle range and calculate in the loop */
803 angle_start=0.0;
804 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000805 if ((flags & YValue) != 0 )
806 {
807 angle_start=DegreesToRadians(geometry_info.xi);
808 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
809 }
810 /*
anthony9cb63cc2012-04-25 06:10:49 +0000811 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000812
anthony9cb63cc2012-04-25 06:10:49 +0000813 As the minimum ellipse radius of support*1.0 the EWA algorithm
814 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
815 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000816
anthony9cb63cc2012-04-25 06:10:49 +0000817 The solution (as well as the problem of preventing any user
818 expert filter settings, is to set our own user settings, then
819 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000820 */
cristy8a11cb12011-10-19 23:53:34 +0000821 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000822 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000823
824 /* do the variable blurring of each pixel in image */
825 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +0000826 composite_view=AcquireVirtualCacheView(composite_image,exception);
827 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000828 for (y=0; y < (ssize_t) composite_image->rows; y++)
829 {
830 MagickBooleanType
831 sync;
832
833 register const Quantum
834 *restrict p;
835
836 register Quantum
837 *restrict q;
838
839 register ssize_t
840 x;
841
842 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
843 continue;
844 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
845 1,exception);
846 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000847 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000848 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
849 break;
850 for (x=0; x < (ssize_t) composite_image->columns; x++)
851 {
852 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
853 {
cristyed231572011-07-14 02:18:59 +0000854 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000855 continue;
856 }
857 if (fabs(angle_range) > MagickEpsilon)
858 {
cristy7159f662012-10-28 17:32:43 +0000859 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000860 angle;
861
862 angle=angle_start+angle_range*QuantumScale*
863 GetPixelBlue(composite_image,p);
864 blur.x1=width*cos(angle);
865 blur.x2=width*sin(angle);
866 blur.y1=(-height*sin(angle));
867 blur.y2=height*cos(angle);
868 }
anthonyd2923912012-04-23 13:06:53 +0000869#if 0
870 if ( x == 10 && y == 60 ) {
cristy7159f662012-10-28 17:32:43 +0000871 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
872 blur.x2,blur.y1, blur.y2);
873 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
874 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
anthonyd2923912012-04-23 13:06:53 +0000875#endif
876 ScaleResampleFilter(resample_filter,
877 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
878 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
879 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
880 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000881 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000882 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000883 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000884 p+=GetPixelChannels(composite_image);
885 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000886 }
887 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
888 if (sync == MagickFalse)
889 break;
890 }
891 resample_filter=DestroyResampleFilter(resample_filter);
892 composite_view=DestroyCacheView(composite_view);
893 destination_view=DestroyCacheView(destination_view);
cristyf661c4d2012-07-28 12:57:17 +0000894 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000895 composite_image=destination_image;
896 break;
897 }
898 case DisplaceCompositeOp:
899 case DistortCompositeOp:
900 {
901 CacheView
902 *composite_view,
903 *destination_view,
904 *image_view;
905
cristyfeb3e962012-03-29 17:25:55 +0000906 const char
907 *value;
908
cristy4c08aed2011-07-01 19:47:50 +0000909 PixelInfo
910 pixel;
911
cristy7159f662012-10-28 17:32:43 +0000912 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000913 horizontal_scale,
914 vertical_scale;
915
916 PointInfo
917 center,
918 offset;
919
920 /*
921 Displace/Distort based on overlay gradient map:
922 X = red_channel; Y = green_channel;
923 compose:args = x_scale[,y_scale[,center.x,center.y]]
924 */
925 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000926 exception);
cristy4c08aed2011-07-01 19:47:50 +0000927 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000928 {
929 composite_image=DestroyImage(composite_image);
930 return(MagickFalse);
931 }
cristy4c08aed2011-07-01 19:47:50 +0000932 SetGeometryInfo(&geometry_info);
933 flags=NoValue;
934 value=GetImageArtifact(composite_image,"compose:args");
935 if (value != (char *) NULL)
936 flags=ParseGeometry(value,&geometry_info);
937 if ((flags & (WidthValue|HeightValue)) == 0 )
938 {
939 if ((flags & AspectValue) == 0)
940 {
cristy7159f662012-10-28 17:32:43 +0000941 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
cristy4c08aed2011-07-01 19:47:50 +0000942 2.0;
cristy7159f662012-10-28 17:32:43 +0000943 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000944 }
945 else
946 {
cristy7159f662012-10-28 17:32:43 +0000947 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
948 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000949 }
950 }
951 else
952 {
953 horizontal_scale=geometry_info.rho;
954 vertical_scale=geometry_info.sigma;
955 if ((flags & PercentValue) != 0)
956 {
957 if ((flags & AspectValue) == 0)
958 {
959 horizontal_scale*=(composite_image->columns-1.0)/200.0;
960 vertical_scale*=(composite_image->rows-1.0)/200.0;
961 }
962 else
963 {
964 horizontal_scale*=(image->columns-1.0)/200.0;
965 vertical_scale*=(image->rows-1.0)/200.0;
966 }
967 }
968 if ((flags & HeightValue) == 0)
969 vertical_scale=horizontal_scale;
970 }
971 /*
972 Determine fixed center point for absolute distortion map
973 Absolute distort ==
974 Displace offset relative to a fixed absolute point
975 Select that point according to +X+Y user inputs.
976 default = center of overlay image
977 arg flag '!' = locations/percentage relative to background image
978 */
cristy7159f662012-10-28 17:32:43 +0000979 center.x=(MagickRealType) x_offset;
980 center.y=(MagickRealType) y_offset;
cristy4c08aed2011-07-01 19:47:50 +0000981 if (compose == DistortCompositeOp)
982 {
983 if ((flags & XValue) == 0)
984 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000985 center.x=(MagickRealType) (x_offset+(composite_image->columns-1)/
986 2.0);
cristy4c08aed2011-07-01 19:47:50 +0000987 else
cristy7159f662012-10-28 17:32:43 +0000988 center.x=(MagickRealType) ((image->columns-1)/2);
cristy4c08aed2011-07-01 19:47:50 +0000989 else
990 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000991 center.x=(MagickRealType) x_offset+geometry_info.xi;
cristy4c08aed2011-07-01 19:47:50 +0000992 else
993 center.x=geometry_info.xi;
994 if ((flags & YValue) == 0)
995 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000996 center.y=(MagickRealType) (y_offset+(composite_image->rows-1)/
997 2.0);
cristy4c08aed2011-07-01 19:47:50 +0000998 else
cristy7159f662012-10-28 17:32:43 +0000999 center.y=(MagickRealType) ((image->rows-1)/2);
cristy4c08aed2011-07-01 19:47:50 +00001000 else
1001 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001002 center.y=(MagickRealType) y_offset+geometry_info.psi;
cristy4c08aed2011-07-01 19:47:50 +00001003 else
1004 center.y=geometry_info.psi;
1005 }
1006 /*
1007 Shift the pixel offset point as defined by the provided,
1008 displacement/distortion map. -- Like a lens...
1009 */
cristye10859a2011-12-18 22:28:59 +00001010 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +00001011 image_view=AcquireVirtualCacheView(image,exception);
1012 composite_view=AcquireVirtualCacheView(composite_image,exception);
1013 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001014 for (y=0; y < (ssize_t) composite_image->rows; y++)
1015 {
1016 MagickBooleanType
1017 sync;
1018
1019 register const Quantum
1020 *restrict p;
1021
1022 register Quantum
1023 *restrict q;
1024
1025 register ssize_t
1026 x;
1027
1028 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1029 continue;
1030 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1031 1,exception);
1032 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001033 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001034 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1035 break;
1036 for (x=0; x < (ssize_t) composite_image->columns; x++)
1037 {
1038 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1039 {
cristyed231572011-07-14 02:18:59 +00001040 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001041 continue;
1042 }
1043 /*
1044 Displace the offset.
1045 */
cristy7159f662012-10-28 17:32:43 +00001046 offset.x=(double) (horizontal_scale*(GetPixelRed(composite_image,p)-
1047 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1048 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1049 x : 0);
1050 offset.y=(double) (vertical_scale*(GetPixelGreen(composite_image,p)-
1051 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1052 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1053 y : 0);
cristy4c08aed2011-07-01 19:47:50 +00001054 (void) InterpolatePixelInfo(image,image_view,
1055 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1056 &pixel,exception);
1057 /*
1058 Mask with the 'invalid pixel mask' in alpha channel.
1059 */
cristy7159f662012-10-28 17:32:43 +00001060 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1061 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001062 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001063 p+=GetPixelChannels(composite_image);
1064 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001065 }
1066 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1067 if (sync == MagickFalse)
1068 break;
1069 }
1070 destination_view=DestroyCacheView(destination_view);
1071 composite_view=DestroyCacheView(composite_view);
1072 image_view=DestroyCacheView(image_view);
cristyf661c4d2012-07-28 12:57:17 +00001073 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001074 composite_image=destination_image;
1075 break;
1076 }
1077 case DissolveCompositeOp:
1078 {
cristyfeb3e962012-03-29 17:25:55 +00001079 const char
1080 *value;
1081
cristy4c08aed2011-07-01 19:47:50 +00001082 /*
1083 Geometry arguments to dissolve factors.
1084 */
1085 value=GetImageArtifact(composite_image,"compose:args");
1086 if (value != (char *) NULL)
1087 {
1088 flags=ParseGeometry(value,&geometry_info);
1089 source_dissolve=geometry_info.rho/100.0;
1090 destination_dissolve=1.0;
1091 if ((source_dissolve-MagickEpsilon) < 0.0)
1092 source_dissolve=0.0;
1093 if ((source_dissolve+MagickEpsilon) > 1.0)
1094 {
1095 destination_dissolve=2.0-source_dissolve;
1096 source_dissolve=1.0;
1097 }
1098 if ((flags & SigmaValue) != 0)
1099 destination_dissolve=geometry_info.sigma/100.0;
1100 if ((destination_dissolve-MagickEpsilon) < 0.0)
1101 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001102 /* posible speed up? -- from IMv6 update
1103 clip_to_self=MagickFalse;
1104 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1105 {
1106 destination_dissolve=1.0;
1107 clip_to_self=MagickTrue;
1108 }
1109 */
cristy4c08aed2011-07-01 19:47:50 +00001110 }
1111 break;
1112 }
1113 case BlendCompositeOp:
1114 {
cristyfeb3e962012-03-29 17:25:55 +00001115 const char
1116 *value;
1117
cristy4c08aed2011-07-01 19:47:50 +00001118 value=GetImageArtifact(composite_image,"compose:args");
1119 if (value != (char *) NULL)
1120 {
1121 flags=ParseGeometry(value,&geometry_info);
1122 source_dissolve=geometry_info.rho/100.0;
1123 destination_dissolve=1.0-source_dissolve;
1124 if ((flags & SigmaValue) != 0)
1125 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001126 }
1127 break;
1128 }
1129 case MathematicsCompositeOp:
1130 {
cristyfeb3e962012-03-29 17:25:55 +00001131 const char
1132 *value;
1133
cristy4c08aed2011-07-01 19:47:50 +00001134 /*
1135 Just collect the values from "compose:args", setting.
1136 Unused values are set to zero automagically.
1137
1138 Arguments are normally a comma separated list, so this probably should
1139 be changed to some 'general comma list' parser, (with a minimum
1140 number of values)
1141 */
1142 SetGeometryInfo(&geometry_info);
1143 value=GetImageArtifact(composite_image,"compose:args");
1144 if (value != (char *) NULL)
1145 (void) ParseGeometry(value,&geometry_info);
1146 break;
1147 }
1148 case ModulateCompositeOp:
1149 {
cristyfeb3e962012-03-29 17:25:55 +00001150 const char
1151 *value;
1152
cristy4c08aed2011-07-01 19:47:50 +00001153 /*
cristy7133e642012-08-14 11:04:11 +00001154 Determine the luma and chroma scale.
cristy4c08aed2011-07-01 19:47:50 +00001155 */
1156 value=GetImageArtifact(composite_image,"compose:args");
1157 if (value != (char *) NULL)
1158 {
1159 flags=ParseGeometry(value,&geometry_info);
cristy7133e642012-08-14 11:04:11 +00001160 percent_luma=geometry_info.rho;
cristy4c08aed2011-07-01 19:47:50 +00001161 if ((flags & SigmaValue) != 0)
cristy7133e642012-08-14 11:04:11 +00001162 percent_chroma=geometry_info.sigma;
cristy4c08aed2011-07-01 19:47:50 +00001163 }
1164 break;
1165 }
1166 case ThresholdCompositeOp:
1167 {
cristyfeb3e962012-03-29 17:25:55 +00001168 const char
1169 *value;
1170
cristy4c08aed2011-07-01 19:47:50 +00001171 /*
1172 Determine the amount and threshold.
1173 */
1174 value=GetImageArtifact(composite_image,"compose:args");
1175 if (value != (char *) NULL)
1176 {
1177 flags=ParseGeometry(value,&geometry_info);
1178 amount=geometry_info.rho;
1179 threshold=geometry_info.sigma;
1180 if ((flags & SigmaValue) == 0)
1181 threshold=0.05f;
1182 }
1183 threshold*=QuantumRange;
1184 break;
1185 }
1186 default:
1187 break;
1188 }
cristy4c08aed2011-07-01 19:47:50 +00001189 /*
1190 Composite image.
1191 */
1192 status=MagickTrue;
1193 progress=0;
cristy7159f662012-10-28 17:32:43 +00001194 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy46ff2672012-12-14 15:32:26 +00001195 composite_view=AcquireVirtualCacheView(composite_image,exception);
1196 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001197#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001198 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001199 magick_threads(composite_image,image,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001200#endif
1201 for (y=0; y < (ssize_t) image->rows; y++)
1202 {
1203 const Quantum
1204 *pixels;
1205
cristy7159f662012-10-28 17:32:43 +00001206 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001207 blue,
cristy7133e642012-08-14 11:04:11 +00001208 luma,
cristye4a40472011-12-22 02:56:19 +00001209 green,
cristy4c08aed2011-07-01 19:47:50 +00001210 hue,
cristye4a40472011-12-22 02:56:19 +00001211 red,
cristy7133e642012-08-14 11:04:11 +00001212 chroma;
cristy4c08aed2011-07-01 19:47:50 +00001213
cristyddeeea22012-04-12 01:33:09 +00001214 PixelInfo
1215 destination_pixel,
1216 source_pixel;
1217
cristy4c08aed2011-07-01 19:47:50 +00001218 register const Quantum
1219 *restrict p;
1220
1221 register Quantum
1222 *restrict q;
1223
1224 register ssize_t
1225 x;
1226
1227 if (status == MagickFalse)
1228 continue;
cristyfeb3e962012-03-29 17:25:55 +00001229 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001230 {
1231 if (y < y_offset)
1232 continue;
1233 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1234 continue;
1235 }
1236 /*
1237 If pixels is NULL, y is outside overlay region.
1238 */
1239 pixels=(Quantum *) NULL;
1240 p=(Quantum *) NULL;
1241 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1242 {
1243 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1244 composite_image->columns,1,exception);
1245 if (p == (const Quantum *) NULL)
1246 {
1247 status=MagickFalse;
1248 continue;
1249 }
1250 pixels=p;
1251 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001252 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001253 }
1254 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001255 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001256 {
1257 status=MagickFalse;
1258 continue;
1259 }
cristy4c08aed2011-07-01 19:47:50 +00001260 hue=0.0;
cristy7133e642012-08-14 11:04:11 +00001261 chroma=0.0;
1262 luma=0.0;
cristyddeeea22012-04-12 01:33:09 +00001263 GetPixelInfo(image,&destination_pixel);
1264 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001265 for (x=0; x < (ssize_t) image->columns; x++)
1266 {
cristy17028dc2013-01-24 12:28:39 +00001267 double
1268 gamma;
1269
cristy7159f662012-10-28 17:32:43 +00001270 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001271 alpha,
1272 Da,
1273 Dc,
1274 Dca,
cristye4a40472011-12-22 02:56:19 +00001275 Sa,
1276 Sc,
1277 Sca;
1278
1279 register ssize_t
1280 i;
1281
cristy564a5692012-01-20 23:56:26 +00001282 size_t
1283 channels;
1284
cristyfeb3e962012-03-29 17:25:55 +00001285 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001286 {
1287 if (x < x_offset)
1288 {
cristyed231572011-07-14 02:18:59 +00001289 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001290 continue;
1291 }
1292 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1293 break;
1294 }
cristye4a40472011-12-22 02:56:19 +00001295 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1296 ((x-x_offset) >= (ssize_t) composite_image->columns))
1297 {
1298 Quantum
1299 source[MaxPixelChannels];
1300
1301 /*
1302 Virtual composite:
1303 Sc: source color.
1304 Dc: destination color.
1305 */
1306 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1307 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001308 if (GetPixelMask(image,q) != 0)
1309 {
1310 q+=GetPixelChannels(image);
1311 continue;
1312 }
cristye4a40472011-12-22 02:56:19 +00001313 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1314 {
cristy7159f662012-10-28 17:32:43 +00001315 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001316 pixel;
1317
1318 PixelChannel
1319 channel;
1320
1321 PixelTrait
1322 composite_traits,
1323 traits;
1324
cristycf1296e2012-08-26 23:40:49 +00001325 channel=GetPixelChannelChannel(image,i);
1326 traits=GetPixelChannelTraits(image,channel);
1327 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001328 if ((traits == UndefinedPixelTrait) ||
1329 (composite_traits == UndefinedPixelTrait))
1330 continue;
1331 switch (compose)
1332 {
cristyc8d63672012-01-11 13:03:13 +00001333 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001334 case ChangeMaskCompositeOp:
1335 case CopyAlphaCompositeOp:
1336 case DstAtopCompositeOp:
1337 case DstInCompositeOp:
1338 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001339 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001340 case OutCompositeOp:
1341 case SrcInCompositeOp:
1342 case SrcOutCompositeOp:
1343 {
cristy7159f662012-10-28 17:32:43 +00001344 pixel=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001345 if (channel == AlphaPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001346 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001347 break;
1348 }
1349 case ClearCompositeOp:
1350 case CopyCompositeOp:
1351 case ReplaceCompositeOp:
1352 case SrcCompositeOp:
1353 {
1354 if (channel == AlphaPixelChannel)
1355 {
cristy7159f662012-10-28 17:32:43 +00001356 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001357 break;
1358 }
1359 pixel=0.0;
1360 break;
1361 }
cristy99abff32011-12-24 20:45:16 +00001362 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001363 case DissolveCompositeOp:
1364 {
1365 if (channel == AlphaPixelChannel)
1366 {
1367 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1368 source);
1369 break;
1370 }
cristy7159f662012-10-28 17:32:43 +00001371 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001372 break;
1373 }
1374 default:
1375 {
cristy7159f662012-10-28 17:32:43 +00001376 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001377 break;
1378 }
1379 }
1380 q[i]=ClampToQuantum(pixel);
1381 }
1382 q+=GetPixelChannels(image);
1383 continue;
1384 }
1385 /*
1386 Authentic composite:
1387 Sa: normalized source alpha.
1388 Da: normalized destination alpha.
1389 */
1390 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1391 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001392 switch (compose)
1393 {
cristye4a40472011-12-22 02:56:19 +00001394 case BumpmapCompositeOp:
1395 {
1396 alpha=GetPixelIntensity(composite_image,p)*Sa;
1397 break;
1398 }
cristycdc168f2011-12-21 15:24:39 +00001399 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001400 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001401 case DifferenceCompositeOp:
1402 case DivideDstCompositeOp:
1403 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001404 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001405 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001406 case LinearBurnCompositeOp:
1407 case LinearDodgeCompositeOp:
1408 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001409 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001410 case MinusDstCompositeOp:
1411 case MinusSrcCompositeOp:
1412 case ModulusAddCompositeOp:
1413 case ModulusSubtractCompositeOp:
1414 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001415 case OverlayCompositeOp:
1416 case PegtopLightCompositeOp:
1417 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001418 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001419 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001420 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001421 {
1422 alpha=RoundToUnity(Sa+Da-Sa*Da);
1423 break;
1424 }
1425 case DarkenCompositeOp:
1426 case DstAtopCompositeOp:
1427 case DstInCompositeOp:
1428 case InCompositeOp:
1429 case LightenCompositeOp:
1430 case SrcInCompositeOp:
1431 {
1432 alpha=Sa*Da;
1433 break;
1434 }
1435 case DissolveCompositeOp:
1436 {
1437 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1438 Sa+destination_dissolve*Da;
1439 break;
1440 }
1441 case DstOverCompositeOp:
1442 {
1443 alpha=Da*(-Sa)+Da+Sa;
1444 break;
1445 }
1446 case DstOutCompositeOp:
1447 {
1448 alpha=Da*(1.0-Sa);
1449 break;
1450 }
1451 case OutCompositeOp:
1452 case SrcOutCompositeOp:
1453 {
1454 alpha=Sa*(1.0-Da);
1455 break;
1456 }
1457 case OverCompositeOp:
1458 case SrcOverCompositeOp:
1459 {
1460 alpha=Sa*(-Da)+Sa+Da;
1461 break;
1462 }
cristy99abff32011-12-24 20:45:16 +00001463 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001464 case PlusCompositeOp:
1465 {
1466 alpha=RoundToUnity(Sa+Da);
1467 break;
1468 }
cristy4c08aed2011-07-01 19:47:50 +00001469 case XorCompositeOp:
1470 {
cristye4a40472011-12-22 02:56:19 +00001471 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001472 break;
1473 }
1474 default:
cristye4a40472011-12-22 02:56:19 +00001475 {
1476 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001477 break;
cristye4a40472011-12-22 02:56:19 +00001478 }
cristy4c08aed2011-07-01 19:47:50 +00001479 }
cristyd03b8fa2013-02-10 18:59:28 +00001480 if (GetPixelMask(image,q) != 0)
cristy10a6c612012-01-29 21:41:05 +00001481 {
1482 p+=GetPixelChannels(composite_image);
1483 q+=GetPixelChannels(image);
1484 continue;
1485 }
cristy9d3d2792012-04-14 15:15:19 +00001486 switch (compose)
1487 {
1488 case ColorizeCompositeOp:
1489 case HueCompositeOp:
1490 case LuminizeCompositeOp:
1491 case ModulateCompositeOp:
1492 case SaturateCompositeOp:
1493 {
1494 GetPixelInfoPixel(composite_image,p,&source_pixel);
1495 GetPixelInfoPixel(image,q,&destination_pixel);
1496 break;
1497 }
1498 default:
1499 break;
1500 }
cristye4a40472011-12-22 02:56:19 +00001501 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1502 {
cristy7159f662012-10-28 17:32:43 +00001503 MagickRealType
1504 pixel,
cristy564a5692012-01-20 23:56:26 +00001505 sans;
1506
cristye4a40472011-12-22 02:56:19 +00001507 PixelChannel
1508 channel;
cristye10859a2011-12-18 22:28:59 +00001509
cristye4a40472011-12-22 02:56:19 +00001510 PixelTrait
1511 composite_traits,
1512 traits;
1513
cristycf1296e2012-08-26 23:40:49 +00001514 channel=GetPixelChannelChannel(image,i);
1515 traits=GetPixelChannelTraits(image,channel);
1516 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001517 if (traits == UndefinedPixelTrait)
1518 continue;
cristya7b07912012-01-11 20:01:32 +00001519 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001520 (composite_traits == UndefinedPixelTrait))
1521 continue;
1522 /*
1523 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001524 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001525 */
cristy7159f662012-10-28 17:32:43 +00001526 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
1527 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001528 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001529 {
cristye4a40472011-12-22 02:56:19 +00001530 if (channel != AlphaPixelChannel)
1531 {
1532 /*
1533 Copy channel.
1534 */
1535 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001536 continue;
cristye10859a2011-12-18 22:28:59 +00001537 }
cristye4a40472011-12-22 02:56:19 +00001538 /*
1539 Set alpha channel.
1540 */
cristye10859a2011-12-18 22:28:59 +00001541 switch (compose)
1542 {
cristyc8d63672012-01-11 13:03:13 +00001543 case AlphaCompositeOp:
1544 {
cristya7b07912012-01-11 20:01:32 +00001545 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001546 break;
1547 }
cristye4a40472011-12-22 02:56:19 +00001548 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001549 case CopyBlackCompositeOp:
1550 case CopyBlueCompositeOp:
1551 case CopyCyanCompositeOp:
1552 case CopyGreenCompositeOp:
1553 case CopyMagentaCompositeOp:
1554 case CopyRedCompositeOp:
1555 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001556 case SrcAtopCompositeOp:
1557 case DstCompositeOp:
1558 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001559 {
cristye4a40472011-12-22 02:56:19 +00001560 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001561 break;
1562 }
cristye10859a2011-12-18 22:28:59 +00001563 case ChangeMaskCompositeOp:
1564 {
cristye4a40472011-12-22 02:56:19 +00001565 MagickBooleanType
1566 equivalent;
1567
cristy7159f662012-10-28 17:32:43 +00001568 if (Da > ((MagickRealType) QuantumRange/2.0))
cristy99abff32011-12-24 20:45:16 +00001569 {
cristy7159f662012-10-28 17:32:43 +00001570 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001571 break;
1572 }
cristye4a40472011-12-22 02:56:19 +00001573 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001574 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001575 {
cristy7159f662012-10-28 17:32:43 +00001576 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001577 break;
1578 }
cristy7159f662012-10-28 17:32:43 +00001579 pixel=(MagickRealType) OpaqueAlpha;
cristye4a40472011-12-22 02:56:19 +00001580 break;
1581 }
cristy99abff32011-12-24 20:45:16 +00001582 case ClearCompositeOp:
1583 {
cristy7159f662012-10-28 17:32:43 +00001584 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001585 break;
1586 }
1587 case ColorizeCompositeOp:
1588 case HueCompositeOp:
1589 case LuminizeCompositeOp:
1590 case SaturateCompositeOp:
1591 {
1592 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1593 {
1594 pixel=QuantumRange*Da;
1595 break;
1596 }
1597 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1598 {
1599 pixel=QuantumRange*Sa;
1600 break;
1601 }
1602 if (Sa < Da)
1603 {
1604 pixel=QuantumRange*Da;
1605 break;
1606 }
1607 pixel=QuantumRange*Sa;
1608 break;
1609 }
cristy99abff32011-12-24 20:45:16 +00001610 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001611 {
1612 pixel=QuantumRange*Sa;
cristy8a46d822012-08-28 23:32:39 +00001613 if (composite_image->alpha_trait != BlendPixelTrait)
cristy24d5d722012-05-17 12:27:27 +00001614 pixel=GetPixelIntensity(composite_image,p);
1615 break;
1616 }
1617 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001618 case DisplaceCompositeOp:
1619 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001620 case DstAtopCompositeOp:
1621 case ReplaceCompositeOp:
1622 case SrcCompositeOp:
1623 {
1624 pixel=QuantumRange*Sa;
1625 break;
1626 }
1627 case DarkenIntensityCompositeOp:
1628 {
cristy99abff32011-12-24 20:45:16 +00001629 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1630 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001631 break;
1632 }
cristy98621462011-12-31 22:31:11 +00001633 case IntensityCompositeOp:
1634 {
cristyf13c5942012-08-08 23:50:11 +00001635 pixel=GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001636 break;
1637 }
cristye4a40472011-12-22 02:56:19 +00001638 case LightenIntensityCompositeOp:
1639 {
1640 pixel=Sa*GetPixelIntensity(composite_image,p) >
1641 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001642 break;
1643 }
cristy99abff32011-12-24 20:45:16 +00001644 case ModulateCompositeOp:
1645 {
1646 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1647 {
1648 pixel=QuantumRange*Da;
1649 break;
1650 }
1651 pixel=QuantumRange*Da;
1652 break;
1653 }
cristye10859a2011-12-18 22:28:59 +00001654 default:
1655 {
cristye4a40472011-12-22 02:56:19 +00001656 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001657 break;
1658 }
1659 }
cristye4a40472011-12-22 02:56:19 +00001660 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001661 continue;
1662 }
1663 /*
cristy99abff32011-12-24 20:45:16 +00001664 Porter-Duff compositions:
1665 Sca: source normalized color multiplied by alpha.
1666 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001667 */
cristy99abff32011-12-24 20:45:16 +00001668 Sca=QuantumScale*Sa*Sc;
1669 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001670 switch (compose)
1671 {
cristye10859a2011-12-18 22:28:59 +00001672 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001673 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001674 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001675 {
cristy99abff32011-12-24 20:45:16 +00001676 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001677 break;
1678 }
1679 default:
1680 break;
1681 }
cristy3e3ec3a2012-11-03 23:11:06 +00001682 gamma=PerceptibleReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001683 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001684 switch (compose)
1685 {
cristya7b07912012-01-11 20:01:32 +00001686 case AlphaCompositeOp:
1687 {
1688 pixel=QuantumRange*Sa;
1689 break;
1690 }
cristye4a40472011-12-22 02:56:19 +00001691 case AtopCompositeOp:
1692 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001693 {
cristye4a40472011-12-22 02:56:19 +00001694 pixel=Sc*Sa+Dc*(1.0-Sa);
1695 break;
cristye10859a2011-12-18 22:28:59 +00001696 }
cristye4a40472011-12-22 02:56:19 +00001697 case BlendCompositeOp:
1698 {
1699 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1700 break;
1701 }
1702 case BlurCompositeOp:
1703 case DisplaceCompositeOp:
1704 case DistortCompositeOp:
1705 case CopyCompositeOp:
1706 case ReplaceCompositeOp:
1707 case SrcCompositeOp:
1708 {
1709 pixel=Sc;
1710 break;
1711 }
1712 case BumpmapCompositeOp:
1713 {
cristy99abff32011-12-24 20:45:16 +00001714 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001715 {
1716 pixel=Dc;
1717 break;
1718 }
1719 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1720 break;
1721 }
cristy99abff32011-12-24 20:45:16 +00001722 case ChangeMaskCompositeOp:
1723 {
1724 pixel=Dc;
1725 break;
1726 }
1727 case ClearCompositeOp:
1728 {
1729 pixel=0.0;
1730 break;
1731 }
cristye4a40472011-12-22 02:56:19 +00001732 case ColorBurnCompositeOp:
1733 {
1734 /*
1735 Refer to the March 2009 SVG specification.
1736 */
1737 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1738 {
cristy99abff32011-12-24 20:45:16 +00001739 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001740 break;
1741 }
1742 if (Sca < MagickEpsilon)
1743 {
cristy99abff32011-12-24 20:45:16 +00001744 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001745 break;
1746 }
cristy99abff32011-12-24 20:45:16 +00001747 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1748 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001749 break;
1750 }
1751 case ColorDodgeCompositeOp:
1752 {
1753 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1754 {
cristy99abff32011-12-24 20:45:16 +00001755 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001756 break;
1757 }
1758 if (fabs(Sca-Sa) < MagickEpsilon)
1759 {
cristy99abff32011-12-24 20:45:16 +00001760 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001761 break;
1762 }
cristy99abff32011-12-24 20:45:16 +00001763 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001764 (1.0-Sa));
1765 break;
1766 }
1767 case ColorizeCompositeOp:
1768 {
cristy99abff32011-12-24 20:45:16 +00001769 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001770 {
1771 pixel=Dc;
1772 break;
1773 }
cristy99abff32011-12-24 20:45:16 +00001774 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001775 {
1776 pixel=Sc;
1777 break;
1778 }
cristy7133e642012-08-14 11:04:11 +00001779 CompositeHCL(destination_pixel.red,destination_pixel.green,
1780 destination_pixel.blue,&sans,&sans,&luma);
1781 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1782 &hue,&chroma,&sans);
1783 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001784 switch (channel)
1785 {
1786 case RedPixelChannel: pixel=red; break;
1787 case GreenPixelChannel: pixel=green; break;
1788 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001789 default: pixel=Dc; break;
1790 }
1791 break;
1792 }
cristye4a40472011-12-22 02:56:19 +00001793 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001794 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001795 {
cristy24d5d722012-05-17 12:27:27 +00001796 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001797 break;
1798 }
1799 case CopyBlackCompositeOp:
1800 {
cristyd197cbb2012-01-13 02:14:12 +00001801 if (channel == BlackPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001802 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001803 break;
1804 }
1805 case CopyBlueCompositeOp:
1806 case CopyYellowCompositeOp:
1807 {
cristyd197cbb2012-01-13 02:14:12 +00001808 if (channel == BluePixelChannel)
cristy7159f662012-10-28 17:32:43 +00001809 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001810 break;
1811 }
1812 case CopyGreenCompositeOp:
1813 case CopyMagentaCompositeOp:
1814 {
cristyd197cbb2012-01-13 02:14:12 +00001815 if (channel == GreenPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001816 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001817 break;
1818 }
1819 case CopyRedCompositeOp:
1820 case CopyCyanCompositeOp:
1821 {
cristyd197cbb2012-01-13 02:14:12 +00001822 if (channel == RedPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001823 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001824 break;
1825 }
cristy99abff32011-12-24 20:45:16 +00001826 case DarkenCompositeOp:
1827 {
1828 /*
1829 Darken is equivalent to a 'Minimum' method
1830 OR a greyscale version of a binary 'Or'
1831 OR the 'Intersection' of pixel sets.
1832 */
1833 if (Sc < Dc)
1834 {
1835 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1836 break;
1837 }
1838 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1839 break;
1840 }
cristye4a40472011-12-22 02:56:19 +00001841 case DarkenIntensityCompositeOp:
1842 {
cristy99abff32011-12-24 20:45:16 +00001843 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1844 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001845 break;
1846 }
1847 case DifferenceCompositeOp:
1848 {
1849 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1850 break;
1851 }
1852 case DissolveCompositeOp:
1853 {
1854 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1855 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1856 break;
1857 }
1858 case DivideDstCompositeOp:
1859 {
1860 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1861 {
cristy99abff32011-12-24 20:45:16 +00001862 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001863 break;
1864 }
1865 if (fabs(Dca) < MagickEpsilon)
1866 {
cristy99abff32011-12-24 20:45:16 +00001867 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001868 break;
1869 }
cristy99abff32011-12-24 20:45:16 +00001870 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001871 break;
1872 }
1873 case DivideSrcCompositeOp:
1874 {
1875 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1876 {
cristy99abff32011-12-24 20:45:16 +00001877 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001878 break;
1879 }
1880 if (fabs(Sca) < MagickEpsilon)
1881 {
cristy99abff32011-12-24 20:45:16 +00001882 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001883 break;
1884 }
cristy99abff32011-12-24 20:45:16 +00001885 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001886 break;
1887 }
1888 case DstAtopCompositeOp:
1889 {
1890 pixel=Dc*Da+Sc*(1.0-Da);
1891 break;
1892 }
1893 case DstCompositeOp:
1894 case NoCompositeOp:
1895 {
1896 pixel=Dc;
1897 break;
1898 }
1899 case DstInCompositeOp:
1900 {
1901 pixel=gamma*(Sa*Dc*Sa);
1902 break;
1903 }
1904 case DstOutCompositeOp:
1905 {
1906 pixel=gamma*(Da*Dc*(1.0-Sa));
1907 break;
1908 }
1909 case DstOverCompositeOp:
1910 {
1911 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1912 break;
1913 }
1914 case ExclusionCompositeOp:
1915 {
cristy99abff32011-12-24 20:45:16 +00001916 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1917 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001918 break;
1919 }
1920 case HardLightCompositeOp:
1921 {
1922 if ((2.0*Sca) < Sa)
1923 {
cristy99abff32011-12-24 20:45:16 +00001924 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001925 (1.0-Sa));
1926 break;
1927 }
cristy99abff32011-12-24 20:45:16 +00001928 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001929 Dca*(1.0-Sa));
1930 break;
1931 }
1932 case HueCompositeOp:
1933 {
cristy99abff32011-12-24 20:45:16 +00001934 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001935 {
1936 pixel=Dc;
1937 break;
1938 }
cristy99abff32011-12-24 20:45:16 +00001939 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001940 {
1941 pixel=Sc;
1942 break;
1943 }
cristy7133e642012-08-14 11:04:11 +00001944 CompositeHCL(destination_pixel.red,destination_pixel.green,
1945 destination_pixel.blue,&hue,&chroma,&luma);
1946 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001947 &hue,&sans,&sans);
cristy7133e642012-08-14 11:04:11 +00001948 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001949 switch (channel)
1950 {
1951 case RedPixelChannel: pixel=red; break;
1952 case GreenPixelChannel: pixel=green; break;
1953 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001954 default: pixel=Dc; break;
1955 }
1956 break;
1957 }
1958 case InCompositeOp:
1959 case SrcInCompositeOp:
1960 {
1961 pixel=gamma*(Da*Sc*Da);
1962 break;
1963 }
cristy99abff32011-12-24 20:45:16 +00001964 case LinearBurnCompositeOp:
1965 {
1966 /*
1967 LinearBurn: as defined by Abode Photoshop, according to
1968 http://www.simplefilter.de/en/basics/mixmods.html is:
1969
1970 f(Sc,Dc) = Sc + Dc - 1
1971 */
1972 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1973 break;
1974 }
1975 case LinearDodgeCompositeOp:
1976 {
1977 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1978 break;
1979 }
1980 case LinearLightCompositeOp:
1981 {
1982 /*
1983 LinearLight: as defined by Abode Photoshop, according to
1984 http://www.simplefilter.de/en/basics/mixmods.html is:
1985
1986 f(Sc,Dc) = Dc + 2*Sc - 1
1987 */
1988 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1989 break;
1990 }
1991 case LightenCompositeOp:
1992 {
1993 if (Sc > Dc)
1994 {
cristy24d5d722012-05-17 12:27:27 +00001995 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00001996 break;
1997 }
cristy24d5d722012-05-17 12:27:27 +00001998 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00001999 break;
2000 }
cristye4a40472011-12-22 02:56:19 +00002001 case LightenIntensityCompositeOp:
2002 {
2003 /*
2004 Lighten is equivalent to a 'Maximum' method
2005 OR a greyscale version of a binary 'And'
2006 OR the 'Union' of pixel sets.
2007 */
2008 pixel=Sa*GetPixelIntensity(composite_image,p) >
2009 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2010 break;
2011 }
cristye4a40472011-12-22 02:56:19 +00002012 case LuminizeCompositeOp:
2013 {
cristy99abff32011-12-24 20:45:16 +00002014 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002015 {
2016 pixel=Dc;
2017 break;
2018 }
cristy99abff32011-12-24 20:45:16 +00002019 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002020 {
2021 pixel=Sc;
2022 break;
2023 }
cristy7133e642012-08-14 11:04:11 +00002024 CompositeHCL(destination_pixel.red,destination_pixel.green,
2025 destination_pixel.blue,&hue,&chroma,&luma);
2026 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2027 &sans,&sans,&luma);
2028 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002029 switch (channel)
2030 {
2031 case RedPixelChannel: pixel=red; break;
2032 case GreenPixelChannel: pixel=green; break;
2033 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002034 default: pixel=Dc; break;
2035 }
2036 break;
2037 }
2038 case MathematicsCompositeOp:
2039 {
2040 /*
2041 'Mathematics' a free form user control mathematical composition
2042 is defined as...
2043
2044 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2045
2046 Where the arguments A,B,C,D are (currently) passed to composite
2047 as a command separated 'geometry' string in "compose:args" image
2048 artifact.
2049
2050 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2051
2052 Applying the SVG transparency formula (see above), we get...
2053
2054 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2055
2056 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2057 Dca*(1.0-Sa)
2058 */
2059 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2060 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2061 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2062 break;
2063 }
2064 case MinusDstCompositeOp:
2065 {
2066 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2067 break;
2068 }
2069 case MinusSrcCompositeOp:
2070 {
2071 /*
2072 Minus source from destination.
2073
2074 f(Sc,Dc) = Sc - Dc
2075 */
cristy99abff32011-12-24 20:45:16 +00002076 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002077 break;
2078 }
2079 case ModulateCompositeOp:
2080 {
2081 ssize_t
2082 offset;
2083
cristy99abff32011-12-24 20:45:16 +00002084 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002085 {
2086 pixel=Dc;
2087 break;
2088 }
2089 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2090 if (offset == 0)
2091 {
2092 pixel=Dc;
2093 break;
2094 }
cristy7133e642012-08-14 11:04:11 +00002095 CompositeHCL(destination_pixel.red,destination_pixel.green,
2096 destination_pixel.blue,&hue,&chroma,&luma);
2097 luma+=(0.01*percent_luma*offset)/midpoint;
2098 chroma*=0.01*percent_chroma;
2099 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002100 switch (channel)
2101 {
2102 case RedPixelChannel: pixel=red; break;
2103 case GreenPixelChannel: pixel=green; break;
2104 case BluePixelChannel: pixel=blue; break;
2105 default: pixel=Dc; break;
2106 }
2107 break;
2108 }
2109 case ModulusAddCompositeOp:
2110 {
2111 pixel=Sc+Dc;
2112 if (pixel > QuantumRange)
2113 pixel-=(QuantumRange+1.0);
2114 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2115 break;
2116 }
2117 case ModulusSubtractCompositeOp:
2118 {
cristy99abff32011-12-24 20:45:16 +00002119 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002120 if (pixel < 0.0)
2121 pixel+=(QuantumRange+1.0);
2122 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2123 break;
2124 }
2125 case MultiplyCompositeOp:
2126 {
cristy99abff32011-12-24 20:45:16 +00002127 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002128 break;
2129 }
2130 case OutCompositeOp:
2131 case SrcOutCompositeOp:
2132 {
2133 pixel=gamma*(Sa*Sc*(1.0-Da));
2134 break;
2135 }
2136 case OverCompositeOp:
2137 case SrcOverCompositeOp:
2138 {
cristy99abff32011-12-24 20:45:16 +00002139 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002140 break;
2141 }
2142 case OverlayCompositeOp:
2143 {
2144 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002145 {
2146 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2147 (1.0-Da));
2148 break;
2149 }
2150 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2151 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002152 break;
2153 }
2154 case PegtopLightCompositeOp:
2155 {
2156 /*
2157 PegTop: A Soft-Light alternative: A continuous version of the
2158 Softlight function, producing very similar results.
2159
2160 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2161
2162 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2163 */
2164 if (fabs(Da) < MagickEpsilon)
2165 {
cristy99abff32011-12-24 20:45:16 +00002166 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002167 break;
2168 }
cristy99abff32011-12-24 20:45:16 +00002169 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2170 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002171 break;
2172 }
2173 case PinLightCompositeOp:
2174 {
2175 /*
2176 PinLight: A Photoshop 7 composition method
2177 http://www.simplefilter.de/en/basics/mixmods.html
2178
2179 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2180 */
2181 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2182 {
cristy99abff32011-12-24 20:45:16 +00002183 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002184 break;
2185 }
2186 if ((Dca*Sa) > (2.0*Sca*Da))
2187 {
cristy99abff32011-12-24 20:45:16 +00002188 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002189 break;
2190 }
cristy99abff32011-12-24 20:45:16 +00002191 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002192 break;
2193 }
2194 case PlusCompositeOp:
2195 {
cristy24d5d722012-05-17 12:27:27 +00002196 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002197 break;
2198 }
2199 case SaturateCompositeOp:
2200 {
cristy99abff32011-12-24 20:45:16 +00002201 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002202 {
2203 pixel=Dc;
2204 break;
2205 }
cristy99abff32011-12-24 20:45:16 +00002206 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002207 {
2208 pixel=Sc;
2209 break;
2210 }
cristy7133e642012-08-14 11:04:11 +00002211 CompositeHCL(destination_pixel.red,destination_pixel.green,
2212 destination_pixel.blue,&hue,&chroma,&luma);
2213 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2214 &sans,&chroma,&sans);
2215 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002216 switch (channel)
2217 {
2218 case RedPixelChannel: pixel=red; break;
2219 case GreenPixelChannel: pixel=green; break;
2220 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002221 default: pixel=Dc; break;
2222 }
2223 break;
2224 }
2225 case ScreenCompositeOp:
2226 {
2227 /*
2228 Screen: a negated multiply:
2229
2230 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2231 */
cristy99abff32011-12-24 20:45:16 +00002232 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002233 break;
2234 }
2235 case SoftLightCompositeOp:
2236 {
2237 /*
2238 Refer to the March 2009 SVG specification.
2239 */
2240 if ((2.0*Sca) < Sa)
2241 {
cristy99abff32011-12-24 20:45:16 +00002242 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2243 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002244 break;
2245 }
2246 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2247 {
cristy99abff32011-12-24 20:45:16 +00002248 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2249 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2250 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002251 break;
2252 }
cristy99abff32011-12-24 20:45:16 +00002253 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2254 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002255 break;
2256 }
2257 case ThresholdCompositeOp:
2258 {
cristy7159f662012-10-28 17:32:43 +00002259 MagickRealType
cristye4a40472011-12-22 02:56:19 +00002260 delta;
2261
2262 delta=Sc-Dc;
cristy7159f662012-10-28 17:32:43 +00002263 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002264 {
2265 pixel=gamma*Dc;
2266 break;
2267 }
2268 pixel=gamma*(Dc+delta*amount);
2269 break;
2270 }
2271 case VividLightCompositeOp:
2272 {
2273 /*
2274 VividLight: A Photoshop 7 composition method. See
2275 http://www.simplefilter.de/en/basics/mixmods.html.
2276
2277 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2278 */
2279 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2280 {
cristy99abff32011-12-24 20:45:16 +00002281 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002282 break;
2283 }
2284 if ((2.0*Sca) <= Sa)
2285 {
cristy99abff32011-12-24 20:45:16 +00002286 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2287 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002288 break;
2289 }
cristy99abff32011-12-24 20:45:16 +00002290 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2291 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002292 break;
2293 }
2294 case XorCompositeOp:
2295 {
cristy99abff32011-12-24 20:45:16 +00002296 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002297 break;
2298 }
2299 default:
2300 {
2301 pixel=Sc;
2302 break;
2303 }
2304 }
2305 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002306 }
cristyed231572011-07-14 02:18:59 +00002307 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002308 channels=GetPixelChannels(composite_image);
2309 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002310 p=pixels;
cristyed231572011-07-14 02:18:59 +00002311 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002312 }
2313 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2314 status=MagickFalse;
2315 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2316 {
2317 MagickBooleanType
2318 proceed;
2319
2320#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002321 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002322#endif
2323 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2324 image->rows);
2325 if (proceed == MagickFalse)
2326 status=MagickFalse;
2327 }
2328 }
2329 composite_view=DestroyCacheView(composite_view);
2330 image_view=DestroyCacheView(image_view);
2331 if (destination_image != (Image * ) NULL)
2332 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002333 else
2334 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00002335 return(status);
2336}
2337
2338/*
2339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2340% %
2341% %
2342% %
2343% T e x t u r e I m a g e %
2344% %
2345% %
2346% %
2347%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2348%
2349% TextureImage() repeatedly tiles the texture image across and down the image
2350% canvas.
2351%
2352% The format of the TextureImage method is:
2353%
cristy30d8c942012-02-07 13:44:59 +00002354% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002355% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002356%
2357% A description of each parameter follows:
2358%
2359% o image: the image.
2360%
cristye6178502011-12-23 17:02:29 +00002361% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002362%
2363*/
cristy30d8c942012-02-07 13:44:59 +00002364MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2365 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002366{
2367#define TextureImageTag "Texture/Image"
2368
2369 CacheView
2370 *image_view,
2371 *texture_view;
2372
cristy30d8c942012-02-07 13:44:59 +00002373 Image
2374 *texture_image;
2375
cristy4c08aed2011-07-01 19:47:50 +00002376 MagickBooleanType
2377 status;
2378
2379 ssize_t
2380 y;
2381
2382 assert(image != (Image *) NULL);
2383 if (image->debug != MagickFalse)
2384 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2385 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002386 if (texture == (const Image *) NULL)
2387 return(MagickFalse);
2388 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2389 return(MagickFalse);
2390 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002391 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002392 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002393 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002394 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2395 exception);
cristy4c08aed2011-07-01 19:47:50 +00002396 status=MagickTrue;
2397 if ((image->compose != CopyCompositeOp) &&
cristy8a46d822012-08-28 23:32:39 +00002398 ((image->compose != OverCompositeOp) || (image->alpha_trait == BlendPixelTrait) ||
2399 (texture_image->alpha_trait == BlendPixelTrait)))
cristy4c08aed2011-07-01 19:47:50 +00002400 {
2401 /*
2402 Tile texture onto the image background.
2403 */
cristye6178502011-12-23 17:02:29 +00002404 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002405 {
2406 register ssize_t
2407 x;
2408
2409 if (status == MagickFalse)
2410 continue;
cristye6178502011-12-23 17:02:29 +00002411 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002412 {
2413 MagickBooleanType
2414 thread_status;
2415
cristyfeb3e962012-03-29 17:25:55 +00002416 thread_status=CompositeImage(image,texture_image,image->compose,
2417 MagickFalse,x+texture_image->tile_offset.x,y+
2418 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002419 if (thread_status == MagickFalse)
2420 {
2421 status=thread_status;
2422 break;
2423 }
2424 }
2425 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2426 {
2427 MagickBooleanType
2428 proceed;
2429
cristy4c08aed2011-07-01 19:47:50 +00002430 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2431 y,image->rows);
2432 if (proceed == MagickFalse)
2433 status=MagickFalse;
2434 }
2435 }
2436 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2437 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002438 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002439 return(status);
2440 }
2441 /*
2442 Tile texture onto the image background (optimized).
2443 */
2444 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00002445 texture_view=AcquireVirtualCacheView(texture_image,exception);
2446 image_view=AcquireAuthenticCacheView(image,exception);
cristye8914392012-12-16 21:16:11 +00002447#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00002448 #pragma omp parallel for schedule(static,4) shared(status) \
2449 magick_threads(texture_image,image,1,1)
cristye8914392012-12-16 21:16:11 +00002450#endif
cristy4c08aed2011-07-01 19:47:50 +00002451 for (y=0; y < (ssize_t) image->rows; y++)
2452 {
2453 MagickBooleanType
2454 sync;
2455
2456 register const Quantum
2457 *p,
2458 *pixels;
2459
2460 register ssize_t
2461 x;
2462
2463 register Quantum
2464 *q;
2465
2466 size_t
2467 width;
2468
2469 if (status == MagickFalse)
2470 continue;
cristye6178502011-12-23 17:02:29 +00002471 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2472 (y+texture_image->tile_offset.y) % texture_image->rows,
2473 texture_image->columns,1,exception);
2474 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002475 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2476 {
2477 status=MagickFalse;
2478 continue;
2479 }
cristye6178502011-12-23 17:02:29 +00002480 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002481 {
2482 register ssize_t
cristye6178502011-12-23 17:02:29 +00002483 j;
cristy4c08aed2011-07-01 19:47:50 +00002484
2485 p=pixels;
cristye6178502011-12-23 17:02:29 +00002486 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002487 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2488 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002489 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002490 {
cristye6178502011-12-23 17:02:29 +00002491 register ssize_t
2492 i;
2493
cristyd03b8fa2013-02-10 18:59:28 +00002494 if (GetPixelMask(image,q) != 0)
cristy10a6c612012-01-29 21:41:05 +00002495 {
2496 p+=GetPixelChannels(texture_image);
2497 q+=GetPixelChannels(image);
2498 continue;
2499 }
cristye6178502011-12-23 17:02:29 +00002500 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2501 {
2502 PixelChannel
2503 channel;
2504
2505 PixelTrait
2506 texture_traits,
2507 traits;
2508
cristycf1296e2012-08-26 23:40:49 +00002509 channel=GetPixelChannelChannel(texture_image,i);
2510 texture_traits=GetPixelChannelTraits(texture_image,channel);
2511 traits=GetPixelChannelTraits(image,channel);
cristye6178502011-12-23 17:02:29 +00002512 if ((traits == UndefinedPixelTrait) ||
2513 (texture_traits == UndefinedPixelTrait))
2514 continue;
2515 SetPixelChannel(image,channel,p[i],q);
2516 }
2517 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002518 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002519 }
2520 }
2521 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2522 if (sync == MagickFalse)
2523 status=MagickFalse;
2524 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2525 {
2526 MagickBooleanType
2527 proceed;
2528
cristy4c08aed2011-07-01 19:47:50 +00002529 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2530 image->rows);
2531 if (proceed == MagickFalse)
2532 status=MagickFalse;
2533 }
2534 }
2535 texture_view=DestroyCacheView(texture_view);
2536 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002537 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002538 return(status);
2539}