MagickCore  7.1.1-43
Convert, Edit, Or Compose Bitmap Images
paint.c
1 /*
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 % Cristy %
17 % July 1998 %
18 % %
19 % %
20 % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
21 % 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 % https://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 "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/channel.h"
45 #include "MagickCore/color.h"
46 #include "MagickCore/color-private.h"
47 #include "MagickCore/colorspace-private.h"
48 #include "MagickCore/composite.h"
49 #include "MagickCore/composite-private.h"
50 #include "MagickCore/draw.h"
51 #include "MagickCore/draw-private.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/gem.h"
55 #include "MagickCore/gem-private.h"
56 #include "MagickCore/monitor.h"
57 #include "MagickCore/monitor-private.h"
58 #include "MagickCore/option.h"
59 #include "MagickCore/paint.h"
60 #include "MagickCore/pixel-accessor.h"
61 #include "MagickCore/resource_.h"
62 #include "MagickCore/statistic.h"
63 #include "MagickCore/string_.h"
64 #include "MagickCore/string-private.h"
65 #include "MagickCore/thread-private.h"
66 
67 /*
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
69 % %
70 % %
71 % %
72 % F l o o d f i l l P a i n t I m a g e %
73 % %
74 % %
75 % %
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 %
78 % FloodfillPaintImage() changes the color value of any pixel that matches
79 % target and is an immediate neighbor. If the method FillToBorderMethod is
80 % specified, the color value is changed for any neighbor pixel that does not
81 % match the bordercolor member of image.
82 %
83 % By default target must match a particular pixel color exactly. However,
84 % in many cases two colors may differ by a small amount. The fuzz member of
85 % image defines how much tolerance is acceptable to consider two colors as
86 % the same. For example, set fuzz to 10 and the color red at intensities of
87 % 100 and 102 respectively are now interpreted as the same color for the
88 % purposes of the floodfill.
89 %
90 % The format of the FloodfillPaintImage method is:
91 %
92 % MagickBooleanType FloodfillPaintImage(Image *image,
93 % const DrawInfo *draw_info,const PixelInfo target,
94 % const ssize_t x_offset,const ssize_t y_offset,
95 % const MagickBooleanType invert,ExceptionInfo *exception)
96 %
97 % A description of each parameter follows:
98 %
99 % o image: the image.
100 %
101 % o draw_info: the draw info.
102 %
103 % o target: the RGB value of the target color.
104 %
105 % o x_offset,y_offset: the starting location of the operation.
106 %
107 % o invert: paint any pixel that does not match the target color.
108 %
109 % o exception: return any errors or warnings in this structure.
110 %
111 */
112 MagickExport MagickBooleanType FloodfillPaintImage(Image *image,
113  const DrawInfo *draw_info,const PixelInfo *target,const ssize_t x_offset,
114  const ssize_t y_offset,const MagickBooleanType invert,
115  ExceptionInfo *exception)
116 {
117 #define MaxStacksize 524288UL
118 #define PushSegmentStack(up,left,right,delta) \
119 { \
120  if (s >= (segment_stack+MaxStacksize)) \
121  { \
122  segment_info=RelinquishVirtualMemory(segment_info); \
123  image_view=DestroyCacheView(image_view); \
124  floodplane_view=DestroyCacheView(floodplane_view); \
125  floodplane_image=DestroyImage(floodplane_image); \
126  ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \
127  } \
128  else \
129  { \
130  if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (ssize_t) image->rows)) \
131  { \
132  s->x1=(double) (left); \
133  s->y1=(double) (up); \
134  s->x2=(double) (right); \
135  s->y2=(double) (delta); \
136  s++; \
137  } \
138  } \
139 }
140 
141  CacheView
142  *floodplane_view,
143  *image_view;
144 
145  Image
146  *floodplane_image;
147 
148  MagickBooleanType
149  skip,
150  status;
151 
152  MemoryInfo
153  *segment_info;
154 
155  PixelInfo
156  pixel;
157 
159  *s;
160 
162  *segment_stack;
163 
164  ssize_t
165  offset,
166  start,
167  x1,
168  x2,
169  y;
170 
171  /*
172  Check boundary conditions.
173  */
174  assert(image != (Image *) NULL);
175  assert(image->signature == MagickCoreSignature);
176  assert(draw_info != (DrawInfo *) NULL);
177  assert(draw_info->signature == MagickCoreSignature);
178  if (IsEventLogging() != MagickFalse)
179  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
180  if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
181  return(MagickFalse);
182  if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
183  return(MagickFalse);
184  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
185  return(MagickFalse);
186  if (IsGrayColorspace(image->colorspace) != MagickFalse)
187  (void) SetImageColorspace(image,sRGBColorspace,exception);
188  if (((image->alpha_trait & BlendPixelTrait) == 0) &&
189  (draw_info->fill.alpha_trait != UndefinedPixelTrait))
190  (void) SetImageAlpha(image,OpaqueAlpha,exception);
191  /*
192  Set floodfill state.
193  */
194  floodplane_image=CloneImage(image,0,0,MagickTrue,exception);
195  if (floodplane_image == (Image *) NULL)
196  return(MagickFalse);
197  floodplane_image->alpha_trait=UndefinedPixelTrait;
198  floodplane_image->colorspace=GRAYColorspace;
199  (void) QueryColorCompliance("#000",AllCompliance,
200  &floodplane_image->background_color,exception);
201  (void) SetImageBackgroundColor(floodplane_image,exception);
202  segment_info=AcquireVirtualMemory(MaxStacksize,sizeof(*segment_stack));
203  if (segment_info == (MemoryInfo *) NULL)
204  {
205  floodplane_image=DestroyImage(floodplane_image);
206  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
207  image->filename);
208  }
209  segment_stack=(SegmentInfo *) GetVirtualMemoryBlob(segment_info);
210  /*
211  Push initial segment on stack.
212  */
213  status=MagickTrue;
214  start=0;
215  s=segment_stack;
216  GetPixelInfo(image,&pixel);
217  image_view=AcquireVirtualCacheView(image,exception);
218  floodplane_view=AcquireAuthenticCacheView(floodplane_image,exception);
219  PushSegmentStack(y_offset,x_offset,x_offset,1);
220  PushSegmentStack(y_offset+1,x_offset,x_offset,-1);
221  while (s > segment_stack)
222  {
223  const Quantum
224  *magick_restrict p;
225 
226  Quantum
227  *magick_restrict q;
228 
229  ssize_t
230  x;
231 
232  /*
233  Pop segment off stack.
234  */
235  s--;
236  x1=(ssize_t) s->x1;
237  x2=(ssize_t) s->x2;
238  offset=(ssize_t) s->y2;
239  y=(ssize_t) s->y1+offset;
240  /*
241  Recolor neighboring pixels.
242  */
243  p=GetCacheViewVirtualPixels(image_view,0,y,(size_t) (x1+1),1,exception);
244  q=GetCacheViewAuthenticPixels(floodplane_view,0,y,(size_t) (x1+1),1,
245  exception);
246  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
247  break;
248  p+=(ptrdiff_t) x1*(ssize_t) GetPixelChannels(image);
249  q+=(ptrdiff_t) x1*(ssize_t) GetPixelChannels(floodplane_image);
250  for (x=x1; x >= 0; x--)
251  {
252  if (GetPixelGray(floodplane_image,q) != 0)
253  break;
254  GetPixelInfoPixel(image,p,&pixel);
255  if (IsFuzzyEquivalencePixelInfo(&pixel,target) == invert)
256  break;
257  SetPixelGray(floodplane_image,QuantumRange,q);
258  p-=(ptrdiff_t)GetPixelChannels(image);
259  q-=GetPixelChannels(floodplane_image);
260  }
261  if (SyncCacheViewAuthenticPixels(floodplane_view,exception) == MagickFalse)
262  break;
263  skip=x >= x1 ? MagickTrue : MagickFalse;
264  if (skip == MagickFalse)
265  {
266  start=x+1;
267  if (start < x1)
268  PushSegmentStack(y,start,x1-1,-offset);
269  x=x1+1;
270  }
271  do
272  {
273  if (skip == MagickFalse)
274  {
275  if (x < (ssize_t) image->columns)
276  {
277  p=GetCacheViewVirtualPixels(image_view,x,y,(size_t)
278  ((ssize_t) image->columns-x),1,exception);
279  q=GetCacheViewAuthenticPixels(floodplane_view,x,y,(size_t)
280  ((ssize_t) image->columns-x),1,exception);
281  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
282  break;
283  for ( ; x < (ssize_t) image->columns; x++)
284  {
285  if (GetPixelGray(floodplane_image,q) != 0)
286  break;
287  GetPixelInfoPixel(image,p,&pixel);
288  if (IsFuzzyEquivalencePixelInfo(&pixel,target) == invert)
289  break;
290  SetPixelGray(floodplane_image,QuantumRange,q);
291  p+=(ptrdiff_t) GetPixelChannels(image);
292  q+=(ptrdiff_t) GetPixelChannels(floodplane_image);
293  }
294  status=SyncCacheViewAuthenticPixels(floodplane_view,exception);
295  if (status == MagickFalse)
296  break;
297  }
298  PushSegmentStack(y,start,x-1,offset);
299  if (x > (x2+1))
300  PushSegmentStack(y,x2+1,x-1,-offset);
301  }
302  skip=MagickFalse;
303  x++;
304  if (x <= x2)
305  {
306  p=GetCacheViewVirtualPixels(image_view,x,y,(size_t) (x2-x+1),1,
307  exception);
308  q=GetCacheViewAuthenticPixels(floodplane_view,x,y,(size_t) (x2-x+1),1,
309  exception);
310  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
311  break;
312  for ( ; x <= x2; x++)
313  {
314  if (GetPixelGray(floodplane_image,q) != 0)
315  break;
316  GetPixelInfoPixel(image,p,&pixel);
317  if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert)
318  break;
319  p+=(ptrdiff_t) GetPixelChannels(image);
320  q+=(ptrdiff_t) GetPixelChannels(floodplane_image);
321  }
322  }
323  start=x;
324  } while (x <= x2);
325  }
326  status=MagickTrue;
327 #if defined(MAGICKCORE_OPENMP_SUPPORT)
328  #pragma omp parallel for schedule(static) shared(status) \
329  magick_number_threads(floodplane_image,image,image->rows,2)
330 #endif
331  for (y=0; y < (ssize_t) image->rows; y++)
332  {
333  const Quantum
334  *magick_restrict p;
335 
336  Quantum
337  *magick_restrict q;
338 
339  ssize_t
340  x;
341 
342  /*
343  Tile fill color onto floodplane.
344  */
345  if (status == MagickFalse)
346  continue;
347  p=GetCacheViewVirtualPixels(floodplane_view,0,y,image->columns,1,exception);
348  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
349  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
350  {
351  status=MagickFalse;
352  continue;
353  }
354  for (x=0; x < (ssize_t) image->columns; x++)
355  {
356  if (GetPixelGray(floodplane_image,p) != 0)
357  {
358  PixelInfo
359  fill_color;
360 
361  GetFillColor(draw_info,x,y,&fill_color,exception);
362  if ((image->channel_mask & RedChannel) != 0)
363  SetPixelRed(image,(Quantum) fill_color.red,q);
364  if ((image->channel_mask & GreenChannel) != 0)
365  SetPixelGreen(image,(Quantum) fill_color.green,q);
366  if ((image->channel_mask & BlueChannel) != 0)
367  SetPixelBlue(image,(Quantum) fill_color.blue,q);
368  if ((image->channel_mask & BlackChannel) != 0)
369  SetPixelBlack(image,(Quantum) fill_color.black,q);
370  if (((image->channel_mask & AlphaChannel) != 0) &&
371  ((image->alpha_trait & BlendPixelTrait) != 0))
372  SetPixelAlpha(image,(Quantum) fill_color.alpha,q);
373  }
374  p+=(ptrdiff_t) GetPixelChannels(floodplane_image);
375  q+=(ptrdiff_t) GetPixelChannels(image);
376  }
377  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
378  status=MagickFalse;
379  }
380  floodplane_view=DestroyCacheView(floodplane_view);
381  image_view=DestroyCacheView(image_view);
382  segment_info=RelinquishVirtualMemory(segment_info);
383  floodplane_image=DestroyImage(floodplane_image);
384  return(status);
385 }
386 
387 /*
388 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
389 % %
390 % %
391 % %
392 + G r a d i e n t I m a g e %
393 % %
394 % %
395 % %
396 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397 %
398 % GradientImage() applies a continuously smooth color transitions along a
399 % vector from one color to another.
400 %
401 % Note, the interface of this method will change in the future to support
402 % more than one transition.
403 %
404 % The format of the GradientImage method is:
405 %
406 % MagickBooleanType GradientImage(Image *image,const GradientType type,
407 % const SpreadMethod method,const PixelInfo *start_color,
408 % const PixelInfo *stop_color,ExceptionInfo *exception)
409 %
410 % A description of each parameter follows:
411 %
412 % o image: the image.
413 %
414 % o type: the gradient type: linear or radial.
415 %
416 % o spread: the gradient spread method: pad, reflect, or repeat.
417 %
418 % o start_color: the start color.
419 %
420 % o stop_color: the stop color.
421 %
422 % o exception: return any errors or warnings in this structure.
423 %
424 */
425 MagickExport MagickBooleanType GradientImage(Image *image,
426  const GradientType type,const SpreadMethod method,const StopInfo *stops,
427  const size_t number_stops,ExceptionInfo *exception)
428 {
429  const char
430  *artifact;
431 
432  DrawInfo
433  *draw_info;
434 
436  *gradient;
437 
438  MagickBooleanType
439  status;
440 
441  /*
442  Set gradient start-stop end points.
443  */
444  assert(image != (const Image *) NULL);
445  assert(image->signature == MagickCoreSignature);
446  assert(stops != (const StopInfo *) NULL);
447  assert(number_stops > 0);
448  if (IsEventLogging() != MagickFalse)
449  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
450  draw_info=AcquireDrawInfo();
451  gradient=(&draw_info->gradient);
452  gradient->type=type;
453  gradient->bounding_box.width=image->columns;
454  gradient->bounding_box.height=image->rows;
455  artifact=GetImageArtifact(image,"gradient:bounding-box");
456  if (artifact != (const char *) NULL)
457  (void) ParseAbsoluteGeometry(artifact,&gradient->bounding_box);
458  gradient->gradient_vector.x2=(double) image->columns-1;
459  gradient->gradient_vector.y2=(double) image->rows-1;
460  artifact=GetImageArtifact(image,"gradient:direction");
461  if (artifact != (const char *) NULL)
462  {
463  GravityType
464  direction;
465 
466  direction=(GravityType) ParseCommandOption(MagickGravityOptions,
467  MagickFalse,artifact);
468  switch (direction)
469  {
470  case NorthWestGravity:
471  {
472  gradient->gradient_vector.x1=(double) image->columns-1;
473  gradient->gradient_vector.y1=(double) image->rows-1;
474  gradient->gradient_vector.x2=0.0;
475  gradient->gradient_vector.y2=0.0;
476  break;
477  }
478  case NorthGravity:
479  {
480  gradient->gradient_vector.x1=0.0;
481  gradient->gradient_vector.y1=(double) image->rows-1;
482  gradient->gradient_vector.x2=0.0;
483  gradient->gradient_vector.y2=0.0;
484  break;
485  }
486  case NorthEastGravity:
487  {
488  gradient->gradient_vector.x1=0.0;
489  gradient->gradient_vector.y1=(double) image->rows-1;
490  gradient->gradient_vector.x2=(double) image->columns-1;
491  gradient->gradient_vector.y2=0.0;
492  break;
493  }
494  case WestGravity:
495  {
496  gradient->gradient_vector.x1=(double) image->columns-1;
497  gradient->gradient_vector.y1=0.0;
498  gradient->gradient_vector.x2=0.0;
499  gradient->gradient_vector.y2=0.0;
500  break;
501  }
502  case EastGravity:
503  {
504  gradient->gradient_vector.x1=0.0;
505  gradient->gradient_vector.y1=0.0;
506  gradient->gradient_vector.x2=(double) image->columns-1;
507  gradient->gradient_vector.y2=0.0;
508  break;
509  }
510  case SouthWestGravity:
511  {
512  gradient->gradient_vector.x1=(double) image->columns-1;
513  gradient->gradient_vector.y1=0.0;
514  gradient->gradient_vector.x2=0.0;
515  gradient->gradient_vector.y2=(double) image->rows-1;
516  break;
517  }
518  case SouthGravity:
519  {
520  gradient->gradient_vector.x1=0.0;
521  gradient->gradient_vector.y1=0.0;
522  gradient->gradient_vector.x2=0.0;
523  gradient->gradient_vector.y2=(double) image->rows-1;
524  break;
525  }
526  case SouthEastGravity:
527  {
528  gradient->gradient_vector.x1=0.0;
529  gradient->gradient_vector.y1=0.0;
530  gradient->gradient_vector.x2=(double) image->columns-1;
531  gradient->gradient_vector.y2=(double) image->rows-1;
532  break;
533  }
534  default:
535  break;
536  }
537  }
538  artifact=GetImageArtifact(image,"gradient:angle");
539  if (artifact != (const char *) NULL)
540  gradient->angle=StringToDouble(artifact,(char **) NULL);
541  artifact=GetImageArtifact(image,"gradient:vector");
542  if (artifact != (const char *) NULL)
543  (void) sscanf(artifact,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",
544  &gradient->gradient_vector.x1,&gradient->gradient_vector.y1,
545  &gradient->gradient_vector.x2,&gradient->gradient_vector.y2);
546  if ((GetImageArtifact(image,"gradient:angle") == (const char *) NULL) &&
547  (GetImageArtifact(image,"gradient:direction") == (const char *) NULL) &&
548  (GetImageArtifact(image,"gradient:extent") == (const char *) NULL) &&
549  (GetImageArtifact(image,"gradient:vector") == (const char *) NULL))
550  if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
551  gradient->gradient_vector.x2=0.0;
552  gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
553  gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
554  artifact=GetImageArtifact(image,"gradient:center");
555  if (artifact != (const char *) NULL)
556  (void) sscanf(artifact,"%lf%*[ ,]%lf",&gradient->center.x,
557  &gradient->center.y);
558  artifact=GetImageArtifact(image,"gradient:angle");
559  if ((type == LinearGradient) && (artifact != (const char *) NULL))
560  {
561  double
562  sine,
563  cosine,
564  distance;
565 
566  /*
567  Reference https://drafts.csswg.org/css-images-3/#linear-gradients.
568  */
569  sine=sin((double) DegreesToRadians(gradient->angle-90.0));
570  cosine=cos((double) DegreesToRadians(gradient->angle-90.0));
571  distance=fabs((double) (image->columns-1.0)*cosine)+
572  fabs((double) (image->rows-1.0)*sine);
573  gradient->gradient_vector.x1=0.5*((image->columns-1.0)-distance*cosine);
574  gradient->gradient_vector.y1=0.5*((image->rows-1.0)-distance*sine);
575  gradient->gradient_vector.x2=0.5*((image->columns-1.0)+distance*cosine);
576  gradient->gradient_vector.y2=0.5*((image->rows-1.0)+distance*sine);
577  }
578  gradient->radii.x=(double) MagickMax((image->columns-1.0),(image->rows-1.0))/
579  2.0;
580  gradient->radii.y=gradient->radii.x;
581  artifact=GetImageArtifact(image,"gradient:extent");
582  if (artifact != (const char *) NULL)
583  {
584  if (LocaleCompare(artifact,"Circle") == 0)
585  {
586  gradient->radii.x=(double) MagickMax((image->columns-1.0),
587  (image->rows-1.0))/2.0;
588  gradient->radii.y=gradient->radii.x;
589  }
590  if (LocaleCompare(artifact,"Diagonal") == 0)
591  {
592  gradient->radii.x=(double) (sqrt((double) (image->columns-1.0)*
593  (image->columns-1.0)+(image->rows-1.0)*(image->rows-1.0)))/2.0;
594  gradient->radii.y=gradient->radii.x;
595  }
596  if (LocaleCompare(artifact,"Ellipse") == 0)
597  {
598  gradient->radii.x=(double) (image->columns-1.0)/2.0;
599  gradient->radii.y=(double) (image->rows-1.0)/2.0;
600  }
601  if (LocaleCompare(artifact,"Maximum") == 0)
602  {
603  gradient->radii.x=(double) MagickMax((image->columns-1.0),
604  (image->rows-1.0))/2.0;
605  gradient->radii.y=gradient->radii.x;
606  }
607  if (LocaleCompare(artifact,"Minimum") == 0)
608  {
609  gradient->radii.x=(double) (MagickMin((image->columns-1.0),
610  (image->rows-1.0)))/2.0;
611  gradient->radii.y=gradient->radii.x;
612  }
613  }
614  artifact=GetImageArtifact(image,"gradient:radii");
615  if (artifact != (const char *) NULL)
616  (void) sscanf(artifact,"%lf%*[ ,]%lf",&gradient->radii.x,
617  &gradient->radii.y);
618  gradient->radius=MagickMax(gradient->radii.x,gradient->radii.y);
619  gradient->spread=method;
620  /*
621  Define the gradient to fill between the stops.
622  */
623  gradient->number_stops=number_stops;
624  gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
625  sizeof(*gradient->stops));
626  if (gradient->stops == (StopInfo *) NULL)
627  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
628  image->filename);
629  (void) memcpy(gradient->stops,stops,(size_t) number_stops*sizeof(*stops));
630  /*
631  Draw a gradient on the image.
632  */
633  status=DrawGradientImage(image,draw_info,exception);
634  draw_info=DestroyDrawInfo(draw_info);
635  return(status);
636 }
637 
638 /*
639 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
640 % %
641 % %
642 % %
643 % O i l P a i n t I m a g e %
644 % %
645 % %
646 % %
647 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648 %
649 % OilPaintImage() applies a special effect filter that simulates an oil
650 % painting. Each pixel is replaced by the most frequent color occurring
651 % in a circular region defined by radius.
652 %
653 % The format of the OilPaintImage method is:
654 %
655 % Image *OilPaintImage(const Image *image,const double radius,
656 % const double sigma,ExceptionInfo *exception)
657 %
658 % A description of each parameter follows:
659 %
660 % o image: the image.
661 %
662 % o radius: the radius of the circular neighborhood.
663 %
664 % o sigma: the standard deviation of the Gaussian, in pixels.
665 %
666 % o exception: return any errors or warnings in this structure.
667 %
668 */
669 
670 static size_t **DestroyHistogramTLS(size_t **histogram)
671 {
672  ssize_t
673  i;
674 
675  assert(histogram != (size_t **) NULL);
676  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
677  if (histogram[i] != (size_t *) NULL)
678  histogram[i]=(size_t *) RelinquishMagickMemory(histogram[i]);
679  histogram=(size_t **) RelinquishMagickMemory(histogram);
680  return(histogram);
681 }
682 
683 static size_t **AcquireHistogramTLS(const size_t count)
684 {
685  ssize_t
686  i;
687 
688  size_t
689  **histogram,
690  number_threads;
691 
692  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
693  histogram=(size_t **) AcquireQuantumMemory(number_threads,sizeof(*histogram));
694  if (histogram == (size_t **) NULL)
695  return((size_t **) NULL);
696  (void) memset(histogram,0,number_threads*sizeof(*histogram));
697  for (i=0; i < (ssize_t) number_threads; i++)
698  {
699  histogram[i]=(size_t *) AcquireQuantumMemory(count,sizeof(**histogram));
700  if (histogram[i] == (size_t *) NULL)
701  return(DestroyHistogramTLS(histogram));
702  }
703  return(histogram);
704 }
705 
706 MagickExport Image *OilPaintImage(const Image *image,const double radius,
707  const double sigma,ExceptionInfo *exception)
708 {
709 #define NumberPaintBins 256
710 #define OilPaintImageTag "OilPaint/Image"
711 
712  CacheView
713  *image_view,
714  *paint_view;
715 
716  Image
717  *linear_image,
718  *paint_image;
719 
720  MagickBooleanType
721  status;
722 
723  MagickOffsetType
724  progress;
725 
726  size_t
727  **histograms,
728  width;
729 
730  ssize_t
731  center,
732  y;
733 
734  /*
735  Initialize painted image attributes.
736  */
737  assert(image != (const Image *) NULL);
738  assert(image->signature == MagickCoreSignature);
739  assert(exception != (ExceptionInfo *) NULL);
740  assert(exception->signature == MagickCoreSignature);
741  if (IsEventLogging() != MagickFalse)
742  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
743  width=GetOptimalKernelWidth2D(radius,sigma);
744  linear_image=CloneImage(image,0,0,MagickTrue,exception);
745  paint_image=CloneImage(image,0,0,MagickTrue,exception);
746  if ((linear_image == (Image *) NULL) || (paint_image == (Image *) NULL))
747  {
748  if (linear_image != (Image *) NULL)
749  linear_image=DestroyImage(linear_image);
750  if (paint_image != (Image *) NULL)
751  linear_image=DestroyImage(paint_image);
752  return((Image *) NULL);
753  }
754  if (SetImageStorageClass(paint_image,DirectClass,exception) == MagickFalse)
755  {
756  linear_image=DestroyImage(linear_image);
757  paint_image=DestroyImage(paint_image);
758  return((Image *) NULL);
759  }
760  histograms=AcquireHistogramTLS(NumberPaintBins);
761  if (histograms == (size_t **) NULL)
762  {
763  linear_image=DestroyImage(linear_image);
764  paint_image=DestroyImage(paint_image);
765  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
766  }
767  /*
768  Oil paint image.
769  */
770  status=MagickTrue;
771  progress=0;
772  center=(ssize_t) (GetPixelChannels(linear_image)*(linear_image->columns+
773  width)*(width/2L)+GetPixelChannels(linear_image)*(width/2L));
774  image_view=AcquireVirtualCacheView(linear_image,exception);
775  paint_view=AcquireAuthenticCacheView(paint_image,exception);
776 #if defined(MAGICKCORE_OPENMP_SUPPORT)
777  #pragma omp parallel for schedule(static) shared(progress,status) \
778  magick_number_threads(linear_image,paint_image,linear_image->rows,1)
779 #endif
780  for (y=0; y < (ssize_t) linear_image->rows; y++)
781  {
782  const Quantum
783  *magick_restrict p;
784 
785  Quantum
786  *magick_restrict q;
787 
788  size_t
789  *histogram;
790 
791  ssize_t
792  x;
793 
794  if (status == MagickFalse)
795  continue;
796  p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
797  (width/2L),linear_image->columns+width,width,exception);
798  q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
799  exception);
800  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
801  {
802  status=MagickFalse;
803  continue;
804  }
805  histogram=histograms[GetOpenMPThreadId()];
806  for (x=0; x < (ssize_t) linear_image->columns; x++)
807  {
808  ssize_t
809  i,
810  u;
811 
812  size_t
813  count;
814 
815  ssize_t
816  j,
817  k,
818  n,
819  v;
820 
821  /*
822  Assign most frequent color.
823  */
824  k=0;
825  j=0;
826  count=0;
827  (void) memset(histogram,0,NumberPaintBins* sizeof(*histogram));
828  for (v=0; v < (ssize_t) width; v++)
829  {
830  for (u=0; u < (ssize_t) width; u++)
831  {
832  n=(ssize_t) ScaleQuantumToChar(ClampToQuantum(GetPixelIntensity(
833  linear_image,p+(ssize_t) GetPixelChannels(linear_image)*(u+k))));
834  histogram[n]++;
835  if (histogram[n] > count)
836  {
837  j=k+u;
838  count=histogram[n];
839  }
840  }
841  k+=(ssize_t) (linear_image->columns+width);
842  }
843  for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
844  {
845  PixelChannel channel = GetPixelChannelChannel(linear_image,i);
846  PixelTrait traits = GetPixelChannelTraits(linear_image,channel);
847  PixelTrait paint_traits=GetPixelChannelTraits(paint_image,channel);
848  if ((traits == UndefinedPixelTrait) ||
849  (paint_traits == UndefinedPixelTrait))
850  continue;
851  if ((paint_traits & CopyPixelTrait) != 0)
852  {
853  SetPixelChannel(paint_image,channel,p[center+i],q);
854  continue;
855  }
856  SetPixelChannel(paint_image,channel,p[j*(ssize_t)
857  GetPixelChannels(linear_image)+i],q);
858  }
859  p+=(ptrdiff_t) GetPixelChannels(linear_image);
860  q+=(ptrdiff_t) GetPixelChannels(paint_image);
861  }
862  if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
863  status=MagickFalse;
864  if (linear_image->progress_monitor != (MagickProgressMonitor) NULL)
865  {
866  MagickBooleanType
867  proceed;
868 
869 #if defined(MAGICKCORE_OPENMP_SUPPORT)
870  #pragma omp atomic
871 #endif
872  progress++;
873  proceed=SetImageProgress(linear_image,OilPaintImageTag,progress,
874  linear_image->rows);
875  if (proceed == MagickFalse)
876  status=MagickFalse;
877  }
878  }
879  paint_view=DestroyCacheView(paint_view);
880  image_view=DestroyCacheView(image_view);
881  histograms=DestroyHistogramTLS(histograms);
882  linear_image=DestroyImage(linear_image);
883  if (status == MagickFalse)
884  paint_image=DestroyImage(paint_image);
885  return(paint_image);
886 }
887 
888 /*
889 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
890 % %
891 % %
892 % %
893 % O p a q u e P a i n t I m a g e %
894 % %
895 % %
896 % %
897 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
898 %
899 % OpaquePaintImage() changes any pixel that matches color with the color
900 % defined by fill argument.
901 %
902 % By default color must match a particular pixel color exactly. However, in
903 % many cases two colors may differ by a small amount. Fuzz defines how much
904 % tolerance is acceptable to consider two colors as the same. For example,
905 % set fuzz to 10 and the color red at intensities of 100 and 102 respectively
906 % are now interpreted as the same color.
907 %
908 % The format of the OpaquePaintImage method is:
909 %
910 % MagickBooleanType OpaquePaintImage(Image *image,const PixelInfo *target,
911 % const PixelInfo *fill,const MagickBooleanType invert,
912 % ExceptionInfo *exception)
913 %
914 % A description of each parameter follows:
915 %
916 % o image: the image.
917 %
918 % o target: the RGB value of the target color.
919 %
920 % o fill: the replacement color.
921 %
922 % o invert: paint any pixel that does not match the target color.
923 %
924 % o exception: return any errors or warnings in this structure.
925 %
926 */
927 MagickExport MagickBooleanType OpaquePaintImage(Image *image,
928  const PixelInfo *target,const PixelInfo *fill,const MagickBooleanType invert,
929  ExceptionInfo *exception)
930 {
931 #define OpaquePaintImageTag "Opaque/Image"
932 
933  CacheView
934  *image_view;
935 
936  MagickBooleanType
937  status;
938 
939  MagickOffsetType
940  progress;
941 
942  PixelInfo
943  conform_fill,
944  conform_target,
945  zero;
946 
947  ssize_t
948  y;
949 
950  assert(image != (Image *) NULL);
951  assert(image->signature == MagickCoreSignature);
952  assert(target != (PixelInfo *) NULL);
953  assert(fill != (PixelInfo *) NULL);
954  if (IsEventLogging() != MagickFalse)
955  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
956  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
957  return(MagickFalse);
958  ConformPixelInfo(image,fill,&conform_fill,exception);
959  ConformPixelInfo(image,target,&conform_target,exception);
960  /*
961  Make image color opaque.
962  */
963  status=MagickTrue;
964  progress=0;
965  GetPixelInfo(image,&zero);
966  image_view=AcquireAuthenticCacheView(image,exception);
967 #if defined(MAGICKCORE_OPENMP_SUPPORT)
968  #pragma omp parallel for schedule(static) shared(progress,status) \
969  magick_number_threads(image,image,image->rows,1)
970 #endif
971  for (y=0; y < (ssize_t) image->rows; y++)
972  {
973  PixelInfo
974  pixel;
975 
976  Quantum
977  *magick_restrict q;
978 
979  ssize_t
980  x;
981 
982  if (status == MagickFalse)
983  continue;
984  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
985  if (q == (Quantum *) NULL)
986  {
987  status=MagickFalse;
988  continue;
989  }
990  pixel=zero;
991  for (x=0; x < (ssize_t) image->columns; x++)
992  {
993  GetPixelInfoPixel(image,q,&pixel);
994  if (IsFuzzyEquivalencePixelInfo(&pixel,&conform_target) != invert)
995  {
996  PixelTrait
997  traits;
998 
999  traits=GetPixelChannelTraits(image,RedPixelChannel);
1000  if ((traits & UpdatePixelTrait) != 0)
1001  SetPixelRed(image,(Quantum) conform_fill.red,q);
1002  traits=GetPixelChannelTraits(image,GreenPixelChannel);
1003  if ((traits & UpdatePixelTrait) != 0)
1004  SetPixelGreen(image,(Quantum) conform_fill.green,q);
1005  traits=GetPixelChannelTraits(image,BluePixelChannel);
1006  if ((traits & UpdatePixelTrait) != 0)
1007  SetPixelBlue(image,(Quantum) conform_fill.blue,q);
1008  traits=GetPixelChannelTraits(image,BlackPixelChannel);
1009  if ((traits & UpdatePixelTrait) != 0)
1010  SetPixelBlack(image,(Quantum) conform_fill.black,q);
1011  traits=GetPixelChannelTraits(image,AlphaPixelChannel);
1012  if ((traits & UpdatePixelTrait) != 0)
1013  SetPixelAlpha(image,(Quantum) conform_fill.alpha,q);
1014  }
1015  q+=(ptrdiff_t) GetPixelChannels(image);
1016  }
1017  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1018  status=MagickFalse;
1019  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1020  {
1021  MagickBooleanType
1022  proceed;
1023 
1024 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1025  #pragma omp atomic
1026 #endif
1027  progress++;
1028  proceed=SetImageProgress(image,OpaquePaintImageTag,progress,
1029  image->rows);
1030  if (proceed == MagickFalse)
1031  status=MagickFalse;
1032  }
1033  }
1034  image_view=DestroyCacheView(image_view);
1035  return(status);
1036 }
1037 
1038 /*
1039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1040 % %
1041 % %
1042 % %
1043 % T r a n s p a r e n t P a i n t I m a g e %
1044 % %
1045 % %
1046 % %
1047 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1048 %
1049 % TransparentPaintImage() changes the opacity value associated with any pixel
1050 % that matches color to the value defined by opacity.
1051 %
1052 % By default color must match a particular pixel color exactly. However, in
1053 % many cases two colors may differ by a small amount. Fuzz defines how much
1054 % tolerance is acceptable to consider two colors as the same. For example,
1055 % set fuzz to 10 and the color red at intensities of 100 and 102 respectively
1056 % are now interpreted as the same color.
1057 %
1058 % The format of the TransparentPaintImage method is:
1059 %
1060 % MagickBooleanType TransparentPaintImage(Image *image,
1061 % const PixelInfo *target,const Quantum opacity,
1062 % const MagickBooleanType invert,ExceptionInfo *exception)
1063 %
1064 % A description of each parameter follows:
1065 %
1066 % o image: the image.
1067 %
1068 % o target: the target color.
1069 %
1070 % o opacity: the replacement opacity value.
1071 %
1072 % o invert: paint any pixel that does not match the target color.
1073 %
1074 % o exception: return any errors or warnings in this structure.
1075 %
1076 */
1077 MagickExport MagickBooleanType TransparentPaintImage(Image *image,
1078  const PixelInfo *target,const Quantum opacity,const MagickBooleanType invert,
1079  ExceptionInfo *exception)
1080 {
1081 #define TransparentPaintImageTag "Transparent/Image"
1082 
1083  CacheView
1084  *image_view;
1085 
1086  MagickBooleanType
1087  status;
1088 
1089  MagickOffsetType
1090  progress;
1091 
1092  PixelInfo
1093  zero;
1094 
1095  ssize_t
1096  y;
1097 
1098  assert(image != (Image *) NULL);
1099  assert(image->signature == MagickCoreSignature);
1100  assert(target != (PixelInfo *) NULL);
1101  if (IsEventLogging() != MagickFalse)
1102  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1103  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1104  return(MagickFalse);
1105  if ((image->alpha_trait & BlendPixelTrait) == 0)
1106  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1107  /*
1108  Make image color transparent.
1109  */
1110  status=MagickTrue;
1111  progress=0;
1112  GetPixelInfo(image,&zero);
1113  image_view=AcquireAuthenticCacheView(image,exception);
1114 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1115  #pragma omp parallel for schedule(static) shared(progress,status) \
1116  magick_number_threads(image,image,image->rows,1)
1117 #endif
1118  for (y=0; y < (ssize_t) image->rows; y++)
1119  {
1120  PixelInfo
1121  pixel;
1122 
1123  ssize_t
1124  x;
1125 
1126  Quantum
1127  *magick_restrict q;
1128 
1129  if (status == MagickFalse)
1130  continue;
1131  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1132  if (q == (Quantum *) NULL)
1133  {
1134  status=MagickFalse;
1135  continue;
1136  }
1137  pixel=zero;
1138  for (x=0; x < (ssize_t) image->columns; x++)
1139  {
1140  GetPixelInfoPixel(image,q,&pixel);
1141  if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert)
1142  SetPixelAlpha(image,opacity,q);
1143  q+=(ptrdiff_t) GetPixelChannels(image);
1144  }
1145  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1146  status=MagickFalse;
1147  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1148  {
1149  MagickBooleanType
1150  proceed;
1151 
1152 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1153  #pragma omp atomic
1154 #endif
1155  progress++;
1156  proceed=SetImageProgress(image,TransparentPaintImageTag,progress,
1157  image->rows);
1158  if (proceed == MagickFalse)
1159  status=MagickFalse;
1160  }
1161  }
1162  image_view=DestroyCacheView(image_view);
1163  return(status);
1164 }
1165 
1166 /*
1167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1168 % %
1169 % %
1170 % %
1171 % 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 %
1172 % %
1173 % %
1174 % %
1175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1176 %
1177 % TransparentPaintImageChroma() changes the opacity value associated with any
1178 % pixel that matches color to the value defined by opacity.
1179 %
1180 % As there is one fuzz value for the all the channels, TransparentPaintImage()
1181 % is not suitable for the operations like chroma, where the tolerance for
1182 % similarity of two color component (RGB) can be different. Thus we define
1183 % this method to take two target pixels (one low and one high) and all the
1184 % pixels of an image which are lying between these two pixels are made
1185 % transparent.
1186 %
1187 % The format of the TransparentPaintImageChroma method is:
1188 %
1189 % MagickBooleanType TransparentPaintImageChroma(Image *image,
1190 % const PixelInfo *low,const PixelInfo *high,const Quantum opacity,
1191 % const MagickBooleanType invert,ExceptionInfo *exception)
1192 %
1193 % A description of each parameter follows:
1194 %
1195 % o image: the image.
1196 %
1197 % o low: the low target color.
1198 %
1199 % o high: the high target color.
1200 %
1201 % o opacity: the replacement opacity value.
1202 %
1203 % o invert: paint any pixel that does not match the target color.
1204 %
1205 % o exception: return any errors or warnings in this structure.
1206 %
1207 */
1208 MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1209  const PixelInfo *low,const PixelInfo *high,const Quantum opacity,
1210  const MagickBooleanType invert,ExceptionInfo *exception)
1211 {
1212 #define TransparentPaintImageTag "Transparent/Image"
1213 
1214  CacheView
1215  *image_view;
1216 
1217  MagickBooleanType
1218  status;
1219 
1220  MagickOffsetType
1221  progress;
1222 
1223  ssize_t
1224  y;
1225 
1226  assert(image != (Image *) NULL);
1227  assert(image->signature == MagickCoreSignature);
1228  assert(high != (PixelInfo *) NULL);
1229  assert(low != (PixelInfo *) NULL);
1230  if (IsEventLogging() != MagickFalse)
1231  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1232  if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1233  return(MagickFalse);
1234  if ((image->alpha_trait & BlendPixelTrait) == 0)
1235  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1236  /*
1237  Make image color transparent.
1238  */
1239  status=MagickTrue;
1240  progress=0;
1241  image_view=AcquireAuthenticCacheView(image,exception);
1242 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1243  #pragma omp parallel for schedule(static) shared(progress,status) \
1244  magick_number_threads(image,image,image->rows,1)
1245 #endif
1246  for (y=0; y < (ssize_t) image->rows; y++)
1247  {
1248  MagickBooleanType
1249  match;
1250 
1251  PixelInfo
1252  pixel;
1253 
1254  Quantum
1255  *magick_restrict q;
1256 
1257  ssize_t
1258  x;
1259 
1260  if (status == MagickFalse)
1261  continue;
1262  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1263  if (q == (Quantum *) NULL)
1264  {
1265  status=MagickFalse;
1266  continue;
1267  }
1268  GetPixelInfo(image,&pixel);
1269  for (x=0; x < (ssize_t) image->columns; x++)
1270  {
1271  GetPixelInfoPixel(image,q,&pixel);
1272  match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1273  (pixel.green >= low->green) && (pixel.green <= high->green) &&
1274  (pixel.blue >= low->blue) && (pixel.blue <= high->blue)) ? MagickTrue :
1275  MagickFalse;
1276  if (match != invert)
1277  SetPixelAlpha(image,opacity,q);
1278  q+=(ptrdiff_t) GetPixelChannels(image);
1279  }
1280  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1281  status=MagickFalse;
1282  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1283  {
1284  MagickBooleanType
1285  proceed;
1286 
1287 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1288  #pragma omp atomic
1289 #endif
1290  progress++;
1291  proceed=SetImageProgress(image,TransparentPaintImageTag,progress,
1292  image->rows);
1293  if (proceed == MagickFalse)
1294  status=MagickFalse;
1295  }
1296  }
1297  image_view=DestroyCacheView(image_view);
1298  return(status);
1299 }
_SegmentInfo
Definition: image.h:84
_CacheView
Definition: cache-view.c:65
_MemoryInfo
Definition: memory.c:163
_Image
Definition: image.h:131
_PixelInfo
Definition: pixel.h:181
_ExceptionInfo
Definition: exception.h:101
_StopInfo
Definition: draw.h:146
_GradientInfo
Definition: draw.h:155
_DrawInfo
Definition: draw.h:209