paint.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                      PPPP    AAA   IIIII  N   N  TTTTT                      %
00006 %                      P   P  A   A    I    NN  N    T                        %
00007 %                      PPPP   AAAAA    I    N N N    T                        %
00008 %                      P      A   A    I    N  NN    T                        %
00009 %                      P      A   A  IIIII  N   N    T                        %
00010 %                                                                             %
00011 %                                                                             %
00012 %                        Methods to Paint on an Image                         %
00013 %                                                                             %
00014 %                              Software Design                                %
00015 %                                John Cristy                                  %
00016 %                                 July 1998                                   %
00017 %                                                                             %
00018 %                                                                             %
00019 %  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
00020 %  dedicated to making software imaging solutions freely available.           %
00021 %                                                                             %
00022 %  You may not use this file except in compliance with the License.  You may  %
00023 %  obtain a copy of the License at                                            %
00024 %                                                                             %
00025 %    http://www.imagemagick.org/script/license.php                            %
00026 %                                                                             %
00027 %  Unless required by applicable law or agreed to in writing, software        %
00028 %  distributed under the License is distributed on an "AS IS" BASIS,          %
00029 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
00030 %  See the License for the specific language governing permissions and        %
00031 %  limitations under the License.                                             %
00032 %                                                                             %
00033 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00034 %
00035 %
00036 */
00037 
00038 /*
00039  Include declarations.
00040 */
00041 #include "magick/studio.h"
00042 #include "magick/color.h"
00043 #include "magick/color-private.h"
00044 #include "magick/colorspace-private.h"
00045 #include "magick/composite.h"
00046 #include "magick/composite-private.h"
00047 #include "magick/draw.h"
00048 #include "magick/draw-private.h"
00049 #include "magick/exception.h"
00050 #include "magick/exception-private.h"
00051 #include "magick/gem.h"
00052 #include "magick/monitor.h"
00053 #include "magick/monitor-private.h"
00054 #include "magick/paint.h"
00055 #include "magick/pixel-private.h"
00056 #include "magick/string_.h"
00057 
00058 static inline double MagickMax(const double x,const double y)
00059 {
00060     return( x > y ? x : y);
00061 }
00062 /*
00063 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00064 %                                                                             %
00065 %                                                                             %
00066 %                                                                             %
00067 %   F l o o d f i l l P a i n t I m a g e                                     %
00068 %                                                                             %
00069 %                                                                             %
00070 %                                                                             %
00071 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00072 %
00073 %  FloodfillPaintImage() changes the color value of any pixel that matches
00074 %  target and is an immediate neighbor.  If the method FillToBorderMethod is
00075 %  specified, the color value is changed for any neighbor pixel that does not
00076 %  match the bordercolor member of image.
00077 %
00078 %  By default target must match a particular pixel color exactly.
00079 %  However, in many cases two colors may differ by a small amount.  The
00080 %  fuzz member of image defines how much tolerance is acceptable to
00081 %  consider two colors as the same.  For example, set fuzz to 10 and the
00082 %  color red at intensities of 100 and 102 respectively are now
00083 %  interpreted as the same color for the purposes of the floodfill.
00084 %
00085 %  The format of the FloodfillPaintImage method is:
00086 %
00087 %      MagickBooleanType FloodfillPaintImage(Image *image,
00088 %        const ChannelType channel,const DrawInfo *draw_info,
00089 %        const MagickPixelPacket target,const long x_offset,const long y_offset,
00090 %        const MagickBooleanType invert)
00091 %
00092 %  A description of each parameter follows:
00093 %
00094 %    o image: the image.
00095 %
00096 %    o channel: the channel(s).
00097 %
00098 %    o draw_info: the draw info.
00099 %
00100 %    o target: the RGB value of the target color.
00101 %
00102 %    o x_offset,y_offset: the starting location of the operation.
00103 %
00104 %    o invert: paint any pixel that does not match the target color.
00105 %
00106 */
00107 MagickExport MagickBooleanType FloodfillPaintImage(Image *image,
00108   const ChannelType channel,const DrawInfo *draw_info,
00109   const MagickPixelPacket *target,const long x_offset,const long y_offset,
00110   const MagickBooleanType invert)
00111 {
00112 #define MaxStacksize  (1UL << 15)
00113 #define PushSegmentStack(up,left,right,delta) \
00114 { \
00115   if (s >= (segment_stack+MaxStacksize)) \
00116     ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \
00117   else \
00118     { \
00119       if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (long) image->rows)) \
00120         { \
00121           s->x1=(double) (left); \
00122           s->y1=(double) (up); \
00123           s->x2=(double) (right); \
00124           s->y2=(double) (delta); \
00125           s++; \
00126         } \
00127     } \
00128 }
00129 
00130   ExceptionInfo
00131     *exception;
00132 
00133   Image
00134     *floodplane_image;
00135 
00136   long
00137     offset,
00138     start,
00139     x1,
00140     x2,
00141     y;
00142 
00143   MagickBooleanType
00144     skip;
00145 
00146   MagickPixelPacket
00147     fill,
00148     pixel;
00149 
00150   PixelPacket
00151     fill_color;
00152 
00153   register const IndexPacket
00154     *indexes;
00155 
00156   register const PixelPacket
00157     *p;
00158 
00159   register long
00160     x;
00161 
00162   register PixelPacket
00163     *q;
00164 
00165   register SegmentInfo
00166     *s;
00167 
00168   SegmentInfo
00169     *segment_stack;
00170 
00171   /*
00172     Check boundary conditions.
00173   */
00174   assert(image != (Image *) NULL);
00175   assert(image->signature == MagickSignature);
00176   if (image->debug != MagickFalse)
00177     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00178   assert(draw_info != (DrawInfo *) NULL);
00179   assert(draw_info->signature == MagickSignature);
00180   if ((x_offset < 0) || (x_offset >= (long) image->columns))
00181     return(MagickFalse);
00182   if ((y_offset < 0) || (y_offset >= (long) image->rows))
00183     return(MagickFalse);
00184   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00185     return(MagickFalse);
00186   if (image->matte == MagickFalse)
00187     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
00188   /*
00189     Set floodfill state.
00190   */
00191   floodplane_image=CloneImage(image,0,0,MagickTrue,&image->exception);
00192   if (floodplane_image == (Image *) NULL)
00193     return(MagickFalse);
00194   (void) SetImageAlphaChannel(floodplane_image,OpaqueAlphaChannel);
00195   segment_stack=(SegmentInfo *) AcquireQuantumMemory(MaxStacksize,
00196     sizeof(*segment_stack));
00197   if (segment_stack == (SegmentInfo *) NULL)
00198     {
00199       floodplane_image=DestroyImage(floodplane_image);
00200       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00201         image->filename);
00202     }
00203   /*
00204     Push initial segment on stack.
00205   */
00206   exception=(&image->exception);
00207   x=x_offset;
00208   y=y_offset;
00209   start=0;
00210   s=segment_stack;
00211   PushSegmentStack(y,x,x,1);
00212   PushSegmentStack(y+1,x,x,-1);
00213   GetMagickPixelPacket(image,&fill);
00214   GetMagickPixelPacket(image,&pixel);
00215   while (s > segment_stack)
00216   {
00217     /*
00218       Pop segment off stack.
00219     */
00220     s--;
00221     x1=(long) s->x1;
00222     x2=(long) s->x2;
00223     offset=(long) s->y2;
00224     y=(long) s->y1+offset;
00225     /*
00226       Recolor neighboring pixels.
00227     */
00228     p=GetVirtualPixels(image,0,y,(unsigned long) (x1+1),1,exception);
00229     q=GetAuthenticPixels(floodplane_image,0,y,(unsigned long) (x1+1),1,
00230       exception);
00231     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00232       break;
00233     indexes=GetVirtualIndexQueue(image);
00234     p+=x1;
00235     q+=x1;
00236     for (x=x1; x >= 0; x--)
00237     {
00238       if (q->opacity == (Quantum) TransparentOpacity)
00239         break;
00240       SetMagickPixelPacket(image,p,indexes+x,&pixel);
00241       if (IsMagickColorSimilar(&pixel,target) == invert)
00242         break;
00243       q->opacity=(Quantum) TransparentOpacity;
00244       p--;
00245       q--;
00246     }
00247     if (SyncAuthenticPixels(floodplane_image,exception) == MagickFalse)
00248       break;
00249     skip=x >= x1 ? MagickTrue : MagickFalse;
00250     if (skip == MagickFalse)
00251       {
00252         start=x+1;
00253         if (start < x1)
00254           PushSegmentStack(y,start,x1-1,-offset);
00255         x=x1+1;
00256       }
00257     do
00258     {
00259       if (skip == MagickFalse)
00260         {
00261           if (x < (long) image->columns)
00262             {
00263               p=GetVirtualPixels(image,x,y,image->columns-x,1,exception);
00264               q=GetAuthenticPixels(floodplane_image,x,y,image->columns-x,1,
00265                 exception);
00266               if ((p == (const PixelPacket *) NULL) ||
00267                   (q == (PixelPacket *) NULL))
00268                 break;
00269               indexes=GetVirtualIndexQueue(image);
00270               for ( ; x < (long) image->columns; x++)
00271               {
00272                 if (q->opacity == (Quantum) TransparentOpacity)
00273                   break;
00274                 SetMagickPixelPacket(image,p,indexes+x,&pixel);
00275                 if (IsMagickColorSimilar(&pixel,target) == invert)
00276                   break;
00277                 q->opacity=(Quantum) TransparentOpacity;
00278                 p++;
00279                 q++;
00280               }
00281               if (SyncAuthenticPixels(floodplane_image,exception) == MagickFalse)
00282                 break;
00283             }
00284           PushSegmentStack(y,start,x-1,offset);
00285           if (x > (x2+1))
00286             PushSegmentStack(y,x2+1,x-1,-offset);
00287         }
00288       skip=MagickFalse;
00289       x++;
00290       if (x <= x2)
00291         {
00292           p=GetVirtualPixels(image,x,y,(unsigned long) (x2-x+1),1,exception);
00293           q=GetAuthenticPixels(floodplane_image,x,y,(unsigned long) (x2-x+1),1,
00294             exception);
00295           if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00296             break;
00297           indexes=GetVirtualIndexQueue(image);
00298           for ( ; x <= x2; x++)
00299           {
00300             if (q->opacity == (Quantum) TransparentOpacity)
00301               break;
00302             SetMagickPixelPacket(image,p,indexes+x,&pixel);
00303             if (IsMagickColorSimilar(&pixel,target) != invert)
00304               break;
00305             p++;
00306             q++;
00307           }
00308         }
00309       start=x;
00310     } while (x <= x2);
00311   }
00312   for (y=0; y < (long) image->rows; y++)
00313   {
00314     register IndexPacket
00315       *indexes;
00316 
00317     /*
00318       Tile fill color onto floodplane.
00319     */
00320     p=GetVirtualPixels(floodplane_image,0,y,image->columns,1,exception);
00321     q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
00322     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00323       break;
00324     indexes=GetAuthenticIndexQueue(image);
00325     for (x=0; x < (long) image->columns; x++)
00326     {
00327       if (p->opacity != OpaqueOpacity)
00328         {
00329           (void) GetFillColor(draw_info,x,y,&fill_color);
00330           SetMagickPixelPacket(image,&fill_color,(IndexPacket *) NULL,&fill);
00331           if (image->colorspace == CMYKColorspace)
00332             ConvertRGBToCMYK(&fill);
00333           if ((channel & RedChannel) != 0)
00334             q->red=RoundToQuantum(fill.red);
00335           if ((channel & GreenChannel) != 0)
00336             q->green=RoundToQuantum(fill.green);
00337           if ((channel & BlueChannel) != 0)
00338             q->blue=RoundToQuantum(fill.blue);
00339           if ((channel & OpacityChannel) != 0)
00340             q->opacity=RoundToQuantum(fill.opacity);
00341           if (((channel & IndexChannel) != 0) &&
00342               (image->colorspace == CMYKColorspace))
00343             indexes[x]=RoundToQuantum(fill.index);
00344         }
00345       p++;
00346       q++;
00347     }
00348     if (SyncAuthenticPixels(image,exception) == MagickFalse)
00349       break;
00350   }
00351   segment_stack=(SegmentInfo *) RelinquishMagickMemory(segment_stack);
00352   floodplane_image=DestroyImage(floodplane_image);
00353   return(y == (long) image->rows ? MagickTrue : MagickFalse);
00354 }
00355 
00356 /*
00357 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00358 %                                                                             %
00359 %                                                                             %
00360 %                                                                             %
00361 +     G r a d i e n t I m a g e                                               %
00362 %                                                                             %
00363 %                                                                             %
00364 %                                                                             %
00365 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00366 %
00367 %  GradientImage() applies a continuously smooth color transitions along a
00368 %  vector from one color to another.
00369 %
00370 %  Note, the interface of this method will change in the future to support
00371 %  more than one transistion.
00372 %
00373 %  The format of the GradientImage method is:
00374 %
00375 %      MagickBooleanType GradientImage(Image *image,const GradientType type,
00376 %        const SpreadMethod method,const PixelPacket *start_color,
00377 %        const PixelPacket *stop_color)
00378 %
00379 %  A description of each parameter follows:
00380 %
00381 %    o image: the image.
00382 %
00383 %    o type: the gradient type: linear or radial.
00384 %
00385 %    o spread: the gradient spread meathod: pad, reflect, or repeat.
00386 %
00387 %    o start_color: the start color.
00388 %
00389 %    o stop_color: the stop color.
00390 %
00391 % This provides a good example of making use of the DrawGradientImage
00392 % function and the gradient structure in draw_info.
00393 */
00394 MagickExport MagickBooleanType GradientImage(Image *image,
00395   const GradientType type,const SpreadMethod method,
00396   const PixelPacket *start_color,const PixelPacket *stop_color)
00397 {
00398   DrawInfo
00399     *draw_info;
00400 
00401   GradientInfo
00402     *gradient;
00403 
00404   MagickBooleanType
00405     status;
00406 
00407   register long
00408     i;
00409 
00410   /*
00411     Set gradient start-stop end points.
00412   */
00413   assert(image != (const Image *) NULL);
00414   assert(image->signature == MagickSignature);
00415   if (image->debug != MagickFalse)
00416     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00417   assert(start_color != (const PixelPacket *) NULL);
00418   assert(stop_color != (const PixelPacket *) NULL);
00419   draw_info=AcquireDrawInfo();
00420   gradient=(&draw_info->gradient);
00421   gradient->type=type;
00422   gradient->bounding_box.width=image->columns;
00423   gradient->bounding_box.height=image->rows;
00424   gradient->gradient_vector.x2=(double) image->columns-1.0;
00425   gradient->gradient_vector.y2=(double) image->rows-1.0;
00426   if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
00427     gradient->gradient_vector.x2=0.0;
00428   gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
00429   gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
00430   gradient->radius=MagickMax(gradient->center.x,gradient->center.y);
00431   gradient->spread=method;
00432   /*
00433     Define the gradient to fill between the stops.
00434   */
00435   gradient->number_stops=2;
00436   gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
00437     sizeof(*gradient->stops));
00438   if (gradient->stops == (StopInfo *) NULL)
00439     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
00440       image->filename);
00441   (void) ResetMagickMemory(gradient->stops,0,gradient->number_stops*
00442     sizeof(*gradient->stops));
00443   for (i=0; i < (long) gradient->number_stops; i++)
00444     GetMagickPixelPacket(image,&gradient->stops[i].color);
00445   SetMagickPixelPacket(image,start_color,(IndexPacket *) NULL,
00446     &gradient->stops[0].color);
00447   gradient->stops[0].offset=0.0;
00448   SetMagickPixelPacket(image,stop_color,(IndexPacket *) NULL,
00449     &gradient->stops[1].color);
00450   gradient->stops[1].offset=1.0;
00451   /*
00452     Draw a gradient on the image.
00453   */
00454   status=DrawGradientImage(image,draw_info);
00455   draw_info=DestroyDrawInfo(draw_info);
00456   if ((start_color->opacity == OpaqueOpacity) &&
00457       (stop_color->opacity == OpaqueOpacity))
00458     image->matte=MagickFalse;
00459   if ((IsGrayPixel(start_color) != MagickFalse) &&
00460       (IsGrayPixel(stop_color) != MagickFalse))
00461     image->type=GrayscaleType;
00462   return(status);
00463 }
00464 
00465 /*
00466 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00467 %                                                                             %
00468 %                                                                             %
00469 %                                                                             %
00470 %     O i l P a i n t I m a g e                                               %
00471 %                                                                             %
00472 %                                                                             %
00473 %                                                                             %
00474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00475 %
00476 %  OilPaintImage() applies a special effect filter that simulates an oil
00477 %  painting.  Each pixel is replaced by the most frequent color occurring
00478 %  in a circular region defined by radius.
00479 %
00480 %  The format of the OilPaintImage method is:
00481 %
00482 %      Image *OilPaintImage(const Image *image,const double radius,
00483 %        ExceptionInfo *exception)
00484 %
00485 %  A description of each parameter follows:
00486 %
00487 %    o image: the image.
00488 %
00489 %    o radius: the radius of the circular neighborhood.
00490 %
00491 %    o exception: return any errors or warnings in this structure.
00492 %
00493 */
00494 
00495 static unsigned long **DestroyHistogramThreadSet(unsigned long **histogram)
00496 {
00497   register long
00498     i;
00499 
00500   assert(histogram != (unsigned long **) NULL);
00501   for (i=0; i < (long) GetPixelCacheMaximumThreads(); i++)
00502     if (histogram[i] != (unsigned long *) NULL)
00503       histogram[i]=(unsigned long *) RelinquishMagickMemory(histogram[i]);
00504   return((unsigned long **) RelinquishMagickMemory(histogram));
00505 }
00506 
00507 static unsigned long **AcquireHistogramThreadSet(const size_t count)
00508 {
00509   register long
00510     i;
00511 
00512   unsigned long
00513     **histogram,
00514     number_threads;
00515 
00516   number_threads=GetPixelCacheMaximumThreads();
00517   histogram=(unsigned long **) AcquireQuantumMemory(number_threads,
00518     sizeof(*histogram));
00519   if (histogram == (unsigned long **) NULL)
00520     return((unsigned long **) NULL);
00521   (void) ResetMagickMemory(histogram,0,number_threads*sizeof(*histogram));
00522   for (i=0; i < (long) number_threads; i++)
00523   {
00524     histogram[i]=(unsigned long *) AcquireQuantumMemory(count,
00525       sizeof(**histogram));
00526     if (histogram[i] == (unsigned long *) NULL)
00527       return(DestroyHistogramThreadSet(histogram));
00528   }
00529   return(histogram);
00530 }
00531 
00532 MagickExport Image *OilPaintImage(const Image *image,const double radius,
00533   ExceptionInfo *exception)
00534 {
00535 #define NumberPaintBins  256
00536 #define OilPaintImageTag  "OilPaint/Image"
00537 
00538   Image
00539     *paint_image;
00540 
00541   long
00542     progress,
00543     y;
00544 
00545   MagickBooleanType
00546     status;
00547 
00548   unsigned long
00549     **histogram,
00550     width;
00551 
00552   ViewInfo
00553     *image_view,
00554     *paint_view;
00555 
00556   /*
00557     Initialize painted image attributes.
00558   */
00559   assert(image != (const Image *) NULL);
00560   assert(image->signature == MagickSignature);
00561   if (image->debug != MagickFalse)
00562     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00563   assert(exception != (ExceptionInfo *) NULL);
00564   assert(exception->signature == MagickSignature);
00565   width=GetOptimalKernelWidth2D(radius,0.5);
00566   if ((image->columns < width) || (image->rows < width))
00567     ThrowImageException(OptionError,"ImageSmallerThanRadius");
00568   paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
00569   if (paint_image == (Image *) NULL)
00570     return((Image *) NULL);
00571   if (SetImageStorageClass(paint_image,DirectClass) == MagickFalse)
00572     {
00573       InheritException(exception,&paint_image->exception);
00574       paint_image=DestroyImage(paint_image);
00575       return((Image *) NULL);
00576     }
00577   histogram=AcquireHistogramThreadSet(NumberPaintBins);
00578   if (histogram == (unsigned long **) NULL)
00579     {
00580       paint_image=DestroyImage(paint_image);
00581       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
00582     }
00583   /*
00584     Oil paint image.
00585   */
00586   status=MagickTrue;
00587   progress=0;
00588   image_view=AcquireCacheView(image);
00589   paint_view=AcquireCacheView(paint_image);
00590 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00591   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00592 #endif
00593   for (y=0; y < (long) image->rows; y++)
00594   {
00595     const IndexPacket
00596       *indexes;
00597 
00598     IndexPacket
00599       *paint_indexes;
00600 
00601     register const PixelPacket
00602       *p;
00603 
00604     register long
00605       id,
00606       x;
00607 
00608     register PixelPacket
00609       *q;
00610 
00611     if (status == MagickFalse)
00612       continue;
00613     p=GetCacheViewVirtualPixels(image_view,-((long) width/2L),y-(long) (width/
00614       2L),image->columns+width,width,exception);
00615     q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
00616       exception);
00617     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
00618       {
00619         status=MagickFalse;
00620         continue;
00621       }
00622     indexes=GetCacheViewVirtualIndexQueue(image_view);
00623     paint_indexes=GetCacheViewAuthenticIndexQueue(paint_view);
00624     id=GetPixelCacheThreadId();
00625     for (x=0; x < (long) image->columns; x++)
00626     {
00627       long
00628         j,
00629         k,
00630         v;
00631 
00632       register long
00633         i,
00634         u;
00635 
00636       unsigned long
00637         count;
00638 
00639       /*
00640         Assign most frequent color.
00641       */
00642       i=0;
00643       j=0;
00644       count=0;
00645       (void) ResetMagickMemory(histogram[id],0,NumberPaintBins*
00646         sizeof(**histogram));
00647       for (v=0; v < (long) width; v++)
00648       {
00649         for (u=0; u < (long) width; u++)
00650         {
00651           k=(long) ScaleQuantumToChar(PixelIntensityToQuantum(p+u+i));
00652           histogram[id][k]++;
00653           if (histogram[id][k] > count)
00654             {
00655               j=i+u;
00656               count=histogram[id][k];
00657             }
00658         }
00659         i+=image->columns+width;
00660       }
00661       *q=(*(p+j));
00662       if (image->colorspace == CMYKColorspace)
00663         paint_indexes[x]=indexes[x+j];
00664       p++;
00665       q++;
00666     }
00667     if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
00668       status=MagickFalse;
00669     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00670       {
00671         MagickBooleanType
00672           proceed;
00673 
00674 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00675   #pragma omp critical
00676 #endif
00677         proceed=SetImageProgress(image,OilPaintImageTag,progress++,image->rows);
00678         if (proceed == MagickFalse)
00679           status=MagickFalse;
00680       }
00681   }
00682   paint_view=DestroyCacheView(paint_view);
00683   image_view=DestroyCacheView(image_view);
00684   histogram=DestroyHistogramThreadSet(histogram);
00685   if (status == MagickFalse)
00686     paint_image=DestroyImage(paint_image);
00687   return(paint_image);
00688 }
00689 
00690 /*
00691 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00692 %                                                                             %
00693 %                                                                             %
00694 %                                                                             %
00695 %     O p a q u e P a i n t I m a g e                                         %
00696 %                                                                             %
00697 %                                                                             %
00698 %                                                                             %
00699 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00700 %
00701 %  OpaquePaintImage() changes any pixel that matches color with the color
00702 %  defined by fill.
00703 %
00704 %  By default color must match a particular pixel color exactly.  However,
00705 %  in many cases two colors may differ by a small amount.  Fuzz defines
00706 %  how much tolerance is acceptable to consider two colors as the same.
00707 %  For example, set fuzz to 10 and the color red at intensities of 100 and
00708 %  102 respectively are now interpreted as the same color.
00709 %
00710 %  The format of the OpaquePaintImage method is:
00711 %
00712 %      MagickBooleanType OpaquePaintImage(Image *image,
00713 %        const PixelPacket *target,const PixelPacket *fill,
00714 %        const MagickBooleanType invert)
00715 %      MagickBooleanType OpaquePaintImageChannel(Image *image,
00716 %        const ChannelType channel,const PixelPacket *target,
00717 %        const PixelPacket *fill,const MagickBooleanType invert)
00718 %
00719 %  A description of each parameter follows:
00720 %
00721 %    o image: the image.
00722 %
00723 %    o channel: the channel(s).
00724 %
00725 %    o target: the RGB value of the target color.
00726 %
00727 %    o fill: the replacement color.
00728 %
00729 %    o invert: paint any pixel that does not match the target color.
00730 %
00731 */
00732 
00733 MagickExport MagickBooleanType OpaquePaintImage(Image *image,
00734   const MagickPixelPacket *target,const MagickPixelPacket *fill,
00735   const MagickBooleanType invert)
00736 {
00737   return(OpaquePaintImageChannel(image,AllChannels,target,fill,invert));
00738 }
00739 
00740 MagickExport MagickBooleanType OpaquePaintImageChannel(Image *image,
00741   const ChannelType channel,const MagickPixelPacket *target,
00742   const MagickPixelPacket *fill,const MagickBooleanType invert)
00743 {
00744 #define OpaquePaintImageTag  "Opaque/Image"
00745 
00746   ExceptionInfo
00747     *exception;
00748 
00749   long
00750     progress,
00751     y;
00752 
00753   MagickBooleanType
00754     status;
00755 
00756   MagickPixelPacket
00757     zero;
00758 
00759   ViewInfo
00760     *image_view;
00761 
00762   assert(image != (Image *) NULL);
00763   assert(image->signature == MagickSignature);
00764   assert(target != (MagickPixelPacket *) NULL);
00765   assert(fill != (MagickPixelPacket *) NULL);
00766   if (image->debug != MagickFalse)
00767     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00768   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00769     return(MagickFalse);
00770   /*
00771     Make image color opaque.
00772   */
00773   status=MagickTrue;
00774   progress=0;
00775   exception=(&image->exception);
00776   GetMagickPixelPacket(image,&zero);
00777   image_view=AcquireCacheView(image);
00778 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00779   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00780 #endif
00781   for (y=0; y < (long) image->rows; y++)
00782   {
00783     MagickPixelPacket
00784       pixel;
00785 
00786     register IndexPacket
00787       *indexes;
00788 
00789     register long
00790       x;
00791 
00792     register PixelPacket
00793       *q;
00794 
00795     if (status == MagickFalse)
00796       continue;
00797     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00798     if (q == (PixelPacket *) NULL)
00799       {
00800         status=MagickFalse;
00801         continue;
00802       }
00803     indexes=GetCacheViewAuthenticIndexQueue(image_view);
00804     pixel=zero;
00805     for (x=0; x < (long) image->columns; x++)
00806     {
00807       SetMagickPixelPacket(image,q,indexes+x,&pixel);
00808       if (IsMagickColorSimilar(&pixel,target) != invert)
00809         {
00810           if ((channel & RedChannel) != 0)
00811             q->red=RoundToQuantum(fill->red);
00812           if ((channel & GreenChannel) != 0)
00813             q->green=RoundToQuantum(fill->green);
00814           if ((channel & BlueChannel) != 0)
00815             q->blue=RoundToQuantum(fill->blue);
00816           if ((channel & OpacityChannel) != 0)
00817             q->opacity=RoundToQuantum(fill->opacity);
00818           if (((channel & IndexChannel) != 0) &&
00819               (image->colorspace == CMYKColorspace))
00820             indexes[x]=RoundToQuantum(fill->index);
00821         }
00822       q++;
00823     }
00824     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00825       status=MagickFalse;
00826     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00827       {
00828         MagickBooleanType
00829           proceed;
00830 
00831 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00832   #pragma omp critical
00833 #endif
00834         proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
00835           image->rows);
00836         if (proceed == MagickFalse)
00837           status=MagickFalse;
00838       }
00839   }
00840   image_view=DestroyCacheView(image_view);
00841   return(status);
00842 }
00843 
00844 /*
00845 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00846 %                                                                             %
00847 %                                                                             %
00848 %                                                                             %
00849 %     T r a n s p a r e n t P a i n t I m a g e                               %
00850 %                                                                             %
00851 %                                                                             %
00852 %                                                                             %
00853 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00854 %
00855 %  TransparentPaintImage() changes the opacity value associated with any pixel
00856 %  that matches color to the value defined by opacity.
00857 %
00858 %  By default color must match a particular pixel color exactly.  However,
00859 %  in many cases two colors may differ by a small amount.  Fuzz defines
00860 %  how much tolerance is acceptable to consider two colors as the same.
00861 %  For example, set fuzz to 10 and the color red at intensities of 100 and
00862 %  102 respectively are now interpreted as the same color.
00863 %
00864 %  The format of the TransparentPaintImage method is:
00865 %
00866 %      MagickBooleanType TransparentPaintImage(Image *image,
00867 %        const MagickPixelPacket *target,const Quantum opacity,
00868 %        const MagickBooleanType invert)
00869 %
00870 %  A description of each parameter follows:
00871 %
00872 %    o image: the image.
00873 %
00874 %    o target: the target color.
00875 %
00876 %    o opacity: the replacement opacity value.
00877 %
00878 %    o invert: paint any pixel that does not match the target color.
00879 %
00880 */
00881 MagickExport MagickBooleanType TransparentPaintImage(Image *image,
00882   const MagickPixelPacket *target,const Quantum opacity,
00883   const MagickBooleanType invert)
00884 {
00885 #define TransparentPaintImageTag  "Transparent/Image"
00886 
00887   ExceptionInfo
00888     *exception;
00889 
00890   long
00891     progress,
00892     y;
00893 
00894   MagickBooleanType
00895     status;
00896 
00897   MagickPixelPacket
00898     zero;
00899 
00900   ViewInfo
00901     *image_view;
00902 
00903   assert(image != (Image *) NULL);
00904   assert(image->signature == MagickSignature);
00905   assert(target != (MagickPixelPacket *) NULL);
00906   if (image->debug != MagickFalse)
00907     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00908   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
00909     return(MagickFalse);
00910   if (image->matte == MagickFalse)
00911     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
00912   /*
00913     Make image color transparent.
00914   */
00915   status=MagickTrue;
00916   progress=0;
00917   exception=(&image->exception);
00918   GetMagickPixelPacket(image,&zero);
00919   image_view=AcquireCacheView(image);
00920 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00921   #pragma omp parallel for schedule(dynamic,8) shared(progress,status)
00922 #endif
00923   for (y=0; y < (long) image->rows; y++)
00924   {
00925     MagickPixelPacket
00926       pixel;
00927 
00928     register IndexPacket
00929       *indexes;
00930 
00931     register long
00932       x;
00933 
00934     register PixelPacket
00935       *q;
00936 
00937     if (status == MagickFalse)
00938       continue;
00939     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
00940     if (q == (PixelPacket *) NULL)
00941       {
00942         status=MagickFalse;
00943         continue;
00944       }
00945     indexes=GetCacheViewAuthenticIndexQueue(image_view);
00946     pixel=zero;
00947     for (x=0; x < (long) image->columns; x++)
00948     {
00949       SetMagickPixelPacket(image,q,indexes+x,&pixel);
00950       if (IsMagickColorSimilar(&pixel,target) != invert)
00951         q->opacity=opacity;
00952       q++;
00953     }
00954     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
00955       status=MagickFalse;
00956     if (image->progress_monitor != (MagickProgressMonitor) NULL)
00957       {
00958         MagickBooleanType
00959           proceed;
00960 
00961 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00962   #pragma omp critical
00963 #endif
00964         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
00965           image->rows);
00966         if (proceed == MagickFalse)
00967           status=MagickFalse;
00968       }
00969   }
00970   image_view=DestroyCacheView(image_view);
00971   return(status);
00972 }
00973 
00974 /*
00975 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00976 %                                                                             %
00977 %                                                                             %
00978 %                                                                             %
00979 %     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                   %
00980 %                                                                             %
00981 %                                                                             %
00982 %                                                                             %
00983 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00984 %
00985 %  TransparentPaintImageChroma() changes the opacity value associated with any
00986 %  pixel that matches color to the value defined by opacity.
00987 %
00988 %  As there is one fuzz value for the all the channels, the
00989 %  TransparentPaintImage() API is not suitable for the operations like chroma,
00990 %  where the tolerance for similarity of two color component (RGB) can be
00991 %  different, Thus we define this method take two target pixels (one
00992 %  low and one hight) and all the pixels of an image which are lying between
00993 %  these two pixels are made transparent.
00994 %
00995 %  The format of the TransparentPaintImage method is:
00996 %
00997 %      MagickBooleanType TransparentPaintImage(Image *image,
00998 %        const MagickPixelPacket *low,const MagickPixelPacket *hight,
00999 %        const Quantum opacity,const MagickBooleanType invert)
01000 %
01001 %  A description of each parameter follows:
01002 %
01003 %    o image: the image.
01004 %
01005 %    o low: the low target color.
01006 %
01007 %    o high: the high target color.
01008 %
01009 %    o opacity: the replacement opacity value.
01010 %
01011 %    o invert: paint any pixel that does not match the target color.
01012 %
01013 */
01014 MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
01015   const MagickPixelPacket *low,const MagickPixelPacket *high,
01016   const Quantum opacity,const MagickBooleanType invert)
01017 {
01018 #define TransparentPaintImageTag  "Transparent/Image"
01019 
01020   ExceptionInfo
01021     *exception;
01022 
01023   long
01024     progress,
01025     y;
01026 
01027   MagickBooleanType
01028     status;
01029 
01030   ViewInfo
01031     *image_view;
01032 
01033   assert(image != (Image *) NULL);
01034   assert(image->signature == MagickSignature);
01035   assert(high != (MagickPixelPacket *) NULL);
01036   assert(low != (MagickPixelPacket *) NULL);
01037   if (image->debug != MagickFalse)
01038     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
01039   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
01040     return(MagickFalse);
01041   if (image->matte == MagickFalse)
01042     (void) SetImageAlphaChannel(image,ResetAlphaChannel);
01043   /*
01044     Make image color transparent.
01045   */
01046   status=MagickTrue;
01047   progress=0;
01048   exception=(&image->