blob: 7d62cefda18cbde4d04e03a0bc8add652444f5df [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"
75#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000076#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000077#include "MagickCore/version.h"
78
79/*
80%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81% %
82% %
83% %
cristyf4ad9df2011-07-08 16:49:03 +000084% C o m p o s i t e I m a g e %
cristy4c08aed2011-07-01 19:47:50 +000085% %
86% %
87% %
88%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89%
cristyf4ad9df2011-07-08 16:49:03 +000090% CompositeImage() returns the second image composited onto the first
cristy4c08aed2011-07-01 19:47:50 +000091% at the specified offset, using the specified composite method.
92%
cristyf4ad9df2011-07-08 16:49:03 +000093% The format of the CompositeImage method is:
cristy4c08aed2011-07-01 19:47:50 +000094%
95% MagickBooleanType CompositeImage(Image *image,
96% const CompositeOperator compose,Image *composite_image,
cristye941a752011-10-15 01:52:48 +000097% const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +000098%
99% A description of each parameter follows:
100%
101% o image: the destination image, modified by he composition
102%
cristy4c08aed2011-07-01 19:47:50 +0000103% o compose: This operator affects how the composite is applied to
104% the image. The operators and how they are utilized are listed here
105% http://www.w3.org/TR/SVG12/#compositing.
106%
107% o composite_image: the composite (source) image.
108%
109% o x_offset: the column offset of the composited image.
110%
111% o y_offset: the row offset of the composited image.
112%
113% Extra Controls from Image meta-data in 'composite_image' (artifacts)
114%
115% o "compose:args"
116% A string containing extra numerical arguments for specific compose
117% methods, generally expressed as a 'geometry' or a comma separated list
118% of numbers.
119%
120% Compose methods needing such arguments include "BlendCompositeOp" and
121% "DisplaceCompositeOp".
122%
123% o "compose:outside-overlay"
124% Modify how the composition is to effect areas not directly covered
125% by the 'composite_image' at the offset given. Normally this is
126% dependant on the 'compose' method, especially Duff-Porter methods.
127%
128% If set to "false" then disable all normal handling of pixels not
129% covered by the composite_image. Typically used for repeated tiling
130% of the composite_image by the calling API.
131%
132% Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
133%
cristye941a752011-10-15 01:52:48 +0000134% o exception: return any errors or warnings in this structure.
135%
cristy4c08aed2011-07-01 19:47:50 +0000136*/
137
cristye4a40472011-12-22 02:56:19 +0000138static void CompositeHSB(const Quantum red,const Quantum green,
139 const Quantum blue,double *hue,double *saturation,double *brightness)
cristy4c08aed2011-07-01 19:47:50 +0000140{
cristye4a40472011-12-22 02:56:19 +0000141 double
142 delta;
cristy4c08aed2011-07-01 19:47:50 +0000143
cristye4a40472011-12-22 02:56:19 +0000144 Quantum
cristy4c08aed2011-07-01 19:47:50 +0000145 max,
146 min;
147
148 /*
149 Convert RGB to HSB colorspace.
150 */
151 assert(hue != (double *) NULL);
152 assert(saturation != (double *) NULL);
153 assert(brightness != (double *) NULL);
154 max=(red > green ? red : green);
155 if (blue > max)
156 max=blue;
157 min=(red < green ? red : green);
158 if (blue < min)
159 min=blue;
160 *hue=0.0;
161 *saturation=0.0;
162 *brightness=(double) (QuantumScale*max);
cristye4a40472011-12-22 02:56:19 +0000163 if (fabs((double) max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000164 return;
165 *saturation=(double) (1.0-min/max);
cristye4a40472011-12-22 02:56:19 +0000166 delta=(MagickRealType) max-min;
cristyaa83c2c2011-09-21 13:36:25 +0000167 if (fabs(delta) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000168 return;
cristye4a40472011-12-22 02:56:19 +0000169 if (fabs((double) red-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000170 *hue=(double) ((green-blue)/delta);
171 else
cristye4a40472011-12-22 02:56:19 +0000172 if (fabs((double) green-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000173 *hue=(double) (2.0+(blue-red)/delta);
174 else
cristye4a40472011-12-22 02:56:19 +0000175 if (fabs((double) blue-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000176 *hue=(double) (4.0+(red-green)/delta);
177 *hue/=6.0;
178 if (*hue < 0.0)
179 *hue+=1.0;
180}
181
cristy4c08aed2011-07-01 19:47:50 +0000182static void HSBComposite(const double hue,const double saturation,
cristye4a40472011-12-22 02:56:19 +0000183 const double brightness,double *red,double *green,double *blue)
cristy4c08aed2011-07-01 19:47:50 +0000184{
cristya96f2492011-12-14 18:25:41 +0000185 double
cristy4c08aed2011-07-01 19:47:50 +0000186 f,
187 h,
188 p,
189 q,
190 t;
191
192 /*
193 Convert HSB to RGB colorspace.
194 */
cristya96f2492011-12-14 18:25:41 +0000195 assert(red != (double *) NULL);
196 assert(green != (double *) NULL);
197 assert(blue != (double *) NULL);
cristy4c08aed2011-07-01 19:47:50 +0000198 if (saturation == 0.0)
199 {
cristya96f2492011-12-14 18:25:41 +0000200 *red=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000201 *green=(*red);
202 *blue=(*red);
203 return;
204 }
205 h=6.0*(hue-floor(hue));
206 f=h-floor((double) h);
207 p=brightness*(1.0-saturation);
208 q=brightness*(1.0-saturation*f);
209 t=brightness*(1.0-saturation*(1.0-f));
210 switch ((int) h)
211 {
212 case 0:
213 default:
214 {
cristya96f2492011-12-14 18:25:41 +0000215 *red=(double) QuantumRange*brightness;
216 *green=(double) QuantumRange*t;
217 *blue=(double) QuantumRange*p;
cristy4c08aed2011-07-01 19:47:50 +0000218 break;
219 }
220 case 1:
221 {
cristya96f2492011-12-14 18:25:41 +0000222 *red=(double) QuantumRange*q;
223 *green=(double) QuantumRange*brightness;
224 *blue=(double) QuantumRange*p;
cristy4c08aed2011-07-01 19:47:50 +0000225 break;
226 }
227 case 2:
228 {
cristya96f2492011-12-14 18:25:41 +0000229 *red=(double) QuantumRange*p;
230 *green=(double) QuantumRange*brightness;
231 *blue=(double) QuantumRange*t;
cristy4c08aed2011-07-01 19:47:50 +0000232 break;
233 }
234 case 3:
235 {
cristya96f2492011-12-14 18:25:41 +0000236 *red=(double) QuantumRange*p;
237 *green=(double) QuantumRange*q;
238 *blue=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000239 break;
240 }
241 case 4:
242 {
cristya96f2492011-12-14 18:25:41 +0000243 *red=(double) QuantumRange*t;
244 *green=(double) QuantumRange*p;
245 *blue=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000246 break;
247 }
248 case 5:
249 {
cristya96f2492011-12-14 18:25:41 +0000250 *red=(double) QuantumRange*brightness;
251 *green=(double) QuantumRange*p;
252 *blue=(double) QuantumRange*q;
cristy4c08aed2011-07-01 19:47:50 +0000253 break;
254 }
255 }
256}
257
cristye4a40472011-12-22 02:56:19 +0000258static inline double MagickMin(const double x,const double y)
259{
260 if (x < y)
261 return(x);
262 return(y);
263}
264static inline double MagickMax(const double x,const double y)
265{
266 if (x > y)
267 return(x);
268 return(y);
269}
270
271static MagickBooleanType CompositeOverImage(Image *image,
272 const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset,
273 ExceptionInfo *exception)
274{
275#define CompositeImageTag "Composite/Image"
276
277 CacheView
278 *composite_view,
279 *image_view;
280
281 const char
282 *value;
283
284 MagickBooleanType
285 modify_outside_overlay,
286 status;
287
288 MagickOffsetType
289 progress;
290
291 ssize_t
292 y;
293
cristye4a40472011-12-22 02:56:19 +0000294 /*
295 Prepare composite image.
296 */
297 modify_outside_overlay=MagickFalse;
298 value=GetImageArtifact(composite_image,"compose:outside-overlay");
299 if (value != (const char *) NULL)
300 modify_outside_overlay=IsMagickTrue(value);
301 /*
302 Composite image.
303 */
304 status=MagickTrue;
305 progress=0;
306 image_view=AcquireCacheView(image);
307 composite_view=AcquireCacheView(composite_image);
308#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000309 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye4a40472011-12-22 02:56:19 +0000310#endif
311 for (y=0; y < (ssize_t) image->rows; y++)
312 {
313 const Quantum
314 *pixels;
315
316 register const Quantum
317 *restrict p;
318
319 register Quantum
320 *restrict q;
321
322 register ssize_t
323 x;
324
cristy564a5692012-01-20 23:56:26 +0000325 size_t
326 channels;
327
cristye4a40472011-12-22 02:56:19 +0000328 if (status == MagickFalse)
329 continue;
330 if (modify_outside_overlay == MagickFalse)
331 {
332 if (y < y_offset)
333 continue;
334 if ((y-y_offset) >= (ssize_t) composite_image->rows)
335 continue;
336 }
337 /*
338 If pixels is NULL, y is outside overlay region.
339 */
340 pixels=(Quantum *) NULL;
341 p=(Quantum *) NULL;
342 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
343 {
344 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
345 composite_image->columns,1,exception);
346 if (p == (const Quantum *) NULL)
347 {
348 status=MagickFalse;
349 continue;
350 }
351 pixels=p;
352 if (x_offset < 0)
353 p-=x_offset*GetPixelChannels(composite_image);
354 }
355 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
356 if (q == (Quantum *) NULL)
357 {
358 status=MagickFalse;
359 continue;
360 }
361 for (x=0; x < (ssize_t) image->columns; x++)
362 {
363 MagickRealType
364 alpha,
365 Da,
366 Dc,
367 gamma,
368 Sa,
369 Sc;
370
371 register ssize_t
372 i;
373
374 if (modify_outside_overlay == MagickFalse)
375 {
376 if (x < x_offset)
377 {
378 q+=GetPixelChannels(image);
379 continue;
380 }
381 if ((x-x_offset) >= (ssize_t) composite_image->columns)
382 break;
383 }
384 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
385 ((x-x_offset) >= (ssize_t) composite_image->columns))
386 {
387 Quantum
388 source[MaxPixelChannels];
389
390 /*
391 Virtual composite:
392 Sc: source color.
393 Dc: destination color.
394 */
cristy10a6c612012-01-29 21:41:05 +0000395 if (GetPixelMask(image,q) != 0)
396 {
397 q+=GetPixelChannels(image);
398 continue;
399 }
cristyc94ba6f2012-01-29 23:19:58 +0000400 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
401 source,exception);
cristye4a40472011-12-22 02:56:19 +0000402 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
403 {
404 PixelChannel
405 channel;
406
407 PixelTrait
408 composite_traits,
409 traits;
410
411 channel=GetPixelChannelMapChannel(image,i);
412 traits=GetPixelChannelMapTraits(image,channel);
cristy10a6c612012-01-29 21:41:05 +0000413 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +0000414 if ((traits == UndefinedPixelTrait) ||
415 (composite_traits == UndefinedPixelTrait))
416 continue;
417 q[i]=source[channel];
418 }
419 q+=GetPixelChannels(image);
420 continue;
421 }
422 /*
423 Authentic composite:
424 Sa: normalized source alpha.
425 Da: normalized destination alpha.
426 */
cristyc94ba6f2012-01-29 23:19:58 +0000427 if (GetPixelMask(composite_image,p) != 0)
428 {
429 p+=GetPixelChannels(composite_image);
430 channels=GetPixelChannels(composite_image);
431 if (p >= (pixels+channels*composite_image->columns))
432 p=pixels;
433 q+=GetPixelChannels(image);
434 continue;
435 }
cristye4a40472011-12-22 02:56:19 +0000436 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
437 Da=QuantumScale*GetPixelAlpha(image,q);
438 alpha=Sa*(-Da)+Sa+Da;
439 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
440 {
441 PixelChannel
442 channel;
443
444 PixelTrait
445 composite_traits,
446 traits;
447
448 channel=GetPixelChannelMapChannel(image,i);
449 traits=GetPixelChannelMapTraits(image,channel);
450 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
451 if ((traits == UndefinedPixelTrait) ||
452 (composite_traits == UndefinedPixelTrait))
453 continue;
454 if ((traits & CopyPixelTrait) != 0)
455 {
456 if (channel != AlphaPixelChannel)
457 {
458 /*
459 Copy channel.
460 */
461 q[i]=GetPixelChannel(composite_image,channel,p);
462 continue;
463 }
464 /*
465 Set alpha channel.
466 */
467 q[i]=ClampToQuantum(QuantumRange*alpha);
468 continue;
469 }
470 /*
471 Sc: source color.
472 Dc: destination color.
473 */
474 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
475 Dc=(MagickRealType) q[i];
476 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
477 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
478 }
479 p+=GetPixelChannels(composite_image);
480 channels=GetPixelChannels(composite_image);
481 if (p >= (pixels+channels*composite_image->columns))
482 p=pixels;
483 q+=GetPixelChannels(image);
484 }
485 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
486 status=MagickFalse;
487 if (image->progress_monitor != (MagickProgressMonitor) NULL)
488 {
489 MagickBooleanType
490 proceed;
491
492#if defined(MAGICKCORE_OPENMP_SUPPORT)
493 #pragma omp critical (MagickCore_CompositeImage)
494#endif
495 proceed=SetImageProgress(image,CompositeImageTag,progress++,
496 image->rows);
497 if (proceed == MagickFalse)
498 status=MagickFalse;
499 }
500 }
501 composite_view=DestroyCacheView(composite_view);
502 image_view=DestroyCacheView(image_view);
503 return(status);
504}
505
cristy4c08aed2011-07-01 19:47:50 +0000506MagickExport MagickBooleanType CompositeImage(Image *image,
507 const CompositeOperator compose,const Image *composite_image,
cristye941a752011-10-15 01:52:48 +0000508 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000509{
cristy4c08aed2011-07-01 19:47:50 +0000510#define CompositeImageTag "Composite/Image"
511
512 CacheView
513 *composite_view,
514 *image_view;
515
516 const char
517 *value;
518
cristy4c08aed2011-07-01 19:47:50 +0000519 GeometryInfo
520 geometry_info;
521
522 Image
523 *destination_image;
524
525 MagickBooleanType
526 modify_outside_overlay,
527 status;
528
529 MagickOffsetType
530 progress;
531
cristy4c08aed2011-07-01 19:47:50 +0000532 MagickRealType
533 amount,
534 destination_dissolve,
535 midpoint,
536 percent_brightness,
537 percent_saturation,
538 source_dissolve,
539 threshold;
540
541 MagickStatusType
542 flags;
543
cristyd197cbb2012-01-13 02:14:12 +0000544 ssize_t
545 y;
546
cristy4c08aed2011-07-01 19:47:50 +0000547 /*
cristye4a40472011-12-22 02:56:19 +0000548 Composition based on the SVG specification:
549
550 A Composition is defined by...
551 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
552 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
553 Y = 1 for source preserved
554 Z = 1 for destination preserved
555
556 Conversion to transparency (then optimized)
557 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
558 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
559
560 Where...
561 Sca = Sc*Sa normalized Source color divided by Source alpha
562 Dca = Dc*Da normalized Dest color divided by Dest alpha
563 Dc' = Dca'/Da' the desired color value for this channel.
564
565 Da' in in the follow formula as 'gamma' The resulting alpla value.
566
567 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
568 the following optimizations...
569 gamma = Sa+Da-Sa*Da;
570 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
571 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
572
573 The above SVG definitions also definate that Mathematical Composition
574 methods should use a 'Over' blending mode for Alpha Channel.
575 It however was not applied for composition modes of 'Plus', 'Minus',
576 the modulus versions of 'Add' and 'Subtract'.
577
578 Mathematical operator changes to be applied from IM v6.7...
579
580 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
581 'ModulusAdd' and 'ModulusSubtract' for clarity.
582
583 2) All mathematical compositions work as per the SVG specification
584 with regard to blending. This now includes 'ModulusAdd' and
585 'ModulusSubtract'.
586
587 3) When the special channel flag 'sync' (syncronize channel updates)
588 is turned off (enabled by default) then mathematical compositions are
589 only performed on the channels specified, and are applied
590 independantally of each other. In other words the mathematics is
591 performed as 'pure' mathematical operations, rather than as image
592 operations.
cristy4c08aed2011-07-01 19:47:50 +0000593 */
594 assert(image != (Image *) NULL);
595 assert(image->signature == MagickSignature);
596 if (image->debug != MagickFalse)
597 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
598 assert(composite_image != (Image *) NULL);
599 assert(composite_image->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000600 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000601 return(MagickFalse);
cristye4a40472011-12-22 02:56:19 +0000602 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
603 {
604 status=CompositeOverImage(image,composite_image,x_offset,y_offset,
605 exception);
606 return(status);
607 }
cristy4c08aed2011-07-01 19:47:50 +0000608 destination_image=(Image *) NULL;
609 amount=0.5;
610 destination_dissolve=1.0;
611 modify_outside_overlay=MagickFalse;
612 percent_brightness=100.0;
613 percent_saturation=100.0;
614 source_dissolve=1.0;
615 threshold=0.05f;
616 switch (compose)
617 {
618 case ClearCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000619 case DstAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +0000620 case DstInCompositeOp:
621 case InCompositeOp:
622 case OutCompositeOp:
623 case SrcCompositeOp:
624 case SrcInCompositeOp:
625 case SrcOutCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000626 {
627 /*
628 Modify destination outside the overlaid region.
629 */
630 modify_outside_overlay=MagickTrue;
631 break;
632 }
633 case CopyCompositeOp:
634 {
635 if ((x_offset < 0) || (y_offset < 0))
636 break;
637 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
638 break;
639 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
640 break;
641 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000642 image_view=AcquireCacheView(image);
643 composite_view=AcquireCacheView(composite_image);
644#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000645 #pragma omp parallel for schedule(static,4) shared(status)
cristy4c08aed2011-07-01 19:47:50 +0000646#endif
647 for (y=0; y < (ssize_t) composite_image->rows; y++)
648 {
649 MagickBooleanType
650 sync;
651
652 register const Quantum
653 *p;
654
655 register Quantum
656 *q;
657
658 register ssize_t
659 x;
660
661 if (status == MagickFalse)
662 continue;
663 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
664 1,exception);
665 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
666 composite_image->columns,1,exception);
667 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
668 {
669 status=MagickFalse;
670 continue;
671 }
672 for (x=0; x < (ssize_t) composite_image->columns; x++)
673 {
cristybdecccc2011-12-24 22:52:16 +0000674 register ssize_t
675 i;
676
cristy10a6c612012-01-29 21:41:05 +0000677 if (GetPixelMask(image,p) != 0)
678 {
679 p+=GetPixelChannels(composite_image);
680 q+=GetPixelChannels(image);
681 continue;
682 }
cristybdecccc2011-12-24 22:52:16 +0000683 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
684 {
685 PixelChannel
686 channel;
687
688 PixelTrait
689 composite_traits,
690 traits;
691
692 channel=GetPixelChannelMapChannel(composite_image,i);
693 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
694 traits=GetPixelChannelMapTraits(image,channel);
695 if ((traits == UndefinedPixelTrait) ||
696 (composite_traits == UndefinedPixelTrait))
697 continue;
698 SetPixelChannel(image,channel,p[i],q);
699 }
cristyed231572011-07-14 02:18:59 +0000700 p+=GetPixelChannels(composite_image);
701 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000702 }
703 sync=SyncCacheViewAuthenticPixels(image_view,exception);
704 if (sync == MagickFalse)
705 status=MagickFalse;
706 if (image->progress_monitor != (MagickProgressMonitor) NULL)
707 {
708 MagickBooleanType
709 proceed;
710
711#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000712 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000713#endif
714 proceed=SetImageProgress(image,CompositeImageTag,
715 (MagickOffsetType) y,image->rows);
716 if (proceed == MagickFalse)
717 status=MagickFalse;
718 }
719 }
720 composite_view=DestroyCacheView(composite_view);
721 image_view=DestroyCacheView(image_view);
722 return(status);
723 }
cristye4a40472011-12-22 02:56:19 +0000724 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000725 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000726 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000727 {
728 /*
729 Modify destination outside the overlaid region and require an alpha
730 channel to exist, to add transparency.
731 */
732 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +0000733 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000734 modify_outside_overlay=MagickTrue;
735 break;
736 }
737 case BlurCompositeOp:
738 {
739 CacheView
740 *composite_view,
741 *destination_view;
742
743 PixelInfo
744 pixel;
745
746 MagickRealType
747 angle_range,
748 angle_start,
749 height,
750 width;
751
752 ResampleFilter
753 *resample_filter;
754
755 SegmentInfo
756 blur;
757
758 /*
759 Blur Image dictated by an overlay gradient map: X = red_channel;
760 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
761 */
762 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000763 exception);
cristy4c08aed2011-07-01 19:47:50 +0000764 if (destination_image == (Image *) NULL)
765 return(MagickFalse);
766 /*
767 Determine the horizontal and vertical maximim blur.
768 */
769 SetGeometryInfo(&geometry_info);
770 flags=NoValue;
771 value=GetImageArtifact(composite_image,"compose:args");
772 if (value != (char *) NULL)
773 flags=ParseGeometry(value,&geometry_info);
774 if ((flags & WidthValue) == 0 )
775 {
776 destination_image=DestroyImage(destination_image);
777 return(MagickFalse);
778 }
779 width=geometry_info.rho;
780 height=geometry_info.sigma;
781 blur.x1=geometry_info.rho;
782 blur.x2=0.0;
783 blur.y1=0.0;
784 blur.y2=geometry_info.sigma;
785 angle_start=0.0;
786 angle_range=0.0;
787 if ((flags & HeightValue) == 0)
788 blur.y2=blur.x1;
789 if ((flags & XValue) != 0 )
790 {
791 MagickRealType
792 angle;
793
794 angle=DegreesToRadians(geometry_info.xi);
795 blur.x1=width*cos(angle);
796 blur.x2=width*sin(angle);
797 blur.y1=(-height*sin(angle));
798 blur.y2=height*cos(angle);
799 }
800 if ((flags & YValue) != 0 )
801 {
802 angle_start=DegreesToRadians(geometry_info.xi);
803 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
804 }
805 /*
806 Blur Image by resampling.
807 */
cristy8a11cb12011-10-19 23:53:34 +0000808 resample_filter=AcquireResampleFilter(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000809 SetResampleFilter(resample_filter,CubicFilter,2.0);
810 destination_view=AcquireCacheView(destination_image);
811 composite_view=AcquireCacheView(composite_image);
812 for (y=0; y < (ssize_t) composite_image->rows; y++)
813 {
814 MagickBooleanType
815 sync;
816
817 register const Quantum
818 *restrict p;
819
820 register Quantum
821 *restrict q;
822
823 register ssize_t
824 x;
825
826 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
827 continue;
828 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
829 1,exception);
830 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000831 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000832 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
833 break;
834 for (x=0; x < (ssize_t) composite_image->columns; x++)
835 {
836 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
837 {
cristyed231572011-07-14 02:18:59 +0000838 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000839 continue;
840 }
841 if (fabs(angle_range) > MagickEpsilon)
842 {
843 MagickRealType
844 angle;
845
846 angle=angle_start+angle_range*QuantumScale*
847 GetPixelBlue(composite_image,p);
848 blur.x1=width*cos(angle);
849 blur.x2=width*sin(angle);
850 blur.y1=(-height*sin(angle));
851 blur.y2=height*cos(angle);
852 }
853 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
854 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
855 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
856 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
857 GetPixelGreen(composite_image,p));
858 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
859 (double) y_offset+y,&pixel);
cristy803640d2011-11-17 02:11:32 +0000860 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000861 p+=GetPixelChannels(composite_image);
862 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000863 }
864 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
865 if (sync == MagickFalse)
866 break;
867 }
868 resample_filter=DestroyResampleFilter(resample_filter);
869 composite_view=DestroyCacheView(composite_view);
870 destination_view=DestroyCacheView(destination_view);
871 composite_image=destination_image;
872 break;
873 }
874 case DisplaceCompositeOp:
875 case DistortCompositeOp:
876 {
877 CacheView
878 *composite_view,
879 *destination_view,
880 *image_view;
881
882 PixelInfo
883 pixel;
884
885 MagickRealType
886 horizontal_scale,
887 vertical_scale;
888
889 PointInfo
890 center,
891 offset;
892
893 /*
894 Displace/Distort based on overlay gradient map:
895 X = red_channel; Y = green_channel;
896 compose:args = x_scale[,y_scale[,center.x,center.y]]
897 */
898 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000899 exception);
cristy4c08aed2011-07-01 19:47:50 +0000900 if (destination_image == (Image *) NULL)
901 return(MagickFalse);
902 SetGeometryInfo(&geometry_info);
903 flags=NoValue;
904 value=GetImageArtifact(composite_image,"compose:args");
905 if (value != (char *) NULL)
906 flags=ParseGeometry(value,&geometry_info);
907 if ((flags & (WidthValue|HeightValue)) == 0 )
908 {
909 if ((flags & AspectValue) == 0)
910 {
911 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
912 2.0;
913 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
914 }
915 else
916 {
917 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
918 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
919 }
920 }
921 else
922 {
923 horizontal_scale=geometry_info.rho;
924 vertical_scale=geometry_info.sigma;
925 if ((flags & PercentValue) != 0)
926 {
927 if ((flags & AspectValue) == 0)
928 {
929 horizontal_scale*=(composite_image->columns-1.0)/200.0;
930 vertical_scale*=(composite_image->rows-1.0)/200.0;
931 }
932 else
933 {
934 horizontal_scale*=(image->columns-1.0)/200.0;
935 vertical_scale*=(image->rows-1.0)/200.0;
936 }
937 }
938 if ((flags & HeightValue) == 0)
939 vertical_scale=horizontal_scale;
940 }
941 /*
942 Determine fixed center point for absolute distortion map
943 Absolute distort ==
944 Displace offset relative to a fixed absolute point
945 Select that point according to +X+Y user inputs.
946 default = center of overlay image
947 arg flag '!' = locations/percentage relative to background image
948 */
949 center.x=(MagickRealType) x_offset;
950 center.y=(MagickRealType) y_offset;
951 if (compose == DistortCompositeOp)
952 {
953 if ((flags & XValue) == 0)
954 if ((flags & AspectValue) == 0)
955 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
956 2.0;
957 else
958 center.x=((MagickRealType) image->columns-1)/2.0;
959 else
960 if ((flags & AspectValue) == 0)
961 center.x=(MagickRealType) x_offset+geometry_info.xi;
962 else
963 center.x=geometry_info.xi;
964 if ((flags & YValue) == 0)
965 if ((flags & AspectValue) == 0)
966 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
967 else
968 center.y=((MagickRealType) image->rows-1)/2.0;
969 else
970 if ((flags & AspectValue) == 0)
971 center.y=(MagickRealType) y_offset+geometry_info.psi;
972 else
973 center.y=geometry_info.psi;
974 }
975 /*
976 Shift the pixel offset point as defined by the provided,
977 displacement/distortion map. -- Like a lens...
978 */
cristye10859a2011-12-18 22:28:59 +0000979 GetPixelInfo(image,&pixel);
cristy4c08aed2011-07-01 19:47:50 +0000980 image_view=AcquireCacheView(image);
981 destination_view=AcquireCacheView(destination_image);
982 composite_view=AcquireCacheView(composite_image);
983 for (y=0; y < (ssize_t) composite_image->rows; y++)
984 {
985 MagickBooleanType
986 sync;
987
988 register const Quantum
989 *restrict p;
990
991 register Quantum
992 *restrict q;
993
994 register ssize_t
995 x;
996
997 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
998 continue;
999 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1000 1,exception);
1001 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001002 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001003 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1004 break;
1005 for (x=0; x < (ssize_t) composite_image->columns; x++)
1006 {
1007 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1008 {
cristyed231572011-07-14 02:18:59 +00001009 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001010 continue;
1011 }
1012 /*
1013 Displace the offset.
1014 */
1015 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1016 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1017 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1018 x : 0);
1019 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1020 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1021 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1022 y : 0);
1023 (void) InterpolatePixelInfo(image,image_view,
1024 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1025 &pixel,exception);
1026 /*
1027 Mask with the 'invalid pixel mask' in alpha channel.
1028 */
1029 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001030 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001031 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001032 p+=GetPixelChannels(composite_image);
1033 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001034 }
1035 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1036 if (sync == MagickFalse)
1037 break;
1038 }
1039 destination_view=DestroyCacheView(destination_view);
1040 composite_view=DestroyCacheView(composite_view);
1041 image_view=DestroyCacheView(image_view);
1042 composite_image=destination_image;
1043 break;
1044 }
1045 case DissolveCompositeOp:
1046 {
1047 /*
1048 Geometry arguments to dissolve factors.
1049 */
1050 value=GetImageArtifact(composite_image,"compose:args");
1051 if (value != (char *) NULL)
1052 {
1053 flags=ParseGeometry(value,&geometry_info);
1054 source_dissolve=geometry_info.rho/100.0;
1055 destination_dissolve=1.0;
1056 if ((source_dissolve-MagickEpsilon) < 0.0)
1057 source_dissolve=0.0;
1058 if ((source_dissolve+MagickEpsilon) > 1.0)
1059 {
1060 destination_dissolve=2.0-source_dissolve;
1061 source_dissolve=1.0;
1062 }
1063 if ((flags & SigmaValue) != 0)
1064 destination_dissolve=geometry_info.sigma/100.0;
1065 if ((destination_dissolve-MagickEpsilon) < 0.0)
1066 destination_dissolve=0.0;
1067 modify_outside_overlay=MagickTrue;
1068 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1069 {
1070 destination_dissolve=1.0;
1071 modify_outside_overlay=MagickFalse;
1072 }
1073 }
1074 break;
1075 }
1076 case BlendCompositeOp:
1077 {
1078 value=GetImageArtifact(composite_image,"compose:args");
1079 if (value != (char *) NULL)
1080 {
1081 flags=ParseGeometry(value,&geometry_info);
1082 source_dissolve=geometry_info.rho/100.0;
1083 destination_dissolve=1.0-source_dissolve;
1084 if ((flags & SigmaValue) != 0)
1085 destination_dissolve=geometry_info.sigma/100.0;
1086 modify_outside_overlay=MagickTrue;
1087 if ((destination_dissolve+MagickEpsilon) > 1.0)
1088 modify_outside_overlay=MagickFalse;
1089 }
1090 break;
1091 }
1092 case MathematicsCompositeOp:
1093 {
1094 /*
1095 Just collect the values from "compose:args", setting.
1096 Unused values are set to zero automagically.
1097
1098 Arguments are normally a comma separated list, so this probably should
1099 be changed to some 'general comma list' parser, (with a minimum
1100 number of values)
1101 */
1102 SetGeometryInfo(&geometry_info);
1103 value=GetImageArtifact(composite_image,"compose:args");
1104 if (value != (char *) NULL)
1105 (void) ParseGeometry(value,&geometry_info);
1106 break;
1107 }
1108 case ModulateCompositeOp:
1109 {
1110 /*
1111 Determine the brightness and saturation scale.
1112 */
1113 value=GetImageArtifact(composite_image,"compose:args");
1114 if (value != (char *) NULL)
1115 {
1116 flags=ParseGeometry(value,&geometry_info);
1117 percent_brightness=geometry_info.rho;
1118 if ((flags & SigmaValue) != 0)
1119 percent_saturation=geometry_info.sigma;
1120 }
1121 break;
1122 }
1123 case ThresholdCompositeOp:
1124 {
1125 /*
1126 Determine the amount and threshold.
1127 */
1128 value=GetImageArtifact(composite_image,"compose:args");
1129 if (value != (char *) NULL)
1130 {
1131 flags=ParseGeometry(value,&geometry_info);
1132 amount=geometry_info.rho;
1133 threshold=geometry_info.sigma;
1134 if ((flags & SigmaValue) == 0)
1135 threshold=0.05f;
1136 }
1137 threshold*=QuantumRange;
1138 break;
1139 }
1140 default:
1141 break;
1142 }
1143 value=GetImageArtifact(composite_image,"compose:outside-overlay");
1144 if (value != (const char *) NULL)
1145 modify_outside_overlay=IsMagickTrue(value);
1146 /*
1147 Composite image.
1148 */
1149 status=MagickTrue;
1150 progress=0;
1151 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy4c08aed2011-07-01 19:47:50 +00001152 image_view=AcquireCacheView(image);
1153 composite_view=AcquireCacheView(composite_image);
1154#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00001155 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy4c08aed2011-07-01 19:47:50 +00001156#endif
1157 for (y=0; y < (ssize_t) image->rows; y++)
1158 {
1159 const Quantum
1160 *pixels;
1161
1162 double
cristye4a40472011-12-22 02:56:19 +00001163 blue,
cristy4c08aed2011-07-01 19:47:50 +00001164 brightness,
cristye4a40472011-12-22 02:56:19 +00001165 green,
cristy4c08aed2011-07-01 19:47:50 +00001166 hue,
cristye4a40472011-12-22 02:56:19 +00001167 red,
cristy4c08aed2011-07-01 19:47:50 +00001168 saturation;
1169
cristy4c08aed2011-07-01 19:47:50 +00001170 register const Quantum
1171 *restrict p;
1172
1173 register Quantum
1174 *restrict q;
1175
1176 register ssize_t
1177 x;
1178
1179 if (status == MagickFalse)
1180 continue;
1181 if (modify_outside_overlay == MagickFalse)
1182 {
1183 if (y < y_offset)
1184 continue;
1185 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1186 continue;
1187 }
1188 /*
1189 If pixels is NULL, y is outside overlay region.
1190 */
1191 pixels=(Quantum *) NULL;
1192 p=(Quantum *) NULL;
1193 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1194 {
1195 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1196 composite_image->columns,1,exception);
1197 if (p == (const Quantum *) NULL)
1198 {
1199 status=MagickFalse;
1200 continue;
1201 }
1202 pixels=p;
1203 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001204 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001205 }
1206 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001207 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001208 {
1209 status=MagickFalse;
1210 continue;
1211 }
cristy4c08aed2011-07-01 19:47:50 +00001212 hue=0.0;
1213 saturation=0.0;
1214 brightness=0.0;
1215 for (x=0; x < (ssize_t) image->columns; x++)
1216 {
cristye4a40472011-12-22 02:56:19 +00001217 MagickRealType
1218 alpha,
1219 Da,
1220 Dc,
1221 Dca,
1222 gamma,
1223 Sa,
1224 Sc,
1225 Sca;
1226
1227 register ssize_t
1228 i;
1229
cristy564a5692012-01-20 23:56:26 +00001230 size_t
1231 channels;
1232
cristy4c08aed2011-07-01 19:47:50 +00001233 if (modify_outside_overlay == MagickFalse)
1234 {
1235 if (x < x_offset)
1236 {
cristyed231572011-07-14 02:18:59 +00001237 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001238 continue;
1239 }
1240 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1241 break;
1242 }
cristye4a40472011-12-22 02:56:19 +00001243 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1244 ((x-x_offset) >= (ssize_t) composite_image->columns))
1245 {
1246 Quantum
1247 source[MaxPixelChannels];
1248
1249 /*
1250 Virtual composite:
1251 Sc: source color.
1252 Dc: destination color.
1253 */
1254 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1255 source,exception);
cristy10a6c612012-01-29 21:41:05 +00001256 if (GetPixelMask(image,q) != 0)
1257 {
1258 q+=GetPixelChannels(image);
1259 continue;
1260 }
cristye4a40472011-12-22 02:56:19 +00001261 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1262 {
1263 MagickRealType
1264 pixel;
1265
1266 PixelChannel
1267 channel;
1268
1269 PixelTrait
1270 composite_traits,
1271 traits;
1272
1273 channel=GetPixelChannelMapChannel(image,i);
1274 traits=GetPixelChannelMapTraits(image,channel);
cristyd09f8802012-02-04 16:44:10 +00001275 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristye4a40472011-12-22 02:56:19 +00001276 if ((traits == UndefinedPixelTrait) ||
1277 (composite_traits == UndefinedPixelTrait))
1278 continue;
1279 switch (compose)
1280 {
cristyc8d63672012-01-11 13:03:13 +00001281 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001282 case ChangeMaskCompositeOp:
1283 case CopyAlphaCompositeOp:
1284 case DstAtopCompositeOp:
1285 case DstInCompositeOp:
1286 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001287 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001288 case OutCompositeOp:
1289 case SrcInCompositeOp:
1290 case SrcOutCompositeOp:
1291 {
1292 pixel=(MagickRealType) q[i];
1293 if (channel == AlphaPixelChannel)
1294 pixel=(MagickRealType) TransparentAlpha;
1295 break;
1296 }
1297 case ClearCompositeOp:
1298 case CopyCompositeOp:
1299 case ReplaceCompositeOp:
1300 case SrcCompositeOp:
1301 {
1302 if (channel == AlphaPixelChannel)
1303 {
1304 pixel=(MagickRealType) TransparentAlpha;
1305 break;
1306 }
1307 pixel=0.0;
1308 break;
1309 }
cristy99abff32011-12-24 20:45:16 +00001310 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001311 case DissolveCompositeOp:
1312 {
1313 if (channel == AlphaPixelChannel)
1314 {
1315 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1316 source);
1317 break;
1318 }
1319 pixel=(MagickRealType) source[channel];
1320 break;
1321 }
1322 default:
1323 {
1324 pixel=(MagickRealType) source[channel];
1325 break;
1326 }
1327 }
1328 q[i]=ClampToQuantum(pixel);
1329 }
1330 q+=GetPixelChannels(image);
1331 continue;
1332 }
1333 /*
1334 Authentic composite:
1335 Sa: normalized source alpha.
1336 Da: normalized destination alpha.
1337 */
1338 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1339 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001340 switch (compose)
1341 {
cristye4a40472011-12-22 02:56:19 +00001342 case BumpmapCompositeOp:
1343 {
1344 alpha=GetPixelIntensity(composite_image,p)*Sa;
1345 break;
1346 }
cristycdc168f2011-12-21 15:24:39 +00001347 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001348 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001349 case DifferenceCompositeOp:
1350 case DivideDstCompositeOp:
1351 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001352 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001353 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001354 case LinearBurnCompositeOp:
1355 case LinearDodgeCompositeOp:
1356 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001357 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001358 case MinusDstCompositeOp:
1359 case MinusSrcCompositeOp:
1360 case ModulusAddCompositeOp:
1361 case ModulusSubtractCompositeOp:
1362 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001363 case OverlayCompositeOp:
1364 case PegtopLightCompositeOp:
1365 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001366 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001367 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001368 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001369 {
1370 alpha=RoundToUnity(Sa+Da-Sa*Da);
1371 break;
1372 }
1373 case DarkenCompositeOp:
1374 case DstAtopCompositeOp:
1375 case DstInCompositeOp:
1376 case InCompositeOp:
1377 case LightenCompositeOp:
1378 case SrcInCompositeOp:
1379 {
1380 alpha=Sa*Da;
1381 break;
1382 }
1383 case DissolveCompositeOp:
1384 {
1385 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1386 Sa+destination_dissolve*Da;
1387 break;
1388 }
1389 case DstOverCompositeOp:
1390 {
1391 alpha=Da*(-Sa)+Da+Sa;
1392 break;
1393 }
1394 case DstOutCompositeOp:
1395 {
1396 alpha=Da*(1.0-Sa);
1397 break;
1398 }
1399 case OutCompositeOp:
1400 case SrcOutCompositeOp:
1401 {
1402 alpha=Sa*(1.0-Da);
1403 break;
1404 }
1405 case OverCompositeOp:
1406 case SrcOverCompositeOp:
1407 {
1408 alpha=Sa*(-Da)+Sa+Da;
1409 break;
1410 }
cristy99abff32011-12-24 20:45:16 +00001411 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001412 case PlusCompositeOp:
1413 {
1414 alpha=RoundToUnity(Sa+Da);
1415 break;
1416 }
cristy4c08aed2011-07-01 19:47:50 +00001417 case XorCompositeOp:
1418 {
cristye4a40472011-12-22 02:56:19 +00001419 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001420 break;
1421 }
1422 default:
cristye4a40472011-12-22 02:56:19 +00001423 {
1424 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001425 break;
cristye4a40472011-12-22 02:56:19 +00001426 }
cristy4c08aed2011-07-01 19:47:50 +00001427 }
cristy10a6c612012-01-29 21:41:05 +00001428 if (GetPixelMask(image,p) != 0)
1429 {
1430 p+=GetPixelChannels(composite_image);
1431 q+=GetPixelChannels(image);
1432 continue;
1433 }
cristye4a40472011-12-22 02:56:19 +00001434 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1435 {
cristy564a5692012-01-20 23:56:26 +00001436 double
1437 sans;
1438
cristye10859a2011-12-18 22:28:59 +00001439 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001440 pixel;
cristye10859a2011-12-18 22:28:59 +00001441
cristye4a40472011-12-22 02:56:19 +00001442 PixelChannel
1443 channel;
cristye10859a2011-12-18 22:28:59 +00001444
cristye4a40472011-12-22 02:56:19 +00001445 PixelTrait
1446 composite_traits,
1447 traits;
1448
1449 channel=GetPixelChannelMapChannel(image,i);
1450 traits=GetPixelChannelMapTraits(image,channel);
1451 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001452 if (traits == UndefinedPixelTrait)
1453 continue;
cristya7b07912012-01-11 20:01:32 +00001454 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001455 (composite_traits == UndefinedPixelTrait))
1456 continue;
1457 /*
1458 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001459 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001460 */
1461 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
cristye4a40472011-12-22 02:56:19 +00001462 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001463 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001464 {
cristye4a40472011-12-22 02:56:19 +00001465 if (channel != AlphaPixelChannel)
1466 {
1467 /*
1468 Copy channel.
1469 */
1470 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001471 continue;
cristye10859a2011-12-18 22:28:59 +00001472 }
cristye4a40472011-12-22 02:56:19 +00001473 /*
1474 Set alpha channel.
1475 */
cristye10859a2011-12-18 22:28:59 +00001476 switch (compose)
1477 {
cristyc8d63672012-01-11 13:03:13 +00001478 case AlphaCompositeOp:
1479 {
cristya7b07912012-01-11 20:01:32 +00001480 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001481 break;
1482 }
cristye4a40472011-12-22 02:56:19 +00001483 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001484 case CopyBlackCompositeOp:
1485 case CopyBlueCompositeOp:
1486 case CopyCyanCompositeOp:
1487 case CopyGreenCompositeOp:
1488 case CopyMagentaCompositeOp:
1489 case CopyRedCompositeOp:
1490 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001491 case SrcAtopCompositeOp:
1492 case DstCompositeOp:
1493 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001494 {
cristye4a40472011-12-22 02:56:19 +00001495 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001496 break;
1497 }
cristye10859a2011-12-18 22:28:59 +00001498 case ChangeMaskCompositeOp:
1499 {
cristye4a40472011-12-22 02:56:19 +00001500 MagickBooleanType
1501 equivalent;
1502
cristy99abff32011-12-24 20:45:16 +00001503 if (Da > ((MagickRealType) QuantumRange/2.0))
1504 {
1505 pixel=(MagickRealType) TransparentAlpha;
1506 break;
1507 }
cristye4a40472011-12-22 02:56:19 +00001508 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001509 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001510 {
1511 pixel=(MagickRealType) TransparentAlpha;
1512 break;
1513 }
1514 pixel=(MagickRealType) OpaqueAlpha;
1515 break;
1516 }
cristy99abff32011-12-24 20:45:16 +00001517 case ClearCompositeOp:
1518 {
1519 pixel=(MagickRealType) TransparentAlpha;
1520 break;
1521 }
1522 case ColorizeCompositeOp:
1523 case HueCompositeOp:
1524 case LuminizeCompositeOp:
1525 case SaturateCompositeOp:
1526 {
1527 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1528 {
1529 pixel=QuantumRange*Da;
1530 break;
1531 }
1532 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1533 {
1534 pixel=QuantumRange*Sa;
1535 break;
1536 }
1537 if (Sa < Da)
1538 {
1539 pixel=QuantumRange*Da;
1540 break;
1541 }
1542 pixel=QuantumRange*Sa;
1543 break;
1544 }
cristye4a40472011-12-22 02:56:19 +00001545 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001546 case CopyAlphaCompositeOp:
1547 case DisplaceCompositeOp:
1548 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001549 case DstAtopCompositeOp:
1550 case ReplaceCompositeOp:
1551 case SrcCompositeOp:
1552 {
1553 pixel=QuantumRange*Sa;
1554 break;
1555 }
1556 case DarkenIntensityCompositeOp:
1557 {
cristy99abff32011-12-24 20:45:16 +00001558 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1559 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001560 break;
1561 }
cristy98621462011-12-31 22:31:11 +00001562 case IntensityCompositeOp:
1563 {
cristy20d5f622012-01-11 13:04:26 +00001564 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001565 break;
1566 }
cristye4a40472011-12-22 02:56:19 +00001567 case LightenIntensityCompositeOp:
1568 {
1569 pixel=Sa*GetPixelIntensity(composite_image,p) >
1570 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001571 break;
1572 }
cristy99abff32011-12-24 20:45:16 +00001573 case ModulateCompositeOp:
1574 {
1575 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1576 {
1577 pixel=QuantumRange*Da;
1578 break;
1579 }
1580 pixel=QuantumRange*Da;
1581 break;
1582 }
cristye10859a2011-12-18 22:28:59 +00001583 default:
1584 {
cristye4a40472011-12-22 02:56:19 +00001585 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001586 break;
1587 }
1588 }
cristye4a40472011-12-22 02:56:19 +00001589 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001590 continue;
1591 }
1592 /*
cristy99abff32011-12-24 20:45:16 +00001593 Porter-Duff compositions:
1594 Sca: source normalized color multiplied by alpha.
1595 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001596 */
cristy99abff32011-12-24 20:45:16 +00001597 Sca=QuantumScale*Sa*Sc;
1598 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001599 switch (compose)
1600 {
cristye10859a2011-12-18 22:28:59 +00001601 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001602 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001603 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001604 {
cristy99abff32011-12-24 20:45:16 +00001605 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001606 break;
1607 }
1608 default:
1609 break;
1610 }
cristye4a40472011-12-22 02:56:19 +00001611 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
cristyd197cbb2012-01-13 02:14:12 +00001612 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001613 switch (compose)
1614 {
cristya7b07912012-01-11 20:01:32 +00001615 case AlphaCompositeOp:
1616 {
1617 pixel=QuantumRange*Sa;
1618 break;
1619 }
cristye4a40472011-12-22 02:56:19 +00001620 case AtopCompositeOp:
1621 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001622 {
cristye4a40472011-12-22 02:56:19 +00001623 pixel=Sc*Sa+Dc*(1.0-Sa);
1624 break;
cristye10859a2011-12-18 22:28:59 +00001625 }
cristye4a40472011-12-22 02:56:19 +00001626 case BlendCompositeOp:
1627 {
1628 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1629 break;
1630 }
1631 case BlurCompositeOp:
1632 case DisplaceCompositeOp:
1633 case DistortCompositeOp:
1634 case CopyCompositeOp:
1635 case ReplaceCompositeOp:
1636 case SrcCompositeOp:
1637 {
1638 pixel=Sc;
1639 break;
1640 }
1641 case BumpmapCompositeOp:
1642 {
cristy99abff32011-12-24 20:45:16 +00001643 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001644 {
1645 pixel=Dc;
1646 break;
1647 }
1648 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1649 break;
1650 }
cristy99abff32011-12-24 20:45:16 +00001651 case ChangeMaskCompositeOp:
1652 {
1653 pixel=Dc;
1654 break;
1655 }
1656 case ClearCompositeOp:
1657 {
1658 pixel=0.0;
1659 break;
1660 }
cristye4a40472011-12-22 02:56:19 +00001661 case ColorBurnCompositeOp:
1662 {
1663 /*
1664 Refer to the March 2009 SVG specification.
1665 */
1666 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1667 {
cristy99abff32011-12-24 20:45:16 +00001668 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001669 break;
1670 }
1671 if (Sca < MagickEpsilon)
1672 {
cristy99abff32011-12-24 20:45:16 +00001673 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001674 break;
1675 }
cristy99abff32011-12-24 20:45:16 +00001676 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1677 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001678 break;
1679 }
1680 case ColorDodgeCompositeOp:
1681 {
1682 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1683 {
cristy99abff32011-12-24 20:45:16 +00001684 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001685 break;
1686 }
1687 if (fabs(Sca-Sa) < MagickEpsilon)
1688 {
cristy99abff32011-12-24 20:45:16 +00001689 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001690 break;
1691 }
cristy99abff32011-12-24 20:45:16 +00001692 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001693 (1.0-Sa));
1694 break;
1695 }
1696 case ColorizeCompositeOp:
1697 {
cristy99abff32011-12-24 20:45:16 +00001698 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001699 {
1700 pixel=Dc;
1701 break;
1702 }
cristy99abff32011-12-24 20:45:16 +00001703 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001704 {
1705 pixel=Sc;
1706 break;
1707 }
1708 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
cristy99abff32011-12-24 20:45:16 +00001709 GetPixelBlue(image,q),&sans,&sans,&brightness);
cristye4a40472011-12-22 02:56:19 +00001710 CompositeHSB(GetPixelRed(composite_image,p),
1711 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1712 &hue,&saturation,&sans);
1713 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1714 switch (channel)
1715 {
1716 case RedPixelChannel: pixel=red; break;
1717 case GreenPixelChannel: pixel=green; break;
1718 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001719 default: pixel=Dc; break;
1720 }
1721 break;
1722 }
cristye4a40472011-12-22 02:56:19 +00001723 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001724 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001725 {
cristyd197cbb2012-01-13 02:14:12 +00001726 if (channel == AlphaPixelChannel)
1727 pixel=(MagickRealType) GetPixelAlpha(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001728 break;
1729 }
1730 case CopyBlackCompositeOp:
1731 {
cristyd197cbb2012-01-13 02:14:12 +00001732 if (channel == BlackPixelChannel)
1733 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001734 break;
1735 }
1736 case CopyBlueCompositeOp:
1737 case CopyYellowCompositeOp:
1738 {
cristyd197cbb2012-01-13 02:14:12 +00001739 if (channel == BluePixelChannel)
1740 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001741 break;
1742 }
1743 case CopyGreenCompositeOp:
1744 case CopyMagentaCompositeOp:
1745 {
cristyd197cbb2012-01-13 02:14:12 +00001746 if (channel == GreenPixelChannel)
1747 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001748 break;
1749 }
1750 case CopyRedCompositeOp:
1751 case CopyCyanCompositeOp:
1752 {
cristyd197cbb2012-01-13 02:14:12 +00001753 if (channel == RedPixelChannel)
1754 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001755 break;
1756 }
cristy99abff32011-12-24 20:45:16 +00001757 case DarkenCompositeOp:
1758 {
1759 /*
1760 Darken is equivalent to a 'Minimum' method
1761 OR a greyscale version of a binary 'Or'
1762 OR the 'Intersection' of pixel sets.
1763 */
1764 if (Sc < Dc)
1765 {
1766 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1767 break;
1768 }
1769 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1770 break;
1771 }
cristye4a40472011-12-22 02:56:19 +00001772 case DarkenIntensityCompositeOp:
1773 {
cristy99abff32011-12-24 20:45:16 +00001774 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1775 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001776 break;
1777 }
1778 case DifferenceCompositeOp:
1779 {
1780 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1781 break;
1782 }
1783 case DissolveCompositeOp:
1784 {
1785 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1786 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1787 break;
1788 }
1789 case DivideDstCompositeOp:
1790 {
1791 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1792 {
cristy99abff32011-12-24 20:45:16 +00001793 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001794 break;
1795 }
1796 if (fabs(Dca) < MagickEpsilon)
1797 {
cristy99abff32011-12-24 20:45:16 +00001798 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001799 break;
1800 }
cristy99abff32011-12-24 20:45:16 +00001801 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001802 break;
1803 }
1804 case DivideSrcCompositeOp:
1805 {
1806 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1807 {
cristy99abff32011-12-24 20:45:16 +00001808 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001809 break;
1810 }
1811 if (fabs(Sca) < MagickEpsilon)
1812 {
cristy99abff32011-12-24 20:45:16 +00001813 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001814 break;
1815 }
cristy99abff32011-12-24 20:45:16 +00001816 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001817 break;
1818 }
1819 case DstAtopCompositeOp:
1820 {
1821 pixel=Dc*Da+Sc*(1.0-Da);
1822 break;
1823 }
1824 case DstCompositeOp:
1825 case NoCompositeOp:
1826 {
1827 pixel=Dc;
1828 break;
1829 }
1830 case DstInCompositeOp:
1831 {
1832 pixel=gamma*(Sa*Dc*Sa);
1833 break;
1834 }
1835 case DstOutCompositeOp:
1836 {
1837 pixel=gamma*(Da*Dc*(1.0-Sa));
1838 break;
1839 }
1840 case DstOverCompositeOp:
1841 {
1842 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1843 break;
1844 }
1845 case ExclusionCompositeOp:
1846 {
cristy99abff32011-12-24 20:45:16 +00001847 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1848 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001849 break;
1850 }
1851 case HardLightCompositeOp:
1852 {
1853 if ((2.0*Sca) < Sa)
1854 {
cristy99abff32011-12-24 20:45:16 +00001855 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001856 (1.0-Sa));
1857 break;
1858 }
cristy99abff32011-12-24 20:45:16 +00001859 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001860 Dca*(1.0-Sa));
1861 break;
1862 }
1863 case HueCompositeOp:
1864 {
cristy99abff32011-12-24 20:45:16 +00001865 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001866 {
1867 pixel=Dc;
1868 break;
1869 }
cristy99abff32011-12-24 20:45:16 +00001870 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001871 {
1872 pixel=Sc;
1873 break;
1874 }
1875 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1876 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1877 CompositeHSB(GetPixelRed(composite_image,p),
1878 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1879 &hue,&sans,&sans);
1880 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1881 switch (channel)
1882 {
1883 case RedPixelChannel: pixel=red; break;
1884 case GreenPixelChannel: pixel=green; break;
1885 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001886 default: pixel=Dc; break;
1887 }
1888 break;
1889 }
1890 case InCompositeOp:
1891 case SrcInCompositeOp:
1892 {
1893 pixel=gamma*(Da*Sc*Da);
1894 break;
1895 }
cristy99abff32011-12-24 20:45:16 +00001896 case LinearBurnCompositeOp:
1897 {
1898 /*
1899 LinearBurn: as defined by Abode Photoshop, according to
1900 http://www.simplefilter.de/en/basics/mixmods.html is:
1901
1902 f(Sc,Dc) = Sc + Dc - 1
1903 */
1904 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1905 break;
1906 }
1907 case LinearDodgeCompositeOp:
1908 {
1909 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1910 break;
1911 }
1912 case LinearLightCompositeOp:
1913 {
1914 /*
1915 LinearLight: as defined by Abode Photoshop, according to
1916 http://www.simplefilter.de/en/basics/mixmods.html is:
1917
1918 f(Sc,Dc) = Dc + 2*Sc - 1
1919 */
1920 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1921 break;
1922 }
1923 case LightenCompositeOp:
1924 {
1925 if (Sc > Dc)
1926 {
1927 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1928 break;
1929 }
1930 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1931 break;
1932 }
cristye4a40472011-12-22 02:56:19 +00001933 case LightenIntensityCompositeOp:
1934 {
1935 /*
1936 Lighten is equivalent to a 'Maximum' method
1937 OR a greyscale version of a binary 'And'
1938 OR the 'Union' of pixel sets.
1939 */
1940 pixel=Sa*GetPixelIntensity(composite_image,p) >
1941 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1942 break;
1943 }
cristye4a40472011-12-22 02:56:19 +00001944 case LuminizeCompositeOp:
1945 {
cristy99abff32011-12-24 20:45:16 +00001946 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001947 {
1948 pixel=Dc;
1949 break;
1950 }
cristy99abff32011-12-24 20:45:16 +00001951 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001952 {
1953 pixel=Sc;
1954 break;
1955 }
1956 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1957 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1958 CompositeHSB(GetPixelRed(composite_image,p),
1959 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1960 &sans,&sans,&brightness);
1961 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1962 switch (channel)
1963 {
1964 case RedPixelChannel: pixel=red; break;
1965 case GreenPixelChannel: pixel=green; break;
1966 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001967 default: pixel=Dc; break;
1968 }
1969 break;
1970 }
1971 case MathematicsCompositeOp:
1972 {
1973 /*
1974 'Mathematics' a free form user control mathematical composition
1975 is defined as...
1976
1977 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
1978
1979 Where the arguments A,B,C,D are (currently) passed to composite
1980 as a command separated 'geometry' string in "compose:args" image
1981 artifact.
1982
1983 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
1984
1985 Applying the SVG transparency formula (see above), we get...
1986
1987 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
1988
1989 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
1990 Dca*(1.0-Sa)
1991 */
1992 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
1993 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
1994 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
1995 break;
1996 }
1997 case MinusDstCompositeOp:
1998 {
1999 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2000 break;
2001 }
2002 case MinusSrcCompositeOp:
2003 {
2004 /*
2005 Minus source from destination.
2006
2007 f(Sc,Dc) = Sc - Dc
2008 */
cristy99abff32011-12-24 20:45:16 +00002009 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002010 break;
2011 }
2012 case ModulateCompositeOp:
2013 {
2014 ssize_t
2015 offset;
2016
cristy99abff32011-12-24 20:45:16 +00002017 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002018 {
2019 pixel=Dc;
2020 break;
2021 }
2022 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2023 if (offset == 0)
2024 {
2025 pixel=Dc;
2026 break;
2027 }
2028 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2029 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2030 brightness+=(0.01*percent_brightness*offset)/midpoint;
2031 saturation*=0.01*percent_saturation;
2032 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2033 switch (channel)
2034 {
2035 case RedPixelChannel: pixel=red; break;
2036 case GreenPixelChannel: pixel=green; break;
2037 case BluePixelChannel: pixel=blue; break;
2038 default: pixel=Dc; break;
2039 }
2040 break;
2041 }
2042 case ModulusAddCompositeOp:
2043 {
2044 pixel=Sc+Dc;
2045 if (pixel > QuantumRange)
2046 pixel-=(QuantumRange+1.0);
2047 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2048 break;
2049 }
2050 case ModulusSubtractCompositeOp:
2051 {
cristy99abff32011-12-24 20:45:16 +00002052 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002053 if (pixel < 0.0)
2054 pixel+=(QuantumRange+1.0);
2055 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2056 break;
2057 }
2058 case MultiplyCompositeOp:
2059 {
cristy99abff32011-12-24 20:45:16 +00002060 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002061 break;
2062 }
2063 case OutCompositeOp:
2064 case SrcOutCompositeOp:
2065 {
2066 pixel=gamma*(Sa*Sc*(1.0-Da));
2067 break;
2068 }
2069 case OverCompositeOp:
2070 case SrcOverCompositeOp:
2071 {
cristy99abff32011-12-24 20:45:16 +00002072 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002073 break;
2074 }
2075 case OverlayCompositeOp:
2076 {
2077 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002078 {
2079 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2080 (1.0-Da));
2081 break;
2082 }
2083 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2084 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002085 break;
2086 }
2087 case PegtopLightCompositeOp:
2088 {
2089 /*
2090 PegTop: A Soft-Light alternative: A continuous version of the
2091 Softlight function, producing very similar results.
2092
2093 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2094
2095 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2096 */
2097 if (fabs(Da) < MagickEpsilon)
2098 {
cristy99abff32011-12-24 20:45:16 +00002099 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002100 break;
2101 }
cristy99abff32011-12-24 20:45:16 +00002102 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2103 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002104 break;
2105 }
2106 case PinLightCompositeOp:
2107 {
2108 /*
2109 PinLight: A Photoshop 7 composition method
2110 http://www.simplefilter.de/en/basics/mixmods.html
2111
2112 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2113 */
2114 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2115 {
cristy99abff32011-12-24 20:45:16 +00002116 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002117 break;
2118 }
2119 if ((Dca*Sa) > (2.0*Sca*Da))
2120 {
cristy99abff32011-12-24 20:45:16 +00002121 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002122 break;
2123 }
cristy99abff32011-12-24 20:45:16 +00002124 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002125 break;
2126 }
2127 case PlusCompositeOp:
2128 {
cristy99abff32011-12-24 20:45:16 +00002129 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002130 break;
2131 }
2132 case SaturateCompositeOp:
2133 {
cristy99abff32011-12-24 20:45:16 +00002134 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002135 {
2136 pixel=Dc;
2137 break;
2138 }
cristy99abff32011-12-24 20:45:16 +00002139 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002140 {
2141 pixel=Sc;
2142 break;
2143 }
2144 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2145 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2146 CompositeHSB(GetPixelRed(composite_image,p),
2147 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
2148 &sans,&saturation,&sans);
2149 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2150 switch (channel)
2151 {
2152 case RedPixelChannel: pixel=red; break;
2153 case GreenPixelChannel: pixel=green; break;
2154 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002155 default: pixel=Dc; break;
2156 }
2157 break;
2158 }
2159 case ScreenCompositeOp:
2160 {
2161 /*
2162 Screen: a negated multiply:
2163
2164 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2165 */
cristy99abff32011-12-24 20:45:16 +00002166 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002167 break;
2168 }
2169 case SoftLightCompositeOp:
2170 {
2171 /*
2172 Refer to the March 2009 SVG specification.
2173 */
2174 if ((2.0*Sca) < Sa)
2175 {
cristy99abff32011-12-24 20:45:16 +00002176 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2177 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002178 break;
2179 }
2180 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2181 {
cristy99abff32011-12-24 20:45:16 +00002182 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2183 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2184 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002185 break;
2186 }
cristy99abff32011-12-24 20:45:16 +00002187 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2188 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002189 break;
2190 }
2191 case ThresholdCompositeOp:
2192 {
2193 MagickRealType
2194 delta;
2195
2196 delta=Sc-Dc;
2197 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2198 {
2199 pixel=gamma*Dc;
2200 break;
2201 }
2202 pixel=gamma*(Dc+delta*amount);
2203 break;
2204 }
2205 case VividLightCompositeOp:
2206 {
2207 /*
2208 VividLight: A Photoshop 7 composition method. See
2209 http://www.simplefilter.de/en/basics/mixmods.html.
2210
2211 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2212 */
2213 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2214 {
cristy99abff32011-12-24 20:45:16 +00002215 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002216 break;
2217 }
2218 if ((2.0*Sca) <= Sa)
2219 {
cristy99abff32011-12-24 20:45:16 +00002220 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2221 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002222 break;
2223 }
cristy99abff32011-12-24 20:45:16 +00002224 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2225 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002226 break;
2227 }
2228 case XorCompositeOp:
2229 {
cristy99abff32011-12-24 20:45:16 +00002230 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002231 break;
2232 }
2233 default:
2234 {
2235 pixel=Sc;
2236 break;
2237 }
2238 }
2239 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002240 }
cristyed231572011-07-14 02:18:59 +00002241 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002242 channels=GetPixelChannels(composite_image);
2243 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002244 p=pixels;
cristyed231572011-07-14 02:18:59 +00002245 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002246 }
2247 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2248 status=MagickFalse;
2249 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2250 {
2251 MagickBooleanType
2252 proceed;
2253
2254#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002255 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002256#endif
2257 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2258 image->rows);
2259 if (proceed == MagickFalse)
2260 status=MagickFalse;
2261 }
2262 }
2263 composite_view=DestroyCacheView(composite_view);
2264 image_view=DestroyCacheView(image_view);
2265 if (destination_image != (Image * ) NULL)
2266 destination_image=DestroyImage(destination_image);
2267 return(status);
2268}
2269
2270/*
2271%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2272% %
2273% %
2274% %
2275% T e x t u r e I m a g e %
2276% %
2277% %
2278% %
2279%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2280%
2281% TextureImage() repeatedly tiles the texture image across and down the image
2282% canvas.
2283%
2284% The format of the TextureImage method is:
2285%
cristye6178502011-12-23 17:02:29 +00002286% MagickBooleanType TextureImage(Image *image,const Image *texture_image,
cristye941a752011-10-15 01:52:48 +00002287% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002288%
2289% A description of each parameter follows:
2290%
2291% o image: the image.
2292%
cristye6178502011-12-23 17:02:29 +00002293% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002294%
2295*/
cristye6178502011-12-23 17:02:29 +00002296MagickExport MagickBooleanType TextureImage(Image *image,
2297 const Image *texture_image,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002298{
2299#define TextureImageTag "Texture/Image"
2300
2301 CacheView
2302 *image_view,
2303 *texture_view;
2304
cristy4c08aed2011-07-01 19:47:50 +00002305 MagickBooleanType
2306 status;
2307
2308 ssize_t
2309 y;
2310
2311 assert(image != (Image *) NULL);
2312 if (image->debug != MagickFalse)
2313 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2314 assert(image->signature == MagickSignature);
cristye6178502011-12-23 17:02:29 +00002315 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002316 return(MagickFalse);
cristye6178502011-12-23 17:02:29 +00002317 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod);
cristy574cc262011-08-05 01:23:58 +00002318 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00002319 return(MagickFalse);
2320 status=MagickTrue;
2321 if ((image->compose != CopyCompositeOp) &&
2322 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002323 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002324 {
2325 /*
2326 Tile texture onto the image background.
2327 */
2328#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy19593872012-01-22 02:00:33 +00002329 #pragma omp parallel for schedule(static) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002330#endif
cristye6178502011-12-23 17:02:29 +00002331 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002332 {
2333 register ssize_t
2334 x;
2335
2336 if (status == MagickFalse)
2337 continue;
cristye6178502011-12-23 17:02:29 +00002338 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002339 {
2340 MagickBooleanType
2341 thread_status;
2342
cristye6178502011-12-23 17:02:29 +00002343 thread_status=CompositeImage(image,image->compose,texture_image,x+
2344 texture_image->tile_offset.x,y+texture_image->tile_offset.y,
2345 exception);
cristy4c08aed2011-07-01 19:47:50 +00002346 if (thread_status == MagickFalse)
2347 {
2348 status=thread_status;
2349 break;
2350 }
2351 }
2352 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2353 {
2354 MagickBooleanType
2355 proceed;
2356
2357#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002358 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002359#endif
2360 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2361 y,image->rows);
2362 if (proceed == MagickFalse)
2363 status=MagickFalse;
2364 }
2365 }
2366 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2367 image->rows,image->rows);
2368 return(status);
2369 }
2370 /*
2371 Tile texture onto the image background (optimized).
2372 */
2373 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00002374 image_view=AcquireCacheView(image);
cristye6178502011-12-23 17:02:29 +00002375 texture_view=AcquireCacheView(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002376#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy19593872012-01-22 02:00:33 +00002377 #pragma omp parallel for schedule(static) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002378#endif
2379 for (y=0; y < (ssize_t) image->rows; y++)
2380 {
2381 MagickBooleanType
2382 sync;
2383
2384 register const Quantum
2385 *p,
2386 *pixels;
2387
2388 register ssize_t
2389 x;
2390
2391 register Quantum
2392 *q;
2393
2394 size_t
2395 width;
2396
2397 if (status == MagickFalse)
2398 continue;
cristye6178502011-12-23 17:02:29 +00002399 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2400 (y+texture_image->tile_offset.y) % texture_image->rows,
2401 texture_image->columns,1,exception);
2402 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002403 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2404 {
2405 status=MagickFalse;
2406 continue;
2407 }
cristye6178502011-12-23 17:02:29 +00002408 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002409 {
2410 register ssize_t
cristye6178502011-12-23 17:02:29 +00002411 j;
cristy4c08aed2011-07-01 19:47:50 +00002412
2413 p=pixels;
cristye6178502011-12-23 17:02:29 +00002414 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002415 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2416 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002417 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002418 {
cristye6178502011-12-23 17:02:29 +00002419 register ssize_t
2420 i;
2421
cristy10a6c612012-01-29 21:41:05 +00002422 if (GetPixelMask(image,p) != 0)
2423 {
2424 p+=GetPixelChannels(texture_image);
2425 q+=GetPixelChannels(image);
2426 continue;
2427 }
cristye6178502011-12-23 17:02:29 +00002428 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2429 {
2430 PixelChannel
2431 channel;
2432
2433 PixelTrait
2434 texture_traits,
2435 traits;
2436
2437 channel=GetPixelChannelMapChannel(texture_image,i);
2438 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2439 traits=GetPixelChannelMapTraits(image,channel);
2440 if ((traits == UndefinedPixelTrait) ||
2441 (texture_traits == UndefinedPixelTrait))
2442 continue;
2443 SetPixelChannel(image,channel,p[i],q);
2444 }
2445 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002446 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002447 }
2448 }
2449 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2450 if (sync == MagickFalse)
2451 status=MagickFalse;
2452 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2453 {
2454 MagickBooleanType
2455 proceed;
2456
2457#if defined(MAGICKCORE_OPENMP_SUPPORT)
2458 #pragma omp critical (MagickCore_TextureImage)
2459#endif
2460 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2461 image->rows);
2462 if (proceed == MagickFalse)
2463 status=MagickFalse;
2464 }
2465 }
2466 texture_view=DestroyCacheView(texture_view);
2467 image_view=DestroyCacheView(image_view);
2468 return(status);
2469}