blob: e2bf6048d3486c864067f34c5aff9625bd9b8ebe [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) \
cristyddacdd12012-05-07 23:08:14 +0000338 dynamic_number_threads(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];
505 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
506 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) \
cristyddacdd12012-05-07 23:08:14 +0000612 dynamic_number_threads(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
cristy10a6c612012-01-29 21:41:05 +0000644 if (GetPixelMask(image,p) != 0)
645 {
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) \
cristyddacdd12012-05-07 23:08:14 +00001169 dynamic_number_threads(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 }
cristye4a40472011-12-22 02:56:19 +00001580 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001581 case CopyAlphaCompositeOp:
1582 case DisplaceCompositeOp:
1583 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001584 case DstAtopCompositeOp:
1585 case ReplaceCompositeOp:
1586 case SrcCompositeOp:
1587 {
1588 pixel=QuantumRange*Sa;
1589 break;
1590 }
1591 case DarkenIntensityCompositeOp:
1592 {
cristy99abff32011-12-24 20:45:16 +00001593 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1594 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001595 break;
1596 }
cristy98621462011-12-31 22:31:11 +00001597 case IntensityCompositeOp:
1598 {
cristy20d5f622012-01-11 13:04:26 +00001599 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001600 break;
1601 }
cristye4a40472011-12-22 02:56:19 +00001602 case LightenIntensityCompositeOp:
1603 {
1604 pixel=Sa*GetPixelIntensity(composite_image,p) >
1605 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001606 break;
1607 }
cristy99abff32011-12-24 20:45:16 +00001608 case ModulateCompositeOp:
1609 {
1610 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1611 {
1612 pixel=QuantumRange*Da;
1613 break;
1614 }
1615 pixel=QuantumRange*Da;
1616 break;
1617 }
cristye10859a2011-12-18 22:28:59 +00001618 default:
1619 {
cristye4a40472011-12-22 02:56:19 +00001620 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001621 break;
1622 }
1623 }
cristye4a40472011-12-22 02:56:19 +00001624 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001625 continue;
1626 }
1627 /*
cristy99abff32011-12-24 20:45:16 +00001628 Porter-Duff compositions:
1629 Sca: source normalized color multiplied by alpha.
1630 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001631 */
cristy99abff32011-12-24 20:45:16 +00001632 Sca=QuantumScale*Sa*Sc;
1633 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001634 switch (compose)
1635 {
cristye10859a2011-12-18 22:28:59 +00001636 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001637 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001638 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001639 {
cristy99abff32011-12-24 20:45:16 +00001640 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001641 break;
1642 }
1643 default:
1644 break;
1645 }
cristye4a40472011-12-22 02:56:19 +00001646 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
cristyd197cbb2012-01-13 02:14:12 +00001647 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001648 switch (compose)
1649 {
cristya7b07912012-01-11 20:01:32 +00001650 case AlphaCompositeOp:
1651 {
1652 pixel=QuantumRange*Sa;
1653 break;
1654 }
cristye4a40472011-12-22 02:56:19 +00001655 case AtopCompositeOp:
1656 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001657 {
cristye4a40472011-12-22 02:56:19 +00001658 pixel=Sc*Sa+Dc*(1.0-Sa);
1659 break;
cristye10859a2011-12-18 22:28:59 +00001660 }
cristye4a40472011-12-22 02:56:19 +00001661 case BlendCompositeOp:
1662 {
1663 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1664 break;
1665 }
1666 case BlurCompositeOp:
1667 case DisplaceCompositeOp:
1668 case DistortCompositeOp:
1669 case CopyCompositeOp:
1670 case ReplaceCompositeOp:
1671 case SrcCompositeOp:
1672 {
1673 pixel=Sc;
1674 break;
1675 }
1676 case BumpmapCompositeOp:
1677 {
cristy99abff32011-12-24 20:45:16 +00001678 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001679 {
1680 pixel=Dc;
1681 break;
1682 }
1683 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1684 break;
1685 }
cristy99abff32011-12-24 20:45:16 +00001686 case ChangeMaskCompositeOp:
1687 {
1688 pixel=Dc;
1689 break;
1690 }
1691 case ClearCompositeOp:
1692 {
1693 pixel=0.0;
1694 break;
1695 }
cristye4a40472011-12-22 02:56:19 +00001696 case ColorBurnCompositeOp:
1697 {
1698 /*
1699 Refer to the March 2009 SVG specification.
1700 */
1701 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1702 {
cristy99abff32011-12-24 20:45:16 +00001703 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001704 break;
1705 }
1706 if (Sca < MagickEpsilon)
1707 {
cristy99abff32011-12-24 20:45:16 +00001708 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001709 break;
1710 }
cristy99abff32011-12-24 20:45:16 +00001711 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1712 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001713 break;
1714 }
1715 case ColorDodgeCompositeOp:
1716 {
1717 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1718 {
cristy99abff32011-12-24 20:45:16 +00001719 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001720 break;
1721 }
1722 if (fabs(Sca-Sa) < MagickEpsilon)
1723 {
cristy99abff32011-12-24 20:45:16 +00001724 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001725 break;
1726 }
cristy99abff32011-12-24 20:45:16 +00001727 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001728 (1.0-Sa));
1729 break;
1730 }
1731 case ColorizeCompositeOp:
1732 {
cristy99abff32011-12-24 20:45:16 +00001733 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001734 {
1735 pixel=Dc;
1736 break;
1737 }
cristy99abff32011-12-24 20:45:16 +00001738 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001739 {
1740 pixel=Sc;
1741 break;
1742 }
cristyddeeea22012-04-12 01:33:09 +00001743 CompositeHSB(destination_pixel.red,destination_pixel.green,
1744 destination_pixel.blue,&sans,&sans,&brightness);
1745 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001746 &hue,&saturation,&sans);
1747 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1748 switch (channel)
1749 {
1750 case RedPixelChannel: pixel=red; break;
1751 case GreenPixelChannel: pixel=green; break;
1752 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001753 default: pixel=Dc; break;
1754 }
1755 break;
1756 }
cristye4a40472011-12-22 02:56:19 +00001757 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001758 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001759 {
cristyd197cbb2012-01-13 02:14:12 +00001760 if (channel == AlphaPixelChannel)
1761 pixel=(MagickRealType) GetPixelAlpha(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001762 break;
1763 }
1764 case CopyBlackCompositeOp:
1765 {
cristyd197cbb2012-01-13 02:14:12 +00001766 if (channel == BlackPixelChannel)
1767 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001768 break;
1769 }
1770 case CopyBlueCompositeOp:
1771 case CopyYellowCompositeOp:
1772 {
cristyd197cbb2012-01-13 02:14:12 +00001773 if (channel == BluePixelChannel)
1774 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001775 break;
1776 }
1777 case CopyGreenCompositeOp:
1778 case CopyMagentaCompositeOp:
1779 {
cristyd197cbb2012-01-13 02:14:12 +00001780 if (channel == GreenPixelChannel)
1781 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001782 break;
1783 }
1784 case CopyRedCompositeOp:
1785 case CopyCyanCompositeOp:
1786 {
cristyd197cbb2012-01-13 02:14:12 +00001787 if (channel == RedPixelChannel)
1788 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001789 break;
1790 }
cristy99abff32011-12-24 20:45:16 +00001791 case DarkenCompositeOp:
1792 {
1793 /*
1794 Darken is equivalent to a 'Minimum' method
1795 OR a greyscale version of a binary 'Or'
1796 OR the 'Intersection' of pixel sets.
1797 */
1798 if (Sc < Dc)
1799 {
1800 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1801 break;
1802 }
1803 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1804 break;
1805 }
cristye4a40472011-12-22 02:56:19 +00001806 case DarkenIntensityCompositeOp:
1807 {
cristy99abff32011-12-24 20:45:16 +00001808 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1809 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001810 break;
1811 }
1812 case DifferenceCompositeOp:
1813 {
1814 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1815 break;
1816 }
1817 case DissolveCompositeOp:
1818 {
1819 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1820 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1821 break;
1822 }
1823 case DivideDstCompositeOp:
1824 {
1825 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1826 {
cristy99abff32011-12-24 20:45:16 +00001827 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001828 break;
1829 }
1830 if (fabs(Dca) < MagickEpsilon)
1831 {
cristy99abff32011-12-24 20:45:16 +00001832 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001833 break;
1834 }
cristy99abff32011-12-24 20:45:16 +00001835 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001836 break;
1837 }
1838 case DivideSrcCompositeOp:
1839 {
1840 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1841 {
cristy99abff32011-12-24 20:45:16 +00001842 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001843 break;
1844 }
1845 if (fabs(Sca) < MagickEpsilon)
1846 {
cristy99abff32011-12-24 20:45:16 +00001847 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001848 break;
1849 }
cristy99abff32011-12-24 20:45:16 +00001850 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001851 break;
1852 }
1853 case DstAtopCompositeOp:
1854 {
1855 pixel=Dc*Da+Sc*(1.0-Da);
1856 break;
1857 }
1858 case DstCompositeOp:
1859 case NoCompositeOp:
1860 {
1861 pixel=Dc;
1862 break;
1863 }
1864 case DstInCompositeOp:
1865 {
1866 pixel=gamma*(Sa*Dc*Sa);
1867 break;
1868 }
1869 case DstOutCompositeOp:
1870 {
1871 pixel=gamma*(Da*Dc*(1.0-Sa));
1872 break;
1873 }
1874 case DstOverCompositeOp:
1875 {
1876 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1877 break;
1878 }
1879 case ExclusionCompositeOp:
1880 {
cristy99abff32011-12-24 20:45:16 +00001881 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1882 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001883 break;
1884 }
1885 case HardLightCompositeOp:
1886 {
1887 if ((2.0*Sca) < Sa)
1888 {
cristy99abff32011-12-24 20:45:16 +00001889 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001890 (1.0-Sa));
1891 break;
1892 }
cristy99abff32011-12-24 20:45:16 +00001893 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001894 Dca*(1.0-Sa));
1895 break;
1896 }
1897 case HueCompositeOp:
1898 {
cristy99abff32011-12-24 20:45:16 +00001899 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001900 {
1901 pixel=Dc;
1902 break;
1903 }
cristy99abff32011-12-24 20:45:16 +00001904 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001905 {
1906 pixel=Sc;
1907 break;
1908 }
cristyddeeea22012-04-12 01:33:09 +00001909 CompositeHSB(destination_pixel.red,destination_pixel.green,
1910 destination_pixel.blue,&hue,&saturation,&brightness);
1911 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001912 &hue,&sans,&sans);
1913 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1914 switch (channel)
1915 {
1916 case RedPixelChannel: pixel=red; break;
1917 case GreenPixelChannel: pixel=green; break;
1918 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001919 default: pixel=Dc; break;
1920 }
1921 break;
1922 }
1923 case InCompositeOp:
1924 case SrcInCompositeOp:
1925 {
1926 pixel=gamma*(Da*Sc*Da);
1927 break;
1928 }
cristy99abff32011-12-24 20:45:16 +00001929 case LinearBurnCompositeOp:
1930 {
1931 /*
1932 LinearBurn: as defined by Abode Photoshop, according to
1933 http://www.simplefilter.de/en/basics/mixmods.html is:
1934
1935 f(Sc,Dc) = Sc + Dc - 1
1936 */
1937 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1938 break;
1939 }
1940 case LinearDodgeCompositeOp:
1941 {
1942 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1943 break;
1944 }
1945 case LinearLightCompositeOp:
1946 {
1947 /*
1948 LinearLight: as defined by Abode Photoshop, according to
1949 http://www.simplefilter.de/en/basics/mixmods.html is:
1950
1951 f(Sc,Dc) = Dc + 2*Sc - 1
1952 */
1953 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1954 break;
1955 }
1956 case LightenCompositeOp:
1957 {
1958 if (Sc > Dc)
1959 {
1960 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1961 break;
1962 }
1963 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1964 break;
1965 }
cristye4a40472011-12-22 02:56:19 +00001966 case LightenIntensityCompositeOp:
1967 {
1968 /*
1969 Lighten is equivalent to a 'Maximum' method
1970 OR a greyscale version of a binary 'And'
1971 OR the 'Union' of pixel sets.
1972 */
1973 pixel=Sa*GetPixelIntensity(composite_image,p) >
1974 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1975 break;
1976 }
cristye4a40472011-12-22 02:56:19 +00001977 case LuminizeCompositeOp:
1978 {
cristy99abff32011-12-24 20:45:16 +00001979 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001980 {
1981 pixel=Dc;
1982 break;
1983 }
cristy99abff32011-12-24 20:45:16 +00001984 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001985 {
1986 pixel=Sc;
1987 break;
1988 }
cristyddeeea22012-04-12 01:33:09 +00001989 CompositeHSB(destination_pixel.red,destination_pixel.green,
1990 destination_pixel.blue,&hue,&saturation,&brightness);
1991 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001992 &sans,&sans,&brightness);
1993 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1994 switch (channel)
1995 {
1996 case RedPixelChannel: pixel=red; break;
1997 case GreenPixelChannel: pixel=green; break;
1998 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001999 default: pixel=Dc; break;
2000 }
2001 break;
2002 }
2003 case MathematicsCompositeOp:
2004 {
2005 /*
2006 'Mathematics' a free form user control mathematical composition
2007 is defined as...
2008
2009 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2010
2011 Where the arguments A,B,C,D are (currently) passed to composite
2012 as a command separated 'geometry' string in "compose:args" image
2013 artifact.
2014
2015 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2016
2017 Applying the SVG transparency formula (see above), we get...
2018
2019 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2020
2021 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2022 Dca*(1.0-Sa)
2023 */
2024 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2025 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2026 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2027 break;
2028 }
2029 case MinusDstCompositeOp:
2030 {
2031 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2032 break;
2033 }
2034 case MinusSrcCompositeOp:
2035 {
2036 /*
2037 Minus source from destination.
2038
2039 f(Sc,Dc) = Sc - Dc
2040 */
cristy99abff32011-12-24 20:45:16 +00002041 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002042 break;
2043 }
2044 case ModulateCompositeOp:
2045 {
2046 ssize_t
2047 offset;
2048
cristy99abff32011-12-24 20:45:16 +00002049 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002050 {
2051 pixel=Dc;
2052 break;
2053 }
2054 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2055 if (offset == 0)
2056 {
2057 pixel=Dc;
2058 break;
2059 }
cristyddeeea22012-04-12 01:33:09 +00002060 CompositeHSB(destination_pixel.red,destination_pixel.green,
2061 destination_pixel.blue,&hue,&saturation,&brightness);
cristye4a40472011-12-22 02:56:19 +00002062 brightness+=(0.01*percent_brightness*offset)/midpoint;
2063 saturation*=0.01*percent_saturation;
2064 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2065 switch (channel)
2066 {
2067 case RedPixelChannel: pixel=red; break;
2068 case GreenPixelChannel: pixel=green; break;
2069 case BluePixelChannel: pixel=blue; break;
2070 default: pixel=Dc; break;
2071 }
2072 break;
2073 }
2074 case ModulusAddCompositeOp:
2075 {
2076 pixel=Sc+Dc;
2077 if (pixel > QuantumRange)
2078 pixel-=(QuantumRange+1.0);
2079 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2080 break;
2081 }
2082 case ModulusSubtractCompositeOp:
2083 {
cristy99abff32011-12-24 20:45:16 +00002084 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002085 if (pixel < 0.0)
2086 pixel+=(QuantumRange+1.0);
2087 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2088 break;
2089 }
2090 case MultiplyCompositeOp:
2091 {
cristy99abff32011-12-24 20:45:16 +00002092 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002093 break;
2094 }
2095 case OutCompositeOp:
2096 case SrcOutCompositeOp:
2097 {
2098 pixel=gamma*(Sa*Sc*(1.0-Da));
2099 break;
2100 }
2101 case OverCompositeOp:
2102 case SrcOverCompositeOp:
2103 {
cristy99abff32011-12-24 20:45:16 +00002104 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002105 break;
2106 }
2107 case OverlayCompositeOp:
2108 {
2109 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002110 {
2111 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2112 (1.0-Da));
2113 break;
2114 }
2115 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2116 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002117 break;
2118 }
2119 case PegtopLightCompositeOp:
2120 {
2121 /*
2122 PegTop: A Soft-Light alternative: A continuous version of the
2123 Softlight function, producing very similar results.
2124
2125 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2126
2127 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2128 */
2129 if (fabs(Da) < MagickEpsilon)
2130 {
cristy99abff32011-12-24 20:45:16 +00002131 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002132 break;
2133 }
cristy99abff32011-12-24 20:45:16 +00002134 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2135 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002136 break;
2137 }
2138 case PinLightCompositeOp:
2139 {
2140 /*
2141 PinLight: A Photoshop 7 composition method
2142 http://www.simplefilter.de/en/basics/mixmods.html
2143
2144 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2145 */
2146 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2147 {
cristy99abff32011-12-24 20:45:16 +00002148 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002149 break;
2150 }
2151 if ((Dca*Sa) > (2.0*Sca*Da))
2152 {
cristy99abff32011-12-24 20:45:16 +00002153 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002154 break;
2155 }
cristy99abff32011-12-24 20:45:16 +00002156 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002157 break;
2158 }
2159 case PlusCompositeOp:
2160 {
cristy99abff32011-12-24 20:45:16 +00002161 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002162 break;
2163 }
2164 case SaturateCompositeOp:
2165 {
cristy99abff32011-12-24 20:45:16 +00002166 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002167 {
2168 pixel=Dc;
2169 break;
2170 }
cristy99abff32011-12-24 20:45:16 +00002171 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002172 {
2173 pixel=Sc;
2174 break;
2175 }
cristyddeeea22012-04-12 01:33:09 +00002176 CompositeHSB(destination_pixel.red,destination_pixel.green,
2177 destination_pixel.blue,&hue,&saturation,&brightness);
2178 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00002179 &sans,&saturation,&sans);
2180 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2181 switch (channel)
2182 {
2183 case RedPixelChannel: pixel=red; break;
2184 case GreenPixelChannel: pixel=green; break;
2185 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002186 default: pixel=Dc; break;
2187 }
2188 break;
2189 }
2190 case ScreenCompositeOp:
2191 {
2192 /*
2193 Screen: a negated multiply:
2194
2195 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2196 */
cristy99abff32011-12-24 20:45:16 +00002197 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002198 break;
2199 }
2200 case SoftLightCompositeOp:
2201 {
2202 /*
2203 Refer to the March 2009 SVG specification.
2204 */
2205 if ((2.0*Sca) < Sa)
2206 {
cristy99abff32011-12-24 20:45:16 +00002207 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2208 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002209 break;
2210 }
2211 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2212 {
cristy99abff32011-12-24 20:45:16 +00002213 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2214 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2215 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002216 break;
2217 }
cristy99abff32011-12-24 20:45:16 +00002218 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2219 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002220 break;
2221 }
2222 case ThresholdCompositeOp:
2223 {
2224 MagickRealType
2225 delta;
2226
2227 delta=Sc-Dc;
2228 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2229 {
2230 pixel=gamma*Dc;
2231 break;
2232 }
2233 pixel=gamma*(Dc+delta*amount);
2234 break;
2235 }
2236 case VividLightCompositeOp:
2237 {
2238 /*
2239 VividLight: A Photoshop 7 composition method. See
2240 http://www.simplefilter.de/en/basics/mixmods.html.
2241
2242 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2243 */
2244 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2245 {
cristy99abff32011-12-24 20:45:16 +00002246 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002247 break;
2248 }
2249 if ((2.0*Sca) <= Sa)
2250 {
cristy99abff32011-12-24 20:45:16 +00002251 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2252 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002253 break;
2254 }
cristy99abff32011-12-24 20:45:16 +00002255 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2256 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002257 break;
2258 }
2259 case XorCompositeOp:
2260 {
cristy99abff32011-12-24 20:45:16 +00002261 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002262 break;
2263 }
2264 default:
2265 {
2266 pixel=Sc;
2267 break;
2268 }
2269 }
2270 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002271 }
cristyed231572011-07-14 02:18:59 +00002272 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002273 channels=GetPixelChannels(composite_image);
2274 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002275 p=pixels;
cristyed231572011-07-14 02:18:59 +00002276 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002277 }
2278 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2279 status=MagickFalse;
2280 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2281 {
2282 MagickBooleanType
2283 proceed;
2284
2285#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002286 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002287#endif
2288 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2289 image->rows);
2290 if (proceed == MagickFalse)
2291 status=MagickFalse;
2292 }
2293 }
2294 composite_view=DestroyCacheView(composite_view);
2295 image_view=DestroyCacheView(image_view);
2296 if (destination_image != (Image * ) NULL)
2297 destination_image=DestroyImage(destination_image);
2298 return(status);
2299}
2300
2301/*
2302%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2303% %
2304% %
2305% %
2306% T e x t u r e I m a g e %
2307% %
2308% %
2309% %
2310%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2311%
2312% TextureImage() repeatedly tiles the texture image across and down the image
2313% canvas.
2314%
2315% The format of the TextureImage method is:
2316%
cristy30d8c942012-02-07 13:44:59 +00002317% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002318% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002319%
2320% A description of each parameter follows:
2321%
2322% o image: the image.
2323%
cristye6178502011-12-23 17:02:29 +00002324% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002325%
2326*/
cristy30d8c942012-02-07 13:44:59 +00002327MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2328 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002329{
2330#define TextureImageTag "Texture/Image"
2331
2332 CacheView
2333 *image_view,
2334 *texture_view;
2335
cristy30d8c942012-02-07 13:44:59 +00002336 Image
2337 *texture_image;
2338
cristy4c08aed2011-07-01 19:47:50 +00002339 MagickBooleanType
2340 status;
2341
2342 ssize_t
2343 y;
2344
2345 assert(image != (Image *) NULL);
2346 if (image->debug != MagickFalse)
2347 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2348 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002349 if (texture == (const Image *) NULL)
2350 return(MagickFalse);
2351 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2352 return(MagickFalse);
2353 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002354 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002355 return(MagickFalse);
cristy387430f2012-02-07 13:09:46 +00002356 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2357 exception);
cristy4c08aed2011-07-01 19:47:50 +00002358 status=MagickTrue;
2359 if ((image->compose != CopyCompositeOp) &&
2360 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002361 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002362 {
2363 /*
2364 Tile texture onto the image background.
2365 */
2366#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002367 #pragma omp parallel for schedule(static) shared(status) \
cristyddacdd12012-05-07 23:08:14 +00002368 dynamic_number_threads(image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002369#endif
cristye6178502011-12-23 17:02:29 +00002370 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002371 {
2372 register ssize_t
2373 x;
2374
2375 if (status == MagickFalse)
2376 continue;
cristye6178502011-12-23 17:02:29 +00002377 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002378 {
2379 MagickBooleanType
2380 thread_status;
2381
cristyfeb3e962012-03-29 17:25:55 +00002382 thread_status=CompositeImage(image,texture_image,image->compose,
2383 MagickFalse,x+texture_image->tile_offset.x,y+
2384 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002385 if (thread_status == MagickFalse)
2386 {
2387 status=thread_status;
2388 break;
2389 }
2390 }
2391 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2392 {
2393 MagickBooleanType
2394 proceed;
2395
2396#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002397 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002398#endif
2399 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2400 y,image->rows);
2401 if (proceed == MagickFalse)
2402 status=MagickFalse;
2403 }
2404 }
2405 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2406 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002407 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002408 return(status);
2409 }
2410 /*
2411 Tile texture onto the image background (optimized).
2412 */
2413 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +00002414 texture_view=AcquireVirtualCacheView(texture_image,exception);
2415 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00002416#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002417 #pragma omp parallel for schedule(static) shared(status) \
cristyddacdd12012-05-07 23:08:14 +00002418 dynamic_number_threads(image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002419#endif
2420 for (y=0; y < (ssize_t) image->rows; y++)
2421 {
2422 MagickBooleanType
2423 sync;
2424
2425 register const Quantum
2426 *p,
2427 *pixels;
2428
2429 register ssize_t
2430 x;
2431
2432 register Quantum
2433 *q;
2434
2435 size_t
2436 width;
2437
2438 if (status == MagickFalse)
2439 continue;
cristye6178502011-12-23 17:02:29 +00002440 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2441 (y+texture_image->tile_offset.y) % texture_image->rows,
2442 texture_image->columns,1,exception);
2443 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002444 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2445 {
2446 status=MagickFalse;
2447 continue;
2448 }
cristye6178502011-12-23 17:02:29 +00002449 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002450 {
2451 register ssize_t
cristye6178502011-12-23 17:02:29 +00002452 j;
cristy4c08aed2011-07-01 19:47:50 +00002453
2454 p=pixels;
cristye6178502011-12-23 17:02:29 +00002455 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002456 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2457 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002458 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002459 {
cristye6178502011-12-23 17:02:29 +00002460 register ssize_t
2461 i;
2462
cristy10a6c612012-01-29 21:41:05 +00002463 if (GetPixelMask(image,p) != 0)
2464 {
2465 p+=GetPixelChannels(texture_image);
2466 q+=GetPixelChannels(image);
2467 continue;
2468 }
cristye6178502011-12-23 17:02:29 +00002469 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2470 {
2471 PixelChannel
2472 channel;
2473
2474 PixelTrait
2475 texture_traits,
2476 traits;
2477
2478 channel=GetPixelChannelMapChannel(texture_image,i);
2479 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2480 traits=GetPixelChannelMapTraits(image,channel);
2481 if ((traits == UndefinedPixelTrait) ||
2482 (texture_traits == UndefinedPixelTrait))
2483 continue;
2484 SetPixelChannel(image,channel,p[i],q);
2485 }
2486 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002487 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002488 }
2489 }
2490 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2491 if (sync == MagickFalse)
2492 status=MagickFalse;
2493 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2494 {
2495 MagickBooleanType
2496 proceed;
2497
2498#if defined(MAGICKCORE_OPENMP_SUPPORT)
2499 #pragma omp critical (MagickCore_TextureImage)
2500#endif
2501 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2502 image->rows);
2503 if (proceed == MagickFalse)
2504 status=MagickFalse;
2505 }
2506 }
2507 texture_view=DestroyCacheView(texture_view);
2508 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002509 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002510 return(status);
2511}