blob: 64f5afb8b8f136ce704da46205c35efeadc1187e [file] [log] [blame]
cristy4c08aed2011-07-01 19:47:50 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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"
cristy63a81872012-03-22 15:52:52 +000075#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000076#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000077#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000078#include "MagickCore/version.h"
79
80/*
81%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82% %
83% %
84% %
cristyf4ad9df2011-07-08 16:49:03 +000085% C o m p o s i t e I m a g e %
cristy4c08aed2011-07-01 19:47:50 +000086% %
87% %
88% %
89%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90%
cristyf4ad9df2011-07-08 16:49:03 +000091% CompositeImage() returns the second image composited onto the first
cristy4c08aed2011-07-01 19:47:50 +000092% at the specified offset, using the specified composite method.
93%
cristyf4ad9df2011-07-08 16:49:03 +000094% The format of the CompositeImage method is:
cristy4c08aed2011-07-01 19:47:50 +000095%
96% MagickBooleanType CompositeImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +000097% const Image *composite_image,const CompositeOperator compose,
98% const MagickBooleanType clip_to_self,const ssize_t x_offset,
99% const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000100%
101% A description of each parameter follows:
102%
103% o image: the destination image, modified by he composition
104%
cristyfeb3e962012-03-29 17:25:55 +0000105% o composite_image: the composite (source) image.
106%
cristy4c08aed2011-07-01 19:47:50 +0000107% o compose: This operator affects how the composite is applied to
108% the image. The operators and how they are utilized are listed here
109% http://www.w3.org/TR/SVG12/#compositing.
110%
cristyfeb3e962012-03-29 17:25:55 +0000111% o clip_to_self: set to MagickTrue to limit composition to area composed.
cristy4c08aed2011-07-01 19:47:50 +0000112%
113% o x_offset: the column offset of the composited image.
114%
115% o y_offset: the row offset of the composited image.
116%
117% Extra Controls from Image meta-data in 'composite_image' (artifacts)
118%
119% o "compose:args"
120% A string containing extra numerical arguments for specific compose
121% methods, generally expressed as a 'geometry' or a comma separated list
122% of numbers.
123%
124% Compose methods needing such arguments include "BlendCompositeOp" and
125% "DisplaceCompositeOp".
126%
cristye941a752011-10-15 01:52:48 +0000127% o exception: return any errors or warnings in this structure.
128%
cristy4c08aed2011-07-01 19:47:50 +0000129*/
130
anthonyea068a52012-04-09 05:46:25 +0000131/*
132 Composition based on the SVG specification:
133
134 A Composition is defined by...
135 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
136 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
137 Y = 1 for source preserved
138 Z = 1 for destination preserved
139
140 Conversion to transparency (then optimized)
141 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
142 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
143
144 Where...
145 Sca = Sc*Sa normalized Source color divided by Source alpha
146 Dca = Dc*Da normalized Dest color divided by Dest alpha
147 Dc' = Dca'/Da' the desired color value for this channel.
148
149 Da' in in the follow formula as 'gamma' The resulting alpla value.
150
151 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
152 the following optimizations...
153 gamma = Sa+Da-Sa*Da;
154 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
155 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
156
157 The above SVG definitions also definate that Mathematical Composition
158 methods should use a 'Over' blending mode for Alpha Channel.
159 It however was not applied for composition modes of 'Plus', 'Minus',
160 the modulus versions of 'Add' and 'Subtract'.
161
162 Mathematical operator changes to be applied from IM v6.7...
163
164 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
165 'ModulusAdd' and 'ModulusSubtract' for clarity.
166
167 2) All mathematical compositions work as per the SVG specification
168 with regard to blending. This now includes 'ModulusAdd' and
169 'ModulusSubtract'.
170
171 3) When the special channel flag 'sync' (syncronize channel updates)
172 is turned off (enabled by default) then mathematical compositions are
173 only performed on the channels specified, and are applied
174 independantally of each other. In other words the mathematics is
175 performed as 'pure' mathematical operations, rather than as image
176 operations.
177*/
cristyddeeea22012-04-12 01:33:09 +0000178static void CompositeHSB(const double red,const double green,
179 const double blue,double *hue,double *saturation,double *brightness)
cristy4c08aed2011-07-01 19:47:50 +0000180{
cristye4a40472011-12-22 02:56:19 +0000181 double
cristyddeeea22012-04-12 01:33:09 +0000182 delta,
cristy4c08aed2011-07-01 19:47:50 +0000183 max,
184 min;
185
186 /*
187 Convert RGB to HSB colorspace.
188 */
189 assert(hue != (double *) NULL);
190 assert(saturation != (double *) NULL);
191 assert(brightness != (double *) NULL);
192 max=(red > green ? red : green);
193 if (blue > max)
194 max=blue;
195 min=(red < green ? red : green);
196 if (blue < min)
197 min=blue;
198 *hue=0.0;
199 *saturation=0.0;
200 *brightness=(double) (QuantumScale*max);
cristye4a40472011-12-22 02:56:19 +0000201 if (fabs((double) max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000202 return;
203 *saturation=(double) (1.0-min/max);
cristye4a40472011-12-22 02:56:19 +0000204 delta=(MagickRealType) max-min;
cristyaa83c2c2011-09-21 13:36:25 +0000205 if (fabs(delta) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000206 return;
cristye4a40472011-12-22 02:56:19 +0000207 if (fabs((double) red-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000208 *hue=(double) ((green-blue)/delta);
209 else
cristye4a40472011-12-22 02:56:19 +0000210 if (fabs((double) green-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000211 *hue=(double) (2.0+(blue-red)/delta);
212 else
cristye4a40472011-12-22 02:56:19 +0000213 if (fabs((double) blue-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000214 *hue=(double) (4.0+(red-green)/delta);
215 *hue/=6.0;
216 if (*hue < 0.0)
217 *hue+=1.0;
218}
219
cristy4c08aed2011-07-01 19:47:50 +0000220static void HSBComposite(const double hue,const double saturation,
cristye4a40472011-12-22 02:56:19 +0000221 const double brightness,double *red,double *green,double *blue)
cristy4c08aed2011-07-01 19:47:50 +0000222{
cristya96f2492011-12-14 18:25:41 +0000223 double
cristy4c08aed2011-07-01 19:47:50 +0000224 f,
225 h,
226 p,
227 q,
228 t;
229
230 /*
231 Convert HSB to RGB colorspace.
232 */
cristya96f2492011-12-14 18:25:41 +0000233 assert(red != (double *) NULL);
234 assert(green != (double *) NULL);
235 assert(blue != (double *) NULL);
cristy4c08aed2011-07-01 19:47:50 +0000236 if (saturation == 0.0)
237 {
cristya96f2492011-12-14 18:25:41 +0000238 *red=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000239 *green=(*red);
240 *blue=(*red);
241 return;
242 }
243 h=6.0*(hue-floor(hue));
244 f=h-floor((double) h);
245 p=brightness*(1.0-saturation);
246 q=brightness*(1.0-saturation*f);
247 t=brightness*(1.0-saturation*(1.0-f));
248 switch ((int) h)
249 {
250 case 0:
251 default:
252 {
cristya96f2492011-12-14 18:25:41 +0000253 *red=(double) QuantumRange*brightness;
254 *green=(double) QuantumRange*t;
255 *blue=(double) QuantumRange*p;
cristy4c08aed2011-07-01 19:47:50 +0000256 break;
257 }
258 case 1:
259 {
cristya96f2492011-12-14 18:25:41 +0000260 *red=(double) QuantumRange*q;
261 *green=(double) QuantumRange*brightness;
262 *blue=(double) QuantumRange*p;
cristy4c08aed2011-07-01 19:47:50 +0000263 break;
264 }
265 case 2:
266 {
cristya96f2492011-12-14 18:25:41 +0000267 *red=(double) QuantumRange*p;
268 *green=(double) QuantumRange*brightness;
269 *blue=(double) QuantumRange*t;
cristy4c08aed2011-07-01 19:47:50 +0000270 break;
271 }
272 case 3:
273 {
cristya96f2492011-12-14 18:25:41 +0000274 *red=(double) QuantumRange*p;
275 *green=(double) QuantumRange*q;
276 *blue=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000277 break;
278 }
279 case 4:
280 {
cristya96f2492011-12-14 18:25:41 +0000281 *red=(double) QuantumRange*t;
282 *green=(double) QuantumRange*p;
283 *blue=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000284 break;
285 }
286 case 5:
287 {
cristya96f2492011-12-14 18:25:41 +0000288 *red=(double) QuantumRange*brightness;
289 *green=(double) QuantumRange*p;
290 *blue=(double) QuantumRange*q;
cristy4c08aed2011-07-01 19:47:50 +0000291 break;
292 }
293 }
294}
295
cristye4a40472011-12-22 02:56:19 +0000296static inline double MagickMin(const double x,const double y)
297{
298 if (x < y)
299 return(x);
300 return(y);
301}
cristyddeeea22012-04-12 01:33:09 +0000302
cristye4a40472011-12-22 02:56:19 +0000303static inline double MagickMax(const double x,const double y)
304{
305 if (x > y)
306 return(x);
307 return(y);
308}
309
310static MagickBooleanType CompositeOverImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000311 const Image *composite_image,const MagickBooleanType clip_to_self,
312 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristye4a40472011-12-22 02:56:19 +0000313{
314#define CompositeImageTag "Composite/Image"
315
316 CacheView
317 *composite_view,
318 *image_view;
319
cristye4a40472011-12-22 02:56:19 +0000320 MagickBooleanType
cristye4a40472011-12-22 02:56:19 +0000321 status;
322
323 MagickOffsetType
324 progress;
325
326 ssize_t
327 y;
328
cristye4a40472011-12-22 02:56:19 +0000329 /*
cristye4a40472011-12-22 02:56:19 +0000330 Composite image.
331 */
332 status=MagickTrue;
333 progress=0;
cristydb070952012-04-20 14:33:00 +0000334 composite_view=AcquireVirtualCacheView(composite_image,exception);
335 image_view=AcquireAuthenticCacheView(image,exception);
cristye4a40472011-12-22 02:56:19 +0000336#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000337 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000338 dynamic_number_threads(image,image->columns,image->rows,1)
cristye4a40472011-12-22 02:56:19 +0000339#endif
340 for (y=0; y < (ssize_t) image->rows; y++)
341 {
342 const Quantum
343 *pixels;
344
345 register const Quantum
346 *restrict p;
347
348 register Quantum
349 *restrict q;
350
351 register ssize_t
352 x;
353
cristy564a5692012-01-20 23:56:26 +0000354 size_t
355 channels;
356
cristye4a40472011-12-22 02:56:19 +0000357 if (status == MagickFalse)
358 continue;
cristyfeb3e962012-03-29 17:25:55 +0000359 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000360 {
361 if (y < y_offset)
362 continue;
363 if ((y-y_offset) >= (ssize_t) composite_image->rows)
364 continue;
365 }
366 /*
367 If pixels is NULL, y is outside overlay region.
368 */
369 pixels=(Quantum *) NULL;
370 p=(Quantum *) NULL;
371 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
372 {
373 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
374 composite_image->columns,1,exception);
375 if (p == (const Quantum *) NULL)
376 {
377 status=MagickFalse;
378 continue;
379 }
380 pixels=p;
381 if (x_offset < 0)
382 p-=x_offset*GetPixelChannels(composite_image);
383 }
384 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
385 if (q == (Quantum *) NULL)
386 {
387 status=MagickFalse;
388 continue;
389 }
390 for (x=0; x < (ssize_t) image->columns; x++)
391 {
392 MagickRealType
393 alpha,
394 Da,
395 Dc,
396 gamma,
397 Sa,
398 Sc;
399
400 register ssize_t
401 i;
402
cristyfeb3e962012-03-29 17:25:55 +0000403 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000404 {
405 if (x < x_offset)
406 {
407 q+=GetPixelChannels(image);
408 continue;
409 }
410 if ((x-x_offset) >= (ssize_t) composite_image->columns)
411 break;
412 }
413 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
414 ((x-x_offset) >= (ssize_t) composite_image->columns))
415 {
416 Quantum
417 source[MaxPixelChannels];
418
419 /*
420 Virtual composite:
421 Sc: source color.
422 Dc: destination color.
423 */
cristy10a6c612012-01-29 21:41:05 +0000424 if (GetPixelMask(image,q) != 0)
425 {
426 q+=GetPixelChannels(image);
427 continue;
428 }
cristyc94ba6f2012-01-29 23:19:58 +0000429 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
430 source,exception);
cristye4a40472011-12-22 02:56:19 +0000431 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
432 {
433 PixelChannel
434 channel;
435
436 PixelTrait
437 composite_traits,
438 traits;
439
440 channel=GetPixelChannelMapChannel(image,i);
441 traits=GetPixelChannelMapTraits(image,channel);
cristy10a6c612012-01-29 21:41:05 +0000442 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000443 if ((traits == UndefinedPixelTrait) ||
444 (composite_traits == UndefinedPixelTrait))
445 continue;
446 q[i]=source[channel];
447 }
448 q+=GetPixelChannels(image);
449 continue;
450 }
451 /*
452 Authentic composite:
453 Sa: normalized source alpha.
454 Da: normalized destination alpha.
455 */
cristyc94ba6f2012-01-29 23:19:58 +0000456 if (GetPixelMask(composite_image,p) != 0)
457 {
458 p+=GetPixelChannels(composite_image);
459 channels=GetPixelChannels(composite_image);
460 if (p >= (pixels+channels*composite_image->columns))
461 p=pixels;
462 q+=GetPixelChannels(image);
463 continue;
464 }
cristye4a40472011-12-22 02:56:19 +0000465 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
466 Da=QuantumScale*GetPixelAlpha(image,q);
467 alpha=Sa*(-Da)+Sa+Da;
468 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
469 {
470 PixelChannel
471 channel;
472
473 PixelTrait
474 composite_traits,
475 traits;
476
477 channel=GetPixelChannelMapChannel(image,i);
478 traits=GetPixelChannelMapTraits(image,channel);
479 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
480 if ((traits == UndefinedPixelTrait) ||
481 (composite_traits == UndefinedPixelTrait))
482 continue;
483 if ((traits & CopyPixelTrait) != 0)
484 {
485 if (channel != AlphaPixelChannel)
486 {
487 /*
488 Copy channel.
489 */
490 q[i]=GetPixelChannel(composite_image,channel,p);
491 continue;
492 }
493 /*
494 Set alpha channel.
495 */
496 q[i]=ClampToQuantum(QuantumRange*alpha);
497 continue;
498 }
499 /*
500 Sc: source color.
501 Dc: destination color.
502 */
503 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
504 Dc=(MagickRealType) q[i];
cristyc58380a2012-06-03 15:12:30 +0000505 gamma=MagickEpsilonReciprocal(alpha);
cristye4a40472011-12-22 02:56:19 +0000506 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
507 }
508 p+=GetPixelChannels(composite_image);
509 channels=GetPixelChannels(composite_image);
510 if (p >= (pixels+channels*composite_image->columns))
511 p=pixels;
512 q+=GetPixelChannels(image);
513 }
514 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
515 status=MagickFalse;
516 if (image->progress_monitor != (MagickProgressMonitor) NULL)
517 {
518 MagickBooleanType
519 proceed;
520
521#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000522 #pragma omp critical (MagickCore_CompositeImage)
cristye4a40472011-12-22 02:56:19 +0000523#endif
524 proceed=SetImageProgress(image,CompositeImageTag,progress++,
525 image->rows);
526 if (proceed == MagickFalse)
527 status=MagickFalse;
528 }
529 }
530 composite_view=DestroyCacheView(composite_view);
531 image_view=DestroyCacheView(image_view);
532 return(status);
533}
534
cristy4c08aed2011-07-01 19:47:50 +0000535MagickExport MagickBooleanType CompositeImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000536 const Image *composite_image,const CompositeOperator compose,
cristy44c98412012-03-31 18:25:40 +0000537 const MagickBooleanType clip_to_self,const ssize_t x_offset,
538 const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000539{
cristy4c08aed2011-07-01 19:47:50 +0000540#define CompositeImageTag "Composite/Image"
541
542 CacheView
543 *composite_view,
544 *image_view;
545
cristy4c08aed2011-07-01 19:47:50 +0000546 GeometryInfo
547 geometry_info;
548
549 Image
550 *destination_image;
551
552 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000553 status;
554
555 MagickOffsetType
556 progress;
557
cristy4c08aed2011-07-01 19:47:50 +0000558 MagickRealType
559 amount,
560 destination_dissolve,
561 midpoint,
562 percent_brightness,
563 percent_saturation,
564 source_dissolve,
565 threshold;
566
567 MagickStatusType
568 flags;
569
cristyd197cbb2012-01-13 02:14:12 +0000570 ssize_t
571 y;
572
cristy4c08aed2011-07-01 19:47:50 +0000573 assert(image != (Image *) NULL);
574 assert(image->signature == MagickSignature);
575 if (image->debug != MagickFalse)
576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
577 assert(composite_image != (Image *) NULL);
578 assert(composite_image->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000579 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000580 return(MagickFalse);
cristy9e210442012-04-08 22:31:14 +0000581 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
582 (IsGrayColorspace(composite_image->colorspace) == MagickFalse))
583 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristye4a40472011-12-22 02:56:19 +0000584 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
585 {
cristyfeb3e962012-03-29 17:25:55 +0000586 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
587 y_offset,exception);
cristye4a40472011-12-22 02:56:19 +0000588 return(status);
589 }
cristy4c08aed2011-07-01 19:47:50 +0000590 destination_image=(Image *) NULL;
591 amount=0.5;
592 destination_dissolve=1.0;
cristy4c08aed2011-07-01 19:47:50 +0000593 percent_brightness=100.0;
594 percent_saturation=100.0;
595 source_dissolve=1.0;
596 threshold=0.05f;
597 switch (compose)
598 {
cristy4c08aed2011-07-01 19:47:50 +0000599 case CopyCompositeOp:
600 {
601 if ((x_offset < 0) || (y_offset < 0))
602 break;
603 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
604 break;
605 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
606 break;
607 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +0000608 composite_view=AcquireVirtualCacheView(composite_image,exception);
609 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000610#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000611 #pragma omp parallel for schedule(static,4) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000612 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000613#endif
614 for (y=0; y < (ssize_t) composite_image->rows; y++)
615 {
616 MagickBooleanType
617 sync;
618
619 register const Quantum
620 *p;
621
622 register Quantum
623 *q;
624
625 register ssize_t
626 x;
627
628 if (status == MagickFalse)
629 continue;
630 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
631 1,exception);
632 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
633 composite_image->columns,1,exception);
634 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
635 {
636 status=MagickFalse;
637 continue;
638 }
639 for (x=0; x < (ssize_t) composite_image->columns; x++)
640 {
cristybdecccc2011-12-24 22:52:16 +0000641 register ssize_t
642 i;
643
cristy665e18f2012-05-17 12:39:54 +0000644 if (GetPixelMask(composite_image,p) != 0)
cristy10a6c612012-01-29 21:41:05 +0000645 {
646 p+=GetPixelChannels(composite_image);
647 q+=GetPixelChannels(image);
648 continue;
649 }
cristybdecccc2011-12-24 22:52:16 +0000650 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
651 {
652 PixelChannel
653 channel;
654
655 PixelTrait
656 composite_traits,
657 traits;
658
659 channel=GetPixelChannelMapChannel(composite_image,i);
660 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
661 traits=GetPixelChannelMapTraits(image,channel);
662 if ((traits == UndefinedPixelTrait) ||
663 (composite_traits == UndefinedPixelTrait))
664 continue;
665 SetPixelChannel(image,channel,p[i],q);
666 }
cristyed231572011-07-14 02:18:59 +0000667 p+=GetPixelChannels(composite_image);
668 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000669 }
670 sync=SyncCacheViewAuthenticPixels(image_view,exception);
671 if (sync == MagickFalse)
672 status=MagickFalse;
673 if (image->progress_monitor != (MagickProgressMonitor) NULL)
674 {
675 MagickBooleanType
676 proceed;
677
678#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000679 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000680#endif
681 proceed=SetImageProgress(image,CompositeImageTag,
682 (MagickOffsetType) y,image->rows);
683 if (proceed == MagickFalse)
684 status=MagickFalse;
685 }
686 }
687 composite_view=DestroyCacheView(composite_view);
688 image_view=DestroyCacheView(image_view);
689 return(status);
690 }
cristye4a40472011-12-22 02:56:19 +0000691 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000692 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000693 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000694 {
695 /*
696 Modify destination outside the overlaid region and require an alpha
697 channel to exist, to add transparency.
698 */
699 if (image->matte == MagickFalse)
cristy42c41de2012-05-05 18:36:31 +0000700 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000701 break;
702 }
703 case BlurCompositeOp:
704 {
705 CacheView
706 *composite_view,
707 *destination_view;
708
cristyfeb3e962012-03-29 17:25:55 +0000709 const char
710 *value;
711
cristy4c08aed2011-07-01 19:47:50 +0000712 PixelInfo
713 pixel;
714
715 MagickRealType
716 angle_range,
717 angle_start,
718 height,
719 width;
720
721 ResampleFilter
722 *resample_filter;
723
724 SegmentInfo
725 blur;
726
727 /*
anthony9cb63cc2012-04-25 06:10:49 +0000728 Blur Image by resampling.
729
cristy4c08aed2011-07-01 19:47:50 +0000730 Blur Image dictated by an overlay gradient map: X = red_channel;
731 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
732 */
733 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000734 exception);
cristy4c08aed2011-07-01 19:47:50 +0000735 if (destination_image == (Image *) NULL)
736 return(MagickFalse);
737 /*
anthony9cb63cc2012-04-25 06:10:49 +0000738 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000739 */
740 SetGeometryInfo(&geometry_info);
741 flags=NoValue;
742 value=GetImageArtifact(composite_image,"compose:args");
743 if (value != (char *) NULL)
744 flags=ParseGeometry(value,&geometry_info);
anthonyd2923912012-04-23 13:06:53 +0000745 if ((flags & WidthValue) == 0 ) {
746 (void) ThrowMagickException(exception,GetMagickModule(),
747 OptionWarning,"InvalidSetting","'%s' '%s'",
anthony9cb63cc2012-04-25 06:10:49 +0000748 "compose:args",value);
cristy4c08aed2011-07-01 19:47:50 +0000749 destination_image=DestroyImage(destination_image);
750 return(MagickFalse);
751 }
anthony9cb63cc2012-04-25 06:10:49 +0000752 /*
753 Users input sigma now needs to be converted to the EWA ellipse size.
754 The filter defaults to a sigma of 0.5 so to make this match the
755 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000756 */
757 width=height=geometry_info.rho*2.0;
758 if ((flags & HeightValue) != 0 )
759 height=geometry_info.sigma*2.0;
760
anthony9cb63cc2012-04-25 06:10:49 +0000761 /* default the unrotated ellipse width and height axis vectors */
anthonyd2923912012-04-23 13:06:53 +0000762 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000763 blur.x2=0.0;
764 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000765 blur.y2=height;
766 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000767 if ((flags & XValue) != 0 )
768 {
769 MagickRealType
770 angle;
771
772 angle=DegreesToRadians(geometry_info.xi);
773 blur.x1=width*cos(angle);
774 blur.x2=width*sin(angle);
775 blur.y1=(-height*sin(angle));
776 blur.y2=height*cos(angle);
777 }
anthonyd2923912012-04-23 13:06:53 +0000778 /* Otherwise lets set a angle range and calculate in the loop */
779 angle_start=0.0;
780 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000781 if ((flags & YValue) != 0 )
782 {
783 angle_start=DegreesToRadians(geometry_info.xi);
784 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
785 }
786 /*
anthony9cb63cc2012-04-25 06:10:49 +0000787 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000788
anthony9cb63cc2012-04-25 06:10:49 +0000789 As the minimum ellipse radius of support*1.0 the EWA algorithm
790 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
791 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000792
anthony9cb63cc2012-04-25 06:10:49 +0000793 The solution (as well as the problem of preventing any user
794 expert filter settings, is to set our own user settings, then
795 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000796 */
cristy8a11cb12011-10-19 23:53:34 +0000797 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000798 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000799
800 /* do the variable blurring of each pixel in image */
801 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000802 composite_view=AcquireVirtualCacheView(composite_image,exception);
803 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000804 for (y=0; y < (ssize_t) composite_image->rows; y++)
805 {
806 MagickBooleanType
807 sync;
808
809 register const Quantum
810 *restrict p;
811
812 register Quantum
813 *restrict q;
814
815 register ssize_t
816 x;
817
818 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
819 continue;
820 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
821 1,exception);
822 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000823 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000824 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
825 break;
826 for (x=0; x < (ssize_t) composite_image->columns; x++)
827 {
828 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
829 {
cristyed231572011-07-14 02:18:59 +0000830 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000831 continue;
832 }
833 if (fabs(angle_range) > MagickEpsilon)
834 {
835 MagickRealType
836 angle;
837
838 angle=angle_start+angle_range*QuantumScale*
839 GetPixelBlue(composite_image,p);
840 blur.x1=width*cos(angle);
841 blur.x2=width*sin(angle);
842 blur.y1=(-height*sin(angle));
843 blur.y2=height*cos(angle);
844 }
anthonyd2923912012-04-23 13:06:53 +0000845#if 0
846 if ( x == 10 && y == 60 ) {
847 fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
848 blur.x1, blur.x2, blur.y1, blur.y2);
849 fprintf(stderr, "scaled by=%lf,%lf\n",
850 QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
851#endif
852 ScaleResampleFilter(resample_filter,
853 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
854 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
855 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
856 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000857 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000858 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000859 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000860 p+=GetPixelChannels(composite_image);
861 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000862 }
863 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
864 if (sync == MagickFalse)
865 break;
866 }
867 resample_filter=DestroyResampleFilter(resample_filter);
868 composite_view=DestroyCacheView(composite_view);
869 destination_view=DestroyCacheView(destination_view);
870 composite_image=destination_image;
871 break;
872 }
873 case DisplaceCompositeOp:
874 case DistortCompositeOp:
875 {
876 CacheView
877 *composite_view,
878 *destination_view,
879 *image_view;
880
cristyfeb3e962012-03-29 17:25:55 +0000881 const char
882 *value;
883
cristy4c08aed2011-07-01 19:47:50 +0000884 PixelInfo
885 pixel;
886
887 MagickRealType
888 horizontal_scale,
889 vertical_scale;
890
891 PointInfo
892 center,
893 offset;
894
895 /*
896 Displace/Distort based on overlay gradient map:
897 X = red_channel; Y = green_channel;
898 compose:args = x_scale[,y_scale[,center.x,center.y]]
899 */
900 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000901 exception);
cristy4c08aed2011-07-01 19:47:50 +0000902 if (destination_image == (Image *) NULL)
903 return(MagickFalse);
904 SetGeometryInfo(&geometry_info);
905 flags=NoValue;
906 value=GetImageArtifact(composite_image,"compose:args");
907 if (value != (char *) NULL)
908 flags=ParseGeometry(value,&geometry_info);
909 if ((flags & (WidthValue|HeightValue)) == 0 )
910 {
911 if ((flags & AspectValue) == 0)
912 {
913 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
914 2.0;
915 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
916 }
917 else
918 {
919 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
920 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
921 }
922 }
923 else
924 {
925 horizontal_scale=geometry_info.rho;
926 vertical_scale=geometry_info.sigma;
927 if ((flags & PercentValue) != 0)
928 {
929 if ((flags & AspectValue) == 0)
930 {
931 horizontal_scale*=(composite_image->columns-1.0)/200.0;
932 vertical_scale*=(composite_image->rows-1.0)/200.0;
933 }
934 else
935 {
936 horizontal_scale*=(image->columns-1.0)/200.0;
937 vertical_scale*=(image->rows-1.0)/200.0;
938 }
939 }
940 if ((flags & HeightValue) == 0)
941 vertical_scale=horizontal_scale;
942 }
943 /*
944 Determine fixed center point for absolute distortion map
945 Absolute distort ==
946 Displace offset relative to a fixed absolute point
947 Select that point according to +X+Y user inputs.
948 default = center of overlay image
949 arg flag '!' = locations/percentage relative to background image
950 */
951 center.x=(MagickRealType) x_offset;
952 center.y=(MagickRealType) y_offset;
953 if (compose == DistortCompositeOp)
954 {
955 if ((flags & XValue) == 0)
956 if ((flags & AspectValue) == 0)
957 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
958 2.0;
959 else
960 center.x=((MagickRealType) image->columns-1)/2.0;
961 else
962 if ((flags & AspectValue) == 0)
963 center.x=(MagickRealType) x_offset+geometry_info.xi;
964 else
965 center.x=geometry_info.xi;
966 if ((flags & YValue) == 0)
967 if ((flags & AspectValue) == 0)
968 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
969 else
970 center.y=((MagickRealType) image->rows-1)/2.0;
971 else
972 if ((flags & AspectValue) == 0)
973 center.y=(MagickRealType) y_offset+geometry_info.psi;
974 else
975 center.y=geometry_info.psi;
976 }
977 /*
978 Shift the pixel offset point as defined by the provided,
979 displacement/distortion map. -- Like a lens...
980 */
cristye10859a2011-12-18 22:28:59 +0000981 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000982 image_view=AcquireVirtualCacheView(image,exception);
983 composite_view=AcquireVirtualCacheView(composite_image,exception);
984 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000985 for (y=0; y < (ssize_t) composite_image->rows; y++)
986 {
987 MagickBooleanType
988 sync;
989
990 register const Quantum
991 *restrict p;
992
993 register Quantum
994 *restrict q;
995
996 register ssize_t
997 x;
998
999 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1000 continue;
1001 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1002 1,exception);
1003 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001004 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001005 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1006 break;
1007 for (x=0; x < (ssize_t) composite_image->columns; x++)
1008 {
1009 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1010 {
cristyed231572011-07-14 02:18:59 +00001011 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001012 continue;
1013 }
1014 /*
1015 Displace the offset.
1016 */
1017 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1018 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1019 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1020 x : 0);
1021 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1022 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1023 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1024 y : 0);
1025 (void) InterpolatePixelInfo(image,image_view,
1026 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1027 &pixel,exception);
1028 /*
1029 Mask with the 'invalid pixel mask' in alpha channel.
1030 */
1031 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001032 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001033 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001034 p+=GetPixelChannels(composite_image);
1035 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001036 }
1037 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1038 if (sync == MagickFalse)
1039 break;
1040 }
1041 destination_view=DestroyCacheView(destination_view);
1042 composite_view=DestroyCacheView(composite_view);
1043 image_view=DestroyCacheView(image_view);
1044 composite_image=destination_image;
1045 break;
1046 }
1047 case DissolveCompositeOp:
1048 {
cristyfeb3e962012-03-29 17:25:55 +00001049 const char
1050 *value;
1051
cristy4c08aed2011-07-01 19:47:50 +00001052 /*
1053 Geometry arguments to dissolve factors.
1054 */
1055 value=GetImageArtifact(composite_image,"compose:args");
1056 if (value != (char *) NULL)
1057 {
1058 flags=ParseGeometry(value,&geometry_info);
1059 source_dissolve=geometry_info.rho/100.0;
1060 destination_dissolve=1.0;
1061 if ((source_dissolve-MagickEpsilon) < 0.0)
1062 source_dissolve=0.0;
1063 if ((source_dissolve+MagickEpsilon) > 1.0)
1064 {
1065 destination_dissolve=2.0-source_dissolve;
1066 source_dissolve=1.0;
1067 }
1068 if ((flags & SigmaValue) != 0)
1069 destination_dissolve=geometry_info.sigma/100.0;
1070 if ((destination_dissolve-MagickEpsilon) < 0.0)
1071 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001072 /* posible speed up? -- from IMv6 update
1073 clip_to_self=MagickFalse;
1074 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1075 {
1076 destination_dissolve=1.0;
1077 clip_to_self=MagickTrue;
1078 }
1079 */
cristy4c08aed2011-07-01 19:47:50 +00001080 }
1081 break;
1082 }
1083 case BlendCompositeOp:
1084 {
cristyfeb3e962012-03-29 17:25:55 +00001085 const char
1086 *value;
1087
cristy4c08aed2011-07-01 19:47:50 +00001088 value=GetImageArtifact(composite_image,"compose:args");
1089 if (value != (char *) NULL)
1090 {
1091 flags=ParseGeometry(value,&geometry_info);
1092 source_dissolve=geometry_info.rho/100.0;
1093 destination_dissolve=1.0-source_dissolve;
1094 if ((flags & SigmaValue) != 0)
1095 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001096 }
1097 break;
1098 }
1099 case MathematicsCompositeOp:
1100 {
cristyfeb3e962012-03-29 17:25:55 +00001101 const char
1102 *value;
1103
cristy4c08aed2011-07-01 19:47:50 +00001104 /*
1105 Just collect the values from "compose:args", setting.
1106 Unused values are set to zero automagically.
1107
1108 Arguments are normally a comma separated list, so this probably should
1109 be changed to some 'general comma list' parser, (with a minimum
1110 number of values)
1111 */
1112 SetGeometryInfo(&geometry_info);
1113 value=GetImageArtifact(composite_image,"compose:args");
1114 if (value != (char *) NULL)
1115 (void) ParseGeometry(value,&geometry_info);
1116 break;
1117 }
1118 case ModulateCompositeOp:
1119 {
cristyfeb3e962012-03-29 17:25:55 +00001120 const char
1121 *value;
1122
cristy4c08aed2011-07-01 19:47:50 +00001123 /*
1124 Determine the brightness and saturation scale.
1125 */
1126 value=GetImageArtifact(composite_image,"compose:args");
1127 if (value != (char *) NULL)
1128 {
1129 flags=ParseGeometry(value,&geometry_info);
1130 percent_brightness=geometry_info.rho;
1131 if ((flags & SigmaValue) != 0)
1132 percent_saturation=geometry_info.sigma;
1133 }
1134 break;
1135 }
1136 case ThresholdCompositeOp:
1137 {
cristyfeb3e962012-03-29 17:25:55 +00001138 const char
1139 *value;
1140
cristy4c08aed2011-07-01 19:47:50 +00001141 /*
1142 Determine the amount and threshold.
1143 */
1144 value=GetImageArtifact(composite_image,"compose:args");
1145 if (value != (char *) NULL)
1146 {
1147 flags=ParseGeometry(value,&geometry_info);
1148 amount=geometry_info.rho;
1149 threshold=geometry_info.sigma;
1150 if ((flags & SigmaValue) == 0)
1151 threshold=0.05f;
1152 }
1153 threshold*=QuantumRange;
1154 break;
1155 }
1156 default:
1157 break;
1158 }
cristy4c08aed2011-07-01 19:47:50 +00001159 /*
1160 Composite image.
1161 */
1162 status=MagickTrue;
1163 progress=0;
1164 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristydb070952012-04-20 14:33:00 +00001165 composite_view=AcquireVirtualCacheView(composite_image,exception);
1166 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001167#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001168 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +00001169 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001170#endif
1171 for (y=0; y < (ssize_t) image->rows; y++)
1172 {
1173 const Quantum
1174 *pixels;
1175
1176 double
cristye4a40472011-12-22 02:56:19 +00001177 blue,
cristy4c08aed2011-07-01 19:47:50 +00001178 brightness,
cristye4a40472011-12-22 02:56:19 +00001179 green,
cristy4c08aed2011-07-01 19:47:50 +00001180 hue,
cristye4a40472011-12-22 02:56:19 +00001181 red,
cristy4c08aed2011-07-01 19:47:50 +00001182 saturation;
1183
cristyddeeea22012-04-12 01:33:09 +00001184 PixelInfo
1185 destination_pixel,
1186 source_pixel;
1187
cristy4c08aed2011-07-01 19:47:50 +00001188 register const Quantum
1189 *restrict p;
1190
1191 register Quantum
1192 *restrict q;
1193
1194 register ssize_t
1195 x;
1196
1197 if (status == MagickFalse)
1198 continue;
cristyfeb3e962012-03-29 17:25:55 +00001199 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001200 {
1201 if (y < y_offset)
1202 continue;
1203 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1204 continue;
1205 }
1206 /*
1207 If pixels is NULL, y is outside overlay region.
1208 */
1209 pixels=(Quantum *) NULL;
1210 p=(Quantum *) NULL;
1211 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1212 {
1213 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1214 composite_image->columns,1,exception);
1215 if (p == (const Quantum *) NULL)
1216 {
1217 status=MagickFalse;
1218 continue;
1219 }
1220 pixels=p;
1221 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001222 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001223 }
1224 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001225 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001226 {
1227 status=MagickFalse;
1228 continue;
1229 }
cristy4c08aed2011-07-01 19:47:50 +00001230 hue=0.0;
1231 saturation=0.0;
1232 brightness=0.0;
cristyddeeea22012-04-12 01:33:09 +00001233 GetPixelInfo(image,&destination_pixel);
1234 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001235 for (x=0; x < (ssize_t) image->columns; x++)
1236 {
cristye4a40472011-12-22 02:56:19 +00001237 MagickRealType
1238 alpha,
1239 Da,
1240 Dc,
1241 Dca,
1242 gamma,
1243 Sa,
1244 Sc,
1245 Sca;
1246
1247 register ssize_t
1248 i;
1249
cristy564a5692012-01-20 23:56:26 +00001250 size_t
1251 channels;
1252
cristyfeb3e962012-03-29 17:25:55 +00001253 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001254 {
1255 if (x < x_offset)
1256 {
cristyed231572011-07-14 02:18:59 +00001257 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001258 continue;
1259 }
1260 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1261 break;
1262 }
cristye4a40472011-12-22 02:56:19 +00001263 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1264 ((x-x_offset) >= (ssize_t) composite_image->columns))
1265 {
1266 Quantum
1267 source[MaxPixelChannels];
1268
1269 /*
1270 Virtual composite:
1271 Sc: source color.
1272 Dc: destination color.
1273 */
1274 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1275 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001276 if (GetPixelMask(image,q) != 0)
1277 {
1278 q+=GetPixelChannels(image);
1279 continue;
1280 }
cristye4a40472011-12-22 02:56:19 +00001281 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1282 {
1283 MagickRealType
1284 pixel;
1285
1286 PixelChannel
1287 channel;
1288
1289 PixelTrait
1290 composite_traits,
1291 traits;
1292
1293 channel=GetPixelChannelMapChannel(image,i);
1294 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +00001295 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001296 if ((traits == UndefinedPixelTrait) ||
1297 (composite_traits == UndefinedPixelTrait))
1298 continue;
1299 switch (compose)
1300 {
cristyc8d63672012-01-11 13:03:13 +00001301 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001302 case ChangeMaskCompositeOp:
1303 case CopyAlphaCompositeOp:
1304 case DstAtopCompositeOp:
1305 case DstInCompositeOp:
1306 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001307 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001308 case OutCompositeOp:
1309 case SrcInCompositeOp:
1310 case SrcOutCompositeOp:
1311 {
1312 pixel=(MagickRealType) q[i];
1313 if (channel == AlphaPixelChannel)
1314 pixel=(MagickRealType) TransparentAlpha;
1315 break;
1316 }
1317 case ClearCompositeOp:
1318 case CopyCompositeOp:
1319 case ReplaceCompositeOp:
1320 case SrcCompositeOp:
1321 {
1322 if (channel == AlphaPixelChannel)
1323 {
1324 pixel=(MagickRealType) TransparentAlpha;
1325 break;
1326 }
1327 pixel=0.0;
1328 break;
1329 }
cristy99abff32011-12-24 20:45:16 +00001330 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001331 case DissolveCompositeOp:
1332 {
1333 if (channel == AlphaPixelChannel)
1334 {
1335 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1336 source);
1337 break;
1338 }
1339 pixel=(MagickRealType) source[channel];
1340 break;
1341 }
1342 default:
1343 {
1344 pixel=(MagickRealType) source[channel];
1345 break;
1346 }
1347 }
1348 q[i]=ClampToQuantum(pixel);
1349 }
1350 q+=GetPixelChannels(image);
1351 continue;
1352 }
1353 /*
1354 Authentic composite:
1355 Sa: normalized source alpha.
1356 Da: normalized destination alpha.
1357 */
1358 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1359 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001360 switch (compose)
1361 {
cristye4a40472011-12-22 02:56:19 +00001362 case BumpmapCompositeOp:
1363 {
1364 alpha=GetPixelIntensity(composite_image,p)*Sa;
1365 break;
1366 }
cristycdc168f2011-12-21 15:24:39 +00001367 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001368 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001369 case DifferenceCompositeOp:
1370 case DivideDstCompositeOp:
1371 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001372 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001373 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001374 case LinearBurnCompositeOp:
1375 case LinearDodgeCompositeOp:
1376 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001377 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001378 case MinusDstCompositeOp:
1379 case MinusSrcCompositeOp:
1380 case ModulusAddCompositeOp:
1381 case ModulusSubtractCompositeOp:
1382 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001383 case OverlayCompositeOp:
1384 case PegtopLightCompositeOp:
1385 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001386 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001387 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001388 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001389 {
1390 alpha=RoundToUnity(Sa+Da-Sa*Da);
1391 break;
1392 }
1393 case DarkenCompositeOp:
1394 case DstAtopCompositeOp:
1395 case DstInCompositeOp:
1396 case InCompositeOp:
1397 case LightenCompositeOp:
1398 case SrcInCompositeOp:
1399 {
1400 alpha=Sa*Da;
1401 break;
1402 }
1403 case DissolveCompositeOp:
1404 {
1405 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1406 Sa+destination_dissolve*Da;
1407 break;
1408 }
1409 case DstOverCompositeOp:
1410 {
1411 alpha=Da*(-Sa)+Da+Sa;
1412 break;
1413 }
1414 case DstOutCompositeOp:
1415 {
1416 alpha=Da*(1.0-Sa);
1417 break;
1418 }
1419 case OutCompositeOp:
1420 case SrcOutCompositeOp:
1421 {
1422 alpha=Sa*(1.0-Da);
1423 break;
1424 }
1425 case OverCompositeOp:
1426 case SrcOverCompositeOp:
1427 {
1428 alpha=Sa*(-Da)+Sa+Da;
1429 break;
1430 }
cristy99abff32011-12-24 20:45:16 +00001431 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001432 case PlusCompositeOp:
1433 {
1434 alpha=RoundToUnity(Sa+Da);
1435 break;
1436 }
cristy4c08aed2011-07-01 19:47:50 +00001437 case XorCompositeOp:
1438 {
cristye4a40472011-12-22 02:56:19 +00001439 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001440 break;
1441 }
1442 default:
cristye4a40472011-12-22 02:56:19 +00001443 {
1444 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001445 break;
cristye4a40472011-12-22 02:56:19 +00001446 }
cristy4c08aed2011-07-01 19:47:50 +00001447 }
cristy10a6c612012-01-29 21:41:05 +00001448 if (GetPixelMask(image,p) != 0)
1449 {
1450 p+=GetPixelChannels(composite_image);
1451 q+=GetPixelChannels(image);
1452 continue;
1453 }
cristy9d3d2792012-04-14 15:15:19 +00001454 switch (compose)
1455 {
1456 case ColorizeCompositeOp:
1457 case HueCompositeOp:
1458 case LuminizeCompositeOp:
1459 case ModulateCompositeOp:
1460 case SaturateCompositeOp:
1461 {
1462 GetPixelInfoPixel(composite_image,p,&source_pixel);
1463 GetPixelInfoPixel(image,q,&destination_pixel);
1464 break;
1465 }
1466 default:
1467 break;
1468 }
cristye4a40472011-12-22 02:56:19 +00001469 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1470 {
cristy564a5692012-01-20 23:56:26 +00001471 double
1472 sans;
1473
cristye10859a2011-12-18 22:28:59 +00001474 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001475 pixel;
cristye10859a2011-12-18 22:28:59 +00001476
cristye4a40472011-12-22 02:56:19 +00001477 PixelChannel
1478 channel;
cristye10859a2011-12-18 22:28:59 +00001479
cristye4a40472011-12-22 02:56:19 +00001480 PixelTrait
1481 composite_traits,
1482 traits;
1483
1484 channel=GetPixelChannelMapChannel(image,i);
1485 traits=GetPixelChannelMapTraits(image,channel);
1486 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001487 if (traits == UndefinedPixelTrait)
1488 continue;
cristya7b07912012-01-11 20:01:32 +00001489 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001490 (composite_traits == UndefinedPixelTrait))
1491 continue;
1492 /*
1493 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001494 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001495 */
1496 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
cristye4a40472011-12-22 02:56:19 +00001497 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001498 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001499 {
cristye4a40472011-12-22 02:56:19 +00001500 if (channel != AlphaPixelChannel)
1501 {
1502 /*
1503 Copy channel.
1504 */
1505 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001506 continue;
cristye10859a2011-12-18 22:28:59 +00001507 }
cristye4a40472011-12-22 02:56:19 +00001508 /*
1509 Set alpha channel.
1510 */
cristye10859a2011-12-18 22:28:59 +00001511 switch (compose)
1512 {
cristyc8d63672012-01-11 13:03:13 +00001513 case AlphaCompositeOp:
1514 {
cristya7b07912012-01-11 20:01:32 +00001515 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001516 break;
1517 }
cristye4a40472011-12-22 02:56:19 +00001518 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001519 case CopyBlackCompositeOp:
1520 case CopyBlueCompositeOp:
1521 case CopyCyanCompositeOp:
1522 case CopyGreenCompositeOp:
1523 case CopyMagentaCompositeOp:
1524 case CopyRedCompositeOp:
1525 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001526 case SrcAtopCompositeOp:
1527 case DstCompositeOp:
1528 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001529 {
cristye4a40472011-12-22 02:56:19 +00001530 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001531 break;
1532 }
cristye10859a2011-12-18 22:28:59 +00001533 case ChangeMaskCompositeOp:
1534 {
cristye4a40472011-12-22 02:56:19 +00001535 MagickBooleanType
1536 equivalent;
1537
cristy99abff32011-12-24 20:45:16 +00001538 if (Da > ((MagickRealType) QuantumRange/2.0))
1539 {
1540 pixel=(MagickRealType) TransparentAlpha;
1541 break;
1542 }
cristye4a40472011-12-22 02:56:19 +00001543 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001544 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001545 {
1546 pixel=(MagickRealType) TransparentAlpha;
1547 break;
1548 }
1549 pixel=(MagickRealType) OpaqueAlpha;
1550 break;
1551 }
cristy99abff32011-12-24 20:45:16 +00001552 case ClearCompositeOp:
1553 {
1554 pixel=(MagickRealType) TransparentAlpha;
1555 break;
1556 }
1557 case ColorizeCompositeOp:
1558 case HueCompositeOp:
1559 case LuminizeCompositeOp:
1560 case SaturateCompositeOp:
1561 {
1562 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1563 {
1564 pixel=QuantumRange*Da;
1565 break;
1566 }
1567 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1568 {
1569 pixel=QuantumRange*Sa;
1570 break;
1571 }
1572 if (Sa < Da)
1573 {
1574 pixel=QuantumRange*Da;
1575 break;
1576 }
1577 pixel=QuantumRange*Sa;
1578 break;
1579 }
cristy99abff32011-12-24 20:45:16 +00001580 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001581 {
1582 pixel=QuantumRange*Sa;
1583 if (composite_image->matte == MagickFalse)
1584 pixel=GetPixelIntensity(composite_image,p);
1585 break;
1586 }
1587 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001588 case DisplaceCompositeOp:
1589 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001590 case DstAtopCompositeOp:
1591 case ReplaceCompositeOp:
1592 case SrcCompositeOp:
1593 {
1594 pixel=QuantumRange*Sa;
1595 break;
1596 }
1597 case DarkenIntensityCompositeOp:
1598 {
cristy99abff32011-12-24 20:45:16 +00001599 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1600 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001601 break;
1602 }
cristy98621462011-12-31 22:31:11 +00001603 case IntensityCompositeOp:
1604 {
cristy20d5f622012-01-11 13:04:26 +00001605 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001606 break;
1607 }
cristye4a40472011-12-22 02:56:19 +00001608 case LightenIntensityCompositeOp:
1609 {
1610 pixel=Sa*GetPixelIntensity(composite_image,p) >
1611 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001612 break;
1613 }
cristy99abff32011-12-24 20:45:16 +00001614 case ModulateCompositeOp:
1615 {
1616 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1617 {
1618 pixel=QuantumRange*Da;
1619 break;
1620 }
1621 pixel=QuantumRange*Da;
1622 break;
1623 }
cristye10859a2011-12-18 22:28:59 +00001624 default:
1625 {
cristye4a40472011-12-22 02:56:19 +00001626 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001627 break;
1628 }
1629 }
cristye4a40472011-12-22 02:56:19 +00001630 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001631 continue;
1632 }
1633 /*
cristy99abff32011-12-24 20:45:16 +00001634 Porter-Duff compositions:
1635 Sca: source normalized color multiplied by alpha.
1636 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001637 */
cristy99abff32011-12-24 20:45:16 +00001638 Sca=QuantumScale*Sa*Sc;
1639 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001640 switch (compose)
1641 {
cristye10859a2011-12-18 22:28:59 +00001642 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001643 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001644 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001645 {
cristy99abff32011-12-24 20:45:16 +00001646 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001647 break;
1648 }
1649 default:
1650 break;
1651 }
cristyc58380a2012-06-03 15:12:30 +00001652 gamma=MagickEpsilonReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001653 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001654 switch (compose)
1655 {
cristya7b07912012-01-11 20:01:32 +00001656 case AlphaCompositeOp:
1657 {
1658 pixel=QuantumRange*Sa;
1659 break;
1660 }
cristye4a40472011-12-22 02:56:19 +00001661 case AtopCompositeOp:
1662 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001663 {
cristye4a40472011-12-22 02:56:19 +00001664 pixel=Sc*Sa+Dc*(1.0-Sa);
1665 break;
cristye10859a2011-12-18 22:28:59 +00001666 }
cristye4a40472011-12-22 02:56:19 +00001667 case BlendCompositeOp:
1668 {
1669 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1670 break;
1671 }
1672 case BlurCompositeOp:
1673 case DisplaceCompositeOp:
1674 case DistortCompositeOp:
1675 case CopyCompositeOp:
1676 case ReplaceCompositeOp:
1677 case SrcCompositeOp:
1678 {
1679 pixel=Sc;
1680 break;
1681 }
1682 case BumpmapCompositeOp:
1683 {
cristy99abff32011-12-24 20:45:16 +00001684 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001685 {
1686 pixel=Dc;
1687 break;
1688 }
1689 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1690 break;
1691 }
cristy99abff32011-12-24 20:45:16 +00001692 case ChangeMaskCompositeOp:
1693 {
1694 pixel=Dc;
1695 break;
1696 }
1697 case ClearCompositeOp:
1698 {
1699 pixel=0.0;
1700 break;
1701 }
cristye4a40472011-12-22 02:56:19 +00001702 case ColorBurnCompositeOp:
1703 {
1704 /*
1705 Refer to the March 2009 SVG specification.
1706 */
1707 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1708 {
cristy99abff32011-12-24 20:45:16 +00001709 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001710 break;
1711 }
1712 if (Sca < MagickEpsilon)
1713 {
cristy99abff32011-12-24 20:45:16 +00001714 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001715 break;
1716 }
cristy99abff32011-12-24 20:45:16 +00001717 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1718 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001719 break;
1720 }
1721 case ColorDodgeCompositeOp:
1722 {
1723 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1724 {
cristy99abff32011-12-24 20:45:16 +00001725 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001726 break;
1727 }
1728 if (fabs(Sca-Sa) < MagickEpsilon)
1729 {
cristy99abff32011-12-24 20:45:16 +00001730 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001731 break;
1732 }
cristy99abff32011-12-24 20:45:16 +00001733 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001734 (1.0-Sa));
1735 break;
1736 }
1737 case ColorizeCompositeOp:
1738 {
cristy99abff32011-12-24 20:45:16 +00001739 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001740 {
1741 pixel=Dc;
1742 break;
1743 }
cristy99abff32011-12-24 20:45:16 +00001744 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001745 {
1746 pixel=Sc;
1747 break;
1748 }
cristyddeeea22012-04-12 01:33:09 +00001749 CompositeHSB(destination_pixel.red,destination_pixel.green,
1750 destination_pixel.blue,&sans,&sans,&brightness);
1751 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001752 &hue,&saturation,&sans);
1753 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1754 switch (channel)
1755 {
1756 case RedPixelChannel: pixel=red; break;
1757 case GreenPixelChannel: pixel=green; break;
1758 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001759 default: pixel=Dc; break;
1760 }
1761 break;
1762 }
cristye4a40472011-12-22 02:56:19 +00001763 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001764 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001765 {
cristy24d5d722012-05-17 12:27:27 +00001766 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001767 break;
1768 }
1769 case CopyBlackCompositeOp:
1770 {
cristyd197cbb2012-01-13 02:14:12 +00001771 if (channel == BlackPixelChannel)
1772 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001773 break;
1774 }
1775 case CopyBlueCompositeOp:
1776 case CopyYellowCompositeOp:
1777 {
cristyd197cbb2012-01-13 02:14:12 +00001778 if (channel == BluePixelChannel)
1779 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001780 break;
1781 }
1782 case CopyGreenCompositeOp:
1783 case CopyMagentaCompositeOp:
1784 {
cristyd197cbb2012-01-13 02:14:12 +00001785 if (channel == GreenPixelChannel)
1786 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001787 break;
1788 }
1789 case CopyRedCompositeOp:
1790 case CopyCyanCompositeOp:
1791 {
cristyd197cbb2012-01-13 02:14:12 +00001792 if (channel == RedPixelChannel)
1793 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001794 break;
1795 }
cristy99abff32011-12-24 20:45:16 +00001796 case DarkenCompositeOp:
1797 {
1798 /*
1799 Darken is equivalent to a 'Minimum' method
1800 OR a greyscale version of a binary 'Or'
1801 OR the 'Intersection' of pixel sets.
1802 */
1803 if (Sc < Dc)
1804 {
1805 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1806 break;
1807 }
1808 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1809 break;
1810 }
cristye4a40472011-12-22 02:56:19 +00001811 case DarkenIntensityCompositeOp:
1812 {
cristy99abff32011-12-24 20:45:16 +00001813 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1814 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001815 break;
1816 }
1817 case DifferenceCompositeOp:
1818 {
1819 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1820 break;
1821 }
1822 case DissolveCompositeOp:
1823 {
1824 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1825 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1826 break;
1827 }
1828 case DivideDstCompositeOp:
1829 {
1830 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1831 {
cristy99abff32011-12-24 20:45:16 +00001832 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001833 break;
1834 }
1835 if (fabs(Dca) < MagickEpsilon)
1836 {
cristy99abff32011-12-24 20:45:16 +00001837 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001838 break;
1839 }
cristy99abff32011-12-24 20:45:16 +00001840 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001841 break;
1842 }
1843 case DivideSrcCompositeOp:
1844 {
1845 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1846 {
cristy99abff32011-12-24 20:45:16 +00001847 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001848 break;
1849 }
1850 if (fabs(Sca) < MagickEpsilon)
1851 {
cristy99abff32011-12-24 20:45:16 +00001852 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001853 break;
1854 }
cristy99abff32011-12-24 20:45:16 +00001855 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001856 break;
1857 }
1858 case DstAtopCompositeOp:
1859 {
1860 pixel=Dc*Da+Sc*(1.0-Da);
1861 break;
1862 }
1863 case DstCompositeOp:
1864 case NoCompositeOp:
1865 {
1866 pixel=Dc;
1867 break;
1868 }
1869 case DstInCompositeOp:
1870 {
1871 pixel=gamma*(Sa*Dc*Sa);
1872 break;
1873 }
1874 case DstOutCompositeOp:
1875 {
1876 pixel=gamma*(Da*Dc*(1.0-Sa));
1877 break;
1878 }
1879 case DstOverCompositeOp:
1880 {
1881 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1882 break;
1883 }
1884 case ExclusionCompositeOp:
1885 {
cristy99abff32011-12-24 20:45:16 +00001886 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1887 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001888 break;
1889 }
1890 case HardLightCompositeOp:
1891 {
1892 if ((2.0*Sca) < Sa)
1893 {
cristy99abff32011-12-24 20:45:16 +00001894 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001895 (1.0-Sa));
1896 break;
1897 }
cristy99abff32011-12-24 20:45:16 +00001898 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001899 Dca*(1.0-Sa));
1900 break;
1901 }
1902 case HueCompositeOp:
1903 {
cristy99abff32011-12-24 20:45:16 +00001904 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001905 {
1906 pixel=Dc;
1907 break;
1908 }
cristy99abff32011-12-24 20:45:16 +00001909 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001910 {
1911 pixel=Sc;
1912 break;
1913 }
cristyddeeea22012-04-12 01:33:09 +00001914 CompositeHSB(destination_pixel.red,destination_pixel.green,
1915 destination_pixel.blue,&hue,&saturation,&brightness);
1916 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001917 &hue,&sans,&sans);
1918 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1919 switch (channel)
1920 {
1921 case RedPixelChannel: pixel=red; break;
1922 case GreenPixelChannel: pixel=green; break;
1923 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001924 default: pixel=Dc; break;
1925 }
1926 break;
1927 }
1928 case InCompositeOp:
1929 case SrcInCompositeOp:
1930 {
1931 pixel=gamma*(Da*Sc*Da);
1932 break;
1933 }
cristy99abff32011-12-24 20:45:16 +00001934 case LinearBurnCompositeOp:
1935 {
1936 /*
1937 LinearBurn: as defined by Abode Photoshop, according to
1938 http://www.simplefilter.de/en/basics/mixmods.html is:
1939
1940 f(Sc,Dc) = Sc + Dc - 1
1941 */
1942 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1943 break;
1944 }
1945 case LinearDodgeCompositeOp:
1946 {
1947 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1948 break;
1949 }
1950 case LinearLightCompositeOp:
1951 {
1952 /*
1953 LinearLight: as defined by Abode Photoshop, according to
1954 http://www.simplefilter.de/en/basics/mixmods.html is:
1955
1956 f(Sc,Dc) = Dc + 2*Sc - 1
1957 */
1958 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1959 break;
1960 }
1961 case LightenCompositeOp:
1962 {
1963 if (Sc > Dc)
1964 {
cristy24d5d722012-05-17 12:27:27 +00001965 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00001966 break;
1967 }
cristy24d5d722012-05-17 12:27:27 +00001968 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00001969 break;
1970 }
cristye4a40472011-12-22 02:56:19 +00001971 case LightenIntensityCompositeOp:
1972 {
1973 /*
1974 Lighten is equivalent to a 'Maximum' method
1975 OR a greyscale version of a binary 'And'
1976 OR the 'Union' of pixel sets.
1977 */
1978 pixel=Sa*GetPixelIntensity(composite_image,p) >
1979 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1980 break;
1981 }
cristye4a40472011-12-22 02:56:19 +00001982 case LuminizeCompositeOp:
1983 {
cristy99abff32011-12-24 20:45:16 +00001984 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001985 {
1986 pixel=Dc;
1987 break;
1988 }
cristy99abff32011-12-24 20:45:16 +00001989 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001990 {
1991 pixel=Sc;
1992 break;
1993 }
cristyddeeea22012-04-12 01:33:09 +00001994 CompositeHSB(destination_pixel.red,destination_pixel.green,
1995 destination_pixel.blue,&hue,&saturation,&brightness);
1996 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001997 &sans,&sans,&brightness);
1998 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1999 switch (channel)
2000 {
2001 case RedPixelChannel: pixel=red; break;
2002 case GreenPixelChannel: pixel=green; break;
2003 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002004 default: pixel=Dc; break;
2005 }
2006 break;
2007 }
2008 case MathematicsCompositeOp:
2009 {
2010 /*
2011 'Mathematics' a free form user control mathematical composition
2012 is defined as...
2013
2014 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2015
2016 Where the arguments A,B,C,D are (currently) passed to composite
2017 as a command separated 'geometry' string in "compose:args" image
2018 artifact.
2019
2020 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2021
2022 Applying the SVG transparency formula (see above), we get...
2023
2024 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2025
2026 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2027 Dca*(1.0-Sa)
2028 */
2029 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2030 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2031 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2032 break;
2033 }
2034 case MinusDstCompositeOp:
2035 {
2036 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2037 break;
2038 }
2039 case MinusSrcCompositeOp:
2040 {
2041 /*
2042 Minus source from destination.
2043
2044 f(Sc,Dc) = Sc - Dc
2045 */
cristy99abff32011-12-24 20:45:16 +00002046 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002047 break;
2048 }
2049 case ModulateCompositeOp:
2050 {
2051 ssize_t
2052 offset;
2053
cristy99abff32011-12-24 20:45:16 +00002054 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002055 {
2056 pixel=Dc;
2057 break;
2058 }
2059 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2060 if (offset == 0)
2061 {
2062 pixel=Dc;
2063 break;
2064 }
cristyddeeea22012-04-12 01:33:09 +00002065 CompositeHSB(destination_pixel.red,destination_pixel.green,
2066 destination_pixel.blue,&hue,&saturation,&brightness);
cristye4a40472011-12-22 02:56:19 +00002067 brightness+=(0.01*percent_brightness*offset)/midpoint;
2068 saturation*=0.01*percent_saturation;
2069 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2070 switch (channel)
2071 {
2072 case RedPixelChannel: pixel=red; break;
2073 case GreenPixelChannel: pixel=green; break;
2074 case BluePixelChannel: pixel=blue; break;
2075 default: pixel=Dc; break;
2076 }
2077 break;
2078 }
2079 case ModulusAddCompositeOp:
2080 {
2081 pixel=Sc+Dc;
2082 if (pixel > QuantumRange)
2083 pixel-=(QuantumRange+1.0);
2084 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2085 break;
2086 }
2087 case ModulusSubtractCompositeOp:
2088 {
cristy99abff32011-12-24 20:45:16 +00002089 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002090 if (pixel < 0.0)
2091 pixel+=(QuantumRange+1.0);
2092 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2093 break;
2094 }
2095 case MultiplyCompositeOp:
2096 {
cristy99abff32011-12-24 20:45:16 +00002097 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002098 break;
2099 }
2100 case OutCompositeOp:
2101 case SrcOutCompositeOp:
2102 {
2103 pixel=gamma*(Sa*Sc*(1.0-Da));
2104 break;
2105 }
2106 case OverCompositeOp:
2107 case SrcOverCompositeOp:
2108 {
cristy99abff32011-12-24 20:45:16 +00002109 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002110 break;
2111 }
2112 case OverlayCompositeOp:
2113 {
2114 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002115 {
2116 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2117 (1.0-Da));
2118 break;
2119 }
2120 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2121 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002122 break;
2123 }
2124 case PegtopLightCompositeOp:
2125 {
2126 /*
2127 PegTop: A Soft-Light alternative: A continuous version of the
2128 Softlight function, producing very similar results.
2129
2130 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2131
2132 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2133 */
2134 if (fabs(Da) < MagickEpsilon)
2135 {
cristy99abff32011-12-24 20:45:16 +00002136 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002137 break;
2138 }
cristy99abff32011-12-24 20:45:16 +00002139 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2140 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002141 break;
2142 }
2143 case PinLightCompositeOp:
2144 {
2145 /*
2146 PinLight: A Photoshop 7 composition method
2147 http://www.simplefilter.de/en/basics/mixmods.html
2148
2149 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2150 */
2151 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2152 {
cristy99abff32011-12-24 20:45:16 +00002153 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002154 break;
2155 }
2156 if ((Dca*Sa) > (2.0*Sca*Da))
2157 {
cristy99abff32011-12-24 20:45:16 +00002158 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002159 break;
2160 }
cristy99abff32011-12-24 20:45:16 +00002161 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002162 break;
2163 }
2164 case PlusCompositeOp:
2165 {
cristy24d5d722012-05-17 12:27:27 +00002166 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002167 break;
2168 }
2169 case SaturateCompositeOp:
2170 {
cristy99abff32011-12-24 20:45:16 +00002171 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002172 {
2173 pixel=Dc;
2174 break;
2175 }
cristy99abff32011-12-24 20:45:16 +00002176 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002177 {
2178 pixel=Sc;
2179 break;
2180 }
cristyddeeea22012-04-12 01:33:09 +00002181 CompositeHSB(destination_pixel.red,destination_pixel.green,
2182 destination_pixel.blue,&hue,&saturation,&brightness);
2183 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00002184 &sans,&saturation,&sans);
2185 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2186 switch (channel)
2187 {
2188 case RedPixelChannel: pixel=red; break;
2189 case GreenPixelChannel: pixel=green; break;
2190 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002191 default: pixel=Dc; break;
2192 }
2193 break;
2194 }
2195 case ScreenCompositeOp:
2196 {
2197 /*
2198 Screen: a negated multiply:
2199
2200 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2201 */
cristy99abff32011-12-24 20:45:16 +00002202 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002203 break;
2204 }
2205 case SoftLightCompositeOp:
2206 {
2207 /*
2208 Refer to the March 2009 SVG specification.
2209 */
2210 if ((2.0*Sca) < Sa)
2211 {
cristy99abff32011-12-24 20:45:16 +00002212 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2213 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002214 break;
2215 }
2216 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2217 {
cristy99abff32011-12-24 20:45:16 +00002218 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2219 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2220 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002221 break;
2222 }
cristy99abff32011-12-24 20:45:16 +00002223 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2224 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002225 break;
2226 }
2227 case ThresholdCompositeOp:
2228 {
2229 MagickRealType
2230 delta;
2231
2232 delta=Sc-Dc;
2233 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2234 {
2235 pixel=gamma*Dc;
2236 break;
2237 }
2238 pixel=gamma*(Dc+delta*amount);
2239 break;
2240 }
2241 case VividLightCompositeOp:
2242 {
2243 /*
2244 VividLight: A Photoshop 7 composition method. See
2245 http://www.simplefilter.de/en/basics/mixmods.html.
2246
2247 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2248 */
2249 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2250 {
cristy99abff32011-12-24 20:45:16 +00002251 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002252 break;
2253 }
2254 if ((2.0*Sca) <= Sa)
2255 {
cristy99abff32011-12-24 20:45:16 +00002256 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2257 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002258 break;
2259 }
cristy99abff32011-12-24 20:45:16 +00002260 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2261 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002262 break;
2263 }
2264 case XorCompositeOp:
2265 {
cristy99abff32011-12-24 20:45:16 +00002266 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002267 break;
2268 }
2269 default:
2270 {
2271 pixel=Sc;
2272 break;
2273 }
2274 }
2275 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002276 }
cristyed231572011-07-14 02:18:59 +00002277 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002278 channels=GetPixelChannels(composite_image);
2279 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002280 p=pixels;
cristyed231572011-07-14 02:18:59 +00002281 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002282 }
2283 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2284 status=MagickFalse;
2285 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2286 {
2287 MagickBooleanType
2288 proceed;
2289
2290#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002291 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002292#endif
2293 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2294 image->rows);
2295 if (proceed == MagickFalse)
2296 status=MagickFalse;
2297 }
2298 }
2299 composite_view=DestroyCacheView(composite_view);
2300 image_view=DestroyCacheView(image_view);
2301 if (destination_image != (Image * ) NULL)
2302 destination_image=DestroyImage(destination_image);
2303 return(status);
2304}
2305
2306/*
2307%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2308% %
2309% %
2310% %
2311% T e x t u r e I m a g e %
2312% %
2313% %
2314% %
2315%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2316%
2317% TextureImage() repeatedly tiles the texture image across and down the image
2318% canvas.
2319%
2320% The format of the TextureImage method is:
2321%
cristy30d8c942012-02-07 13:44:59 +00002322% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002323% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002324%
2325% A description of each parameter follows:
2326%
2327% o image: the image.
2328%
cristye6178502011-12-23 17:02:29 +00002329% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002330%
2331*/
cristy30d8c942012-02-07 13:44:59 +00002332MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2333 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002334{
2335#define TextureImageTag "Texture/Image"
2336
2337 CacheView
2338 *image_view,
2339 *texture_view;
2340
cristy30d8c942012-02-07 13:44:59 +00002341 Image
2342 *texture_image;
2343
cristy4c08aed2011-07-01 19:47:50 +00002344 MagickBooleanType
2345 status;
2346
2347 ssize_t
2348 y;
2349
2350 assert(image != (Image *) NULL);
2351 if (image->debug != MagickFalse)
2352 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2353 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002354 if (texture == (const Image *) NULL)
2355 return(MagickFalse);
2356 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2357 return(MagickFalse);
2358 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002359 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002360 return(MagickFalse);
cristydba8abf2012-05-24 01:28:10 +00002361 if (IsGrayColorspace(texture_image->colorspace) != MagickFalse)
2362 (void) TransformImageColorspace(texture_image,sRGBColorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002363 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2364 exception);
cristy4c08aed2011-07-01 19:47:50 +00002365 status=MagickTrue;
2366 if ((image->compose != CopyCompositeOp) &&
2367 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002368 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002369 {
2370 /*
2371 Tile texture onto the image background.
2372 */
2373#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002374 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002375 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002376#endif
cristye6178502011-12-23 17:02:29 +00002377 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002378 {
2379 register ssize_t
2380 x;
2381
2382 if (status == MagickFalse)
2383 continue;
cristye6178502011-12-23 17:02:29 +00002384 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002385 {
2386 MagickBooleanType
2387 thread_status;
2388
cristyfeb3e962012-03-29 17:25:55 +00002389 thread_status=CompositeImage(image,texture_image,image->compose,
2390 MagickFalse,x+texture_image->tile_offset.x,y+
2391 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002392 if (thread_status == MagickFalse)
2393 {
2394 status=thread_status;
2395 break;
2396 }
2397 }
2398 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2399 {
2400 MagickBooleanType
2401 proceed;
2402
2403#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002404 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002405#endif
2406 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2407 y,image->rows);
2408 if (proceed == MagickFalse)
2409 status=MagickFalse;
2410 }
2411 }
2412 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2413 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002414 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002415 return(status);
2416 }
2417 /*
2418 Tile texture onto the image background (optimized).
2419 */
2420 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +00002421 texture_view=AcquireVirtualCacheView(texture_image,exception);
2422 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00002423#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002424 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002425 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002426#endif
2427 for (y=0; y < (ssize_t) image->rows; y++)
2428 {
2429 MagickBooleanType
2430 sync;
2431
2432 register const Quantum
2433 *p,
2434 *pixels;
2435
2436 register ssize_t
2437 x;
2438
2439 register Quantum
2440 *q;
2441
2442 size_t
2443 width;
2444
2445 if (status == MagickFalse)
2446 continue;
cristye6178502011-12-23 17:02:29 +00002447 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2448 (y+texture_image->tile_offset.y) % texture_image->rows,
2449 texture_image->columns,1,exception);
2450 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002451 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2452 {
2453 status=MagickFalse;
2454 continue;
2455 }
cristye6178502011-12-23 17:02:29 +00002456 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002457 {
2458 register ssize_t
cristye6178502011-12-23 17:02:29 +00002459 j;
cristy4c08aed2011-07-01 19:47:50 +00002460
2461 p=pixels;
cristye6178502011-12-23 17:02:29 +00002462 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002463 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2464 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002465 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002466 {
cristye6178502011-12-23 17:02:29 +00002467 register ssize_t
2468 i;
2469
cristy10a6c612012-01-29 21:41:05 +00002470 if (GetPixelMask(image,p) != 0)
2471 {
2472 p+=GetPixelChannels(texture_image);
2473 q+=GetPixelChannels(image);
2474 continue;
2475 }
cristye6178502011-12-23 17:02:29 +00002476 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2477 {
2478 PixelChannel
2479 channel;
2480
2481 PixelTrait
2482 texture_traits,
2483 traits;
2484
2485 channel=GetPixelChannelMapChannel(texture_image,i);
2486 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2487 traits=GetPixelChannelMapTraits(image,channel);
2488 if ((traits == UndefinedPixelTrait) ||
2489 (texture_traits == UndefinedPixelTrait))
2490 continue;
2491 SetPixelChannel(image,channel,p[i],q);
2492 }
2493 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002494 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002495 }
2496 }
2497 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2498 if (sync == MagickFalse)
2499 status=MagickFalse;
2500 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2501 {
2502 MagickBooleanType
2503 proceed;
2504
2505#if defined(MAGICKCORE_OPENMP_SUPPORT)
2506 #pragma omp critical (MagickCore_TextureImage)
2507#endif
2508 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2509 image->rows);
2510 if (proceed == MagickFalse)
2511 status=MagickFalse;
2512 }
2513 }
2514 texture_view=DestroyCacheView(texture_view);
2515 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002516 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002517 return(status);
2518}