blob: 40dc59c2d2268d96bbce0647910b6d673cd65503 [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);
cristy60dc90c2013-02-09 18:33:21 +0000615 if ((image->alpha_trait == BlendPixelTrait) &&
616 (composite_image->alpha_trait != BlendPixelTrait))
617 SetImageAlphaChannel(composite_image,SetAlphaChannel,exception);
cristye4a40472011-12-22 02:56:19 +0000618 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
619 {
cristyfeb3e962012-03-29 17:25:55 +0000620 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
621 y_offset,exception);
cristya865ccd2012-07-28 00:33:10 +0000622 composite_image=DestroyImage(composite_image);
cristye4a40472011-12-22 02:56:19 +0000623 return(status);
624 }
cristy4c08aed2011-07-01 19:47:50 +0000625 destination_image=(Image *) NULL;
626 amount=0.5;
627 destination_dissolve=1.0;
cristy7133e642012-08-14 11:04:11 +0000628 percent_luma=100.0;
629 percent_chroma=100.0;
cristy4c08aed2011-07-01 19:47:50 +0000630 source_dissolve=1.0;
631 threshold=0.05f;
632 switch (compose)
633 {
cristy4c08aed2011-07-01 19:47:50 +0000634 case CopyCompositeOp:
635 {
636 if ((x_offset < 0) || (y_offset < 0))
637 break;
638 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
639 break;
640 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
641 break;
642 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +0000643 composite_view=AcquireVirtualCacheView(composite_image,exception);
644 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000645#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000646 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +0000647 magick_threads(composite_image,image,composite_image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000648#endif
649 for (y=0; y < (ssize_t) composite_image->rows; y++)
650 {
651 MagickBooleanType
652 sync;
653
654 register const Quantum
655 *p;
656
657 register Quantum
658 *q;
659
660 register ssize_t
661 x;
662
663 if (status == MagickFalse)
664 continue;
665 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
666 1,exception);
667 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
668 composite_image->columns,1,exception);
669 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
670 {
671 status=MagickFalse;
672 continue;
673 }
674 for (x=0; x < (ssize_t) composite_image->columns; x++)
675 {
cristybdecccc2011-12-24 22:52:16 +0000676 register ssize_t
677 i;
678
cristy665e18f2012-05-17 12:39:54 +0000679 if (GetPixelMask(composite_image,p) != 0)
cristy10a6c612012-01-29 21:41:05 +0000680 {
681 p+=GetPixelChannels(composite_image);
682 q+=GetPixelChannels(image);
683 continue;
684 }
cristybdecccc2011-12-24 22:52:16 +0000685 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
686 {
687 PixelChannel
688 channel;
689
690 PixelTrait
691 composite_traits,
692 traits;
693
cristycf1296e2012-08-26 23:40:49 +0000694 channel=GetPixelChannelChannel(composite_image,i);
695 composite_traits=GetPixelChannelTraits(composite_image,channel);
696 traits=GetPixelChannelTraits(image,channel);
cristybdecccc2011-12-24 22:52:16 +0000697 if ((traits == UndefinedPixelTrait) ||
698 (composite_traits == UndefinedPixelTrait))
699 continue;
700 SetPixelChannel(image,channel,p[i],q);
701 }
cristyed231572011-07-14 02:18:59 +0000702 p+=GetPixelChannels(composite_image);
703 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000704 }
705 sync=SyncCacheViewAuthenticPixels(image_view,exception);
706 if (sync == MagickFalse)
707 status=MagickFalse;
708 if (image->progress_monitor != (MagickProgressMonitor) NULL)
709 {
710 MagickBooleanType
711 proceed;
712
713#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000714 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000715#endif
716 proceed=SetImageProgress(image,CompositeImageTag,
717 (MagickOffsetType) y,image->rows);
718 if (proceed == MagickFalse)
719 status=MagickFalse;
720 }
721 }
722 composite_view=DestroyCacheView(composite_view);
723 image_view=DestroyCacheView(image_view);
cristy44886b92012-07-28 13:07:09 +0000724 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000725 return(status);
726 }
cristye4a40472011-12-22 02:56:19 +0000727 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000728 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000729 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000730 {
731 /*
732 Modify destination outside the overlaid region and require an alpha
733 channel to exist, to add transparency.
734 */
cristy8a46d822012-08-28 23:32:39 +0000735 if (image->alpha_trait != BlendPixelTrait)
cristy42c41de2012-05-05 18:36:31 +0000736 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000737 break;
738 }
739 case BlurCompositeOp:
740 {
741 CacheView
742 *composite_view,
743 *destination_view;
744
cristyfeb3e962012-03-29 17:25:55 +0000745 const char
746 *value;
747
cristy7159f662012-10-28 17:32:43 +0000748 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000749 angle_range,
750 angle_start,
751 height,
752 width;
753
cristybce4f4a2012-10-14 14:57:47 +0000754 PixelInfo
755 pixel;
756
cristy4c08aed2011-07-01 19:47:50 +0000757 ResampleFilter
758 *resample_filter;
759
760 SegmentInfo
761 blur;
762
763 /*
anthony9cb63cc2012-04-25 06:10:49 +0000764 Blur Image by resampling.
765
cristy4c08aed2011-07-01 19:47:50 +0000766 Blur Image dictated by an overlay gradient map: X = red_channel;
767 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
768 */
769 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000770 exception);
cristy4c08aed2011-07-01 19:47:50 +0000771 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000772 {
773 composite_image=DestroyImage(composite_image);
774 return(MagickFalse);
775 }
cristy4c08aed2011-07-01 19:47:50 +0000776 /*
anthony9cb63cc2012-04-25 06:10:49 +0000777 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000778 */
779 SetGeometryInfo(&geometry_info);
780 flags=NoValue;
781 value=GetImageArtifact(composite_image,"compose:args");
782 if (value != (char *) NULL)
783 flags=ParseGeometry(value,&geometry_info);
anthonyd2923912012-04-23 13:06:53 +0000784 if ((flags & WidthValue) == 0 ) {
785 (void) ThrowMagickException(exception,GetMagickModule(),
786 OptionWarning,"InvalidSetting","'%s' '%s'",
anthony9cb63cc2012-04-25 06:10:49 +0000787 "compose:args",value);
cristy44886b92012-07-28 13:07:09 +0000788 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000789 destination_image=DestroyImage(destination_image);
790 return(MagickFalse);
791 }
anthony9cb63cc2012-04-25 06:10:49 +0000792 /*
793 Users input sigma now needs to be converted to the EWA ellipse size.
794 The filter defaults to a sigma of 0.5 so to make this match the
795 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000796 */
797 width=height=geometry_info.rho*2.0;
798 if ((flags & HeightValue) != 0 )
799 height=geometry_info.sigma*2.0;
cristy7159f662012-10-28 17:32:43 +0000800 /*
801 Default the unrotated ellipse width and height axis vectors.
802 */
anthonyd2923912012-04-23 13:06:53 +0000803 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000804 blur.x2=0.0;
805 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000806 blur.y2=height;
807 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000808 if ((flags & XValue) != 0 )
809 {
cristy7159f662012-10-28 17:32:43 +0000810 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000811 angle;
812
813 angle=DegreesToRadians(geometry_info.xi);
814 blur.x1=width*cos(angle);
815 blur.x2=width*sin(angle);
816 blur.y1=(-height*sin(angle));
817 blur.y2=height*cos(angle);
818 }
anthonyd2923912012-04-23 13:06:53 +0000819 /* Otherwise lets set a angle range and calculate in the loop */
820 angle_start=0.0;
821 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000822 if ((flags & YValue) != 0 )
823 {
824 angle_start=DegreesToRadians(geometry_info.xi);
825 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
826 }
827 /*
anthony9cb63cc2012-04-25 06:10:49 +0000828 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000829
anthony9cb63cc2012-04-25 06:10:49 +0000830 As the minimum ellipse radius of support*1.0 the EWA algorithm
831 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
832 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000833
anthony9cb63cc2012-04-25 06:10:49 +0000834 The solution (as well as the problem of preventing any user
835 expert filter settings, is to set our own user settings, then
836 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000837 */
cristy8a11cb12011-10-19 23:53:34 +0000838 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000839 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000840
841 /* do the variable blurring of each pixel in image */
842 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +0000843 composite_view=AcquireVirtualCacheView(composite_image,exception);
844 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000845 for (y=0; y < (ssize_t) composite_image->rows; y++)
846 {
847 MagickBooleanType
848 sync;
849
850 register const Quantum
851 *restrict p;
852
853 register Quantum
854 *restrict q;
855
856 register ssize_t
857 x;
858
859 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
860 continue;
861 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
862 1,exception);
863 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000864 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000865 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
866 break;
867 for (x=0; x < (ssize_t) composite_image->columns; x++)
868 {
869 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
870 {
cristyed231572011-07-14 02:18:59 +0000871 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000872 continue;
873 }
874 if (fabs(angle_range) > MagickEpsilon)
875 {
cristy7159f662012-10-28 17:32:43 +0000876 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000877 angle;
878
879 angle=angle_start+angle_range*QuantumScale*
880 GetPixelBlue(composite_image,p);
881 blur.x1=width*cos(angle);
882 blur.x2=width*sin(angle);
883 blur.y1=(-height*sin(angle));
884 blur.y2=height*cos(angle);
885 }
anthonyd2923912012-04-23 13:06:53 +0000886#if 0
887 if ( x == 10 && y == 60 ) {
cristy7159f662012-10-28 17:32:43 +0000888 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
889 blur.x2,blur.y1, blur.y2);
890 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
891 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
anthonyd2923912012-04-23 13:06:53 +0000892#endif
893 ScaleResampleFilter(resample_filter,
894 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
895 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
896 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
897 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000898 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000899 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000900 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000901 p+=GetPixelChannels(composite_image);
902 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000903 }
904 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
905 if (sync == MagickFalse)
906 break;
907 }
908 resample_filter=DestroyResampleFilter(resample_filter);
909 composite_view=DestroyCacheView(composite_view);
910 destination_view=DestroyCacheView(destination_view);
cristyf661c4d2012-07-28 12:57:17 +0000911 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000912 composite_image=destination_image;
913 break;
914 }
915 case DisplaceCompositeOp:
916 case DistortCompositeOp:
917 {
918 CacheView
919 *composite_view,
920 *destination_view,
921 *image_view;
922
cristyfeb3e962012-03-29 17:25:55 +0000923 const char
924 *value;
925
cristy4c08aed2011-07-01 19:47:50 +0000926 PixelInfo
927 pixel;
928
cristy7159f662012-10-28 17:32:43 +0000929 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000930 horizontal_scale,
931 vertical_scale;
932
933 PointInfo
934 center,
935 offset;
936
937 /*
938 Displace/Distort based on overlay gradient map:
939 X = red_channel; Y = green_channel;
940 compose:args = x_scale[,y_scale[,center.x,center.y]]
941 */
942 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000943 exception);
cristy4c08aed2011-07-01 19:47:50 +0000944 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000945 {
946 composite_image=DestroyImage(composite_image);
947 return(MagickFalse);
948 }
cristy4c08aed2011-07-01 19:47:50 +0000949 SetGeometryInfo(&geometry_info);
950 flags=NoValue;
951 value=GetImageArtifact(composite_image,"compose:args");
952 if (value != (char *) NULL)
953 flags=ParseGeometry(value,&geometry_info);
954 if ((flags & (WidthValue|HeightValue)) == 0 )
955 {
956 if ((flags & AspectValue) == 0)
957 {
cristy7159f662012-10-28 17:32:43 +0000958 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
cristy4c08aed2011-07-01 19:47:50 +0000959 2.0;
cristy7159f662012-10-28 17:32:43 +0000960 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000961 }
962 else
963 {
cristy7159f662012-10-28 17:32:43 +0000964 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
965 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000966 }
967 }
968 else
969 {
970 horizontal_scale=geometry_info.rho;
971 vertical_scale=geometry_info.sigma;
972 if ((flags & PercentValue) != 0)
973 {
974 if ((flags & AspectValue) == 0)
975 {
976 horizontal_scale*=(composite_image->columns-1.0)/200.0;
977 vertical_scale*=(composite_image->rows-1.0)/200.0;
978 }
979 else
980 {
981 horizontal_scale*=(image->columns-1.0)/200.0;
982 vertical_scale*=(image->rows-1.0)/200.0;
983 }
984 }
985 if ((flags & HeightValue) == 0)
986 vertical_scale=horizontal_scale;
987 }
988 /*
989 Determine fixed center point for absolute distortion map
990 Absolute distort ==
991 Displace offset relative to a fixed absolute point
992 Select that point according to +X+Y user inputs.
993 default = center of overlay image
994 arg flag '!' = locations/percentage relative to background image
995 */
cristy7159f662012-10-28 17:32:43 +0000996 center.x=(MagickRealType) x_offset;
997 center.y=(MagickRealType) y_offset;
cristy4c08aed2011-07-01 19:47:50 +0000998 if (compose == DistortCompositeOp)
999 {
1000 if ((flags & XValue) == 0)
1001 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001002 center.x=(MagickRealType) (x_offset+(composite_image->columns-1)/
1003 2.0);
cristy4c08aed2011-07-01 19:47:50 +00001004 else
cristy7159f662012-10-28 17:32:43 +00001005 center.x=(MagickRealType) ((image->columns-1)/2);
cristy4c08aed2011-07-01 19:47:50 +00001006 else
1007 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001008 center.x=(MagickRealType) x_offset+geometry_info.xi;
cristy4c08aed2011-07-01 19:47:50 +00001009 else
1010 center.x=geometry_info.xi;
1011 if ((flags & YValue) == 0)
1012 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001013 center.y=(MagickRealType) (y_offset+(composite_image->rows-1)/
1014 2.0);
cristy4c08aed2011-07-01 19:47:50 +00001015 else
cristy7159f662012-10-28 17:32:43 +00001016 center.y=(MagickRealType) ((image->rows-1)/2);
cristy4c08aed2011-07-01 19:47:50 +00001017 else
1018 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001019 center.y=(MagickRealType) y_offset+geometry_info.psi;
cristy4c08aed2011-07-01 19:47:50 +00001020 else
1021 center.y=geometry_info.psi;
1022 }
1023 /*
1024 Shift the pixel offset point as defined by the provided,
1025 displacement/distortion map. -- Like a lens...
1026 */
cristye10859a2011-12-18 22:28:59 +00001027 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +00001028 image_view=AcquireVirtualCacheView(image,exception);
1029 composite_view=AcquireVirtualCacheView(composite_image,exception);
1030 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001031 for (y=0; y < (ssize_t) composite_image->rows; y++)
1032 {
1033 MagickBooleanType
1034 sync;
1035
1036 register const Quantum
1037 *restrict p;
1038
1039 register Quantum
1040 *restrict q;
1041
1042 register ssize_t
1043 x;
1044
1045 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1046 continue;
1047 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1048 1,exception);
1049 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001050 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001051 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1052 break;
1053 for (x=0; x < (ssize_t) composite_image->columns; x++)
1054 {
1055 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1056 {
cristyed231572011-07-14 02:18:59 +00001057 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001058 continue;
1059 }
1060 /*
1061 Displace the offset.
1062 */
cristy7159f662012-10-28 17:32:43 +00001063 offset.x=(double) (horizontal_scale*(GetPixelRed(composite_image,p)-
1064 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1065 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1066 x : 0);
1067 offset.y=(double) (vertical_scale*(GetPixelGreen(composite_image,p)-
1068 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1069 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1070 y : 0);
cristy4c08aed2011-07-01 19:47:50 +00001071 (void) InterpolatePixelInfo(image,image_view,
1072 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1073 &pixel,exception);
1074 /*
1075 Mask with the 'invalid pixel mask' in alpha channel.
1076 */
cristy7159f662012-10-28 17:32:43 +00001077 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1078 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001079 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001080 p+=GetPixelChannels(composite_image);
1081 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001082 }
1083 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1084 if (sync == MagickFalse)
1085 break;
1086 }
1087 destination_view=DestroyCacheView(destination_view);
1088 composite_view=DestroyCacheView(composite_view);
1089 image_view=DestroyCacheView(image_view);
cristyf661c4d2012-07-28 12:57:17 +00001090 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001091 composite_image=destination_image;
1092 break;
1093 }
1094 case DissolveCompositeOp:
1095 {
cristyfeb3e962012-03-29 17:25:55 +00001096 const char
1097 *value;
1098
cristy4c08aed2011-07-01 19:47:50 +00001099 /*
1100 Geometry arguments to dissolve factors.
1101 */
1102 value=GetImageArtifact(composite_image,"compose:args");
1103 if (value != (char *) NULL)
1104 {
1105 flags=ParseGeometry(value,&geometry_info);
1106 source_dissolve=geometry_info.rho/100.0;
1107 destination_dissolve=1.0;
1108 if ((source_dissolve-MagickEpsilon) < 0.0)
1109 source_dissolve=0.0;
1110 if ((source_dissolve+MagickEpsilon) > 1.0)
1111 {
1112 destination_dissolve=2.0-source_dissolve;
1113 source_dissolve=1.0;
1114 }
1115 if ((flags & SigmaValue) != 0)
1116 destination_dissolve=geometry_info.sigma/100.0;
1117 if ((destination_dissolve-MagickEpsilon) < 0.0)
1118 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001119 /* posible speed up? -- from IMv6 update
1120 clip_to_self=MagickFalse;
1121 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1122 {
1123 destination_dissolve=1.0;
1124 clip_to_self=MagickTrue;
1125 }
1126 */
cristy4c08aed2011-07-01 19:47:50 +00001127 }
1128 break;
1129 }
1130 case BlendCompositeOp:
1131 {
cristyfeb3e962012-03-29 17:25:55 +00001132 const char
1133 *value;
1134
cristy4c08aed2011-07-01 19:47:50 +00001135 value=GetImageArtifact(composite_image,"compose:args");
1136 if (value != (char *) NULL)
1137 {
1138 flags=ParseGeometry(value,&geometry_info);
1139 source_dissolve=geometry_info.rho/100.0;
1140 destination_dissolve=1.0-source_dissolve;
1141 if ((flags & SigmaValue) != 0)
1142 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001143 }
1144 break;
1145 }
1146 case MathematicsCompositeOp:
1147 {
cristyfeb3e962012-03-29 17:25:55 +00001148 const char
1149 *value;
1150
cristy4c08aed2011-07-01 19:47:50 +00001151 /*
1152 Just collect the values from "compose:args", setting.
1153 Unused values are set to zero automagically.
1154
1155 Arguments are normally a comma separated list, so this probably should
1156 be changed to some 'general comma list' parser, (with a minimum
1157 number of values)
1158 */
1159 SetGeometryInfo(&geometry_info);
1160 value=GetImageArtifact(composite_image,"compose:args");
1161 if (value != (char *) NULL)
1162 (void) ParseGeometry(value,&geometry_info);
1163 break;
1164 }
1165 case ModulateCompositeOp:
1166 {
cristyfeb3e962012-03-29 17:25:55 +00001167 const char
1168 *value;
1169
cristy4c08aed2011-07-01 19:47:50 +00001170 /*
cristy7133e642012-08-14 11:04:11 +00001171 Determine the luma and chroma scale.
cristy4c08aed2011-07-01 19:47:50 +00001172 */
1173 value=GetImageArtifact(composite_image,"compose:args");
1174 if (value != (char *) NULL)
1175 {
1176 flags=ParseGeometry(value,&geometry_info);
cristy7133e642012-08-14 11:04:11 +00001177 percent_luma=geometry_info.rho;
cristy4c08aed2011-07-01 19:47:50 +00001178 if ((flags & SigmaValue) != 0)
cristy7133e642012-08-14 11:04:11 +00001179 percent_chroma=geometry_info.sigma;
cristy4c08aed2011-07-01 19:47:50 +00001180 }
1181 break;
1182 }
1183 case ThresholdCompositeOp:
1184 {
cristyfeb3e962012-03-29 17:25:55 +00001185 const char
1186 *value;
1187
cristy4c08aed2011-07-01 19:47:50 +00001188 /*
1189 Determine the amount and threshold.
1190 */
1191 value=GetImageArtifact(composite_image,"compose:args");
1192 if (value != (char *) NULL)
1193 {
1194 flags=ParseGeometry(value,&geometry_info);
1195 amount=geometry_info.rho;
1196 threshold=geometry_info.sigma;
1197 if ((flags & SigmaValue) == 0)
1198 threshold=0.05f;
1199 }
1200 threshold*=QuantumRange;
1201 break;
1202 }
1203 default:
1204 break;
1205 }
cristy4c08aed2011-07-01 19:47:50 +00001206 /*
1207 Composite image.
1208 */
1209 status=MagickTrue;
1210 progress=0;
cristy7159f662012-10-28 17:32:43 +00001211 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy46ff2672012-12-14 15:32:26 +00001212 composite_view=AcquireVirtualCacheView(composite_image,exception);
1213 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001214#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001215 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001216 magick_threads(composite_image,image,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001217#endif
1218 for (y=0; y < (ssize_t) image->rows; y++)
1219 {
1220 const Quantum
1221 *pixels;
1222
cristy7159f662012-10-28 17:32:43 +00001223 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001224 blue,
cristy7133e642012-08-14 11:04:11 +00001225 luma,
cristye4a40472011-12-22 02:56:19 +00001226 green,
cristy4c08aed2011-07-01 19:47:50 +00001227 hue,
cristye4a40472011-12-22 02:56:19 +00001228 red,
cristy7133e642012-08-14 11:04:11 +00001229 chroma;
cristy4c08aed2011-07-01 19:47:50 +00001230
cristyddeeea22012-04-12 01:33:09 +00001231 PixelInfo
1232 destination_pixel,
1233 source_pixel;
1234
cristy4c08aed2011-07-01 19:47:50 +00001235 register const Quantum
1236 *restrict p;
1237
1238 register Quantum
1239 *restrict q;
1240
1241 register ssize_t
1242 x;
1243
1244 if (status == MagickFalse)
1245 continue;
cristyfeb3e962012-03-29 17:25:55 +00001246 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001247 {
1248 if (y < y_offset)
1249 continue;
1250 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1251 continue;
1252 }
1253 /*
1254 If pixels is NULL, y is outside overlay region.
1255 */
1256 pixels=(Quantum *) NULL;
1257 p=(Quantum *) NULL;
1258 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1259 {
1260 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1261 composite_image->columns,1,exception);
1262 if (p == (const Quantum *) NULL)
1263 {
1264 status=MagickFalse;
1265 continue;
1266 }
1267 pixels=p;
1268 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001269 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001270 }
1271 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001272 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001273 {
1274 status=MagickFalse;
1275 continue;
1276 }
cristy4c08aed2011-07-01 19:47:50 +00001277 hue=0.0;
cristy7133e642012-08-14 11:04:11 +00001278 chroma=0.0;
1279 luma=0.0;
cristyddeeea22012-04-12 01:33:09 +00001280 GetPixelInfo(image,&destination_pixel);
1281 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001282 for (x=0; x < (ssize_t) image->columns; x++)
1283 {
cristy17028dc2013-01-24 12:28:39 +00001284 double
1285 gamma;
1286
cristy7159f662012-10-28 17:32:43 +00001287 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001288 alpha,
1289 Da,
1290 Dc,
1291 Dca,
cristye4a40472011-12-22 02:56:19 +00001292 Sa,
1293 Sc,
1294 Sca;
1295
1296 register ssize_t
1297 i;
1298
cristy564a5692012-01-20 23:56:26 +00001299 size_t
1300 channels;
1301
cristyfeb3e962012-03-29 17:25:55 +00001302 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001303 {
1304 if (x < x_offset)
1305 {
cristyed231572011-07-14 02:18:59 +00001306 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001307 continue;
1308 }
1309 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1310 break;
1311 }
cristye4a40472011-12-22 02:56:19 +00001312 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1313 ((x-x_offset) >= (ssize_t) composite_image->columns))
1314 {
1315 Quantum
1316 source[MaxPixelChannels];
1317
1318 /*
1319 Virtual composite:
1320 Sc: source color.
1321 Dc: destination color.
1322 */
1323 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1324 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001325 if (GetPixelMask(image,q) != 0)
1326 {
1327 q+=GetPixelChannels(image);
1328 continue;
1329 }
cristye4a40472011-12-22 02:56:19 +00001330 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1331 {
cristy7159f662012-10-28 17:32:43 +00001332 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001333 pixel;
1334
1335 PixelChannel
1336 channel;
1337
1338 PixelTrait
1339 composite_traits,
1340 traits;
1341
cristycf1296e2012-08-26 23:40:49 +00001342 channel=GetPixelChannelChannel(image,i);
1343 traits=GetPixelChannelTraits(image,channel);
1344 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001345 if ((traits == UndefinedPixelTrait) ||
1346 (composite_traits == UndefinedPixelTrait))
1347 continue;
1348 switch (compose)
1349 {
cristyc8d63672012-01-11 13:03:13 +00001350 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001351 case ChangeMaskCompositeOp:
1352 case CopyAlphaCompositeOp:
1353 case DstAtopCompositeOp:
1354 case DstInCompositeOp:
1355 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001356 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001357 case OutCompositeOp:
1358 case SrcInCompositeOp:
1359 case SrcOutCompositeOp:
1360 {
cristy7159f662012-10-28 17:32:43 +00001361 pixel=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001362 if (channel == AlphaPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001363 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001364 break;
1365 }
1366 case ClearCompositeOp:
1367 case CopyCompositeOp:
1368 case ReplaceCompositeOp:
1369 case SrcCompositeOp:
1370 {
1371 if (channel == AlphaPixelChannel)
1372 {
cristy7159f662012-10-28 17:32:43 +00001373 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001374 break;
1375 }
1376 pixel=0.0;
1377 break;
1378 }
cristy99abff32011-12-24 20:45:16 +00001379 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001380 case DissolveCompositeOp:
1381 {
1382 if (channel == AlphaPixelChannel)
1383 {
1384 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1385 source);
1386 break;
1387 }
cristy7159f662012-10-28 17:32:43 +00001388 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001389 break;
1390 }
1391 default:
1392 {
cristy7159f662012-10-28 17:32:43 +00001393 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001394 break;
1395 }
1396 }
1397 q[i]=ClampToQuantum(pixel);
1398 }
1399 q+=GetPixelChannels(image);
1400 continue;
1401 }
1402 /*
1403 Authentic composite:
1404 Sa: normalized source alpha.
1405 Da: normalized destination alpha.
1406 */
1407 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1408 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001409 switch (compose)
1410 {
cristye4a40472011-12-22 02:56:19 +00001411 case BumpmapCompositeOp:
1412 {
1413 alpha=GetPixelIntensity(composite_image,p)*Sa;
1414 break;
1415 }
cristycdc168f2011-12-21 15:24:39 +00001416 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001417 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001418 case DifferenceCompositeOp:
1419 case DivideDstCompositeOp:
1420 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001421 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001422 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001423 case LinearBurnCompositeOp:
1424 case LinearDodgeCompositeOp:
1425 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001426 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001427 case MinusDstCompositeOp:
1428 case MinusSrcCompositeOp:
1429 case ModulusAddCompositeOp:
1430 case ModulusSubtractCompositeOp:
1431 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001432 case OverlayCompositeOp:
1433 case PegtopLightCompositeOp:
1434 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001435 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001436 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001437 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001438 {
1439 alpha=RoundToUnity(Sa+Da-Sa*Da);
1440 break;
1441 }
1442 case DarkenCompositeOp:
1443 case DstAtopCompositeOp:
1444 case DstInCompositeOp:
1445 case InCompositeOp:
1446 case LightenCompositeOp:
1447 case SrcInCompositeOp:
1448 {
1449 alpha=Sa*Da;
1450 break;
1451 }
1452 case DissolveCompositeOp:
1453 {
1454 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1455 Sa+destination_dissolve*Da;
1456 break;
1457 }
1458 case DstOverCompositeOp:
1459 {
1460 alpha=Da*(-Sa)+Da+Sa;
1461 break;
1462 }
1463 case DstOutCompositeOp:
1464 {
1465 alpha=Da*(1.0-Sa);
1466 break;
1467 }
1468 case OutCompositeOp:
1469 case SrcOutCompositeOp:
1470 {
1471 alpha=Sa*(1.0-Da);
1472 break;
1473 }
1474 case OverCompositeOp:
1475 case SrcOverCompositeOp:
1476 {
1477 alpha=Sa*(-Da)+Sa+Da;
1478 break;
1479 }
cristy99abff32011-12-24 20:45:16 +00001480 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001481 case PlusCompositeOp:
1482 {
1483 alpha=RoundToUnity(Sa+Da);
1484 break;
1485 }
cristy4c08aed2011-07-01 19:47:50 +00001486 case XorCompositeOp:
1487 {
cristye4a40472011-12-22 02:56:19 +00001488 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001489 break;
1490 }
1491 default:
cristye4a40472011-12-22 02:56:19 +00001492 {
1493 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001494 break;
cristye4a40472011-12-22 02:56:19 +00001495 }
cristy4c08aed2011-07-01 19:47:50 +00001496 }
cristy10a6c612012-01-29 21:41:05 +00001497 if (GetPixelMask(image,p) != 0)
1498 {
1499 p+=GetPixelChannels(composite_image);
1500 q+=GetPixelChannels(image);
1501 continue;
1502 }
cristy9d3d2792012-04-14 15:15:19 +00001503 switch (compose)
1504 {
1505 case ColorizeCompositeOp:
1506 case HueCompositeOp:
1507 case LuminizeCompositeOp:
1508 case ModulateCompositeOp:
1509 case SaturateCompositeOp:
1510 {
1511 GetPixelInfoPixel(composite_image,p,&source_pixel);
1512 GetPixelInfoPixel(image,q,&destination_pixel);
1513 break;
1514 }
1515 default:
1516 break;
1517 }
cristye4a40472011-12-22 02:56:19 +00001518 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1519 {
cristy7159f662012-10-28 17:32:43 +00001520 MagickRealType
1521 pixel,
cristy564a5692012-01-20 23:56:26 +00001522 sans;
1523
cristye4a40472011-12-22 02:56:19 +00001524 PixelChannel
1525 channel;
cristye10859a2011-12-18 22:28:59 +00001526
cristye4a40472011-12-22 02:56:19 +00001527 PixelTrait
1528 composite_traits,
1529 traits;
1530
cristycf1296e2012-08-26 23:40:49 +00001531 channel=GetPixelChannelChannel(image,i);
1532 traits=GetPixelChannelTraits(image,channel);
1533 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001534 if (traits == UndefinedPixelTrait)
1535 continue;
cristya7b07912012-01-11 20:01:32 +00001536 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001537 (composite_traits == UndefinedPixelTrait))
1538 continue;
1539 /*
1540 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001541 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001542 */
cristy7159f662012-10-28 17:32:43 +00001543 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
1544 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001545 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001546 {
cristye4a40472011-12-22 02:56:19 +00001547 if (channel != AlphaPixelChannel)
1548 {
1549 /*
1550 Copy channel.
1551 */
1552 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001553 continue;
cristye10859a2011-12-18 22:28:59 +00001554 }
cristye4a40472011-12-22 02:56:19 +00001555 /*
1556 Set alpha channel.
1557 */
cristye10859a2011-12-18 22:28:59 +00001558 switch (compose)
1559 {
cristyc8d63672012-01-11 13:03:13 +00001560 case AlphaCompositeOp:
1561 {
cristya7b07912012-01-11 20:01:32 +00001562 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001563 break;
1564 }
cristye4a40472011-12-22 02:56:19 +00001565 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001566 case CopyBlackCompositeOp:
1567 case CopyBlueCompositeOp:
1568 case CopyCyanCompositeOp:
1569 case CopyGreenCompositeOp:
1570 case CopyMagentaCompositeOp:
1571 case CopyRedCompositeOp:
1572 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001573 case SrcAtopCompositeOp:
1574 case DstCompositeOp:
1575 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001576 {
cristye4a40472011-12-22 02:56:19 +00001577 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001578 break;
1579 }
cristye10859a2011-12-18 22:28:59 +00001580 case ChangeMaskCompositeOp:
1581 {
cristye4a40472011-12-22 02:56:19 +00001582 MagickBooleanType
1583 equivalent;
1584
cristy7159f662012-10-28 17:32:43 +00001585 if (Da > ((MagickRealType) QuantumRange/2.0))
cristy99abff32011-12-24 20:45:16 +00001586 {
cristy7159f662012-10-28 17:32:43 +00001587 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001588 break;
1589 }
cristye4a40472011-12-22 02:56:19 +00001590 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001591 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001592 {
cristy7159f662012-10-28 17:32:43 +00001593 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001594 break;
1595 }
cristy7159f662012-10-28 17:32:43 +00001596 pixel=(MagickRealType) OpaqueAlpha;
cristye4a40472011-12-22 02:56:19 +00001597 break;
1598 }
cristy99abff32011-12-24 20:45:16 +00001599 case ClearCompositeOp:
1600 {
cristy7159f662012-10-28 17:32:43 +00001601 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001602 break;
1603 }
1604 case ColorizeCompositeOp:
1605 case HueCompositeOp:
1606 case LuminizeCompositeOp:
1607 case SaturateCompositeOp:
1608 {
1609 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1610 {
1611 pixel=QuantumRange*Da;
1612 break;
1613 }
1614 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1615 {
1616 pixel=QuantumRange*Sa;
1617 break;
1618 }
1619 if (Sa < Da)
1620 {
1621 pixel=QuantumRange*Da;
1622 break;
1623 }
1624 pixel=QuantumRange*Sa;
1625 break;
1626 }
cristy99abff32011-12-24 20:45:16 +00001627 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001628 {
1629 pixel=QuantumRange*Sa;
cristy8a46d822012-08-28 23:32:39 +00001630 if (composite_image->alpha_trait != BlendPixelTrait)
cristy24d5d722012-05-17 12:27:27 +00001631 pixel=GetPixelIntensity(composite_image,p);
1632 break;
1633 }
1634 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001635 case DisplaceCompositeOp:
1636 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001637 case DstAtopCompositeOp:
1638 case ReplaceCompositeOp:
1639 case SrcCompositeOp:
1640 {
1641 pixel=QuantumRange*Sa;
1642 break;
1643 }
1644 case DarkenIntensityCompositeOp:
1645 {
cristy99abff32011-12-24 20:45:16 +00001646 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1647 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001648 break;
1649 }
cristy98621462011-12-31 22:31:11 +00001650 case IntensityCompositeOp:
1651 {
cristyf13c5942012-08-08 23:50:11 +00001652 pixel=GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001653 break;
1654 }
cristye4a40472011-12-22 02:56:19 +00001655 case LightenIntensityCompositeOp:
1656 {
1657 pixel=Sa*GetPixelIntensity(composite_image,p) >
1658 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001659 break;
1660 }
cristy99abff32011-12-24 20:45:16 +00001661 case ModulateCompositeOp:
1662 {
1663 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1664 {
1665 pixel=QuantumRange*Da;
1666 break;
1667 }
1668 pixel=QuantumRange*Da;
1669 break;
1670 }
cristye10859a2011-12-18 22:28:59 +00001671 default:
1672 {
cristye4a40472011-12-22 02:56:19 +00001673 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001674 break;
1675 }
1676 }
cristye4a40472011-12-22 02:56:19 +00001677 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001678 continue;
1679 }
1680 /*
cristy99abff32011-12-24 20:45:16 +00001681 Porter-Duff compositions:
1682 Sca: source normalized color multiplied by alpha.
1683 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001684 */
cristy99abff32011-12-24 20:45:16 +00001685 Sca=QuantumScale*Sa*Sc;
1686 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001687 switch (compose)
1688 {
cristye10859a2011-12-18 22:28:59 +00001689 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001690 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001691 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001692 {
cristy99abff32011-12-24 20:45:16 +00001693 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001694 break;
1695 }
1696 default:
1697 break;
1698 }
cristy3e3ec3a2012-11-03 23:11:06 +00001699 gamma=PerceptibleReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001700 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001701 switch (compose)
1702 {
cristya7b07912012-01-11 20:01:32 +00001703 case AlphaCompositeOp:
1704 {
1705 pixel=QuantumRange*Sa;
1706 break;
1707 }
cristye4a40472011-12-22 02:56:19 +00001708 case AtopCompositeOp:
1709 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001710 {
cristye4a40472011-12-22 02:56:19 +00001711 pixel=Sc*Sa+Dc*(1.0-Sa);
1712 break;
cristye10859a2011-12-18 22:28:59 +00001713 }
cristye4a40472011-12-22 02:56:19 +00001714 case BlendCompositeOp:
1715 {
1716 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1717 break;
1718 }
1719 case BlurCompositeOp:
1720 case DisplaceCompositeOp:
1721 case DistortCompositeOp:
1722 case CopyCompositeOp:
1723 case ReplaceCompositeOp:
1724 case SrcCompositeOp:
1725 {
1726 pixel=Sc;
1727 break;
1728 }
1729 case BumpmapCompositeOp:
1730 {
cristy99abff32011-12-24 20:45:16 +00001731 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001732 {
1733 pixel=Dc;
1734 break;
1735 }
1736 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1737 break;
1738 }
cristy99abff32011-12-24 20:45:16 +00001739 case ChangeMaskCompositeOp:
1740 {
1741 pixel=Dc;
1742 break;
1743 }
1744 case ClearCompositeOp:
1745 {
1746 pixel=0.0;
1747 break;
1748 }
cristye4a40472011-12-22 02:56:19 +00001749 case ColorBurnCompositeOp:
1750 {
1751 /*
1752 Refer to the March 2009 SVG specification.
1753 */
1754 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1755 {
cristy99abff32011-12-24 20:45:16 +00001756 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001757 break;
1758 }
1759 if (Sca < MagickEpsilon)
1760 {
cristy99abff32011-12-24 20:45:16 +00001761 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001762 break;
1763 }
cristy99abff32011-12-24 20:45:16 +00001764 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1765 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001766 break;
1767 }
1768 case ColorDodgeCompositeOp:
1769 {
1770 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1771 {
cristy99abff32011-12-24 20:45:16 +00001772 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001773 break;
1774 }
1775 if (fabs(Sca-Sa) < MagickEpsilon)
1776 {
cristy99abff32011-12-24 20:45:16 +00001777 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001778 break;
1779 }
cristy99abff32011-12-24 20:45:16 +00001780 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001781 (1.0-Sa));
1782 break;
1783 }
1784 case ColorizeCompositeOp:
1785 {
cristy99abff32011-12-24 20:45:16 +00001786 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001787 {
1788 pixel=Dc;
1789 break;
1790 }
cristy99abff32011-12-24 20:45:16 +00001791 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001792 {
1793 pixel=Sc;
1794 break;
1795 }
cristy7133e642012-08-14 11:04:11 +00001796 CompositeHCL(destination_pixel.red,destination_pixel.green,
1797 destination_pixel.blue,&sans,&sans,&luma);
1798 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1799 &hue,&chroma,&sans);
1800 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001801 switch (channel)
1802 {
1803 case RedPixelChannel: pixel=red; break;
1804 case GreenPixelChannel: pixel=green; break;
1805 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001806 default: pixel=Dc; break;
1807 }
1808 break;
1809 }
cristye4a40472011-12-22 02:56:19 +00001810 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001811 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001812 {
cristy24d5d722012-05-17 12:27:27 +00001813 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001814 break;
1815 }
1816 case CopyBlackCompositeOp:
1817 {
cristyd197cbb2012-01-13 02:14:12 +00001818 if (channel == BlackPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001819 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001820 break;
1821 }
1822 case CopyBlueCompositeOp:
1823 case CopyYellowCompositeOp:
1824 {
cristyd197cbb2012-01-13 02:14:12 +00001825 if (channel == BluePixelChannel)
cristy7159f662012-10-28 17:32:43 +00001826 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001827 break;
1828 }
1829 case CopyGreenCompositeOp:
1830 case CopyMagentaCompositeOp:
1831 {
cristyd197cbb2012-01-13 02:14:12 +00001832 if (channel == GreenPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001833 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001834 break;
1835 }
1836 case CopyRedCompositeOp:
1837 case CopyCyanCompositeOp:
1838 {
cristyd197cbb2012-01-13 02:14:12 +00001839 if (channel == RedPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001840 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001841 break;
1842 }
cristy99abff32011-12-24 20:45:16 +00001843 case DarkenCompositeOp:
1844 {
1845 /*
1846 Darken is equivalent to a 'Minimum' method
1847 OR a greyscale version of a binary 'Or'
1848 OR the 'Intersection' of pixel sets.
1849 */
1850 if (Sc < Dc)
1851 {
1852 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1853 break;
1854 }
1855 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1856 break;
1857 }
cristye4a40472011-12-22 02:56:19 +00001858 case DarkenIntensityCompositeOp:
1859 {
cristy99abff32011-12-24 20:45:16 +00001860 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1861 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001862 break;
1863 }
1864 case DifferenceCompositeOp:
1865 {
1866 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1867 break;
1868 }
1869 case DissolveCompositeOp:
1870 {
1871 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1872 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1873 break;
1874 }
1875 case DivideDstCompositeOp:
1876 {
1877 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1878 {
cristy99abff32011-12-24 20:45:16 +00001879 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001880 break;
1881 }
1882 if (fabs(Dca) < MagickEpsilon)
1883 {
cristy99abff32011-12-24 20:45:16 +00001884 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001885 break;
1886 }
cristy99abff32011-12-24 20:45:16 +00001887 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001888 break;
1889 }
1890 case DivideSrcCompositeOp:
1891 {
1892 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1893 {
cristy99abff32011-12-24 20:45:16 +00001894 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001895 break;
1896 }
1897 if (fabs(Sca) < MagickEpsilon)
1898 {
cristy99abff32011-12-24 20:45:16 +00001899 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001900 break;
1901 }
cristy99abff32011-12-24 20:45:16 +00001902 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001903 break;
1904 }
1905 case DstAtopCompositeOp:
1906 {
1907 pixel=Dc*Da+Sc*(1.0-Da);
1908 break;
1909 }
1910 case DstCompositeOp:
1911 case NoCompositeOp:
1912 {
1913 pixel=Dc;
1914 break;
1915 }
1916 case DstInCompositeOp:
1917 {
1918 pixel=gamma*(Sa*Dc*Sa);
1919 break;
1920 }
1921 case DstOutCompositeOp:
1922 {
1923 pixel=gamma*(Da*Dc*(1.0-Sa));
1924 break;
1925 }
1926 case DstOverCompositeOp:
1927 {
1928 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1929 break;
1930 }
1931 case ExclusionCompositeOp:
1932 {
cristy99abff32011-12-24 20:45:16 +00001933 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1934 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001935 break;
1936 }
1937 case HardLightCompositeOp:
1938 {
1939 if ((2.0*Sca) < Sa)
1940 {
cristy99abff32011-12-24 20:45:16 +00001941 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001942 (1.0-Sa));
1943 break;
1944 }
cristy99abff32011-12-24 20:45:16 +00001945 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001946 Dca*(1.0-Sa));
1947 break;
1948 }
1949 case HueCompositeOp:
1950 {
cristy99abff32011-12-24 20:45:16 +00001951 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001952 {
1953 pixel=Dc;
1954 break;
1955 }
cristy99abff32011-12-24 20:45:16 +00001956 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001957 {
1958 pixel=Sc;
1959 break;
1960 }
cristy7133e642012-08-14 11:04:11 +00001961 CompositeHCL(destination_pixel.red,destination_pixel.green,
1962 destination_pixel.blue,&hue,&chroma,&luma);
1963 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001964 &hue,&sans,&sans);
cristy7133e642012-08-14 11:04:11 +00001965 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001966 switch (channel)
1967 {
1968 case RedPixelChannel: pixel=red; break;
1969 case GreenPixelChannel: pixel=green; break;
1970 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001971 default: pixel=Dc; break;
1972 }
1973 break;
1974 }
1975 case InCompositeOp:
1976 case SrcInCompositeOp:
1977 {
1978 pixel=gamma*(Da*Sc*Da);
1979 break;
1980 }
cristy99abff32011-12-24 20:45:16 +00001981 case LinearBurnCompositeOp:
1982 {
1983 /*
1984 LinearBurn: as defined by Abode Photoshop, according to
1985 http://www.simplefilter.de/en/basics/mixmods.html is:
1986
1987 f(Sc,Dc) = Sc + Dc - 1
1988 */
1989 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1990 break;
1991 }
1992 case LinearDodgeCompositeOp:
1993 {
1994 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1995 break;
1996 }
1997 case LinearLightCompositeOp:
1998 {
1999 /*
2000 LinearLight: as defined by Abode Photoshop, according to
2001 http://www.simplefilter.de/en/basics/mixmods.html is:
2002
2003 f(Sc,Dc) = Dc + 2*Sc - 1
2004 */
2005 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
2006 break;
2007 }
2008 case LightenCompositeOp:
2009 {
2010 if (Sc > Dc)
2011 {
cristy24d5d722012-05-17 12:27:27 +00002012 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00002013 break;
2014 }
cristy24d5d722012-05-17 12:27:27 +00002015 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00002016 break;
2017 }
cristye4a40472011-12-22 02:56:19 +00002018 case LightenIntensityCompositeOp:
2019 {
2020 /*
2021 Lighten is equivalent to a 'Maximum' method
2022 OR a greyscale version of a binary 'And'
2023 OR the 'Union' of pixel sets.
2024 */
2025 pixel=Sa*GetPixelIntensity(composite_image,p) >
2026 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2027 break;
2028 }
cristye4a40472011-12-22 02:56:19 +00002029 case LuminizeCompositeOp:
2030 {
cristy99abff32011-12-24 20:45:16 +00002031 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002032 {
2033 pixel=Dc;
2034 break;
2035 }
cristy99abff32011-12-24 20:45:16 +00002036 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002037 {
2038 pixel=Sc;
2039 break;
2040 }
cristy7133e642012-08-14 11:04:11 +00002041 CompositeHCL(destination_pixel.red,destination_pixel.green,
2042 destination_pixel.blue,&hue,&chroma,&luma);
2043 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2044 &sans,&sans,&luma);
2045 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002046 switch (channel)
2047 {
2048 case RedPixelChannel: pixel=red; break;
2049 case GreenPixelChannel: pixel=green; break;
2050 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002051 default: pixel=Dc; break;
2052 }
2053 break;
2054 }
2055 case MathematicsCompositeOp:
2056 {
2057 /*
2058 'Mathematics' a free form user control mathematical composition
2059 is defined as...
2060
2061 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2062
2063 Where the arguments A,B,C,D are (currently) passed to composite
2064 as a command separated 'geometry' string in "compose:args" image
2065 artifact.
2066
2067 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2068
2069 Applying the SVG transparency formula (see above), we get...
2070
2071 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2072
2073 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2074 Dca*(1.0-Sa)
2075 */
2076 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2077 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2078 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2079 break;
2080 }
2081 case MinusDstCompositeOp:
2082 {
2083 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2084 break;
2085 }
2086 case MinusSrcCompositeOp:
2087 {
2088 /*
2089 Minus source from destination.
2090
2091 f(Sc,Dc) = Sc - Dc
2092 */
cristy99abff32011-12-24 20:45:16 +00002093 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002094 break;
2095 }
2096 case ModulateCompositeOp:
2097 {
2098 ssize_t
2099 offset;
2100
cristy99abff32011-12-24 20:45:16 +00002101 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002102 {
2103 pixel=Dc;
2104 break;
2105 }
2106 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2107 if (offset == 0)
2108 {
2109 pixel=Dc;
2110 break;
2111 }
cristy7133e642012-08-14 11:04:11 +00002112 CompositeHCL(destination_pixel.red,destination_pixel.green,
2113 destination_pixel.blue,&hue,&chroma,&luma);
2114 luma+=(0.01*percent_luma*offset)/midpoint;
2115 chroma*=0.01*percent_chroma;
2116 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002117 switch (channel)
2118 {
2119 case RedPixelChannel: pixel=red; break;
2120 case GreenPixelChannel: pixel=green; break;
2121 case BluePixelChannel: pixel=blue; break;
2122 default: pixel=Dc; break;
2123 }
2124 break;
2125 }
2126 case ModulusAddCompositeOp:
2127 {
2128 pixel=Sc+Dc;
2129 if (pixel > QuantumRange)
2130 pixel-=(QuantumRange+1.0);
2131 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2132 break;
2133 }
2134 case ModulusSubtractCompositeOp:
2135 {
cristy99abff32011-12-24 20:45:16 +00002136 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002137 if (pixel < 0.0)
2138 pixel+=(QuantumRange+1.0);
2139 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2140 break;
2141 }
2142 case MultiplyCompositeOp:
2143 {
cristy99abff32011-12-24 20:45:16 +00002144 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002145 break;
2146 }
2147 case OutCompositeOp:
2148 case SrcOutCompositeOp:
2149 {
2150 pixel=gamma*(Sa*Sc*(1.0-Da));
2151 break;
2152 }
2153 case OverCompositeOp:
2154 case SrcOverCompositeOp:
2155 {
cristy99abff32011-12-24 20:45:16 +00002156 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002157 break;
2158 }
2159 case OverlayCompositeOp:
2160 {
2161 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002162 {
2163 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2164 (1.0-Da));
2165 break;
2166 }
2167 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2168 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002169 break;
2170 }
2171 case PegtopLightCompositeOp:
2172 {
2173 /*
2174 PegTop: A Soft-Light alternative: A continuous version of the
2175 Softlight function, producing very similar results.
2176
2177 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2178
2179 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2180 */
2181 if (fabs(Da) < MagickEpsilon)
2182 {
cristy99abff32011-12-24 20:45:16 +00002183 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002184 break;
2185 }
cristy99abff32011-12-24 20:45:16 +00002186 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2187 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002188 break;
2189 }
2190 case PinLightCompositeOp:
2191 {
2192 /*
2193 PinLight: A Photoshop 7 composition method
2194 http://www.simplefilter.de/en/basics/mixmods.html
2195
2196 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2197 */
2198 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2199 {
cristy99abff32011-12-24 20:45:16 +00002200 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002201 break;
2202 }
2203 if ((Dca*Sa) > (2.0*Sca*Da))
2204 {
cristy99abff32011-12-24 20:45:16 +00002205 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002206 break;
2207 }
cristy99abff32011-12-24 20:45:16 +00002208 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002209 break;
2210 }
2211 case PlusCompositeOp:
2212 {
cristy24d5d722012-05-17 12:27:27 +00002213 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002214 break;
2215 }
2216 case SaturateCompositeOp:
2217 {
cristy99abff32011-12-24 20:45:16 +00002218 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002219 {
2220 pixel=Dc;
2221 break;
2222 }
cristy99abff32011-12-24 20:45:16 +00002223 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002224 {
2225 pixel=Sc;
2226 break;
2227 }
cristy7133e642012-08-14 11:04:11 +00002228 CompositeHCL(destination_pixel.red,destination_pixel.green,
2229 destination_pixel.blue,&hue,&chroma,&luma);
2230 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2231 &sans,&chroma,&sans);
2232 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002233 switch (channel)
2234 {
2235 case RedPixelChannel: pixel=red; break;
2236 case GreenPixelChannel: pixel=green; break;
2237 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002238 default: pixel=Dc; break;
2239 }
2240 break;
2241 }
2242 case ScreenCompositeOp:
2243 {
2244 /*
2245 Screen: a negated multiply:
2246
2247 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2248 */
cristy99abff32011-12-24 20:45:16 +00002249 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002250 break;
2251 }
2252 case SoftLightCompositeOp:
2253 {
2254 /*
2255 Refer to the March 2009 SVG specification.
2256 */
2257 if ((2.0*Sca) < Sa)
2258 {
cristy99abff32011-12-24 20:45:16 +00002259 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2260 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002261 break;
2262 }
2263 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2264 {
cristy99abff32011-12-24 20:45:16 +00002265 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2266 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2267 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002268 break;
2269 }
cristy99abff32011-12-24 20:45:16 +00002270 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2271 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002272 break;
2273 }
2274 case ThresholdCompositeOp:
2275 {
cristy7159f662012-10-28 17:32:43 +00002276 MagickRealType
cristye4a40472011-12-22 02:56:19 +00002277 delta;
2278
2279 delta=Sc-Dc;
cristy7159f662012-10-28 17:32:43 +00002280 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002281 {
2282 pixel=gamma*Dc;
2283 break;
2284 }
2285 pixel=gamma*(Dc+delta*amount);
2286 break;
2287 }
2288 case VividLightCompositeOp:
2289 {
2290 /*
2291 VividLight: A Photoshop 7 composition method. See
2292 http://www.simplefilter.de/en/basics/mixmods.html.
2293
2294 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2295 */
2296 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2297 {
cristy99abff32011-12-24 20:45:16 +00002298 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002299 break;
2300 }
2301 if ((2.0*Sca) <= Sa)
2302 {
cristy99abff32011-12-24 20:45:16 +00002303 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2304 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002305 break;
2306 }
cristy99abff32011-12-24 20:45:16 +00002307 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2308 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002309 break;
2310 }
2311 case XorCompositeOp:
2312 {
cristy99abff32011-12-24 20:45:16 +00002313 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002314 break;
2315 }
2316 default:
2317 {
2318 pixel=Sc;
2319 break;
2320 }
2321 }
2322 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002323 }
cristyed231572011-07-14 02:18:59 +00002324 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002325 channels=GetPixelChannels(composite_image);
2326 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002327 p=pixels;
cristyed231572011-07-14 02:18:59 +00002328 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002329 }
2330 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2331 status=MagickFalse;
2332 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2333 {
2334 MagickBooleanType
2335 proceed;
2336
2337#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002338 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002339#endif
2340 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2341 image->rows);
2342 if (proceed == MagickFalse)
2343 status=MagickFalse;
2344 }
2345 }
2346 composite_view=DestroyCacheView(composite_view);
2347 image_view=DestroyCacheView(image_view);
2348 if (destination_image != (Image * ) NULL)
2349 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002350 else
2351 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00002352 return(status);
2353}
2354
2355/*
2356%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2357% %
2358% %
2359% %
2360% T e x t u r e I m a g e %
2361% %
2362% %
2363% %
2364%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2365%
2366% TextureImage() repeatedly tiles the texture image across and down the image
2367% canvas.
2368%
2369% The format of the TextureImage method is:
2370%
cristy30d8c942012-02-07 13:44:59 +00002371% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002372% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002373%
2374% A description of each parameter follows:
2375%
2376% o image: the image.
2377%
cristye6178502011-12-23 17:02:29 +00002378% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002379%
2380*/
cristy30d8c942012-02-07 13:44:59 +00002381MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2382 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002383{
2384#define TextureImageTag "Texture/Image"
2385
2386 CacheView
2387 *image_view,
2388 *texture_view;
2389
cristy30d8c942012-02-07 13:44:59 +00002390 Image
2391 *texture_image;
2392
cristy4c08aed2011-07-01 19:47:50 +00002393 MagickBooleanType
2394 status;
2395
2396 ssize_t
2397 y;
2398
2399 assert(image != (Image *) NULL);
2400 if (image->debug != MagickFalse)
2401 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2402 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002403 if (texture == (const Image *) NULL)
2404 return(MagickFalse);
2405 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2406 return(MagickFalse);
2407 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002408 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002409 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002410 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002411 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2412 exception);
cristy4c08aed2011-07-01 19:47:50 +00002413 status=MagickTrue;
2414 if ((image->compose != CopyCompositeOp) &&
cristy8a46d822012-08-28 23:32:39 +00002415 ((image->compose != OverCompositeOp) || (image->alpha_trait == BlendPixelTrait) ||
2416 (texture_image->alpha_trait == BlendPixelTrait)))
cristy4c08aed2011-07-01 19:47:50 +00002417 {
2418 /*
2419 Tile texture onto the image background.
2420 */
cristye6178502011-12-23 17:02:29 +00002421 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002422 {
2423 register ssize_t
2424 x;
2425
2426 if (status == MagickFalse)
2427 continue;
cristye6178502011-12-23 17:02:29 +00002428 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002429 {
2430 MagickBooleanType
2431 thread_status;
2432
cristyfeb3e962012-03-29 17:25:55 +00002433 thread_status=CompositeImage(image,texture_image,image->compose,
2434 MagickFalse,x+texture_image->tile_offset.x,y+
2435 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002436 if (thread_status == MagickFalse)
2437 {
2438 status=thread_status;
2439 break;
2440 }
2441 }
2442 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2443 {
2444 MagickBooleanType
2445 proceed;
2446
cristy4c08aed2011-07-01 19:47:50 +00002447 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2448 y,image->rows);
2449 if (proceed == MagickFalse)
2450 status=MagickFalse;
2451 }
2452 }
2453 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2454 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002455 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002456 return(status);
2457 }
2458 /*
2459 Tile texture onto the image background (optimized).
2460 */
2461 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00002462 texture_view=AcquireVirtualCacheView(texture_image,exception);
2463 image_view=AcquireAuthenticCacheView(image,exception);
cristye8914392012-12-16 21:16:11 +00002464#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00002465 #pragma omp parallel for schedule(static,4) shared(status) \
2466 magick_threads(texture_image,image,1,1)
cristye8914392012-12-16 21:16:11 +00002467#endif
cristy4c08aed2011-07-01 19:47:50 +00002468 for (y=0; y < (ssize_t) image->rows; y++)
2469 {
2470 MagickBooleanType
2471 sync;
2472
2473 register const Quantum
2474 *p,
2475 *pixels;
2476
2477 register ssize_t
2478 x;
2479
2480 register Quantum
2481 *q;
2482
2483 size_t
2484 width;
2485
2486 if (status == MagickFalse)
2487 continue;
cristye6178502011-12-23 17:02:29 +00002488 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2489 (y+texture_image->tile_offset.y) % texture_image->rows,
2490 texture_image->columns,1,exception);
2491 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002492 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2493 {
2494 status=MagickFalse;
2495 continue;
2496 }
cristye6178502011-12-23 17:02:29 +00002497 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002498 {
2499 register ssize_t
cristye6178502011-12-23 17:02:29 +00002500 j;
cristy4c08aed2011-07-01 19:47:50 +00002501
2502 p=pixels;
cristye6178502011-12-23 17:02:29 +00002503 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002504 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2505 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002506 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002507 {
cristye6178502011-12-23 17:02:29 +00002508 register ssize_t
2509 i;
2510
cristy10a6c612012-01-29 21:41:05 +00002511 if (GetPixelMask(image,p) != 0)
2512 {
2513 p+=GetPixelChannels(texture_image);
2514 q+=GetPixelChannels(image);
2515 continue;
2516 }
cristye6178502011-12-23 17:02:29 +00002517 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2518 {
2519 PixelChannel
2520 channel;
2521
2522 PixelTrait
2523 texture_traits,
2524 traits;
2525
cristycf1296e2012-08-26 23:40:49 +00002526 channel=GetPixelChannelChannel(texture_image,i);
2527 texture_traits=GetPixelChannelTraits(texture_image,channel);
2528 traits=GetPixelChannelTraits(image,channel);
cristye6178502011-12-23 17:02:29 +00002529 if ((traits == UndefinedPixelTrait) ||
2530 (texture_traits == UndefinedPixelTrait))
2531 continue;
2532 SetPixelChannel(image,channel,p[i],q);
2533 }
2534 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002535 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002536 }
2537 }
2538 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2539 if (sync == MagickFalse)
2540 status=MagickFalse;
2541 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2542 {
2543 MagickBooleanType
2544 proceed;
2545
cristy4c08aed2011-07-01 19:47:50 +00002546 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2547 image->rows);
2548 if (proceed == MagickFalse)
2549 status=MagickFalse;
2550 }
2551 }
2552 texture_view=DestroyCacheView(texture_view);
2553 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002554 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002555 return(status);
2556}