blob: 0289911b59d6d6b93a0e83c42db561a1e122e77c [file] [log] [blame]
cristy4c08aed2011-07-01 19:47:50 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% CCCC OOO M M PPPP OOO SSSSS IIIII TTTTT EEEEE %
7% C O O MM MM P P O O SS I T E %
8% C O O M M M PPPP O O SSS I T EEE %
9% C O O M M P O O SS I T E %
10% CCCC OOO M M P OOO SSSSS IIIII T EEEEE %
11% %
12% %
13% MagickCore Image Composite Methods %
14% %
15% Software Design %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy4c08aed2011-07-01 19:47:50 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37%
38*/
39
40/*
41 Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/artifact.h"
45#include "MagickCore/cache.h"
cristy52010022011-10-21 18:07:37 +000046#include "MagickCore/cache-private.h"
cristy4c08aed2011-07-01 19:47:50 +000047#include "MagickCore/cache-view.h"
48#include "MagickCore/client.h"
49#include "MagickCore/color.h"
50#include "MagickCore/color-private.h"
51#include "MagickCore/colorspace.h"
52#include "MagickCore/colorspace-private.h"
53#include "MagickCore/composite.h"
54#include "MagickCore/composite-private.h"
55#include "MagickCore/constitute.h"
56#include "MagickCore/draw.h"
57#include "MagickCore/fx.h"
58#include "MagickCore/gem.h"
59#include "MagickCore/geometry.h"
60#include "MagickCore/image.h"
61#include "MagickCore/image-private.h"
62#include "MagickCore/list.h"
63#include "MagickCore/log.h"
64#include "MagickCore/monitor.h"
65#include "MagickCore/monitor-private.h"
66#include "MagickCore/memory_.h"
67#include "MagickCore/option.h"
68#include "MagickCore/pixel-accessor.h"
69#include "MagickCore/property.h"
70#include "MagickCore/quantum.h"
71#include "MagickCore/resample.h"
72#include "MagickCore/resource_.h"
73#include "MagickCore/string_.h"
74#include "MagickCore/thread-private.h"
75#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000076#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000077#include "MagickCore/version.h"
78
79/*
80%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81% %
82% %
83% %
cristyf4ad9df2011-07-08 16:49:03 +000084% C o m p o s i t e I m a g e %
cristy4c08aed2011-07-01 19:47:50 +000085% %
86% %
87% %
88%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89%
cristyf4ad9df2011-07-08 16:49:03 +000090% CompositeImage() returns the second image composited onto the first
cristy4c08aed2011-07-01 19:47:50 +000091% at the specified offset, using the specified composite method.
92%
cristyf4ad9df2011-07-08 16:49:03 +000093% The format of the CompositeImage method is:
cristy4c08aed2011-07-01 19:47:50 +000094%
95% MagickBooleanType CompositeImage(Image *image,
96% const CompositeOperator compose,Image *composite_image,
cristye941a752011-10-15 01:52:48 +000097% const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +000098%
99% A description of each parameter follows:
100%
101% o image: the destination image, modified by he composition
102%
cristy4c08aed2011-07-01 19:47:50 +0000103% o compose: This operator affects how the composite is applied to
104% the image. The operators and how they are utilized are listed here
105% http://www.w3.org/TR/SVG12/#compositing.
106%
107% o composite_image: the composite (source) image.
108%
109% o x_offset: the column offset of the composited image.
110%
111% o y_offset: the row offset of the composited image.
112%
113% Extra Controls from Image meta-data in 'composite_image' (artifacts)
114%
115% o "compose:args"
116% A string containing extra numerical arguments for specific compose
117% methods, generally expressed as a 'geometry' or a comma separated list
118% of numbers.
119%
120% Compose methods needing such arguments include "BlendCompositeOp" and
121% "DisplaceCompositeOp".
122%
123% o "compose:outside-overlay"
124% Modify how the composition is to effect areas not directly covered
125% by the 'composite_image' at the offset given. Normally this is
126% dependant on the 'compose' method, especially Duff-Porter methods.
127%
128% If set to "false" then disable all normal handling of pixels not
129% covered by the composite_image. Typically used for repeated tiling
130% of the composite_image by the calling API.
131%
132% Previous to IM v6.5.3-3 this was called "modify-outside-overlay"
133%
cristye941a752011-10-15 01:52:48 +0000134% o exception: return any errors or warnings in this structure.
135%
cristy4c08aed2011-07-01 19:47:50 +0000136*/
137
cristye4a40472011-12-22 02:56:19 +0000138static void CompositeHSB(const Quantum red,const Quantum green,
139 const Quantum blue,double *hue,double *saturation,double *brightness)
cristy4c08aed2011-07-01 19:47:50 +0000140{
cristye4a40472011-12-22 02:56:19 +0000141 double
142 delta;
cristy4c08aed2011-07-01 19:47:50 +0000143
cristye4a40472011-12-22 02:56:19 +0000144 Quantum
cristy4c08aed2011-07-01 19:47:50 +0000145 max,
146 min;
147
148 /*
149 Convert RGB to HSB colorspace.
150 */
151 assert(hue != (double *) NULL);
152 assert(saturation != (double *) NULL);
153 assert(brightness != (double *) NULL);
154 max=(red > green ? red : green);
155 if (blue > max)
156 max=blue;
157 min=(red < green ? red : green);
158 if (blue < min)
159 min=blue;
160 *hue=0.0;
161 *saturation=0.0;
162 *brightness=(double) (QuantumScale*max);
cristye4a40472011-12-22 02:56:19 +0000163 if (fabs((double) max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000164 return;
165 *saturation=(double) (1.0-min/max);
cristye4a40472011-12-22 02:56:19 +0000166 delta=(MagickRealType) max-min;
cristyaa83c2c2011-09-21 13:36:25 +0000167 if (fabs(delta) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000168 return;
cristye4a40472011-12-22 02:56:19 +0000169 if (fabs((double) red-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000170 *hue=(double) ((green-blue)/delta);
171 else
cristye4a40472011-12-22 02:56:19 +0000172 if (fabs((double) green-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000173 *hue=(double) (2.0+(blue-red)/delta);
174 else
cristye4a40472011-12-22 02:56:19 +0000175 if (fabs((double) blue-max) < MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000176 *hue=(double) (4.0+(red-green)/delta);
177 *hue/=6.0;
178 if (*hue < 0.0)
179 *hue+=1.0;
180}
181
cristy4c08aed2011-07-01 19:47:50 +0000182static void HSBComposite(const double hue,const double saturation,
cristye4a40472011-12-22 02:56:19 +0000183 const double brightness,double *red,double *green,double *blue)
cristy4c08aed2011-07-01 19:47:50 +0000184{
cristya96f2492011-12-14 18:25:41 +0000185 double
cristy4c08aed2011-07-01 19:47:50 +0000186 f,
187 h,
188 p,
189 q,
190 t;
191
192 /*
193 Convert HSB to RGB colorspace.
194 */
cristya96f2492011-12-14 18:25:41 +0000195 assert(red != (double *) NULL);
196 assert(green != (double *) NULL);
197 assert(blue != (double *) NULL);
cristy4c08aed2011-07-01 19:47:50 +0000198 if (saturation == 0.0)
199 {
cristya96f2492011-12-14 18:25:41 +0000200 *red=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000201 *green=(*red);
202 *blue=(*red);
203 return;
204 }
205 h=6.0*(hue-floor(hue));
206 f=h-floor((double) h);
207 p=brightness*(1.0-saturation);
208 q=brightness*(1.0-saturation*f);
209 t=brightness*(1.0-saturation*(1.0-f));
210 switch ((int) h)
211 {
212 case 0:
213 default:
214 {
cristya96f2492011-12-14 18:25:41 +0000215 *red=(double) QuantumRange*brightness;
216 *green=(double) QuantumRange*t;
217 *blue=(double) QuantumRange*p;
cristy4c08aed2011-07-01 19:47:50 +0000218 break;
219 }
220 case 1:
221 {
cristya96f2492011-12-14 18:25:41 +0000222 *red=(double) QuantumRange*q;
223 *green=(double) QuantumRange*brightness;
224 *blue=(double) QuantumRange*p;
cristy4c08aed2011-07-01 19:47:50 +0000225 break;
226 }
227 case 2:
228 {
cristya96f2492011-12-14 18:25:41 +0000229 *red=(double) QuantumRange*p;
230 *green=(double) QuantumRange*brightness;
231 *blue=(double) QuantumRange*t;
cristy4c08aed2011-07-01 19:47:50 +0000232 break;
233 }
234 case 3:
235 {
cristya96f2492011-12-14 18:25:41 +0000236 *red=(double) QuantumRange*p;
237 *green=(double) QuantumRange*q;
238 *blue=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000239 break;
240 }
241 case 4:
242 {
cristya96f2492011-12-14 18:25:41 +0000243 *red=(double) QuantumRange*t;
244 *green=(double) QuantumRange*p;
245 *blue=(double) QuantumRange*brightness;
cristy4c08aed2011-07-01 19:47:50 +0000246 break;
247 }
248 case 5:
249 {
cristya96f2492011-12-14 18:25:41 +0000250 *red=(double) QuantumRange*brightness;
251 *green=(double) QuantumRange*p;
252 *blue=(double) QuantumRange*q;
cristy4c08aed2011-07-01 19:47:50 +0000253 break;
254 }
255 }
256}
257
cristye4a40472011-12-22 02:56:19 +0000258static inline double MagickMin(const double x,const double y)
259{
260 if (x < y)
261 return(x);
262 return(y);
263}
264static inline double MagickMax(const double x,const double y)
265{
266 if (x > y)
267 return(x);
268 return(y);
269}
270
271static MagickBooleanType CompositeOverImage(Image *image,
272 const Image *composite_image,const ssize_t x_offset,const ssize_t y_offset,
273 ExceptionInfo *exception)
274{
275#define CompositeImageTag "Composite/Image"
276
277 CacheView
278 *composite_view,
279 *image_view;
280
281 const char
282 *value;
283
284 MagickBooleanType
285 modify_outside_overlay,
286 status;
287
288 MagickOffsetType
289 progress;
290
291 ssize_t
292 y;
293
cristye4a40472011-12-22 02:56:19 +0000294 /*
295 Prepare composite image.
296 */
297 modify_outside_overlay=MagickFalse;
298 value=GetImageArtifact(composite_image,"compose:outside-overlay");
299 if (value != (const char *) NULL)
300 modify_outside_overlay=IsMagickTrue(value);
301 /*
302 Composite image.
303 */
304 status=MagickTrue;
305 progress=0;
306 image_view=AcquireCacheView(image);
307 composite_view=AcquireCacheView(composite_image);
308#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +0000309 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristye4a40472011-12-22 02:56:19 +0000310#endif
311 for (y=0; y < (ssize_t) image->rows; y++)
312 {
313 const Quantum
314 *pixels;
315
316 register const Quantum
317 *restrict p;
318
319 register Quantum
320 *restrict q;
321
322 register ssize_t
323 x;
324
cristy564a5692012-01-20 23:56:26 +0000325 size_t
326 channels;
327
cristye4a40472011-12-22 02:56:19 +0000328 if (status == MagickFalse)
329 continue;
330 if (modify_outside_overlay == MagickFalse)
331 {
332 if (y < y_offset)
333 continue;
334 if ((y-y_offset) >= (ssize_t) composite_image->rows)
335 continue;
336 }
337 /*
338 If pixels is NULL, y is outside overlay region.
339 */
340 pixels=(Quantum *) NULL;
341 p=(Quantum *) NULL;
342 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
343 {
344 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
345 composite_image->columns,1,exception);
346 if (p == (const Quantum *) NULL)
347 {
348 status=MagickFalse;
349 continue;
350 }
351 pixels=p;
352 if (x_offset < 0)
353 p-=x_offset*GetPixelChannels(composite_image);
354 }
355 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
356 if (q == (Quantum *) NULL)
357 {
358 status=MagickFalse;
359 continue;
360 }
361 for (x=0; x < (ssize_t) image->columns; x++)
362 {
363 MagickRealType
364 alpha,
365 Da,
366 Dc,
367 gamma,
368 Sa,
369 Sc;
370
371 register ssize_t
372 i;
373
374 if (modify_outside_overlay == MagickFalse)
375 {
376 if (x < x_offset)
377 {
378 q+=GetPixelChannels(image);
379 continue;
380 }
381 if ((x-x_offset) >= (ssize_t) composite_image->columns)
382 break;
383 }
384 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
385 ((x-x_offset) >= (ssize_t) composite_image->columns))
386 {
387 Quantum
388 source[MaxPixelChannels];
389
390 /*
391 Virtual composite:
392 Sc: source color.
393 Dc: destination color.
394 */
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
cristy4c08aed2011-07-01 19:47:50 +0000506 GeometryInfo
507 geometry_info;
508
509 Image
510 *destination_image;
511
512 MagickBooleanType
513 modify_outside_overlay,
514 status;
515
516 MagickOffsetType
517 progress;
518
cristy4c08aed2011-07-01 19:47:50 +0000519 MagickRealType
520 amount,
521 destination_dissolve,
522 midpoint,
523 percent_brightness,
524 percent_saturation,
525 source_dissolve,
526 threshold;
527
528 MagickStatusType
529 flags;
530
cristyd197cbb2012-01-13 02:14:12 +0000531 ssize_t
532 y;
533
cristy4c08aed2011-07-01 19:47:50 +0000534 /*
cristye4a40472011-12-22 02:56:19 +0000535 Composition based on the SVG specification:
536
537 A Composition is defined by...
538 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
539 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
540 Y = 1 for source preserved
541 Z = 1 for destination preserved
542
543 Conversion to transparency (then optimized)
544 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
545 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
546
547 Where...
548 Sca = Sc*Sa normalized Source color divided by Source alpha
549 Dca = Dc*Da normalized Dest color divided by Dest alpha
550 Dc' = Dca'/Da' the desired color value for this channel.
551
552 Da' in in the follow formula as 'gamma' The resulting alpla value.
553
554 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
555 the following optimizations...
556 gamma = Sa+Da-Sa*Da;
557 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
558 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
559
560 The above SVG definitions also definate that Mathematical Composition
561 methods should use a 'Over' blending mode for Alpha Channel.
562 It however was not applied for composition modes of 'Plus', 'Minus',
563 the modulus versions of 'Add' and 'Subtract'.
564
565 Mathematical operator changes to be applied from IM v6.7...
566
567 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
568 'ModulusAdd' and 'ModulusSubtract' for clarity.
569
570 2) All mathematical compositions work as per the SVG specification
571 with regard to blending. This now includes 'ModulusAdd' and
572 'ModulusSubtract'.
573
574 3) When the special channel flag 'sync' (syncronize channel updates)
575 is turned off (enabled by default) then mathematical compositions are
576 only performed on the channels specified, and are applied
577 independantally of each other. In other words the mathematics is
578 performed as 'pure' mathematical operations, rather than as image
579 operations.
cristy4c08aed2011-07-01 19:47:50 +0000580 */
581 assert(image != (Image *) NULL);
582 assert(image->signature == MagickSignature);
583 if (image->debug != MagickFalse)
584 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
585 assert(composite_image != (Image *) NULL);
586 assert(composite_image->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000587 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000588 return(MagickFalse);
cristye4a40472011-12-22 02:56:19 +0000589 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
590 {
591 status=CompositeOverImage(image,composite_image,x_offset,y_offset,
592 exception);
593 return(status);
594 }
cristy4c08aed2011-07-01 19:47:50 +0000595 destination_image=(Image *) NULL;
596 amount=0.5;
597 destination_dissolve=1.0;
598 modify_outside_overlay=MagickFalse;
599 percent_brightness=100.0;
600 percent_saturation=100.0;
601 source_dissolve=1.0;
602 threshold=0.05f;
603 switch (compose)
604 {
605 case ClearCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000606 case DstAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +0000607 case DstInCompositeOp:
608 case InCompositeOp:
609 case OutCompositeOp:
610 case SrcCompositeOp:
611 case SrcInCompositeOp:
612 case SrcOutCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000613 {
614 /*
615 Modify destination outside the overlaid region.
616 */
617 modify_outside_overlay=MagickTrue;
618 break;
619 }
620 case CopyCompositeOp:
621 {
622 if ((x_offset < 0) || (y_offset < 0))
623 break;
624 if ((x_offset+(ssize_t) composite_image->columns) >= (ssize_t) image->columns)
625 break;
626 if ((y_offset+(ssize_t) composite_image->rows) >= (ssize_t) image->rows)
627 break;
628 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +0000629 image_view=AcquireCacheView(image);
630 composite_view=AcquireCacheView(composite_image);
631#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000632 #pragma omp parallel for schedule(static,4) shared(status)
cristy4c08aed2011-07-01 19:47:50 +0000633#endif
634 for (y=0; y < (ssize_t) composite_image->rows; y++)
635 {
636 MagickBooleanType
637 sync;
638
639 register const Quantum
640 *p;
641
642 register Quantum
643 *q;
644
645 register ssize_t
646 x;
647
648 if (status == MagickFalse)
649 continue;
650 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
651 1,exception);
652 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
653 composite_image->columns,1,exception);
654 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
655 {
656 status=MagickFalse;
657 continue;
658 }
659 for (x=0; x < (ssize_t) composite_image->columns; x++)
660 {
cristybdecccc2011-12-24 22:52:16 +0000661 register ssize_t
662 i;
663
664 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
665 {
666 PixelChannel
667 channel;
668
669 PixelTrait
670 composite_traits,
671 traits;
672
673 channel=GetPixelChannelMapChannel(composite_image,i);
674 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
675 traits=GetPixelChannelMapTraits(image,channel);
676 if ((traits == UndefinedPixelTrait) ||
677 (composite_traits == UndefinedPixelTrait))
678 continue;
679 SetPixelChannel(image,channel,p[i],q);
680 }
cristyed231572011-07-14 02:18:59 +0000681 p+=GetPixelChannels(composite_image);
682 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000683 }
684 sync=SyncCacheViewAuthenticPixels(image_view,exception);
685 if (sync == MagickFalse)
686 status=MagickFalse;
687 if (image->progress_monitor != (MagickProgressMonitor) NULL)
688 {
689 MagickBooleanType
690 proceed;
691
692#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000693 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000694#endif
695 proceed=SetImageProgress(image,CompositeImageTag,
696 (MagickOffsetType) y,image->rows);
697 if (proceed == MagickFalse)
698 status=MagickFalse;
699 }
700 }
701 composite_view=DestroyCacheView(composite_view);
702 image_view=DestroyCacheView(image_view);
703 return(status);
704 }
cristye4a40472011-12-22 02:56:19 +0000705 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000706 case ChangeMaskCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +0000707 case IntensityCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000708 {
709 /*
710 Modify destination outside the overlaid region and require an alpha
711 channel to exist, to add transparency.
712 */
713 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +0000714 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000715 modify_outside_overlay=MagickTrue;
716 break;
717 }
718 case BlurCompositeOp:
719 {
720 CacheView
721 *composite_view,
722 *destination_view;
723
724 PixelInfo
725 pixel;
726
727 MagickRealType
728 angle_range,
729 angle_start,
730 height,
731 width;
732
733 ResampleFilter
734 *resample_filter;
735
736 SegmentInfo
737 blur;
738
739 /*
740 Blur Image dictated by an overlay gradient map: X = red_channel;
741 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
742 */
743 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000744 exception);
cristy4c08aed2011-07-01 19:47:50 +0000745 if (destination_image == (Image *) NULL)
746 return(MagickFalse);
747 /*
748 Determine the horizontal and vertical maximim blur.
749 */
750 SetGeometryInfo(&geometry_info);
751 flags=NoValue;
752 value=GetImageArtifact(composite_image,"compose:args");
753 if (value != (char *) NULL)
754 flags=ParseGeometry(value,&geometry_info);
755 if ((flags & WidthValue) == 0 )
756 {
757 destination_image=DestroyImage(destination_image);
758 return(MagickFalse);
759 }
760 width=geometry_info.rho;
761 height=geometry_info.sigma;
762 blur.x1=geometry_info.rho;
763 blur.x2=0.0;
764 blur.y1=0.0;
765 blur.y2=geometry_info.sigma;
766 angle_start=0.0;
767 angle_range=0.0;
768 if ((flags & HeightValue) == 0)
769 blur.y2=blur.x1;
770 if ((flags & XValue) != 0 )
771 {
772 MagickRealType
773 angle;
774
775 angle=DegreesToRadians(geometry_info.xi);
776 blur.x1=width*cos(angle);
777 blur.x2=width*sin(angle);
778 blur.y1=(-height*sin(angle));
779 blur.y2=height*cos(angle);
780 }
781 if ((flags & YValue) != 0 )
782 {
783 angle_start=DegreesToRadians(geometry_info.xi);
784 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
785 }
786 /*
787 Blur Image by resampling.
788 */
cristy8a11cb12011-10-19 23:53:34 +0000789 resample_filter=AcquireResampleFilter(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000790 SetResampleFilter(resample_filter,CubicFilter,2.0);
791 destination_view=AcquireCacheView(destination_image);
792 composite_view=AcquireCacheView(composite_image);
793 for (y=0; y < (ssize_t) composite_image->rows; y++)
794 {
795 MagickBooleanType
796 sync;
797
798 register const Quantum
799 *restrict p;
800
801 register Quantum
802 *restrict q;
803
804 register ssize_t
805 x;
806
807 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
808 continue;
809 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
810 1,exception);
811 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000812 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000813 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
814 break;
815 for (x=0; x < (ssize_t) composite_image->columns; x++)
816 {
817 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
818 {
cristyed231572011-07-14 02:18:59 +0000819 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000820 continue;
821 }
822 if (fabs(angle_range) > MagickEpsilon)
823 {
824 MagickRealType
825 angle;
826
827 angle=angle_start+angle_range*QuantumScale*
828 GetPixelBlue(composite_image,p);
829 blur.x1=width*cos(angle);
830 blur.x2=width*sin(angle);
831 blur.y1=(-height*sin(angle));
832 blur.y2=height*cos(angle);
833 }
834 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
835 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
836 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
837 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
838 GetPixelGreen(composite_image,p));
839 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
840 (double) y_offset+y,&pixel);
cristy803640d2011-11-17 02:11:32 +0000841 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000842 p+=GetPixelChannels(composite_image);
843 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000844 }
845 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
846 if (sync == MagickFalse)
847 break;
848 }
849 resample_filter=DestroyResampleFilter(resample_filter);
850 composite_view=DestroyCacheView(composite_view);
851 destination_view=DestroyCacheView(destination_view);
852 composite_image=destination_image;
853 break;
854 }
855 case DisplaceCompositeOp:
856 case DistortCompositeOp:
857 {
858 CacheView
859 *composite_view,
860 *destination_view,
861 *image_view;
862
863 PixelInfo
864 pixel;
865
866 MagickRealType
867 horizontal_scale,
868 vertical_scale;
869
870 PointInfo
871 center,
872 offset;
873
874 /*
875 Displace/Distort based on overlay gradient map:
876 X = red_channel; Y = green_channel;
877 compose:args = x_scale[,y_scale[,center.x,center.y]]
878 */
879 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000880 exception);
cristy4c08aed2011-07-01 19:47:50 +0000881 if (destination_image == (Image *) NULL)
882 return(MagickFalse);
883 SetGeometryInfo(&geometry_info);
884 flags=NoValue;
885 value=GetImageArtifact(composite_image,"compose:args");
886 if (value != (char *) NULL)
887 flags=ParseGeometry(value,&geometry_info);
888 if ((flags & (WidthValue|HeightValue)) == 0 )
889 {
890 if ((flags & AspectValue) == 0)
891 {
892 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
893 2.0;
894 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
895 }
896 else
897 {
898 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
899 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
900 }
901 }
902 else
903 {
904 horizontal_scale=geometry_info.rho;
905 vertical_scale=geometry_info.sigma;
906 if ((flags & PercentValue) != 0)
907 {
908 if ((flags & AspectValue) == 0)
909 {
910 horizontal_scale*=(composite_image->columns-1.0)/200.0;
911 vertical_scale*=(composite_image->rows-1.0)/200.0;
912 }
913 else
914 {
915 horizontal_scale*=(image->columns-1.0)/200.0;
916 vertical_scale*=(image->rows-1.0)/200.0;
917 }
918 }
919 if ((flags & HeightValue) == 0)
920 vertical_scale=horizontal_scale;
921 }
922 /*
923 Determine fixed center point for absolute distortion map
924 Absolute distort ==
925 Displace offset relative to a fixed absolute point
926 Select that point according to +X+Y user inputs.
927 default = center of overlay image
928 arg flag '!' = locations/percentage relative to background image
929 */
930 center.x=(MagickRealType) x_offset;
931 center.y=(MagickRealType) y_offset;
932 if (compose == DistortCompositeOp)
933 {
934 if ((flags & XValue) == 0)
935 if ((flags & AspectValue) == 0)
936 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
937 2.0;
938 else
939 center.x=((MagickRealType) image->columns-1)/2.0;
940 else
941 if ((flags & AspectValue) == 0)
942 center.x=(MagickRealType) x_offset+geometry_info.xi;
943 else
944 center.x=geometry_info.xi;
945 if ((flags & YValue) == 0)
946 if ((flags & AspectValue) == 0)
947 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
948 else
949 center.y=((MagickRealType) image->rows-1)/2.0;
950 else
951 if ((flags & AspectValue) == 0)
952 center.y=(MagickRealType) y_offset+geometry_info.psi;
953 else
954 center.y=geometry_info.psi;
955 }
956 /*
957 Shift the pixel offset point as defined by the provided,
958 displacement/distortion map. -- Like a lens...
959 */
cristye10859a2011-12-18 22:28:59 +0000960 GetPixelInfo(image,&pixel);
cristy4c08aed2011-07-01 19:47:50 +0000961 image_view=AcquireCacheView(image);
962 destination_view=AcquireCacheView(destination_image);
963 composite_view=AcquireCacheView(composite_image);
964 for (y=0; y < (ssize_t) composite_image->rows; y++)
965 {
966 MagickBooleanType
967 sync;
968
969 register const Quantum
970 *restrict p;
971
972 register Quantum
973 *restrict q;
974
975 register ssize_t
976 x;
977
978 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
979 continue;
980 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
981 1,exception);
982 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000983 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000984 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
985 break;
986 for (x=0; x < (ssize_t) composite_image->columns; x++)
987 {
988 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
989 {
cristyed231572011-07-14 02:18:59 +0000990 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000991 continue;
992 }
993 /*
994 Displace the offset.
995 */
996 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
997 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
998 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
999 x : 0);
1000 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1001 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1002 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1003 y : 0);
1004 (void) InterpolatePixelInfo(image,image_view,
1005 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1006 &pixel,exception);
1007 /*
1008 Mask with the 'invalid pixel mask' in alpha channel.
1009 */
1010 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001011 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001012 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001013 p+=GetPixelChannels(composite_image);
1014 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001015 }
1016 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1017 if (sync == MagickFalse)
1018 break;
1019 }
1020 destination_view=DestroyCacheView(destination_view);
1021 composite_view=DestroyCacheView(composite_view);
1022 image_view=DestroyCacheView(image_view);
1023 composite_image=destination_image;
1024 break;
1025 }
1026 case DissolveCompositeOp:
1027 {
1028 /*
1029 Geometry arguments to dissolve factors.
1030 */
1031 value=GetImageArtifact(composite_image,"compose:args");
1032 if (value != (char *) NULL)
1033 {
1034 flags=ParseGeometry(value,&geometry_info);
1035 source_dissolve=geometry_info.rho/100.0;
1036 destination_dissolve=1.0;
1037 if ((source_dissolve-MagickEpsilon) < 0.0)
1038 source_dissolve=0.0;
1039 if ((source_dissolve+MagickEpsilon) > 1.0)
1040 {
1041 destination_dissolve=2.0-source_dissolve;
1042 source_dissolve=1.0;
1043 }
1044 if ((flags & SigmaValue) != 0)
1045 destination_dissolve=geometry_info.sigma/100.0;
1046 if ((destination_dissolve-MagickEpsilon) < 0.0)
1047 destination_dissolve=0.0;
1048 modify_outside_overlay=MagickTrue;
1049 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1050 {
1051 destination_dissolve=1.0;
1052 modify_outside_overlay=MagickFalse;
1053 }
1054 }
1055 break;
1056 }
1057 case BlendCompositeOp:
1058 {
1059 value=GetImageArtifact(composite_image,"compose:args");
1060 if (value != (char *) NULL)
1061 {
1062 flags=ParseGeometry(value,&geometry_info);
1063 source_dissolve=geometry_info.rho/100.0;
1064 destination_dissolve=1.0-source_dissolve;
1065 if ((flags & SigmaValue) != 0)
1066 destination_dissolve=geometry_info.sigma/100.0;
1067 modify_outside_overlay=MagickTrue;
1068 if ((destination_dissolve+MagickEpsilon) > 1.0)
1069 modify_outside_overlay=MagickFalse;
1070 }
1071 break;
1072 }
1073 case MathematicsCompositeOp:
1074 {
1075 /*
1076 Just collect the values from "compose:args", setting.
1077 Unused values are set to zero automagically.
1078
1079 Arguments are normally a comma separated list, so this probably should
1080 be changed to some 'general comma list' parser, (with a minimum
1081 number of values)
1082 */
1083 SetGeometryInfo(&geometry_info);
1084 value=GetImageArtifact(composite_image,"compose:args");
1085 if (value != (char *) NULL)
1086 (void) ParseGeometry(value,&geometry_info);
1087 break;
1088 }
1089 case ModulateCompositeOp:
1090 {
1091 /*
1092 Determine the brightness and saturation scale.
1093 */
1094 value=GetImageArtifact(composite_image,"compose:args");
1095 if (value != (char *) NULL)
1096 {
1097 flags=ParseGeometry(value,&geometry_info);
1098 percent_brightness=geometry_info.rho;
1099 if ((flags & SigmaValue) != 0)
1100 percent_saturation=geometry_info.sigma;
1101 }
1102 break;
1103 }
1104 case ThresholdCompositeOp:
1105 {
1106 /*
1107 Determine the amount and threshold.
1108 */
1109 value=GetImageArtifact(composite_image,"compose:args");
1110 if (value != (char *) NULL)
1111 {
1112 flags=ParseGeometry(value,&geometry_info);
1113 amount=geometry_info.rho;
1114 threshold=geometry_info.sigma;
1115 if ((flags & SigmaValue) == 0)
1116 threshold=0.05f;
1117 }
1118 threshold*=QuantumRange;
1119 break;
1120 }
1121 default:
1122 break;
1123 }
1124 value=GetImageArtifact(composite_image,"compose:outside-overlay");
1125 if (value != (const char *) NULL)
1126 modify_outside_overlay=IsMagickTrue(value);
1127 /*
1128 Composite image.
1129 */
1130 status=MagickTrue;
1131 progress=0;
1132 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy4c08aed2011-07-01 19:47:50 +00001133 image_view=AcquireCacheView(image);
1134 composite_view=AcquireCacheView(composite_image);
1135#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00001136 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy4c08aed2011-07-01 19:47:50 +00001137#endif
1138 for (y=0; y < (ssize_t) image->rows; y++)
1139 {
1140 const Quantum
1141 *pixels;
1142
1143 double
cristye4a40472011-12-22 02:56:19 +00001144 blue,
cristy4c08aed2011-07-01 19:47:50 +00001145 brightness,
cristye4a40472011-12-22 02:56:19 +00001146 green,
cristy4c08aed2011-07-01 19:47:50 +00001147 hue,
cristye4a40472011-12-22 02:56:19 +00001148 red,
cristy4c08aed2011-07-01 19:47:50 +00001149 saturation;
1150
cristy4c08aed2011-07-01 19:47:50 +00001151 register const Quantum
1152 *restrict p;
1153
1154 register Quantum
1155 *restrict q;
1156
1157 register ssize_t
1158 x;
1159
1160 if (status == MagickFalse)
1161 continue;
1162 if (modify_outside_overlay == MagickFalse)
1163 {
1164 if (y < y_offset)
1165 continue;
1166 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1167 continue;
1168 }
1169 /*
1170 If pixels is NULL, y is outside overlay region.
1171 */
1172 pixels=(Quantum *) NULL;
1173 p=(Quantum *) NULL;
1174 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1175 {
1176 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1177 composite_image->columns,1,exception);
1178 if (p == (const Quantum *) NULL)
1179 {
1180 status=MagickFalse;
1181 continue;
1182 }
1183 pixels=p;
1184 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001185 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001186 }
1187 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001188 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001189 {
1190 status=MagickFalse;
1191 continue;
1192 }
cristy4c08aed2011-07-01 19:47:50 +00001193 hue=0.0;
1194 saturation=0.0;
1195 brightness=0.0;
1196 for (x=0; x < (ssize_t) image->columns; x++)
1197 {
cristye4a40472011-12-22 02:56:19 +00001198 MagickRealType
1199 alpha,
1200 Da,
1201 Dc,
1202 Dca,
1203 gamma,
1204 Sa,
1205 Sc,
1206 Sca;
1207
1208 register ssize_t
1209 i;
1210
cristy564a5692012-01-20 23:56:26 +00001211 size_t
1212 channels;
1213
cristy4c08aed2011-07-01 19:47:50 +00001214 if (modify_outside_overlay == MagickFalse)
1215 {
1216 if (x < x_offset)
1217 {
cristyed231572011-07-14 02:18:59 +00001218 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001219 continue;
1220 }
1221 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1222 break;
1223 }
cristye4a40472011-12-22 02:56:19 +00001224 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1225 ((x-x_offset) >= (ssize_t) composite_image->columns))
1226 {
1227 Quantum
1228 source[MaxPixelChannels];
1229
1230 /*
1231 Virtual composite:
1232 Sc: source color.
1233 Dc: destination color.
1234 */
1235 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1236 source,exception);
1237 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1238 {
1239 MagickRealType
1240 pixel;
1241
1242 PixelChannel
1243 channel;
1244
1245 PixelTrait
1246 composite_traits,
1247 traits;
1248
1249 channel=GetPixelChannelMapChannel(image,i);
1250 traits=GetPixelChannelMapTraits(image,channel);
1251 composite_traits=GetPixelChannelMapTraits(composite_image,
1252 channel);
1253 if ((traits == UndefinedPixelTrait) ||
1254 (composite_traits == UndefinedPixelTrait))
1255 continue;
1256 switch (compose)
1257 {
cristyc8d63672012-01-11 13:03:13 +00001258 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001259 case ChangeMaskCompositeOp:
1260 case CopyAlphaCompositeOp:
1261 case DstAtopCompositeOp:
1262 case DstInCompositeOp:
1263 case InCompositeOp:
cristy0cd1f212012-01-05 15:45:59 +00001264 case IntensityCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001265 case OutCompositeOp:
1266 case SrcInCompositeOp:
1267 case SrcOutCompositeOp:
1268 {
1269 pixel=(MagickRealType) q[i];
1270 if (channel == AlphaPixelChannel)
1271 pixel=(MagickRealType) TransparentAlpha;
1272 break;
1273 }
1274 case ClearCompositeOp:
1275 case CopyCompositeOp:
1276 case ReplaceCompositeOp:
1277 case SrcCompositeOp:
1278 {
1279 if (channel == AlphaPixelChannel)
1280 {
1281 pixel=(MagickRealType) TransparentAlpha;
1282 break;
1283 }
1284 pixel=0.0;
1285 break;
1286 }
cristy99abff32011-12-24 20:45:16 +00001287 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001288 case DissolveCompositeOp:
1289 {
1290 if (channel == AlphaPixelChannel)
1291 {
1292 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1293 source);
1294 break;
1295 }
1296 pixel=(MagickRealType) source[channel];
1297 break;
1298 }
1299 default:
1300 {
1301 pixel=(MagickRealType) source[channel];
1302 break;
1303 }
1304 }
1305 q[i]=ClampToQuantum(pixel);
1306 }
1307 q+=GetPixelChannels(image);
1308 continue;
1309 }
1310 /*
1311 Authentic composite:
1312 Sa: normalized source alpha.
1313 Da: normalized destination alpha.
1314 */
1315 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1316 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001317 switch (compose)
1318 {
cristye4a40472011-12-22 02:56:19 +00001319 case BumpmapCompositeOp:
1320 {
1321 alpha=GetPixelIntensity(composite_image,p)*Sa;
1322 break;
1323 }
cristycdc168f2011-12-21 15:24:39 +00001324 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001325 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001326 case DifferenceCompositeOp:
1327 case DivideDstCompositeOp:
1328 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001329 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001330 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001331 case LinearBurnCompositeOp:
1332 case LinearDodgeCompositeOp:
1333 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001334 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001335 case MinusDstCompositeOp:
1336 case MinusSrcCompositeOp:
1337 case ModulusAddCompositeOp:
1338 case ModulusSubtractCompositeOp:
1339 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001340 case OverlayCompositeOp:
1341 case PegtopLightCompositeOp:
1342 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001343 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001344 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001345 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001346 {
1347 alpha=RoundToUnity(Sa+Da-Sa*Da);
1348 break;
1349 }
1350 case DarkenCompositeOp:
1351 case DstAtopCompositeOp:
1352 case DstInCompositeOp:
1353 case InCompositeOp:
1354 case LightenCompositeOp:
1355 case SrcInCompositeOp:
1356 {
1357 alpha=Sa*Da;
1358 break;
1359 }
1360 case DissolveCompositeOp:
1361 {
1362 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1363 Sa+destination_dissolve*Da;
1364 break;
1365 }
1366 case DstOverCompositeOp:
1367 {
1368 alpha=Da*(-Sa)+Da+Sa;
1369 break;
1370 }
1371 case DstOutCompositeOp:
1372 {
1373 alpha=Da*(1.0-Sa);
1374 break;
1375 }
1376 case OutCompositeOp:
1377 case SrcOutCompositeOp:
1378 {
1379 alpha=Sa*(1.0-Da);
1380 break;
1381 }
1382 case OverCompositeOp:
1383 case SrcOverCompositeOp:
1384 {
1385 alpha=Sa*(-Da)+Sa+Da;
1386 break;
1387 }
cristy99abff32011-12-24 20:45:16 +00001388 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001389 case PlusCompositeOp:
1390 {
1391 alpha=RoundToUnity(Sa+Da);
1392 break;
1393 }
cristy4c08aed2011-07-01 19:47:50 +00001394 case XorCompositeOp:
1395 {
cristye4a40472011-12-22 02:56:19 +00001396 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001397 break;
1398 }
1399 default:
cristye4a40472011-12-22 02:56:19 +00001400 {
1401 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001402 break;
cristye4a40472011-12-22 02:56:19 +00001403 }
cristy4c08aed2011-07-01 19:47:50 +00001404 }
cristye4a40472011-12-22 02:56:19 +00001405 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1406 {
cristy564a5692012-01-20 23:56:26 +00001407 double
1408 sans;
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}