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