blob: dfa66d78e267471ae00d5213db38693829aa938f [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
294 size_t
295 channels;
296
297 /*
298 Prepare composite image.
299 */
300 modify_outside_overlay=MagickFalse;
301 value=GetImageArtifact(composite_image,"compose:outside-overlay");
302 if (value != (const char *) NULL)
303 modify_outside_overlay=IsMagickTrue(value);
304 /*
305 Composite image.
306 */
307 status=MagickTrue;
308 progress=0;
309 image_view=AcquireCacheView(image);
310 composite_view=AcquireCacheView(composite_image);
311#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000312 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye4a40472011-12-22 02:56:19 +0000313#endif
314 for (y=0; y < (ssize_t) image->rows; y++)
315 {
316 const Quantum
317 *pixels;
318
319 register const Quantum
320 *restrict p;
321
322 register Quantum
323 *restrict q;
324
325 register ssize_t
326 x;
327
328 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 */
395 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
396 source,exception);
397 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
398 {
399 PixelChannel
400 channel;
401
402 PixelTrait
403 composite_traits,
404 traits;
405
406 channel=GetPixelChannelMapChannel(image,i);
407 traits=GetPixelChannelMapTraits(image,channel);
408 composite_traits=GetPixelChannelMapTraits(composite_image,
409 channel);
410 if ((traits == UndefinedPixelTrait) ||
411 (composite_traits == UndefinedPixelTrait))
412 continue;
413 q[i]=source[channel];
414 }
415 q+=GetPixelChannels(image);
416 continue;
417 }
418 /*
419 Authentic composite:
420 Sa: normalized source alpha.
421 Da: normalized destination alpha.
422 */
423 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
424 Da=QuantumScale*GetPixelAlpha(image,q);
425 alpha=Sa*(-Da)+Sa+Da;
426 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
427 {
428 PixelChannel
429 channel;
430
431 PixelTrait
432 composite_traits,
433 traits;
434
435 channel=GetPixelChannelMapChannel(image,i);
436 traits=GetPixelChannelMapTraits(image,channel);
437 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
438 if ((traits == UndefinedPixelTrait) ||
439 (composite_traits == UndefinedPixelTrait))
440 continue;
441 if ((traits & CopyPixelTrait) != 0)
442 {
443 if (channel != AlphaPixelChannel)
444 {
445 /*
446 Copy channel.
447 */
448 q[i]=GetPixelChannel(composite_image,channel,p);
449 continue;
450 }
451 /*
452 Set alpha channel.
453 */
454 q[i]=ClampToQuantum(QuantumRange*alpha);
455 continue;
456 }
457 /*
458 Sc: source color.
459 Dc: destination color.
460 */
461 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
462 Dc=(MagickRealType) q[i];
463 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
464 q[i]=ClampToQuantum(gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc));
465 }
466 p+=GetPixelChannels(composite_image);
467 channels=GetPixelChannels(composite_image);
468 if (p >= (pixels+channels*composite_image->columns))
469 p=pixels;
470 q+=GetPixelChannels(image);
471 }
472 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
473 status=MagickFalse;
474 if (image->progress_monitor != (MagickProgressMonitor) NULL)
475 {
476 MagickBooleanType
477 proceed;
478
479#if defined(MAGICKCORE_OPENMP_SUPPORT)
480 #pragma omp critical (MagickCore_CompositeImage)
481#endif
482 proceed=SetImageProgress(image,CompositeImageTag,progress++,
483 image->rows);
484 if (proceed == MagickFalse)
485 status=MagickFalse;
486 }
487 }
488 composite_view=DestroyCacheView(composite_view);
489 image_view=DestroyCacheView(image_view);
490 return(status);
491}
492
cristy4c08aed2011-07-01 19:47:50 +0000493MagickExport MagickBooleanType CompositeImage(Image *image,
494 const CompositeOperator compose,const Image *composite_image,
cristye941a752011-10-15 01:52:48 +0000495 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000496{
cristy4c08aed2011-07-01 19:47:50 +0000497#define CompositeImageTag "Composite/Image"
498
499 CacheView
500 *composite_view,
501 *image_view;
502
503 const char
504 *value;
505
506 double
507 sans;
508
cristy4c08aed2011-07-01 19:47:50 +0000509 GeometryInfo
510 geometry_info;
511
512 Image
513 *destination_image;
514
515 MagickBooleanType
516 modify_outside_overlay,
517 status;
518
519 MagickOffsetType
520 progress;
521
cristy4c08aed2011-07-01 19:47:50 +0000522 MagickRealType
523 amount,
524 destination_dissolve,
525 midpoint,
526 percent_brightness,
527 percent_saturation,
528 source_dissolve,
529 threshold;
530
531 MagickStatusType
532 flags;
533
cristye4a40472011-12-22 02:56:19 +0000534 size_t
535 channels;
536
cristyd197cbb2012-01-13 02:14:12 +0000537 ssize_t
538 y;
539
cristy4c08aed2011-07-01 19:47:50 +0000540 /*
cristye4a40472011-12-22 02:56:19 +0000541 Composition based on the SVG specification:
542
543 A Composition is defined by...
544 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
545 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
546 Y = 1 for source preserved
547 Z = 1 for destination preserved
548
549 Conversion to transparency (then optimized)
550 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
551 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
552
553 Where...
554 Sca = Sc*Sa normalized Source color divided by Source alpha
555 Dca = Dc*Da normalized Dest color divided by Dest alpha
556 Dc' = Dca'/Da' the desired color value for this channel.
557
558 Da' in in the follow formula as 'gamma' The resulting alpla value.
559
560 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
561 the following optimizations...
562 gamma = Sa+Da-Sa*Da;
563 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
564 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
565
566 The above SVG definitions also definate that Mathematical Composition
567 methods should use a 'Over' blending mode for Alpha Channel.
568 It however was not applied for composition modes of 'Plus', 'Minus',
569 the modulus versions of 'Add' and 'Subtract'.
570
571 Mathematical operator changes to be applied from IM v6.7...
572
573 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
574 'ModulusAdd' and 'ModulusSubtract' for clarity.
575
576 2) All mathematical compositions work as per the SVG specification
577 with regard to blending. This now includes 'ModulusAdd' and
578 'ModulusSubtract'.
579
580 3) When the special channel flag 'sync' (syncronize channel updates)
581 is turned off (enabled by default) then mathematical compositions are
582 only performed on the channels specified, and are applied
583 independantally of each other. In other words the mathematics is
584 performed as 'pure' mathematical operations, rather than as image
585 operations.
cristy4c08aed2011-07-01 19:47:50 +0000586 */
587 assert(image != (Image *) NULL);
588 assert(image->signature == MagickSignature);
589 if (image->debug != MagickFalse)
590 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
591 assert(composite_image != (Image *) NULL);
592 assert(composite_image->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000593 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000594 return(MagickFalse);
cristye4a40472011-12-22 02:56:19 +0000595 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
596 {
597 status=CompositeOverImage(image,composite_image,x_offset,y_offset,
598 exception);
599 return(status);
600 }
cristy4c08aed2011-07-01 19:47:50 +0000601 destination_image=(Image *) NULL;
602 amount=0.5;
603 destination_dissolve=1.0;
604 modify_outside_overlay=MagickFalse;
605 percent_brightness=100.0;
606 percent_saturation=100.0;
607 source_dissolve=1.0;
608 threshold=0.05f;
609 switch (compose)
610 {
611 case ClearCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000612 case DstAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +0000613 case DstInCompositeOp:
614 case InCompositeOp:
615 case OutCompositeOp:
616 case SrcCompositeOp:
617 case SrcInCompositeOp:
618 case SrcOutCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000619 {
620 /*
621 Modify destination outside the overlaid region.
622 */
623 modify_outside_overlay=MagickTrue;
624 break;
625 }
626 case CopyCompositeOp:
627 {
628 if ((x_offset < 0) || (y_offset < 0))
629 break;
630 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
631 break;
632 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
633 break;
634 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000635 image_view=AcquireCacheView(image);
636 composite_view=AcquireCacheView(composite_image);
637#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000638 #pragma omp parallel for schedule(static,4) shared(status)
cristy4c08aed2011-07-01 19:47:50 +0000639#endif
640 for (y=0; y < (ssize_t) composite_image->rows; y++)
641 {
642 MagickBooleanType
643 sync;
644
645 register const Quantum
646 *p;
647
648 register Quantum
649 *q;
650
651 register ssize_t
652 x;
653
654 if (status == MagickFalse)
655 continue;
656 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
657 1,exception);
658 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
659 composite_image->columns,1,exception);
660 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
661 {
662 status=MagickFalse;
663 continue;
664 }
665 for (x=0; x < (ssize_t) composite_image->columns; x++)
666 {
cristybdecccc2011-12-24 22:52:16 +0000667 register ssize_t
668 i;
669
670 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
671 {
672 PixelChannel
673 channel;
674
675 PixelTrait
676 composite_traits,
677 traits;
678
679 channel=GetPixelChannelMapChannel(composite_image,i);
680 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
681 traits=GetPixelChannelMapTraits(image,channel);
682 if ((traits == UndefinedPixelTrait) ||
683 (composite_traits == UndefinedPixelTrait))
684 continue;
685 SetPixelChannel(image,channel,p[i],q);
686 }
cristyed231572011-07-14 02:18:59 +0000687 p+=GetPixelChannels(composite_image);
688 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000689 }
690 sync=SyncCacheViewAuthenticPixels(image_view,exception);
691 if (sync == MagickFalse)
692 status=MagickFalse;
693 if (image->progress_monitor != (MagickProgressMonitor) NULL)
694 {
695 MagickBooleanType
696 proceed;
697
698#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000699 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000700#endif
701 proceed=SetImageProgress(image,CompositeImageTag,
702 (MagickOffsetType) y,image->rows);
703 if (proceed == MagickFalse)
704 status=MagickFalse;
705 }
706 }
707 composite_view=DestroyCacheView(composite_view);
708 image_view=DestroyCacheView(image_view);
709 return(status);
710 }
cristye4a40472011-12-22 02:56:19 +0000711 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000712 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000713 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000714 {
715 /*
716 Modify destination outside the overlaid region and require an alpha
717 channel to exist, to add transparency.
718 */
719 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +0000720 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000721 modify_outside_overlay=MagickTrue;
722 break;
723 }
724 case BlurCompositeOp:
725 {
726 CacheView
727 *composite_view,
728 *destination_view;
729
730 PixelInfo
731 pixel;
732
733 MagickRealType
734 angle_range,
735 angle_start,
736 height,
737 width;
738
739 ResampleFilter
740 *resample_filter;
741
742 SegmentInfo
743 blur;
744
745 /*
746 Blur Image dictated by an overlay gradient map: X = red_channel;
747 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
748 */
749 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000750 exception);
cristy4c08aed2011-07-01 19:47:50 +0000751 if (destination_image == (Image *) NULL)
752 return(MagickFalse);
753 /*
754 Determine the horizontal and vertical maximim blur.
755 */
756 SetGeometryInfo(&geometry_info);
757 flags=NoValue;
758 value=GetImageArtifact(composite_image,"compose:args");
759 if (value != (char *) NULL)
760 flags=ParseGeometry(value,&geometry_info);
761 if ((flags & WidthValue) == 0 )
762 {
763 destination_image=DestroyImage(destination_image);
764 return(MagickFalse);
765 }
766 width=geometry_info.rho;
767 height=geometry_info.sigma;
768 blur.x1=geometry_info.rho;
769 blur.x2=0.0;
770 blur.y1=0.0;
771 blur.y2=geometry_info.sigma;
772 angle_start=0.0;
773 angle_range=0.0;
774 if ((flags & HeightValue) == 0)
775 blur.y2=blur.x1;
776 if ((flags & XValue) != 0 )
777 {
778 MagickRealType
779 angle;
780
781 angle=DegreesToRadians(geometry_info.xi);
782 blur.x1=width*cos(angle);
783 blur.x2=width*sin(angle);
784 blur.y1=(-height*sin(angle));
785 blur.y2=height*cos(angle);
786 }
787 if ((flags & YValue) != 0 )
788 {
789 angle_start=DegreesToRadians(geometry_info.xi);
790 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
791 }
792 /*
793 Blur Image by resampling.
794 */
cristy8a11cb12011-10-19 23:53:34 +0000795 resample_filter=AcquireResampleFilter(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000796 SetResampleFilter(resample_filter,CubicFilter,2.0);
797 destination_view=AcquireCacheView(destination_image);
798 composite_view=AcquireCacheView(composite_image);
799 for (y=0; y < (ssize_t) composite_image->rows; y++)
800 {
801 MagickBooleanType
802 sync;
803
804 register const Quantum
805 *restrict p;
806
807 register Quantum
808 *restrict q;
809
810 register ssize_t
811 x;
812
813 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
814 continue;
815 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
816 1,exception);
817 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000818 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000819 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
820 break;
821 for (x=0; x < (ssize_t) composite_image->columns; x++)
822 {
823 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
824 {
cristyed231572011-07-14 02:18:59 +0000825 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000826 continue;
827 }
828 if (fabs(angle_range) > MagickEpsilon)
829 {
830 MagickRealType
831 angle;
832
833 angle=angle_start+angle_range*QuantumScale*
834 GetPixelBlue(composite_image,p);
835 blur.x1=width*cos(angle);
836 blur.x2=width*sin(angle);
837 blur.y1=(-height*sin(angle));
838 blur.y2=height*cos(angle);
839 }
840 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
841 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
842 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
843 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
844 GetPixelGreen(composite_image,p));
845 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
846 (double) y_offset+y,&pixel);
cristy803640d2011-11-17 02:11:32 +0000847 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000848 p+=GetPixelChannels(composite_image);
849 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000850 }
851 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
852 if (sync == MagickFalse)
853 break;
854 }
855 resample_filter=DestroyResampleFilter(resample_filter);
856 composite_view=DestroyCacheView(composite_view);
857 destination_view=DestroyCacheView(destination_view);
858 composite_image=destination_image;
859 break;
860 }
861 case DisplaceCompositeOp:
862 case DistortCompositeOp:
863 {
864 CacheView
865 *composite_view,
866 *destination_view,
867 *image_view;
868
869 PixelInfo
870 pixel;
871
872 MagickRealType
873 horizontal_scale,
874 vertical_scale;
875
876 PointInfo
877 center,
878 offset;
879
880 /*
881 Displace/Distort based on overlay gradient map:
882 X = red_channel; Y = green_channel;
883 compose:args = x_scale[,y_scale[,center.x,center.y]]
884 */
885 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000886 exception);
cristy4c08aed2011-07-01 19:47:50 +0000887 if (destination_image == (Image *) NULL)
888 return(MagickFalse);
889 SetGeometryInfo(&geometry_info);
890 flags=NoValue;
891 value=GetImageArtifact(composite_image,"compose:args");
892 if (value != (char *) NULL)
893 flags=ParseGeometry(value,&geometry_info);
894 if ((flags & (WidthValue|HeightValue)) == 0 )
895 {
896 if ((flags & AspectValue) == 0)
897 {
898 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
899 2.0;
900 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
901 }
902 else
903 {
904 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
905 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
906 }
907 }
908 else
909 {
910 horizontal_scale=geometry_info.rho;
911 vertical_scale=geometry_info.sigma;
912 if ((flags & PercentValue) != 0)
913 {
914 if ((flags & AspectValue) == 0)
915 {
916 horizontal_scale*=(composite_image->columns-1.0)/200.0;
917 vertical_scale*=(composite_image->rows-1.0)/200.0;
918 }
919 else
920 {
921 horizontal_scale*=(image->columns-1.0)/200.0;
922 vertical_scale*=(image->rows-1.0)/200.0;
923 }
924 }
925 if ((flags & HeightValue) == 0)
926 vertical_scale=horizontal_scale;
927 }
928 /*
929 Determine fixed center point for absolute distortion map
930 Absolute distort ==
931 Displace offset relative to a fixed absolute point
932 Select that point according to +X+Y user inputs.
933 default = center of overlay image
934 arg flag '!' = locations/percentage relative to background image
935 */
936 center.x=(MagickRealType) x_offset;
937 center.y=(MagickRealType) y_offset;
938 if (compose == DistortCompositeOp)
939 {
940 if ((flags & XValue) == 0)
941 if ((flags & AspectValue) == 0)
942 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
943 2.0;
944 else
945 center.x=((MagickRealType) image->columns-1)/2.0;
946 else
947 if ((flags & AspectValue) == 0)
948 center.x=(MagickRealType) x_offset+geometry_info.xi;
949 else
950 center.x=geometry_info.xi;
951 if ((flags & YValue) == 0)
952 if ((flags & AspectValue) == 0)
953 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
954 else
955 center.y=((MagickRealType) image->rows-1)/2.0;
956 else
957 if ((flags & AspectValue) == 0)
958 center.y=(MagickRealType) y_offset+geometry_info.psi;
959 else
960 center.y=geometry_info.psi;
961 }
962 /*
963 Shift the pixel offset point as defined by the provided,
964 displacement/distortion map. -- Like a lens...
965 */
cristye10859a2011-12-18 22:28:59 +0000966 GetPixelInfo(image,&pixel);
cristy4c08aed2011-07-01 19:47:50 +0000967 image_view=AcquireCacheView(image);
968 destination_view=AcquireCacheView(destination_image);
969 composite_view=AcquireCacheView(composite_image);
970 for (y=0; y < (ssize_t) composite_image->rows; y++)
971 {
972 MagickBooleanType
973 sync;
974
975 register const Quantum
976 *restrict p;
977
978 register Quantum
979 *restrict q;
980
981 register ssize_t
982 x;
983
984 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
985 continue;
986 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
987 1,exception);
988 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000989 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000990 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
991 break;
992 for (x=0; x < (ssize_t) composite_image->columns; x++)
993 {
994 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
995 {
cristyed231572011-07-14 02:18:59 +0000996 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000997 continue;
998 }
999 /*
1000 Displace the offset.
1001 */
1002 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1003 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1004 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1005 x : 0);
1006 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1007 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1008 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1009 y : 0);
1010 (void) InterpolatePixelInfo(image,image_view,
1011 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1012 &pixel,exception);
1013 /*
1014 Mask with the 'invalid pixel mask' in alpha channel.
1015 */
1016 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001017 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001018 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001019 p+=GetPixelChannels(composite_image);
1020 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001021 }
1022 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1023 if (sync == MagickFalse)
1024 break;
1025 }
1026 destination_view=DestroyCacheView(destination_view);
1027 composite_view=DestroyCacheView(composite_view);
1028 image_view=DestroyCacheView(image_view);
1029 composite_image=destination_image;
1030 break;
1031 }
1032 case DissolveCompositeOp:
1033 {
1034 /*
1035 Geometry arguments to dissolve factors.
1036 */
1037 value=GetImageArtifact(composite_image,"compose:args");
1038 if (value != (char *) NULL)
1039 {
1040 flags=ParseGeometry(value,&geometry_info);
1041 source_dissolve=geometry_info.rho/100.0;
1042 destination_dissolve=1.0;
1043 if ((source_dissolve-MagickEpsilon) < 0.0)
1044 source_dissolve=0.0;
1045 if ((source_dissolve+MagickEpsilon) > 1.0)
1046 {
1047 destination_dissolve=2.0-source_dissolve;
1048 source_dissolve=1.0;
1049 }
1050 if ((flags & SigmaValue) != 0)
1051 destination_dissolve=geometry_info.sigma/100.0;
1052 if ((destination_dissolve-MagickEpsilon) < 0.0)
1053 destination_dissolve=0.0;
1054 modify_outside_overlay=MagickTrue;
1055 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1056 {
1057 destination_dissolve=1.0;
1058 modify_outside_overlay=MagickFalse;
1059 }
1060 }
1061 break;
1062 }
1063 case BlendCompositeOp:
1064 {
1065 value=GetImageArtifact(composite_image,"compose:args");
1066 if (value != (char *) NULL)
1067 {
1068 flags=ParseGeometry(value,&geometry_info);
1069 source_dissolve=geometry_info.rho/100.0;
1070 destination_dissolve=1.0-source_dissolve;
1071 if ((flags & SigmaValue) != 0)
1072 destination_dissolve=geometry_info.sigma/100.0;
1073 modify_outside_overlay=MagickTrue;
1074 if ((destination_dissolve+MagickEpsilon) > 1.0)
1075 modify_outside_overlay=MagickFalse;
1076 }
1077 break;
1078 }
1079 case MathematicsCompositeOp:
1080 {
1081 /*
1082 Just collect the values from "compose:args", setting.
1083 Unused values are set to zero automagically.
1084
1085 Arguments are normally a comma separated list, so this probably should
1086 be changed to some 'general comma list' parser, (with a minimum
1087 number of values)
1088 */
1089 SetGeometryInfo(&geometry_info);
1090 value=GetImageArtifact(composite_image,"compose:args");
1091 if (value != (char *) NULL)
1092 (void) ParseGeometry(value,&geometry_info);
1093 break;
1094 }
1095 case ModulateCompositeOp:
1096 {
1097 /*
1098 Determine the brightness and saturation scale.
1099 */
1100 value=GetImageArtifact(composite_image,"compose:args");
1101 if (value != (char *) NULL)
1102 {
1103 flags=ParseGeometry(value,&geometry_info);
1104 percent_brightness=geometry_info.rho;
1105 if ((flags & SigmaValue) != 0)
1106 percent_saturation=geometry_info.sigma;
1107 }
1108 break;
1109 }
1110 case ThresholdCompositeOp:
1111 {
1112 /*
1113 Determine the amount and threshold.
1114 */
1115 value=GetImageArtifact(composite_image,"compose:args");
1116 if (value != (char *) NULL)
1117 {
1118 flags=ParseGeometry(value,&geometry_info);
1119 amount=geometry_info.rho;
1120 threshold=geometry_info.sigma;
1121 if ((flags & SigmaValue) == 0)
1122 threshold=0.05f;
1123 }
1124 threshold*=QuantumRange;
1125 break;
1126 }
1127 default:
1128 break;
1129 }
1130 value=GetImageArtifact(composite_image,"compose:outside-overlay");
1131 if (value != (const char *) NULL)
1132 modify_outside_overlay=IsMagickTrue(value);
1133 /*
1134 Composite image.
1135 */
1136 status=MagickTrue;
1137 progress=0;
1138 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy4c08aed2011-07-01 19:47:50 +00001139 image_view=AcquireCacheView(image);
1140 composite_view=AcquireCacheView(composite_image);
1141#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00001142 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy4c08aed2011-07-01 19:47:50 +00001143#endif
1144 for (y=0; y < (ssize_t) image->rows; y++)
1145 {
1146 const Quantum
1147 *pixels;
1148
1149 double
cristye4a40472011-12-22 02:56:19 +00001150 blue,
cristy4c08aed2011-07-01 19:47:50 +00001151 brightness,
cristye4a40472011-12-22 02:56:19 +00001152 green,
cristy4c08aed2011-07-01 19:47:50 +00001153 hue,
cristye4a40472011-12-22 02:56:19 +00001154 red,
cristy4c08aed2011-07-01 19:47:50 +00001155 saturation;
1156
cristy4c08aed2011-07-01 19:47:50 +00001157 register const Quantum
1158 *restrict p;
1159
1160 register Quantum
1161 *restrict q;
1162
1163 register ssize_t
1164 x;
1165
1166 if (status == MagickFalse)
1167 continue;
1168 if (modify_outside_overlay == MagickFalse)
1169 {
1170 if (y < y_offset)
1171 continue;
1172 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1173 continue;
1174 }
1175 /*
1176 If pixels is NULL, y is outside overlay region.
1177 */
1178 pixels=(Quantum *) NULL;
1179 p=(Quantum *) NULL;
1180 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1181 {
1182 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1183 composite_image->columns,1,exception);
1184 if (p == (const Quantum *) NULL)
1185 {
1186 status=MagickFalse;
1187 continue;
1188 }
1189 pixels=p;
1190 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001191 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001192 }
1193 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001194 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001195 {
1196 status=MagickFalse;
1197 continue;
1198 }
cristy4c08aed2011-07-01 19:47:50 +00001199 hue=0.0;
1200 saturation=0.0;
1201 brightness=0.0;
1202 for (x=0; x < (ssize_t) image->columns; x++)
1203 {
cristye4a40472011-12-22 02:56:19 +00001204 MagickRealType
1205 alpha,
1206 Da,
1207 Dc,
1208 Dca,
1209 gamma,
1210 Sa,
1211 Sc,
1212 Sca;
1213
1214 register ssize_t
1215 i;
1216
cristy4c08aed2011-07-01 19:47:50 +00001217 if (modify_outside_overlay == MagickFalse)
1218 {
1219 if (x < x_offset)
1220 {
cristyed231572011-07-14 02:18:59 +00001221 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001222 continue;
1223 }
1224 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1225 break;
1226 }
cristye4a40472011-12-22 02:56:19 +00001227 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1228 ((x-x_offset) >= (ssize_t) composite_image->columns))
1229 {
1230 Quantum
1231 source[MaxPixelChannels];
1232
1233 /*
1234 Virtual composite:
1235 Sc: source color.
1236 Dc: destination color.
1237 */
1238 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1239 source,exception);
1240 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1241 {
1242 MagickRealType
1243 pixel;
1244
1245 PixelChannel
1246 channel;
1247
1248 PixelTrait
1249 composite_traits,
1250 traits;
1251
1252 channel=GetPixelChannelMapChannel(image,i);
1253 traits=GetPixelChannelMapTraits(image,channel);
1254 composite_traits=GetPixelChannelMapTraits(composite_image,
1255 channel);
1256 if ((traits == UndefinedPixelTrait) ||
1257 (composite_traits == UndefinedPixelTrait))
1258 continue;
1259 switch (compose)
1260 {
cristyc8d63672012-01-11 13:03:13 +00001261 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001262 case ChangeMaskCompositeOp:
1263 case CopyAlphaCompositeOp:
1264 case DstAtopCompositeOp:
1265 case DstInCompositeOp:
1266 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001267 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001268 case OutCompositeOp:
1269 case SrcInCompositeOp:
1270 case SrcOutCompositeOp:
1271 {
1272 pixel=(MagickRealType) q[i];
1273 if (channel == AlphaPixelChannel)
1274 pixel=(MagickRealType) TransparentAlpha;
1275 break;
1276 }
1277 case ClearCompositeOp:
1278 case CopyCompositeOp:
1279 case ReplaceCompositeOp:
1280 case SrcCompositeOp:
1281 {
1282 if (channel == AlphaPixelChannel)
1283 {
1284 pixel=(MagickRealType) TransparentAlpha;
1285 break;
1286 }
1287 pixel=0.0;
1288 break;
1289 }
cristy99abff32011-12-24 20:45:16 +00001290 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001291 case DissolveCompositeOp:
1292 {
1293 if (channel == AlphaPixelChannel)
1294 {
1295 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1296 source);
1297 break;
1298 }
1299 pixel=(MagickRealType) source[channel];
1300 break;
1301 }
1302 default:
1303 {
1304 pixel=(MagickRealType) source[channel];
1305 break;
1306 }
1307 }
1308 q[i]=ClampToQuantum(pixel);
1309 }
1310 q+=GetPixelChannels(image);
1311 continue;
1312 }
1313 /*
1314 Authentic composite:
1315 Sa: normalized source alpha.
1316 Da: normalized destination alpha.
1317 */
1318 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1319 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001320 switch (compose)
1321 {
cristye4a40472011-12-22 02:56:19 +00001322 case BumpmapCompositeOp:
1323 {
1324 alpha=GetPixelIntensity(composite_image,p)*Sa;
1325 break;
1326 }
cristycdc168f2011-12-21 15:24:39 +00001327 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001328 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001329 case DifferenceCompositeOp:
1330 case DivideDstCompositeOp:
1331 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001332 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001333 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001334 case LinearBurnCompositeOp:
1335 case LinearDodgeCompositeOp:
1336 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001337 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001338 case MinusDstCompositeOp:
1339 case MinusSrcCompositeOp:
1340 case ModulusAddCompositeOp:
1341 case ModulusSubtractCompositeOp:
1342 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001343 case OverlayCompositeOp:
1344 case PegtopLightCompositeOp:
1345 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001346 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001347 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001348 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001349 {
1350 alpha=RoundToUnity(Sa+Da-Sa*Da);
1351 break;
1352 }
1353 case DarkenCompositeOp:
1354 case DstAtopCompositeOp:
1355 case DstInCompositeOp:
1356 case InCompositeOp:
1357 case LightenCompositeOp:
1358 case SrcInCompositeOp:
1359 {
1360 alpha=Sa*Da;
1361 break;
1362 }
1363 case DissolveCompositeOp:
1364 {
1365 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1366 Sa+destination_dissolve*Da;
1367 break;
1368 }
1369 case DstOverCompositeOp:
1370 {
1371 alpha=Da*(-Sa)+Da+Sa;
1372 break;
1373 }
1374 case DstOutCompositeOp:
1375 {
1376 alpha=Da*(1.0-Sa);
1377 break;
1378 }
1379 case OutCompositeOp:
1380 case SrcOutCompositeOp:
1381 {
1382 alpha=Sa*(1.0-Da);
1383 break;
1384 }
1385 case OverCompositeOp:
1386 case SrcOverCompositeOp:
1387 {
1388 alpha=Sa*(-Da)+Sa+Da;
1389 break;
1390 }
cristy99abff32011-12-24 20:45:16 +00001391 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001392 case PlusCompositeOp:
1393 {
1394 alpha=RoundToUnity(Sa+Da);
1395 break;
1396 }
cristy4c08aed2011-07-01 19:47:50 +00001397 case XorCompositeOp:
1398 {
cristye4a40472011-12-22 02:56:19 +00001399 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001400 break;
1401 }
1402 default:
cristye4a40472011-12-22 02:56:19 +00001403 {
1404 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001405 break;
cristye4a40472011-12-22 02:56:19 +00001406 }
cristy4c08aed2011-07-01 19:47:50 +00001407 }
cristye4a40472011-12-22 02:56:19 +00001408 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1409 {
cristye10859a2011-12-18 22:28:59 +00001410 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001411 pixel;
cristye10859a2011-12-18 22:28:59 +00001412
cristye4a40472011-12-22 02:56:19 +00001413 PixelChannel
1414 channel;
cristye10859a2011-12-18 22:28:59 +00001415
cristye4a40472011-12-22 02:56:19 +00001416 PixelTrait
1417 composite_traits,
1418 traits;
1419
1420 channel=GetPixelChannelMapChannel(image,i);
1421 traits=GetPixelChannelMapTraits(image,channel);
1422 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
cristy0cd1f212012-01-05 15:45:59 +00001423 if (traits == UndefinedPixelTrait)
1424 continue;
cristya7b07912012-01-11 20:01:32 +00001425 if ((compose != IntensityCompositeOp) &&
cristye4a40472011-12-22 02:56:19 +00001426 (composite_traits == UndefinedPixelTrait))
1427 continue;
1428 /*
1429 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001430 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001431 */
1432 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
cristye4a40472011-12-22 02:56:19 +00001433 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001434 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001435 {
cristye4a40472011-12-22 02:56:19 +00001436 if (channel != AlphaPixelChannel)
1437 {
1438 /*
1439 Copy channel.
1440 */
1441 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001442 continue;
cristye10859a2011-12-18 22:28:59 +00001443 }
cristye4a40472011-12-22 02:56:19 +00001444 /*
1445 Set alpha channel.
1446 */
cristye10859a2011-12-18 22:28:59 +00001447 switch (compose)
1448 {
cristyc8d63672012-01-11 13:03:13 +00001449 case AlphaCompositeOp:
1450 {
cristya7b07912012-01-11 20:01:32 +00001451 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001452 break;
1453 }
cristye4a40472011-12-22 02:56:19 +00001454 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001455 case CopyBlackCompositeOp:
1456 case CopyBlueCompositeOp:
1457 case CopyCyanCompositeOp:
1458 case CopyGreenCompositeOp:
1459 case CopyMagentaCompositeOp:
1460 case CopyRedCompositeOp:
1461 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001462 case SrcAtopCompositeOp:
1463 case DstCompositeOp:
1464 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001465 {
cristye4a40472011-12-22 02:56:19 +00001466 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001467 break;
1468 }
cristye10859a2011-12-18 22:28:59 +00001469 case ChangeMaskCompositeOp:
1470 {
cristye4a40472011-12-22 02:56:19 +00001471 MagickBooleanType
1472 equivalent;
1473
cristy99abff32011-12-24 20:45:16 +00001474 if (Da > ((MagickRealType) QuantumRange/2.0))
1475 {
1476 pixel=(MagickRealType) TransparentAlpha;
1477 break;
1478 }
cristye4a40472011-12-22 02:56:19 +00001479 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001480 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001481 {
1482 pixel=(MagickRealType) TransparentAlpha;
1483 break;
1484 }
1485 pixel=(MagickRealType) OpaqueAlpha;
1486 break;
1487 }
cristy99abff32011-12-24 20:45:16 +00001488 case ClearCompositeOp:
1489 {
1490 pixel=(MagickRealType) TransparentAlpha;
1491 break;
1492 }
1493 case ColorizeCompositeOp:
1494 case HueCompositeOp:
1495 case LuminizeCompositeOp:
1496 case SaturateCompositeOp:
1497 {
1498 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1499 {
1500 pixel=QuantumRange*Da;
1501 break;
1502 }
1503 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1504 {
1505 pixel=QuantumRange*Sa;
1506 break;
1507 }
1508 if (Sa < Da)
1509 {
1510 pixel=QuantumRange*Da;
1511 break;
1512 }
1513 pixel=QuantumRange*Sa;
1514 break;
1515 }
cristye4a40472011-12-22 02:56:19 +00001516 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001517 case CopyAlphaCompositeOp:
1518 case DisplaceCompositeOp:
1519 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001520 case DstAtopCompositeOp:
1521 case ReplaceCompositeOp:
1522 case SrcCompositeOp:
1523 {
1524 pixel=QuantumRange*Sa;
1525 break;
1526 }
1527 case DarkenIntensityCompositeOp:
1528 {
cristy99abff32011-12-24 20:45:16 +00001529 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1530 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001531 break;
1532 }
cristy98621462011-12-31 22:31:11 +00001533 case IntensityCompositeOp:
1534 {
cristy20d5f622012-01-11 13:04:26 +00001535 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
cristy98621462011-12-31 22:31:11 +00001536 break;
1537 }
cristye4a40472011-12-22 02:56:19 +00001538 case LightenIntensityCompositeOp:
1539 {
1540 pixel=Sa*GetPixelIntensity(composite_image,p) >
1541 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001542 break;
1543 }
cristy99abff32011-12-24 20:45:16 +00001544 case ModulateCompositeOp:
1545 {
1546 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1547 {
1548 pixel=QuantumRange*Da;
1549 break;
1550 }
1551 pixel=QuantumRange*Da;
1552 break;
1553 }
cristye10859a2011-12-18 22:28:59 +00001554 default:
1555 {
cristye4a40472011-12-22 02:56:19 +00001556 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001557 break;
1558 }
1559 }
cristye4a40472011-12-22 02:56:19 +00001560 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001561 continue;
1562 }
1563 /*
cristy99abff32011-12-24 20:45:16 +00001564 Porter-Duff compositions:
1565 Sca: source normalized color multiplied by alpha.
1566 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001567 */
cristy99abff32011-12-24 20:45:16 +00001568 Sca=QuantumScale*Sa*Sc;
1569 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001570 switch (compose)
1571 {
cristye10859a2011-12-18 22:28:59 +00001572 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001573 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001574 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001575 {
cristy99abff32011-12-24 20:45:16 +00001576 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001577 break;
1578 }
1579 default:
1580 break;
1581 }
cristye4a40472011-12-22 02:56:19 +00001582 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
cristyd197cbb2012-01-13 02:14:12 +00001583 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001584 switch (compose)
1585 {
cristya7b07912012-01-11 20:01:32 +00001586 case AlphaCompositeOp:
1587 {
1588 pixel=QuantumRange*Sa;
1589 break;
1590 }
cristye4a40472011-12-22 02:56:19 +00001591 case AtopCompositeOp:
1592 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001593 {
cristye4a40472011-12-22 02:56:19 +00001594 pixel=Sc*Sa+Dc*(1.0-Sa);
1595 break;
cristye10859a2011-12-18 22:28:59 +00001596 }
cristye4a40472011-12-22 02:56:19 +00001597 case BlendCompositeOp:
1598 {
1599 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1600 break;
1601 }
1602 case BlurCompositeOp:
1603 case DisplaceCompositeOp:
1604 case DistortCompositeOp:
1605 case CopyCompositeOp:
1606 case ReplaceCompositeOp:
1607 case SrcCompositeOp:
1608 {
1609 pixel=Sc;
1610 break;
1611 }
1612 case BumpmapCompositeOp:
1613 {
cristy99abff32011-12-24 20:45:16 +00001614 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001615 {
1616 pixel=Dc;
1617 break;
1618 }
1619 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1620 break;
1621 }
cristy99abff32011-12-24 20:45:16 +00001622 case ChangeMaskCompositeOp:
1623 {
1624 pixel=Dc;
1625 break;
1626 }
1627 case ClearCompositeOp:
1628 {
1629 pixel=0.0;
1630 break;
1631 }
cristye4a40472011-12-22 02:56:19 +00001632 case ColorBurnCompositeOp:
1633 {
1634 /*
1635 Refer to the March 2009 SVG specification.
1636 */
1637 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1638 {
cristy99abff32011-12-24 20:45:16 +00001639 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001640 break;
1641 }
1642 if (Sca < MagickEpsilon)
1643 {
cristy99abff32011-12-24 20:45:16 +00001644 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001645 break;
1646 }
cristy99abff32011-12-24 20:45:16 +00001647 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1648 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001649 break;
1650 }
1651 case ColorDodgeCompositeOp:
1652 {
1653 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1654 {
cristy99abff32011-12-24 20:45:16 +00001655 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001656 break;
1657 }
1658 if (fabs(Sca-Sa) < MagickEpsilon)
1659 {
cristy99abff32011-12-24 20:45:16 +00001660 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001661 break;
1662 }
cristy99abff32011-12-24 20:45:16 +00001663 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001664 (1.0-Sa));
1665 break;
1666 }
1667 case ColorizeCompositeOp:
1668 {
cristy99abff32011-12-24 20:45:16 +00001669 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001670 {
1671 pixel=Dc;
1672 break;
1673 }
cristy99abff32011-12-24 20:45:16 +00001674 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001675 {
1676 pixel=Sc;
1677 break;
1678 }
1679 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
cristy99abff32011-12-24 20:45:16 +00001680 GetPixelBlue(image,q),&sans,&sans,&brightness);
cristye4a40472011-12-22 02:56:19 +00001681 CompositeHSB(GetPixelRed(composite_image,p),
1682 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1683 &hue,&saturation,&sans);
1684 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1685 switch (channel)
1686 {
1687 case RedPixelChannel: pixel=red; break;
1688 case GreenPixelChannel: pixel=green; break;
1689 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001690 default: pixel=Dc; break;
1691 }
1692 break;
1693 }
cristye4a40472011-12-22 02:56:19 +00001694 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001695 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001696 {
cristyd197cbb2012-01-13 02:14:12 +00001697 if (channel == AlphaPixelChannel)
1698 pixel=(MagickRealType) GetPixelAlpha(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001699 break;
1700 }
1701 case CopyBlackCompositeOp:
1702 {
cristyd197cbb2012-01-13 02:14:12 +00001703 if (channel == BlackPixelChannel)
1704 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001705 break;
1706 }
1707 case CopyBlueCompositeOp:
1708 case CopyYellowCompositeOp:
1709 {
cristyd197cbb2012-01-13 02:14:12 +00001710 if (channel == BluePixelChannel)
1711 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001712 break;
1713 }
1714 case CopyGreenCompositeOp:
1715 case CopyMagentaCompositeOp:
1716 {
cristyd197cbb2012-01-13 02:14:12 +00001717 if (channel == GreenPixelChannel)
1718 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001719 break;
1720 }
1721 case CopyRedCompositeOp:
1722 case CopyCyanCompositeOp:
1723 {
cristyd197cbb2012-01-13 02:14:12 +00001724 if (channel == RedPixelChannel)
1725 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001726 break;
1727 }
cristy99abff32011-12-24 20:45:16 +00001728 case DarkenCompositeOp:
1729 {
1730 /*
1731 Darken is equivalent to a 'Minimum' method
1732 OR a greyscale version of a binary 'Or'
1733 OR the 'Intersection' of pixel sets.
1734 */
1735 if (Sc < Dc)
1736 {
1737 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1738 break;
1739 }
1740 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1741 break;
1742 }
cristye4a40472011-12-22 02:56:19 +00001743 case DarkenIntensityCompositeOp:
1744 {
cristy99abff32011-12-24 20:45:16 +00001745 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1746 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001747 break;
1748 }
1749 case DifferenceCompositeOp:
1750 {
1751 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1752 break;
1753 }
1754 case DissolveCompositeOp:
1755 {
1756 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1757 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1758 break;
1759 }
1760 case DivideDstCompositeOp:
1761 {
1762 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1763 {
cristy99abff32011-12-24 20:45:16 +00001764 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001765 break;
1766 }
1767 if (fabs(Dca) < MagickEpsilon)
1768 {
cristy99abff32011-12-24 20:45:16 +00001769 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001770 break;
1771 }
cristy99abff32011-12-24 20:45:16 +00001772 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001773 break;
1774 }
1775 case DivideSrcCompositeOp:
1776 {
1777 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1778 {
cristy99abff32011-12-24 20:45:16 +00001779 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001780 break;
1781 }
1782 if (fabs(Sca) < MagickEpsilon)
1783 {
cristy99abff32011-12-24 20:45:16 +00001784 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001785 break;
1786 }
cristy99abff32011-12-24 20:45:16 +00001787 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001788 break;
1789 }
1790 case DstAtopCompositeOp:
1791 {
1792 pixel=Dc*Da+Sc*(1.0-Da);
1793 break;
1794 }
1795 case DstCompositeOp:
1796 case NoCompositeOp:
1797 {
1798 pixel=Dc;
1799 break;
1800 }
1801 case DstInCompositeOp:
1802 {
1803 pixel=gamma*(Sa*Dc*Sa);
1804 break;
1805 }
1806 case DstOutCompositeOp:
1807 {
1808 pixel=gamma*(Da*Dc*(1.0-Sa));
1809 break;
1810 }
1811 case DstOverCompositeOp:
1812 {
1813 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1814 break;
1815 }
1816 case ExclusionCompositeOp:
1817 {
cristy99abff32011-12-24 20:45:16 +00001818 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1819 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001820 break;
1821 }
1822 case HardLightCompositeOp:
1823 {
1824 if ((2.0*Sca) < Sa)
1825 {
cristy99abff32011-12-24 20:45:16 +00001826 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001827 (1.0-Sa));
1828 break;
1829 }
cristy99abff32011-12-24 20:45:16 +00001830 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001831 Dca*(1.0-Sa));
1832 break;
1833 }
1834 case HueCompositeOp:
1835 {
cristy99abff32011-12-24 20:45:16 +00001836 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001837 {
1838 pixel=Dc;
1839 break;
1840 }
cristy99abff32011-12-24 20:45:16 +00001841 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001842 {
1843 pixel=Sc;
1844 break;
1845 }
1846 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1847 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1848 CompositeHSB(GetPixelRed(composite_image,p),
1849 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1850 &hue,&sans,&sans);
1851 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1852 switch (channel)
1853 {
1854 case RedPixelChannel: pixel=red; break;
1855 case GreenPixelChannel: pixel=green; break;
1856 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001857 default: pixel=Dc; break;
1858 }
1859 break;
1860 }
1861 case InCompositeOp:
1862 case SrcInCompositeOp:
1863 {
1864 pixel=gamma*(Da*Sc*Da);
1865 break;
1866 }
cristy99abff32011-12-24 20:45:16 +00001867 case LinearBurnCompositeOp:
1868 {
1869 /*
1870 LinearBurn: as defined by Abode Photoshop, according to
1871 http://www.simplefilter.de/en/basics/mixmods.html is:
1872
1873 f(Sc,Dc) = Sc + Dc - 1
1874 */
1875 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1876 break;
1877 }
1878 case LinearDodgeCompositeOp:
1879 {
1880 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1881 break;
1882 }
1883 case LinearLightCompositeOp:
1884 {
1885 /*
1886 LinearLight: as defined by Abode Photoshop, according to
1887 http://www.simplefilter.de/en/basics/mixmods.html is:
1888
1889 f(Sc,Dc) = Dc + 2*Sc - 1
1890 */
1891 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1892 break;
1893 }
1894 case LightenCompositeOp:
1895 {
1896 if (Sc > Dc)
1897 {
1898 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1899 break;
1900 }
1901 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1902 break;
1903 }
cristye4a40472011-12-22 02:56:19 +00001904 case LightenIntensityCompositeOp:
1905 {
1906 /*
1907 Lighten is equivalent to a 'Maximum' method
1908 OR a greyscale version of a binary 'And'
1909 OR the 'Union' of pixel sets.
1910 */
1911 pixel=Sa*GetPixelIntensity(composite_image,p) >
1912 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1913 break;
1914 }
cristye4a40472011-12-22 02:56:19 +00001915 case LuminizeCompositeOp:
1916 {
cristy99abff32011-12-24 20:45:16 +00001917 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001918 {
1919 pixel=Dc;
1920 break;
1921 }
cristy99abff32011-12-24 20:45:16 +00001922 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001923 {
1924 pixel=Sc;
1925 break;
1926 }
1927 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1928 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1929 CompositeHSB(GetPixelRed(composite_image,p),
1930 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1931 &sans,&sans,&brightness);
1932 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1933 switch (channel)
1934 {
1935 case RedPixelChannel: pixel=red; break;
1936 case GreenPixelChannel: pixel=green; break;
1937 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001938 default: pixel=Dc; break;
1939 }
1940 break;
1941 }
1942 case MathematicsCompositeOp:
1943 {
1944 /*
1945 'Mathematics' a free form user control mathematical composition
1946 is defined as...
1947
1948 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
1949
1950 Where the arguments A,B,C,D are (currently) passed to composite
1951 as a command separated 'geometry' string in "compose:args" image
1952 artifact.
1953
1954 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
1955
1956 Applying the SVG transparency formula (see above), we get...
1957
1958 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
1959
1960 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
1961 Dca*(1.0-Sa)
1962 */
1963 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
1964 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
1965 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
1966 break;
1967 }
1968 case MinusDstCompositeOp:
1969 {
1970 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
1971 break;
1972 }
1973 case MinusSrcCompositeOp:
1974 {
1975 /*
1976 Minus source from destination.
1977
1978 f(Sc,Dc) = Sc - Dc
1979 */
cristy99abff32011-12-24 20:45:16 +00001980 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00001981 break;
1982 }
1983 case ModulateCompositeOp:
1984 {
1985 ssize_t
1986 offset;
1987
cristy99abff32011-12-24 20:45:16 +00001988 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001989 {
1990 pixel=Dc;
1991 break;
1992 }
1993 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
1994 if (offset == 0)
1995 {
1996 pixel=Dc;
1997 break;
1998 }
1999 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2000 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2001 brightness+=(0.01*percent_brightness*offset)/midpoint;
2002 saturation*=0.01*percent_saturation;
2003 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2004 switch (channel)
2005 {
2006 case RedPixelChannel: pixel=red; break;
2007 case GreenPixelChannel: pixel=green; break;
2008 case BluePixelChannel: pixel=blue; break;
2009 default: pixel=Dc; break;
2010 }
2011 break;
2012 }
2013 case ModulusAddCompositeOp:
2014 {
2015 pixel=Sc+Dc;
2016 if (pixel > QuantumRange)
2017 pixel-=(QuantumRange+1.0);
2018 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2019 break;
2020 }
2021 case ModulusSubtractCompositeOp:
2022 {
cristy99abff32011-12-24 20:45:16 +00002023 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002024 if (pixel < 0.0)
2025 pixel+=(QuantumRange+1.0);
2026 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2027 break;
2028 }
2029 case MultiplyCompositeOp:
2030 {
cristy99abff32011-12-24 20:45:16 +00002031 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002032 break;
2033 }
2034 case OutCompositeOp:
2035 case SrcOutCompositeOp:
2036 {
2037 pixel=gamma*(Sa*Sc*(1.0-Da));
2038 break;
2039 }
2040 case OverCompositeOp:
2041 case SrcOverCompositeOp:
2042 {
cristy99abff32011-12-24 20:45:16 +00002043 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002044 break;
2045 }
2046 case OverlayCompositeOp:
2047 {
2048 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002049 {
2050 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2051 (1.0-Da));
2052 break;
2053 }
2054 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2055 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002056 break;
2057 }
2058 case PegtopLightCompositeOp:
2059 {
2060 /*
2061 PegTop: A Soft-Light alternative: A continuous version of the
2062 Softlight function, producing very similar results.
2063
2064 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2065
2066 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2067 */
2068 if (fabs(Da) < MagickEpsilon)
2069 {
cristy99abff32011-12-24 20:45:16 +00002070 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002071 break;
2072 }
cristy99abff32011-12-24 20:45:16 +00002073 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2074 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002075 break;
2076 }
2077 case PinLightCompositeOp:
2078 {
2079 /*
2080 PinLight: A Photoshop 7 composition method
2081 http://www.simplefilter.de/en/basics/mixmods.html
2082
2083 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2084 */
2085 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2086 {
cristy99abff32011-12-24 20:45:16 +00002087 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002088 break;
2089 }
2090 if ((Dca*Sa) > (2.0*Sca*Da))
2091 {
cristy99abff32011-12-24 20:45:16 +00002092 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002093 break;
2094 }
cristy99abff32011-12-24 20:45:16 +00002095 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002096 break;
2097 }
2098 case PlusCompositeOp:
2099 {
cristy99abff32011-12-24 20:45:16 +00002100 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002101 break;
2102 }
2103 case SaturateCompositeOp:
2104 {
cristy99abff32011-12-24 20:45:16 +00002105 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002106 {
2107 pixel=Dc;
2108 break;
2109 }
cristy99abff32011-12-24 20:45:16 +00002110 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002111 {
2112 pixel=Sc;
2113 break;
2114 }
2115 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2116 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2117 CompositeHSB(GetPixelRed(composite_image,p),
2118 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
2119 &sans,&saturation,&sans);
2120 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2121 switch (channel)
2122 {
2123 case RedPixelChannel: pixel=red; break;
2124 case GreenPixelChannel: pixel=green; break;
2125 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002126 default: pixel=Dc; break;
2127 }
2128 break;
2129 }
2130 case ScreenCompositeOp:
2131 {
2132 /*
2133 Screen: a negated multiply:
2134
2135 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2136 */
cristy99abff32011-12-24 20:45:16 +00002137 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002138 break;
2139 }
2140 case SoftLightCompositeOp:
2141 {
2142 /*
2143 Refer to the March 2009 SVG specification.
2144 */
2145 if ((2.0*Sca) < Sa)
2146 {
cristy99abff32011-12-24 20:45:16 +00002147 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2148 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002149 break;
2150 }
2151 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2152 {
cristy99abff32011-12-24 20:45:16 +00002153 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2154 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2155 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002156 break;
2157 }
cristy99abff32011-12-24 20:45:16 +00002158 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2159 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002160 break;
2161 }
2162 case ThresholdCompositeOp:
2163 {
2164 MagickRealType
2165 delta;
2166
2167 delta=Sc-Dc;
2168 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2169 {
2170 pixel=gamma*Dc;
2171 break;
2172 }
2173 pixel=gamma*(Dc+delta*amount);
2174 break;
2175 }
2176 case VividLightCompositeOp:
2177 {
2178 /*
2179 VividLight: A Photoshop 7 composition method. See
2180 http://www.simplefilter.de/en/basics/mixmods.html.
2181
2182 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2183 */
2184 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2185 {
cristy99abff32011-12-24 20:45:16 +00002186 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002187 break;
2188 }
2189 if ((2.0*Sca) <= Sa)
2190 {
cristy99abff32011-12-24 20:45:16 +00002191 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2192 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002193 break;
2194 }
cristy99abff32011-12-24 20:45:16 +00002195 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2196 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002197 break;
2198 }
2199 case XorCompositeOp:
2200 {
cristy99abff32011-12-24 20:45:16 +00002201 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002202 break;
2203 }
2204 default:
2205 {
2206 pixel=Sc;
2207 break;
2208 }
2209 }
2210 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002211 }
cristyed231572011-07-14 02:18:59 +00002212 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002213 channels=GetPixelChannels(composite_image);
2214 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002215 p=pixels;
cristyed231572011-07-14 02:18:59 +00002216 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002217 }
2218 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2219 status=MagickFalse;
2220 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2221 {
2222 MagickBooleanType
2223 proceed;
2224
2225#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002226 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002227#endif
2228 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2229 image->rows);
2230 if (proceed == MagickFalse)
2231 status=MagickFalse;
2232 }
2233 }
2234 composite_view=DestroyCacheView(composite_view);
2235 image_view=DestroyCacheView(image_view);
2236 if (destination_image != (Image * ) NULL)
2237 destination_image=DestroyImage(destination_image);
2238 return(status);
2239}
2240
2241/*
2242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2243% %
2244% %
2245% %
2246% T e x t u r e I m a g e %
2247% %
2248% %
2249% %
2250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2251%
2252% TextureImage() repeatedly tiles the texture image across and down the image
2253% canvas.
2254%
2255% The format of the TextureImage method is:
2256%
cristye6178502011-12-23 17:02:29 +00002257% MagickBooleanType TextureImage(Image *image,const Image *texture_image,
cristye941a752011-10-15 01:52:48 +00002258% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002259%
2260% A description of each parameter follows:
2261%
2262% o image: the image.
2263%
cristye6178502011-12-23 17:02:29 +00002264% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002265%
2266*/
cristye6178502011-12-23 17:02:29 +00002267MagickExport MagickBooleanType TextureImage(Image *image,
2268 const Image *texture_image,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002269{
2270#define TextureImageTag "Texture/Image"
2271
2272 CacheView
2273 *image_view,
2274 *texture_view;
2275
cristy4c08aed2011-07-01 19:47:50 +00002276 MagickBooleanType
2277 status;
2278
2279 ssize_t
2280 y;
2281
2282 assert(image != (Image *) NULL);
2283 if (image->debug != MagickFalse)
2284 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2285 assert(image->signature == MagickSignature);
cristye6178502011-12-23 17:02:29 +00002286 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002287 return(MagickFalse);
cristye6178502011-12-23 17:02:29 +00002288 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod);
cristy574cc262011-08-05 01:23:58 +00002289 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00002290 return(MagickFalse);
2291 status=MagickTrue;
2292 if ((image->compose != CopyCompositeOp) &&
2293 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002294 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002295 {
2296 /*
2297 Tile texture onto the image background.
2298 */
2299#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy13e98362012-01-12 02:08:08 +00002300 #pragma omp parallel for schedule(static,1) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002301#endif
cristye6178502011-12-23 17:02:29 +00002302 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002303 {
2304 register ssize_t
2305 x;
2306
2307 if (status == MagickFalse)
2308 continue;
cristye6178502011-12-23 17:02:29 +00002309 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002310 {
2311 MagickBooleanType
2312 thread_status;
2313
cristye6178502011-12-23 17:02:29 +00002314 thread_status=CompositeImage(image,image->compose,texture_image,x+
2315 texture_image->tile_offset.x,y+texture_image->tile_offset.y,
2316 exception);
cristy4c08aed2011-07-01 19:47:50 +00002317 if (thread_status == MagickFalse)
2318 {
2319 status=thread_status;
2320 break;
2321 }
2322 }
2323 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2324 {
2325 MagickBooleanType
2326 proceed;
2327
2328#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002329 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002330#endif
2331 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2332 y,image->rows);
2333 if (proceed == MagickFalse)
2334 status=MagickFalse;
2335 }
2336 }
2337 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2338 image->rows,image->rows);
2339 return(status);
2340 }
2341 /*
2342 Tile texture onto the image background (optimized).
2343 */
2344 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00002345 image_view=AcquireCacheView(image);
cristye6178502011-12-23 17:02:29 +00002346 texture_view=AcquireCacheView(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002347#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy13e98362012-01-12 02:08:08 +00002348 #pragma omp parallel for schedule(static,1) shared(status)
cristy4c08aed2011-07-01 19:47:50 +00002349#endif
2350 for (y=0; y < (ssize_t) image->rows; y++)
2351 {
2352 MagickBooleanType
2353 sync;
2354
2355 register const Quantum
2356 *p,
2357 *pixels;
2358
2359 register ssize_t
2360 x;
2361
2362 register Quantum
2363 *q;
2364
2365 size_t
2366 width;
2367
2368 if (status == MagickFalse)
2369 continue;
cristye6178502011-12-23 17:02:29 +00002370 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2371 (y+texture_image->tile_offset.y) % texture_image->rows,
2372 texture_image->columns,1,exception);
2373 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002374 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2375 {
2376 status=MagickFalse;
2377 continue;
2378 }
cristye6178502011-12-23 17:02:29 +00002379 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002380 {
2381 register ssize_t
cristye6178502011-12-23 17:02:29 +00002382 j;
cristy4c08aed2011-07-01 19:47:50 +00002383
2384 p=pixels;
cristye6178502011-12-23 17:02:29 +00002385 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002386 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2387 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002388 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002389 {
cristye6178502011-12-23 17:02:29 +00002390 register ssize_t
2391 i;
2392
2393 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2394 {
2395 PixelChannel
2396 channel;
2397
2398 PixelTrait
2399 texture_traits,
2400 traits;
2401
2402 channel=GetPixelChannelMapChannel(texture_image,i);
2403 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2404 traits=GetPixelChannelMapTraits(image,channel);
2405 if ((traits == UndefinedPixelTrait) ||
2406 (texture_traits == UndefinedPixelTrait))
2407 continue;
2408 SetPixelChannel(image,channel,p[i],q);
2409 }
2410 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002411 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002412 }
2413 }
2414 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2415 if (sync == MagickFalse)
2416 status=MagickFalse;
2417 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2418 {
2419 MagickBooleanType
2420 proceed;
2421
2422#if defined(MAGICKCORE_OPENMP_SUPPORT)
2423 #pragma omp critical (MagickCore_TextureImage)
2424#endif
2425 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2426 image->rows);
2427 if (proceed == MagickFalse)
2428 status=MagickFalse;
2429 }
2430 }
2431 texture_view=DestroyCacheView(texture_view);
2432 image_view=DestroyCacheView(image_view);
2433 return(status);
2434}