blob: f19777a8b747464b8c09a0ace95aa5cb559df1aa [file] [log] [blame]
cristy0b3cd892012-08-01 23:13:50 +00001/*
cristy4c08aed2011-07-01 19:47:50 +00002%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy4c08aed2011-07-01 19:47:50 +000017% July 1992 %
18% %
19% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 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"
cristy6a2180c2013-05-27 10:28:36 +000048#include "MagickCore/channel.h"
cristy4c08aed2011-07-01 19:47:50 +000049#include "MagickCore/client.h"
50#include "MagickCore/color.h"
51#include "MagickCore/color-private.h"
52#include "MagickCore/colorspace.h"
53#include "MagickCore/colorspace-private.h"
54#include "MagickCore/composite.h"
55#include "MagickCore/composite-private.h"
56#include "MagickCore/constitute.h"
57#include "MagickCore/draw.h"
58#include "MagickCore/fx.h"
59#include "MagickCore/gem.h"
60#include "MagickCore/geometry.h"
61#include "MagickCore/image.h"
62#include "MagickCore/image-private.h"
63#include "MagickCore/list.h"
64#include "MagickCore/log.h"
65#include "MagickCore/monitor.h"
66#include "MagickCore/monitor-private.h"
67#include "MagickCore/memory_.h"
68#include "MagickCore/option.h"
69#include "MagickCore/pixel-accessor.h"
70#include "MagickCore/property.h"
71#include "MagickCore/quantum.h"
72#include "MagickCore/resample.h"
73#include "MagickCore/resource_.h"
74#include "MagickCore/string_.h"
75#include "MagickCore/thread-private.h"
cristy191c0b72012-08-12 16:29:52 +000076#include "MagickCore/threshold.h"
cristy63a81872012-03-22 15:52:52 +000077#include "MagickCore/token.h"
cristy4c08aed2011-07-01 19:47:50 +000078#include "MagickCore/utility.h"
cristyd1dd6e42011-09-04 01:46:08 +000079#include "MagickCore/utility-private.h"
cristy4c08aed2011-07-01 19:47:50 +000080#include "MagickCore/version.h"
81
82/*
83%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84% %
85% %
86% %
cristyf4ad9df2011-07-08 16:49:03 +000087% C o m p o s i t e I m a g e %
cristy4c08aed2011-07-01 19:47:50 +000088% %
89% %
90% %
91%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
92%
cristyf4ad9df2011-07-08 16:49:03 +000093% CompositeImage() returns the second image composited onto the first
cristy4c08aed2011-07-01 19:47:50 +000094% at the specified offset, using the specified composite method.
95%
cristyf4ad9df2011-07-08 16:49:03 +000096% The format of the CompositeImage method is:
cristy4c08aed2011-07-01 19:47:50 +000097%
98% MagickBooleanType CompositeImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +000099% const Image *composite_image,const CompositeOperator compose,
100% const MagickBooleanType clip_to_self,const ssize_t x_offset,
101% const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000102%
103% A description of each parameter follows:
104%
105% o image: the destination image, modified by he composition
106%
cristyfeb3e962012-03-29 17:25:55 +0000107% o composite_image: the composite (source) image.
108%
cristy4c08aed2011-07-01 19:47:50 +0000109% o compose: This operator affects how the composite is applied to
110% the image. The operators and how they are utilized are listed here
111% http://www.w3.org/TR/SVG12/#compositing.
112%
cristyfeb3e962012-03-29 17:25:55 +0000113% o clip_to_self: set to MagickTrue to limit composition to area composed.
cristy4c08aed2011-07-01 19:47:50 +0000114%
115% o x_offset: the column offset of the composited image.
116%
117% o y_offset: the row offset of the composited image.
118%
dirk55a83c72014-08-22 18:44:23 +0000119% Extra Controls from Image meta-data in 'image' (artifacts)
cristy4c08aed2011-07-01 19:47:50 +0000120%
121% o "compose:args"
122% A string containing extra numerical arguments for specific compose
123% methods, generally expressed as a 'geometry' or a comma separated list
124% of numbers.
125%
126% Compose methods needing such arguments include "BlendCompositeOp" and
127% "DisplaceCompositeOp".
128%
cristye941a752011-10-15 01:52:48 +0000129% o exception: return any errors or warnings in this structure.
130%
cristy4c08aed2011-07-01 19:47:50 +0000131*/
132
anthonyea068a52012-04-09 05:46:25 +0000133/*
134 Composition based on the SVG specification:
135
136 A Composition is defined by...
137 Color Function : f(Sc,Dc) where Sc and Dc are the normizalized colors
138 Blending areas : X = 1 for area of overlap, ie: f(Sc,Dc)
139 Y = 1 for source preserved
140 Z = 1 for destination preserved
141
142 Conversion to transparency (then optimized)
143 Dca' = f(Sc, Dc)*Sa*Da + Y*Sca*(1-Da) + Z*Dca*(1-Sa)
144 Da' = X*Sa*Da + Y*Sa*(1-Da) + Z*Da*(1-Sa)
145
146 Where...
147 Sca = Sc*Sa normalized Source color divided by Source alpha
148 Dca = Dc*Da normalized Dest color divided by Dest alpha
149 Dc' = Dca'/Da' the desired color value for this channel.
150
151 Da' in in the follow formula as 'gamma' The resulting alpla value.
152
153 Most functions use a blending mode of over (X=1,Y=1,Z=1) this results in
154 the following optimizations...
155 gamma = Sa+Da-Sa*Da;
156 gamma = 1 - QuantiumScale*alpha * QuantiumScale*beta;
157 opacity = QuantiumScale*alpha*beta; // over blend, optimized 1-Gamma
158
159 The above SVG definitions also definate that Mathematical Composition
160 methods should use a 'Over' blending mode for Alpha Channel.
161 It however was not applied for composition modes of 'Plus', 'Minus',
162 the modulus versions of 'Add' and 'Subtract'.
163
164 Mathematical operator changes to be applied from IM v6.7...
165
166 1) Modulus modes 'Add' and 'Subtract' are obsoleted and renamed
167 'ModulusAdd' and 'ModulusSubtract' for clarity.
168
169 2) All mathematical compositions work as per the SVG specification
170 with regard to blending. This now includes 'ModulusAdd' and
171 'ModulusSubtract'.
172
173 3) When the special channel flag 'sync' (syncronize channel updates)
174 is turned off (enabled by default) then mathematical compositions are
175 only performed on the channels specified, and are applied
176 independantally of each other. In other words the mathematics is
177 performed as 'pure' mathematical operations, rather than as image
178 operations.
179*/
cristy4c08aed2011-07-01 19:47:50 +0000180
cristy7159f662012-10-28 17:32:43 +0000181static inline MagickRealType MagickMin(const MagickRealType x,
182 const MagickRealType y)
cristye4a40472011-12-22 02:56:19 +0000183{
184 if (x < y)
185 return(x);
186 return(y);
187}
cristyddeeea22012-04-12 01:33:09 +0000188
cristy7159f662012-10-28 17:32:43 +0000189static inline MagickRealType MagickMax(const MagickRealType x,
190 const MagickRealType y)
cristye4a40472011-12-22 02:56:19 +0000191{
192 if (x > y)
193 return(x);
194 return(y);
195}
196
cristy7159f662012-10-28 17:32:43 +0000197static void HCLComposite(const MagickRealType hue,const MagickRealType chroma,
198 const MagickRealType luma,MagickRealType *red,MagickRealType *green,
199 MagickRealType *blue)
cristyd8f16f72012-08-13 12:49:50 +0000200{
cristy7159f662012-10-28 17:32:43 +0000201 MagickRealType
cristyd8f16f72012-08-13 12:49:50 +0000202 b,
cristy7133e642012-08-14 11:04:11 +0000203 c,
cristyd8f16f72012-08-13 12:49:50 +0000204 g,
cristy7133e642012-08-14 11:04:11 +0000205 h,
206 m,
cristyd8f16f72012-08-13 12:49:50 +0000207 r,
cristy398fd022013-06-26 23:08:33 +0000208 x;
cristyd8f16f72012-08-13 12:49:50 +0000209
210 /*
cristy7133e642012-08-14 11:04:11 +0000211 Convert HCL to RGB colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000212 */
cristy7159f662012-10-28 17:32:43 +0000213 assert(red != (MagickRealType *) NULL);
214 assert(green != (MagickRealType *) NULL);
215 assert(blue != (MagickRealType *) NULL);
cristy7133e642012-08-14 11:04:11 +0000216 h=6.0*hue;
217 c=chroma;
218 x=c*(1.0-fabs(fmod(h,2.0)-1.0));
219 r=0.0;
220 g=0.0;
221 b=0.0;
222 if ((0.0 <= h) && (h < 1.0))
cristyd8f16f72012-08-13 12:49:50 +0000223 {
cristye1715282012-08-15 13:10:55 +0000224 r=c;
225 g=x;
cristyd8f16f72012-08-13 12:49:50 +0000226 }
cristyd8f16f72012-08-13 12:49:50 +0000227 else
cristy7133e642012-08-14 11:04:11 +0000228 if ((1.0 <= h) && (h < 2.0))
229 {
cristye1715282012-08-15 13:10:55 +0000230 r=x;
231 g=c;
cristy7133e642012-08-14 11:04:11 +0000232 }
233 else
234 if ((2.0 <= h) && (h < 3.0))
235 {
cristye1715282012-08-15 13:10:55 +0000236 g=c;
237 b=x;
cristy7133e642012-08-14 11:04:11 +0000238 }
239 else
240 if ((3.0 <= h) && (h < 4.0))
241 {
cristye1715282012-08-15 13:10:55 +0000242 g=x;
243 b=c;
cristy7133e642012-08-14 11:04:11 +0000244 }
245 else
246 if ((4.0 <= h) && (h < 5.0))
247 {
cristye1715282012-08-15 13:10:55 +0000248 r=x;
249 b=c;
cristy7133e642012-08-14 11:04:11 +0000250 }
251 else
252 if ((5.0 <= h) && (h < 6.0))
253 {
cristye1715282012-08-15 13:10:55 +0000254 r=c;
255 b=x;
cristy7133e642012-08-14 11:04:11 +0000256 }
cristy9e2436a2013-05-02 20:35:59 +0000257 m=luma-(0.298839*r+0.586811*g+0.114350*b);
cristy398fd022013-06-26 23:08:33 +0000258 *red=QuantumRange*(r+m);
259 *green=QuantumRange*(g+m);
260 *blue=QuantumRange*(b+m);
cristyd8f16f72012-08-13 12:49:50 +0000261}
262
cristy7159f662012-10-28 17:32:43 +0000263static void CompositeHCL(const MagickRealType red,const MagickRealType green,
264 const MagickRealType blue,MagickRealType *hue,MagickRealType *chroma,
265 MagickRealType *luma)
cristyd8f16f72012-08-13 12:49:50 +0000266{
cristy7159f662012-10-28 17:32:43 +0000267 MagickRealType
cristyd8f16f72012-08-13 12:49:50 +0000268 b,
cristy7133e642012-08-14 11:04:11 +0000269 c,
cristyd8f16f72012-08-13 12:49:50 +0000270 g,
cristy7133e642012-08-14 11:04:11 +0000271 h,
cristyd8f16f72012-08-13 12:49:50 +0000272 max,
cristyd8f16f72012-08-13 12:49:50 +0000273 r;
274
275 /*
cristy7133e642012-08-14 11:04:11 +0000276 Convert RGB to HCL colorspace.
cristyd8f16f72012-08-13 12:49:50 +0000277 */
cristy7159f662012-10-28 17:32:43 +0000278 assert(hue != (MagickRealType *) NULL);
279 assert(chroma != (MagickRealType *) NULL);
280 assert(luma != (MagickRealType *) NULL);
cristy7133e642012-08-14 11:04:11 +0000281 r=red;
282 g=green;
283 b=blue;
cristyd8f16f72012-08-13 12:49:50 +0000284 max=MagickMax(r,MagickMax(g,b));
cristy7159f662012-10-28 17:32:43 +0000285 c=max-(MagickRealType) MagickMin(r,MagickMin(g,b));
cristy7133e642012-08-14 11:04:11 +0000286 h=0.0;
287 if (c == 0)
288 h=0.0;
cristyd8f16f72012-08-13 12:49:50 +0000289 else
cristy7133e642012-08-14 11:04:11 +0000290 if (red == max)
cristy9e2436a2013-05-02 20:35:59 +0000291 h=fmod((g-b)/c+6.0,6.0);
cristyd8f16f72012-08-13 12:49:50 +0000292 else
cristy7133e642012-08-14 11:04:11 +0000293 if (green == max)
294 h=((b-r)/c)+2.0;
295 else
296 if (blue == max)
297 h=((r-g)/c)+4.0;
298 *hue=(h/6.0);
299 *chroma=QuantumScale*c;
cristy9e2436a2013-05-02 20:35:59 +0000300 *luma=QuantumScale*(0.298839*r+0.586811*g+0.114350*b);
cristyd8f16f72012-08-13 12:49:50 +0000301}
302
cristye4a40472011-12-22 02:56:19 +0000303static MagickBooleanType CompositeOverImage(Image *image,
cristyfeb3e962012-03-29 17:25:55 +0000304 const Image *composite_image,const MagickBooleanType clip_to_self,
305 const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo *exception)
cristye4a40472011-12-22 02:56:19 +0000306{
307#define CompositeImageTag "Composite/Image"
308
309 CacheView
310 *composite_view,
311 *image_view;
312
cristye4a40472011-12-22 02:56:19 +0000313 MagickBooleanType
cristye4a40472011-12-22 02:56:19 +0000314 status;
315
316 MagickOffsetType
317 progress;
318
319 ssize_t
320 y;
321
cristye4a40472011-12-22 02:56:19 +0000322 /*
cristye4a40472011-12-22 02:56:19 +0000323 Composite image.
324 */
325 status=MagickTrue;
326 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000327 composite_view=AcquireVirtualCacheView(composite_image,exception);
328 image_view=AcquireAuthenticCacheView(image,exception);
cristye4a40472011-12-22 02:56:19 +0000329#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000330 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000331 magick_threads(composite_image,image,image->rows,1)
cristye4a40472011-12-22 02:56:19 +0000332#endif
333 for (y=0; y < (ssize_t) image->rows; y++)
334 {
335 const Quantum
336 *pixels;
337
338 register const Quantum
339 *restrict p;
340
341 register Quantum
342 *restrict q;
343
344 register ssize_t
345 x;
346
cristy564a5692012-01-20 23:56:26 +0000347 size_t
348 channels;
349
cristye4a40472011-12-22 02:56:19 +0000350 if (status == MagickFalse)
351 continue;
cristyfeb3e962012-03-29 17:25:55 +0000352 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000353 {
354 if (y < y_offset)
355 continue;
356 if ((y-y_offset) >= (ssize_t) composite_image->rows)
357 continue;
358 }
359 /*
360 If pixels is NULL, y is outside overlay region.
361 */
362 pixels=(Quantum *) NULL;
363 p=(Quantum *) NULL;
364 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
365 {
366 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
367 composite_image->columns,1,exception);
368 if (p == (const Quantum *) NULL)
369 {
370 status=MagickFalse;
371 continue;
372 }
373 pixels=p;
374 if (x_offset < 0)
375 p-=x_offset*GetPixelChannels(composite_image);
376 }
377 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
378 if (q == (Quantum *) NULL)
379 {
380 status=MagickFalse;
381 continue;
382 }
383 for (x=0; x < (ssize_t) image->columns; x++)
384 {
cristy7159f662012-10-28 17:32:43 +0000385 MagickRealType
cristye4a40472011-12-22 02:56:19 +0000386 Da,
387 Dc,
cristy2bd6f5b2014-12-09 00:27:57 +0000388 Dca,
cristye4a40472011-12-22 02:56:19 +0000389 Sa,
cristy2bd6f5b2014-12-09 00:27:57 +0000390 Sc,
391 Sca;
cristye4a40472011-12-22 02:56:19 +0000392
393 register ssize_t
394 i;
395
cristyfeb3e962012-03-29 17:25:55 +0000396 if (clip_to_self != MagickFalse)
cristye4a40472011-12-22 02:56:19 +0000397 {
398 if (x < x_offset)
399 {
400 q+=GetPixelChannels(image);
401 continue;
402 }
403 if ((x-x_offset) >= (ssize_t) composite_image->columns)
404 break;
405 }
406 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
407 ((x-x_offset) >= (ssize_t) composite_image->columns))
408 {
409 Quantum
410 source[MaxPixelChannels];
411
412 /*
413 Virtual composite:
414 Sc: source color.
415 Dc: destination color.
416 */
cristy883fde12013-04-08 00:50:13 +0000417 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +0000418 {
419 q+=GetPixelChannels(image);
420 continue;
421 }
cristyc94ba6f2012-01-29 23:19:58 +0000422 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
423 source,exception);
cristye4a40472011-12-22 02:56:19 +0000424 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
425 {
cristy5a23c552013-02-13 14:34:28 +0000426 PixelChannel channel=GetPixelChannelChannel(image,i);
427 PixelTrait traits=GetPixelChannelTraits(image,channel);
428 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
429 channel);
cristye4a40472011-12-22 02:56:19 +0000430 if ((traits == UndefinedPixelTrait) ||
431 (composite_traits == UndefinedPixelTrait))
432 continue;
433 q[i]=source[channel];
434 }
435 q+=GetPixelChannels(image);
436 continue;
437 }
438 /*
439 Authentic composite:
440 Sa: normalized source alpha.
441 Da: normalized destination alpha.
442 */
cristy883fde12013-04-08 00:50:13 +0000443 if (GetPixelReadMask(composite_image,p) == 0)
cristyc94ba6f2012-01-29 23:19:58 +0000444 {
445 p+=GetPixelChannels(composite_image);
446 channels=GetPixelChannels(composite_image);
447 if (p >= (pixels+channels*composite_image->columns))
448 p=pixels;
449 q+=GetPixelChannels(image);
450 continue;
451 }
cristye4a40472011-12-22 02:56:19 +0000452 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
453 Da=QuantumScale*GetPixelAlpha(image,q);
cristye4a40472011-12-22 02:56:19 +0000454 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
455 {
cristy5a23c552013-02-13 14:34:28 +0000456 PixelChannel channel=GetPixelChannelChannel(image,i);
457 PixelTrait traits=GetPixelChannelTraits(image,channel);
458 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
459 channel);
cristye4a40472011-12-22 02:56:19 +0000460 if ((traits == UndefinedPixelTrait) ||
461 (composite_traits == UndefinedPixelTrait))
462 continue;
463 if ((traits & CopyPixelTrait) != 0)
464 {
cristye224ed72014-12-10 23:55:01 +0000465 /*
466 Copy channel.
467 */
468 q[i]=GetPixelChannel(composite_image,channel,p);
469 continue;
470 }
471 if (channel == AlphaPixelChannel)
472 {
cristye4a40472011-12-22 02:56:19 +0000473 /*
474 Set alpha channel.
475 */
cristye224ed72014-12-10 23:55:01 +0000476 q[i]=ClampToQuantum(QuantumRange*(Sa+Da-Sa*Da));
cristye4a40472011-12-22 02:56:19 +0000477 continue;
478 }
479 /*
480 Sc: source color.
481 Dc: destination color.
482 */
cristy7159f662012-10-28 17:32:43 +0000483 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
484 Dc=(MagickRealType) q[i];
cristy2bd6f5b2014-12-09 00:27:57 +0000485 Sca=QuantumScale*Sa*Sc;
486 Dca=QuantumScale*Da*Dc;
cristye224ed72014-12-10 23:55:01 +0000487 q[i]=ClampToQuantum(QuantumRange*(Sca+Dca*(1.0-Sa)));
cristye4a40472011-12-22 02:56:19 +0000488 }
489 p+=GetPixelChannels(composite_image);
490 channels=GetPixelChannels(composite_image);
491 if (p >= (pixels+channels*composite_image->columns))
492 p=pixels;
493 q+=GetPixelChannels(image);
494 }
495 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
496 status=MagickFalse;
497 if (image->progress_monitor != (MagickProgressMonitor) NULL)
498 {
499 MagickBooleanType
500 proceed;
501
502#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000503 #pragma omp critical (MagickCore_CompositeImage)
cristye4a40472011-12-22 02:56:19 +0000504#endif
505 proceed=SetImageProgress(image,CompositeImageTag,progress++,
506 image->rows);
507 if (proceed == MagickFalse)
508 status=MagickFalse;
509 }
510 }
511 composite_view=DestroyCacheView(composite_view);
512 image_view=DestroyCacheView(image_view);
513 return(status);
514}
515
cristy4c08aed2011-07-01 19:47:50 +0000516MagickExport MagickBooleanType CompositeImage(Image *image,
cristya865ccd2012-07-28 00:33:10 +0000517 const Image *composite,const CompositeOperator compose,
cristy44c98412012-03-31 18:25:40 +0000518 const MagickBooleanType clip_to_self,const ssize_t x_offset,
519 const ssize_t y_offset,ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +0000520{
cristy4c08aed2011-07-01 19:47:50 +0000521#define CompositeImageTag "Composite/Image"
522
523 CacheView
524 *composite_view,
525 *image_view;
526
cristy4c08aed2011-07-01 19:47:50 +0000527 GeometryInfo
528 geometry_info;
529
530 Image
cristya865ccd2012-07-28 00:33:10 +0000531 *composite_image,
cristy4c08aed2011-07-01 19:47:50 +0000532 *destination_image;
533
534 MagickBooleanType
cristy4c08aed2011-07-01 19:47:50 +0000535 status;
536
537 MagickOffsetType
538 progress;
539
cristy7159f662012-10-28 17:32:43 +0000540 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000541 amount,
542 destination_dissolve,
543 midpoint,
cristy7133e642012-08-14 11:04:11 +0000544 percent_luma,
545 percent_chroma,
cristy4c08aed2011-07-01 19:47:50 +0000546 source_dissolve,
547 threshold;
548
549 MagickStatusType
550 flags;
551
cristyd197cbb2012-01-13 02:14:12 +0000552 ssize_t
553 y;
554
cristy4c08aed2011-07-01 19:47:50 +0000555 assert(image != (Image *) NULL);
556 assert(image->signature == MagickSignature);
557 if (image->debug != MagickFalse)
558 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristya865ccd2012-07-28 00:33:10 +0000559 assert(composite!= (Image *) NULL);
560 assert(composite->signature == MagickSignature);
cristy574cc262011-08-05 01:23:58 +0000561 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +0000562 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +0000563 composite_image=CloneImage(composite,0,0,MagickTrue,exception);
564 if (composite_image == (const Image *) NULL)
565 return(MagickFalse);
cristyde067e92013-04-18 00:47:50 +0000566 if (IsGrayColorspace(image->colorspace) != MagickFalse)
567 (void) SetImageColorspace(image,sRGBColorspace,exception);
cristya4a22a22012-08-01 23:16:38 +0000568 (void) SetImageColorspace(composite_image,image->colorspace,exception);
cristy0197c3c2014-11-28 23:23:39 +0000569 if (image->alpha_trait == UndefinedPixelTrait)
570 (void) SetImageAlphaChannel(image,SetAlphaChannel,exception);
571 if (composite_image->alpha_trait == UndefinedPixelTrait)
cristydc4bf872013-03-09 15:08:20 +0000572 (void) SetImageAlphaChannel(composite_image,SetAlphaChannel,exception);
cristye224ed72014-12-10 23:55:01 +0000573if (0)
cristye4a40472011-12-22 02:56:19 +0000574 if ((compose == OverCompositeOp) || (compose == SrcOverCompositeOp))
575 {
cristyfeb3e962012-03-29 17:25:55 +0000576 status=CompositeOverImage(image,composite_image,clip_to_self,x_offset,
577 y_offset,exception);
cristya865ccd2012-07-28 00:33:10 +0000578 composite_image=DestroyImage(composite_image);
cristye4a40472011-12-22 02:56:19 +0000579 return(status);
580 }
cristy4c08aed2011-07-01 19:47:50 +0000581 destination_image=(Image *) NULL;
582 amount=0.5;
583 destination_dissolve=1.0;
cristy7133e642012-08-14 11:04:11 +0000584 percent_luma=100.0;
585 percent_chroma=100.0;
cristy4c08aed2011-07-01 19:47:50 +0000586 source_dissolve=1.0;
587 threshold=0.05f;
588 switch (compose)
589 {
cristy4c08aed2011-07-01 19:47:50 +0000590 case CopyCompositeOp:
591 {
592 if ((x_offset < 0) || (y_offset < 0))
593 break;
cristy17a88592014-11-29 15:05:48 +0000594 if ((x_offset+(ssize_t) composite_image->columns) > (ssize_t) image->columns)
cristy4c08aed2011-07-01 19:47:50 +0000595 break;
cristy17a88592014-11-29 15:05:48 +0000596 if ((y_offset+(ssize_t) composite_image->rows) > (ssize_t) image->rows)
cristy4c08aed2011-07-01 19:47:50 +0000597 break;
598 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +0000599 composite_view=AcquireVirtualCacheView(composite_image,exception);
600 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000601#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000602 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +0000603 magick_threads(composite_image,image,composite_image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +0000604#endif
605 for (y=0; y < (ssize_t) composite_image->rows; y++)
606 {
607 MagickBooleanType
608 sync;
609
610 register const Quantum
611 *p;
612
613 register Quantum
614 *q;
615
616 register ssize_t
617 x;
618
619 if (status == MagickFalse)
620 continue;
621 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
622 1,exception);
623 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
624 composite_image->columns,1,exception);
625 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
626 {
627 status=MagickFalse;
628 continue;
629 }
630 for (x=0; x < (ssize_t) composite_image->columns; x++)
631 {
cristybdecccc2011-12-24 22:52:16 +0000632 register ssize_t
633 i;
634
cristy883fde12013-04-08 00:50:13 +0000635 if (GetPixelReadMask(composite_image,p) == 0)
cristy10a6c612012-01-29 21:41:05 +0000636 {
637 p+=GetPixelChannels(composite_image);
638 q+=GetPixelChannels(image);
639 continue;
640 }
cristybdecccc2011-12-24 22:52:16 +0000641 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
642 {
cristy5a23c552013-02-13 14:34:28 +0000643 PixelChannel channel=GetPixelChannelChannel(composite_image,i);
644 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
645 channel);
646 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristybdecccc2011-12-24 22:52:16 +0000647 if ((traits == UndefinedPixelTrait) ||
648 (composite_traits == UndefinedPixelTrait))
649 continue;
650 SetPixelChannel(image,channel,p[i],q);
651 }
cristyed231572011-07-14 02:18:59 +0000652 p+=GetPixelChannels(composite_image);
653 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +0000654 }
655 sync=SyncCacheViewAuthenticPixels(image_view,exception);
656 if (sync == MagickFalse)
657 status=MagickFalse;
658 if (image->progress_monitor != (MagickProgressMonitor) NULL)
659 {
660 MagickBooleanType
661 proceed;
662
663#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +0000664 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +0000665#endif
666 proceed=SetImageProgress(image,CompositeImageTag,
667 (MagickOffsetType) y,image->rows);
668 if (proceed == MagickFalse)
669 status=MagickFalse;
670 }
671 }
672 composite_view=DestroyCacheView(composite_view);
673 image_view=DestroyCacheView(image_view);
cristy44886b92012-07-28 13:07:09 +0000674 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000675 return(status);
676 }
cristy17a88592014-11-29 15:05:48 +0000677 case IntensityCompositeOp:
678 {
679 if ((x_offset < 0) || (y_offset < 0))
680 break;
681 if ((x_offset+(ssize_t) composite_image->columns) > (ssize_t) image->columns)
682 break;
683 if ((y_offset+(ssize_t) composite_image->rows) > (ssize_t) image->rows)
684 break;
685 status=MagickTrue;
686 composite_view=AcquireVirtualCacheView(composite_image,exception);
687 image_view=AcquireAuthenticCacheView(image,exception);
688#if defined(MAGICKCORE_OPENMP_SUPPORT)
689 #pragma omp parallel for schedule(static,4) shared(status) \
690 magick_threads(composite_image,image,composite_image->rows,1)
691#endif
692 for (y=0; y < (ssize_t) composite_image->rows; y++)
693 {
694 MagickBooleanType
695 sync;
696
697 register const Quantum
698 *p;
699
700 register Quantum
701 *q;
702
703 register ssize_t
704 x;
705
706 if (status == MagickFalse)
707 continue;
708 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
709 1,exception);
710 q=GetCacheViewAuthenticPixels(image_view,x_offset,y+y_offset,
711 composite_image->columns,1,exception);
712 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
713 {
714 status=MagickFalse;
715 continue;
716 }
717 for (x=0; x < (ssize_t) composite_image->columns; x++)
718 {
719 register ssize_t
720 i;
721
722 if (GetPixelReadMask(composite_image,p) == 0)
723 {
724 p+=GetPixelChannels(composite_image);
725 q+=GetPixelChannels(image);
726 continue;
727 }
728 for (i=0; i < (ssize_t) GetPixelChannels(composite_image); i++)
cristye224ed72014-12-10 23:55:01 +0000729 SetPixelAlpha(image,ClampToQuantum(GetPixelIntensity(
730 composite_image,p)),q);
cristy17a88592014-11-29 15:05:48 +0000731 p+=GetPixelChannels(composite_image);
732 q+=GetPixelChannels(image);
733 }
734 sync=SyncCacheViewAuthenticPixels(image_view,exception);
735 if (sync == MagickFalse)
736 status=MagickFalse;
737 if (image->progress_monitor != (MagickProgressMonitor) NULL)
738 {
739 MagickBooleanType
740 proceed;
741
742#if defined(MAGICKCORE_OPENMP_SUPPORT)
743 #pragma omp critical (MagickCore_CompositeImage)
744#endif
745 proceed=SetImageProgress(image,CompositeImageTag,
746 (MagickOffsetType) y,image->rows);
747 if (proceed == MagickFalse)
748 status=MagickFalse;
749 }
750 }
751 composite_view=DestroyCacheView(composite_view);
752 image_view=DestroyCacheView(image_view);
753 composite_image=DestroyImage(composite_image);
754 return(status);
755 }
cristye4a40472011-12-22 02:56:19 +0000756 case CopyAlphaCompositeOp:
cristy4c08aed2011-07-01 19:47:50 +0000757 case ChangeMaskCompositeOp:
758 {
759 /*
760 Modify destination outside the overlaid region and require an alpha
761 channel to exist, to add transparency.
762 */
cristy17f11b02014-12-20 19:37:04 +0000763 if (image->alpha_trait == UndefinedPixelTrait)
cristy42c41de2012-05-05 18:36:31 +0000764 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
cristy4c08aed2011-07-01 19:47:50 +0000765 break;
766 }
767 case BlurCompositeOp:
768 {
769 CacheView
770 *composite_view,
771 *destination_view;
772
cristyfeb3e962012-03-29 17:25:55 +0000773 const char
774 *value;
775
cristy7159f662012-10-28 17:32:43 +0000776 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000777 angle_range,
778 angle_start,
779 height,
780 width;
781
cristybce4f4a2012-10-14 14:57:47 +0000782 PixelInfo
783 pixel;
784
cristy4c08aed2011-07-01 19:47:50 +0000785 ResampleFilter
786 *resample_filter;
787
788 SegmentInfo
789 blur;
790
791 /*
anthony9cb63cc2012-04-25 06:10:49 +0000792 Blur Image by resampling.
793
cristy4c08aed2011-07-01 19:47:50 +0000794 Blur Image dictated by an overlay gradient map: X = red_channel;
795 Y = green_channel; compose:args = x_scale[,y_scale[,angle]].
796 */
797 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000798 exception);
cristy4c08aed2011-07-01 19:47:50 +0000799 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000800 {
801 composite_image=DestroyImage(composite_image);
802 return(MagickFalse);
803 }
cristy4c08aed2011-07-01 19:47:50 +0000804 /*
anthony9cb63cc2012-04-25 06:10:49 +0000805 Gather the maximum blur sigma values from user.
cristy4c08aed2011-07-01 19:47:50 +0000806 */
807 SetGeometryInfo(&geometry_info);
808 flags=NoValue;
cristy1a780952013-02-10 17:15:30 +0000809 value=GetImageArtifact(image,"compose:args");
810 if (value != (const char *) NULL)
cristy4c08aed2011-07-01 19:47:50 +0000811 flags=ParseGeometry(value,&geometry_info);
cristy1a780952013-02-10 17:15:30 +0000812 if ((flags & WidthValue) == 0)
813 {
814 (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
815 "InvalidSetting","'%s' '%s'","compose:args",value);
cristy44886b92012-07-28 13:07:09 +0000816 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000817 destination_image=DestroyImage(destination_image);
818 return(MagickFalse);
819 }
anthony9cb63cc2012-04-25 06:10:49 +0000820 /*
821 Users input sigma now needs to be converted to the EWA ellipse size.
822 The filter defaults to a sigma of 0.5 so to make this match the
823 users input the ellipse size needs to be doubled.
anthonyd2923912012-04-23 13:06:53 +0000824 */
825 width=height=geometry_info.rho*2.0;
826 if ((flags & HeightValue) != 0 )
827 height=geometry_info.sigma*2.0;
cristy7159f662012-10-28 17:32:43 +0000828 /*
829 Default the unrotated ellipse width and height axis vectors.
830 */
anthonyd2923912012-04-23 13:06:53 +0000831 blur.x1=width;
cristy4c08aed2011-07-01 19:47:50 +0000832 blur.x2=0.0;
833 blur.y1=0.0;
anthonyd2923912012-04-23 13:06:53 +0000834 blur.y2=height;
835 /* rotate vectors if a rotation angle is given */
cristy4c08aed2011-07-01 19:47:50 +0000836 if ((flags & XValue) != 0 )
837 {
cristy7159f662012-10-28 17:32:43 +0000838 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000839 angle;
840
841 angle=DegreesToRadians(geometry_info.xi);
842 blur.x1=width*cos(angle);
843 blur.x2=width*sin(angle);
844 blur.y1=(-height*sin(angle));
845 blur.y2=height*cos(angle);
846 }
anthonyd2923912012-04-23 13:06:53 +0000847 /* Otherwise lets set a angle range and calculate in the loop */
848 angle_start=0.0;
849 angle_range=0.0;
cristy4c08aed2011-07-01 19:47:50 +0000850 if ((flags & YValue) != 0 )
851 {
852 angle_start=DegreesToRadians(geometry_info.xi);
853 angle_range=DegreesToRadians(geometry_info.psi)-angle_start;
854 }
855 /*
anthony9cb63cc2012-04-25 06:10:49 +0000856 Set up a gaussian cylindrical filter for EWA Bluring.
anthonyd2923912012-04-23 13:06:53 +0000857
anthony9cb63cc2012-04-25 06:10:49 +0000858 As the minimum ellipse radius of support*1.0 the EWA algorithm
859 can only produce a minimum blur of 0.5 for Gaussian (support=2.0)
860 This means that even 'No Blur' will be still a little blurry!
anthonyd2923912012-04-23 13:06:53 +0000861
anthony9cb63cc2012-04-25 06:10:49 +0000862 The solution (as well as the problem of preventing any user
863 expert filter settings, is to set our own user settings, then
864 restore them afterwards.
cristy4c08aed2011-07-01 19:47:50 +0000865 */
cristy8a11cb12011-10-19 23:53:34 +0000866 resample_filter=AcquireResampleFilter(image,exception);
anthonyd2923912012-04-23 13:06:53 +0000867 SetResampleFilter(resample_filter,GaussianFilter);
anthony9cb63cc2012-04-25 06:10:49 +0000868
869 /* do the variable blurring of each pixel in image */
870 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +0000871 composite_view=AcquireVirtualCacheView(composite_image,exception);
872 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +0000873 for (y=0; y < (ssize_t) composite_image->rows; y++)
874 {
875 MagickBooleanType
876 sync;
877
878 register const Quantum
879 *restrict p;
880
881 register Quantum
882 *restrict q;
883
884 register ssize_t
885 x;
886
887 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
888 continue;
889 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
890 1,exception);
891 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +0000892 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000893 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
894 break;
895 for (x=0; x < (ssize_t) composite_image->columns; x++)
896 {
897 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
898 {
cristyed231572011-07-14 02:18:59 +0000899 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000900 continue;
901 }
cristy3bdd9252014-12-21 20:01:43 +0000902 if (fabs((double) angle_range) > MagickEpsilon)
cristy4c08aed2011-07-01 19:47:50 +0000903 {
cristy7159f662012-10-28 17:32:43 +0000904 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000905 angle;
906
907 angle=angle_start+angle_range*QuantumScale*
908 GetPixelBlue(composite_image,p);
909 blur.x1=width*cos(angle);
910 blur.x2=width*sin(angle);
911 blur.y1=(-height*sin(angle));
912 blur.y2=height*cos(angle);
913 }
anthonyd2923912012-04-23 13:06:53 +0000914#if 0
915 if ( x == 10 && y == 60 ) {
cristy7159f662012-10-28 17:32:43 +0000916 (void) fprintf(stderr, "blur.x=%lf,%lf, blur.y=%lf,%lf\n",blur.x1,
917 blur.x2,blur.y1, blur.y2);
918 (void) fprintf(stderr, "scaled by=%lf,%lf\n",QuantumScale*
919 GetPixelRed(p),QuantumScale*GetPixelGreen(p));
anthonyd2923912012-04-23 13:06:53 +0000920#endif
921 ScaleResampleFilter(resample_filter,
922 blur.x1*QuantumScale*GetPixelRed(composite_image,p),
923 blur.y1*QuantumScale*GetPixelGreen(composite_image,p),
924 blur.x2*QuantumScale*GetPixelRed(composite_image,p),
925 blur.y2*QuantumScale*GetPixelGreen(composite_image,p) );
cristy4c08aed2011-07-01 19:47:50 +0000926 (void) ResamplePixelColor(resample_filter,(double) x_offset+x,
cristydb070952012-04-20 14:33:00 +0000927 (double) y_offset+y,&pixel,exception);
cristy803640d2011-11-17 02:11:32 +0000928 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +0000929 p+=GetPixelChannels(composite_image);
930 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +0000931 }
932 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
933 if (sync == MagickFalse)
934 break;
935 }
936 resample_filter=DestroyResampleFilter(resample_filter);
937 composite_view=DestroyCacheView(composite_view);
938 destination_view=DestroyCacheView(destination_view);
cristyf661c4d2012-07-28 12:57:17 +0000939 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +0000940 composite_image=destination_image;
941 break;
942 }
943 case DisplaceCompositeOp:
944 case DistortCompositeOp:
945 {
946 CacheView
947 *composite_view,
948 *destination_view,
949 *image_view;
950
cristyfeb3e962012-03-29 17:25:55 +0000951 const char
952 *value;
953
cristy4c08aed2011-07-01 19:47:50 +0000954 PixelInfo
955 pixel;
956
cristy7159f662012-10-28 17:32:43 +0000957 MagickRealType
cristy4c08aed2011-07-01 19:47:50 +0000958 horizontal_scale,
959 vertical_scale;
960
961 PointInfo
962 center,
963 offset;
964
965 /*
966 Displace/Distort based on overlay gradient map:
967 X = red_channel; Y = green_channel;
968 compose:args = x_scale[,y_scale[,center.x,center.y]]
969 */
970 destination_image=CloneImage(image,image->columns,image->rows,MagickTrue,
cristy8a11cb12011-10-19 23:53:34 +0000971 exception);
cristy4c08aed2011-07-01 19:47:50 +0000972 if (destination_image == (Image *) NULL)
cristy44886b92012-07-28 13:07:09 +0000973 {
974 composite_image=DestroyImage(composite_image);
975 return(MagickFalse);
976 }
cristy4c08aed2011-07-01 19:47:50 +0000977 SetGeometryInfo(&geometry_info);
978 flags=NoValue;
dirk55a83c72014-08-22 18:44:23 +0000979 value=GetImageArtifact(image,"compose:args");
cristy4c08aed2011-07-01 19:47:50 +0000980 if (value != (char *) NULL)
981 flags=ParseGeometry(value,&geometry_info);
982 if ((flags & (WidthValue|HeightValue)) == 0 )
983 {
984 if ((flags & AspectValue) == 0)
985 {
cristy7159f662012-10-28 17:32:43 +0000986 horizontal_scale=(MagickRealType) (composite_image->columns-1.0)/
cristy4c08aed2011-07-01 19:47:50 +0000987 2.0;
cristy7159f662012-10-28 17:32:43 +0000988 vertical_scale=(MagickRealType) (composite_image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000989 }
990 else
991 {
cristy7159f662012-10-28 17:32:43 +0000992 horizontal_scale=(MagickRealType) (image->columns-1.0)/2.0;
993 vertical_scale=(MagickRealType) (image->rows-1.0)/2.0;
cristy4c08aed2011-07-01 19:47:50 +0000994 }
995 }
996 else
997 {
998 horizontal_scale=geometry_info.rho;
999 vertical_scale=geometry_info.sigma;
1000 if ((flags & PercentValue) != 0)
1001 {
1002 if ((flags & AspectValue) == 0)
1003 {
1004 horizontal_scale*=(composite_image->columns-1.0)/200.0;
1005 vertical_scale*=(composite_image->rows-1.0)/200.0;
1006 }
1007 else
1008 {
1009 horizontal_scale*=(image->columns-1.0)/200.0;
1010 vertical_scale*=(image->rows-1.0)/200.0;
1011 }
1012 }
1013 if ((flags & HeightValue) == 0)
1014 vertical_scale=horizontal_scale;
1015 }
1016 /*
1017 Determine fixed center point for absolute distortion map
1018 Absolute distort ==
1019 Displace offset relative to a fixed absolute point
1020 Select that point according to +X+Y user inputs.
1021 default = center of overlay image
1022 arg flag '!' = locations/percentage relative to background image
1023 */
cristy7159f662012-10-28 17:32:43 +00001024 center.x=(MagickRealType) x_offset;
1025 center.y=(MagickRealType) y_offset;
cristy4c08aed2011-07-01 19:47:50 +00001026 if (compose == DistortCompositeOp)
1027 {
1028 if ((flags & XValue) == 0)
1029 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001030 center.x=(MagickRealType) (x_offset+(composite_image->columns-1)/
1031 2.0);
cristy4c08aed2011-07-01 19:47:50 +00001032 else
cristy7159f662012-10-28 17:32:43 +00001033 center.x=(MagickRealType) ((image->columns-1)/2);
cristy4c08aed2011-07-01 19:47:50 +00001034 else
1035 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001036 center.x=(MagickRealType) x_offset+geometry_info.xi;
cristy4c08aed2011-07-01 19:47:50 +00001037 else
1038 center.x=geometry_info.xi;
1039 if ((flags & YValue) == 0)
1040 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001041 center.y=(MagickRealType) (y_offset+(composite_image->rows-1)/
1042 2.0);
cristy4c08aed2011-07-01 19:47:50 +00001043 else
cristy7159f662012-10-28 17:32:43 +00001044 center.y=(MagickRealType) ((image->rows-1)/2);
cristy4c08aed2011-07-01 19:47:50 +00001045 else
1046 if ((flags & AspectValue) == 0)
cristy7159f662012-10-28 17:32:43 +00001047 center.y=(MagickRealType) y_offset+geometry_info.psi;
cristy4c08aed2011-07-01 19:47:50 +00001048 else
1049 center.y=geometry_info.psi;
1050 }
1051 /*
1052 Shift the pixel offset point as defined by the provided,
1053 displacement/distortion map. -- Like a lens...
1054 */
cristye10859a2011-12-18 22:28:59 +00001055 GetPixelInfo(image,&pixel);
cristy46ff2672012-12-14 15:32:26 +00001056 image_view=AcquireVirtualCacheView(image,exception);
1057 composite_view=AcquireVirtualCacheView(composite_image,exception);
1058 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001059 for (y=0; y < (ssize_t) composite_image->rows; y++)
1060 {
1061 MagickBooleanType
1062 sync;
1063
1064 register const Quantum
1065 *restrict p;
1066
1067 register Quantum
1068 *restrict q;
1069
1070 register ssize_t
1071 x;
1072
1073 if (((y+y_offset) < 0) || ((y+y_offset) >= (ssize_t) image->rows))
1074 continue;
1075 p=GetCacheViewVirtualPixels(composite_view,0,y,composite_image->columns,
1076 1,exception);
1077 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
cristy8a11cb12011-10-19 23:53:34 +00001078 destination_image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001079 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1080 break;
1081 for (x=0; x < (ssize_t) composite_image->columns; x++)
1082 {
1083 if (((x_offset+x) < 0) || ((x_offset+x) >= (ssize_t) image->columns))
1084 {
cristyed231572011-07-14 02:18:59 +00001085 p+=GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001086 continue;
1087 }
1088 /*
1089 Displace the offset.
1090 */
cristy7159f662012-10-28 17:32:43 +00001091 offset.x=(double) (horizontal_scale*(GetPixelRed(composite_image,p)-
1092 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1093 QuantumRange+1.0)/2.0)+center.x+((compose == DisplaceCompositeOp) ?
1094 x : 0);
1095 offset.y=(double) (vertical_scale*(GetPixelGreen(composite_image,p)-
1096 (((MagickRealType) QuantumRange+1.0)/2.0)))/(((MagickRealType)
1097 QuantumRange+1.0)/2.0)+center.y+((compose == DisplaceCompositeOp) ?
1098 y : 0);
cristy4c08aed2011-07-01 19:47:50 +00001099 (void) InterpolatePixelInfo(image,image_view,
1100 UndefinedInterpolatePixel,(double) offset.x,(double) offset.y,
1101 &pixel,exception);
1102 /*
1103 Mask with the 'invalid pixel mask' in alpha channel.
1104 */
cristy7159f662012-10-28 17:32:43 +00001105 pixel.alpha=(MagickRealType) QuantumRange*(1.0-(1.0-QuantumScale*
1106 pixel.alpha)*(1.0-QuantumScale*GetPixelAlpha(composite_image,p)));
cristy803640d2011-11-17 02:11:32 +00001107 SetPixelInfoPixel(destination_image,&pixel,q);
cristyed231572011-07-14 02:18:59 +00001108 p+=GetPixelChannels(composite_image);
1109 q+=GetPixelChannels(destination_image);
cristy4c08aed2011-07-01 19:47:50 +00001110 }
1111 sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1112 if (sync == MagickFalse)
1113 break;
1114 }
1115 destination_view=DestroyCacheView(destination_view);
1116 composite_view=DestroyCacheView(composite_view);
1117 image_view=DestroyCacheView(image_view);
cristyf661c4d2012-07-28 12:57:17 +00001118 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001119 composite_image=destination_image;
1120 break;
1121 }
1122 case DissolveCompositeOp:
1123 {
cristyfeb3e962012-03-29 17:25:55 +00001124 const char
1125 *value;
1126
cristy4c08aed2011-07-01 19:47:50 +00001127 /*
1128 Geometry arguments to dissolve factors.
1129 */
dirk55a83c72014-08-22 18:44:23 +00001130 value=GetImageArtifact(image,"compose:args");
cristy4c08aed2011-07-01 19:47:50 +00001131 if (value != (char *) NULL)
1132 {
1133 flags=ParseGeometry(value,&geometry_info);
1134 source_dissolve=geometry_info.rho/100.0;
1135 destination_dissolve=1.0;
1136 if ((source_dissolve-MagickEpsilon) < 0.0)
1137 source_dissolve=0.0;
1138 if ((source_dissolve+MagickEpsilon) > 1.0)
1139 {
1140 destination_dissolve=2.0-source_dissolve;
1141 source_dissolve=1.0;
1142 }
1143 if ((flags & SigmaValue) != 0)
1144 destination_dissolve=geometry_info.sigma/100.0;
1145 if ((destination_dissolve-MagickEpsilon) < 0.0)
1146 destination_dissolve=0.0;
cristy4c08aed2011-07-01 19:47:50 +00001147 }
1148 break;
1149 }
1150 case BlendCompositeOp:
1151 {
cristyfeb3e962012-03-29 17:25:55 +00001152 const char
1153 *value;
1154
dirk55a83c72014-08-22 18:44:23 +00001155 value=GetImageArtifact(image,"compose:args");
cristy4c08aed2011-07-01 19:47:50 +00001156 if (value != (char *) NULL)
1157 {
1158 flags=ParseGeometry(value,&geometry_info);
1159 source_dissolve=geometry_info.rho/100.0;
1160 destination_dissolve=1.0-source_dissolve;
1161 if ((flags & SigmaValue) != 0)
1162 destination_dissolve=geometry_info.sigma/100.0;
cristy4c08aed2011-07-01 19:47:50 +00001163 }
1164 break;
1165 }
1166 case MathematicsCompositeOp:
1167 {
cristyfeb3e962012-03-29 17:25:55 +00001168 const char
1169 *value;
1170
cristy4c08aed2011-07-01 19:47:50 +00001171 /*
1172 Just collect the values from "compose:args", setting.
1173 Unused values are set to zero automagically.
1174
1175 Arguments are normally a comma separated list, so this probably should
1176 be changed to some 'general comma list' parser, (with a minimum
1177 number of values)
1178 */
1179 SetGeometryInfo(&geometry_info);
dirk55a83c72014-08-22 18:44:23 +00001180 value=GetImageArtifact(image,"compose:args");
cristy4c08aed2011-07-01 19:47:50 +00001181 if (value != (char *) NULL)
1182 (void) ParseGeometry(value,&geometry_info);
1183 break;
1184 }
1185 case ModulateCompositeOp:
1186 {
cristyfeb3e962012-03-29 17:25:55 +00001187 const char
1188 *value;
1189
cristy4c08aed2011-07-01 19:47:50 +00001190 /*
cristy7133e642012-08-14 11:04:11 +00001191 Determine the luma and chroma scale.
cristy4c08aed2011-07-01 19:47:50 +00001192 */
dirk55a83c72014-08-22 18:44:23 +00001193 value=GetImageArtifact(image,"compose:args");
cristy4c08aed2011-07-01 19:47:50 +00001194 if (value != (char *) NULL)
1195 {
1196 flags=ParseGeometry(value,&geometry_info);
cristy7133e642012-08-14 11:04:11 +00001197 percent_luma=geometry_info.rho;
cristy4c08aed2011-07-01 19:47:50 +00001198 if ((flags & SigmaValue) != 0)
cristy7133e642012-08-14 11:04:11 +00001199 percent_chroma=geometry_info.sigma;
cristy4c08aed2011-07-01 19:47:50 +00001200 }
1201 break;
1202 }
1203 case ThresholdCompositeOp:
1204 {
cristyfeb3e962012-03-29 17:25:55 +00001205 const char
1206 *value;
1207
cristy4c08aed2011-07-01 19:47:50 +00001208 /*
1209 Determine the amount and threshold.
1210 */
dirk55a83c72014-08-22 18:44:23 +00001211 value=GetImageArtifact(image,"compose:args");
cristy4c08aed2011-07-01 19:47:50 +00001212 if (value != (char *) NULL)
1213 {
1214 flags=ParseGeometry(value,&geometry_info);
1215 amount=geometry_info.rho;
1216 threshold=geometry_info.sigma;
1217 if ((flags & SigmaValue) == 0)
1218 threshold=0.05f;
1219 }
1220 threshold*=QuantumRange;
1221 break;
1222 }
1223 default:
1224 break;
1225 }
cristy4c08aed2011-07-01 19:47:50 +00001226 /*
1227 Composite image.
1228 */
1229 status=MagickTrue;
1230 progress=0;
cristy7159f662012-10-28 17:32:43 +00001231 midpoint=((MagickRealType) QuantumRange+1.0)/2;
cristy46ff2672012-12-14 15:32:26 +00001232 composite_view=AcquireVirtualCacheView(composite_image,exception);
1233 image_view=AcquireAuthenticCacheView(image,exception);
cristy4c08aed2011-07-01 19:47:50 +00001234#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +00001235 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +00001236 magick_threads(composite_image,image,image->rows,1)
cristy4c08aed2011-07-01 19:47:50 +00001237#endif
1238 for (y=0; y < (ssize_t) image->rows; y++)
1239 {
1240 const Quantum
1241 *pixels;
1242
cristy7159f662012-10-28 17:32:43 +00001243 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001244 blue,
cristy7133e642012-08-14 11:04:11 +00001245 luma,
cristye4a40472011-12-22 02:56:19 +00001246 green,
cristy4c08aed2011-07-01 19:47:50 +00001247 hue,
cristye4a40472011-12-22 02:56:19 +00001248 red,
cristy7133e642012-08-14 11:04:11 +00001249 chroma;
cristy4c08aed2011-07-01 19:47:50 +00001250
cristyddeeea22012-04-12 01:33:09 +00001251 PixelInfo
1252 destination_pixel,
1253 source_pixel;
1254
cristy4c08aed2011-07-01 19:47:50 +00001255 register const Quantum
1256 *restrict p;
1257
1258 register Quantum
1259 *restrict q;
1260
1261 register ssize_t
1262 x;
1263
1264 if (status == MagickFalse)
1265 continue;
cristyfeb3e962012-03-29 17:25:55 +00001266 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001267 {
1268 if (y < y_offset)
1269 continue;
1270 if ((y-y_offset) >= (ssize_t) composite_image->rows)
1271 continue;
1272 }
1273 /*
1274 If pixels is NULL, y is outside overlay region.
1275 */
1276 pixels=(Quantum *) NULL;
1277 p=(Quantum *) NULL;
1278 if ((y >= y_offset) && ((y-y_offset) < (ssize_t) composite_image->rows))
1279 {
1280 p=GetCacheViewVirtualPixels(composite_view,0,y-y_offset,
1281 composite_image->columns,1,exception);
1282 if (p == (const Quantum *) NULL)
1283 {
1284 status=MagickFalse;
1285 continue;
1286 }
1287 pixels=p;
1288 if (x_offset < 0)
cristyed231572011-07-14 02:18:59 +00001289 p-=x_offset*GetPixelChannels(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00001290 }
1291 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +00001292 if (q == (Quantum *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00001293 {
1294 status=MagickFalse;
1295 continue;
1296 }
cristy4c08aed2011-07-01 19:47:50 +00001297 hue=0.0;
cristy7133e642012-08-14 11:04:11 +00001298 chroma=0.0;
1299 luma=0.0;
cristyddeeea22012-04-12 01:33:09 +00001300 GetPixelInfo(image,&destination_pixel);
1301 GetPixelInfo(composite_image,&source_pixel);
cristy4c08aed2011-07-01 19:47:50 +00001302 for (x=0; x < (ssize_t) image->columns; x++)
1303 {
cristy17028dc2013-01-24 12:28:39 +00001304 double
1305 gamma;
1306
cristy7159f662012-10-28 17:32:43 +00001307 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001308 alpha,
1309 Da,
1310 Dc,
1311 Dca,
cristye4a40472011-12-22 02:56:19 +00001312 Sa,
1313 Sc,
1314 Sca;
1315
1316 register ssize_t
1317 i;
1318
cristy564a5692012-01-20 23:56:26 +00001319 size_t
1320 channels;
1321
cristyfeb3e962012-03-29 17:25:55 +00001322 if (clip_to_self != MagickFalse)
cristy4c08aed2011-07-01 19:47:50 +00001323 {
1324 if (x < x_offset)
1325 {
cristyed231572011-07-14 02:18:59 +00001326 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001327 continue;
1328 }
1329 if ((x-x_offset) >= (ssize_t) composite_image->columns)
1330 break;
1331 }
cristye4a40472011-12-22 02:56:19 +00001332 if ((pixels == (Quantum *) NULL) || (x < x_offset) ||
1333 ((x-x_offset) >= (ssize_t) composite_image->columns))
1334 {
1335 Quantum
1336 source[MaxPixelChannels];
1337
1338 /*
1339 Virtual composite:
1340 Sc: source color.
1341 Dc: destination color.
1342 */
1343 (void) GetOneVirtualPixel(composite_image,x-x_offset,y-y_offset,
1344 source,exception);
cristy883fde12013-04-08 00:50:13 +00001345 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001346 {
1347 q+=GetPixelChannels(image);
1348 continue;
1349 }
cristye4a40472011-12-22 02:56:19 +00001350 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1351 {
cristy7159f662012-10-28 17:32:43 +00001352 MagickRealType
cristye4a40472011-12-22 02:56:19 +00001353 pixel;
1354
cristy5a23c552013-02-13 14:34:28 +00001355 PixelChannel channel=GetPixelChannelChannel(image,i);
1356 PixelTrait traits=GetPixelChannelTraits(image,channel);
1357 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
1358 channel);
cristye4a40472011-12-22 02:56:19 +00001359 if ((traits == UndefinedPixelTrait) ||
1360 (composite_traits == UndefinedPixelTrait))
1361 continue;
1362 switch (compose)
1363 {
cristyc8d63672012-01-11 13:03:13 +00001364 case AlphaCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001365 case ChangeMaskCompositeOp:
1366 case CopyAlphaCompositeOp:
1367 case DstAtopCompositeOp:
1368 case DstInCompositeOp:
1369 case InCompositeOp:
1370 case OutCompositeOp:
1371 case SrcInCompositeOp:
1372 case SrcOutCompositeOp:
1373 {
cristye4a40472011-12-22 02:56:19 +00001374 if (channel == AlphaPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001375 pixel=(MagickRealType) TransparentAlpha;
dirk1474c082014-12-17 16:11:01 +00001376 else
1377 pixel=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001378 break;
1379 }
1380 case ClearCompositeOp:
1381 case CopyCompositeOp:
1382 case ReplaceCompositeOp:
1383 case SrcCompositeOp:
1384 {
1385 if (channel == AlphaPixelChannel)
dirk1474c082014-12-17 16:11:01 +00001386 pixel=(MagickRealType) TransparentAlpha;
1387 else
1388 pixel=0.0;
cristye4a40472011-12-22 02:56:19 +00001389 break;
1390 }
cristy99abff32011-12-24 20:45:16 +00001391 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001392 case DissolveCompositeOp:
1393 {
1394 if (channel == AlphaPixelChannel)
dirk1474c082014-12-17 16:11:01 +00001395 pixel=destination_dissolve*GetPixelAlpha(composite_image,
1396 source);
1397 else
1398 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001399 break;
1400 }
1401 default:
1402 {
cristy7159f662012-10-28 17:32:43 +00001403 pixel=(MagickRealType) source[channel];
cristye4a40472011-12-22 02:56:19 +00001404 break;
1405 }
1406 }
1407 q[i]=ClampToQuantum(pixel);
1408 }
1409 q+=GetPixelChannels(image);
1410 continue;
1411 }
1412 /*
1413 Authentic composite:
1414 Sa: normalized source alpha.
1415 Da: normalized destination alpha.
1416 */
1417 Sa=QuantumScale*GetPixelAlpha(composite_image,p);
1418 Da=QuantumScale*GetPixelAlpha(image,q);
cristy4c08aed2011-07-01 19:47:50 +00001419 switch (compose)
1420 {
cristye4a40472011-12-22 02:56:19 +00001421 case BumpmapCompositeOp:
1422 {
1423 alpha=GetPixelIntensity(composite_image,p)*Sa;
1424 break;
1425 }
cristycdc168f2011-12-21 15:24:39 +00001426 case ColorBurnCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001427 case ColorDodgeCompositeOp:
cristy30dac202014-12-10 02:03:31 +00001428 case DarkenCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001429 case DifferenceCompositeOp:
1430 case DivideDstCompositeOp:
1431 case DivideSrcCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001432 case ExclusionCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001433 case HardLightCompositeOp:
cristy99fc2172014-06-26 10:30:53 +00001434 case HardMixCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001435 case LinearBurnCompositeOp:
1436 case LinearDodgeCompositeOp:
1437 case LinearLightCompositeOp:
cristy30dac202014-12-10 02:03:31 +00001438 case LightenCompositeOp:
cristyd06bf862011-12-21 02:18:45 +00001439 case MathematicsCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001440 case MinusDstCompositeOp:
1441 case MinusSrcCompositeOp:
1442 case ModulusAddCompositeOp:
1443 case ModulusSubtractCompositeOp:
1444 case MultiplyCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001445 case OverlayCompositeOp:
1446 case PegtopLightCompositeOp:
1447 case PinLightCompositeOp:
cristye0984c82011-12-20 02:35:37 +00001448 case ScreenCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001449 case SoftLightCompositeOp:
cristycdc168f2011-12-21 15:24:39 +00001450 case VividLightCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001451 {
1452 alpha=RoundToUnity(Sa+Da-Sa*Da);
1453 break;
1454 }
cristye4a40472011-12-22 02:56:19 +00001455 case DstAtopCompositeOp:
1456 case DstInCompositeOp:
1457 case InCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001458 case SrcInCompositeOp:
1459 {
1460 alpha=Sa*Da;
1461 break;
1462 }
1463 case DissolveCompositeOp:
1464 {
1465 alpha=source_dissolve*Sa*(-destination_dissolve*Da)+source_dissolve*
1466 Sa+destination_dissolve*Da;
1467 break;
1468 }
1469 case DstOverCompositeOp:
1470 {
cristy30dac202014-12-10 02:03:31 +00001471 alpha=Sa+Da-Sa*Da;
cristye4a40472011-12-22 02:56:19 +00001472 break;
1473 }
1474 case DstOutCompositeOp:
1475 {
1476 alpha=Da*(1.0-Sa);
1477 break;
1478 }
1479 case OutCompositeOp:
1480 case SrcOutCompositeOp:
1481 {
1482 alpha=Sa*(1.0-Da);
1483 break;
1484 }
1485 case OverCompositeOp:
1486 case SrcOverCompositeOp:
1487 {
cristy2bd6f5b2014-12-09 00:27:57 +00001488 alpha=Sa+Da-Sa*Da;
cristye4a40472011-12-22 02:56:19 +00001489 break;
1490 }
cristy99abff32011-12-24 20:45:16 +00001491 case BlendCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001492 case PlusCompositeOp:
1493 {
1494 alpha=RoundToUnity(Sa+Da);
1495 break;
1496 }
cristy4c08aed2011-07-01 19:47:50 +00001497 case XorCompositeOp:
1498 {
cristye4a40472011-12-22 02:56:19 +00001499 alpha=Sa+Da-2.0*Sa*Da;
cristy4c08aed2011-07-01 19:47:50 +00001500 break;
1501 }
1502 default:
cristye4a40472011-12-22 02:56:19 +00001503 {
1504 alpha=1.0;
cristy4c08aed2011-07-01 19:47:50 +00001505 break;
cristye4a40472011-12-22 02:56:19 +00001506 }
cristy4c08aed2011-07-01 19:47:50 +00001507 }
cristy883fde12013-04-08 00:50:13 +00001508 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00001509 {
1510 p+=GetPixelChannels(composite_image);
1511 q+=GetPixelChannels(image);
1512 continue;
1513 }
cristy9d3d2792012-04-14 15:15:19 +00001514 switch (compose)
1515 {
1516 case ColorizeCompositeOp:
1517 case HueCompositeOp:
1518 case LuminizeCompositeOp:
1519 case ModulateCompositeOp:
1520 case SaturateCompositeOp:
1521 {
1522 GetPixelInfoPixel(composite_image,p,&source_pixel);
1523 GetPixelInfoPixel(image,q,&destination_pixel);
1524 break;
1525 }
1526 default:
1527 break;
1528 }
cristye4a40472011-12-22 02:56:19 +00001529 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1530 {
cristy7159f662012-10-28 17:32:43 +00001531 MagickRealType
1532 pixel,
cristy564a5692012-01-20 23:56:26 +00001533 sans;
1534
cristy5a23c552013-02-13 14:34:28 +00001535 PixelChannel channel=GetPixelChannelChannel(image,i);
1536 PixelTrait traits=GetPixelChannelTraits(image,channel);
1537 PixelTrait composite_traits=GetPixelChannelTraits(composite_image,
1538 channel);
cristy0cd1f212012-01-05 15:45:59 +00001539 if (traits == UndefinedPixelTrait)
1540 continue;
cristy17a88592014-11-29 15:05:48 +00001541 if (composite_traits == UndefinedPixelTrait)
cristye4a40472011-12-22 02:56:19 +00001542 continue;
1543 /*
1544 Sc: source color.
cristye4a40472011-12-22 02:56:19 +00001545 Dc: destination color.
cristye4a40472011-12-22 02:56:19 +00001546 */
cristy7159f662012-10-28 17:32:43 +00001547 Sc=(MagickRealType) GetPixelChannel(composite_image,channel,p);
1548 Dc=(MagickRealType) q[i];
cristye4a40472011-12-22 02:56:19 +00001549 if ((traits & CopyPixelTrait) != 0)
cristye10859a2011-12-18 22:28:59 +00001550 {
cristye224ed72014-12-10 23:55:01 +00001551 /*
1552 Copy channel.
1553 */
1554 q[i]=ClampToQuantum(Sc);
1555 continue;
1556 }
1557 if (channel == AlphaPixelChannel)
1558 {
cristye4a40472011-12-22 02:56:19 +00001559 /*
1560 Set alpha channel.
1561 */
cristye10859a2011-12-18 22:28:59 +00001562 switch (compose)
1563 {
cristyc8d63672012-01-11 13:03:13 +00001564 case AlphaCompositeOp:
1565 {
cristya7b07912012-01-11 20:01:32 +00001566 pixel=QuantumRange*Sa;
cristyc8d63672012-01-11 13:03:13 +00001567 break;
1568 }
cristye4a40472011-12-22 02:56:19 +00001569 case AtopCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001570 case CopyBlackCompositeOp:
1571 case CopyBlueCompositeOp:
1572 case CopyCyanCompositeOp:
1573 case CopyGreenCompositeOp:
1574 case CopyMagentaCompositeOp:
1575 case CopyRedCompositeOp:
1576 case CopyYellowCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001577 case SrcAtopCompositeOp:
1578 case DstCompositeOp:
1579 case NoCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001580 {
cristye4a40472011-12-22 02:56:19 +00001581 pixel=QuantumRange*Da;
cristye10859a2011-12-18 22:28:59 +00001582 break;
1583 }
cristye10859a2011-12-18 22:28:59 +00001584 case ChangeMaskCompositeOp:
1585 {
cristye4a40472011-12-22 02:56:19 +00001586 MagickBooleanType
1587 equivalent;
1588
cristy7159f662012-10-28 17:32:43 +00001589 if (Da > ((MagickRealType) QuantumRange/2.0))
cristy99abff32011-12-24 20:45:16 +00001590 {
cristy7159f662012-10-28 17:32:43 +00001591 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001592 break;
1593 }
cristye4a40472011-12-22 02:56:19 +00001594 equivalent=IsFuzzyEquivalencePixel(composite_image,p,image,q);
cristy99abff32011-12-24 20:45:16 +00001595 if (equivalent != MagickFalse)
dirk40e1cb52014-12-22 21:38:03 +00001596 pixel=(MagickRealType) TransparentAlpha;
1597 else
1598 pixel=(MagickRealType) OpaqueAlpha;
cristye4a40472011-12-22 02:56:19 +00001599 break;
1600 }
cristy99abff32011-12-24 20:45:16 +00001601 case ClearCompositeOp:
1602 {
cristy7159f662012-10-28 17:32:43 +00001603 pixel=(MagickRealType) TransparentAlpha;
cristy99abff32011-12-24 20:45:16 +00001604 break;
1605 }
1606 case ColorizeCompositeOp:
1607 case HueCompositeOp:
1608 case LuminizeCompositeOp:
1609 case SaturateCompositeOp:
1610 {
cristy3bdd9252014-12-21 20:01:43 +00001611 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
cristy99abff32011-12-24 20:45:16 +00001612 {
1613 pixel=QuantumRange*Da;
1614 break;
1615 }
cristy3bdd9252014-12-21 20:01:43 +00001616 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
cristy99abff32011-12-24 20:45:16 +00001617 {
1618 pixel=QuantumRange*Sa;
1619 break;
1620 }
1621 if (Sa < Da)
1622 {
1623 pixel=QuantumRange*Da;
1624 break;
1625 }
1626 pixel=QuantumRange*Sa;
1627 break;
1628 }
cristy99abff32011-12-24 20:45:16 +00001629 case CopyAlphaCompositeOp:
cristy24d5d722012-05-17 12:27:27 +00001630 {
dirk40e1cb52014-12-22 21:38:03 +00001631 if (Sa == 1.0)
cristy24d5d722012-05-17 12:27:27 +00001632 pixel=GetPixelIntensity(composite_image,p);
dirk40e1cb52014-12-22 21:38:03 +00001633 else
1634 pixel=QuantumRange*Sa;
cristy24d5d722012-05-17 12:27:27 +00001635 break;
1636 }
1637 case CopyCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001638 case DisplaceCompositeOp:
1639 case DistortCompositeOp:
cristye4a40472011-12-22 02:56:19 +00001640 case DstAtopCompositeOp:
1641 case ReplaceCompositeOp:
1642 case SrcCompositeOp:
1643 {
1644 pixel=QuantumRange*Sa;
1645 break;
1646 }
1647 case DarkenIntensityCompositeOp:
1648 {
cristy99abff32011-12-24 20:45:16 +00001649 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1650 (1.0-Da)*GetPixelIntensity(image,q) ? Sa : Da;
cristye4a40472011-12-22 02:56:19 +00001651 break;
1652 }
1653 case LightenIntensityCompositeOp:
1654 {
1655 pixel=Sa*GetPixelIntensity(composite_image,p) >
1656 Da*GetPixelIntensity(image,q) ? Sa : Da;
cristye10859a2011-12-18 22:28:59 +00001657 break;
1658 }
cristy99abff32011-12-24 20:45:16 +00001659 case ModulateCompositeOp:
1660 {
cristy3bdd9252014-12-21 20:01:43 +00001661 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
cristy99abff32011-12-24 20:45:16 +00001662 {
1663 pixel=QuantumRange*Da;
1664 break;
1665 }
1666 pixel=QuantumRange*Da;
1667 break;
1668 }
cristye10859a2011-12-18 22:28:59 +00001669 default:
1670 {
cristye4a40472011-12-22 02:56:19 +00001671 pixel=QuantumRange*alpha;
cristye10859a2011-12-18 22:28:59 +00001672 break;
1673 }
1674 }
cristye4a40472011-12-22 02:56:19 +00001675 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00001676 continue;
1677 }
1678 /*
cristy99abff32011-12-24 20:45:16 +00001679 Porter-Duff compositions:
1680 Sca: source normalized color multiplied by alpha.
1681 Dca: normalized destination color multiplied by alpha.
cristye10859a2011-12-18 22:28:59 +00001682 */
cristy99abff32011-12-24 20:45:16 +00001683 Sca=QuantumScale*Sa*Sc;
1684 Dca=QuantumScale*Da*Dc;
cristye10859a2011-12-18 22:28:59 +00001685 switch (compose)
1686 {
cristye10859a2011-12-18 22:28:59 +00001687 case DarkenCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001688 case LightenCompositeOp:
cristy99abff32011-12-24 20:45:16 +00001689 case ModulusSubtractCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001690 {
cristy99abff32011-12-24 20:45:16 +00001691 gamma=1.0-alpha;
cristye10859a2011-12-18 22:28:59 +00001692 break;
1693 }
1694 default:
1695 break;
1696 }
cristy3e3ec3a2012-11-03 23:11:06 +00001697 gamma=PerceptibleReciprocal(alpha);
cristyd197cbb2012-01-13 02:14:12 +00001698 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001699 switch (compose)
1700 {
cristya7b07912012-01-11 20:01:32 +00001701 case AlphaCompositeOp:
1702 {
1703 pixel=QuantumRange*Sa;
1704 break;
1705 }
cristye4a40472011-12-22 02:56:19 +00001706 case AtopCompositeOp:
1707 case SrcAtopCompositeOp:
cristye10859a2011-12-18 22:28:59 +00001708 {
cristye224ed72014-12-10 23:55:01 +00001709 pixel=QuantumRange*(Sca*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001710 break;
cristye10859a2011-12-18 22:28:59 +00001711 }
cristye4a40472011-12-22 02:56:19 +00001712 case BlendCompositeOp:
1713 {
1714 pixel=gamma*(source_dissolve*Sa*Sc+destination_dissolve*Da*Dc);
1715 break;
1716 }
1717 case BlurCompositeOp:
1718 case DisplaceCompositeOp:
1719 case DistortCompositeOp:
1720 case CopyCompositeOp:
1721 case ReplaceCompositeOp:
1722 case SrcCompositeOp:
1723 {
cristy30dac202014-12-10 02:03:31 +00001724 pixel=QuantumRange*Sca;
cristye4a40472011-12-22 02:56:19 +00001725 break;
1726 }
1727 case BumpmapCompositeOp:
1728 {
cristy3bdd9252014-12-21 20:01:43 +00001729 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001730 {
1731 pixel=Dc;
1732 break;
1733 }
1734 pixel=QuantumScale*GetPixelIntensity(composite_image,p)*Dc;
1735 break;
1736 }
cristy99abff32011-12-24 20:45:16 +00001737 case ChangeMaskCompositeOp:
1738 {
1739 pixel=Dc;
1740 break;
1741 }
1742 case ClearCompositeOp:
1743 {
1744 pixel=0.0;
1745 break;
1746 }
cristye4a40472011-12-22 02:56:19 +00001747 case ColorBurnCompositeOp:
1748 {
cristy30dac202014-12-10 02:03:31 +00001749 if ((Sca == 0.0) && (Dca == Da))
cristye4a40472011-12-22 02:56:19 +00001750 {
cristye224ed72014-12-10 23:55:01 +00001751 pixel=QuantumRange*(Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001752 break;
1753 }
cristy30dac202014-12-10 02:03:31 +00001754 if (Sca == 0.0)
cristye4a40472011-12-22 02:56:19 +00001755 {
cristye224ed72014-12-10 23:55:01 +00001756 pixel=QuantumRange*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001757 break;
1758 }
cristye224ed72014-12-10 23:55:01 +00001759 pixel=QuantumRange*(Sa*Da-Sa*Da*MagickMin(1.0,(1.0-Dca/Da)*Sa/Sca)+
1760 Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001761 break;
1762 }
1763 case ColorDodgeCompositeOp:
1764 {
cristy30dac202014-12-10 02:03:31 +00001765 if ((Sca == Sa) && (Dca == 0.0))
1766 {
cristye224ed72014-12-10 23:55:01 +00001767 pixel=QuantumRange*(Sca*(1.0-Da));
cristy30dac202014-12-10 02:03:31 +00001768 break;
1769 }
1770 if (Sca == Sa)
cristye4a40472011-12-22 02:56:19 +00001771 {
cristye224ed72014-12-10 23:55:01 +00001772 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001773 break;
1774 }
cristye224ed72014-12-10 23:55:01 +00001775 pixel=QuantumRange*(Sa*Da*MagickMin(1.0,Dca/Da*Sa/(Sa-Sca)));
cristye4a40472011-12-22 02:56:19 +00001776 break;
1777 }
1778 case ColorizeCompositeOp:
1779 {
cristy3bdd9252014-12-21 20:01:43 +00001780 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001781 {
1782 pixel=Dc;
1783 break;
1784 }
cristy3bdd9252014-12-21 20:01:43 +00001785 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001786 {
1787 pixel=Sc;
1788 break;
1789 }
cristy7133e642012-08-14 11:04:11 +00001790 CompositeHCL(destination_pixel.red,destination_pixel.green,
1791 destination_pixel.blue,&sans,&sans,&luma);
1792 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
1793 &hue,&chroma,&sans);
1794 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001795 switch (channel)
1796 {
1797 case RedPixelChannel: pixel=red; break;
1798 case GreenPixelChannel: pixel=green; break;
1799 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001800 default: pixel=Dc; break;
1801 }
1802 break;
1803 }
cristye4a40472011-12-22 02:56:19 +00001804 case CopyAlphaCompositeOp:
1805 {
cristy24d5d722012-05-17 12:27:27 +00001806 pixel=Dc;
cristye4a40472011-12-22 02:56:19 +00001807 break;
1808 }
1809 case CopyBlackCompositeOp:
1810 {
cristyd197cbb2012-01-13 02:14:12 +00001811 if (channel == BlackPixelChannel)
cristy2fda00b2013-10-21 16:12:19 +00001812 pixel=(MagickRealType) (QuantumRange-
1813 GetPixelBlack(composite_image,p));
cristye4a40472011-12-22 02:56:19 +00001814 break;
1815 }
1816 case CopyBlueCompositeOp:
1817 case CopyYellowCompositeOp:
1818 {
cristyd197cbb2012-01-13 02:14:12 +00001819 if (channel == BluePixelChannel)
cristy7159f662012-10-28 17:32:43 +00001820 pixel=(MagickRealType) GetPixelBlue(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001821 break;
1822 }
1823 case CopyGreenCompositeOp:
1824 case CopyMagentaCompositeOp:
1825 {
cristyd197cbb2012-01-13 02:14:12 +00001826 if (channel == GreenPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001827 pixel=(MagickRealType) GetPixelGreen(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001828 break;
1829 }
1830 case CopyRedCompositeOp:
1831 case CopyCyanCompositeOp:
1832 {
cristyd197cbb2012-01-13 02:14:12 +00001833 if (channel == RedPixelChannel)
cristy7159f662012-10-28 17:32:43 +00001834 pixel=(MagickRealType) GetPixelRed(composite_image,p);
cristye4a40472011-12-22 02:56:19 +00001835 break;
1836 }
cristy99abff32011-12-24 20:45:16 +00001837 case DarkenCompositeOp:
1838 {
1839 /*
1840 Darken is equivalent to a 'Minimum' method
1841 OR a greyscale version of a binary 'Or'
1842 OR the 'Intersection' of pixel sets.
1843 */
cristy30dac202014-12-10 02:03:31 +00001844 if ((Sca*Da) < (Dca*Sa))
cristy99abff32011-12-24 20:45:16 +00001845 {
cristye224ed72014-12-10 23:55:01 +00001846 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
cristy99abff32011-12-24 20:45:16 +00001847 break;
1848 }
cristye224ed72014-12-10 23:55:01 +00001849 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
cristy99abff32011-12-24 20:45:16 +00001850 break;
1851 }
cristye4a40472011-12-22 02:56:19 +00001852 case DarkenIntensityCompositeOp:
1853 {
cristy99abff32011-12-24 20:45:16 +00001854 pixel=(1.0-Sa)*GetPixelIntensity(composite_image,p) <
1855 (1.0-Da)*GetPixelIntensity(image,q) ? Sc : Dc;
cristye4a40472011-12-22 02:56:19 +00001856 break;
1857 }
1858 case DifferenceCompositeOp:
1859 {
cristye224ed72014-12-10 23:55:01 +00001860 pixel=QuantumRange*(Sca+Dca-2.0*MagickMin(Sca*Da,Dca*Sa));
cristye4a40472011-12-22 02:56:19 +00001861 break;
1862 }
1863 case DissolveCompositeOp:
1864 {
1865 pixel=gamma*(source_dissolve*Sa*Sc-source_dissolve*Sa*
1866 destination_dissolve*Da*Dc+destination_dissolve*Da*Dc);
1867 break;
1868 }
1869 case DivideDstCompositeOp:
1870 {
cristy3bdd9252014-12-21 20:01:43 +00001871 if ((fabs((double) Sca) < MagickEpsilon) &&
1872 (fabs((double) Dca) < MagickEpsilon))
cristye4a40472011-12-22 02:56:19 +00001873 {
cristye224ed72014-12-10 23:55:01 +00001874 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001875 break;
1876 }
cristy3bdd9252014-12-21 20:01:43 +00001877 if (fabs((double) Dca) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001878 {
cristye224ed72014-12-10 23:55:01 +00001879 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001880 break;
1881 }
cristye224ed72014-12-10 23:55:01 +00001882 pixel=QuantumRange*(Sca*Da*Da/Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001883 break;
1884 }
1885 case DivideSrcCompositeOp:
1886 {
cristy3bdd9252014-12-21 20:01:43 +00001887 if ((fabs((double) Dca) < MagickEpsilon) &&
1888 (fabs((double) Sca) < MagickEpsilon))
cristye4a40472011-12-22 02:56:19 +00001889 {
cristye224ed72014-12-10 23:55:01 +00001890 pixel=QuantumRange*(Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001891 break;
1892 }
cristy3bdd9252014-12-21 20:01:43 +00001893 if (fabs((double) Sca) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001894 {
cristye224ed72014-12-10 23:55:01 +00001895 pixel=QuantumRange*(Da*Sa+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001896 break;
1897 }
cristye224ed72014-12-10 23:55:01 +00001898 pixel=QuantumRange*(Dca*Sa*Sa/Sca+Dca*(1.0-Sa)+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001899 break;
1900 }
1901 case DstAtopCompositeOp:
1902 {
cristye224ed72014-12-10 23:55:01 +00001903 pixel=QuantumRange*(Dca*Sa+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001904 break;
1905 }
1906 case DstCompositeOp:
1907 case NoCompositeOp:
1908 {
cristy30dac202014-12-10 02:03:31 +00001909 pixel=QuantumRange*Dca;
cristye4a40472011-12-22 02:56:19 +00001910 break;
1911 }
1912 case DstInCompositeOp:
1913 {
cristye224ed72014-12-10 23:55:01 +00001914 pixel=QuantumRange*(Dca*Sa);
cristye4a40472011-12-22 02:56:19 +00001915 break;
1916 }
1917 case DstOutCompositeOp:
1918 {
cristye224ed72014-12-10 23:55:01 +00001919 pixel=QuantumRange*(Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001920 break;
1921 }
1922 case DstOverCompositeOp:
1923 {
cristye224ed72014-12-10 23:55:01 +00001924 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00001925 break;
1926 }
1927 case ExclusionCompositeOp:
1928 {
cristye224ed72014-12-10 23:55:01 +00001929 pixel=QuantumRange*(Sca*Da+Dca*Sa-2.0*Sca*Dca+Sca*(1.0-Da)+Dca*
1930 (1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001931 break;
1932 }
1933 case HardLightCompositeOp:
1934 {
1935 if ((2.0*Sca) < Sa)
1936 {
cristye224ed72014-12-10 23:55:01 +00001937 pixel=QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001938 break;
1939 }
cristye224ed72014-12-10 23:55:01 +00001940 pixel=QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*
1941 (1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00001942 break;
1943 }
cristy99fc2172014-06-26 10:30:53 +00001944 case HardMixCompositeOp:
1945 {
cristya9781452014-06-26 10:58:54 +00001946 double
1947 gamma;
1948
cristyaa19c352014-06-26 12:46:02 +00001949 if ((Sa+Da) < 1.0)
cristya9781452014-06-26 10:58:54 +00001950 gamma=0.0;
1951 else
1952 gamma=1.0;
cristyaa19c352014-06-26 12:46:02 +00001953 pixel=(gamma*(1.0-Sca)*(1.0-Dca))+Sa*(1.0-Sca)*Dca+Da*(1.0-Dca)*Sca;
cristy99fc2172014-06-26 10:30:53 +00001954 break;
1955 }
cristye4a40472011-12-22 02:56:19 +00001956 case HueCompositeOp:
1957 {
cristy3bdd9252014-12-21 20:01:43 +00001958 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001959 {
1960 pixel=Dc;
1961 break;
1962 }
cristy3bdd9252014-12-21 20:01:43 +00001963 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00001964 {
1965 pixel=Sc;
1966 break;
1967 }
cristy7133e642012-08-14 11:04:11 +00001968 CompositeHCL(destination_pixel.red,destination_pixel.green,
1969 destination_pixel.blue,&hue,&chroma,&luma);
1970 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
cristye4a40472011-12-22 02:56:19 +00001971 &hue,&sans,&sans);
cristy7133e642012-08-14 11:04:11 +00001972 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00001973 switch (channel)
1974 {
1975 case RedPixelChannel: pixel=red; break;
1976 case GreenPixelChannel: pixel=green; break;
1977 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00001978 default: pixel=Dc; break;
1979 }
1980 break;
1981 }
1982 case InCompositeOp:
1983 case SrcInCompositeOp:
1984 {
cristye224ed72014-12-10 23:55:01 +00001985 pixel=QuantumRange*(Sca*Da);
cristye4a40472011-12-22 02:56:19 +00001986 break;
1987 }
cristy99abff32011-12-24 20:45:16 +00001988 case LinearBurnCompositeOp:
1989 {
1990 /*
1991 LinearBurn: as defined by Abode Photoshop, according to
1992 http://www.simplefilter.de/en/basics/mixmods.html is:
1993
1994 f(Sc,Dc) = Sc + Dc - 1
1995 */
cristye224ed72014-12-10 23:55:01 +00001996 pixel=QuantumRange*(Sca+Dca-Sa*Da);
cristy99abff32011-12-24 20:45:16 +00001997 break;
1998 }
1999 case LinearDodgeCompositeOp:
2000 {
cristye224ed72014-12-10 23:55:01 +00002001 pixel=QuantumRange*(Sa*Sc+Da*Dc);
cristy99abff32011-12-24 20:45:16 +00002002 break;
2003 }
2004 case LinearLightCompositeOp:
2005 {
2006 /*
2007 LinearLight: as defined by Abode Photoshop, according to
2008 http://www.simplefilter.de/en/basics/mixmods.html is:
2009
2010 f(Sc,Dc) = Dc + 2*Sc - 1
2011 */
cristye224ed72014-12-10 23:55:01 +00002012 pixel=QuantumRange*((Sca-Sa)*Da+Sca+Dca);
cristy99abff32011-12-24 20:45:16 +00002013 break;
2014 }
2015 case LightenCompositeOp:
2016 {
cristy30dac202014-12-10 02:03:31 +00002017 if ((Sca*Da) > (Dca*Sa))
cristy99abff32011-12-24 20:45:16 +00002018 {
cristye224ed72014-12-10 23:55:01 +00002019 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
cristy99abff32011-12-24 20:45:16 +00002020 break;
2021 }
cristye224ed72014-12-10 23:55:01 +00002022 pixel=QuantumRange*(Dca+Sca*(1.0-Da));
cristy99abff32011-12-24 20:45:16 +00002023 break;
2024 }
cristye4a40472011-12-22 02:56:19 +00002025 case LightenIntensityCompositeOp:
2026 {
2027 /*
2028 Lighten is equivalent to a 'Maximum' method
2029 OR a greyscale version of a binary 'And'
2030 OR the 'Union' of pixel sets.
2031 */
2032 pixel=Sa*GetPixelIntensity(composite_image,p) >
2033 Da*GetPixelIntensity(image,q) ? Sc : Dc;
2034 break;
2035 }
cristye4a40472011-12-22 02:56:19 +00002036 case LuminizeCompositeOp:
2037 {
cristy3bdd9252014-12-21 20:01:43 +00002038 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002039 {
2040 pixel=Dc;
2041 break;
2042 }
cristy3bdd9252014-12-21 20:01:43 +00002043 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002044 {
2045 pixel=Sc;
2046 break;
2047 }
cristy7133e642012-08-14 11:04:11 +00002048 CompositeHCL(destination_pixel.red,destination_pixel.green,
2049 destination_pixel.blue,&hue,&chroma,&luma);
2050 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2051 &sans,&sans,&luma);
2052 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002053 switch (channel)
2054 {
2055 case RedPixelChannel: pixel=red; break;
2056 case GreenPixelChannel: pixel=green; break;
2057 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002058 default: pixel=Dc; break;
2059 }
2060 break;
2061 }
2062 case MathematicsCompositeOp:
2063 {
2064 /*
2065 'Mathematics' a free form user control mathematical composition
2066 is defined as...
2067
2068 f(Sc,Dc) = A*Sc*Dc + B*Sc + C*Dc + D
2069
2070 Where the arguments A,B,C,D are (currently) passed to composite
2071 as a command separated 'geometry' string in "compose:args" image
2072 artifact.
2073
2074 A = a->rho, B = a->sigma, C = a->xi, D = a->psi
2075
2076 Applying the SVG transparency formula (see above), we get...
2077
2078 Dca' = Sa*Da*f(Sc,Dc) + Sca*(1.0-Da) + Dca*(1.0-Sa)
2079
2080 Dca' = A*Sca*Dca + B*Sca*Da + C*Dca*Sa + D*Sa*Da + Sca*(1.0-Da) +
2081 Dca*(1.0-Sa)
2082 */
2083 pixel=gamma*geometry_info.rho*Sa*Sc*Da*Dc+geometry_info.sigma*
2084 Sa*Sc*Da+geometry_info.xi*Da*Dc*Sa+geometry_info.psi*Sa*Da+
2085 Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa);
2086 break;
2087 }
2088 case MinusDstCompositeOp:
2089 {
2090 pixel=gamma*(Sa*Sc+Da*Dc-2.0*Da*Dc*Sa);
2091 break;
2092 }
2093 case MinusSrcCompositeOp:
2094 {
2095 /*
2096 Minus source from destination.
2097
2098 f(Sc,Dc) = Sc - Dc
2099 */
cristye224ed72014-12-10 23:55:01 +00002100 pixel=QuantumRange*(Da*Dc+Sa*Sc-2.0*Sa*Sc*Da);
cristye4a40472011-12-22 02:56:19 +00002101 break;
2102 }
2103 case ModulateCompositeOp:
2104 {
2105 ssize_t
2106 offset;
2107
cristy3bdd9252014-12-21 20:01:43 +00002108 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002109 {
2110 pixel=Dc;
2111 break;
2112 }
2113 offset=(ssize_t) (GetPixelIntensity(composite_image,p)-midpoint);
2114 if (offset == 0)
2115 {
2116 pixel=Dc;
2117 break;
2118 }
cristy7133e642012-08-14 11:04:11 +00002119 CompositeHCL(destination_pixel.red,destination_pixel.green,
2120 destination_pixel.blue,&hue,&chroma,&luma);
2121 luma+=(0.01*percent_luma*offset)/midpoint;
2122 chroma*=0.01*percent_chroma;
2123 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002124 switch (channel)
2125 {
2126 case RedPixelChannel: pixel=red; break;
2127 case GreenPixelChannel: pixel=green; break;
2128 case BluePixelChannel: pixel=blue; break;
2129 default: pixel=Dc; break;
2130 }
2131 break;
2132 }
2133 case ModulusAddCompositeOp:
2134 {
2135 pixel=Sc+Dc;
2136 if (pixel > QuantumRange)
cristy516edc12013-10-27 10:53:42 +00002137 pixel-=QuantumRange;
cristy91de9ea2013-11-17 14:45:54 +00002138 pixel=gamma*(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002139 break;
2140 }
2141 case ModulusSubtractCompositeOp:
2142 {
cristy99abff32011-12-24 20:45:16 +00002143 pixel=Sc-Dc;
cristye4a40472011-12-22 02:56:19 +00002144 if (pixel < 0.0)
cristy516edc12013-10-27 10:53:42 +00002145 pixel+=QuantumRange;
cristy91de9ea2013-11-17 14:45:54 +00002146 pixel=gamma*(Sa*Da*pixel+Sa*Sc*(1.0-Da)+Da*Dc*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002147 break;
2148 }
2149 case MultiplyCompositeOp:
2150 {
cristye224ed72014-12-10 23:55:01 +00002151 pixel=QuantumRange*(Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002152 break;
2153 }
2154 case OutCompositeOp:
2155 case SrcOutCompositeOp:
2156 {
cristye224ed72014-12-10 23:55:01 +00002157 pixel=QuantumRange*(Sca*(1.0-Da));
cristye4a40472011-12-22 02:56:19 +00002158 break;
2159 }
2160 case OverCompositeOp:
2161 case SrcOverCompositeOp:
2162 {
cristye224ed72014-12-10 23:55:01 +00002163 pixel=QuantumRange*(Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002164 break;
2165 }
2166 case OverlayCompositeOp:
2167 {
cristy30dac202014-12-10 02:03:31 +00002168 if ((2.0*Dca) <= Da)
cristy99abff32011-12-24 20:45:16 +00002169 {
cristye224ed72014-12-10 23:55:01 +00002170 pixel=QuantumRange*(2.0*Sca*Dca+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristy99abff32011-12-24 20:45:16 +00002171 break;
2172 }
cristye224ed72014-12-10 23:55:01 +00002173 pixel=QuantumRange*(Sa*Da-2.0*(Da-Dca)*(Sa-Sca)+Sca*(1.0-Da)+Dca*
2174 (1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002175 break;
2176 }
2177 case PegtopLightCompositeOp:
2178 {
2179 /*
2180 PegTop: A Soft-Light alternative: A continuous version of the
2181 Softlight function, producing very similar results.
2182
2183 f(Sc,Dc) = Dc^2*(1-2*Sc) + 2*Sc*Dc
2184
2185 http://www.pegtop.net/delphi/articles/blendmodes/softlight.htm.
2186 */
cristy3bdd9252014-12-21 20:01:43 +00002187 if (fabs((double) Da) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002188 {
cristye224ed72014-12-10 23:55:01 +00002189 pixel=QuantumRange*(Sca);
cristye4a40472011-12-22 02:56:19 +00002190 break;
2191 }
cristye224ed72014-12-10 23:55:01 +00002192 pixel=QuantumRange*(Dca*Dca*(Sa-2.0*Sca)/Da+Sca*(2.0*Dca+1.0-Da)+
2193 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002194 break;
2195 }
2196 case PinLightCompositeOp:
2197 {
2198 /*
2199 PinLight: A Photoshop 7 composition method
2200 http://www.simplefilter.de/en/basics/mixmods.html
2201
2202 f(Sc,Dc) = Dc<2*Sc-1 ? 2*Sc-1 : Dc>2*Sc ? 2*Sc : Dc
2203 */
2204 if ((Dca*Sa) < (Da*(2.0*Sca-Sa)))
2205 {
cristye224ed72014-12-10 23:55:01 +00002206 pixel=QuantumRange*(Sca*(Da+1.0)-Sa*Da+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002207 break;
2208 }
2209 if ((Dca*Sa) > (2.0*Sca*Da))
2210 {
cristye224ed72014-12-10 23:55:01 +00002211 pixel=QuantumRange*(Sca*Da+Sca+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002212 break;
2213 }
cristye224ed72014-12-10 23:55:01 +00002214 pixel=QuantumRange*(Sca*(1.0-Da)+Dca);
cristye4a40472011-12-22 02:56:19 +00002215 break;
2216 }
2217 case PlusCompositeOp:
2218 {
cristye224ed72014-12-10 23:55:01 +00002219 pixel=QuantumRange*(Sca+Dca);
cristye4a40472011-12-22 02:56:19 +00002220 break;
2221 }
2222 case SaturateCompositeOp:
2223 {
cristy3bdd9252014-12-21 20:01:43 +00002224 if (fabs((double) (QuantumRange*Sa-TransparentAlpha)) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002225 {
2226 pixel=Dc;
2227 break;
2228 }
cristy3bdd9252014-12-21 20:01:43 +00002229 if (fabs((double) (QuantumRange*Da-TransparentAlpha)) < MagickEpsilon)
cristye4a40472011-12-22 02:56:19 +00002230 {
2231 pixel=Sc;
2232 break;
2233 }
cristy7133e642012-08-14 11:04:11 +00002234 CompositeHCL(destination_pixel.red,destination_pixel.green,
2235 destination_pixel.blue,&hue,&chroma,&luma);
2236 CompositeHCL(source_pixel.red,source_pixel.green,source_pixel.blue,
2237 &sans,&chroma,&sans);
2238 HCLComposite(hue,chroma,luma,&red,&green,&blue);
cristye4a40472011-12-22 02:56:19 +00002239 switch (channel)
2240 {
2241 case RedPixelChannel: pixel=red; break;
2242 case GreenPixelChannel: pixel=green; break;
2243 case BluePixelChannel: pixel=blue; break;
cristye4a40472011-12-22 02:56:19 +00002244 default: pixel=Dc; break;
2245 }
2246 break;
2247 }
2248 case ScreenCompositeOp:
2249 {
2250 /*
2251 Screen: a negated multiply:
2252
2253 f(Sc,Dc) = 1.0-(1.0-Sc)*(1.0-Dc)
2254 */
cristye224ed72014-12-10 23:55:01 +00002255 pixel=QuantumRange*(Sca+Dca-Sca*Dca);
cristye4a40472011-12-22 02:56:19 +00002256 break;
2257 }
2258 case SoftLightCompositeOp:
2259 {
cristye4a40472011-12-22 02:56:19 +00002260 if ((2.0*Sca) < Sa)
2261 {
cristye224ed72014-12-10 23:55:01 +00002262 pixel=QuantumRange*(Dca*(Sa+(2.0*Sca-Sa)*(1.0-(Dca/Da)))+Sca*
2263 (1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002264 break;
2265 }
2266 if (((2.0*Sca) > Sa) && ((4.0*Dca) <= Da))
2267 {
cristye224ed72014-12-10 23:55:01 +00002268 pixel=QuantumRange*(Dca*Sa+Da*(2.0*Sca-Sa)*(4.0*(Dca/Da)*(4.0*
2269 (Dca/Da)+1.0)*((Dca/Da)-1.0)+7.0*(Dca/Da))+Sca*(1.0-Da)+Dca*
2270 (1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002271 break;
2272 }
cristye224ed72014-12-10 23:55:01 +00002273 pixel=QuantumRange*(Dca*Sa+Da*(2.0*Sca-Sa)*(pow((Dca/Da),0.5)-(Dca/
2274 Da))+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002275 break;
2276 }
2277 case ThresholdCompositeOp:
2278 {
cristy7159f662012-10-28 17:32:43 +00002279 MagickRealType
cristye4a40472011-12-22 02:56:19 +00002280 delta;
2281
2282 delta=Sc-Dc;
cristy7159f662012-10-28 17:32:43 +00002283 if ((MagickRealType) fabs((double) (2.0*delta)) < threshold)
cristye4a40472011-12-22 02:56:19 +00002284 {
2285 pixel=gamma*Dc;
2286 break;
2287 }
2288 pixel=gamma*(Dc+delta*amount);
2289 break;
2290 }
2291 case VividLightCompositeOp:
2292 {
2293 /*
2294 VividLight: A Photoshop 7 composition method. See
2295 http://www.simplefilter.de/en/basics/mixmods.html.
2296
2297 f(Sc,Dc) = (2*Sc < 1) ? 1-(1-Dc)/(2*Sc) : Dc/(2*(1-Sc))
2298 */
cristy3bdd9252014-12-21 20:01:43 +00002299 if ((fabs((double) Sa) < MagickEpsilon) ||
2300 (fabs((double) (Sca-Sa)) < MagickEpsilon))
cristye4a40472011-12-22 02:56:19 +00002301 {
cristye224ed72014-12-10 23:55:01 +00002302 pixel=QuantumRange*(Sa*Da+Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002303 break;
2304 }
2305 if ((2.0*Sca) <= Sa)
2306 {
cristye224ed72014-12-10 23:55:01 +00002307 pixel=QuantumRange*(Sa*(Da+Sa*(Dca-Da)/(2.0*Sca))+Sca*(1.0-Da)+
2308 Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002309 break;
2310 }
cristye224ed72014-12-10 23:55:01 +00002311 pixel=QuantumRange*(Dca*Sa*Sa/(2.0*(Sa-Sca))+Sca*(1.0-Da)+Dca*
2312 (1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002313 break;
2314 }
2315 case XorCompositeOp:
2316 {
cristye224ed72014-12-10 23:55:01 +00002317 pixel=QuantumRange*(Sca*(1.0-Da)+Dca*(1.0-Sa));
cristye4a40472011-12-22 02:56:19 +00002318 break;
2319 }
2320 default:
2321 {
2322 pixel=Sc;
2323 break;
2324 }
2325 }
2326 q[i]=ClampToQuantum(pixel);
cristye10859a2011-12-18 22:28:59 +00002327 }
cristyed231572011-07-14 02:18:59 +00002328 p+=GetPixelChannels(composite_image);
cristye4a40472011-12-22 02:56:19 +00002329 channels=GetPixelChannels(composite_image);
2330 if (p >= (pixels+channels*composite_image->columns))
cristy4c08aed2011-07-01 19:47:50 +00002331 p=pixels;
cristyed231572011-07-14 02:18:59 +00002332 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002333 }
2334 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2335 status=MagickFalse;
2336 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2337 {
2338 MagickBooleanType
2339 proceed;
2340
2341#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristybd0435b2012-01-05 16:20:47 +00002342 #pragma omp critical (MagickCore_CompositeImage)
cristy4c08aed2011-07-01 19:47:50 +00002343#endif
2344 proceed=SetImageProgress(image,CompositeImageTag,progress++,
2345 image->rows);
2346 if (proceed == MagickFalse)
2347 status=MagickFalse;
2348 }
2349 }
2350 composite_view=DestroyCacheView(composite_view);
2351 image_view=DestroyCacheView(image_view);
2352 if (destination_image != (Image * ) NULL)
2353 destination_image=DestroyImage(destination_image);
cristyf661c4d2012-07-28 12:57:17 +00002354 else
2355 composite_image=DestroyImage(composite_image);
cristy4c08aed2011-07-01 19:47:50 +00002356 return(status);
2357}
2358
2359/*
2360%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2361% %
2362% %
2363% %
2364% T e x t u r e I m a g e %
2365% %
2366% %
2367% %
2368%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2369%
2370% TextureImage() repeatedly tiles the texture image across and down the image
2371% canvas.
2372%
2373% The format of the TextureImage method is:
2374%
cristy30d8c942012-02-07 13:44:59 +00002375% MagickBooleanType TextureImage(Image *image,const Image *texture,
cristye941a752011-10-15 01:52:48 +00002376% ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002377%
2378% A description of each parameter follows:
2379%
2380% o image: the image.
2381%
cristye6178502011-12-23 17:02:29 +00002382% o texture_image: This image is the texture to layer on the background.
cristy4c08aed2011-07-01 19:47:50 +00002383%
2384*/
cristy30d8c942012-02-07 13:44:59 +00002385MagickExport MagickBooleanType TextureImage(Image *image,const Image *texture,
2386 ExceptionInfo *exception)
cristy4c08aed2011-07-01 19:47:50 +00002387{
2388#define TextureImageTag "Texture/Image"
2389
2390 CacheView
2391 *image_view,
2392 *texture_view;
2393
cristy30d8c942012-02-07 13:44:59 +00002394 Image
2395 *texture_image;
2396
cristy4c08aed2011-07-01 19:47:50 +00002397 MagickBooleanType
2398 status;
2399
2400 ssize_t
2401 y;
2402
2403 assert(image != (Image *) NULL);
2404 if (image->debug != MagickFalse)
2405 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2406 assert(image->signature == MagickSignature);
cristy30d8c942012-02-07 13:44:59 +00002407 if (texture == (const Image *) NULL)
2408 return(MagickFalse);
2409 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
2410 return(MagickFalse);
2411 texture_image=CloneImage(texture,0,0,MagickTrue,exception);
cristye6178502011-12-23 17:02:29 +00002412 if (texture_image == (const Image *) NULL)
cristy4c08aed2011-07-01 19:47:50 +00002413 return(MagickFalse);
cristya865ccd2012-07-28 00:33:10 +00002414 (void) TransformImageColorspace(texture_image,image->colorspace,exception);
cristy387430f2012-02-07 13:09:46 +00002415 (void) SetImageVirtualPixelMethod(texture_image,TileVirtualPixelMethod,
2416 exception);
cristy4c08aed2011-07-01 19:47:50 +00002417 status=MagickTrue;
2418 if ((image->compose != CopyCompositeOp) &&
cristy974a2a82013-04-23 12:57:16 +00002419 ((image->compose != OverCompositeOp) ||
cristy17f11b02014-12-20 19:37:04 +00002420 (image->alpha_trait != UndefinedPixelTrait) ||
2421 (texture_image->alpha_trait != UndefinedPixelTrait)))
cristy4c08aed2011-07-01 19:47:50 +00002422 {
2423 /*
2424 Tile texture onto the image background.
2425 */
cristye6178502011-12-23 17:02:29 +00002426 for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) texture_image->rows)
cristy4c08aed2011-07-01 19:47:50 +00002427 {
2428 register ssize_t
2429 x;
2430
2431 if (status == MagickFalse)
2432 continue;
cristye6178502011-12-23 17:02:29 +00002433 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002434 {
2435 MagickBooleanType
2436 thread_status;
2437
cristyfeb3e962012-03-29 17:25:55 +00002438 thread_status=CompositeImage(image,texture_image,image->compose,
2439 MagickFalse,x+texture_image->tile_offset.x,y+
2440 texture_image->tile_offset.y,exception);
cristy4c08aed2011-07-01 19:47:50 +00002441 if (thread_status == MagickFalse)
2442 {
2443 status=thread_status;
2444 break;
2445 }
2446 }
2447 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2448 {
2449 MagickBooleanType
2450 proceed;
2451
cristy4c08aed2011-07-01 19:47:50 +00002452 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2453 y,image->rows);
2454 if (proceed == MagickFalse)
2455 status=MagickFalse;
2456 }
2457 }
2458 (void) SetImageProgress(image,TextureImageTag,(MagickOffsetType)
2459 image->rows,image->rows);
cristy30d8c942012-02-07 13:44:59 +00002460 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002461 return(status);
2462 }
2463 /*
2464 Tile texture onto the image background (optimized).
2465 */
2466 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +00002467 texture_view=AcquireVirtualCacheView(texture_image,exception);
2468 image_view=AcquireAuthenticCacheView(image,exception);
cristye8914392012-12-16 21:16:11 +00002469#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyd6432472013-01-06 16:56:13 +00002470 #pragma omp parallel for schedule(static,4) shared(status) \
2471 magick_threads(texture_image,image,1,1)
cristye8914392012-12-16 21:16:11 +00002472#endif
cristy4c08aed2011-07-01 19:47:50 +00002473 for (y=0; y < (ssize_t) image->rows; y++)
2474 {
2475 MagickBooleanType
2476 sync;
2477
2478 register const Quantum
2479 *p,
2480 *pixels;
2481
2482 register ssize_t
2483 x;
2484
2485 register Quantum
2486 *q;
2487
2488 size_t
2489 width;
2490
2491 if (status == MagickFalse)
2492 continue;
cristye6178502011-12-23 17:02:29 +00002493 pixels=GetCacheViewVirtualPixels(texture_view,texture_image->tile_offset.x,
2494 (y+texture_image->tile_offset.y) % texture_image->rows,
2495 texture_image->columns,1,exception);
2496 q=QueueCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00002497 if ((pixels == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2498 {
2499 status=MagickFalse;
2500 continue;
2501 }
cristye6178502011-12-23 17:02:29 +00002502 for (x=0; x < (ssize_t) image->columns; x+=(ssize_t) texture_image->columns)
cristy4c08aed2011-07-01 19:47:50 +00002503 {
2504 register ssize_t
cristye6178502011-12-23 17:02:29 +00002505 j;
cristy4c08aed2011-07-01 19:47:50 +00002506
2507 p=pixels;
cristye6178502011-12-23 17:02:29 +00002508 width=texture_image->columns;
cristy4c08aed2011-07-01 19:47:50 +00002509 if ((x+(ssize_t) width) > (ssize_t) image->columns)
2510 width=image->columns-x;
cristye6178502011-12-23 17:02:29 +00002511 for (j=0; j < (ssize_t) width; j++)
cristy4c08aed2011-07-01 19:47:50 +00002512 {
cristye6178502011-12-23 17:02:29 +00002513 register ssize_t
2514 i;
2515
cristy883fde12013-04-08 00:50:13 +00002516 if (GetPixelReadMask(image,q) == 0)
cristy10a6c612012-01-29 21:41:05 +00002517 {
2518 p+=GetPixelChannels(texture_image);
2519 q+=GetPixelChannels(image);
2520 continue;
2521 }
cristye6178502011-12-23 17:02:29 +00002522 for (i=0; i < (ssize_t) GetPixelChannels(texture_image); i++)
2523 {
cristy5a23c552013-02-13 14:34:28 +00002524 PixelChannel channel=GetPixelChannelChannel(texture_image,i);
2525 PixelTrait traits=GetPixelChannelTraits(image,channel);
2526 PixelTrait texture_traits=GetPixelChannelTraits(texture_image,
2527 channel);
cristye6178502011-12-23 17:02:29 +00002528 if ((traits == UndefinedPixelTrait) ||
2529 (texture_traits == UndefinedPixelTrait))
2530 continue;
2531 SetPixelChannel(image,channel,p[i],q);
2532 }
2533 p+=GetPixelChannels(texture_image);
cristyed231572011-07-14 02:18:59 +00002534 q+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00002535 }
2536 }
2537 sync=SyncCacheViewAuthenticPixels(image_view,exception);
2538 if (sync == MagickFalse)
2539 status=MagickFalse;
2540 if (image->progress_monitor != (MagickProgressMonitor) NULL)
2541 {
2542 MagickBooleanType
2543 proceed;
2544
cristy4c08aed2011-07-01 19:47:50 +00002545 proceed=SetImageProgress(image,TextureImageTag,(MagickOffsetType) y,
2546 image->rows);
2547 if (proceed == MagickFalse)
2548 status=MagickFalse;
2549 }
2550 }
2551 texture_view=DestroyCacheView(texture_view);
2552 image_view=DestroyCacheView(image_view);
cristy30d8c942012-02-07 13:44:59 +00002553 texture_image=DestroyImage(texture_image);
cristy4c08aed2011-07-01 19:47:50 +00002554 return(status);
2555}