blob: c2213e3a22756ac6cbed9c1e631a3e86e816d6d4 [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
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);
594 if ((image->columns < width) || (image->rows < width))
595 ThrowImageException(OptionError,"ImageSmallerThanRadius");
596 paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
597 if (paint_image == (Image *) NULL)
598 return((Image *) NULL);
599 if (SetImageStorageClass(paint_image,DirectClass) == MagickFalse)
600 {
601 InheritException(exception,&paint_image->exception);
602 paint_image=DestroyImage(paint_image);
603 return((Image *) NULL);
604 }
605 histograms=AcquireHistogramThreadSet(NumberPaintBins);
cristybb503372010-05-27 20:51:26 +0000606 if (histograms == (size_t **) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000607 {
608 paint_image=DestroyImage(paint_image);
609 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
610 }
611 /*
612 Oil paint image.
613 */
614 status=MagickTrue;
615 progress=0;
616 image_view=AcquireCacheView(image);
617 paint_view=AcquireCacheView(paint_image);
cristyb5d5f722009-11-04 03:03:49 +0000618#if defined(MAGICKCORE_OPENMP_SUPPORT)
619 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000620#endif
cristybb503372010-05-27 20:51:26 +0000621 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000622 {
623 register const IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000624 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000625
626 register const PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000627 *restrict p;
cristy3ed852e2009-09-05 21:47:34 +0000628
629 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000630 *restrict paint_indexes;
cristy3ed852e2009-09-05 21:47:34 +0000631
cristybb503372010-05-27 20:51:26 +0000632 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000633 x;
634
635 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000636 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000637
cristybb503372010-05-27 20:51:26 +0000638 register size_t
cristy3ed852e2009-09-05 21:47:34 +0000639 *histogram;
640
641 if (status == MagickFalse)
642 continue;
cristybb503372010-05-27 20:51:26 +0000643 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t) (width/
cristy3ed852e2009-09-05 21:47:34 +0000644 2L),image->columns+width,width,exception);
645 q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
646 exception);
647 if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
648 {
649 status=MagickFalse;
650 continue;
651 }
652 indexes=GetCacheViewVirtualIndexQueue(image_view);
653 paint_indexes=GetCacheViewAuthenticIndexQueue(paint_view);
654 histogram=histograms[GetOpenMPThreadId()];
cristybb503372010-05-27 20:51:26 +0000655 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000656 {
cristybb503372010-05-27 20:51:26 +0000657 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000658 j,
659 k,
660 v;
661
cristybb503372010-05-27 20:51:26 +0000662 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000663 i,
664 u;
665
cristybb503372010-05-27 20:51:26 +0000666 size_t
cristy3ed852e2009-09-05 21:47:34 +0000667 count;
668
669 /*
670 Assign most frequent color.
671 */
672 i=0;
673 j=0;
674 count=0;
675 (void) ResetMagickMemory(histogram,0,NumberPaintBins*sizeof(*histogram));
cristybb503372010-05-27 20:51:26 +0000676 for (v=0; v < (ssize_t) width; v++)
cristy3ed852e2009-09-05 21:47:34 +0000677 {
cristybb503372010-05-27 20:51:26 +0000678 for (u=0; u < (ssize_t) width; u++)
cristy3ed852e2009-09-05 21:47:34 +0000679 {
cristybb503372010-05-27 20:51:26 +0000680 k=(ssize_t) ScaleQuantumToChar(PixelIntensityToQuantum(p+u+i));
cristy3ed852e2009-09-05 21:47:34 +0000681 histogram[k]++;
682 if (histogram[k] > count)
683 {
684 j=i+u;
685 count=histogram[k];
686 }
687 }
cristyd99b0962010-05-29 23:14:26 +0000688 i+=(ssize_t) (image->columns+width);
cristy3ed852e2009-09-05 21:47:34 +0000689 }
690 *q=(*(p+j));
691 if (image->colorspace == CMYKColorspace)
692 paint_indexes[x]=indexes[x+j];
693 p++;
694 q++;
695 }
696 if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
697 status=MagickFalse;
698 if (image->progress_monitor != (MagickProgressMonitor) NULL)
699 {
700 MagickBooleanType
701 proceed;
702
cristyb5d5f722009-11-04 03:03:49 +0000703#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000704 #pragma omp critical (MagickCore_OilPaintImage)
705#endif
706 proceed=SetImageProgress(image,OilPaintImageTag,progress++,image->rows);
707 if (proceed == MagickFalse)
708 status=MagickFalse;
709 }
710 }
711 paint_view=DestroyCacheView(paint_view);
712 image_view=DestroyCacheView(image_view);
713 histograms=DestroyHistogramThreadSet(histograms);
714 if (status == MagickFalse)
715 paint_image=DestroyImage(paint_image);
716 return(paint_image);
717}
718
719/*
720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
721% %
722% %
723% %
724% O p a q u e P a i n t I m a g e %
725% %
726% %
727% %
728%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
729%
730% OpaquePaintImage() changes any pixel that matches color with the color
731% defined by fill.
732%
733% By default color must match a particular pixel color exactly. However,
734% in many cases two colors may differ by a small amount. Fuzz defines
735% how much tolerance is acceptable to consider two colors as the same.
736% For example, set fuzz to 10 and the color red at intensities of 100 and
737% 102 respectively are now interpreted as the same color.
738%
739% The format of the OpaquePaintImage method is:
740%
741% MagickBooleanType OpaquePaintImage(Image *image,
742% const PixelPacket *target,const PixelPacket *fill,
743% const MagickBooleanType invert)
744% MagickBooleanType OpaquePaintImageChannel(Image *image,
745% const ChannelType channel,const PixelPacket *target,
746% const PixelPacket *fill,const MagickBooleanType invert)
747%
748% A description of each parameter follows:
749%
750% o image: the image.
751%
752% o channel: the channel(s).
753%
754% o target: the RGB value of the target color.
755%
756% o fill: the replacement color.
757%
758% o invert: paint any pixel that does not match the target color.
759%
760*/
761
762MagickExport MagickBooleanType OpaquePaintImage(Image *image,
763 const MagickPixelPacket *target,const MagickPixelPacket *fill,
764 const MagickBooleanType invert)
765{
766 return(OpaquePaintImageChannel(image,AllChannels,target,fill,invert));
767}
768
769MagickExport MagickBooleanType OpaquePaintImageChannel(Image *image,
770 const ChannelType channel,const MagickPixelPacket *target,
771 const MagickPixelPacket *fill,const MagickBooleanType invert)
772{
773#define OpaquePaintImageTag "Opaque/Image"
774
cristyc4c8d132010-01-07 01:58:38 +0000775 CacheView
776 *image_view;
777
cristy3ed852e2009-09-05 21:47:34 +0000778 ExceptionInfo
779 *exception;
780
cristy3ed852e2009-09-05 21:47:34 +0000781 MagickBooleanType
782 status;
783
cristybb503372010-05-27 20:51:26 +0000784 MagickOffsetType
785 progress;
786
cristy3ed852e2009-09-05 21:47:34 +0000787 MagickPixelPacket
788 zero;
789
cristybb503372010-05-27 20:51:26 +0000790 ssize_t
791 y;
792
cristy3ed852e2009-09-05 21:47:34 +0000793 assert(image != (Image *) NULL);
794 assert(image->signature == MagickSignature);
795 assert(target != (MagickPixelPacket *) NULL);
796 assert(fill != (MagickPixelPacket *) NULL);
797 if (image->debug != MagickFalse)
798 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
799 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
800 return(MagickFalse);
801 /*
802 Make image color opaque.
803 */
804 status=MagickTrue;
805 progress=0;
806 exception=(&image->exception);
807 GetMagickPixelPacket(image,&zero);
808 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000809#if defined(MAGICKCORE_OPENMP_SUPPORT)
810 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000811#endif
cristybb503372010-05-27 20:51:26 +0000812 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000813 {
814 MagickPixelPacket
815 pixel;
816
817 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000818 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000819
cristybb503372010-05-27 20:51:26 +0000820 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000821 x;
822
823 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000824 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000825
826 if (status == MagickFalse)
827 continue;
828 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
829 if (q == (PixelPacket *) NULL)
830 {
831 status=MagickFalse;
832 continue;
833 }
834 indexes=GetCacheViewAuthenticIndexQueue(image_view);
835 pixel=zero;
cristybb503372010-05-27 20:51:26 +0000836 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000837 {
838 SetMagickPixelPacket(image,q,indexes+x,&pixel);
839 if (IsMagickColorSimilar(&pixel,target) != invert)
840 {
841 if ((channel & RedChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000842 q->red=ClampToQuantum(fill->red);
cristy3ed852e2009-09-05 21:47:34 +0000843 if ((channel & GreenChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000844 q->green=ClampToQuantum(fill->green);
cristy3ed852e2009-09-05 21:47:34 +0000845 if ((channel & BlueChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000846 q->blue=ClampToQuantum(fill->blue);
cristy3ed852e2009-09-05 21:47:34 +0000847 if ((channel & OpacityChannel) != 0)
cristyce70c172010-01-07 17:15:30 +0000848 q->opacity=ClampToQuantum(fill->opacity);
cristy3ed852e2009-09-05 21:47:34 +0000849 if (((channel & IndexChannel) != 0) &&
850 (image->colorspace == CMYKColorspace))
cristyce70c172010-01-07 17:15:30 +0000851 indexes[x]=ClampToQuantum(fill->index);
cristy3ed852e2009-09-05 21:47:34 +0000852 }
853 q++;
854 }
855 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
856 status=MagickFalse;
857 if (image->progress_monitor != (MagickProgressMonitor) NULL)
858 {
859 MagickBooleanType
860 proceed;
861
cristyb5d5f722009-11-04 03:03:49 +0000862#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000863 #pragma omp critical (MagickCore_OpaquePaintImageChannel)
864#endif
865 proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
866 image->rows);
867 if (proceed == MagickFalse)
868 status=MagickFalse;
869 }
870 }
871 image_view=DestroyCacheView(image_view);
872 return(status);
873}
874
875/*
876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
877% %
878% %
879% %
880% T r a n s p a r e n t P a i n t I m a g e %
881% %
882% %
883% %
884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
885%
886% TransparentPaintImage() changes the opacity value associated with any pixel
887% that matches color to the value defined by opacity.
888%
889% By default color must match a particular pixel color exactly. However,
890% in many cases two colors may differ by a small amount. Fuzz defines
891% how much tolerance is acceptable to consider two colors as the same.
892% For example, set fuzz to 10 and the color red at intensities of 100 and
893% 102 respectively are now interpreted as the same color.
894%
895% The format of the TransparentPaintImage method is:
896%
897% MagickBooleanType TransparentPaintImage(Image *image,
898% const MagickPixelPacket *target,const Quantum opacity,
899% const MagickBooleanType invert)
900%
901% A description of each parameter follows:
902%
903% o image: the image.
904%
905% o target: the target color.
906%
907% o opacity: the replacement opacity value.
908%
909% o invert: paint any pixel that does not match the target color.
910%
911*/
912MagickExport MagickBooleanType TransparentPaintImage(Image *image,
913 const MagickPixelPacket *target,const Quantum opacity,
914 const MagickBooleanType invert)
915{
916#define TransparentPaintImageTag "Transparent/Image"
917
cristyc4c8d132010-01-07 01:58:38 +0000918 CacheView
919 *image_view;
920
cristy3ed852e2009-09-05 21:47:34 +0000921 ExceptionInfo
922 *exception;
923
cristy3ed852e2009-09-05 21:47:34 +0000924 MagickBooleanType
925 status;
926
cristybb503372010-05-27 20:51:26 +0000927 MagickOffsetType
928 progress;
929
cristy3ed852e2009-09-05 21:47:34 +0000930 MagickPixelPacket
931 zero;
932
cristybb503372010-05-27 20:51:26 +0000933 ssize_t
934 y;
935
cristy3ed852e2009-09-05 21:47:34 +0000936 assert(image != (Image *) NULL);
937 assert(image->signature == MagickSignature);
938 assert(target != (MagickPixelPacket *) NULL);
939 if (image->debug != MagickFalse)
940 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
941 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
942 return(MagickFalse);
943 if (image->matte == MagickFalse)
944 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
945 /*
946 Make image color transparent.
947 */
948 status=MagickTrue;
949 progress=0;
950 exception=(&image->exception);
951 GetMagickPixelPacket(image,&zero);
952 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +0000953#if defined(MAGICKCORE_OPENMP_SUPPORT)
954 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +0000955#endif
cristybb503372010-05-27 20:51:26 +0000956 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000957 {
958 MagickPixelPacket
959 pixel;
960
961 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +0000962 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +0000963
cristybb503372010-05-27 20:51:26 +0000964 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000965 x;
966
967 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +0000968 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +0000969
970 if (status == MagickFalse)
971 continue;
972 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
973 if (q == (PixelPacket *) NULL)
974 {
975 status=MagickFalse;
976 continue;
977 }
978 indexes=GetCacheViewAuthenticIndexQueue(image_view);
979 pixel=zero;
cristybb503372010-05-27 20:51:26 +0000980 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000981 {
982 SetMagickPixelPacket(image,q,indexes+x,&pixel);
983 if (IsMagickColorSimilar(&pixel,target) != invert)
984 q->opacity=opacity;
985 q++;
986 }
987 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
988 status=MagickFalse;
989 if (image->progress_monitor != (MagickProgressMonitor) NULL)
990 {
991 MagickBooleanType
992 proceed;
993
cristyb5d5f722009-11-04 03:03:49 +0000994#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +0000995 #pragma omp critical (MagickCore_TransparentPaintImage)
996#endif
997 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
998 image->rows);
999 if (proceed == MagickFalse)
1000 status=MagickFalse;
1001 }
1002 }
1003 image_view=DestroyCacheView(image_view);
1004 return(status);
1005}
1006
1007/*
1008%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1009% %
1010% %
1011% %
1012% 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 %
1013% %
1014% %
1015% %
1016%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1017%
1018% TransparentPaintImageChroma() changes the opacity value associated with any
1019% pixel that matches color to the value defined by opacity.
1020%
1021% As there is one fuzz value for the all the channels, the
1022% TransparentPaintImage() API is not suitable for the operations like chroma,
1023% where the tolerance for similarity of two color component (RGB) can be
1024% different, Thus we define this method take two target pixels (one
1025% low and one hight) and all the pixels of an image which are lying between
1026% these two pixels are made transparent.
1027%
1028% The format of the TransparentPaintImage method is:
1029%
1030% MagickBooleanType TransparentPaintImage(Image *image,
1031% const MagickPixelPacket *low,const MagickPixelPacket *hight,
1032% const Quantum opacity,const MagickBooleanType invert)
1033%
1034% A description of each parameter follows:
1035%
1036% o image: the image.
1037%
1038% o low: the low target color.
1039%
1040% o high: the high target color.
1041%
1042% o opacity: the replacement opacity value.
1043%
1044% o invert: paint any pixel that does not match the target color.
1045%
1046*/
1047MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1048 const MagickPixelPacket *low,const MagickPixelPacket *high,
1049 const Quantum opacity,const MagickBooleanType invert)
1050{
1051#define TransparentPaintImageTag "Transparent/Image"
1052
cristyc4c8d132010-01-07 01:58:38 +00001053 CacheView
1054 *image_view;
1055
cristy3ed852e2009-09-05 21:47:34 +00001056 ExceptionInfo
1057 *exception;
1058
cristy3ed852e2009-09-05 21:47:34 +00001059 MagickBooleanType
1060 status;
1061
cristybb503372010-05-27 20:51:26 +00001062 MagickOffsetType
1063 progress;
1064
1065 ssize_t
1066 y;
1067
cristy3ed852e2009-09-05 21:47:34 +00001068 assert(image != (Image *) NULL);
1069 assert(image->signature == MagickSignature);
1070 assert(high != (MagickPixelPacket *) NULL);
1071 assert(low != (MagickPixelPacket *) NULL);
1072 if (image->debug != MagickFalse)
1073 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1074 if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1075 return(MagickFalse);
1076 if (image->matte == MagickFalse)
1077 (void) SetImageAlphaChannel(image,ResetAlphaChannel);
1078 /*
1079 Make image color transparent.
1080 */
1081 status=MagickTrue;
1082 progress=0;
1083 exception=(&image->exception);
1084 image_view=AcquireCacheView(image);
cristyb5d5f722009-11-04 03:03:49 +00001085#if defined(MAGICKCORE_OPENMP_SUPPORT)
1086 #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
cristy3ed852e2009-09-05 21:47:34 +00001087#endif
cristybb503372010-05-27 20:51:26 +00001088 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001089 {
1090 MagickBooleanType
1091 match;
1092
1093 MagickPixelPacket
1094 pixel;
1095
1096 register IndexPacket
cristyc47d1f82009-11-26 01:44:43 +00001097 *restrict indexes;
cristy3ed852e2009-09-05 21:47:34 +00001098
cristybb503372010-05-27 20:51:26 +00001099 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001100 x;
1101
1102 register PixelPacket
cristyc47d1f82009-11-26 01:44:43 +00001103 *restrict q;
cristy3ed852e2009-09-05 21:47:34 +00001104
1105 if (status == MagickFalse)
1106 continue;
1107 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1108 if (q == (PixelPacket *) NULL)
1109 {
1110 status=MagickFalse;
1111 continue;
1112 }
1113 indexes=GetCacheViewAuthenticIndexQueue(image_view);
1114 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001115 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001116 {
1117 SetMagickPixelPacket(image,q,indexes+x,&pixel);
1118 match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1119 (pixel.green >= low->green) && (pixel.green <= high->green) &&
1120 (pixel.blue >= low->blue) && (pixel.blue <= high->blue)) ?
1121 MagickTrue : MagickFalse;
1122 if (match != invert)
1123 q->opacity=opacity;
1124 q++;
1125 }
1126 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1127 status=MagickFalse;
1128 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1129 {
1130 MagickBooleanType
1131 proceed;
1132
cristyb5d5f722009-11-04 03:03:49 +00001133#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy3ed852e2009-09-05 21:47:34 +00001134 #pragma omp critical (MagickCore_TransparentPaintImageChroma)
1135#endif
1136 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1137 image->rows);
1138 if (proceed == MagickFalse)
1139 status=MagickFalse;
1140 }
1141 }
1142 image_view=DestroyCacheView(image_view);
1143 return(status);
1144}