blob: 561b7303b02b9bf1510505459b8ffe5623539e86 [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)
cristye6178502011-12-23 17:02:29 +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)
700#pragma omp critical (MagickCore_CompositeImage)
701#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:
714 {
715 /*
716 Modify destination outside the overlaid region and require an alpha
717 channel to exist, to add transparency.
718 */
719 if (image->matte == MagickFalse)
cristy63240882011-08-05 19:05:27 +0000720 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000721 modify_outside_overlay=MagickTrue;
722 break;
723 }
724 case BlurCompositeOp:
725 {
726 CacheView
727 *composite_view,
728 *destination_view;
729
730 PixelInfo
731 pixel;
732
733 MagickRealType
734 angle_range,
735 angle_start,
736 height,
737 width;
738
739 ResampleFilter
740 *resample_filter;
741
742 SegmentInfo
743 blur;
744
745 /*
746 Blur Image dictated by an overlay gradient map: X = red_channel;
747 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
748 */
749 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000750 exception);
cristy4c08aed2011-07-01 19:47:50 +0000751 if (destination_image == (Image *) NULL)
752 return(MagickFalse);
753 /*
754 Determine the horizontal and vertical maximim blur.
755 */
756 SetGeometryInfo(&geometry_info);
757 flags=NoValue;
758 value=GetImageArtifact(composite_image,"compose:args");
759 if (value != (char *) NULL)
760 flags=ParseGeometry(value,&geometry_info);
761 if ((flags & WidthValue) == 0 )
762 {
763 destination_image=DestroyImage(destination_image);
764 return(MagickFalse);
765 }
766 width=geometry_info.rho;
767 height=geometry_info.sigma;
768 blur.x1=geometry_info.rho;
769 blur.x2=0.0;
770 blur.y1=0.0;
771 blur.y2=geometry_info.sigma;
772 angle_start=0.0;
773 angle_range=0.0;
774 if ((flags & HeightValue) == 0)
775 blur.y2=blur.x1;
776 if ((flags & XValue) != 0 )
777 {
778 MagickRealType
779 angle;
780
781 angle=DegreesToRadians(geometry_info.xi);
782 blur.x1=width*cos(angle);
783 blur.x2=width*sin(angle);
784 blur.y1=(-height*sin(angle));
785 blur.y2=height*cos(angle);
786 }
787 if ((flags & YValue) != 0 )
788 {
789 angle_start=DegreesToRadians(geometry_info.xi);
790 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
791 }
792 /*
793 Blur Image by resampling.
794 */
cristy8a11cb12011-10-19 23:53:34 +0000795 resample_filter=AcquireResampleFilter(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000796 SetResampleFilter(resample_filter,CubicFilter,2.0);
797 destination_view=AcquireCacheView(destination_image);
798 composite_view=AcquireCacheView(composite_image);
799 for (y=0; y < (ssize_t) composite_image->rows; y++)
800 {
801 MagickBooleanType
802 sync;
803
804 register const Quantum
805 *restrict p;
806
807 register Quantum
808 *restrict q;
809
810 register ssize_t
811 x;
812
813 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
814 continue;
815 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
816 1,exception);
817 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000818 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000819 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
820 break;
821 for (x=0; x < (ssize_t) composite_image->columns; x++)
822 {
823 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
824 {
cristyed231572011-07-14 02:18:59 +0000825 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000826 continue;
827 }
828 if (fabs(angle_range) > MagickEpsilon)
829 {
830 MagickRealType
831 angle;
832
833 angle=angle_start+angle_range*QuantumScale*
834 GetPixelBlue(composite_image,p);
835 blur.x1=width*cos(angle);
836 blur.x2=width*sin(angle);
837 blur.y1=(-height*sin(angle));
838 blur.y2=height*cos(angle);
839 }
840 ScaleResampleFilter(resample_filter,blur.x1*QuantumScale*
841 GetPixelRed(composite_image,p),blur.y1*QuantumScale*
842 GetPixelGreen(composite_image,p),blur.x2*QuantumScale*
843 GetPixelRed(composite_image,p),blur.y2*QuantumScale*
844 GetPixelGreen(composite_image,p));
845 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
846 (double) y_offset+y,&pixel);
cristy803640d2011-11-17 02:11:32 +0000847 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000848 p+=GetPixelChannels(composite_image);
849 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000850 }
851 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
852 if (sync == MagickFalse)
853 break;
854 }
855 resample_filter=DestroyResampleFilter(resample_filter);
856 composite_view=DestroyCacheView(composite_view);
857 destination_view=DestroyCacheView(destination_view);
858 composite_image=destination_image;
859 break;
860 }
861 case DisplaceCompositeOp:
862 case DistortCompositeOp:
863 {
864 CacheView
865 *composite_view,
866 *destination_view,
867 *image_view;
868
869 PixelInfo
870 pixel;
871
872 MagickRealType
873 horizontal_scale,
874 vertical_scale;
875
876 PointInfo
877 center,
878 offset;
879
880 /*
881 Displace/Distort based on overlay gradient map:
882 X = red_channel; Y = green_channel;
883 compose:args = x_scale[,y_scale[,center.x,center.y]]
884 */
885 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000886 exception);
cristy4c08aed2011-07-01 19:47:50 +0000887 if (destination_image == (Image *) NULL)
888 return(MagickFalse);
889 SetGeometryInfo(&geometry_info);
890 flags=NoValue;
891 value=GetImageArtifact(composite_image,"compose:args");
892 if (value != (char *) NULL)
893 flags=ParseGeometry(value,&geometry_info);
894 if ((flags & (WidthValue|HeightValue)) == 0 )
895 {
896 if ((flags & AspectValue) == 0)
897 {
898 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
899 2.0;
900 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
901 }
902 else
903 {
904 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
905 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
906 }
907 }
908 else
909 {
910 horizontal_scale=geometry_info.rho;
911 vertical_scale=geometry_info.sigma;
912 if ((flags & PercentValue) != 0)
913 {
914 if ((flags & AspectValue) == 0)
915 {
916 horizontal_scale*=(composite_image->columns-1.0)/200.0;
917 vertical_scale*=(composite_image->rows-1.0)/200.0;
918 }
919 else
920 {
921 horizontal_scale*=(image->columns-1.0)/200.0;
922 vertical_scale*=(image->rows-1.0)/200.0;
923 }
924 }
925 if ((flags & HeightValue) == 0)
926 vertical_scale=horizontal_scale;
927 }
928 /*
929 Determine fixed center point for absolute distortion map
930 Absolute distort ==
931 Displace offset relative to a fixed absolute point
932 Select that point according to +X+Y user inputs.
933 default = center of overlay image
934 arg flag '!' = locations/percentage relative to background image
935 */
936 center.x=(MagickRealType) x_offset;
937 center.y=(MagickRealType) y_offset;
938 if (compose == DistortCompositeOp)
939 {
940 if ((flags & XValue) == 0)
941 if ((flags & AspectValue) == 0)
942 center.x=(MagickRealType) x_offset+(composite_image->columns-1)/
943 2.0;
944 else
945 center.x=((MagickRealType) image->columns-1)/2.0;
946 else
947 if ((flags & AspectValue) == 0)
948 center.x=(MagickRealType) x_offset+geometry_info.xi;
949 else
950 center.x=geometry_info.xi;
951 if ((flags & YValue) == 0)
952 if ((flags & AspectValue) == 0)
953 center.y=(MagickRealType) y_offset+(composite_image->rows-1)/2.0;
954 else
955 center.y=((MagickRealType) image->rows-1)/2.0;
956 else
957 if ((flags & AspectValue) == 0)
958 center.y=(MagickRealType) y_offset+geometry_info.psi;
959 else
960 center.y=geometry_info.psi;
961 }
962 /*
963 Shift the pixel offset point as defined by the provided,
964 displacement/distortion map. -- Like a lens...
965 */
cristye10859a2011-12-18 22:28:59 +0000966 GetPixelInfo(image,&pixel);
cristy4c08aed2011-07-01 19:47:50 +0000967 image_view=AcquireCacheView(image);
968 destination_view=AcquireCacheView(destination_image);
969 composite_view=AcquireCacheView(composite_image);
970 for (y=0; y < (ssize_t) composite_image->rows; y++)
971 {
972 MagickBooleanType
973 sync;
974
975 register const Quantum
976 *restrict p;
977
978 register Quantum
979 *restrict q;
980
981 register ssize_t
982 x;
983
984 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
985 continue;
986 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
987 1,exception);
988 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000989 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000990 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
991 break;
992 for (x=0; x < (ssize_t) composite_image->columns; x++)
993 {
994 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
995 {
cristyed231572011-07-14 02:18:59 +0000996 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000997 continue;
998 }
999 /*
1000 Displace the offset.
1001 */
1002 offset.x=(horizontal_scale*(GetPixelRed(composite_image,p)-
1003 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1004 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1005 x : 0);
1006 offset.y=(vertical_scale*(GetPixelGreen(composite_image,p)-
1007 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1008 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1009 y : 0);
1010 (void) InterpolatePixelInfo(image,image_view,
1011 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1012 &pixel,exception);
1013 /*
1014 Mask with the 'invalid pixel mask' in alpha channel.
1015 */
1016 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
cristy99abff32011-12-24 20:45:16 +00001017 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001018 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001019 p+=GetPixelChannels(composite_image);
1020 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001021 }
1022 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1023 if (sync == MagickFalse)
1024 break;
1025 }
1026 destination_view=DestroyCacheView(destination_view);
1027 composite_view=DestroyCacheView(composite_view);
1028 image_view=DestroyCacheView(image_view);
1029 composite_image=destination_image;
1030 break;
1031 }
1032 case DissolveCompositeOp:
1033 {
1034 /*
1035 Geometry arguments to dissolve factors.
1036 */
1037 value=GetImageArtifact(composite_image,"compose:args");
1038 if (value != (char *) NULL)
1039 {
1040 flags=ParseGeometry(value,&geometry_info);
1041 source_dissolve=geometry_info.rho/100.0;
1042 destination_dissolve=1.0;
1043 if ((source_dissolve-MagickEpsilon) < 0.0)
1044 source_dissolve=0.0;
1045 if ((source_dissolve+MagickEpsilon) > 1.0)
1046 {
1047 destination_dissolve=2.0-source_dissolve;
1048 source_dissolve=1.0;
1049 }
1050 if ((flags & SigmaValue) != 0)
1051 destination_dissolve=geometry_info.sigma/100.0;
1052 if ((destination_dissolve-MagickEpsilon) < 0.0)
1053 destination_dissolve=0.0;
1054 modify_outside_overlay=MagickTrue;
1055 if ((destination_dissolve+MagickEpsilon) > 1.0 )
1056 {
1057 destination_dissolve=1.0;
1058 modify_outside_overlay=MagickFalse;
1059 }
1060 }
1061 break;
1062 }
1063 case BlendCompositeOp:
1064 {
1065 value=GetImageArtifact(composite_image,"compose:args");
1066 if (value != (char *) NULL)
1067 {
1068 flags=ParseGeometry(value,&geometry_info);
1069 source_dissolve=geometry_info.rho/100.0;
1070 destination_dissolve=1.0-source_dissolve;
1071 if ((flags & SigmaValue) != 0)
1072 destination_dissolve=geometry_info.sigma/100.0;
1073 modify_outside_overlay=MagickTrue;
1074 if ((destination_dissolve+MagickEpsilon) > 1.0)
1075 modify_outside_overlay=MagickFalse;
1076 }
1077 break;
1078 }
1079 case MathematicsCompositeOp:
1080 {
1081 /*
1082 Just collect the values from "compose:args", setting.
1083 Unused values are set to zero automagically.
1084
1085 Arguments are normally a comma separated list, so this probably should
1086 be changed to some 'general comma list' parser, (with a minimum
1087 number of values)
1088 */
1089 SetGeometryInfo(&geometry_info);
1090 value=GetImageArtifact(composite_image,"compose:args");
1091 if (value != (char *) NULL)
1092 (void) ParseGeometry(value,&geometry_info);
1093 break;
1094 }
1095 case ModulateCompositeOp:
1096 {
1097 /*
1098 Determine the brightness and saturation scale.
1099 */
1100 value=GetImageArtifact(composite_image,"compose:args");
1101 if (value != (char *) NULL)
1102 {
1103 flags=ParseGeometry(value,&geometry_info);
1104 percent_brightness=geometry_info.rho;
1105 if ((flags & SigmaValue) != 0)
1106 percent_saturation=geometry_info.sigma;
1107 }
1108 break;
1109 }
1110 case ThresholdCompositeOp:
1111 {
1112 /*
1113 Determine the amount and threshold.
1114 */
1115 value=GetImageArtifact(composite_image,"compose:args");
1116 if (value != (char *) NULL)
1117 {
1118 flags=ParseGeometry(value,&geometry_info);
1119 amount=geometry_info.rho;
1120 threshold=geometry_info.sigma;
1121 if ((flags & SigmaValue) == 0)
1122 threshold=0.05f;
1123 }
1124 threshold*=QuantumRange;
1125 break;
1126 }
1127 default:
1128 break;
1129 }
1130 value=GetImageArtifact(composite_image,"compose:outside-overlay");
1131 if (value != (const char *) NULL)
1132 modify_outside_overlay=IsMagickTrue(value);
1133 /*
1134 Composite image.
1135 */
1136 status=MagickTrue;
1137 progress=0;
1138 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy4c08aed2011-07-01 19:47:50 +00001139 image_view=AcquireCacheView(image);
1140 composite_view=AcquireCacheView(composite_image);
1141#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00001142 #pragma omp parallel for schedule(static,4) shared(progress,status)
cristy4c08aed2011-07-01 19:47:50 +00001143#endif
1144 for (y=0; y < (ssize_t) image->rows; y++)
1145 {
1146 const Quantum
1147 *pixels;
1148
1149 double
cristye4a40472011-12-22 02:56:19 +00001150 blue,
cristy4c08aed2011-07-01 19:47:50 +00001151 brightness,
cristye4a40472011-12-22 02:56:19 +00001152 green,
cristy4c08aed2011-07-01 19:47:50 +00001153 hue,
cristye4a40472011-12-22 02:56:19 +00001154 red,
cristy4c08aed2011-07-01 19:47:50 +00001155 saturation;
1156
cristy4c08aed2011-07-01 19:47:50 +00001157 register const Quantum
1158 *restrict p;
1159
1160 register Quantum
1161 *restrict q;
1162
1163 register ssize_t
1164 x;
1165
1166 if (status == MagickFalse)
1167 continue;
1168 if (modify_outside_overlay == MagickFalse)
1169 {
1170 if (y < y_offset)
1171 continue;
1172 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1173 continue;
1174 }
1175 /*
1176 If pixels is NULL, y is outside overlay region.
1177 */
1178 pixels=(Quantum *) NULL;
1179 p=(Quantum *) NULL;
1180 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1181 {
1182 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1183 composite_image->columns,1,exception);
1184 if (p == (const Quantum *) NULL)
1185 {
1186 status=MagickFalse;
1187 continue;
1188 }
1189 pixels=p;
1190 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001191 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001192 }
1193 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001194 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001195 {
1196 status=MagickFalse;
1197 continue;
1198 }
cristy4c08aed2011-07-01 19:47:50 +00001199 hue=0.0;
1200 saturation=0.0;
1201 brightness=0.0;
1202 for (x=0; x < (ssize_t) image->columns; x++)
1203 {
cristye4a40472011-12-22 02:56:19 +00001204 MagickRealType
1205 alpha,
1206 Da,
1207 Dc,
1208 Dca,
1209 gamma,
1210 Sa,
1211 Sc,
1212 Sca;
1213
1214 register ssize_t
1215 i;
1216
cristy4c08aed2011-07-01 19:47:50 +00001217 if (modify_outside_overlay == MagickFalse)
1218 {
1219 if (x < x_offset)
1220 {
cristyed231572011-07-14 02:18:59 +00001221 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001222 continue;
1223 }
1224 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1225 break;
1226 }
cristye4a40472011-12-22 02:56:19 +00001227 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1228 ((x-x_offset) >= (ssize_t) composite_image->columns))
1229 {
1230 Quantum
1231 source[MaxPixelChannels];
1232
1233 /*
1234 Virtual composite:
1235 Sc: source color.
1236 Dc: destination color.
1237 */
1238 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1239 source,exception);
1240 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1241 {
1242 MagickRealType
1243 pixel;
1244
1245 PixelChannel
1246 channel;
1247
1248 PixelTrait
1249 composite_traits,
1250 traits;
1251
1252 channel=GetPixelChannelMapChannel(image,i);
1253 traits=GetPixelChannelMapTraits(image,channel);
1254 composite_traits=GetPixelChannelMapTraits(composite_image,
1255 channel);
1256 if ((traits == UndefinedPixelTrait) ||
1257 (composite_traits == UndefinedPixelTrait))
1258 continue;
1259 switch (compose)
1260 {
1261 case ChangeMaskCompositeOp:
1262 case CopyAlphaCompositeOp:
1263 case DstAtopCompositeOp:
1264 case DstInCompositeOp:
1265 case InCompositeOp:
1266 case OutCompositeOp:
1267 case SrcInCompositeOp:
1268 case SrcOutCompositeOp:
1269 {
1270 pixel=(MagickRealType) q[i];
1271 if (channel == AlphaPixelChannel)
1272 pixel=(MagickRealType) TransparentAlpha;
1273 break;
1274 }
1275 case ClearCompositeOp:
1276 case CopyCompositeOp:
1277 case ReplaceCompositeOp:
1278 case SrcCompositeOp:
1279 {
1280 if (channel == AlphaPixelChannel)
1281 {
1282 pixel=(MagickRealType) TransparentAlpha;
1283 break;
1284 }
1285 pixel=0.0;
1286 break;
1287 }
cristy99abff32011-12-24 20:45:16 +00001288 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001289 case DissolveCompositeOp:
1290 {
1291 if (channel == AlphaPixelChannel)
1292 {
1293 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1294 source);
1295 break;
1296 }
1297 pixel=(MagickRealType) source[channel];
1298 break;
1299 }
1300 default:
1301 {
1302 pixel=(MagickRealType) source[channel];
1303 break;
1304 }
1305 }
1306 q[i]=ClampToQuantum(pixel);
1307 }
1308 q+=GetPixelChannels(image);
1309 continue;
1310 }
1311 /*
1312 Authentic composite:
1313 Sa: normalized source alpha.
1314 Da: normalized destination alpha.
1315 */
1316 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1317 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001318 switch (compose)
1319 {
cristye4a40472011-12-22 02:56:19 +00001320 case BumpmapCompositeOp:
1321 {
1322 alpha=GetPixelIntensity(composite_image,p)*Sa;
1323 break;
1324 }
cristycdc168f2011-12-21 15:24:39 +00001325 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001326 case ColorDodgeCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001327 case DifferenceCompositeOp:
1328 case DivideDstCompositeOp:
1329 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001330 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001331 case HardLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001332 case LinearBurnCompositeOp:
1333 case LinearDodgeCompositeOp:
1334 case LinearLightCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001335 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001336 case MinusDstCompositeOp:
1337 case MinusSrcCompositeOp:
1338 case ModulusAddCompositeOp:
1339 case ModulusSubtractCompositeOp:
1340 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001341 case OverlayCompositeOp:
1342 case PegtopLightCompositeOp:
1343 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001344 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001345 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001346 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001347 {
1348 alpha=RoundToUnity(Sa+Da-Sa*Da);
1349 break;
1350 }
1351 case DarkenCompositeOp:
1352 case DstAtopCompositeOp:
1353 case DstInCompositeOp:
1354 case InCompositeOp:
1355 case LightenCompositeOp:
1356 case SrcInCompositeOp:
1357 {
1358 alpha=Sa*Da;
1359 break;
1360 }
1361 case DissolveCompositeOp:
1362 {
1363 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1364 Sa+destination_dissolve*Da;
1365 break;
1366 }
1367 case DstOverCompositeOp:
1368 {
1369 alpha=Da*(-Sa)+Da+Sa;
1370 break;
1371 }
1372 case DstOutCompositeOp:
1373 {
1374 alpha=Da*(1.0-Sa);
1375 break;
1376 }
1377 case OutCompositeOp:
1378 case SrcOutCompositeOp:
1379 {
1380 alpha=Sa*(1.0-Da);
1381 break;
1382 }
1383 case OverCompositeOp:
1384 case SrcOverCompositeOp:
1385 {
1386 alpha=Sa*(-Da)+Sa+Da;
1387 break;
1388 }
cristy99abff32011-12-24 20:45:16 +00001389 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001390 case PlusCompositeOp:
1391 {
1392 alpha=RoundToUnity(Sa+Da);
1393 break;
1394 }
cristy4c08aed2011-07-01 19:47:50 +00001395 case XorCompositeOp:
1396 {
cristye4a40472011-12-22 02:56:19 +00001397 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001398 break;
1399 }
1400 default:
cristye4a40472011-12-22 02:56:19 +00001401 {
1402 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001403 break;
cristye4a40472011-12-22 02:56:19 +00001404 }
cristy4c08aed2011-07-01 19:47:50 +00001405 }
cristye4a40472011-12-22 02:56:19 +00001406 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1407 {
cristye10859a2011-12-18 22:28:59 +00001408 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001409 pixel;
cristye10859a2011-12-18 22:28:59 +00001410
cristye4a40472011-12-22 02:56:19 +00001411 PixelChannel
1412 channel;
cristye10859a2011-12-18 22:28:59 +00001413
cristye4a40472011-12-22 02:56:19 +00001414 PixelTrait
1415 composite_traits,
1416 traits;
1417
1418 channel=GetPixelChannelMapChannel(image,i);
1419 traits=GetPixelChannelMapTraits(image,channel);
1420 composite_traits=GetPixelChannelMapTraits(composite_image,channel);
1421 if ((traits == UndefinedPixelTrait) ||
1422 (composite_traits == UndefinedPixelTrait))
1423 continue;
1424 /*
1425 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001426 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001427 */
1428 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
cristye4a40472011-12-22 02:56:19 +00001429 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001430 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001431 {
cristye4a40472011-12-22 02:56:19 +00001432 if (channel != AlphaPixelChannel)
1433 {
1434 /*
1435 Copy channel.
1436 */
1437 q[i]=ClampToQuantum(Sc);
cristye10859a2011-12-18 22:28:59 +00001438 continue;
cristye10859a2011-12-18 22:28:59 +00001439 }
cristye4a40472011-12-22 02:56:19 +00001440 /*
1441 Set alpha channel.
1442 */
cristye10859a2011-12-18 22:28:59 +00001443 switch (compose)
1444 {
cristye4a40472011-12-22 02:56:19 +00001445 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001446 case CopyBlackCompositeOp:
1447 case CopyBlueCompositeOp:
1448 case CopyCyanCompositeOp:
1449 case CopyGreenCompositeOp:
1450 case CopyMagentaCompositeOp:
1451 case CopyRedCompositeOp:
1452 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001453 case SrcAtopCompositeOp:
1454 case DstCompositeOp:
1455 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001456 {
cristye4a40472011-12-22 02:56:19 +00001457 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001458 break;
1459 }
cristye10859a2011-12-18 22:28:59 +00001460 case ChangeMaskCompositeOp:
1461 {
cristye4a40472011-12-22 02:56:19 +00001462 MagickBooleanType
1463 equivalent;
1464
cristy99abff32011-12-24 20:45:16 +00001465 if (Da > ((MagickRealType) QuantumRange/2.0))
1466 {
1467 pixel=(MagickRealType) TransparentAlpha;
1468 break;
1469 }
cristye4a40472011-12-22 02:56:19 +00001470 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001471 if (equivalent != MagickFalse)
cristye4a40472011-12-22 02:56:19 +00001472 {
1473 pixel=(MagickRealType) TransparentAlpha;
1474 break;
1475 }
1476 pixel=(MagickRealType) OpaqueAlpha;
1477 break;
1478 }
cristy99abff32011-12-24 20:45:16 +00001479 case ClearCompositeOp:
1480 {
1481 pixel=(MagickRealType) TransparentAlpha;
1482 break;
1483 }
1484 case ColorizeCompositeOp:
1485 case HueCompositeOp:
1486 case LuminizeCompositeOp:
1487 case SaturateCompositeOp:
1488 {
1489 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1490 {
1491 pixel=QuantumRange*Da;
1492 break;
1493 }
1494 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
1495 {
1496 pixel=QuantumRange*Sa;
1497 break;
1498 }
1499 if (Sa < Da)
1500 {
1501 pixel=QuantumRange*Da;
1502 break;
1503 }
1504 pixel=QuantumRange*Sa;
1505 break;
1506 }
cristye4a40472011-12-22 02:56:19 +00001507 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001508 case CopyAlphaCompositeOp:
1509 case DisplaceCompositeOp:
1510 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001511 case DstAtopCompositeOp:
1512 case ReplaceCompositeOp:
1513 case SrcCompositeOp:
1514 {
1515 pixel=QuantumRange*Sa;
1516 break;
1517 }
1518 case DarkenIntensityCompositeOp:
1519 {
cristy99abff32011-12-24 20:45:16 +00001520 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1521 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001522 break;
1523 }
1524 case LightenIntensityCompositeOp:
1525 {
1526 pixel=Sa*GetPixelIntensity(composite_image,p) >
1527 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001528 break;
1529 }
cristy99abff32011-12-24 20:45:16 +00001530 case ModulateCompositeOp:
1531 {
1532 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
1533 {
1534 pixel=QuantumRange*Da;
1535 break;
1536 }
1537 pixel=QuantumRange*Da;
1538 break;
1539 }
cristye10859a2011-12-18 22:28:59 +00001540 default:
1541 {
cristye4a40472011-12-22 02:56:19 +00001542 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001543 break;
1544 }
1545 }
cristye4a40472011-12-22 02:56:19 +00001546 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001547 continue;
1548 }
1549 /*
cristy99abff32011-12-24 20:45:16 +00001550 Porter-Duff compositions:
1551 Sca: source normalized color multiplied by alpha.
1552 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001553 */
cristy99abff32011-12-24 20:45:16 +00001554 Sca=QuantumScale*Sa*Sc;
1555 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001556 switch (compose)
1557 {
cristye10859a2011-12-18 22:28:59 +00001558 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001559 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001560 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001561 {
cristy99abff32011-12-24 20:45:16 +00001562 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001563 break;
1564 }
1565 default:
1566 break;
1567 }
cristye4a40472011-12-22 02:56:19 +00001568 gamma=1.0/(fabs(alpha) <= MagickEpsilon ? 1.0 : alpha);
1569 switch (compose)
1570 {
1571 case AtopCompositeOp:
1572 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001573 {
cristye4a40472011-12-22 02:56:19 +00001574 pixel=Sc*Sa+Dc*(1.0-Sa);
1575 break;
cristye10859a2011-12-18 22:28:59 +00001576 }
cristye4a40472011-12-22 02:56:19 +00001577 case BlendCompositeOp:
1578 {
1579 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1580 break;
1581 }
1582 case BlurCompositeOp:
1583 case DisplaceCompositeOp:
1584 case DistortCompositeOp:
1585 case CopyCompositeOp:
1586 case ReplaceCompositeOp:
1587 case SrcCompositeOp:
1588 {
1589 pixel=Sc;
1590 break;
1591 }
1592 case BumpmapCompositeOp:
1593 {
cristy99abff32011-12-24 20:45:16 +00001594 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001595 {
1596 pixel=Dc;
1597 break;
1598 }
1599 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1600 break;
1601 }
cristy99abff32011-12-24 20:45:16 +00001602 case ChangeMaskCompositeOp:
1603 {
1604 pixel=Dc;
1605 break;
1606 }
1607 case ClearCompositeOp:
1608 {
1609 pixel=0.0;
1610 break;
1611 }
cristye4a40472011-12-22 02:56:19 +00001612 case ColorBurnCompositeOp:
1613 {
1614 /*
1615 Refer to the March 2009 SVG specification.
1616 */
1617 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca-Da) < MagickEpsilon))
1618 {
cristy99abff32011-12-24 20:45:16 +00001619 pixel=QuantumRange*gamma*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001620 break;
1621 }
1622 if (Sca < MagickEpsilon)
1623 {
cristy99abff32011-12-24 20:45:16 +00001624 pixel=QuantumRange*gamma*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001625 break;
1626 }
cristy99abff32011-12-24 20:45:16 +00001627 pixel=QuantumRange*gamma*(Sa*Da-Sa*MagickMin(Da,(Da-Dca)*Sa/Sca)+
1628 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001629 break;
1630 }
1631 case ColorDodgeCompositeOp:
1632 {
1633 if ((fabs(Sca-Sa) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1634 {
cristy99abff32011-12-24 20:45:16 +00001635 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001636 break;
1637 }
1638 if (fabs(Sca-Sa) < MagickEpsilon)
1639 {
cristy99abff32011-12-24 20:45:16 +00001640 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001641 break;
1642 }
cristy99abff32011-12-24 20:45:16 +00001643 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(Sa-Sca)+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001644 (1.0-Sa));
1645 break;
1646 }
1647 case ColorizeCompositeOp:
1648 {
cristy99abff32011-12-24 20:45:16 +00001649 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001650 {
1651 pixel=Dc;
1652 break;
1653 }
cristy99abff32011-12-24 20:45:16 +00001654 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001655 {
1656 pixel=Sc;
1657 break;
1658 }
1659 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
cristy99abff32011-12-24 20:45:16 +00001660 GetPixelBlue(image,q),&sans,&sans,&brightness);
cristye4a40472011-12-22 02:56:19 +00001661 CompositeHSB(GetPixelRed(composite_image,p),
1662 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1663 &hue,&saturation,&sans);
1664 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1665 switch (channel)
1666 {
1667 case RedPixelChannel: pixel=red; break;
1668 case GreenPixelChannel: pixel=green; break;
1669 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001670 default: pixel=Dc; break;
1671 }
1672 break;
1673 }
cristye4a40472011-12-22 02:56:19 +00001674 case CopyAlphaCompositeOp:
1675 {
1676 if (channel == AlphaPixelChannel)
1677 {
1678 if (composite_image->matte != MagickFalse)
1679 pixel=(MagickRealType) GetPixelAlpha(composite_image,p);
1680 else
1681 pixel=(MagickRealType) GetPixelIntensity(composite_image,p);
1682 break;
1683 }
1684 pixel=Dc;
1685 break;
1686 }
1687 case CopyBlackCompositeOp:
1688 {
1689 pixel=(MagickRealType) GetPixelBlack(composite_image,p);
1690 break;
1691 }
1692 case CopyBlueCompositeOp:
1693 case CopyYellowCompositeOp:
1694 {
1695 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
1696 break;
1697 }
1698 case CopyGreenCompositeOp:
1699 case CopyMagentaCompositeOp:
1700 {
1701 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
1702 break;
1703 }
1704 case CopyRedCompositeOp:
1705 case CopyCyanCompositeOp:
1706 {
1707 pixel=(MagickRealType) GetPixelRed(composite_image,p);
1708 break;
1709 }
cristy99abff32011-12-24 20:45:16 +00001710 case DarkenCompositeOp:
1711 {
1712 /*
1713 Darken is equivalent to a 'Minimum' method
1714 OR a greyscale version of a binary 'Or'
1715 OR the 'Intersection' of pixel sets.
1716 */
1717 if (Sc < Dc)
1718 {
1719 pixel=gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1720 break;
1721 }
1722 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1723 break;
1724 }
cristye4a40472011-12-22 02:56:19 +00001725 case DarkenIntensityCompositeOp:
1726 {
cristy99abff32011-12-24 20:45:16 +00001727 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1728 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001729 break;
1730 }
1731 case DifferenceCompositeOp:
1732 {
1733 pixel=gamma*(Sa*Sc+Da*Dc-Sa*Da*2.0*MagickMin(Sc,Dc));
1734 break;
1735 }
1736 case DissolveCompositeOp:
1737 {
1738 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1739 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1740 break;
1741 }
1742 case DivideDstCompositeOp:
1743 {
1744 if ((fabs(Sca) < MagickEpsilon) && (fabs(Dca) < MagickEpsilon))
1745 {
cristy99abff32011-12-24 20:45:16 +00001746 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001747 break;
1748 }
1749 if (fabs(Dca) < MagickEpsilon)
1750 {
cristy99abff32011-12-24 20:45:16 +00001751 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001752 break;
1753 }
cristy99abff32011-12-24 20:45:16 +00001754 pixel=QuantumRange*gamma*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001755 break;
1756 }
1757 case DivideSrcCompositeOp:
1758 {
1759 if ((fabs(Dca) < MagickEpsilon) && (fabs(Sca) < MagickEpsilon))
1760 {
cristy99abff32011-12-24 20:45:16 +00001761 pixel=QuantumRange*gamma*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001762 break;
1763 }
1764 if (fabs(Sca) < MagickEpsilon)
1765 {
cristy99abff32011-12-24 20:45:16 +00001766 pixel=QuantumRange*gamma*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001767 break;
1768 }
cristy99abff32011-12-24 20:45:16 +00001769 pixel=QuantumRange*gamma*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001770 break;
1771 }
1772 case DstAtopCompositeOp:
1773 {
1774 pixel=Dc*Da+Sc*(1.0-Da);
1775 break;
1776 }
1777 case DstCompositeOp:
1778 case NoCompositeOp:
1779 {
1780 pixel=Dc;
1781 break;
1782 }
1783 case DstInCompositeOp:
1784 {
1785 pixel=gamma*(Sa*Dc*Sa);
1786 break;
1787 }
1788 case DstOutCompositeOp:
1789 {
1790 pixel=gamma*(Da*Dc*(1.0-Sa));
1791 break;
1792 }
1793 case DstOverCompositeOp:
1794 {
1795 pixel=gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1796 break;
1797 }
1798 case ExclusionCompositeOp:
1799 {
cristy99abff32011-12-24 20:45:16 +00001800 pixel=QuantumRange*gamma*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+
1801 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001802 break;
1803 }
1804 case HardLightCompositeOp:
1805 {
1806 if ((2.0*Sca) < Sa)
1807 {
cristy99abff32011-12-24 20:45:16 +00001808 pixel=QuantumRange*gamma*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
cristye4a40472011-12-22 02:56:19 +00001809 (1.0-Sa));
1810 break;
1811 }
cristy99abff32011-12-24 20:45:16 +00001812 pixel=QuantumRange*gamma*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+
cristye4a40472011-12-22 02:56:19 +00001813 Dca*(1.0-Sa));
1814 break;
1815 }
1816 case HueCompositeOp:
1817 {
cristy99abff32011-12-24 20:45:16 +00001818 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001819 {
1820 pixel=Dc;
1821 break;
1822 }
cristy99abff32011-12-24 20:45:16 +00001823 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001824 {
1825 pixel=Sc;
1826 break;
1827 }
1828 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1829 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1830 CompositeHSB(GetPixelRed(composite_image,p),
1831 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1832 &hue,&sans,&sans);
1833 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1834 switch (channel)
1835 {
1836 case RedPixelChannel: pixel=red; break;
1837 case GreenPixelChannel: pixel=green; break;
1838 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001839 default: pixel=Dc; break;
1840 }
1841 break;
1842 }
1843 case InCompositeOp:
1844 case SrcInCompositeOp:
1845 {
1846 pixel=gamma*(Da*Sc*Da);
1847 break;
1848 }
cristy99abff32011-12-24 20:45:16 +00001849 case LinearBurnCompositeOp:
1850 {
1851 /*
1852 LinearBurn: as defined by Abode Photoshop, according to
1853 http://www.simplefilter.de/en/basics/mixmods.html is:
1854
1855 f(Sc,Dc) = Sc + Dc - 1
1856 */
1857 pixel=QuantumRange*gamma*(Sca+Dca-Sa*Da);
1858 break;
1859 }
1860 case LinearDodgeCompositeOp:
1861 {
1862 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
1863 break;
1864 }
1865 case LinearLightCompositeOp:
1866 {
1867 /*
1868 LinearLight: as defined by Abode Photoshop, according to
1869 http://www.simplefilter.de/en/basics/mixmods.html is:
1870
1871 f(Sc,Dc) = Dc + 2*Sc - 1
1872 */
1873 pixel=QuantumRange*gamma*((Sca-Sa)*Da+Sca+Dca);
1874 break;
1875 }
1876 case LightenCompositeOp:
1877 {
1878 if (Sc > Dc)
1879 {
1880 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
1881 break;
1882 }
1883 pixel=QuantumRange*gamma*(Da*Dc-Da*Sa*Sc+Sa*Sc);
1884 break;
1885 }
cristye4a40472011-12-22 02:56:19 +00001886 case LightenIntensityCompositeOp:
1887 {
1888 /*
1889 Lighten is equivalent to a 'Maximum' method
1890 OR a greyscale version of a binary 'And'
1891 OR the 'Union' of pixel sets.
1892 */
1893 pixel=Sa*GetPixelIntensity(composite_image,p) >
1894 Da*GetPixelIntensity(image,q) ? Sc : Dc;
1895 break;
1896 }
cristye4a40472011-12-22 02:56:19 +00001897 case LuminizeCompositeOp:
1898 {
cristy99abff32011-12-24 20:45:16 +00001899 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001900 {
1901 pixel=Dc;
1902 break;
1903 }
cristy99abff32011-12-24 20:45:16 +00001904 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001905 {
1906 pixel=Sc;
1907 break;
1908 }
1909 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1910 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1911 CompositeHSB(GetPixelRed(composite_image,p),
1912 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
1913 &sans,&sans,&brightness);
1914 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1915 switch (channel)
1916 {
1917 case RedPixelChannel: pixel=red; break;
1918 case GreenPixelChannel: pixel=green; break;
1919 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001920 default: pixel=Dc; break;
1921 }
1922 break;
1923 }
1924 case MathematicsCompositeOp:
1925 {
1926 /*
1927 'Mathematics' a free form user control mathematical composition
1928 is defined as...
1929
1930 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
1931
1932 Where the arguments A,B,C,D are (currently) passed to composite
1933 as a command separated 'geometry' string in "compose:args" image
1934 artifact.
1935
1936 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
1937
1938 Applying the SVG transparency formula (see above), we get...
1939
1940 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
1941
1942 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
1943 Dca*(1.0-Sa)
1944 */
1945 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
1946 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
1947 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
1948 break;
1949 }
1950 case MinusDstCompositeOp:
1951 {
1952 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
1953 break;
1954 }
1955 case MinusSrcCompositeOp:
1956 {
1957 /*
1958 Minus source from destination.
1959
1960 f(Sc,Dc) = Sc - Dc
1961 */
cristy99abff32011-12-24 20:45:16 +00001962 pixel=QuantumRange*gamma*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00001963 break;
1964 }
1965 case ModulateCompositeOp:
1966 {
1967 ssize_t
1968 offset;
1969
cristy99abff32011-12-24 20:45:16 +00001970 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001971 {
1972 pixel=Dc;
1973 break;
1974 }
1975 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
1976 if (offset == 0)
1977 {
1978 pixel=Dc;
1979 break;
1980 }
1981 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
1982 GetPixelBlue(image,q),&hue,&saturation,&brightness);
1983 brightness+=(0.01*percent_brightness*offset)/midpoint;
1984 saturation*=0.01*percent_saturation;
1985 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
1986 switch (channel)
1987 {
1988 case RedPixelChannel: pixel=red; break;
1989 case GreenPixelChannel: pixel=green; break;
1990 case BluePixelChannel: pixel=blue; break;
1991 default: pixel=Dc; break;
1992 }
1993 break;
1994 }
1995 case ModulusAddCompositeOp:
1996 {
1997 pixel=Sc+Dc;
1998 if (pixel > QuantumRange)
1999 pixel-=(QuantumRange+1.0);
2000 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2001 break;
2002 }
2003 case ModulusSubtractCompositeOp:
2004 {
cristy99abff32011-12-24 20:45:16 +00002005 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002006 if (pixel < 0.0)
2007 pixel+=(QuantumRange+1.0);
2008 pixel=gamma*(pixel*Sa*Da+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
2009 break;
2010 }
2011 case MultiplyCompositeOp:
2012 {
cristy99abff32011-12-24 20:45:16 +00002013 pixel=QuantumRange*gamma*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002014 break;
2015 }
2016 case OutCompositeOp:
2017 case SrcOutCompositeOp:
2018 {
2019 pixel=gamma*(Sa*Sc*(1.0-Da));
2020 break;
2021 }
2022 case OverCompositeOp:
2023 case SrcOverCompositeOp:
2024 {
cristy99abff32011-12-24 20:45:16 +00002025 pixel=QuantumRange*gamma*(Sa*Sc-Sa*Da*Dc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002026 break;
2027 }
2028 case OverlayCompositeOp:
2029 {
2030 if ((2.0*Dca) < Da)
cristy99abff32011-12-24 20:45:16 +00002031 {
2032 pixel=QuantumRange*gamma*(2.0*Dca*Sca+Dca*(1.0-Sa)+Sca*
2033 (1.0-Da));
2034 break;
2035 }
2036 pixel=QuantumRange*gamma*(Da*Sa-2.0*(Sa-Sca)*(Da-Dca)+Dca*(1.0-Sa)+
2037 Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002038 break;
2039 }
2040 case PegtopLightCompositeOp:
2041 {
2042 /*
2043 PegTop: A Soft-Light alternative: A continuous version of the
2044 Softlight function, producing very similar results.
2045
2046 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2047
2048 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2049 */
2050 if (fabs(Da) < MagickEpsilon)
2051 {
cristy99abff32011-12-24 20:45:16 +00002052 pixel=QuantumRange*gamma*(Sca);
cristye4a40472011-12-22 02:56:19 +00002053 break;
2054 }
cristy99abff32011-12-24 20:45:16 +00002055 pixel=QuantumRange*gamma*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-
2056 Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002057 break;
2058 }
2059 case PinLightCompositeOp:
2060 {
2061 /*
2062 PinLight: A Photoshop 7 composition method
2063 http://www.simplefilter.de/en/basics/mixmods.html
2064
2065 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2066 */
2067 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2068 {
cristy99abff32011-12-24 20:45:16 +00002069 pixel=QuantumRange*gamma*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002070 break;
2071 }
2072 if ((Dca*Sa) > (2.0*Sca*Da))
2073 {
cristy99abff32011-12-24 20:45:16 +00002074 pixel=QuantumRange*gamma*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002075 break;
2076 }
cristy99abff32011-12-24 20:45:16 +00002077 pixel=QuantumRange*gamma*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002078 break;
2079 }
2080 case PlusCompositeOp:
2081 {
cristy99abff32011-12-24 20:45:16 +00002082 pixel=QuantumRange*gamma*(Sa*Sc+Da*Dc);
cristye4a40472011-12-22 02:56:19 +00002083 break;
2084 }
2085 case SaturateCompositeOp:
2086 {
cristy99abff32011-12-24 20:45:16 +00002087 if (fabs(QuantumRange*Sa-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002088 {
2089 pixel=Dc;
2090 break;
2091 }
cristy99abff32011-12-24 20:45:16 +00002092 if (fabs(QuantumRange*Da-TransparentAlpha) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002093 {
2094 pixel=Sc;
2095 break;
2096 }
2097 CompositeHSB(GetPixelRed(image,q),GetPixelGreen(image,q),
2098 GetPixelBlue(image,q),&hue,&saturation,&brightness);
2099 CompositeHSB(GetPixelRed(composite_image,p),
2100 GetPixelGreen(composite_image,p),GetPixelBlue(composite_image,p),
2101 &sans,&saturation,&sans);
2102 HSBComposite(hue,saturation,brightness,&red,&green,&blue);
2103 switch (channel)
2104 {
2105 case RedPixelChannel: pixel=red; break;
2106 case GreenPixelChannel: pixel=green; break;
2107 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002108 default: pixel=Dc; break;
2109 }
2110 break;
2111 }
2112 case ScreenCompositeOp:
2113 {
2114 /*
2115 Screen: a negated multiply:
2116
2117 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2118 */
cristy99abff32011-12-24 20:45:16 +00002119 pixel=QuantumRange*gamma*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002120 break;
2121 }
2122 case SoftLightCompositeOp:
2123 {
2124 /*
2125 Refer to the March 2009 SVG specification.
2126 */
2127 if ((2.0*Sca) < Sa)
2128 {
cristy99abff32011-12-24 20:45:16 +00002129 pixel=QuantumRange*gamma*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+
2130 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002131 break;
2132 }
2133 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2134 {
cristy99abff32011-12-24 20:45:16 +00002135 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*
2136 (4.0*(Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+
2137 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002138 break;
2139 }
cristy99abff32011-12-24 20:45:16 +00002140 pixel=QuantumRange*gamma*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-
2141 (Dca/Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002142 break;
2143 }
2144 case ThresholdCompositeOp:
2145 {
2146 MagickRealType
2147 delta;
2148
2149 delta=Sc-Dc;
2150 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
2151 {
2152 pixel=gamma*Dc;
2153 break;
2154 }
2155 pixel=gamma*(Dc+delta*amount);
2156 break;
2157 }
2158 case VividLightCompositeOp:
2159 {
2160 /*
2161 VividLight: A Photoshop 7 composition method. See
2162 http://www.simplefilter.de/en/basics/mixmods.html.
2163
2164 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2165 */
2166 if ((fabs(Sa) < MagickEpsilon) || (fabs(Sca-Sa) < MagickEpsilon))
2167 {
cristy99abff32011-12-24 20:45:16 +00002168 pixel=QuantumRange*gamma*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002169 break;
2170 }
2171 if ((2.0*Sca) <= Sa)
2172 {
cristy99abff32011-12-24 20:45:16 +00002173 pixel=QuantumRange*gamma*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*
2174 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002175 break;
2176 }
cristy99abff32011-12-24 20:45:16 +00002177 pixel=QuantumRange*gamma*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+
2178 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002179 break;
2180 }
2181 case XorCompositeOp:
2182 {
cristy99abff32011-12-24 20:45:16 +00002183 pixel=QuantumRange*gamma*(Sc*Sa*(1.0-Da)+Dc*Da*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002184 break;
2185 }
2186 default:
2187 {
2188 pixel=Sc;
2189 break;
2190 }
2191 }
2192 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002193 }
cristyed231572011-07-14 02:18:59 +00002194 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002195 channels=GetPixelChannels(composite_image);
2196 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002197 p=pixels;
cristyed231572011-07-14 02:18:59 +00002198 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002199 }
2200 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2201 status=MagickFalse;
2202 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2203 {
2204 MagickBooleanType
2205 proceed;
2206
2207#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyf4ad9df2011-07-08 16:49:03 +00002208 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002209#endif
2210 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2211 image->rows);
2212 if (proceed == MagickFalse)
2213 status=MagickFalse;
2214 }
2215 }
2216 composite_view=DestroyCacheView(composite_view);
2217 image_view=DestroyCacheView(image_view);
2218 if (destination_image != (Image * ) NULL)
2219 destination_image=DestroyImage(destination_image);
2220 return(status);
2221}
2222
2223/*
2224%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2225% %
2226% %
2227% %
2228% T e x t u r e I m a g e %
2229% %
2230% %
2231% %
2232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2233%
2234% TextureImage() repeatedly tiles the texture image across and down the image
2235% canvas.
2236%
2237% The format of the TextureImage method is:
2238%
cristye6178502011-12-23 17:02:29 +00002239% MagickBooleanType TextureImage(Image *image,const Image *texture_image,
cristye941a752011-10-15 01:52:48 +00002240% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002241%
2242% A description of each parameter follows:
2243%
2244% o image: the image.
2245%
cristye6178502011-12-23 17:02:29 +00002246% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002247%
2248*/
cristye6178502011-12-23 17:02:29 +00002249MagickExport MagickBooleanType TextureImage(Image *image,
2250 const Image *texture_image,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002251{
2252#define TextureImageTag "Texture/Image"
2253
2254 CacheView
2255 *image_view,
2256 *texture_view;
2257
cristy4c08aed2011-07-01 19:47:50 +00002258 MagickBooleanType
2259 status;
2260
2261 ssize_t
2262 y;
2263
2264 assert(image != (Image *) NULL);
2265 if (image->debug != MagickFalse)
2266 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2267 assert(image->signature == MagickSignature);
cristye6178502011-12-23 17:02:29 +00002268 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002269 return(MagickFalse);
cristye6178502011-12-23 17:02:29 +00002270 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod);
cristy574cc262011-08-05 01:23:58 +00002271 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00002272 return(MagickFalse);
2273 status=MagickTrue;
2274 if ((image->compose != CopyCompositeOp) &&
2275 ((image->compose != OverCompositeOp) || (image->matte != MagickFalse) ||
cristye6178502011-12-23 17:02:29 +00002276 (texture_image->matte != MagickFalse)))
cristy4c08aed2011-07-01 19:47:50 +00002277 {
2278 /*
2279 Tile texture onto the image background.
2280 */
2281#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00002282 #pragma omp parallel for schedule(static,4) shared(status) omp_throttle(1)
cristy4c08aed2011-07-01 19:47:50 +00002283#endif
cristye6178502011-12-23 17:02:29 +00002284 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002285 {
2286 register ssize_t
2287 x;
2288
2289 if (status == MagickFalse)
2290 continue;
cristye6178502011-12-23 17:02:29 +00002291 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002292 {
2293 MagickBooleanType
2294 thread_status;
2295
cristye6178502011-12-23 17:02:29 +00002296 thread_status=CompositeImage(image,image->compose,texture_image,x+
2297 texture_image->tile_offset.x,y+texture_image->tile_offset.y,
2298 exception);
cristy4c08aed2011-07-01 19:47:50 +00002299 if (thread_status == MagickFalse)
2300 {
2301 status=thread_status;
2302 break;
2303 }
2304 }
2305 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2306 {
2307 MagickBooleanType
2308 proceed;
2309
2310#if defined(MAGICKCORE_OPENMP_SUPPORT)
2311 #pragma omp critical (MagickCore_TextureImage)
2312#endif
2313 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2314 y,image->rows);
2315 if (proceed == MagickFalse)
2316 status=MagickFalse;
2317 }
2318 }
2319 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2320 image->rows,image->rows);
2321 return(status);
2322 }
2323 /*
2324 Tile texture onto the image background (optimized).
2325 */
2326 status=MagickTrue;
cristy4c08aed2011-07-01 19:47:50 +00002327 image_view=AcquireCacheView(image);
cristye6178502011-12-23 17:02:29 +00002328 texture_view=AcquireCacheView(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002329#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristye6178502011-12-23 17:02:29 +00002330 #pragma omp parallel for schedule(static,4) shared(status) omp_throttle(1)
cristy4c08aed2011-07-01 19:47:50 +00002331#endif
2332 for (y=0; y < (ssize_t) image->rows; y++)
2333 {
2334 MagickBooleanType
2335 sync;
2336
2337 register const Quantum
2338 *p,
2339 *pixels;
2340
2341 register ssize_t
2342 x;
2343
2344 register Quantum
2345 *q;
2346
2347 size_t
2348 width;
2349
2350 if (status == MagickFalse)
2351 continue;
cristye6178502011-12-23 17:02:29 +00002352 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2353 (y+texture_image->tile_offset.y) % texture_image->rows,
2354 texture_image->columns,1,exception);
2355 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002356 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2357 {
2358 status=MagickFalse;
2359 continue;
2360 }
cristye6178502011-12-23 17:02:29 +00002361 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002362 {
2363 register ssize_t
cristye6178502011-12-23 17:02:29 +00002364 j;
cristy4c08aed2011-07-01 19:47:50 +00002365
2366 p=pixels;
cristye6178502011-12-23 17:02:29 +00002367 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002368 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2369 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002370 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002371 {
cristye6178502011-12-23 17:02:29 +00002372 register ssize_t
2373 i;
2374
2375 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2376 {
2377 PixelChannel
2378 channel;
2379
2380 PixelTrait
2381 texture_traits,
2382 traits;
2383
2384 channel=GetPixelChannelMapChannel(texture_image,i);
2385 texture_traits=GetPixelChannelMapTraits(texture_image,channel);
2386 traits=GetPixelChannelMapTraits(image,channel);
2387 if ((traits == UndefinedPixelTrait) ||
2388 (texture_traits == UndefinedPixelTrait))
2389 continue;
2390 SetPixelChannel(image,channel,p[i],q);
2391 }
2392 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002393 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002394 }
2395 }
2396 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2397 if (sync == MagickFalse)
2398 status=MagickFalse;
2399 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2400 {
2401 MagickBooleanType
2402 proceed;
2403
2404#if defined(MAGICKCORE_OPENMP_SUPPORT)
2405 #pragma omp critical (MagickCore_TextureImage)
2406#endif
2407 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2408 image->rows);
2409 if (proceed == MagickFalse)
2410 status=MagickFalse;
2411 }
2412 }
2413 texture_view=DestroyCacheView(texture_view);
2414 image_view=DestroyCacheView(image_view);
2415 return(status);
2416}