blob: c9d5bcc5da8bebc2ac8f9ef14dfb920d902efde9 [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.
anthonyf46d4262012-03-26 03:30:34 +0000808 FUTURE: this is currently broken, especially for small sigma blurs
809 This needs to be fixed to use a non-user filter setup that provides
810 far more control than currently available.
cristy4c08aed2011-07-01 19:47:50 +0000811 */
cristy8a11cb12011-10-19 23:53:34 +0000812 resample_filter=AcquireResampleFilter(image,exception);
anthonyf46d4262012-03-26 03:30:34 +0000813 SetResampleFilter(resample_filter,CubicFilter); /* was blur*2 */
cristy4c08aed2011-07-01 19:47:50 +0000814 destination_view=AcquireCacheView(destination_image);
815 composite_view=AcquireCacheView(composite_image);
816 for (y=0; y < (ssize_t) composite_image->rows; y++)
817 {
818 MagickBooleanType
819 sync;
820
821 register const Quantum
822 *restrict p;
823
824 register Quantum
825 *restrict q;
826
827 register ssize_t
828 x;
829
830 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
831 continue;
832 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
833 1,exception);
834 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000835 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000836 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
837 break;
838 for (x=0; x < (ssize_t) composite_image->columns; x++)
839 {
840 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
841 {
cristyed231572011-07-14 02:18:59 +0000842 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000843 continue;
844 }
845 if (fabs(angle_range) > MagickEpsilon)
846 {
847 MagickRealType
848 angle;
849
850 angle=angle_start+angle_range*QuantumScale*
851 GetPixelBlue(composite_image,p);
852 blur.x1=width*cos(angle);
853 blur.x2=width*sin(angle);
854 blur.y1=(-height*sin(angle));
855 blur.y2=height*cos(angle);
856 }
857 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
858 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
859 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
860 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
861 GetPixelGreen(composite_image,p));
862 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
863 (double) y_offset+y,&pixel);
cristy803640d2011-11-17 02:11:32 +0000864 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000865 p+=GetPixelChannels(composite_image);
866 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000867 }
868 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
869 if (sync == MagickFalse)
870 break;
871 }
872 resample_filter=DestroyResampleFilter(resample_filter);
873 composite_view=DestroyCacheView(composite_view);
874 destination_view=DestroyCacheView(destination_view);
875 composite_image=destination_image;
876 break;
877 }
878 case DisplaceCompositeOp:
879 case DistortCompositeOp:
880 {
881 CacheView
882 *composite_view,
883 *destination_view,
884 *image_view;
885
886 PixelInfo
887 pixel;
888
889 MagickRealType
890 horizontal_scale,
891 vertical_scale;
892
893 PointInfo
894 center,
895 offset;
896
897 /*
898 Displace/Distort based on overlay gradient map:
899 X = red_channel; Y = green_channel;
900 compose:args = x_scale[,y_scale[,center.x,center.y]]
901 */
902 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000903 exception);
cristy4c08aed2011-07-01 19:47:50 +0000904 if (destination_image == (Image *) NULL)
905 return(MagickFalse);
906 SetGeometryInfo(&geometry_info);
907 flags=NoValue;
908 value=GetImageArtifact(composite_image,"compose:args");
909 if (value != (char *) NULL)
910 flags=ParseGeometry(value,&geometry_info);
911 if ((flags & (WidthValue|HeightValue)) == 0 )
912 {
913 if ((flags & AspectValue) == 0)
914 {
915 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
916 2.0;
917 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
918 }
919 else
920 {
921 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
922 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
923 }
924 }
925 else
926 {
927 horizontal_scale=geometry_info.rho;
928 vertical_scale=geometry_info.sigma;
929 if ((flags & PercentValue) != 0)
930 {
931 if ((flags & AspectValue) == 0)
932 {
933 horizontal_scale*=(composite_image->columns-1.0)/200.0;
934 vertical_scale*=(composite_image->rows-1.0)/200.0;
935 }
936 else
937 {
938 horizontal_scale*=(image->columns-1.0)/200.0;
939 vertical_scale*=(image->rows-1.0)/200.0;
940 }
941 }
942 if ((flags & HeightValue) == 0)
943 vertical_scale=horizontal_scale;
944 }
945 /*
946 Determine fixed center point for absolute distortion map
947 Absolute distort ==
948 Displace offset relative to a fixed absolute point
949 Select that point according to +X+Y user inputs.
950 default = center of overlay image
951 arg flag '!' = locations/percentage relative to background image
952 */
953 center.x=(MagickRealType) x_offset;
954 center.y=(MagickRealType) y_offset;
955 if (compose == DistortCompositeOp)
956 {
957 if ((flags & XValue) == 0)
958 if ((flags & AspectValue) == 0)
959 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
960 2.0;
961 else
962 center.x=((MagickRealType) image->columns-1)/2.0;
963 else
964 if ((flags & AspectValue) == 0)
965 center.x=(MagickRealType) x_offset+geometry_info.xi;
966 else
967 center.x=geometry_info.xi;
968 if ((flags & YValue) == 0)
969 if ((flags & AspectValue) == 0)
970 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
971 else
972 center.y=((MagickRealType) image->rows-1)/2.0;
973 else
974 if ((flags & AspectValue) == 0)
975 center.y=(MagickRealType) y_offset+geometry_info.psi;
976 else
977 center.y=geometry_info.psi;
978 }
979 /*
980 Shift the pixel offset point as defined by the provided,
981 displacement/distortion map. -- Like a lens...
982 */
cristye10859a2011-12-18 22:28:59 +0000983 GetPixelInfo(image,&pixel);
cristy4c08aed2011-07-01 19:47:50 +0000984 image_view=AcquireCacheView(image);
985 destination_view=AcquireCacheView(destination_image);
986 composite_view=AcquireCacheView(composite_image);
987 for (y=0; y < (ssize_t) composite_image->rows; y++)
988 {
989 MagickBooleanType
990 sync;
991
992 register const Quantum
993 *restrict p;
994
995 register Quantum
996 *restrict q;
997
998 register ssize_t
999 x;
1000
1001 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1002 continue;
1003 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1004 1,exception);
1005 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001006 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001007 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1008 break;
1009 for (x=0; x < (ssize_t) composite_image->columns; x++)
1010 {
1011 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1012 {
cristyed231572011-07-14 02:18:59 +00001013 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001014 continue;
1015 }
1016 /*
1017 Displace the offset.
1018 */
1019 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1020 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1021 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1022 x : 0);
1023 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1024 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1025 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1026 y : 0);
1027 (void) InterpolatePixelInfo(image,image_view,
1028 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1029 &pixel,exception);
1030 /*
1031 Mask with the 'invalid pixel mask' in alpha channel.
1032 */
1033 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001034 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001035 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001036 p+=GetPixelChannels(composite_image);
1037 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001038 }
1039 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1040 if (sync == MagickFalse)
1041 break;
1042 }
1043 destination_view=DestroyCacheView(destination_view);
1044 composite_view=DestroyCacheView(composite_view);
1045 image_view=DestroyCacheView(image_view);
1046 composite_image=destination_image;
1047 break;
1048 }
1049 case DissolveCompositeOp:
1050 {
1051 /*
1052 Geometry arguments to dissolve factors.
1053 */
1054 value=GetImageArtifact(composite_image,"compose:args");
1055 if (value != (char *) NULL)
1056 {
1057 flags=ParseGeometry(value,&geometry_info);
1058 source_dissolve=geometry_info.rho/100.0;
1059 destination_dissolve=1.0;
1060 if ((source_dissolve-MagickEpsilon) < 0.0)
1061 source_dissolve=0.0;
1062 if ((source_dissolve+MagickEpsilon) > 1.0)
1063 {
1064 destination_dissolve=2.0-source_dissolve;
1065 source_dissolve=1.0;
1066 }
1067 if ((flags & SigmaValue) != 0)
1068 destination_dissolve=geometry_info.sigma/100.0;
1069 if ((destination_dissolve-MagickEpsilon) < 0.0)
1070 destination_dissolve=0.0;
1071 modify_outside_overlay=MagickTrue;
1072 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1073 {
1074 destination_dissolve=1.0;
1075 modify_outside_overlay=MagickFalse;
1076 }
1077 }
1078 break;
1079 }
1080 case BlendCompositeOp:
1081 {
1082 value=GetImageArtifact(composite_image,"compose:args");
1083 if (value != (char *) NULL)
1084 {
1085 flags=ParseGeometry(value,&geometry_info);
1086 source_dissolve=geometry_info.rho/100.0;
1087 destination_dissolve=1.0-source_dissolve;
1088 if ((flags & SigmaValue) != 0)
1089 destination_dissolve=geometry_info.sigma/100.0;
1090 modify_outside_overlay=MagickTrue;
1091 if ((destination_dissolve+MagickEpsilon) > 1.0)
1092 modify_outside_overlay=MagickFalse;
1093 }
1094 break;
1095 }
1096 case MathematicsCompositeOp:
1097 {
1098 /*
1099 Just collect the values from "compose:args", setting.
1100 Unused values are set to zero automagically.
1101
1102 Arguments are normally a comma separated list, so this probably should
1103 be changed to some 'general comma list' parser, (with a minimum
1104 number of values)
1105 */
1106 SetGeometryInfo(&geometry_info);
1107 value=GetImageArtifact(composite_image,"compose:args");
1108 if (value != (char *) NULL)
1109 (void) ParseGeometry(value,&geometry_info);
1110 break;
1111 }
1112 case ModulateCompositeOp:
1113 {
1114 /*
1115 Determine the brightness and saturation scale.
1116 */
1117 value=GetImageArtifact(composite_image,"compose:args");
1118 if (value != (char *) NULL)
1119 {
1120 flags=ParseGeometry(value,&geometry_info);
1121 percent_brightness=geometry_info.rho;
1122 if ((flags & SigmaValue) != 0)
1123 percent_saturation=geometry_info.sigma;
1124 }
1125 break;
1126 }
1127 case ThresholdCompositeOp:
1128 {
1129 /*
1130 Determine the amount and threshold.
1131 */
1132 value=GetImageArtifact(composite_image,"compose:args");
1133 if (value != (char *) NULL)
1134 {
1135 flags=ParseGeometry(value,&geometry_info);
1136 amount=geometry_info.rho;
1137 threshold=geometry_info.sigma;
1138 if ((flags & SigmaValue) == 0)
1139 threshold=0.05f;
1140 }
1141 threshold*=QuantumRange;
1142 break;
1143 }
1144 default:
1145 break;
1146 }
1147 value=GetImageArtifact(composite_image,"compose:outside-overlay");
1148 if (value != (const char *) NULL)
1149 modify_outside_overlay=IsMagickTrue(value);
1150 /*
1151 Composite image.
1152 */
1153 status=MagickTrue;
1154 progress=0;
1155 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy4c08aed2011-07-01 19:47:50 +00001156 image_view=AcquireCacheView(image);
1157 composite_view=AcquireCacheView(composite_image);
1158#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00001159 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy4c08aed2011-07-01 19:47:50 +00001160#endif
1161 for (y=0; y < (ssize_t) image->rows; y++)
1162 {
1163 const Quantum
1164 *pixels;
1165
1166 double
cristye4a40472011-12-22 02:56:19 +00001167 blue,
cristy4c08aed2011-07-01 19:47:50 +00001168 brightness,
cristye4a40472011-12-22 02:56:19 +00001169 green,
cristy4c08aed2011-07-01 19:47:50 +00001170 hue,
cristye4a40472011-12-22 02:56:19 +00001171 red,
cristy4c08aed2011-07-01 19:47:50 +00001172 saturation;
1173
cristy4c08aed2011-07-01 19:47:50 +00001174 register const Quantum
1175 *restrict p;
1176
1177 register Quantum
1178 *restrict q;
1179
1180 register ssize_t
1181 x;
1182
1183 if (status == MagickFalse)
1184 continue;
1185 if (modify_outside_overlay == MagickFalse)
1186 {
1187 if (y < y_offset)
1188 continue;
1189 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1190 continue;
1191 }
1192 /*
1193 If pixels is NULL, y is outside overlay region.
1194 */
1195 pixels=(Quantum *) NULL;
1196 p=(Quantum *) NULL;
1197 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1198 {
1199 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1200 composite_image->columns,1,exception);
1201 if (p == (const Quantum *) NULL)
1202 {
1203 status=MagickFalse;
1204 continue;
1205 }
1206 pixels=p;
1207 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001208 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001209 }
1210 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001211 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001212 {
1213 status=MagickFalse;
1214 continue;
1215 }
cristy4c08aed2011-07-01 19:47:50 +00001216 hue=0.0;
1217 saturation=0.0;
1218 brightness=0.0;
1219 for (x=0; x < (ssize_t) image->columns; x++)
1220 {
cristye4a40472011-12-22 02:56:19 +00001221 MagickRealType
1222 alpha,
1223 Da,
1224 Dc,
1225 Dca,
1226 gamma,
1227 Sa,
1228 Sc,
1229 Sca;
1230
1231 register ssize_t
1232 i;
1233
cristy564a5692012-01-20 23:56:26 +00001234 size_t
1235 channels;
1236
cristy4c08aed2011-07-01 19:47:50 +00001237 if (modify_outside_overlay == MagickFalse)
1238 {
1239 if (x < x_offset)
1240 {
cristyed231572011-07-14 02:18:59 +00001241 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001242 continue;
1243 }
1244 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1245 break;
1246 }
cristye4a40472011-12-22 02:56:19 +00001247 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1248 ((x-x_offset) >= (ssize_t) composite_image->columns))
1249 {
1250 Quantum
1251 source[MaxPixelChannels];
1252
1253 /*
1254 Virtual composite:
1255 Sc: source color.
1256 Dc: destination color.
1257 */
1258 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1259 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001260 if (GetPixelMask(image,q) != 0)
1261 {
1262 q+=GetPixelChannels(image);
1263 continue;
1264 }
cristye4a40472011-12-22 02:56:19 +00001265 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1266 {
1267 MagickRealType
1268 pixel;
1269
1270 PixelChannel
1271 channel;
1272
1273 PixelTrait
1274 composite_traits,
1275 traits;
1276
1277 channel=GetPixelChannelMapChannel(image,i);
1278 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +00001279 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001280 if ((traits == UndefinedPixelTrait) ||
1281 (composite_traits == UndefinedPixelTrait))
1282 continue;
1283 switch (compose)
1284 {
cristyc8d63672012-01-11 13:03:13 +00001285 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001286 case ChangeMaskCompositeOp:
1287 case CopyAlphaCompositeOp:
1288 case DstAtopCompositeOp:
1289 case DstInCompositeOp:
1290 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001291 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001292 case OutCompositeOp:
1293 case SrcInCompositeOp:
1294 case SrcOutCompositeOp:
1295 {
1296 pixel=(MagickRealType) q[i];
1297 if (channel == AlphaPixelChannel)
1298 pixel=(MagickRealType) TransparentAlpha;
1299 break;
1300 }
1301 case ClearCompositeOp:
1302 case CopyCompositeOp:
1303 case ReplaceCompositeOp:
1304 case SrcCompositeOp:
1305 {
1306 if (channel == AlphaPixelChannel)
1307 {
1308 pixel=(MagickRealType) TransparentAlpha;
1309 break;
1310 }
1311 pixel=0.0;
1312 break;
1313 }
cristy99abff32011-12-24 20:45:16 +00001314 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001315 case DissolveCompositeOp:
1316 {
1317 if (channel == AlphaPixelChannel)
1318 {
1319 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1320 source);
1321 break;
1322 }
1323 pixel=(MagickRealType) source[channel];
1324 break;
1325 }
1326 default:
1327 {
1328 pixel=(MagickRealType) source[channel];
1329 break;
1330 }
1331 }
1332 q[i]=ClampToQuantum(pixel);
1333 }
1334 q+=GetPixelChannels(image);
1335 continue;
1336 }
1337 /*
1338 Authentic composite:
1339 Sa: normalized source alpha.
1340 Da: normalized destination alpha.
1341 */
1342 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1343 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001344 switch (compose)
1345 {
cristye4a40472011-12-22 02:56:19 +00001346 case BumpmapCompositeOp:
1347 {
1348 alpha=GetPixelIntensity(composite_image,p)*Sa;
1349 break;
1350 }
cristycdc168f2011-12-21 15:24:39 +00001351 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001352 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001353 case DifferenceCompositeOp:
1354 case DivideDstCompositeOp:
1355 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001356 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001357 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001358 case LinearBurnCompositeOp:
1359 case LinearDodgeCompositeOp:
1360 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001361 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001362 case MinusDstCompositeOp:
1363 case MinusSrcCompositeOp:
1364 case ModulusAddCompositeOp:
1365 case ModulusSubtractCompositeOp:
1366 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001367 case OverlayCompositeOp:
1368 case PegtopLightCompositeOp:
1369 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001370 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001371 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001372 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001373 {
1374 alpha=RoundToUnity(Sa+Da-Sa*Da);
1375 break;
1376 }
1377 case DarkenCompositeOp:
1378 case DstAtopCompositeOp:
1379 case DstInCompositeOp:
1380 case InCompositeOp:
1381 case LightenCompositeOp:
1382 case SrcInCompositeOp:
1383 {
1384 alpha=Sa*Da;
1385 break;
1386 }
1387 case DissolveCompositeOp:
1388 {
1389 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1390 Sa+destination_dissolve*Da;
1391 break;
1392 }
1393 case DstOverCompositeOp:
1394 {
1395 alpha=Da*(-Sa)+Da+Sa;
1396 break;
1397 }
1398 case DstOutCompositeOp:
1399 {
1400 alpha=Da*(1.0-Sa);
1401 break;
1402 }
1403 case OutCompositeOp:
1404 case SrcOutCompositeOp:
1405 {
1406 alpha=Sa*(1.0-Da);
1407 break;
1408 }
1409 case OverCompositeOp:
1410 case SrcOverCompositeOp:
1411 {
1412 alpha=Sa*(-Da)+Sa+Da;
1413 break;
1414 }
cristy99abff32011-12-24 20:45:16 +00001415 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001416 case PlusCompositeOp:
1417 {
1418 alpha=RoundToUnity(Sa+Da);
1419 break;
1420 }
cristy4c08aed2011-07-01 19:47:50 +00001421 case XorCompositeOp:
1422 {
cristye4a40472011-12-22 02:56:19 +00001423 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001424 break;
1425 }
1426 default:
cristye4a40472011-12-22 02:56:19 +00001427 {
1428 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001429 break;
cristye4a40472011-12-22 02:56:19 +00001430 }
cristy4c08aed2011-07-01 19:47:50 +00001431 }
cristy10a6c612012-01-29 21:41:05 +00001432 if (GetPixelMask(image,p) != 0)
1433 {
1434 p+=GetPixelChannels(composite_image);
1435 q+=GetPixelChannels(image);
1436 continue;
1437 }
cristye4a40472011-12-22 02:56:19 +00001438 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1439 {
cristy564a5692012-01-20 23:56:26 +00001440 double
1441 sans;
1442
cristye10859a2011-12-18 22:28:59 +00001443 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001444 pixel;
cristye10859a2011-12-18 22:28:59 +00001445
cristye4a40472011-12-22 02:56:19 +00001446 PixelChannel
1447 channel;
cristye10859a2011-12-18 22:28:59 +00001448
cristye4a40472011-12-22 02:56:19 +00001449 PixelTrait
1450 composite_traits,
1451 traits;
1452
1453 channel=GetPixelChannelMapChannel(image,i);
1454 traits=GetPixelChannelMapTraits(image,channel);
1455 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001456 if (traits == UndefinedPixelTrait)
1457 continue;
cristya7b07912012-01-11 20:01:32 +00001458 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001459 (composite_traits == UndefinedPixelTrait))
1460 continue;
1461 /*
1462 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001463 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001464 */
1465 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
cristye4a40472011-12-22 02:56:19 +00001466 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001467 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001468 {
cristye4a40472011-12-22 02:56:19 +00001469 if (channel != AlphaPixelChannel)
1470 {
1471 /*
1472 Copy channel.
1473 */
1474 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001475 continue;
cristye10859a2011-12-18 22:28:59 +00001476 }
cristye4a40472011-12-22 02:56:19 +00001477 /*
1478 Set alpha channel.
1479 */
cristye10859a2011-12-18 22:28:59 +00001480 switch (compose)
1481 {
cristyc8d63672012-01-11 13:03:13 +00001482 case AlphaCompositeOp:
1483 {
cristya7b07912012-01-11 20:01:32 +00001484 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001485 break;
1486 }
cristye4a40472011-12-22 02:56:19 +00001487 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001488 case CopyBlackCompositeOp:
1489 case CopyBlueCompositeOp:
1490 case CopyCyanCompositeOp:
1491 case CopyGreenCompositeOp:
1492 case CopyMagentaCompositeOp:
1493 case CopyRedCompositeOp:
1494 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001495 case SrcAtopCompositeOp:
1496 case DstCompositeOp:
1497 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001498 {
cristye4a40472011-12-22 02:56:19 +00001499 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001500 break;
1501 }
cristye10859a2011-12-18 22:28:59 +00001502 case ChangeMaskCompositeOp:
1503 {
cristye4a40472011-12-22 02:56:19 +00001504 MagickBooleanType
1505 equivalent;
1506
cristy99abff32011-12-24 20:45:16 +00001507 if (Da > ((MagickRealType) QuantumRange/2.0))
1508 {
1509 pixel=(MagickRealType) TransparentAlpha;
1510 break;
1511 }
cristye4a40472011-12-22 02:56:19 +00001512 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001513 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001514 {
1515 pixel=(MagickRealType) TransparentAlpha;
1516 break;
1517 }
1518 pixel=(MagickRealType) OpaqueAlpha;
1519 break;
1520 }
cristy99abff32011-12-24 20:45:16 +00001521 case ClearCompositeOp:
1522 {
1523 pixel=(MagickRealType) TransparentAlpha;
1524 break;
1525 }
1526 case ColorizeCompositeOp:
1527 case HueCompositeOp:
1528 case LuminizeCompositeOp:
1529 case SaturateCompositeOp:
1530 {
1531 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1532 {
1533 pixel=QuantumRange*Da;
1534 break;
1535 }
1536 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1537 {
1538 pixel=QuantumRange*Sa;
1539 break;
1540 }
1541 if (Sa < Da)
1542 {
1543 pixel=QuantumRange*Da;
1544 break;
1545 }
1546 pixel=QuantumRange*Sa;
1547 break;
1548 }
cristye4a40472011-12-22 02:56:19 +00001549 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001550 case CopyAlphaCompositeOp:
1551 case DisplaceCompositeOp:
1552 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001553 case DstAtopCompositeOp:
1554 case ReplaceCompositeOp:
1555 case SrcCompositeOp:
1556 {
1557 pixel=QuantumRange*Sa;
1558 break;
1559 }
1560 case DarkenIntensityCompositeOp:
1561 {
cristy99abff32011-12-24 20:45:16 +00001562 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1563 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001564 break;
1565 }
cristy98621462011-12-31 22:31:11 +00001566 case IntensityCompositeOp:
1567 {
cristy20d5f622012-01-11 13:04:26 +00001568 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001569 break;
1570 }
cristye4a40472011-12-22 02:56:19 +00001571 case LightenIntensityCompositeOp:
1572 {
1573 pixel=Sa*GetPixelIntensity(composite_image,p) >
1574 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001575 break;
1576 }
cristy99abff32011-12-24 20:45:16 +00001577 case ModulateCompositeOp:
1578 {
1579 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1580 {
1581 pixel=QuantumRange*Da;
1582 break;
1583 }
1584 pixel=QuantumRange*Da;
1585 break;
1586 }
cristye10859a2011-12-18 22:28:59 +00001587 default:
1588 {
cristye4a40472011-12-22 02:56:19 +00001589 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001590 break;
1591 }
1592 }
cristye4a40472011-12-22 02:56:19 +00001593 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001594 continue;
1595 }
1596 /*
cristy99abff32011-12-24 20:45:16 +00001597 Porter-Duff compositions:
1598 Sca: source normalized color multiplied by alpha.
1599 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001600 */
cristy99abff32011-12-24 20:45:16 +00001601 Sca=QuantumScale*Sa*Sc;
1602 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001603 switch (compose)
1604 {
cristye10859a2011-12-18 22:28:59 +00001605 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001606 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001607 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001608 {
cristy99abff32011-12-24 20:45:16 +00001609 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001610 break;
1611 }
1612 default:
1613 break;
1614 }
cristye4a40472011-12-22 02:56:19 +00001615 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
cristyd197cbb2012-01-13 02:14:12 +00001616 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001617 switch (compose)
1618 {
cristya7b07912012-01-11 20:01:32 +00001619 case AlphaCompositeOp:
1620 {
1621 pixel=QuantumRange*Sa;
1622 break;
1623 }
cristye4a40472011-12-22 02:56:19 +00001624 case AtopCompositeOp:
1625 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001626 {
cristye4a40472011-12-22 02:56:19 +00001627 pixel=Sc*Sa+Dc*(1.0-Sa);
1628 break;
cristye10859a2011-12-18 22:28:59 +00001629 }
cristye4a40472011-12-22 02:56:19 +00001630 case BlendCompositeOp:
1631 {
1632 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1633 break;
1634 }
1635 case BlurCompositeOp:
1636 case DisplaceCompositeOp:
1637 case DistortCompositeOp:
1638 case CopyCompositeOp:
1639 case ReplaceCompositeOp:
1640 case SrcCompositeOp:
1641 {
1642 pixel=Sc;
1643 break;
1644 }
1645 case BumpmapCompositeOp:
1646 {
cristy99abff32011-12-24 20:45:16 +00001647 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001648 {
1649 pixel=Dc;
1650 break;
1651 }
1652 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1653 break;
1654 }
cristy99abff32011-12-24 20:45:16 +00001655 case ChangeMaskCompositeOp:
1656 {
1657 pixel=Dc;
1658 break;
1659 }
1660 case ClearCompositeOp:
1661 {
1662 pixel=0.0;
1663 break;
1664 }
cristye4a40472011-12-22 02:56:19 +00001665 case ColorBurnCompositeOp:
1666 {
1667 /*
1668 Refer to the March 2009 SVG specification.
1669 */
1670 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1671 {
cristy99abff32011-12-24 20:45:16 +00001672 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001673 break;
1674 }
1675 if (Sca < MagickEpsilon)
1676 {
cristy99abff32011-12-24 20:45:16 +00001677 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001678 break;
1679 }
cristy99abff32011-12-24 20:45:16 +00001680 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1681 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001682 break;
1683 }
1684 case ColorDodgeCompositeOp:
1685 {
1686 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1687 {
cristy99abff32011-12-24 20:45:16 +00001688 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001689 break;
1690 }
1691 if (fabs(Sca-Sa) < MagickEpsilon)
1692 {
cristy99abff32011-12-24 20:45:16 +00001693 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001694 break;
1695 }
cristy99abff32011-12-24 20:45:16 +00001696 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001697 (1.0-Sa));
1698 break;
1699 }
1700 case ColorizeCompositeOp:
1701 {
cristy99abff32011-12-24 20:45:16 +00001702 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001703 {
1704 pixel=Dc;
1705 break;
1706 }
cristy99abff32011-12-24 20:45:16 +00001707 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001708 {
1709 pixel=Sc;
1710 break;
1711 }
1712 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
cristy99abff32011-12-24 20:45:16 +00001713 GetPixelBlue(image,q),&sans,&sans,&brightness);
cristye4a40472011-12-22 02:56:19 +00001714 CompositeHSB(GetPixelRed(composite_image,p),
1715 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1716 &hue,&saturation,&sans);
1717 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1718 switch (channel)
1719 {
1720 case RedPixelChannel: pixel=red; break;
1721 case GreenPixelChannel: pixel=green; break;
1722 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001723 default: pixel=Dc; break;
1724 }
1725 break;
1726 }
cristye4a40472011-12-22 02:56:19 +00001727 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001728 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001729 {
cristyd197cbb2012-01-13 02:14:12 +00001730 if (channel == AlphaPixelChannel)
1731 pixel=(MagickRealType) GetPixelAlpha(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001732 break;
1733 }
1734 case CopyBlackCompositeOp:
1735 {
cristyd197cbb2012-01-13 02:14:12 +00001736 if (channel == BlackPixelChannel)
1737 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001738 break;
1739 }
1740 case CopyBlueCompositeOp:
1741 case CopyYellowCompositeOp:
1742 {
cristyd197cbb2012-01-13 02:14:12 +00001743 if (channel == BluePixelChannel)
1744 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001745 break;
1746 }
1747 case CopyGreenCompositeOp:
1748 case CopyMagentaCompositeOp:
1749 {
cristyd197cbb2012-01-13 02:14:12 +00001750 if (channel == GreenPixelChannel)
1751 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001752 break;
1753 }
1754 case CopyRedCompositeOp:
1755 case CopyCyanCompositeOp:
1756 {
cristyd197cbb2012-01-13 02:14:12 +00001757 if (channel == RedPixelChannel)
1758 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001759 break;
1760 }
cristy99abff32011-12-24 20:45:16 +00001761 case DarkenCompositeOp:
1762 {
1763 /*
1764 Darken is equivalent to a 'Minimum' method
1765 OR a greyscale version of a binary 'Or'
1766 OR the 'Intersection' of pixel sets.
1767 */
1768 if (Sc < Dc)
1769 {
1770 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1771 break;
1772 }
1773 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1774 break;
1775 }
cristye4a40472011-12-22 02:56:19 +00001776 case DarkenIntensityCompositeOp:
1777 {
cristy99abff32011-12-24 20:45:16 +00001778 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1779 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001780 break;
1781 }
1782 case DifferenceCompositeOp:
1783 {
1784 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1785 break;
1786 }
1787 case DissolveCompositeOp:
1788 {
1789 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1790 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1791 break;
1792 }
1793 case DivideDstCompositeOp:
1794 {
1795 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1796 {
cristy99abff32011-12-24 20:45:16 +00001797 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001798 break;
1799 }
1800 if (fabs(Dca) < MagickEpsilon)
1801 {
cristy99abff32011-12-24 20:45:16 +00001802 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001803 break;
1804 }
cristy99abff32011-12-24 20:45:16 +00001805 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001806 break;
1807 }
1808 case DivideSrcCompositeOp:
1809 {
1810 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1811 {
cristy99abff32011-12-24 20:45:16 +00001812 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001813 break;
1814 }
1815 if (fabs(Sca) < MagickEpsilon)
1816 {
cristy99abff32011-12-24 20:45:16 +00001817 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001818 break;
1819 }
cristy99abff32011-12-24 20:45:16 +00001820 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001821 break;
1822 }
1823 case DstAtopCompositeOp:
1824 {
1825 pixel=Dc*Da+Sc*(1.0-Da);
1826 break;
1827 }
1828 case DstCompositeOp:
1829 case NoCompositeOp:
1830 {
1831 pixel=Dc;
1832 break;
1833 }
1834 case DstInCompositeOp:
1835 {
1836 pixel=gamma*(Sa*Dc*Sa);
1837 break;
1838 }
1839 case DstOutCompositeOp:
1840 {
1841 pixel=gamma*(Da*Dc*(1.0-Sa));
1842 break;
1843 }
1844 case DstOverCompositeOp:
1845 {
1846 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1847 break;
1848 }
1849 case ExclusionCompositeOp:
1850 {
cristy99abff32011-12-24 20:45:16 +00001851 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1852 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001853 break;
1854 }
1855 case HardLightCompositeOp:
1856 {
1857 if ((2.0*Sca) < Sa)
1858 {
cristy99abff32011-12-24 20:45:16 +00001859 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001860 (1.0-Sa));
1861 break;
1862 }
cristy99abff32011-12-24 20:45:16 +00001863 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001864 Dca*(1.0-Sa));
1865 break;
1866 }
1867 case HueCompositeOp:
1868 {
cristy99abff32011-12-24 20:45:16 +00001869 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001870 {
1871 pixel=Dc;
1872 break;
1873 }
cristy99abff32011-12-24 20:45:16 +00001874 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001875 {
1876 pixel=Sc;
1877 break;
1878 }
1879 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1880 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1881 CompositeHSB(GetPixelRed(composite_image,p),
1882 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1883 &hue,&sans,&sans);
1884 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1885 switch (channel)
1886 {
1887 case RedPixelChannel: pixel=red; break;
1888 case GreenPixelChannel: pixel=green; break;
1889 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001890 default: pixel=Dc; break;
1891 }
1892 break;
1893 }
1894 case InCompositeOp:
1895 case SrcInCompositeOp:
1896 {
1897 pixel=gamma*(Da*Sc*Da);
1898 break;
1899 }
cristy99abff32011-12-24 20:45:16 +00001900 case LinearBurnCompositeOp:
1901 {
1902 /*
1903 LinearBurn: as defined by Abode Photoshop, according to
1904 http://www.simplefilter.de/en/basics/mixmods.html is:
1905
1906 f(Sc,Dc) = Sc + Dc - 1
1907 */
1908 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1909 break;
1910 }
1911 case LinearDodgeCompositeOp:
1912 {
1913 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1914 break;
1915 }
1916 case LinearLightCompositeOp:
1917 {
1918 /*
1919 LinearLight: as defined by Abode Photoshop, according to
1920 http://www.simplefilter.de/en/basics/mixmods.html is:
1921
1922 f(Sc,Dc) = Dc + 2*Sc - 1
1923 */
1924 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1925 break;
1926 }
1927 case LightenCompositeOp:
1928 {
1929 if (Sc > Dc)
1930 {
1931 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1932 break;
1933 }
1934 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1935 break;
1936 }
cristye4a40472011-12-22 02:56:19 +00001937 case LightenIntensityCompositeOp:
1938 {
1939 /*
1940 Lighten is equivalent to a 'Maximum' method
1941 OR a greyscale version of a binary 'And'
1942 OR the 'Union' of pixel sets.
1943 */
1944 pixel=Sa*GetPixelIntensity(composite_image,p) >
1945 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1946 break;
1947 }
cristye4a40472011-12-22 02:56:19 +00001948 case LuminizeCompositeOp:
1949 {
cristy99abff32011-12-24 20:45:16 +00001950 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001951 {
1952 pixel=Dc;
1953 break;
1954 }
cristy99abff32011-12-24 20:45:16 +00001955 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001956 {
1957 pixel=Sc;
1958 break;
1959 }
1960 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1961 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1962 CompositeHSB(GetPixelRed(composite_image,p),
1963 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1964 &sans,&sans,&brightness);
1965 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1966 switch (channel)
1967 {
1968 case RedPixelChannel: pixel=red; break;
1969 case GreenPixelChannel: pixel=green; break;
1970 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001971 default: pixel=Dc; break;
1972 }
1973 break;
1974 }
1975 case MathematicsCompositeOp:
1976 {
1977 /*
1978 'Mathematics' a free form user control mathematical composition
1979 is defined as...
1980
1981 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
1982
1983 Where the arguments A,B,C,D are (currently) passed to composite
1984 as a command separated 'geometry' string in "compose:args" image
1985 artifact.
1986
1987 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
1988
1989 Applying the SVG transparency formula (see above), we get...
1990
1991 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
1992
1993 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
1994 Dca*(1.0-Sa)
1995 */
1996 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
1997 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
1998 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
1999 break;
2000 }
2001 case MinusDstCompositeOp:
2002 {
2003 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2004 break;
2005 }
2006 case MinusSrcCompositeOp:
2007 {
2008 /*
2009 Minus source from destination.
2010
2011 f(Sc,Dc) = Sc - Dc
2012 */
cristy99abff32011-12-24 20:45:16 +00002013 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002014 break;
2015 }
2016 case ModulateCompositeOp:
2017 {
2018 ssize_t
2019 offset;
2020
cristy99abff32011-12-24 20:45:16 +00002021 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002022 {
2023 pixel=Dc;
2024 break;
2025 }
2026 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2027 if (offset == 0)
2028 {
2029 pixel=Dc;
2030 break;
2031 }
2032 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2033 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2034 brightness+=(0.01*percent_brightness*offset)/midpoint;
2035 saturation*=0.01*percent_saturation;
2036 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2037 switch (channel)
2038 {
2039 case RedPixelChannel: pixel=red; break;
2040 case GreenPixelChannel: pixel=green; break;
2041 case BluePixelChannel: pixel=blue; break;
2042 default: pixel=Dc; break;
2043 }
2044 break;
2045 }
2046 case ModulusAddCompositeOp:
2047 {
2048 pixel=Sc+Dc;
2049 if (pixel > QuantumRange)
2050 pixel-=(QuantumRange+1.0);
2051 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2052 break;
2053 }
2054 case ModulusSubtractCompositeOp:
2055 {
cristy99abff32011-12-24 20:45:16 +00002056 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002057 if (pixel < 0.0)
2058 pixel+=(QuantumRange+1.0);
2059 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2060 break;
2061 }
2062 case MultiplyCompositeOp:
2063 {
cristy99abff32011-12-24 20:45:16 +00002064 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002065 break;
2066 }
2067 case OutCompositeOp:
2068 case SrcOutCompositeOp:
2069 {
2070 pixel=gamma*(Sa*Sc*(1.0-Da));
2071 break;
2072 }
2073 case OverCompositeOp:
2074 case SrcOverCompositeOp:
2075 {
cristy99abff32011-12-24 20:45:16 +00002076 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002077 break;
2078 }
2079 case OverlayCompositeOp:
2080 {
2081 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002082 {
2083 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2084 (1.0-Da));
2085 break;
2086 }
2087 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2088 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002089 break;
2090 }
2091 case PegtopLightCompositeOp:
2092 {
2093 /*
2094 PegTop: A Soft-Light alternative: A continuous version of the
2095 Softlight function, producing very similar results.
2096
2097 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2098
2099 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2100 */
2101 if (fabs(Da) < MagickEpsilon)
2102 {
cristy99abff32011-12-24 20:45:16 +00002103 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002104 break;
2105 }
cristy99abff32011-12-24 20:45:16 +00002106 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2107 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002108 break;
2109 }
2110 case PinLightCompositeOp:
2111 {
2112 /*
2113 PinLight: A Photoshop 7 composition method
2114 http://www.simplefilter.de/en/basics/mixmods.html
2115
2116 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2117 */
2118 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2119 {
cristy99abff32011-12-24 20:45:16 +00002120 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002121 break;
2122 }
2123 if ((Dca*Sa) > (2.0*Sca*Da))
2124 {
cristy99abff32011-12-24 20:45:16 +00002125 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002126 break;
2127 }
cristy99abff32011-12-24 20:45:16 +00002128 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002129 break;
2130 }
2131 case PlusCompositeOp:
2132 {
cristy99abff32011-12-24 20:45:16 +00002133 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002134 break;
2135 }
2136 case SaturateCompositeOp:
2137 {
cristy99abff32011-12-24 20:45:16 +00002138 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002139 {
2140 pixel=Dc;
2141 break;
2142 }
cristy99abff32011-12-24 20:45:16 +00002143 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002144 {
2145 pixel=Sc;
2146 break;
2147 }
2148 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2149 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2150 CompositeHSB(GetPixelRed(composite_image,p),
2151 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
2152 &sans,&saturation,&sans);
2153 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2154 switch (channel)
2155 {
2156 case RedPixelChannel: pixel=red; break;
2157 case GreenPixelChannel: pixel=green; break;
2158 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002159 default: pixel=Dc; break;
2160 }
2161 break;
2162 }
2163 case ScreenCompositeOp:
2164 {
2165 /*
2166 Screen: a negated multiply:
2167
2168 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2169 */
cristy99abff32011-12-24 20:45:16 +00002170 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002171 break;
2172 }
2173 case SoftLightCompositeOp:
2174 {
2175 /*
2176 Refer to the March 2009 SVG specification.
2177 */
2178 if ((2.0*Sca) < Sa)
2179 {
cristy99abff32011-12-24 20:45:16 +00002180 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2181 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002182 break;
2183 }
2184 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2185 {
cristy99abff32011-12-24 20:45:16 +00002186 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2187 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2188 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002189 break;
2190 }
cristy99abff32011-12-24 20:45:16 +00002191 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2192 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002193 break;
2194 }
2195 case ThresholdCompositeOp:
2196 {
2197 MagickRealType
2198 delta;
2199
2200 delta=Sc-Dc;
2201 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2202 {
2203 pixel=gamma*Dc;
2204 break;
2205 }
2206 pixel=gamma*(Dc+delta*amount);
2207 break;
2208 }
2209 case VividLightCompositeOp:
2210 {
2211 /*
2212 VividLight: A Photoshop 7 composition method. See
2213 http://www.simplefilter.de/en/basics/mixmods.html.
2214
2215 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2216 */
2217 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2218 {
cristy99abff32011-12-24 20:45:16 +00002219 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002220 break;
2221 }
2222 if ((2.0*Sca) <= Sa)
2223 {
cristy99abff32011-12-24 20:45:16 +00002224 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2225 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002226 break;
2227 }
cristy99abff32011-12-24 20:45:16 +00002228 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2229 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002230 break;
2231 }
2232 case XorCompositeOp:
2233 {
cristy99abff32011-12-24 20:45:16 +00002234 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002235 break;
2236 }
2237 default:
2238 {
2239 pixel=Sc;
2240 break;
2241 }
2242 }
2243 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002244 }
cristyed231572011-07-14 02:18:59 +00002245 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002246 channels=GetPixelChannels(composite_image);
2247 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002248 p=pixels;
cristyed231572011-07-14 02:18:59 +00002249 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002250 }
2251 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2252 status=MagickFalse;
2253 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2254 {
2255 MagickBooleanType
2256 proceed;
2257
2258#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002259 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002260#endif
2261 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2262 image->rows);
2263 if (proceed == MagickFalse)
2264 status=MagickFalse;
2265 }
2266 }
2267 composite_view=DestroyCacheView(composite_view);
2268 image_view=DestroyCacheView(image_view);
2269 if (destination_image != (Image * ) NULL)
2270 destination_image=DestroyImage(destination_image);
2271 return(status);
2272}
2273
2274/*
2275%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2276% %
2277% %
2278% %
2279% T e x t u r e I m a g e %
2280% %
2281% %
2282% %
2283%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2284%
2285% TextureImage() repeatedly tiles the texture image across and down the image
2286% canvas.
2287%
2288% The format of the TextureImage method is:
2289%
cristy30d8c942012-02-07 13:44:59 +00002290% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002291% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002292%
2293% A description of each parameter follows:
2294%
2295% o image: the image.
2296%
cristye6178502011-12-23 17:02:29 +00002297% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002298%
2299*/
cristy30d8c942012-02-07 13:44:59 +00002300MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2301 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002302{
2303#define TextureImageTag "Texture/Image"
2304
2305 CacheView
2306 *image_view,
2307 *texture_view;
2308
cristy30d8c942012-02-07 13:44:59 +00002309 Image
2310 *texture_image;
2311
cristy4c08aed2011-07-01 19:47:50 +00002312 MagickBooleanType
2313 status;
2314
2315 ssize_t
2316 y;
2317
2318 assert(image != (Image *) NULL);
2319 if (image->debug != MagickFalse)
2320 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2321 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002322 if (texture == (const Image *) NULL)
2323 return(MagickFalse);
2324 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2325 return(MagickFalse);
2326 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002327 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002328 return(MagickFalse);
cristy387430f2012-02-07 13:09:46 +00002329 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2330 exception);
cristy4c08aed2011-07-01 19:47:50 +00002331 status=MagickTrue;
2332 if ((image->compose != CopyCompositeOp) &&
2333 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002334 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002335 {
2336 /*
2337 Tile texture onto the image background.
2338 */
2339#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy19593872012-01-22 02:00:33 +00002340 #pragma omp parallel for schedule(static) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002341#endif
cristye6178502011-12-23 17:02:29 +00002342 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002343 {
2344 register ssize_t
2345 x;
2346
2347 if (status == MagickFalse)
2348 continue;
cristye6178502011-12-23 17:02:29 +00002349 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002350 {
2351 MagickBooleanType
2352 thread_status;
2353
cristye6178502011-12-23 17:02:29 +00002354 thread_status=CompositeImage(image,image->compose,texture_image,x+
2355 texture_image->tile_offset.x,y+texture_image->tile_offset.y,
2356 exception);
cristy4c08aed2011-07-01 19:47:50 +00002357 if (thread_status == MagickFalse)
2358 {
2359 status=thread_status;
2360 break;
2361 }
2362 }
2363 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2364 {
2365 MagickBooleanType
2366 proceed;
2367
2368#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002369 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002370#endif
2371 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2372 y,image->rows);
2373 if (proceed == MagickFalse)
2374 status=MagickFalse;
2375 }
2376 }
2377 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2378 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002379 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002380 return(status);
2381 }
2382 /*
2383 Tile texture onto the image background (optimized).
2384 */
2385 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00002386 image_view=AcquireCacheView(image);
cristye6178502011-12-23 17:02:29 +00002387 texture_view=AcquireCacheView(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002388#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy19593872012-01-22 02:00:33 +00002389 #pragma omp parallel for schedule(static) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002390#endif
2391 for (y=0; y < (ssize_t) image->rows; y++)
2392 {
2393 MagickBooleanType
2394 sync;
2395
2396 register const Quantum
2397 *p,
2398 *pixels;
2399
2400 register ssize_t
2401 x;
2402
2403 register Quantum
2404 *q;
2405
2406 size_t
2407 width;
2408
2409 if (status == MagickFalse)
2410 continue;
cristye6178502011-12-23 17:02:29 +00002411 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2412 (y+texture_image->tile_offset.y) % texture_image->rows,
2413 texture_image->columns,1,exception);
2414 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002415 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2416 {
2417 status=MagickFalse;
2418 continue;
2419 }
cristye6178502011-12-23 17:02:29 +00002420 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002421 {
2422 register ssize_t
cristye6178502011-12-23 17:02:29 +00002423 j;
cristy4c08aed2011-07-01 19:47:50 +00002424
2425 p=pixels;
cristye6178502011-12-23 17:02:29 +00002426 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002427 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2428 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002429 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002430 {
cristye6178502011-12-23 17:02:29 +00002431 register ssize_t
2432 i;
2433
cristy10a6c612012-01-29 21:41:05 +00002434 if (GetPixelMask(image,p) != 0)
2435 {
2436 p+=GetPixelChannels(texture_image);
2437 q+=GetPixelChannels(image);
2438 continue;
2439 }
cristye6178502011-12-23 17:02:29 +00002440 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2441 {
2442 PixelChannel
2443 channel;
2444
2445 PixelTrait
2446 texture_traits,
2447 traits;
2448
2449 channel=GetPixelChannelMapChannel(texture_image,i);
2450 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2451 traits=GetPixelChannelMapTraits(image,channel);
2452 if ((traits == UndefinedPixelTrait) ||
2453 (texture_traits == UndefinedPixelTrait))
2454 continue;
2455 SetPixelChannel(image,channel,p[i],q);
2456 }
2457 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002458 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002459 }
2460 }
2461 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2462 if (sync == MagickFalse)
2463 status=MagickFalse;
2464 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2465 {
2466 MagickBooleanType
2467 proceed;
2468
2469#if defined(MAGICKCORE_OPENMP_SUPPORT)
2470 #pragma omp critical (MagickCore_TextureImage)
2471#endif
2472 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2473 image->rows);
2474 if (proceed == MagickFalse)
2475 status=MagickFalse;
2476 }
2477 }
2478 texture_view=DestroyCacheView(texture_view);
2479 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002480 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002481 return(status);
2482}