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