blob: ea9a82dd029c5f4898a1855f4a4bc72ce8bb1f7c [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
534 ssize_t
535 y;
536
cristye4a40472011-12-22 02:56:19 +0000537 size_t
538 channels;
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 */
cristye4a40472011-12-22 02:56:19 +0000587
cristy4c08aed2011-07-01 19:47:50 +0000588 assert(image != (Image *) NULL);
589 assert(image->signature == MagickSignature);
590 if (image->debug != MagickFalse)
591 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
592 assert(composite_image != (Image *) NULL);
593 assert(composite_image->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000594 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000595 return(MagickFalse);
cristye4a40472011-12-22 02:56:19 +0000596 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
597 {
598 status=CompositeOverImage(image,composite_image,x_offset,y_offset,
599 exception);
600 return(status);
601 }
cristy4c08aed2011-07-01 19:47:50 +0000602 destination_image=(Image *) NULL;
603 amount=0.5;
604 destination_dissolve=1.0;
605 modify_outside_overlay=MagickFalse;
606 percent_brightness=100.0;
607 percent_saturation=100.0;
608 source_dissolve=1.0;
609 threshold=0.05f;
610 switch (compose)
611 {
612 case ClearCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000613 case DstAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +0000614 case DstInCompositeOp:
615 case InCompositeOp:
616 case OutCompositeOp:
617 case SrcCompositeOp:
618 case SrcInCompositeOp:
619 case SrcOutCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000620 {
621 /*
622 Modify destination outside the overlaid region.
623 */
624 modify_outside_overlay=MagickTrue;
625 break;
626 }
627 case CopyCompositeOp:
628 {
629 if ((x_offset < 0) || (y_offset < 0))
630 break;
631 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
632 break;
633 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
634 break;
635 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000636 image_view=AcquireCacheView(image);
637 composite_view=AcquireCacheView(composite_image);
638#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000639 #pragma omp parallel for schedule(static,4) shared(status)
cristy4c08aed2011-07-01 19:47:50 +0000640#endif
641 for (y=0; y < (ssize_t) composite_image->rows; y++)
642 {
643 MagickBooleanType
644 sync;
645
646 register const Quantum
647 *p;
648
649 register Quantum
650 *q;
651
652 register ssize_t
653 x;
654
655 if (status == MagickFalse)
656 continue;
657 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
658 1,exception);
659 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
660 composite_image->columns,1,exception);
661 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
662 {
663 status=MagickFalse;
664 continue;
665 }
666 for (x=0; x < (ssize_t) composite_image->columns; x++)
667 {
cristybdecccc2011-12-24 22:52:16 +0000668 register ssize_t
669 i;
670
671 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
672 {
673 PixelChannel
674 channel;
675
676 PixelTrait
677 composite_traits,
678 traits;
679
680 channel=GetPixelChannelMapChannel(composite_image,i);
681 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
682 traits=GetPixelChannelMapTraits(image,channel);
683 if ((traits == UndefinedPixelTrait) ||
684 (composite_traits == UndefinedPixelTrait))
685 continue;
686 SetPixelChannel(image,channel,p[i],q);
687 }
cristyed231572011-07-14 02:18:59 +0000688 p+=GetPixelChannels(composite_image);
689 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000690 }
691 sync=SyncCacheViewAuthenticPixels(image_view,exception);
692 if (sync == MagickFalse)
693 status=MagickFalse;
694 if (image->progress_monitor != (MagickProgressMonitor) NULL)
695 {
696 MagickBooleanType
697 proceed;
698
699#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000700 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000701#endif
702 proceed=SetImageProgress(image,CompositeImageTag,
703 (MagickOffsetType) y,image->rows);
704 if (proceed == MagickFalse)
705 status=MagickFalse;
706 }
707 }
708 composite_view=DestroyCacheView(composite_view);
709 image_view=DestroyCacheView(image_view);
710 return(status);
711 }
cristye4a40472011-12-22 02:56:19 +0000712 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000713 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000714 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000715 {
716 /*
717 Modify destination outside the overlaid region and require an alpha
718 channel to exist, to add transparency.
719 */
720 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +0000721 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000722 modify_outside_overlay=MagickTrue;
723 break;
724 }
725 case BlurCompositeOp:
726 {
727 CacheView
728 *composite_view,
729 *destination_view;
730
731 PixelInfo
732 pixel;
733
734 MagickRealType
735 angle_range,
736 angle_start,
737 height,
738 width;
739
740 ResampleFilter
741 *resample_filter;
742
743 SegmentInfo
744 blur;
745
746 /*
747 Blur Image dictated by an overlay gradient map: X = red_channel;
748 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
749 */
750 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000751 exception);
cristy4c08aed2011-07-01 19:47:50 +0000752 if (destination_image == (Image *) NULL)
753 return(MagickFalse);
754 /*
755 Determine the horizontal and vertical maximim blur.
756 */
757 SetGeometryInfo(&geometry_info);
758 flags=NoValue;
759 value=GetImageArtifact(composite_image,"compose:args");
760 if (value != (char *) NULL)
761 flags=ParseGeometry(value,&geometry_info);
762 if ((flags & WidthValue) == 0 )
763 {
764 destination_image=DestroyImage(destination_image);
765 return(MagickFalse);
766 }
767 width=geometry_info.rho;
768 height=geometry_info.sigma;
769 blur.x1=geometry_info.rho;
770 blur.x2=0.0;
771 blur.y1=0.0;
772 blur.y2=geometry_info.sigma;
773 angle_start=0.0;
774 angle_range=0.0;
775 if ((flags & HeightValue) == 0)
776 blur.y2=blur.x1;
777 if ((flags & XValue) != 0 )
778 {
779 MagickRealType
780 angle;
781
782 angle=DegreesToRadians(geometry_info.xi);
783 blur.x1=width*cos(angle);
784 blur.x2=width*sin(angle);
785 blur.y1=(-height*sin(angle));
786 blur.y2=height*cos(angle);
787 }
788 if ((flags & YValue) != 0 )
789 {
790 angle_start=DegreesToRadians(geometry_info.xi);
791 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
792 }
793 /*
794 Blur Image by resampling.
795 */
cristy8a11cb12011-10-19 23:53:34 +0000796 resample_filter=AcquireResampleFilter(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000797 SetResampleFilter(resample_filter,CubicFilter,2.0);
798 destination_view=AcquireCacheView(destination_image);
799 composite_view=AcquireCacheView(composite_image);
800 for (y=0; y < (ssize_t) composite_image->rows; y++)
801 {
802 MagickBooleanType
803 sync;
804
805 register const Quantum
806 *restrict p;
807
808 register Quantum
809 *restrict q;
810
811 register ssize_t
812 x;
813
814 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
815 continue;
816 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
817 1,exception);
818 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000819 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000820 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
821 break;
822 for (x=0; x < (ssize_t) composite_image->columns; x++)
823 {
824 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
825 {
cristyed231572011-07-14 02:18:59 +0000826 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000827 continue;
828 }
829 if (fabs(angle_range) > MagickEpsilon)
830 {
831 MagickRealType
832 angle;
833
834 angle=angle_start+angle_range*QuantumScale*
835 GetPixelBlue(composite_image,p);
836 blur.x1=width*cos(angle);
837 blur.x2=width*sin(angle);
838 blur.y1=(-height*sin(angle));
839 blur.y2=height*cos(angle);
840 }
841 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
842 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
843 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
844 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
845 GetPixelGreen(composite_image,p));
846 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
847 (double) y_offset+y,&pixel);
cristy803640d2011-11-17 02:11:32 +0000848 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000849 p+=GetPixelChannels(composite_image);
850 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000851 }
852 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
853 if (sync == MagickFalse)
854 break;
855 }
856 resample_filter=DestroyResampleFilter(resample_filter);
857 composite_view=DestroyCacheView(composite_view);
858 destination_view=DestroyCacheView(destination_view);
859 composite_image=destination_image;
860 break;
861 }
862 case DisplaceCompositeOp:
863 case DistortCompositeOp:
864 {
865 CacheView
866 *composite_view,
867 *destination_view,
868 *image_view;
869
870 PixelInfo
871 pixel;
872
873 MagickRealType
874 horizontal_scale,
875 vertical_scale;
876
877 PointInfo
878 center,
879 offset;
880
881 /*
882 Displace/Distort based on overlay gradient map:
883 X = red_channel; Y = green_channel;
884 compose:args = x_scale[,y_scale[,center.x,center.y]]
885 */
886 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000887 exception);
cristy4c08aed2011-07-01 19:47:50 +0000888 if (destination_image == (Image *) NULL)
889 return(MagickFalse);
890 SetGeometryInfo(&geometry_info);
891 flags=NoValue;
892 value=GetImageArtifact(composite_image,"compose:args");
893 if (value != (char *) NULL)
894 flags=ParseGeometry(value,&geometry_info);
895 if ((flags & (WidthValue|HeightValue)) == 0 )
896 {
897 if ((flags & AspectValue) == 0)
898 {
899 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
900 2.0;
901 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
902 }
903 else
904 {
905 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
906 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
907 }
908 }
909 else
910 {
911 horizontal_scale=geometry_info.rho;
912 vertical_scale=geometry_info.sigma;
913 if ((flags & PercentValue) != 0)
914 {
915 if ((flags & AspectValue) == 0)
916 {
917 horizontal_scale*=(composite_image->columns-1.0)/200.0;
918 vertical_scale*=(composite_image->rows-1.0)/200.0;
919 }
920 else
921 {
922 horizontal_scale*=(image->columns-1.0)/200.0;
923 vertical_scale*=(image->rows-1.0)/200.0;
924 }
925 }
926 if ((flags & HeightValue) == 0)
927 vertical_scale=horizontal_scale;
928 }
929 /*
930 Determine fixed center point for absolute distortion map
931 Absolute distort ==
932 Displace offset relative to a fixed absolute point
933 Select that point according to +X+Y user inputs.
934 default = center of overlay image
935 arg flag '!' = locations/percentage relative to background image
936 */
937 center.x=(MagickRealType) x_offset;
938 center.y=(MagickRealType) y_offset;
939 if (compose == DistortCompositeOp)
940 {
941 if ((flags & XValue) == 0)
942 if ((flags & AspectValue) == 0)
943 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
944 2.0;
945 else
946 center.x=((MagickRealType) image->columns-1)/2.0;
947 else
948 if ((flags & AspectValue) == 0)
949 center.x=(MagickRealType) x_offset+geometry_info.xi;
950 else
951 center.x=geometry_info.xi;
952 if ((flags & YValue) == 0)
953 if ((flags & AspectValue) == 0)
954 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
955 else
956 center.y=((MagickRealType) image->rows-1)/2.0;
957 else
958 if ((flags & AspectValue) == 0)
959 center.y=(MagickRealType) y_offset+geometry_info.psi;
960 else
961 center.y=geometry_info.psi;
962 }
963 /*
964 Shift the pixel offset point as defined by the provided,
965 displacement/distortion map. -- Like a lens...
966 */
cristye10859a2011-12-18 22:28:59 +0000967 GetPixelInfo(image,&pixel);
cristy4c08aed2011-07-01 19:47:50 +0000968 image_view=AcquireCacheView(image);
969 destination_view=AcquireCacheView(destination_image);
970 composite_view=AcquireCacheView(composite_image);
971 for (y=0; y < (ssize_t) composite_image->rows; y++)
972 {
973 MagickBooleanType
974 sync;
975
976 register const Quantum
977 *restrict p;
978
979 register Quantum
980 *restrict q;
981
982 register ssize_t
983 x;
984
985 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
986 continue;
987 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
988 1,exception);
989 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000990 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000991 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
992 break;
993 for (x=0; x < (ssize_t) composite_image->columns; x++)
994 {
995 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
996 {
cristyed231572011-07-14 02:18:59 +0000997 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000998 continue;
999 }
1000 /*
1001 Displace the offset.
1002 */
1003 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1004 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1005 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1006 x : 0);
1007 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1008 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1009 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1010 y : 0);
1011 (void) InterpolatePixelInfo(image,image_view,
1012 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1013 &pixel,exception);
1014 /*
1015 Mask with the 'invalid pixel mask' in alpha channel.
1016 */
1017 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001018 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001019 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001020 p+=GetPixelChannels(composite_image);
1021 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001022 }
1023 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1024 if (sync == MagickFalse)
1025 break;
1026 }
1027 destination_view=DestroyCacheView(destination_view);
1028 composite_view=DestroyCacheView(composite_view);
1029 image_view=DestroyCacheView(image_view);
1030 composite_image=destination_image;
1031 break;
1032 }
1033 case DissolveCompositeOp:
1034 {
1035 /*
1036 Geometry arguments to dissolve factors.
1037 */
1038 value=GetImageArtifact(composite_image,"compose:args");
1039 if (value != (char *) NULL)
1040 {
1041 flags=ParseGeometry(value,&geometry_info);
1042 source_dissolve=geometry_info.rho/100.0;
1043 destination_dissolve=1.0;
1044 if ((source_dissolve-MagickEpsilon) < 0.0)
1045 source_dissolve=0.0;
1046 if ((source_dissolve+MagickEpsilon) > 1.0)
1047 {
1048 destination_dissolve=2.0-source_dissolve;
1049 source_dissolve=1.0;
1050 }
1051 if ((flags & SigmaValue) != 0)
1052 destination_dissolve=geometry_info.sigma/100.0;
1053 if ((destination_dissolve-MagickEpsilon) < 0.0)
1054 destination_dissolve=0.0;
1055 modify_outside_overlay=MagickTrue;
1056 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1057 {
1058 destination_dissolve=1.0;
1059 modify_outside_overlay=MagickFalse;
1060 }
1061 }
1062 break;
1063 }
1064 case BlendCompositeOp:
1065 {
1066 value=GetImageArtifact(composite_image,"compose:args");
1067 if (value != (char *) NULL)
1068 {
1069 flags=ParseGeometry(value,&geometry_info);
1070 source_dissolve=geometry_info.rho/100.0;
1071 destination_dissolve=1.0-source_dissolve;
1072 if ((flags & SigmaValue) != 0)
1073 destination_dissolve=geometry_info.sigma/100.0;
1074 modify_outside_overlay=MagickTrue;
1075 if ((destination_dissolve+MagickEpsilon) > 1.0)
1076 modify_outside_overlay=MagickFalse;
1077 }
1078 break;
1079 }
1080 case MathematicsCompositeOp:
1081 {
1082 /*
1083 Just collect the values from "compose:args", setting.
1084 Unused values are set to zero automagically.
1085
1086 Arguments are normally a comma separated list, so this probably should
1087 be changed to some 'general comma list' parser, (with a minimum
1088 number of values)
1089 */
1090 SetGeometryInfo(&geometry_info);
1091 value=GetImageArtifact(composite_image,"compose:args");
1092 if (value != (char *) NULL)
1093 (void) ParseGeometry(value,&geometry_info);
1094 break;
1095 }
1096 case ModulateCompositeOp:
1097 {
1098 /*
1099 Determine the brightness and saturation scale.
1100 */
1101 value=GetImageArtifact(composite_image,"compose:args");
1102 if (value != (char *) NULL)
1103 {
1104 flags=ParseGeometry(value,&geometry_info);
1105 percent_brightness=geometry_info.rho;
1106 if ((flags & SigmaValue) != 0)
1107 percent_saturation=geometry_info.sigma;
1108 }
1109 break;
1110 }
1111 case ThresholdCompositeOp:
1112 {
1113 /*
1114 Determine the amount and threshold.
1115 */
1116 value=GetImageArtifact(composite_image,"compose:args");
1117 if (value != (char *) NULL)
1118 {
1119 flags=ParseGeometry(value,&geometry_info);
1120 amount=geometry_info.rho;
1121 threshold=geometry_info.sigma;
1122 if ((flags & SigmaValue) == 0)
1123 threshold=0.05f;
1124 }
1125 threshold*=QuantumRange;
1126 break;
1127 }
1128 default:
1129 break;
1130 }
1131 value=GetImageArtifact(composite_image,"compose:outside-overlay");
1132 if (value != (const char *) NULL)
1133 modify_outside_overlay=IsMagickTrue(value);
1134 /*
1135 Composite image.
1136 */
1137 status=MagickTrue;
1138 progress=0;
1139 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy4c08aed2011-07-01 19:47:50 +00001140 image_view=AcquireCacheView(image);
1141 composite_view=AcquireCacheView(composite_image);
1142#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00001143 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy4c08aed2011-07-01 19:47:50 +00001144#endif
1145 for (y=0; y < (ssize_t) image->rows; y++)
1146 {
1147 const Quantum
1148 *pixels;
1149
1150 double
cristye4a40472011-12-22 02:56:19 +00001151 blue,
cristy4c08aed2011-07-01 19:47:50 +00001152 brightness,
cristye4a40472011-12-22 02:56:19 +00001153 green,
cristy4c08aed2011-07-01 19:47:50 +00001154 hue,
cristye4a40472011-12-22 02:56:19 +00001155 red,
cristy4c08aed2011-07-01 19:47:50 +00001156 saturation;
1157
cristy4c08aed2011-07-01 19:47:50 +00001158 register const Quantum
1159 *restrict p;
1160
1161 register Quantum
1162 *restrict q;
1163
1164 register ssize_t
1165 x;
1166
1167 if (status == MagickFalse)
1168 continue;
1169 if (modify_outside_overlay == MagickFalse)
1170 {
1171 if (y < y_offset)
1172 continue;
1173 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1174 continue;
1175 }
1176 /*
1177 If pixels is NULL, y is outside overlay region.
1178 */
1179 pixels=(Quantum *) NULL;
1180 p=(Quantum *) NULL;
1181 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1182 {
1183 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1184 composite_image->columns,1,exception);
1185 if (p == (const Quantum *) NULL)
1186 {
1187 status=MagickFalse;
1188 continue;
1189 }
1190 pixels=p;
1191 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001192 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001193 }
1194 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001195 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001196 {
1197 status=MagickFalse;
1198 continue;
1199 }
cristy4c08aed2011-07-01 19:47:50 +00001200 hue=0.0;
1201 saturation=0.0;
1202 brightness=0.0;
1203 for (x=0; x < (ssize_t) image->columns; x++)
1204 {
cristye4a40472011-12-22 02:56:19 +00001205 MagickRealType
1206 alpha,
1207 Da,
1208 Dc,
1209 Dca,
1210 gamma,
1211 Sa,
1212 Sc,
1213 Sca;
1214
1215 register ssize_t
1216 i;
1217
cristy4c08aed2011-07-01 19:47:50 +00001218 if (modify_outside_overlay == MagickFalse)
1219 {
1220 if (x < x_offset)
1221 {
cristyed231572011-07-14 02:18:59 +00001222 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001223 continue;
1224 }
1225 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1226 break;
1227 }
cristye4a40472011-12-22 02:56:19 +00001228 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1229 ((x-x_offset) >= (ssize_t) composite_image->columns))
1230 {
1231 Quantum
1232 source[MaxPixelChannels];
1233
1234 /*
1235 Virtual composite:
1236 Sc: source color.
1237 Dc: destination color.
1238 */
1239 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1240 source,exception);
1241 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1242 {
1243 MagickRealType
1244 pixel;
1245
1246 PixelChannel
1247 channel;
1248
1249 PixelTrait
1250 composite_traits,
1251 traits;
1252
1253 channel=GetPixelChannelMapChannel(image,i);
1254 traits=GetPixelChannelMapTraits(image,channel);
1255 composite_traits=GetPixelChannelMapTraits(composite_image,
1256 channel);
1257 if ((traits == UndefinedPixelTrait) ||
1258 (composite_traits == UndefinedPixelTrait))
1259 continue;
1260 switch (compose)
1261 {
1262 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;
1425 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 {
cristye4a40472011-12-22 02:56:19 +00001449 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001450 case CopyBlackCompositeOp:
1451 case CopyBlueCompositeOp:
1452 case CopyCyanCompositeOp:
1453 case CopyGreenCompositeOp:
1454 case CopyMagentaCompositeOp:
1455 case CopyRedCompositeOp:
1456 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001457 case SrcAtopCompositeOp:
1458 case DstCompositeOp:
1459 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001460 {
cristye4a40472011-12-22 02:56:19 +00001461 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001462 break;
1463 }
cristye10859a2011-12-18 22:28:59 +00001464 case ChangeMaskCompositeOp:
1465 {
cristye4a40472011-12-22 02:56:19 +00001466 MagickBooleanType
1467 equivalent;
1468
cristy99abff32011-12-24 20:45:16 +00001469 if (Da > ((MagickRealType) QuantumRange/2.0))
1470 {
1471 pixel=(MagickRealType) TransparentAlpha;
1472 break;
1473 }
cristye4a40472011-12-22 02:56:19 +00001474 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001475 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001476 {
1477 pixel=(MagickRealType) TransparentAlpha;
1478 break;
1479 }
1480 pixel=(MagickRealType) OpaqueAlpha;
1481 break;
1482 }
cristy99abff32011-12-24 20:45:16 +00001483 case ClearCompositeOp:
1484 {
1485 pixel=(MagickRealType) TransparentAlpha;
1486 break;
1487 }
1488 case ColorizeCompositeOp:
1489 case HueCompositeOp:
1490 case LuminizeCompositeOp:
1491 case SaturateCompositeOp:
1492 {
1493 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1494 {
1495 pixel=QuantumRange*Da;
1496 break;
1497 }
1498 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1499 {
1500 pixel=QuantumRange*Sa;
1501 break;
1502 }
1503 if (Sa < Da)
1504 {
1505 pixel=QuantumRange*Da;
1506 break;
1507 }
1508 pixel=QuantumRange*Sa;
1509 break;
1510 }
cristye4a40472011-12-22 02:56:19 +00001511 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001512 case CopyAlphaCompositeOp:
1513 case DisplaceCompositeOp:
1514 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001515 case DstAtopCompositeOp:
1516 case ReplaceCompositeOp:
1517 case SrcCompositeOp:
1518 {
1519 pixel=QuantumRange*Sa;
1520 break;
1521 }
1522 case DarkenIntensityCompositeOp:
1523 {
cristy99abff32011-12-24 20:45:16 +00001524 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1525 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001526 break;
1527 }
cristy98621462011-12-31 22:31:11 +00001528 case IntensityCompositeOp:
1529 {
1530 pixel=GetPixelIntensity(composite_image,p);
1531 break;
1532 }
cristye4a40472011-12-22 02:56:19 +00001533 case LightenIntensityCompositeOp:
1534 {
1535 pixel=Sa*GetPixelIntensity(composite_image,p) >
1536 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001537 break;
1538 }
cristy99abff32011-12-24 20:45:16 +00001539 case ModulateCompositeOp:
1540 {
1541 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1542 {
1543 pixel=QuantumRange*Da;
1544 break;
1545 }
1546 pixel=QuantumRange*Da;
1547 break;
1548 }
cristye10859a2011-12-18 22:28:59 +00001549 default:
1550 {
cristye4a40472011-12-22 02:56:19 +00001551 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001552 break;
1553 }
1554 }
cristye4a40472011-12-22 02:56:19 +00001555 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001556 continue;
1557 }
1558 /*
cristy99abff32011-12-24 20:45:16 +00001559 Porter-Duff compositions:
1560 Sca: source normalized color multiplied by alpha.
1561 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001562 */
cristy99abff32011-12-24 20:45:16 +00001563 Sca=QuantumScale*Sa*Sc;
1564 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001565 switch (compose)
1566 {
cristye10859a2011-12-18 22:28:59 +00001567 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001568 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001569 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001570 {
cristy99abff32011-12-24 20:45:16 +00001571 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001572 break;
1573 }
1574 default:
1575 break;
1576 }
cristye4a40472011-12-22 02:56:19 +00001577 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
1578 switch (compose)
1579 {
1580 case AtopCompositeOp:
1581 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001582 {
cristye4a40472011-12-22 02:56:19 +00001583 pixel=Sc*Sa+Dc*(1.0-Sa);
1584 break;
cristye10859a2011-12-18 22:28:59 +00001585 }
cristye4a40472011-12-22 02:56:19 +00001586 case BlendCompositeOp:
1587 {
1588 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1589 break;
1590 }
1591 case BlurCompositeOp:
1592 case DisplaceCompositeOp:
1593 case DistortCompositeOp:
1594 case CopyCompositeOp:
1595 case ReplaceCompositeOp:
1596 case SrcCompositeOp:
1597 {
1598 pixel=Sc;
1599 break;
1600 }
1601 case BumpmapCompositeOp:
1602 {
cristy99abff32011-12-24 20:45:16 +00001603 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001604 {
1605 pixel=Dc;
1606 break;
1607 }
1608 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1609 break;
1610 }
cristy99abff32011-12-24 20:45:16 +00001611 case ChangeMaskCompositeOp:
1612 {
1613 pixel=Dc;
1614 break;
1615 }
1616 case ClearCompositeOp:
1617 {
1618 pixel=0.0;
1619 break;
1620 }
cristye4a40472011-12-22 02:56:19 +00001621 case ColorBurnCompositeOp:
1622 {
1623 /*
1624 Refer to the March 2009 SVG specification.
1625 */
1626 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1627 {
cristy99abff32011-12-24 20:45:16 +00001628 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001629 break;
1630 }
1631 if (Sca < MagickEpsilon)
1632 {
cristy99abff32011-12-24 20:45:16 +00001633 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001634 break;
1635 }
cristy99abff32011-12-24 20:45:16 +00001636 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1637 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001638 break;
1639 }
1640 case ColorDodgeCompositeOp:
1641 {
1642 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1643 {
cristy99abff32011-12-24 20:45:16 +00001644 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001645 break;
1646 }
1647 if (fabs(Sca-Sa) < MagickEpsilon)
1648 {
cristy99abff32011-12-24 20:45:16 +00001649 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001650 break;
1651 }
cristy99abff32011-12-24 20:45:16 +00001652 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001653 (1.0-Sa));
1654 break;
1655 }
1656 case ColorizeCompositeOp:
1657 {
cristy99abff32011-12-24 20:45:16 +00001658 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001659 {
1660 pixel=Dc;
1661 break;
1662 }
cristy99abff32011-12-24 20:45:16 +00001663 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001664 {
1665 pixel=Sc;
1666 break;
1667 }
1668 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
cristy99abff32011-12-24 20:45:16 +00001669 GetPixelBlue(image,q),&sans,&sans,&brightness);
cristye4a40472011-12-22 02:56:19 +00001670 CompositeHSB(GetPixelRed(composite_image,p),
1671 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1672 &hue,&saturation,&sans);
1673 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1674 switch (channel)
1675 {
1676 case RedPixelChannel: pixel=red; break;
1677 case GreenPixelChannel: pixel=green; break;
1678 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001679 default: pixel=Dc; break;
1680 }
1681 break;
1682 }
cristye4a40472011-12-22 02:56:19 +00001683 case CopyAlphaCompositeOp:
cristy98621462011-12-31 22:31:11 +00001684 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001685 {
cristye4a40472011-12-22 02:56:19 +00001686 pixel=Dc;
1687 break;
1688 }
1689 case CopyBlackCompositeOp:
1690 {
1691 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
1692 break;
1693 }
1694 case CopyBlueCompositeOp:
1695 case CopyYellowCompositeOp:
1696 {
1697 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
1698 break;
1699 }
1700 case CopyGreenCompositeOp:
1701 case CopyMagentaCompositeOp:
1702 {
1703 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
1704 break;
1705 }
1706 case CopyRedCompositeOp:
1707 case CopyCyanCompositeOp:
1708 {
1709 pixel=(MagickRealType) GetPixelRed(composite_image,p);
1710 break;
1711 }
cristy99abff32011-12-24 20:45:16 +00001712 case DarkenCompositeOp:
1713 {
1714 /*
1715 Darken is equivalent to a 'Minimum' method
1716 OR a greyscale version of a binary 'Or'
1717 OR the 'Intersection' of pixel sets.
1718 */
1719 if (Sc < Dc)
1720 {
1721 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1722 break;
1723 }
1724 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1725 break;
1726 }
cristye4a40472011-12-22 02:56:19 +00001727 case DarkenIntensityCompositeOp:
1728 {
cristy99abff32011-12-24 20:45:16 +00001729 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1730 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001731 break;
1732 }
1733 case DifferenceCompositeOp:
1734 {
1735 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1736 break;
1737 }
1738 case DissolveCompositeOp:
1739 {
1740 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1741 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1742 break;
1743 }
1744 case DivideDstCompositeOp:
1745 {
1746 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1747 {
cristy99abff32011-12-24 20:45:16 +00001748 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001749 break;
1750 }
1751 if (fabs(Dca) < MagickEpsilon)
1752 {
cristy99abff32011-12-24 20:45:16 +00001753 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001754 break;
1755 }
cristy99abff32011-12-24 20:45:16 +00001756 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001757 break;
1758 }
1759 case DivideSrcCompositeOp:
1760 {
1761 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1762 {
cristy99abff32011-12-24 20:45:16 +00001763 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001764 break;
1765 }
1766 if (fabs(Sca) < MagickEpsilon)
1767 {
cristy99abff32011-12-24 20:45:16 +00001768 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001769 break;
1770 }
cristy99abff32011-12-24 20:45:16 +00001771 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001772 break;
1773 }
1774 case DstAtopCompositeOp:
1775 {
1776 pixel=Dc*Da+Sc*(1.0-Da);
1777 break;
1778 }
1779 case DstCompositeOp:
1780 case NoCompositeOp:
1781 {
1782 pixel=Dc;
1783 break;
1784 }
1785 case DstInCompositeOp:
1786 {
1787 pixel=gamma*(Sa*Dc*Sa);
1788 break;
1789 }
1790 case DstOutCompositeOp:
1791 {
1792 pixel=gamma*(Da*Dc*(1.0-Sa));
1793 break;
1794 }
1795 case DstOverCompositeOp:
1796 {
1797 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1798 break;
1799 }
1800 case ExclusionCompositeOp:
1801 {
cristy99abff32011-12-24 20:45:16 +00001802 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1803 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001804 break;
1805 }
1806 case HardLightCompositeOp:
1807 {
1808 if ((2.0*Sca) < Sa)
1809 {
cristy99abff32011-12-24 20:45:16 +00001810 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001811 (1.0-Sa));
1812 break;
1813 }
cristy99abff32011-12-24 20:45:16 +00001814 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001815 Dca*(1.0-Sa));
1816 break;
1817 }
1818 case HueCompositeOp:
1819 {
cristy99abff32011-12-24 20:45:16 +00001820 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001821 {
1822 pixel=Dc;
1823 break;
1824 }
cristy99abff32011-12-24 20:45:16 +00001825 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001826 {
1827 pixel=Sc;
1828 break;
1829 }
1830 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1831 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1832 CompositeHSB(GetPixelRed(composite_image,p),
1833 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1834 &hue,&sans,&sans);
1835 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1836 switch (channel)
1837 {
1838 case RedPixelChannel: pixel=red; break;
1839 case GreenPixelChannel: pixel=green; break;
1840 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001841 default: pixel=Dc; break;
1842 }
1843 break;
1844 }
1845 case InCompositeOp:
1846 case SrcInCompositeOp:
1847 {
1848 pixel=gamma*(Da*Sc*Da);
1849 break;
1850 }
cristy99abff32011-12-24 20:45:16 +00001851 case LinearBurnCompositeOp:
1852 {
1853 /*
1854 LinearBurn: as defined by Abode Photoshop, according to
1855 http://www.simplefilter.de/en/basics/mixmods.html is:
1856
1857 f(Sc,Dc) = Sc + Dc - 1
1858 */
1859 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1860 break;
1861 }
1862 case LinearDodgeCompositeOp:
1863 {
1864 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1865 break;
1866 }
1867 case LinearLightCompositeOp:
1868 {
1869 /*
1870 LinearLight: as defined by Abode Photoshop, according to
1871 http://www.simplefilter.de/en/basics/mixmods.html is:
1872
1873 f(Sc,Dc) = Dc + 2*Sc - 1
1874 */
1875 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1876 break;
1877 }
1878 case LightenCompositeOp:
1879 {
1880 if (Sc > Dc)
1881 {
1882 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1883 break;
1884 }
1885 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1886 break;
1887 }
cristye4a40472011-12-22 02:56:19 +00001888 case LightenIntensityCompositeOp:
1889 {
1890 /*
1891 Lighten is equivalent to a 'Maximum' method
1892 OR a greyscale version of a binary 'And'
1893 OR the 'Union' of pixel sets.
1894 */
1895 pixel=Sa*GetPixelIntensity(composite_image,p) >
1896 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1897 break;
1898 }
cristye4a40472011-12-22 02:56:19 +00001899 case LuminizeCompositeOp:
1900 {
cristy99abff32011-12-24 20:45:16 +00001901 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001902 {
1903 pixel=Dc;
1904 break;
1905 }
cristy99abff32011-12-24 20:45:16 +00001906 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001907 {
1908 pixel=Sc;
1909 break;
1910 }
1911 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1912 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1913 CompositeHSB(GetPixelRed(composite_image,p),
1914 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1915 &sans,&sans,&brightness);
1916 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1917 switch (channel)
1918 {
1919 case RedPixelChannel: pixel=red; break;
1920 case GreenPixelChannel: pixel=green; break;
1921 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001922 default: pixel=Dc; break;
1923 }
1924 break;
1925 }
1926 case MathematicsCompositeOp:
1927 {
1928 /*
1929 'Mathematics' a free form user control mathematical composition
1930 is defined as...
1931
1932 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
1933
1934 Where the arguments A,B,C,D are (currently) passed to composite
1935 as a command separated 'geometry' string in "compose:args" image
1936 artifact.
1937
1938 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
1939
1940 Applying the SVG transparency formula (see above), we get...
1941
1942 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
1943
1944 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
1945 Dca*(1.0-Sa)
1946 */
1947 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
1948 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
1949 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
1950 break;
1951 }
1952 case MinusDstCompositeOp:
1953 {
1954 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
1955 break;
1956 }
1957 case MinusSrcCompositeOp:
1958 {
1959 /*
1960 Minus source from destination.
1961
1962 f(Sc,Dc) = Sc - Dc
1963 */
cristy99abff32011-12-24 20:45:16 +00001964 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00001965 break;
1966 }
1967 case ModulateCompositeOp:
1968 {
1969 ssize_t
1970 offset;
1971
cristy99abff32011-12-24 20:45:16 +00001972 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001973 {
1974 pixel=Dc;
1975 break;
1976 }
1977 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
1978 if (offset == 0)
1979 {
1980 pixel=Dc;
1981 break;
1982 }
1983 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1984 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1985 brightness+=(0.01*percent_brightness*offset)/midpoint;
1986 saturation*=0.01*percent_saturation;
1987 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1988 switch (channel)
1989 {
1990 case RedPixelChannel: pixel=red; break;
1991 case GreenPixelChannel: pixel=green; break;
1992 case BluePixelChannel: pixel=blue; break;
1993 default: pixel=Dc; break;
1994 }
1995 break;
1996 }
1997 case ModulusAddCompositeOp:
1998 {
1999 pixel=Sc+Dc;
2000 if (pixel > QuantumRange)
2001 pixel-=(QuantumRange+1.0);
2002 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2003 break;
2004 }
2005 case ModulusSubtractCompositeOp:
2006 {
cristy99abff32011-12-24 20:45:16 +00002007 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002008 if (pixel < 0.0)
2009 pixel+=(QuantumRange+1.0);
2010 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2011 break;
2012 }
2013 case MultiplyCompositeOp:
2014 {
cristy99abff32011-12-24 20:45:16 +00002015 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002016 break;
2017 }
2018 case OutCompositeOp:
2019 case SrcOutCompositeOp:
2020 {
2021 pixel=gamma*(Sa*Sc*(1.0-Da));
2022 break;
2023 }
2024 case OverCompositeOp:
2025 case SrcOverCompositeOp:
2026 {
cristy99abff32011-12-24 20:45:16 +00002027 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002028 break;
2029 }
2030 case OverlayCompositeOp:
2031 {
2032 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002033 {
2034 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2035 (1.0-Da));
2036 break;
2037 }
2038 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2039 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002040 break;
2041 }
2042 case PegtopLightCompositeOp:
2043 {
2044 /*
2045 PegTop: A Soft-Light alternative: A continuous version of the
2046 Softlight function, producing very similar results.
2047
2048 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2049
2050 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2051 */
2052 if (fabs(Da) < MagickEpsilon)
2053 {
cristy99abff32011-12-24 20:45:16 +00002054 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002055 break;
2056 }
cristy99abff32011-12-24 20:45:16 +00002057 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2058 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002059 break;
2060 }
2061 case PinLightCompositeOp:
2062 {
2063 /*
2064 PinLight: A Photoshop 7 composition method
2065 http://www.simplefilter.de/en/basics/mixmods.html
2066
2067 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2068 */
2069 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2070 {
cristy99abff32011-12-24 20:45:16 +00002071 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002072 break;
2073 }
2074 if ((Dca*Sa) > (2.0*Sca*Da))
2075 {
cristy99abff32011-12-24 20:45:16 +00002076 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002077 break;
2078 }
cristy99abff32011-12-24 20:45:16 +00002079 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002080 break;
2081 }
2082 case PlusCompositeOp:
2083 {
cristy99abff32011-12-24 20:45:16 +00002084 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002085 break;
2086 }
2087 case SaturateCompositeOp:
2088 {
cristy99abff32011-12-24 20:45:16 +00002089 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002090 {
2091 pixel=Dc;
2092 break;
2093 }
cristy99abff32011-12-24 20:45:16 +00002094 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002095 {
2096 pixel=Sc;
2097 break;
2098 }
2099 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2100 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2101 CompositeHSB(GetPixelRed(composite_image,p),
2102 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
2103 &sans,&saturation,&sans);
2104 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2105 switch (channel)
2106 {
2107 case RedPixelChannel: pixel=red; break;
2108 case GreenPixelChannel: pixel=green; break;
2109 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002110 default: pixel=Dc; break;
2111 }
2112 break;
2113 }
2114 case ScreenCompositeOp:
2115 {
2116 /*
2117 Screen: a negated multiply:
2118
2119 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2120 */
cristy99abff32011-12-24 20:45:16 +00002121 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002122 break;
2123 }
2124 case SoftLightCompositeOp:
2125 {
2126 /*
2127 Refer to the March 2009 SVG specification.
2128 */
2129 if ((2.0*Sca) < Sa)
2130 {
cristy99abff32011-12-24 20:45:16 +00002131 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2132 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002133 break;
2134 }
2135 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2136 {
cristy99abff32011-12-24 20:45:16 +00002137 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2138 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2139 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002140 break;
2141 }
cristy99abff32011-12-24 20:45:16 +00002142 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2143 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002144 break;
2145 }
2146 case ThresholdCompositeOp:
2147 {
2148 MagickRealType
2149 delta;
2150
2151 delta=Sc-Dc;
2152 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2153 {
2154 pixel=gamma*Dc;
2155 break;
2156 }
2157 pixel=gamma*(Dc+delta*amount);
2158 break;
2159 }
2160 case VividLightCompositeOp:
2161 {
2162 /*
2163 VividLight: A Photoshop 7 composition method. See
2164 http://www.simplefilter.de/en/basics/mixmods.html.
2165
2166 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2167 */
2168 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2169 {
cristy99abff32011-12-24 20:45:16 +00002170 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002171 break;
2172 }
2173 if ((2.0*Sca) <= Sa)
2174 {
cristy99abff32011-12-24 20:45:16 +00002175 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2176 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002177 break;
2178 }
cristy99abff32011-12-24 20:45:16 +00002179 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2180 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002181 break;
2182 }
2183 case XorCompositeOp:
2184 {
cristy99abff32011-12-24 20:45:16 +00002185 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002186 break;
2187 }
2188 default:
2189 {
2190 pixel=Sc;
2191 break;
2192 }
2193 }
2194 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002195 }
cristyed231572011-07-14 02:18:59 +00002196 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002197 channels=GetPixelChannels(composite_image);
2198 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002199 p=pixels;
cristyed231572011-07-14 02:18:59 +00002200 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002201 }
2202 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2203 status=MagickFalse;
2204 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2205 {
2206 MagickBooleanType
2207 proceed;
2208
2209#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002210 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002211#endif
2212 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2213 image->rows);
2214 if (proceed == MagickFalse)
2215 status=MagickFalse;
2216 }
2217 }
2218 composite_view=DestroyCacheView(composite_view);
2219 image_view=DestroyCacheView(image_view);
2220 if (destination_image != (Image * ) NULL)
2221 destination_image=DestroyImage(destination_image);
2222 return(status);
2223}
2224
2225/*
2226%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2227% %
2228% %
2229% %
2230% T e x t u r e I m a g e %
2231% %
2232% %
2233% %
2234%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2235%
2236% TextureImage() repeatedly tiles the texture image across and down the image
2237% canvas.
2238%
2239% The format of the TextureImage method is:
2240%
cristye6178502011-12-23 17:02:29 +00002241% MagickBooleanType TextureImage(Image *image,const Image *texture_image,
cristye941a752011-10-15 01:52:48 +00002242% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002243%
2244% A description of each parameter follows:
2245%
2246% o image: the image.
2247%
cristye6178502011-12-23 17:02:29 +00002248% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002249%
2250*/
cristye6178502011-12-23 17:02:29 +00002251MagickExport MagickBooleanType TextureImage(Image *image,
2252 const Image *texture_image,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002253{
2254#define TextureImageTag "Texture/Image"
2255
2256 CacheView
2257 *image_view,
2258 *texture_view;
2259
cristy4c08aed2011-07-01 19:47:50 +00002260 MagickBooleanType
2261 status;
2262
2263 ssize_t
2264 y;
2265
2266 assert(image != (Image *) NULL);
2267 if (image->debug != MagickFalse)
2268 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2269 assert(image->signature == MagickSignature);
cristye6178502011-12-23 17:02:29 +00002270 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002271 return(MagickFalse);
cristye6178502011-12-23 17:02:29 +00002272 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod);
cristy574cc262011-08-05 01:23:58 +00002273 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00002274 return(MagickFalse);
2275 status=MagickTrue;
2276 if ((image->compose != CopyCompositeOp) &&
2277 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002278 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002279 {
2280 /*
2281 Tile texture onto the image background.
2282 */
2283#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00002284 #pragma omp parallel for schedule(static,4) shared(status) omp_throttle(1)
cristy4c08aed2011-07-01 19:47:50 +00002285#endif
cristye6178502011-12-23 17:02:29 +00002286 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002287 {
2288 register ssize_t
2289 x;
2290
2291 if (status == MagickFalse)
2292 continue;
cristye6178502011-12-23 17:02:29 +00002293 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002294 {
2295 MagickBooleanType
2296 thread_status;
2297
cristye6178502011-12-23 17:02:29 +00002298 thread_status=CompositeImage(image,image->compose,texture_image,x+
2299 texture_image->tile_offset.x,y+texture_image->tile_offset.y,
2300 exception);
cristy4c08aed2011-07-01 19:47:50 +00002301 if (thread_status == MagickFalse)
2302 {
2303 status=thread_status;
2304 break;
2305 }
2306 }
2307 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2308 {
2309 MagickBooleanType
2310 proceed;
2311
2312#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002313 #pragma omp critical (MagickCore_TextureImage)
cristy4c08aed2011-07-01 19:47:50 +00002314#endif
2315 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2316 y,image->rows);
2317 if (proceed == MagickFalse)
2318 status=MagickFalse;
2319 }
2320 }
2321 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2322 image->rows,image->rows);
2323 return(status);
2324 }
2325 /*
2326 Tile texture onto the image background (optimized).
2327 */
2328 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00002329 image_view=AcquireCacheView(image);
cristye6178502011-12-23 17:02:29 +00002330 texture_view=AcquireCacheView(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002331#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00002332 #pragma omp parallel for schedule(static,4) shared(status) omp_throttle(1)
cristy4c08aed2011-07-01 19:47:50 +00002333#endif
2334 for (y=0; y < (ssize_t) image->rows; y++)
2335 {
2336 MagickBooleanType
2337 sync;
2338
2339 register const Quantum
2340 *p,
2341 *pixels;
2342
2343 register ssize_t
2344 x;
2345
2346 register Quantum
2347 *q;
2348
2349 size_t
2350 width;
2351
2352 if (status == MagickFalse)
2353 continue;
cristye6178502011-12-23 17:02:29 +00002354 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2355 (y+texture_image->tile_offset.y) % texture_image->rows,
2356 texture_image->columns,1,exception);
2357 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002358 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2359 {
2360 status=MagickFalse;
2361 continue;
2362 }
cristye6178502011-12-23 17:02:29 +00002363 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002364 {
2365 register ssize_t
cristye6178502011-12-23 17:02:29 +00002366 j;
cristy4c08aed2011-07-01 19:47:50 +00002367
2368 p=pixels;
cristye6178502011-12-23 17:02:29 +00002369 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002370 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2371 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002372 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002373 {
cristye6178502011-12-23 17:02:29 +00002374 register ssize_t
2375 i;
2376
2377 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2378 {
2379 PixelChannel
2380 channel;
2381
2382 PixelTrait
2383 texture_traits,
2384 traits;
2385
2386 channel=GetPixelChannelMapChannel(texture_image,i);
2387 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2388 traits=GetPixelChannelMapTraits(image,channel);
2389 if ((traits == UndefinedPixelTrait) ||
2390 (texture_traits == UndefinedPixelTrait))
2391 continue;
2392 SetPixelChannel(image,channel,p[i],q);
2393 }
2394 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002395 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002396 }
2397 }
2398 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2399 if (sync == MagickFalse)
2400 status=MagickFalse;
2401 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2402 {
2403 MagickBooleanType
2404 proceed;
2405
2406#if defined(MAGICKCORE_OPENMP_SUPPORT)
2407 #pragma omp critical (MagickCore_TextureImage)
2408#endif
2409 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2410 image->rows);
2411 if (proceed == MagickFalse)
2412 status=MagickFalse;
2413 }
2414 }
2415 texture_view=DestroyCacheView(texture_view);
2416 image_view=DestroyCacheView(image_view);
2417 return(status);
2418}