blob: ec602f432c29b9b1349f465cf0c13ce9b4d4a574 [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);
cristy63725a62012-07-11 11:56:03 +0000581 if (IsGrayColorspace(image->colorspace) != MagickFalse)
582 {
583 if (IsGrayColorspace(composite_image->colorspace) != MagickFalse)
584 (void) SetImageColorspace(image,RGBColorspace,exception);
585 else
586 (void) TransformImageColorspace(image,composite_image->colorspace,
587 exception);
588 }
cristye4a40472011-12-22 02:56:19 +0000589 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
590 {
cristyfeb3e962012-03-29 17:25:55 +0000591 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
592 y_offset,exception);
cristye4a40472011-12-22 02:56:19 +0000593 return(status);
594 }
cristy4c08aed2011-07-01 19:47:50 +0000595 destination_image=(Image *) NULL;
596 amount=0.5;
597 destination_dissolve=1.0;
cristy4c08aed2011-07-01 19:47:50 +0000598 percent_brightness=100.0;
599 percent_saturation=100.0;
600 source_dissolve=1.0;
601 threshold=0.05f;
602 switch (compose)
603 {
cristy4c08aed2011-07-01 19:47:50 +0000604 case CopyCompositeOp:
605 {
606 if ((x_offset < 0) || (y_offset < 0))
607 break;
608 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
609 break;
610 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
611 break;
612 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +0000613 composite_view=AcquireVirtualCacheView(composite_image,exception);
614 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000615#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000616 #pragma omp parallel for schedule(static,4) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000617 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000618#endif
619 for (y=0; y < (ssize_t) composite_image->rows; y++)
620 {
621 MagickBooleanType
622 sync;
623
624 register const Quantum
625 *p;
626
627 register Quantum
628 *q;
629
630 register ssize_t
631 x;
632
633 if (status == MagickFalse)
634 continue;
635 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
636 1,exception);
637 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
638 composite_image->columns,1,exception);
639 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
640 {
641 status=MagickFalse;
642 continue;
643 }
644 for (x=0; x < (ssize_t) composite_image->columns; x++)
645 {
cristybdecccc2011-12-24 22:52:16 +0000646 register ssize_t
647 i;
648
cristy665e18f2012-05-17 12:39:54 +0000649 if (GetPixelMask(composite_image,p) != 0)
cristy10a6c612012-01-29 21:41:05 +0000650 {
651 p+=GetPixelChannels(composite_image);
652 q+=GetPixelChannels(image);
653 continue;
654 }
cristybdecccc2011-12-24 22:52:16 +0000655 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
656 {
657 PixelChannel
658 channel;
659
660 PixelTrait
661 composite_traits,
662 traits;
663
664 channel=GetPixelChannelMapChannel(composite_image,i);
665 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
666 traits=GetPixelChannelMapTraits(image,channel);
667 if ((traits == UndefinedPixelTrait) ||
668 (composite_traits == UndefinedPixelTrait))
669 continue;
670 SetPixelChannel(image,channel,p[i],q);
671 }
cristyed231572011-07-14 02:18:59 +0000672 p+=GetPixelChannels(composite_image);
673 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000674 }
675 sync=SyncCacheViewAuthenticPixels(image_view,exception);
676 if (sync == MagickFalse)
677 status=MagickFalse;
678 if (image->progress_monitor != (MagickProgressMonitor) NULL)
679 {
680 MagickBooleanType
681 proceed;
682
683#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000684 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000685#endif
686 proceed=SetImageProgress(image,CompositeImageTag,
687 (MagickOffsetType) y,image->rows);
688 if (proceed == MagickFalse)
689 status=MagickFalse;
690 }
691 }
692 composite_view=DestroyCacheView(composite_view);
693 image_view=DestroyCacheView(image_view);
694 return(status);
695 }
cristye4a40472011-12-22 02:56:19 +0000696 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000697 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000698 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000699 {
700 /*
701 Modify destination outside the overlaid region and require an alpha
702 channel to exist, to add transparency.
703 */
704 if (image->matte == MagickFalse)
cristy42c41de2012-05-05 18:36:31 +0000705 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000706 break;
707 }
708 case BlurCompositeOp:
709 {
710 CacheView
711 *composite_view,
712 *destination_view;
713
cristyfeb3e962012-03-29 17:25:55 +0000714 const char
715 *value;
716
cristy4c08aed2011-07-01 19:47:50 +0000717 PixelInfo
718 pixel;
719
720 MagickRealType
721 angle_range,
722 angle_start,
723 height,
724 width;
725
726 ResampleFilter
727 *resample_filter;
728
729 SegmentInfo
730 blur;
731
732 /*
anthony9cb63cc2012-04-25 06:10:49 +0000733 Blur Image by resampling.
734
cristy4c08aed2011-07-01 19:47:50 +0000735 Blur Image dictated by an overlay gradient map: X = red_channel;
736 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
737 */
738 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000739 exception);
cristy4c08aed2011-07-01 19:47:50 +0000740 if (destination_image == (Image *) NULL)
741 return(MagickFalse);
742 /*
anthony9cb63cc2012-04-25 06:10:49 +0000743 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000744 */
745 SetGeometryInfo(&geometry_info);
746 flags=NoValue;
747 value=GetImageArtifact(composite_image,"compose:args");
748 if (value != (char *) NULL)
749 flags=ParseGeometry(value,&geometry_info);
anthonyd2923912012-04-23 13:06:53 +0000750 if ((flags & WidthValue) == 0 ) {
751 (void) ThrowMagickException(exception,GetMagickModule(),
752 OptionWarning,"InvalidSetting","'%s' '%s'",
anthony9cb63cc2012-04-25 06:10:49 +0000753 "compose:args",value);
cristy4c08aed2011-07-01 19:47:50 +0000754 destination_image=DestroyImage(destination_image);
755 return(MagickFalse);
756 }
anthony9cb63cc2012-04-25 06:10:49 +0000757 /*
758 Users input sigma now needs to be converted to the EWA ellipse size.
759 The filter defaults to a sigma of 0.5 so to make this match the
760 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000761 */
762 width=height=geometry_info.rho*2.0;
763 if ((flags & HeightValue) != 0 )
764 height=geometry_info.sigma*2.0;
765
anthony9cb63cc2012-04-25 06:10:49 +0000766 /* default the unrotated ellipse width and height axis vectors */
anthonyd2923912012-04-23 13:06:53 +0000767 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000768 blur.x2=0.0;
769 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000770 blur.y2=height;
771 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000772 if ((flags & XValue) != 0 )
773 {
774 MagickRealType
775 angle;
776
777 angle=DegreesToRadians(geometry_info.xi);
778 blur.x1=width*cos(angle);
779 blur.x2=width*sin(angle);
780 blur.y1=(-height*sin(angle));
781 blur.y2=height*cos(angle);
782 }
anthonyd2923912012-04-23 13:06:53 +0000783 /* Otherwise lets set a angle range and calculate in the loop */
784 angle_start=0.0;
785 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000786 if ((flags & YValue) != 0 )
787 {
788 angle_start=DegreesToRadians(geometry_info.xi);
789 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
790 }
791 /*
anthony9cb63cc2012-04-25 06:10:49 +0000792 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000793
anthony9cb63cc2012-04-25 06:10:49 +0000794 As the minimum ellipse radius of support*1.0 the EWA algorithm
795 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
796 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000797
anthony9cb63cc2012-04-25 06:10:49 +0000798 The solution (as well as the problem of preventing any user
799 expert filter settings, is to set our own user settings, then
800 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000801 */
cristy8a11cb12011-10-19 23:53:34 +0000802 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000803 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000804
805 /* do the variable blurring of each pixel in image */
806 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000807 composite_view=AcquireVirtualCacheView(composite_image,exception);
808 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000809 for (y=0; y < (ssize_t) composite_image->rows; y++)
810 {
811 MagickBooleanType
812 sync;
813
814 register const Quantum
815 *restrict p;
816
817 register Quantum
818 *restrict q;
819
820 register ssize_t
821 x;
822
823 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
824 continue;
825 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
826 1,exception);
827 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000828 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000829 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
830 break;
831 for (x=0; x < (ssize_t) composite_image->columns; x++)
832 {
833 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
834 {
cristyed231572011-07-14 02:18:59 +0000835 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000836 continue;
837 }
838 if (fabs(angle_range) > MagickEpsilon)
839 {
840 MagickRealType
841 angle;
842
843 angle=angle_start+angle_range*QuantumScale*
844 GetPixelBlue(composite_image,p);
845 blur.x1=width*cos(angle);
846 blur.x2=width*sin(angle);
847 blur.y1=(-height*sin(angle));
848 blur.y2=height*cos(angle);
849 }
anthonyd2923912012-04-23 13:06:53 +0000850#if 0
851 if ( x == 10 && y == 60 ) {
852 fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
853 blur.x1, blur.x2, blur.y1, blur.y2);
854 fprintf(stderr, "scaled by=%lf,%lf\n",
855 QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
856#endif
857 ScaleResampleFilter(resample_filter,
858 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
859 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
860 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
861 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000862 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000863 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000864 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000865 p+=GetPixelChannels(composite_image);
866 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000867 }
868 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
869 if (sync == MagickFalse)
870 break;
871 }
872 resample_filter=DestroyResampleFilter(resample_filter);
873 composite_view=DestroyCacheView(composite_view);
874 destination_view=DestroyCacheView(destination_view);
875 composite_image=destination_image;
876 break;
877 }
878 case DisplaceCompositeOp:
879 case DistortCompositeOp:
880 {
881 CacheView
882 *composite_view,
883 *destination_view,
884 *image_view;
885
cristyfeb3e962012-03-29 17:25:55 +0000886 const char
887 *value;
888
cristy4c08aed2011-07-01 19:47:50 +0000889 PixelInfo
890 pixel;
891
892 MagickRealType
893 horizontal_scale,
894 vertical_scale;
895
896 PointInfo
897 center,
898 offset;
899
900 /*
901 Displace/Distort based on overlay gradient map:
902 X = red_channel; Y = green_channel;
903 compose:args = x_scale[,y_scale[,center.x,center.y]]
904 */
905 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000906 exception);
cristy4c08aed2011-07-01 19:47:50 +0000907 if (destination_image == (Image *) NULL)
908 return(MagickFalse);
909 SetGeometryInfo(&geometry_info);
910 flags=NoValue;
911 value=GetImageArtifact(composite_image,"compose:args");
912 if (value != (char *) NULL)
913 flags=ParseGeometry(value,&geometry_info);
914 if ((flags & (WidthValue|HeightValue)) == 0 )
915 {
916 if ((flags & AspectValue) == 0)
917 {
918 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
919 2.0;
920 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
921 }
922 else
923 {
924 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
925 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
926 }
927 }
928 else
929 {
930 horizontal_scale=geometry_info.rho;
931 vertical_scale=geometry_info.sigma;
932 if ((flags & PercentValue) != 0)
933 {
934 if ((flags & AspectValue) == 0)
935 {
936 horizontal_scale*=(composite_image->columns-1.0)/200.0;
937 vertical_scale*=(composite_image->rows-1.0)/200.0;
938 }
939 else
940 {
941 horizontal_scale*=(image->columns-1.0)/200.0;
942 vertical_scale*=(image->rows-1.0)/200.0;
943 }
944 }
945 if ((flags & HeightValue) == 0)
946 vertical_scale=horizontal_scale;
947 }
948 /*
949 Determine fixed center point for absolute distortion map
950 Absolute distort ==
951 Displace offset relative to a fixed absolute point
952 Select that point according to +X+Y user inputs.
953 default = center of overlay image
954 arg flag '!' = locations/percentage relative to background image
955 */
956 center.x=(MagickRealType) x_offset;
957 center.y=(MagickRealType) y_offset;
958 if (compose == DistortCompositeOp)
959 {
960 if ((flags & XValue) == 0)
961 if ((flags & AspectValue) == 0)
962 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
963 2.0;
964 else
965 center.x=((MagickRealType) image->columns-1)/2.0;
966 else
967 if ((flags & AspectValue) == 0)
968 center.x=(MagickRealType) x_offset+geometry_info.xi;
969 else
970 center.x=geometry_info.xi;
971 if ((flags & YValue) == 0)
972 if ((flags & AspectValue) == 0)
973 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
974 else
975 center.y=((MagickRealType) image->rows-1)/2.0;
976 else
977 if ((flags & AspectValue) == 0)
978 center.y=(MagickRealType) y_offset+geometry_info.psi;
979 else
980 center.y=geometry_info.psi;
981 }
982 /*
983 Shift the pixel offset point as defined by the provided,
984 displacement/distortion map. -- Like a lens...
985 */
cristye10859a2011-12-18 22:28:59 +0000986 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000987 image_view=AcquireVirtualCacheView(image,exception);
988 composite_view=AcquireVirtualCacheView(composite_image,exception);
989 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000990 for (y=0; y < (ssize_t) composite_image->rows; y++)
991 {
992 MagickBooleanType
993 sync;
994
995 register const Quantum
996 *restrict p;
997
998 register Quantum
999 *restrict q;
1000
1001 register ssize_t
1002 x;
1003
1004 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1005 continue;
1006 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1007 1,exception);
1008 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001009 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001010 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1011 break;
1012 for (x=0; x < (ssize_t) composite_image->columns; x++)
1013 {
1014 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1015 {
cristyed231572011-07-14 02:18:59 +00001016 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001017 continue;
1018 }
1019 /*
1020 Displace the offset.
1021 */
1022 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1023 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1024 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1025 x : 0);
1026 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1027 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1028 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1029 y : 0);
1030 (void) InterpolatePixelInfo(image,image_view,
1031 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1032 &pixel,exception);
1033 /*
1034 Mask with the 'invalid pixel mask' in alpha channel.
1035 */
1036 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001037 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001038 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001039 p+=GetPixelChannels(composite_image);
1040 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001041 }
1042 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1043 if (sync == MagickFalse)
1044 break;
1045 }
1046 destination_view=DestroyCacheView(destination_view);
1047 composite_view=DestroyCacheView(composite_view);
1048 image_view=DestroyCacheView(image_view);
1049 composite_image=destination_image;
1050 break;
1051 }
1052 case DissolveCompositeOp:
1053 {
cristyfeb3e962012-03-29 17:25:55 +00001054 const char
1055 *value;
1056
cristy4c08aed2011-07-01 19:47:50 +00001057 /*
1058 Geometry arguments to dissolve factors.
1059 */
1060 value=GetImageArtifact(composite_image,"compose:args");
1061 if (value != (char *) NULL)
1062 {
1063 flags=ParseGeometry(value,&geometry_info);
1064 source_dissolve=geometry_info.rho/100.0;
1065 destination_dissolve=1.0;
1066 if ((source_dissolve-MagickEpsilon) < 0.0)
1067 source_dissolve=0.0;
1068 if ((source_dissolve+MagickEpsilon) > 1.0)
1069 {
1070 destination_dissolve=2.0-source_dissolve;
1071 source_dissolve=1.0;
1072 }
1073 if ((flags & SigmaValue) != 0)
1074 destination_dissolve=geometry_info.sigma/100.0;
1075 if ((destination_dissolve-MagickEpsilon) < 0.0)
1076 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001077 /* posible speed up? -- from IMv6 update
1078 clip_to_self=MagickFalse;
1079 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1080 {
1081 destination_dissolve=1.0;
1082 clip_to_self=MagickTrue;
1083 }
1084 */
cristy4c08aed2011-07-01 19:47:50 +00001085 }
1086 break;
1087 }
1088 case BlendCompositeOp:
1089 {
cristyfeb3e962012-03-29 17:25:55 +00001090 const char
1091 *value;
1092
cristy4c08aed2011-07-01 19:47:50 +00001093 value=GetImageArtifact(composite_image,"compose:args");
1094 if (value != (char *) NULL)
1095 {
1096 flags=ParseGeometry(value,&geometry_info);
1097 source_dissolve=geometry_info.rho/100.0;
1098 destination_dissolve=1.0-source_dissolve;
1099 if ((flags & SigmaValue) != 0)
1100 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001101 }
1102 break;
1103 }
1104 case MathematicsCompositeOp:
1105 {
cristyfeb3e962012-03-29 17:25:55 +00001106 const char
1107 *value;
1108
cristy4c08aed2011-07-01 19:47:50 +00001109 /*
1110 Just collect the values from "compose:args", setting.
1111 Unused values are set to zero automagically.
1112
1113 Arguments are normally a comma separated list, so this probably should
1114 be changed to some 'general comma list' parser, (with a minimum
1115 number of values)
1116 */
1117 SetGeometryInfo(&geometry_info);
1118 value=GetImageArtifact(composite_image,"compose:args");
1119 if (value != (char *) NULL)
1120 (void) ParseGeometry(value,&geometry_info);
1121 break;
1122 }
1123 case ModulateCompositeOp:
1124 {
cristyfeb3e962012-03-29 17:25:55 +00001125 const char
1126 *value;
1127
cristy4c08aed2011-07-01 19:47:50 +00001128 /*
1129 Determine the brightness and saturation scale.
1130 */
1131 value=GetImageArtifact(composite_image,"compose:args");
1132 if (value != (char *) NULL)
1133 {
1134 flags=ParseGeometry(value,&geometry_info);
1135 percent_brightness=geometry_info.rho;
1136 if ((flags & SigmaValue) != 0)
1137 percent_saturation=geometry_info.sigma;
1138 }
1139 break;
1140 }
1141 case ThresholdCompositeOp:
1142 {
cristyfeb3e962012-03-29 17:25:55 +00001143 const char
1144 *value;
1145
cristy4c08aed2011-07-01 19:47:50 +00001146 /*
1147 Determine the amount and threshold.
1148 */
1149 value=GetImageArtifact(composite_image,"compose:args");
1150 if (value != (char *) NULL)
1151 {
1152 flags=ParseGeometry(value,&geometry_info);
1153 amount=geometry_info.rho;
1154 threshold=geometry_info.sigma;
1155 if ((flags & SigmaValue) == 0)
1156 threshold=0.05f;
1157 }
1158 threshold*=QuantumRange;
1159 break;
1160 }
1161 default:
1162 break;
1163 }
cristy4c08aed2011-07-01 19:47:50 +00001164 /*
1165 Composite image.
1166 */
1167 status=MagickTrue;
1168 progress=0;
1169 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristydb070952012-04-20 14:33:00 +00001170 composite_view=AcquireVirtualCacheView(composite_image,exception);
1171 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001172#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001173 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +00001174 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001175#endif
1176 for (y=0; y < (ssize_t) image->rows; y++)
1177 {
1178 const Quantum
1179 *pixels;
1180
1181 double
cristye4a40472011-12-22 02:56:19 +00001182 blue,
cristy4c08aed2011-07-01 19:47:50 +00001183 brightness,
cristye4a40472011-12-22 02:56:19 +00001184 green,
cristy4c08aed2011-07-01 19:47:50 +00001185 hue,
cristye4a40472011-12-22 02:56:19 +00001186 red,
cristy4c08aed2011-07-01 19:47:50 +00001187 saturation;
1188
cristyddeeea22012-04-12 01:33:09 +00001189 PixelInfo
1190 destination_pixel,
1191 source_pixel;
1192
cristy4c08aed2011-07-01 19:47:50 +00001193 register const Quantum
1194 *restrict p;
1195
1196 register Quantum
1197 *restrict q;
1198
1199 register ssize_t
1200 x;
1201
1202 if (status == MagickFalse)
1203 continue;
cristyfeb3e962012-03-29 17:25:55 +00001204 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001205 {
1206 if (y < y_offset)
1207 continue;
1208 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1209 continue;
1210 }
1211 /*
1212 If pixels is NULL, y is outside overlay region.
1213 */
1214 pixels=(Quantum *) NULL;
1215 p=(Quantum *) NULL;
1216 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1217 {
1218 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1219 composite_image->columns,1,exception);
1220 if (p == (const Quantum *) NULL)
1221 {
1222 status=MagickFalse;
1223 continue;
1224 }
1225 pixels=p;
1226 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001227 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001228 }
1229 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001230 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001231 {
1232 status=MagickFalse;
1233 continue;
1234 }
cristy4c08aed2011-07-01 19:47:50 +00001235 hue=0.0;
1236 saturation=0.0;
1237 brightness=0.0;
cristyddeeea22012-04-12 01:33:09 +00001238 GetPixelInfo(image,&destination_pixel);
1239 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001240 for (x=0; x < (ssize_t) image->columns; x++)
1241 {
cristye4a40472011-12-22 02:56:19 +00001242 MagickRealType
1243 alpha,
1244 Da,
1245 Dc,
1246 Dca,
1247 gamma,
1248 Sa,
1249 Sc,
1250 Sca;
1251
1252 register ssize_t
1253 i;
1254
cristy564a5692012-01-20 23:56:26 +00001255 size_t
1256 channels;
1257
cristyfeb3e962012-03-29 17:25:55 +00001258 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001259 {
1260 if (x < x_offset)
1261 {
cristyed231572011-07-14 02:18:59 +00001262 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001263 continue;
1264 }
1265 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1266 break;
1267 }
cristye4a40472011-12-22 02:56:19 +00001268 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1269 ((x-x_offset) >= (ssize_t) composite_image->columns))
1270 {
1271 Quantum
1272 source[MaxPixelChannels];
1273
1274 /*
1275 Virtual composite:
1276 Sc: source color.
1277 Dc: destination color.
1278 */
1279 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1280 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001281 if (GetPixelMask(image,q) != 0)
1282 {
1283 q+=GetPixelChannels(image);
1284 continue;
1285 }
cristye4a40472011-12-22 02:56:19 +00001286 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1287 {
1288 MagickRealType
1289 pixel;
1290
1291 PixelChannel
1292 channel;
1293
1294 PixelTrait
1295 composite_traits,
1296 traits;
1297
1298 channel=GetPixelChannelMapChannel(image,i);
1299 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +00001300 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001301 if ((traits == UndefinedPixelTrait) ||
1302 (composite_traits == UndefinedPixelTrait))
1303 continue;
1304 switch (compose)
1305 {
cristyc8d63672012-01-11 13:03:13 +00001306 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001307 case ChangeMaskCompositeOp:
1308 case CopyAlphaCompositeOp:
1309 case DstAtopCompositeOp:
1310 case DstInCompositeOp:
1311 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001312 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001313 case OutCompositeOp:
1314 case SrcInCompositeOp:
1315 case SrcOutCompositeOp:
1316 {
1317 pixel=(MagickRealType) q[i];
1318 if (channel == AlphaPixelChannel)
1319 pixel=(MagickRealType) TransparentAlpha;
1320 break;
1321 }
1322 case ClearCompositeOp:
1323 case CopyCompositeOp:
1324 case ReplaceCompositeOp:
1325 case SrcCompositeOp:
1326 {
1327 if (channel == AlphaPixelChannel)
1328 {
1329 pixel=(MagickRealType) TransparentAlpha;
1330 break;
1331 }
1332 pixel=0.0;
1333 break;
1334 }
cristy99abff32011-12-24 20:45:16 +00001335 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001336 case DissolveCompositeOp:
1337 {
1338 if (channel == AlphaPixelChannel)
1339 {
1340 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1341 source);
1342 break;
1343 }
1344 pixel=(MagickRealType) source[channel];
1345 break;
1346 }
1347 default:
1348 {
1349 pixel=(MagickRealType) source[channel];
1350 break;
1351 }
1352 }
1353 q[i]=ClampToQuantum(pixel);
1354 }
1355 q+=GetPixelChannels(image);
1356 continue;
1357 }
1358 /*
1359 Authentic composite:
1360 Sa: normalized source alpha.
1361 Da: normalized destination alpha.
1362 */
1363 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1364 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001365 switch (compose)
1366 {
cristye4a40472011-12-22 02:56:19 +00001367 case BumpmapCompositeOp:
1368 {
1369 alpha=GetPixelIntensity(composite_image,p)*Sa;
1370 break;
1371 }
cristycdc168f2011-12-21 15:24:39 +00001372 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001373 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001374 case DifferenceCompositeOp:
1375 case DivideDstCompositeOp:
1376 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001377 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001378 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001379 case LinearBurnCompositeOp:
1380 case LinearDodgeCompositeOp:
1381 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001382 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001383 case MinusDstCompositeOp:
1384 case MinusSrcCompositeOp:
1385 case ModulusAddCompositeOp:
1386 case ModulusSubtractCompositeOp:
1387 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001388 case OverlayCompositeOp:
1389 case PegtopLightCompositeOp:
1390 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001391 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001392 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001393 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001394 {
1395 alpha=RoundToUnity(Sa+Da-Sa*Da);
1396 break;
1397 }
1398 case DarkenCompositeOp:
1399 case DstAtopCompositeOp:
1400 case DstInCompositeOp:
1401 case InCompositeOp:
1402 case LightenCompositeOp:
1403 case SrcInCompositeOp:
1404 {
1405 alpha=Sa*Da;
1406 break;
1407 }
1408 case DissolveCompositeOp:
1409 {
1410 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1411 Sa+destination_dissolve*Da;
1412 break;
1413 }
1414 case DstOverCompositeOp:
1415 {
1416 alpha=Da*(-Sa)+Da+Sa;
1417 break;
1418 }
1419 case DstOutCompositeOp:
1420 {
1421 alpha=Da*(1.0-Sa);
1422 break;
1423 }
1424 case OutCompositeOp:
1425 case SrcOutCompositeOp:
1426 {
1427 alpha=Sa*(1.0-Da);
1428 break;
1429 }
1430 case OverCompositeOp:
1431 case SrcOverCompositeOp:
1432 {
1433 alpha=Sa*(-Da)+Sa+Da;
1434 break;
1435 }
cristy99abff32011-12-24 20:45:16 +00001436 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001437 case PlusCompositeOp:
1438 {
1439 alpha=RoundToUnity(Sa+Da);
1440 break;
1441 }
cristy4c08aed2011-07-01 19:47:50 +00001442 case XorCompositeOp:
1443 {
cristye4a40472011-12-22 02:56:19 +00001444 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001445 break;
1446 }
1447 default:
cristye4a40472011-12-22 02:56:19 +00001448 {
1449 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001450 break;
cristye4a40472011-12-22 02:56:19 +00001451 }
cristy4c08aed2011-07-01 19:47:50 +00001452 }
cristy10a6c612012-01-29 21:41:05 +00001453 if (GetPixelMask(image,p) != 0)
1454 {
1455 p+=GetPixelChannels(composite_image);
1456 q+=GetPixelChannels(image);
1457 continue;
1458 }
cristy9d3d2792012-04-14 15:15:19 +00001459 switch (compose)
1460 {
1461 case ColorizeCompositeOp:
1462 case HueCompositeOp:
1463 case LuminizeCompositeOp:
1464 case ModulateCompositeOp:
1465 case SaturateCompositeOp:
1466 {
1467 GetPixelInfoPixel(composite_image,p,&source_pixel);
1468 GetPixelInfoPixel(image,q,&destination_pixel);
1469 break;
1470 }
1471 default:
1472 break;
1473 }
cristye4a40472011-12-22 02:56:19 +00001474 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1475 {
cristy564a5692012-01-20 23:56:26 +00001476 double
1477 sans;
1478
cristye10859a2011-12-18 22:28:59 +00001479 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001480 pixel;
cristye10859a2011-12-18 22:28:59 +00001481
cristye4a40472011-12-22 02:56:19 +00001482 PixelChannel
1483 channel;
cristye10859a2011-12-18 22:28:59 +00001484
cristye4a40472011-12-22 02:56:19 +00001485 PixelTrait
1486 composite_traits,
1487 traits;
1488
1489 channel=GetPixelChannelMapChannel(image,i);
1490 traits=GetPixelChannelMapTraits(image,channel);
1491 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001492 if (traits == UndefinedPixelTrait)
1493 continue;
cristya7b07912012-01-11 20:01:32 +00001494 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001495 (composite_traits == UndefinedPixelTrait))
1496 continue;
1497 /*
1498 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001499 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001500 */
1501 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
cristye4a40472011-12-22 02:56:19 +00001502 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001503 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001504 {
cristye4a40472011-12-22 02:56:19 +00001505 if (channel != AlphaPixelChannel)
1506 {
1507 /*
1508 Copy channel.
1509 */
1510 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001511 continue;
cristye10859a2011-12-18 22:28:59 +00001512 }
cristye4a40472011-12-22 02:56:19 +00001513 /*
1514 Set alpha channel.
1515 */
cristye10859a2011-12-18 22:28:59 +00001516 switch (compose)
1517 {
cristyc8d63672012-01-11 13:03:13 +00001518 case AlphaCompositeOp:
1519 {
cristya7b07912012-01-11 20:01:32 +00001520 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001521 break;
1522 }
cristye4a40472011-12-22 02:56:19 +00001523 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001524 case CopyBlackCompositeOp:
1525 case CopyBlueCompositeOp:
1526 case CopyCyanCompositeOp:
1527 case CopyGreenCompositeOp:
1528 case CopyMagentaCompositeOp:
1529 case CopyRedCompositeOp:
1530 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001531 case SrcAtopCompositeOp:
1532 case DstCompositeOp:
1533 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001534 {
cristye4a40472011-12-22 02:56:19 +00001535 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001536 break;
1537 }
cristye10859a2011-12-18 22:28:59 +00001538 case ChangeMaskCompositeOp:
1539 {
cristye4a40472011-12-22 02:56:19 +00001540 MagickBooleanType
1541 equivalent;
1542
cristy99abff32011-12-24 20:45:16 +00001543 if (Da > ((MagickRealType) QuantumRange/2.0))
1544 {
1545 pixel=(MagickRealType) TransparentAlpha;
1546 break;
1547 }
cristye4a40472011-12-22 02:56:19 +00001548 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001549 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001550 {
1551 pixel=(MagickRealType) TransparentAlpha;
1552 break;
1553 }
1554 pixel=(MagickRealType) OpaqueAlpha;
1555 break;
1556 }
cristy99abff32011-12-24 20:45:16 +00001557 case ClearCompositeOp:
1558 {
1559 pixel=(MagickRealType) TransparentAlpha;
1560 break;
1561 }
1562 case ColorizeCompositeOp:
1563 case HueCompositeOp:
1564 case LuminizeCompositeOp:
1565 case SaturateCompositeOp:
1566 {
1567 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1568 {
1569 pixel=QuantumRange*Da;
1570 break;
1571 }
1572 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1573 {
1574 pixel=QuantumRange*Sa;
1575 break;
1576 }
1577 if (Sa < Da)
1578 {
1579 pixel=QuantumRange*Da;
1580 break;
1581 }
1582 pixel=QuantumRange*Sa;
1583 break;
1584 }
cristy99abff32011-12-24 20:45:16 +00001585 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001586 {
1587 pixel=QuantumRange*Sa;
1588 if (composite_image->matte == MagickFalse)
1589 pixel=GetPixelIntensity(composite_image,p);
1590 break;
1591 }
1592 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001593 case DisplaceCompositeOp:
1594 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001595 case DstAtopCompositeOp:
1596 case ReplaceCompositeOp:
1597 case SrcCompositeOp:
1598 {
1599 pixel=QuantumRange*Sa;
1600 break;
1601 }
1602 case DarkenIntensityCompositeOp:
1603 {
cristy99abff32011-12-24 20:45:16 +00001604 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1605 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001606 break;
1607 }
cristy98621462011-12-31 22:31:11 +00001608 case IntensityCompositeOp:
1609 {
cristy20d5f622012-01-11 13:04:26 +00001610 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001611 break;
1612 }
cristye4a40472011-12-22 02:56:19 +00001613 case LightenIntensityCompositeOp:
1614 {
1615 pixel=Sa*GetPixelIntensity(composite_image,p) >
1616 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001617 break;
1618 }
cristy99abff32011-12-24 20:45:16 +00001619 case ModulateCompositeOp:
1620 {
1621 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1622 {
1623 pixel=QuantumRange*Da;
1624 break;
1625 }
1626 pixel=QuantumRange*Da;
1627 break;
1628 }
cristye10859a2011-12-18 22:28:59 +00001629 default:
1630 {
cristye4a40472011-12-22 02:56:19 +00001631 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001632 break;
1633 }
1634 }
cristye4a40472011-12-22 02:56:19 +00001635 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001636 continue;
1637 }
1638 /*
cristy99abff32011-12-24 20:45:16 +00001639 Porter-Duff compositions:
1640 Sca: source normalized color multiplied by alpha.
1641 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001642 */
cristy99abff32011-12-24 20:45:16 +00001643 Sca=QuantumScale*Sa*Sc;
1644 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001645 switch (compose)
1646 {
cristye10859a2011-12-18 22:28:59 +00001647 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001648 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001649 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001650 {
cristy99abff32011-12-24 20:45:16 +00001651 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001652 break;
1653 }
1654 default:
1655 break;
1656 }
cristyc58380a2012-06-03 15:12:30 +00001657 gamma=MagickEpsilonReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001658 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001659 switch (compose)
1660 {
cristya7b07912012-01-11 20:01:32 +00001661 case AlphaCompositeOp:
1662 {
1663 pixel=QuantumRange*Sa;
1664 break;
1665 }
cristye4a40472011-12-22 02:56:19 +00001666 case AtopCompositeOp:
1667 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001668 {
cristye4a40472011-12-22 02:56:19 +00001669 pixel=Sc*Sa+Dc*(1.0-Sa);
1670 break;
cristye10859a2011-12-18 22:28:59 +00001671 }
cristye4a40472011-12-22 02:56:19 +00001672 case BlendCompositeOp:
1673 {
1674 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1675 break;
1676 }
1677 case BlurCompositeOp:
1678 case DisplaceCompositeOp:
1679 case DistortCompositeOp:
1680 case CopyCompositeOp:
1681 case ReplaceCompositeOp:
1682 case SrcCompositeOp:
1683 {
1684 pixel=Sc;
1685 break;
1686 }
1687 case BumpmapCompositeOp:
1688 {
cristy99abff32011-12-24 20:45:16 +00001689 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001690 {
1691 pixel=Dc;
1692 break;
1693 }
1694 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1695 break;
1696 }
cristy99abff32011-12-24 20:45:16 +00001697 case ChangeMaskCompositeOp:
1698 {
1699 pixel=Dc;
1700 break;
1701 }
1702 case ClearCompositeOp:
1703 {
1704 pixel=0.0;
1705 break;
1706 }
cristye4a40472011-12-22 02:56:19 +00001707 case ColorBurnCompositeOp:
1708 {
1709 /*
1710 Refer to the March 2009 SVG specification.
1711 */
1712 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1713 {
cristy99abff32011-12-24 20:45:16 +00001714 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001715 break;
1716 }
1717 if (Sca < MagickEpsilon)
1718 {
cristy99abff32011-12-24 20:45:16 +00001719 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001720 break;
1721 }
cristy99abff32011-12-24 20:45:16 +00001722 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1723 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001724 break;
1725 }
1726 case ColorDodgeCompositeOp:
1727 {
1728 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1729 {
cristy99abff32011-12-24 20:45:16 +00001730 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001731 break;
1732 }
1733 if (fabs(Sca-Sa) < MagickEpsilon)
1734 {
cristy99abff32011-12-24 20:45:16 +00001735 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001736 break;
1737 }
cristy99abff32011-12-24 20:45:16 +00001738 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001739 (1.0-Sa));
1740 break;
1741 }
1742 case ColorizeCompositeOp:
1743 {
cristy99abff32011-12-24 20:45:16 +00001744 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001745 {
1746 pixel=Dc;
1747 break;
1748 }
cristy99abff32011-12-24 20:45:16 +00001749 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001750 {
1751 pixel=Sc;
1752 break;
1753 }
cristyddeeea22012-04-12 01:33:09 +00001754 CompositeHSB(destination_pixel.red,destination_pixel.green,
1755 destination_pixel.blue,&sans,&sans,&brightness);
1756 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001757 &hue,&saturation,&sans);
1758 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1759 switch (channel)
1760 {
1761 case RedPixelChannel: pixel=red; break;
1762 case GreenPixelChannel: pixel=green; break;
1763 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001764 default: pixel=Dc; break;
1765 }
1766 break;
1767 }
cristye4a40472011-12-22 02:56:19 +00001768 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001769 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001770 {
cristy24d5d722012-05-17 12:27:27 +00001771 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001772 break;
1773 }
1774 case CopyBlackCompositeOp:
1775 {
cristyd197cbb2012-01-13 02:14:12 +00001776 if (channel == BlackPixelChannel)
1777 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001778 break;
1779 }
1780 case CopyBlueCompositeOp:
1781 case CopyYellowCompositeOp:
1782 {
cristyd197cbb2012-01-13 02:14:12 +00001783 if (channel == BluePixelChannel)
1784 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001785 break;
1786 }
1787 case CopyGreenCompositeOp:
1788 case CopyMagentaCompositeOp:
1789 {
cristyd197cbb2012-01-13 02:14:12 +00001790 if (channel == GreenPixelChannel)
1791 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001792 break;
1793 }
1794 case CopyRedCompositeOp:
1795 case CopyCyanCompositeOp:
1796 {
cristyd197cbb2012-01-13 02:14:12 +00001797 if (channel == RedPixelChannel)
1798 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001799 break;
1800 }
cristy99abff32011-12-24 20:45:16 +00001801 case DarkenCompositeOp:
1802 {
1803 /*
1804 Darken is equivalent to a 'Minimum' method
1805 OR a greyscale version of a binary 'Or'
1806 OR the 'Intersection' of pixel sets.
1807 */
1808 if (Sc < Dc)
1809 {
1810 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1811 break;
1812 }
1813 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1814 break;
1815 }
cristye4a40472011-12-22 02:56:19 +00001816 case DarkenIntensityCompositeOp:
1817 {
cristy99abff32011-12-24 20:45:16 +00001818 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1819 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001820 break;
1821 }
1822 case DifferenceCompositeOp:
1823 {
1824 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1825 break;
1826 }
1827 case DissolveCompositeOp:
1828 {
1829 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1830 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1831 break;
1832 }
1833 case DivideDstCompositeOp:
1834 {
1835 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1836 {
cristy99abff32011-12-24 20:45:16 +00001837 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001838 break;
1839 }
1840 if (fabs(Dca) < MagickEpsilon)
1841 {
cristy99abff32011-12-24 20:45:16 +00001842 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001843 break;
1844 }
cristy99abff32011-12-24 20:45:16 +00001845 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001846 break;
1847 }
1848 case DivideSrcCompositeOp:
1849 {
1850 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1851 {
cristy99abff32011-12-24 20:45:16 +00001852 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001853 break;
1854 }
1855 if (fabs(Sca) < MagickEpsilon)
1856 {
cristy99abff32011-12-24 20:45:16 +00001857 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001858 break;
1859 }
cristy99abff32011-12-24 20:45:16 +00001860 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001861 break;
1862 }
1863 case DstAtopCompositeOp:
1864 {
1865 pixel=Dc*Da+Sc*(1.0-Da);
1866 break;
1867 }
1868 case DstCompositeOp:
1869 case NoCompositeOp:
1870 {
1871 pixel=Dc;
1872 break;
1873 }
1874 case DstInCompositeOp:
1875 {
1876 pixel=gamma*(Sa*Dc*Sa);
1877 break;
1878 }
1879 case DstOutCompositeOp:
1880 {
1881 pixel=gamma*(Da*Dc*(1.0-Sa));
1882 break;
1883 }
1884 case DstOverCompositeOp:
1885 {
1886 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1887 break;
1888 }
1889 case ExclusionCompositeOp:
1890 {
cristy99abff32011-12-24 20:45:16 +00001891 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1892 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001893 break;
1894 }
1895 case HardLightCompositeOp:
1896 {
1897 if ((2.0*Sca) < Sa)
1898 {
cristy99abff32011-12-24 20:45:16 +00001899 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001900 (1.0-Sa));
1901 break;
1902 }
cristy99abff32011-12-24 20:45:16 +00001903 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001904 Dca*(1.0-Sa));
1905 break;
1906 }
1907 case HueCompositeOp:
1908 {
cristy99abff32011-12-24 20:45:16 +00001909 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001910 {
1911 pixel=Dc;
1912 break;
1913 }
cristy99abff32011-12-24 20:45:16 +00001914 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001915 {
1916 pixel=Sc;
1917 break;
1918 }
cristyddeeea22012-04-12 01:33:09 +00001919 CompositeHSB(destination_pixel.red,destination_pixel.green,
1920 destination_pixel.blue,&hue,&saturation,&brightness);
1921 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001922 &hue,&sans,&sans);
1923 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1924 switch (channel)
1925 {
1926 case RedPixelChannel: pixel=red; break;
1927 case GreenPixelChannel: pixel=green; break;
1928 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001929 default: pixel=Dc; break;
1930 }
1931 break;
1932 }
1933 case InCompositeOp:
1934 case SrcInCompositeOp:
1935 {
1936 pixel=gamma*(Da*Sc*Da);
1937 break;
1938 }
cristy99abff32011-12-24 20:45:16 +00001939 case LinearBurnCompositeOp:
1940 {
1941 /*
1942 LinearBurn: as defined by Abode Photoshop, according to
1943 http://www.simplefilter.de/en/basics/mixmods.html is:
1944
1945 f(Sc,Dc) = Sc + Dc - 1
1946 */
1947 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1948 break;
1949 }
1950 case LinearDodgeCompositeOp:
1951 {
1952 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1953 break;
1954 }
1955 case LinearLightCompositeOp:
1956 {
1957 /*
1958 LinearLight: as defined by Abode Photoshop, according to
1959 http://www.simplefilter.de/en/basics/mixmods.html is:
1960
1961 f(Sc,Dc) = Dc + 2*Sc - 1
1962 */
1963 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1964 break;
1965 }
1966 case LightenCompositeOp:
1967 {
1968 if (Sc > Dc)
1969 {
cristy24d5d722012-05-17 12:27:27 +00001970 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00001971 break;
1972 }
cristy24d5d722012-05-17 12:27:27 +00001973 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00001974 break;
1975 }
cristye4a40472011-12-22 02:56:19 +00001976 case LightenIntensityCompositeOp:
1977 {
1978 /*
1979 Lighten is equivalent to a 'Maximum' method
1980 OR a greyscale version of a binary 'And'
1981 OR the 'Union' of pixel sets.
1982 */
1983 pixel=Sa*GetPixelIntensity(composite_image,p) >
1984 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1985 break;
1986 }
cristye4a40472011-12-22 02:56:19 +00001987 case LuminizeCompositeOp:
1988 {
cristy99abff32011-12-24 20:45:16 +00001989 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001990 {
1991 pixel=Dc;
1992 break;
1993 }
cristy99abff32011-12-24 20:45:16 +00001994 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001995 {
1996 pixel=Sc;
1997 break;
1998 }
cristyddeeea22012-04-12 01:33:09 +00001999 CompositeHSB(destination_pixel.red,destination_pixel.green,
2000 destination_pixel.blue,&hue,&saturation,&brightness);
2001 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00002002 &sans,&sans,&brightness);
2003 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2004 switch (channel)
2005 {
2006 case RedPixelChannel: pixel=red; break;
2007 case GreenPixelChannel: pixel=green; break;
2008 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002009 default: pixel=Dc; break;
2010 }
2011 break;
2012 }
2013 case MathematicsCompositeOp:
2014 {
2015 /*
2016 'Mathematics' a free form user control mathematical composition
2017 is defined as...
2018
2019 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2020
2021 Where the arguments A,B,C,D are (currently) passed to composite
2022 as a command separated 'geometry' string in "compose:args" image
2023 artifact.
2024
2025 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2026
2027 Applying the SVG transparency formula (see above), we get...
2028
2029 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2030
2031 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2032 Dca*(1.0-Sa)
2033 */
2034 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2035 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2036 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2037 break;
2038 }
2039 case MinusDstCompositeOp:
2040 {
2041 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2042 break;
2043 }
2044 case MinusSrcCompositeOp:
2045 {
2046 /*
2047 Minus source from destination.
2048
2049 f(Sc,Dc) = Sc - Dc
2050 */
cristy99abff32011-12-24 20:45:16 +00002051 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002052 break;
2053 }
2054 case ModulateCompositeOp:
2055 {
2056 ssize_t
2057 offset;
2058
cristy99abff32011-12-24 20:45:16 +00002059 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002060 {
2061 pixel=Dc;
2062 break;
2063 }
2064 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2065 if (offset == 0)
2066 {
2067 pixel=Dc;
2068 break;
2069 }
cristyddeeea22012-04-12 01:33:09 +00002070 CompositeHSB(destination_pixel.red,destination_pixel.green,
2071 destination_pixel.blue,&hue,&saturation,&brightness);
cristye4a40472011-12-22 02:56:19 +00002072 brightness+=(0.01*percent_brightness*offset)/midpoint;
2073 saturation*=0.01*percent_saturation;
2074 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2075 switch (channel)
2076 {
2077 case RedPixelChannel: pixel=red; break;
2078 case GreenPixelChannel: pixel=green; break;
2079 case BluePixelChannel: pixel=blue; break;
2080 default: pixel=Dc; break;
2081 }
2082 break;
2083 }
2084 case ModulusAddCompositeOp:
2085 {
2086 pixel=Sc+Dc;
2087 if (pixel > QuantumRange)
2088 pixel-=(QuantumRange+1.0);
2089 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2090 break;
2091 }
2092 case ModulusSubtractCompositeOp:
2093 {
cristy99abff32011-12-24 20:45:16 +00002094 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002095 if (pixel < 0.0)
2096 pixel+=(QuantumRange+1.0);
2097 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2098 break;
2099 }
2100 case MultiplyCompositeOp:
2101 {
cristy99abff32011-12-24 20:45:16 +00002102 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002103 break;
2104 }
2105 case OutCompositeOp:
2106 case SrcOutCompositeOp:
2107 {
2108 pixel=gamma*(Sa*Sc*(1.0-Da));
2109 break;
2110 }
2111 case OverCompositeOp:
2112 case SrcOverCompositeOp:
2113 {
cristy99abff32011-12-24 20:45:16 +00002114 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002115 break;
2116 }
2117 case OverlayCompositeOp:
2118 {
2119 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002120 {
2121 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2122 (1.0-Da));
2123 break;
2124 }
2125 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2126 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002127 break;
2128 }
2129 case PegtopLightCompositeOp:
2130 {
2131 /*
2132 PegTop: A Soft-Light alternative: A continuous version of the
2133 Softlight function, producing very similar results.
2134
2135 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2136
2137 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2138 */
2139 if (fabs(Da) < MagickEpsilon)
2140 {
cristy99abff32011-12-24 20:45:16 +00002141 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002142 break;
2143 }
cristy99abff32011-12-24 20:45:16 +00002144 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2145 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002146 break;
2147 }
2148 case PinLightCompositeOp:
2149 {
2150 /*
2151 PinLight: A Photoshop 7 composition method
2152 http://www.simplefilter.de/en/basics/mixmods.html
2153
2154 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2155 */
2156 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2157 {
cristy99abff32011-12-24 20:45:16 +00002158 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002159 break;
2160 }
2161 if ((Dca*Sa) > (2.0*Sca*Da))
2162 {
cristy99abff32011-12-24 20:45:16 +00002163 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002164 break;
2165 }
cristy99abff32011-12-24 20:45:16 +00002166 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002167 break;
2168 }
2169 case PlusCompositeOp:
2170 {
cristy24d5d722012-05-17 12:27:27 +00002171 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002172 break;
2173 }
2174 case SaturateCompositeOp:
2175 {
cristy99abff32011-12-24 20:45:16 +00002176 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002177 {
2178 pixel=Dc;
2179 break;
2180 }
cristy99abff32011-12-24 20:45:16 +00002181 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002182 {
2183 pixel=Sc;
2184 break;
2185 }
cristyddeeea22012-04-12 01:33:09 +00002186 CompositeHSB(destination_pixel.red,destination_pixel.green,
2187 destination_pixel.blue,&hue,&saturation,&brightness);
2188 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00002189 &sans,&saturation,&sans);
2190 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2191 switch (channel)
2192 {
2193 case RedPixelChannel: pixel=red; break;
2194 case GreenPixelChannel: pixel=green; break;
2195 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002196 default: pixel=Dc; break;
2197 }
2198 break;
2199 }
2200 case ScreenCompositeOp:
2201 {
2202 /*
2203 Screen: a negated multiply:
2204
2205 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2206 */
cristy99abff32011-12-24 20:45:16 +00002207 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002208 break;
2209 }
2210 case SoftLightCompositeOp:
2211 {
2212 /*
2213 Refer to the March 2009 SVG specification.
2214 */
2215 if ((2.0*Sca) < Sa)
2216 {
cristy99abff32011-12-24 20:45:16 +00002217 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2218 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002219 break;
2220 }
2221 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2222 {
cristy99abff32011-12-24 20:45:16 +00002223 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2224 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2225 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002226 break;
2227 }
cristy99abff32011-12-24 20:45:16 +00002228 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2229 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002230 break;
2231 }
2232 case ThresholdCompositeOp:
2233 {
2234 MagickRealType
2235 delta;
2236
2237 delta=Sc-Dc;
2238 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2239 {
2240 pixel=gamma*Dc;
2241 break;
2242 }
2243 pixel=gamma*(Dc+delta*amount);
2244 break;
2245 }
2246 case VividLightCompositeOp:
2247 {
2248 /*
2249 VividLight: A Photoshop 7 composition method. See
2250 http://www.simplefilter.de/en/basics/mixmods.html.
2251
2252 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2253 */
2254 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2255 {
cristy99abff32011-12-24 20:45:16 +00002256 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002257 break;
2258 }
2259 if ((2.0*Sca) <= Sa)
2260 {
cristy99abff32011-12-24 20:45:16 +00002261 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2262 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002263 break;
2264 }
cristy99abff32011-12-24 20:45:16 +00002265 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2266 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002267 break;
2268 }
2269 case XorCompositeOp:
2270 {
cristy99abff32011-12-24 20:45:16 +00002271 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002272 break;
2273 }
2274 default:
2275 {
2276 pixel=Sc;
2277 break;
2278 }
2279 }
2280 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002281 }
cristyed231572011-07-14 02:18:59 +00002282 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002283 channels=GetPixelChannels(composite_image);
2284 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002285 p=pixels;
cristyed231572011-07-14 02:18:59 +00002286 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002287 }
2288 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2289 status=MagickFalse;
2290 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2291 {
2292 MagickBooleanType
2293 proceed;
2294
2295#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002296 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002297#endif
2298 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2299 image->rows);
2300 if (proceed == MagickFalse)
2301 status=MagickFalse;
2302 }
2303 }
2304 composite_view=DestroyCacheView(composite_view);
2305 image_view=DestroyCacheView(image_view);
2306 if (destination_image != (Image * ) NULL)
2307 destination_image=DestroyImage(destination_image);
2308 return(status);
2309}
2310
2311/*
2312%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2313% %
2314% %
2315% %
2316% T e x t u r e I m a g e %
2317% %
2318% %
2319% %
2320%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2321%
2322% TextureImage() repeatedly tiles the texture image across and down the image
2323% canvas.
2324%
2325% The format of the TextureImage method is:
2326%
cristy30d8c942012-02-07 13:44:59 +00002327% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002328% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002329%
2330% A description of each parameter follows:
2331%
2332% o image: the image.
2333%
cristye6178502011-12-23 17:02:29 +00002334% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002335%
2336*/
cristy30d8c942012-02-07 13:44:59 +00002337MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2338 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002339{
2340#define TextureImageTag "Texture/Image"
2341
2342 CacheView
2343 *image_view,
2344 *texture_view;
2345
cristy30d8c942012-02-07 13:44:59 +00002346 Image
2347 *texture_image;
2348
cristy4c08aed2011-07-01 19:47:50 +00002349 MagickBooleanType
2350 status;
2351
2352 ssize_t
2353 y;
2354
2355 assert(image != (Image *) NULL);
2356 if (image->debug != MagickFalse)
2357 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2358 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002359 if (texture == (const Image *) NULL)
2360 return(MagickFalse);
2361 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2362 return(MagickFalse);
2363 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002364 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002365 return(MagickFalse);
cristydba8abf2012-05-24 01:28:10 +00002366 if (IsGrayColorspace(texture_image->colorspace) != MagickFalse)
cristyb09db112012-07-11 12:04:31 +00002367 (void) TransformImageColorspace(texture_image,RGBColorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002368 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2369 exception);
cristy4c08aed2011-07-01 19:47:50 +00002370 status=MagickTrue;
2371 if ((image->compose != CopyCompositeOp) &&
2372 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002373 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002374 {
2375 /*
2376 Tile texture onto the image background.
2377 */
2378#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002379 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002380 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002381#endif
cristye6178502011-12-23 17:02:29 +00002382 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002383 {
2384 register ssize_t
2385 x;
2386
2387 if (status == MagickFalse)
2388 continue;
cristye6178502011-12-23 17:02:29 +00002389 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002390 {
2391 MagickBooleanType
2392 thread_status;
2393
cristyfeb3e962012-03-29 17:25:55 +00002394 thread_status=CompositeImage(image,texture_image,image->compose,
2395 MagickFalse,x+texture_image->tile_offset.x,y+
2396 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002397 if (thread_status == MagickFalse)
2398 {
2399 status=thread_status;
2400 break;
2401 }
2402 }
2403 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2404 {
2405 MagickBooleanType
2406 proceed;
2407
2408#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002409 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002410#endif
2411 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2412 y,image->rows);
2413 if (proceed == MagickFalse)
2414 status=MagickFalse;
2415 }
2416 }
2417 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2418 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002419 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002420 return(status);
2421 }
2422 /*
2423 Tile texture onto the image background (optimized).
2424 */
2425 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +00002426 texture_view=AcquireVirtualCacheView(texture_image,exception);
2427 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00002428#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002429 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002430 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002431#endif
2432 for (y=0; y < (ssize_t) image->rows; y++)
2433 {
2434 MagickBooleanType
2435 sync;
2436
2437 register const Quantum
2438 *p,
2439 *pixels;
2440
2441 register ssize_t
2442 x;
2443
2444 register Quantum
2445 *q;
2446
2447 size_t
2448 width;
2449
2450 if (status == MagickFalse)
2451 continue;
cristye6178502011-12-23 17:02:29 +00002452 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2453 (y+texture_image->tile_offset.y) % texture_image->rows,
2454 texture_image->columns,1,exception);
2455 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002456 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2457 {
2458 status=MagickFalse;
2459 continue;
2460 }
cristye6178502011-12-23 17:02:29 +00002461 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002462 {
2463 register ssize_t
cristye6178502011-12-23 17:02:29 +00002464 j;
cristy4c08aed2011-07-01 19:47:50 +00002465
2466 p=pixels;
cristye6178502011-12-23 17:02:29 +00002467 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002468 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2469 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002470 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002471 {
cristye6178502011-12-23 17:02:29 +00002472 register ssize_t
2473 i;
2474
cristy10a6c612012-01-29 21:41:05 +00002475 if (GetPixelMask(image,p) != 0)
2476 {
2477 p+=GetPixelChannels(texture_image);
2478 q+=GetPixelChannels(image);
2479 continue;
2480 }
cristye6178502011-12-23 17:02:29 +00002481 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2482 {
2483 PixelChannel
2484 channel;
2485
2486 PixelTrait
2487 texture_traits,
2488 traits;
2489
2490 channel=GetPixelChannelMapChannel(texture_image,i);
2491 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2492 traits=GetPixelChannelMapTraits(image,channel);
2493 if ((traits == UndefinedPixelTrait) ||
2494 (texture_traits == UndefinedPixelTrait))
2495 continue;
2496 SetPixelChannel(image,channel,p[i],q);
2497 }
2498 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002499 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002500 }
2501 }
2502 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2503 if (sync == MagickFalse)
2504 status=MagickFalse;
2505 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2506 {
2507 MagickBooleanType
2508 proceed;
2509
2510#if defined(MAGICKCORE_OPENMP_SUPPORT)
2511 #pragma omp critical (MagickCore_TextureImage)
2512#endif
2513 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2514 image->rows);
2515 if (proceed == MagickFalse)
2516 status=MagickFalse;
2517 }
2518 }
2519 texture_view=DestroyCacheView(texture_view);
2520 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002521 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002522 return(status);
2523}