blob: 2a7c727fc2465e4e2d27f5376b5aea2c0a4a3c76 [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,
cristye1715282012-08-15 13:10:55 +0000223 x,
224 z;
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 }
cristya86a5cb2012-10-14 13:40:33 +0000273 m=luma-(0.298839f*r+0.586811f*g+0.114350f*b);
cristye1715282012-08-15 13:10:55 +0000274 /*
275 Choose saturation strategy to clip it into the RGB cube; hue and luma are
276 preserved and chroma may be changed.
277 */
278 z=1.0;
279 if (m < 0.0)
280 {
281 z=luma/(luma-m);
282 m=0.0;
283 }
284 else
285 if (m+c > 1.0)
286 {
287 z=(1.0-luma)/(m+c-luma);
288 m=1.0-z*c;
289 }
290 *red=QuantumRange*(z*r+m);
291 *green=QuantumRange*(z*g+m);
292 *blue=QuantumRange*(z*b+m);
cristyd8f16f72012-08-13 12:49:50 +0000293}
294
cristy7159f662012-10-28 17:32:43 +0000295static void CompositeHCL(const MagickRealType red,const MagickRealType green,
296 const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
297 MagickRealType *luma)
cristyd8f16f72012-08-13 12:49:50 +0000298{
cristy7159f662012-10-28 17:32:43 +0000299 MagickRealType
cristyd8f16f72012-08-13 12:49:50 +0000300 b,
cristy7133e642012-08-14 11:04:11 +0000301 c,
cristyd8f16f72012-08-13 12:49:50 +0000302 g,
cristy7133e642012-08-14 11:04:11 +0000303 h,
cristyd8f16f72012-08-13 12:49:50 +0000304 max,
cristyd8f16f72012-08-13 12:49:50 +0000305 r;
306
307 /*
cristy7133e642012-08-14 11:04:11 +0000308 Convert RGB to HCL colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000309 */
cristy7159f662012-10-28 17:32:43 +0000310 assert(hue != (MagickRealType *) NULL);
311 assert(chroma != (MagickRealType *) NULL);
312 assert(luma != (MagickRealType *) NULL);
cristy7133e642012-08-14 11:04:11 +0000313 r=red;
314 g=green;
315 b=blue;
cristyd8f16f72012-08-13 12:49:50 +0000316 max=MagickMax(r,MagickMax(g,b));
cristy7159f662012-10-28 17:32:43 +0000317 c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
cristy7133e642012-08-14 11:04:11 +0000318 h=0.0;
319 if (c == 0)
320 h=0.0;
cristyd8f16f72012-08-13 12:49:50 +0000321 else
cristy7133e642012-08-14 11:04:11 +0000322 if (red == max)
cristyf2c5f4a2012-08-14 23:22:31 +0000323 h=fmod(6.0+(g-b)/c,6.0);
cristyd8f16f72012-08-13 12:49:50 +0000324 else
cristy7133e642012-08-14 11:04:11 +0000325 if (green == max)
326 h=((b-r)/c)+2.0;
327 else
328 if (blue == max)
329 h=((r-g)/c)+4.0;
330 *hue=(h/6.0);
331 *chroma=QuantumScale*c;
cristya86a5cb2012-10-14 13:40:33 +0000332 *luma=QuantumScale*(0.298839f*r+0.586811f*g+0.114350f*b);
cristyd8f16f72012-08-13 12:49:50 +0000333}
334
cristye4a40472011-12-22 02:56:19 +0000335static MagickBooleanType CompositeOverImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000336 const Image *composite_image,const MagickBooleanType clip_to_self,
337 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristye4a40472011-12-22 02:56:19 +0000338{
339#define CompositeImageTag "Composite/Image"
340
341 CacheView
342 *composite_view,
343 *image_view;
344
cristye4a40472011-12-22 02:56:19 +0000345 MagickBooleanType
cristye4a40472011-12-22 02:56:19 +0000346 status;
347
348 MagickOffsetType
349 progress;
350
351 ssize_t
352 y;
353
cristye4a40472011-12-22 02:56:19 +0000354 /*
cristye4a40472011-12-22 02:56:19 +0000355 Composite image.
356 */
357 status=MagickTrue;
358 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000359 composite_view=AcquireVirtualCacheView(composite_image,exception);
360 image_view=AcquireAuthenticCacheView(image,exception);
cristye4a40472011-12-22 02:56:19 +0000361#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000362 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000363 magick_threads(composite_image,image,image->rows,1)
cristye4a40472011-12-22 02:56:19 +0000364#endif
365 for (y=0; y < (ssize_t) image->rows; y++)
366 {
367 const Quantum
368 *pixels;
369
370 register const Quantum
371 *restrict p;
372
373 register Quantum
374 *restrict q;
375
376 register ssize_t
377 x;
378
cristy564a5692012-01-20 23:56:26 +0000379 size_t
380 channels;
381
cristye4a40472011-12-22 02:56:19 +0000382 if (status == MagickFalse)
383 continue;
cristyfeb3e962012-03-29 17:25:55 +0000384 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000385 {
386 if (y < y_offset)
387 continue;
388 if ((y-y_offset) >= (ssize_t) composite_image->rows)
389 continue;
390 }
391 /*
392 If pixels is NULL, y is outside overlay region.
393 */
394 pixels=(Quantum *) NULL;
395 p=(Quantum *) NULL;
396 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
397 {
398 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
399 composite_image->columns,1,exception);
400 if (p == (const Quantum *) NULL)
401 {
402 status=MagickFalse;
403 continue;
404 }
405 pixels=p;
406 if (x_offset < 0)
407 p-=x_offset*GetPixelChannels(composite_image);
408 }
409 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
410 if (q == (Quantum *) NULL)
411 {
412 status=MagickFalse;
413 continue;
414 }
415 for (x=0; x < (ssize_t) image->columns; x++)
416 {
cristy17028dc2013-01-24 12:28:39 +0000417 double
418 gamma;
419
cristy7159f662012-10-28 17:32:43 +0000420 MagickRealType
cristye4a40472011-12-22 02:56:19 +0000421 alpha,
422 Da,
423 Dc,
cristye4a40472011-12-22 02:56:19 +0000424 Sa,
425 Sc;
426
427 register ssize_t
428 i;
429
cristyfeb3e962012-03-29 17:25:55 +0000430 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000431 {
432 if (x < x_offset)
433 {
434 q+=GetPixelChannels(image);
435 continue;
436 }
437 if ((x-x_offset) >= (ssize_t) composite_image->columns)
438 break;
439 }
440 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
441 ((x-x_offset) >= (ssize_t) composite_image->columns))
442 {
443 Quantum
444 source[MaxPixelChannels];
445
446 /*
447 Virtual composite:
448 Sc: source color.
449 Dc: destination color.
450 */
cristy10a6c612012-01-29 21:41:05 +0000451 if (GetPixelMask(image,q) != 0)
452 {
453 q+=GetPixelChannels(image);
454 continue;
455 }
cristyc94ba6f2012-01-29 23:19:58 +0000456 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
457 source,exception);
cristye4a40472011-12-22 02:56:19 +0000458 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
459 {
460 PixelChannel
461 channel;
462
463 PixelTrait
464 composite_traits,
465 traits;
466
cristycf1296e2012-08-26 23:40:49 +0000467 channel=GetPixelChannelChannel(image,i);
468 traits=GetPixelChannelTraits(image,channel);
469 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000470 if ((traits == UndefinedPixelTrait) ||
471 (composite_traits == UndefinedPixelTrait))
472 continue;
473 q[i]=source[channel];
474 }
475 q+=GetPixelChannels(image);
476 continue;
477 }
478 /*
479 Authentic composite:
480 Sa: normalized source alpha.
481 Da: normalized destination alpha.
482 */
cristyc94ba6f2012-01-29 23:19:58 +0000483 if (GetPixelMask(composite_image,p) != 0)
484 {
485 p+=GetPixelChannels(composite_image);
486 channels=GetPixelChannels(composite_image);
487 if (p >= (pixels+channels*composite_image->columns))
488 p=pixels;
489 q+=GetPixelChannels(image);
490 continue;
491 }
cristye4a40472011-12-22 02:56:19 +0000492 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
493 Da=QuantumScale*GetPixelAlpha(image,q);
494 alpha=Sa*(-Da)+Sa+Da;
495 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
496 {
497 PixelChannel
498 channel;
499
500 PixelTrait
501 composite_traits,
502 traits;
503
cristycf1296e2012-08-26 23:40:49 +0000504 channel=GetPixelChannelChannel(image,i);
505 traits=GetPixelChannelTraits(image,channel);
506 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000507 if ((traits == UndefinedPixelTrait) ||
508 (composite_traits == UndefinedPixelTrait))
509 continue;
510 if ((traits & CopyPixelTrait) != 0)
511 {
512 if (channel != AlphaPixelChannel)
513 {
514 /*
515 Copy channel.
516 */
517 q[i]=GetPixelChannel(composite_image,channel,p);
518 continue;
519 }
520 /*
521 Set alpha channel.
522 */
523 q[i]=ClampToQuantum(QuantumRange*alpha);
524 continue;
525 }
526 /*
527 Sc: source color.
528 Dc: destination color.
529 */
cristy7159f662012-10-28 17:32:43 +0000530 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
531 Dc=(MagickRealType) q[i];
cristy3e3ec3a2012-11-03 23:11:06 +0000532 gamma=PerceptibleReciprocal(alpha);
cristye4a40472011-12-22 02:56:19 +0000533 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
534 }
535 p+=GetPixelChannels(composite_image);
536 channels=GetPixelChannels(composite_image);
537 if (p >= (pixels+channels*composite_image->columns))
538 p=pixels;
539 q+=GetPixelChannels(image);
540 }
541 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
542 status=MagickFalse;
543 if (image->progress_monitor != (MagickProgressMonitor) NULL)
544 {
545 MagickBooleanType
546 proceed;
547
548#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000549 #pragma omp critical (MagickCore_CompositeImage)
cristye4a40472011-12-22 02:56:19 +0000550#endif
551 proceed=SetImageProgress(image,CompositeImageTag,progress++,
552 image->rows);
553 if (proceed == MagickFalse)
554 status=MagickFalse;
555 }
556 }
557 composite_view=DestroyCacheView(composite_view);
558 image_view=DestroyCacheView(image_view);
559 return(status);
560}
561
cristy4c08aed2011-07-01 19:47:50 +0000562MagickExport MagickBooleanType CompositeImage(Image *image,
cristya865ccd2012-07-28 00:33:10 +0000563 const Image *composite,const CompositeOperator compose,
cristy44c98412012-03-31 18:25:40 +0000564 const MagickBooleanType clip_to_self,const ssize_t x_offset,
565 const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000566{
cristy4c08aed2011-07-01 19:47:50 +0000567#define CompositeImageTag "Composite/Image"
568
569 CacheView
570 *composite_view,
571 *image_view;
572
cristy4c08aed2011-07-01 19:47:50 +0000573 GeometryInfo
574 geometry_info;
575
576 Image
cristya865ccd2012-07-28 00:33:10 +0000577 *composite_image,
cristy4c08aed2011-07-01 19:47:50 +0000578 *destination_image;
579
580 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000581 status;
582
583 MagickOffsetType
584 progress;
585
cristy7159f662012-10-28 17:32:43 +0000586 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000587 amount,
588 destination_dissolve,
589 midpoint,
cristy7133e642012-08-14 11:04:11 +0000590 percent_luma,
591 percent_chroma,
cristy4c08aed2011-07-01 19:47:50 +0000592 source_dissolve,
593 threshold;
594
595 MagickStatusType
596 flags;
597
cristyd197cbb2012-01-13 02:14:12 +0000598 ssize_t
599 y;
600
cristy4c08aed2011-07-01 19:47:50 +0000601 assert(image != (Image *) NULL);
602 assert(image->signature == MagickSignature);
603 if (image->debug != MagickFalse)
604 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristya865ccd2012-07-28 00:33:10 +0000605 assert(composite!= (Image *) NULL);
606 assert(composite->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000607 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000608 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +0000609 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
610 if (composite_image == (const Image *) NULL)
611 return(MagickFalse);
cristyeb16d5e2012-09-12 20:11:53 +0000612 if (IsGrayColorspace(image->colorspace) != MagickFalse)
613 (void) SetImageColorspace(image,RGBColorspace,exception);
cristya4a22a22012-08-01 23:16:38 +0000614 (void) SetImageColorspace(composite_image,image->colorspace,exception);
cristye4a40472011-12-22 02:56:19 +0000615 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
616 {
cristyfeb3e962012-03-29 17:25:55 +0000617 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
618 y_offset,exception);
cristya865ccd2012-07-28 00:33:10 +0000619 composite_image=DestroyImage(composite_image);
cristye4a40472011-12-22 02:56:19 +0000620 return(status);
621 }
cristy4c08aed2011-07-01 19:47:50 +0000622 destination_image=(Image *) NULL;
623 amount=0.5;
624 destination_dissolve=1.0;
cristy7133e642012-08-14 11:04:11 +0000625 percent_luma=100.0;
626 percent_chroma=100.0;
cristy4c08aed2011-07-01 19:47:50 +0000627 source_dissolve=1.0;
628 threshold=0.05f;
629 switch (compose)
630 {
cristy4c08aed2011-07-01 19:47:50 +0000631 case CopyCompositeOp:
632 {
633 if ((x_offset < 0) || (y_offset < 0))
634 break;
635 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
636 break;
637 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
638 break;
639 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +0000640 composite_view=AcquireVirtualCacheView(composite_image,exception);
641 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000642#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000643 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +0000644 magick_threads(composite_image,image,composite_image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000645#endif
646 for (y=0; y < (ssize_t) composite_image->rows; y++)
647 {
648 MagickBooleanType
649 sync;
650
651 register const Quantum
652 *p;
653
654 register Quantum
655 *q;
656
657 register ssize_t
658 x;
659
660 if (status == MagickFalse)
661 continue;
662 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
663 1,exception);
664 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
665 composite_image->columns,1,exception);
666 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
667 {
668 status=MagickFalse;
669 continue;
670 }
671 for (x=0; x < (ssize_t) composite_image->columns; x++)
672 {
cristybdecccc2011-12-24 22:52:16 +0000673 register ssize_t
674 i;
675
cristy665e18f2012-05-17 12:39:54 +0000676 if (GetPixelMask(composite_image,p) != 0)
cristy10a6c612012-01-29 21:41:05 +0000677 {
678 p+=GetPixelChannels(composite_image);
679 q+=GetPixelChannels(image);
680 continue;
681 }
cristybdecccc2011-12-24 22:52:16 +0000682 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
683 {
684 PixelChannel
685 channel;
686
687 PixelTrait
688 composite_traits,
689 traits;
690
cristycf1296e2012-08-26 23:40:49 +0000691 channel=GetPixelChannelChannel(composite_image,i);
692 composite_traits=GetPixelChannelTraits(composite_image,channel);
693 traits=GetPixelChannelTraits(image,channel);
cristybdecccc2011-12-24 22:52:16 +0000694 if ((traits == UndefinedPixelTrait) ||
695 (composite_traits == UndefinedPixelTrait))
696 continue;
697 SetPixelChannel(image,channel,p[i],q);
698 }
cristyed231572011-07-14 02:18:59 +0000699 p+=GetPixelChannels(composite_image);
700 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000701 }
702 sync=SyncCacheViewAuthenticPixels(image_view,exception);
703 if (sync == MagickFalse)
704 status=MagickFalse;
705 if (image->progress_monitor != (MagickProgressMonitor) NULL)
706 {
707 MagickBooleanType
708 proceed;
709
710#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000711 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000712#endif
713 proceed=SetImageProgress(image,CompositeImageTag,
714 (MagickOffsetType) y,image->rows);
715 if (proceed == MagickFalse)
716 status=MagickFalse;
717 }
718 }
719 composite_view=DestroyCacheView(composite_view);
720 image_view=DestroyCacheView(image_view);
cristy44886b92012-07-28 13:07:09 +0000721 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000722 return(status);
723 }
cristye4a40472011-12-22 02:56:19 +0000724 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000725 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000726 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000727 {
728 /*
729 Modify destination outside the overlaid region and require an alpha
730 channel to exist, to add transparency.
731 */
cristy8a46d822012-08-28 23:32:39 +0000732 if (image->alpha_trait != BlendPixelTrait)
cristy42c41de2012-05-05 18:36:31 +0000733 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000734 break;
735 }
736 case BlurCompositeOp:
737 {
738 CacheView
739 *composite_view,
740 *destination_view;
741
cristyfeb3e962012-03-29 17:25:55 +0000742 const char
743 *value;
744
cristy7159f662012-10-28 17:32:43 +0000745 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000746 angle_range,
747 angle_start,
748 height,
749 width;
750
cristybce4f4a2012-10-14 14:57:47 +0000751 PixelInfo
752 pixel;
753
cristy4c08aed2011-07-01 19:47:50 +0000754 ResampleFilter
755 *resample_filter;
756
757 SegmentInfo
758 blur;
759
760 /*
anthony9cb63cc2012-04-25 06:10:49 +0000761 Blur Image by resampling.
762
cristy4c08aed2011-07-01 19:47:50 +0000763 Blur Image dictated by an overlay gradient map: X = red_channel;
764 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
765 */
766 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000767 exception);
cristy4c08aed2011-07-01 19:47:50 +0000768 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000769 {
770 composite_image=DestroyImage(composite_image);
771 return(MagickFalse);
772 }
cristy4c08aed2011-07-01 19:47:50 +0000773 /*
anthony9cb63cc2012-04-25 06:10:49 +0000774 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000775 */
776 SetGeometryInfo(&geometry_info);
777 flags=NoValue;
778 value=GetImageArtifact(composite_image,"compose:args");
779 if (value != (char *) NULL)
780 flags=ParseGeometry(value,&geometry_info);
anthonyd2923912012-04-23 13:06:53 +0000781 if ((flags & WidthValue) == 0 ) {
782 (void) ThrowMagickException(exception,GetMagickModule(),
783 OptionWarning,"InvalidSetting","'%s' '%s'",
anthony9cb63cc2012-04-25 06:10:49 +0000784 "compose:args",value);
cristy44886b92012-07-28 13:07:09 +0000785 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000786 destination_image=DestroyImage(destination_image);
787 return(MagickFalse);
788 }
anthony9cb63cc2012-04-25 06:10:49 +0000789 /*
790 Users input sigma now needs to be converted to the EWA ellipse size.
791 The filter defaults to a sigma of 0.5 so to make this match the
792 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000793 */
794 width=height=geometry_info.rho*2.0;
795 if ((flags & HeightValue) != 0 )
796 height=geometry_info.sigma*2.0;
cristy7159f662012-10-28 17:32:43 +0000797 /*
798 Default the unrotated ellipse width and height axis vectors.
799 */
anthonyd2923912012-04-23 13:06:53 +0000800 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000801 blur.x2=0.0;
802 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000803 blur.y2=height;
804 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000805 if ((flags & XValue) != 0 )
806 {
cristy7159f662012-10-28 17:32:43 +0000807 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000808 angle;
809
810 angle=DegreesToRadians(geometry_info.xi);
811 blur.x1=width*cos(angle);
812 blur.x2=width*sin(angle);
813 blur.y1=(-height*sin(angle));
814 blur.y2=height*cos(angle);
815 }
anthonyd2923912012-04-23 13:06:53 +0000816 /* Otherwise lets set a angle range and calculate in the loop */
817 angle_start=0.0;
818 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000819 if ((flags & YValue) != 0 )
820 {
821 angle_start=DegreesToRadians(geometry_info.xi);
822 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
823 }
824 /*
anthony9cb63cc2012-04-25 06:10:49 +0000825 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000826
anthony9cb63cc2012-04-25 06:10:49 +0000827 As the minimum ellipse radius of support*1.0 the EWA algorithm
828 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
829 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000830
anthony9cb63cc2012-04-25 06:10:49 +0000831 The solution (as well as the problem of preventing any user
832 expert filter settings, is to set our own user settings, then
833 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000834 */
cristy8a11cb12011-10-19 23:53:34 +0000835 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000836 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000837
838 /* do the variable blurring of each pixel in image */
839 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +0000840 composite_view=AcquireVirtualCacheView(composite_image,exception);
841 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000842 for (y=0; y < (ssize_t) composite_image->rows; y++)
843 {
844 MagickBooleanType
845 sync;
846
847 register const Quantum
848 *restrict p;
849
850 register Quantum
851 *restrict q;
852
853 register ssize_t
854 x;
855
856 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
857 continue;
858 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
859 1,exception);
860 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000861 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000862 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
863 break;
864 for (x=0; x < (ssize_t) composite_image->columns; x++)
865 {
866 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
867 {
cristyed231572011-07-14 02:18:59 +0000868 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000869 continue;
870 }
871 if (fabs(angle_range) > MagickEpsilon)
872 {
cristy7159f662012-10-28 17:32:43 +0000873 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000874 angle;
875
876 angle=angle_start+angle_range*QuantumScale*
877 GetPixelBlue(composite_image,p);
878 blur.x1=width*cos(angle);
879 blur.x2=width*sin(angle);
880 blur.y1=(-height*sin(angle));
881 blur.y2=height*cos(angle);
882 }
anthonyd2923912012-04-23 13:06:53 +0000883#if 0
884 if ( x == 10 && y == 60 ) {
cristy7159f662012-10-28 17:32:43 +0000885 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
886 blur.x2,blur.y1, blur.y2);
887 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
888 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
anthonyd2923912012-04-23 13:06:53 +0000889#endif
890 ScaleResampleFilter(resample_filter,
891 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
892 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
893 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
894 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000895 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000896 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000897 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000898 p+=GetPixelChannels(composite_image);
899 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000900 }
901 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
902 if (sync == MagickFalse)
903 break;
904 }
905 resample_filter=DestroyResampleFilter(resample_filter);
906 composite_view=DestroyCacheView(composite_view);
907 destination_view=DestroyCacheView(destination_view);
cristyf661c4d2012-07-28 12:57:17 +0000908 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000909 composite_image=destination_image;
910 break;
911 }
912 case DisplaceCompositeOp:
913 case DistortCompositeOp:
914 {
915 CacheView
916 *composite_view,
917 *destination_view,
918 *image_view;
919
cristyfeb3e962012-03-29 17:25:55 +0000920 const char
921 *value;
922
cristy4c08aed2011-07-01 19:47:50 +0000923 PixelInfo
924 pixel;
925
cristy7159f662012-10-28 17:32:43 +0000926 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000927 horizontal_scale,
928 vertical_scale;
929
930 PointInfo
931 center,
932 offset;
933
934 /*
935 Displace/Distort based on overlay gradient map:
936 X = red_channel; Y = green_channel;
937 compose:args = x_scale[,y_scale[,center.x,center.y]]
938 */
939 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000940 exception);
cristy4c08aed2011-07-01 19:47:50 +0000941 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000942 {
943 composite_image=DestroyImage(composite_image);
944 return(MagickFalse);
945 }
cristy4c08aed2011-07-01 19:47:50 +0000946 SetGeometryInfo(&geometry_info);
947 flags=NoValue;
948 value=GetImageArtifact(composite_image,"compose:args");
949 if (value != (char *) NULL)
950 flags=ParseGeometry(value,&geometry_info);
951 if ((flags & (WidthValue|HeightValue)) == 0 )
952 {
953 if ((flags & AspectValue) == 0)
954 {
cristy7159f662012-10-28 17:32:43 +0000955 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
cristy4c08aed2011-07-01 19:47:50 +0000956 2.0;
cristy7159f662012-10-28 17:32:43 +0000957 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000958 }
959 else
960 {
cristy7159f662012-10-28 17:32:43 +0000961 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
962 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000963 }
964 }
965 else
966 {
967 horizontal_scale=geometry_info.rho;
968 vertical_scale=geometry_info.sigma;
969 if ((flags & PercentValue) != 0)
970 {
971 if ((flags & AspectValue) == 0)
972 {
973 horizontal_scale*=(composite_image->columns-1.0)/200.0;
974 vertical_scale*=(composite_image->rows-1.0)/200.0;
975 }
976 else
977 {
978 horizontal_scale*=(image->columns-1.0)/200.0;
979 vertical_scale*=(image->rows-1.0)/200.0;
980 }
981 }
982 if ((flags & HeightValue) == 0)
983 vertical_scale=horizontal_scale;
984 }
985 /*
986 Determine fixed center point for absolute distortion map
987 Absolute distort ==
988 Displace offset relative to a fixed absolute point
989 Select that point according to +X+Y user inputs.
990 default = center of overlay image
991 arg flag '!' = locations/percentage relative to background image
992 */
cristy7159f662012-10-28 17:32:43 +0000993 center.x=(MagickRealType) x_offset;
994 center.y=(MagickRealType) y_offset;
cristy4c08aed2011-07-01 19:47:50 +0000995 if (compose == DistortCompositeOp)
996 {
997 if ((flags & XValue) == 0)
998 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000999 center.x=(MagickRealType) (x_offset+(composite_image->columns-1)/
1000 2.0);
cristy4c08aed2011-07-01 19:47:50 +00001001 else
cristy7159f662012-10-28 17:32:43 +00001002 center.x=(MagickRealType) ((image->columns-1)/2);
cristy4c08aed2011-07-01 19:47:50 +00001003 else
1004 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001005 center.x=(MagickRealType) x_offset+geometry_info.xi;
cristy4c08aed2011-07-01 19:47:50 +00001006 else
1007 center.x=geometry_info.xi;
1008 if ((flags & YValue) == 0)
1009 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001010 center.y=(MagickRealType) (y_offset+(composite_image->rows-1)/
1011 2.0);
cristy4c08aed2011-07-01 19:47:50 +00001012 else
cristy7159f662012-10-28 17:32:43 +00001013 center.y=(MagickRealType) ((image->rows-1)/2);
cristy4c08aed2011-07-01 19:47:50 +00001014 else
1015 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001016 center.y=(MagickRealType) y_offset+geometry_info.psi;
cristy4c08aed2011-07-01 19:47:50 +00001017 else
1018 center.y=geometry_info.psi;
1019 }
1020 /*
1021 Shift the pixel offset point as defined by the provided,
1022 displacement/distortion map. -- Like a lens...
1023 */
cristye10859a2011-12-18 22:28:59 +00001024 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +00001025 image_view=AcquireVirtualCacheView(image,exception);
1026 composite_view=AcquireVirtualCacheView(composite_image,exception);
1027 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001028 for (y=0; y < (ssize_t) composite_image->rows; y++)
1029 {
1030 MagickBooleanType
1031 sync;
1032
1033 register const Quantum
1034 *restrict p;
1035
1036 register Quantum
1037 *restrict q;
1038
1039 register ssize_t
1040 x;
1041
1042 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1043 continue;
1044 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1045 1,exception);
1046 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001047 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001048 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1049 break;
1050 for (x=0; x < (ssize_t) composite_image->columns; x++)
1051 {
1052 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1053 {
cristyed231572011-07-14 02:18:59 +00001054 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001055 continue;
1056 }
1057 /*
1058 Displace the offset.
1059 */
cristy7159f662012-10-28 17:32:43 +00001060 offset.x=(double) (horizontal_scale*(GetPixelRed(composite_image,p)-
1061 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1062 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1063 x : 0);
1064 offset.y=(double) (vertical_scale*(GetPixelGreen(composite_image,p)-
1065 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1066 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1067 y : 0);
cristy4c08aed2011-07-01 19:47:50 +00001068 (void) InterpolatePixelInfo(image,image_view,
1069 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1070 &pixel,exception);
1071 /*
1072 Mask with the 'invalid pixel mask' in alpha channel.
1073 */
cristy7159f662012-10-28 17:32:43 +00001074 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1075 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001076 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001077 p+=GetPixelChannels(composite_image);
1078 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001079 }
1080 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1081 if (sync == MagickFalse)
1082 break;
1083 }
1084 destination_view=DestroyCacheView(destination_view);
1085 composite_view=DestroyCacheView(composite_view);
1086 image_view=DestroyCacheView(image_view);
cristyf661c4d2012-07-28 12:57:17 +00001087 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001088 composite_image=destination_image;
1089 break;
1090 }
1091 case DissolveCompositeOp:
1092 {
cristyfeb3e962012-03-29 17:25:55 +00001093 const char
1094 *value;
1095
cristy4c08aed2011-07-01 19:47:50 +00001096 /*
1097 Geometry arguments to dissolve factors.
1098 */
1099 value=GetImageArtifact(composite_image,"compose:args");
1100 if (value != (char *) NULL)
1101 {
1102 flags=ParseGeometry(value,&geometry_info);
1103 source_dissolve=geometry_info.rho/100.0;
1104 destination_dissolve=1.0;
1105 if ((source_dissolve-MagickEpsilon) < 0.0)
1106 source_dissolve=0.0;
1107 if ((source_dissolve+MagickEpsilon) > 1.0)
1108 {
1109 destination_dissolve=2.0-source_dissolve;
1110 source_dissolve=1.0;
1111 }
1112 if ((flags & SigmaValue) != 0)
1113 destination_dissolve=geometry_info.sigma/100.0;
1114 if ((destination_dissolve-MagickEpsilon) < 0.0)
1115 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001116 /* posible speed up? -- from IMv6 update
1117 clip_to_self=MagickFalse;
1118 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1119 {
1120 destination_dissolve=1.0;
1121 clip_to_self=MagickTrue;
1122 }
1123 */
cristy4c08aed2011-07-01 19:47:50 +00001124 }
1125 break;
1126 }
1127 case BlendCompositeOp:
1128 {
cristyfeb3e962012-03-29 17:25:55 +00001129 const char
1130 *value;
1131
cristy4c08aed2011-07-01 19:47:50 +00001132 value=GetImageArtifact(composite_image,"compose:args");
1133 if (value != (char *) NULL)
1134 {
1135 flags=ParseGeometry(value,&geometry_info);
1136 source_dissolve=geometry_info.rho/100.0;
1137 destination_dissolve=1.0-source_dissolve;
1138 if ((flags & SigmaValue) != 0)
1139 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001140 }
1141 break;
1142 }
1143 case MathematicsCompositeOp:
1144 {
cristyfeb3e962012-03-29 17:25:55 +00001145 const char
1146 *value;
1147
cristy4c08aed2011-07-01 19:47:50 +00001148 /*
1149 Just collect the values from "compose:args", setting.
1150 Unused values are set to zero automagically.
1151
1152 Arguments are normally a comma separated list, so this probably should
1153 be changed to some 'general comma list' parser, (with a minimum
1154 number of values)
1155 */
1156 SetGeometryInfo(&geometry_info);
1157 value=GetImageArtifact(composite_image,"compose:args");
1158 if (value != (char *) NULL)
1159 (void) ParseGeometry(value,&geometry_info);
1160 break;
1161 }
1162 case ModulateCompositeOp:
1163 {
cristyfeb3e962012-03-29 17:25:55 +00001164 const char
1165 *value;
1166
cristy4c08aed2011-07-01 19:47:50 +00001167 /*
cristy7133e642012-08-14 11:04:11 +00001168 Determine the luma and chroma scale.
cristy4c08aed2011-07-01 19:47:50 +00001169 */
1170 value=GetImageArtifact(composite_image,"compose:args");
1171 if (value != (char *) NULL)
1172 {
1173 flags=ParseGeometry(value,&geometry_info);
cristy7133e642012-08-14 11:04:11 +00001174 percent_luma=geometry_info.rho;
cristy4c08aed2011-07-01 19:47:50 +00001175 if ((flags & SigmaValue) != 0)
cristy7133e642012-08-14 11:04:11 +00001176 percent_chroma=geometry_info.sigma;
cristy4c08aed2011-07-01 19:47:50 +00001177 }
1178 break;
1179 }
1180 case ThresholdCompositeOp:
1181 {
cristyfeb3e962012-03-29 17:25:55 +00001182 const char
1183 *value;
1184
cristy4c08aed2011-07-01 19:47:50 +00001185 /*
1186 Determine the amount and threshold.
1187 */
1188 value=GetImageArtifact(composite_image,"compose:args");
1189 if (value != (char *) NULL)
1190 {
1191 flags=ParseGeometry(value,&geometry_info);
1192 amount=geometry_info.rho;
1193 threshold=geometry_info.sigma;
1194 if ((flags & SigmaValue) == 0)
1195 threshold=0.05f;
1196 }
1197 threshold*=QuantumRange;
1198 break;
1199 }
1200 default:
1201 break;
1202 }
cristy4c08aed2011-07-01 19:47:50 +00001203 /*
1204 Composite image.
1205 */
1206 status=MagickTrue;
1207 progress=0;
cristy7159f662012-10-28 17:32:43 +00001208 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy46ff2672012-12-14 15:32:26 +00001209 composite_view=AcquireVirtualCacheView(composite_image,exception);
1210 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001211#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001212 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001213 magick_threads(composite_image,image,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001214#endif
1215 for (y=0; y < (ssize_t) image->rows; y++)
1216 {
1217 const Quantum
1218 *pixels;
1219
cristy7159f662012-10-28 17:32:43 +00001220 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001221 blue,
cristy7133e642012-08-14 11:04:11 +00001222 luma,
cristye4a40472011-12-22 02:56:19 +00001223 green,
cristy4c08aed2011-07-01 19:47:50 +00001224 hue,
cristye4a40472011-12-22 02:56:19 +00001225 red,
cristy7133e642012-08-14 11:04:11 +00001226 chroma;
cristy4c08aed2011-07-01 19:47:50 +00001227
cristyddeeea22012-04-12 01:33:09 +00001228 PixelInfo
1229 destination_pixel,
1230 source_pixel;
1231
cristy4c08aed2011-07-01 19:47:50 +00001232 register const Quantum
1233 *restrict p;
1234
1235 register Quantum
1236 *restrict q;
1237
1238 register ssize_t
1239 x;
1240
1241 if (status == MagickFalse)
1242 continue;
cristyfeb3e962012-03-29 17:25:55 +00001243 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001244 {
1245 if (y < y_offset)
1246 continue;
1247 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1248 continue;
1249 }
1250 /*
1251 If pixels is NULL, y is outside overlay region.
1252 */
1253 pixels=(Quantum *) NULL;
1254 p=(Quantum *) NULL;
1255 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1256 {
1257 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1258 composite_image->columns,1,exception);
1259 if (p == (const Quantum *) NULL)
1260 {
1261 status=MagickFalse;
1262 continue;
1263 }
1264 pixels=p;
1265 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001266 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001267 }
1268 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001269 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001270 {
1271 status=MagickFalse;
1272 continue;
1273 }
cristy4c08aed2011-07-01 19:47:50 +00001274 hue=0.0;
cristy7133e642012-08-14 11:04:11 +00001275 chroma=0.0;
1276 luma=0.0;
cristyddeeea22012-04-12 01:33:09 +00001277 GetPixelInfo(image,&destination_pixel);
1278 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001279 for (x=0; x < (ssize_t) image->columns; x++)
1280 {
cristy17028dc2013-01-24 12:28:39 +00001281 double
1282 gamma;
1283
cristy7159f662012-10-28 17:32:43 +00001284 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001285 alpha,
1286 Da,
1287 Dc,
1288 Dca,
cristye4a40472011-12-22 02:56:19 +00001289 Sa,
1290 Sc,
1291 Sca;
1292
1293 register ssize_t
1294 i;
1295
cristy564a5692012-01-20 23:56:26 +00001296 size_t
1297 channels;
1298
cristyfeb3e962012-03-29 17:25:55 +00001299 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001300 {
1301 if (x < x_offset)
1302 {
cristyed231572011-07-14 02:18:59 +00001303 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001304 continue;
1305 }
1306 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1307 break;
1308 }
cristye4a40472011-12-22 02:56:19 +00001309 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1310 ((x-x_offset) >= (ssize_t) composite_image->columns))
1311 {
1312 Quantum
1313 source[MaxPixelChannels];
1314
1315 /*
1316 Virtual composite:
1317 Sc: source color.
1318 Dc: destination color.
1319 */
1320 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1321 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001322 if (GetPixelMask(image,q) != 0)
1323 {
1324 q+=GetPixelChannels(image);
1325 continue;
1326 }
cristye4a40472011-12-22 02:56:19 +00001327 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1328 {
cristy7159f662012-10-28 17:32:43 +00001329 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001330 pixel;
1331
1332 PixelChannel
1333 channel;
1334
1335 PixelTrait
1336 composite_traits,
1337 traits;
1338
cristycf1296e2012-08-26 23:40:49 +00001339 channel=GetPixelChannelChannel(image,i);
1340 traits=GetPixelChannelTraits(image,channel);
1341 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001342 if ((traits == UndefinedPixelTrait) ||
1343 (composite_traits == UndefinedPixelTrait))
1344 continue;
1345 switch (compose)
1346 {
cristyc8d63672012-01-11 13:03:13 +00001347 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001348 case ChangeMaskCompositeOp:
1349 case CopyAlphaCompositeOp:
1350 case DstAtopCompositeOp:
1351 case DstInCompositeOp:
1352 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001353 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001354 case OutCompositeOp:
1355 case SrcInCompositeOp:
1356 case SrcOutCompositeOp:
1357 {
cristy7159f662012-10-28 17:32:43 +00001358 pixel=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001359 if (channel == AlphaPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001360 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001361 break;
1362 }
1363 case ClearCompositeOp:
1364 case CopyCompositeOp:
1365 case ReplaceCompositeOp:
1366 case SrcCompositeOp:
1367 {
1368 if (channel == AlphaPixelChannel)
1369 {
cristy7159f662012-10-28 17:32:43 +00001370 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001371 break;
1372 }
1373 pixel=0.0;
1374 break;
1375 }
cristy99abff32011-12-24 20:45:16 +00001376 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001377 case DissolveCompositeOp:
1378 {
1379 if (channel == AlphaPixelChannel)
1380 {
1381 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1382 source);
1383 break;
1384 }
cristy7159f662012-10-28 17:32:43 +00001385 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001386 break;
1387 }
1388 default:
1389 {
cristy7159f662012-10-28 17:32:43 +00001390 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001391 break;
1392 }
1393 }
1394 q[i]=ClampToQuantum(pixel);
1395 }
1396 q+=GetPixelChannels(image);
1397 continue;
1398 }
1399 /*
1400 Authentic composite:
1401 Sa: normalized source alpha.
1402 Da: normalized destination alpha.
1403 */
1404 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1405 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001406 switch (compose)
1407 {
cristye4a40472011-12-22 02:56:19 +00001408 case BumpmapCompositeOp:
1409 {
1410 alpha=GetPixelIntensity(composite_image,p)*Sa;
1411 break;
1412 }
cristycdc168f2011-12-21 15:24:39 +00001413 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001414 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001415 case DifferenceCompositeOp:
1416 case DivideDstCompositeOp:
1417 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001418 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001419 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001420 case LinearBurnCompositeOp:
1421 case LinearDodgeCompositeOp:
1422 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001423 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001424 case MinusDstCompositeOp:
1425 case MinusSrcCompositeOp:
1426 case ModulusAddCompositeOp:
1427 case ModulusSubtractCompositeOp:
1428 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001429 case OverlayCompositeOp:
1430 case PegtopLightCompositeOp:
1431 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001432 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001433 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001434 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001435 {
1436 alpha=RoundToUnity(Sa+Da-Sa*Da);
1437 break;
1438 }
1439 case DarkenCompositeOp:
1440 case DstAtopCompositeOp:
1441 case DstInCompositeOp:
1442 case InCompositeOp:
1443 case LightenCompositeOp:
1444 case SrcInCompositeOp:
1445 {
1446 alpha=Sa*Da;
1447 break;
1448 }
1449 case DissolveCompositeOp:
1450 {
1451 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1452 Sa+destination_dissolve*Da;
1453 break;
1454 }
1455 case DstOverCompositeOp:
1456 {
1457 alpha=Da*(-Sa)+Da+Sa;
1458 break;
1459 }
1460 case DstOutCompositeOp:
1461 {
1462 alpha=Da*(1.0-Sa);
1463 break;
1464 }
1465 case OutCompositeOp:
1466 case SrcOutCompositeOp:
1467 {
1468 alpha=Sa*(1.0-Da);
1469 break;
1470 }
1471 case OverCompositeOp:
1472 case SrcOverCompositeOp:
1473 {
1474 alpha=Sa*(-Da)+Sa+Da;
1475 break;
1476 }
cristy99abff32011-12-24 20:45:16 +00001477 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001478 case PlusCompositeOp:
1479 {
1480 alpha=RoundToUnity(Sa+Da);
1481 break;
1482 }
cristy4c08aed2011-07-01 19:47:50 +00001483 case XorCompositeOp:
1484 {
cristye4a40472011-12-22 02:56:19 +00001485 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001486 break;
1487 }
1488 default:
cristye4a40472011-12-22 02:56:19 +00001489 {
1490 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001491 break;
cristye4a40472011-12-22 02:56:19 +00001492 }
cristy4c08aed2011-07-01 19:47:50 +00001493 }
cristy10a6c612012-01-29 21:41:05 +00001494 if (GetPixelMask(image,p) != 0)
1495 {
1496 p+=GetPixelChannels(composite_image);
1497 q+=GetPixelChannels(image);
1498 continue;
1499 }
cristy9d3d2792012-04-14 15:15:19 +00001500 switch (compose)
1501 {
1502 case ColorizeCompositeOp:
1503 case HueCompositeOp:
1504 case LuminizeCompositeOp:
1505 case ModulateCompositeOp:
1506 case SaturateCompositeOp:
1507 {
1508 GetPixelInfoPixel(composite_image,p,&source_pixel);
1509 GetPixelInfoPixel(image,q,&destination_pixel);
1510 break;
1511 }
1512 default:
1513 break;
1514 }
cristye4a40472011-12-22 02:56:19 +00001515 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1516 {
cristy7159f662012-10-28 17:32:43 +00001517 MagickRealType
1518 pixel,
cristy564a5692012-01-20 23:56:26 +00001519 sans;
1520
cristye4a40472011-12-22 02:56:19 +00001521 PixelChannel
1522 channel;
cristye10859a2011-12-18 22:28:59 +00001523
cristye4a40472011-12-22 02:56:19 +00001524 PixelTrait
1525 composite_traits,
1526 traits;
1527
cristycf1296e2012-08-26 23:40:49 +00001528 channel=GetPixelChannelChannel(image,i);
1529 traits=GetPixelChannelTraits(image,channel);
1530 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001531 if (traits == UndefinedPixelTrait)
1532 continue;
cristya7b07912012-01-11 20:01:32 +00001533 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001534 (composite_traits == UndefinedPixelTrait))
1535 continue;
1536 /*
1537 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001538 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001539 */
cristy7159f662012-10-28 17:32:43 +00001540 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
1541 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001542 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001543 {
cristye4a40472011-12-22 02:56:19 +00001544 if (channel != AlphaPixelChannel)
1545 {
1546 /*
1547 Copy channel.
1548 */
1549 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001550 continue;
cristye10859a2011-12-18 22:28:59 +00001551 }
cristye4a40472011-12-22 02:56:19 +00001552 /*
1553 Set alpha channel.
1554 */
cristye10859a2011-12-18 22:28:59 +00001555 switch (compose)
1556 {
cristyc8d63672012-01-11 13:03:13 +00001557 case AlphaCompositeOp:
1558 {
cristya7b07912012-01-11 20:01:32 +00001559 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001560 break;
1561 }
cristye4a40472011-12-22 02:56:19 +00001562 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001563 case CopyBlackCompositeOp:
1564 case CopyBlueCompositeOp:
1565 case CopyCyanCompositeOp:
1566 case CopyGreenCompositeOp:
1567 case CopyMagentaCompositeOp:
1568 case CopyRedCompositeOp:
1569 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001570 case SrcAtopCompositeOp:
1571 case DstCompositeOp:
1572 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001573 {
cristye4a40472011-12-22 02:56:19 +00001574 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001575 break;
1576 }
cristye10859a2011-12-18 22:28:59 +00001577 case ChangeMaskCompositeOp:
1578 {
cristye4a40472011-12-22 02:56:19 +00001579 MagickBooleanType
1580 equivalent;
1581
cristy7159f662012-10-28 17:32:43 +00001582 if (Da > ((MagickRealType) QuantumRange/2.0))
cristy99abff32011-12-24 20:45:16 +00001583 {
cristy7159f662012-10-28 17:32:43 +00001584 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001585 break;
1586 }
cristye4a40472011-12-22 02:56:19 +00001587 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001588 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001589 {
cristy7159f662012-10-28 17:32:43 +00001590 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001591 break;
1592 }
cristy7159f662012-10-28 17:32:43 +00001593 pixel=(MagickRealType) OpaqueAlpha;
cristye4a40472011-12-22 02:56:19 +00001594 break;
1595 }
cristy99abff32011-12-24 20:45:16 +00001596 case ClearCompositeOp:
1597 {
cristy7159f662012-10-28 17:32:43 +00001598 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001599 break;
1600 }
1601 case ColorizeCompositeOp:
1602 case HueCompositeOp:
1603 case LuminizeCompositeOp:
1604 case SaturateCompositeOp:
1605 {
1606 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1607 {
1608 pixel=QuantumRange*Da;
1609 break;
1610 }
1611 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1612 {
1613 pixel=QuantumRange*Sa;
1614 break;
1615 }
1616 if (Sa < Da)
1617 {
1618 pixel=QuantumRange*Da;
1619 break;
1620 }
1621 pixel=QuantumRange*Sa;
1622 break;
1623 }
cristy99abff32011-12-24 20:45:16 +00001624 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001625 {
1626 pixel=QuantumRange*Sa;
cristy8a46d822012-08-28 23:32:39 +00001627 if (composite_image->alpha_trait != BlendPixelTrait)
cristy24d5d722012-05-17 12:27:27 +00001628 pixel=GetPixelIntensity(composite_image,p);
1629 break;
1630 }
1631 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001632 case DisplaceCompositeOp:
1633 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001634 case DstAtopCompositeOp:
1635 case ReplaceCompositeOp:
1636 case SrcCompositeOp:
1637 {
1638 pixel=QuantumRange*Sa;
1639 break;
1640 }
1641 case DarkenIntensityCompositeOp:
1642 {
cristy99abff32011-12-24 20:45:16 +00001643 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1644 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001645 break;
1646 }
cristy98621462011-12-31 22:31:11 +00001647 case IntensityCompositeOp:
1648 {
cristyf13c5942012-08-08 23:50:11 +00001649 pixel=GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001650 break;
1651 }
cristye4a40472011-12-22 02:56:19 +00001652 case LightenIntensityCompositeOp:
1653 {
1654 pixel=Sa*GetPixelIntensity(composite_image,p) >
1655 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001656 break;
1657 }
cristy99abff32011-12-24 20:45:16 +00001658 case ModulateCompositeOp:
1659 {
1660 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1661 {
1662 pixel=QuantumRange*Da;
1663 break;
1664 }
1665 pixel=QuantumRange*Da;
1666 break;
1667 }
cristye10859a2011-12-18 22:28:59 +00001668 default:
1669 {
cristye4a40472011-12-22 02:56:19 +00001670 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001671 break;
1672 }
1673 }
cristye4a40472011-12-22 02:56:19 +00001674 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001675 continue;
1676 }
1677 /*
cristy99abff32011-12-24 20:45:16 +00001678 Porter-Duff compositions:
1679 Sca: source normalized color multiplied by alpha.
1680 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001681 */
cristy99abff32011-12-24 20:45:16 +00001682 Sca=QuantumScale*Sa*Sc;
1683 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001684 switch (compose)
1685 {
cristye10859a2011-12-18 22:28:59 +00001686 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001687 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001688 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001689 {
cristy99abff32011-12-24 20:45:16 +00001690 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001691 break;
1692 }
1693 default:
1694 break;
1695 }
cristy3e3ec3a2012-11-03 23:11:06 +00001696 gamma=PerceptibleReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001697 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001698 switch (compose)
1699 {
cristya7b07912012-01-11 20:01:32 +00001700 case AlphaCompositeOp:
1701 {
1702 pixel=QuantumRange*Sa;
1703 break;
1704 }
cristye4a40472011-12-22 02:56:19 +00001705 case AtopCompositeOp:
1706 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001707 {
cristye4a40472011-12-22 02:56:19 +00001708 pixel=Sc*Sa+Dc*(1.0-Sa);
1709 break;
cristye10859a2011-12-18 22:28:59 +00001710 }
cristye4a40472011-12-22 02:56:19 +00001711 case BlendCompositeOp:
1712 {
1713 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1714 break;
1715 }
1716 case BlurCompositeOp:
1717 case DisplaceCompositeOp:
1718 case DistortCompositeOp:
1719 case CopyCompositeOp:
1720 case ReplaceCompositeOp:
1721 case SrcCompositeOp:
1722 {
1723 pixel=Sc;
1724 break;
1725 }
1726 case BumpmapCompositeOp:
1727 {
cristy99abff32011-12-24 20:45:16 +00001728 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001729 {
1730 pixel=Dc;
1731 break;
1732 }
1733 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1734 break;
1735 }
cristy99abff32011-12-24 20:45:16 +00001736 case ChangeMaskCompositeOp:
1737 {
1738 pixel=Dc;
1739 break;
1740 }
1741 case ClearCompositeOp:
1742 {
1743 pixel=0.0;
1744 break;
1745 }
cristye4a40472011-12-22 02:56:19 +00001746 case ColorBurnCompositeOp:
1747 {
1748 /*
1749 Refer to the March 2009 SVG specification.
1750 */
1751 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1752 {
cristy99abff32011-12-24 20:45:16 +00001753 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001754 break;
1755 }
1756 if (Sca < MagickEpsilon)
1757 {
cristy99abff32011-12-24 20:45:16 +00001758 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001759 break;
1760 }
cristy99abff32011-12-24 20:45:16 +00001761 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1762 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001763 break;
1764 }
1765 case ColorDodgeCompositeOp:
1766 {
1767 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1768 {
cristy99abff32011-12-24 20:45:16 +00001769 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001770 break;
1771 }
1772 if (fabs(Sca-Sa) < MagickEpsilon)
1773 {
cristy99abff32011-12-24 20:45:16 +00001774 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001775 break;
1776 }
cristy99abff32011-12-24 20:45:16 +00001777 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001778 (1.0-Sa));
1779 break;
1780 }
1781 case ColorizeCompositeOp:
1782 {
cristy99abff32011-12-24 20:45:16 +00001783 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001784 {
1785 pixel=Dc;
1786 break;
1787 }
cristy99abff32011-12-24 20:45:16 +00001788 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001789 {
1790 pixel=Sc;
1791 break;
1792 }
cristy7133e642012-08-14 11:04:11 +00001793 CompositeHCL(destination_pixel.red,destination_pixel.green,
1794 destination_pixel.blue,&sans,&sans,&luma);
1795 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1796 &hue,&chroma,&sans);
1797 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001798 switch (channel)
1799 {
1800 case RedPixelChannel: pixel=red; break;
1801 case GreenPixelChannel: pixel=green; break;
1802 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001803 default: pixel=Dc; break;
1804 }
1805 break;
1806 }
cristye4a40472011-12-22 02:56:19 +00001807 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001808 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001809 {
cristy24d5d722012-05-17 12:27:27 +00001810 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001811 break;
1812 }
1813 case CopyBlackCompositeOp:
1814 {
cristyd197cbb2012-01-13 02:14:12 +00001815 if (channel == BlackPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001816 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001817 break;
1818 }
1819 case CopyBlueCompositeOp:
1820 case CopyYellowCompositeOp:
1821 {
cristyd197cbb2012-01-13 02:14:12 +00001822 if (channel == BluePixelChannel)
cristy7159f662012-10-28 17:32:43 +00001823 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001824 break;
1825 }
1826 case CopyGreenCompositeOp:
1827 case CopyMagentaCompositeOp:
1828 {
cristyd197cbb2012-01-13 02:14:12 +00001829 if (channel == GreenPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001830 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001831 break;
1832 }
1833 case CopyRedCompositeOp:
1834 case CopyCyanCompositeOp:
1835 {
cristyd197cbb2012-01-13 02:14:12 +00001836 if (channel == RedPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001837 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001838 break;
1839 }
cristy99abff32011-12-24 20:45:16 +00001840 case DarkenCompositeOp:
1841 {
1842 /*
1843 Darken is equivalent to a 'Minimum' method
1844 OR a greyscale version of a binary 'Or'
1845 OR the 'Intersection' of pixel sets.
1846 */
1847 if (Sc < Dc)
1848 {
1849 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1850 break;
1851 }
1852 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1853 break;
1854 }
cristye4a40472011-12-22 02:56:19 +00001855 case DarkenIntensityCompositeOp:
1856 {
cristy99abff32011-12-24 20:45:16 +00001857 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1858 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001859 break;
1860 }
1861 case DifferenceCompositeOp:
1862 {
1863 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1864 break;
1865 }
1866 case DissolveCompositeOp:
1867 {
1868 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1869 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1870 break;
1871 }
1872 case DivideDstCompositeOp:
1873 {
1874 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1875 {
cristy99abff32011-12-24 20:45:16 +00001876 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001877 break;
1878 }
1879 if (fabs(Dca) < MagickEpsilon)
1880 {
cristy99abff32011-12-24 20:45:16 +00001881 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001882 break;
1883 }
cristy99abff32011-12-24 20:45:16 +00001884 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001885 break;
1886 }
1887 case DivideSrcCompositeOp:
1888 {
1889 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1890 {
cristy99abff32011-12-24 20:45:16 +00001891 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001892 break;
1893 }
1894 if (fabs(Sca) < MagickEpsilon)
1895 {
cristy99abff32011-12-24 20:45:16 +00001896 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001897 break;
1898 }
cristy99abff32011-12-24 20:45:16 +00001899 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001900 break;
1901 }
1902 case DstAtopCompositeOp:
1903 {
1904 pixel=Dc*Da+Sc*(1.0-Da);
1905 break;
1906 }
1907 case DstCompositeOp:
1908 case NoCompositeOp:
1909 {
1910 pixel=Dc;
1911 break;
1912 }
1913 case DstInCompositeOp:
1914 {
1915 pixel=gamma*(Sa*Dc*Sa);
1916 break;
1917 }
1918 case DstOutCompositeOp:
1919 {
1920 pixel=gamma*(Da*Dc*(1.0-Sa));
1921 break;
1922 }
1923 case DstOverCompositeOp:
1924 {
1925 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1926 break;
1927 }
1928 case ExclusionCompositeOp:
1929 {
cristy99abff32011-12-24 20:45:16 +00001930 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1931 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001932 break;
1933 }
1934 case HardLightCompositeOp:
1935 {
1936 if ((2.0*Sca) < Sa)
1937 {
cristy99abff32011-12-24 20:45:16 +00001938 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001939 (1.0-Sa));
1940 break;
1941 }
cristy99abff32011-12-24 20:45:16 +00001942 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001943 Dca*(1.0-Sa));
1944 break;
1945 }
1946 case HueCompositeOp:
1947 {
cristy99abff32011-12-24 20:45:16 +00001948 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001949 {
1950 pixel=Dc;
1951 break;
1952 }
cristy99abff32011-12-24 20:45:16 +00001953 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001954 {
1955 pixel=Sc;
1956 break;
1957 }
cristy7133e642012-08-14 11:04:11 +00001958 CompositeHCL(destination_pixel.red,destination_pixel.green,
1959 destination_pixel.blue,&hue,&chroma,&luma);
1960 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001961 &hue,&sans,&sans);
cristy7133e642012-08-14 11:04:11 +00001962 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001963 switch (channel)
1964 {
1965 case RedPixelChannel: pixel=red; break;
1966 case GreenPixelChannel: pixel=green; break;
1967 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001968 default: pixel=Dc; break;
1969 }
1970 break;
1971 }
1972 case InCompositeOp:
1973 case SrcInCompositeOp:
1974 {
1975 pixel=gamma*(Da*Sc*Da);
1976 break;
1977 }
cristy99abff32011-12-24 20:45:16 +00001978 case LinearBurnCompositeOp:
1979 {
1980 /*
1981 LinearBurn: as defined by Abode Photoshop, according to
1982 http://www.simplefilter.de/en/basics/mixmods.html is:
1983
1984 f(Sc,Dc) = Sc + Dc - 1
1985 */
1986 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1987 break;
1988 }
1989 case LinearDodgeCompositeOp:
1990 {
1991 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1992 break;
1993 }
1994 case LinearLightCompositeOp:
1995 {
1996 /*
1997 LinearLight: as defined by Abode Photoshop, according to
1998 http://www.simplefilter.de/en/basics/mixmods.html is:
1999
2000 f(Sc,Dc) = Dc + 2*Sc - 1
2001 */
2002 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
2003 break;
2004 }
2005 case LightenCompositeOp:
2006 {
2007 if (Sc > Dc)
2008 {
cristy24d5d722012-05-17 12:27:27 +00002009 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00002010 break;
2011 }
cristy24d5d722012-05-17 12:27:27 +00002012 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00002013 break;
2014 }
cristye4a40472011-12-22 02:56:19 +00002015 case LightenIntensityCompositeOp:
2016 {
2017 /*
2018 Lighten is equivalent to a 'Maximum' method
2019 OR a greyscale version of a binary 'And'
2020 OR the 'Union' of pixel sets.
2021 */
2022 pixel=Sa*GetPixelIntensity(composite_image,p) >
2023 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2024 break;
2025 }
cristye4a40472011-12-22 02:56:19 +00002026 case LuminizeCompositeOp:
2027 {
cristy99abff32011-12-24 20:45:16 +00002028 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002029 {
2030 pixel=Dc;
2031 break;
2032 }
cristy99abff32011-12-24 20:45:16 +00002033 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002034 {
2035 pixel=Sc;
2036 break;
2037 }
cristy7133e642012-08-14 11:04:11 +00002038 CompositeHCL(destination_pixel.red,destination_pixel.green,
2039 destination_pixel.blue,&hue,&chroma,&luma);
2040 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2041 &sans,&sans,&luma);
2042 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002043 switch (channel)
2044 {
2045 case RedPixelChannel: pixel=red; break;
2046 case GreenPixelChannel: pixel=green; break;
2047 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002048 default: pixel=Dc; break;
2049 }
2050 break;
2051 }
2052 case MathematicsCompositeOp:
2053 {
2054 /*
2055 'Mathematics' a free form user control mathematical composition
2056 is defined as...
2057
2058 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2059
2060 Where the arguments A,B,C,D are (currently) passed to composite
2061 as a command separated 'geometry' string in "compose:args" image
2062 artifact.
2063
2064 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2065
2066 Applying the SVG transparency formula (see above), we get...
2067
2068 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2069
2070 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2071 Dca*(1.0-Sa)
2072 */
2073 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2074 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2075 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2076 break;
2077 }
2078 case MinusDstCompositeOp:
2079 {
2080 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2081 break;
2082 }
2083 case MinusSrcCompositeOp:
2084 {
2085 /*
2086 Minus source from destination.
2087
2088 f(Sc,Dc) = Sc - Dc
2089 */
cristy99abff32011-12-24 20:45:16 +00002090 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002091 break;
2092 }
2093 case ModulateCompositeOp:
2094 {
2095 ssize_t
2096 offset;
2097
cristy99abff32011-12-24 20:45:16 +00002098 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002099 {
2100 pixel=Dc;
2101 break;
2102 }
2103 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2104 if (offset == 0)
2105 {
2106 pixel=Dc;
2107 break;
2108 }
cristy7133e642012-08-14 11:04:11 +00002109 CompositeHCL(destination_pixel.red,destination_pixel.green,
2110 destination_pixel.blue,&hue,&chroma,&luma);
2111 luma+=(0.01*percent_luma*offset)/midpoint;
2112 chroma*=0.01*percent_chroma;
2113 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002114 switch (channel)
2115 {
2116 case RedPixelChannel: pixel=red; break;
2117 case GreenPixelChannel: pixel=green; break;
2118 case BluePixelChannel: pixel=blue; break;
2119 default: pixel=Dc; break;
2120 }
2121 break;
2122 }
2123 case ModulusAddCompositeOp:
2124 {
2125 pixel=Sc+Dc;
2126 if (pixel > QuantumRange)
2127 pixel-=(QuantumRange+1.0);
2128 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2129 break;
2130 }
2131 case ModulusSubtractCompositeOp:
2132 {
cristy99abff32011-12-24 20:45:16 +00002133 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002134 if (pixel < 0.0)
2135 pixel+=(QuantumRange+1.0);
2136 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2137 break;
2138 }
2139 case MultiplyCompositeOp:
2140 {
cristy99abff32011-12-24 20:45:16 +00002141 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002142 break;
2143 }
2144 case OutCompositeOp:
2145 case SrcOutCompositeOp:
2146 {
2147 pixel=gamma*(Sa*Sc*(1.0-Da));
2148 break;
2149 }
2150 case OverCompositeOp:
2151 case SrcOverCompositeOp:
2152 {
cristy99abff32011-12-24 20:45:16 +00002153 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002154 break;
2155 }
2156 case OverlayCompositeOp:
2157 {
2158 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002159 {
2160 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2161 (1.0-Da));
2162 break;
2163 }
2164 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2165 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002166 break;
2167 }
2168 case PegtopLightCompositeOp:
2169 {
2170 /*
2171 PegTop: A Soft-Light alternative: A continuous version of the
2172 Softlight function, producing very similar results.
2173
2174 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2175
2176 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2177 */
2178 if (fabs(Da) < MagickEpsilon)
2179 {
cristy99abff32011-12-24 20:45:16 +00002180 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002181 break;
2182 }
cristy99abff32011-12-24 20:45:16 +00002183 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2184 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002185 break;
2186 }
2187 case PinLightCompositeOp:
2188 {
2189 /*
2190 PinLight: A Photoshop 7 composition method
2191 http://www.simplefilter.de/en/basics/mixmods.html
2192
2193 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2194 */
2195 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2196 {
cristy99abff32011-12-24 20:45:16 +00002197 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002198 break;
2199 }
2200 if ((Dca*Sa) > (2.0*Sca*Da))
2201 {
cristy99abff32011-12-24 20:45:16 +00002202 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002203 break;
2204 }
cristy99abff32011-12-24 20:45:16 +00002205 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002206 break;
2207 }
2208 case PlusCompositeOp:
2209 {
cristy24d5d722012-05-17 12:27:27 +00002210 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002211 break;
2212 }
2213 case SaturateCompositeOp:
2214 {
cristy99abff32011-12-24 20:45:16 +00002215 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002216 {
2217 pixel=Dc;
2218 break;
2219 }
cristy99abff32011-12-24 20:45:16 +00002220 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002221 {
2222 pixel=Sc;
2223 break;
2224 }
cristy7133e642012-08-14 11:04:11 +00002225 CompositeHCL(destination_pixel.red,destination_pixel.green,
2226 destination_pixel.blue,&hue,&chroma,&luma);
2227 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2228 &sans,&chroma,&sans);
2229 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002230 switch (channel)
2231 {
2232 case RedPixelChannel: pixel=red; break;
2233 case GreenPixelChannel: pixel=green; break;
2234 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002235 default: pixel=Dc; break;
2236 }
2237 break;
2238 }
2239 case ScreenCompositeOp:
2240 {
2241 /*
2242 Screen: a negated multiply:
2243
2244 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2245 */
cristy99abff32011-12-24 20:45:16 +00002246 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002247 break;
2248 }
2249 case SoftLightCompositeOp:
2250 {
2251 /*
2252 Refer to the March 2009 SVG specification.
2253 */
2254 if ((2.0*Sca) < Sa)
2255 {
cristy99abff32011-12-24 20:45:16 +00002256 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2257 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002258 break;
2259 }
2260 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2261 {
cristy99abff32011-12-24 20:45:16 +00002262 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2263 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2264 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002265 break;
2266 }
cristy99abff32011-12-24 20:45:16 +00002267 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2268 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002269 break;
2270 }
2271 case ThresholdCompositeOp:
2272 {
cristy7159f662012-10-28 17:32:43 +00002273 MagickRealType
cristye4a40472011-12-22 02:56:19 +00002274 delta;
2275
2276 delta=Sc-Dc;
cristy7159f662012-10-28 17:32:43 +00002277 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002278 {
2279 pixel=gamma*Dc;
2280 break;
2281 }
2282 pixel=gamma*(Dc+delta*amount);
2283 break;
2284 }
2285 case VividLightCompositeOp:
2286 {
2287 /*
2288 VividLight: A Photoshop 7 composition method. See
2289 http://www.simplefilter.de/en/basics/mixmods.html.
2290
2291 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2292 */
2293 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2294 {
cristy99abff32011-12-24 20:45:16 +00002295 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002296 break;
2297 }
2298 if ((2.0*Sca) <= Sa)
2299 {
cristy99abff32011-12-24 20:45:16 +00002300 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2301 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002302 break;
2303 }
cristy99abff32011-12-24 20:45:16 +00002304 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2305 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002306 break;
2307 }
2308 case XorCompositeOp:
2309 {
cristy99abff32011-12-24 20:45:16 +00002310 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002311 break;
2312 }
2313 default:
2314 {
2315 pixel=Sc;
2316 break;
2317 }
2318 }
2319 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002320 }
cristyed231572011-07-14 02:18:59 +00002321 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002322 channels=GetPixelChannels(composite_image);
2323 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002324 p=pixels;
cristyed231572011-07-14 02:18:59 +00002325 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002326 }
2327 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2328 status=MagickFalse;
2329 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2330 {
2331 MagickBooleanType
2332 proceed;
2333
2334#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002335 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002336#endif
2337 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2338 image->rows);
2339 if (proceed == MagickFalse)
2340 status=MagickFalse;
2341 }
2342 }
2343 composite_view=DestroyCacheView(composite_view);
2344 image_view=DestroyCacheView(image_view);
2345 if (destination_image != (Image * ) NULL)
2346 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002347 else
2348 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00002349 return(status);
2350}
2351
2352/*
2353%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2354% %
2355% %
2356% %
2357% T e x t u r e I m a g e %
2358% %
2359% %
2360% %
2361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2362%
2363% TextureImage() repeatedly tiles the texture image across and down the image
2364% canvas.
2365%
2366% The format of the TextureImage method is:
2367%
cristy30d8c942012-02-07 13:44:59 +00002368% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002369% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002370%
2371% A description of each parameter follows:
2372%
2373% o image: the image.
2374%
cristye6178502011-12-23 17:02:29 +00002375% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002376%
2377*/
cristy30d8c942012-02-07 13:44:59 +00002378MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2379 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002380{
2381#define TextureImageTag "Texture/Image"
2382
2383 CacheView
2384 *image_view,
2385 *texture_view;
2386
cristy30d8c942012-02-07 13:44:59 +00002387 Image
2388 *texture_image;
2389
cristy4c08aed2011-07-01 19:47:50 +00002390 MagickBooleanType
2391 status;
2392
2393 ssize_t
2394 y;
2395
2396 assert(image != (Image *) NULL);
2397 if (image->debug != MagickFalse)
2398 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2399 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002400 if (texture == (const Image *) NULL)
2401 return(MagickFalse);
2402 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2403 return(MagickFalse);
2404 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002405 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002406 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002407 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002408 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2409 exception);
cristy4c08aed2011-07-01 19:47:50 +00002410 status=MagickTrue;
2411 if ((image->compose != CopyCompositeOp) &&
cristy8a46d822012-08-28 23:32:39 +00002412 ((image->compose != OverCompositeOp) || (image->alpha_trait == BlendPixelTrait) ||
2413 (texture_image->alpha_trait == BlendPixelTrait)))
cristy4c08aed2011-07-01 19:47:50 +00002414 {
2415 /*
2416 Tile texture onto the image background.
2417 */
cristye6178502011-12-23 17:02:29 +00002418 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002419 {
2420 register ssize_t
2421 x;
2422
2423 if (status == MagickFalse)
2424 continue;
cristye6178502011-12-23 17:02:29 +00002425 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002426 {
2427 MagickBooleanType
2428 thread_status;
2429
cristyfeb3e962012-03-29 17:25:55 +00002430 thread_status=CompositeImage(image,texture_image,image->compose,
2431 MagickFalse,x+texture_image->tile_offset.x,y+
2432 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002433 if (thread_status == MagickFalse)
2434 {
2435 status=thread_status;
2436 break;
2437 }
2438 }
2439 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2440 {
2441 MagickBooleanType
2442 proceed;
2443
cristy4c08aed2011-07-01 19:47:50 +00002444 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2445 y,image->rows);
2446 if (proceed == MagickFalse)
2447 status=MagickFalse;
2448 }
2449 }
2450 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2451 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002452 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002453 return(status);
2454 }
2455 /*
2456 Tile texture onto the image background (optimized).
2457 */
2458 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00002459 texture_view=AcquireVirtualCacheView(texture_image,exception);
2460 image_view=AcquireAuthenticCacheView(image,exception);
cristye8914392012-12-16 21:16:11 +00002461#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00002462 #pragma omp parallel for schedule(static,4) shared(status) \
2463 magick_threads(texture_image,image,1,1)
cristye8914392012-12-16 21:16:11 +00002464#endif
cristy4c08aed2011-07-01 19:47:50 +00002465 for (y=0; y < (ssize_t) image->rows; y++)
2466 {
2467 MagickBooleanType
2468 sync;
2469
2470 register const Quantum
2471 *p,
2472 *pixels;
2473
2474 register ssize_t
2475 x;
2476
2477 register Quantum
2478 *q;
2479
2480 size_t
2481 width;
2482
2483 if (status == MagickFalse)
2484 continue;
cristye6178502011-12-23 17:02:29 +00002485 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2486 (y+texture_image->tile_offset.y) % texture_image->rows,
2487 texture_image->columns,1,exception);
2488 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002489 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2490 {
2491 status=MagickFalse;
2492 continue;
2493 }
cristye6178502011-12-23 17:02:29 +00002494 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002495 {
2496 register ssize_t
cristye6178502011-12-23 17:02:29 +00002497 j;
cristy4c08aed2011-07-01 19:47:50 +00002498
2499 p=pixels;
cristye6178502011-12-23 17:02:29 +00002500 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002501 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2502 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002503 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002504 {
cristye6178502011-12-23 17:02:29 +00002505 register ssize_t
2506 i;
2507
cristy10a6c612012-01-29 21:41:05 +00002508 if (GetPixelMask(image,p) != 0)
2509 {
2510 p+=GetPixelChannels(texture_image);
2511 q+=GetPixelChannels(image);
2512 continue;
2513 }
cristye6178502011-12-23 17:02:29 +00002514 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2515 {
2516 PixelChannel
2517 channel;
2518
2519 PixelTrait
2520 texture_traits,
2521 traits;
2522
cristycf1296e2012-08-26 23:40:49 +00002523 channel=GetPixelChannelChannel(texture_image,i);
2524 texture_traits=GetPixelChannelTraits(texture_image,channel);
2525 traits=GetPixelChannelTraits(image,channel);
cristye6178502011-12-23 17:02:29 +00002526 if ((traits == UndefinedPixelTrait) ||
2527 (texture_traits == UndefinedPixelTrait))
2528 continue;
2529 SetPixelChannel(image,channel,p[i],q);
2530 }
2531 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002532 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002533 }
2534 }
2535 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2536 if (sync == MagickFalse)
2537 status=MagickFalse;
2538 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2539 {
2540 MagickBooleanType
2541 proceed;
2542
cristy4c08aed2011-07-01 19:47:50 +00002543 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2544 image->rows);
2545 if (proceed == MagickFalse)
2546 status=MagickFalse;
2547 }
2548 }
2549 texture_view=DestroyCacheView(texture_view);
2550 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002551 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002552 return(status);
2553}