]> granicus.if.org Git - imagemagick/blob - magick/paint.c
(no commit message)
[imagemagick] / magick / paint.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                      PPPP    AAA   IIIII  N   N  TTTTT                      %
7 %                      P   P  A   A    I    NN  N    T                        %
8 %                      PPPP   AAAAA    I    N N N    T                        %
9 %                      P      A   A    I    N  NN    T                        %
10 %                      P      A   A  IIIII  N   N    T                        %
11 %                                                                             %
12 %                                                                             %
13 %                        Methods to Paint on an Image                         %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                                 July 1998                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40  Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/color.h"
44 #include "magick/color-private.h"
45 #include "magick/colorspace-private.h"
46 #include "magick/composite.h"
47 #include "magick/composite-private.h"
48 #include "magick/draw.h"
49 #include "magick/draw-private.h"
50 #include "magick/exception.h"
51 #include "magick/exception-private.h"
52 #include "magick/gem.h"
53 #include "magick/monitor.h"
54 #include "magick/monitor-private.h"
55 #include "magick/paint.h"
56 #include "magick/pixel-private.h"
57 #include "magick/string_.h"
58 #include "magick/thread-private.h"
59 \f
60 /*
61 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62 %                                                                             %
63 %                                                                             %
64 %                                                                             %
65 %   F l o o d f i l l P a i n t I m a g e                                     %
66 %                                                                             %
67 %                                                                             %
68 %                                                                             %
69 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
70 %
71 %  FloodfillPaintImage() changes the color value of any pixel that matches
72 %  target and is an immediate neighbor.  If the method FillToBorderMethod is
73 %  specified, the color value is changed for any neighbor pixel that does not
74 %  match the bordercolor member of image.
75 %
76 %  By default target must match a particular pixel color exactly.
77 %  However, in many cases two colors may differ by a small amount.  The
78 %  fuzz member of image defines how much tolerance is acceptable to
79 %  consider two colors as the same.  For example, set fuzz to 10 and the
80 %  color red at intensities of 100 and 102 respectively are now
81 %  interpreted as the same color for the purposes of the floodfill.
82 %
83 %  The format of the FloodfillPaintImage method is:
84 %
85 %      MagickBooleanType FloodfillPaintImage(Image *image,
86 %        const ChannelType channel,const DrawInfo *draw_info,
87 %        const MagickPixelPacket target,const ssize_t x_offset,
88 %        const ssize_t y_offset,const MagickBooleanType invert)
89 %
90 %  A description of each parameter follows:
91 %
92 %    o image: the image.
93 %
94 %    o channel: the channel(s).
95 %
96 %    o draw_info: the draw info.
97 %
98 %    o target: the RGB value of the target color.
99 %
100 %    o x_offset,y_offset: the starting location of the operation.
101 %
102 %    o invert: paint any pixel that does not match the target color.
103 %
104 */
105 MagickExport MagickBooleanType FloodfillPaintImage(Image *image,
106   const ChannelType channel,const DrawInfo *draw_info,
107   const MagickPixelPacket *target,const ssize_t x_offset,const ssize_t y_offset,
108   const MagickBooleanType invert)
109 {
110 #define MaxStacksize  (1UL << 15)
111 #define PushSegmentStack(up,left,right,delta) \
112 { \
113   if (s >= (segment_stack+MaxStacksize)) \
114     ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \
115   else \
116     { \
117       if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (ssize_t) image->rows)) \
118         { \
119           s->x1=(double) (left); \
120           s->y1=(double) (up); \
121           s->x2=(double) (right); \
122           s->y2=(double) (delta); \
123           s++; \
124         } \
125     } \
126 }
127
128   CacheView
129     *floodplane_view,
130     *image_view;
131
132   ExceptionInfo
133     *exception;
134
135   Image
136     *floodplane_image;
137
138   MagickBooleanType
139     skip;
140
141   MagickPixelPacket
142     fill,
143     pixel;
144
145   PixelPacket
146     fill_color;
147
148   register SegmentInfo
149     *s;
150
151   SegmentInfo
152     *segment_stack;
153
154   ssize_t
155     offset,
156     start,
157     x,
158     x1,
159     x2,
160     y;
161
162   /*
163     Check boundary conditions.
164   */
165   assert(image != (Image *) NULL);
166   assert(image->signature == MagickSignature);
167   if (image->debug != MagickFalse)
168     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
169   assert(draw_info != (DrawInfo *) NULL);
170   assert(draw_info->signature == MagickSignature);
171   if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
172     return(MagickFalse);
173   if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
174     return(MagickFalse);
175   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
176     return(MagickFalse);
177   if (image->matte == MagickFalse)
178     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
179   /*
180     Set floodfill state.
181   */
182   floodplane_image=CloneImage(image,0,0,MagickTrue,&image->exception);
183   if (floodplane_image == (Image *) NULL)
184     return(MagickFalse);
185   (void) SetImageAlphaChannel(floodplane_image,OpaqueAlphaChannel);
186   segment_stack=(SegmentInfo *) AcquireQuantumMemory(MaxStacksize,
187     sizeof(*segment_stack));
188   if (segment_stack == (SegmentInfo *) NULL)
189     {
190       floodplane_image=DestroyImage(floodplane_image);
191       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
192         image->filename);
193     }
194   /*
195     Push initial segment on stack.
196   */
197   exception=(&image->exception);
198   x=x_offset;
199   y=y_offset;
200   start=0;
201   s=segment_stack;
202   PushSegmentStack(y,x,x,1);
203   PushSegmentStack(y+1,x,x,-1);
204   GetMagickPixelPacket(image,&fill);
205   GetMagickPixelPacket(image,&pixel);
206   image_view=AcquireCacheView(image);
207   floodplane_view=AcquireCacheView(floodplane_image);
208   while (s > segment_stack)
209   {
210     register const IndexPacket
211       *restrict indexes;
212
213     register const PixelPacket
214       *restrict p;
215
216     register ssize_t
217       x;
218
219     register PixelPacket
220       *restrict q;
221
222     /*
223       Pop segment off stack.
224     */
225     s--;
226     x1=(ssize_t) s->x1;
227     x2=(ssize_t) s->x2;
228     offset=(ssize_t) s->y2;
229     y=(ssize_t) s->y1+offset;
230     /*
231       Recolor neighboring pixels.
232     */
233     p=GetCacheViewVirtualPixels(image_view,0,y,(size_t) (x1+1),1,exception);
234     q=GetCacheViewAuthenticPixels(floodplane_view,0,y,(size_t) (x1+1),1,
235       exception);
236     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
237       break;
238     indexes=GetCacheViewVirtualIndexQueue(image_view);
239     p+=x1;
240     q+=x1;
241     for (x=x1; x >= 0; x--)
242     {
243       if (q->opacity == (Quantum) TransparentOpacity)
244         break;
245       SetMagickPixelPacket(image,p,indexes+x,&pixel);
246       if (IsMagickColorSimilar(&pixel,target) == invert)
247         break;
248       q->opacity=(Quantum) TransparentOpacity;
249       p--;
250       q--;
251     }
252     if (SyncCacheViewAuthenticPixels(floodplane_view,exception) == MagickFalse)
253       break;
254     skip=x >= x1 ? MagickTrue : MagickFalse;
255     if (skip == MagickFalse)
256       {
257         start=x+1;
258         if (start < x1)
259           PushSegmentStack(y,start,x1-1,-offset);
260         x=x1+1;
261       }
262     do
263     {
264       if (skip == MagickFalse)
265         {
266           if (x < (ssize_t) image->columns)
267             {
268               p=GetCacheViewVirtualPixels(image_view,x,y,image->columns-x,1,
269                 exception);
270               q=GetCacheViewAuthenticPixels(floodplane_view,x,y,
271                 image->columns-x,1,exception);
272               if ((p == (const PixelPacket *) NULL) ||
273                   (q == (PixelPacket *) NULL))
274                 break;
275               indexes=GetCacheViewVirtualIndexQueue(image_view);
276               for ( ; x < (ssize_t) image->columns; x++)
277               {
278                 if (q->opacity == (Quantum) TransparentOpacity)
279                   break;
280                 SetMagickPixelPacket(image,p,indexes+x,&pixel);
281                 if (IsMagickColorSimilar(&pixel,target) == invert)
282                   break;
283                 q->opacity=(Quantum) TransparentOpacity;
284                 p++;
285                 q++;
286               }
287               if (SyncCacheViewAuthenticPixels(floodplane_view,exception) == MagickFalse)
288                 break;
289             }
290           PushSegmentStack(y,start,x-1,offset);
291           if (x > (x2+1))
292             PushSegmentStack(y,x2+1,x-1,-offset);
293         }
294       skip=MagickFalse;
295       x++;
296       if (x <= x2)
297         {
298           p=GetCacheViewVirtualPixels(image_view,x,y,(size_t) (x2-x+1),1,
299             exception);
300           q=GetCacheViewAuthenticPixels(floodplane_view,x,y,(size_t) (x2-x+1),1,
301             exception);
302           if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
303             break;
304           indexes=GetCacheViewVirtualIndexQueue(image_view);
305           for ( ; x <= x2; x++)
306           {
307             if (q->opacity == (Quantum) TransparentOpacity)
308               break;
309             SetMagickPixelPacket(image,p,indexes+x,&pixel);
310             if (IsMagickColorSimilar(&pixel,target) != invert)
311               break;
312             p++;
313             q++;
314           }
315         }
316       start=x;
317     } while (x <= x2);
318   }
319   for (y=0; y < (ssize_t) image->rows; y++)
320   {
321     register const PixelPacket
322       *restrict p;
323
324     register IndexPacket
325       *restrict indexes;
326
327     register ssize_t
328       x;
329
330     register PixelPacket
331       *restrict q;
332
333     /*
334       Tile fill color onto floodplane.
335     */
336     p=GetCacheViewVirtualPixels(floodplane_view,0,y,image->columns,1,
337       exception);
338     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
339     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
340       break;
341     indexes=GetCacheViewAuthenticIndexQueue(image_view);
342     for (x=0; x < (ssize_t) image->columns; x++)
343     {
344       if (p->opacity != OpaqueOpacity)
345         {
346           (void) GetFillColor(draw_info,x,y,&fill_color);
347           SetMagickPixelPacket(image,&fill_color,(IndexPacket *) NULL,&fill);
348           if (image->colorspace == CMYKColorspace)
349             ConvertRGBToCMYK(&fill);
350           if ((channel & RedChannel) != 0)
351             q->red=ClampToQuantum(fill.red);
352           if ((channel & GreenChannel) != 0)
353             q->green=ClampToQuantum(fill.green);
354           if ((channel & BlueChannel) != 0)
355             q->blue=ClampToQuantum(fill.blue);
356           if ((channel & OpacityChannel) != 0)
357             q->opacity=ClampToQuantum(fill.opacity);
358           if (((channel & IndexChannel) != 0) &&
359               (image->colorspace == CMYKColorspace))
360             indexes[x]=ClampToQuantum(fill.index);
361         }
362       p++;
363       q++;
364     }
365     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
366       break;
367   }
368   floodplane_view=DestroyCacheView(floodplane_view);
369   image_view=DestroyCacheView(image_view);
370   segment_stack=(SegmentInfo *) RelinquishMagickMemory(segment_stack);
371   floodplane_image=DestroyImage(floodplane_image);
372   return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
373 }
374 \f
375 /*
376 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
377 %                                                                             %
378 %                                                                             %
379 %                                                                             %
380 +     G r a d i e n t I m a g e                                               %
381 %                                                                             %
382 %                                                                             %
383 %                                                                             %
384 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
385 %
386 %  GradientImage() applies a continuously smooth color transitions along a
387 %  vector from one color to another.
388 %
389 %  Note, the interface of this method will change in the future to support
390 %  more than one transistion.
391 %
392 %  The format of the GradientImage method is:
393 %
394 %      MagickBooleanType GradientImage(Image *image,const GradientType type,
395 %        const SpreadMethod method,const PixelPacket *start_color,
396 %        const PixelPacket *stop_color)
397 %
398 %  A description of each parameter follows:
399 %
400 %    o image: the image.
401 %
402 %    o type: the gradient type: linear or radial.
403 %
404 %    o spread: the gradient spread meathod: pad, reflect, or repeat.
405 %
406 %    o start_color: the start color.
407 %
408 %    o stop_color: the stop color.
409 %
410 % This provides a good example of making use of the DrawGradientImage
411 % function and the gradient structure in draw_info.
412 */
413
414 static inline double MagickMax(const double x,const double y)
415 {
416   return(x > y ? x : y);
417 }
418
419 MagickExport MagickBooleanType GradientImage(Image *image,
420   const GradientType type,const SpreadMethod method,
421   const PixelPacket *start_color,const PixelPacket *stop_color)
422 {
423   DrawInfo
424     *draw_info;
425
426   GradientInfo
427     *gradient;
428
429   MagickBooleanType
430     status;
431
432   register ssize_t
433     i;
434
435   /*
436     Set gradient start-stop end points.
437   */
438   assert(image != (const Image *) NULL);
439   assert(image->signature == MagickSignature);
440   if (image->debug != MagickFalse)
441     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
442   assert(start_color != (const PixelPacket *) NULL);
443   assert(stop_color != (const PixelPacket *) NULL);
444   draw_info=AcquireDrawInfo();
445   gradient=(&draw_info->gradient);
446   gradient->type=type;
447   gradient->bounding_box.width=image->columns;
448   gradient->bounding_box.height=image->rows;
449   gradient->gradient_vector.x2=(double) image->columns-1.0;
450   gradient->gradient_vector.y2=(double) image->rows-1.0;
451   if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
452     gradient->gradient_vector.x2=0.0;
453   gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
454   gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
455   gradient->radius=MagickMax(gradient->center.x,gradient->center.y);
456   gradient->spread=method;
457   /*
458     Define the gradient to fill between the stops.
459   */
460   gradient->number_stops=2;
461   gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
462     sizeof(*gradient->stops));
463   if (gradient->stops == (StopInfo *) NULL)
464     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
465       image->filename);
466   (void) ResetMagickMemory(gradient->stops,0,gradient->number_stops*
467     sizeof(*gradient->stops));
468   for (i=0; i < (ssize_t) gradient->number_stops; i++)
469     GetMagickPixelPacket(image,&gradient->stops[i].color);
470   SetMagickPixelPacket(image,start_color,(IndexPacket *) NULL,
471     &gradient->stops[0].color);
472   gradient->stops[0].offset=0.0;
473   SetMagickPixelPacket(image,stop_color,(IndexPacket *) NULL,
474     &gradient->stops[1].color);
475   gradient->stops[1].offset=1.0;
476   /*
477     Draw a gradient on the image.
478   */
479   status=DrawGradientImage(image,draw_info);
480   draw_info=DestroyDrawInfo(draw_info);
481   if ((start_color->opacity == OpaqueOpacity) &&
482       (stop_color->opacity == OpaqueOpacity))
483     image->matte=MagickFalse;
484   if ((IsGrayPixel(start_color) != MagickFalse) &&
485       (IsGrayPixel(stop_color) != MagickFalse))
486     image->type=GrayscaleType;
487   return(status);
488 }
489 \f
490 /*
491 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
492 %                                                                             %
493 %                                                                             %
494 %                                                                             %
495 %     O i l P a i n t I m a g e                                               %
496 %                                                                             %
497 %                                                                             %
498 %                                                                             %
499 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500 %
501 %  OilPaintImage() applies a special effect filter that simulates an oil
502 %  painting.  Each pixel is replaced by the most frequent color occurring
503 %  in a circular region defined by radius.
504 %
505 %  The format of the OilPaintImage method is:
506 %
507 %      Image *OilPaintImage(const Image *image,const double radius,
508 %        ExceptionInfo *exception)
509 %
510 %  A description of each parameter follows:
511 %
512 %    o image: the image.
513 %
514 %    o radius: the radius of the circular neighborhood.
515 %
516 %    o exception: return any errors or warnings in this structure.
517 %
518 */
519
520 static size_t **DestroyHistogramThreadSet(size_t **histogram)
521 {
522   register ssize_t
523     i;
524
525   assert(histogram != (size_t **) NULL);
526   for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
527     if (histogram[i] != (size_t *) NULL)
528       histogram[i]=(size_t *) RelinquishMagickMemory(histogram[i]);
529   histogram=(size_t **) RelinquishMagickMemory(histogram);
530   return(histogram);
531 }
532
533 static size_t **AcquireHistogramThreadSet(const size_t count)
534 {
535   register ssize_t
536     i;
537
538   size_t
539     **histogram,
540     number_threads;
541
542   number_threads=GetOpenMPMaximumThreads();
543   histogram=(size_t **) AcquireQuantumMemory(number_threads,
544     sizeof(*histogram));
545   if (histogram == (size_t **) NULL)
546     return((size_t **) NULL);
547   (void) ResetMagickMemory(histogram,0,number_threads*sizeof(*histogram));
548   for (i=0; i < (ssize_t) number_threads; i++)
549   {
550     histogram[i]=(size_t *) AcquireQuantumMemory(count,
551       sizeof(**histogram));
552     if (histogram[i] == (size_t *) NULL)
553       return(DestroyHistogramThreadSet(histogram));
554   }
555   return(histogram);
556 }
557
558 MagickExport Image *OilPaintImage(const Image *image,const double radius,
559   ExceptionInfo *exception)
560 {
561 #define NumberPaintBins  256
562 #define OilPaintImageTag  "OilPaint/Image"
563
564   CacheView
565     *image_view,
566     *paint_view;
567
568   Image
569     *paint_image;
570
571   MagickBooleanType
572     status;
573
574   MagickOffsetType
575     progress;
576
577   size_t
578     **restrict histograms,
579     width;
580
581   ssize_t
582     y;
583
584   /*
585     Initialize painted image attributes.
586   */
587   assert(image != (const Image *) NULL);
588   assert(image->signature == MagickSignature);
589   if (image->debug != MagickFalse)
590     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
591   assert(exception != (ExceptionInfo *) NULL);
592   assert(exception->signature == MagickSignature);
593   width=GetOptimalKernelWidth2D(radius,0.5);
594   paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
595   if (paint_image == (Image *) NULL)
596     return((Image *) NULL);
597   if (SetImageStorageClass(paint_image,DirectClass) == MagickFalse)
598     {
599       InheritException(exception,&paint_image->exception);
600       paint_image=DestroyImage(paint_image);
601       return((Image *) NULL);
602     }
603   histograms=AcquireHistogramThreadSet(NumberPaintBins);
604   if (histograms == (size_t **) NULL)
605     {
606       paint_image=DestroyImage(paint_image);
607       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
608     }
609   /*
610     Oil paint image.
611   */
612   status=MagickTrue;
613   progress=0;
614   image_view=AcquireCacheView(image);
615   paint_view=AcquireCacheView(paint_image);
616 #if defined(MAGICKCORE_OPENMP_SUPPORT)
617   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
618 #endif
619   for (y=0; y < (ssize_t) image->rows; y++)
620   {
621     register const IndexPacket
622       *restrict indexes;
623
624     register const PixelPacket
625       *restrict p;
626
627     register IndexPacket
628       *restrict paint_indexes;
629
630     register ssize_t
631       x;
632
633     register PixelPacket
634       *restrict q;
635
636     register size_t
637       *histogram;
638
639     if (status == MagickFalse)
640       continue;
641     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
642       (width/2L),image->columns+width,width,exception);
643     q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
644       exception);
645     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
646       {
647         status=MagickFalse;
648         continue;
649       }
650     indexes=GetCacheViewVirtualIndexQueue(image_view);
651     paint_indexes=GetCacheViewAuthenticIndexQueue(paint_view);
652     histogram=histograms[GetOpenMPThreadId()];
653     for (x=0; x < (ssize_t) image->columns; x++)
654     {
655       register ssize_t
656         i,
657         u;
658
659       size_t
660         count;
661
662       ssize_t
663         j,
664         k,
665         v;
666
667       /*
668         Assign most frequent color.
669       */
670       i=0;
671       j=0;
672       count=0;
673       (void) ResetMagickMemory(histogram,0,NumberPaintBins*sizeof(*histogram));
674       for (v=0; v < (ssize_t) width; v++)
675       {
676         for (u=0; u < (ssize_t) width; u++)
677         {
678           k=(ssize_t) ScaleQuantumToChar(PixelIntensityToQuantum(p+u+i));
679           histogram[k]++;
680           if (histogram[k] > count)
681             {
682               j=i+u;
683               count=histogram[k];
684             }
685         }
686         i+=(ssize_t) (image->columns+width);
687       }
688       *q=(*(p+j));
689       if (image->colorspace == CMYKColorspace)
690         paint_indexes[x]=indexes[x+j];
691       p++;
692       q++;
693     }
694     if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
695       status=MagickFalse;
696     if (image->progress_monitor != (MagickProgressMonitor) NULL)
697       {
698         MagickBooleanType
699           proceed;
700
701 #if defined(MAGICKCORE_OPENMP_SUPPORT)
702   #pragma omp critical (MagickCore_OilPaintImage)
703 #endif
704         proceed=SetImageProgress(image,OilPaintImageTag,progress++,image->rows);
705         if (proceed == MagickFalse)
706           status=MagickFalse;
707       }
708   }
709   paint_view=DestroyCacheView(paint_view);
710   image_view=DestroyCacheView(image_view);
711   histograms=DestroyHistogramThreadSet(histograms);
712   if (status == MagickFalse)
713     paint_image=DestroyImage(paint_image);
714   return(paint_image);
715 }
716 \f
717 /*
718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719 %                                                                             %
720 %                                                                             %
721 %                                                                             %
722 %     O p a q u e P a i n t I m a g e                                         %
723 %                                                                             %
724 %                                                                             %
725 %                                                                             %
726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
727 %
728 %  OpaquePaintImage() changes any pixel that matches color with the color
729 %  defined by fill.
730 %
731 %  By default color must match a particular pixel color exactly.  However,
732 %  in many cases two colors may differ by a small amount.  Fuzz defines
733 %  how much tolerance is acceptable to consider two colors as the same.
734 %  For example, set fuzz to 10 and the color red at intensities of 100 and
735 %  102 respectively are now interpreted as the same color.
736 %
737 %  The format of the OpaquePaintImage method is:
738 %
739 %      MagickBooleanType OpaquePaintImage(Image *image,
740 %        const PixelPacket *target,const PixelPacket *fill,
741 %        const MagickBooleanType invert)
742 %      MagickBooleanType OpaquePaintImageChannel(Image *image,
743 %        const ChannelType channel,const PixelPacket *target,
744 %        const PixelPacket *fill,const MagickBooleanType invert)
745 %
746 %  A description of each parameter follows:
747 %
748 %    o image: the image.
749 %
750 %    o channel: the channel(s).
751 %
752 %    o target: the RGB value of the target color.
753 %
754 %    o fill: the replacement color.
755 %
756 %    o invert: paint any pixel that does not match the target color.
757 %
758 */
759
760 MagickExport MagickBooleanType OpaquePaintImage(Image *image,
761   const MagickPixelPacket *target,const MagickPixelPacket *fill,
762   const MagickBooleanType invert)
763 {
764   return(OpaquePaintImageChannel(image,AllChannels,target,fill,invert));
765 }
766
767 MagickExport MagickBooleanType OpaquePaintImageChannel(Image *image,
768   const ChannelType channel,const MagickPixelPacket *target,
769   const MagickPixelPacket *fill,const MagickBooleanType invert)
770 {
771 #define OpaquePaintImageTag  "Opaque/Image"
772
773   CacheView
774     *image_view;
775
776   ExceptionInfo
777     *exception;
778
779   MagickBooleanType
780     status;
781
782   MagickOffsetType
783     progress;
784
785   MagickPixelPacket
786     zero;
787
788   ssize_t
789     y;
790
791   assert(image != (Image *) NULL);
792   assert(image->signature == MagickSignature);
793   assert(target != (MagickPixelPacket *) NULL);
794   assert(fill != (MagickPixelPacket *) NULL);
795   if (image->debug != MagickFalse)
796     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
797   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
798     return(MagickFalse);
799   /*
800     Make image color opaque.
801   */
802   status=MagickTrue;
803   progress=0;
804   exception=(&image->exception);
805   GetMagickPixelPacket(image,&zero);
806   image_view=AcquireCacheView(image);
807 #if defined(MAGICKCORE_OPENMP_SUPPORT)
808   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
809 #endif
810   for (y=0; y < (ssize_t) image->rows; y++)
811   {
812     MagickPixelPacket
813       pixel;
814
815     register IndexPacket
816       *restrict indexes;
817
818     register ssize_t
819       x;
820
821     register PixelPacket
822       *restrict q;
823
824     if (status == MagickFalse)
825       continue;
826     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
827     if (q == (PixelPacket *) NULL)
828       {
829         status=MagickFalse;
830         continue;
831       }
832     indexes=GetCacheViewAuthenticIndexQueue(image_view);
833     pixel=zero;
834     for (x=0; x < (ssize_t) image->columns; x++)
835     {
836       SetMagickPixelPacket(image,q,indexes+x,&pixel);
837       if (IsMagickColorSimilar(&pixel,target) != invert)
838         {
839           if ((channel & RedChannel) != 0)
840             q->red=ClampToQuantum(fill->red);
841           if ((channel & GreenChannel) != 0)
842             q->green=ClampToQuantum(fill->green);
843           if ((channel & BlueChannel) != 0)
844             q->blue=ClampToQuantum(fill->blue);
845           if ((channel & OpacityChannel) != 0)
846             q->opacity=ClampToQuantum(fill->opacity);
847           if (((channel & IndexChannel) != 0) &&
848               (image->colorspace == CMYKColorspace))
849             indexes[x]=ClampToQuantum(fill->index);
850         }
851       q++;
852     }
853     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
854       status=MagickFalse;
855     if (image->progress_monitor != (MagickProgressMonitor) NULL)
856       {
857         MagickBooleanType
858           proceed;
859
860 #if defined(MAGICKCORE_OPENMP_SUPPORT)
861   #pragma omp critical (MagickCore_OpaquePaintImageChannel)
862 #endif
863         proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
864           image->rows);
865         if (proceed == MagickFalse)
866           status=MagickFalse;
867       }
868   }
869   image_view=DestroyCacheView(image_view);
870   return(status);
871 }
872 \f
873 /*
874 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
875 %                                                                             %
876 %                                                                             %
877 %                                                                             %
878 %     T r a n s p a r e n t P a i n t I m a g e                               %
879 %                                                                             %
880 %                                                                             %
881 %                                                                             %
882 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
883 %
884 %  TransparentPaintImage() changes the opacity value associated with any pixel
885 %  that matches color to the value defined by opacity.
886 %
887 %  By default color must match a particular pixel color exactly.  However,
888 %  in many cases two colors may differ by a small amount.  Fuzz defines
889 %  how much tolerance is acceptable to consider two colors as the same.
890 %  For example, set fuzz to 10 and the color red at intensities of 100 and
891 %  102 respectively are now interpreted as the same color.
892 %
893 %  The format of the TransparentPaintImage method is:
894 %
895 %      MagickBooleanType TransparentPaintImage(Image *image,
896 %        const MagickPixelPacket *target,const Quantum opacity,
897 %        const MagickBooleanType invert)
898 %
899 %  A description of each parameter follows:
900 %
901 %    o image: the image.
902 %
903 %    o target: the target color.
904 %
905 %    o opacity: the replacement opacity value.
906 %
907 %    o invert: paint any pixel that does not match the target color.
908 %
909 */
910 MagickExport MagickBooleanType TransparentPaintImage(Image *image,
911   const MagickPixelPacket *target,const Quantum opacity,
912   const MagickBooleanType invert)
913 {
914 #define TransparentPaintImageTag  "Transparent/Image"
915
916   CacheView
917     *image_view;
918
919   ExceptionInfo
920     *exception;
921
922   MagickBooleanType
923     status;
924
925   MagickOffsetType
926     progress;
927
928   MagickPixelPacket
929     zero;
930
931   ssize_t
932     y;
933
934   assert(image != (Image *) NULL);
935   assert(image->signature == MagickSignature);
936   assert(target != (MagickPixelPacket *) NULL);
937   if (image->debug != MagickFalse)
938     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
939   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
940     return(MagickFalse);
941   if (image->matte == MagickFalse)
942     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
943   /*
944     Make image color transparent.
945   */
946   status=MagickTrue;
947   progress=0;
948   exception=(&image->exception);
949   GetMagickPixelPacket(image,&zero);
950   image_view=AcquireCacheView(image);
951 #if defined(MAGICKCORE_OPENMP_SUPPORT)
952   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
953 #endif
954   for (y=0; y < (ssize_t) image->rows; y++)
955   {
956     MagickPixelPacket
957       pixel;
958
959     register IndexPacket
960       *restrict indexes;
961
962     register ssize_t
963       x;
964
965     register PixelPacket
966       *restrict q;
967
968     if (status == MagickFalse)
969       continue;
970     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
971     if (q == (PixelPacket *) NULL)
972       {
973         status=MagickFalse;
974         continue;
975       }
976     indexes=GetCacheViewAuthenticIndexQueue(image_view);
977     pixel=zero;
978     for (x=0; x < (ssize_t) image->columns; x++)
979     {
980       SetMagickPixelPacket(image,q,indexes+x,&pixel);
981       if (IsMagickColorSimilar(&pixel,target) != invert)
982         q->opacity=opacity;
983       q++;
984     }
985     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
986       status=MagickFalse;
987     if (image->progress_monitor != (MagickProgressMonitor) NULL)
988       {
989         MagickBooleanType
990           proceed;
991
992 #if defined(MAGICKCORE_OPENMP_SUPPORT)
993   #pragma omp critical (MagickCore_TransparentPaintImage)
994 #endif
995         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
996           image->rows);
997         if (proceed == MagickFalse)
998           status=MagickFalse;
999       }
1000   }
1001   image_view=DestroyCacheView(image_view);
1002   return(status);
1003 }
1004 \f
1005 /*
1006 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1007 %                                                                             %
1008 %                                                                             %
1009 %                                                                             %
1010 %     T r a n s p a r e n t P a i n t I m a g e C h r o m a                   %
1011 %                                                                             %
1012 %                                                                             %
1013 %                                                                             %
1014 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1015 %
1016 %  TransparentPaintImageChroma() changes the opacity value associated with any
1017 %  pixel that matches color to the value defined by opacity.
1018 %
1019 %  As there is one fuzz value for the all the channels, the
1020 %  TransparentPaintImage() API is not suitable for the operations like chroma,
1021 %  where the tolerance for similarity of two color component (RGB) can be
1022 %  different, Thus we define this method take two target pixels (one
1023 %  low and one hight) and all the pixels of an image which are lying between
1024 %  these two pixels are made transparent.
1025 %
1026 %  The format of the TransparentPaintImage method is:
1027 %
1028 %      MagickBooleanType TransparentPaintImage(Image *image,
1029 %        const MagickPixelPacket *low,const MagickPixelPacket *hight,
1030 %        const Quantum opacity,const MagickBooleanType invert)
1031 %
1032 %  A description of each parameter follows:
1033 %
1034 %    o image: the image.
1035 %
1036 %    o low: the low target color.
1037 %
1038 %    o high: the high target color.
1039 %
1040 %    o opacity: the replacement opacity value.
1041 %
1042 %    o invert: paint any pixel that does not match the target color.
1043 %
1044 */
1045 MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1046   const MagickPixelPacket *low,const MagickPixelPacket *high,
1047   const Quantum opacity,const MagickBooleanType invert)
1048 {
1049 #define TransparentPaintImageTag  "Transparent/Image"
1050
1051   CacheView
1052     *image_view;
1053
1054   ExceptionInfo
1055     *exception;
1056
1057   MagickBooleanType
1058     status;
1059
1060   MagickOffsetType
1061     progress;
1062
1063   ssize_t
1064     y;
1065
1066   assert(image != (Image *) NULL);
1067   assert(image->signature == MagickSignature);
1068   assert(high != (MagickPixelPacket *) NULL);
1069   assert(low != (MagickPixelPacket *) NULL);
1070   if (image->debug != MagickFalse)
1071     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1072   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1073     return(MagickFalse);
1074   if (image->matte == MagickFalse)
1075     (void) SetImageAlphaChannel(image,ResetAlphaChannel);
1076   /*
1077     Make image color transparent.
1078   */
1079   status=MagickTrue;
1080   progress=0;
1081   exception=(&image->exception);
1082   image_view=AcquireCacheView(image);
1083 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1084   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1085 #endif
1086   for (y=0; y < (ssize_t) image->rows; y++)
1087   {
1088     MagickBooleanType
1089       match;
1090
1091     MagickPixelPacket
1092       pixel;
1093
1094     register IndexPacket
1095       *restrict indexes;
1096
1097     register ssize_t
1098       x;
1099
1100     register PixelPacket
1101       *restrict q;
1102
1103     if (status == MagickFalse)
1104       continue;
1105     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1106     if (q == (PixelPacket *) NULL)
1107       {
1108         status=MagickFalse;
1109         continue;
1110       }
1111     indexes=GetCacheViewAuthenticIndexQueue(image_view);
1112     GetMagickPixelPacket(image,&pixel);
1113     for (x=0; x < (ssize_t) image->columns; x++)
1114     {
1115       SetMagickPixelPacket(image,q,indexes+x,&pixel);
1116       match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1117         (pixel.green >= low->green) && (pixel.green <= high->green) &&
1118         (pixel.blue  >= low->blue) && (pixel.blue <= high->blue)) ?
1119         MagickTrue : MagickFalse;
1120       if (match != invert)
1121         q->opacity=opacity;
1122       q++;
1123     }
1124     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1125       status=MagickFalse;
1126     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1127       {
1128         MagickBooleanType
1129           proceed;
1130
1131 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1132   #pragma omp critical (MagickCore_TransparentPaintImageChroma)
1133 #endif
1134         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1135           image->rows);
1136         if (proceed == MagickFalse)
1137           status=MagickFalse;
1138       }
1139   }
1140   image_view=DestroyCacheView(image_view);
1141   return(status);
1142 }