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