blob: 9093844733db8c55540c9f31bbb1de07020d3877 [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% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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
cristye4a40472011-12-22 02:56:19 +0000180static inline double MagickMin(const double x,const double y)
181{
182 if (x < y)
183 return(x);
184 return(y);
185}
cristyddeeea22012-04-12 01:33:09 +0000186
cristye4a40472011-12-22 02:56:19 +0000187static inline double MagickMax(const double x,const double y)
188{
189 if (x > y)
190 return(x);
191 return(y);
192}
193
cristyd8f16f72012-08-13 12:49:50 +0000194static inline double ConvertHueToRGB(double m1,
195 double m2,double hue)
196{
197 if (hue < 0.0)
198 hue+=1.0;
199 if (hue > 1.0)
200 hue-=1.0;
201 if ((6.0*hue) < 1.0)
202 return(m1+6.0*(m2-m1)*hue);
203 if ((2.0*hue) < 1.0)
204 return(m2);
205 if ((3.0*hue) < 2.0)
206 return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
207 return(m1);
208}
209
cristy7133e642012-08-14 11:04:11 +0000210static void HCLComposite(const double hue,const double chroma,const double luma,
211 double *red,double *green,double *blue)
cristyd8f16f72012-08-13 12:49:50 +0000212{
213 double
214 b,
cristy7133e642012-08-14 11:04:11 +0000215 c,
cristyd8f16f72012-08-13 12:49:50 +0000216 g,
cristy7133e642012-08-14 11:04:11 +0000217 h,
218 m,
cristyd8f16f72012-08-13 12:49:50 +0000219 r,
cristy7133e642012-08-14 11:04:11 +0000220 x;
cristyd8f16f72012-08-13 12:49:50 +0000221
222 /*
cristy7133e642012-08-14 11:04:11 +0000223 Convert HCL to RGB colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000224 */
225 assert(red != (double *) NULL);
226 assert(green != (double *) NULL);
227 assert(blue != (double *) NULL);
cristy7133e642012-08-14 11:04:11 +0000228 h=6.0*hue;
229 c=chroma;
230 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
231 r=0.0;
232 g=0.0;
233 b=0.0;
234 if ((0.0 <= h) && (h < 1.0))
cristyd8f16f72012-08-13 12:49:50 +0000235 {
cristy7133e642012-08-14 11:04:11 +0000236 r=c;
237 g=x;
cristyd8f16f72012-08-13 12:49:50 +0000238 }
cristyd8f16f72012-08-13 12:49:50 +0000239 else
cristy7133e642012-08-14 11:04:11 +0000240 if ((1.0 <= h) && (h < 2.0))
241 {
242 r=x;
243 g=c;
244 }
245 else
246 if ((2.0 <= h) && (h < 3.0))
247 {
248 g=c;
249 b=x;
250 }
251 else
252 if ((3.0 <= h) && (h < 4.0))
253 {
254 g=x;
255 b=c;
256 }
257 else
258 if ((4.0 <= h) && (h < 5.0))
259 {
260 r=x;
261 b=c;
262 }
263 else
264 if ((5.0 <= h) && (h < 6.0))
265 {
266 r=c;
267 b=x;
268 }
cristyf2c5f4a2012-08-14 23:22:31 +0000269 m=luma-(0.298839*r+0.586811*g+0.114350*b);
cristy7133e642012-08-14 11:04:11 +0000270 *red=QuantumRange*(r+m);
271 *green=QuantumRange*(g+m);
272 *blue=QuantumRange*(b+m);
cristyd8f16f72012-08-13 12:49:50 +0000273}
274
cristy7133e642012-08-14 11:04:11 +0000275static void CompositeHCL(const double red,const double green,const double blue,
276 double *hue,double *chroma,double *luma)
cristyd8f16f72012-08-13 12:49:50 +0000277{
278 double
279 b,
cristy7133e642012-08-14 11:04:11 +0000280 c,
cristyd8f16f72012-08-13 12:49:50 +0000281 g,
cristy7133e642012-08-14 11:04:11 +0000282 h,
cristyd8f16f72012-08-13 12:49:50 +0000283 max,
cristyd8f16f72012-08-13 12:49:50 +0000284 r;
285
286 /*
cristy7133e642012-08-14 11:04:11 +0000287 Convert RGB to HCL colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000288 */
289 assert(hue != (double *) NULL);
cristy7133e642012-08-14 11:04:11 +0000290 assert(chroma != (double *) NULL);
291 assert(luma != (double *) NULL);
292 r=red;
293 g=green;
294 b=blue;
cristyd8f16f72012-08-13 12:49:50 +0000295 max=MagickMax(r,MagickMax(g,b));
cristy7133e642012-08-14 11:04:11 +0000296 c=max-(double) MagickMin(r,MagickMin(g,b));
297 h=0.0;
298 if (c == 0)
299 h=0.0;
cristyd8f16f72012-08-13 12:49:50 +0000300 else
cristy7133e642012-08-14 11:04:11 +0000301 if (red == max)
cristyf2c5f4a2012-08-14 23:22:31 +0000302 h=fmod(6.0+(g-b)/c,6.0);
cristyd8f16f72012-08-13 12:49:50 +0000303 else
cristy7133e642012-08-14 11:04:11 +0000304 if (green == max)
305 h=((b-r)/c)+2.0;
306 else
307 if (blue == max)
308 h=((r-g)/c)+4.0;
309 *hue=(h/6.0);
310 *chroma=QuantumScale*c;
311 *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b);
cristyd8f16f72012-08-13 12:49:50 +0000312}
313
cristye4a40472011-12-22 02:56:19 +0000314static MagickBooleanType CompositeOverImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000315 const Image *composite_image,const MagickBooleanType clip_to_self,
316 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristye4a40472011-12-22 02:56:19 +0000317{
318#define CompositeImageTag "Composite/Image"
319
320 CacheView
321 *composite_view,
322 *image_view;
323
cristye4a40472011-12-22 02:56:19 +0000324 MagickBooleanType
cristye4a40472011-12-22 02:56:19 +0000325 status;
326
327 MagickOffsetType
328 progress;
329
330 ssize_t
331 y;
332
cristye4a40472011-12-22 02:56:19 +0000333 /*
cristye4a40472011-12-22 02:56:19 +0000334 Composite image.
335 */
336 status=MagickTrue;
337 progress=0;
cristydb070952012-04-20 14:33:00 +0000338 composite_view=AcquireVirtualCacheView(composite_image,exception);
339 image_view=AcquireAuthenticCacheView(image,exception);
cristye4a40472011-12-22 02:56:19 +0000340#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000341 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000342 dynamic_number_threads(image,image->columns,image->rows,1)
cristye4a40472011-12-22 02:56:19 +0000343#endif
344 for (y=0; y < (ssize_t) image->rows; y++)
345 {
346 const Quantum
347 *pixels;
348
349 register const Quantum
350 *restrict p;
351
352 register Quantum
353 *restrict q;
354
355 register ssize_t
356 x;
357
cristy564a5692012-01-20 23:56:26 +0000358 size_t
359 channels;
360
cristye4a40472011-12-22 02:56:19 +0000361 if (status == MagickFalse)
362 continue;
cristyfeb3e962012-03-29 17:25:55 +0000363 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000364 {
365 if (y < y_offset)
366 continue;
367 if ((y-y_offset) >= (ssize_t) composite_image->rows)
368 continue;
369 }
370 /*
371 If pixels is NULL, y is outside overlay region.
372 */
373 pixels=(Quantum *) NULL;
374 p=(Quantum *) NULL;
375 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
376 {
377 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
378 composite_image->columns,1,exception);
379 if (p == (const Quantum *) NULL)
380 {
381 status=MagickFalse;
382 continue;
383 }
384 pixels=p;
385 if (x_offset < 0)
386 p-=x_offset*GetPixelChannels(composite_image);
387 }
388 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
389 if (q == (Quantum *) NULL)
390 {
391 status=MagickFalse;
392 continue;
393 }
394 for (x=0; x < (ssize_t) image->columns; x++)
395 {
cristya19f1d72012-08-07 18:24:38 +0000396 double
cristye4a40472011-12-22 02:56:19 +0000397 alpha,
398 Da,
399 Dc,
400 gamma,
401 Sa,
402 Sc;
403
404 register ssize_t
405 i;
406
cristyfeb3e962012-03-29 17:25:55 +0000407 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000408 {
409 if (x < x_offset)
410 {
411 q+=GetPixelChannels(image);
412 continue;
413 }
414 if ((x-x_offset) >= (ssize_t) composite_image->columns)
415 break;
416 }
417 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
418 ((x-x_offset) >= (ssize_t) composite_image->columns))
419 {
420 Quantum
421 source[MaxPixelChannels];
422
423 /*
424 Virtual composite:
425 Sc: source color.
426 Dc: destination color.
427 */
cristy10a6c612012-01-29 21:41:05 +0000428 if (GetPixelMask(image,q) != 0)
429 {
430 q+=GetPixelChannels(image);
431 continue;
432 }
cristyc94ba6f2012-01-29 23:19:58 +0000433 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
434 source,exception);
cristye4a40472011-12-22 02:56:19 +0000435 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
436 {
437 PixelChannel
438 channel;
439
440 PixelTrait
441 composite_traits,
442 traits;
443
444 channel=GetPixelChannelMapChannel(image,i);
445 traits=GetPixelChannelMapTraits(image,channel);
cristy10a6c612012-01-29 21:41:05 +0000446 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000447 if ((traits == UndefinedPixelTrait) ||
448 (composite_traits == UndefinedPixelTrait))
449 continue;
450 q[i]=source[channel];
451 }
452 q+=GetPixelChannels(image);
453 continue;
454 }
455 /*
456 Authentic composite:
457 Sa: normalized source alpha.
458 Da: normalized destination alpha.
459 */
cristyc94ba6f2012-01-29 23:19:58 +0000460 if (GetPixelMask(composite_image,p) != 0)
461 {
462 p+=GetPixelChannels(composite_image);
463 channels=GetPixelChannels(composite_image);
464 if (p >= (pixels+channels*composite_image->columns))
465 p=pixels;
466 q+=GetPixelChannels(image);
467 continue;
468 }
cristye4a40472011-12-22 02:56:19 +0000469 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
470 Da=QuantumScale*GetPixelAlpha(image,q);
471 alpha=Sa*(-Da)+Sa+Da;
472 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
473 {
474 PixelChannel
475 channel;
476
477 PixelTrait
478 composite_traits,
479 traits;
480
481 channel=GetPixelChannelMapChannel(image,i);
482 traits=GetPixelChannelMapTraits(image,channel);
483 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
484 if ((traits == UndefinedPixelTrait) ||
485 (composite_traits == UndefinedPixelTrait))
486 continue;
487 if ((traits & CopyPixelTrait) != 0)
488 {
489 if (channel != AlphaPixelChannel)
490 {
491 /*
492 Copy channel.
493 */
494 q[i]=GetPixelChannel(composite_image,channel,p);
495 continue;
496 }
497 /*
498 Set alpha channel.
499 */
500 q[i]=ClampToQuantum(QuantumRange*alpha);
501 continue;
502 }
503 /*
504 Sc: source color.
505 Dc: destination color.
506 */
cristya19f1d72012-08-07 18:24:38 +0000507 Sc=(double) GetPixelChannel(composite_image,channel,p);
508 Dc=(double) q[i];
cristyc58380a2012-06-03 15:12:30 +0000509 gamma=MagickEpsilonReciprocal(alpha);
cristye4a40472011-12-22 02:56:19 +0000510 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
511 }
512 p+=GetPixelChannels(composite_image);
513 channels=GetPixelChannels(composite_image);
514 if (p >= (pixels+channels*composite_image->columns))
515 p=pixels;
516 q+=GetPixelChannels(image);
517 }
518 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
519 status=MagickFalse;
520 if (image->progress_monitor != (MagickProgressMonitor) NULL)
521 {
522 MagickBooleanType
523 proceed;
524
525#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000526 #pragma omp critical (MagickCore_CompositeImage)
cristye4a40472011-12-22 02:56:19 +0000527#endif
528 proceed=SetImageProgress(image,CompositeImageTag,progress++,
529 image->rows);
530 if (proceed == MagickFalse)
531 status=MagickFalse;
532 }
533 }
534 composite_view=DestroyCacheView(composite_view);
535 image_view=DestroyCacheView(image_view);
536 return(status);
537}
538
cristy4c08aed2011-07-01 19:47:50 +0000539MagickExport MagickBooleanType CompositeImage(Image *image,
cristya865ccd2012-07-28 00:33:10 +0000540 const Image *composite,const CompositeOperator compose,
cristy44c98412012-03-31 18:25:40 +0000541 const MagickBooleanType clip_to_self,const ssize_t x_offset,
542 const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000543{
cristy4c08aed2011-07-01 19:47:50 +0000544#define CompositeImageTag "Composite/Image"
545
546 CacheView
547 *composite_view,
548 *image_view;
549
cristy4c08aed2011-07-01 19:47:50 +0000550 GeometryInfo
551 geometry_info;
552
553 Image
cristya865ccd2012-07-28 00:33:10 +0000554 *composite_image,
cristy4c08aed2011-07-01 19:47:50 +0000555 *destination_image;
556
557 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000558 status;
559
560 MagickOffsetType
561 progress;
562
cristya19f1d72012-08-07 18:24:38 +0000563 double
cristy4c08aed2011-07-01 19:47:50 +0000564 amount,
565 destination_dissolve,
566 midpoint,
cristy7133e642012-08-14 11:04:11 +0000567 percent_luma,
568 percent_chroma,
cristy4c08aed2011-07-01 19:47:50 +0000569 source_dissolve,
570 threshold;
571
572 MagickStatusType
573 flags;
574
cristyd197cbb2012-01-13 02:14:12 +0000575 ssize_t
576 y;
577
cristy4c08aed2011-07-01 19:47:50 +0000578 assert(image != (Image *) NULL);
579 assert(image->signature == MagickSignature);
580 if (image->debug != MagickFalse)
581 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristya865ccd2012-07-28 00:33:10 +0000582 assert(composite!= (Image *) NULL);
583 assert(composite->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000584 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000585 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +0000586 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
587 if (composite_image == (const Image *) NULL)
588 return(MagickFalse);
cristya4a22a22012-08-01 23:16:38 +0000589 (void) SetImageColorspace(composite_image,image->colorspace,exception);
cristye4a40472011-12-22 02:56:19 +0000590 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
591 {
cristyfeb3e962012-03-29 17:25:55 +0000592 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
593 y_offset,exception);
cristya865ccd2012-07-28 00:33:10 +0000594 composite_image=DestroyImage(composite_image);
cristye4a40472011-12-22 02:56:19 +0000595 return(status);
596 }
cristy4c08aed2011-07-01 19:47:50 +0000597 destination_image=(Image *) NULL;
598 amount=0.5;
599 destination_dissolve=1.0;
cristy7133e642012-08-14 11:04:11 +0000600 percent_luma=100.0;
601 percent_chroma=100.0;
cristy4c08aed2011-07-01 19:47:50 +0000602 source_dissolve=1.0;
603 threshold=0.05f;
604 switch (compose)
605 {
cristy4c08aed2011-07-01 19:47:50 +0000606 case CopyCompositeOp:
607 {
608 if ((x_offset < 0) || (y_offset < 0))
609 break;
610 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
611 break;
612 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
613 break;
614 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +0000615 composite_view=AcquireVirtualCacheView(composite_image,exception);
616 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000617#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000618 #pragma omp parallel for schedule(static,4) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000619 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000620#endif
621 for (y=0; y < (ssize_t) composite_image->rows; y++)
622 {
623 MagickBooleanType
624 sync;
625
626 register const Quantum
627 *p;
628
629 register Quantum
630 *q;
631
632 register ssize_t
633 x;
634
635 if (status == MagickFalse)
636 continue;
637 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
638 1,exception);
639 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
640 composite_image->columns,1,exception);
641 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
642 {
643 status=MagickFalse;
644 continue;
645 }
646 for (x=0; x < (ssize_t) composite_image->columns; x++)
647 {
cristybdecccc2011-12-24 22:52:16 +0000648 register ssize_t
649 i;
650
cristy665e18f2012-05-17 12:39:54 +0000651 if (GetPixelMask(composite_image,p) != 0)
cristy10a6c612012-01-29 21:41:05 +0000652 {
653 p+=GetPixelChannels(composite_image);
654 q+=GetPixelChannels(image);
655 continue;
656 }
cristybdecccc2011-12-24 22:52:16 +0000657 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
658 {
659 PixelChannel
660 channel;
661
662 PixelTrait
663 composite_traits,
664 traits;
665
666 channel=GetPixelChannelMapChannel(composite_image,i);
667 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
668 traits=GetPixelChannelMapTraits(image,channel);
669 if ((traits == UndefinedPixelTrait) ||
670 (composite_traits == UndefinedPixelTrait))
671 continue;
672 SetPixelChannel(image,channel,p[i],q);
673 }
cristyed231572011-07-14 02:18:59 +0000674 p+=GetPixelChannels(composite_image);
675 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000676 }
677 sync=SyncCacheViewAuthenticPixels(image_view,exception);
678 if (sync == MagickFalse)
679 status=MagickFalse;
680 if (image->progress_monitor != (MagickProgressMonitor) NULL)
681 {
682 MagickBooleanType
683 proceed;
684
685#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000686 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000687#endif
688 proceed=SetImageProgress(image,CompositeImageTag,
689 (MagickOffsetType) y,image->rows);
690 if (proceed == MagickFalse)
691 status=MagickFalse;
692 }
693 }
694 composite_view=DestroyCacheView(composite_view);
695 image_view=DestroyCacheView(image_view);
cristy44886b92012-07-28 13:07:09 +0000696 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000697 return(status);
698 }
cristye4a40472011-12-22 02:56:19 +0000699 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000700 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000701 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000702 {
703 /*
704 Modify destination outside the overlaid region and require an alpha
705 channel to exist, to add transparency.
706 */
707 if (image->matte == MagickFalse)
cristy42c41de2012-05-05 18:36:31 +0000708 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000709 break;
710 }
711 case BlurCompositeOp:
712 {
713 CacheView
714 *composite_view,
715 *destination_view;
716
cristyfeb3e962012-03-29 17:25:55 +0000717 const char
718 *value;
719
cristy4c08aed2011-07-01 19:47:50 +0000720 PixelInfo
721 pixel;
722
cristya19f1d72012-08-07 18:24:38 +0000723 double
cristy4c08aed2011-07-01 19:47:50 +0000724 angle_range,
725 angle_start,
726 height,
727 width;
728
729 ResampleFilter
730 *resample_filter;
731
732 SegmentInfo
733 blur;
734
735 /*
anthony9cb63cc2012-04-25 06:10:49 +0000736 Blur Image by resampling.
737
cristy4c08aed2011-07-01 19:47:50 +0000738 Blur Image dictated by an overlay gradient map: X = red_channel;
739 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
740 */
741 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000742 exception);
cristy4c08aed2011-07-01 19:47:50 +0000743 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000744 {
745 composite_image=DestroyImage(composite_image);
746 return(MagickFalse);
747 }
cristy4c08aed2011-07-01 19:47:50 +0000748 /*
anthony9cb63cc2012-04-25 06:10:49 +0000749 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000750 */
751 SetGeometryInfo(&geometry_info);
752 flags=NoValue;
753 value=GetImageArtifact(composite_image,"compose:args");
754 if (value != (char *) NULL)
755 flags=ParseGeometry(value,&geometry_info);
anthonyd2923912012-04-23 13:06:53 +0000756 if ((flags & WidthValue) == 0 ) {
757 (void) ThrowMagickException(exception,GetMagickModule(),
758 OptionWarning,"InvalidSetting","'%s' '%s'",
anthony9cb63cc2012-04-25 06:10:49 +0000759 "compose:args",value);
cristy44886b92012-07-28 13:07:09 +0000760 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000761 destination_image=DestroyImage(destination_image);
762 return(MagickFalse);
763 }
anthony9cb63cc2012-04-25 06:10:49 +0000764 /*
765 Users input sigma now needs to be converted to the EWA ellipse size.
766 The filter defaults to a sigma of 0.5 so to make this match the
767 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000768 */
769 width=height=geometry_info.rho*2.0;
770 if ((flags & HeightValue) != 0 )
771 height=geometry_info.sigma*2.0;
772
anthony9cb63cc2012-04-25 06:10:49 +0000773 /* default the unrotated ellipse width and height axis vectors */
anthonyd2923912012-04-23 13:06:53 +0000774 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000775 blur.x2=0.0;
776 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000777 blur.y2=height;
778 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000779 if ((flags & XValue) != 0 )
780 {
cristya19f1d72012-08-07 18:24:38 +0000781 double
cristy4c08aed2011-07-01 19:47:50 +0000782 angle;
783
784 angle=DegreesToRadians(geometry_info.xi);
785 blur.x1=width*cos(angle);
786 blur.x2=width*sin(angle);
787 blur.y1=(-height*sin(angle));
788 blur.y2=height*cos(angle);
789 }
anthonyd2923912012-04-23 13:06:53 +0000790 /* Otherwise lets set a angle range and calculate in the loop */
791 angle_start=0.0;
792 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000793 if ((flags & YValue) != 0 )
794 {
795 angle_start=DegreesToRadians(geometry_info.xi);
796 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
797 }
798 /*
anthony9cb63cc2012-04-25 06:10:49 +0000799 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000800
anthony9cb63cc2012-04-25 06:10:49 +0000801 As the minimum ellipse radius of support*1.0 the EWA algorithm
802 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
803 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000804
anthony9cb63cc2012-04-25 06:10:49 +0000805 The solution (as well as the problem of preventing any user
806 expert filter settings, is to set our own user settings, then
807 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000808 */
cristy8a11cb12011-10-19 23:53:34 +0000809 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000810 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000811
812 /* do the variable blurring of each pixel in image */
813 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000814 composite_view=AcquireVirtualCacheView(composite_image,exception);
815 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000816 for (y=0; y < (ssize_t) composite_image->rows; y++)
817 {
818 MagickBooleanType
819 sync;
820
821 register const Quantum
822 *restrict p;
823
824 register Quantum
825 *restrict q;
826
827 register ssize_t
828 x;
829
830 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
831 continue;
832 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
833 1,exception);
834 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000835 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000836 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
837 break;
838 for (x=0; x < (ssize_t) composite_image->columns; x++)
839 {
840 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
841 {
cristyed231572011-07-14 02:18:59 +0000842 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000843 continue;
844 }
845 if (fabs(angle_range) > MagickEpsilon)
846 {
cristya19f1d72012-08-07 18:24:38 +0000847 double
cristy4c08aed2011-07-01 19:47:50 +0000848 angle;
849
850 angle=angle_start+angle_range*QuantumScale*
851 GetPixelBlue(composite_image,p);
852 blur.x1=width*cos(angle);
853 blur.x2=width*sin(angle);
854 blur.y1=(-height*sin(angle));
855 blur.y2=height*cos(angle);
856 }
anthonyd2923912012-04-23 13:06:53 +0000857#if 0
858 if ( x == 10 && y == 60 ) {
859 fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
860 blur.x1, blur.x2, blur.y1, blur.y2);
861 fprintf(stderr, "scaled by=%lf,%lf\n",
862 QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
863#endif
864 ScaleResampleFilter(resample_filter,
865 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
866 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
867 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
868 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000869 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000870 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000871 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000872 p+=GetPixelChannels(composite_image);
873 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000874 }
875 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
876 if (sync == MagickFalse)
877 break;
878 }
879 resample_filter=DestroyResampleFilter(resample_filter);
880 composite_view=DestroyCacheView(composite_view);
881 destination_view=DestroyCacheView(destination_view);
cristyf661c4d2012-07-28 12:57:17 +0000882 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000883 composite_image=destination_image;
884 break;
885 }
886 case DisplaceCompositeOp:
887 case DistortCompositeOp:
888 {
889 CacheView
890 *composite_view,
891 *destination_view,
892 *image_view;
893
cristyfeb3e962012-03-29 17:25:55 +0000894 const char
895 *value;
896
cristy4c08aed2011-07-01 19:47:50 +0000897 PixelInfo
898 pixel;
899
cristya19f1d72012-08-07 18:24:38 +0000900 double
cristy4c08aed2011-07-01 19:47:50 +0000901 horizontal_scale,
902 vertical_scale;
903
904 PointInfo
905 center,
906 offset;
907
908 /*
909 Displace/Distort based on overlay gradient map:
910 X = red_channel; Y = green_channel;
911 compose:args = x_scale[,y_scale[,center.x,center.y]]
912 */
913 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000914 exception);
cristy4c08aed2011-07-01 19:47:50 +0000915 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000916 {
917 composite_image=DestroyImage(composite_image);
918 return(MagickFalse);
919 }
cristy4c08aed2011-07-01 19:47:50 +0000920 SetGeometryInfo(&geometry_info);
921 flags=NoValue;
922 value=GetImageArtifact(composite_image,"compose:args");
923 if (value != (char *) NULL)
924 flags=ParseGeometry(value,&geometry_info);
925 if ((flags & (WidthValue|HeightValue)) == 0 )
926 {
927 if ((flags & AspectValue) == 0)
928 {
cristya19f1d72012-08-07 18:24:38 +0000929 horizontal_scale=(double) (composite_image->columns-1.0)/
cristy4c08aed2011-07-01 19:47:50 +0000930 2.0;
cristya19f1d72012-08-07 18:24:38 +0000931 vertical_scale=(double) (composite_image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000932 }
933 else
934 {
cristya19f1d72012-08-07 18:24:38 +0000935 horizontal_scale=(double) (image->columns-1.0)/2.0;
936 vertical_scale=(double) (image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000937 }
938 }
939 else
940 {
941 horizontal_scale=geometry_info.rho;
942 vertical_scale=geometry_info.sigma;
943 if ((flags & PercentValue) != 0)
944 {
945 if ((flags & AspectValue) == 0)
946 {
947 horizontal_scale*=(composite_image->columns-1.0)/200.0;
948 vertical_scale*=(composite_image->rows-1.0)/200.0;
949 }
950 else
951 {
952 horizontal_scale*=(image->columns-1.0)/200.0;
953 vertical_scale*=(image->rows-1.0)/200.0;
954 }
955 }
956 if ((flags & HeightValue) == 0)
957 vertical_scale=horizontal_scale;
958 }
959 /*
960 Determine fixed center point for absolute distortion map
961 Absolute distort ==
962 Displace offset relative to a fixed absolute point
963 Select that point according to +X+Y user inputs.
964 default = center of overlay image
965 arg flag '!' = locations/percentage relative to background image
966 */
cristya19f1d72012-08-07 18:24:38 +0000967 center.x=(double) x_offset;
968 center.y=(double) y_offset;
cristy4c08aed2011-07-01 19:47:50 +0000969 if (compose == DistortCompositeOp)
970 {
971 if ((flags & XValue) == 0)
972 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000973 center.x=(double) x_offset+(composite_image->columns-1)/
cristy4c08aed2011-07-01 19:47:50 +0000974 2.0;
975 else
cristya19f1d72012-08-07 18:24:38 +0000976 center.x=((double) image->columns-1)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000977 else
978 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000979 center.x=(double) x_offset+geometry_info.xi;
cristy4c08aed2011-07-01 19:47:50 +0000980 else
981 center.x=geometry_info.xi;
982 if ((flags & YValue) == 0)
983 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000984 center.y=(double) y_offset+(composite_image->rows-1)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000985 else
cristya19f1d72012-08-07 18:24:38 +0000986 center.y=((double) image->rows-1)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000987 else
988 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000989 center.y=(double) y_offset+geometry_info.psi;
cristy4c08aed2011-07-01 19:47:50 +0000990 else
991 center.y=geometry_info.psi;
992 }
993 /*
994 Shift the pixel offset point as defined by the provided,
995 displacement/distortion map. -- Like a lens...
996 */
cristye10859a2011-12-18 22:28:59 +0000997 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000998 image_view=AcquireVirtualCacheView(image,exception);
999 composite_view=AcquireVirtualCacheView(composite_image,exception);
1000 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001001 for (y=0; y < (ssize_t) composite_image->rows; y++)
1002 {
1003 MagickBooleanType
1004 sync;
1005
1006 register const Quantum
1007 *restrict p;
1008
1009 register Quantum
1010 *restrict q;
1011
1012 register ssize_t
1013 x;
1014
1015 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1016 continue;
1017 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1018 1,exception);
1019 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001020 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001021 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1022 break;
1023 for (x=0; x < (ssize_t) composite_image->columns; x++)
1024 {
1025 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1026 {
cristyed231572011-07-14 02:18:59 +00001027 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001028 continue;
1029 }
1030 /*
1031 Displace the offset.
1032 */
1033 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
cristya19f1d72012-08-07 18:24:38 +00001034 (((double) QuantumRange+1.0)/2.0)))/(((double)
cristy4c08aed2011-07-01 19:47:50 +00001035 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1036 x : 0);
1037 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
cristya19f1d72012-08-07 18:24:38 +00001038 (((double) QuantumRange+1.0)/2.0)))/(((double)
cristy4c08aed2011-07-01 19:47:50 +00001039 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1040 y : 0);
1041 (void) InterpolatePixelInfo(image,image_view,
1042 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1043 &pixel,exception);
1044 /*
1045 Mask with the 'invalid pixel mask' in alpha channel.
1046 */
cristya19f1d72012-08-07 18:24:38 +00001047 pixel.alpha=(double) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001048 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001049 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001050 p+=GetPixelChannels(composite_image);
1051 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001052 }
1053 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1054 if (sync == MagickFalse)
1055 break;
1056 }
1057 destination_view=DestroyCacheView(destination_view);
1058 composite_view=DestroyCacheView(composite_view);
1059 image_view=DestroyCacheView(image_view);
cristyf661c4d2012-07-28 12:57:17 +00001060 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001061 composite_image=destination_image;
1062 break;
1063 }
1064 case DissolveCompositeOp:
1065 {
cristyfeb3e962012-03-29 17:25:55 +00001066 const char
1067 *value;
1068
cristy4c08aed2011-07-01 19:47:50 +00001069 /*
1070 Geometry arguments to dissolve factors.
1071 */
1072 value=GetImageArtifact(composite_image,"compose:args");
1073 if (value != (char *) NULL)
1074 {
1075 flags=ParseGeometry(value,&geometry_info);
1076 source_dissolve=geometry_info.rho/100.0;
1077 destination_dissolve=1.0;
1078 if ((source_dissolve-MagickEpsilon) < 0.0)
1079 source_dissolve=0.0;
1080 if ((source_dissolve+MagickEpsilon) > 1.0)
1081 {
1082 destination_dissolve=2.0-source_dissolve;
1083 source_dissolve=1.0;
1084 }
1085 if ((flags & SigmaValue) != 0)
1086 destination_dissolve=geometry_info.sigma/100.0;
1087 if ((destination_dissolve-MagickEpsilon) < 0.0)
1088 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001089 /* posible speed up? -- from IMv6 update
1090 clip_to_self=MagickFalse;
1091 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1092 {
1093 destination_dissolve=1.0;
1094 clip_to_self=MagickTrue;
1095 }
1096 */
cristy4c08aed2011-07-01 19:47:50 +00001097 }
1098 break;
1099 }
1100 case BlendCompositeOp:
1101 {
cristyfeb3e962012-03-29 17:25:55 +00001102 const char
1103 *value;
1104
cristy4c08aed2011-07-01 19:47:50 +00001105 value=GetImageArtifact(composite_image,"compose:args");
1106 if (value != (char *) NULL)
1107 {
1108 flags=ParseGeometry(value,&geometry_info);
1109 source_dissolve=geometry_info.rho/100.0;
1110 destination_dissolve=1.0-source_dissolve;
1111 if ((flags & SigmaValue) != 0)
1112 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001113 }
1114 break;
1115 }
1116 case MathematicsCompositeOp:
1117 {
cristyfeb3e962012-03-29 17:25:55 +00001118 const char
1119 *value;
1120
cristy4c08aed2011-07-01 19:47:50 +00001121 /*
1122 Just collect the values from "compose:args", setting.
1123 Unused values are set to zero automagically.
1124
1125 Arguments are normally a comma separated list, so this probably should
1126 be changed to some 'general comma list' parser, (with a minimum
1127 number of values)
1128 */
1129 SetGeometryInfo(&geometry_info);
1130 value=GetImageArtifact(composite_image,"compose:args");
1131 if (value != (char *) NULL)
1132 (void) ParseGeometry(value,&geometry_info);
1133 break;
1134 }
1135 case ModulateCompositeOp:
1136 {
cristyfeb3e962012-03-29 17:25:55 +00001137 const char
1138 *value;
1139
cristy4c08aed2011-07-01 19:47:50 +00001140 /*
cristy7133e642012-08-14 11:04:11 +00001141 Determine the luma and chroma scale.
cristy4c08aed2011-07-01 19:47:50 +00001142 */
1143 value=GetImageArtifact(composite_image,"compose:args");
1144 if (value != (char *) NULL)
1145 {
1146 flags=ParseGeometry(value,&geometry_info);
cristy7133e642012-08-14 11:04:11 +00001147 percent_luma=geometry_info.rho;
cristy4c08aed2011-07-01 19:47:50 +00001148 if ((flags & SigmaValue) != 0)
cristy7133e642012-08-14 11:04:11 +00001149 percent_chroma=geometry_info.sigma;
cristy4c08aed2011-07-01 19:47:50 +00001150 }
1151 break;
1152 }
1153 case ThresholdCompositeOp:
1154 {
cristyfeb3e962012-03-29 17:25:55 +00001155 const char
1156 *value;
1157
cristy4c08aed2011-07-01 19:47:50 +00001158 /*
1159 Determine the amount and threshold.
1160 */
1161 value=GetImageArtifact(composite_image,"compose:args");
1162 if (value != (char *) NULL)
1163 {
1164 flags=ParseGeometry(value,&geometry_info);
1165 amount=geometry_info.rho;
1166 threshold=geometry_info.sigma;
1167 if ((flags & SigmaValue) == 0)
1168 threshold=0.05f;
1169 }
1170 threshold*=QuantumRange;
1171 break;
1172 }
1173 default:
1174 break;
1175 }
cristy4c08aed2011-07-01 19:47:50 +00001176 /*
1177 Composite image.
1178 */
1179 status=MagickTrue;
1180 progress=0;
cristya19f1d72012-08-07 18:24:38 +00001181 midpoint=((double) QuantumRange+1.0)/2;
cristydb070952012-04-20 14:33:00 +00001182 composite_view=AcquireVirtualCacheView(composite_image,exception);
1183 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001184#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001185 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +00001186 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001187#endif
1188 for (y=0; y < (ssize_t) image->rows; y++)
1189 {
1190 const Quantum
1191 *pixels;
1192
1193 double
cristye4a40472011-12-22 02:56:19 +00001194 blue,
cristy7133e642012-08-14 11:04:11 +00001195 luma,
cristye4a40472011-12-22 02:56:19 +00001196 green,
cristy4c08aed2011-07-01 19:47:50 +00001197 hue,
cristye4a40472011-12-22 02:56:19 +00001198 red,
cristy7133e642012-08-14 11:04:11 +00001199 chroma;
cristy4c08aed2011-07-01 19:47:50 +00001200
cristyddeeea22012-04-12 01:33:09 +00001201 PixelInfo
1202 destination_pixel,
1203 source_pixel;
1204
cristy4c08aed2011-07-01 19:47:50 +00001205 register const Quantum
1206 *restrict p;
1207
1208 register Quantum
1209 *restrict q;
1210
1211 register ssize_t
1212 x;
1213
1214 if (status == MagickFalse)
1215 continue;
cristyfeb3e962012-03-29 17:25:55 +00001216 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001217 {
1218 if (y < y_offset)
1219 continue;
1220 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1221 continue;
1222 }
1223 /*
1224 If pixels is NULL, y is outside overlay region.
1225 */
1226 pixels=(Quantum *) NULL;
1227 p=(Quantum *) NULL;
1228 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1229 {
1230 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1231 composite_image->columns,1,exception);
1232 if (p == (const Quantum *) NULL)
1233 {
1234 status=MagickFalse;
1235 continue;
1236 }
1237 pixels=p;
1238 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001239 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001240 }
1241 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001242 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001243 {
1244 status=MagickFalse;
1245 continue;
1246 }
cristy4c08aed2011-07-01 19:47:50 +00001247 hue=0.0;
cristy7133e642012-08-14 11:04:11 +00001248 chroma=0.0;
1249 luma=0.0;
cristyddeeea22012-04-12 01:33:09 +00001250 GetPixelInfo(image,&destination_pixel);
1251 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001252 for (x=0; x < (ssize_t) image->columns; x++)
1253 {
cristya19f1d72012-08-07 18:24:38 +00001254 double
cristye4a40472011-12-22 02:56:19 +00001255 alpha,
1256 Da,
1257 Dc,
1258 Dca,
1259 gamma,
1260 Sa,
1261 Sc,
1262 Sca;
1263
1264 register ssize_t
1265 i;
1266
cristy564a5692012-01-20 23:56:26 +00001267 size_t
1268 channels;
1269
cristyfeb3e962012-03-29 17:25:55 +00001270 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001271 {
1272 if (x < x_offset)
1273 {
cristyed231572011-07-14 02:18:59 +00001274 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001275 continue;
1276 }
1277 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1278 break;
1279 }
cristye4a40472011-12-22 02:56:19 +00001280 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1281 ((x-x_offset) >= (ssize_t) composite_image->columns))
1282 {
1283 Quantum
1284 source[MaxPixelChannels];
1285
1286 /*
1287 Virtual composite:
1288 Sc: source color.
1289 Dc: destination color.
1290 */
1291 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1292 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001293 if (GetPixelMask(image,q) != 0)
1294 {
1295 q+=GetPixelChannels(image);
1296 continue;
1297 }
cristye4a40472011-12-22 02:56:19 +00001298 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1299 {
cristya19f1d72012-08-07 18:24:38 +00001300 double
cristye4a40472011-12-22 02:56:19 +00001301 pixel;
1302
1303 PixelChannel
1304 channel;
1305
1306 PixelTrait
1307 composite_traits,
1308 traits;
1309
1310 channel=GetPixelChannelMapChannel(image,i);
1311 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +00001312 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001313 if ((traits == UndefinedPixelTrait) ||
1314 (composite_traits == UndefinedPixelTrait))
1315 continue;
1316 switch (compose)
1317 {
cristyc8d63672012-01-11 13:03:13 +00001318 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001319 case ChangeMaskCompositeOp:
1320 case CopyAlphaCompositeOp:
1321 case DstAtopCompositeOp:
1322 case DstInCompositeOp:
1323 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001324 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001325 case OutCompositeOp:
1326 case SrcInCompositeOp:
1327 case SrcOutCompositeOp:
1328 {
cristya19f1d72012-08-07 18:24:38 +00001329 pixel=(double) q[i];
cristye4a40472011-12-22 02:56:19 +00001330 if (channel == AlphaPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001331 pixel=(double) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001332 break;
1333 }
1334 case ClearCompositeOp:
1335 case CopyCompositeOp:
1336 case ReplaceCompositeOp:
1337 case SrcCompositeOp:
1338 {
1339 if (channel == AlphaPixelChannel)
1340 {
cristya19f1d72012-08-07 18:24:38 +00001341 pixel=(double) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001342 break;
1343 }
1344 pixel=0.0;
1345 break;
1346 }
cristy99abff32011-12-24 20:45:16 +00001347 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001348 case DissolveCompositeOp:
1349 {
1350 if (channel == AlphaPixelChannel)
1351 {
1352 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1353 source);
1354 break;
1355 }
cristya19f1d72012-08-07 18:24:38 +00001356 pixel=(double) source[channel];
cristye4a40472011-12-22 02:56:19 +00001357 break;
1358 }
1359 default:
1360 {
cristya19f1d72012-08-07 18:24:38 +00001361 pixel=(double) source[channel];
cristye4a40472011-12-22 02:56:19 +00001362 break;
1363 }
1364 }
1365 q[i]=ClampToQuantum(pixel);
1366 }
1367 q+=GetPixelChannels(image);
1368 continue;
1369 }
1370 /*
1371 Authentic composite:
1372 Sa: normalized source alpha.
1373 Da: normalized destination alpha.
1374 */
1375 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1376 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001377 switch (compose)
1378 {
cristye4a40472011-12-22 02:56:19 +00001379 case BumpmapCompositeOp:
1380 {
1381 alpha=GetPixelIntensity(composite_image,p)*Sa;
1382 break;
1383 }
cristycdc168f2011-12-21 15:24:39 +00001384 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001385 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001386 case DifferenceCompositeOp:
1387 case DivideDstCompositeOp:
1388 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001389 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001390 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001391 case LinearBurnCompositeOp:
1392 case LinearDodgeCompositeOp:
1393 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001394 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001395 case MinusDstCompositeOp:
1396 case MinusSrcCompositeOp:
1397 case ModulusAddCompositeOp:
1398 case ModulusSubtractCompositeOp:
1399 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001400 case OverlayCompositeOp:
1401 case PegtopLightCompositeOp:
1402 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001403 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001404 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001405 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001406 {
1407 alpha=RoundToUnity(Sa+Da-Sa*Da);
1408 break;
1409 }
1410 case DarkenCompositeOp:
1411 case DstAtopCompositeOp:
1412 case DstInCompositeOp:
1413 case InCompositeOp:
1414 case LightenCompositeOp:
1415 case SrcInCompositeOp:
1416 {
1417 alpha=Sa*Da;
1418 break;
1419 }
1420 case DissolveCompositeOp:
1421 {
1422 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1423 Sa+destination_dissolve*Da;
1424 break;
1425 }
1426 case DstOverCompositeOp:
1427 {
1428 alpha=Da*(-Sa)+Da+Sa;
1429 break;
1430 }
1431 case DstOutCompositeOp:
1432 {
1433 alpha=Da*(1.0-Sa);
1434 break;
1435 }
1436 case OutCompositeOp:
1437 case SrcOutCompositeOp:
1438 {
1439 alpha=Sa*(1.0-Da);
1440 break;
1441 }
1442 case OverCompositeOp:
1443 case SrcOverCompositeOp:
1444 {
1445 alpha=Sa*(-Da)+Sa+Da;
1446 break;
1447 }
cristy99abff32011-12-24 20:45:16 +00001448 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001449 case PlusCompositeOp:
1450 {
1451 alpha=RoundToUnity(Sa+Da);
1452 break;
1453 }
cristy4c08aed2011-07-01 19:47:50 +00001454 case XorCompositeOp:
1455 {
cristye4a40472011-12-22 02:56:19 +00001456 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001457 break;
1458 }
1459 default:
cristye4a40472011-12-22 02:56:19 +00001460 {
1461 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001462 break;
cristye4a40472011-12-22 02:56:19 +00001463 }
cristy4c08aed2011-07-01 19:47:50 +00001464 }
cristy10a6c612012-01-29 21:41:05 +00001465 if (GetPixelMask(image,p) != 0)
1466 {
1467 p+=GetPixelChannels(composite_image);
1468 q+=GetPixelChannels(image);
1469 continue;
1470 }
cristy9d3d2792012-04-14 15:15:19 +00001471 switch (compose)
1472 {
1473 case ColorizeCompositeOp:
1474 case HueCompositeOp:
1475 case LuminizeCompositeOp:
1476 case ModulateCompositeOp:
1477 case SaturateCompositeOp:
1478 {
1479 GetPixelInfoPixel(composite_image,p,&source_pixel);
1480 GetPixelInfoPixel(image,q,&destination_pixel);
1481 break;
1482 }
1483 default:
1484 break;
1485 }
cristye4a40472011-12-22 02:56:19 +00001486 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1487 {
cristy564a5692012-01-20 23:56:26 +00001488 double
1489 sans;
1490
cristya19f1d72012-08-07 18:24:38 +00001491 double
cristye4a40472011-12-22 02:56:19 +00001492 pixel;
cristye10859a2011-12-18 22:28:59 +00001493
cristye4a40472011-12-22 02:56:19 +00001494 PixelChannel
1495 channel;
cristye10859a2011-12-18 22:28:59 +00001496
cristye4a40472011-12-22 02:56:19 +00001497 PixelTrait
1498 composite_traits,
1499 traits;
1500
1501 channel=GetPixelChannelMapChannel(image,i);
1502 traits=GetPixelChannelMapTraits(image,channel);
1503 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001504 if (traits == UndefinedPixelTrait)
1505 continue;
cristya7b07912012-01-11 20:01:32 +00001506 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001507 (composite_traits == UndefinedPixelTrait))
1508 continue;
1509 /*
1510 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001511 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001512 */
cristya19f1d72012-08-07 18:24:38 +00001513 Sc=(double) GetPixelChannel(composite_image,channel,p);
1514 Dc=(double) q[i];
cristye4a40472011-12-22 02:56:19 +00001515 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001516 {
cristye4a40472011-12-22 02:56:19 +00001517 if (channel != AlphaPixelChannel)
1518 {
1519 /*
1520 Copy channel.
1521 */
1522 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001523 continue;
cristye10859a2011-12-18 22:28:59 +00001524 }
cristye4a40472011-12-22 02:56:19 +00001525 /*
1526 Set alpha channel.
1527 */
cristye10859a2011-12-18 22:28:59 +00001528 switch (compose)
1529 {
cristyc8d63672012-01-11 13:03:13 +00001530 case AlphaCompositeOp:
1531 {
cristya7b07912012-01-11 20:01:32 +00001532 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001533 break;
1534 }
cristye4a40472011-12-22 02:56:19 +00001535 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001536 case CopyBlackCompositeOp:
1537 case CopyBlueCompositeOp:
1538 case CopyCyanCompositeOp:
1539 case CopyGreenCompositeOp:
1540 case CopyMagentaCompositeOp:
1541 case CopyRedCompositeOp:
1542 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001543 case SrcAtopCompositeOp:
1544 case DstCompositeOp:
1545 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001546 {
cristye4a40472011-12-22 02:56:19 +00001547 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001548 break;
1549 }
cristye10859a2011-12-18 22:28:59 +00001550 case ChangeMaskCompositeOp:
1551 {
cristye4a40472011-12-22 02:56:19 +00001552 MagickBooleanType
1553 equivalent;
1554
cristya19f1d72012-08-07 18:24:38 +00001555 if (Da > ((double) QuantumRange/2.0))
cristy99abff32011-12-24 20:45:16 +00001556 {
cristya19f1d72012-08-07 18:24:38 +00001557 pixel=(double) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001558 break;
1559 }
cristye4a40472011-12-22 02:56:19 +00001560 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001561 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001562 {
cristya19f1d72012-08-07 18:24:38 +00001563 pixel=(double) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001564 break;
1565 }
cristya19f1d72012-08-07 18:24:38 +00001566 pixel=(double) OpaqueAlpha;
cristye4a40472011-12-22 02:56:19 +00001567 break;
1568 }
cristy99abff32011-12-24 20:45:16 +00001569 case ClearCompositeOp:
1570 {
cristya19f1d72012-08-07 18:24:38 +00001571 pixel=(double) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001572 break;
1573 }
1574 case ColorizeCompositeOp:
1575 case HueCompositeOp:
1576 case LuminizeCompositeOp:
1577 case SaturateCompositeOp:
1578 {
1579 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1580 {
1581 pixel=QuantumRange*Da;
1582 break;
1583 }
1584 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1585 {
1586 pixel=QuantumRange*Sa;
1587 break;
1588 }
1589 if (Sa < Da)
1590 {
1591 pixel=QuantumRange*Da;
1592 break;
1593 }
1594 pixel=QuantumRange*Sa;
1595 break;
1596 }
cristy99abff32011-12-24 20:45:16 +00001597 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001598 {
1599 pixel=QuantumRange*Sa;
1600 if (composite_image->matte == MagickFalse)
1601 pixel=GetPixelIntensity(composite_image,p);
1602 break;
1603 }
1604 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001605 case DisplaceCompositeOp:
1606 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001607 case DstAtopCompositeOp:
1608 case ReplaceCompositeOp:
1609 case SrcCompositeOp:
1610 {
1611 pixel=QuantumRange*Sa;
1612 break;
1613 }
1614 case DarkenIntensityCompositeOp:
1615 {
cristy99abff32011-12-24 20:45:16 +00001616 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1617 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001618 break;
1619 }
cristy98621462011-12-31 22:31:11 +00001620 case IntensityCompositeOp:
1621 {
cristyf13c5942012-08-08 23:50:11 +00001622 pixel=GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001623 break;
1624 }
cristye4a40472011-12-22 02:56:19 +00001625 case LightenIntensityCompositeOp:
1626 {
1627 pixel=Sa*GetPixelIntensity(composite_image,p) >
1628 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001629 break;
1630 }
cristy99abff32011-12-24 20:45:16 +00001631 case ModulateCompositeOp:
1632 {
1633 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1634 {
1635 pixel=QuantumRange*Da;
1636 break;
1637 }
1638 pixel=QuantumRange*Da;
1639 break;
1640 }
cristye10859a2011-12-18 22:28:59 +00001641 default:
1642 {
cristye4a40472011-12-22 02:56:19 +00001643 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001644 break;
1645 }
1646 }
cristye4a40472011-12-22 02:56:19 +00001647 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001648 continue;
1649 }
1650 /*
cristy99abff32011-12-24 20:45:16 +00001651 Porter-Duff compositions:
1652 Sca: source normalized color multiplied by alpha.
1653 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001654 */
cristy99abff32011-12-24 20:45:16 +00001655 Sca=QuantumScale*Sa*Sc;
1656 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001657 switch (compose)
1658 {
cristye10859a2011-12-18 22:28:59 +00001659 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001660 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001661 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001662 {
cristy99abff32011-12-24 20:45:16 +00001663 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001664 break;
1665 }
1666 default:
1667 break;
1668 }
cristyc58380a2012-06-03 15:12:30 +00001669 gamma=MagickEpsilonReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001670 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001671 switch (compose)
1672 {
cristya7b07912012-01-11 20:01:32 +00001673 case AlphaCompositeOp:
1674 {
1675 pixel=QuantumRange*Sa;
1676 break;
1677 }
cristye4a40472011-12-22 02:56:19 +00001678 case AtopCompositeOp:
1679 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001680 {
cristye4a40472011-12-22 02:56:19 +00001681 pixel=Sc*Sa+Dc*(1.0-Sa);
1682 break;
cristye10859a2011-12-18 22:28:59 +00001683 }
cristye4a40472011-12-22 02:56:19 +00001684 case BlendCompositeOp:
1685 {
1686 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1687 break;
1688 }
1689 case BlurCompositeOp:
1690 case DisplaceCompositeOp:
1691 case DistortCompositeOp:
1692 case CopyCompositeOp:
1693 case ReplaceCompositeOp:
1694 case SrcCompositeOp:
1695 {
1696 pixel=Sc;
1697 break;
1698 }
1699 case BumpmapCompositeOp:
1700 {
cristy99abff32011-12-24 20:45:16 +00001701 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001702 {
1703 pixel=Dc;
1704 break;
1705 }
1706 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1707 break;
1708 }
cristy99abff32011-12-24 20:45:16 +00001709 case ChangeMaskCompositeOp:
1710 {
1711 pixel=Dc;
1712 break;
1713 }
1714 case ClearCompositeOp:
1715 {
1716 pixel=0.0;
1717 break;
1718 }
cristye4a40472011-12-22 02:56:19 +00001719 case ColorBurnCompositeOp:
1720 {
1721 /*
1722 Refer to the March 2009 SVG specification.
1723 */
1724 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1725 {
cristy99abff32011-12-24 20:45:16 +00001726 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001727 break;
1728 }
1729 if (Sca < MagickEpsilon)
1730 {
cristy99abff32011-12-24 20:45:16 +00001731 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001732 break;
1733 }
cristy99abff32011-12-24 20:45:16 +00001734 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1735 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001736 break;
1737 }
1738 case ColorDodgeCompositeOp:
1739 {
1740 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1741 {
cristy99abff32011-12-24 20:45:16 +00001742 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001743 break;
1744 }
1745 if (fabs(Sca-Sa) < MagickEpsilon)
1746 {
cristy99abff32011-12-24 20:45:16 +00001747 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001748 break;
1749 }
cristy99abff32011-12-24 20:45:16 +00001750 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001751 (1.0-Sa));
1752 break;
1753 }
1754 case ColorizeCompositeOp:
1755 {
cristy99abff32011-12-24 20:45:16 +00001756 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001757 {
1758 pixel=Dc;
1759 break;
1760 }
cristy99abff32011-12-24 20:45:16 +00001761 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001762 {
1763 pixel=Sc;
1764 break;
1765 }
cristy7133e642012-08-14 11:04:11 +00001766 CompositeHCL(destination_pixel.red,destination_pixel.green,
1767 destination_pixel.blue,&sans,&sans,&luma);
1768 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1769 &hue,&chroma,&sans);
1770 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001771 switch (channel)
1772 {
1773 case RedPixelChannel: pixel=red; break;
1774 case GreenPixelChannel: pixel=green; break;
1775 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001776 default: pixel=Dc; break;
1777 }
1778 break;
1779 }
cristye4a40472011-12-22 02:56:19 +00001780 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001781 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001782 {
cristy24d5d722012-05-17 12:27:27 +00001783 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001784 break;
1785 }
1786 case CopyBlackCompositeOp:
1787 {
cristyd197cbb2012-01-13 02:14:12 +00001788 if (channel == BlackPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001789 pixel=(double) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001790 break;
1791 }
1792 case CopyBlueCompositeOp:
1793 case CopyYellowCompositeOp:
1794 {
cristyd197cbb2012-01-13 02:14:12 +00001795 if (channel == BluePixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001796 pixel=(double) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001797 break;
1798 }
1799 case CopyGreenCompositeOp:
1800 case CopyMagentaCompositeOp:
1801 {
cristyd197cbb2012-01-13 02:14:12 +00001802 if (channel == GreenPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001803 pixel=(double) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001804 break;
1805 }
1806 case CopyRedCompositeOp:
1807 case CopyCyanCompositeOp:
1808 {
cristyd197cbb2012-01-13 02:14:12 +00001809 if (channel == RedPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001810 pixel=(double) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001811 break;
1812 }
cristy99abff32011-12-24 20:45:16 +00001813 case DarkenCompositeOp:
1814 {
1815 /*
1816 Darken is equivalent to a 'Minimum' method
1817 OR a greyscale version of a binary 'Or'
1818 OR the 'Intersection' of pixel sets.
1819 */
1820 if (Sc < Dc)
1821 {
1822 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1823 break;
1824 }
1825 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1826 break;
1827 }
cristye4a40472011-12-22 02:56:19 +00001828 case DarkenIntensityCompositeOp:
1829 {
cristy99abff32011-12-24 20:45:16 +00001830 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1831 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001832 break;
1833 }
1834 case DifferenceCompositeOp:
1835 {
1836 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1837 break;
1838 }
1839 case DissolveCompositeOp:
1840 {
1841 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1842 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1843 break;
1844 }
1845 case DivideDstCompositeOp:
1846 {
1847 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1848 {
cristy99abff32011-12-24 20:45:16 +00001849 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001850 break;
1851 }
1852 if (fabs(Dca) < MagickEpsilon)
1853 {
cristy99abff32011-12-24 20:45:16 +00001854 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001855 break;
1856 }
cristy99abff32011-12-24 20:45:16 +00001857 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001858 break;
1859 }
1860 case DivideSrcCompositeOp:
1861 {
1862 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1863 {
cristy99abff32011-12-24 20:45:16 +00001864 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001865 break;
1866 }
1867 if (fabs(Sca) < MagickEpsilon)
1868 {
cristy99abff32011-12-24 20:45:16 +00001869 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001870 break;
1871 }
cristy99abff32011-12-24 20:45:16 +00001872 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001873 break;
1874 }
1875 case DstAtopCompositeOp:
1876 {
1877 pixel=Dc*Da+Sc*(1.0-Da);
1878 break;
1879 }
1880 case DstCompositeOp:
1881 case NoCompositeOp:
1882 {
1883 pixel=Dc;
1884 break;
1885 }
1886 case DstInCompositeOp:
1887 {
1888 pixel=gamma*(Sa*Dc*Sa);
1889 break;
1890 }
1891 case DstOutCompositeOp:
1892 {
1893 pixel=gamma*(Da*Dc*(1.0-Sa));
1894 break;
1895 }
1896 case DstOverCompositeOp:
1897 {
1898 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1899 break;
1900 }
1901 case ExclusionCompositeOp:
1902 {
cristy99abff32011-12-24 20:45:16 +00001903 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1904 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001905 break;
1906 }
1907 case HardLightCompositeOp:
1908 {
1909 if ((2.0*Sca) < Sa)
1910 {
cristy99abff32011-12-24 20:45:16 +00001911 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001912 (1.0-Sa));
1913 break;
1914 }
cristy99abff32011-12-24 20:45:16 +00001915 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001916 Dca*(1.0-Sa));
1917 break;
1918 }
1919 case HueCompositeOp:
1920 {
cristy99abff32011-12-24 20:45:16 +00001921 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001922 {
1923 pixel=Dc;
1924 break;
1925 }
cristy99abff32011-12-24 20:45:16 +00001926 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001927 {
1928 pixel=Sc;
1929 break;
1930 }
cristy7133e642012-08-14 11:04:11 +00001931 CompositeHCL(destination_pixel.red,destination_pixel.green,
1932 destination_pixel.blue,&hue,&chroma,&luma);
1933 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001934 &hue,&sans,&sans);
cristy7133e642012-08-14 11:04:11 +00001935 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001936 switch (channel)
1937 {
1938 case RedPixelChannel: pixel=red; break;
1939 case GreenPixelChannel: pixel=green; break;
1940 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001941 default: pixel=Dc; break;
1942 }
1943 break;
1944 }
1945 case InCompositeOp:
1946 case SrcInCompositeOp:
1947 {
1948 pixel=gamma*(Da*Sc*Da);
1949 break;
1950 }
cristy99abff32011-12-24 20:45:16 +00001951 case LinearBurnCompositeOp:
1952 {
1953 /*
1954 LinearBurn: as defined by Abode Photoshop, according to
1955 http://www.simplefilter.de/en/basics/mixmods.html is:
1956
1957 f(Sc,Dc) = Sc + Dc - 1
1958 */
1959 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1960 break;
1961 }
1962 case LinearDodgeCompositeOp:
1963 {
1964 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1965 break;
1966 }
1967 case LinearLightCompositeOp:
1968 {
1969 /*
1970 LinearLight: as defined by Abode Photoshop, according to
1971 http://www.simplefilter.de/en/basics/mixmods.html is:
1972
1973 f(Sc,Dc) = Dc + 2*Sc - 1
1974 */
1975 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1976 break;
1977 }
1978 case LightenCompositeOp:
1979 {
1980 if (Sc > Dc)
1981 {
cristy24d5d722012-05-17 12:27:27 +00001982 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00001983 break;
1984 }
cristy24d5d722012-05-17 12:27:27 +00001985 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00001986 break;
1987 }
cristye4a40472011-12-22 02:56:19 +00001988 case LightenIntensityCompositeOp:
1989 {
1990 /*
1991 Lighten is equivalent to a 'Maximum' method
1992 OR a greyscale version of a binary 'And'
1993 OR the 'Union' of pixel sets.
1994 */
1995 pixel=Sa*GetPixelIntensity(composite_image,p) >
1996 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1997 break;
1998 }
cristye4a40472011-12-22 02:56:19 +00001999 case LuminizeCompositeOp:
2000 {
cristy99abff32011-12-24 20:45:16 +00002001 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002002 {
2003 pixel=Dc;
2004 break;
2005 }
cristy99abff32011-12-24 20:45:16 +00002006 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002007 {
2008 pixel=Sc;
2009 break;
2010 }
cristy7133e642012-08-14 11:04:11 +00002011 CompositeHCL(destination_pixel.red,destination_pixel.green,
2012 destination_pixel.blue,&hue,&chroma,&luma);
2013 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2014 &sans,&sans,&luma);
2015 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002016 switch (channel)
2017 {
2018 case RedPixelChannel: pixel=red; break;
2019 case GreenPixelChannel: pixel=green; break;
2020 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002021 default: pixel=Dc; break;
2022 }
2023 break;
2024 }
2025 case MathematicsCompositeOp:
2026 {
2027 /*
2028 'Mathematics' a free form user control mathematical composition
2029 is defined as...
2030
2031 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2032
2033 Where the arguments A,B,C,D are (currently) passed to composite
2034 as a command separated 'geometry' string in "compose:args" image
2035 artifact.
2036
2037 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2038
2039 Applying the SVG transparency formula (see above), we get...
2040
2041 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2042
2043 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2044 Dca*(1.0-Sa)
2045 */
2046 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2047 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2048 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2049 break;
2050 }
2051 case MinusDstCompositeOp:
2052 {
2053 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2054 break;
2055 }
2056 case MinusSrcCompositeOp:
2057 {
2058 /*
2059 Minus source from destination.
2060
2061 f(Sc,Dc) = Sc - Dc
2062 */
cristy99abff32011-12-24 20:45:16 +00002063 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002064 break;
2065 }
2066 case ModulateCompositeOp:
2067 {
2068 ssize_t
2069 offset;
2070
cristy99abff32011-12-24 20:45:16 +00002071 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002072 {
2073 pixel=Dc;
2074 break;
2075 }
2076 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2077 if (offset == 0)
2078 {
2079 pixel=Dc;
2080 break;
2081 }
cristy7133e642012-08-14 11:04:11 +00002082 CompositeHCL(destination_pixel.red,destination_pixel.green,
2083 destination_pixel.blue,&hue,&chroma,&luma);
2084 luma+=(0.01*percent_luma*offset)/midpoint;
2085 chroma*=0.01*percent_chroma;
2086 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002087 switch (channel)
2088 {
2089 case RedPixelChannel: pixel=red; break;
2090 case GreenPixelChannel: pixel=green; break;
2091 case BluePixelChannel: pixel=blue; break;
2092 default: pixel=Dc; break;
2093 }
2094 break;
2095 }
2096 case ModulusAddCompositeOp:
2097 {
2098 pixel=Sc+Dc;
2099 if (pixel > QuantumRange)
2100 pixel-=(QuantumRange+1.0);
2101 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2102 break;
2103 }
2104 case ModulusSubtractCompositeOp:
2105 {
cristy99abff32011-12-24 20:45:16 +00002106 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002107 if (pixel < 0.0)
2108 pixel+=(QuantumRange+1.0);
2109 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2110 break;
2111 }
2112 case MultiplyCompositeOp:
2113 {
cristy99abff32011-12-24 20:45:16 +00002114 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002115 break;
2116 }
2117 case OutCompositeOp:
2118 case SrcOutCompositeOp:
2119 {
2120 pixel=gamma*(Sa*Sc*(1.0-Da));
2121 break;
2122 }
2123 case OverCompositeOp:
2124 case SrcOverCompositeOp:
2125 {
cristy99abff32011-12-24 20:45:16 +00002126 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002127 break;
2128 }
2129 case OverlayCompositeOp:
2130 {
2131 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002132 {
2133 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2134 (1.0-Da));
2135 break;
2136 }
2137 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2138 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002139 break;
2140 }
2141 case PegtopLightCompositeOp:
2142 {
2143 /*
2144 PegTop: A Soft-Light alternative: A continuous version of the
2145 Softlight function, producing very similar results.
2146
2147 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2148
2149 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2150 */
2151 if (fabs(Da) < MagickEpsilon)
2152 {
cristy99abff32011-12-24 20:45:16 +00002153 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002154 break;
2155 }
cristy99abff32011-12-24 20:45:16 +00002156 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2157 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002158 break;
2159 }
2160 case PinLightCompositeOp:
2161 {
2162 /*
2163 PinLight: A Photoshop 7 composition method
2164 http://www.simplefilter.de/en/basics/mixmods.html
2165
2166 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2167 */
2168 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2169 {
cristy99abff32011-12-24 20:45:16 +00002170 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002171 break;
2172 }
2173 if ((Dca*Sa) > (2.0*Sca*Da))
2174 {
cristy99abff32011-12-24 20:45:16 +00002175 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002176 break;
2177 }
cristy99abff32011-12-24 20:45:16 +00002178 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002179 break;
2180 }
2181 case PlusCompositeOp:
2182 {
cristy24d5d722012-05-17 12:27:27 +00002183 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002184 break;
2185 }
2186 case SaturateCompositeOp:
2187 {
cristy99abff32011-12-24 20:45:16 +00002188 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002189 {
2190 pixel=Dc;
2191 break;
2192 }
cristy99abff32011-12-24 20:45:16 +00002193 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002194 {
2195 pixel=Sc;
2196 break;
2197 }
cristy7133e642012-08-14 11:04:11 +00002198 CompositeHCL(destination_pixel.red,destination_pixel.green,
2199 destination_pixel.blue,&hue,&chroma,&luma);
2200 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2201 &sans,&chroma,&sans);
2202 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002203 switch (channel)
2204 {
2205 case RedPixelChannel: pixel=red; break;
2206 case GreenPixelChannel: pixel=green; break;
2207 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002208 default: pixel=Dc; break;
2209 }
2210 break;
2211 }
2212 case ScreenCompositeOp:
2213 {
2214 /*
2215 Screen: a negated multiply:
2216
2217 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2218 */
cristy99abff32011-12-24 20:45:16 +00002219 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002220 break;
2221 }
2222 case SoftLightCompositeOp:
2223 {
2224 /*
2225 Refer to the March 2009 SVG specification.
2226 */
2227 if ((2.0*Sca) < Sa)
2228 {
cristy99abff32011-12-24 20:45:16 +00002229 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2230 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002231 break;
2232 }
2233 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2234 {
cristy99abff32011-12-24 20:45:16 +00002235 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2236 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2237 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002238 break;
2239 }
cristy99abff32011-12-24 20:45:16 +00002240 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2241 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002242 break;
2243 }
2244 case ThresholdCompositeOp:
2245 {
cristya19f1d72012-08-07 18:24:38 +00002246 double
cristye4a40472011-12-22 02:56:19 +00002247 delta;
2248
2249 delta=Sc-Dc;
cristya19f1d72012-08-07 18:24:38 +00002250 if ((double) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002251 {
2252 pixel=gamma*Dc;
2253 break;
2254 }
2255 pixel=gamma*(Dc+delta*amount);
2256 break;
2257 }
2258 case VividLightCompositeOp:
2259 {
2260 /*
2261 VividLight: A Photoshop 7 composition method. See
2262 http://www.simplefilter.de/en/basics/mixmods.html.
2263
2264 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2265 */
2266 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2267 {
cristy99abff32011-12-24 20:45:16 +00002268 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002269 break;
2270 }
2271 if ((2.0*Sca) <= Sa)
2272 {
cristy99abff32011-12-24 20:45:16 +00002273 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2274 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002275 break;
2276 }
cristy99abff32011-12-24 20:45:16 +00002277 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2278 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002279 break;
2280 }
2281 case XorCompositeOp:
2282 {
cristy99abff32011-12-24 20:45:16 +00002283 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002284 break;
2285 }
2286 default:
2287 {
2288 pixel=Sc;
2289 break;
2290 }
2291 }
2292 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002293 }
cristyed231572011-07-14 02:18:59 +00002294 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002295 channels=GetPixelChannels(composite_image);
2296 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002297 p=pixels;
cristyed231572011-07-14 02:18:59 +00002298 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002299 }
2300 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2301 status=MagickFalse;
2302 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2303 {
2304 MagickBooleanType
2305 proceed;
2306
2307#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002308 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002309#endif
2310 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2311 image->rows);
2312 if (proceed == MagickFalse)
2313 status=MagickFalse;
2314 }
2315 }
2316 composite_view=DestroyCacheView(composite_view);
2317 image_view=DestroyCacheView(image_view);
2318 if (destination_image != (Image * ) NULL)
2319 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002320 else
2321 composite_image=DestroyImage(composite_image);
cristy191c0b72012-08-12 16:29:52 +00002322 if (status != MagickFalse)
2323 (void) ClampImage(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00002324 return(status);
2325}
2326
2327/*
2328%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2329% %
2330% %
2331% %
2332% T e x t u r e I m a g e %
2333% %
2334% %
2335% %
2336%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2337%
2338% TextureImage() repeatedly tiles the texture image across and down the image
2339% canvas.
2340%
2341% The format of the TextureImage method is:
2342%
cristy30d8c942012-02-07 13:44:59 +00002343% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002344% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002345%
2346% A description of each parameter follows:
2347%
2348% o image: the image.
2349%
cristye6178502011-12-23 17:02:29 +00002350% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002351%
2352*/
cristy30d8c942012-02-07 13:44:59 +00002353MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2354 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002355{
2356#define TextureImageTag "Texture/Image"
2357
2358 CacheView
2359 *image_view,
2360 *texture_view;
2361
cristy30d8c942012-02-07 13:44:59 +00002362 Image
2363 *texture_image;
2364
cristy4c08aed2011-07-01 19:47:50 +00002365 MagickBooleanType
2366 status;
2367
2368 ssize_t
2369 y;
2370
2371 assert(image != (Image *) NULL);
2372 if (image->debug != MagickFalse)
2373 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2374 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002375 if (texture == (const Image *) NULL)
2376 return(MagickFalse);
2377 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2378 return(MagickFalse);
2379 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002380 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002381 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002382 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002383 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2384 exception);
cristy4c08aed2011-07-01 19:47:50 +00002385 status=MagickTrue;
2386 if ((image->compose != CopyCompositeOp) &&
2387 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002388 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002389 {
2390 /*
2391 Tile texture onto the image background.
2392 */
2393#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002394 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002395 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002396#endif
cristye6178502011-12-23 17:02:29 +00002397 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002398 {
2399 register ssize_t
2400 x;
2401
2402 if (status == MagickFalse)
2403 continue;
cristye6178502011-12-23 17:02:29 +00002404 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002405 {
2406 MagickBooleanType
2407 thread_status;
2408
cristyfeb3e962012-03-29 17:25:55 +00002409 thread_status=CompositeImage(image,texture_image,image->compose,
2410 MagickFalse,x+texture_image->tile_offset.x,y+
2411 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002412 if (thread_status == MagickFalse)
2413 {
2414 status=thread_status;
2415 break;
2416 }
2417 }
2418 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2419 {
2420 MagickBooleanType
2421 proceed;
2422
2423#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002424 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002425#endif
2426 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2427 y,image->rows);
2428 if (proceed == MagickFalse)
2429 status=MagickFalse;
2430 }
2431 }
2432 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2433 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002434 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002435 return(status);
2436 }
2437 /*
2438 Tile texture onto the image background (optimized).
2439 */
2440 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +00002441 texture_view=AcquireVirtualCacheView(texture_image,exception);
2442 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00002443#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002444 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002445 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002446#endif
2447 for (y=0; y < (ssize_t) image->rows; y++)
2448 {
2449 MagickBooleanType
2450 sync;
2451
2452 register const Quantum
2453 *p,
2454 *pixels;
2455
2456 register ssize_t
2457 x;
2458
2459 register Quantum
2460 *q;
2461
2462 size_t
2463 width;
2464
2465 if (status == MagickFalse)
2466 continue;
cristye6178502011-12-23 17:02:29 +00002467 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2468 (y+texture_image->tile_offset.y) % texture_image->rows,
2469 texture_image->columns,1,exception);
2470 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002471 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2472 {
2473 status=MagickFalse;
2474 continue;
2475 }
cristye6178502011-12-23 17:02:29 +00002476 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002477 {
2478 register ssize_t
cristye6178502011-12-23 17:02:29 +00002479 j;
cristy4c08aed2011-07-01 19:47:50 +00002480
2481 p=pixels;
cristye6178502011-12-23 17:02:29 +00002482 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002483 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2484 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002485 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002486 {
cristye6178502011-12-23 17:02:29 +00002487 register ssize_t
2488 i;
2489
cristy10a6c612012-01-29 21:41:05 +00002490 if (GetPixelMask(image,p) != 0)
2491 {
2492 p+=GetPixelChannels(texture_image);
2493 q+=GetPixelChannels(image);
2494 continue;
2495 }
cristye6178502011-12-23 17:02:29 +00002496 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2497 {
2498 PixelChannel
2499 channel;
2500
2501 PixelTrait
2502 texture_traits,
2503 traits;
2504
2505 channel=GetPixelChannelMapChannel(texture_image,i);
2506 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2507 traits=GetPixelChannelMapTraits(image,channel);
2508 if ((traits == UndefinedPixelTrait) ||
2509 (texture_traits == UndefinedPixelTrait))
2510 continue;
2511 SetPixelChannel(image,channel,p[i],q);
2512 }
2513 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002514 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002515 }
2516 }
2517 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2518 if (sync == MagickFalse)
2519 status=MagickFalse;
2520 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2521 {
2522 MagickBooleanType
2523 proceed;
2524
2525#if defined(MAGICKCORE_OPENMP_SUPPORT)
2526 #pragma omp critical (MagickCore_TextureImage)
2527#endif
2528 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2529 image->rows);
2530 if (proceed == MagickFalse)
2531 status=MagickFalse;
2532 }
2533 }
2534 texture_view=DestroyCacheView(texture_view);
2535 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002536 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002537 return(status);
2538}