]> 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-2010 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,const ssize_t y_offset,
88 %        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   ssize_t
139     offset,
140     start,
141     x,
142     x1,
143     x2,
144     y;
145
146   MagickBooleanType
147     skip;
148
149   MagickPixelPacket
150     fill,
151     pixel;
152
153   PixelPacket
154     fill_color;
155
156   register SegmentInfo
157     *s;
158
159   SegmentInfo
160     *segment_stack;
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   if ((image->columns < width) || (image->rows < width))
595     ThrowImageException(OptionError,"ImageSmallerThanRadius");
596   paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
597   if (paint_image == (Image *) NULL)
598     return((Image *) NULL);
599   if (SetImageStorageClass(paint_image,DirectClass) == MagickFalse)
600     {
601       InheritException(exception,&paint_image->exception);
602       paint_image=DestroyImage(paint_image);
603       return((Image *) NULL);
604     }
605   histograms=AcquireHistogramThreadSet(NumberPaintBins);
606   if (histograms == (size_t **) NULL)
607     {
608       paint_image=DestroyImage(paint_image);
609       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
610     }
611   /*
612     Oil paint image.
613   */
614   status=MagickTrue;
615   progress=0;
616   image_view=AcquireCacheView(image);
617   paint_view=AcquireCacheView(paint_image);
618 #if defined(MAGICKCORE_OPENMP_SUPPORT)
619   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
620 #endif
621   for (y=0; y < (ssize_t) image->rows; y++)
622   {
623     register const IndexPacket
624       *restrict indexes;
625
626     register const PixelPacket
627       *restrict p;
628
629     register IndexPacket
630       *restrict paint_indexes;
631
632     register ssize_t
633       x;
634
635     register PixelPacket
636       *restrict q;
637
638     register size_t
639       *histogram;
640
641     if (status == MagickFalse)
642       continue;
643     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t) (width/
644       2L),image->columns+width,width,exception);
645     q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
646       exception);
647     if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
648       {
649         status=MagickFalse;
650         continue;
651       }
652     indexes=GetCacheViewVirtualIndexQueue(image_view);
653     paint_indexes=GetCacheViewAuthenticIndexQueue(paint_view);
654     histogram=histograms[GetOpenMPThreadId()];
655     for (x=0; x < (ssize_t) image->columns; x++)
656     {
657       ssize_t
658         j,
659         k,
660         v;
661
662       register ssize_t
663         i,
664         u;
665
666       size_t
667         count;
668
669       /*
670         Assign most frequent color.
671       */
672       i=0;
673       j=0;
674       count=0;
675       (void) ResetMagickMemory(histogram,0,NumberPaintBins*sizeof(*histogram));
676       for (v=0; v < (ssize_t) width; v++)
677       {
678         for (u=0; u < (ssize_t) width; u++)
679         {
680           k=(ssize_t) ScaleQuantumToChar(PixelIntensityToQuantum(p+u+i));
681           histogram[k]++;
682           if (histogram[k] > count)
683             {
684               j=i+u;
685               count=histogram[k];
686             }
687         }
688         i+=(ssize_t) (image->columns+width);
689       }
690       *q=(*(p+j));
691       if (image->colorspace == CMYKColorspace)
692         paint_indexes[x]=indexes[x+j];
693       p++;
694       q++;
695     }
696     if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
697       status=MagickFalse;
698     if (image->progress_monitor != (MagickProgressMonitor) NULL)
699       {
700         MagickBooleanType
701           proceed;
702
703 #if defined(MAGICKCORE_OPENMP_SUPPORT)
704   #pragma omp critical (MagickCore_OilPaintImage)
705 #endif
706         proceed=SetImageProgress(image,OilPaintImageTag,progress++,image->rows);
707         if (proceed == MagickFalse)
708           status=MagickFalse;
709       }
710   }
711   paint_view=DestroyCacheView(paint_view);
712   image_view=DestroyCacheView(image_view);
713   histograms=DestroyHistogramThreadSet(histograms);
714   if (status == MagickFalse)
715     paint_image=DestroyImage(paint_image);
716   return(paint_image);
717 }
718 \f
719 /*
720 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
721 %                                                                             %
722 %                                                                             %
723 %                                                                             %
724 %     O p a q u e P a i n t I m a g e                                         %
725 %                                                                             %
726 %                                                                             %
727 %                                                                             %
728 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
729 %
730 %  OpaquePaintImage() changes any pixel that matches color with the color
731 %  defined by fill.
732 %
733 %  By default color must match a particular pixel color exactly.  However,
734 %  in many cases two colors may differ by a small amount.  Fuzz defines
735 %  how much tolerance is acceptable to consider two colors as the same.
736 %  For example, set fuzz to 10 and the color red at intensities of 100 and
737 %  102 respectively are now interpreted as the same color.
738 %
739 %  The format of the OpaquePaintImage method is:
740 %
741 %      MagickBooleanType OpaquePaintImage(Image *image,
742 %        const PixelPacket *target,const PixelPacket *fill,
743 %        const MagickBooleanType invert)
744 %      MagickBooleanType OpaquePaintImageChannel(Image *image,
745 %        const ChannelType channel,const PixelPacket *target,
746 %        const PixelPacket *fill,const MagickBooleanType invert)
747 %
748 %  A description of each parameter follows:
749 %
750 %    o image: the image.
751 %
752 %    o channel: the channel(s).
753 %
754 %    o target: the RGB value of the target color.
755 %
756 %    o fill: the replacement color.
757 %
758 %    o invert: paint any pixel that does not match the target color.
759 %
760 */
761
762 MagickExport MagickBooleanType OpaquePaintImage(Image *image,
763   const MagickPixelPacket *target,const MagickPixelPacket *fill,
764   const MagickBooleanType invert)
765 {
766   return(OpaquePaintImageChannel(image,AllChannels,target,fill,invert));
767 }
768
769 MagickExport MagickBooleanType OpaquePaintImageChannel(Image *image,
770   const ChannelType channel,const MagickPixelPacket *target,
771   const MagickPixelPacket *fill,const MagickBooleanType invert)
772 {
773 #define OpaquePaintImageTag  "Opaque/Image"
774
775   CacheView
776     *image_view;
777
778   ExceptionInfo
779     *exception;
780
781   MagickBooleanType
782     status;
783
784   MagickOffsetType
785     progress;
786
787   MagickPixelPacket
788     zero;
789
790   ssize_t
791     y;
792
793   assert(image != (Image *) NULL);
794   assert(image->signature == MagickSignature);
795   assert(target != (MagickPixelPacket *) NULL);
796   assert(fill != (MagickPixelPacket *) NULL);
797   if (image->debug != MagickFalse)
798     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
799   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
800     return(MagickFalse);
801   /*
802     Make image color opaque.
803   */
804   status=MagickTrue;
805   progress=0;
806   exception=(&image->exception);
807   GetMagickPixelPacket(image,&zero);
808   image_view=AcquireCacheView(image);
809 #if defined(MAGICKCORE_OPENMP_SUPPORT)
810   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
811 #endif
812   for (y=0; y < (ssize_t) image->rows; y++)
813   {
814     MagickPixelPacket
815       pixel;
816
817     register IndexPacket
818       *restrict indexes;
819
820     register ssize_t
821       x;
822
823     register PixelPacket
824       *restrict q;
825
826     if (status == MagickFalse)
827       continue;
828     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
829     if (q == (PixelPacket *) NULL)
830       {
831         status=MagickFalse;
832         continue;
833       }
834     indexes=GetCacheViewAuthenticIndexQueue(image_view);
835     pixel=zero;
836     for (x=0; x < (ssize_t) image->columns; x++)
837     {
838       SetMagickPixelPacket(image,q,indexes+x,&pixel);
839       if (IsMagickColorSimilar(&pixel,target) != invert)
840         {
841           if ((channel & RedChannel) != 0)
842             q->red=ClampToQuantum(fill->red);
843           if ((channel & GreenChannel) != 0)
844             q->green=ClampToQuantum(fill->green);
845           if ((channel & BlueChannel) != 0)
846             q->blue=ClampToQuantum(fill->blue);
847           if ((channel & OpacityChannel) != 0)
848             q->opacity=ClampToQuantum(fill->opacity);
849           if (((channel & IndexChannel) != 0) &&
850               (image->colorspace == CMYKColorspace))
851             indexes[x]=ClampToQuantum(fill->index);
852         }
853       q++;
854     }
855     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
856       status=MagickFalse;
857     if (image->progress_monitor != (MagickProgressMonitor) NULL)
858       {
859         MagickBooleanType
860           proceed;
861
862 #if defined(MAGICKCORE_OPENMP_SUPPORT)
863   #pragma omp critical (MagickCore_OpaquePaintImageChannel)
864 #endif
865         proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
866           image->rows);
867         if (proceed == MagickFalse)
868           status=MagickFalse;
869       }
870   }
871   image_view=DestroyCacheView(image_view);
872   return(status);
873 }
874 \f
875 /*
876 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
877 %                                                                             %
878 %                                                                             %
879 %                                                                             %
880 %     T r a n s p a r e n t P a i n t I m a g e                               %
881 %                                                                             %
882 %                                                                             %
883 %                                                                             %
884 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
885 %
886 %  TransparentPaintImage() changes the opacity value associated with any pixel
887 %  that matches color to the value defined by opacity.
888 %
889 %  By default color must match a particular pixel color exactly.  However,
890 %  in many cases two colors may differ by a small amount.  Fuzz defines
891 %  how much tolerance is acceptable to consider two colors as the same.
892 %  For example, set fuzz to 10 and the color red at intensities of 100 and
893 %  102 respectively are now interpreted as the same color.
894 %
895 %  The format of the TransparentPaintImage method is:
896 %
897 %      MagickBooleanType TransparentPaintImage(Image *image,
898 %        const MagickPixelPacket *target,const Quantum opacity,
899 %        const MagickBooleanType invert)
900 %
901 %  A description of each parameter follows:
902 %
903 %    o image: the image.
904 %
905 %    o target: the target color.
906 %
907 %    o opacity: the replacement opacity value.
908 %
909 %    o invert: paint any pixel that does not match the target color.
910 %
911 */
912 MagickExport MagickBooleanType TransparentPaintImage(Image *image,
913   const MagickPixelPacket *target,const Quantum opacity,
914   const MagickBooleanType invert)
915 {
916 #define TransparentPaintImageTag  "Transparent/Image"
917
918   CacheView
919     *image_view;
920
921   ExceptionInfo
922     *exception;
923
924   MagickBooleanType
925     status;
926
927   MagickOffsetType
928     progress;
929
930   MagickPixelPacket
931     zero;
932
933   ssize_t
934     y;
935
936   assert(image != (Image *) NULL);
937   assert(image->signature == MagickSignature);
938   assert(target != (MagickPixelPacket *) NULL);
939   if (image->debug != MagickFalse)
940     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
941   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
942     return(MagickFalse);
943   if (image->matte == MagickFalse)
944     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
945   /*
946     Make image color transparent.
947   */
948   status=MagickTrue;
949   progress=0;
950   exception=(&image->exception);
951   GetMagickPixelPacket(image,&zero);
952   image_view=AcquireCacheView(image);
953 #if defined(MAGICKCORE_OPENMP_SUPPORT)
954   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
955 #endif
956   for (y=0; y < (ssize_t) image->rows; y++)
957   {
958     MagickPixelPacket
959       pixel;
960
961     register IndexPacket
962       *restrict indexes;
963
964     register ssize_t
965       x;
966
967     register PixelPacket
968       *restrict q;
969
970     if (status == MagickFalse)
971       continue;
972     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
973     if (q == (PixelPacket *) NULL)
974       {
975         status=MagickFalse;
976         continue;
977       }
978     indexes=GetCacheViewAuthenticIndexQueue(image_view);
979     pixel=zero;
980     for (x=0; x < (ssize_t) image->columns; x++)
981     {
982       SetMagickPixelPacket(image,q,indexes+x,&pixel);
983       if (IsMagickColorSimilar(&pixel,target) != invert)
984         q->opacity=opacity;
985       q++;
986     }
987     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
988       status=MagickFalse;
989     if (image->progress_monitor != (MagickProgressMonitor) NULL)
990       {
991         MagickBooleanType
992           proceed;
993
994 #if defined(MAGICKCORE_OPENMP_SUPPORT)
995   #pragma omp critical (MagickCore_TransparentPaintImage)
996 #endif
997         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
998           image->rows);
999         if (proceed == MagickFalse)
1000           status=MagickFalse;
1001       }
1002   }
1003   image_view=DestroyCacheView(image_view);
1004   return(status);
1005 }
1006 \f
1007 /*
1008 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1009 %                                                                             %
1010 %                                                                             %
1011 %                                                                             %
1012 %     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                   %
1013 %                                                                             %
1014 %                                                                             %
1015 %                                                                             %
1016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1017 %
1018 %  TransparentPaintImageChroma() changes the opacity value associated with any
1019 %  pixel that matches color to the value defined by opacity.
1020 %
1021 %  As there is one fuzz value for the all the channels, the
1022 %  TransparentPaintImage() API is not suitable for the operations like chroma,
1023 %  where the tolerance for similarity of two color component (RGB) can be
1024 %  different, Thus we define this method take two target pixels (one
1025 %  low and one hight) and all the pixels of an image which are lying between
1026 %  these two pixels are made transparent.
1027 %
1028 %  The format of the TransparentPaintImage method is:
1029 %
1030 %      MagickBooleanType TransparentPaintImage(Image *image,
1031 %        const MagickPixelPacket *low,const MagickPixelPacket *hight,
1032 %        const Quantum opacity,const MagickBooleanType invert)
1033 %
1034 %  A description of each parameter follows:
1035 %
1036 %    o image: the image.
1037 %
1038 %    o low: the low target color.
1039 %
1040 %    o high: the high target color.
1041 %
1042 %    o opacity: the replacement opacity value.
1043 %
1044 %    o invert: paint any pixel that does not match the target color.
1045 %
1046 */
1047 MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1048   const MagickPixelPacket *low,const MagickPixelPacket *high,
1049   const Quantum opacity,const MagickBooleanType invert)
1050 {
1051 #define TransparentPaintImageTag  "Transparent/Image"
1052
1053   CacheView
1054     *image_view;
1055
1056   ExceptionInfo
1057     *exception;
1058
1059   MagickBooleanType
1060     status;
1061
1062   MagickOffsetType
1063     progress;
1064
1065   ssize_t
1066     y;
1067
1068   assert(image != (Image *) NULL);
1069   assert(image->signature == MagickSignature);
1070   assert(high != (MagickPixelPacket *) NULL);
1071   assert(low != (MagickPixelPacket *) NULL);
1072   if (image->debug != MagickFalse)
1073     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1074   if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1075     return(MagickFalse);
1076   if (image->matte == MagickFalse)
1077     (void) SetImageAlphaChannel(image,ResetAlphaChannel);
1078   /*
1079     Make image color transparent.
1080   */
1081   status=MagickTrue;
1082   progress=0;
1083   exception=(&image->exception);
1084   image_view=AcquireCacheView(image);
1085 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1086   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1087 #endif
1088   for (y=0; y < (ssize_t) image->rows; y++)
1089   {
1090     MagickBooleanType
1091       match;
1092
1093     MagickPixelPacket
1094       pixel;
1095
1096     register IndexPacket
1097       *restrict indexes;
1098
1099     register ssize_t
1100       x;
1101
1102     register PixelPacket
1103       *restrict q;
1104
1105     if (status == MagickFalse)
1106       continue;
1107     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1108     if (q == (PixelPacket *) NULL)
1109       {
1110         status=MagickFalse;
1111         continue;
1112       }
1113     indexes=GetCacheViewAuthenticIndexQueue(image_view);
1114     GetMagickPixelPacket(image,&pixel);
1115     for (x=0; x < (ssize_t) image->columns; x++)
1116     {
1117       SetMagickPixelPacket(image,q,indexes+x,&pixel);
1118       match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1119         (pixel.green >= low->green) && (pixel.green <= high->green) &&
1120         (pixel.blue  >= low->blue) && (pixel.blue <= high->blue)) ?
1121         MagickTrue : MagickFalse;
1122       if (match != invert)
1123         q->opacity=opacity;
1124       q++;
1125     }
1126     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1127       status=MagickFalse;
1128     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1129       {
1130         MagickBooleanType
1131           proceed;
1132
1133 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1134   #pragma omp critical (MagickCore_TransparentPaintImageChroma)
1135 #endif
1136         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1137           image->rows);
1138         if (proceed == MagickFalse)
1139           status=MagickFalse;
1140       }
1141   }
1142   image_view=DestroyCacheView(image_view);
1143   return(status);
1144 }