blob: 6942322a078d1e26cf00e9ff99f3531c94974c03 [file] [log] [blame]
cristy0b3cd892012-08-01 23:13:50 +00001/*
cristy4c08aed2011-07-01 19:47:50 +00002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7% C O O MM MM P P O O SS I T E %
8% C O O M M M PPPP O O SSS I T EEE %
9% C O O M M P O O SS I T E %
10% CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
11% %
12% %
13% MagickCore Image Composite Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy4c08aed2011-07-01 19:47:50 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/cache.h"
cristy52010022011-10-21 18:07:37 +000046#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/cache-view.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/colorspace-private.h"
53#include "MagickCore/composite.h"
54#include "MagickCore/composite-private.h"
55#include "MagickCore/constitute.h"
56#include "MagickCore/draw.h"
57#include "MagickCore/fx.h"
58#include "MagickCore/gem.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/image.h"
61#include "MagickCore/image-private.h"
62#include "MagickCore/list.h"
63#include "MagickCore/log.h"
64#include "MagickCore/monitor.h"
65#include "MagickCore/monitor-private.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/option.h"
68#include "MagickCore/pixel-accessor.h"
69#include "MagickCore/property.h"
70#include "MagickCore/quantum.h"
71#include "MagickCore/resample.h"
72#include "MagickCore/resource_.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/thread-private.h"
cristy191c0b72012-08-12 16:29:52 +000075#include "MagickCore/threshold.h"
cristy63a81872012-03-22 15:52:52 +000076#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000077#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000078#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000079#include "MagickCore/version.h"
80
81/*
82%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83% %
84% %
85% %
cristyf4ad9df2011-07-08 16:49:03 +000086% C o m p o s i t e I m a g e %
cristy4c08aed2011-07-01 19:47:50 +000087% %
88% %
89% %
90%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91%
cristyf4ad9df2011-07-08 16:49:03 +000092% CompositeImage() returns the second image composited onto the first
cristy4c08aed2011-07-01 19:47:50 +000093% at the specified offset, using the specified composite method.
94%
cristyf4ad9df2011-07-08 16:49:03 +000095% The format of the CompositeImage method is:
cristy4c08aed2011-07-01 19:47:50 +000096%
97% MagickBooleanType CompositeImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +000098% const Image *composite_image,const CompositeOperator compose,
99% const MagickBooleanType clip_to_self,const ssize_t x_offset,
100% const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000101%
102% A description of each parameter follows:
103%
104% o image: the destination image, modified by he composition
105%
cristyfeb3e962012-03-29 17:25:55 +0000106% o composite_image: the composite (source) image.
107%
cristy4c08aed2011-07-01 19:47:50 +0000108% o compose: This operator affects how the composite is applied to
109% the image. The operators and how they are utilized are listed here
110% http://www.w3.org/TR/SVG12/#compositing.
111%
cristyfeb3e962012-03-29 17:25:55 +0000112% o clip_to_self: set to MagickTrue to limit composition to area composed.
cristy4c08aed2011-07-01 19:47:50 +0000113%
114% o x_offset: the column offset of the composited image.
115%
116% o y_offset: the row offset of the composited image.
117%
118% Extra Controls from Image meta-data in 'composite_image' (artifacts)
119%
120% o "compose:args"
121% A string containing extra numerical arguments for specific compose
122% methods, generally expressed as a 'geometry' or a comma separated list
123% of numbers.
124%
125% Compose methods needing such arguments include "BlendCompositeOp" and
126% "DisplaceCompositeOp".
127%
cristye941a752011-10-15 01:52:48 +0000128% o exception: return any errors or warnings in this structure.
129%
cristy4c08aed2011-07-01 19:47:50 +0000130*/
131
anthonyea068a52012-04-09 05:46:25 +0000132/*
133 Composition based on the SVG specification:
134
135 A Composition is defined by...
136 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
137 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
138 Y = 1 for source preserved
139 Z = 1 for destination preserved
140
141 Conversion to transparency (then optimized)
142 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
143 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
144
145 Where...
146 Sca = Sc*Sa normalized Source color divided by Source alpha
147 Dca = Dc*Da normalized Dest color divided by Dest alpha
148 Dc' = Dca'/Da' the desired color value for this channel.
149
150 Da' in in the follow formula as 'gamma' The resulting alpla value.
151
152 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
153 the following optimizations...
154 gamma = Sa+Da-Sa*Da;
155 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
156 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
157
158 The above SVG definitions also definate that Mathematical Composition
159 methods should use a 'Over' blending mode for Alpha Channel.
160 It however was not applied for composition modes of 'Plus', 'Minus',
161 the modulus versions of 'Add' and 'Subtract'.
162
163 Mathematical operator changes to be applied from IM v6.7...
164
165 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
166 'ModulusAdd' and 'ModulusSubtract' for clarity.
167
168 2) All mathematical compositions work as per the SVG specification
169 with regard to blending. This now includes 'ModulusAdd' and
170 'ModulusSubtract'.
171
172 3) When the special channel flag 'sync' (syncronize channel updates)
173 is turned off (enabled by default) then mathematical compositions are
174 only performed on the channels specified, and are applied
175 independantally of each other. In other words the mathematics is
176 performed as 'pure' mathematical operations, rather than as image
177 operations.
178*/
cristy4c08aed2011-07-01 19:47:50 +0000179
cristye4a40472011-12-22 02:56:19 +0000180static inline double MagickMin(const double x,const double y)
181{
182 if (x < y)
183 return(x);
184 return(y);
185}
cristyddeeea22012-04-12 01:33:09 +0000186
cristye4a40472011-12-22 02:56:19 +0000187static inline double MagickMax(const double x,const double y)
188{
189 if (x > y)
190 return(x);
191 return(y);
192}
193
cristyd8f16f72012-08-13 12:49:50 +0000194static inline double ConvertHueToRGB(double m1,
195 double m2,double hue)
196{
197 if (hue < 0.0)
198 hue+=1.0;
199 if (hue > 1.0)
200 hue-=1.0;
201 if ((6.0*hue) < 1.0)
202 return(m1+6.0*(m2-m1)*hue);
203 if ((2.0*hue) < 1.0)
204 return(m2);
205 if ((3.0*hue) < 2.0)
206 return(m1+6.0*(m2-m1)*(2.0/3.0-hue));
207 return(m1);
208}
209
210static void HSLComposite(const double hue,const double saturation,
211 const double lightness,double *red,double *green,double *blue)
212{
213 double
214 b,
215 g,
216 r,
217 m1,
218 m2;
219
220 /*
221 Convert HSL to RGB colorspace.
222 */
223 assert(red != (double *) NULL);
224 assert(green != (double *) NULL);
225 assert(blue != (double *) NULL);
226 if (saturation == 0)
227 {
228 *red=(double) QuantumRange*lightness;
229 *green=(*red);
230 *blue=(*red);
231 return;
232 }
233 if (lightness < 0.5)
234 m2=lightness*(saturation+1.0);
235 else
236 m2=(lightness+saturation)-(lightness*saturation);
237 m1=2.0*lightness-m2;
238 r=ConvertHueToRGB(m1,m2,hue+1.0/3.0);
239 g=ConvertHueToRGB(m1,m2,hue);
240 b=ConvertHueToRGB(m1,m2,hue-1.0/3.0);
241 *red=(double) QuantumRange*r;
242 *green=(double) QuantumRange*g;
243 *blue=(double) QuantumRange*b;
244}
245
246static void CompositeHSL(const double red,const double green,const double blue,
247 double *hue,double *saturation,double *lightness)
248{
249 double
250 b,
251 delta,
252 g,
253 max,
254 min,
255 r;
256
257 /*
258 Convert RGB to HSL colorspace.
259 */
260 assert(hue != (double *) NULL);
261 assert(saturation != (double *) NULL);
262 assert(lightness != (double *) NULL);
263 r=QuantumScale*red;
264 g=QuantumScale*green;
265 b=QuantumScale*blue;
266 max=MagickMax(r,MagickMax(g,b));
267 min=MagickMin(r,MagickMin(g,b));
268 *lightness=(double) ((min+max)/2.0);
269 delta=max-min;
270 if (delta == 0.0)
271 {
272 *hue=0.0;
273 *saturation=0.0;
274 return;
275 }
276 if (*lightness < 0.5)
277 *saturation=(double) (delta/(min+max));
278 else
279 *saturation=(double) (delta/(2.0-max-min));
280 if (r == max)
281 *hue=((((max-b)/6.0)+(delta/2.0))-(((max-g)/6.0)+(delta/2.0)))/delta;
282 else
283 if (g == max)
284 *hue=(1.0/3.0)+((((max-r)/6.0)+(delta/2.0))-(((max-b)/6.0)+(delta/2.0)))/
285 delta;
286 else
287 if (b == max)
288 *hue=(2.0/3.0)+((((max-g)/6.0)+(delta/2.0))-(((max-r)/6.0)+
289 (delta/2.0)))/delta;
290 if (*hue < 0.0)
291 *hue+=1.0;
292 if (*hue > 1.0)
293 *hue-=1.0;
294}
295
cristye4a40472011-12-22 02:56:19 +0000296static MagickBooleanType CompositeOverImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000297 const Image *composite_image,const MagickBooleanType clip_to_self,
298 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristye4a40472011-12-22 02:56:19 +0000299{
300#define CompositeImageTag "Composite/Image"
301
302 CacheView
303 *composite_view,
304 *image_view;
305
cristye4a40472011-12-22 02:56:19 +0000306 MagickBooleanType
cristye4a40472011-12-22 02:56:19 +0000307 status;
308
309 MagickOffsetType
310 progress;
311
312 ssize_t
313 y;
314
cristye4a40472011-12-22 02:56:19 +0000315 /*
cristye4a40472011-12-22 02:56:19 +0000316 Composite image.
317 */
318 status=MagickTrue;
319 progress=0;
cristydb070952012-04-20 14:33:00 +0000320 composite_view=AcquireVirtualCacheView(composite_image,exception);
321 image_view=AcquireAuthenticCacheView(image,exception);
cristye4a40472011-12-22 02:56:19 +0000322#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000323 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000324 dynamic_number_threads(image,image->columns,image->rows,1)
cristye4a40472011-12-22 02:56:19 +0000325#endif
326 for (y=0; y < (ssize_t) image->rows; y++)
327 {
328 const Quantum
329 *pixels;
330
331 register const Quantum
332 *restrict p;
333
334 register Quantum
335 *restrict q;
336
337 register ssize_t
338 x;
339
cristy564a5692012-01-20 23:56:26 +0000340 size_t
341 channels;
342
cristye4a40472011-12-22 02:56:19 +0000343 if (status == MagickFalse)
344 continue;
cristyfeb3e962012-03-29 17:25:55 +0000345 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000346 {
347 if (y < y_offset)
348 continue;
349 if ((y-y_offset) >= (ssize_t) composite_image->rows)
350 continue;
351 }
352 /*
353 If pixels is NULL, y is outside overlay region.
354 */
355 pixels=(Quantum *) NULL;
356 p=(Quantum *) NULL;
357 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
358 {
359 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
360 composite_image->columns,1,exception);
361 if (p == (const Quantum *) NULL)
362 {
363 status=MagickFalse;
364 continue;
365 }
366 pixels=p;
367 if (x_offset < 0)
368 p-=x_offset*GetPixelChannels(composite_image);
369 }
370 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
371 if (q == (Quantum *) NULL)
372 {
373 status=MagickFalse;
374 continue;
375 }
376 for (x=0; x < (ssize_t) image->columns; x++)
377 {
cristya19f1d72012-08-07 18:24:38 +0000378 double
cristye4a40472011-12-22 02:56:19 +0000379 alpha,
380 Da,
381 Dc,
382 gamma,
383 Sa,
384 Sc;
385
386 register ssize_t
387 i;
388
cristyfeb3e962012-03-29 17:25:55 +0000389 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000390 {
391 if (x < x_offset)
392 {
393 q+=GetPixelChannels(image);
394 continue;
395 }
396 if ((x-x_offset) >= (ssize_t) composite_image->columns)
397 break;
398 }
399 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
400 ((x-x_offset) >= (ssize_t) composite_image->columns))
401 {
402 Quantum
403 source[MaxPixelChannels];
404
405 /*
406 Virtual composite:
407 Sc: source color.
408 Dc: destination color.
409 */
cristy10a6c612012-01-29 21:41:05 +0000410 if (GetPixelMask(image,q) != 0)
411 {
412 q+=GetPixelChannels(image);
413 continue;
414 }
cristyc94ba6f2012-01-29 23:19:58 +0000415 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
416 source,exception);
cristye4a40472011-12-22 02:56:19 +0000417 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
418 {
419 PixelChannel
420 channel;
421
422 PixelTrait
423 composite_traits,
424 traits;
425
426 channel=GetPixelChannelMapChannel(image,i);
427 traits=GetPixelChannelMapTraits(image,channel);
cristy10a6c612012-01-29 21:41:05 +0000428 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000429 if ((traits == UndefinedPixelTrait) ||
430 (composite_traits == UndefinedPixelTrait))
431 continue;
432 q[i]=source[channel];
433 }
434 q+=GetPixelChannels(image);
435 continue;
436 }
437 /*
438 Authentic composite:
439 Sa: normalized source alpha.
440 Da: normalized destination alpha.
441 */
cristyc94ba6f2012-01-29 23:19:58 +0000442 if (GetPixelMask(composite_image,p) != 0)
443 {
444 p+=GetPixelChannels(composite_image);
445 channels=GetPixelChannels(composite_image);
446 if (p >= (pixels+channels*composite_image->columns))
447 p=pixels;
448 q+=GetPixelChannels(image);
449 continue;
450 }
cristye4a40472011-12-22 02:56:19 +0000451 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
452 Da=QuantumScale*GetPixelAlpha(image,q);
453 alpha=Sa*(-Da)+Sa+Da;
454 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
455 {
456 PixelChannel
457 channel;
458
459 PixelTrait
460 composite_traits,
461 traits;
462
463 channel=GetPixelChannelMapChannel(image,i);
464 traits=GetPixelChannelMapTraits(image,channel);
465 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
466 if ((traits == UndefinedPixelTrait) ||
467 (composite_traits == UndefinedPixelTrait))
468 continue;
469 if ((traits & CopyPixelTrait) != 0)
470 {
471 if (channel != AlphaPixelChannel)
472 {
473 /*
474 Copy channel.
475 */
476 q[i]=GetPixelChannel(composite_image,channel,p);
477 continue;
478 }
479 /*
480 Set alpha channel.
481 */
482 q[i]=ClampToQuantum(QuantumRange*alpha);
483 continue;
484 }
485 /*
486 Sc: source color.
487 Dc: destination color.
488 */
cristya19f1d72012-08-07 18:24:38 +0000489 Sc=(double) GetPixelChannel(composite_image,channel,p);
490 Dc=(double) q[i];
cristyc58380a2012-06-03 15:12:30 +0000491 gamma=MagickEpsilonReciprocal(alpha);
cristye4a40472011-12-22 02:56:19 +0000492 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
493 }
494 p+=GetPixelChannels(composite_image);
495 channels=GetPixelChannels(composite_image);
496 if (p >= (pixels+channels*composite_image->columns))
497 p=pixels;
498 q+=GetPixelChannels(image);
499 }
500 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
501 status=MagickFalse;
502 if (image->progress_monitor != (MagickProgressMonitor) NULL)
503 {
504 MagickBooleanType
505 proceed;
506
507#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000508 #pragma omp critical (MagickCore_CompositeImage)
cristye4a40472011-12-22 02:56:19 +0000509#endif
510 proceed=SetImageProgress(image,CompositeImageTag,progress++,
511 image->rows);
512 if (proceed == MagickFalse)
513 status=MagickFalse;
514 }
515 }
516 composite_view=DestroyCacheView(composite_view);
517 image_view=DestroyCacheView(image_view);
518 return(status);
519}
520
cristy4c08aed2011-07-01 19:47:50 +0000521MagickExport MagickBooleanType CompositeImage(Image *image,
cristya865ccd2012-07-28 00:33:10 +0000522 const Image *composite,const CompositeOperator compose,
cristy44c98412012-03-31 18:25:40 +0000523 const MagickBooleanType clip_to_self,const ssize_t x_offset,
524 const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000525{
cristy4c08aed2011-07-01 19:47:50 +0000526#define CompositeImageTag "Composite/Image"
527
528 CacheView
529 *composite_view,
530 *image_view;
531
cristy4c08aed2011-07-01 19:47:50 +0000532 GeometryInfo
533 geometry_info;
534
535 Image
cristya865ccd2012-07-28 00:33:10 +0000536 *composite_image,
cristy4c08aed2011-07-01 19:47:50 +0000537 *destination_image;
538
539 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000540 status;
541
542 MagickOffsetType
543 progress;
544
cristya19f1d72012-08-07 18:24:38 +0000545 double
cristy4c08aed2011-07-01 19:47:50 +0000546 amount,
547 destination_dissolve,
548 midpoint,
549 percent_brightness,
550 percent_saturation,
551 source_dissolve,
552 threshold;
553
554 MagickStatusType
555 flags;
556
cristyd197cbb2012-01-13 02:14:12 +0000557 ssize_t
558 y;
559
cristy4c08aed2011-07-01 19:47:50 +0000560 assert(image != (Image *) NULL);
561 assert(image->signature == MagickSignature);
562 if (image->debug != MagickFalse)
563 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristya865ccd2012-07-28 00:33:10 +0000564 assert(composite!= (Image *) NULL);
565 assert(composite->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000566 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000567 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +0000568 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
569 if (composite_image == (const Image *) NULL)
570 return(MagickFalse);
cristya4a22a22012-08-01 23:16:38 +0000571 (void) SetImageColorspace(composite_image,image->colorspace,exception);
cristye4a40472011-12-22 02:56:19 +0000572 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
573 {
cristyfeb3e962012-03-29 17:25:55 +0000574 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
575 y_offset,exception);
cristya865ccd2012-07-28 00:33:10 +0000576 composite_image=DestroyImage(composite_image);
cristye4a40472011-12-22 02:56:19 +0000577 return(status);
578 }
cristy4c08aed2011-07-01 19:47:50 +0000579 destination_image=(Image *) NULL;
580 amount=0.5;
581 destination_dissolve=1.0;
cristy4c08aed2011-07-01 19:47:50 +0000582 percent_brightness=100.0;
583 percent_saturation=100.0;
584 source_dissolve=1.0;
585 threshold=0.05f;
586 switch (compose)
587 {
cristy4c08aed2011-07-01 19:47:50 +0000588 case CopyCompositeOp:
589 {
590 if ((x_offset < 0) || (y_offset < 0))
591 break;
592 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
593 break;
594 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
595 break;
596 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +0000597 composite_view=AcquireVirtualCacheView(composite_image,exception);
598 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000599#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000600 #pragma omp parallel for schedule(static,4) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000601 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000602#endif
603 for (y=0; y < (ssize_t) composite_image->rows; y++)
604 {
605 MagickBooleanType
606 sync;
607
608 register const Quantum
609 *p;
610
611 register Quantum
612 *q;
613
614 register ssize_t
615 x;
616
617 if (status == MagickFalse)
618 continue;
619 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
620 1,exception);
621 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
622 composite_image->columns,1,exception);
623 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
624 {
625 status=MagickFalse;
626 continue;
627 }
628 for (x=0; x < (ssize_t) composite_image->columns; x++)
629 {
cristybdecccc2011-12-24 22:52:16 +0000630 register ssize_t
631 i;
632
cristy665e18f2012-05-17 12:39:54 +0000633 if (GetPixelMask(composite_image,p) != 0)
cristy10a6c612012-01-29 21:41:05 +0000634 {
635 p+=GetPixelChannels(composite_image);
636 q+=GetPixelChannels(image);
637 continue;
638 }
cristybdecccc2011-12-24 22:52:16 +0000639 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
640 {
641 PixelChannel
642 channel;
643
644 PixelTrait
645 composite_traits,
646 traits;
647
648 channel=GetPixelChannelMapChannel(composite_image,i);
649 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
650 traits=GetPixelChannelMapTraits(image,channel);
651 if ((traits == UndefinedPixelTrait) ||
652 (composite_traits == UndefinedPixelTrait))
653 continue;
654 SetPixelChannel(image,channel,p[i],q);
655 }
cristyed231572011-07-14 02:18:59 +0000656 p+=GetPixelChannels(composite_image);
657 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000658 }
659 sync=SyncCacheViewAuthenticPixels(image_view,exception);
660 if (sync == MagickFalse)
661 status=MagickFalse;
662 if (image->progress_monitor != (MagickProgressMonitor) NULL)
663 {
664 MagickBooleanType
665 proceed;
666
667#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000668 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000669#endif
670 proceed=SetImageProgress(image,CompositeImageTag,
671 (MagickOffsetType) y,image->rows);
672 if (proceed == MagickFalse)
673 status=MagickFalse;
674 }
675 }
676 composite_view=DestroyCacheView(composite_view);
677 image_view=DestroyCacheView(image_view);
cristy44886b92012-07-28 13:07:09 +0000678 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000679 return(status);
680 }
cristye4a40472011-12-22 02:56:19 +0000681 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000682 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000683 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000684 {
685 /*
686 Modify destination outside the overlaid region and require an alpha
687 channel to exist, to add transparency.
688 */
689 if (image->matte == MagickFalse)
cristy42c41de2012-05-05 18:36:31 +0000690 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000691 break;
692 }
693 case BlurCompositeOp:
694 {
695 CacheView
696 *composite_view,
697 *destination_view;
698
cristyfeb3e962012-03-29 17:25:55 +0000699 const char
700 *value;
701
cristy4c08aed2011-07-01 19:47:50 +0000702 PixelInfo
703 pixel;
704
cristya19f1d72012-08-07 18:24:38 +0000705 double
cristy4c08aed2011-07-01 19:47:50 +0000706 angle_range,
707 angle_start,
708 height,
709 width;
710
711 ResampleFilter
712 *resample_filter;
713
714 SegmentInfo
715 blur;
716
717 /*
anthony9cb63cc2012-04-25 06:10:49 +0000718 Blur Image by resampling.
719
cristy4c08aed2011-07-01 19:47:50 +0000720 Blur Image dictated by an overlay gradient map: X = red_channel;
721 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
722 */
723 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000724 exception);
cristy4c08aed2011-07-01 19:47:50 +0000725 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000726 {
727 composite_image=DestroyImage(composite_image);
728 return(MagickFalse);
729 }
cristy4c08aed2011-07-01 19:47:50 +0000730 /*
anthony9cb63cc2012-04-25 06:10:49 +0000731 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000732 */
733 SetGeometryInfo(&geometry_info);
734 flags=NoValue;
735 value=GetImageArtifact(composite_image,"compose:args");
736 if (value != (char *) NULL)
737 flags=ParseGeometry(value,&geometry_info);
anthonyd2923912012-04-23 13:06:53 +0000738 if ((flags & WidthValue) == 0 ) {
739 (void) ThrowMagickException(exception,GetMagickModule(),
740 OptionWarning,"InvalidSetting","'%s' '%s'",
anthony9cb63cc2012-04-25 06:10:49 +0000741 "compose:args",value);
cristy44886b92012-07-28 13:07:09 +0000742 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000743 destination_image=DestroyImage(destination_image);
744 return(MagickFalse);
745 }
anthony9cb63cc2012-04-25 06:10:49 +0000746 /*
747 Users input sigma now needs to be converted to the EWA ellipse size.
748 The filter defaults to a sigma of 0.5 so to make this match the
749 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000750 */
751 width=height=geometry_info.rho*2.0;
752 if ((flags & HeightValue) != 0 )
753 height=geometry_info.sigma*2.0;
754
anthony9cb63cc2012-04-25 06:10:49 +0000755 /* default the unrotated ellipse width and height axis vectors */
anthonyd2923912012-04-23 13:06:53 +0000756 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000757 blur.x2=0.0;
758 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000759 blur.y2=height;
760 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000761 if ((flags & XValue) != 0 )
762 {
cristya19f1d72012-08-07 18:24:38 +0000763 double
cristy4c08aed2011-07-01 19:47:50 +0000764 angle;
765
766 angle=DegreesToRadians(geometry_info.xi);
767 blur.x1=width*cos(angle);
768 blur.x2=width*sin(angle);
769 blur.y1=(-height*sin(angle));
770 blur.y2=height*cos(angle);
771 }
anthonyd2923912012-04-23 13:06:53 +0000772 /* Otherwise lets set a angle range and calculate in the loop */
773 angle_start=0.0;
774 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000775 if ((flags & YValue) != 0 )
776 {
777 angle_start=DegreesToRadians(geometry_info.xi);
778 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
779 }
780 /*
anthony9cb63cc2012-04-25 06:10:49 +0000781 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000782
anthony9cb63cc2012-04-25 06:10:49 +0000783 As the minimum ellipse radius of support*1.0 the EWA algorithm
784 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
785 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000786
anthony9cb63cc2012-04-25 06:10:49 +0000787 The solution (as well as the problem of preventing any user
788 expert filter settings, is to set our own user settings, then
789 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000790 */
cristy8a11cb12011-10-19 23:53:34 +0000791 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000792 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000793
794 /* do the variable blurring of each pixel in image */
795 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000796 composite_view=AcquireVirtualCacheView(composite_image,exception);
797 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000798 for (y=0; y < (ssize_t) composite_image->rows; y++)
799 {
800 MagickBooleanType
801 sync;
802
803 register const Quantum
804 *restrict p;
805
806 register Quantum
807 *restrict q;
808
809 register ssize_t
810 x;
811
812 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
813 continue;
814 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
815 1,exception);
816 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000817 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000818 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
819 break;
820 for (x=0; x < (ssize_t) composite_image->columns; x++)
821 {
822 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
823 {
cristyed231572011-07-14 02:18:59 +0000824 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000825 continue;
826 }
827 if (fabs(angle_range) > MagickEpsilon)
828 {
cristya19f1d72012-08-07 18:24:38 +0000829 double
cristy4c08aed2011-07-01 19:47:50 +0000830 angle;
831
832 angle=angle_start+angle_range*QuantumScale*
833 GetPixelBlue(composite_image,p);
834 blur.x1=width*cos(angle);
835 blur.x2=width*sin(angle);
836 blur.y1=(-height*sin(angle));
837 blur.y2=height*cos(angle);
838 }
anthonyd2923912012-04-23 13:06:53 +0000839#if 0
840 if ( x == 10 && y == 60 ) {
841 fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",
842 blur.x1, blur.x2, blur.y1, blur.y2);
843 fprintf(stderr, "scaled by=%lf,%lf\n",
844 QuantumScale*GetPixelRed(p), QuantumScale*GetPixelGreen(p));
845#endif
846 ScaleResampleFilter(resample_filter,
847 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
848 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
849 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
850 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000851 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000852 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000853 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000854 p+=GetPixelChannels(composite_image);
855 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000856 }
857 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
858 if (sync == MagickFalse)
859 break;
860 }
861 resample_filter=DestroyResampleFilter(resample_filter);
862 composite_view=DestroyCacheView(composite_view);
863 destination_view=DestroyCacheView(destination_view);
cristyf661c4d2012-07-28 12:57:17 +0000864 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000865 composite_image=destination_image;
866 break;
867 }
868 case DisplaceCompositeOp:
869 case DistortCompositeOp:
870 {
871 CacheView
872 *composite_view,
873 *destination_view,
874 *image_view;
875
cristyfeb3e962012-03-29 17:25:55 +0000876 const char
877 *value;
878
cristy4c08aed2011-07-01 19:47:50 +0000879 PixelInfo
880 pixel;
881
cristya19f1d72012-08-07 18:24:38 +0000882 double
cristy4c08aed2011-07-01 19:47:50 +0000883 horizontal_scale,
884 vertical_scale;
885
886 PointInfo
887 center,
888 offset;
889
890 /*
891 Displace/Distort based on overlay gradient map:
892 X = red_channel; Y = green_channel;
893 compose:args = x_scale[,y_scale[,center.x,center.y]]
894 */
895 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000896 exception);
cristy4c08aed2011-07-01 19:47:50 +0000897 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000898 {
899 composite_image=DestroyImage(composite_image);
900 return(MagickFalse);
901 }
cristy4c08aed2011-07-01 19:47:50 +0000902 SetGeometryInfo(&geometry_info);
903 flags=NoValue;
904 value=GetImageArtifact(composite_image,"compose:args");
905 if (value != (char *) NULL)
906 flags=ParseGeometry(value,&geometry_info);
907 if ((flags & (WidthValue|HeightValue)) == 0 )
908 {
909 if ((flags & AspectValue) == 0)
910 {
cristya19f1d72012-08-07 18:24:38 +0000911 horizontal_scale=(double) (composite_image->columns-1.0)/
cristy4c08aed2011-07-01 19:47:50 +0000912 2.0;
cristya19f1d72012-08-07 18:24:38 +0000913 vertical_scale=(double) (composite_image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000914 }
915 else
916 {
cristya19f1d72012-08-07 18:24:38 +0000917 horizontal_scale=(double) (image->columns-1.0)/2.0;
918 vertical_scale=(double) (image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000919 }
920 }
921 else
922 {
923 horizontal_scale=geometry_info.rho;
924 vertical_scale=geometry_info.sigma;
925 if ((flags & PercentValue) != 0)
926 {
927 if ((flags & AspectValue) == 0)
928 {
929 horizontal_scale*=(composite_image->columns-1.0)/200.0;
930 vertical_scale*=(composite_image->rows-1.0)/200.0;
931 }
932 else
933 {
934 horizontal_scale*=(image->columns-1.0)/200.0;
935 vertical_scale*=(image->rows-1.0)/200.0;
936 }
937 }
938 if ((flags & HeightValue) == 0)
939 vertical_scale=horizontal_scale;
940 }
941 /*
942 Determine fixed center point for absolute distortion map
943 Absolute distort ==
944 Displace offset relative to a fixed absolute point
945 Select that point according to +X+Y user inputs.
946 default = center of overlay image
947 arg flag '!' = locations/percentage relative to background image
948 */
cristya19f1d72012-08-07 18:24:38 +0000949 center.x=(double) x_offset;
950 center.y=(double) y_offset;
cristy4c08aed2011-07-01 19:47:50 +0000951 if (compose == DistortCompositeOp)
952 {
953 if ((flags & XValue) == 0)
954 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000955 center.x=(double) x_offset+(composite_image->columns-1)/
cristy4c08aed2011-07-01 19:47:50 +0000956 2.0;
957 else
cristya19f1d72012-08-07 18:24:38 +0000958 center.x=((double) image->columns-1)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000959 else
960 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000961 center.x=(double) x_offset+geometry_info.xi;
cristy4c08aed2011-07-01 19:47:50 +0000962 else
963 center.x=geometry_info.xi;
964 if ((flags & YValue) == 0)
965 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000966 center.y=(double) y_offset+(composite_image->rows-1)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000967 else
cristya19f1d72012-08-07 18:24:38 +0000968 center.y=((double) image->rows-1)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000969 else
970 if ((flags & AspectValue) == 0)
cristya19f1d72012-08-07 18:24:38 +0000971 center.y=(double) y_offset+geometry_info.psi;
cristy4c08aed2011-07-01 19:47:50 +0000972 else
973 center.y=geometry_info.psi;
974 }
975 /*
976 Shift the pixel offset point as defined by the provided,
977 displacement/distortion map. -- Like a lens...
978 */
cristye10859a2011-12-18 22:28:59 +0000979 GetPixelInfo(image,&pixel);
cristydb070952012-04-20 14:33:00 +0000980 image_view=AcquireVirtualCacheView(image,exception);
981 composite_view=AcquireVirtualCacheView(composite_image,exception);
982 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000983 for (y=0; y < (ssize_t) composite_image->rows; y++)
984 {
985 MagickBooleanType
986 sync;
987
988 register const Quantum
989 *restrict p;
990
991 register Quantum
992 *restrict q;
993
994 register ssize_t
995 x;
996
997 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
998 continue;
999 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1000 1,exception);
1001 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001002 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001003 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1004 break;
1005 for (x=0; x < (ssize_t) composite_image->columns; x++)
1006 {
1007 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1008 {
cristyed231572011-07-14 02:18:59 +00001009 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001010 continue;
1011 }
1012 /*
1013 Displace the offset.
1014 */
1015 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
cristya19f1d72012-08-07 18:24:38 +00001016 (((double) QuantumRange+1.0)/2.0)))/(((double)
cristy4c08aed2011-07-01 19:47:50 +00001017 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1018 x : 0);
1019 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
cristya19f1d72012-08-07 18:24:38 +00001020 (((double) QuantumRange+1.0)/2.0)))/(((double)
cristy4c08aed2011-07-01 19:47:50 +00001021 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1022 y : 0);
1023 (void) InterpolatePixelInfo(image,image_view,
1024 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1025 &pixel,exception);
1026 /*
1027 Mask with the 'invalid pixel mask' in alpha channel.
1028 */
cristya19f1d72012-08-07 18:24:38 +00001029 pixel.alpha=(double) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001030 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001031 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001032 p+=GetPixelChannels(composite_image);
1033 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001034 }
1035 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1036 if (sync == MagickFalse)
1037 break;
1038 }
1039 destination_view=DestroyCacheView(destination_view);
1040 composite_view=DestroyCacheView(composite_view);
1041 image_view=DestroyCacheView(image_view);
cristyf661c4d2012-07-28 12:57:17 +00001042 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001043 composite_image=destination_image;
1044 break;
1045 }
1046 case DissolveCompositeOp:
1047 {
cristyfeb3e962012-03-29 17:25:55 +00001048 const char
1049 *value;
1050
cristy4c08aed2011-07-01 19:47:50 +00001051 /*
1052 Geometry arguments to dissolve factors.
1053 */
1054 value=GetImageArtifact(composite_image,"compose:args");
1055 if (value != (char *) NULL)
1056 {
1057 flags=ParseGeometry(value,&geometry_info);
1058 source_dissolve=geometry_info.rho/100.0;
1059 destination_dissolve=1.0;
1060 if ((source_dissolve-MagickEpsilon) < 0.0)
1061 source_dissolve=0.0;
1062 if ((source_dissolve+MagickEpsilon) > 1.0)
1063 {
1064 destination_dissolve=2.0-source_dissolve;
1065 source_dissolve=1.0;
1066 }
1067 if ((flags & SigmaValue) != 0)
1068 destination_dissolve=geometry_info.sigma/100.0;
1069 if ((destination_dissolve-MagickEpsilon) < 0.0)
1070 destination_dissolve=0.0;
anthony9cb63cc2012-04-25 06:10:49 +00001071 /* posible speed up? -- from IMv6 update
1072 clip_to_self=MagickFalse;
1073 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1074 {
1075 destination_dissolve=1.0;
1076 clip_to_self=MagickTrue;
1077 }
1078 */
cristy4c08aed2011-07-01 19:47:50 +00001079 }
1080 break;
1081 }
1082 case BlendCompositeOp:
1083 {
cristyfeb3e962012-03-29 17:25:55 +00001084 const char
1085 *value;
1086
cristy4c08aed2011-07-01 19:47:50 +00001087 value=GetImageArtifact(composite_image,"compose:args");
1088 if (value != (char *) NULL)
1089 {
1090 flags=ParseGeometry(value,&geometry_info);
1091 source_dissolve=geometry_info.rho/100.0;
1092 destination_dissolve=1.0-source_dissolve;
1093 if ((flags & SigmaValue) != 0)
1094 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001095 }
1096 break;
1097 }
1098 case MathematicsCompositeOp:
1099 {
cristyfeb3e962012-03-29 17:25:55 +00001100 const char
1101 *value;
1102
cristy4c08aed2011-07-01 19:47:50 +00001103 /*
1104 Just collect the values from "compose:args", setting.
1105 Unused values are set to zero automagically.
1106
1107 Arguments are normally a comma separated list, so this probably should
1108 be changed to some 'general comma list' parser, (with a minimum
1109 number of values)
1110 */
1111 SetGeometryInfo(&geometry_info);
1112 value=GetImageArtifact(composite_image,"compose:args");
1113 if (value != (char *) NULL)
1114 (void) ParseGeometry(value,&geometry_info);
1115 break;
1116 }
1117 case ModulateCompositeOp:
1118 {
cristyfeb3e962012-03-29 17:25:55 +00001119 const char
1120 *value;
1121
cristy4c08aed2011-07-01 19:47:50 +00001122 /*
1123 Determine the brightness and saturation scale.
1124 */
1125 value=GetImageArtifact(composite_image,"compose:args");
1126 if (value != (char *) NULL)
1127 {
1128 flags=ParseGeometry(value,&geometry_info);
1129 percent_brightness=geometry_info.rho;
1130 if ((flags & SigmaValue) != 0)
1131 percent_saturation=geometry_info.sigma;
1132 }
1133 break;
1134 }
1135 case ThresholdCompositeOp:
1136 {
cristyfeb3e962012-03-29 17:25:55 +00001137 const char
1138 *value;
1139
cristy4c08aed2011-07-01 19:47:50 +00001140 /*
1141 Determine the amount and threshold.
1142 */
1143 value=GetImageArtifact(composite_image,"compose:args");
1144 if (value != (char *) NULL)
1145 {
1146 flags=ParseGeometry(value,&geometry_info);
1147 amount=geometry_info.rho;
1148 threshold=geometry_info.sigma;
1149 if ((flags & SigmaValue) == 0)
1150 threshold=0.05f;
1151 }
1152 threshold*=QuantumRange;
1153 break;
1154 }
1155 default:
1156 break;
1157 }
cristy4c08aed2011-07-01 19:47:50 +00001158 /*
1159 Composite image.
1160 */
1161 status=MagickTrue;
1162 progress=0;
cristya19f1d72012-08-07 18:24:38 +00001163 midpoint=((double) QuantumRange+1.0)/2;
cristydb070952012-04-20 14:33:00 +00001164 composite_view=AcquireVirtualCacheView(composite_image,exception);
1165 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001166#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001167 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +00001168 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001169#endif
1170 for (y=0; y < (ssize_t) image->rows; y++)
1171 {
1172 const Quantum
1173 *pixels;
1174
1175 double
cristye4a40472011-12-22 02:56:19 +00001176 blue,
cristy4c08aed2011-07-01 19:47:50 +00001177 brightness,
cristye4a40472011-12-22 02:56:19 +00001178 green,
cristy4c08aed2011-07-01 19:47:50 +00001179 hue,
cristye4a40472011-12-22 02:56:19 +00001180 red,
cristy4c08aed2011-07-01 19:47:50 +00001181 saturation;
1182
cristyddeeea22012-04-12 01:33:09 +00001183 PixelInfo
1184 destination_pixel,
1185 source_pixel;
1186
cristy4c08aed2011-07-01 19:47:50 +00001187 register const Quantum
1188 *restrict p;
1189
1190 register Quantum
1191 *restrict q;
1192
1193 register ssize_t
1194 x;
1195
1196 if (status == MagickFalse)
1197 continue;
cristyfeb3e962012-03-29 17:25:55 +00001198 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001199 {
1200 if (y < y_offset)
1201 continue;
1202 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1203 continue;
1204 }
1205 /*
1206 If pixels is NULL, y is outside overlay region.
1207 */
1208 pixels=(Quantum *) NULL;
1209 p=(Quantum *) NULL;
1210 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1211 {
1212 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1213 composite_image->columns,1,exception);
1214 if (p == (const Quantum *) NULL)
1215 {
1216 status=MagickFalse;
1217 continue;
1218 }
1219 pixels=p;
1220 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001221 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001222 }
1223 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001224 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001225 {
1226 status=MagickFalse;
1227 continue;
1228 }
cristy4c08aed2011-07-01 19:47:50 +00001229 hue=0.0;
1230 saturation=0.0;
1231 brightness=0.0;
cristyddeeea22012-04-12 01:33:09 +00001232 GetPixelInfo(image,&destination_pixel);
1233 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001234 for (x=0; x < (ssize_t) image->columns; x++)
1235 {
cristya19f1d72012-08-07 18:24:38 +00001236 double
cristye4a40472011-12-22 02:56:19 +00001237 alpha,
1238 Da,
1239 Dc,
1240 Dca,
1241 gamma,
1242 Sa,
1243 Sc,
1244 Sca;
1245
1246 register ssize_t
1247 i;
1248
cristy564a5692012-01-20 23:56:26 +00001249 size_t
1250 channels;
1251
cristyfeb3e962012-03-29 17:25:55 +00001252 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001253 {
1254 if (x < x_offset)
1255 {
cristyed231572011-07-14 02:18:59 +00001256 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001257 continue;
1258 }
1259 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1260 break;
1261 }
cristye4a40472011-12-22 02:56:19 +00001262 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1263 ((x-x_offset) >= (ssize_t) composite_image->columns))
1264 {
1265 Quantum
1266 source[MaxPixelChannels];
1267
1268 /*
1269 Virtual composite:
1270 Sc: source color.
1271 Dc: destination color.
1272 */
1273 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1274 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001275 if (GetPixelMask(image,q) != 0)
1276 {
1277 q+=GetPixelChannels(image);
1278 continue;
1279 }
cristye4a40472011-12-22 02:56:19 +00001280 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1281 {
cristya19f1d72012-08-07 18:24:38 +00001282 double
cristye4a40472011-12-22 02:56:19 +00001283 pixel;
1284
1285 PixelChannel
1286 channel;
1287
1288 PixelTrait
1289 composite_traits,
1290 traits;
1291
1292 channel=GetPixelChannelMapChannel(image,i);
1293 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +00001294 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001295 if ((traits == UndefinedPixelTrait) ||
1296 (composite_traits == UndefinedPixelTrait))
1297 continue;
1298 switch (compose)
1299 {
cristyc8d63672012-01-11 13:03:13 +00001300 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001301 case ChangeMaskCompositeOp:
1302 case CopyAlphaCompositeOp:
1303 case DstAtopCompositeOp:
1304 case DstInCompositeOp:
1305 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001306 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001307 case OutCompositeOp:
1308 case SrcInCompositeOp:
1309 case SrcOutCompositeOp:
1310 {
cristya19f1d72012-08-07 18:24:38 +00001311 pixel=(double) q[i];
cristye4a40472011-12-22 02:56:19 +00001312 if (channel == AlphaPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001313 pixel=(double) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001314 break;
1315 }
1316 case ClearCompositeOp:
1317 case CopyCompositeOp:
1318 case ReplaceCompositeOp:
1319 case SrcCompositeOp:
1320 {
1321 if (channel == AlphaPixelChannel)
1322 {
cristya19f1d72012-08-07 18:24:38 +00001323 pixel=(double) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001324 break;
1325 }
1326 pixel=0.0;
1327 break;
1328 }
cristy99abff32011-12-24 20:45:16 +00001329 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001330 case DissolveCompositeOp:
1331 {
1332 if (channel == AlphaPixelChannel)
1333 {
1334 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1335 source);
1336 break;
1337 }
cristya19f1d72012-08-07 18:24:38 +00001338 pixel=(double) source[channel];
cristye4a40472011-12-22 02:56:19 +00001339 break;
1340 }
1341 default:
1342 {
cristya19f1d72012-08-07 18:24:38 +00001343 pixel=(double) source[channel];
cristye4a40472011-12-22 02:56:19 +00001344 break;
1345 }
1346 }
1347 q[i]=ClampToQuantum(pixel);
1348 }
1349 q+=GetPixelChannels(image);
1350 continue;
1351 }
1352 /*
1353 Authentic composite:
1354 Sa: normalized source alpha.
1355 Da: normalized destination alpha.
1356 */
1357 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1358 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001359 switch (compose)
1360 {
cristye4a40472011-12-22 02:56:19 +00001361 case BumpmapCompositeOp:
1362 {
1363 alpha=GetPixelIntensity(composite_image,p)*Sa;
1364 break;
1365 }
cristycdc168f2011-12-21 15:24:39 +00001366 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001367 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001368 case DifferenceCompositeOp:
1369 case DivideDstCompositeOp:
1370 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001371 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001372 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001373 case LinearBurnCompositeOp:
1374 case LinearDodgeCompositeOp:
1375 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001376 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001377 case MinusDstCompositeOp:
1378 case MinusSrcCompositeOp:
1379 case ModulusAddCompositeOp:
1380 case ModulusSubtractCompositeOp:
1381 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001382 case OverlayCompositeOp:
1383 case PegtopLightCompositeOp:
1384 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001385 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001386 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001387 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001388 {
1389 alpha=RoundToUnity(Sa+Da-Sa*Da);
1390 break;
1391 }
1392 case DarkenCompositeOp:
1393 case DstAtopCompositeOp:
1394 case DstInCompositeOp:
1395 case InCompositeOp:
1396 case LightenCompositeOp:
1397 case SrcInCompositeOp:
1398 {
1399 alpha=Sa*Da;
1400 break;
1401 }
1402 case DissolveCompositeOp:
1403 {
1404 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1405 Sa+destination_dissolve*Da;
1406 break;
1407 }
1408 case DstOverCompositeOp:
1409 {
1410 alpha=Da*(-Sa)+Da+Sa;
1411 break;
1412 }
1413 case DstOutCompositeOp:
1414 {
1415 alpha=Da*(1.0-Sa);
1416 break;
1417 }
1418 case OutCompositeOp:
1419 case SrcOutCompositeOp:
1420 {
1421 alpha=Sa*(1.0-Da);
1422 break;
1423 }
1424 case OverCompositeOp:
1425 case SrcOverCompositeOp:
1426 {
1427 alpha=Sa*(-Da)+Sa+Da;
1428 break;
1429 }
cristy99abff32011-12-24 20:45:16 +00001430 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001431 case PlusCompositeOp:
1432 {
1433 alpha=RoundToUnity(Sa+Da);
1434 break;
1435 }
cristy4c08aed2011-07-01 19:47:50 +00001436 case XorCompositeOp:
1437 {
cristye4a40472011-12-22 02:56:19 +00001438 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001439 break;
1440 }
1441 default:
cristye4a40472011-12-22 02:56:19 +00001442 {
1443 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001444 break;
cristye4a40472011-12-22 02:56:19 +00001445 }
cristy4c08aed2011-07-01 19:47:50 +00001446 }
cristy10a6c612012-01-29 21:41:05 +00001447 if (GetPixelMask(image,p) != 0)
1448 {
1449 p+=GetPixelChannels(composite_image);
1450 q+=GetPixelChannels(image);
1451 continue;
1452 }
cristy9d3d2792012-04-14 15:15:19 +00001453 switch (compose)
1454 {
1455 case ColorizeCompositeOp:
1456 case HueCompositeOp:
1457 case LuminizeCompositeOp:
1458 case ModulateCompositeOp:
1459 case SaturateCompositeOp:
1460 {
1461 GetPixelInfoPixel(composite_image,p,&source_pixel);
1462 GetPixelInfoPixel(image,q,&destination_pixel);
1463 break;
1464 }
1465 default:
1466 break;
1467 }
cristye4a40472011-12-22 02:56:19 +00001468 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1469 {
cristy564a5692012-01-20 23:56:26 +00001470 double
1471 sans;
1472
cristya19f1d72012-08-07 18:24:38 +00001473 double
cristye4a40472011-12-22 02:56:19 +00001474 pixel;
cristye10859a2011-12-18 22:28:59 +00001475
cristye4a40472011-12-22 02:56:19 +00001476 PixelChannel
1477 channel;
cristye10859a2011-12-18 22:28:59 +00001478
cristye4a40472011-12-22 02:56:19 +00001479 PixelTrait
1480 composite_traits,
1481 traits;
1482
1483 channel=GetPixelChannelMapChannel(image,i);
1484 traits=GetPixelChannelMapTraits(image,channel);
1485 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001486 if (traits == UndefinedPixelTrait)
1487 continue;
cristya7b07912012-01-11 20:01:32 +00001488 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001489 (composite_traits == UndefinedPixelTrait))
1490 continue;
1491 /*
1492 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001493 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001494 */
cristya19f1d72012-08-07 18:24:38 +00001495 Sc=(double) GetPixelChannel(composite_image,channel,p);
1496 Dc=(double) q[i];
cristye4a40472011-12-22 02:56:19 +00001497 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001498 {
cristye4a40472011-12-22 02:56:19 +00001499 if (channel != AlphaPixelChannel)
1500 {
1501 /*
1502 Copy channel.
1503 */
1504 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001505 continue;
cristye10859a2011-12-18 22:28:59 +00001506 }
cristye4a40472011-12-22 02:56:19 +00001507 /*
1508 Set alpha channel.
1509 */
cristye10859a2011-12-18 22:28:59 +00001510 switch (compose)
1511 {
cristyc8d63672012-01-11 13:03:13 +00001512 case AlphaCompositeOp:
1513 {
cristya7b07912012-01-11 20:01:32 +00001514 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001515 break;
1516 }
cristye4a40472011-12-22 02:56:19 +00001517 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001518 case CopyBlackCompositeOp:
1519 case CopyBlueCompositeOp:
1520 case CopyCyanCompositeOp:
1521 case CopyGreenCompositeOp:
1522 case CopyMagentaCompositeOp:
1523 case CopyRedCompositeOp:
1524 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001525 case SrcAtopCompositeOp:
1526 case DstCompositeOp:
1527 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001528 {
cristye4a40472011-12-22 02:56:19 +00001529 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001530 break;
1531 }
cristye10859a2011-12-18 22:28:59 +00001532 case ChangeMaskCompositeOp:
1533 {
cristye4a40472011-12-22 02:56:19 +00001534 MagickBooleanType
1535 equivalent;
1536
cristya19f1d72012-08-07 18:24:38 +00001537 if (Da > ((double) QuantumRange/2.0))
cristy99abff32011-12-24 20:45:16 +00001538 {
cristya19f1d72012-08-07 18:24:38 +00001539 pixel=(double) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001540 break;
1541 }
cristye4a40472011-12-22 02:56:19 +00001542 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001543 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001544 {
cristya19f1d72012-08-07 18:24:38 +00001545 pixel=(double) TransparentAlpha;
cristye4a40472011-12-22 02:56:19 +00001546 break;
1547 }
cristya19f1d72012-08-07 18:24:38 +00001548 pixel=(double) OpaqueAlpha;
cristye4a40472011-12-22 02:56:19 +00001549 break;
1550 }
cristy99abff32011-12-24 20:45:16 +00001551 case ClearCompositeOp:
1552 {
cristya19f1d72012-08-07 18:24:38 +00001553 pixel=(double) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001554 break;
1555 }
1556 case ColorizeCompositeOp:
1557 case HueCompositeOp:
1558 case LuminizeCompositeOp:
1559 case SaturateCompositeOp:
1560 {
1561 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1562 {
1563 pixel=QuantumRange*Da;
1564 break;
1565 }
1566 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1567 {
1568 pixel=QuantumRange*Sa;
1569 break;
1570 }
1571 if (Sa < Da)
1572 {
1573 pixel=QuantumRange*Da;
1574 break;
1575 }
1576 pixel=QuantumRange*Sa;
1577 break;
1578 }
cristy99abff32011-12-24 20:45:16 +00001579 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001580 {
1581 pixel=QuantumRange*Sa;
1582 if (composite_image->matte == MagickFalse)
1583 pixel=GetPixelIntensity(composite_image,p);
1584 break;
1585 }
1586 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001587 case DisplaceCompositeOp:
1588 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001589 case DstAtopCompositeOp:
1590 case ReplaceCompositeOp:
1591 case SrcCompositeOp:
1592 {
1593 pixel=QuantumRange*Sa;
1594 break;
1595 }
1596 case DarkenIntensityCompositeOp:
1597 {
cristy99abff32011-12-24 20:45:16 +00001598 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1599 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001600 break;
1601 }
cristy98621462011-12-31 22:31:11 +00001602 case IntensityCompositeOp:
1603 {
cristyf13c5942012-08-08 23:50:11 +00001604 pixel=GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001605 break;
1606 }
cristye4a40472011-12-22 02:56:19 +00001607 case LightenIntensityCompositeOp:
1608 {
1609 pixel=Sa*GetPixelIntensity(composite_image,p) >
1610 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001611 break;
1612 }
cristy99abff32011-12-24 20:45:16 +00001613 case ModulateCompositeOp:
1614 {
1615 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1616 {
1617 pixel=QuantumRange*Da;
1618 break;
1619 }
1620 pixel=QuantumRange*Da;
1621 break;
1622 }
cristye10859a2011-12-18 22:28:59 +00001623 default:
1624 {
cristye4a40472011-12-22 02:56:19 +00001625 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001626 break;
1627 }
1628 }
cristye4a40472011-12-22 02:56:19 +00001629 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001630 continue;
1631 }
1632 /*
cristy99abff32011-12-24 20:45:16 +00001633 Porter-Duff compositions:
1634 Sca: source normalized color multiplied by alpha.
1635 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001636 */
cristy99abff32011-12-24 20:45:16 +00001637 Sca=QuantumScale*Sa*Sc;
1638 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001639 switch (compose)
1640 {
cristye10859a2011-12-18 22:28:59 +00001641 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001642 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001643 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001644 {
cristy99abff32011-12-24 20:45:16 +00001645 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001646 break;
1647 }
1648 default:
1649 break;
1650 }
cristyc58380a2012-06-03 15:12:30 +00001651 gamma=MagickEpsilonReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001652 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001653 switch (compose)
1654 {
cristya7b07912012-01-11 20:01:32 +00001655 case AlphaCompositeOp:
1656 {
1657 pixel=QuantumRange*Sa;
1658 break;
1659 }
cristye4a40472011-12-22 02:56:19 +00001660 case AtopCompositeOp:
1661 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001662 {
cristye4a40472011-12-22 02:56:19 +00001663 pixel=Sc*Sa+Dc*(1.0-Sa);
1664 break;
cristye10859a2011-12-18 22:28:59 +00001665 }
cristye4a40472011-12-22 02:56:19 +00001666 case BlendCompositeOp:
1667 {
1668 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1669 break;
1670 }
1671 case BlurCompositeOp:
1672 case DisplaceCompositeOp:
1673 case DistortCompositeOp:
1674 case CopyCompositeOp:
1675 case ReplaceCompositeOp:
1676 case SrcCompositeOp:
1677 {
1678 pixel=Sc;
1679 break;
1680 }
1681 case BumpmapCompositeOp:
1682 {
cristy99abff32011-12-24 20:45:16 +00001683 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001684 {
1685 pixel=Dc;
1686 break;
1687 }
1688 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1689 break;
1690 }
cristy99abff32011-12-24 20:45:16 +00001691 case ChangeMaskCompositeOp:
1692 {
1693 pixel=Dc;
1694 break;
1695 }
1696 case ClearCompositeOp:
1697 {
1698 pixel=0.0;
1699 break;
1700 }
cristye4a40472011-12-22 02:56:19 +00001701 case ColorBurnCompositeOp:
1702 {
1703 /*
1704 Refer to the March 2009 SVG specification.
1705 */
1706 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1707 {
cristy99abff32011-12-24 20:45:16 +00001708 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001709 break;
1710 }
1711 if (Sca < MagickEpsilon)
1712 {
cristy99abff32011-12-24 20:45:16 +00001713 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001714 break;
1715 }
cristy99abff32011-12-24 20:45:16 +00001716 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1717 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001718 break;
1719 }
1720 case ColorDodgeCompositeOp:
1721 {
1722 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1723 {
cristy99abff32011-12-24 20:45:16 +00001724 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001725 break;
1726 }
1727 if (fabs(Sca-Sa) < MagickEpsilon)
1728 {
cristy99abff32011-12-24 20:45:16 +00001729 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001730 break;
1731 }
cristy99abff32011-12-24 20:45:16 +00001732 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001733 (1.0-Sa));
1734 break;
1735 }
1736 case ColorizeCompositeOp:
1737 {
cristy99abff32011-12-24 20:45:16 +00001738 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001739 {
1740 pixel=Dc;
1741 break;
1742 }
cristy99abff32011-12-24 20:45:16 +00001743 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001744 {
1745 pixel=Sc;
1746 break;
1747 }
cristyd8f16f72012-08-13 12:49:50 +00001748 CompositeHSL(destination_pixel.red,destination_pixel.green,
cristyddeeea22012-04-12 01:33:09 +00001749 destination_pixel.blue,&sans,&sans,&brightness);
cristyd8f16f72012-08-13 12:49:50 +00001750 CompositeHSL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001751 &hue,&saturation,&sans);
cristyd8f16f72012-08-13 12:49:50 +00001752 HSLComposite(hue,saturation,brightness,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001753 switch (channel)
1754 {
1755 case RedPixelChannel: pixel=red; break;
1756 case GreenPixelChannel: pixel=green; break;
1757 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001758 default: pixel=Dc; break;
1759 }
1760 break;
1761 }
cristye4a40472011-12-22 02:56:19 +00001762 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001763 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001764 {
cristy24d5d722012-05-17 12:27:27 +00001765 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001766 break;
1767 }
1768 case CopyBlackCompositeOp:
1769 {
cristyd197cbb2012-01-13 02:14:12 +00001770 if (channel == BlackPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001771 pixel=(double) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001772 break;
1773 }
1774 case CopyBlueCompositeOp:
1775 case CopyYellowCompositeOp:
1776 {
cristyd197cbb2012-01-13 02:14:12 +00001777 if (channel == BluePixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001778 pixel=(double) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001779 break;
1780 }
1781 case CopyGreenCompositeOp:
1782 case CopyMagentaCompositeOp:
1783 {
cristyd197cbb2012-01-13 02:14:12 +00001784 if (channel == GreenPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001785 pixel=(double) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001786 break;
1787 }
1788 case CopyRedCompositeOp:
1789 case CopyCyanCompositeOp:
1790 {
cristyd197cbb2012-01-13 02:14:12 +00001791 if (channel == RedPixelChannel)
cristya19f1d72012-08-07 18:24:38 +00001792 pixel=(double) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001793 break;
1794 }
cristy99abff32011-12-24 20:45:16 +00001795 case DarkenCompositeOp:
1796 {
1797 /*
1798 Darken is equivalent to a 'Minimum' method
1799 OR a greyscale version of a binary 'Or'
1800 OR the 'Intersection' of pixel sets.
1801 */
1802 if (Sc < Dc)
1803 {
1804 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1805 break;
1806 }
1807 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1808 break;
1809 }
cristye4a40472011-12-22 02:56:19 +00001810 case DarkenIntensityCompositeOp:
1811 {
cristy99abff32011-12-24 20:45:16 +00001812 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1813 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001814 break;
1815 }
1816 case DifferenceCompositeOp:
1817 {
1818 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1819 break;
1820 }
1821 case DissolveCompositeOp:
1822 {
1823 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1824 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1825 break;
1826 }
1827 case DivideDstCompositeOp:
1828 {
1829 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1830 {
cristy99abff32011-12-24 20:45:16 +00001831 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001832 break;
1833 }
1834 if (fabs(Dca) < MagickEpsilon)
1835 {
cristy99abff32011-12-24 20:45:16 +00001836 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001837 break;
1838 }
cristy99abff32011-12-24 20:45:16 +00001839 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001840 break;
1841 }
1842 case DivideSrcCompositeOp:
1843 {
1844 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1845 {
cristy99abff32011-12-24 20:45:16 +00001846 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001847 break;
1848 }
1849 if (fabs(Sca) < MagickEpsilon)
1850 {
cristy99abff32011-12-24 20:45:16 +00001851 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001852 break;
1853 }
cristy99abff32011-12-24 20:45:16 +00001854 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001855 break;
1856 }
1857 case DstAtopCompositeOp:
1858 {
1859 pixel=Dc*Da+Sc*(1.0-Da);
1860 break;
1861 }
1862 case DstCompositeOp:
1863 case NoCompositeOp:
1864 {
1865 pixel=Dc;
1866 break;
1867 }
1868 case DstInCompositeOp:
1869 {
1870 pixel=gamma*(Sa*Dc*Sa);
1871 break;
1872 }
1873 case DstOutCompositeOp:
1874 {
1875 pixel=gamma*(Da*Dc*(1.0-Sa));
1876 break;
1877 }
1878 case DstOverCompositeOp:
1879 {
1880 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1881 break;
1882 }
1883 case ExclusionCompositeOp:
1884 {
cristy99abff32011-12-24 20:45:16 +00001885 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1886 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001887 break;
1888 }
1889 case HardLightCompositeOp:
1890 {
1891 if ((2.0*Sca) < Sa)
1892 {
cristy99abff32011-12-24 20:45:16 +00001893 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001894 (1.0-Sa));
1895 break;
1896 }
cristy99abff32011-12-24 20:45:16 +00001897 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001898 Dca*(1.0-Sa));
1899 break;
1900 }
1901 case HueCompositeOp:
1902 {
cristy99abff32011-12-24 20:45:16 +00001903 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001904 {
1905 pixel=Dc;
1906 break;
1907 }
cristy99abff32011-12-24 20:45:16 +00001908 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001909 {
1910 pixel=Sc;
1911 break;
1912 }
cristyd8f16f72012-08-13 12:49:50 +00001913 CompositeHSL(destination_pixel.red,destination_pixel.green,
cristyddeeea22012-04-12 01:33:09 +00001914 destination_pixel.blue,&hue,&saturation,&brightness);
cristyd8f16f72012-08-13 12:49:50 +00001915 CompositeHSL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001916 &hue,&sans,&sans);
cristyd8f16f72012-08-13 12:49:50 +00001917 HSLComposite(hue,saturation,brightness,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001918 switch (channel)
1919 {
1920 case RedPixelChannel: pixel=red; break;
1921 case GreenPixelChannel: pixel=green; break;
1922 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001923 default: pixel=Dc; break;
1924 }
1925 break;
1926 }
1927 case InCompositeOp:
1928 case SrcInCompositeOp:
1929 {
1930 pixel=gamma*(Da*Sc*Da);
1931 break;
1932 }
cristy99abff32011-12-24 20:45:16 +00001933 case LinearBurnCompositeOp:
1934 {
1935 /*
1936 LinearBurn: as defined by Abode Photoshop, according to
1937 http://www.simplefilter.de/en/basics/mixmods.html is:
1938
1939 f(Sc,Dc) = Sc + Dc - 1
1940 */
1941 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1942 break;
1943 }
1944 case LinearDodgeCompositeOp:
1945 {
1946 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1947 break;
1948 }
1949 case LinearLightCompositeOp:
1950 {
1951 /*
1952 LinearLight: as defined by Abode Photoshop, according to
1953 http://www.simplefilter.de/en/basics/mixmods.html is:
1954
1955 f(Sc,Dc) = Dc + 2*Sc - 1
1956 */
1957 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1958 break;
1959 }
1960 case LightenCompositeOp:
1961 {
1962 if (Sc > Dc)
1963 {
cristy24d5d722012-05-17 12:27:27 +00001964 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00001965 break;
1966 }
cristy24d5d722012-05-17 12:27:27 +00001967 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
cristy99abff32011-12-24 20:45:16 +00001968 break;
1969 }
cristye4a40472011-12-22 02:56:19 +00001970 case LightenIntensityCompositeOp:
1971 {
1972 /*
1973 Lighten is equivalent to a 'Maximum' method
1974 OR a greyscale version of a binary 'And'
1975 OR the 'Union' of pixel sets.
1976 */
1977 pixel=Sa*GetPixelIntensity(composite_image,p) >
1978 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1979 break;
1980 }
cristye4a40472011-12-22 02:56:19 +00001981 case LuminizeCompositeOp:
1982 {
cristy99abff32011-12-24 20:45:16 +00001983 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001984 {
1985 pixel=Dc;
1986 break;
1987 }
cristy99abff32011-12-24 20:45:16 +00001988 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001989 {
1990 pixel=Sc;
1991 break;
1992 }
cristyd8f16f72012-08-13 12:49:50 +00001993 CompositeHSL(destination_pixel.red,destination_pixel.green,
cristyddeeea22012-04-12 01:33:09 +00001994 destination_pixel.blue,&hue,&saturation,&brightness);
cristyd8f16f72012-08-13 12:49:50 +00001995 CompositeHSL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001996 &sans,&sans,&brightness);
cristyd8f16f72012-08-13 12:49:50 +00001997 HSLComposite(hue,saturation,brightness,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001998 switch (channel)
1999 {
2000 case RedPixelChannel: pixel=red; break;
2001 case GreenPixelChannel: pixel=green; break;
2002 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002003 default: pixel=Dc; break;
2004 }
2005 break;
2006 }
2007 case MathematicsCompositeOp:
2008 {
2009 /*
2010 'Mathematics' a free form user control mathematical composition
2011 is defined as...
2012
2013 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2014
2015 Where the arguments A,B,C,D are (currently) passed to composite
2016 as a command separated 'geometry' string in "compose:args" image
2017 artifact.
2018
2019 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2020
2021 Applying the SVG transparency formula (see above), we get...
2022
2023 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2024
2025 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2026 Dca*(1.0-Sa)
2027 */
2028 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2029 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2030 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2031 break;
2032 }
2033 case MinusDstCompositeOp:
2034 {
2035 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2036 break;
2037 }
2038 case MinusSrcCompositeOp:
2039 {
2040 /*
2041 Minus source from destination.
2042
2043 f(Sc,Dc) = Sc - Dc
2044 */
cristy99abff32011-12-24 20:45:16 +00002045 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002046 break;
2047 }
2048 case ModulateCompositeOp:
2049 {
2050 ssize_t
2051 offset;
2052
cristy99abff32011-12-24 20:45:16 +00002053 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002054 {
2055 pixel=Dc;
2056 break;
2057 }
2058 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2059 if (offset == 0)
2060 {
2061 pixel=Dc;
2062 break;
2063 }
cristyd8f16f72012-08-13 12:49:50 +00002064 CompositeHSL(destination_pixel.red,destination_pixel.green,
cristyddeeea22012-04-12 01:33:09 +00002065 destination_pixel.blue,&hue,&saturation,&brightness);
cristye4a40472011-12-22 02:56:19 +00002066 brightness+=(0.01*percent_brightness*offset)/midpoint;
2067 saturation*=0.01*percent_saturation;
cristyd8f16f72012-08-13 12:49:50 +00002068 HSLComposite(hue,saturation,brightness,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002069 switch (channel)
2070 {
2071 case RedPixelChannel: pixel=red; break;
2072 case GreenPixelChannel: pixel=green; break;
2073 case BluePixelChannel: pixel=blue; break;
2074 default: pixel=Dc; break;
2075 }
2076 break;
2077 }
2078 case ModulusAddCompositeOp:
2079 {
2080 pixel=Sc+Dc;
2081 if (pixel > QuantumRange)
2082 pixel-=(QuantumRange+1.0);
2083 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2084 break;
2085 }
2086 case ModulusSubtractCompositeOp:
2087 {
cristy99abff32011-12-24 20:45:16 +00002088 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002089 if (pixel < 0.0)
2090 pixel+=(QuantumRange+1.0);
2091 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2092 break;
2093 }
2094 case MultiplyCompositeOp:
2095 {
cristy99abff32011-12-24 20:45:16 +00002096 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002097 break;
2098 }
2099 case OutCompositeOp:
2100 case SrcOutCompositeOp:
2101 {
2102 pixel=gamma*(Sa*Sc*(1.0-Da));
2103 break;
2104 }
2105 case OverCompositeOp:
2106 case SrcOverCompositeOp:
2107 {
cristy99abff32011-12-24 20:45:16 +00002108 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002109 break;
2110 }
2111 case OverlayCompositeOp:
2112 {
2113 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002114 {
2115 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2116 (1.0-Da));
2117 break;
2118 }
2119 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2120 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002121 break;
2122 }
2123 case PegtopLightCompositeOp:
2124 {
2125 /*
2126 PegTop: A Soft-Light alternative: A continuous version of the
2127 Softlight function, producing very similar results.
2128
2129 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2130
2131 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2132 */
2133 if (fabs(Da) < MagickEpsilon)
2134 {
cristy99abff32011-12-24 20:45:16 +00002135 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002136 break;
2137 }
cristy99abff32011-12-24 20:45:16 +00002138 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2139 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002140 break;
2141 }
2142 case PinLightCompositeOp:
2143 {
2144 /*
2145 PinLight: A Photoshop 7 composition method
2146 http://www.simplefilter.de/en/basics/mixmods.html
2147
2148 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2149 */
2150 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2151 {
cristy99abff32011-12-24 20:45:16 +00002152 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002153 break;
2154 }
2155 if ((Dca*Sa) > (2.0*Sca*Da))
2156 {
cristy99abff32011-12-24 20:45:16 +00002157 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002158 break;
2159 }
cristy99abff32011-12-24 20:45:16 +00002160 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002161 break;
2162 }
2163 case PlusCompositeOp:
2164 {
cristy24d5d722012-05-17 12:27:27 +00002165 pixel=gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002166 break;
2167 }
2168 case SaturateCompositeOp:
2169 {
cristy99abff32011-12-24 20:45:16 +00002170 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002171 {
2172 pixel=Dc;
2173 break;
2174 }
cristy99abff32011-12-24 20:45:16 +00002175 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002176 {
2177 pixel=Sc;
2178 break;
2179 }
cristyd8f16f72012-08-13 12:49:50 +00002180 CompositeHSL(destination_pixel.red,destination_pixel.green,
cristyddeeea22012-04-12 01:33:09 +00002181 destination_pixel.blue,&hue,&saturation,&brightness);
cristyd8f16f72012-08-13 12:49:50 +00002182 CompositeHSL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00002183 &sans,&saturation,&sans);
cristyd8f16f72012-08-13 12:49:50 +00002184 HSLComposite(hue,saturation,brightness,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002185 switch (channel)
2186 {
2187 case RedPixelChannel: pixel=red; break;
2188 case GreenPixelChannel: pixel=green; break;
2189 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002190 default: pixel=Dc; break;
2191 }
2192 break;
2193 }
2194 case ScreenCompositeOp:
2195 {
2196 /*
2197 Screen: a negated multiply:
2198
2199 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2200 */
cristy99abff32011-12-24 20:45:16 +00002201 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002202 break;
2203 }
2204 case SoftLightCompositeOp:
2205 {
2206 /*
2207 Refer to the March 2009 SVG specification.
2208 */
2209 if ((2.0*Sca) < Sa)
2210 {
cristy99abff32011-12-24 20:45:16 +00002211 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2212 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002213 break;
2214 }
2215 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2216 {
cristy99abff32011-12-24 20:45:16 +00002217 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2218 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2219 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002220 break;
2221 }
cristy99abff32011-12-24 20:45:16 +00002222 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2223 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002224 break;
2225 }
2226 case ThresholdCompositeOp:
2227 {
cristya19f1d72012-08-07 18:24:38 +00002228 double
cristye4a40472011-12-22 02:56:19 +00002229 delta;
2230
2231 delta=Sc-Dc;
cristya19f1d72012-08-07 18:24:38 +00002232 if ((double) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002233 {
2234 pixel=gamma*Dc;
2235 break;
2236 }
2237 pixel=gamma*(Dc+delta*amount);
2238 break;
2239 }
2240 case VividLightCompositeOp:
2241 {
2242 /*
2243 VividLight: A Photoshop 7 composition method. See
2244 http://www.simplefilter.de/en/basics/mixmods.html.
2245
2246 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2247 */
2248 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2249 {
cristy99abff32011-12-24 20:45:16 +00002250 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002251 break;
2252 }
2253 if ((2.0*Sca) <= Sa)
2254 {
cristy99abff32011-12-24 20:45:16 +00002255 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2256 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002257 break;
2258 }
cristy99abff32011-12-24 20:45:16 +00002259 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2260 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002261 break;
2262 }
2263 case XorCompositeOp:
2264 {
cristy99abff32011-12-24 20:45:16 +00002265 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002266 break;
2267 }
2268 default:
2269 {
2270 pixel=Sc;
2271 break;
2272 }
2273 }
2274 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002275 }
cristyed231572011-07-14 02:18:59 +00002276 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002277 channels=GetPixelChannels(composite_image);
2278 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002279 p=pixels;
cristyed231572011-07-14 02:18:59 +00002280 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002281 }
2282 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2283 status=MagickFalse;
2284 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2285 {
2286 MagickBooleanType
2287 proceed;
2288
2289#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002290 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002291#endif
2292 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2293 image->rows);
2294 if (proceed == MagickFalse)
2295 status=MagickFalse;
2296 }
2297 }
2298 composite_view=DestroyCacheView(composite_view);
2299 image_view=DestroyCacheView(image_view);
2300 if (destination_image != (Image * ) NULL)
2301 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002302 else
2303 composite_image=DestroyImage(composite_image);
cristy191c0b72012-08-12 16:29:52 +00002304 if (status != MagickFalse)
2305 (void) ClampImage(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00002306 return(status);
2307}
2308
2309/*
2310%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2311% %
2312% %
2313% %
2314% T e x t u r e I m a g e %
2315% %
2316% %
2317% %
2318%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2319%
2320% TextureImage() repeatedly tiles the texture image across and down the image
2321% canvas.
2322%
2323% The format of the TextureImage method is:
2324%
cristy30d8c942012-02-07 13:44:59 +00002325% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002326% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002327%
2328% A description of each parameter follows:
2329%
2330% o image: the image.
2331%
cristye6178502011-12-23 17:02:29 +00002332% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002333%
2334*/
cristy30d8c942012-02-07 13:44:59 +00002335MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2336 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002337{
2338#define TextureImageTag "Texture/Image"
2339
2340 CacheView
2341 *image_view,
2342 *texture_view;
2343
cristy30d8c942012-02-07 13:44:59 +00002344 Image
2345 *texture_image;
2346
cristy4c08aed2011-07-01 19:47:50 +00002347 MagickBooleanType
2348 status;
2349
2350 ssize_t
2351 y;
2352
2353 assert(image != (Image *) NULL);
2354 if (image->debug != MagickFalse)
2355 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2356 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002357 if (texture == (const Image *) NULL)
2358 return(MagickFalse);
2359 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2360 return(MagickFalse);
2361 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002362 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002363 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002364 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002365 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2366 exception);
cristy4c08aed2011-07-01 19:47:50 +00002367 status=MagickTrue;
2368 if ((image->compose != CopyCompositeOp) &&
2369 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002370 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002371 {
2372 /*
2373 Tile texture onto the image background.
2374 */
2375#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002376 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002377 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002378#endif
cristye6178502011-12-23 17:02:29 +00002379 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002380 {
2381 register ssize_t
2382 x;
2383
2384 if (status == MagickFalse)
2385 continue;
cristye6178502011-12-23 17:02:29 +00002386 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002387 {
2388 MagickBooleanType
2389 thread_status;
2390
cristyfeb3e962012-03-29 17:25:55 +00002391 thread_status=CompositeImage(image,texture_image,image->compose,
2392 MagickFalse,x+texture_image->tile_offset.x,y+
2393 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002394 if (thread_status == MagickFalse)
2395 {
2396 status=thread_status;
2397 break;
2398 }
2399 }
2400 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2401 {
2402 MagickBooleanType
2403 proceed;
2404
2405#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002406 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002407#endif
2408 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2409 y,image->rows);
2410 if (proceed == MagickFalse)
2411 status=MagickFalse;
2412 }
2413 }
2414 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2415 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002416 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002417 return(status);
2418 }
2419 /*
2420 Tile texture onto the image background (optimized).
2421 */
2422 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +00002423 texture_view=AcquireVirtualCacheView(texture_image,exception);
2424 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00002425#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00002426 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +00002427 dynamic_number_threads(image,image->columns,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00002428#endif
2429 for (y=0; y < (ssize_t) image->rows; y++)
2430 {
2431 MagickBooleanType
2432 sync;
2433
2434 register const Quantum
2435 *p,
2436 *pixels;
2437
2438 register ssize_t
2439 x;
2440
2441 register Quantum
2442 *q;
2443
2444 size_t
2445 width;
2446
2447 if (status == MagickFalse)
2448 continue;
cristye6178502011-12-23 17:02:29 +00002449 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2450 (y+texture_image->tile_offset.y) % texture_image->rows,
2451 texture_image->columns,1,exception);
2452 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002453 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2454 {
2455 status=MagickFalse;
2456 continue;
2457 }
cristye6178502011-12-23 17:02:29 +00002458 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002459 {
2460 register ssize_t
cristye6178502011-12-23 17:02:29 +00002461 j;
cristy4c08aed2011-07-01 19:47:50 +00002462
2463 p=pixels;
cristye6178502011-12-23 17:02:29 +00002464 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002465 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2466 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002467 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002468 {
cristye6178502011-12-23 17:02:29 +00002469 register ssize_t
2470 i;
2471
cristy10a6c612012-01-29 21:41:05 +00002472 if (GetPixelMask(image,p) != 0)
2473 {
2474 p+=GetPixelChannels(texture_image);
2475 q+=GetPixelChannels(image);
2476 continue;
2477 }
cristye6178502011-12-23 17:02:29 +00002478 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2479 {
2480 PixelChannel
2481 channel;
2482
2483 PixelTrait
2484 texture_traits,
2485 traits;
2486
2487 channel=GetPixelChannelMapChannel(texture_image,i);
2488 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2489 traits=GetPixelChannelMapTraits(image,channel);
2490 if ((traits == UndefinedPixelTrait) ||
2491 (texture_traits == UndefinedPixelTrait))
2492 continue;
2493 SetPixelChannel(image,channel,p[i],q);
2494 }
2495 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002496 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002497 }
2498 }
2499 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2500 if (sync == MagickFalse)
2501 status=MagickFalse;
2502 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2503 {
2504 MagickBooleanType
2505 proceed;
2506
2507#if defined(MAGICKCORE_OPENMP_SUPPORT)
2508 #pragma omp critical (MagickCore_TextureImage)
2509#endif
2510 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2511 image->rows);
2512 if (proceed == MagickFalse)
2513 status=MagickFalse;
2514 }
2515 }
2516 texture_view=DestroyCacheView(texture_view);
2517 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002518 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002519 return(status);
2520}