blob: 2b2ab3ca31b50dd58d5540b994dcddbd8beab5cb [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;
cristydb070952012-04-20 14:33:00 +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) \
cristy4ee2b0c2012-05-15 00:30:35 +0000363 dynamic_number_threads(image,image->columns,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 {
cristy7159f662012-10-28 17:32:43 +0000417 MagickRealType
cristye4a40472011-12-22 02:56:19 +0000418 alpha,
419 Da,
420 Dc,
421 gamma,
422 Sa,
423 Sc;
424
425 register ssize_t
426 i;
427
cristyfeb3e962012-03-29 17:25:55 +0000428 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000429 {
430 if (x < x_offset)
431 {
432 q+=GetPixelChannels(image);
433 continue;
434 }
435 if ((x-x_offset) >= (ssize_t) composite_image->columns)
436 break;
437 }
438 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
439 ((x-x_offset) >= (ssize_t) composite_image->columns))
440 {
441 Quantum
442 source[MaxPixelChannels];
443
444 /*
445 Virtual composite:
446 Sc: source color.
447 Dc: destination color.
448 */
cristy10a6c612012-01-29 21:41:05 +0000449 if (GetPixelMask(image,q) != 0)
450 {
451 q+=GetPixelChannels(image);
452 continue;
453 }
cristyc94ba6f2012-01-29 23:19:58 +0000454 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
455 source,exception);
cristye4a40472011-12-22 02:56:19 +0000456 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
457 {
458 PixelChannel
459 channel;
460
461 PixelTrait
462 composite_traits,
463 traits;
464
cristycf1296e2012-08-26 23:40:49 +0000465 channel=GetPixelChannelChannel(image,i);
466 traits=GetPixelChannelTraits(image,channel);
467 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000468 if ((traits == UndefinedPixelTrait) ||
469 (composite_traits == UndefinedPixelTrait))
470 continue;
471 q[i]=source[channel];
472 }
473 q+=GetPixelChannels(image);
474 continue;
475 }
476 /*
477 Authentic composite:
478 Sa: normalized source alpha.
479 Da: normalized destination alpha.
480 */
cristyc94ba6f2012-01-29 23:19:58 +0000481 if (GetPixelMask(composite_image,p) != 0)
482 {
483 p+=GetPixelChannels(composite_image);
484 channels=GetPixelChannels(composite_image);
485 if (p >= (pixels+channels*composite_image->columns))
486 p=pixels;
487 q+=GetPixelChannels(image);
488 continue;
489 }
cristye4a40472011-12-22 02:56:19 +0000490 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
491 Da=QuantumScale*GetPixelAlpha(image,q);
492 alpha=Sa*(-Da)+Sa+Da;
493 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
494 {
495 PixelChannel
496 channel;
497
498 PixelTrait
499 composite_traits,
500 traits;
501
cristycf1296e2012-08-26 23:40:49 +0000502 channel=GetPixelChannelChannel(image,i);
503 traits=GetPixelChannelTraits(image,channel);
504 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000505 if ((traits == UndefinedPixelTrait) ||
506 (composite_traits == UndefinedPixelTrait))
507 continue;
508 if ((traits & CopyPixelTrait) != 0)
509 {
510 if (channel != AlphaPixelChannel)
511 {
512 /*
513 Copy channel.
514 */
515 q[i]=GetPixelChannel(composite_image,channel,p);
516 continue;
517 }
518 /*
519 Set alpha channel.
520 */
521 q[i]=ClampToQuantum(QuantumRange*alpha);
522 continue;
523 }
524 /*
525 Sc: source color.
526 Dc: destination color.
527 */
cristy7159f662012-10-28 17:32:43 +0000528 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
529 Dc=(MagickRealType) q[i];
cristy3e3ec3a2012-11-03 23:11:06 +0000530 gamma=PerceptibleReciprocal(alpha);
cristye4a40472011-12-22 02:56:19 +0000531 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
532 }
533 p+=GetPixelChannels(composite_image);
534 channels=GetPixelChannels(composite_image);
535 if (p >= (pixels+channels*composite_image->columns))
536 p=pixels;
537 q+=GetPixelChannels(image);
538 }
539 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
540 status=MagickFalse;
541 if (image->progress_monitor != (MagickProgressMonitor) NULL)
542 {
543 MagickBooleanType
544 proceed;
545
546#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000547 #pragma omp critical (MagickCore_CompositeImage)
cristye4a40472011-12-22 02:56:19 +0000548#endif
549 proceed=SetImageProgress(image,CompositeImageTag,progress++,
550 image->rows);
551 if (proceed == MagickFalse)
552 status=MagickFalse;
553 }
554 }
555 composite_view=DestroyCacheView(composite_view);
556 image_view=DestroyCacheView(image_view);
557 return(status);
558}
559
cristy4c08aed2011-07-01 19:47:50 +0000560MagickExport MagickBooleanType CompositeImage(Image *image,
cristya865ccd2012-07-28 00:33:10 +0000561 const Image *composite,const CompositeOperator compose,
cristy44c98412012-03-31 18:25:40 +0000562 const MagickBooleanType clip_to_self,const ssize_t x_offset,
563 const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000564{
cristy4c08aed2011-07-01 19:47:50 +0000565#define CompositeImageTag "Composite/Image"
566
567 CacheView
568 *composite_view,
569 *image_view;
570
cristy4c08aed2011-07-01 19:47:50 +0000571 GeometryInfo
572 geometry_info;
573
574 Image
cristya865ccd2012-07-28 00:33:10 +0000575 *composite_image,
cristy4c08aed2011-07-01 19:47:50 +0000576 *destination_image;
577
578 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000579 status;
580
581 MagickOffsetType
582 progress;
583
cristy7159f662012-10-28 17:32:43 +0000584 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000585 amount,
586 destination_dissolve,
587 midpoint,
cristy7133e642012-08-14 11:04:11 +0000588 percent_luma,
589 percent_chroma,
cristy4c08aed2011-07-01 19:47:50 +0000590 source_dissolve,
591 threshold;
592
593 MagickStatusType
594 flags;
595
cristyd197cbb2012-01-13 02:14:12 +0000596 ssize_t
597 y;
598
cristy4c08aed2011-07-01 19:47:50 +0000599 assert(image != (Image *) NULL);
600 assert(image->signature == MagickSignature);
601 if (image->debug != MagickFalse)
602 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristya865ccd2012-07-28 00:33:10 +0000603 assert(composite!= (Image *) NULL);
604 assert(composite->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000605 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000606 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +0000607 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
608 if (composite_image == (const Image *) NULL)
609 return(MagickFalse);
cristyeb16d5e2012-09-12 20:11:53 +0000610 if (IsGrayColorspace(image->colorspace) != MagickFalse)
611 (void) SetImageColorspace(image,RGBColorspace,exception);
cristya4a22a22012-08-01 23:16:38 +0000612 (void) SetImageColorspace(composite_image,image->colorspace,exception);
cristye4a40472011-12-22 02:56:19 +0000613 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
614 {
cristyfeb3e962012-03-29 17:25:55 +0000615 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
616 y_offset,exception);
cristya865ccd2012-07-28 00:33:10 +0000617 composite_image=DestroyImage(composite_image);
cristye4a40472011-12-22 02:56:19 +0000618 return(status);
619 }
cristy4c08aed2011-07-01 19:47:50 +0000620 destination_image=(Image *) NULL;
621 amount=0.5;
622 destination_dissolve=1.0;
cristy7133e642012-08-14 11:04:11 +0000623 percent_luma=100.0;
624 percent_chroma=100.0;
cristy4c08aed2011-07-01 19:47:50 +0000625 source_dissolve=1.0;
626 threshold=0.05f;
627 switch (compose)
628 {
cristy4c08aed2011-07-01 19:47:50 +0000629 case CopyCompositeOp:
630 {
631 if ((x_offset < 0) || (y_offset < 0))
632 break;
633 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
634 break;
635 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
636 break;
637 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +0000638 composite_view=AcquireVirtualCacheView(composite_image,exception);
639 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000640#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000641 #pragma omp parallel for schedule(static,4) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000642 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000643#endif
644 for (y=0; y < (ssize_t) composite_image->rows; y++)
645 {
646 MagickBooleanType
647 sync;
648
649 register const Quantum
650 *p;
651
652 register Quantum
653 *q;
654
655 register ssize_t
656 x;
657
658 if (status == MagickFalse)
659 continue;
660 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
661 1,exception);
662 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
663 composite_image->columns,1,exception);
664 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
665 {
666 status=MagickFalse;
667 continue;
668 }
669 for (x=0; x < (ssize_t) composite_image->columns; x++)
670 {
cristybdecccc2011-12-24 22:52:16 +0000671 register ssize_t
672 i;
673
cristy665e18f2012-05-17 12:39:54 +0000674 if (GetPixelMask(composite_image,p) != 0)
cristy10a6c612012-01-29 21:41:05 +0000675 {
676 p+=GetPixelChannels(composite_image);
677 q+=GetPixelChannels(image);
678 continue;
679 }
cristybdecccc2011-12-24 22:52:16 +0000680 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
681 {
682 PixelChannel
683 channel;
684
685 PixelTrait
686 composite_traits,
687 traits;
688
cristycf1296e2012-08-26 23:40:49 +0000689 channel=GetPixelChannelChannel(composite_image,i);
690 composite_traits=GetPixelChannelTraits(composite_image,channel);
691 traits=GetPixelChannelTraits(image,channel);
cristybdecccc2011-12-24 22:52:16 +0000692 if ((traits == UndefinedPixelTrait) ||
693 (composite_traits == UndefinedPixelTrait))
694 continue;
695 SetPixelChannel(image,channel,p[i],q);
696 }
cristyed231572011-07-14 02:18:59 +0000697 p+=GetPixelChannels(composite_image);
698 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000699 }
700 sync=SyncCacheViewAuthenticPixels(image_view,exception);
701 if (sync == MagickFalse)
702 status=MagickFalse;
703 if (image->progress_monitor != (MagickProgressMonitor) NULL)
704 {
705 MagickBooleanType
706 proceed;
707
708#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000709 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000710#endif
711 proceed=SetImageProgress(image,CompositeImageTag,
712 (MagickOffsetType) y,image->rows);
713 if (proceed == MagickFalse)
714 status=MagickFalse;
715 }
716 }
717 composite_view=DestroyCacheView(composite_view);
718 image_view=DestroyCacheView(image_view);
cristy44886b92012-07-28 13:07:09 +0000719 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000720 return(status);
721 }
cristye4a40472011-12-22 02:56:19 +0000722 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000723 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000724 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000725 {
726 /*
727 Modify destination outside the overlaid region and require an alpha
728 channel to exist, to add transparency.
729 */
cristy8a46d822012-08-28 23:32:39 +0000730 if (image->alpha_trait != BlendPixelTrait)
cristy42c41de2012-05-05 18:36:31 +0000731 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000732 break;
733 }
734 case BlurCompositeOp:
735 {
736 CacheView
737 *composite_view,
738 *destination_view;
739
cristyfeb3e962012-03-29 17:25:55 +0000740 const char
741 *value;
742
cristy7159f662012-10-28 17:32:43 +0000743 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000744 angle_range,
745 angle_start,
746 height,
747 width;
748
cristybce4f4a2012-10-14 14:57:47 +0000749 PixelInfo
750 pixel;
751
cristy4c08aed2011-07-01 19:47:50 +0000752 ResampleFilter
753 *resample_filter;
754
755 SegmentInfo
756 blur;
757
758 /*
anthony9cb63cc2012-04-25 06:10:49 +0000759 Blur Image by resampling.
760
cristy4c08aed2011-07-01 19:47:50 +0000761 Blur Image dictated by an overlay gradient map: X = red_channel;
762 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
763 */
764 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000765 exception);
cristy4c08aed2011-07-01 19:47:50 +0000766 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000767 {
768 composite_image=DestroyImage(composite_image);
769 return(MagickFalse);
770 }
cristy4c08aed2011-07-01 19:47:50 +0000771 /*
anthony9cb63cc2012-04-25 06:10:49 +0000772 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000773 */
774 SetGeometryInfo(&geometry_info);
775 flags=NoValue;
776 value=GetImageArtifact(composite_image,"compose:args");
777 if (value != (char *) NULL)
778 flags=ParseGeometry(value,&geometry_info);
anthonyd2923912012-04-23 13:06:53 +0000779 if ((flags & WidthValue) == 0 ) {
780 (void) ThrowMagickException(exception,GetMagickModule(),
781 OptionWarning,"InvalidSetting","'%s' '%s'",
anthony9cb63cc2012-04-25 06:10:49 +0000782 "compose:args",value);
cristy44886b92012-07-28 13:07:09 +0000783 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000784 destination_image=DestroyImage(destination_image);
785 return(MagickFalse);
786 }
anthony9cb63cc2012-04-25 06:10:49 +0000787 /*
788 Users input sigma now needs to be converted to the EWA ellipse size.
789 The filter defaults to a sigma of 0.5 so to make this match the
790 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000791 */
792 width=height=geometry_info.rho*2.0;
793 if ((flags & HeightValue) != 0 )
794 height=geometry_info.sigma*2.0;
cristy7159f662012-10-28 17:32:43 +0000795 /*
796 Default the unrotated ellipse width and height axis vectors.
797 */
anthonyd2923912012-04-23 13:06:53 +0000798 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000799 blur.x2=0.0;
800 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000801 blur.y2=height;
802 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000803 if ((flags & XValue) != 0 )
804 {
cristy7159f662012-10-28 17:32:43 +0000805 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000806 angle;
807
808 angle=DegreesToRadians(geometry_info.xi);
809 blur.x1=width*cos(angle);
810 blur.x2=width*sin(angle);
811 blur.y1=(-height*sin(angle));
812 blur.y2=height*cos(angle);
813 }
anthonyd2923912012-04-23 13:06:53 +0000814 /* Otherwise lets set a angle range and calculate in the loop */
815 angle_start=0.0;
816 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000817 if ((flags & YValue) != 0 )
818 {
819 angle_start=DegreesToRadians(geometry_info.xi);
820 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
821 }
822 /*
anthony9cb63cc2012-04-25 06:10:49 +0000823 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000824
anthony9cb63cc2012-04-25 06:10:49 +0000825 As the minimum ellipse radius of support*1.0 the EWA algorithm
826 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
827 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000828
anthony9cb63cc2012-04-25 06:10:49 +0000829 The solution (as well as the problem of preventing any user
830 expert filter settings, is to set our own user settings, then
831 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000832 */
cristy8a11cb12011-10-19 23:53:34 +0000833 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000834 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000835
836 /* do the variable blurring of each pixel in image */
837 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000838 composite_view=AcquireVirtualCacheView(composite_image,exception);
839 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000840 for (y=0; y < (ssize_t) composite_image->rows; y++)
841 {
842 MagickBooleanType
843 sync;
844
845 register const Quantum
846 *restrict p;
847
848 register Quantum
849 *restrict q;
850
851 register ssize_t
852 x;
853
854 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
855 continue;
856 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
857 1,exception);
858 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000859 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000860 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
861 break;
862 for (x=0; x < (ssize_t) composite_image->columns; x++)
863 {
864 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
865 {
cristyed231572011-07-14 02:18:59 +0000866 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000867 continue;
868 }
869 if (fabs(angle_range) > MagickEpsilon)
870 {
cristy7159f662012-10-28 17:32:43 +0000871 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000872 angle;
873
874 angle=angle_start+angle_range*QuantumScale*
875 GetPixelBlue(composite_image,p);
876 blur.x1=width*cos(angle);
877 blur.x2=width*sin(angle);
878 blur.y1=(-height*sin(angle));
879 blur.y2=height*cos(angle);
880 }
anthonyd2923912012-04-23 13:06:53 +0000881#if 0
882 if ( x == 10 && y == 60 ) {
cristy7159f662012-10-28 17:32:43 +0000883 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
884 blur.x2,blur.y1, blur.y2);
885 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
886 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
anthonyd2923912012-04-23 13:06:53 +0000887#endif
888 ScaleResampleFilter(resample_filter,
889 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
890 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
891 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
892 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000893 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000894 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000895 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000896 p+=GetPixelChannels(composite_image);
897 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000898 }
899 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
900 if (sync == MagickFalse)
901 break;
902 }
903 resample_filter=DestroyResampleFilter(resample_filter);
904 composite_view=DestroyCacheView(composite_view);
905 destination_view=DestroyCacheView(destination_view);
cristyf661c4d2012-07-28 12:57:17 +0000906 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000907 composite_image=destination_image;
908 break;
909 }
910 case DisplaceCompositeOp:
911 case DistortCompositeOp:
912 {
913 CacheView
914 *composite_view,
915 *destination_view,
916 *image_view;
917
cristyfeb3e962012-03-29 17:25:55 +0000918 const char
919 *value;
920
cristy4c08aed2011-07-01 19:47:50 +0000921 PixelInfo
922 pixel;
923
cristy7159f662012-10-28 17:32:43 +0000924 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000925 horizontal_scale,
926 vertical_scale;
927
928 PointInfo
929 center,
930 offset;
931
932 /*
933 Displace/Distort based on overlay gradient map:
934 X = red_channel; Y = green_channel;
935 compose:args = x_scale[,y_scale[,center.x,center.y]]
936 */
937 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000938 exception);
cristy4c08aed2011-07-01 19:47:50 +0000939 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000940 {
941 composite_image=DestroyImage(composite_image);
942 return(MagickFalse);
943 }
cristy4c08aed2011-07-01 19:47:50 +0000944 SetGeometryInfo(&geometry_info);
945 flags=NoValue;
946 value=GetImageArtifact(composite_image,"compose:args");
947 if (value != (char *) NULL)
948 flags=ParseGeometry(value,&geometry_info);
949 if ((flags & (WidthValue|HeightValue)) == 0 )
950 {
951 if ((flags & AspectValue) == 0)
952 {
cristy7159f662012-10-28 17:32:43 +0000953 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
cristy4c08aed2011-07-01 19:47:50 +0000954 2.0;
cristy7159f662012-10-28 17:32:43 +0000955 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000956 }
957 else
958 {
cristy7159f662012-10-28 17:32:43 +0000959 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
960 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000961 }
962 }
963 else
964 {
965 horizontal_scale=geometry_info.rho;
966 vertical_scale=geometry_info.sigma;
967 if ((flags & PercentValue) != 0)
968 {
969 if ((flags & AspectValue) == 0)
970 {
971 horizontal_scale*=(composite_image->columns-1.0)/200.0;
972 vertical_scale*=(composite_image->rows-1.0)/200.0;
973 }
974 else
975 {
976 horizontal_scale*=(image->columns-1.0)/200.0;
977 vertical_scale*=(image->rows-1.0)/200.0;
978 }
979 }
980 if ((flags & HeightValue) == 0)
981 vertical_scale=horizontal_scale;
982 }
983 /*
984 Determine fixed center point for absolute distortion map
985 Absolute distort ==
986 Displace offset relative to a fixed absolute point
987 Select that point according to +X+Y user inputs.
988 default = center of overlay image
989 arg flag '!' = locations/percentage relative to background image
990 */
cristy7159f662012-10-28 17:32:43 +0000991 center.x=(MagickRealType) x_offset;
992 center.y=(MagickRealType) y_offset;
cristy4c08aed2011-07-01 19:47:50 +0000993 if (compose == DistortCompositeOp)
994 {
995 if ((flags & XValue) == 0)
996 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +0000997 center.x=(MagickRealType) (x_offset+(composite_image->columns-1)/
998 2.0);
cristy4c08aed2011-07-01 19:47:50 +0000999 else
cristy7159f662012-10-28 17:32:43 +00001000 center.x=(MagickRealType) ((image->columns-1)/2);
cristy4c08aed2011-07-01 19:47:50 +00001001 else
1002 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001003 center.x=(MagickRealType) x_offset+geometry_info.xi;
cristy4c08aed2011-07-01 19:47:50 +00001004 else
1005 center.x=geometry_info.xi;
1006 if ((flags & YValue) == 0)
1007 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001008 center.y=(MagickRealType) (y_offset+(composite_image->rows-1)/
1009 2.0);
cristy4c08aed2011-07-01 19:47:50 +00001010 else
cristy7159f662012-10-28 17:32:43 +00001011 center.y=(MagickRealType) ((image->rows-1)/2);
cristy4c08aed2011-07-01 19:47:50 +00001012 else
1013 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001014 center.y=(MagickRealType) y_offset+geometry_info.psi;
cristy4c08aed2011-07-01 19:47:50 +00001015 else
1016 center.y=geometry_info.psi;
1017 }
1018 /*
1019 Shift the pixel offset point as defined by the provided,
1020 displacement/distortion map. -- Like a lens...
1021 */
cristye10859a2011-12-18 22:28:59 +00001022 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +00001023 image_view=AcquireVirtualCacheView(image,exception);
1024 composite_view=AcquireVirtualCacheView(composite_image,exception);
1025 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001026 for (y=0; y < (ssize_t) composite_image->rows; y++)
1027 {
1028 MagickBooleanType
1029 sync;
1030
1031 register const Quantum
1032 *restrict p;
1033
1034 register Quantum
1035 *restrict q;
1036
1037 register ssize_t
1038 x;
1039
1040 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1041 continue;
1042 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1043 1,exception);
1044 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001045 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001046 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1047 break;
1048 for (x=0; x < (ssize_t) composite_image->columns; x++)
1049 {
1050 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1051 {
cristyed231572011-07-14 02:18:59 +00001052 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001053 continue;
1054 }
1055 /*
1056 Displace the offset.
1057 */
cristy7159f662012-10-28 17:32:43 +00001058 offset.x=(double) (horizontal_scale*(GetPixelRed(composite_image,p)-
1059 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1060 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1061 x : 0);
1062 offset.y=(double) (vertical_scale*(GetPixelGreen(composite_image,p)-
1063 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1064 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1065 y : 0);
cristy4c08aed2011-07-01 19:47:50 +00001066 (void) InterpolatePixelInfo(image,image_view,
1067 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1068 &pixel,exception);
1069 /*
1070 Mask with the 'invalid pixel mask' in alpha channel.
1071 */
cristy7159f662012-10-28 17:32:43 +00001072 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1073 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001074 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001075 p+=GetPixelChannels(composite_image);
1076 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001077 }
1078 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1079 if (sync == MagickFalse)
1080 break;
1081 }
1082 destination_view=DestroyCacheView(destination_view);
1083 composite_view=DestroyCacheView(composite_view);
1084 image_view=DestroyCacheView(image_view);
cristyf661c4d2012-07-28 12:57:17 +00001085 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001086 composite_image=destination_image;
1087 break;
1088 }
1089 case DissolveCompositeOp:
1090 {
cristyfeb3e962012-03-29 17:25:55 +00001091 const char
1092 *value;
1093
cristy4c08aed2011-07-01 19:47:50 +00001094 /*
1095 Geometry arguments to dissolve factors.
1096 */
1097 value=GetImageArtifact(composite_image,"compose:args");
1098 if (value != (char *) NULL)
1099 {
1100 flags=ParseGeometry(value,&geometry_info);
1101 source_dissolve=geometry_info.rho/100.0;
1102 destination_dissolve=1.0;
1103 if ((source_dissolve-MagickEpsilon) < 0.0)
1104 source_dissolve=0.0;
1105 if ((source_dissolve+MagickEpsilon) > 1.0)
1106 {
1107 destination_dissolve=2.0-source_dissolve;
1108 source_dissolve=1.0;
1109 }
1110 if ((flags & SigmaValue) != 0)
1111 destination_dissolve=geometry_info.sigma/100.0;
1112 if ((destination_dissolve-MagickEpsilon) < 0.0)
1113 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001114 /* posible speed up? -- from IMv6 update
1115 clip_to_self=MagickFalse;
1116 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1117 {
1118 destination_dissolve=1.0;
1119 clip_to_self=MagickTrue;
1120 }
1121 */
cristy4c08aed2011-07-01 19:47:50 +00001122 }
1123 break;
1124 }
1125 case BlendCompositeOp:
1126 {
cristyfeb3e962012-03-29 17:25:55 +00001127 const char
1128 *value;
1129
cristy4c08aed2011-07-01 19:47:50 +00001130 value=GetImageArtifact(composite_image,"compose:args");
1131 if (value != (char *) NULL)
1132 {
1133 flags=ParseGeometry(value,&geometry_info);
1134 source_dissolve=geometry_info.rho/100.0;
1135 destination_dissolve=1.0-source_dissolve;
1136 if ((flags & SigmaValue) != 0)
1137 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001138 }
1139 break;
1140 }
1141 case MathematicsCompositeOp:
1142 {
cristyfeb3e962012-03-29 17:25:55 +00001143 const char
1144 *value;
1145
cristy4c08aed2011-07-01 19:47:50 +00001146 /*
1147 Just collect the values from "compose:args", setting.
1148 Unused values are set to zero automagically.
1149
1150 Arguments are normally a comma separated list, so this probably should
1151 be changed to some 'general comma list' parser, (with a minimum
1152 number of values)
1153 */
1154 SetGeometryInfo(&geometry_info);
1155 value=GetImageArtifact(composite_image,"compose:args");
1156 if (value != (char *) NULL)
1157 (void) ParseGeometry(value,&geometry_info);
1158 break;
1159 }
1160 case ModulateCompositeOp:
1161 {
cristyfeb3e962012-03-29 17:25:55 +00001162 const char
1163 *value;
1164
cristy4c08aed2011-07-01 19:47:50 +00001165 /*
cristy7133e642012-08-14 11:04:11 +00001166 Determine the luma and chroma scale.
cristy4c08aed2011-07-01 19:47:50 +00001167 */
1168 value=GetImageArtifact(composite_image,"compose:args");
1169 if (value != (char *) NULL)
1170 {
1171 flags=ParseGeometry(value,&geometry_info);
cristy7133e642012-08-14 11:04:11 +00001172 percent_luma=geometry_info.rho;
cristy4c08aed2011-07-01 19:47:50 +00001173 if ((flags & SigmaValue) != 0)
cristy7133e642012-08-14 11:04:11 +00001174 percent_chroma=geometry_info.sigma;
cristy4c08aed2011-07-01 19:47:50 +00001175 }
1176 break;
1177 }
1178 case ThresholdCompositeOp:
1179 {
cristyfeb3e962012-03-29 17:25:55 +00001180 const char
1181 *value;
1182
cristy4c08aed2011-07-01 19:47:50 +00001183 /*
1184 Determine the amount and threshold.
1185 */
1186 value=GetImageArtifact(composite_image,"compose:args");
1187 if (value != (char *) NULL)
1188 {
1189 flags=ParseGeometry(value,&geometry_info);
1190 amount=geometry_info.rho;
1191 threshold=geometry_info.sigma;
1192 if ((flags & SigmaValue) == 0)
1193 threshold=0.05f;
1194 }
1195 threshold*=QuantumRange;
1196 break;
1197 }
1198 default:
1199 break;
1200 }
cristy4c08aed2011-07-01 19:47:50 +00001201 /*
1202 Composite image.
1203 */
1204 status=MagickTrue;
1205 progress=0;
cristy7159f662012-10-28 17:32:43 +00001206 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristydb070952012-04-20 14:33:00 +00001207 composite_view=AcquireVirtualCacheView(composite_image,exception);
1208 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001209#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001210 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +00001211 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001212#endif
1213 for (y=0; y < (ssize_t) image->rows; y++)
1214 {
1215 const Quantum
1216 *pixels;
1217
cristy7159f662012-10-28 17:32:43 +00001218 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001219 blue,
cristy7133e642012-08-14 11:04:11 +00001220 luma,
cristye4a40472011-12-22 02:56:19 +00001221 green,
cristy4c08aed2011-07-01 19:47:50 +00001222 hue,
cristye4a40472011-12-22 02:56:19 +00001223 red,
cristy7133e642012-08-14 11:04:11 +00001224 chroma;
cristy4c08aed2011-07-01 19:47:50 +00001225
cristyddeeea22012-04-12 01:33:09 +00001226 PixelInfo
1227 destination_pixel,
1228 source_pixel;
1229
cristy4c08aed2011-07-01 19:47:50 +00001230 register const Quantum
1231 *restrict p;
1232
1233 register Quantum
1234 *restrict q;
1235
1236 register ssize_t
1237 x;
1238
1239 if (status == MagickFalse)
1240 continue;
cristyfeb3e962012-03-29 17:25:55 +00001241 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001242 {
1243 if (y < y_offset)
1244 continue;
1245 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1246 continue;
1247 }
1248 /*
1249 If pixels is NULL, y is outside overlay region.
1250 */
1251 pixels=(Quantum *) NULL;
1252 p=(Quantum *) NULL;
1253 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1254 {
1255 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1256 composite_image->columns,1,exception);
1257 if (p == (const Quantum *) NULL)
1258 {
1259 status=MagickFalse;
1260 continue;
1261 }
1262 pixels=p;
1263 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001264 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001265 }
1266 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001267 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001268 {
1269 status=MagickFalse;
1270 continue;
1271 }
cristy4c08aed2011-07-01 19:47:50 +00001272 hue=0.0;
cristy7133e642012-08-14 11:04:11 +00001273 chroma=0.0;
1274 luma=0.0;
cristyddeeea22012-04-12 01:33:09 +00001275 GetPixelInfo(image,&destination_pixel);
1276 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001277 for (x=0; x < (ssize_t) image->columns; x++)
1278 {
cristy7159f662012-10-28 17:32:43 +00001279 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001280 alpha,
1281 Da,
1282 Dc,
1283 Dca,
1284 gamma,
1285 Sa,
1286 Sc,
1287 Sca;
1288
1289 register ssize_t
1290 i;
1291
cristy564a5692012-01-20 23:56:26 +00001292 size_t
1293 channels;
1294
cristyfeb3e962012-03-29 17:25:55 +00001295 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001296 {
1297 if (x < x_offset)
1298 {
cristyed231572011-07-14 02:18:59 +00001299 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001300 continue;
1301 }
1302 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1303 break;
1304 }
cristye4a40472011-12-22 02:56:19 +00001305 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1306 ((x-x_offset) >= (ssize_t) composite_image->columns))
1307 {
1308 Quantum
1309 source[MaxPixelChannels];
1310
1311 /*
1312 Virtual composite:
1313 Sc: source color.
1314 Dc: destination color.
1315 */
1316 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1317 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001318 if (GetPixelMask(image,q) != 0)
1319 {
1320 q+=GetPixelChannels(image);
1321 continue;
1322 }
cristye4a40472011-12-22 02:56:19 +00001323 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1324 {
cristy7159f662012-10-28 17:32:43 +00001325 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001326 pixel;
1327
1328 PixelChannel
1329 channel;
1330
1331 PixelTrait
1332 composite_traits,
1333 traits;
1334
cristycf1296e2012-08-26 23:40:49 +00001335 channel=GetPixelChannelChannel(image,i);
1336 traits=GetPixelChannelTraits(image,channel);
1337 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001338 if ((traits == UndefinedPixelTrait) ||
1339 (composite_traits == UndefinedPixelTrait))
1340 continue;
1341 switch (compose)
1342 {
cristyc8d63672012-01-11 13:03:13 +00001343 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001344 case ChangeMaskCompositeOp:
1345 case CopyAlphaCompositeOp:
1346 case DstAtopCompositeOp:
1347 case DstInCompositeOp:
1348 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001349 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001350 case OutCompositeOp:
1351 case SrcInCompositeOp:
1352 case SrcOutCompositeOp:
1353 {
cristy7159f662012-10-28 17:32:43 +00001354 pixel=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001355 if (channel == AlphaPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001356 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001357 break;
1358 }
1359 case ClearCompositeOp:
1360 case CopyCompositeOp:
1361 case ReplaceCompositeOp:
1362 case SrcCompositeOp:
1363 {
1364 if (channel == AlphaPixelChannel)
1365 {
cristy7159f662012-10-28 17:32:43 +00001366 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001367 break;
1368 }
1369 pixel=0.0;
1370 break;
1371 }
cristy99abff32011-12-24 20:45:16 +00001372 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001373 case DissolveCompositeOp:
1374 {
1375 if (channel == AlphaPixelChannel)
1376 {
1377 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1378 source);
1379 break;
1380 }
cristy7159f662012-10-28 17:32:43 +00001381 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001382 break;
1383 }
1384 default:
1385 {
cristy7159f662012-10-28 17:32:43 +00001386 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001387 break;
1388 }
1389 }
1390 q[i]=ClampToQuantum(pixel);
1391 }
1392 q+=GetPixelChannels(image);
1393 continue;
1394 }
1395 /*
1396 Authentic composite:
1397 Sa: normalized source alpha.
1398 Da: normalized destination alpha.
1399 */
1400 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1401 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001402 switch (compose)
1403 {
cristye4a40472011-12-22 02:56:19 +00001404 case BumpmapCompositeOp:
1405 {
1406 alpha=GetPixelIntensity(composite_image,p)*Sa;
1407 break;
1408 }
cristycdc168f2011-12-21 15:24:39 +00001409 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001410 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001411 case DifferenceCompositeOp:
1412 case DivideDstCompositeOp:
1413 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001414 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001415 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001416 case LinearBurnCompositeOp:
1417 case LinearDodgeCompositeOp:
1418 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001419 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001420 case MinusDstCompositeOp:
1421 case MinusSrcCompositeOp:
1422 case ModulusAddCompositeOp:
1423 case ModulusSubtractCompositeOp:
1424 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001425 case OverlayCompositeOp:
1426 case PegtopLightCompositeOp:
1427 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001428 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001429 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001430 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001431 {
1432 alpha=RoundToUnity(Sa+Da-Sa*Da);
1433 break;
1434 }
1435 case DarkenCompositeOp:
1436 case DstAtopCompositeOp:
1437 case DstInCompositeOp:
1438 case InCompositeOp:
1439 case LightenCompositeOp:
1440 case SrcInCompositeOp:
1441 {
1442 alpha=Sa*Da;
1443 break;
1444 }
1445 case DissolveCompositeOp:
1446 {
1447 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1448 Sa+destination_dissolve*Da;
1449 break;
1450 }
1451 case DstOverCompositeOp:
1452 {
1453 alpha=Da*(-Sa)+Da+Sa;
1454 break;
1455 }
1456 case DstOutCompositeOp:
1457 {
1458 alpha=Da*(1.0-Sa);
1459 break;
1460 }
1461 case OutCompositeOp:
1462 case SrcOutCompositeOp:
1463 {
1464 alpha=Sa*(1.0-Da);
1465 break;
1466 }
1467 case OverCompositeOp:
1468 case SrcOverCompositeOp:
1469 {
1470 alpha=Sa*(-Da)+Sa+Da;
1471 break;
1472 }
cristy99abff32011-12-24 20:45:16 +00001473 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001474 case PlusCompositeOp:
1475 {
1476 alpha=RoundToUnity(Sa+Da);
1477 break;
1478 }
cristy4c08aed2011-07-01 19:47:50 +00001479 case XorCompositeOp:
1480 {
cristye4a40472011-12-22 02:56:19 +00001481 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001482 break;
1483 }
1484 default:
cristye4a40472011-12-22 02:56:19 +00001485 {
1486 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001487 break;
cristye4a40472011-12-22 02:56:19 +00001488 }
cristy4c08aed2011-07-01 19:47:50 +00001489 }
cristy10a6c612012-01-29 21:41:05 +00001490 if (GetPixelMask(image,p) != 0)
1491 {
1492 p+=GetPixelChannels(composite_image);
1493 q+=GetPixelChannels(image);
1494 continue;
1495 }
cristy9d3d2792012-04-14 15:15:19 +00001496 switch (compose)
1497 {
1498 case ColorizeCompositeOp:
1499 case HueCompositeOp:
1500 case LuminizeCompositeOp:
1501 case ModulateCompositeOp:
1502 case SaturateCompositeOp:
1503 {
1504 GetPixelInfoPixel(composite_image,p,&source_pixel);
1505 GetPixelInfoPixel(image,q,&destination_pixel);
1506 break;
1507 }
1508 default:
1509 break;
1510 }
cristye4a40472011-12-22 02:56:19 +00001511 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1512 {
cristy7159f662012-10-28 17:32:43 +00001513 MagickRealType
1514 pixel,
cristy564a5692012-01-20 23:56:26 +00001515 sans;
1516
cristye4a40472011-12-22 02:56:19 +00001517 PixelChannel
1518 channel;
cristye10859a2011-12-18 22:28:59 +00001519
cristye4a40472011-12-22 02:56:19 +00001520 PixelTrait
1521 composite_traits,
1522 traits;
1523
cristycf1296e2012-08-26 23:40:49 +00001524 channel=GetPixelChannelChannel(image,i);
1525 traits=GetPixelChannelTraits(image,channel);
1526 composite_traits=GetPixelChannelTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001527 if (traits == UndefinedPixelTrait)
1528 continue;
cristya7b07912012-01-11 20:01:32 +00001529 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001530 (composite_traits == UndefinedPixelTrait))
1531 continue;
1532 /*
1533 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001534 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001535 */
cristy7159f662012-10-28 17:32:43 +00001536 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
1537 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001538 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001539 {
cristye4a40472011-12-22 02:56:19 +00001540 if (channel != AlphaPixelChannel)
1541 {
1542 /*
1543 Copy channel.
1544 */
1545 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001546 continue;
cristye10859a2011-12-18 22:28:59 +00001547 }
cristye4a40472011-12-22 02:56:19 +00001548 /*
1549 Set alpha channel.
1550 */
cristye10859a2011-12-18 22:28:59 +00001551 switch (compose)
1552 {
cristyc8d63672012-01-11 13:03:13 +00001553 case AlphaCompositeOp:
1554 {
cristya7b07912012-01-11 20:01:32 +00001555 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001556 break;
1557 }
cristye4a40472011-12-22 02:56:19 +00001558 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001559 case CopyBlackCompositeOp:
1560 case CopyBlueCompositeOp:
1561 case CopyCyanCompositeOp:
1562 case CopyGreenCompositeOp:
1563 case CopyMagentaCompositeOp:
1564 case CopyRedCompositeOp:
1565 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001566 case SrcAtopCompositeOp:
1567 case DstCompositeOp:
1568 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001569 {
cristye4a40472011-12-22 02:56:19 +00001570 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001571 break;
1572 }
cristye10859a2011-12-18 22:28:59 +00001573 case ChangeMaskCompositeOp:
1574 {
cristye4a40472011-12-22 02:56:19 +00001575 MagickBooleanType
1576 equivalent;
1577
cristy7159f662012-10-28 17:32:43 +00001578 if (Da > ((MagickRealType) QuantumRange/2.0))
cristy99abff32011-12-24 20:45:16 +00001579 {
cristy7159f662012-10-28 17:32:43 +00001580 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001581 break;
1582 }
cristye4a40472011-12-22 02:56:19 +00001583 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001584 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001585 {
cristy7159f662012-10-28 17:32:43 +00001586 pixel=(MagickRealType) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001587 break;
1588 }
cristy7159f662012-10-28 17:32:43 +00001589 pixel=(MagickRealType) OpaqueAlpha;
cristye4a40472011-12-22 02:56:19 +00001590 break;
1591 }
cristy99abff32011-12-24 20:45:16 +00001592 case ClearCompositeOp:
1593 {
cristy7159f662012-10-28 17:32:43 +00001594 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001595 break;
1596 }
1597 case ColorizeCompositeOp:
1598 case HueCompositeOp:
1599 case LuminizeCompositeOp:
1600 case SaturateCompositeOp:
1601 {
1602 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1603 {
1604 pixel=QuantumRange*Da;
1605 break;
1606 }
1607 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1608 {
1609 pixel=QuantumRange*Sa;
1610 break;
1611 }
1612 if (Sa < Da)
1613 {
1614 pixel=QuantumRange*Da;
1615 break;
1616 }
1617 pixel=QuantumRange*Sa;
1618 break;
1619 }
cristy99abff32011-12-24 20:45:16 +00001620 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001621 {
1622 pixel=QuantumRange*Sa;
cristy8a46d822012-08-28 23:32:39 +00001623 if (composite_image->alpha_trait != BlendPixelTrait)
cristy24d5d722012-05-17 12:27:27 +00001624 pixel=GetPixelIntensity(composite_image,p);
1625 break;
1626 }
1627 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001628 case DisplaceCompositeOp:
1629 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001630 case DstAtopCompositeOp:
1631 case ReplaceCompositeOp:
1632 case SrcCompositeOp:
1633 {
1634 pixel=QuantumRange*Sa;
1635 break;
1636 }
1637 case DarkenIntensityCompositeOp:
1638 {
cristy99abff32011-12-24 20:45:16 +00001639 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1640 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001641 break;
1642 }
cristy98621462011-12-31 22:31:11 +00001643 case IntensityCompositeOp:
1644 {
cristyf13c5942012-08-08 23:50:11 +00001645 pixel=GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001646 break;
1647 }
cristye4a40472011-12-22 02:56:19 +00001648 case LightenIntensityCompositeOp:
1649 {
1650 pixel=Sa*GetPixelIntensity(composite_image,p) >
1651 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001652 break;
1653 }
cristy99abff32011-12-24 20:45:16 +00001654 case ModulateCompositeOp:
1655 {
1656 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1657 {
1658 pixel=QuantumRange*Da;
1659 break;
1660 }
1661 pixel=QuantumRange*Da;
1662 break;
1663 }
cristye10859a2011-12-18 22:28:59 +00001664 default:
1665 {
cristye4a40472011-12-22 02:56:19 +00001666 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001667 break;
1668 }
1669 }
cristye4a40472011-12-22 02:56:19 +00001670 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001671 continue;
1672 }
1673 /*
cristy99abff32011-12-24 20:45:16 +00001674 Porter-Duff compositions:
1675 Sca: source normalized color multiplied by alpha.
1676 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001677 */
cristy99abff32011-12-24 20:45:16 +00001678 Sca=QuantumScale*Sa*Sc;
1679 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001680 switch (compose)
1681 {
cristye10859a2011-12-18 22:28:59 +00001682 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001683 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001684 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001685 {
cristy99abff32011-12-24 20:45:16 +00001686 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001687 break;
1688 }
1689 default:
1690 break;
1691 }
cristy3e3ec3a2012-11-03 23:11:06 +00001692 gamma=PerceptibleReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001693 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001694 switch (compose)
1695 {
cristya7b07912012-01-11 20:01:32 +00001696 case AlphaCompositeOp:
1697 {
1698 pixel=QuantumRange*Sa;
1699 break;
1700 }
cristye4a40472011-12-22 02:56:19 +00001701 case AtopCompositeOp:
1702 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001703 {
cristye4a40472011-12-22 02:56:19 +00001704 pixel=Sc*Sa+Dc*(1.0-Sa);
1705 break;
cristye10859a2011-12-18 22:28:59 +00001706 }
cristye4a40472011-12-22 02:56:19 +00001707 case BlendCompositeOp:
1708 {
1709 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1710 break;
1711 }
1712 case BlurCompositeOp:
1713 case DisplaceCompositeOp:
1714 case DistortCompositeOp:
1715 case CopyCompositeOp:
1716 case ReplaceCompositeOp:
1717 case SrcCompositeOp:
1718 {
1719 pixel=Sc;
1720 break;
1721 }
1722 case BumpmapCompositeOp:
1723 {
cristy99abff32011-12-24 20:45:16 +00001724 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001725 {
1726 pixel=Dc;
1727 break;
1728 }
1729 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1730 break;
1731 }
cristy99abff32011-12-24 20:45:16 +00001732 case ChangeMaskCompositeOp:
1733 {
1734 pixel=Dc;
1735 break;
1736 }
1737 case ClearCompositeOp:
1738 {
1739 pixel=0.0;
1740 break;
1741 }
cristye4a40472011-12-22 02:56:19 +00001742 case ColorBurnCompositeOp:
1743 {
1744 /*
1745 Refer to the March 2009 SVG specification.
1746 */
1747 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1748 {
cristy99abff32011-12-24 20:45:16 +00001749 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001750 break;
1751 }
1752 if (Sca < MagickEpsilon)
1753 {
cristy99abff32011-12-24 20:45:16 +00001754 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001755 break;
1756 }
cristy99abff32011-12-24 20:45:16 +00001757 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1758 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001759 break;
1760 }
1761 case ColorDodgeCompositeOp:
1762 {
1763 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1764 {
cristy99abff32011-12-24 20:45:16 +00001765 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001766 break;
1767 }
1768 if (fabs(Sca-Sa) < MagickEpsilon)
1769 {
cristy99abff32011-12-24 20:45:16 +00001770 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001771 break;
1772 }
cristy99abff32011-12-24 20:45:16 +00001773 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001774 (1.0-Sa));
1775 break;
1776 }
1777 case ColorizeCompositeOp:
1778 {
cristy99abff32011-12-24 20:45:16 +00001779 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001780 {
1781 pixel=Dc;
1782 break;
1783 }
cristy99abff32011-12-24 20:45:16 +00001784 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001785 {
1786 pixel=Sc;
1787 break;
1788 }
cristy7133e642012-08-14 11:04:11 +00001789 CompositeHCL(destination_pixel.red,destination_pixel.green,
1790 destination_pixel.blue,&sans,&sans,&luma);
1791 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1792 &hue,&chroma,&sans);
1793 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001794 switch (channel)
1795 {
1796 case RedPixelChannel: pixel=red; break;
1797 case GreenPixelChannel: pixel=green; break;
1798 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001799 default: pixel=Dc; break;
1800 }
1801 break;
1802 }
cristye4a40472011-12-22 02:56:19 +00001803 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001804 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001805 {
cristy24d5d722012-05-17 12:27:27 +00001806 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001807 break;
1808 }
1809 case CopyBlackCompositeOp:
1810 {
cristyd197cbb2012-01-13 02:14:12 +00001811 if (channel == BlackPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001812 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001813 break;
1814 }
1815 case CopyBlueCompositeOp:
1816 case CopyYellowCompositeOp:
1817 {
cristyd197cbb2012-01-13 02:14:12 +00001818 if (channel == BluePixelChannel)
cristy7159f662012-10-28 17:32:43 +00001819 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001820 break;
1821 }
1822 case CopyGreenCompositeOp:
1823 case CopyMagentaCompositeOp:
1824 {
cristyd197cbb2012-01-13 02:14:12 +00001825 if (channel == GreenPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001826 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001827 break;
1828 }
1829 case CopyRedCompositeOp:
1830 case CopyCyanCompositeOp:
1831 {
cristyd197cbb2012-01-13 02:14:12 +00001832 if (channel == RedPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001833 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001834 break;
1835 }
cristy99abff32011-12-24 20:45:16 +00001836 case DarkenCompositeOp:
1837 {
1838 /*
1839 Darken is equivalent to a 'Minimum' method
1840 OR a greyscale version of a binary 'Or'
1841 OR the 'Intersection' of pixel sets.
1842 */
1843 if (Sc < Dc)
1844 {
1845 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1846 break;
1847 }
1848 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1849 break;
1850 }
cristye4a40472011-12-22 02:56:19 +00001851 case DarkenIntensityCompositeOp:
1852 {
cristy99abff32011-12-24 20:45:16 +00001853 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1854 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001855 break;
1856 }
1857 case DifferenceCompositeOp:
1858 {
1859 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1860 break;
1861 }
1862 case DissolveCompositeOp:
1863 {
1864 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1865 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1866 break;
1867 }
1868 case DivideDstCompositeOp:
1869 {
1870 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1871 {
cristy99abff32011-12-24 20:45:16 +00001872 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001873 break;
1874 }
1875 if (fabs(Dca) < MagickEpsilon)
1876 {
cristy99abff32011-12-24 20:45:16 +00001877 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001878 break;
1879 }
cristy99abff32011-12-24 20:45:16 +00001880 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001881 break;
1882 }
1883 case DivideSrcCompositeOp:
1884 {
1885 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1886 {
cristy99abff32011-12-24 20:45:16 +00001887 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001888 break;
1889 }
1890 if (fabs(Sca) < MagickEpsilon)
1891 {
cristy99abff32011-12-24 20:45:16 +00001892 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001893 break;
1894 }
cristy99abff32011-12-24 20:45:16 +00001895 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001896 break;
1897 }
1898 case DstAtopCompositeOp:
1899 {
1900 pixel=Dc*Da+Sc*(1.0-Da);
1901 break;
1902 }
1903 case DstCompositeOp:
1904 case NoCompositeOp:
1905 {
1906 pixel=Dc;
1907 break;
1908 }
1909 case DstInCompositeOp:
1910 {
1911 pixel=gamma*(Sa*Dc*Sa);
1912 break;
1913 }
1914 case DstOutCompositeOp:
1915 {
1916 pixel=gamma*(Da*Dc*(1.0-Sa));
1917 break;
1918 }
1919 case DstOverCompositeOp:
1920 {
1921 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1922 break;
1923 }
1924 case ExclusionCompositeOp:
1925 {
cristy99abff32011-12-24 20:45:16 +00001926 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1927 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001928 break;
1929 }
1930 case HardLightCompositeOp:
1931 {
1932 if ((2.0*Sca) < Sa)
1933 {
cristy99abff32011-12-24 20:45:16 +00001934 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001935 (1.0-Sa));
1936 break;
1937 }
cristy99abff32011-12-24 20:45:16 +00001938 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001939 Dca*(1.0-Sa));
1940 break;
1941 }
1942 case HueCompositeOp:
1943 {
cristy99abff32011-12-24 20:45:16 +00001944 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001945 {
1946 pixel=Dc;
1947 break;
1948 }
cristy99abff32011-12-24 20:45:16 +00001949 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001950 {
1951 pixel=Sc;
1952 break;
1953 }
cristy7133e642012-08-14 11:04:11 +00001954 CompositeHCL(destination_pixel.red,destination_pixel.green,
1955 destination_pixel.blue,&hue,&chroma,&luma);
1956 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001957 &hue,&sans,&sans);
cristy7133e642012-08-14 11:04:11 +00001958 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001959 switch (channel)
1960 {
1961 case RedPixelChannel: pixel=red; break;
1962 case GreenPixelChannel: pixel=green; break;
1963 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001964 default: pixel=Dc; break;
1965 }
1966 break;
1967 }
1968 case InCompositeOp:
1969 case SrcInCompositeOp:
1970 {
1971 pixel=gamma*(Da*Sc*Da);
1972 break;
1973 }
cristy99abff32011-12-24 20:45:16 +00001974 case LinearBurnCompositeOp:
1975 {
1976 /*
1977 LinearBurn: as defined by Abode Photoshop, according to
1978 http://www.simplefilter.de/en/basics/mixmods.html is:
1979
1980 f(Sc,Dc) = Sc + Dc - 1
1981 */
1982 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1983 break;
1984 }
1985 case LinearDodgeCompositeOp:
1986 {
1987 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1988 break;
1989 }
1990 case LinearLightCompositeOp:
1991 {
1992 /*
1993 LinearLight: as defined by Abode Photoshop, according to
1994 http://www.simplefilter.de/en/basics/mixmods.html is:
1995
1996 f(Sc,Dc) = Dc + 2*Sc - 1
1997 */
1998 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1999 break;
2000 }
2001 case LightenCompositeOp:
2002 {
2003 if (Sc > Dc)
2004 {
cristy24d5d722012-05-17 12:27:27 +00002005 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00002006 break;
2007 }
cristy24d5d722012-05-17 12:27:27 +00002008 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00002009 break;
2010 }
cristye4a40472011-12-22 02:56:19 +00002011 case LightenIntensityCompositeOp:
2012 {
2013 /*
2014 Lighten is equivalent to a 'Maximum' method
2015 OR a greyscale version of a binary 'And'
2016 OR the 'Union' of pixel sets.
2017 */
2018 pixel=Sa*GetPixelIntensity(composite_image,p) >
2019 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2020 break;
2021 }
cristye4a40472011-12-22 02:56:19 +00002022 case LuminizeCompositeOp:
2023 {
cristy99abff32011-12-24 20:45:16 +00002024 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002025 {
2026 pixel=Dc;
2027 break;
2028 }
cristy99abff32011-12-24 20:45:16 +00002029 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002030 {
2031 pixel=Sc;
2032 break;
2033 }
cristy7133e642012-08-14 11:04:11 +00002034 CompositeHCL(destination_pixel.red,destination_pixel.green,
2035 destination_pixel.blue,&hue,&chroma,&luma);
2036 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2037 &sans,&sans,&luma);
2038 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002039 switch (channel)
2040 {
2041 case RedPixelChannel: pixel=red; break;
2042 case GreenPixelChannel: pixel=green; break;
2043 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002044 default: pixel=Dc; break;
2045 }
2046 break;
2047 }
2048 case MathematicsCompositeOp:
2049 {
2050 /*
2051 'Mathematics' a free form user control mathematical composition
2052 is defined as...
2053
2054 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2055
2056 Where the arguments A,B,C,D are (currently) passed to composite
2057 as a command separated 'geometry' string in "compose:args" image
2058 artifact.
2059
2060 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2061
2062 Applying the SVG transparency formula (see above), we get...
2063
2064 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2065
2066 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2067 Dca*(1.0-Sa)
2068 */
2069 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2070 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2071 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2072 break;
2073 }
2074 case MinusDstCompositeOp:
2075 {
2076 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2077 break;
2078 }
2079 case MinusSrcCompositeOp:
2080 {
2081 /*
2082 Minus source from destination.
2083
2084 f(Sc,Dc) = Sc - Dc
2085 */
cristy99abff32011-12-24 20:45:16 +00002086 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002087 break;
2088 }
2089 case ModulateCompositeOp:
2090 {
2091 ssize_t
2092 offset;
2093
cristy99abff32011-12-24 20:45:16 +00002094 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002095 {
2096 pixel=Dc;
2097 break;
2098 }
2099 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2100 if (offset == 0)
2101 {
2102 pixel=Dc;
2103 break;
2104 }
cristy7133e642012-08-14 11:04:11 +00002105 CompositeHCL(destination_pixel.red,destination_pixel.green,
2106 destination_pixel.blue,&hue,&chroma,&luma);
2107 luma+=(0.01*percent_luma*offset)/midpoint;
2108 chroma*=0.01*percent_chroma;
2109 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002110 switch (channel)
2111 {
2112 case RedPixelChannel: pixel=red; break;
2113 case GreenPixelChannel: pixel=green; break;
2114 case BluePixelChannel: pixel=blue; break;
2115 default: pixel=Dc; break;
2116 }
2117 break;
2118 }
2119 case ModulusAddCompositeOp:
2120 {
2121 pixel=Sc+Dc;
2122 if (pixel > QuantumRange)
2123 pixel-=(QuantumRange+1.0);
2124 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2125 break;
2126 }
2127 case ModulusSubtractCompositeOp:
2128 {
cristy99abff32011-12-24 20:45:16 +00002129 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002130 if (pixel < 0.0)
2131 pixel+=(QuantumRange+1.0);
2132 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2133 break;
2134 }
2135 case MultiplyCompositeOp:
2136 {
cristy99abff32011-12-24 20:45:16 +00002137 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002138 break;
2139 }
2140 case OutCompositeOp:
2141 case SrcOutCompositeOp:
2142 {
2143 pixel=gamma*(Sa*Sc*(1.0-Da));
2144 break;
2145 }
2146 case OverCompositeOp:
2147 case SrcOverCompositeOp:
2148 {
cristy99abff32011-12-24 20:45:16 +00002149 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002150 break;
2151 }
2152 case OverlayCompositeOp:
2153 {
2154 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002155 {
2156 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2157 (1.0-Da));
2158 break;
2159 }
2160 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2161 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002162 break;
2163 }
2164 case PegtopLightCompositeOp:
2165 {
2166 /*
2167 PegTop: A Soft-Light alternative: A continuous version of the
2168 Softlight function, producing very similar results.
2169
2170 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2171
2172 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2173 */
2174 if (fabs(Da) < MagickEpsilon)
2175 {
cristy99abff32011-12-24 20:45:16 +00002176 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002177 break;
2178 }
cristy99abff32011-12-24 20:45:16 +00002179 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2180 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002181 break;
2182 }
2183 case PinLightCompositeOp:
2184 {
2185 /*
2186 PinLight: A Photoshop 7 composition method
2187 http://www.simplefilter.de/en/basics/mixmods.html
2188
2189 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2190 */
2191 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2192 {
cristy99abff32011-12-24 20:45:16 +00002193 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002194 break;
2195 }
2196 if ((Dca*Sa) > (2.0*Sca*Da))
2197 {
cristy99abff32011-12-24 20:45:16 +00002198 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002199 break;
2200 }
cristy99abff32011-12-24 20:45:16 +00002201 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002202 break;
2203 }
2204 case PlusCompositeOp:
2205 {
cristy24d5d722012-05-17 12:27:27 +00002206 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002207 break;
2208 }
2209 case SaturateCompositeOp:
2210 {
cristy99abff32011-12-24 20:45:16 +00002211 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002212 {
2213 pixel=Dc;
2214 break;
2215 }
cristy99abff32011-12-24 20:45:16 +00002216 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002217 {
2218 pixel=Sc;
2219 break;
2220 }
cristy7133e642012-08-14 11:04:11 +00002221 CompositeHCL(destination_pixel.red,destination_pixel.green,
2222 destination_pixel.blue,&hue,&chroma,&luma);
2223 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2224 &sans,&chroma,&sans);
2225 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002226 switch (channel)
2227 {
2228 case RedPixelChannel: pixel=red; break;
2229 case GreenPixelChannel: pixel=green; break;
2230 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002231 default: pixel=Dc; break;
2232 }
2233 break;
2234 }
2235 case ScreenCompositeOp:
2236 {
2237 /*
2238 Screen: a negated multiply:
2239
2240 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2241 */
cristy99abff32011-12-24 20:45:16 +00002242 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002243 break;
2244 }
2245 case SoftLightCompositeOp:
2246 {
2247 /*
2248 Refer to the March 2009 SVG specification.
2249 */
2250 if ((2.0*Sca) < Sa)
2251 {
cristy99abff32011-12-24 20:45:16 +00002252 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2253 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002254 break;
2255 }
2256 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2257 {
cristy99abff32011-12-24 20:45:16 +00002258 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2259 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2260 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002261 break;
2262 }
cristy99abff32011-12-24 20:45:16 +00002263 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2264 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002265 break;
2266 }
2267 case ThresholdCompositeOp:
2268 {
cristy7159f662012-10-28 17:32:43 +00002269 MagickRealType
cristye4a40472011-12-22 02:56:19 +00002270 delta;
2271
2272 delta=Sc-Dc;
cristy7159f662012-10-28 17:32:43 +00002273 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002274 {
2275 pixel=gamma*Dc;
2276 break;
2277 }
2278 pixel=gamma*(Dc+delta*amount);
2279 break;
2280 }
2281 case VividLightCompositeOp:
2282 {
2283 /*
2284 VividLight: A Photoshop 7 composition method. See
2285 http://www.simplefilter.de/en/basics/mixmods.html.
2286
2287 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2288 */
2289 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2290 {
cristy99abff32011-12-24 20:45:16 +00002291 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002292 break;
2293 }
2294 if ((2.0*Sca) <= Sa)
2295 {
cristy99abff32011-12-24 20:45:16 +00002296 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2297 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002298 break;
2299 }
cristy99abff32011-12-24 20:45:16 +00002300 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2301 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002302 break;
2303 }
2304 case XorCompositeOp:
2305 {
cristy99abff32011-12-24 20:45:16 +00002306 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002307 break;
2308 }
2309 default:
2310 {
2311 pixel=Sc;
2312 break;
2313 }
2314 }
2315 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002316 }
cristyed231572011-07-14 02:18:59 +00002317 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002318 channels=GetPixelChannels(composite_image);
2319 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002320 p=pixels;
cristyed231572011-07-14 02:18:59 +00002321 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002322 }
2323 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2324 status=MagickFalse;
2325 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2326 {
2327 MagickBooleanType
2328 proceed;
2329
2330#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002331 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002332#endif
2333 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2334 image->rows);
2335 if (proceed == MagickFalse)
2336 status=MagickFalse;
2337 }
2338 }
2339 composite_view=DestroyCacheView(composite_view);
2340 image_view=DestroyCacheView(image_view);
2341 if (destination_image != (Image * ) NULL)
2342 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002343 else
2344 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00002345 return(status);
2346}
2347
2348/*
2349%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2350% %
2351% %
2352% %
2353% T e x t u r e I m a g e %
2354% %
2355% %
2356% %
2357%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2358%
2359% TextureImage() repeatedly tiles the texture image across and down the image
2360% canvas.
2361%
2362% The format of the TextureImage method is:
2363%
cristy30d8c942012-02-07 13:44:59 +00002364% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002365% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002366%
2367% A description of each parameter follows:
2368%
2369% o image: the image.
2370%
cristye6178502011-12-23 17:02:29 +00002371% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002372%
2373*/
cristy30d8c942012-02-07 13:44:59 +00002374MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2375 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002376{
2377#define TextureImageTag "Texture/Image"
2378
2379 CacheView
2380 *image_view,
2381 *texture_view;
2382
cristy30d8c942012-02-07 13:44:59 +00002383 Image
2384 *texture_image;
2385
cristy4c08aed2011-07-01 19:47:50 +00002386 MagickBooleanType
2387 status;
2388
2389 ssize_t
2390 y;
2391
2392 assert(image != (Image *) NULL);
2393 if (image->debug != MagickFalse)
2394 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2395 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002396 if (texture == (const Image *) NULL)
2397 return(MagickFalse);
2398 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2399 return(MagickFalse);
2400 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002401 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002402 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002403 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002404 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2405 exception);
cristy4c08aed2011-07-01 19:47:50 +00002406 status=MagickTrue;
2407 if ((image->compose != CopyCompositeOp) &&
cristy8a46d822012-08-28 23:32:39 +00002408 ((image->compose != OverCompositeOp) || (image->alpha_trait == BlendPixelTrait) ||
2409 (texture_image->alpha_trait == BlendPixelTrait)))
cristy4c08aed2011-07-01 19:47:50 +00002410 {
2411 /*
2412 Tile texture onto the image background.
2413 */
cristye8ac1d02012-10-09 14:46:57 +00002414#if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(NoBenefitFromParallelism)
cristy9a5a52f2012-10-09 14:40:31 +00002415 #pragma omp parallel for schedule(static,4) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002416 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002417#endif
cristye6178502011-12-23 17:02:29 +00002418 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002419 {
2420 register ssize_t
2421 x;
2422
2423 if (status == MagickFalse)
2424 continue;
cristye6178502011-12-23 17:02:29 +00002425 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002426 {
2427 MagickBooleanType
2428 thread_status;
2429
cristyfeb3e962012-03-29 17:25:55 +00002430 thread_status=CompositeImage(image,texture_image,image->compose,
2431 MagickFalse,x+texture_image->tile_offset.x,y+
2432 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002433 if (thread_status == MagickFalse)
2434 {
2435 status=thread_status;
2436 break;
2437 }
2438 }
2439 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2440 {
2441 MagickBooleanType
2442 proceed;
2443
cristye8ac1d02012-10-09 14:46:57 +00002444#if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(NoBenefitFromParallelism)
cristybd0435b2012-01-05 16:20:47 +00002445 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002446#endif
2447 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;
cristydb070952012-04-20 14:33:00 +00002462 texture_view=AcquireVirtualCacheView(texture_image,exception);
2463 image_view=AcquireAuthenticCacheView(image,exception);
cristye8ac1d02012-10-09 14:46:57 +00002464#if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(NoBenefitFromParallelism)
cristy9a5a52f2012-10-09 14:40:31 +00002465 #pragma omp parallel for schedule(static,4) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002466 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002467#endif
2468 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
cristye8ac1d02012-10-09 14:46:57 +00002546#if defined(MAGICKCORE_OPENMP_SUPPORT) && defined(NoBenefitFromParallelism)
cristy4c08aed2011-07-01 19:47:50 +00002547 #pragma omp critical (MagickCore_TextureImage)
2548#endif
2549 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2550 image->rows);
2551 if (proceed == MagickFalse)
2552 status=MagickFalse;
2553 }
2554 }
2555 texture_view=DestroyCacheView(texture_view);
2556 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002557 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002558 return(status);
2559}