blob: 6de39fbdd71df523610a1cb4674f0fbb19f4a503 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP AAA IIIII N N TTTTT %
7% P P A A I NN N T %
8% PPPP AAAAA I N N N T %
9% P A A I N NN T %
10% P A A IIIII N N T %
11% %
12% %
13% Methods to Paint on an Image %
14% %
15% Software Design %
16% John Cristy %
17% July 1998 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +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 Include declarations.
41*/
42#include "magick/studio.h"
43#include "magick/color.h"
44#include "magick/color-private.h"
45#include "magick/colorspace-private.h"
46#include "magick/composite.h"
47#include "magick/composite-private.h"
48#include "magick/draw.h"
49#include "magick/draw-private.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/gem.h"
53#include "magick/monitor.h"
54#include "magick/monitor-private.h"
55#include "magick/paint.h"
56#include "magick/pixel-private.h"
57#include "magick/string_.h"
58#include "magick/thread-private.h"
59
cristy3ed852e2009-09-05 21:47:34 +000060/*
61%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62% %
63% %
64% %
65% F l o o d f i l l P a i n t I m a g e %
66% %
67% %
68% %
69%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
70%
71% FloodfillPaintImage() changes the color value of any pixel that matches
72% target and is an immediate neighbor. If the method FillToBorderMethod is
73% specified, the color value is changed for any neighbor pixel that does not
74% match the bordercolor member of image.
75%
76% By default target must match a particular pixel color exactly.
77% However, in many cases two colors may differ by a small amount. The
78% fuzz member of image defines how much tolerance is acceptable to
79% consider two colors as the same. For example, set fuzz to 10 and the
80% color red at intensities of 100 and 102 respectively are now
81% interpreted as the same color for the purposes of the floodfill.
82%
83% The format of the FloodfillPaintImage method is:
84%
85% MagickBooleanType FloodfillPaintImage(Image *image,
86% const ChannelType channel,const DrawInfo *draw_info,
cristybb503372010-05-27 20:51:26 +000087% const MagickPixelPacket target,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +000088% const MagickBooleanType invert)
89%
90% A description of each parameter follows:
91%
92% o image: the image.
93%
94% o channel: the channel(s).
95%
96% o draw_info: the draw info.
97%
98% o target: the RGB value of the target color.
99%
100% o x_offset,y_offset: the starting location of the operation.
101%
102% o invert: paint any pixel that does not match the target color.
103%
104*/
105MagickExport MagickBooleanType FloodfillPaintImage(Image *image,
106 const ChannelType channel,const DrawInfo *draw_info,
cristybb503372010-05-27 20:51:26 +0000107 const MagickPixelPacket *target,const ssize_t x_offset,const ssize_t y_offset,
cristy3ed852e2009-09-05 21:47:34 +0000108 const MagickBooleanType invert)
109{
110#define MaxStacksize (1UL << 15)
111#define PushSegmentStack(up,left,right,delta) \
112{ \
113 if (s >= (segment_stack+MaxStacksize)) \
114 ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \
115 else \
116 { \
cristybb503372010-05-27 20:51:26 +0000117 if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (ssize_t) image->rows)) \
cristy3ed852e2009-09-05 21:47:34 +0000118 { \
119 s->x1=(double) (left); \
120 s->y1=(double) (up); \
121 s->x2=(double) (right); \
122 s->y2=(double) (delta); \
123 s++; \
124 } \
125 } \
126}
127
128 ExceptionInfo
129 *exception;
130
131 Image
132 *floodplane_image;
133
cristybb503372010-05-27 20:51:26 +0000134 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000135 offset,
136 start,
137 x,
138 x1,
139 x2,
140 y;
141
142 MagickBooleanType
143 skip;
144
145 MagickPixelPacket
146 fill,
147 pixel;
148
149 PixelPacket
150 fill_color;
151
152 register SegmentInfo
153 *s;
154
155 SegmentInfo
156 *segment_stack;
157
158 /*
159 Check boundary conditions.
160 */
161 assert(image != (Image *) NULL);
162 assert(image->signature == MagickSignature);
163 if (image->debug != MagickFalse)
164 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
165 assert(draw_info != (DrawInfo *) NULL);
166 assert(draw_info->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +0000167 if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
cristy3ed852e2009-09-05 21:47:34 +0000168 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000169 if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000170 return(MagickFalse);
171 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
172 return(MagickFalse);
173 if (image->matte == MagickFalse)
174 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
175 /*
176 Set floodfill state.
177 */
178 floodplane_image=CloneImage(image,0,0,MagickTrue,&image->exception);
179 if (floodplane_image == (Image *) NULL)
180 return(MagickFalse);
181 (void) SetImageAlphaChannel(floodplane_image,OpaqueAlphaChannel);
182 segment_stack=(SegmentInfo *) AcquireQuantumMemory(MaxStacksize,
183 sizeof(*segment_stack));
184 if (segment_stack == (SegmentInfo *) NULL)
185 {
186 floodplane_image=DestroyImage(floodplane_image);
187 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
188 image->filename);
189 }
190 /*
191 Push initial segment on stack.
192 */
193 exception=(&image->exception);
194 x=x_offset;
195 y=y_offset;
196 start=0;
197 s=segment_stack;
198 PushSegmentStack(y,x,x,1);
199 PushSegmentStack(y+1,x,x,-1);
200 GetMagickPixelPacket(image,&fill);
201 GetMagickPixelPacket(image,&pixel);
202 while (s > segment_stack)
203 {
204 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000205 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000206
207 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000208 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000209
cristybb503372010-05-27 20:51:26 +0000210 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000211 x;
212
213 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000214 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000215
216 /*
217 Pop segment off stack.
218 */
219 s--;
cristybb503372010-05-27 20:51:26 +0000220 x1=(ssize_t) s->x1;
221 x2=(ssize_t) s->x2;
222 offset=(ssize_t) s->y2;
223 y=(ssize_t) s->y1+offset;
cristy3ed852e2009-09-05 21:47:34 +0000224 /*
225 Recolor neighboring pixels.
226 */
cristybb503372010-05-27 20:51:26 +0000227 p=GetVirtualPixels(image,0,y,(size_t) (x1+1),1,exception);
228 q=GetAuthenticPixels(floodplane_image,0,y,(size_t) (x1+1),1,
cristy3ed852e2009-09-05 21:47:34 +0000229 exception);
230 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
231 break;
232 indexes=GetVirtualIndexQueue(image);
233 p+=x1;
234 q+=x1;
235 for (x=x1; x >= 0; x--)
236 {
237 if (q->opacity == (Quantum) TransparentOpacity)
238 break;
239 SetMagickPixelPacket(image,p,indexes+x,&pixel);
240 if (IsMagickColorSimilar(&pixel,target) == invert)
241 break;
242 q->opacity=(Quantum) TransparentOpacity;
243 p--;
244 q--;
245 }
246 if (SyncAuthenticPixels(floodplane_image,exception) == MagickFalse)
247 break;
248 skip=x >= x1 ? MagickTrue : MagickFalse;
249 if (skip == MagickFalse)
250 {
251 start=x+1;
252 if (start < x1)
253 PushSegmentStack(y,start,x1-1,-offset);
254 x=x1+1;
255 }
256 do
257 {
258 if (skip == MagickFalse)
259 {
cristybb503372010-05-27 20:51:26 +0000260 if (x < (ssize_t) image->columns)
cristy3ed852e2009-09-05 21:47:34 +0000261 {
262 p=GetVirtualPixels(image,x,y,image->columns-x,1,exception);
263 q=GetAuthenticPixels(floodplane_image,x,y,image->columns-x,1,
264 exception);
265 if ((p == (const PixelPacket *) NULL) ||
266 (q == (PixelPacket *) NULL))
267 break;
268 indexes=GetVirtualIndexQueue(image);
cristybb503372010-05-27 20:51:26 +0000269 for ( ; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000270 {
271 if (q->opacity == (Quantum) TransparentOpacity)
272 break;
273 SetMagickPixelPacket(image,p,indexes+x,&pixel);
274 if (IsMagickColorSimilar(&pixel,target) == invert)
275 break;
276 q->opacity=(Quantum) TransparentOpacity;
277 p++;
278 q++;
279 }
280 if (SyncAuthenticPixels(floodplane_image,exception) == MagickFalse)
281 break;
282 }
283 PushSegmentStack(y,start,x-1,offset);
284 if (x > (x2+1))
285 PushSegmentStack(y,x2+1,x-1,-offset);
286 }
287 skip=MagickFalse;
288 x++;
289 if (x <= x2)
290 {
cristybb503372010-05-27 20:51:26 +0000291 p=GetVirtualPixels(image,x,y,(size_t) (x2-x+1),1,exception);
292 q=GetAuthenticPixels(floodplane_image,x,y,(size_t) (x2-x+1),1,
cristy3ed852e2009-09-05 21:47:34 +0000293 exception);
294 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
295 break;
296 indexes=GetVirtualIndexQueue(image);
297 for ( ; x <= x2; x++)
298 {
299 if (q->opacity == (Quantum) TransparentOpacity)
300 break;
301 SetMagickPixelPacket(image,p,indexes+x,&pixel);
302 if (IsMagickColorSimilar(&pixel,target) != invert)
303 break;
304 p++;
305 q++;
306 }
307 }
308 start=x;
309 } while (x <= x2);
310 }
cristybb503372010-05-27 20:51:26 +0000311 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000312 {
313 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000314 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000315
316 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000317 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000318
cristybb503372010-05-27 20:51:26 +0000319 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000320 x;
321
322 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000323 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000324
325 /*
326 Tile fill color onto floodplane.
327 */
328 p=GetVirtualPixels(floodplane_image,0,y,image->columns,1,exception);
329 q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
330 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
331 break;
332 indexes=GetAuthenticIndexQueue(image);
cristybb503372010-05-27 20:51:26 +0000333 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000334 {
335 if (p->opacity != OpaqueOpacity)
336 {
337 (void) GetFillColor(draw_info,x,y,&fill_color);
338 SetMagickPixelPacket(image,&fill_color,(IndexPacket *) NULL,&fill);
339 if (image->colorspace == CMYKColorspace)
340 ConvertRGBToCMYK(&fill);
341 if ((channel & RedChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000342 q->red=ClampToQuantum(fill.red);
cristy3ed852e2009-09-05 21:47:34 +0000343 if ((channel & GreenChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000344 q->green=ClampToQuantum(fill.green);
cristy3ed852e2009-09-05 21:47:34 +0000345 if ((channel & BlueChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000346 q->blue=ClampToQuantum(fill.blue);
cristy3ed852e2009-09-05 21:47:34 +0000347 if ((channel & OpacityChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000348 q->opacity=ClampToQuantum(fill.opacity);
cristy3ed852e2009-09-05 21:47:34 +0000349 if (((channel & IndexChannel) != 0) &&
350 (image->colorspace == CMYKColorspace))
cristyce70c172010-01-07 17:15:30 +0000351 indexes[x]=ClampToQuantum(fill.index);
cristy3ed852e2009-09-05 21:47:34 +0000352 }
353 p++;
354 q++;
355 }
356 if (SyncAuthenticPixels(image,exception) == MagickFalse)
357 break;
358 }
359 segment_stack=(SegmentInfo *) RelinquishMagickMemory(segment_stack);
360 floodplane_image=DestroyImage(floodplane_image);
cristybb503372010-05-27 20:51:26 +0000361 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000362}
363
364/*
365%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
366% %
367% %
368% %
369+ G r a d i e n t I m a g e %
370% %
371% %
372% %
373%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
374%
cristycee97112010-05-28 00:44:52 +0000375% GradientImage() applies a continuously smooth color transitions along a
cristy3ed852e2009-09-05 21:47:34 +0000376% vector from one color to another.
377%
378% Note, the interface of this method will change in the future to support
379% more than one transistion.
380%
381% The format of the GradientImage method is:
382%
383% MagickBooleanType GradientImage(Image *image,const GradientType type,
384% const SpreadMethod method,const PixelPacket *start_color,
385% const PixelPacket *stop_color)
386%
387% A description of each parameter follows:
388%
389% o image: the image.
390%
391% o type: the gradient type: linear or radial.
392%
393% o spread: the gradient spread meathod: pad, reflect, or repeat.
394%
395% o start_color: the start color.
396%
397% o stop_color: the stop color.
398%
399% This provides a good example of making use of the DrawGradientImage
400% function and the gradient structure in draw_info.
401*/
cristy117ff172010-08-15 21:35:32 +0000402
403static inline double MagickMax(const double x,const double y)
404{
405 return(x > y ? x : y);
406}
407
cristy3ed852e2009-09-05 21:47:34 +0000408MagickExport MagickBooleanType GradientImage(Image *image,
409 const GradientType type,const SpreadMethod method,
410 const PixelPacket *start_color,const PixelPacket *stop_color)
411{
412 DrawInfo
413 *draw_info;
414
415 GradientInfo
416 *gradient;
417
418 MagickBooleanType
419 status;
420
cristybb503372010-05-27 20:51:26 +0000421 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000422 i;
423
424 /*
425 Set gradient start-stop end points.
426 */
427 assert(image != (const Image *) NULL);
428 assert(image->signature == MagickSignature);
429 if (image->debug != MagickFalse)
430 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
431 assert(start_color != (const PixelPacket *) NULL);
432 assert(stop_color != (const PixelPacket *) NULL);
433 draw_info=AcquireDrawInfo();
434 gradient=(&draw_info->gradient);
435 gradient->type=type;
436 gradient->bounding_box.width=image->columns;
437 gradient->bounding_box.height=image->rows;
438 gradient->gradient_vector.x2=(double) image->columns-1.0;
439 gradient->gradient_vector.y2=(double) image->rows-1.0;
440 if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
441 gradient->gradient_vector.x2=0.0;
442 gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
443 gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
444 gradient->radius=MagickMax(gradient->center.x,gradient->center.y);
445 gradient->spread=method;
446 /*
447 Define the gradient to fill between the stops.
448 */
449 gradient->number_stops=2;
450 gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
451 sizeof(*gradient->stops));
452 if (gradient->stops == (StopInfo *) NULL)
453 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
454 image->filename);
455 (void) ResetMagickMemory(gradient->stops,0,gradient->number_stops*
456 sizeof(*gradient->stops));
cristybb503372010-05-27 20:51:26 +0000457 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +0000458 GetMagickPixelPacket(image,&gradient->stops[i].color);
459 SetMagickPixelPacket(image,start_color,(IndexPacket *) NULL,
460 &gradient->stops[0].color);
461 gradient->stops[0].offset=0.0;
462 SetMagickPixelPacket(image,stop_color,(IndexPacket *) NULL,
463 &gradient->stops[1].color);
464 gradient->stops[1].offset=1.0;
465 /*
466 Draw a gradient on the image.
467 */
468 status=DrawGradientImage(image,draw_info);
469 draw_info=DestroyDrawInfo(draw_info);
470 if ((start_color->opacity == OpaqueOpacity) &&
471 (stop_color->opacity == OpaqueOpacity))
472 image->matte=MagickFalse;
473 if ((IsGrayPixel(start_color) != MagickFalse) &&
474 (IsGrayPixel(stop_color) != MagickFalse))
475 image->type=GrayscaleType;
476 return(status);
477}
478
479/*
480%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
481% %
482% %
483% %
484% O i l P a i n t I m a g e %
485% %
486% %
487% %
488%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
489%
490% OilPaintImage() applies a special effect filter that simulates an oil
491% painting. Each pixel is replaced by the most frequent color occurring
492% in a circular region defined by radius.
493%
494% The format of the OilPaintImage method is:
495%
496% Image *OilPaintImage(const Image *image,const double radius,
497% ExceptionInfo *exception)
498%
499% A description of each parameter follows:
500%
501% o image: the image.
502%
503% o radius: the radius of the circular neighborhood.
504%
505% o exception: return any errors or warnings in this structure.
506%
507*/
508
cristybb503372010-05-27 20:51:26 +0000509static size_t **DestroyHistogramThreadSet(size_t **histogram)
cristy3ed852e2009-09-05 21:47:34 +0000510{
cristybb503372010-05-27 20:51:26 +0000511 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000512 i;
513
cristybb503372010-05-27 20:51:26 +0000514 assert(histogram != (size_t **) NULL);
515 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
516 if (histogram[i] != (size_t *) NULL)
517 histogram[i]=(size_t *) RelinquishMagickMemory(histogram[i]);
518 histogram=(size_t **) RelinquishAlignedMemory(histogram);
cristy3ed852e2009-09-05 21:47:34 +0000519 return(histogram);
520}
521
cristybb503372010-05-27 20:51:26 +0000522static size_t **AcquireHistogramThreadSet(const size_t count)
cristy3ed852e2009-09-05 21:47:34 +0000523{
cristybb503372010-05-27 20:51:26 +0000524 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000525 i;
526
cristybb503372010-05-27 20:51:26 +0000527 size_t
cristy3ed852e2009-09-05 21:47:34 +0000528 **histogram,
529 number_threads;
530
531 number_threads=GetOpenMPMaximumThreads();
cristybb503372010-05-27 20:51:26 +0000532 histogram=(size_t **) AcquireAlignedMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +0000533 sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000534 if (histogram == (size_t **) NULL)
535 return((size_t **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000536 (void) ResetMagickMemory(histogram,0,number_threads*sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000537 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +0000538 {
cristybb503372010-05-27 20:51:26 +0000539 histogram[i]=(size_t *) AcquireQuantumMemory(count,
cristy3ed852e2009-09-05 21:47:34 +0000540 sizeof(**histogram));
cristybb503372010-05-27 20:51:26 +0000541 if (histogram[i] == (size_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000542 return(DestroyHistogramThreadSet(histogram));
543 }
544 return(histogram);
545}
546
547MagickExport Image *OilPaintImage(const Image *image,const double radius,
548 ExceptionInfo *exception)
549{
550#define NumberPaintBins 256
551#define OilPaintImageTag "OilPaint/Image"
552
cristyfa112112010-01-04 17:48:07 +0000553 CacheView
554 *image_view,
555 *paint_view;
556
cristy3ed852e2009-09-05 21:47:34 +0000557 Image
558 *paint_image;
559
cristy3ed852e2009-09-05 21:47:34 +0000560 MagickBooleanType
561 status;
562
cristybb503372010-05-27 20:51:26 +0000563 MagickOffsetType
564 progress;
565
566 size_t
cristyfa112112010-01-04 17:48:07 +0000567 **restrict histograms,
cristy3ed852e2009-09-05 21:47:34 +0000568 width;
569
cristybb503372010-05-27 20:51:26 +0000570 ssize_t
571 y;
572
cristy3ed852e2009-09-05 21:47:34 +0000573 /*
574 Initialize painted image attributes.
575 */
576 assert(image != (const Image *) NULL);
577 assert(image->signature == MagickSignature);
578 if (image->debug != MagickFalse)
579 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
580 assert(exception != (ExceptionInfo *) NULL);
581 assert(exception->signature == MagickSignature);
582 width=GetOptimalKernelWidth2D(radius,0.5);
583 if ((image->columns < width) || (image->rows < width))
584 ThrowImageException(OptionError,"ImageSmallerThanRadius");
585 paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
586 if (paint_image == (Image *) NULL)
587 return((Image *) NULL);
588 if (SetImageStorageClass(paint_image,DirectClass) == MagickFalse)
589 {
590 InheritException(exception,&paint_image->exception);
591 paint_image=DestroyImage(paint_image);
592 return((Image *) NULL);
593 }
594 histograms=AcquireHistogramThreadSet(NumberPaintBins);
cristybb503372010-05-27 20:51:26 +0000595 if (histograms == (size_t **) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000596 {
597 paint_image=DestroyImage(paint_image);
598 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
599 }
600 /*
601 Oil paint image.
602 */
603 status=MagickTrue;
604 progress=0;
605 image_view=AcquireCacheView(image);
606 paint_view=AcquireCacheView(paint_image);
cristyb5d5f722009-11-04 03:03:49 +0000607#if defined(MAGICKCORE_OPENMP_SUPPORT)
608 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000609#endif
cristybb503372010-05-27 20:51:26 +0000610 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000611 {
612 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000613 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000614
615 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000616 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000617
618 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000619 *restrict paint_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000620
cristybb503372010-05-27 20:51:26 +0000621 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000622 x;
623
624 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000625 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000626
cristybb503372010-05-27 20:51:26 +0000627 register size_t
cristy3ed852e2009-09-05 21:47:34 +0000628 *histogram;
629
630 if (status == MagickFalse)
631 continue;
cristybb503372010-05-27 20:51:26 +0000632 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t) (width/
cristy3ed852e2009-09-05 21:47:34 +0000633 2L),image->columns+width,width,exception);
634 q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
635 exception);
636 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
637 {
638 status=MagickFalse;
639 continue;
640 }
641 indexes=GetCacheViewVirtualIndexQueue(image_view);
642 paint_indexes=GetCacheViewAuthenticIndexQueue(paint_view);
643 histogram=histograms[GetOpenMPThreadId()];
cristybb503372010-05-27 20:51:26 +0000644 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000645 {
cristybb503372010-05-27 20:51:26 +0000646 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000647 j,
648 k,
649 v;
650
cristybb503372010-05-27 20:51:26 +0000651 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000652 i,
653 u;
654
cristybb503372010-05-27 20:51:26 +0000655 size_t
cristy3ed852e2009-09-05 21:47:34 +0000656 count;
657
658 /*
659 Assign most frequent color.
660 */
661 i=0;
662 j=0;
663 count=0;
664 (void) ResetMagickMemory(histogram,0,NumberPaintBins*sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000665 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +0000666 {
cristybb503372010-05-27 20:51:26 +0000667 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +0000668 {
cristybb503372010-05-27 20:51:26 +0000669 k=(ssize_t) ScaleQuantumToChar(PixelIntensityToQuantum(p+u+i));
cristy3ed852e2009-09-05 21:47:34 +0000670 histogram[k]++;
671 if (histogram[k] > count)
672 {
673 j=i+u;
674 count=histogram[k];
675 }
676 }
cristyd99b0962010-05-29 23:14:26 +0000677 i+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +0000678 }
679 *q=(*(p+j));
680 if (image->colorspace == CMYKColorspace)
681 paint_indexes[x]=indexes[x+j];
682 p++;
683 q++;
684 }
685 if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
686 status=MagickFalse;
687 if (image->progress_monitor != (MagickProgressMonitor) NULL)
688 {
689 MagickBooleanType
690 proceed;
691
cristyb5d5f722009-11-04 03:03:49 +0000692#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000693 #pragma omp critical (MagickCore_OilPaintImage)
694#endif
695 proceed=SetImageProgress(image,OilPaintImageTag,progress++,image->rows);
696 if (proceed == MagickFalse)
697 status=MagickFalse;
698 }
699 }
700 paint_view=DestroyCacheView(paint_view);
701 image_view=DestroyCacheView(image_view);
702 histograms=DestroyHistogramThreadSet(histograms);
703 if (status == MagickFalse)
704 paint_image=DestroyImage(paint_image);
705 return(paint_image);
706}
707
708/*
709%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
710% %
711% %
712% %
713% O p a q u e P a i n t I m a g e %
714% %
715% %
716% %
717%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
718%
719% OpaquePaintImage() changes any pixel that matches color with the color
720% defined by fill.
721%
722% By default color must match a particular pixel color exactly. However,
723% in many cases two colors may differ by a small amount. Fuzz defines
724% how much tolerance is acceptable to consider two colors as the same.
725% For example, set fuzz to 10 and the color red at intensities of 100 and
726% 102 respectively are now interpreted as the same color.
727%
728% The format of the OpaquePaintImage method is:
729%
730% MagickBooleanType OpaquePaintImage(Image *image,
731% const PixelPacket *target,const PixelPacket *fill,
732% const MagickBooleanType invert)
733% MagickBooleanType OpaquePaintImageChannel(Image *image,
734% const ChannelType channel,const PixelPacket *target,
735% const PixelPacket *fill,const MagickBooleanType invert)
736%
737% A description of each parameter follows:
738%
739% o image: the image.
740%
741% o channel: the channel(s).
742%
743% o target: the RGB value of the target color.
744%
745% o fill: the replacement color.
746%
747% o invert: paint any pixel that does not match the target color.
748%
749*/
750
751MagickExport MagickBooleanType OpaquePaintImage(Image *image,
752 const MagickPixelPacket *target,const MagickPixelPacket *fill,
753 const MagickBooleanType invert)
754{
755 return(OpaquePaintImageChannel(image,AllChannels,target,fill,invert));
756}
757
758MagickExport MagickBooleanType OpaquePaintImageChannel(Image *image,
759 const ChannelType channel,const MagickPixelPacket *target,
760 const MagickPixelPacket *fill,const MagickBooleanType invert)
761{
762#define OpaquePaintImageTag "Opaque/Image"
763
cristyc4c8d132010-01-07 01:58:38 +0000764 CacheView
765 *image_view;
766
cristy3ed852e2009-09-05 21:47:34 +0000767 ExceptionInfo
768 *exception;
769
cristy3ed852e2009-09-05 21:47:34 +0000770 MagickBooleanType
771 status;
772
cristybb503372010-05-27 20:51:26 +0000773 MagickOffsetType
774 progress;
775
cristy3ed852e2009-09-05 21:47:34 +0000776 MagickPixelPacket
777 zero;
778
cristybb503372010-05-27 20:51:26 +0000779 ssize_t
780 y;
781
cristy3ed852e2009-09-05 21:47:34 +0000782 assert(image != (Image *) NULL);
783 assert(image->signature == MagickSignature);
784 assert(target != (MagickPixelPacket *) NULL);
785 assert(fill != (MagickPixelPacket *) NULL);
786 if (image->debug != MagickFalse)
787 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
788 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
789 return(MagickFalse);
790 /*
791 Make image color opaque.
792 */
793 status=MagickTrue;
794 progress=0;
795 exception=(&image->exception);
796 GetMagickPixelPacket(image,&zero);
797 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000798#if defined(MAGICKCORE_OPENMP_SUPPORT)
799 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000800#endif
cristybb503372010-05-27 20:51:26 +0000801 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000802 {
803 MagickPixelPacket
804 pixel;
805
806 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000807 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000808
cristybb503372010-05-27 20:51:26 +0000809 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000810 x;
811
812 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000813 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000814
815 if (status == MagickFalse)
816 continue;
817 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
818 if (q == (PixelPacket *) NULL)
819 {
820 status=MagickFalse;
821 continue;
822 }
823 indexes=GetCacheViewAuthenticIndexQueue(image_view);
824 pixel=zero;
cristybb503372010-05-27 20:51:26 +0000825 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000826 {
827 SetMagickPixelPacket(image,q,indexes+x,&pixel);
828 if (IsMagickColorSimilar(&pixel,target) != invert)
829 {
830 if ((channel & RedChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000831 q->red=ClampToQuantum(fill->red);
cristy3ed852e2009-09-05 21:47:34 +0000832 if ((channel & GreenChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000833 q->green=ClampToQuantum(fill->green);
cristy3ed852e2009-09-05 21:47:34 +0000834 if ((channel & BlueChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000835 q->blue=ClampToQuantum(fill->blue);
cristy3ed852e2009-09-05 21:47:34 +0000836 if ((channel & OpacityChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000837 q->opacity=ClampToQuantum(fill->opacity);
cristy3ed852e2009-09-05 21:47:34 +0000838 if (((channel & IndexChannel) != 0) &&
839 (image->colorspace == CMYKColorspace))
cristyce70c172010-01-07 17:15:30 +0000840 indexes[x]=ClampToQuantum(fill->index);
cristy3ed852e2009-09-05 21:47:34 +0000841 }
842 q++;
843 }
844 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
845 status=MagickFalse;
846 if (image->progress_monitor != (MagickProgressMonitor) NULL)
847 {
848 MagickBooleanType
849 proceed;
850
cristyb5d5f722009-11-04 03:03:49 +0000851#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000852 #pragma omp critical (MagickCore_OpaquePaintImageChannel)
853#endif
854 proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
855 image->rows);
856 if (proceed == MagickFalse)
857 status=MagickFalse;
858 }
859 }
860 image_view=DestroyCacheView(image_view);
861 return(status);
862}
863
864/*
865%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
866% %
867% %
868% %
869% T r a n s p a r e n t P a i n t I m a g e %
870% %
871% %
872% %
873%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
874%
875% TransparentPaintImage() changes the opacity value associated with any pixel
876% that matches color to the value defined by opacity.
877%
878% By default color must match a particular pixel color exactly. However,
879% in many cases two colors may differ by a small amount. Fuzz defines
880% how much tolerance is acceptable to consider two colors as the same.
881% For example, set fuzz to 10 and the color red at intensities of 100 and
882% 102 respectively are now interpreted as the same color.
883%
884% The format of the TransparentPaintImage method is:
885%
886% MagickBooleanType TransparentPaintImage(Image *image,
887% const MagickPixelPacket *target,const Quantum opacity,
888% const MagickBooleanType invert)
889%
890% A description of each parameter follows:
891%
892% o image: the image.
893%
894% o target: the target color.
895%
896% o opacity: the replacement opacity value.
897%
898% o invert: paint any pixel that does not match the target color.
899%
900*/
901MagickExport MagickBooleanType TransparentPaintImage(Image *image,
902 const MagickPixelPacket *target,const Quantum opacity,
903 const MagickBooleanType invert)
904{
905#define TransparentPaintImageTag "Transparent/Image"
906
cristyc4c8d132010-01-07 01:58:38 +0000907 CacheView
908 *image_view;
909
cristy3ed852e2009-09-05 21:47:34 +0000910 ExceptionInfo
911 *exception;
912
cristy3ed852e2009-09-05 21:47:34 +0000913 MagickBooleanType
914 status;
915
cristybb503372010-05-27 20:51:26 +0000916 MagickOffsetType
917 progress;
918
cristy3ed852e2009-09-05 21:47:34 +0000919 MagickPixelPacket
920 zero;
921
cristybb503372010-05-27 20:51:26 +0000922 ssize_t
923 y;
924
cristy3ed852e2009-09-05 21:47:34 +0000925 assert(image != (Image *) NULL);
926 assert(image->signature == MagickSignature);
927 assert(target != (MagickPixelPacket *) NULL);
928 if (image->debug != MagickFalse)
929 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
930 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
931 return(MagickFalse);
932 if (image->matte == MagickFalse)
933 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
934 /*
935 Make image color transparent.
936 */
937 status=MagickTrue;
938 progress=0;
939 exception=(&image->exception);
940 GetMagickPixelPacket(image,&zero);
941 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000942#if defined(MAGICKCORE_OPENMP_SUPPORT)
943 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000944#endif
cristybb503372010-05-27 20:51:26 +0000945 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000946 {
947 MagickPixelPacket
948 pixel;
949
950 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000951 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000952
cristybb503372010-05-27 20:51:26 +0000953 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000954 x;
955
956 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000957 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000958
959 if (status == MagickFalse)
960 continue;
961 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
962 if (q == (PixelPacket *) NULL)
963 {
964 status=MagickFalse;
965 continue;
966 }
967 indexes=GetCacheViewAuthenticIndexQueue(image_view);
968 pixel=zero;
cristybb503372010-05-27 20:51:26 +0000969 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000970 {
971 SetMagickPixelPacket(image,q,indexes+x,&pixel);
972 if (IsMagickColorSimilar(&pixel,target) != invert)
973 q->opacity=opacity;
974 q++;
975 }
976 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
977 status=MagickFalse;
978 if (image->progress_monitor != (MagickProgressMonitor) NULL)
979 {
980 MagickBooleanType
981 proceed;
982
cristyb5d5f722009-11-04 03:03:49 +0000983#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000984 #pragma omp critical (MagickCore_TransparentPaintImage)
985#endif
986 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
987 image->rows);
988 if (proceed == MagickFalse)
989 status=MagickFalse;
990 }
991 }
992 image_view=DestroyCacheView(image_view);
993 return(status);
994}
995
996/*
997%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
998% %
999% %
1000% %
1001% T r a n s p a r e n t P a i n t I m a g e C h r o m a %
1002% %
1003% %
1004% %
1005%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1006%
1007% TransparentPaintImageChroma() changes the opacity value associated with any
1008% pixel that matches color to the value defined by opacity.
1009%
1010% As there is one fuzz value for the all the channels, the
1011% TransparentPaintImage() API is not suitable for the operations like chroma,
1012% where the tolerance for similarity of two color component (RGB) can be
1013% different, Thus we define this method take two target pixels (one
1014% low and one hight) and all the pixels of an image which are lying between
1015% these two pixels are made transparent.
1016%
1017% The format of the TransparentPaintImage method is:
1018%
1019% MagickBooleanType TransparentPaintImage(Image *image,
1020% const MagickPixelPacket *low,const MagickPixelPacket *hight,
1021% const Quantum opacity,const MagickBooleanType invert)
1022%
1023% A description of each parameter follows:
1024%
1025% o image: the image.
1026%
1027% o low: the low target color.
1028%
1029% o high: the high target color.
1030%
1031% o opacity: the replacement opacity value.
1032%
1033% o invert: paint any pixel that does not match the target color.
1034%
1035*/
1036MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1037 const MagickPixelPacket *low,const MagickPixelPacket *high,
1038 const Quantum opacity,const MagickBooleanType invert)
1039{
1040#define TransparentPaintImageTag "Transparent/Image"
1041
cristyc4c8d132010-01-07 01:58:38 +00001042 CacheView
1043 *image_view;
1044
cristy3ed852e2009-09-05 21:47:34 +00001045 ExceptionInfo
1046 *exception;
1047
cristy3ed852e2009-09-05 21:47:34 +00001048 MagickBooleanType
1049 status;
1050
cristybb503372010-05-27 20:51:26 +00001051 MagickOffsetType
1052 progress;
1053
1054 ssize_t
1055 y;
1056
cristy3ed852e2009-09-05 21:47:34 +00001057 assert(image != (Image *) NULL);
1058 assert(image->signature == MagickSignature);
1059 assert(high != (MagickPixelPacket *) NULL);
1060 assert(low != (MagickPixelPacket *) NULL);
1061 if (image->debug != MagickFalse)
1062 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1063 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1064 return(MagickFalse);
1065 if (image->matte == MagickFalse)
1066 (void) SetImageAlphaChannel(image,ResetAlphaChannel);
1067 /*
1068 Make image color transparent.
1069 */
1070 status=MagickTrue;
1071 progress=0;
1072 exception=(&image->exception);
1073 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001074#if defined(MAGICKCORE_OPENMP_SUPPORT)
1075 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001076#endif
cristybb503372010-05-27 20:51:26 +00001077 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001078 {
1079 MagickBooleanType
1080 match;
1081
1082 MagickPixelPacket
1083 pixel;
1084
1085 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001086 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001087
cristybb503372010-05-27 20:51:26 +00001088 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001089 x;
1090
1091 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001092 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001093
1094 if (status == MagickFalse)
1095 continue;
1096 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1097 if (q == (PixelPacket *) NULL)
1098 {
1099 status=MagickFalse;
1100 continue;
1101 }
1102 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1103 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001104 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001105 {
1106 SetMagickPixelPacket(image,q,indexes+x,&pixel);
1107 match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1108 (pixel.green >= low->green) && (pixel.green <= high->green) &&
1109 (pixel.blue >= low->blue) && (pixel.blue <= high->blue)) ?
1110 MagickTrue : MagickFalse;
1111 if (match != invert)
1112 q->opacity=opacity;
1113 q++;
1114 }
1115 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1116 status=MagickFalse;
1117 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1118 {
1119 MagickBooleanType
1120 proceed;
1121
cristyb5d5f722009-11-04 03:03:49 +00001122#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001123 #pragma omp critical (MagickCore_TransparentPaintImageChroma)
1124#endif
1125 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1126 image->rows);
1127 if (proceed == MagickFalse)
1128 status=MagickFalse;
1129 }
1130 }
1131 image_view=DestroyCacheView(image_view);
1132 return(status);
1133}