blob: e8a37d592e34d5a6b87cf4cf3d72f732dbb06a27 [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% %
cristy7e41fe82010-12-04 23:12:08 +000020% Copyright 1999-2011 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,
cristy178edaf2011-01-23 03:42:39 +000087% const MagickPixelPacket target,const ssize_t x_offset,
88% const ssize_t y_offset,const MagickBooleanType invert)
cristy3ed852e2009-09-05 21:47:34 +000089%
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
cristyb0d3bb92010-09-22 14:37:58 +0000128 CacheView
129 *floodplane_view,
130 *image_view;
131
cristy3ed852e2009-09-05 21:47:34 +0000132 ExceptionInfo
133 *exception;
134
135 Image
136 *floodplane_image;
137
cristybb503372010-05-27 20:51:26 +0000138 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000139 offset,
140 start,
141 x,
142 x1,
143 x2,
144 y;
145
146 MagickBooleanType
147 skip;
148
149 MagickPixelPacket
150 fill,
151 pixel;
152
153 PixelPacket
154 fill_color;
155
156 register SegmentInfo
157 *s;
158
159 SegmentInfo
160 *segment_stack;
161
162 /*
163 Check boundary conditions.
164 */
165 assert(image != (Image *) NULL);
166 assert(image->signature == MagickSignature);
167 if (image->debug != MagickFalse)
168 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
169 assert(draw_info != (DrawInfo *) NULL);
170 assert(draw_info->signature == MagickSignature);
cristybb503372010-05-27 20:51:26 +0000171 if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
cristy3ed852e2009-09-05 21:47:34 +0000172 return(MagickFalse);
cristybb503372010-05-27 20:51:26 +0000173 if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
cristy3ed852e2009-09-05 21:47:34 +0000174 return(MagickFalse);
175 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
176 return(MagickFalse);
177 if (image->matte == MagickFalse)
178 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
179 /*
180 Set floodfill state.
181 */
182 floodplane_image=CloneImage(image,0,0,MagickTrue,&image->exception);
183 if (floodplane_image == (Image *) NULL)
184 return(MagickFalse);
185 (void) SetImageAlphaChannel(floodplane_image,OpaqueAlphaChannel);
186 segment_stack=(SegmentInfo *) AcquireQuantumMemory(MaxStacksize,
187 sizeof(*segment_stack));
188 if (segment_stack == (SegmentInfo *) NULL)
189 {
190 floodplane_image=DestroyImage(floodplane_image);
191 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
192 image->filename);
193 }
194 /*
195 Push initial segment on stack.
196 */
197 exception=(&image->exception);
198 x=x_offset;
199 y=y_offset;
200 start=0;
201 s=segment_stack;
202 PushSegmentStack(y,x,x,1);
203 PushSegmentStack(y+1,x,x,-1);
204 GetMagickPixelPacket(image,&fill);
205 GetMagickPixelPacket(image,&pixel);
cristyb0d3bb92010-09-22 14:37:58 +0000206 image_view=AcquireCacheView(image);
207 floodplane_view=AcquireCacheView(floodplane_image);
cristy3ed852e2009-09-05 21:47:34 +0000208 while (s > segment_stack)
209 {
210 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000211 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000212
213 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000214 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000215
cristybb503372010-05-27 20:51:26 +0000216 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000217 x;
218
219 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000220 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000221
222 /*
223 Pop segment off stack.
224 */
225 s--;
cristybb503372010-05-27 20:51:26 +0000226 x1=(ssize_t) s->x1;
227 x2=(ssize_t) s->x2;
228 offset=(ssize_t) s->y2;
229 y=(ssize_t) s->y1+offset;
cristy3ed852e2009-09-05 21:47:34 +0000230 /*
231 Recolor neighboring pixels.
232 */
cristyb0d3bb92010-09-22 14:37:58 +0000233 p=GetCacheViewVirtualPixels(image_view,0,y,(size_t) (x1+1),1,exception);
234 q=GetCacheViewAuthenticPixels(floodplane_view,0,y,(size_t) (x1+1),1,
cristy3ed852e2009-09-05 21:47:34 +0000235 exception);
236 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
237 break;
cristyb0d3bb92010-09-22 14:37:58 +0000238 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy3ed852e2009-09-05 21:47:34 +0000239 p+=x1;
240 q+=x1;
241 for (x=x1; x >= 0; x--)
242 {
243 if (q->opacity == (Quantum) TransparentOpacity)
244 break;
245 SetMagickPixelPacket(image,p,indexes+x,&pixel);
246 if (IsMagickColorSimilar(&pixel,target) == invert)
247 break;
248 q->opacity=(Quantum) TransparentOpacity;
249 p--;
250 q--;
251 }
cristyb0d3bb92010-09-22 14:37:58 +0000252 if (SyncCacheViewAuthenticPixels(floodplane_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000253 break;
254 skip=x >= x1 ? MagickTrue : MagickFalse;
255 if (skip == MagickFalse)
256 {
257 start=x+1;
258 if (start < x1)
259 PushSegmentStack(y,start,x1-1,-offset);
260 x=x1+1;
261 }
262 do
263 {
264 if (skip == MagickFalse)
265 {
cristybb503372010-05-27 20:51:26 +0000266 if (x < (ssize_t) image->columns)
cristy3ed852e2009-09-05 21:47:34 +0000267 {
cristyb0d3bb92010-09-22 14:37:58 +0000268 p=GetCacheViewVirtualPixels(image_view,x,y,image->columns-x,1,
cristy3ed852e2009-09-05 21:47:34 +0000269 exception);
cristyb0d3bb92010-09-22 14:37:58 +0000270 q=GetCacheViewAuthenticPixels(floodplane_view,x,y,
271 image->columns-x,1,exception);
cristy3ed852e2009-09-05 21:47:34 +0000272 if ((p == (const PixelPacket *) NULL) ||
273 (q == (PixelPacket *) NULL))
274 break;
cristyb0d3bb92010-09-22 14:37:58 +0000275 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000276 for ( ; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000277 {
278 if (q->opacity == (Quantum) TransparentOpacity)
279 break;
280 SetMagickPixelPacket(image,p,indexes+x,&pixel);
281 if (IsMagickColorSimilar(&pixel,target) == invert)
282 break;
283 q->opacity=(Quantum) TransparentOpacity;
284 p++;
285 q++;
286 }
cristyb0d3bb92010-09-22 14:37:58 +0000287 if (SyncCacheViewAuthenticPixels(floodplane_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000288 break;
289 }
290 PushSegmentStack(y,start,x-1,offset);
291 if (x > (x2+1))
292 PushSegmentStack(y,x2+1,x-1,-offset);
293 }
294 skip=MagickFalse;
295 x++;
296 if (x <= x2)
297 {
cristyb0d3bb92010-09-22 14:37:58 +0000298 p=GetCacheViewVirtualPixels(image_view,x,y,(size_t) (x2-x+1),1,
299 exception);
300 q=GetCacheViewAuthenticPixels(floodplane_view,x,y,(size_t) (x2-x+1),1,
cristy3ed852e2009-09-05 21:47:34 +0000301 exception);
302 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
303 break;
cristyb0d3bb92010-09-22 14:37:58 +0000304 indexes=GetCacheViewVirtualIndexQueue(image_view);
cristy3ed852e2009-09-05 21:47:34 +0000305 for ( ; x <= x2; x++)
306 {
307 if (q->opacity == (Quantum) TransparentOpacity)
308 break;
309 SetMagickPixelPacket(image,p,indexes+x,&pixel);
310 if (IsMagickColorSimilar(&pixel,target) != invert)
311 break;
312 p++;
313 q++;
314 }
315 }
316 start=x;
317 } while (x <= x2);
318 }
cristybb503372010-05-27 20:51:26 +0000319 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000320 {
321 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000322 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000323
324 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000325 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000326
cristybb503372010-05-27 20:51:26 +0000327 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000328 x;
329
330 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000331 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000332
333 /*
334 Tile fill color onto floodplane.
335 */
cristyb0d3bb92010-09-22 14:37:58 +0000336 p=GetCacheViewVirtualPixels(floodplane_view,0,y,image->columns,1,
337 exception);
338 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
cristy3ed852e2009-09-05 21:47:34 +0000339 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
340 break;
cristy6c1892e2010-09-23 01:59:28 +0000341 indexes=GetCacheViewAuthenticIndexQueue(image_view);
cristybb503372010-05-27 20:51:26 +0000342 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000343 {
344 if (p->opacity != OpaqueOpacity)
345 {
346 (void) GetFillColor(draw_info,x,y,&fill_color);
347 SetMagickPixelPacket(image,&fill_color,(IndexPacket *) NULL,&fill);
348 if (image->colorspace == CMYKColorspace)
349 ConvertRGBToCMYK(&fill);
350 if ((channel & RedChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000351 q->red=ClampToQuantum(fill.red);
cristy3ed852e2009-09-05 21:47:34 +0000352 if ((channel & GreenChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000353 q->green=ClampToQuantum(fill.green);
cristy3ed852e2009-09-05 21:47:34 +0000354 if ((channel & BlueChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000355 q->blue=ClampToQuantum(fill.blue);
cristy3ed852e2009-09-05 21:47:34 +0000356 if ((channel & OpacityChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000357 q->opacity=ClampToQuantum(fill.opacity);
cristy3ed852e2009-09-05 21:47:34 +0000358 if (((channel & IndexChannel) != 0) &&
359 (image->colorspace == CMYKColorspace))
cristyce70c172010-01-07 17:15:30 +0000360 indexes[x]=ClampToQuantum(fill.index);
cristy3ed852e2009-09-05 21:47:34 +0000361 }
362 p++;
363 q++;
364 }
cristyb0d3bb92010-09-22 14:37:58 +0000365 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000366 break;
367 }
cristyb0d3bb92010-09-22 14:37:58 +0000368 floodplane_view=DestroyCacheView(floodplane_view);
369 image_view=DestroyCacheView(image_view);
cristy3ed852e2009-09-05 21:47:34 +0000370 segment_stack=(SegmentInfo *) RelinquishMagickMemory(segment_stack);
371 floodplane_image=DestroyImage(floodplane_image);
cristybb503372010-05-27 20:51:26 +0000372 return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000373}
374
375/*
376%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
377% %
378% %
379% %
380+ G r a d i e n t I m a g e %
381% %
382% %
383% %
384%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
385%
cristycee97112010-05-28 00:44:52 +0000386% GradientImage() applies a continuously smooth color transitions along a
cristy3ed852e2009-09-05 21:47:34 +0000387% vector from one color to another.
388%
389% Note, the interface of this method will change in the future to support
390% more than one transistion.
391%
392% The format of the GradientImage method is:
393%
394% MagickBooleanType GradientImage(Image *image,const GradientType type,
395% const SpreadMethod method,const PixelPacket *start_color,
396% const PixelPacket *stop_color)
397%
398% A description of each parameter follows:
399%
400% o image: the image.
401%
402% o type: the gradient type: linear or radial.
403%
404% o spread: the gradient spread meathod: pad, reflect, or repeat.
405%
406% o start_color: the start color.
407%
408% o stop_color: the stop color.
409%
410% This provides a good example of making use of the DrawGradientImage
411% function and the gradient structure in draw_info.
412*/
cristy117ff172010-08-15 21:35:32 +0000413
414static inline double MagickMax(const double x,const double y)
415{
416 return(x > y ? x : y);
417}
418
cristy3ed852e2009-09-05 21:47:34 +0000419MagickExport MagickBooleanType GradientImage(Image *image,
420 const GradientType type,const SpreadMethod method,
421 const PixelPacket *start_color,const PixelPacket *stop_color)
422{
423 DrawInfo
424 *draw_info;
425
426 GradientInfo
427 *gradient;
428
429 MagickBooleanType
430 status;
431
cristybb503372010-05-27 20:51:26 +0000432 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000433 i;
434
435 /*
436 Set gradient start-stop end points.
437 */
438 assert(image != (const Image *) NULL);
439 assert(image->signature == MagickSignature);
440 if (image->debug != MagickFalse)
441 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
442 assert(start_color != (const PixelPacket *) NULL);
443 assert(stop_color != (const PixelPacket *) NULL);
444 draw_info=AcquireDrawInfo();
445 gradient=(&draw_info->gradient);
446 gradient->type=type;
447 gradient->bounding_box.width=image->columns;
448 gradient->bounding_box.height=image->rows;
449 gradient->gradient_vector.x2=(double) image->columns-1.0;
450 gradient->gradient_vector.y2=(double) image->rows-1.0;
451 if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
452 gradient->gradient_vector.x2=0.0;
453 gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
454 gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
455 gradient->radius=MagickMax(gradient->center.x,gradient->center.y);
456 gradient->spread=method;
457 /*
458 Define the gradient to fill between the stops.
459 */
460 gradient->number_stops=2;
461 gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
462 sizeof(*gradient->stops));
463 if (gradient->stops == (StopInfo *) NULL)
464 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
465 image->filename);
466 (void) ResetMagickMemory(gradient->stops,0,gradient->number_stops*
467 sizeof(*gradient->stops));
cristybb503372010-05-27 20:51:26 +0000468 for (i=0; i < (ssize_t) gradient->number_stops; i++)
cristy3ed852e2009-09-05 21:47:34 +0000469 GetMagickPixelPacket(image,&gradient->stops[i].color);
470 SetMagickPixelPacket(image,start_color,(IndexPacket *) NULL,
471 &gradient->stops[0].color);
472 gradient->stops[0].offset=0.0;
473 SetMagickPixelPacket(image,stop_color,(IndexPacket *) NULL,
474 &gradient->stops[1].color);
475 gradient->stops[1].offset=1.0;
476 /*
477 Draw a gradient on the image.
478 */
479 status=DrawGradientImage(image,draw_info);
480 draw_info=DestroyDrawInfo(draw_info);
481 if ((start_color->opacity == OpaqueOpacity) &&
482 (stop_color->opacity == OpaqueOpacity))
483 image->matte=MagickFalse;
484 if ((IsGrayPixel(start_color) != MagickFalse) &&
485 (IsGrayPixel(stop_color) != MagickFalse))
486 image->type=GrayscaleType;
487 return(status);
488}
489
490/*
491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
492% %
493% %
494% %
495% O i l P a i n t I m a g e %
496% %
497% %
498% %
499%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500%
501% OilPaintImage() applies a special effect filter that simulates an oil
502% painting. Each pixel is replaced by the most frequent color occurring
503% in a circular region defined by radius.
504%
505% The format of the OilPaintImage method is:
506%
507% Image *OilPaintImage(const Image *image,const double radius,
508% ExceptionInfo *exception)
509%
510% A description of each parameter follows:
511%
512% o image: the image.
513%
514% o radius: the radius of the circular neighborhood.
515%
516% o exception: return any errors or warnings in this structure.
517%
518*/
519
cristybb503372010-05-27 20:51:26 +0000520static size_t **DestroyHistogramThreadSet(size_t **histogram)
cristy3ed852e2009-09-05 21:47:34 +0000521{
cristybb503372010-05-27 20:51:26 +0000522 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000523 i;
524
cristybb503372010-05-27 20:51:26 +0000525 assert(histogram != (size_t **) NULL);
526 for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
527 if (histogram[i] != (size_t *) NULL)
528 histogram[i]=(size_t *) RelinquishMagickMemory(histogram[i]);
cristyb41ee102010-10-04 16:46:15 +0000529 histogram=(size_t **) RelinquishMagickMemory(histogram);
cristy3ed852e2009-09-05 21:47:34 +0000530 return(histogram);
531}
532
cristybb503372010-05-27 20:51:26 +0000533static size_t **AcquireHistogramThreadSet(const size_t count)
cristy3ed852e2009-09-05 21:47:34 +0000534{
cristybb503372010-05-27 20:51:26 +0000535 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000536 i;
537
cristybb503372010-05-27 20:51:26 +0000538 size_t
cristy3ed852e2009-09-05 21:47:34 +0000539 **histogram,
540 number_threads;
541
542 number_threads=GetOpenMPMaximumThreads();
cristyb41ee102010-10-04 16:46:15 +0000543 histogram=(size_t **) AcquireQuantumMemory(number_threads,
cristy3ed852e2009-09-05 21:47:34 +0000544 sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000545 if (histogram == (size_t **) NULL)
546 return((size_t **) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000547 (void) ResetMagickMemory(histogram,0,number_threads*sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000548 for (i=0; i < (ssize_t) number_threads; i++)
cristy3ed852e2009-09-05 21:47:34 +0000549 {
cristybb503372010-05-27 20:51:26 +0000550 histogram[i]=(size_t *) AcquireQuantumMemory(count,
cristy3ed852e2009-09-05 21:47:34 +0000551 sizeof(**histogram));
cristybb503372010-05-27 20:51:26 +0000552 if (histogram[i] == (size_t *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000553 return(DestroyHistogramThreadSet(histogram));
554 }
555 return(histogram);
556}
557
558MagickExport Image *OilPaintImage(const Image *image,const double radius,
559 ExceptionInfo *exception)
560{
561#define NumberPaintBins 256
562#define OilPaintImageTag "OilPaint/Image"
563
cristyfa112112010-01-04 17:48:07 +0000564 CacheView
565 *image_view,
566 *paint_view;
567
cristy3ed852e2009-09-05 21:47:34 +0000568 Image
569 *paint_image;
570
cristy3ed852e2009-09-05 21:47:34 +0000571 MagickBooleanType
572 status;
573
cristybb503372010-05-27 20:51:26 +0000574 MagickOffsetType
575 progress;
576
577 size_t
cristyfa112112010-01-04 17:48:07 +0000578 **restrict histograms,
cristy3ed852e2009-09-05 21:47:34 +0000579 width;
580
cristybb503372010-05-27 20:51:26 +0000581 ssize_t
582 y;
583
cristy3ed852e2009-09-05 21:47:34 +0000584 /*
585 Initialize painted image attributes.
586 */
587 assert(image != (const Image *) NULL);
588 assert(image->signature == MagickSignature);
589 if (image->debug != MagickFalse)
590 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
591 assert(exception != (ExceptionInfo *) NULL);
592 assert(exception->signature == MagickSignature);
593 width=GetOptimalKernelWidth2D(radius,0.5);
cristy3ed852e2009-09-05 21:47:34 +0000594 paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
595 if (paint_image == (Image *) NULL)
596 return((Image *) NULL);
597 if (SetImageStorageClass(paint_image,DirectClass) == MagickFalse)
598 {
599 InheritException(exception,&paint_image->exception);
600 paint_image=DestroyImage(paint_image);
601 return((Image *) NULL);
602 }
603 histograms=AcquireHistogramThreadSet(NumberPaintBins);
cristybb503372010-05-27 20:51:26 +0000604 if (histograms == (size_t **) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000605 {
606 paint_image=DestroyImage(paint_image);
607 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
608 }
609 /*
610 Oil paint image.
611 */
612 status=MagickTrue;
613 progress=0;
614 image_view=AcquireCacheView(image);
615 paint_view=AcquireCacheView(paint_image);
cristyb5d5f722009-11-04 03:03:49 +0000616#if defined(MAGICKCORE_OPENMP_SUPPORT)
617 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000618#endif
cristybb503372010-05-27 20:51:26 +0000619 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000620 {
621 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000622 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000623
624 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000625 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000626
627 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000628 *restrict paint_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000629
cristybb503372010-05-27 20:51:26 +0000630 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000631 x;
632
633 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000634 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000635
cristybb503372010-05-27 20:51:26 +0000636 register size_t
cristy3ed852e2009-09-05 21:47:34 +0000637 *histogram;
638
639 if (status == MagickFalse)
640 continue;
cristyfe4ba002011-02-28 14:54:12 +0000641 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
642 (width/2L),image->columns+width,width,exception);
cristy3ed852e2009-09-05 21:47:34 +0000643 q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
644 exception);
645 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
646 {
647 status=MagickFalse;
648 continue;
649 }
650 indexes=GetCacheViewVirtualIndexQueue(image_view);
651 paint_indexes=GetCacheViewAuthenticIndexQueue(paint_view);
652 histogram=histograms[GetOpenMPThreadId()];
cristybb503372010-05-27 20:51:26 +0000653 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000654 {
cristybb503372010-05-27 20:51:26 +0000655 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000656 j,
657 k,
658 v;
659
cristybb503372010-05-27 20:51:26 +0000660 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000661 i,
662 u;
663
cristybb503372010-05-27 20:51:26 +0000664 size_t
cristy3ed852e2009-09-05 21:47:34 +0000665 count;
666
667 /*
668 Assign most frequent color.
669 */
670 i=0;
671 j=0;
672 count=0;
673 (void) ResetMagickMemory(histogram,0,NumberPaintBins*sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000674 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +0000675 {
cristybb503372010-05-27 20:51:26 +0000676 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +0000677 {
cristybb503372010-05-27 20:51:26 +0000678 k=(ssize_t) ScaleQuantumToChar(PixelIntensityToQuantum(p+u+i));
cristy3ed852e2009-09-05 21:47:34 +0000679 histogram[k]++;
680 if (histogram[k] > count)
681 {
682 j=i+u;
683 count=histogram[k];
684 }
685 }
cristyd99b0962010-05-29 23:14:26 +0000686 i+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +0000687 }
688 *q=(*(p+j));
689 if (image->colorspace == CMYKColorspace)
690 paint_indexes[x]=indexes[x+j];
691 p++;
692 q++;
693 }
694 if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
695 status=MagickFalse;
696 if (image->progress_monitor != (MagickProgressMonitor) NULL)
697 {
698 MagickBooleanType
699 proceed;
700
cristyb5d5f722009-11-04 03:03:49 +0000701#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000702 #pragma omp critical (MagickCore_OilPaintImage)
703#endif
704 proceed=SetImageProgress(image,OilPaintImageTag,progress++,image->rows);
705 if (proceed == MagickFalse)
706 status=MagickFalse;
707 }
708 }
709 paint_view=DestroyCacheView(paint_view);
710 image_view=DestroyCacheView(image_view);
711 histograms=DestroyHistogramThreadSet(histograms);
712 if (status == MagickFalse)
713 paint_image=DestroyImage(paint_image);
714 return(paint_image);
715}
716
717/*
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719% %
720% %
721% %
722% O p a q u e P a i n t I m a g e %
723% %
724% %
725% %
726%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727%
728% OpaquePaintImage() changes any pixel that matches color with the color
729% defined by fill.
730%
731% By default color must match a particular pixel color exactly. However,
732% in many cases two colors may differ by a small amount. Fuzz defines
733% how much tolerance is acceptable to consider two colors as the same.
734% For example, set fuzz to 10 and the color red at intensities of 100 and
735% 102 respectively are now interpreted as the same color.
736%
737% The format of the OpaquePaintImage method is:
738%
739% MagickBooleanType OpaquePaintImage(Image *image,
740% const PixelPacket *target,const PixelPacket *fill,
741% const MagickBooleanType invert)
742% MagickBooleanType OpaquePaintImageChannel(Image *image,
743% const ChannelType channel,const PixelPacket *target,
744% const PixelPacket *fill,const MagickBooleanType invert)
745%
746% A description of each parameter follows:
747%
748% o image: the image.
749%
750% o channel: the channel(s).
751%
752% o target: the RGB value of the target color.
753%
754% o fill: the replacement color.
755%
756% o invert: paint any pixel that does not match the target color.
757%
758*/
759
760MagickExport MagickBooleanType OpaquePaintImage(Image *image,
761 const MagickPixelPacket *target,const MagickPixelPacket *fill,
762 const MagickBooleanType invert)
763{
764 return(OpaquePaintImageChannel(image,AllChannels,target,fill,invert));
765}
766
767MagickExport MagickBooleanType OpaquePaintImageChannel(Image *image,
768 const ChannelType channel,const MagickPixelPacket *target,
769 const MagickPixelPacket *fill,const MagickBooleanType invert)
770{
771#define OpaquePaintImageTag "Opaque/Image"
772
cristyc4c8d132010-01-07 01:58:38 +0000773 CacheView
774 *image_view;
775
cristy3ed852e2009-09-05 21:47:34 +0000776 ExceptionInfo
777 *exception;
778
cristy3ed852e2009-09-05 21:47:34 +0000779 MagickBooleanType
780 status;
781
cristybb503372010-05-27 20:51:26 +0000782 MagickOffsetType
783 progress;
784
cristy3ed852e2009-09-05 21:47:34 +0000785 MagickPixelPacket
786 zero;
787
cristybb503372010-05-27 20:51:26 +0000788 ssize_t
789 y;
790
cristy3ed852e2009-09-05 21:47:34 +0000791 assert(image != (Image *) NULL);
792 assert(image->signature == MagickSignature);
793 assert(target != (MagickPixelPacket *) NULL);
794 assert(fill != (MagickPixelPacket *) NULL);
795 if (image->debug != MagickFalse)
796 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
797 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
798 return(MagickFalse);
799 /*
800 Make image color opaque.
801 */
802 status=MagickTrue;
803 progress=0;
804 exception=(&image->exception);
805 GetMagickPixelPacket(image,&zero);
806 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000807#if defined(MAGICKCORE_OPENMP_SUPPORT)
808 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000809#endif
cristybb503372010-05-27 20:51:26 +0000810 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000811 {
812 MagickPixelPacket
813 pixel;
814
815 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000816 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000817
cristybb503372010-05-27 20:51:26 +0000818 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000819 x;
820
821 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000822 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000823
824 if (status == MagickFalse)
825 continue;
826 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
827 if (q == (PixelPacket *) NULL)
828 {
829 status=MagickFalse;
830 continue;
831 }
832 indexes=GetCacheViewAuthenticIndexQueue(image_view);
833 pixel=zero;
cristybb503372010-05-27 20:51:26 +0000834 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000835 {
836 SetMagickPixelPacket(image,q,indexes+x,&pixel);
837 if (IsMagickColorSimilar(&pixel,target) != invert)
838 {
839 if ((channel & RedChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000840 q->red=ClampToQuantum(fill->red);
cristy3ed852e2009-09-05 21:47:34 +0000841 if ((channel & GreenChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000842 q->green=ClampToQuantum(fill->green);
cristy3ed852e2009-09-05 21:47:34 +0000843 if ((channel & BlueChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000844 q->blue=ClampToQuantum(fill->blue);
cristy3ed852e2009-09-05 21:47:34 +0000845 if ((channel & OpacityChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000846 q->opacity=ClampToQuantum(fill->opacity);
cristy3ed852e2009-09-05 21:47:34 +0000847 if (((channel & IndexChannel) != 0) &&
848 (image->colorspace == CMYKColorspace))
cristyce70c172010-01-07 17:15:30 +0000849 indexes[x]=ClampToQuantum(fill->index);
cristy3ed852e2009-09-05 21:47:34 +0000850 }
851 q++;
852 }
853 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
854 status=MagickFalse;
855 if (image->progress_monitor != (MagickProgressMonitor) NULL)
856 {
857 MagickBooleanType
858 proceed;
859
cristyb5d5f722009-11-04 03:03:49 +0000860#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000861 #pragma omp critical (MagickCore_OpaquePaintImageChannel)
862#endif
863 proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
864 image->rows);
865 if (proceed == MagickFalse)
866 status=MagickFalse;
867 }
868 }
869 image_view=DestroyCacheView(image_view);
870 return(status);
871}
872
873/*
874%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
875% %
876% %
877% %
878% T r a n s p a r e n t P a i n t I m a g e %
879% %
880% %
881% %
882%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
883%
884% TransparentPaintImage() changes the opacity value associated with any pixel
885% that matches color to the value defined by opacity.
886%
887% By default color must match a particular pixel color exactly. However,
888% in many cases two colors may differ by a small amount. Fuzz defines
889% how much tolerance is acceptable to consider two colors as the same.
890% For example, set fuzz to 10 and the color red at intensities of 100 and
891% 102 respectively are now interpreted as the same color.
892%
893% The format of the TransparentPaintImage method is:
894%
895% MagickBooleanType TransparentPaintImage(Image *image,
896% const MagickPixelPacket *target,const Quantum opacity,
897% const MagickBooleanType invert)
898%
899% A description of each parameter follows:
900%
901% o image: the image.
902%
903% o target: the target color.
904%
905% o opacity: the replacement opacity value.
906%
907% o invert: paint any pixel that does not match the target color.
908%
909*/
910MagickExport MagickBooleanType TransparentPaintImage(Image *image,
911 const MagickPixelPacket *target,const Quantum opacity,
912 const MagickBooleanType invert)
913{
914#define TransparentPaintImageTag "Transparent/Image"
915
cristyc4c8d132010-01-07 01:58:38 +0000916 CacheView
917 *image_view;
918
cristy3ed852e2009-09-05 21:47:34 +0000919 ExceptionInfo
920 *exception;
921
cristy3ed852e2009-09-05 21:47:34 +0000922 MagickBooleanType
923 status;
924
cristybb503372010-05-27 20:51:26 +0000925 MagickOffsetType
926 progress;
927
cristy3ed852e2009-09-05 21:47:34 +0000928 MagickPixelPacket
929 zero;
930
cristybb503372010-05-27 20:51:26 +0000931 ssize_t
932 y;
933
cristy3ed852e2009-09-05 21:47:34 +0000934 assert(image != (Image *) NULL);
935 assert(image->signature == MagickSignature);
936 assert(target != (MagickPixelPacket *) NULL);
937 if (image->debug != MagickFalse)
938 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
939 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
940 return(MagickFalse);
941 if (image->matte == MagickFalse)
942 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
943 /*
944 Make image color transparent.
945 */
946 status=MagickTrue;
947 progress=0;
948 exception=(&image->exception);
949 GetMagickPixelPacket(image,&zero);
950 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000951#if defined(MAGICKCORE_OPENMP_SUPPORT)
952 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000953#endif
cristybb503372010-05-27 20:51:26 +0000954 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000955 {
956 MagickPixelPacket
957 pixel;
958
959 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000960 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000961
cristybb503372010-05-27 20:51:26 +0000962 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000963 x;
964
965 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000966 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000967
968 if (status == MagickFalse)
969 continue;
970 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
971 if (q == (PixelPacket *) NULL)
972 {
973 status=MagickFalse;
974 continue;
975 }
976 indexes=GetCacheViewAuthenticIndexQueue(image_view);
977 pixel=zero;
cristybb503372010-05-27 20:51:26 +0000978 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000979 {
980 SetMagickPixelPacket(image,q,indexes+x,&pixel);
981 if (IsMagickColorSimilar(&pixel,target) != invert)
982 q->opacity=opacity;
983 q++;
984 }
985 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
986 status=MagickFalse;
987 if (image->progress_monitor != (MagickProgressMonitor) NULL)
988 {
989 MagickBooleanType
990 proceed;
991
cristyb5d5f722009-11-04 03:03:49 +0000992#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000993 #pragma omp critical (MagickCore_TransparentPaintImage)
994#endif
995 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
996 image->rows);
997 if (proceed == MagickFalse)
998 status=MagickFalse;
999 }
1000 }
1001 image_view=DestroyCacheView(image_view);
1002 return(status);
1003}
1004
1005/*
1006%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1007% %
1008% %
1009% %
1010% 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 %
1011% %
1012% %
1013% %
1014%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1015%
1016% TransparentPaintImageChroma() changes the opacity value associated with any
1017% pixel that matches color to the value defined by opacity.
1018%
1019% As there is one fuzz value for the all the channels, the
1020% TransparentPaintImage() API is not suitable for the operations like chroma,
1021% where the tolerance for similarity of two color component (RGB) can be
1022% different, Thus we define this method take two target pixels (one
1023% low and one hight) and all the pixels of an image which are lying between
1024% these two pixels are made transparent.
1025%
1026% The format of the TransparentPaintImage method is:
1027%
1028% MagickBooleanType TransparentPaintImage(Image *image,
1029% const MagickPixelPacket *low,const MagickPixelPacket *hight,
1030% const Quantum opacity,const MagickBooleanType invert)
1031%
1032% A description of each parameter follows:
1033%
1034% o image: the image.
1035%
1036% o low: the low target color.
1037%
1038% o high: the high target color.
1039%
1040% o opacity: the replacement opacity value.
1041%
1042% o invert: paint any pixel that does not match the target color.
1043%
1044*/
1045MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1046 const MagickPixelPacket *low,const MagickPixelPacket *high,
1047 const Quantum opacity,const MagickBooleanType invert)
1048{
1049#define TransparentPaintImageTag "Transparent/Image"
1050
cristyc4c8d132010-01-07 01:58:38 +00001051 CacheView
1052 *image_view;
1053
cristy3ed852e2009-09-05 21:47:34 +00001054 ExceptionInfo
1055 *exception;
1056
cristy3ed852e2009-09-05 21:47:34 +00001057 MagickBooleanType
1058 status;
1059
cristybb503372010-05-27 20:51:26 +00001060 MagickOffsetType
1061 progress;
1062
1063 ssize_t
1064 y;
1065
cristy3ed852e2009-09-05 21:47:34 +00001066 assert(image != (Image *) NULL);
1067 assert(image->signature == MagickSignature);
1068 assert(high != (MagickPixelPacket *) NULL);
1069 assert(low != (MagickPixelPacket *) NULL);
1070 if (image->debug != MagickFalse)
1071 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1072 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1073 return(MagickFalse);
1074 if (image->matte == MagickFalse)
1075 (void) SetImageAlphaChannel(image,ResetAlphaChannel);
1076 /*
1077 Make image color transparent.
1078 */
1079 status=MagickTrue;
1080 progress=0;
1081 exception=(&image->exception);
1082 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001083#if defined(MAGICKCORE_OPENMP_SUPPORT)
1084 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001085#endif
cristybb503372010-05-27 20:51:26 +00001086 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001087 {
1088 MagickBooleanType
1089 match;
1090
1091 MagickPixelPacket
1092 pixel;
1093
1094 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001095 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001096
cristybb503372010-05-27 20:51:26 +00001097 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001098 x;
1099
1100 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001101 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001102
1103 if (status == MagickFalse)
1104 continue;
1105 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1106 if (q == (PixelPacket *) NULL)
1107 {
1108 status=MagickFalse;
1109 continue;
1110 }
1111 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1112 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001113 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001114 {
1115 SetMagickPixelPacket(image,q,indexes+x,&pixel);
1116 match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1117 (pixel.green >= low->green) && (pixel.green <= high->green) &&
1118 (pixel.blue >= low->blue) && (pixel.blue <= high->blue)) ?
1119 MagickTrue : MagickFalse;
1120 if (match != invert)
1121 q->opacity=opacity;
1122 q++;
1123 }
1124 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1125 status=MagickFalse;
1126 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1127 {
1128 MagickBooleanType
1129 proceed;
1130
cristyb5d5f722009-11-04 03:03:49 +00001131#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001132 #pragma omp critical (MagickCore_TransparentPaintImageChroma)
1133#endif
1134 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1135 image->rows);
1136 if (proceed == MagickFalse)
1137 status=MagickFalse;
1138 }
1139 }
1140 image_view=DestroyCacheView(image_view);
1141 return(status);
1142}