blob: 1b5fd1214469252cd5c2057d23ec50e21c7091bf [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);
cristye4a40472011-12-22 02:56:19 +0000581 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
582 {
cristyfeb3e962012-03-29 17:25:55 +0000583 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
584 y_offset,exception);
cristye4a40472011-12-22 02:56:19 +0000585 return(status);
586 }
cristy4c08aed2011-07-01 19:47:50 +0000587 destination_image=(Image *) NULL;
588 amount=0.5;
589 destination_dissolve=1.0;
cristy4c08aed2011-07-01 19:47:50 +0000590 percent_brightness=100.0;
591 percent_saturation=100.0;
592 source_dissolve=1.0;
593 threshold=0.05f;
594 switch (compose)
595 {
cristy4c08aed2011-07-01 19:47:50 +0000596 case CopyCompositeOp:
597 {
598 if ((x_offset < 0) || (y_offset < 0))
599 break;
600 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
601 break;
602 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
603 break;
604 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000605 image_view=AcquireCacheView(image);
606 composite_view=AcquireCacheView(composite_image);
607#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000608 #pragma omp parallel for schedule(static,4) shared(status)
cristy4c08aed2011-07-01 19:47:50 +0000609#endif
610 for (y=0; y < (ssize_t) composite_image->rows; y++)
611 {
612 MagickBooleanType
613 sync;
614
615 register const Quantum
616 *p;
617
618 register Quantum
619 *q;
620
621 register ssize_t
622 x;
623
624 if (status == MagickFalse)
625 continue;
626 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
627 1,exception);
628 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
629 composite_image->columns,1,exception);
630 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
631 {
632 status=MagickFalse;
633 continue;
634 }
635 for (x=0; x < (ssize_t) composite_image->columns; x++)
636 {
cristybdecccc2011-12-24 22:52:16 +0000637 register ssize_t
638 i;
639
cristy10a6c612012-01-29 21:41:05 +0000640 if (GetPixelMask(image,p) != 0)
641 {
642 p+=GetPixelChannels(composite_image);
643 q+=GetPixelChannels(image);
644 continue;
645 }
cristybdecccc2011-12-24 22:52:16 +0000646 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
647 {
648 PixelChannel
649 channel;
650
651 PixelTrait
652 composite_traits,
653 traits;
654
655 channel=GetPixelChannelMapChannel(composite_image,i);
656 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
657 traits=GetPixelChannelMapTraits(image,channel);
658 if ((traits == UndefinedPixelTrait) ||
659 (composite_traits == UndefinedPixelTrait))
660 continue;
661 SetPixelChannel(image,channel,p[i],q);
662 }
cristyed231572011-07-14 02:18:59 +0000663 p+=GetPixelChannels(composite_image);
664 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000665 }
666 sync=SyncCacheViewAuthenticPixels(image_view,exception);
667 if (sync == MagickFalse)
668 status=MagickFalse;
669 if (image->progress_monitor != (MagickProgressMonitor) NULL)
670 {
671 MagickBooleanType
672 proceed;
673
674#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000675 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000676#endif
677 proceed=SetImageProgress(image,CompositeImageTag,
678 (MagickOffsetType) y,image->rows);
679 if (proceed == MagickFalse)
680 status=MagickFalse;
681 }
682 }
683 composite_view=DestroyCacheView(composite_view);
684 image_view=DestroyCacheView(image_view);
685 return(status);
686 }
cristye4a40472011-12-22 02:56:19 +0000687 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000688 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000689 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000690 {
691 /*
692 Modify destination outside the overlaid region and require an alpha
693 channel to exist, to add transparency.
694 */
695 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +0000696 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000697 break;
698 }
699 case BlurCompositeOp:
700 {
701 CacheView
702 *composite_view,
703 *destination_view;
704
cristyfeb3e962012-03-29 17:25:55 +0000705 const char
706 *value;
707
cristy4c08aed2011-07-01 19:47:50 +0000708 PixelInfo
709 pixel;
710
711 MagickRealType
712 angle_range,
713 angle_start,
714 height,
715 width;
716
717 ResampleFilter
718 *resample_filter;
719
720 SegmentInfo
721 blur;
722
723 /*
724 Blur Image dictated by an overlay gradient map: X = red_channel;
725 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
726 */
727 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000728 exception);
cristy4c08aed2011-07-01 19:47:50 +0000729 if (destination_image == (Image *) NULL)
730 return(MagickFalse);
731 /*
732 Determine the horizontal and vertical maximim blur.
733 */
734 SetGeometryInfo(&geometry_info);
735 flags=NoValue;
736 value=GetImageArtifact(composite_image,"compose:args");
737 if (value != (char *) NULL)
738 flags=ParseGeometry(value,&geometry_info);
739 if ((flags & WidthValue) == 0 )
740 {
741 destination_image=DestroyImage(destination_image);
742 return(MagickFalse);
743 }
744 width=geometry_info.rho;
745 height=geometry_info.sigma;
746 blur.x1=geometry_info.rho;
747 blur.x2=0.0;
748 blur.y1=0.0;
749 blur.y2=geometry_info.sigma;
750 angle_start=0.0;
751 angle_range=0.0;
752 if ((flags & HeightValue) == 0)
753 blur.y2=blur.x1;
754 if ((flags & XValue) != 0 )
755 {
756 MagickRealType
757 angle;
758
759 angle=DegreesToRadians(geometry_info.xi);
760 blur.x1=width*cos(angle);
761 blur.x2=width*sin(angle);
762 blur.y1=(-height*sin(angle));
763 blur.y2=height*cos(angle);
764 }
765 if ((flags & YValue) != 0 )
766 {
767 angle_start=DegreesToRadians(geometry_info.xi);
768 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
769 }
770 /*
771 Blur Image by resampling.
anthonyf46d4262012-03-26 03:30:34 +0000772 FUTURE: this is currently broken, especially for small sigma blurs
773 This needs to be fixed to use a non-user filter setup that provides
774 far more control than currently available.
cristy4c08aed2011-07-01 19:47:50 +0000775 */
cristy8a11cb12011-10-19 23:53:34 +0000776 resample_filter=AcquireResampleFilter(image,exception);
anthonyf46d4262012-03-26 03:30:34 +0000777 SetResampleFilter(resample_filter,CubicFilter); /* was blur*2 */
cristy4c08aed2011-07-01 19:47:50 +0000778 destination_view=AcquireCacheView(destination_image);
779 composite_view=AcquireCacheView(composite_image);
780 for (y=0; y < (ssize_t) composite_image->rows; y++)
781 {
782 MagickBooleanType
783 sync;
784
785 register const Quantum
786 *restrict p;
787
788 register Quantum
789 *restrict q;
790
791 register ssize_t
792 x;
793
794 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
795 continue;
796 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
797 1,exception);
798 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000799 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000800 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
801 break;
802 for (x=0; x < (ssize_t) composite_image->columns; x++)
803 {
804 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
805 {
cristyed231572011-07-14 02:18:59 +0000806 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000807 continue;
808 }
809 if (fabs(angle_range) > MagickEpsilon)
810 {
811 MagickRealType
812 angle;
813
814 angle=angle_start+angle_range*QuantumScale*
815 GetPixelBlue(composite_image,p);
816 blur.x1=width*cos(angle);
817 blur.x2=width*sin(angle);
818 blur.y1=(-height*sin(angle));
819 blur.y2=height*cos(angle);
820 }
821 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
822 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
823 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
824 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
825 GetPixelGreen(composite_image,p));
826 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
827 (double) y_offset+y,&pixel);
cristy803640d2011-11-17 02:11:32 +0000828 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000829 p+=GetPixelChannels(composite_image);
830 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000831 }
832 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
833 if (sync == MagickFalse)
834 break;
835 }
836 resample_filter=DestroyResampleFilter(resample_filter);
837 composite_view=DestroyCacheView(composite_view);
838 destination_view=DestroyCacheView(destination_view);
839 composite_image=destination_image;
840 break;
841 }
842 case DisplaceCompositeOp:
843 case DistortCompositeOp:
844 {
845 CacheView
846 *composite_view,
847 *destination_view,
848 *image_view;
849
cristyfeb3e962012-03-29 17:25:55 +0000850 const char
851 *value;
852
cristy4c08aed2011-07-01 19:47:50 +0000853 PixelInfo
854 pixel;
855
856 MagickRealType
857 horizontal_scale,
858 vertical_scale;
859
860 PointInfo
861 center,
862 offset;
863
864 /*
865 Displace/Distort based on overlay gradient map:
866 X = red_channel; Y = green_channel;
867 compose:args = x_scale[,y_scale[,center.x,center.y]]
868 */
869 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000870 exception);
cristy4c08aed2011-07-01 19:47:50 +0000871 if (destination_image == (Image *) NULL)
872 return(MagickFalse);
873 SetGeometryInfo(&geometry_info);
874 flags=NoValue;
875 value=GetImageArtifact(composite_image,"compose:args");
876 if (value != (char *) NULL)
877 flags=ParseGeometry(value,&geometry_info);
878 if ((flags & (WidthValue|HeightValue)) == 0 )
879 {
880 if ((flags & AspectValue) == 0)
881 {
882 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
883 2.0;
884 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
885 }
886 else
887 {
888 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
889 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
890 }
891 }
892 else
893 {
894 horizontal_scale=geometry_info.rho;
895 vertical_scale=geometry_info.sigma;
896 if ((flags & PercentValue) != 0)
897 {
898 if ((flags & AspectValue) == 0)
899 {
900 horizontal_scale*=(composite_image->columns-1.0)/200.0;
901 vertical_scale*=(composite_image->rows-1.0)/200.0;
902 }
903 else
904 {
905 horizontal_scale*=(image->columns-1.0)/200.0;
906 vertical_scale*=(image->rows-1.0)/200.0;
907 }
908 }
909 if ((flags & HeightValue) == 0)
910 vertical_scale=horizontal_scale;
911 }
912 /*
913 Determine fixed center point for absolute distortion map
914 Absolute distort ==
915 Displace offset relative to a fixed absolute point
916 Select that point according to +X+Y user inputs.
917 default = center of overlay image
918 arg flag '!' = locations/percentage relative to background image
919 */
920 center.x=(MagickRealType) x_offset;
921 center.y=(MagickRealType) y_offset;
922 if (compose == DistortCompositeOp)
923 {
924 if ((flags & XValue) == 0)
925 if ((flags & AspectValue) == 0)
926 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
927 2.0;
928 else
929 center.x=((MagickRealType) image->columns-1)/2.0;
930 else
931 if ((flags & AspectValue) == 0)
932 center.x=(MagickRealType) x_offset+geometry_info.xi;
933 else
934 center.x=geometry_info.xi;
935 if ((flags & YValue) == 0)
936 if ((flags & AspectValue) == 0)
937 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
938 else
939 center.y=((MagickRealType) image->rows-1)/2.0;
940 else
941 if ((flags & AspectValue) == 0)
942 center.y=(MagickRealType) y_offset+geometry_info.psi;
943 else
944 center.y=geometry_info.psi;
945 }
946 /*
947 Shift the pixel offset point as defined by the provided,
948 displacement/distortion map. -- Like a lens...
949 */
cristye10859a2011-12-18 22:28:59 +0000950 GetPixelInfo(image,&pixel);
cristy4c08aed2011-07-01 19:47:50 +0000951 image_view=AcquireCacheView(image);
952 destination_view=AcquireCacheView(destination_image);
953 composite_view=AcquireCacheView(composite_image);
954 for (y=0; y < (ssize_t) composite_image->rows; y++)
955 {
956 MagickBooleanType
957 sync;
958
959 register const Quantum
960 *restrict p;
961
962 register Quantum
963 *restrict q;
964
965 register ssize_t
966 x;
967
968 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
969 continue;
970 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
971 1,exception);
972 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000973 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000974 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
975 break;
976 for (x=0; x < (ssize_t) composite_image->columns; x++)
977 {
978 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
979 {
cristyed231572011-07-14 02:18:59 +0000980 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000981 continue;
982 }
983 /*
984 Displace the offset.
985 */
986 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
987 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
988 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
989 x : 0);
990 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
991 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
992 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
993 y : 0);
994 (void) InterpolatePixelInfo(image,image_view,
995 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
996 &pixel,exception);
997 /*
998 Mask with the 'invalid pixel mask' in alpha channel.
999 */
1000 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001001 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001002 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001003 p+=GetPixelChannels(composite_image);
1004 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001005 }
1006 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1007 if (sync == MagickFalse)
1008 break;
1009 }
1010 destination_view=DestroyCacheView(destination_view);
1011 composite_view=DestroyCacheView(composite_view);
1012 image_view=DestroyCacheView(image_view);
1013 composite_image=destination_image;
1014 break;
1015 }
1016 case DissolveCompositeOp:
1017 {
cristyfeb3e962012-03-29 17:25:55 +00001018 const char
1019 *value;
1020
cristy4c08aed2011-07-01 19:47:50 +00001021 /*
1022 Geometry arguments to dissolve factors.
1023 */
1024 value=GetImageArtifact(composite_image,"compose:args");
1025 if (value != (char *) NULL)
1026 {
1027 flags=ParseGeometry(value,&geometry_info);
1028 source_dissolve=geometry_info.rho/100.0;
1029 destination_dissolve=1.0;
1030 if ((source_dissolve-MagickEpsilon) < 0.0)
1031 source_dissolve=0.0;
1032 if ((source_dissolve+MagickEpsilon) > 1.0)
1033 {
1034 destination_dissolve=2.0-source_dissolve;
1035 source_dissolve=1.0;
1036 }
1037 if ((flags & SigmaValue) != 0)
1038 destination_dissolve=geometry_info.sigma/100.0;
1039 if ((destination_dissolve-MagickEpsilon) < 0.0)
1040 destination_dissolve=0.0;
cristy4c08aed2011-07-01 19:47:50 +00001041 }
1042 break;
1043 }
1044 case BlendCompositeOp:
1045 {
cristyfeb3e962012-03-29 17:25:55 +00001046 const char
1047 *value;
1048
cristy4c08aed2011-07-01 19:47:50 +00001049 value=GetImageArtifact(composite_image,"compose:args");
1050 if (value != (char *) NULL)
1051 {
1052 flags=ParseGeometry(value,&geometry_info);
1053 source_dissolve=geometry_info.rho/100.0;
1054 destination_dissolve=1.0-source_dissolve;
1055 if ((flags & SigmaValue) != 0)
1056 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001057 }
1058 break;
1059 }
1060 case MathematicsCompositeOp:
1061 {
cristyfeb3e962012-03-29 17:25:55 +00001062 const char
1063 *value;
1064
cristy4c08aed2011-07-01 19:47:50 +00001065 /*
1066 Just collect the values from "compose:args", setting.
1067 Unused values are set to zero automagically.
1068
1069 Arguments are normally a comma separated list, so this probably should
1070 be changed to some 'general comma list' parser, (with a minimum
1071 number of values)
1072 */
1073 SetGeometryInfo(&geometry_info);
1074 value=GetImageArtifact(composite_image,"compose:args");
1075 if (value != (char *) NULL)
1076 (void) ParseGeometry(value,&geometry_info);
1077 break;
1078 }
1079 case ModulateCompositeOp:
1080 {
cristyfeb3e962012-03-29 17:25:55 +00001081 const char
1082 *value;
1083
cristy4c08aed2011-07-01 19:47:50 +00001084 /*
1085 Determine the brightness and saturation scale.
1086 */
1087 value=GetImageArtifact(composite_image,"compose:args");
1088 if (value != (char *) NULL)
1089 {
1090 flags=ParseGeometry(value,&geometry_info);
1091 percent_brightness=geometry_info.rho;
1092 if ((flags & SigmaValue) != 0)
1093 percent_saturation=geometry_info.sigma;
1094 }
1095 break;
1096 }
1097 case ThresholdCompositeOp:
1098 {
cristyfeb3e962012-03-29 17:25:55 +00001099 const char
1100 *value;
1101
cristy4c08aed2011-07-01 19:47:50 +00001102 /*
1103 Determine the amount and threshold.
1104 */
1105 value=GetImageArtifact(composite_image,"compose:args");
1106 if (value != (char *) NULL)
1107 {
1108 flags=ParseGeometry(value,&geometry_info);
1109 amount=geometry_info.rho;
1110 threshold=geometry_info.sigma;
1111 if ((flags & SigmaValue) == 0)
1112 threshold=0.05f;
1113 }
1114 threshold*=QuantumRange;
1115 break;
1116 }
1117 default:
1118 break;
1119 }
cristy4c08aed2011-07-01 19:47:50 +00001120 /*
1121 Composite image.
1122 */
1123 status=MagickTrue;
1124 progress=0;
1125 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy4c08aed2011-07-01 19:47:50 +00001126 image_view=AcquireCacheView(image);
1127 composite_view=AcquireCacheView(composite_image);
1128#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00001129 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy4c08aed2011-07-01 19:47:50 +00001130#endif
1131 for (y=0; y < (ssize_t) image->rows; y++)
1132 {
1133 const Quantum
1134 *pixels;
1135
1136 double
cristye4a40472011-12-22 02:56:19 +00001137 blue,
cristy4c08aed2011-07-01 19:47:50 +00001138 brightness,
cristye4a40472011-12-22 02:56:19 +00001139 green,
cristy4c08aed2011-07-01 19:47:50 +00001140 hue,
cristye4a40472011-12-22 02:56:19 +00001141 red,
cristy4c08aed2011-07-01 19:47:50 +00001142 saturation;
1143
cristy4c08aed2011-07-01 19:47:50 +00001144 register const Quantum
1145 *restrict p;
1146
1147 register Quantum
1148 *restrict q;
1149
1150 register ssize_t
1151 x;
1152
1153 if (status == MagickFalse)
1154 continue;
cristyfeb3e962012-03-29 17:25:55 +00001155 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001156 {
1157 if (y < y_offset)
1158 continue;
1159 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1160 continue;
1161 }
1162 /*
1163 If pixels is NULL, y is outside overlay region.
1164 */
1165 pixels=(Quantum *) NULL;
1166 p=(Quantum *) NULL;
1167 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1168 {
1169 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1170 composite_image->columns,1,exception);
1171 if (p == (const Quantum *) NULL)
1172 {
1173 status=MagickFalse;
1174 continue;
1175 }
1176 pixels=p;
1177 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001178 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001179 }
1180 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001181 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001182 {
1183 status=MagickFalse;
1184 continue;
1185 }
cristy4c08aed2011-07-01 19:47:50 +00001186 hue=0.0;
1187 saturation=0.0;
1188 brightness=0.0;
1189 for (x=0; x < (ssize_t) image->columns; x++)
1190 {
cristye4a40472011-12-22 02:56:19 +00001191 MagickRealType
1192 alpha,
1193 Da,
1194 Dc,
1195 Dca,
1196 gamma,
1197 Sa,
1198 Sc,
1199 Sca;
1200
1201 register ssize_t
1202 i;
1203
cristy564a5692012-01-20 23:56:26 +00001204 size_t
1205 channels;
1206
cristyfeb3e962012-03-29 17:25:55 +00001207 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001208 {
1209 if (x < x_offset)
1210 {
cristyed231572011-07-14 02:18:59 +00001211 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001212 continue;
1213 }
1214 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1215 break;
1216 }
cristye4a40472011-12-22 02:56:19 +00001217 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1218 ((x-x_offset) >= (ssize_t) composite_image->columns))
1219 {
1220 Quantum
1221 source[MaxPixelChannels];
1222
1223 /*
1224 Virtual composite:
1225 Sc: source color.
1226 Dc: destination color.
1227 */
1228 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1229 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001230 if (GetPixelMask(image,q) != 0)
1231 {
1232 q+=GetPixelChannels(image);
1233 continue;
1234 }
cristye4a40472011-12-22 02:56:19 +00001235 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1236 {
1237 MagickRealType
1238 pixel;
1239
1240 PixelChannel
1241 channel;
1242
1243 PixelTrait
1244 composite_traits,
1245 traits;
1246
1247 channel=GetPixelChannelMapChannel(image,i);
1248 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +00001249 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001250 if ((traits == UndefinedPixelTrait) ||
1251 (composite_traits == UndefinedPixelTrait))
1252 continue;
1253 switch (compose)
1254 {
cristyc8d63672012-01-11 13:03:13 +00001255 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001256 case ChangeMaskCompositeOp:
1257 case CopyAlphaCompositeOp:
1258 case DstAtopCompositeOp:
1259 case DstInCompositeOp:
1260 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001261 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001262 case OutCompositeOp:
1263 case SrcInCompositeOp:
1264 case SrcOutCompositeOp:
1265 {
1266 pixel=(MagickRealType) q[i];
1267 if (channel == AlphaPixelChannel)
1268 pixel=(MagickRealType) TransparentAlpha;
1269 break;
1270 }
1271 case ClearCompositeOp:
1272 case CopyCompositeOp:
1273 case ReplaceCompositeOp:
1274 case SrcCompositeOp:
1275 {
1276 if (channel == AlphaPixelChannel)
1277 {
1278 pixel=(MagickRealType) TransparentAlpha;
1279 break;
1280 }
1281 pixel=0.0;
1282 break;
1283 }
cristy99abff32011-12-24 20:45:16 +00001284 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001285 case DissolveCompositeOp:
1286 {
1287 if (channel == AlphaPixelChannel)
1288 {
1289 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1290 source);
1291 break;
1292 }
1293 pixel=(MagickRealType) source[channel];
1294 break;
1295 }
1296 default:
1297 {
1298 pixel=(MagickRealType) source[channel];
1299 break;
1300 }
1301 }
1302 q[i]=ClampToQuantum(pixel);
1303 }
1304 q+=GetPixelChannels(image);
1305 continue;
1306 }
1307 /*
1308 Authentic composite:
1309 Sa: normalized source alpha.
1310 Da: normalized destination alpha.
1311 */
1312 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1313 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001314 switch (compose)
1315 {
cristye4a40472011-12-22 02:56:19 +00001316 case BumpmapCompositeOp:
1317 {
1318 alpha=GetPixelIntensity(composite_image,p)*Sa;
1319 break;
1320 }
cristycdc168f2011-12-21 15:24:39 +00001321 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001322 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001323 case DifferenceCompositeOp:
1324 case DivideDstCompositeOp:
1325 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001326 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001327 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001328 case LinearBurnCompositeOp:
1329 case LinearDodgeCompositeOp:
1330 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001331 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001332 case MinusDstCompositeOp:
1333 case MinusSrcCompositeOp:
1334 case ModulusAddCompositeOp:
1335 case ModulusSubtractCompositeOp:
1336 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001337 case OverlayCompositeOp:
1338 case PegtopLightCompositeOp:
1339 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001340 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001341 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001342 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001343 {
1344 alpha=RoundToUnity(Sa+Da-Sa*Da);
1345 break;
1346 }
1347 case DarkenCompositeOp:
1348 case DstAtopCompositeOp:
1349 case DstInCompositeOp:
1350 case InCompositeOp:
1351 case LightenCompositeOp:
1352 case SrcInCompositeOp:
1353 {
1354 alpha=Sa*Da;
1355 break;
1356 }
1357 case DissolveCompositeOp:
1358 {
1359 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1360 Sa+destination_dissolve*Da;
1361 break;
1362 }
1363 case DstOverCompositeOp:
1364 {
1365 alpha=Da*(-Sa)+Da+Sa;
1366 break;
1367 }
1368 case DstOutCompositeOp:
1369 {
1370 alpha=Da*(1.0-Sa);
1371 break;
1372 }
1373 case OutCompositeOp:
1374 case SrcOutCompositeOp:
1375 {
1376 alpha=Sa*(1.0-Da);
1377 break;
1378 }
1379 case OverCompositeOp:
1380 case SrcOverCompositeOp:
1381 {
1382 alpha=Sa*(-Da)+Sa+Da;
1383 break;
1384 }
cristy99abff32011-12-24 20:45:16 +00001385 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001386 case PlusCompositeOp:
1387 {
1388 alpha=RoundToUnity(Sa+Da);
1389 break;
1390 }
cristy4c08aed2011-07-01 19:47:50 +00001391 case XorCompositeOp:
1392 {
cristye4a40472011-12-22 02:56:19 +00001393 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001394 break;
1395 }
1396 default:
cristye4a40472011-12-22 02:56:19 +00001397 {
1398 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001399 break;
cristye4a40472011-12-22 02:56:19 +00001400 }
cristy4c08aed2011-07-01 19:47:50 +00001401 }
cristy10a6c612012-01-29 21:41:05 +00001402 if (GetPixelMask(image,p) != 0)
1403 {
1404 p+=GetPixelChannels(composite_image);
1405 q+=GetPixelChannels(image);
1406 continue;
1407 }
cristye4a40472011-12-22 02:56:19 +00001408 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1409 {
cristy564a5692012-01-20 23:56:26 +00001410 double
1411 sans;
1412
cristye10859a2011-12-18 22:28:59 +00001413 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001414 pixel;
cristye10859a2011-12-18 22:28:59 +00001415
cristye4a40472011-12-22 02:56:19 +00001416 PixelChannel
1417 channel;
cristye10859a2011-12-18 22:28:59 +00001418
cristye4a40472011-12-22 02:56:19 +00001419 PixelTrait
1420 composite_traits,
1421 traits;
1422
1423 channel=GetPixelChannelMapChannel(image,i);
1424 traits=GetPixelChannelMapTraits(image,channel);
1425 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001426 if (traits == UndefinedPixelTrait)
1427 continue;
cristya7b07912012-01-11 20:01:32 +00001428 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001429 (composite_traits == UndefinedPixelTrait))
1430 continue;
1431 /*
1432 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001433 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001434 */
1435 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
cristye4a40472011-12-22 02:56:19 +00001436 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001437 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001438 {
cristye4a40472011-12-22 02:56:19 +00001439 if (channel != AlphaPixelChannel)
1440 {
1441 /*
1442 Copy channel.
1443 */
1444 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001445 continue;
cristye10859a2011-12-18 22:28:59 +00001446 }
cristye4a40472011-12-22 02:56:19 +00001447 /*
1448 Set alpha channel.
1449 */
cristye10859a2011-12-18 22:28:59 +00001450 switch (compose)
1451 {
cristyc8d63672012-01-11 13:03:13 +00001452 case AlphaCompositeOp:
1453 {
cristya7b07912012-01-11 20:01:32 +00001454 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001455 break;
1456 }
cristye4a40472011-12-22 02:56:19 +00001457 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001458 case CopyBlackCompositeOp:
1459 case CopyBlueCompositeOp:
1460 case CopyCyanCompositeOp:
1461 case CopyGreenCompositeOp:
1462 case CopyMagentaCompositeOp:
1463 case CopyRedCompositeOp:
1464 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001465 case SrcAtopCompositeOp:
1466 case DstCompositeOp:
1467 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001468 {
cristye4a40472011-12-22 02:56:19 +00001469 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001470 break;
1471 }
cristye10859a2011-12-18 22:28:59 +00001472 case ChangeMaskCompositeOp:
1473 {
cristye4a40472011-12-22 02:56:19 +00001474 MagickBooleanType
1475 equivalent;
1476
cristy99abff32011-12-24 20:45:16 +00001477 if (Da > ((MagickRealType) QuantumRange/2.0))
1478 {
1479 pixel=(MagickRealType) TransparentAlpha;
1480 break;
1481 }
cristye4a40472011-12-22 02:56:19 +00001482 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001483 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001484 {
1485 pixel=(MagickRealType) TransparentAlpha;
1486 break;
1487 }
1488 pixel=(MagickRealType) OpaqueAlpha;
1489 break;
1490 }
cristy99abff32011-12-24 20:45:16 +00001491 case ClearCompositeOp:
1492 {
1493 pixel=(MagickRealType) TransparentAlpha;
1494 break;
1495 }
1496 case ColorizeCompositeOp:
1497 case HueCompositeOp:
1498 case LuminizeCompositeOp:
1499 case SaturateCompositeOp:
1500 {
1501 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1502 {
1503 pixel=QuantumRange*Da;
1504 break;
1505 }
1506 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1507 {
1508 pixel=QuantumRange*Sa;
1509 break;
1510 }
1511 if (Sa < Da)
1512 {
1513 pixel=QuantumRange*Da;
1514 break;
1515 }
1516 pixel=QuantumRange*Sa;
1517 break;
1518 }
cristye4a40472011-12-22 02:56:19 +00001519 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001520 case CopyAlphaCompositeOp:
1521 case DisplaceCompositeOp:
1522 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001523 case DstAtopCompositeOp:
1524 case ReplaceCompositeOp:
1525 case SrcCompositeOp:
1526 {
1527 pixel=QuantumRange*Sa;
1528 break;
1529 }
1530 case DarkenIntensityCompositeOp:
1531 {
cristy99abff32011-12-24 20:45:16 +00001532 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1533 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001534 break;
1535 }
cristy98621462011-12-31 22:31:11 +00001536 case IntensityCompositeOp:
1537 {
cristy20d5f622012-01-11 13:04:26 +00001538 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001539 break;
1540 }
cristye4a40472011-12-22 02:56:19 +00001541 case LightenIntensityCompositeOp:
1542 {
1543 pixel=Sa*GetPixelIntensity(composite_image,p) >
1544 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001545 break;
1546 }
cristy99abff32011-12-24 20:45:16 +00001547 case ModulateCompositeOp:
1548 {
1549 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1550 {
1551 pixel=QuantumRange*Da;
1552 break;
1553 }
1554 pixel=QuantumRange*Da;
1555 break;
1556 }
cristye10859a2011-12-18 22:28:59 +00001557 default:
1558 {
cristye4a40472011-12-22 02:56:19 +00001559 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001560 break;
1561 }
1562 }
cristye4a40472011-12-22 02:56:19 +00001563 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001564 continue;
1565 }
1566 /*
cristy99abff32011-12-24 20:45:16 +00001567 Porter-Duff compositions:
1568 Sca: source normalized color multiplied by alpha.
1569 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001570 */
cristy99abff32011-12-24 20:45:16 +00001571 Sca=QuantumScale*Sa*Sc;
1572 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001573 switch (compose)
1574 {
cristye10859a2011-12-18 22:28:59 +00001575 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001576 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001577 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001578 {
cristy99abff32011-12-24 20:45:16 +00001579 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001580 break;
1581 }
1582 default:
1583 break;
1584 }
cristye4a40472011-12-22 02:56:19 +00001585 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
cristyd197cbb2012-01-13 02:14:12 +00001586 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001587 switch (compose)
1588 {
cristya7b07912012-01-11 20:01:32 +00001589 case AlphaCompositeOp:
1590 {
1591 pixel=QuantumRange*Sa;
1592 break;
1593 }
cristye4a40472011-12-22 02:56:19 +00001594 case AtopCompositeOp:
1595 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001596 {
cristye4a40472011-12-22 02:56:19 +00001597 pixel=Sc*Sa+Dc*(1.0-Sa);
1598 break;
cristye10859a2011-12-18 22:28:59 +00001599 }
cristye4a40472011-12-22 02:56:19 +00001600 case BlendCompositeOp:
1601 {
1602 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1603 break;
1604 }
1605 case BlurCompositeOp:
1606 case DisplaceCompositeOp:
1607 case DistortCompositeOp:
1608 case CopyCompositeOp:
1609 case ReplaceCompositeOp:
1610 case SrcCompositeOp:
1611 {
1612 pixel=Sc;
1613 break;
1614 }
1615 case BumpmapCompositeOp:
1616 {
cristy99abff32011-12-24 20:45:16 +00001617 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001618 {
1619 pixel=Dc;
1620 break;
1621 }
1622 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1623 break;
1624 }
cristy99abff32011-12-24 20:45:16 +00001625 case ChangeMaskCompositeOp:
1626 {
1627 pixel=Dc;
1628 break;
1629 }
1630 case ClearCompositeOp:
1631 {
1632 pixel=0.0;
1633 break;
1634 }
cristye4a40472011-12-22 02:56:19 +00001635 case ColorBurnCompositeOp:
1636 {
1637 /*
1638 Refer to the March 2009 SVG specification.
1639 */
1640 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1641 {
cristy99abff32011-12-24 20:45:16 +00001642 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001643 break;
1644 }
1645 if (Sca < MagickEpsilon)
1646 {
cristy99abff32011-12-24 20:45:16 +00001647 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001648 break;
1649 }
cristy99abff32011-12-24 20:45:16 +00001650 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1651 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001652 break;
1653 }
1654 case ColorDodgeCompositeOp:
1655 {
1656 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1657 {
cristy99abff32011-12-24 20:45:16 +00001658 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001659 break;
1660 }
1661 if (fabs(Sca-Sa) < MagickEpsilon)
1662 {
cristy99abff32011-12-24 20:45:16 +00001663 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001664 break;
1665 }
cristy99abff32011-12-24 20:45:16 +00001666 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001667 (1.0-Sa));
1668 break;
1669 }
1670 case ColorizeCompositeOp:
1671 {
cristy99abff32011-12-24 20:45:16 +00001672 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001673 {
1674 pixel=Dc;
1675 break;
1676 }
cristy99abff32011-12-24 20:45:16 +00001677 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001678 {
1679 pixel=Sc;
1680 break;
1681 }
1682 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
cristy99abff32011-12-24 20:45:16 +00001683 GetPixelBlue(image,q),&sans,&sans,&brightness);
cristye4a40472011-12-22 02:56:19 +00001684 CompositeHSB(GetPixelRed(composite_image,p),
1685 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1686 &hue,&saturation,&sans);
1687 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1688 switch (channel)
1689 {
1690 case RedPixelChannel: pixel=red; break;
1691 case GreenPixelChannel: pixel=green; break;
1692 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001693 default: pixel=Dc; break;
1694 }
1695 break;
1696 }
cristye4a40472011-12-22 02:56:19 +00001697 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001698 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001699 {
cristyd197cbb2012-01-13 02:14:12 +00001700 if (channel == AlphaPixelChannel)
1701 pixel=(MagickRealType) GetPixelAlpha(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001702 break;
1703 }
1704 case CopyBlackCompositeOp:
1705 {
cristyd197cbb2012-01-13 02:14:12 +00001706 if (channel == BlackPixelChannel)
1707 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001708 break;
1709 }
1710 case CopyBlueCompositeOp:
1711 case CopyYellowCompositeOp:
1712 {
cristyd197cbb2012-01-13 02:14:12 +00001713 if (channel == BluePixelChannel)
1714 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001715 break;
1716 }
1717 case CopyGreenCompositeOp:
1718 case CopyMagentaCompositeOp:
1719 {
cristyd197cbb2012-01-13 02:14:12 +00001720 if (channel == GreenPixelChannel)
1721 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001722 break;
1723 }
1724 case CopyRedCompositeOp:
1725 case CopyCyanCompositeOp:
1726 {
cristyd197cbb2012-01-13 02:14:12 +00001727 if (channel == RedPixelChannel)
1728 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001729 break;
1730 }
cristy99abff32011-12-24 20:45:16 +00001731 case DarkenCompositeOp:
1732 {
1733 /*
1734 Darken is equivalent to a 'Minimum' method
1735 OR a greyscale version of a binary 'Or'
1736 OR the 'Intersection' of pixel sets.
1737 */
1738 if (Sc < Dc)
1739 {
1740 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1741 break;
1742 }
1743 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1744 break;
1745 }
cristye4a40472011-12-22 02:56:19 +00001746 case DarkenIntensityCompositeOp:
1747 {
cristy99abff32011-12-24 20:45:16 +00001748 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1749 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001750 break;
1751 }
1752 case DifferenceCompositeOp:
1753 {
1754 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1755 break;
1756 }
1757 case DissolveCompositeOp:
1758 {
1759 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1760 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1761 break;
1762 }
1763 case DivideDstCompositeOp:
1764 {
1765 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1766 {
cristy99abff32011-12-24 20:45:16 +00001767 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001768 break;
1769 }
1770 if (fabs(Dca) < MagickEpsilon)
1771 {
cristy99abff32011-12-24 20:45:16 +00001772 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001773 break;
1774 }
cristy99abff32011-12-24 20:45:16 +00001775 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001776 break;
1777 }
1778 case DivideSrcCompositeOp:
1779 {
1780 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1781 {
cristy99abff32011-12-24 20:45:16 +00001782 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001783 break;
1784 }
1785 if (fabs(Sca) < MagickEpsilon)
1786 {
cristy99abff32011-12-24 20:45:16 +00001787 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001788 break;
1789 }
cristy99abff32011-12-24 20:45:16 +00001790 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001791 break;
1792 }
1793 case DstAtopCompositeOp:
1794 {
1795 pixel=Dc*Da+Sc*(1.0-Da);
1796 break;
1797 }
1798 case DstCompositeOp:
1799 case NoCompositeOp:
1800 {
1801 pixel=Dc;
1802 break;
1803 }
1804 case DstInCompositeOp:
1805 {
1806 pixel=gamma*(Sa*Dc*Sa);
1807 break;
1808 }
1809 case DstOutCompositeOp:
1810 {
1811 pixel=gamma*(Da*Dc*(1.0-Sa));
1812 break;
1813 }
1814 case DstOverCompositeOp:
1815 {
1816 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1817 break;
1818 }
1819 case ExclusionCompositeOp:
1820 {
cristy99abff32011-12-24 20:45:16 +00001821 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1822 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001823 break;
1824 }
1825 case HardLightCompositeOp:
1826 {
1827 if ((2.0*Sca) < Sa)
1828 {
cristy99abff32011-12-24 20:45:16 +00001829 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001830 (1.0-Sa));
1831 break;
1832 }
cristy99abff32011-12-24 20:45:16 +00001833 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001834 Dca*(1.0-Sa));
1835 break;
1836 }
1837 case HueCompositeOp:
1838 {
cristy99abff32011-12-24 20:45:16 +00001839 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001840 {
1841 pixel=Dc;
1842 break;
1843 }
cristy99abff32011-12-24 20:45:16 +00001844 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001845 {
1846 pixel=Sc;
1847 break;
1848 }
1849 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1850 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1851 CompositeHSB(GetPixelRed(composite_image,p),
1852 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1853 &hue,&sans,&sans);
1854 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1855 switch (channel)
1856 {
1857 case RedPixelChannel: pixel=red; break;
1858 case GreenPixelChannel: pixel=green; break;
1859 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001860 default: pixel=Dc; break;
1861 }
1862 break;
1863 }
1864 case InCompositeOp:
1865 case SrcInCompositeOp:
1866 {
1867 pixel=gamma*(Da*Sc*Da);
1868 break;
1869 }
cristy99abff32011-12-24 20:45:16 +00001870 case LinearBurnCompositeOp:
1871 {
1872 /*
1873 LinearBurn: as defined by Abode Photoshop, according to
1874 http://www.simplefilter.de/en/basics/mixmods.html is:
1875
1876 f(Sc,Dc) = Sc + Dc - 1
1877 */
1878 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1879 break;
1880 }
1881 case LinearDodgeCompositeOp:
1882 {
1883 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1884 break;
1885 }
1886 case LinearLightCompositeOp:
1887 {
1888 /*
1889 LinearLight: as defined by Abode Photoshop, according to
1890 http://www.simplefilter.de/en/basics/mixmods.html is:
1891
1892 f(Sc,Dc) = Dc + 2*Sc - 1
1893 */
1894 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1895 break;
1896 }
1897 case LightenCompositeOp:
1898 {
1899 if (Sc > Dc)
1900 {
1901 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1902 break;
1903 }
1904 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1905 break;
1906 }
cristye4a40472011-12-22 02:56:19 +00001907 case LightenIntensityCompositeOp:
1908 {
1909 /*
1910 Lighten is equivalent to a 'Maximum' method
1911 OR a greyscale version of a binary 'And'
1912 OR the 'Union' of pixel sets.
1913 */
1914 pixel=Sa*GetPixelIntensity(composite_image,p) >
1915 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1916 break;
1917 }
cristye4a40472011-12-22 02:56:19 +00001918 case LuminizeCompositeOp:
1919 {
cristy99abff32011-12-24 20:45:16 +00001920 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001921 {
1922 pixel=Dc;
1923 break;
1924 }
cristy99abff32011-12-24 20:45:16 +00001925 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001926 {
1927 pixel=Sc;
1928 break;
1929 }
1930 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1931 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1932 CompositeHSB(GetPixelRed(composite_image,p),
1933 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1934 &sans,&sans,&brightness);
1935 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1936 switch (channel)
1937 {
1938 case RedPixelChannel: pixel=red; break;
1939 case GreenPixelChannel: pixel=green; break;
1940 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001941 default: pixel=Dc; break;
1942 }
1943 break;
1944 }
1945 case MathematicsCompositeOp:
1946 {
1947 /*
1948 'Mathematics' a free form user control mathematical composition
1949 is defined as...
1950
1951 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
1952
1953 Where the arguments A,B,C,D are (currently) passed to composite
1954 as a command separated 'geometry' string in "compose:args" image
1955 artifact.
1956
1957 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
1958
1959 Applying the SVG transparency formula (see above), we get...
1960
1961 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
1962
1963 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
1964 Dca*(1.0-Sa)
1965 */
1966 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
1967 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
1968 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
1969 break;
1970 }
1971 case MinusDstCompositeOp:
1972 {
1973 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
1974 break;
1975 }
1976 case MinusSrcCompositeOp:
1977 {
1978 /*
1979 Minus source from destination.
1980
1981 f(Sc,Dc) = Sc - Dc
1982 */
cristy99abff32011-12-24 20:45:16 +00001983 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00001984 break;
1985 }
1986 case ModulateCompositeOp:
1987 {
1988 ssize_t
1989 offset;
1990
cristy99abff32011-12-24 20:45:16 +00001991 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001992 {
1993 pixel=Dc;
1994 break;
1995 }
1996 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
1997 if (offset == 0)
1998 {
1999 pixel=Dc;
2000 break;
2001 }
2002 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2003 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2004 brightness+=(0.01*percent_brightness*offset)/midpoint;
2005 saturation*=0.01*percent_saturation;
2006 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2007 switch (channel)
2008 {
2009 case RedPixelChannel: pixel=red; break;
2010 case GreenPixelChannel: pixel=green; break;
2011 case BluePixelChannel: pixel=blue; break;
2012 default: pixel=Dc; break;
2013 }
2014 break;
2015 }
2016 case ModulusAddCompositeOp:
2017 {
2018 pixel=Sc+Dc;
2019 if (pixel > QuantumRange)
2020 pixel-=(QuantumRange+1.0);
2021 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2022 break;
2023 }
2024 case ModulusSubtractCompositeOp:
2025 {
cristy99abff32011-12-24 20:45:16 +00002026 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002027 if (pixel < 0.0)
2028 pixel+=(QuantumRange+1.0);
2029 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2030 break;
2031 }
2032 case MultiplyCompositeOp:
2033 {
cristy99abff32011-12-24 20:45:16 +00002034 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002035 break;
2036 }
2037 case OutCompositeOp:
2038 case SrcOutCompositeOp:
2039 {
2040 pixel=gamma*(Sa*Sc*(1.0-Da));
2041 break;
2042 }
2043 case OverCompositeOp:
2044 case SrcOverCompositeOp:
2045 {
cristy99abff32011-12-24 20:45:16 +00002046 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002047 break;
2048 }
2049 case OverlayCompositeOp:
2050 {
2051 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002052 {
2053 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2054 (1.0-Da));
2055 break;
2056 }
2057 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2058 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002059 break;
2060 }
2061 case PegtopLightCompositeOp:
2062 {
2063 /*
2064 PegTop: A Soft-Light alternative: A continuous version of the
2065 Softlight function, producing very similar results.
2066
2067 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2068
2069 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2070 */
2071 if (fabs(Da) < MagickEpsilon)
2072 {
cristy99abff32011-12-24 20:45:16 +00002073 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002074 break;
2075 }
cristy99abff32011-12-24 20:45:16 +00002076 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2077 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002078 break;
2079 }
2080 case PinLightCompositeOp:
2081 {
2082 /*
2083 PinLight: A Photoshop 7 composition method
2084 http://www.simplefilter.de/en/basics/mixmods.html
2085
2086 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2087 */
2088 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2089 {
cristy99abff32011-12-24 20:45:16 +00002090 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002091 break;
2092 }
2093 if ((Dca*Sa) > (2.0*Sca*Da))
2094 {
cristy99abff32011-12-24 20:45:16 +00002095 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002096 break;
2097 }
cristy99abff32011-12-24 20:45:16 +00002098 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002099 break;
2100 }
2101 case PlusCompositeOp:
2102 {
cristy99abff32011-12-24 20:45:16 +00002103 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002104 break;
2105 }
2106 case SaturateCompositeOp:
2107 {
cristy99abff32011-12-24 20:45:16 +00002108 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002109 {
2110 pixel=Dc;
2111 break;
2112 }
cristy99abff32011-12-24 20:45:16 +00002113 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002114 {
2115 pixel=Sc;
2116 break;
2117 }
2118 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2119 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2120 CompositeHSB(GetPixelRed(composite_image,p),
2121 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
2122 &sans,&saturation,&sans);
2123 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2124 switch (channel)
2125 {
2126 case RedPixelChannel: pixel=red; break;
2127 case GreenPixelChannel: pixel=green; break;
2128 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002129 default: pixel=Dc; break;
2130 }
2131 break;
2132 }
2133 case ScreenCompositeOp:
2134 {
2135 /*
2136 Screen: a negated multiply:
2137
2138 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2139 */
cristy99abff32011-12-24 20:45:16 +00002140 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002141 break;
2142 }
2143 case SoftLightCompositeOp:
2144 {
2145 /*
2146 Refer to the March 2009 SVG specification.
2147 */
2148 if ((2.0*Sca) < Sa)
2149 {
cristy99abff32011-12-24 20:45:16 +00002150 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2151 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002152 break;
2153 }
2154 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2155 {
cristy99abff32011-12-24 20:45:16 +00002156 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2157 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2158 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002159 break;
2160 }
cristy99abff32011-12-24 20:45:16 +00002161 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2162 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002163 break;
2164 }
2165 case ThresholdCompositeOp:
2166 {
2167 MagickRealType
2168 delta;
2169
2170 delta=Sc-Dc;
2171 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2172 {
2173 pixel=gamma*Dc;
2174 break;
2175 }
2176 pixel=gamma*(Dc+delta*amount);
2177 break;
2178 }
2179 case VividLightCompositeOp:
2180 {
2181 /*
2182 VividLight: A Photoshop 7 composition method. See
2183 http://www.simplefilter.de/en/basics/mixmods.html.
2184
2185 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2186 */
2187 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2188 {
cristy99abff32011-12-24 20:45:16 +00002189 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002190 break;
2191 }
2192 if ((2.0*Sca) <= Sa)
2193 {
cristy99abff32011-12-24 20:45:16 +00002194 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2195 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002196 break;
2197 }
cristy99abff32011-12-24 20:45:16 +00002198 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2199 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002200 break;
2201 }
2202 case XorCompositeOp:
2203 {
cristy99abff32011-12-24 20:45:16 +00002204 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002205 break;
2206 }
2207 default:
2208 {
2209 pixel=Sc;
2210 break;
2211 }
2212 }
2213 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002214 }
cristyed231572011-07-14 02:18:59 +00002215 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002216 channels=GetPixelChannels(composite_image);
2217 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002218 p=pixels;
cristyed231572011-07-14 02:18:59 +00002219 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002220 }
2221 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2222 status=MagickFalse;
2223 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2224 {
2225 MagickBooleanType
2226 proceed;
2227
2228#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002229 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002230#endif
2231 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2232 image->rows);
2233 if (proceed == MagickFalse)
2234 status=MagickFalse;
2235 }
2236 }
2237 composite_view=DestroyCacheView(composite_view);
2238 image_view=DestroyCacheView(image_view);
2239 if (destination_image != (Image * ) NULL)
2240 destination_image=DestroyImage(destination_image);
2241 return(status);
2242}
2243
2244/*
2245%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2246% %
2247% %
2248% %
2249% T e x t u r e I m a g e %
2250% %
2251% %
2252% %
2253%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2254%
2255% TextureImage() repeatedly tiles the texture image across and down the image
2256% canvas.
2257%
2258% The format of the TextureImage method is:
2259%
cristy30d8c942012-02-07 13:44:59 +00002260% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002261% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002262%
2263% A description of each parameter follows:
2264%
2265% o image: the image.
2266%
cristye6178502011-12-23 17:02:29 +00002267% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002268%
2269*/
cristy30d8c942012-02-07 13:44:59 +00002270MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2271 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002272{
2273#define TextureImageTag "Texture/Image"
2274
2275 CacheView
2276 *image_view,
2277 *texture_view;
2278
cristy30d8c942012-02-07 13:44:59 +00002279 Image
2280 *texture_image;
2281
cristy4c08aed2011-07-01 19:47:50 +00002282 MagickBooleanType
2283 status;
2284
2285 ssize_t
2286 y;
2287
2288 assert(image != (Image *) NULL);
2289 if (image->debug != MagickFalse)
2290 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2291 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002292 if (texture == (const Image *) NULL)
2293 return(MagickFalse);
2294 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2295 return(MagickFalse);
2296 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002297 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002298 return(MagickFalse);
cristy387430f2012-02-07 13:09:46 +00002299 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2300 exception);
cristy4c08aed2011-07-01 19:47:50 +00002301 status=MagickTrue;
2302 if ((image->compose != CopyCompositeOp) &&
2303 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002304 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002305 {
2306 /*
2307 Tile texture onto the image background.
2308 */
2309#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy19593872012-01-22 02:00:33 +00002310 #pragma omp parallel for schedule(static) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002311#endif
cristye6178502011-12-23 17:02:29 +00002312 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002313 {
2314 register ssize_t
2315 x;
2316
2317 if (status == MagickFalse)
2318 continue;
cristye6178502011-12-23 17:02:29 +00002319 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002320 {
2321 MagickBooleanType
2322 thread_status;
2323
cristyfeb3e962012-03-29 17:25:55 +00002324 thread_status=CompositeImage(image,texture_image,image->compose,
2325 MagickFalse,x+texture_image->tile_offset.x,y+
2326 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002327 if (thread_status == MagickFalse)
2328 {
2329 status=thread_status;
2330 break;
2331 }
2332 }
2333 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2334 {
2335 MagickBooleanType
2336 proceed;
2337
2338#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002339 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002340#endif
2341 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2342 y,image->rows);
2343 if (proceed == MagickFalse)
2344 status=MagickFalse;
2345 }
2346 }
2347 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2348 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002349 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002350 return(status);
2351 }
2352 /*
2353 Tile texture onto the image background (optimized).
2354 */
2355 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00002356 image_view=AcquireCacheView(image);
cristye6178502011-12-23 17:02:29 +00002357 texture_view=AcquireCacheView(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002358#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy19593872012-01-22 02:00:33 +00002359 #pragma omp parallel for schedule(static) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002360#endif
2361 for (y=0; y < (ssize_t) image->rows; y++)
2362 {
2363 MagickBooleanType
2364 sync;
2365
2366 register const Quantum
2367 *p,
2368 *pixels;
2369
2370 register ssize_t
2371 x;
2372
2373 register Quantum
2374 *q;
2375
2376 size_t
2377 width;
2378
2379 if (status == MagickFalse)
2380 continue;
cristye6178502011-12-23 17:02:29 +00002381 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2382 (y+texture_image->tile_offset.y) % texture_image->rows,
2383 texture_image->columns,1,exception);
2384 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002385 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2386 {
2387 status=MagickFalse;
2388 continue;
2389 }
cristye6178502011-12-23 17:02:29 +00002390 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002391 {
2392 register ssize_t
cristye6178502011-12-23 17:02:29 +00002393 j;
cristy4c08aed2011-07-01 19:47:50 +00002394
2395 p=pixels;
cristye6178502011-12-23 17:02:29 +00002396 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002397 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2398 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002399 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002400 {
cristye6178502011-12-23 17:02:29 +00002401 register ssize_t
2402 i;
2403
cristy10a6c612012-01-29 21:41:05 +00002404 if (GetPixelMask(image,p) != 0)
2405 {
2406 p+=GetPixelChannels(texture_image);
2407 q+=GetPixelChannels(image);
2408 continue;
2409 }
cristye6178502011-12-23 17:02:29 +00002410 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2411 {
2412 PixelChannel
2413 channel;
2414
2415 PixelTrait
2416 texture_traits,
2417 traits;
2418
2419 channel=GetPixelChannelMapChannel(texture_image,i);
2420 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2421 traits=GetPixelChannelMapTraits(image,channel);
2422 if ((traits == UndefinedPixelTrait) ||
2423 (texture_traits == UndefinedPixelTrait))
2424 continue;
2425 SetPixelChannel(image,channel,p[i],q);
2426 }
2427 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002428 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002429 }
2430 }
2431 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2432 if (sync == MagickFalse)
2433 status=MagickFalse;
2434 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2435 {
2436 MagickBooleanType
2437 proceed;
2438
2439#if defined(MAGICKCORE_OPENMP_SUPPORT)
2440 #pragma omp critical (MagickCore_TextureImage)
2441#endif
2442 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2443 image->rows);
2444 if (proceed == MagickFalse)
2445 status=MagickFalse;
2446 }
2447 }
2448 texture_view=DestroyCacheView(texture_view);
2449 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002450 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002451 return(status);
2452}