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