blob: 59a36e6560dd7571d002bc15da22c0e28e31a0af [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,
97% const CompositeOperator compose,Image *composite_image,
cristye941a752011-10-15 01:52:48 +000098% const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +000099%
100% A description of each parameter follows:
101%
102% o image: the destination image, modified by he composition
103%
cristy4c08aed2011-07-01 19:47:50 +0000104% o compose: This operator affects how the composite is applied to
105% the image. The operators and how they are utilized are listed here
106% http://www.w3.org/TR/SVG12/#compositing.
107%
108% o composite_image: the composite (source) image.
109%
110% o x_offset: the column offset of the composited image.
111%
112% o y_offset: the row offset of the composited image.
113%
114% Extra Controls from Image meta-data in 'composite_image' (artifacts)
115%
116% o "compose:args"
117% A string containing extra numerical arguments for specific compose
118% methods, generally expressed as a 'geometry' or a comma separated list
119% of numbers.
120%
121% Compose methods needing such arguments include "BlendCompositeOp" and
122% "DisplaceCompositeOp".
123%
124% o "compose:outside-overlay"
125% Modify how the composition is to effect areas not directly covered
126% by the 'composite_image' at the offset given. Normally this is
127% dependant on the 'compose' method, especially Duff-Porter methods.
128%
129% If set to "false" then disable all normal handling of pixels not
130% covered by the composite_image. Typically used for repeated tiling
131% of the composite_image by the calling API.
132%
133% Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
134%
cristye941a752011-10-15 01:52:48 +0000135% o exception: return any errors or warnings in this structure.
136%
cristy4c08aed2011-07-01 19:47:50 +0000137*/
138
cristye4a40472011-12-22 02:56:19 +0000139static void CompositeHSB(const Quantum red,const Quantum green,
140 const Quantum blue,double *hue,double *saturation,double *brightness)
cristy4c08aed2011-07-01 19:47:50 +0000141{
cristye4a40472011-12-22 02:56:19 +0000142 double
143 delta;
cristy4c08aed2011-07-01 19:47:50 +0000144
cristye4a40472011-12-22 02:56:19 +0000145 Quantum
cristy4c08aed2011-07-01 19:47:50 +0000146 max,
147 min;
148
149 /*
150 Convert RGB to HSB colorspace.
151 */
152 assert(hue != (double *) NULL);
153 assert(saturation != (double *) NULL);
154 assert(brightness != (double *) NULL);
155 max=(red > green ? red : green);
156 if (blue > max)
157 max=blue;
158 min=(red < green ? red : green);
159 if (blue < min)
160 min=blue;
161 *hue=0.0;
162 *saturation=0.0;
163 *brightness=(double) (QuantumScale*max);
cristye4a40472011-12-22 02:56:19 +0000164 if (fabs((double) max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000165 return;
166 *saturation=(double) (1.0-min/max);
cristye4a40472011-12-22 02:56:19 +0000167 delta=(MagickRealType) max-min;
cristyaa83c2c2011-09-21 13:36:25 +0000168 if (fabs(delta) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000169 return;
cristye4a40472011-12-22 02:56:19 +0000170 if (fabs((double) red-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000171 *hue=(double) ((green-blue)/delta);
172 else
cristye4a40472011-12-22 02:56:19 +0000173 if (fabs((double) green-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000174 *hue=(double) (2.0+(blue-red)/delta);
175 else
cristye4a40472011-12-22 02:56:19 +0000176 if (fabs((double) blue-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000177 *hue=(double) (4.0+(red-green)/delta);
178 *hue/=6.0;
179 if (*hue < 0.0)
180 *hue+=1.0;
181}
182
cristy4c08aed2011-07-01 19:47:50 +0000183static void HSBComposite(const double hue,const double saturation,
cristye4a40472011-12-22 02:56:19 +0000184 const double brightness,double *red,double *green,double *blue)
cristy4c08aed2011-07-01 19:47:50 +0000185{
cristya96f2492011-12-14 18:25:41 +0000186 double
cristy4c08aed2011-07-01 19:47:50 +0000187 f,
188 h,
189 p,
190 q,
191 t;
192
193 /*
194 Convert HSB to RGB colorspace.
195 */
cristya96f2492011-12-14 18:25:41 +0000196 assert(red != (double *) NULL);
197 assert(green != (double *) NULL);
198 assert(blue != (double *) NULL);
cristy4c08aed2011-07-01 19:47:50 +0000199 if (saturation == 0.0)
200 {
cristya96f2492011-12-14 18:25:41 +0000201 *red=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000202 *green=(*red);
203 *blue=(*red);
204 return;
205 }
206 h=6.0*(hue-floor(hue));
207 f=h-floor((double) h);
208 p=brightness*(1.0-saturation);
209 q=brightness*(1.0-saturation*f);
210 t=brightness*(1.0-saturation*(1.0-f));
211 switch ((int) h)
212 {
213 case 0:
214 default:
215 {
cristya96f2492011-12-14 18:25:41 +0000216 *red=(double) QuantumRange*brightness;
217 *green=(double) QuantumRange*t;
218 *blue=(double) QuantumRange*p;
cristy4c08aed2011-07-01 19:47:50 +0000219 break;
220 }
221 case 1:
222 {
cristya96f2492011-12-14 18:25:41 +0000223 *red=(double) QuantumRange*q;
224 *green=(double) QuantumRange*brightness;
225 *blue=(double) QuantumRange*p;
cristy4c08aed2011-07-01 19:47:50 +0000226 break;
227 }
228 case 2:
229 {
cristya96f2492011-12-14 18:25:41 +0000230 *red=(double) QuantumRange*p;
231 *green=(double) QuantumRange*brightness;
232 *blue=(double) QuantumRange*t;
cristy4c08aed2011-07-01 19:47:50 +0000233 break;
234 }
235 case 3:
236 {
cristya96f2492011-12-14 18:25:41 +0000237 *red=(double) QuantumRange*p;
238 *green=(double) QuantumRange*q;
239 *blue=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000240 break;
241 }
242 case 4:
243 {
cristya96f2492011-12-14 18:25:41 +0000244 *red=(double) QuantumRange*t;
245 *green=(double) QuantumRange*p;
246 *blue=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000247 break;
248 }
249 case 5:
250 {
cristya96f2492011-12-14 18:25:41 +0000251 *red=(double) QuantumRange*brightness;
252 *green=(double) QuantumRange*p;
253 *blue=(double) QuantumRange*q;
cristy4c08aed2011-07-01 19:47:50 +0000254 break;
255 }
256 }
257}
258
cristye4a40472011-12-22 02:56:19 +0000259static inline double MagickMin(const double x,const double y)
260{
261 if (x < y)
262 return(x);
263 return(y);
264}
265static inline double MagickMax(const double x,const double y)
266{
267 if (x > y)
268 return(x);
269 return(y);
270}
271
272static MagickBooleanType CompositeOverImage(Image *image,
273 const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset,
274 ExceptionInfo *exception)
275{
276#define CompositeImageTag "Composite/Image"
277
278 CacheView
279 *composite_view,
280 *image_view;
281
282 const char
283 *value;
284
285 MagickBooleanType
286 modify_outside_overlay,
287 status;
288
289 MagickOffsetType
290 progress;
291
292 ssize_t
293 y;
294
cristye4a40472011-12-22 02:56:19 +0000295 /*
296 Prepare composite image.
297 */
298 modify_outside_overlay=MagickFalse;
299 value=GetImageArtifact(composite_image,"compose:outside-overlay");
300 if (value != (const char *) NULL)
301 modify_outside_overlay=IsMagickTrue(value);
302 /*
303 Composite image.
304 */
305 status=MagickTrue;
306 progress=0;
307 image_view=AcquireCacheView(image);
308 composite_view=AcquireCacheView(composite_image);
309#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000310 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye4a40472011-12-22 02:56:19 +0000311#endif
312 for (y=0; y < (ssize_t) image->rows; y++)
313 {
314 const Quantum
315 *pixels;
316
317 register const Quantum
318 *restrict p;
319
320 register Quantum
321 *restrict q;
322
323 register ssize_t
324 x;
325
cristy564a5692012-01-20 23:56:26 +0000326 size_t
327 channels;
328
cristye4a40472011-12-22 02:56:19 +0000329 if (status == MagickFalse)
330 continue;
331 if (modify_outside_overlay == MagickFalse)
332 {
333 if (y < y_offset)
334 continue;
335 if ((y-y_offset) >= (ssize_t) composite_image->rows)
336 continue;
337 }
338 /*
339 If pixels is NULL, y is outside overlay region.
340 */
341 pixels=(Quantum *) NULL;
342 p=(Quantum *) NULL;
343 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
344 {
345 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
346 composite_image->columns,1,exception);
347 if (p == (const Quantum *) NULL)
348 {
349 status=MagickFalse;
350 continue;
351 }
352 pixels=p;
353 if (x_offset < 0)
354 p-=x_offset*GetPixelChannels(composite_image);
355 }
356 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
357 if (q == (Quantum *) NULL)
358 {
359 status=MagickFalse;
360 continue;
361 }
362 for (x=0; x < (ssize_t) image->columns; x++)
363 {
364 MagickRealType
365 alpha,
366 Da,
367 Dc,
368 gamma,
369 Sa,
370 Sc;
371
372 register ssize_t
373 i;
374
375 if (modify_outside_overlay == MagickFalse)
376 {
377 if (x < x_offset)
378 {
379 q+=GetPixelChannels(image);
380 continue;
381 }
382 if ((x-x_offset) >= (ssize_t) composite_image->columns)
383 break;
384 }
385 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
386 ((x-x_offset) >= (ssize_t) composite_image->columns))
387 {
388 Quantum
389 source[MaxPixelChannels];
390
391 /*
392 Virtual composite:
393 Sc: source color.
394 Dc: destination color.
395 */
cristy10a6c612012-01-29 21:41:05 +0000396 if (GetPixelMask(image,q) != 0)
397 {
398 q+=GetPixelChannels(image);
399 continue;
400 }
cristyc94ba6f2012-01-29 23:19:58 +0000401 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
402 source,exception);
cristye4a40472011-12-22 02:56:19 +0000403 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
404 {
405 PixelChannel
406 channel;
407
408 PixelTrait
409 composite_traits,
410 traits;
411
412 channel=GetPixelChannelMapChannel(image,i);
413 traits=GetPixelChannelMapTraits(image,channel);
cristy10a6c612012-01-29 21:41:05 +0000414 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000415 if ((traits == UndefinedPixelTrait) ||
416 (composite_traits == UndefinedPixelTrait))
417 continue;
418 q[i]=source[channel];
419 }
420 q+=GetPixelChannels(image);
421 continue;
422 }
423 /*
424 Authentic composite:
425 Sa: normalized source alpha.
426 Da: normalized destination alpha.
427 */
cristyc94ba6f2012-01-29 23:19:58 +0000428 if (GetPixelMask(composite_image,p) != 0)
429 {
430 p+=GetPixelChannels(composite_image);
431 channels=GetPixelChannels(composite_image);
432 if (p >= (pixels+channels*composite_image->columns))
433 p=pixels;
434 q+=GetPixelChannels(image);
435 continue;
436 }
cristye4a40472011-12-22 02:56:19 +0000437 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
438 Da=QuantumScale*GetPixelAlpha(image,q);
439 alpha=Sa*(-Da)+Sa+Da;
440 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
441 {
442 PixelChannel
443 channel;
444
445 PixelTrait
446 composite_traits,
447 traits;
448
449 channel=GetPixelChannelMapChannel(image,i);
450 traits=GetPixelChannelMapTraits(image,channel);
451 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
452 if ((traits == UndefinedPixelTrait) ||
453 (composite_traits == UndefinedPixelTrait))
454 continue;
455 if ((traits & CopyPixelTrait) != 0)
456 {
457 if (channel != AlphaPixelChannel)
458 {
459 /*
460 Copy channel.
461 */
462 q[i]=GetPixelChannel(composite_image,channel,p);
463 continue;
464 }
465 /*
466 Set alpha channel.
467 */
468 q[i]=ClampToQuantum(QuantumRange*alpha);
469 continue;
470 }
471 /*
472 Sc: source color.
473 Dc: destination color.
474 */
475 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
476 Dc=(MagickRealType) q[i];
477 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
478 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
479 }
480 p+=GetPixelChannels(composite_image);
481 channels=GetPixelChannels(composite_image);
482 if (p >= (pixels+channels*composite_image->columns))
483 p=pixels;
484 q+=GetPixelChannels(image);
485 }
486 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
487 status=MagickFalse;
488 if (image->progress_monitor != (MagickProgressMonitor) NULL)
489 {
490 MagickBooleanType
491 proceed;
492
493#if defined(MAGICKCORE_OPENMP_SUPPORT)
494 #pragma omp critical (MagickCore_CompositeImage)
495#endif
496 proceed=SetImageProgress(image,CompositeImageTag,progress++,
497 image->rows);
498 if (proceed == MagickFalse)
499 status=MagickFalse;
500 }
501 }
502 composite_view=DestroyCacheView(composite_view);
503 image_view=DestroyCacheView(image_view);
504 return(status);
505}
506
cristy4c08aed2011-07-01 19:47:50 +0000507MagickExport MagickBooleanType CompositeImage(Image *image,
508 const CompositeOperator compose,const Image *composite_image,
cristye941a752011-10-15 01:52:48 +0000509 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000510{
cristy4c08aed2011-07-01 19:47:50 +0000511#define CompositeImageTag "Composite/Image"
512
513 CacheView
514 *composite_view,
515 *image_view;
516
517 const char
518 *value;
519
cristy4c08aed2011-07-01 19:47:50 +0000520 GeometryInfo
521 geometry_info;
522
523 Image
524 *destination_image;
525
526 MagickBooleanType
527 modify_outside_overlay,
528 status;
529
530 MagickOffsetType
531 progress;
532
cristy4c08aed2011-07-01 19:47:50 +0000533 MagickRealType
534 amount,
535 destination_dissolve,
536 midpoint,
537 percent_brightness,
538 percent_saturation,
539 source_dissolve,
540 threshold;
541
542 MagickStatusType
543 flags;
544
cristyd197cbb2012-01-13 02:14:12 +0000545 ssize_t
546 y;
547
cristy4c08aed2011-07-01 19:47:50 +0000548 /*
cristye4a40472011-12-22 02:56:19 +0000549 Composition based on the SVG specification:
550
551 A Composition is defined by...
552 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
553 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
554 Y = 1 for source preserved
555 Z = 1 for destination preserved
556
557 Conversion to transparency (then optimized)
558 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
559 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
560
561 Where...
562 Sca = Sc*Sa normalized Source color divided by Source alpha
563 Dca = Dc*Da normalized Dest color divided by Dest alpha
564 Dc' = Dca'/Da' the desired color value for this channel.
565
566 Da' in in the follow formula as 'gamma' The resulting alpla value.
567
568 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
569 the following optimizations...
570 gamma = Sa+Da-Sa*Da;
571 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
572 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
573
574 The above SVG definitions also definate that Mathematical Composition
575 methods should use a 'Over' blending mode for Alpha Channel.
576 It however was not applied for composition modes of 'Plus', 'Minus',
577 the modulus versions of 'Add' and 'Subtract'.
578
579 Mathematical operator changes to be applied from IM v6.7...
580
581 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
582 'ModulusAdd' and 'ModulusSubtract' for clarity.
583
584 2) All mathematical compositions work as per the SVG specification
585 with regard to blending. This now includes 'ModulusAdd' and
586 'ModulusSubtract'.
587
588 3) When the special channel flag 'sync' (syncronize channel updates)
589 is turned off (enabled by default) then mathematical compositions are
590 only performed on the channels specified, and are applied
591 independantally of each other. In other words the mathematics is
592 performed as 'pure' mathematical operations, rather than as image
593 operations.
cristy4c08aed2011-07-01 19:47:50 +0000594 */
595 assert(image != (Image *) NULL);
596 assert(image->signature == MagickSignature);
597 if (image->debug != MagickFalse)
598 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
599 assert(composite_image != (Image *) NULL);
600 assert(composite_image->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000601 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000602 return(MagickFalse);
cristye4a40472011-12-22 02:56:19 +0000603 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
604 {
605 status=CompositeOverImage(image,composite_image,x_offset,y_offset,
606 exception);
607 return(status);
608 }
cristy4c08aed2011-07-01 19:47:50 +0000609 destination_image=(Image *) NULL;
610 amount=0.5;
611 destination_dissolve=1.0;
612 modify_outside_overlay=MagickFalse;
613 percent_brightness=100.0;
614 percent_saturation=100.0;
615 source_dissolve=1.0;
616 threshold=0.05f;
617 switch (compose)
618 {
619 case ClearCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000620 case DstAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +0000621 case DstInCompositeOp:
622 case InCompositeOp:
623 case OutCompositeOp:
624 case SrcCompositeOp:
625 case SrcInCompositeOp:
626 case SrcOutCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000627 {
628 /*
629 Modify destination outside the overlaid region.
630 */
631 modify_outside_overlay=MagickTrue;
632 break;
633 }
634 case CopyCompositeOp:
635 {
636 if ((x_offset < 0) || (y_offset < 0))
637 break;
638 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
639 break;
640 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
641 break;
642 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000643 image_view=AcquireCacheView(image);
644 composite_view=AcquireCacheView(composite_image);
645#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000646 #pragma omp parallel for schedule(static,4) shared(status)
cristy4c08aed2011-07-01 19:47:50 +0000647#endif
648 for (y=0; y < (ssize_t) composite_image->rows; y++)
649 {
650 MagickBooleanType
651 sync;
652
653 register const Quantum
654 *p;
655
656 register Quantum
657 *q;
658
659 register ssize_t
660 x;
661
662 if (status == MagickFalse)
663 continue;
664 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
665 1,exception);
666 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
667 composite_image->columns,1,exception);
668 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
669 {
670 status=MagickFalse;
671 continue;
672 }
673 for (x=0; x < (ssize_t) composite_image->columns; x++)
674 {
cristybdecccc2011-12-24 22:52:16 +0000675 register ssize_t
676 i;
677
cristy10a6c612012-01-29 21:41:05 +0000678 if (GetPixelMask(image,p) != 0)
679 {
680 p+=GetPixelChannels(composite_image);
681 q+=GetPixelChannels(image);
682 continue;
683 }
cristybdecccc2011-12-24 22:52:16 +0000684 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
685 {
686 PixelChannel
687 channel;
688
689 PixelTrait
690 composite_traits,
691 traits;
692
693 channel=GetPixelChannelMapChannel(composite_image,i);
694 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
695 traits=GetPixelChannelMapTraits(image,channel);
696 if ((traits == UndefinedPixelTrait) ||
697 (composite_traits == UndefinedPixelTrait))
698 continue;
699 SetPixelChannel(image,channel,p[i],q);
700 }
cristyed231572011-07-14 02:18:59 +0000701 p+=GetPixelChannels(composite_image);
702 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000703 }
704 sync=SyncCacheViewAuthenticPixels(image_view,exception);
705 if (sync == MagickFalse)
706 status=MagickFalse;
707 if (image->progress_monitor != (MagickProgressMonitor) NULL)
708 {
709 MagickBooleanType
710 proceed;
711
712#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000713 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000714#endif
715 proceed=SetImageProgress(image,CompositeImageTag,
716 (MagickOffsetType) y,image->rows);
717 if (proceed == MagickFalse)
718 status=MagickFalse;
719 }
720 }
721 composite_view=DestroyCacheView(composite_view);
722 image_view=DestroyCacheView(image_view);
723 return(status);
724 }
cristye4a40472011-12-22 02:56:19 +0000725 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000726 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000727 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000728 {
729 /*
730 Modify destination outside the overlaid region and require an alpha
731 channel to exist, to add transparency.
732 */
733 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +0000734 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000735 modify_outside_overlay=MagickTrue;
736 break;
737 }
738 case BlurCompositeOp:
739 {
740 CacheView
741 *composite_view,
742 *destination_view;
743
744 PixelInfo
745 pixel;
746
747 MagickRealType
748 angle_range,
749 angle_start,
750 height,
751 width;
752
753 ResampleFilter
754 *resample_filter;
755
756 SegmentInfo
757 blur;
758
759 /*
760 Blur Image dictated by an overlay gradient map: X = red_channel;
761 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
762 */
763 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000764 exception);
cristy4c08aed2011-07-01 19:47:50 +0000765 if (destination_image == (Image *) NULL)
766 return(MagickFalse);
767 /*
768 Determine the horizontal and vertical maximim blur.
769 */
770 SetGeometryInfo(&geometry_info);
771 flags=NoValue;
772 value=GetImageArtifact(composite_image,"compose:args");
773 if (value != (char *) NULL)
774 flags=ParseGeometry(value,&geometry_info);
775 if ((flags & WidthValue) == 0 )
776 {
777 destination_image=DestroyImage(destination_image);
778 return(MagickFalse);
779 }
780 width=geometry_info.rho;
781 height=geometry_info.sigma;
782 blur.x1=geometry_info.rho;
783 blur.x2=0.0;
784 blur.y1=0.0;
785 blur.y2=geometry_info.sigma;
786 angle_start=0.0;
787 angle_range=0.0;
788 if ((flags & HeightValue) == 0)
789 blur.y2=blur.x1;
790 if ((flags & XValue) != 0 )
791 {
792 MagickRealType
793 angle;
794
795 angle=DegreesToRadians(geometry_info.xi);
796 blur.x1=width*cos(angle);
797 blur.x2=width*sin(angle);
798 blur.y1=(-height*sin(angle));
799 blur.y2=height*cos(angle);
800 }
801 if ((flags & YValue) != 0 )
802 {
803 angle_start=DegreesToRadians(geometry_info.xi);
804 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
805 }
806 /*
807 Blur Image by resampling.
808 */
cristy8a11cb12011-10-19 23:53:34 +0000809 resample_filter=AcquireResampleFilter(image,exception);
cristyaa2c16c2012-03-25 22:21:35 +0000810 SetResampleFilter(resample_filter,CubicFilter);
cristy4c08aed2011-07-01 19:47:50 +0000811 destination_view=AcquireCacheView(destination_image);
812 composite_view=AcquireCacheView(composite_image);
813 for (y=0; y < (ssize_t) composite_image->rows; y++)
814 {
815 MagickBooleanType
816 sync;
817
818 register const Quantum
819 *restrict p;
820
821 register Quantum
822 *restrict q;
823
824 register ssize_t
825 x;
826
827 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
828 continue;
829 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
830 1,exception);
831 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000832 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000833 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
834 break;
835 for (x=0; x < (ssize_t) composite_image->columns; x++)
836 {
837 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
838 {
cristyed231572011-07-14 02:18:59 +0000839 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000840 continue;
841 }
842 if (fabs(angle_range) > MagickEpsilon)
843 {
844 MagickRealType
845 angle;
846
847 angle=angle_start+angle_range*QuantumScale*
848 GetPixelBlue(composite_image,p);
849 blur.x1=width*cos(angle);
850 blur.x2=width*sin(angle);
851 blur.y1=(-height*sin(angle));
852 blur.y2=height*cos(angle);
853 }
854 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
855 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
856 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
857 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
858 GetPixelGreen(composite_image,p));
859 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
860 (double) y_offset+y,&pixel);
cristy803640d2011-11-17 02:11:32 +0000861 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000862 p+=GetPixelChannels(composite_image);
863 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000864 }
865 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
866 if (sync == MagickFalse)
867 break;
868 }
869 resample_filter=DestroyResampleFilter(resample_filter);
870 composite_view=DestroyCacheView(composite_view);
871 destination_view=DestroyCacheView(destination_view);
872 composite_image=destination_image;
873 break;
874 }
875 case DisplaceCompositeOp:
876 case DistortCompositeOp:
877 {
878 CacheView
879 *composite_view,
880 *destination_view,
881 *image_view;
882
883 PixelInfo
884 pixel;
885
886 MagickRealType
887 horizontal_scale,
888 vertical_scale;
889
890 PointInfo
891 center,
892 offset;
893
894 /*
895 Displace/Distort based on overlay gradient map:
896 X = red_channel; Y = green_channel;
897 compose:args = x_scale[,y_scale[,center.x,center.y]]
898 */
899 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000900 exception);
cristy4c08aed2011-07-01 19:47:50 +0000901 if (destination_image == (Image *) NULL)
902 return(MagickFalse);
903 SetGeometryInfo(&geometry_info);
904 flags=NoValue;
905 value=GetImageArtifact(composite_image,"compose:args");
906 if (value != (char *) NULL)
907 flags=ParseGeometry(value,&geometry_info);
908 if ((flags & (WidthValue|HeightValue)) == 0 )
909 {
910 if ((flags & AspectValue) == 0)
911 {
912 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
913 2.0;
914 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
915 }
916 else
917 {
918 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
919 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
920 }
921 }
922 else
923 {
924 horizontal_scale=geometry_info.rho;
925 vertical_scale=geometry_info.sigma;
926 if ((flags & PercentValue) != 0)
927 {
928 if ((flags & AspectValue) == 0)
929 {
930 horizontal_scale*=(composite_image->columns-1.0)/200.0;
931 vertical_scale*=(composite_image->rows-1.0)/200.0;
932 }
933 else
934 {
935 horizontal_scale*=(image->columns-1.0)/200.0;
936 vertical_scale*=(image->rows-1.0)/200.0;
937 }
938 }
939 if ((flags & HeightValue) == 0)
940 vertical_scale=horizontal_scale;
941 }
942 /*
943 Determine fixed center point for absolute distortion map
944 Absolute distort ==
945 Displace offset relative to a fixed absolute point
946 Select that point according to +X+Y user inputs.
947 default = center of overlay image
948 arg flag '!' = locations/percentage relative to background image
949 */
950 center.x=(MagickRealType) x_offset;
951 center.y=(MagickRealType) y_offset;
952 if (compose == DistortCompositeOp)
953 {
954 if ((flags & XValue) == 0)
955 if ((flags & AspectValue) == 0)
956 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
957 2.0;
958 else
959 center.x=((MagickRealType) image->columns-1)/2.0;
960 else
961 if ((flags & AspectValue) == 0)
962 center.x=(MagickRealType) x_offset+geometry_info.xi;
963 else
964 center.x=geometry_info.xi;
965 if ((flags & YValue) == 0)
966 if ((flags & AspectValue) == 0)
967 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
968 else
969 center.y=((MagickRealType) image->rows-1)/2.0;
970 else
971 if ((flags & AspectValue) == 0)
972 center.y=(MagickRealType) y_offset+geometry_info.psi;
973 else
974 center.y=geometry_info.psi;
975 }
976 /*
977 Shift the pixel offset point as defined by the provided,
978 displacement/distortion map. -- Like a lens...
979 */
cristye10859a2011-12-18 22:28:59 +0000980 GetPixelInfo(image,&pixel);
cristy4c08aed2011-07-01 19:47:50 +0000981 image_view=AcquireCacheView(image);
982 destination_view=AcquireCacheView(destination_image);
983 composite_view=AcquireCacheView(composite_image);
984 for (y=0; y < (ssize_t) composite_image->rows; y++)
985 {
986 MagickBooleanType
987 sync;
988
989 register const Quantum
990 *restrict p;
991
992 register Quantum
993 *restrict q;
994
995 register ssize_t
996 x;
997
998 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
999 continue;
1000 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1001 1,exception);
1002 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001003 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001004 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1005 break;
1006 for (x=0; x < (ssize_t) composite_image->columns; x++)
1007 {
1008 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1009 {
cristyed231572011-07-14 02:18:59 +00001010 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001011 continue;
1012 }
1013 /*
1014 Displace the offset.
1015 */
1016 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1017 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1018 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1019 x : 0);
1020 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1021 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1022 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1023 y : 0);
1024 (void) InterpolatePixelInfo(image,image_view,
1025 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1026 &pixel,exception);
1027 /*
1028 Mask with the 'invalid pixel mask' in alpha channel.
1029 */
1030 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001031 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001032 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001033 p+=GetPixelChannels(composite_image);
1034 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001035 }
1036 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1037 if (sync == MagickFalse)
1038 break;
1039 }
1040 destination_view=DestroyCacheView(destination_view);
1041 composite_view=DestroyCacheView(composite_view);
1042 image_view=DestroyCacheView(image_view);
1043 composite_image=destination_image;
1044 break;
1045 }
1046 case DissolveCompositeOp:
1047 {
1048 /*
1049 Geometry arguments to dissolve factors.
1050 */
1051 value=GetImageArtifact(composite_image,"compose:args");
1052 if (value != (char *) NULL)
1053 {
1054 flags=ParseGeometry(value,&geometry_info);
1055 source_dissolve=geometry_info.rho/100.0;
1056 destination_dissolve=1.0;
1057 if ((source_dissolve-MagickEpsilon) < 0.0)
1058 source_dissolve=0.0;
1059 if ((source_dissolve+MagickEpsilon) > 1.0)
1060 {
1061 destination_dissolve=2.0-source_dissolve;
1062 source_dissolve=1.0;
1063 }
1064 if ((flags & SigmaValue) != 0)
1065 destination_dissolve=geometry_info.sigma/100.0;
1066 if ((destination_dissolve-MagickEpsilon) < 0.0)
1067 destination_dissolve=0.0;
1068 modify_outside_overlay=MagickTrue;
1069 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1070 {
1071 destination_dissolve=1.0;
1072 modify_outside_overlay=MagickFalse;
1073 }
1074 }
1075 break;
1076 }
1077 case BlendCompositeOp:
1078 {
1079 value=GetImageArtifact(composite_image,"compose:args");
1080 if (value != (char *) NULL)
1081 {
1082 flags=ParseGeometry(value,&geometry_info);
1083 source_dissolve=geometry_info.rho/100.0;
1084 destination_dissolve=1.0-source_dissolve;
1085 if ((flags & SigmaValue) != 0)
1086 destination_dissolve=geometry_info.sigma/100.0;
1087 modify_outside_overlay=MagickTrue;
1088 if ((destination_dissolve+MagickEpsilon) > 1.0)
1089 modify_outside_overlay=MagickFalse;
1090 }
1091 break;
1092 }
1093 case MathematicsCompositeOp:
1094 {
1095 /*
1096 Just collect the values from "compose:args", setting.
1097 Unused values are set to zero automagically.
1098
1099 Arguments are normally a comma separated list, so this probably should
1100 be changed to some 'general comma list' parser, (with a minimum
1101 number of values)
1102 */
1103 SetGeometryInfo(&geometry_info);
1104 value=GetImageArtifact(composite_image,"compose:args");
1105 if (value != (char *) NULL)
1106 (void) ParseGeometry(value,&geometry_info);
1107 break;
1108 }
1109 case ModulateCompositeOp:
1110 {
1111 /*
1112 Determine the brightness and saturation scale.
1113 */
1114 value=GetImageArtifact(composite_image,"compose:args");
1115 if (value != (char *) NULL)
1116 {
1117 flags=ParseGeometry(value,&geometry_info);
1118 percent_brightness=geometry_info.rho;
1119 if ((flags & SigmaValue) != 0)
1120 percent_saturation=geometry_info.sigma;
1121 }
1122 break;
1123 }
1124 case ThresholdCompositeOp:
1125 {
1126 /*
1127 Determine the amount and threshold.
1128 */
1129 value=GetImageArtifact(composite_image,"compose:args");
1130 if (value != (char *) NULL)
1131 {
1132 flags=ParseGeometry(value,&geometry_info);
1133 amount=geometry_info.rho;
1134 threshold=geometry_info.sigma;
1135 if ((flags & SigmaValue) == 0)
1136 threshold=0.05f;
1137 }
1138 threshold*=QuantumRange;
1139 break;
1140 }
1141 default:
1142 break;
1143 }
1144 value=GetImageArtifact(composite_image,"compose:outside-overlay");
1145 if (value != (const char *) NULL)
1146 modify_outside_overlay=IsMagickTrue(value);
1147 /*
1148 Composite image.
1149 */
1150 status=MagickTrue;
1151 progress=0;
1152 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy4c08aed2011-07-01 19:47:50 +00001153 image_view=AcquireCacheView(image);
1154 composite_view=AcquireCacheView(composite_image);
1155#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00001156 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy4c08aed2011-07-01 19:47:50 +00001157#endif
1158 for (y=0; y < (ssize_t) image->rows; y++)
1159 {
1160 const Quantum
1161 *pixels;
1162
1163 double
cristye4a40472011-12-22 02:56:19 +00001164 blue,
cristy4c08aed2011-07-01 19:47:50 +00001165 brightness,
cristye4a40472011-12-22 02:56:19 +00001166 green,
cristy4c08aed2011-07-01 19:47:50 +00001167 hue,
cristye4a40472011-12-22 02:56:19 +00001168 red,
cristy4c08aed2011-07-01 19:47:50 +00001169 saturation;
1170
cristy4c08aed2011-07-01 19:47:50 +00001171 register const Quantum
1172 *restrict p;
1173
1174 register Quantum
1175 *restrict q;
1176
1177 register ssize_t
1178 x;
1179
1180 if (status == MagickFalse)
1181 continue;
1182 if (modify_outside_overlay == MagickFalse)
1183 {
1184 if (y < y_offset)
1185 continue;
1186 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1187 continue;
1188 }
1189 /*
1190 If pixels is NULL, y is outside overlay region.
1191 */
1192 pixels=(Quantum *) NULL;
1193 p=(Quantum *) NULL;
1194 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1195 {
1196 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1197 composite_image->columns,1,exception);
1198 if (p == (const Quantum *) NULL)
1199 {
1200 status=MagickFalse;
1201 continue;
1202 }
1203 pixels=p;
1204 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001205 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001206 }
1207 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001208 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001209 {
1210 status=MagickFalse;
1211 continue;
1212 }
cristy4c08aed2011-07-01 19:47:50 +00001213 hue=0.0;
1214 saturation=0.0;
1215 brightness=0.0;
1216 for (x=0; x < (ssize_t) image->columns; x++)
1217 {
cristye4a40472011-12-22 02:56:19 +00001218 MagickRealType
1219 alpha,
1220 Da,
1221 Dc,
1222 Dca,
1223 gamma,
1224 Sa,
1225 Sc,
1226 Sca;
1227
1228 register ssize_t
1229 i;
1230
cristy564a5692012-01-20 23:56:26 +00001231 size_t
1232 channels;
1233
cristy4c08aed2011-07-01 19:47:50 +00001234 if (modify_outside_overlay == MagickFalse)
1235 {
1236 if (x < x_offset)
1237 {
cristyed231572011-07-14 02:18:59 +00001238 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001239 continue;
1240 }
1241 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1242 break;
1243 }
cristye4a40472011-12-22 02:56:19 +00001244 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1245 ((x-x_offset) >= (ssize_t) composite_image->columns))
1246 {
1247 Quantum
1248 source[MaxPixelChannels];
1249
1250 /*
1251 Virtual composite:
1252 Sc: source color.
1253 Dc: destination color.
1254 */
1255 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1256 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001257 if (GetPixelMask(image,q) != 0)
1258 {
1259 q+=GetPixelChannels(image);
1260 continue;
1261 }
cristye4a40472011-12-22 02:56:19 +00001262 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1263 {
1264 MagickRealType
1265 pixel;
1266
1267 PixelChannel
1268 channel;
1269
1270 PixelTrait
1271 composite_traits,
1272 traits;
1273
1274 channel=GetPixelChannelMapChannel(image,i);
1275 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +00001276 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001277 if ((traits == UndefinedPixelTrait) ||
1278 (composite_traits == UndefinedPixelTrait))
1279 continue;
1280 switch (compose)
1281 {
cristyc8d63672012-01-11 13:03:13 +00001282 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001283 case ChangeMaskCompositeOp:
1284 case CopyAlphaCompositeOp:
1285 case DstAtopCompositeOp:
1286 case DstInCompositeOp:
1287 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001288 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001289 case OutCompositeOp:
1290 case SrcInCompositeOp:
1291 case SrcOutCompositeOp:
1292 {
1293 pixel=(MagickRealType) q[i];
1294 if (channel == AlphaPixelChannel)
1295 pixel=(MagickRealType) TransparentAlpha;
1296 break;
1297 }
1298 case ClearCompositeOp:
1299 case CopyCompositeOp:
1300 case ReplaceCompositeOp:
1301 case SrcCompositeOp:
1302 {
1303 if (channel == AlphaPixelChannel)
1304 {
1305 pixel=(MagickRealType) TransparentAlpha;
1306 break;
1307 }
1308 pixel=0.0;
1309 break;
1310 }
cristy99abff32011-12-24 20:45:16 +00001311 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001312 case DissolveCompositeOp:
1313 {
1314 if (channel == AlphaPixelChannel)
1315 {
1316 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1317 source);
1318 break;
1319 }
1320 pixel=(MagickRealType) source[channel];
1321 break;
1322 }
1323 default:
1324 {
1325 pixel=(MagickRealType) source[channel];
1326 break;
1327 }
1328 }
1329 q[i]=ClampToQuantum(pixel);
1330 }
1331 q+=GetPixelChannels(image);
1332 continue;
1333 }
1334 /*
1335 Authentic composite:
1336 Sa: normalized source alpha.
1337 Da: normalized destination alpha.
1338 */
1339 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1340 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001341 switch (compose)
1342 {
cristye4a40472011-12-22 02:56:19 +00001343 case BumpmapCompositeOp:
1344 {
1345 alpha=GetPixelIntensity(composite_image,p)*Sa;
1346 break;
1347 }
cristycdc168f2011-12-21 15:24:39 +00001348 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001349 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001350 case DifferenceCompositeOp:
1351 case DivideDstCompositeOp:
1352 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001353 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001354 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001355 case LinearBurnCompositeOp:
1356 case LinearDodgeCompositeOp:
1357 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001358 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001359 case MinusDstCompositeOp:
1360 case MinusSrcCompositeOp:
1361 case ModulusAddCompositeOp:
1362 case ModulusSubtractCompositeOp:
1363 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001364 case OverlayCompositeOp:
1365 case PegtopLightCompositeOp:
1366 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001367 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001368 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001369 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001370 {
1371 alpha=RoundToUnity(Sa+Da-Sa*Da);
1372 break;
1373 }
1374 case DarkenCompositeOp:
1375 case DstAtopCompositeOp:
1376 case DstInCompositeOp:
1377 case InCompositeOp:
1378 case LightenCompositeOp:
1379 case SrcInCompositeOp:
1380 {
1381 alpha=Sa*Da;
1382 break;
1383 }
1384 case DissolveCompositeOp:
1385 {
1386 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1387 Sa+destination_dissolve*Da;
1388 break;
1389 }
1390 case DstOverCompositeOp:
1391 {
1392 alpha=Da*(-Sa)+Da+Sa;
1393 break;
1394 }
1395 case DstOutCompositeOp:
1396 {
1397 alpha=Da*(1.0-Sa);
1398 break;
1399 }
1400 case OutCompositeOp:
1401 case SrcOutCompositeOp:
1402 {
1403 alpha=Sa*(1.0-Da);
1404 break;
1405 }
1406 case OverCompositeOp:
1407 case SrcOverCompositeOp:
1408 {
1409 alpha=Sa*(-Da)+Sa+Da;
1410 break;
1411 }
cristy99abff32011-12-24 20:45:16 +00001412 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001413 case PlusCompositeOp:
1414 {
1415 alpha=RoundToUnity(Sa+Da);
1416 break;
1417 }
cristy4c08aed2011-07-01 19:47:50 +00001418 case XorCompositeOp:
1419 {
cristye4a40472011-12-22 02:56:19 +00001420 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001421 break;
1422 }
1423 default:
cristye4a40472011-12-22 02:56:19 +00001424 {
1425 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001426 break;
cristye4a40472011-12-22 02:56:19 +00001427 }
cristy4c08aed2011-07-01 19:47:50 +00001428 }
cristy10a6c612012-01-29 21:41:05 +00001429 if (GetPixelMask(image,p) != 0)
1430 {
1431 p+=GetPixelChannels(composite_image);
1432 q+=GetPixelChannels(image);
1433 continue;
1434 }
cristye4a40472011-12-22 02:56:19 +00001435 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1436 {
cristy564a5692012-01-20 23:56:26 +00001437 double
1438 sans;
1439
cristye10859a2011-12-18 22:28:59 +00001440 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001441 pixel;
cristye10859a2011-12-18 22:28:59 +00001442
cristye4a40472011-12-22 02:56:19 +00001443 PixelChannel
1444 channel;
cristye10859a2011-12-18 22:28:59 +00001445
cristye4a40472011-12-22 02:56:19 +00001446 PixelTrait
1447 composite_traits,
1448 traits;
1449
1450 channel=GetPixelChannelMapChannel(image,i);
1451 traits=GetPixelChannelMapTraits(image,channel);
1452 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001453 if (traits == UndefinedPixelTrait)
1454 continue;
cristya7b07912012-01-11 20:01:32 +00001455 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001456 (composite_traits == UndefinedPixelTrait))
1457 continue;
1458 /*
1459 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001460 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001461 */
1462 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
cristye4a40472011-12-22 02:56:19 +00001463 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001464 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001465 {
cristye4a40472011-12-22 02:56:19 +00001466 if (channel != AlphaPixelChannel)
1467 {
1468 /*
1469 Copy channel.
1470 */
1471 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001472 continue;
cristye10859a2011-12-18 22:28:59 +00001473 }
cristye4a40472011-12-22 02:56:19 +00001474 /*
1475 Set alpha channel.
1476 */
cristye10859a2011-12-18 22:28:59 +00001477 switch (compose)
1478 {
cristyc8d63672012-01-11 13:03:13 +00001479 case AlphaCompositeOp:
1480 {
cristya7b07912012-01-11 20:01:32 +00001481 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001482 break;
1483 }
cristye4a40472011-12-22 02:56:19 +00001484 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001485 case CopyBlackCompositeOp:
1486 case CopyBlueCompositeOp:
1487 case CopyCyanCompositeOp:
1488 case CopyGreenCompositeOp:
1489 case CopyMagentaCompositeOp:
1490 case CopyRedCompositeOp:
1491 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001492 case SrcAtopCompositeOp:
1493 case DstCompositeOp:
1494 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001495 {
cristye4a40472011-12-22 02:56:19 +00001496 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001497 break;
1498 }
cristye10859a2011-12-18 22:28:59 +00001499 case ChangeMaskCompositeOp:
1500 {
cristye4a40472011-12-22 02:56:19 +00001501 MagickBooleanType
1502 equivalent;
1503
cristy99abff32011-12-24 20:45:16 +00001504 if (Da > ((MagickRealType) QuantumRange/2.0))
1505 {
1506 pixel=(MagickRealType) TransparentAlpha;
1507 break;
1508 }
cristye4a40472011-12-22 02:56:19 +00001509 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001510 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001511 {
1512 pixel=(MagickRealType) TransparentAlpha;
1513 break;
1514 }
1515 pixel=(MagickRealType) OpaqueAlpha;
1516 break;
1517 }
cristy99abff32011-12-24 20:45:16 +00001518 case ClearCompositeOp:
1519 {
1520 pixel=(MagickRealType) TransparentAlpha;
1521 break;
1522 }
1523 case ColorizeCompositeOp:
1524 case HueCompositeOp:
1525 case LuminizeCompositeOp:
1526 case SaturateCompositeOp:
1527 {
1528 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1529 {
1530 pixel=QuantumRange*Da;
1531 break;
1532 }
1533 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1534 {
1535 pixel=QuantumRange*Sa;
1536 break;
1537 }
1538 if (Sa < Da)
1539 {
1540 pixel=QuantumRange*Da;
1541 break;
1542 }
1543 pixel=QuantumRange*Sa;
1544 break;
1545 }
cristye4a40472011-12-22 02:56:19 +00001546 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001547 case CopyAlphaCompositeOp:
1548 case DisplaceCompositeOp:
1549 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001550 case DstAtopCompositeOp:
1551 case ReplaceCompositeOp:
1552 case SrcCompositeOp:
1553 {
1554 pixel=QuantumRange*Sa;
1555 break;
1556 }
1557 case DarkenIntensityCompositeOp:
1558 {
cristy99abff32011-12-24 20:45:16 +00001559 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1560 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001561 break;
1562 }
cristy98621462011-12-31 22:31:11 +00001563 case IntensityCompositeOp:
1564 {
cristy20d5f622012-01-11 13:04:26 +00001565 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001566 break;
1567 }
cristye4a40472011-12-22 02:56:19 +00001568 case LightenIntensityCompositeOp:
1569 {
1570 pixel=Sa*GetPixelIntensity(composite_image,p) >
1571 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001572 break;
1573 }
cristy99abff32011-12-24 20:45:16 +00001574 case ModulateCompositeOp:
1575 {
1576 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1577 {
1578 pixel=QuantumRange*Da;
1579 break;
1580 }
1581 pixel=QuantumRange*Da;
1582 break;
1583 }
cristye10859a2011-12-18 22:28:59 +00001584 default:
1585 {
cristye4a40472011-12-22 02:56:19 +00001586 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001587 break;
1588 }
1589 }
cristye4a40472011-12-22 02:56:19 +00001590 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001591 continue;
1592 }
1593 /*
cristy99abff32011-12-24 20:45:16 +00001594 Porter-Duff compositions:
1595 Sca: source normalized color multiplied by alpha.
1596 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001597 */
cristy99abff32011-12-24 20:45:16 +00001598 Sca=QuantumScale*Sa*Sc;
1599 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001600 switch (compose)
1601 {
cristye10859a2011-12-18 22:28:59 +00001602 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001603 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001604 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001605 {
cristy99abff32011-12-24 20:45:16 +00001606 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001607 break;
1608 }
1609 default:
1610 break;
1611 }
cristye4a40472011-12-22 02:56:19 +00001612 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
cristyd197cbb2012-01-13 02:14:12 +00001613 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001614 switch (compose)
1615 {
cristya7b07912012-01-11 20:01:32 +00001616 case AlphaCompositeOp:
1617 {
1618 pixel=QuantumRange*Sa;
1619 break;
1620 }
cristye4a40472011-12-22 02:56:19 +00001621 case AtopCompositeOp:
1622 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001623 {
cristye4a40472011-12-22 02:56:19 +00001624 pixel=Sc*Sa+Dc*(1.0-Sa);
1625 break;
cristye10859a2011-12-18 22:28:59 +00001626 }
cristye4a40472011-12-22 02:56:19 +00001627 case BlendCompositeOp:
1628 {
1629 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1630 break;
1631 }
1632 case BlurCompositeOp:
1633 case DisplaceCompositeOp:
1634 case DistortCompositeOp:
1635 case CopyCompositeOp:
1636 case ReplaceCompositeOp:
1637 case SrcCompositeOp:
1638 {
1639 pixel=Sc;
1640 break;
1641 }
1642 case BumpmapCompositeOp:
1643 {
cristy99abff32011-12-24 20:45:16 +00001644 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001645 {
1646 pixel=Dc;
1647 break;
1648 }
1649 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1650 break;
1651 }
cristy99abff32011-12-24 20:45:16 +00001652 case ChangeMaskCompositeOp:
1653 {
1654 pixel=Dc;
1655 break;
1656 }
1657 case ClearCompositeOp:
1658 {
1659 pixel=0.0;
1660 break;
1661 }
cristye4a40472011-12-22 02:56:19 +00001662 case ColorBurnCompositeOp:
1663 {
1664 /*
1665 Refer to the March 2009 SVG specification.
1666 */
1667 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1668 {
cristy99abff32011-12-24 20:45:16 +00001669 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001670 break;
1671 }
1672 if (Sca < MagickEpsilon)
1673 {
cristy99abff32011-12-24 20:45:16 +00001674 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001675 break;
1676 }
cristy99abff32011-12-24 20:45:16 +00001677 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1678 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001679 break;
1680 }
1681 case ColorDodgeCompositeOp:
1682 {
1683 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1684 {
cristy99abff32011-12-24 20:45:16 +00001685 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001686 break;
1687 }
1688 if (fabs(Sca-Sa) < MagickEpsilon)
1689 {
cristy99abff32011-12-24 20:45:16 +00001690 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001691 break;
1692 }
cristy99abff32011-12-24 20:45:16 +00001693 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001694 (1.0-Sa));
1695 break;
1696 }
1697 case ColorizeCompositeOp:
1698 {
cristy99abff32011-12-24 20:45:16 +00001699 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001700 {
1701 pixel=Dc;
1702 break;
1703 }
cristy99abff32011-12-24 20:45:16 +00001704 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001705 {
1706 pixel=Sc;
1707 break;
1708 }
1709 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
cristy99abff32011-12-24 20:45:16 +00001710 GetPixelBlue(image,q),&sans,&sans,&brightness);
cristye4a40472011-12-22 02:56:19 +00001711 CompositeHSB(GetPixelRed(composite_image,p),
1712 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1713 &hue,&saturation,&sans);
1714 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1715 switch (channel)
1716 {
1717 case RedPixelChannel: pixel=red; break;
1718 case GreenPixelChannel: pixel=green; break;
1719 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001720 default: pixel=Dc; break;
1721 }
1722 break;
1723 }
cristye4a40472011-12-22 02:56:19 +00001724 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001725 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001726 {
cristyd197cbb2012-01-13 02:14:12 +00001727 if (channel == AlphaPixelChannel)
1728 pixel=(MagickRealType) GetPixelAlpha(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001729 break;
1730 }
1731 case CopyBlackCompositeOp:
1732 {
cristyd197cbb2012-01-13 02:14:12 +00001733 if (channel == BlackPixelChannel)
1734 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001735 break;
1736 }
1737 case CopyBlueCompositeOp:
1738 case CopyYellowCompositeOp:
1739 {
cristyd197cbb2012-01-13 02:14:12 +00001740 if (channel == BluePixelChannel)
1741 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001742 break;
1743 }
1744 case CopyGreenCompositeOp:
1745 case CopyMagentaCompositeOp:
1746 {
cristyd197cbb2012-01-13 02:14:12 +00001747 if (channel == GreenPixelChannel)
1748 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001749 break;
1750 }
1751 case CopyRedCompositeOp:
1752 case CopyCyanCompositeOp:
1753 {
cristyd197cbb2012-01-13 02:14:12 +00001754 if (channel == RedPixelChannel)
1755 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001756 break;
1757 }
cristy99abff32011-12-24 20:45:16 +00001758 case DarkenCompositeOp:
1759 {
1760 /*
1761 Darken is equivalent to a 'Minimum' method
1762 OR a greyscale version of a binary 'Or'
1763 OR the 'Intersection' of pixel sets.
1764 */
1765 if (Sc < Dc)
1766 {
1767 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1768 break;
1769 }
1770 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1771 break;
1772 }
cristye4a40472011-12-22 02:56:19 +00001773 case DarkenIntensityCompositeOp:
1774 {
cristy99abff32011-12-24 20:45:16 +00001775 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1776 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001777 break;
1778 }
1779 case DifferenceCompositeOp:
1780 {
1781 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1782 break;
1783 }
1784 case DissolveCompositeOp:
1785 {
1786 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1787 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1788 break;
1789 }
1790 case DivideDstCompositeOp:
1791 {
1792 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1793 {
cristy99abff32011-12-24 20:45:16 +00001794 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001795 break;
1796 }
1797 if (fabs(Dca) < MagickEpsilon)
1798 {
cristy99abff32011-12-24 20:45:16 +00001799 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001800 break;
1801 }
cristy99abff32011-12-24 20:45:16 +00001802 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001803 break;
1804 }
1805 case DivideSrcCompositeOp:
1806 {
1807 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1808 {
cristy99abff32011-12-24 20:45:16 +00001809 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001810 break;
1811 }
1812 if (fabs(Sca) < MagickEpsilon)
1813 {
cristy99abff32011-12-24 20:45:16 +00001814 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001815 break;
1816 }
cristy99abff32011-12-24 20:45:16 +00001817 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001818 break;
1819 }
1820 case DstAtopCompositeOp:
1821 {
1822 pixel=Dc*Da+Sc*(1.0-Da);
1823 break;
1824 }
1825 case DstCompositeOp:
1826 case NoCompositeOp:
1827 {
1828 pixel=Dc;
1829 break;
1830 }
1831 case DstInCompositeOp:
1832 {
1833 pixel=gamma*(Sa*Dc*Sa);
1834 break;
1835 }
1836 case DstOutCompositeOp:
1837 {
1838 pixel=gamma*(Da*Dc*(1.0-Sa));
1839 break;
1840 }
1841 case DstOverCompositeOp:
1842 {
1843 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1844 break;
1845 }
1846 case ExclusionCompositeOp:
1847 {
cristy99abff32011-12-24 20:45:16 +00001848 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1849 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001850 break;
1851 }
1852 case HardLightCompositeOp:
1853 {
1854 if ((2.0*Sca) < Sa)
1855 {
cristy99abff32011-12-24 20:45:16 +00001856 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001857 (1.0-Sa));
1858 break;
1859 }
cristy99abff32011-12-24 20:45:16 +00001860 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001861 Dca*(1.0-Sa));
1862 break;
1863 }
1864 case HueCompositeOp:
1865 {
cristy99abff32011-12-24 20:45:16 +00001866 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001867 {
1868 pixel=Dc;
1869 break;
1870 }
cristy99abff32011-12-24 20:45:16 +00001871 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001872 {
1873 pixel=Sc;
1874 break;
1875 }
1876 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1877 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1878 CompositeHSB(GetPixelRed(composite_image,p),
1879 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1880 &hue,&sans,&sans);
1881 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1882 switch (channel)
1883 {
1884 case RedPixelChannel: pixel=red; break;
1885 case GreenPixelChannel: pixel=green; break;
1886 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001887 default: pixel=Dc; break;
1888 }
1889 break;
1890 }
1891 case InCompositeOp:
1892 case SrcInCompositeOp:
1893 {
1894 pixel=gamma*(Da*Sc*Da);
1895 break;
1896 }
cristy99abff32011-12-24 20:45:16 +00001897 case LinearBurnCompositeOp:
1898 {
1899 /*
1900 LinearBurn: as defined by Abode Photoshop, according to
1901 http://www.simplefilter.de/en/basics/mixmods.html is:
1902
1903 f(Sc,Dc) = Sc + Dc - 1
1904 */
1905 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1906 break;
1907 }
1908 case LinearDodgeCompositeOp:
1909 {
1910 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1911 break;
1912 }
1913 case LinearLightCompositeOp:
1914 {
1915 /*
1916 LinearLight: as defined by Abode Photoshop, according to
1917 http://www.simplefilter.de/en/basics/mixmods.html is:
1918
1919 f(Sc,Dc) = Dc + 2*Sc - 1
1920 */
1921 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1922 break;
1923 }
1924 case LightenCompositeOp:
1925 {
1926 if (Sc > Dc)
1927 {
1928 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1929 break;
1930 }
1931 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1932 break;
1933 }
cristye4a40472011-12-22 02:56:19 +00001934 case LightenIntensityCompositeOp:
1935 {
1936 /*
1937 Lighten is equivalent to a 'Maximum' method
1938 OR a greyscale version of a binary 'And'
1939 OR the 'Union' of pixel sets.
1940 */
1941 pixel=Sa*GetPixelIntensity(composite_image,p) >
1942 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1943 break;
1944 }
cristye4a40472011-12-22 02:56:19 +00001945 case LuminizeCompositeOp:
1946 {
cristy99abff32011-12-24 20:45:16 +00001947 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001948 {
1949 pixel=Dc;
1950 break;
1951 }
cristy99abff32011-12-24 20:45:16 +00001952 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001953 {
1954 pixel=Sc;
1955 break;
1956 }
1957 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1958 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1959 CompositeHSB(GetPixelRed(composite_image,p),
1960 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1961 &sans,&sans,&brightness);
1962 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1963 switch (channel)
1964 {
1965 case RedPixelChannel: pixel=red; break;
1966 case GreenPixelChannel: pixel=green; break;
1967 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001968 default: pixel=Dc; break;
1969 }
1970 break;
1971 }
1972 case MathematicsCompositeOp:
1973 {
1974 /*
1975 'Mathematics' a free form user control mathematical composition
1976 is defined as...
1977
1978 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
1979
1980 Where the arguments A,B,C,D are (currently) passed to composite
1981 as a command separated 'geometry' string in "compose:args" image
1982 artifact.
1983
1984 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
1985
1986 Applying the SVG transparency formula (see above), we get...
1987
1988 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
1989
1990 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
1991 Dca*(1.0-Sa)
1992 */
1993 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
1994 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
1995 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
1996 break;
1997 }
1998 case MinusDstCompositeOp:
1999 {
2000 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2001 break;
2002 }
2003 case MinusSrcCompositeOp:
2004 {
2005 /*
2006 Minus source from destination.
2007
2008 f(Sc,Dc) = Sc - Dc
2009 */
cristy99abff32011-12-24 20:45:16 +00002010 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002011 break;
2012 }
2013 case ModulateCompositeOp:
2014 {
2015 ssize_t
2016 offset;
2017
cristy99abff32011-12-24 20:45:16 +00002018 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002019 {
2020 pixel=Dc;
2021 break;
2022 }
2023 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2024 if (offset == 0)
2025 {
2026 pixel=Dc;
2027 break;
2028 }
2029 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2030 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2031 brightness+=(0.01*percent_brightness*offset)/midpoint;
2032 saturation*=0.01*percent_saturation;
2033 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2034 switch (channel)
2035 {
2036 case RedPixelChannel: pixel=red; break;
2037 case GreenPixelChannel: pixel=green; break;
2038 case BluePixelChannel: pixel=blue; break;
2039 default: pixel=Dc; break;
2040 }
2041 break;
2042 }
2043 case ModulusAddCompositeOp:
2044 {
2045 pixel=Sc+Dc;
2046 if (pixel > QuantumRange)
2047 pixel-=(QuantumRange+1.0);
2048 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2049 break;
2050 }
2051 case ModulusSubtractCompositeOp:
2052 {
cristy99abff32011-12-24 20:45:16 +00002053 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002054 if (pixel < 0.0)
2055 pixel+=(QuantumRange+1.0);
2056 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2057 break;
2058 }
2059 case MultiplyCompositeOp:
2060 {
cristy99abff32011-12-24 20:45:16 +00002061 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002062 break;
2063 }
2064 case OutCompositeOp:
2065 case SrcOutCompositeOp:
2066 {
2067 pixel=gamma*(Sa*Sc*(1.0-Da));
2068 break;
2069 }
2070 case OverCompositeOp:
2071 case SrcOverCompositeOp:
2072 {
cristy99abff32011-12-24 20:45:16 +00002073 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002074 break;
2075 }
2076 case OverlayCompositeOp:
2077 {
2078 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002079 {
2080 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2081 (1.0-Da));
2082 break;
2083 }
2084 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2085 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002086 break;
2087 }
2088 case PegtopLightCompositeOp:
2089 {
2090 /*
2091 PegTop: A Soft-Light alternative: A continuous version of the
2092 Softlight function, producing very similar results.
2093
2094 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2095
2096 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2097 */
2098 if (fabs(Da) < MagickEpsilon)
2099 {
cristy99abff32011-12-24 20:45:16 +00002100 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002101 break;
2102 }
cristy99abff32011-12-24 20:45:16 +00002103 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2104 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002105 break;
2106 }
2107 case PinLightCompositeOp:
2108 {
2109 /*
2110 PinLight: A Photoshop 7 composition method
2111 http://www.simplefilter.de/en/basics/mixmods.html
2112
2113 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2114 */
2115 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2116 {
cristy99abff32011-12-24 20:45:16 +00002117 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002118 break;
2119 }
2120 if ((Dca*Sa) > (2.0*Sca*Da))
2121 {
cristy99abff32011-12-24 20:45:16 +00002122 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002123 break;
2124 }
cristy99abff32011-12-24 20:45:16 +00002125 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002126 break;
2127 }
2128 case PlusCompositeOp:
2129 {
cristy99abff32011-12-24 20:45:16 +00002130 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002131 break;
2132 }
2133 case SaturateCompositeOp:
2134 {
cristy99abff32011-12-24 20:45:16 +00002135 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002136 {
2137 pixel=Dc;
2138 break;
2139 }
cristy99abff32011-12-24 20:45:16 +00002140 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002141 {
2142 pixel=Sc;
2143 break;
2144 }
2145 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2146 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2147 CompositeHSB(GetPixelRed(composite_image,p),
2148 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
2149 &sans,&saturation,&sans);
2150 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2151 switch (channel)
2152 {
2153 case RedPixelChannel: pixel=red; break;
2154 case GreenPixelChannel: pixel=green; break;
2155 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002156 default: pixel=Dc; break;
2157 }
2158 break;
2159 }
2160 case ScreenCompositeOp:
2161 {
2162 /*
2163 Screen: a negated multiply:
2164
2165 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2166 */
cristy99abff32011-12-24 20:45:16 +00002167 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002168 break;
2169 }
2170 case SoftLightCompositeOp:
2171 {
2172 /*
2173 Refer to the March 2009 SVG specification.
2174 */
2175 if ((2.0*Sca) < Sa)
2176 {
cristy99abff32011-12-24 20:45:16 +00002177 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2178 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002179 break;
2180 }
2181 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2182 {
cristy99abff32011-12-24 20:45:16 +00002183 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2184 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2185 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002186 break;
2187 }
cristy99abff32011-12-24 20:45:16 +00002188 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2189 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002190 break;
2191 }
2192 case ThresholdCompositeOp:
2193 {
2194 MagickRealType
2195 delta;
2196
2197 delta=Sc-Dc;
2198 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2199 {
2200 pixel=gamma*Dc;
2201 break;
2202 }
2203 pixel=gamma*(Dc+delta*amount);
2204 break;
2205 }
2206 case VividLightCompositeOp:
2207 {
2208 /*
2209 VividLight: A Photoshop 7 composition method. See
2210 http://www.simplefilter.de/en/basics/mixmods.html.
2211
2212 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2213 */
2214 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2215 {
cristy99abff32011-12-24 20:45:16 +00002216 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002217 break;
2218 }
2219 if ((2.0*Sca) <= Sa)
2220 {
cristy99abff32011-12-24 20:45:16 +00002221 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2222 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002223 break;
2224 }
cristy99abff32011-12-24 20:45:16 +00002225 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2226 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002227 break;
2228 }
2229 case XorCompositeOp:
2230 {
cristy99abff32011-12-24 20:45:16 +00002231 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002232 break;
2233 }
2234 default:
2235 {
2236 pixel=Sc;
2237 break;
2238 }
2239 }
2240 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002241 }
cristyed231572011-07-14 02:18:59 +00002242 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002243 channels=GetPixelChannels(composite_image);
2244 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002245 p=pixels;
cristyed231572011-07-14 02:18:59 +00002246 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002247 }
2248 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2249 status=MagickFalse;
2250 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2251 {
2252 MagickBooleanType
2253 proceed;
2254
2255#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002256 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002257#endif
2258 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2259 image->rows);
2260 if (proceed == MagickFalse)
2261 status=MagickFalse;
2262 }
2263 }
2264 composite_view=DestroyCacheView(composite_view);
2265 image_view=DestroyCacheView(image_view);
2266 if (destination_image != (Image * ) NULL)
2267 destination_image=DestroyImage(destination_image);
2268 return(status);
2269}
2270
2271/*
2272%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2273% %
2274% %
2275% %
2276% T e x t u r e I m a g e %
2277% %
2278% %
2279% %
2280%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2281%
2282% TextureImage() repeatedly tiles the texture image across and down the image
2283% canvas.
2284%
2285% The format of the TextureImage method is:
2286%
cristy30d8c942012-02-07 13:44:59 +00002287% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002288% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002289%
2290% A description of each parameter follows:
2291%
2292% o image: the image.
2293%
cristye6178502011-12-23 17:02:29 +00002294% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002295%
2296*/
cristy30d8c942012-02-07 13:44:59 +00002297MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2298 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002299{
2300#define TextureImageTag "Texture/Image"
2301
2302 CacheView
2303 *image_view,
2304 *texture_view;
2305
cristy30d8c942012-02-07 13:44:59 +00002306 Image
2307 *texture_image;
2308
cristy4c08aed2011-07-01 19:47:50 +00002309 MagickBooleanType
2310 status;
2311
2312 ssize_t
2313 y;
2314
2315 assert(image != (Image *) NULL);
2316 if (image->debug != MagickFalse)
2317 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2318 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002319 if (texture == (const Image *) NULL)
2320 return(MagickFalse);
2321 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2322 return(MagickFalse);
2323 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002324 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002325 return(MagickFalse);
cristy387430f2012-02-07 13:09:46 +00002326 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2327 exception);
cristy4c08aed2011-07-01 19:47:50 +00002328 status=MagickTrue;
2329 if ((image->compose != CopyCompositeOp) &&
2330 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002331 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002332 {
2333 /*
2334 Tile texture onto the image background.
2335 */
2336#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy19593872012-01-22 02:00:33 +00002337 #pragma omp parallel for schedule(static) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002338#endif
cristye6178502011-12-23 17:02:29 +00002339 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002340 {
2341 register ssize_t
2342 x;
2343
2344 if (status == MagickFalse)
2345 continue;
cristye6178502011-12-23 17:02:29 +00002346 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002347 {
2348 MagickBooleanType
2349 thread_status;
2350
cristye6178502011-12-23 17:02:29 +00002351 thread_status=CompositeImage(image,image->compose,texture_image,x+
2352 texture_image->tile_offset.x,y+texture_image->tile_offset.y,
2353 exception);
cristy4c08aed2011-07-01 19:47:50 +00002354 if (thread_status == MagickFalse)
2355 {
2356 status=thread_status;
2357 break;
2358 }
2359 }
2360 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2361 {
2362 MagickBooleanType
2363 proceed;
2364
2365#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002366 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002367#endif
2368 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2369 y,image->rows);
2370 if (proceed == MagickFalse)
2371 status=MagickFalse;
2372 }
2373 }
2374 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2375 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002376 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002377 return(status);
2378 }
2379 /*
2380 Tile texture onto the image background (optimized).
2381 */
2382 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00002383 image_view=AcquireCacheView(image);
cristye6178502011-12-23 17:02:29 +00002384 texture_view=AcquireCacheView(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002385#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy19593872012-01-22 02:00:33 +00002386 #pragma omp parallel for schedule(static) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002387#endif
2388 for (y=0; y < (ssize_t) image->rows; y++)
2389 {
2390 MagickBooleanType
2391 sync;
2392
2393 register const Quantum
2394 *p,
2395 *pixels;
2396
2397 register ssize_t
2398 x;
2399
2400 register Quantum
2401 *q;
2402
2403 size_t
2404 width;
2405
2406 if (status == MagickFalse)
2407 continue;
cristye6178502011-12-23 17:02:29 +00002408 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2409 (y+texture_image->tile_offset.y) % texture_image->rows,
2410 texture_image->columns,1,exception);
2411 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002412 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2413 {
2414 status=MagickFalse;
2415 continue;
2416 }
cristye6178502011-12-23 17:02:29 +00002417 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002418 {
2419 register ssize_t
cristye6178502011-12-23 17:02:29 +00002420 j;
cristy4c08aed2011-07-01 19:47:50 +00002421
2422 p=pixels;
cristye6178502011-12-23 17:02:29 +00002423 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002424 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2425 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002426 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002427 {
cristye6178502011-12-23 17:02:29 +00002428 register ssize_t
2429 i;
2430
cristy10a6c612012-01-29 21:41:05 +00002431 if (GetPixelMask(image,p) != 0)
2432 {
2433 p+=GetPixelChannels(texture_image);
2434 q+=GetPixelChannels(image);
2435 continue;
2436 }
cristye6178502011-12-23 17:02:29 +00002437 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2438 {
2439 PixelChannel
2440 channel;
2441
2442 PixelTrait
2443 texture_traits,
2444 traits;
2445
2446 channel=GetPixelChannelMapChannel(texture_image,i);
2447 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2448 traits=GetPixelChannelMapTraits(image,channel);
2449 if ((traits == UndefinedPixelTrait) ||
2450 (texture_traits == UndefinedPixelTrait))
2451 continue;
2452 SetPixelChannel(image,channel,p[i],q);
2453 }
2454 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002455 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002456 }
2457 }
2458 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2459 if (sync == MagickFalse)
2460 status=MagickFalse;
2461 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2462 {
2463 MagickBooleanType
2464 proceed;
2465
2466#if defined(MAGICKCORE_OPENMP_SUPPORT)
2467 #pragma omp critical (MagickCore_TextureImage)
2468#endif
2469 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2470 image->rows);
2471 if (proceed == MagickFalse)
2472 status=MagickFalse;
2473 }
2474 }
2475 texture_view=DestroyCacheView(texture_view);
2476 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002477 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002478 return(status);
2479}