blob: 0804cd9b41c8a37e7ad9048218cdbf4cbd1986a1 [file] [log] [blame]
cristy4c08aed2011-07-01 19:47:50 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7% C O O MM MM P P O O SS I T E %
8% C O O M M M PPPP O O SSS I T EEE %
9% C O O M M P O O SS I T E %
10% CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
11% %
12% %
13% MagickCore Image Composite Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy4c08aed2011-07-01 19:47:50 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/cache.h"
cristy52010022011-10-21 18:07:37 +000046#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/cache-view.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/colorspace-private.h"
53#include "MagickCore/composite.h"
54#include "MagickCore/composite-private.h"
55#include "MagickCore/constitute.h"
56#include "MagickCore/draw.h"
57#include "MagickCore/fx.h"
58#include "MagickCore/gem.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/image.h"
61#include "MagickCore/image-private.h"
62#include "MagickCore/list.h"
63#include "MagickCore/log.h"
64#include "MagickCore/monitor.h"
65#include "MagickCore/monitor-private.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/option.h"
68#include "MagickCore/pixel-accessor.h"
69#include "MagickCore/property.h"
70#include "MagickCore/quantum.h"
71#include "MagickCore/resample.h"
72#include "MagickCore/resource_.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/thread-private.h"
cristy63a81872012-03-22 15:52:52 +000075#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000076#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000077#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000078#include "MagickCore/version.h"
79
80/*
81%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
82% %
83% %
84% %
cristyf4ad9df2011-07-08 16:49:03 +000085% C o m p o s i t e I m a g e %
cristy4c08aed2011-07-01 19:47:50 +000086% %
87% %
88% %
89%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
90%
cristyf4ad9df2011-07-08 16:49:03 +000091% CompositeImage() returns the second image composited onto the first
cristy4c08aed2011-07-01 19:47:50 +000092% at the specified offset, using the specified composite method.
93%
cristyf4ad9df2011-07-08 16:49:03 +000094% The format of the CompositeImage method is:
cristy4c08aed2011-07-01 19:47:50 +000095%
96% MagickBooleanType CompositeImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +000097% const Image *composite_image,const CompositeOperator compose,
98% const MagickBooleanType clip_to_self,const ssize_t x_offset,
99% const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000100%
101% A description of each parameter follows:
102%
103% o image: the destination image, modified by he composition
104%
cristyfeb3e962012-03-29 17:25:55 +0000105% o composite_image: the composite (source) image.
106%
cristy4c08aed2011-07-01 19:47:50 +0000107% o compose: This operator affects how the composite is applied to
108% the image. The operators and how they are utilized are listed here
109% http://www.w3.org/TR/SVG12/#compositing.
110%
cristyfeb3e962012-03-29 17:25:55 +0000111% o clip_to_self: set to MagickTrue to limit composition to area composed.
cristy4c08aed2011-07-01 19:47:50 +0000112%
113% o x_offset: the column offset of the composited image.
114%
115% o y_offset: the row offset of the composited image.
116%
117% Extra Controls from Image meta-data in 'composite_image' (artifacts)
118%
119% o "compose:args"
120% A string containing extra numerical arguments for specific compose
121% methods, generally expressed as a 'geometry' or a comma separated list
122% of numbers.
123%
124% Compose methods needing such arguments include "BlendCompositeOp" and
125% "DisplaceCompositeOp".
126%
cristye941a752011-10-15 01:52:48 +0000127% o exception: return any errors or warnings in this structure.
128%
cristy4c08aed2011-07-01 19:47:50 +0000129*/
130
cristye4a40472011-12-22 02:56:19 +0000131static void CompositeHSB(const Quantum red,const Quantum green,
132 const Quantum blue,double *hue,double *saturation,double *brightness)
cristy4c08aed2011-07-01 19:47:50 +0000133{
cristye4a40472011-12-22 02:56:19 +0000134 double
135 delta;
cristy4c08aed2011-07-01 19:47:50 +0000136
cristye4a40472011-12-22 02:56:19 +0000137 Quantum
cristy4c08aed2011-07-01 19:47:50 +0000138 max,
139 min;
140
141 /*
142 Convert RGB to HSB colorspace.
143 */
144 assert(hue != (double *) NULL);
145 assert(saturation != (double *) NULL);
146 assert(brightness != (double *) NULL);
147 max=(red > green ? red : green);
148 if (blue > max)
149 max=blue;
150 min=(red < green ? red : green);
151 if (blue < min)
152 min=blue;
153 *hue=0.0;
154 *saturation=0.0;
155 *brightness=(double) (QuantumScale*max);
cristye4a40472011-12-22 02:56:19 +0000156 if (fabs((double) max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000157 return;
158 *saturation=(double) (1.0-min/max);
cristye4a40472011-12-22 02:56:19 +0000159 delta=(MagickRealType) max-min;
cristyaa83c2c2011-09-21 13:36:25 +0000160 if (fabs(delta) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000161 return;
cristye4a40472011-12-22 02:56:19 +0000162 if (fabs((double) red-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000163 *hue=(double) ((green-blue)/delta);
164 else
cristye4a40472011-12-22 02:56:19 +0000165 if (fabs((double) green-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000166 *hue=(double) (2.0+(blue-red)/delta);
167 else
cristye4a40472011-12-22 02:56:19 +0000168 if (fabs((double) blue-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000169 *hue=(double) (4.0+(red-green)/delta);
170 *hue/=6.0;
171 if (*hue < 0.0)
172 *hue+=1.0;
173}
174
cristy4c08aed2011-07-01 19:47:50 +0000175static void HSBComposite(const double hue,const double saturation,
cristye4a40472011-12-22 02:56:19 +0000176 const double brightness,double *red,double *green,double *blue)
cristy4c08aed2011-07-01 19:47:50 +0000177{
cristya96f2492011-12-14 18:25:41 +0000178 double
cristy4c08aed2011-07-01 19:47:50 +0000179 f,
180 h,
181 p,
182 q,
183 t;
184
185 /*
186 Convert HSB to RGB colorspace.
187 */
cristya96f2492011-12-14 18:25:41 +0000188 assert(red != (double *) NULL);
189 assert(green != (double *) NULL);
190 assert(blue != (double *) NULL);
cristy4c08aed2011-07-01 19:47:50 +0000191 if (saturation == 0.0)
192 {
cristya96f2492011-12-14 18:25:41 +0000193 *red=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000194 *green=(*red);
195 *blue=(*red);
196 return;
197 }
198 h=6.0*(hue-floor(hue));
199 f=h-floor((double) h);
200 p=brightness*(1.0-saturation);
201 q=brightness*(1.0-saturation*f);
202 t=brightness*(1.0-saturation*(1.0-f));
203 switch ((int) h)
204 {
205 case 0:
206 default:
207 {
cristya96f2492011-12-14 18:25:41 +0000208 *red=(double) QuantumRange*brightness;
209 *green=(double) QuantumRange*t;
210 *blue=(double) QuantumRange*p;
cristy4c08aed2011-07-01 19:47:50 +0000211 break;
212 }
213 case 1:
214 {
cristya96f2492011-12-14 18:25:41 +0000215 *red=(double) QuantumRange*q;
216 *green=(double) QuantumRange*brightness;
217 *blue=(double) QuantumRange*p;
cristy4c08aed2011-07-01 19:47:50 +0000218 break;
219 }
220 case 2:
221 {
cristya96f2492011-12-14 18:25:41 +0000222 *red=(double) QuantumRange*p;
223 *green=(double) QuantumRange*brightness;
224 *blue=(double) QuantumRange*t;
cristy4c08aed2011-07-01 19:47:50 +0000225 break;
226 }
227 case 3:
228 {
cristya96f2492011-12-14 18:25:41 +0000229 *red=(double) QuantumRange*p;
230 *green=(double) QuantumRange*q;
231 *blue=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000232 break;
233 }
234 case 4:
235 {
cristya96f2492011-12-14 18:25:41 +0000236 *red=(double) QuantumRange*t;
237 *green=(double) QuantumRange*p;
238 *blue=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000239 break;
240 }
241 case 5:
242 {
cristya96f2492011-12-14 18:25:41 +0000243 *red=(double) QuantumRange*brightness;
244 *green=(double) QuantumRange*p;
245 *blue=(double) QuantumRange*q;
cristy4c08aed2011-07-01 19:47:50 +0000246 break;
247 }
248 }
249}
250
cristye4a40472011-12-22 02:56:19 +0000251static inline double MagickMin(const double x,const double y)
252{
253 if (x < y)
254 return(x);
255 return(y);
256}
257static inline double MagickMax(const double x,const double y)
258{
259 if (x > y)
260 return(x);
261 return(y);
262}
263
264static MagickBooleanType CompositeOverImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000265 const Image *composite_image,const MagickBooleanType clip_to_self,
266 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristye4a40472011-12-22 02:56:19 +0000267{
268#define CompositeImageTag "Composite/Image"
269
270 CacheView
271 *composite_view,
272 *image_view;
273
cristye4a40472011-12-22 02:56:19 +0000274 MagickBooleanType
cristye4a40472011-12-22 02:56:19 +0000275 status;
276
277 MagickOffsetType
278 progress;
279
280 ssize_t
281 y;
282
cristye4a40472011-12-22 02:56:19 +0000283 /*
cristye4a40472011-12-22 02:56:19 +0000284 Composite image.
285 */
286 status=MagickTrue;
287 progress=0;
288 image_view=AcquireCacheView(image);
289 composite_view=AcquireCacheView(composite_image);
290#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000291 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye4a40472011-12-22 02:56:19 +0000292#endif
293 for (y=0; y < (ssize_t) image->rows; y++)
294 {
295 const Quantum
296 *pixels;
297
298 register const Quantum
299 *restrict p;
300
301 register Quantum
302 *restrict q;
303
304 register ssize_t
305 x;
306
cristy564a5692012-01-20 23:56:26 +0000307 size_t
308 channels;
309
cristye4a40472011-12-22 02:56:19 +0000310 if (status == MagickFalse)
311 continue;
cristyfeb3e962012-03-29 17:25:55 +0000312 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000313 {
314 if (y < y_offset)
315 continue;
316 if ((y-y_offset) >= (ssize_t) composite_image->rows)
317 continue;
318 }
319 /*
320 If pixels is NULL, y is outside overlay region.
321 */
322 pixels=(Quantum *) NULL;
323 p=(Quantum *) NULL;
324 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
325 {
326 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
327 composite_image->columns,1,exception);
328 if (p == (const Quantum *) NULL)
329 {
330 status=MagickFalse;
331 continue;
332 }
333 pixels=p;
334 if (x_offset < 0)
335 p-=x_offset*GetPixelChannels(composite_image);
336 }
337 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
338 if (q == (Quantum *) NULL)
339 {
340 status=MagickFalse;
341 continue;
342 }
343 for (x=0; x < (ssize_t) image->columns; x++)
344 {
345 MagickRealType
346 alpha,
347 Da,
348 Dc,
349 gamma,
350 Sa,
351 Sc;
352
353 register ssize_t
354 i;
355
cristyfeb3e962012-03-29 17:25:55 +0000356 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000357 {
358 if (x < x_offset)
359 {
360 q+=GetPixelChannels(image);
361 continue;
362 }
363 if ((x-x_offset) >= (ssize_t) composite_image->columns)
364 break;
365 }
366 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
367 ((x-x_offset) >= (ssize_t) composite_image->columns))
368 {
369 Quantum
370 source[MaxPixelChannels];
371
372 /*
373 Virtual composite:
374 Sc: source color.
375 Dc: destination color.
376 */
cristy10a6c612012-01-29 21:41:05 +0000377 if (GetPixelMask(image,q) != 0)
378 {
379 q+=GetPixelChannels(image);
380 continue;
381 }
cristyc94ba6f2012-01-29 23:19:58 +0000382 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
383 source,exception);
cristye4a40472011-12-22 02:56:19 +0000384 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
385 {
386 PixelChannel
387 channel;
388
389 PixelTrait
390 composite_traits,
391 traits;
392
393 channel=GetPixelChannelMapChannel(image,i);
394 traits=GetPixelChannelMapTraits(image,channel);
cristy10a6c612012-01-29 21:41:05 +0000395 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000396 if ((traits == UndefinedPixelTrait) ||
397 (composite_traits == UndefinedPixelTrait))
398 continue;
399 q[i]=source[channel];
400 }
401 q+=GetPixelChannels(image);
402 continue;
403 }
404 /*
405 Authentic composite:
406 Sa: normalized source alpha.
407 Da: normalized destination alpha.
408 */
cristyc94ba6f2012-01-29 23:19:58 +0000409 if (GetPixelMask(composite_image,p) != 0)
410 {
411 p+=GetPixelChannels(composite_image);
412 channels=GetPixelChannels(composite_image);
413 if (p >= (pixels+channels*composite_image->columns))
414 p=pixels;
415 q+=GetPixelChannels(image);
416 continue;
417 }
cristye4a40472011-12-22 02:56:19 +0000418 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
419 Da=QuantumScale*GetPixelAlpha(image,q);
420 alpha=Sa*(-Da)+Sa+Da;
421 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
422 {
423 PixelChannel
424 channel;
425
426 PixelTrait
427 composite_traits,
428 traits;
429
430 channel=GetPixelChannelMapChannel(image,i);
431 traits=GetPixelChannelMapTraits(image,channel);
432 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
433 if ((traits == UndefinedPixelTrait) ||
434 (composite_traits == UndefinedPixelTrait))
435 continue;
436 if ((traits & CopyPixelTrait) != 0)
437 {
438 if (channel != AlphaPixelChannel)
439 {
440 /*
441 Copy channel.
442 */
443 q[i]=GetPixelChannel(composite_image,channel,p);
444 continue;
445 }
446 /*
447 Set alpha channel.
448 */
449 q[i]=ClampToQuantum(QuantumRange*alpha);
450 continue;
451 }
452 /*
453 Sc: source color.
454 Dc: destination color.
455 */
456 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
457 Dc=(MagickRealType) q[i];
458 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
459 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
460 }
461 p+=GetPixelChannels(composite_image);
462 channels=GetPixelChannels(composite_image);
463 if (p >= (pixels+channels*composite_image->columns))
464 p=pixels;
465 q+=GetPixelChannels(image);
466 }
467 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
468 status=MagickFalse;
469 if (image->progress_monitor != (MagickProgressMonitor) NULL)
470 {
471 MagickBooleanType
472 proceed;
473
474#if defined(MAGICKCORE_OPENMP_SUPPORT)
475 #pragma omp critical (MagickCore_CompositeImage)
476#endif
477 proceed=SetImageProgress(image,CompositeImageTag,progress++,
478 image->rows);
479 if (proceed == MagickFalse)
480 status=MagickFalse;
481 }
482 }
483 composite_view=DestroyCacheView(composite_view);
484 image_view=DestroyCacheView(image_view);
485 return(status);
486}
487
cristy4c08aed2011-07-01 19:47:50 +0000488MagickExport MagickBooleanType CompositeImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000489 const Image *composite_image,const CompositeOperator compose,
cristy44c98412012-03-31 18:25:40 +0000490 const MagickBooleanType clip_to_self,const ssize_t x_offset,
491 const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000492{
cristy4c08aed2011-07-01 19:47:50 +0000493#define CompositeImageTag "Composite/Image"
494
495 CacheView
496 *composite_view,
497 *image_view;
498
cristy4c08aed2011-07-01 19:47:50 +0000499 GeometryInfo
500 geometry_info;
501
502 Image
503 *destination_image;
504
505 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000506 status;
507
508 MagickOffsetType
509 progress;
510
cristy4c08aed2011-07-01 19:47:50 +0000511 MagickRealType
512 amount,
513 destination_dissolve,
514 midpoint,
515 percent_brightness,
516 percent_saturation,
517 source_dissolve,
518 threshold;
519
520 MagickStatusType
521 flags;
522
cristyd197cbb2012-01-13 02:14:12 +0000523 ssize_t
524 y;
525
cristy4c08aed2011-07-01 19:47:50 +0000526 /*
cristye4a40472011-12-22 02:56:19 +0000527 Composition based on the SVG specification:
528
529 A Composition is defined by...
530 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
531 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
532 Y = 1 for source preserved
533 Z = 1 for destination preserved
534
535 Conversion to transparency (then optimized)
536 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
537 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
538
539 Where...
540 Sca = Sc*Sa normalized Source color divided by Source alpha
541 Dca = Dc*Da normalized Dest color divided by Dest alpha
542 Dc' = Dca'/Da' the desired color value for this channel.
543
544 Da' in in the follow formula as 'gamma' The resulting alpla value.
545
546 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
547 the following optimizations...
548 gamma = Sa+Da-Sa*Da;
549 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
550 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
551
552 The above SVG definitions also definate that Mathematical Composition
553 methods should use a 'Over' blending mode for Alpha Channel.
554 It however was not applied for composition modes of 'Plus', 'Minus',
555 the modulus versions of 'Add' and 'Subtract'.
556
557 Mathematical operator changes to be applied from IM v6.7...
558
559 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
560 'ModulusAdd' and 'ModulusSubtract' for clarity.
561
562 2) All mathematical compositions work as per the SVG specification
563 with regard to blending. This now includes 'ModulusAdd' and
564 'ModulusSubtract'.
565
566 3) When the special channel flag 'sync' (syncronize channel updates)
567 is turned off (enabled by default) then mathematical compositions are
568 only performed on the channels specified, and are applied
569 independantally of each other. In other words the mathematics is
570 performed as 'pure' mathematical operations, rather than as image
571 operations.
cristy4c08aed2011-07-01 19:47:50 +0000572 */
573 assert(image != (Image *) NULL);
574 assert(image->signature == MagickSignature);
575 if (image->debug != MagickFalse)
576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
577 assert(composite_image != (Image *) NULL);
578 assert(composite_image->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000579 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000580 return(MagickFalse);
cristy9e210442012-04-08 22:31:14 +0000581 if ((IsGrayColorspace(image->colorspace) != MagickFalse) &&
582 (IsGrayColorspace(composite_image->colorspace) == MagickFalse))
583 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristye4a40472011-12-22 02:56:19 +0000584 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
585 {
cristyfeb3e962012-03-29 17:25:55 +0000586 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
587 y_offset,exception);
cristye4a40472011-12-22 02:56:19 +0000588 return(status);
589 }
cristy4c08aed2011-07-01 19:47:50 +0000590 destination_image=(Image *) NULL;
591 amount=0.5;
592 destination_dissolve=1.0;
cristy4c08aed2011-07-01 19:47:50 +0000593 percent_brightness=100.0;
594 percent_saturation=100.0;
595 source_dissolve=1.0;
596 threshold=0.05f;
597 switch (compose)
598 {
cristy4c08aed2011-07-01 19:47:50 +0000599 case CopyCompositeOp:
600 {
601 if ((x_offset < 0) || (y_offset < 0))
602 break;
603 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
604 break;
605 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
606 break;
607 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000608 image_view=AcquireCacheView(image);
609 composite_view=AcquireCacheView(composite_image);
610#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000611 #pragma omp parallel for schedule(static,4) shared(status)
cristy4c08aed2011-07-01 19:47:50 +0000612#endif
613 for (y=0; y < (ssize_t) composite_image->rows; y++)
614 {
615 MagickBooleanType
616 sync;
617
618 register const Quantum
619 *p;
620
621 register Quantum
622 *q;
623
624 register ssize_t
625 x;
626
627 if (status == MagickFalse)
628 continue;
629 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
630 1,exception);
631 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
632 composite_image->columns,1,exception);
633 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
634 {
635 status=MagickFalse;
636 continue;
637 }
638 for (x=0; x < (ssize_t) composite_image->columns; x++)
639 {
cristybdecccc2011-12-24 22:52:16 +0000640 register ssize_t
641 i;
642
cristy10a6c612012-01-29 21:41:05 +0000643 if (GetPixelMask(image,p) != 0)
644 {
645 p+=GetPixelChannels(composite_image);
646 q+=GetPixelChannels(image);
647 continue;
648 }
cristybdecccc2011-12-24 22:52:16 +0000649 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
650 {
651 PixelChannel
652 channel;
653
654 PixelTrait
655 composite_traits,
656 traits;
657
658 channel=GetPixelChannelMapChannel(composite_image,i);
659 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
660 traits=GetPixelChannelMapTraits(image,channel);
661 if ((traits == UndefinedPixelTrait) ||
662 (composite_traits == UndefinedPixelTrait))
663 continue;
664 SetPixelChannel(image,channel,p[i],q);
665 }
cristyed231572011-07-14 02:18:59 +0000666 p+=GetPixelChannels(composite_image);
667 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000668 }
669 sync=SyncCacheViewAuthenticPixels(image_view,exception);
670 if (sync == MagickFalse)
671 status=MagickFalse;
672 if (image->progress_monitor != (MagickProgressMonitor) NULL)
673 {
674 MagickBooleanType
675 proceed;
676
677#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000678 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000679#endif
680 proceed=SetImageProgress(image,CompositeImageTag,
681 (MagickOffsetType) y,image->rows);
682 if (proceed == MagickFalse)
683 status=MagickFalse;
684 }
685 }
686 composite_view=DestroyCacheView(composite_view);
687 image_view=DestroyCacheView(image_view);
688 return(status);
689 }
cristye4a40472011-12-22 02:56:19 +0000690 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000691 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000692 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000693 {
694 /*
695 Modify destination outside the overlaid region and require an alpha
696 channel to exist, to add transparency.
697 */
698 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +0000699 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000700 break;
701 }
702 case BlurCompositeOp:
703 {
704 CacheView
705 *composite_view,
706 *destination_view;
707
cristyfeb3e962012-03-29 17:25:55 +0000708 const char
709 *value;
710
cristy4c08aed2011-07-01 19:47:50 +0000711 PixelInfo
712 pixel;
713
714 MagickRealType
715 angle_range,
716 angle_start,
717 height,
718 width;
719
720 ResampleFilter
721 *resample_filter;
722
723 SegmentInfo
724 blur;
725
726 /*
727 Blur Image dictated by an overlay gradient map: X = red_channel;
728 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
729 */
730 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000731 exception);
cristy4c08aed2011-07-01 19:47:50 +0000732 if (destination_image == (Image *) NULL)
733 return(MagickFalse);
734 /*
735 Determine the horizontal and vertical maximim blur.
736 */
737 SetGeometryInfo(&geometry_info);
738 flags=NoValue;
739 value=GetImageArtifact(composite_image,"compose:args");
740 if (value != (char *) NULL)
741 flags=ParseGeometry(value,&geometry_info);
742 if ((flags & WidthValue) == 0 )
743 {
744 destination_image=DestroyImage(destination_image);
745 return(MagickFalse);
746 }
747 width=geometry_info.rho;
748 height=geometry_info.sigma;
749 blur.x1=geometry_info.rho;
750 blur.x2=0.0;
751 blur.y1=0.0;
752 blur.y2=geometry_info.sigma;
753 angle_start=0.0;
754 angle_range=0.0;
755 if ((flags & HeightValue) == 0)
756 blur.y2=blur.x1;
757 if ((flags & XValue) != 0 )
758 {
759 MagickRealType
760 angle;
761
762 angle=DegreesToRadians(geometry_info.xi);
763 blur.x1=width*cos(angle);
764 blur.x2=width*sin(angle);
765 blur.y1=(-height*sin(angle));
766 blur.y2=height*cos(angle);
767 }
768 if ((flags & YValue) != 0 )
769 {
770 angle_start=DegreesToRadians(geometry_info.xi);
771 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
772 }
773 /*
774 Blur Image by resampling.
anthonyf46d4262012-03-26 03:30:34 +0000775 FUTURE: this is currently broken, especially for small sigma blurs
776 This needs to be fixed to use a non-user filter setup that provides
777 far more control than currently available.
cristy4c08aed2011-07-01 19:47:50 +0000778 */
cristy8a11cb12011-10-19 23:53:34 +0000779 resample_filter=AcquireResampleFilter(image,exception);
anthonyf46d4262012-03-26 03:30:34 +0000780 SetResampleFilter(resample_filter,CubicFilter); /* was blur*2 */
cristy4c08aed2011-07-01 19:47:50 +0000781 destination_view=AcquireCacheView(destination_image);
782 composite_view=AcquireCacheView(composite_image);
783 for (y=0; y < (ssize_t) composite_image->rows; y++)
784 {
785 MagickBooleanType
786 sync;
787
788 register const Quantum
789 *restrict p;
790
791 register Quantum
792 *restrict q;
793
794 register ssize_t
795 x;
796
797 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
798 continue;
799 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
800 1,exception);
801 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000802 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000803 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
804 break;
805 for (x=0; x < (ssize_t) composite_image->columns; x++)
806 {
807 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
808 {
cristyed231572011-07-14 02:18:59 +0000809 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000810 continue;
811 }
812 if (fabs(angle_range) > MagickEpsilon)
813 {
814 MagickRealType
815 angle;
816
817 angle=angle_start+angle_range*QuantumScale*
818 GetPixelBlue(composite_image,p);
819 blur.x1=width*cos(angle);
820 blur.x2=width*sin(angle);
821 blur.y1=(-height*sin(angle));
822 blur.y2=height*cos(angle);
823 }
824 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
825 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
826 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
827 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
828 GetPixelGreen(composite_image,p));
829 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
830 (double) y_offset+y,&pixel);
cristy803640d2011-11-17 02:11:32 +0000831 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000832 p+=GetPixelChannels(composite_image);
833 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000834 }
835 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
836 if (sync == MagickFalse)
837 break;
838 }
839 resample_filter=DestroyResampleFilter(resample_filter);
840 composite_view=DestroyCacheView(composite_view);
841 destination_view=DestroyCacheView(destination_view);
842 composite_image=destination_image;
843 break;
844 }
845 case DisplaceCompositeOp:
846 case DistortCompositeOp:
847 {
848 CacheView
849 *composite_view,
850 *destination_view,
851 *image_view;
852
cristyfeb3e962012-03-29 17:25:55 +0000853 const char
854 *value;
855
cristy4c08aed2011-07-01 19:47:50 +0000856 PixelInfo
857 pixel;
858
859 MagickRealType
860 horizontal_scale,
861 vertical_scale;
862
863 PointInfo
864 center,
865 offset;
866
867 /*
868 Displace/Distort based on overlay gradient map:
869 X = red_channel; Y = green_channel;
870 compose:args = x_scale[,y_scale[,center.x,center.y]]
871 */
872 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000873 exception);
cristy4c08aed2011-07-01 19:47:50 +0000874 if (destination_image == (Image *) NULL)
875 return(MagickFalse);
876 SetGeometryInfo(&geometry_info);
877 flags=NoValue;
878 value=GetImageArtifact(composite_image,"compose:args");
879 if (value != (char *) NULL)
880 flags=ParseGeometry(value,&geometry_info);
881 if ((flags & (WidthValue|HeightValue)) == 0 )
882 {
883 if ((flags & AspectValue) == 0)
884 {
885 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
886 2.0;
887 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
888 }
889 else
890 {
891 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
892 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
893 }
894 }
895 else
896 {
897 horizontal_scale=geometry_info.rho;
898 vertical_scale=geometry_info.sigma;
899 if ((flags & PercentValue) != 0)
900 {
901 if ((flags & AspectValue) == 0)
902 {
903 horizontal_scale*=(composite_image->columns-1.0)/200.0;
904 vertical_scale*=(composite_image->rows-1.0)/200.0;
905 }
906 else
907 {
908 horizontal_scale*=(image->columns-1.0)/200.0;
909 vertical_scale*=(image->rows-1.0)/200.0;
910 }
911 }
912 if ((flags & HeightValue) == 0)
913 vertical_scale=horizontal_scale;
914 }
915 /*
916 Determine fixed center point for absolute distortion map
917 Absolute distort ==
918 Displace offset relative to a fixed absolute point
919 Select that point according to +X+Y user inputs.
920 default = center of overlay image
921 arg flag '!' = locations/percentage relative to background image
922 */
923 center.x=(MagickRealType) x_offset;
924 center.y=(MagickRealType) y_offset;
925 if (compose == DistortCompositeOp)
926 {
927 if ((flags & XValue) == 0)
928 if ((flags & AspectValue) == 0)
929 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
930 2.0;
931 else
932 center.x=((MagickRealType) image->columns-1)/2.0;
933 else
934 if ((flags & AspectValue) == 0)
935 center.x=(MagickRealType) x_offset+geometry_info.xi;
936 else
937 center.x=geometry_info.xi;
938 if ((flags & YValue) == 0)
939 if ((flags & AspectValue) == 0)
940 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
941 else
942 center.y=((MagickRealType) image->rows-1)/2.0;
943 else
944 if ((flags & AspectValue) == 0)
945 center.y=(MagickRealType) y_offset+geometry_info.psi;
946 else
947 center.y=geometry_info.psi;
948 }
949 /*
950 Shift the pixel offset point as defined by the provided,
951 displacement/distortion map. -- Like a lens...
952 */
cristye10859a2011-12-18 22:28:59 +0000953 GetPixelInfo(image,&pixel);
cristy4c08aed2011-07-01 19:47:50 +0000954 image_view=AcquireCacheView(image);
955 destination_view=AcquireCacheView(destination_image);
956 composite_view=AcquireCacheView(composite_image);
957 for (y=0; y < (ssize_t) composite_image->rows; y++)
958 {
959 MagickBooleanType
960 sync;
961
962 register const Quantum
963 *restrict p;
964
965 register Quantum
966 *restrict q;
967
968 register ssize_t
969 x;
970
971 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
972 continue;
973 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
974 1,exception);
975 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000976 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000977 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
978 break;
979 for (x=0; x < (ssize_t) composite_image->columns; x++)
980 {
981 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
982 {
cristyed231572011-07-14 02:18:59 +0000983 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000984 continue;
985 }
986 /*
987 Displace the offset.
988 */
989 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
990 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
991 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
992 x : 0);
993 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
994 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
995 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
996 y : 0);
997 (void) InterpolatePixelInfo(image,image_view,
998 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
999 &pixel,exception);
1000 /*
1001 Mask with the 'invalid pixel mask' in alpha channel.
1002 */
1003 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001004 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001005 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001006 p+=GetPixelChannels(composite_image);
1007 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001008 }
1009 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1010 if (sync == MagickFalse)
1011 break;
1012 }
1013 destination_view=DestroyCacheView(destination_view);
1014 composite_view=DestroyCacheView(composite_view);
1015 image_view=DestroyCacheView(image_view);
1016 composite_image=destination_image;
1017 break;
1018 }
1019 case DissolveCompositeOp:
1020 {
cristyfeb3e962012-03-29 17:25:55 +00001021 const char
1022 *value;
1023
cristy4c08aed2011-07-01 19:47:50 +00001024 /*
1025 Geometry arguments to dissolve factors.
1026 */
1027 value=GetImageArtifact(composite_image,"compose:args");
1028 if (value != (char *) NULL)
1029 {
1030 flags=ParseGeometry(value,&geometry_info);
1031 source_dissolve=geometry_info.rho/100.0;
1032 destination_dissolve=1.0;
1033 if ((source_dissolve-MagickEpsilon) < 0.0)
1034 source_dissolve=0.0;
1035 if ((source_dissolve+MagickEpsilon) > 1.0)
1036 {
1037 destination_dissolve=2.0-source_dissolve;
1038 source_dissolve=1.0;
1039 }
1040 if ((flags & SigmaValue) != 0)
1041 destination_dissolve=geometry_info.sigma/100.0;
1042 if ((destination_dissolve-MagickEpsilon) < 0.0)
1043 destination_dissolve=0.0;
cristy4c08aed2011-07-01 19:47:50 +00001044 }
1045 break;
1046 }
1047 case BlendCompositeOp:
1048 {
cristyfeb3e962012-03-29 17:25:55 +00001049 const char
1050 *value;
1051
cristy4c08aed2011-07-01 19:47:50 +00001052 value=GetImageArtifact(composite_image,"compose:args");
1053 if (value != (char *) NULL)
1054 {
1055 flags=ParseGeometry(value,&geometry_info);
1056 source_dissolve=geometry_info.rho/100.0;
1057 destination_dissolve=1.0-source_dissolve;
1058 if ((flags & SigmaValue) != 0)
1059 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001060 }
1061 break;
1062 }
1063 case MathematicsCompositeOp:
1064 {
cristyfeb3e962012-03-29 17:25:55 +00001065 const char
1066 *value;
1067
cristy4c08aed2011-07-01 19:47:50 +00001068 /*
1069 Just collect the values from "compose:args", setting.
1070 Unused values are set to zero automagically.
1071
1072 Arguments are normally a comma separated list, so this probably should
1073 be changed to some 'general comma list' parser, (with a minimum
1074 number of values)
1075 */
1076 SetGeometryInfo(&geometry_info);
1077 value=GetImageArtifact(composite_image,"compose:args");
1078 if (value != (char *) NULL)
1079 (void) ParseGeometry(value,&geometry_info);
1080 break;
1081 }
1082 case ModulateCompositeOp:
1083 {
cristyfeb3e962012-03-29 17:25:55 +00001084 const char
1085 *value;
1086
cristy4c08aed2011-07-01 19:47:50 +00001087 /*
1088 Determine the brightness and saturation scale.
1089 */
1090 value=GetImageArtifact(composite_image,"compose:args");
1091 if (value != (char *) NULL)
1092 {
1093 flags=ParseGeometry(value,&geometry_info);
1094 percent_brightness=geometry_info.rho;
1095 if ((flags & SigmaValue) != 0)
1096 percent_saturation=geometry_info.sigma;
1097 }
1098 break;
1099 }
1100 case ThresholdCompositeOp:
1101 {
cristyfeb3e962012-03-29 17:25:55 +00001102 const char
1103 *value;
1104
cristy4c08aed2011-07-01 19:47:50 +00001105 /*
1106 Determine the amount and threshold.
1107 */
1108 value=GetImageArtifact(composite_image,"compose:args");
1109 if (value != (char *) NULL)
1110 {
1111 flags=ParseGeometry(value,&geometry_info);
1112 amount=geometry_info.rho;
1113 threshold=geometry_info.sigma;
1114 if ((flags & SigmaValue) == 0)
1115 threshold=0.05f;
1116 }
1117 threshold*=QuantumRange;
1118 break;
1119 }
1120 default:
1121 break;
1122 }
cristy4c08aed2011-07-01 19:47:50 +00001123 /*
1124 Composite image.
1125 */
1126 status=MagickTrue;
1127 progress=0;
1128 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy4c08aed2011-07-01 19:47:50 +00001129 image_view=AcquireCacheView(image);
1130 composite_view=AcquireCacheView(composite_image);
1131#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00001132 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy4c08aed2011-07-01 19:47:50 +00001133#endif
1134 for (y=0; y < (ssize_t) image->rows; y++)
1135 {
1136 const Quantum
1137 *pixels;
1138
1139 double
cristye4a40472011-12-22 02:56:19 +00001140 blue,
cristy4c08aed2011-07-01 19:47:50 +00001141 brightness,
cristye4a40472011-12-22 02:56:19 +00001142 green,
cristy4c08aed2011-07-01 19:47:50 +00001143 hue,
cristye4a40472011-12-22 02:56:19 +00001144 red,
cristy4c08aed2011-07-01 19:47:50 +00001145 saturation;
1146
cristy4c08aed2011-07-01 19:47:50 +00001147 register const Quantum
1148 *restrict p;
1149
1150 register Quantum
1151 *restrict q;
1152
1153 register ssize_t
1154 x;
1155
1156 if (status == MagickFalse)
1157 continue;
cristyfeb3e962012-03-29 17:25:55 +00001158 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001159 {
1160 if (y < y_offset)
1161 continue;
1162 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1163 continue;
1164 }
1165 /*
1166 If pixels is NULL, y is outside overlay region.
1167 */
1168 pixels=(Quantum *) NULL;
1169 p=(Quantum *) NULL;
1170 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1171 {
1172 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1173 composite_image->columns,1,exception);
1174 if (p == (const Quantum *) NULL)
1175 {
1176 status=MagickFalse;
1177 continue;
1178 }
1179 pixels=p;
1180 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001181 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001182 }
1183 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001184 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001185 {
1186 status=MagickFalse;
1187 continue;
1188 }
cristy4c08aed2011-07-01 19:47:50 +00001189 hue=0.0;
1190 saturation=0.0;
1191 brightness=0.0;
1192 for (x=0; x < (ssize_t) image->columns; x++)
1193 {
cristye4a40472011-12-22 02:56:19 +00001194 MagickRealType
1195 alpha,
1196 Da,
1197 Dc,
1198 Dca,
1199 gamma,
1200 Sa,
1201 Sc,
1202 Sca;
1203
1204 register ssize_t
1205 i;
1206
cristy564a5692012-01-20 23:56:26 +00001207 size_t
1208 channels;
1209
cristyfeb3e962012-03-29 17:25:55 +00001210 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001211 {
1212 if (x < x_offset)
1213 {
cristyed231572011-07-14 02:18:59 +00001214 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001215 continue;
1216 }
1217 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1218 break;
1219 }
cristye4a40472011-12-22 02:56:19 +00001220 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1221 ((x-x_offset) >= (ssize_t) composite_image->columns))
1222 {
1223 Quantum
1224 source[MaxPixelChannels];
1225
1226 /*
1227 Virtual composite:
1228 Sc: source color.
1229 Dc: destination color.
1230 */
1231 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1232 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001233 if (GetPixelMask(image,q) != 0)
1234 {
1235 q+=GetPixelChannels(image);
1236 continue;
1237 }
cristye4a40472011-12-22 02:56:19 +00001238 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1239 {
1240 MagickRealType
1241 pixel;
1242
1243 PixelChannel
1244 channel;
1245
1246 PixelTrait
1247 composite_traits,
1248 traits;
1249
1250 channel=GetPixelChannelMapChannel(image,i);
1251 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +00001252 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001253 if ((traits == UndefinedPixelTrait) ||
1254 (composite_traits == UndefinedPixelTrait))
1255 continue;
1256 switch (compose)
1257 {
cristyc8d63672012-01-11 13:03:13 +00001258 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001259 case ChangeMaskCompositeOp:
1260 case CopyAlphaCompositeOp:
1261 case DstAtopCompositeOp:
1262 case DstInCompositeOp:
1263 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001264 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001265 case OutCompositeOp:
1266 case SrcInCompositeOp:
1267 case SrcOutCompositeOp:
1268 {
1269 pixel=(MagickRealType) q[i];
1270 if (channel == AlphaPixelChannel)
1271 pixel=(MagickRealType) TransparentAlpha;
1272 break;
1273 }
1274 case ClearCompositeOp:
1275 case CopyCompositeOp:
1276 case ReplaceCompositeOp:
1277 case SrcCompositeOp:
1278 {
1279 if (channel == AlphaPixelChannel)
1280 {
1281 pixel=(MagickRealType) TransparentAlpha;
1282 break;
1283 }
1284 pixel=0.0;
1285 break;
1286 }
cristy99abff32011-12-24 20:45:16 +00001287 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001288 case DissolveCompositeOp:
1289 {
1290 if (channel == AlphaPixelChannel)
1291 {
1292 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1293 source);
1294 break;
1295 }
1296 pixel=(MagickRealType) source[channel];
1297 break;
1298 }
1299 default:
1300 {
1301 pixel=(MagickRealType) source[channel];
1302 break;
1303 }
1304 }
1305 q[i]=ClampToQuantum(pixel);
1306 }
1307 q+=GetPixelChannels(image);
1308 continue;
1309 }
1310 /*
1311 Authentic composite:
1312 Sa: normalized source alpha.
1313 Da: normalized destination alpha.
1314 */
1315 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1316 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001317 switch (compose)
1318 {
cristye4a40472011-12-22 02:56:19 +00001319 case BumpmapCompositeOp:
1320 {
1321 alpha=GetPixelIntensity(composite_image,p)*Sa;
1322 break;
1323 }
cristycdc168f2011-12-21 15:24:39 +00001324 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001325 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001326 case DifferenceCompositeOp:
1327 case DivideDstCompositeOp:
1328 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001329 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001330 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001331 case LinearBurnCompositeOp:
1332 case LinearDodgeCompositeOp:
1333 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001334 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001335 case MinusDstCompositeOp:
1336 case MinusSrcCompositeOp:
1337 case ModulusAddCompositeOp:
1338 case ModulusSubtractCompositeOp:
1339 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001340 case OverlayCompositeOp:
1341 case PegtopLightCompositeOp:
1342 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001343 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001344 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001345 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001346 {
1347 alpha=RoundToUnity(Sa+Da-Sa*Da);
1348 break;
1349 }
1350 case DarkenCompositeOp:
1351 case DstAtopCompositeOp:
1352 case DstInCompositeOp:
1353 case InCompositeOp:
1354 case LightenCompositeOp:
1355 case SrcInCompositeOp:
1356 {
1357 alpha=Sa*Da;
1358 break;
1359 }
1360 case DissolveCompositeOp:
1361 {
1362 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1363 Sa+destination_dissolve*Da;
1364 break;
1365 }
1366 case DstOverCompositeOp:
1367 {
1368 alpha=Da*(-Sa)+Da+Sa;
1369 break;
1370 }
1371 case DstOutCompositeOp:
1372 {
1373 alpha=Da*(1.0-Sa);
1374 break;
1375 }
1376 case OutCompositeOp:
1377 case SrcOutCompositeOp:
1378 {
1379 alpha=Sa*(1.0-Da);
1380 break;
1381 }
1382 case OverCompositeOp:
1383 case SrcOverCompositeOp:
1384 {
1385 alpha=Sa*(-Da)+Sa+Da;
1386 break;
1387 }
cristy99abff32011-12-24 20:45:16 +00001388 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001389 case PlusCompositeOp:
1390 {
1391 alpha=RoundToUnity(Sa+Da);
1392 break;
1393 }
cristy4c08aed2011-07-01 19:47:50 +00001394 case XorCompositeOp:
1395 {
cristye4a40472011-12-22 02:56:19 +00001396 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001397 break;
1398 }
1399 default:
cristye4a40472011-12-22 02:56:19 +00001400 {
1401 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001402 break;
cristye4a40472011-12-22 02:56:19 +00001403 }
cristy4c08aed2011-07-01 19:47:50 +00001404 }
cristy10a6c612012-01-29 21:41:05 +00001405 if (GetPixelMask(image,p) != 0)
1406 {
1407 p+=GetPixelChannels(composite_image);
1408 q+=GetPixelChannels(image);
1409 continue;
1410 }
cristye4a40472011-12-22 02:56:19 +00001411 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1412 {
cristy564a5692012-01-20 23:56:26 +00001413 double
1414 sans;
1415
cristye10859a2011-12-18 22:28:59 +00001416 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001417 pixel;
cristye10859a2011-12-18 22:28:59 +00001418
cristye4a40472011-12-22 02:56:19 +00001419 PixelChannel
1420 channel;
cristye10859a2011-12-18 22:28:59 +00001421
cristye4a40472011-12-22 02:56:19 +00001422 PixelTrait
1423 composite_traits,
1424 traits;
1425
1426 channel=GetPixelChannelMapChannel(image,i);
1427 traits=GetPixelChannelMapTraits(image,channel);
1428 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001429 if (traits == UndefinedPixelTrait)
1430 continue;
cristya7b07912012-01-11 20:01:32 +00001431 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001432 (composite_traits == UndefinedPixelTrait))
1433 continue;
1434 /*
1435 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001436 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001437 */
1438 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
cristye4a40472011-12-22 02:56:19 +00001439 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001440 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001441 {
cristye4a40472011-12-22 02:56:19 +00001442 if (channel != AlphaPixelChannel)
1443 {
1444 /*
1445 Copy channel.
1446 */
1447 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001448 continue;
cristye10859a2011-12-18 22:28:59 +00001449 }
cristye4a40472011-12-22 02:56:19 +00001450 /*
1451 Set alpha channel.
1452 */
cristye10859a2011-12-18 22:28:59 +00001453 switch (compose)
1454 {
cristyc8d63672012-01-11 13:03:13 +00001455 case AlphaCompositeOp:
1456 {
cristya7b07912012-01-11 20:01:32 +00001457 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001458 break;
1459 }
cristye4a40472011-12-22 02:56:19 +00001460 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001461 case CopyBlackCompositeOp:
1462 case CopyBlueCompositeOp:
1463 case CopyCyanCompositeOp:
1464 case CopyGreenCompositeOp:
1465 case CopyMagentaCompositeOp:
1466 case CopyRedCompositeOp:
1467 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001468 case SrcAtopCompositeOp:
1469 case DstCompositeOp:
1470 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001471 {
cristye4a40472011-12-22 02:56:19 +00001472 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001473 break;
1474 }
cristye10859a2011-12-18 22:28:59 +00001475 case ChangeMaskCompositeOp:
1476 {
cristye4a40472011-12-22 02:56:19 +00001477 MagickBooleanType
1478 equivalent;
1479
cristy99abff32011-12-24 20:45:16 +00001480 if (Da > ((MagickRealType) QuantumRange/2.0))
1481 {
1482 pixel=(MagickRealType) TransparentAlpha;
1483 break;
1484 }
cristye4a40472011-12-22 02:56:19 +00001485 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001486 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001487 {
1488 pixel=(MagickRealType) TransparentAlpha;
1489 break;
1490 }
1491 pixel=(MagickRealType) OpaqueAlpha;
1492 break;
1493 }
cristy99abff32011-12-24 20:45:16 +00001494 case ClearCompositeOp:
1495 {
1496 pixel=(MagickRealType) TransparentAlpha;
1497 break;
1498 }
1499 case ColorizeCompositeOp:
1500 case HueCompositeOp:
1501 case LuminizeCompositeOp:
1502 case SaturateCompositeOp:
1503 {
1504 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1505 {
1506 pixel=QuantumRange*Da;
1507 break;
1508 }
1509 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1510 {
1511 pixel=QuantumRange*Sa;
1512 break;
1513 }
1514 if (Sa < Da)
1515 {
1516 pixel=QuantumRange*Da;
1517 break;
1518 }
1519 pixel=QuantumRange*Sa;
1520 break;
1521 }
cristye4a40472011-12-22 02:56:19 +00001522 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001523 case CopyAlphaCompositeOp:
1524 case DisplaceCompositeOp:
1525 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001526 case DstAtopCompositeOp:
1527 case ReplaceCompositeOp:
1528 case SrcCompositeOp:
1529 {
1530 pixel=QuantumRange*Sa;
1531 break;
1532 }
1533 case DarkenIntensityCompositeOp:
1534 {
cristy99abff32011-12-24 20:45:16 +00001535 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1536 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001537 break;
1538 }
cristy98621462011-12-31 22:31:11 +00001539 case IntensityCompositeOp:
1540 {
cristy20d5f622012-01-11 13:04:26 +00001541 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001542 break;
1543 }
cristye4a40472011-12-22 02:56:19 +00001544 case LightenIntensityCompositeOp:
1545 {
1546 pixel=Sa*GetPixelIntensity(composite_image,p) >
1547 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001548 break;
1549 }
cristy99abff32011-12-24 20:45:16 +00001550 case ModulateCompositeOp:
1551 {
1552 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1553 {
1554 pixel=QuantumRange*Da;
1555 break;
1556 }
1557 pixel=QuantumRange*Da;
1558 break;
1559 }
cristye10859a2011-12-18 22:28:59 +00001560 default:
1561 {
cristye4a40472011-12-22 02:56:19 +00001562 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001563 break;
1564 }
1565 }
cristye4a40472011-12-22 02:56:19 +00001566 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001567 continue;
1568 }
1569 /*
cristy99abff32011-12-24 20:45:16 +00001570 Porter-Duff compositions:
1571 Sca: source normalized color multiplied by alpha.
1572 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001573 */
cristy99abff32011-12-24 20:45:16 +00001574 Sca=QuantumScale*Sa*Sc;
1575 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001576 switch (compose)
1577 {
cristye10859a2011-12-18 22:28:59 +00001578 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001579 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001580 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001581 {
cristy99abff32011-12-24 20:45:16 +00001582 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001583 break;
1584 }
1585 default:
1586 break;
1587 }
cristye4a40472011-12-22 02:56:19 +00001588 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
cristyd197cbb2012-01-13 02:14:12 +00001589 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001590 switch (compose)
1591 {
cristya7b07912012-01-11 20:01:32 +00001592 case AlphaCompositeOp:
1593 {
1594 pixel=QuantumRange*Sa;
1595 break;
1596 }
cristye4a40472011-12-22 02:56:19 +00001597 case AtopCompositeOp:
1598 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001599 {
cristye4a40472011-12-22 02:56:19 +00001600 pixel=Sc*Sa+Dc*(1.0-Sa);
1601 break;
cristye10859a2011-12-18 22:28:59 +00001602 }
cristye4a40472011-12-22 02:56:19 +00001603 case BlendCompositeOp:
1604 {
1605 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1606 break;
1607 }
1608 case BlurCompositeOp:
1609 case DisplaceCompositeOp:
1610 case DistortCompositeOp:
1611 case CopyCompositeOp:
1612 case ReplaceCompositeOp:
1613 case SrcCompositeOp:
1614 {
1615 pixel=Sc;
1616 break;
1617 }
1618 case BumpmapCompositeOp:
1619 {
cristy99abff32011-12-24 20:45:16 +00001620 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001621 {
1622 pixel=Dc;
1623 break;
1624 }
1625 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1626 break;
1627 }
cristy99abff32011-12-24 20:45:16 +00001628 case ChangeMaskCompositeOp:
1629 {
1630 pixel=Dc;
1631 break;
1632 }
1633 case ClearCompositeOp:
1634 {
1635 pixel=0.0;
1636 break;
1637 }
cristye4a40472011-12-22 02:56:19 +00001638 case ColorBurnCompositeOp:
1639 {
1640 /*
1641 Refer to the March 2009 SVG specification.
1642 */
1643 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1644 {
cristy99abff32011-12-24 20:45:16 +00001645 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001646 break;
1647 }
1648 if (Sca < MagickEpsilon)
1649 {
cristy99abff32011-12-24 20:45:16 +00001650 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001651 break;
1652 }
cristy99abff32011-12-24 20:45:16 +00001653 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1654 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001655 break;
1656 }
1657 case ColorDodgeCompositeOp:
1658 {
1659 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1660 {
cristy99abff32011-12-24 20:45:16 +00001661 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001662 break;
1663 }
1664 if (fabs(Sca-Sa) < MagickEpsilon)
1665 {
cristy99abff32011-12-24 20:45:16 +00001666 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001667 break;
1668 }
cristy99abff32011-12-24 20:45:16 +00001669 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001670 (1.0-Sa));
1671 break;
1672 }
1673 case ColorizeCompositeOp:
1674 {
cristy99abff32011-12-24 20:45:16 +00001675 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001676 {
1677 pixel=Dc;
1678 break;
1679 }
cristy99abff32011-12-24 20:45:16 +00001680 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001681 {
1682 pixel=Sc;
1683 break;
1684 }
1685 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
cristy99abff32011-12-24 20:45:16 +00001686 GetPixelBlue(image,q),&sans,&sans,&brightness);
cristye4a40472011-12-22 02:56:19 +00001687 CompositeHSB(GetPixelRed(composite_image,p),
1688 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1689 &hue,&saturation,&sans);
1690 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1691 switch (channel)
1692 {
1693 case RedPixelChannel: pixel=red; break;
1694 case GreenPixelChannel: pixel=green; break;
1695 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001696 default: pixel=Dc; break;
1697 }
1698 break;
1699 }
cristye4a40472011-12-22 02:56:19 +00001700 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001701 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001702 {
cristyd197cbb2012-01-13 02:14:12 +00001703 if (channel == AlphaPixelChannel)
1704 pixel=(MagickRealType) GetPixelAlpha(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001705 break;
1706 }
1707 case CopyBlackCompositeOp:
1708 {
cristyd197cbb2012-01-13 02:14:12 +00001709 if (channel == BlackPixelChannel)
1710 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001711 break;
1712 }
1713 case CopyBlueCompositeOp:
1714 case CopyYellowCompositeOp:
1715 {
cristyd197cbb2012-01-13 02:14:12 +00001716 if (channel == BluePixelChannel)
1717 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001718 break;
1719 }
1720 case CopyGreenCompositeOp:
1721 case CopyMagentaCompositeOp:
1722 {
cristyd197cbb2012-01-13 02:14:12 +00001723 if (channel == GreenPixelChannel)
1724 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001725 break;
1726 }
1727 case CopyRedCompositeOp:
1728 case CopyCyanCompositeOp:
1729 {
cristyd197cbb2012-01-13 02:14:12 +00001730 if (channel == RedPixelChannel)
1731 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001732 break;
1733 }
cristy99abff32011-12-24 20:45:16 +00001734 case DarkenCompositeOp:
1735 {
1736 /*
1737 Darken is equivalent to a 'Minimum' method
1738 OR a greyscale version of a binary 'Or'
1739 OR the 'Intersection' of pixel sets.
1740 */
1741 if (Sc < Dc)
1742 {
1743 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1744 break;
1745 }
1746 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1747 break;
1748 }
cristye4a40472011-12-22 02:56:19 +00001749 case DarkenIntensityCompositeOp:
1750 {
cristy99abff32011-12-24 20:45:16 +00001751 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1752 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001753 break;
1754 }
1755 case DifferenceCompositeOp:
1756 {
1757 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1758 break;
1759 }
1760 case DissolveCompositeOp:
1761 {
1762 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1763 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1764 break;
1765 }
1766 case DivideDstCompositeOp:
1767 {
1768 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1769 {
cristy99abff32011-12-24 20:45:16 +00001770 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001771 break;
1772 }
1773 if (fabs(Dca) < MagickEpsilon)
1774 {
cristy99abff32011-12-24 20:45:16 +00001775 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001776 break;
1777 }
cristy99abff32011-12-24 20:45:16 +00001778 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001779 break;
1780 }
1781 case DivideSrcCompositeOp:
1782 {
1783 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1784 {
cristy99abff32011-12-24 20:45:16 +00001785 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001786 break;
1787 }
1788 if (fabs(Sca) < MagickEpsilon)
1789 {
cristy99abff32011-12-24 20:45:16 +00001790 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001791 break;
1792 }
cristy99abff32011-12-24 20:45:16 +00001793 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001794 break;
1795 }
1796 case DstAtopCompositeOp:
1797 {
1798 pixel=Dc*Da+Sc*(1.0-Da);
1799 break;
1800 }
1801 case DstCompositeOp:
1802 case NoCompositeOp:
1803 {
1804 pixel=Dc;
1805 break;
1806 }
1807 case DstInCompositeOp:
1808 {
1809 pixel=gamma*(Sa*Dc*Sa);
1810 break;
1811 }
1812 case DstOutCompositeOp:
1813 {
1814 pixel=gamma*(Da*Dc*(1.0-Sa));
1815 break;
1816 }
1817 case DstOverCompositeOp:
1818 {
1819 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1820 break;
1821 }
1822 case ExclusionCompositeOp:
1823 {
cristy99abff32011-12-24 20:45:16 +00001824 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1825 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001826 break;
1827 }
1828 case HardLightCompositeOp:
1829 {
1830 if ((2.0*Sca) < Sa)
1831 {
cristy99abff32011-12-24 20:45:16 +00001832 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001833 (1.0-Sa));
1834 break;
1835 }
cristy99abff32011-12-24 20:45:16 +00001836 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001837 Dca*(1.0-Sa));
1838 break;
1839 }
1840 case HueCompositeOp:
1841 {
cristy99abff32011-12-24 20:45:16 +00001842 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001843 {
1844 pixel=Dc;
1845 break;
1846 }
cristy99abff32011-12-24 20:45:16 +00001847 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001848 {
1849 pixel=Sc;
1850 break;
1851 }
1852 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1853 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1854 CompositeHSB(GetPixelRed(composite_image,p),
1855 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1856 &hue,&sans,&sans);
1857 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1858 switch (channel)
1859 {
1860 case RedPixelChannel: pixel=red; break;
1861 case GreenPixelChannel: pixel=green; break;
1862 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001863 default: pixel=Dc; break;
1864 }
1865 break;
1866 }
1867 case InCompositeOp:
1868 case SrcInCompositeOp:
1869 {
1870 pixel=gamma*(Da*Sc*Da);
1871 break;
1872 }
cristy99abff32011-12-24 20:45:16 +00001873 case LinearBurnCompositeOp:
1874 {
1875 /*
1876 LinearBurn: as defined by Abode Photoshop, according to
1877 http://www.simplefilter.de/en/basics/mixmods.html is:
1878
1879 f(Sc,Dc) = Sc + Dc - 1
1880 */
1881 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1882 break;
1883 }
1884 case LinearDodgeCompositeOp:
1885 {
1886 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1887 break;
1888 }
1889 case LinearLightCompositeOp:
1890 {
1891 /*
1892 LinearLight: as defined by Abode Photoshop, according to
1893 http://www.simplefilter.de/en/basics/mixmods.html is:
1894
1895 f(Sc,Dc) = Dc + 2*Sc - 1
1896 */
1897 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1898 break;
1899 }
1900 case LightenCompositeOp:
1901 {
1902 if (Sc > Dc)
1903 {
1904 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1905 break;
1906 }
1907 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1908 break;
1909 }
cristye4a40472011-12-22 02:56:19 +00001910 case LightenIntensityCompositeOp:
1911 {
1912 /*
1913 Lighten is equivalent to a 'Maximum' method
1914 OR a greyscale version of a binary 'And'
1915 OR the 'Union' of pixel sets.
1916 */
1917 pixel=Sa*GetPixelIntensity(composite_image,p) >
1918 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1919 break;
1920 }
cristye4a40472011-12-22 02:56:19 +00001921 case LuminizeCompositeOp:
1922 {
cristy99abff32011-12-24 20:45:16 +00001923 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001924 {
1925 pixel=Dc;
1926 break;
1927 }
cristy99abff32011-12-24 20:45:16 +00001928 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001929 {
1930 pixel=Sc;
1931 break;
1932 }
1933 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1934 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1935 CompositeHSB(GetPixelRed(composite_image,p),
1936 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1937 &sans,&sans,&brightness);
1938 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1939 switch (channel)
1940 {
1941 case RedPixelChannel: pixel=red; break;
1942 case GreenPixelChannel: pixel=green; break;
1943 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001944 default: pixel=Dc; break;
1945 }
1946 break;
1947 }
1948 case MathematicsCompositeOp:
1949 {
1950 /*
1951 'Mathematics' a free form user control mathematical composition
1952 is defined as...
1953
1954 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
1955
1956 Where the arguments A,B,C,D are (currently) passed to composite
1957 as a command separated 'geometry' string in "compose:args" image
1958 artifact.
1959
1960 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
1961
1962 Applying the SVG transparency formula (see above), we get...
1963
1964 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
1965
1966 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
1967 Dca*(1.0-Sa)
1968 */
1969 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
1970 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
1971 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
1972 break;
1973 }
1974 case MinusDstCompositeOp:
1975 {
1976 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
1977 break;
1978 }
1979 case MinusSrcCompositeOp:
1980 {
1981 /*
1982 Minus source from destination.
1983
1984 f(Sc,Dc) = Sc - Dc
1985 */
cristy99abff32011-12-24 20:45:16 +00001986 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00001987 break;
1988 }
1989 case ModulateCompositeOp:
1990 {
1991 ssize_t
1992 offset;
1993
cristy99abff32011-12-24 20:45:16 +00001994 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001995 {
1996 pixel=Dc;
1997 break;
1998 }
1999 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2000 if (offset == 0)
2001 {
2002 pixel=Dc;
2003 break;
2004 }
2005 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2006 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2007 brightness+=(0.01*percent_brightness*offset)/midpoint;
2008 saturation*=0.01*percent_saturation;
2009 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2010 switch (channel)
2011 {
2012 case RedPixelChannel: pixel=red; break;
2013 case GreenPixelChannel: pixel=green; break;
2014 case BluePixelChannel: pixel=blue; break;
2015 default: pixel=Dc; break;
2016 }
2017 break;
2018 }
2019 case ModulusAddCompositeOp:
2020 {
2021 pixel=Sc+Dc;
2022 if (pixel > QuantumRange)
2023 pixel-=(QuantumRange+1.0);
2024 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2025 break;
2026 }
2027 case ModulusSubtractCompositeOp:
2028 {
cristy99abff32011-12-24 20:45:16 +00002029 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002030 if (pixel < 0.0)
2031 pixel+=(QuantumRange+1.0);
2032 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2033 break;
2034 }
2035 case MultiplyCompositeOp:
2036 {
cristy99abff32011-12-24 20:45:16 +00002037 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002038 break;
2039 }
2040 case OutCompositeOp:
2041 case SrcOutCompositeOp:
2042 {
2043 pixel=gamma*(Sa*Sc*(1.0-Da));
2044 break;
2045 }
2046 case OverCompositeOp:
2047 case SrcOverCompositeOp:
2048 {
cristy99abff32011-12-24 20:45:16 +00002049 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002050 break;
2051 }
2052 case OverlayCompositeOp:
2053 {
2054 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002055 {
2056 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2057 (1.0-Da));
2058 break;
2059 }
2060 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2061 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002062 break;
2063 }
2064 case PegtopLightCompositeOp:
2065 {
2066 /*
2067 PegTop: A Soft-Light alternative: A continuous version of the
2068 Softlight function, producing very similar results.
2069
2070 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2071
2072 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2073 */
2074 if (fabs(Da) < MagickEpsilon)
2075 {
cristy99abff32011-12-24 20:45:16 +00002076 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002077 break;
2078 }
cristy99abff32011-12-24 20:45:16 +00002079 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2080 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002081 break;
2082 }
2083 case PinLightCompositeOp:
2084 {
2085 /*
2086 PinLight: A Photoshop 7 composition method
2087 http://www.simplefilter.de/en/basics/mixmods.html
2088
2089 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2090 */
2091 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2092 {
cristy99abff32011-12-24 20:45:16 +00002093 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002094 break;
2095 }
2096 if ((Dca*Sa) > (2.0*Sca*Da))
2097 {
cristy99abff32011-12-24 20:45:16 +00002098 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002099 break;
2100 }
cristy99abff32011-12-24 20:45:16 +00002101 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002102 break;
2103 }
2104 case PlusCompositeOp:
2105 {
cristy99abff32011-12-24 20:45:16 +00002106 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002107 break;
2108 }
2109 case SaturateCompositeOp:
2110 {
cristy99abff32011-12-24 20:45:16 +00002111 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002112 {
2113 pixel=Dc;
2114 break;
2115 }
cristy99abff32011-12-24 20:45:16 +00002116 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002117 {
2118 pixel=Sc;
2119 break;
2120 }
2121 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2122 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2123 CompositeHSB(GetPixelRed(composite_image,p),
2124 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
2125 &sans,&saturation,&sans);
2126 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2127 switch (channel)
2128 {
2129 case RedPixelChannel: pixel=red; break;
2130 case GreenPixelChannel: pixel=green; break;
2131 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002132 default: pixel=Dc; break;
2133 }
2134 break;
2135 }
2136 case ScreenCompositeOp:
2137 {
2138 /*
2139 Screen: a negated multiply:
2140
2141 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2142 */
cristy99abff32011-12-24 20:45:16 +00002143 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002144 break;
2145 }
2146 case SoftLightCompositeOp:
2147 {
2148 /*
2149 Refer to the March 2009 SVG specification.
2150 */
2151 if ((2.0*Sca) < Sa)
2152 {
cristy99abff32011-12-24 20:45:16 +00002153 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2154 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002155 break;
2156 }
2157 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2158 {
cristy99abff32011-12-24 20:45:16 +00002159 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2160 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2161 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002162 break;
2163 }
cristy99abff32011-12-24 20:45:16 +00002164 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2165 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002166 break;
2167 }
2168 case ThresholdCompositeOp:
2169 {
2170 MagickRealType
2171 delta;
2172
2173 delta=Sc-Dc;
2174 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2175 {
2176 pixel=gamma*Dc;
2177 break;
2178 }
2179 pixel=gamma*(Dc+delta*amount);
2180 break;
2181 }
2182 case VividLightCompositeOp:
2183 {
2184 /*
2185 VividLight: A Photoshop 7 composition method. See
2186 http://www.simplefilter.de/en/basics/mixmods.html.
2187
2188 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2189 */
2190 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2191 {
cristy99abff32011-12-24 20:45:16 +00002192 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002193 break;
2194 }
2195 if ((2.0*Sca) <= Sa)
2196 {
cristy99abff32011-12-24 20:45:16 +00002197 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2198 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002199 break;
2200 }
cristy99abff32011-12-24 20:45:16 +00002201 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2202 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002203 break;
2204 }
2205 case XorCompositeOp:
2206 {
cristy99abff32011-12-24 20:45:16 +00002207 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002208 break;
2209 }
2210 default:
2211 {
2212 pixel=Sc;
2213 break;
2214 }
2215 }
2216 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002217 }
cristyed231572011-07-14 02:18:59 +00002218 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002219 channels=GetPixelChannels(composite_image);
2220 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002221 p=pixels;
cristyed231572011-07-14 02:18:59 +00002222 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002223 }
2224 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2225 status=MagickFalse;
2226 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2227 {
2228 MagickBooleanType
2229 proceed;
2230
2231#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002232 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002233#endif
2234 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2235 image->rows);
2236 if (proceed == MagickFalse)
2237 status=MagickFalse;
2238 }
2239 }
2240 composite_view=DestroyCacheView(composite_view);
2241 image_view=DestroyCacheView(image_view);
2242 if (destination_image != (Image * ) NULL)
2243 destination_image=DestroyImage(destination_image);
2244 return(status);
2245}
2246
2247/*
2248%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2249% %
2250% %
2251% %
2252% T e x t u r e I m a g e %
2253% %
2254% %
2255% %
2256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2257%
2258% TextureImage() repeatedly tiles the texture image across and down the image
2259% canvas.
2260%
2261% The format of the TextureImage method is:
2262%
cristy30d8c942012-02-07 13:44:59 +00002263% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002264% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002265%
2266% A description of each parameter follows:
2267%
2268% o image: the image.
2269%
cristye6178502011-12-23 17:02:29 +00002270% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002271%
2272*/
cristy30d8c942012-02-07 13:44:59 +00002273MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2274 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002275{
2276#define TextureImageTag "Texture/Image"
2277
2278 CacheView
2279 *image_view,
2280 *texture_view;
2281
cristy30d8c942012-02-07 13:44:59 +00002282 Image
2283 *texture_image;
2284
cristy4c08aed2011-07-01 19:47:50 +00002285 MagickBooleanType
2286 status;
2287
2288 ssize_t
2289 y;
2290
2291 assert(image != (Image *) NULL);
2292 if (image->debug != MagickFalse)
2293 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2294 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002295 if (texture == (const Image *) NULL)
2296 return(MagickFalse);
2297 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2298 return(MagickFalse);
2299 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002300 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002301 return(MagickFalse);
cristy387430f2012-02-07 13:09:46 +00002302 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2303 exception);
cristy4c08aed2011-07-01 19:47:50 +00002304 status=MagickTrue;
2305 if ((image->compose != CopyCompositeOp) &&
2306 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002307 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002308 {
2309 /*
2310 Tile texture onto the image background.
2311 */
2312#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy19593872012-01-22 02:00:33 +00002313 #pragma omp parallel for schedule(static) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002314#endif
cristye6178502011-12-23 17:02:29 +00002315 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002316 {
2317 register ssize_t
2318 x;
2319
2320 if (status == MagickFalse)
2321 continue;
cristye6178502011-12-23 17:02:29 +00002322 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002323 {
2324 MagickBooleanType
2325 thread_status;
2326
cristyfeb3e962012-03-29 17:25:55 +00002327 thread_status=CompositeImage(image,texture_image,image->compose,
2328 MagickFalse,x+texture_image->tile_offset.x,y+
2329 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002330 if (thread_status == MagickFalse)
2331 {
2332 status=thread_status;
2333 break;
2334 }
2335 }
2336 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2337 {
2338 MagickBooleanType
2339 proceed;
2340
2341#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002342 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002343#endif
2344 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2345 y,image->rows);
2346 if (proceed == MagickFalse)
2347 status=MagickFalse;
2348 }
2349 }
2350 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2351 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002352 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002353 return(status);
2354 }
2355 /*
2356 Tile texture onto the image background (optimized).
2357 */
2358 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00002359 image_view=AcquireCacheView(image);
cristye6178502011-12-23 17:02:29 +00002360 texture_view=AcquireCacheView(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002361#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy19593872012-01-22 02:00:33 +00002362 #pragma omp parallel for schedule(static) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002363#endif
2364 for (y=0; y < (ssize_t) image->rows; y++)
2365 {
2366 MagickBooleanType
2367 sync;
2368
2369 register const Quantum
2370 *p,
2371 *pixels;
2372
2373 register ssize_t
2374 x;
2375
2376 register Quantum
2377 *q;
2378
2379 size_t
2380 width;
2381
2382 if (status == MagickFalse)
2383 continue;
cristye6178502011-12-23 17:02:29 +00002384 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2385 (y+texture_image->tile_offset.y) % texture_image->rows,
2386 texture_image->columns,1,exception);
2387 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002388 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2389 {
2390 status=MagickFalse;
2391 continue;
2392 }
cristye6178502011-12-23 17:02:29 +00002393 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002394 {
2395 register ssize_t
cristye6178502011-12-23 17:02:29 +00002396 j;
cristy4c08aed2011-07-01 19:47:50 +00002397
2398 p=pixels;
cristye6178502011-12-23 17:02:29 +00002399 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002400 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2401 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002402 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002403 {
cristye6178502011-12-23 17:02:29 +00002404 register ssize_t
2405 i;
2406
cristy10a6c612012-01-29 21:41:05 +00002407 if (GetPixelMask(image,p) != 0)
2408 {
2409 p+=GetPixelChannels(texture_image);
2410 q+=GetPixelChannels(image);
2411 continue;
2412 }
cristye6178502011-12-23 17:02:29 +00002413 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2414 {
2415 PixelChannel
2416 channel;
2417
2418 PixelTrait
2419 texture_traits,
2420 traits;
2421
2422 channel=GetPixelChannelMapChannel(texture_image,i);
2423 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2424 traits=GetPixelChannelMapTraits(image,channel);
2425 if ((traits == UndefinedPixelTrait) ||
2426 (texture_traits == UndefinedPixelTrait))
2427 continue;
2428 SetPixelChannel(image,channel,p[i],q);
2429 }
2430 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002431 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002432 }
2433 }
2434 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2435 if (sync == MagickFalse)
2436 status=MagickFalse;
2437 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2438 {
2439 MagickBooleanType
2440 proceed;
2441
2442#if defined(MAGICKCORE_OPENMP_SUPPORT)
2443 #pragma omp critical (MagickCore_TextureImage)
2444#endif
2445 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2446 image->rows);
2447 if (proceed == MagickFalse)
2448 status=MagickFalse;
2449 }
2450 }
2451 texture_view=DestroyCacheView(texture_view);
2452 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002453 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002454 return(status);
2455}