blob: 28674345c13518b0877daf47ee2e14a7ce55927c [file] [log] [blame]
cristy0b3cd892012-08-01 23:13:50 +00001/*
cristy4c08aed2011-07-01 19:47:50 +00002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7% C O O MM MM P P O O SS I T E %
8% C O O M M M PPPP O O SSS I T EEE %
9% C O O M M P O O SS I T E %
10% CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
11% %
12% %
13% MagickCore Image Composite Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy4c08aed2011-07-01 19:47:50 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/cache.h"
cristy52010022011-10-21 18:07:37 +000046#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/cache-view.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/colorspace-private.h"
53#include "MagickCore/composite.h"
54#include "MagickCore/composite-private.h"
55#include "MagickCore/constitute.h"
56#include "MagickCore/draw.h"
57#include "MagickCore/fx.h"
58#include "MagickCore/gem.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/image.h"
61#include "MagickCore/image-private.h"
62#include "MagickCore/list.h"
63#include "MagickCore/log.h"
64#include "MagickCore/monitor.h"
65#include "MagickCore/monitor-private.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/option.h"
68#include "MagickCore/pixel-accessor.h"
69#include "MagickCore/property.h"
70#include "MagickCore/quantum.h"
71#include "MagickCore/resample.h"
72#include "MagickCore/resource_.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/thread-private.h"
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);
cristya19f1d72012-08-07 18:24:38 +0000204 delta=(double) 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 {
cristya19f1d72012-08-07 18:24:38 +0000392 double
cristye4a40472011-12-22 02:56:19 +0000393 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 */
cristya19f1d72012-08-07 18:24:38 +0000503 Sc=(double) GetPixelChannel(composite_image,channel,p);
504 Dc=(double) 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,
cristya865ccd2012-07-28 00:33:10 +0000536 const Image *composite,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
cristya865ccd2012-07-28 00:33:10 +0000550 *composite_image,
cristy4c08aed2011-07-01 19:47:50 +0000551 *destination_image;
552
553 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000554 status;
555
556 MagickOffsetType
557 progress;
558
cristya19f1d72012-08-07 18:24:38 +0000559 double
cristy4c08aed2011-07-01 19:47:50 +0000560 amount,
561 destination_dissolve,
562 midpoint,
563 percent_brightness,
564 percent_saturation,
565 source_dissolve,
566 threshold;
567
568 MagickStatusType
569 flags;
570
cristyd197cbb2012-01-13 02:14:12 +0000571 ssize_t
572 y;
573
cristy4c08aed2011-07-01 19:47:50 +0000574 assert(image != (Image *) NULL);
575 assert(image->signature == MagickSignature);
576 if (image->debug != MagickFalse)
577 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristya865ccd2012-07-28 00:33:10 +0000578 assert(composite!= (Image *) NULL);
579 assert(composite->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000580 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000581 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +0000582 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
583 if (composite_image == (const Image *) NULL)
584 return(MagickFalse);
cristya4a22a22012-08-01 23:16:38 +0000585 (void) SetImageColorspace(composite_image,image->colorspace,exception);
cristye4a40472011-12-22 02:56:19 +0000586 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
587 {
cristyfeb3e962012-03-29 17:25:55 +0000588 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
589 y_offset,exception);
cristya865ccd2012-07-28 00:33:10 +0000590 composite_image=DestroyImage(composite_image);
cristye4a40472011-12-22 02:56:19 +0000591 return(status);
592 }
cristy4c08aed2011-07-01 19:47:50 +0000593 destination_image=(Image *) NULL;
594 amount=0.5;
595 destination_dissolve=1.0;
cristy4c08aed2011-07-01 19:47:50 +0000596 percent_brightness=100.0;
597 percent_saturation=100.0;
598 source_dissolve=1.0;
599 threshold=0.05f;
600 switch (compose)
601 {
cristy4c08aed2011-07-01 19:47:50 +0000602 case CopyCompositeOp:
603 {
604 if ((x_offset < 0) || (y_offset < 0))
605 break;
606 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
607 break;
608 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
609 break;
610 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +0000611 composite_view=AcquireVirtualCacheView(composite_image,exception);
612 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000613#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000614 #pragma omp parallel for schedule(static,4) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000615 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000616#endif
617 for (y=0; y < (ssize_t) composite_image->rows; y++)
618 {
619 MagickBooleanType
620 sync;
621
622 register const Quantum
623 *p;
624
625 register Quantum
626 *q;
627
628 register ssize_t
629 x;
630
631 if (status == MagickFalse)
632 continue;
633 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
634 1,exception);
635 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
636 composite_image->columns,1,exception);
637 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
638 {
639 status=MagickFalse;
640 continue;
641 }
642 for (x=0; x < (ssize_t) composite_image->columns; x++)
643 {
cristybdecccc2011-12-24 22:52:16 +0000644 register ssize_t
645 i;
646
cristy665e18f2012-05-17 12:39:54 +0000647 if (GetPixelMask(composite_image,p) != 0)
cristy10a6c612012-01-29 21:41:05 +0000648 {
649 p+=GetPixelChannels(composite_image);
650 q+=GetPixelChannels(image);
651 continue;
652 }
cristybdecccc2011-12-24 22:52:16 +0000653 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
654 {
655 PixelChannel
656 channel;
657
658 PixelTrait
659 composite_traits,
660 traits;
661
662 channel=GetPixelChannelMapChannel(composite_image,i);
663 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
664 traits=GetPixelChannelMapTraits(image,channel);
665 if ((traits == UndefinedPixelTrait) ||
666 (composite_traits == UndefinedPixelTrait))
667 continue;
668 SetPixelChannel(image,channel,p[i],q);
669 }
cristyed231572011-07-14 02:18:59 +0000670 p+=GetPixelChannels(composite_image);
671 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000672 }
673 sync=SyncCacheViewAuthenticPixels(image_view,exception);
674 if (sync == MagickFalse)
675 status=MagickFalse;
676 if (image->progress_monitor != (MagickProgressMonitor) NULL)
677 {
678 MagickBooleanType
679 proceed;
680
681#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000682 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000683#endif
684 proceed=SetImageProgress(image,CompositeImageTag,
685 (MagickOffsetType) y,image->rows);
686 if (proceed == MagickFalse)
687 status=MagickFalse;
688 }
689 }
690 composite_view=DestroyCacheView(composite_view);
691 image_view=DestroyCacheView(image_view);
cristy44886b92012-07-28 13:07:09 +0000692 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000693 return(status);
694 }
cristye4a40472011-12-22 02:56:19 +0000695 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000696 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000697 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000698 {
699 /*
700 Modify destination outside the overlaid region and require an alpha
701 channel to exist, to add transparency.
702 */
703 if (image->matte == MagickFalse)
cristy42c41de2012-05-05 18:36:31 +0000704 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000705 break;
706 }
707 case BlurCompositeOp:
708 {
709 CacheView
710 *composite_view,
711 *destination_view;
712
cristyfeb3e962012-03-29 17:25:55 +0000713 const char
714 *value;
715
cristy4c08aed2011-07-01 19:47:50 +0000716 PixelInfo
717 pixel;
718
cristya19f1d72012-08-07 18:24:38 +0000719 double
cristy4c08aed2011-07-01 19:47:50 +0000720 angle_range,
721 angle_start,
722 height,
723 width;
724
725 ResampleFilter
726 *resample_filter;
727
728 SegmentInfo
729 blur;
730
731 /*
anthony9cb63cc2012-04-25 06:10:49 +0000732 Blur Image by resampling.
733
cristy4c08aed2011-07-01 19:47:50 +0000734 Blur Image dictated by an overlay gradient map: X = red_channel;
735 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
736 */
737 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000738 exception);
cristy4c08aed2011-07-01 19:47:50 +0000739 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000740 {
741 composite_image=DestroyImage(composite_image);
742 return(MagickFalse);
743 }
cristy4c08aed2011-07-01 19:47:50 +0000744 /*
anthony9cb63cc2012-04-25 06:10:49 +0000745 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000746 */
747 SetGeometryInfo(&geometry_info);
748 flags=NoValue;
749 value=GetImageArtifact(composite_image,"compose:args");
750 if (value != (char *) NULL)
751 flags=ParseGeometry(value,&geometry_info);
anthonyd2923912012-04-23 13:06:53 +0000752 if ((flags & WidthValue) == 0 ) {
753 (void) ThrowMagickException(exception,GetMagickModule(),
754 OptionWarning,"InvalidSetting","'%s' '%s'",
anthony9cb63cc2012-04-25 06:10:49 +0000755 "compose:args",value);
cristy44886b92012-07-28 13:07:09 +0000756 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000757 destination_image=DestroyImage(destination_image);
758 return(MagickFalse);
759 }
anthony9cb63cc2012-04-25 06:10:49 +0000760 /*
761 Users input sigma now needs to be converted to the EWA ellipse size.
762 The filter defaults to a sigma of 0.5 so to make this match the
763 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000764 */
765 width=height=geometry_info.rho*2.0;
766 if ((flags & HeightValue) != 0 )
767 height=geometry_info.sigma*2.0;
768
anthony9cb63cc2012-04-25 06:10:49 +0000769 /* default the unrotated ellipse width and height axis vectors */
anthonyd2923912012-04-23 13:06:53 +0000770 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000771 blur.x2=0.0;
772 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000773 blur.y2=height;
774 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000775 if ((flags & XValue) != 0 )
776 {
cristya19f1d72012-08-07 18:24:38 +0000777 double
cristy4c08aed2011-07-01 19:47:50 +0000778 angle;
779
780 angle=DegreesToRadians(geometry_info.xi);
781 blur.x1=width*cos(angle);
782 blur.x2=width*sin(angle);
783 blur.y1=(-height*sin(angle));
784 blur.y2=height*cos(angle);
785 }
anthonyd2923912012-04-23 13:06:53 +0000786 /* Otherwise lets set a angle range and calculate in the loop */
787 angle_start=0.0;
788 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000789 if ((flags & YValue) != 0 )
790 {
791 angle_start=DegreesToRadians(geometry_info.xi);
792 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
793 }
794 /*
anthony9cb63cc2012-04-25 06:10:49 +0000795 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000796
anthony9cb63cc2012-04-25 06:10:49 +0000797 As the minimum ellipse radius of support*1.0 the EWA algorithm
798 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
799 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000800
anthony9cb63cc2012-04-25 06:10:49 +0000801 The solution (as well as the problem of preventing any user
802 expert filter settings, is to set our own user settings, then
803 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000804 */
cristy8a11cb12011-10-19 23:53:34 +0000805 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000806 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000807
808 /* do the variable blurring of each pixel in image */
809 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000810 composite_view=AcquireVirtualCacheView(composite_image,exception);
811 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000812 for (y=0; y < (ssize_t) composite_image->rows; y++)
813 {
814 MagickBooleanType
815 sync;
816
817 register const Quantum
818 *restrict p;
819
820 register Quantum
821 *restrict q;
822
823 register ssize_t
824 x;
825
826 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
827 continue;
828 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
829 1,exception);
830 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000831 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000832 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
833 break;
834 for (x=0; x < (ssize_t) composite_image->columns; x++)
835 {
836 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
837 {
cristyed231572011-07-14 02:18:59 +0000838 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000839 continue;
840 }
841 if (fabs(angle_range) > MagickEpsilon)
842 {
cristya19f1d72012-08-07 18:24:38 +0000843 double
cristy4c08aed2011-07-01 19:47:50 +0000844 angle;
845
846 angle=angle_start+angle_range*QuantumScale*
847 GetPixelBlue(composite_image,p);
848 blur.x1=width*cos(angle);
849 blur.x2=width*sin(angle);
850 blur.y1=(-height*sin(angle));
851 blur.y2=height*cos(angle);
852 }
anthonyd2923912012-04-23 13:06:53 +0000853#if 0
854 if ( x == 10 && y == 60 ) {
855 fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
856 blur.x1, blur.x2, blur.y1, blur.y2);
857 fprintf(stderr, "scaled by=%lf,%lf\n",
858 QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
859#endif
860 ScaleResampleFilter(resample_filter,
861 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
862 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
863 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
864 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000865 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000866 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000867 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000868 p+=GetPixelChannels(composite_image);
869 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000870 }
871 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
872 if (sync == MagickFalse)
873 break;
874 }
875 resample_filter=DestroyResampleFilter(resample_filter);
876 composite_view=DestroyCacheView(composite_view);
877 destination_view=DestroyCacheView(destination_view);
cristyf661c4d2012-07-28 12:57:17 +0000878 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000879 composite_image=destination_image;
880 break;
881 }
882 case DisplaceCompositeOp:
883 case DistortCompositeOp:
884 {
885 CacheView
886 *composite_view,
887 *destination_view,
888 *image_view;
889
cristyfeb3e962012-03-29 17:25:55 +0000890 const char
891 *value;
892
cristy4c08aed2011-07-01 19:47:50 +0000893 PixelInfo
894 pixel;
895
cristya19f1d72012-08-07 18:24:38 +0000896 double
cristy4c08aed2011-07-01 19:47:50 +0000897 horizontal_scale,
898 vertical_scale;
899
900 PointInfo
901 center,
902 offset;
903
904 /*
905 Displace/Distort based on overlay gradient map:
906 X = red_channel; Y = green_channel;
907 compose:args = x_scale[,y_scale[,center.x,center.y]]
908 */
909 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000910 exception);
cristy4c08aed2011-07-01 19:47:50 +0000911 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000912 {
913 composite_image=DestroyImage(composite_image);
914 return(MagickFalse);
915 }
cristy4c08aed2011-07-01 19:47:50 +0000916 SetGeometryInfo(&geometry_info);
917 flags=NoValue;
918 value=GetImageArtifact(composite_image,"compose:args");
919 if (value != (char *) NULL)
920 flags=ParseGeometry(value,&geometry_info);
921 if ((flags & (WidthValue|HeightValue)) == 0 )
922 {
923 if ((flags & AspectValue) == 0)
924 {
cristya19f1d72012-08-07 18:24:38 +0000925 horizontal_scale=(double) (composite_image->columns-1.0)/
cristy4c08aed2011-07-01 19:47:50 +0000926 2.0;
cristya19f1d72012-08-07 18:24:38 +0000927 vertical_scale=(double) (composite_image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000928 }
929 else
930 {
cristya19f1d72012-08-07 18:24:38 +0000931 horizontal_scale=(double) (image->columns-1.0)/2.0;
932 vertical_scale=(double) (image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000933 }
934 }
935 else
936 {
937 horizontal_scale=geometry_info.rho;
938 vertical_scale=geometry_info.sigma;
939 if ((flags & PercentValue) != 0)
940 {
941 if ((flags & AspectValue) == 0)
942 {
943 horizontal_scale*=(composite_image->columns-1.0)/200.0;
944 vertical_scale*=(composite_image->rows-1.0)/200.0;
945 }
946 else
947 {
948 horizontal_scale*=(image->columns-1.0)/200.0;
949 vertical_scale*=(image->rows-1.0)/200.0;
950 }
951 }
952 if ((flags & HeightValue) == 0)
953 vertical_scale=horizontal_scale;
954 }
955 /*
956 Determine fixed center point for absolute distortion map
957 Absolute distort ==
958 Displace offset relative to a fixed absolute point
959 Select that point according to +X+Y user inputs.
960 default = center of overlay image
961 arg flag '!' = locations/percentage relative to background image
962 */
cristya19f1d72012-08-07 18:24:38 +0000963 center.x=(double) x_offset;
964 center.y=(double) y_offset;
cristy4c08aed2011-07-01 19:47:50 +0000965 if (compose == DistortCompositeOp)
966 {
967 if ((flags & XValue) == 0)
968 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000969 center.x=(double) x_offset+(composite_image->columns-1)/
cristy4c08aed2011-07-01 19:47:50 +0000970 2.0;
971 else
cristya19f1d72012-08-07 18:24:38 +0000972 center.x=((double) image->columns-1)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000973 else
974 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000975 center.x=(double) x_offset+geometry_info.xi;
cristy4c08aed2011-07-01 19:47:50 +0000976 else
977 center.x=geometry_info.xi;
978 if ((flags & YValue) == 0)
979 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000980 center.y=(double) y_offset+(composite_image->rows-1)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000981 else
cristya19f1d72012-08-07 18:24:38 +0000982 center.y=((double) image->rows-1)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000983 else
984 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000985 center.y=(double) y_offset+geometry_info.psi;
cristy4c08aed2011-07-01 19:47:50 +0000986 else
987 center.y=geometry_info.psi;
988 }
989 /*
990 Shift the pixel offset point as defined by the provided,
991 displacement/distortion map. -- Like a lens...
992 */
cristye10859a2011-12-18 22:28:59 +0000993 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000994 image_view=AcquireVirtualCacheView(image,exception);
995 composite_view=AcquireVirtualCacheView(composite_image,exception);
996 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000997 for (y=0; y < (ssize_t) composite_image->rows; y++)
998 {
999 MagickBooleanType
1000 sync;
1001
1002 register const Quantum
1003 *restrict p;
1004
1005 register Quantum
1006 *restrict q;
1007
1008 register ssize_t
1009 x;
1010
1011 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1012 continue;
1013 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1014 1,exception);
1015 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001016 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001017 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1018 break;
1019 for (x=0; x < (ssize_t) composite_image->columns; x++)
1020 {
1021 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1022 {
cristyed231572011-07-14 02:18:59 +00001023 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001024 continue;
1025 }
1026 /*
1027 Displace the offset.
1028 */
1029 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
cristya19f1d72012-08-07 18:24:38 +00001030 (((double) QuantumRange+1.0)/2.0)))/(((double)
cristy4c08aed2011-07-01 19:47:50 +00001031 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1032 x : 0);
1033 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
cristya19f1d72012-08-07 18:24:38 +00001034 (((double) QuantumRange+1.0)/2.0)))/(((double)
cristy4c08aed2011-07-01 19:47:50 +00001035 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1036 y : 0);
1037 (void) InterpolatePixelInfo(image,image_view,
1038 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1039 &pixel,exception);
1040 /*
1041 Mask with the 'invalid pixel mask' in alpha channel.
1042 */
cristya19f1d72012-08-07 18:24:38 +00001043 pixel.alpha=(double) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001044 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001045 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001046 p+=GetPixelChannels(composite_image);
1047 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001048 }
1049 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1050 if (sync == MagickFalse)
1051 break;
1052 }
1053 destination_view=DestroyCacheView(destination_view);
1054 composite_view=DestroyCacheView(composite_view);
1055 image_view=DestroyCacheView(image_view);
cristyf661c4d2012-07-28 12:57:17 +00001056 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001057 composite_image=destination_image;
1058 break;
1059 }
1060 case DissolveCompositeOp:
1061 {
cristyfeb3e962012-03-29 17:25:55 +00001062 const char
1063 *value;
1064
cristy4c08aed2011-07-01 19:47:50 +00001065 /*
1066 Geometry arguments to dissolve factors.
1067 */
1068 value=GetImageArtifact(composite_image,"compose:args");
1069 if (value != (char *) NULL)
1070 {
1071 flags=ParseGeometry(value,&geometry_info);
1072 source_dissolve=geometry_info.rho/100.0;
1073 destination_dissolve=1.0;
1074 if ((source_dissolve-MagickEpsilon) < 0.0)
1075 source_dissolve=0.0;
1076 if ((source_dissolve+MagickEpsilon) > 1.0)
1077 {
1078 destination_dissolve=2.0-source_dissolve;
1079 source_dissolve=1.0;
1080 }
1081 if ((flags & SigmaValue) != 0)
1082 destination_dissolve=geometry_info.sigma/100.0;
1083 if ((destination_dissolve-MagickEpsilon) < 0.0)
1084 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001085 /* posible speed up? -- from IMv6 update
1086 clip_to_self=MagickFalse;
1087 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1088 {
1089 destination_dissolve=1.0;
1090 clip_to_self=MagickTrue;
1091 }
1092 */
cristy4c08aed2011-07-01 19:47:50 +00001093 }
1094 break;
1095 }
1096 case BlendCompositeOp:
1097 {
cristyfeb3e962012-03-29 17:25:55 +00001098 const char
1099 *value;
1100
cristy4c08aed2011-07-01 19:47:50 +00001101 value=GetImageArtifact(composite_image,"compose:args");
1102 if (value != (char *) NULL)
1103 {
1104 flags=ParseGeometry(value,&geometry_info);
1105 source_dissolve=geometry_info.rho/100.0;
1106 destination_dissolve=1.0-source_dissolve;
1107 if ((flags & SigmaValue) != 0)
1108 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001109 }
1110 break;
1111 }
1112 case MathematicsCompositeOp:
1113 {
cristyfeb3e962012-03-29 17:25:55 +00001114 const char
1115 *value;
1116
cristy4c08aed2011-07-01 19:47:50 +00001117 /*
1118 Just collect the values from "compose:args", setting.
1119 Unused values are set to zero automagically.
1120
1121 Arguments are normally a comma separated list, so this probably should
1122 be changed to some 'general comma list' parser, (with a minimum
1123 number of values)
1124 */
1125 SetGeometryInfo(&geometry_info);
1126 value=GetImageArtifact(composite_image,"compose:args");
1127 if (value != (char *) NULL)
1128 (void) ParseGeometry(value,&geometry_info);
1129 break;
1130 }
1131 case ModulateCompositeOp:
1132 {
cristyfeb3e962012-03-29 17:25:55 +00001133 const char
1134 *value;
1135
cristy4c08aed2011-07-01 19:47:50 +00001136 /*
1137 Determine the brightness and saturation scale.
1138 */
1139 value=GetImageArtifact(composite_image,"compose:args");
1140 if (value != (char *) NULL)
1141 {
1142 flags=ParseGeometry(value,&geometry_info);
1143 percent_brightness=geometry_info.rho;
1144 if ((flags & SigmaValue) != 0)
1145 percent_saturation=geometry_info.sigma;
1146 }
1147 break;
1148 }
1149 case ThresholdCompositeOp:
1150 {
cristyfeb3e962012-03-29 17:25:55 +00001151 const char
1152 *value;
1153
cristy4c08aed2011-07-01 19:47:50 +00001154 /*
1155 Determine the amount and threshold.
1156 */
1157 value=GetImageArtifact(composite_image,"compose:args");
1158 if (value != (char *) NULL)
1159 {
1160 flags=ParseGeometry(value,&geometry_info);
1161 amount=geometry_info.rho;
1162 threshold=geometry_info.sigma;
1163 if ((flags & SigmaValue) == 0)
1164 threshold=0.05f;
1165 }
1166 threshold*=QuantumRange;
1167 break;
1168 }
1169 default:
1170 break;
1171 }
cristy4c08aed2011-07-01 19:47:50 +00001172 /*
1173 Composite image.
1174 */
1175 status=MagickTrue;
1176 progress=0;
cristya19f1d72012-08-07 18:24:38 +00001177 midpoint=((double) QuantumRange+1.0)/2;
cristydb070952012-04-20 14:33:00 +00001178 composite_view=AcquireVirtualCacheView(composite_image,exception);
1179 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001180#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001181 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +00001182 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001183#endif
1184 for (y=0; y < (ssize_t) image->rows; y++)
1185 {
1186 const Quantum
1187 *pixels;
1188
1189 double
cristye4a40472011-12-22 02:56:19 +00001190 blue,
cristy4c08aed2011-07-01 19:47:50 +00001191 brightness,
cristye4a40472011-12-22 02:56:19 +00001192 green,
cristy4c08aed2011-07-01 19:47:50 +00001193 hue,
cristye4a40472011-12-22 02:56:19 +00001194 red,
cristy4c08aed2011-07-01 19:47:50 +00001195 saturation;
1196
cristyddeeea22012-04-12 01:33:09 +00001197 PixelInfo
1198 destination_pixel,
1199 source_pixel;
1200
cristy4c08aed2011-07-01 19:47:50 +00001201 register const Quantum
1202 *restrict p;
1203
1204 register Quantum
1205 *restrict q;
1206
1207 register ssize_t
1208 x;
1209
1210 if (status == MagickFalse)
1211 continue;
cristyfeb3e962012-03-29 17:25:55 +00001212 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001213 {
1214 if (y < y_offset)
1215 continue;
1216 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1217 continue;
1218 }
1219 /*
1220 If pixels is NULL, y is outside overlay region.
1221 */
1222 pixels=(Quantum *) NULL;
1223 p=(Quantum *) NULL;
1224 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1225 {
1226 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1227 composite_image->columns,1,exception);
1228 if (p == (const Quantum *) NULL)
1229 {
1230 status=MagickFalse;
1231 continue;
1232 }
1233 pixels=p;
1234 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001235 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001236 }
1237 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001238 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001239 {
1240 status=MagickFalse;
1241 continue;
1242 }
cristy4c08aed2011-07-01 19:47:50 +00001243 hue=0.0;
1244 saturation=0.0;
1245 brightness=0.0;
cristyddeeea22012-04-12 01:33:09 +00001246 GetPixelInfo(image,&destination_pixel);
1247 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001248 for (x=0; x < (ssize_t) image->columns; x++)
1249 {
cristya19f1d72012-08-07 18:24:38 +00001250 double
cristye4a40472011-12-22 02:56:19 +00001251 alpha,
1252 Da,
1253 Dc,
1254 Dca,
1255 gamma,
1256 Sa,
1257 Sc,
1258 Sca;
1259
1260 register ssize_t
1261 i;
1262
cristy564a5692012-01-20 23:56:26 +00001263 size_t
1264 channels;
1265
cristyfeb3e962012-03-29 17:25:55 +00001266 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001267 {
1268 if (x < x_offset)
1269 {
cristyed231572011-07-14 02:18:59 +00001270 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001271 continue;
1272 }
1273 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1274 break;
1275 }
cristye4a40472011-12-22 02:56:19 +00001276 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1277 ((x-x_offset) >= (ssize_t) composite_image->columns))
1278 {
1279 Quantum
1280 source[MaxPixelChannels];
1281
1282 /*
1283 Virtual composite:
1284 Sc: source color.
1285 Dc: destination color.
1286 */
1287 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1288 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001289 if (GetPixelMask(image,q) != 0)
1290 {
1291 q+=GetPixelChannels(image);
1292 continue;
1293 }
cristye4a40472011-12-22 02:56:19 +00001294 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1295 {
cristya19f1d72012-08-07 18:24:38 +00001296 double
cristye4a40472011-12-22 02:56:19 +00001297 pixel;
1298
1299 PixelChannel
1300 channel;
1301
1302 PixelTrait
1303 composite_traits,
1304 traits;
1305
1306 channel=GetPixelChannelMapChannel(image,i);
1307 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +00001308 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001309 if ((traits == UndefinedPixelTrait) ||
1310 (composite_traits == UndefinedPixelTrait))
1311 continue;
1312 switch (compose)
1313 {
cristyc8d63672012-01-11 13:03:13 +00001314 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001315 case ChangeMaskCompositeOp:
1316 case CopyAlphaCompositeOp:
1317 case DstAtopCompositeOp:
1318 case DstInCompositeOp:
1319 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001320 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001321 case OutCompositeOp:
1322 case SrcInCompositeOp:
1323 case SrcOutCompositeOp:
1324 {
cristya19f1d72012-08-07 18:24:38 +00001325 pixel=(double) q[i];
cristye4a40472011-12-22 02:56:19 +00001326 if (channel == AlphaPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001327 pixel=(double) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001328 break;
1329 }
1330 case ClearCompositeOp:
1331 case CopyCompositeOp:
1332 case ReplaceCompositeOp:
1333 case SrcCompositeOp:
1334 {
1335 if (channel == AlphaPixelChannel)
1336 {
cristya19f1d72012-08-07 18:24:38 +00001337 pixel=(double) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001338 break;
1339 }
1340 pixel=0.0;
1341 break;
1342 }
cristy99abff32011-12-24 20:45:16 +00001343 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001344 case DissolveCompositeOp:
1345 {
1346 if (channel == AlphaPixelChannel)
1347 {
1348 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1349 source);
1350 break;
1351 }
cristya19f1d72012-08-07 18:24:38 +00001352 pixel=(double) source[channel];
cristye4a40472011-12-22 02:56:19 +00001353 break;
1354 }
1355 default:
1356 {
cristya19f1d72012-08-07 18:24:38 +00001357 pixel=(double) source[channel];
cristye4a40472011-12-22 02:56:19 +00001358 break;
1359 }
1360 }
1361 q[i]=ClampToQuantum(pixel);
1362 }
1363 q+=GetPixelChannels(image);
1364 continue;
1365 }
1366 /*
1367 Authentic composite:
1368 Sa: normalized source alpha.
1369 Da: normalized destination alpha.
1370 */
1371 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1372 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001373 switch (compose)
1374 {
cristye4a40472011-12-22 02:56:19 +00001375 case BumpmapCompositeOp:
1376 {
1377 alpha=GetPixelIntensity(composite_image,p)*Sa;
1378 break;
1379 }
cristycdc168f2011-12-21 15:24:39 +00001380 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001381 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001382 case DifferenceCompositeOp:
1383 case DivideDstCompositeOp:
1384 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001385 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001386 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001387 case LinearBurnCompositeOp:
1388 case LinearDodgeCompositeOp:
1389 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001390 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001391 case MinusDstCompositeOp:
1392 case MinusSrcCompositeOp:
1393 case ModulusAddCompositeOp:
1394 case ModulusSubtractCompositeOp:
1395 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001396 case OverlayCompositeOp:
1397 case PegtopLightCompositeOp:
1398 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001399 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001400 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001401 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001402 {
1403 alpha=RoundToUnity(Sa+Da-Sa*Da);
1404 break;
1405 }
1406 case DarkenCompositeOp:
1407 case DstAtopCompositeOp:
1408 case DstInCompositeOp:
1409 case InCompositeOp:
1410 case LightenCompositeOp:
1411 case SrcInCompositeOp:
1412 {
1413 alpha=Sa*Da;
1414 break;
1415 }
1416 case DissolveCompositeOp:
1417 {
1418 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1419 Sa+destination_dissolve*Da;
1420 break;
1421 }
1422 case DstOverCompositeOp:
1423 {
1424 alpha=Da*(-Sa)+Da+Sa;
1425 break;
1426 }
1427 case DstOutCompositeOp:
1428 {
1429 alpha=Da*(1.0-Sa);
1430 break;
1431 }
1432 case OutCompositeOp:
1433 case SrcOutCompositeOp:
1434 {
1435 alpha=Sa*(1.0-Da);
1436 break;
1437 }
1438 case OverCompositeOp:
1439 case SrcOverCompositeOp:
1440 {
1441 alpha=Sa*(-Da)+Sa+Da;
1442 break;
1443 }
cristy99abff32011-12-24 20:45:16 +00001444 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001445 case PlusCompositeOp:
1446 {
1447 alpha=RoundToUnity(Sa+Da);
1448 break;
1449 }
cristy4c08aed2011-07-01 19:47:50 +00001450 case XorCompositeOp:
1451 {
cristye4a40472011-12-22 02:56:19 +00001452 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001453 break;
1454 }
1455 default:
cristye4a40472011-12-22 02:56:19 +00001456 {
1457 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001458 break;
cristye4a40472011-12-22 02:56:19 +00001459 }
cristy4c08aed2011-07-01 19:47:50 +00001460 }
cristy10a6c612012-01-29 21:41:05 +00001461 if (GetPixelMask(image,p) != 0)
1462 {
1463 p+=GetPixelChannels(composite_image);
1464 q+=GetPixelChannels(image);
1465 continue;
1466 }
cristy9d3d2792012-04-14 15:15:19 +00001467 switch (compose)
1468 {
1469 case ColorizeCompositeOp:
1470 case HueCompositeOp:
1471 case LuminizeCompositeOp:
1472 case ModulateCompositeOp:
1473 case SaturateCompositeOp:
1474 {
1475 GetPixelInfoPixel(composite_image,p,&source_pixel);
1476 GetPixelInfoPixel(image,q,&destination_pixel);
1477 break;
1478 }
1479 default:
1480 break;
1481 }
cristye4a40472011-12-22 02:56:19 +00001482 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1483 {
cristy564a5692012-01-20 23:56:26 +00001484 double
1485 sans;
1486
cristya19f1d72012-08-07 18:24:38 +00001487 double
cristye4a40472011-12-22 02:56:19 +00001488 pixel;
cristye10859a2011-12-18 22:28:59 +00001489
cristye4a40472011-12-22 02:56:19 +00001490 PixelChannel
1491 channel;
cristye10859a2011-12-18 22:28:59 +00001492
cristye4a40472011-12-22 02:56:19 +00001493 PixelTrait
1494 composite_traits,
1495 traits;
1496
1497 channel=GetPixelChannelMapChannel(image,i);
1498 traits=GetPixelChannelMapTraits(image,channel);
1499 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001500 if (traits == UndefinedPixelTrait)
1501 continue;
cristya7b07912012-01-11 20:01:32 +00001502 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001503 (composite_traits == UndefinedPixelTrait))
1504 continue;
1505 /*
1506 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001507 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001508 */
cristya19f1d72012-08-07 18:24:38 +00001509 Sc=(double) GetPixelChannel(composite_image,channel,p);
1510 Dc=(double) q[i];
cristye4a40472011-12-22 02:56:19 +00001511 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001512 {
cristye4a40472011-12-22 02:56:19 +00001513 if (channel != AlphaPixelChannel)
1514 {
1515 /*
1516 Copy channel.
1517 */
1518 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001519 continue;
cristye10859a2011-12-18 22:28:59 +00001520 }
cristye4a40472011-12-22 02:56:19 +00001521 /*
1522 Set alpha channel.
1523 */
cristye10859a2011-12-18 22:28:59 +00001524 switch (compose)
1525 {
cristyc8d63672012-01-11 13:03:13 +00001526 case AlphaCompositeOp:
1527 {
cristya7b07912012-01-11 20:01:32 +00001528 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001529 break;
1530 }
cristye4a40472011-12-22 02:56:19 +00001531 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001532 case CopyBlackCompositeOp:
1533 case CopyBlueCompositeOp:
1534 case CopyCyanCompositeOp:
1535 case CopyGreenCompositeOp:
1536 case CopyMagentaCompositeOp:
1537 case CopyRedCompositeOp:
1538 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001539 case SrcAtopCompositeOp:
1540 case DstCompositeOp:
1541 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001542 {
cristye4a40472011-12-22 02:56:19 +00001543 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001544 break;
1545 }
cristye10859a2011-12-18 22:28:59 +00001546 case ChangeMaskCompositeOp:
1547 {
cristye4a40472011-12-22 02:56:19 +00001548 MagickBooleanType
1549 equivalent;
1550
cristya19f1d72012-08-07 18:24:38 +00001551 if (Da > ((double) QuantumRange/2.0))
cristy99abff32011-12-24 20:45:16 +00001552 {
cristya19f1d72012-08-07 18:24:38 +00001553 pixel=(double) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001554 break;
1555 }
cristye4a40472011-12-22 02:56:19 +00001556 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001557 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001558 {
cristya19f1d72012-08-07 18:24:38 +00001559 pixel=(double) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001560 break;
1561 }
cristya19f1d72012-08-07 18:24:38 +00001562 pixel=(double) OpaqueAlpha;
cristye4a40472011-12-22 02:56:19 +00001563 break;
1564 }
cristy99abff32011-12-24 20:45:16 +00001565 case ClearCompositeOp:
1566 {
cristya19f1d72012-08-07 18:24:38 +00001567 pixel=(double) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001568 break;
1569 }
1570 case ColorizeCompositeOp:
1571 case HueCompositeOp:
1572 case LuminizeCompositeOp:
1573 case SaturateCompositeOp:
1574 {
1575 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1576 {
1577 pixel=QuantumRange*Da;
1578 break;
1579 }
1580 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1581 {
1582 pixel=QuantumRange*Sa;
1583 break;
1584 }
1585 if (Sa < Da)
1586 {
1587 pixel=QuantumRange*Da;
1588 break;
1589 }
1590 pixel=QuantumRange*Sa;
1591 break;
1592 }
cristy99abff32011-12-24 20:45:16 +00001593 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001594 {
1595 pixel=QuantumRange*Sa;
1596 if (composite_image->matte == MagickFalse)
1597 pixel=GetPixelIntensity(composite_image,p);
1598 break;
1599 }
1600 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001601 case DisplaceCompositeOp:
1602 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001603 case DstAtopCompositeOp:
1604 case ReplaceCompositeOp:
1605 case SrcCompositeOp:
1606 {
1607 pixel=QuantumRange*Sa;
1608 break;
1609 }
1610 case DarkenIntensityCompositeOp:
1611 {
cristy99abff32011-12-24 20:45:16 +00001612 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1613 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001614 break;
1615 }
cristy98621462011-12-31 22:31:11 +00001616 case IntensityCompositeOp:
1617 {
cristyf13c5942012-08-08 23:50:11 +00001618 pixel=GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001619 break;
1620 }
cristye4a40472011-12-22 02:56:19 +00001621 case LightenIntensityCompositeOp:
1622 {
1623 pixel=Sa*GetPixelIntensity(composite_image,p) >
1624 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001625 break;
1626 }
cristy99abff32011-12-24 20:45:16 +00001627 case ModulateCompositeOp:
1628 {
1629 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1630 {
1631 pixel=QuantumRange*Da;
1632 break;
1633 }
1634 pixel=QuantumRange*Da;
1635 break;
1636 }
cristye10859a2011-12-18 22:28:59 +00001637 default:
1638 {
cristye4a40472011-12-22 02:56:19 +00001639 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001640 break;
1641 }
1642 }
cristye4a40472011-12-22 02:56:19 +00001643 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001644 continue;
1645 }
1646 /*
cristy99abff32011-12-24 20:45:16 +00001647 Porter-Duff compositions:
1648 Sca: source normalized color multiplied by alpha.
1649 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001650 */
cristy99abff32011-12-24 20:45:16 +00001651 Sca=QuantumScale*Sa*Sc;
1652 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001653 switch (compose)
1654 {
cristye10859a2011-12-18 22:28:59 +00001655 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001656 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001657 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001658 {
cristy99abff32011-12-24 20:45:16 +00001659 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001660 break;
1661 }
1662 default:
1663 break;
1664 }
cristyc58380a2012-06-03 15:12:30 +00001665 gamma=MagickEpsilonReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001666 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001667 switch (compose)
1668 {
cristya7b07912012-01-11 20:01:32 +00001669 case AlphaCompositeOp:
1670 {
1671 pixel=QuantumRange*Sa;
1672 break;
1673 }
cristye4a40472011-12-22 02:56:19 +00001674 case AtopCompositeOp:
1675 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001676 {
cristye4a40472011-12-22 02:56:19 +00001677 pixel=Sc*Sa+Dc*(1.0-Sa);
1678 break;
cristye10859a2011-12-18 22:28:59 +00001679 }
cristye4a40472011-12-22 02:56:19 +00001680 case BlendCompositeOp:
1681 {
1682 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1683 break;
1684 }
1685 case BlurCompositeOp:
1686 case DisplaceCompositeOp:
1687 case DistortCompositeOp:
1688 case CopyCompositeOp:
1689 case ReplaceCompositeOp:
1690 case SrcCompositeOp:
1691 {
1692 pixel=Sc;
1693 break;
1694 }
1695 case BumpmapCompositeOp:
1696 {
cristy99abff32011-12-24 20:45:16 +00001697 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001698 {
1699 pixel=Dc;
1700 break;
1701 }
1702 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1703 break;
1704 }
cristy99abff32011-12-24 20:45:16 +00001705 case ChangeMaskCompositeOp:
1706 {
1707 pixel=Dc;
1708 break;
1709 }
1710 case ClearCompositeOp:
1711 {
1712 pixel=0.0;
1713 break;
1714 }
cristye4a40472011-12-22 02:56:19 +00001715 case ColorBurnCompositeOp:
1716 {
1717 /*
1718 Refer to the March 2009 SVG specification.
1719 */
1720 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1721 {
cristy99abff32011-12-24 20:45:16 +00001722 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001723 break;
1724 }
1725 if (Sca < MagickEpsilon)
1726 {
cristy99abff32011-12-24 20:45:16 +00001727 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001728 break;
1729 }
cristy99abff32011-12-24 20:45:16 +00001730 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1731 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001732 break;
1733 }
1734 case ColorDodgeCompositeOp:
1735 {
1736 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1737 {
cristy99abff32011-12-24 20:45:16 +00001738 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001739 break;
1740 }
1741 if (fabs(Sca-Sa) < MagickEpsilon)
1742 {
cristy99abff32011-12-24 20:45:16 +00001743 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001744 break;
1745 }
cristy99abff32011-12-24 20:45:16 +00001746 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001747 (1.0-Sa));
1748 break;
1749 }
1750 case ColorizeCompositeOp:
1751 {
cristy99abff32011-12-24 20:45:16 +00001752 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001753 {
1754 pixel=Dc;
1755 break;
1756 }
cristy99abff32011-12-24 20:45:16 +00001757 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001758 {
1759 pixel=Sc;
1760 break;
1761 }
cristyddeeea22012-04-12 01:33:09 +00001762 CompositeHSB(destination_pixel.red,destination_pixel.green,
1763 destination_pixel.blue,&sans,&sans,&brightness);
1764 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001765 &hue,&saturation,&sans);
1766 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1767 switch (channel)
1768 {
1769 case RedPixelChannel: pixel=red; break;
1770 case GreenPixelChannel: pixel=green; break;
1771 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001772 default: pixel=Dc; break;
1773 }
1774 break;
1775 }
cristye4a40472011-12-22 02:56:19 +00001776 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001777 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001778 {
cristy24d5d722012-05-17 12:27:27 +00001779 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001780 break;
1781 }
1782 case CopyBlackCompositeOp:
1783 {
cristyd197cbb2012-01-13 02:14:12 +00001784 if (channel == BlackPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001785 pixel=(double) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001786 break;
1787 }
1788 case CopyBlueCompositeOp:
1789 case CopyYellowCompositeOp:
1790 {
cristyd197cbb2012-01-13 02:14:12 +00001791 if (channel == BluePixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001792 pixel=(double) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001793 break;
1794 }
1795 case CopyGreenCompositeOp:
1796 case CopyMagentaCompositeOp:
1797 {
cristyd197cbb2012-01-13 02:14:12 +00001798 if (channel == GreenPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001799 pixel=(double) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001800 break;
1801 }
1802 case CopyRedCompositeOp:
1803 case CopyCyanCompositeOp:
1804 {
cristyd197cbb2012-01-13 02:14:12 +00001805 if (channel == RedPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001806 pixel=(double) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001807 break;
1808 }
cristy99abff32011-12-24 20:45:16 +00001809 case DarkenCompositeOp:
1810 {
1811 /*
1812 Darken is equivalent to a 'Minimum' method
1813 OR a greyscale version of a binary 'Or'
1814 OR the 'Intersection' of pixel sets.
1815 */
1816 if (Sc < Dc)
1817 {
1818 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1819 break;
1820 }
1821 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1822 break;
1823 }
cristye4a40472011-12-22 02:56:19 +00001824 case DarkenIntensityCompositeOp:
1825 {
cristy99abff32011-12-24 20:45:16 +00001826 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1827 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001828 break;
1829 }
1830 case DifferenceCompositeOp:
1831 {
1832 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1833 break;
1834 }
1835 case DissolveCompositeOp:
1836 {
1837 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1838 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1839 break;
1840 }
1841 case DivideDstCompositeOp:
1842 {
1843 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1844 {
cristy99abff32011-12-24 20:45:16 +00001845 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001846 break;
1847 }
1848 if (fabs(Dca) < MagickEpsilon)
1849 {
cristy99abff32011-12-24 20:45:16 +00001850 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001851 break;
1852 }
cristy99abff32011-12-24 20:45:16 +00001853 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001854 break;
1855 }
1856 case DivideSrcCompositeOp:
1857 {
1858 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1859 {
cristy99abff32011-12-24 20:45:16 +00001860 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001861 break;
1862 }
1863 if (fabs(Sca) < MagickEpsilon)
1864 {
cristy99abff32011-12-24 20:45:16 +00001865 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001866 break;
1867 }
cristy99abff32011-12-24 20:45:16 +00001868 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001869 break;
1870 }
1871 case DstAtopCompositeOp:
1872 {
1873 pixel=Dc*Da+Sc*(1.0-Da);
1874 break;
1875 }
1876 case DstCompositeOp:
1877 case NoCompositeOp:
1878 {
1879 pixel=Dc;
1880 break;
1881 }
1882 case DstInCompositeOp:
1883 {
1884 pixel=gamma*(Sa*Dc*Sa);
1885 break;
1886 }
1887 case DstOutCompositeOp:
1888 {
1889 pixel=gamma*(Da*Dc*(1.0-Sa));
1890 break;
1891 }
1892 case DstOverCompositeOp:
1893 {
1894 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1895 break;
1896 }
1897 case ExclusionCompositeOp:
1898 {
cristy99abff32011-12-24 20:45:16 +00001899 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1900 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001901 break;
1902 }
1903 case HardLightCompositeOp:
1904 {
1905 if ((2.0*Sca) < Sa)
1906 {
cristy99abff32011-12-24 20:45:16 +00001907 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001908 (1.0-Sa));
1909 break;
1910 }
cristy99abff32011-12-24 20:45:16 +00001911 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001912 Dca*(1.0-Sa));
1913 break;
1914 }
1915 case HueCompositeOp:
1916 {
cristy99abff32011-12-24 20:45:16 +00001917 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001918 {
1919 pixel=Dc;
1920 break;
1921 }
cristy99abff32011-12-24 20:45:16 +00001922 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001923 {
1924 pixel=Sc;
1925 break;
1926 }
cristyddeeea22012-04-12 01:33:09 +00001927 CompositeHSB(destination_pixel.red,destination_pixel.green,
1928 destination_pixel.blue,&hue,&saturation,&brightness);
1929 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001930 &hue,&sans,&sans);
1931 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1932 switch (channel)
1933 {
1934 case RedPixelChannel: pixel=red; break;
1935 case GreenPixelChannel: pixel=green; break;
1936 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001937 default: pixel=Dc; break;
1938 }
1939 break;
1940 }
1941 case InCompositeOp:
1942 case SrcInCompositeOp:
1943 {
1944 pixel=gamma*(Da*Sc*Da);
1945 break;
1946 }
cristy99abff32011-12-24 20:45:16 +00001947 case LinearBurnCompositeOp:
1948 {
1949 /*
1950 LinearBurn: as defined by Abode Photoshop, according to
1951 http://www.simplefilter.de/en/basics/mixmods.html is:
1952
1953 f(Sc,Dc) = Sc + Dc - 1
1954 */
1955 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1956 break;
1957 }
1958 case LinearDodgeCompositeOp:
1959 {
1960 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1961 break;
1962 }
1963 case LinearLightCompositeOp:
1964 {
1965 /*
1966 LinearLight: as defined by Abode Photoshop, according to
1967 http://www.simplefilter.de/en/basics/mixmods.html is:
1968
1969 f(Sc,Dc) = Dc + 2*Sc - 1
1970 */
1971 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1972 break;
1973 }
1974 case LightenCompositeOp:
1975 {
1976 if (Sc > Dc)
1977 {
cristy24d5d722012-05-17 12:27:27 +00001978 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00001979 break;
1980 }
cristy24d5d722012-05-17 12:27:27 +00001981 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00001982 break;
1983 }
cristye4a40472011-12-22 02:56:19 +00001984 case LightenIntensityCompositeOp:
1985 {
1986 /*
1987 Lighten is equivalent to a 'Maximum' method
1988 OR a greyscale version of a binary 'And'
1989 OR the 'Union' of pixel sets.
1990 */
1991 pixel=Sa*GetPixelIntensity(composite_image,p) >
1992 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1993 break;
1994 }
cristye4a40472011-12-22 02:56:19 +00001995 case LuminizeCompositeOp:
1996 {
cristy99abff32011-12-24 20:45:16 +00001997 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001998 {
1999 pixel=Dc;
2000 break;
2001 }
cristy99abff32011-12-24 20:45:16 +00002002 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002003 {
2004 pixel=Sc;
2005 break;
2006 }
cristyddeeea22012-04-12 01:33:09 +00002007 CompositeHSB(destination_pixel.red,destination_pixel.green,
2008 destination_pixel.blue,&hue,&saturation,&brightness);
2009 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00002010 &sans,&sans,&brightness);
2011 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2012 switch (channel)
2013 {
2014 case RedPixelChannel: pixel=red; break;
2015 case GreenPixelChannel: pixel=green; break;
2016 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002017 default: pixel=Dc; break;
2018 }
2019 break;
2020 }
2021 case MathematicsCompositeOp:
2022 {
2023 /*
2024 'Mathematics' a free form user control mathematical composition
2025 is defined as...
2026
2027 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2028
2029 Where the arguments A,B,C,D are (currently) passed to composite
2030 as a command separated 'geometry' string in "compose:args" image
2031 artifact.
2032
2033 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2034
2035 Applying the SVG transparency formula (see above), we get...
2036
2037 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2038
2039 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2040 Dca*(1.0-Sa)
2041 */
2042 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2043 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2044 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2045 break;
2046 }
2047 case MinusDstCompositeOp:
2048 {
2049 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2050 break;
2051 }
2052 case MinusSrcCompositeOp:
2053 {
2054 /*
2055 Minus source from destination.
2056
2057 f(Sc,Dc) = Sc - Dc
2058 */
cristy99abff32011-12-24 20:45:16 +00002059 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002060 break;
2061 }
2062 case ModulateCompositeOp:
2063 {
2064 ssize_t
2065 offset;
2066
cristy99abff32011-12-24 20:45:16 +00002067 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002068 {
2069 pixel=Dc;
2070 break;
2071 }
2072 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2073 if (offset == 0)
2074 {
2075 pixel=Dc;
2076 break;
2077 }
cristyddeeea22012-04-12 01:33:09 +00002078 CompositeHSB(destination_pixel.red,destination_pixel.green,
2079 destination_pixel.blue,&hue,&saturation,&brightness);
cristye4a40472011-12-22 02:56:19 +00002080 brightness+=(0.01*percent_brightness*offset)/midpoint;
2081 saturation*=0.01*percent_saturation;
2082 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2083 switch (channel)
2084 {
2085 case RedPixelChannel: pixel=red; break;
2086 case GreenPixelChannel: pixel=green; break;
2087 case BluePixelChannel: pixel=blue; break;
2088 default: pixel=Dc; break;
2089 }
2090 break;
2091 }
2092 case ModulusAddCompositeOp:
2093 {
2094 pixel=Sc+Dc;
2095 if (pixel > QuantumRange)
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 ModulusSubtractCompositeOp:
2101 {
cristy99abff32011-12-24 20:45:16 +00002102 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002103 if (pixel < 0.0)
2104 pixel+=(QuantumRange+1.0);
2105 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2106 break;
2107 }
2108 case MultiplyCompositeOp:
2109 {
cristy99abff32011-12-24 20:45:16 +00002110 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002111 break;
2112 }
2113 case OutCompositeOp:
2114 case SrcOutCompositeOp:
2115 {
2116 pixel=gamma*(Sa*Sc*(1.0-Da));
2117 break;
2118 }
2119 case OverCompositeOp:
2120 case SrcOverCompositeOp:
2121 {
cristy99abff32011-12-24 20:45:16 +00002122 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002123 break;
2124 }
2125 case OverlayCompositeOp:
2126 {
2127 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002128 {
2129 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2130 (1.0-Da));
2131 break;
2132 }
2133 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2134 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002135 break;
2136 }
2137 case PegtopLightCompositeOp:
2138 {
2139 /*
2140 PegTop: A Soft-Light alternative: A continuous version of the
2141 Softlight function, producing very similar results.
2142
2143 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2144
2145 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2146 */
2147 if (fabs(Da) < MagickEpsilon)
2148 {
cristy99abff32011-12-24 20:45:16 +00002149 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002150 break;
2151 }
cristy99abff32011-12-24 20:45:16 +00002152 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2153 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002154 break;
2155 }
2156 case PinLightCompositeOp:
2157 {
2158 /*
2159 PinLight: A Photoshop 7 composition method
2160 http://www.simplefilter.de/en/basics/mixmods.html
2161
2162 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2163 */
2164 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2165 {
cristy99abff32011-12-24 20:45:16 +00002166 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002167 break;
2168 }
2169 if ((Dca*Sa) > (2.0*Sca*Da))
2170 {
cristy99abff32011-12-24 20:45:16 +00002171 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002172 break;
2173 }
cristy99abff32011-12-24 20:45:16 +00002174 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002175 break;
2176 }
2177 case PlusCompositeOp:
2178 {
cristy24d5d722012-05-17 12:27:27 +00002179 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002180 break;
2181 }
2182 case SaturateCompositeOp:
2183 {
cristy99abff32011-12-24 20:45:16 +00002184 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002185 {
2186 pixel=Dc;
2187 break;
2188 }
cristy99abff32011-12-24 20:45:16 +00002189 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002190 {
2191 pixel=Sc;
2192 break;
2193 }
cristyddeeea22012-04-12 01:33:09 +00002194 CompositeHSB(destination_pixel.red,destination_pixel.green,
2195 destination_pixel.blue,&hue,&saturation,&brightness);
2196 CompositeHSB(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00002197 &sans,&saturation,&sans);
2198 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2199 switch (channel)
2200 {
2201 case RedPixelChannel: pixel=red; break;
2202 case GreenPixelChannel: pixel=green; break;
2203 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002204 default: pixel=Dc; break;
2205 }
2206 break;
2207 }
2208 case ScreenCompositeOp:
2209 {
2210 /*
2211 Screen: a negated multiply:
2212
2213 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2214 */
cristy99abff32011-12-24 20:45:16 +00002215 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002216 break;
2217 }
2218 case SoftLightCompositeOp:
2219 {
2220 /*
2221 Refer to the March 2009 SVG specification.
2222 */
2223 if ((2.0*Sca) < Sa)
2224 {
cristy99abff32011-12-24 20:45:16 +00002225 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2226 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002227 break;
2228 }
2229 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2230 {
cristy99abff32011-12-24 20:45:16 +00002231 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2232 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2233 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002234 break;
2235 }
cristy99abff32011-12-24 20:45:16 +00002236 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2237 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002238 break;
2239 }
2240 case ThresholdCompositeOp:
2241 {
cristya19f1d72012-08-07 18:24:38 +00002242 double
cristye4a40472011-12-22 02:56:19 +00002243 delta;
2244
2245 delta=Sc-Dc;
cristya19f1d72012-08-07 18:24:38 +00002246 if ((double) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002247 {
2248 pixel=gamma*Dc;
2249 break;
2250 }
2251 pixel=gamma*(Dc+delta*amount);
2252 break;
2253 }
2254 case VividLightCompositeOp:
2255 {
2256 /*
2257 VividLight: A Photoshop 7 composition method. See
2258 http://www.simplefilter.de/en/basics/mixmods.html.
2259
2260 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2261 */
2262 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2263 {
cristy99abff32011-12-24 20:45:16 +00002264 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002265 break;
2266 }
2267 if ((2.0*Sca) <= Sa)
2268 {
cristy99abff32011-12-24 20:45:16 +00002269 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2270 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002271 break;
2272 }
cristy99abff32011-12-24 20:45:16 +00002273 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2274 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002275 break;
2276 }
2277 case XorCompositeOp:
2278 {
cristy99abff32011-12-24 20:45:16 +00002279 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002280 break;
2281 }
2282 default:
2283 {
2284 pixel=Sc;
2285 break;
2286 }
2287 }
2288 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002289 }
cristyed231572011-07-14 02:18:59 +00002290 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002291 channels=GetPixelChannels(composite_image);
2292 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002293 p=pixels;
cristyed231572011-07-14 02:18:59 +00002294 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002295 }
2296 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2297 status=MagickFalse;
2298 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2299 {
2300 MagickBooleanType
2301 proceed;
2302
2303#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002304 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002305#endif
2306 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2307 image->rows);
2308 if (proceed == MagickFalse)
2309 status=MagickFalse;
2310 }
2311 }
2312 composite_view=DestroyCacheView(composite_view);
2313 image_view=DestroyCacheView(image_view);
2314 if (destination_image != (Image * ) NULL)
2315 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002316 else
2317 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00002318 return(status);
2319}
2320
2321/*
2322%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2323% %
2324% %
2325% %
2326% T e x t u r e I m a g e %
2327% %
2328% %
2329% %
2330%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2331%
2332% TextureImage() repeatedly tiles the texture image across and down the image
2333% canvas.
2334%
2335% The format of the TextureImage method is:
2336%
cristy30d8c942012-02-07 13:44:59 +00002337% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002338% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002339%
2340% A description of each parameter follows:
2341%
2342% o image: the image.
2343%
cristye6178502011-12-23 17:02:29 +00002344% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002345%
2346*/
cristy30d8c942012-02-07 13:44:59 +00002347MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2348 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002349{
2350#define TextureImageTag "Texture/Image"
2351
2352 CacheView
2353 *image_view,
2354 *texture_view;
2355
cristy30d8c942012-02-07 13:44:59 +00002356 Image
2357 *texture_image;
2358
cristy4c08aed2011-07-01 19:47:50 +00002359 MagickBooleanType
2360 status;
2361
2362 ssize_t
2363 y;
2364
2365 assert(image != (Image *) NULL);
2366 if (image->debug != MagickFalse)
2367 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2368 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002369 if (texture == (const Image *) NULL)
2370 return(MagickFalse);
2371 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2372 return(MagickFalse);
2373 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002374 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002375 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002376 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002377 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2378 exception);
cristy4c08aed2011-07-01 19:47:50 +00002379 status=MagickTrue;
2380 if ((image->compose != CopyCompositeOp) &&
2381 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002382 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002383 {
2384 /*
2385 Tile texture onto the image background.
2386 */
2387#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002388 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002389 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002390#endif
cristye6178502011-12-23 17:02:29 +00002391 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002392 {
2393 register ssize_t
2394 x;
2395
2396 if (status == MagickFalse)
2397 continue;
cristye6178502011-12-23 17:02:29 +00002398 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002399 {
2400 MagickBooleanType
2401 thread_status;
2402
cristyfeb3e962012-03-29 17:25:55 +00002403 thread_status=CompositeImage(image,texture_image,image->compose,
2404 MagickFalse,x+texture_image->tile_offset.x,y+
2405 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002406 if (thread_status == MagickFalse)
2407 {
2408 status=thread_status;
2409 break;
2410 }
2411 }
2412 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2413 {
2414 MagickBooleanType
2415 proceed;
2416
2417#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002418 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002419#endif
2420 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2421 y,image->rows);
2422 if (proceed == MagickFalse)
2423 status=MagickFalse;
2424 }
2425 }
2426 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2427 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002428 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002429 return(status);
2430 }
2431 /*
2432 Tile texture onto the image background (optimized).
2433 */
2434 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +00002435 texture_view=AcquireVirtualCacheView(texture_image,exception);
2436 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00002437#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002438 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002439 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002440#endif
2441 for (y=0; y < (ssize_t) image->rows; y++)
2442 {
2443 MagickBooleanType
2444 sync;
2445
2446 register const Quantum
2447 *p,
2448 *pixels;
2449
2450 register ssize_t
2451 x;
2452
2453 register Quantum
2454 *q;
2455
2456 size_t
2457 width;
2458
2459 if (status == MagickFalse)
2460 continue;
cristye6178502011-12-23 17:02:29 +00002461 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2462 (y+texture_image->tile_offset.y) % texture_image->rows,
2463 texture_image->columns,1,exception);
2464 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002465 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2466 {
2467 status=MagickFalse;
2468 continue;
2469 }
cristye6178502011-12-23 17:02:29 +00002470 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002471 {
2472 register ssize_t
cristye6178502011-12-23 17:02:29 +00002473 j;
cristy4c08aed2011-07-01 19:47:50 +00002474
2475 p=pixels;
cristye6178502011-12-23 17:02:29 +00002476 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002477 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2478 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002479 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002480 {
cristye6178502011-12-23 17:02:29 +00002481 register ssize_t
2482 i;
2483
cristy10a6c612012-01-29 21:41:05 +00002484 if (GetPixelMask(image,p) != 0)
2485 {
2486 p+=GetPixelChannels(texture_image);
2487 q+=GetPixelChannels(image);
2488 continue;
2489 }
cristye6178502011-12-23 17:02:29 +00002490 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2491 {
2492 PixelChannel
2493 channel;
2494
2495 PixelTrait
2496 texture_traits,
2497 traits;
2498
2499 channel=GetPixelChannelMapChannel(texture_image,i);
2500 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2501 traits=GetPixelChannelMapTraits(image,channel);
2502 if ((traits == UndefinedPixelTrait) ||
2503 (texture_traits == UndefinedPixelTrait))
2504 continue;
2505 SetPixelChannel(image,channel,p[i],q);
2506 }
2507 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002508 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002509 }
2510 }
2511 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2512 if (sync == MagickFalse)
2513 status=MagickFalse;
2514 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2515 {
2516 MagickBooleanType
2517 proceed;
2518
2519#if defined(MAGICKCORE_OPENMP_SUPPORT)
2520 #pragma omp critical (MagickCore_TextureImage)
2521#endif
2522 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2523 image->rows);
2524 if (proceed == MagickFalse)
2525 status=MagickFalse;
2526 }
2527 }
2528 texture_view=DestroyCacheView(texture_view);
2529 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002530 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002531 return(status);
2532}