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