]> 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     pixel;
142
143   PixelInfo
144     fill_color;
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 (image->matte == MagickFalse)
176     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
177   /*
178     Set floodfill state.
179   */
180   floodplane_image=CloneImage(image,0,0,MagickTrue,exception);
181   if (floodplane_image == (Image *) NULL)
182     return(MagickFalse);
183   (void) SetImageAlphaChannel(floodplane_image,OpaqueAlphaChannel,exception);
184   segment_stack=(SegmentInfo *) AcquireQuantumMemory(MaxStacksize,
185     sizeof(*segment_stack));
186   if (segment_stack == (SegmentInfo *) NULL)
187     {
188       floodplane_image=DestroyImage(floodplane_image);
189       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
190         image->filename);
191     }
192   /*
193     Push initial segment on stack.
194   */
195   status=MagickTrue;
196   fill_color.black=0.0;
197   fill_color.index=0.0;
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   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       GetPixelInfoPixel(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                 GetPixelInfoPixel(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             GetPixelInfoPixel(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,exception);
336           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
337             SetPixelRed(image,ClampToQuantum(fill_color.red),q);
338           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
339             SetPixelGreen(image,ClampToQuantum(fill_color.green),q);
340           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
341             SetPixelBlue(image,ClampToQuantum(fill_color.blue),q);
342           if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0)
343             SetPixelBlack(image,ClampToQuantum(fill_color.black),q);
344           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
345             SetPixelAlpha(image,ClampToQuantum(fill_color.alpha),q);
346         }
347       p+=GetPixelChannels(floodplane_image);
348       q+=GetPixelChannels(image);
349     }
350     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
351       break;
352   }
353   floodplane_view=DestroyCacheView(floodplane_view);
354   image_view=DestroyCacheView(image_view);
355   segment_stack=(SegmentInfo *) RelinquishMagickMemory(segment_stack);
356   floodplane_image=DestroyImage(floodplane_image);
357   return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
358 }
359 \f
360 /*
361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
362 %                                                                             %
363 %                                                                             %
364 %                                                                             %
365 +     G r a d i e n t I m a g e                                               %
366 %                                                                             %
367 %                                                                             %
368 %                                                                             %
369 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370 %
371 %  GradientImage() applies a continuously smooth color transitions along a
372 %  vector from one color to another.
373 %
374 %  Note, the interface of this method will change in the future to support
375 %  more than one transistion.
376 %
377 %  The format of the GradientImage method is:
378 %
379 %      MagickBooleanType GradientImage(Image *image,const GradientType type,
380 %        const SpreadMethod method,const PixelInfo *start_color,
381 %        const PixelInfo *stop_color,ExceptionInfo *exception)
382 %
383 %  A description of each parameter follows:
384 %
385 %    o image: the image.
386 %
387 %    o type: the gradient type: linear or radial.
388 %
389 %    o spread: the gradient spread meathod: pad, reflect, or repeat.
390 %
391 %    o start_color: the start color.
392 %
393 %    o stop_color: the stop color.
394 %
395 %    o exception: return any errors or warnings in this structure.
396 %
397 */
398
399 static inline double MagickMax(const double x,const double y)
400 {
401   return(x > y ? x : y);
402 }
403
404 MagickExport MagickBooleanType GradientImage(Image *image,
405   const GradientType type,const SpreadMethod method,
406   const PixelInfo *start_color,const PixelInfo *stop_color,
407   ExceptionInfo *exception)
408 {
409   DrawInfo
410     *draw_info;
411
412   GradientInfo
413     *gradient;
414
415   MagickBooleanType
416     status;
417
418   register ssize_t
419     i;
420
421   /*
422     Set gradient start-stop end points.
423   */
424   assert(image != (const Image *) NULL);
425   assert(image->signature == MagickSignature);
426   if (image->debug != MagickFalse)
427     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
428   assert(start_color != (const PixelInfo *) NULL);
429   assert(stop_color != (const PixelInfo *) NULL);
430   draw_info=AcquireDrawInfo();
431   gradient=(&draw_info->gradient);
432   gradient->type=type;
433   gradient->bounding_box.width=image->columns;
434   gradient->bounding_box.height=image->rows;
435   gradient->gradient_vector.x2=(double) image->columns-1.0;
436   gradient->gradient_vector.y2=(double) image->rows-1.0;
437   if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
438     gradient->gradient_vector.x2=0.0;
439   gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
440   gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
441   gradient->radius=MagickMax(gradient->center.x,gradient->center.y);
442   gradient->spread=method;
443   /*
444     Define the gradient to fill between the stops.
445   */
446   gradient->number_stops=2;
447   gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
448     sizeof(*gradient->stops));
449   if (gradient->stops == (StopInfo *) NULL)
450     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
451       image->filename);
452   (void) ResetMagickMemory(gradient->stops,0,gradient->number_stops*
453     sizeof(*gradient->stops));
454   for (i=0; i < (ssize_t) gradient->number_stops; i++)
455     GetPixelInfo(image,&gradient->stops[i].color);
456   gradient->stops[0].color=(*start_color);
457   gradient->stops[0].offset=0.0;
458   gradient->stops[1].color=(*stop_color);
459   gradient->stops[1].offset=1.0;
460   /*
461     Draw a gradient on the image.
462   */
463   status=DrawGradientImage(image,draw_info,exception);
464   draw_info=DestroyDrawInfo(draw_info);
465   if ((start_color->alpha == OpaqueAlpha) && (stop_color->alpha == OpaqueAlpha))
466     image->matte=MagickFalse;
467   if ((IsPixelInfoGray(start_color) != MagickFalse) &&
468       (IsPixelInfoGray(stop_color) != MagickFalse))
469     image->type=GrayscaleType;
470   return(status);
471 }
472 \f
473 /*
474 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
475 %                                                                             %
476 %                                                                             %
477 %                                                                             %
478 %     O i l P a i n t I m a g e                                               %
479 %                                                                             %
480 %                                                                             %
481 %                                                                             %
482 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
483 %
484 %  OilPaintImage() applies a special effect filter that simulates an oil
485 %  painting.  Each pixel is replaced by the most frequent color occurring
486 %  in a circular region defined by radius.
487 %
488 %  The format of the OilPaintImage method is:
489 %
490 %      Image *OilPaintImage(const Image *image,const double radius,
491 %        const double sigma,ExceptionInfo *exception)
492 %
493 %  A description of each parameter follows:
494 %
495 %    o image: the image.
496 %
497 %    o radius: the radius of the circular neighborhood.
498 %
499 %    o sigma: the standard deviation of the Gaussian, in pixels.
500 %
501 %    o exception: return any errors or warnings in this structure.
502 %
503 */
504
505 static size_t **DestroyHistogramThreadSet(size_t **histogram)
506 {
507   register ssize_t
508     i;
509
510   assert(histogram != (size_t **) NULL);
511   for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
512     if (histogram[i] != (size_t *) NULL)
513       histogram[i]=(size_t *) RelinquishMagickMemory(histogram[i]);
514   histogram=(size_t **) RelinquishMagickMemory(histogram);
515   return(histogram);
516 }
517
518 static size_t **AcquireHistogramThreadSet(const size_t count)
519 {
520   register ssize_t
521     i;
522
523   size_t
524     **histogram,
525     number_threads;
526
527   number_threads=GetOpenMPMaximumThreads();
528   histogram=(size_t **) AcquireQuantumMemory(number_threads,sizeof(*histogram));
529   if (histogram == (size_t **) NULL)
530     return((size_t **) NULL);
531   (void) ResetMagickMemory(histogram,0,number_threads*sizeof(*histogram));
532   for (i=0; i < (ssize_t) number_threads; i++)
533   {
534     histogram[i]=(size_t *) AcquireQuantumMemory(count,sizeof(**histogram));
535     if (histogram[i] == (size_t *) NULL)
536       return(DestroyHistogramThreadSet(histogram));
537   }
538   return(histogram);
539 }
540
541 MagickExport Image *OilPaintImage(const Image *image,const double radius,
542   const double sigma,ExceptionInfo *exception)
543 {
544 #define NumberPaintBins  256
545 #define OilPaintImageTag  "OilPaint/Image"
546
547   CacheView
548     *image_view,
549     *paint_view;
550
551   Image
552     *paint_image;
553
554   MagickBooleanType
555     status;
556
557   MagickOffsetType
558     progress;
559
560   size_t
561     **histograms,
562     width;
563
564   ssize_t
565     y;
566
567   /*
568     Initialize painted image attributes.
569   */
570   assert(image != (const Image *) NULL);
571   assert(image->signature == MagickSignature);
572   if (image->debug != MagickFalse)
573     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
574   assert(exception != (ExceptionInfo *) NULL);
575   assert(exception->signature == MagickSignature);
576   width=GetOptimalKernelWidth2D(radius,sigma);
577   paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
578   if (paint_image == (Image *) NULL)
579     return((Image *) NULL);
580   if (SetImageStorageClass(paint_image,DirectClass,exception) == MagickFalse)
581     {
582       paint_image=DestroyImage(paint_image);
583       return((Image *) NULL);
584     }
585   histograms=AcquireHistogramThreadSet(NumberPaintBins);
586   if (histograms == (size_t **) NULL)
587     {
588       paint_image=DestroyImage(paint_image);
589       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
590     }
591   /*
592     Oil paint image.
593   */
594   status=MagickTrue;
595   progress=0;
596   image_view=AcquireCacheView(image);
597   paint_view=AcquireCacheView(paint_image);
598 #if defined(MAGICKCORE_OPENMP_SUPPORT)
599   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
600 #endif
601   for (y=0; y < (ssize_t) image->rows; y++)
602   {
603     register const Quantum
604       *restrict p;
605
606     register Quantum
607       *restrict q;
608
609     register size_t
610       *histogram;
611
612     register ssize_t
613       x;
614
615     if (status == MagickFalse)
616       continue;
617     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
618       (width/2L),image->columns+width,width,exception);
619     q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
620       exception);
621     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
622       {
623         status=MagickFalse;
624         continue;
625       }
626     histogram=histograms[GetOpenMPThreadId()];
627     for (x=0; x < (ssize_t) image->columns; x++)
628     {
629       register ssize_t
630         i,
631         u;
632
633       size_t
634         count;
635
636       ssize_t
637         j,
638         k,
639         v;
640
641       /*
642         Assign most frequent color.
643       */
644       i=0;
645       j=0;
646       count=0;
647       (void) ResetMagickMemory(histogram,0,NumberPaintBins*sizeof(*histogram));
648       for (v=0; v < (ssize_t) width; v++)
649       {
650         for (u=0; u < (ssize_t) width; u++)
651         {
652           k=(ssize_t) ScaleQuantumToChar(GetPixelIntensity(image,p+
653             GetPixelChannels(image)*(u+i)));
654           histogram[k]++;
655           if (histogram[k] > count)
656             {
657               j=i+u;
658               count=histogram[k];
659             }
660         }
661         i+=(ssize_t) (image->columns+width);
662       }
663       if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
664         SetPixelRed(paint_image,GetPixelRed(image,p+j*
665           GetPixelChannels(image)),q);
666       if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
667         SetPixelGreen(paint_image,GetPixelGreen(image,p+j*
668           GetPixelChannels(image)),q);
669       if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
670         SetPixelBlue(paint_image,GetPixelBlue(image,p+j*
671           GetPixelChannels(image)),q);
672       if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0)
673         SetPixelBlack(paint_image,GetPixelBlack(image,p+j*
674           GetPixelChannels(image)),q);
675       if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
676         SetPixelAlpha(paint_image,GetPixelAlpha(image,p+j*
677           GetPixelChannels(image)),q);
678       p+=GetPixelChannels(image);
679       q+=GetPixelChannels(paint_image);
680     }
681     if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
682       status=MagickFalse;
683     if (image->progress_monitor != (MagickProgressMonitor) NULL)
684       {
685         MagickBooleanType
686           proceed;
687
688 #if defined(MAGICKCORE_OPENMP_SUPPORT)
689   #pragma omp critical (MagickCore_OilPaintImage)
690 #endif
691         proceed=SetImageProgress(image,OilPaintImageTag,progress++,image->rows);
692         if (proceed == MagickFalse)
693           status=MagickFalse;
694       }
695   }
696   paint_view=DestroyCacheView(paint_view);
697   image_view=DestroyCacheView(image_view);
698   histograms=DestroyHistogramThreadSet(histograms);
699   if (status == MagickFalse)
700     paint_image=DestroyImage(paint_image);
701   return(paint_image);
702 }
703 \f
704 /*
705 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
706 %                                                                             %
707 %                                                                             %
708 %                                                                             %
709 %     O p a q u e P a i n t I m a g e                                         %
710 %                                                                             %
711 %                                                                             %
712 %                                                                             %
713 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
714 %
715 %  OpaquePaintImage() changes any pixel that matches color with the color
716 %  defined by fill.
717 %
718 %  By default color must match a particular pixel color exactly.  However, in
719 %  many cases two colors may differ by a small amount.  Fuzz defines how much
720 %  tolerance is acceptable to consider two colors as the same.  For example,
721 %  set fuzz to 10 and the color red at intensities of 100 and 102 respectively
722 %  are now interpreted as the same color.
723 %
724 %  The format of the OpaquePaintImage method is:
725 %
726 %      MagickBooleanType OpaquePaintImage(Image *image,
727 %        const PixelInfo *target,const PixelInfo *fill,
728 %        const MagickBooleanType invert,ExceptionInfo *exception)
729 %
730 %  A description of each parameter follows:
731 %
732 %    o image: the image.
733 %
734 %    o target: the RGB value of the target color.
735 %
736 %    o fill: the replacement color.
737 %
738 %    o invert: paint any pixel that does not match the target color.
739 %
740 %    o exception: return any errors or warnings in this structure.
741 %
742 */
743 MagickExport MagickBooleanType OpaquePaintImage(Image *image,
744   const PixelInfo *target,const PixelInfo *fill,const MagickBooleanType invert,
745   ExceptionInfo *exception)
746 {
747 #define OpaquePaintImageTag  "Opaque/Image"
748
749   CacheView
750     *image_view;
751
752   MagickBooleanType
753     status;
754
755   MagickOffsetType
756     progress;
757
758   PixelInfo
759     zero;
760
761   ssize_t
762     y;
763
764   assert(image != (Image *) NULL);
765   assert(image->signature == MagickSignature);
766   assert(target != (PixelInfo *) NULL);
767   assert(fill != (PixelInfo *) NULL);
768   if (image->debug != MagickFalse)
769     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
770   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
771     return(MagickFalse);
772   /*
773     Make image color opaque.
774   */
775   status=MagickTrue;
776   progress=0;
777   GetPixelInfo(image,&zero);
778   image_view=AcquireCacheView(image);
779 #if defined(MAGICKCORE_OPENMP_SUPPORT)
780   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
781 #endif
782   for (y=0; y < (ssize_t) image->rows; y++)
783   {
784     PixelInfo
785       pixel;
786
787     register Quantum
788       *restrict q;
789
790     register ssize_t
791       x;
792
793     if (status == MagickFalse)
794       continue;
795     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
796     if (q == (Quantum *) NULL)
797       {
798         status=MagickFalse;
799         continue;
800       }
801     pixel=zero;
802     for (x=0; x < (ssize_t) image->columns; x++)
803     {
804       GetPixelInfoPixel(image,q,&pixel);
805       if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert)
806         {
807           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
808             SetPixelRed(image,ClampToQuantum(fill->red),q);
809           if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
810             SetPixelGreen(image,ClampToQuantum(fill->green),q);
811           if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
812             SetPixelBlue(image,ClampToQuantum(fill->blue),q);
813           if ((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0)
814             SetPixelBlack(image,ClampToQuantum(fill->black),q);
815           if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
816             SetPixelAlpha(image,ClampToQuantum(fill->alpha),q);
817         }
818       q+=GetPixelChannels(image);
819     }
820     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
821       status=MagickFalse;
822     if (image->progress_monitor != (MagickProgressMonitor) NULL)
823       {
824         MagickBooleanType
825           proceed;
826
827 #if defined(MAGICKCORE_OPENMP_SUPPORT)
828   #pragma omp critical (MagickCore_OpaquePaintImage)
829 #endif
830         proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
831           image->rows);
832         if (proceed == MagickFalse)
833           status=MagickFalse;
834       }
835   }
836   image_view=DestroyCacheView(image_view);
837   return(status);
838 }
839 \f
840 /*
841 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
842 %                                                                             %
843 %                                                                             %
844 %                                                                             %
845 %     T r a n s p a r e n t P a i n t I m a g e                               %
846 %                                                                             %
847 %                                                                             %
848 %                                                                             %
849 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
850 %
851 %  TransparentPaintImage() changes the opacity value associated with any pixel
852 %  that matches color to the value defined by opacity.
853 %
854 %  By default color must match a particular pixel color exactly.  However, in
855 %  many cases two colors may differ by a small amount.  Fuzz defines how much
856 %  tolerance is acceptable to consider two colors as the same.  For example,
857 %  set fuzz to 10 and the color red at intensities of 100 and 102 respectively
858 %  are now interpreted as the same color.
859 %
860 %  The format of the TransparentPaintImage method is:
861 %
862 %      MagickBooleanType TransparentPaintImage(Image *image,
863 %        const PixelInfo *target,const Quantum opacity,
864 %        const MagickBooleanType invert,ExceptionInfo *exception)
865 %
866 %  A description of each parameter follows:
867 %
868 %    o image: the image.
869 %
870 %    o target: the target color.
871 %
872 %    o opacity: the replacement opacity value.
873 %
874 %    o invert: paint any pixel that does not match the target color.
875 %
876 %    o exception: return any errors or warnings in this structure.
877 %
878 */
879 MagickExport MagickBooleanType TransparentPaintImage(Image *image,
880   const PixelInfo *target,const Quantum opacity,const MagickBooleanType invert,
881   ExceptionInfo *exception)
882 {
883 #define TransparentPaintImageTag  "Transparent/Image"
884
885   CacheView
886     *image_view;
887
888   MagickBooleanType
889     status;
890
891   MagickOffsetType
892     progress;
893
894   PixelInfo
895     zero;
896
897   ssize_t
898     y;
899
900   assert(image != (Image *) NULL);
901   assert(image->signature == MagickSignature);
902   assert(target != (PixelInfo *) NULL);
903   if (image->debug != MagickFalse)
904     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
905   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
906     return(MagickFalse);
907   if (image->matte == MagickFalse)
908     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
909   /*
910     Make image color transparent.
911   */
912   status=MagickTrue;
913   progress=0;
914   GetPixelInfo(image,&zero);
915   image_view=AcquireCacheView(image);
916 #if defined(MAGICKCORE_OPENMP_SUPPORT)
917   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
918 #endif
919   for (y=0; y < (ssize_t) image->rows; y++)
920   {
921     PixelInfo
922       pixel;
923
924     register ssize_t
925       x;
926
927     register Quantum
928       *restrict q;
929
930     if (status == MagickFalse)
931       continue;
932     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
933     if (q == (Quantum *) NULL)
934       {
935         status=MagickFalse;
936         continue;
937       }
938     pixel=zero;
939     for (x=0; x < (ssize_t) image->columns; x++)
940     {
941       GetPixelInfoPixel(image,q,&pixel);
942       if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert)
943         SetPixelAlpha(image,opacity,q);
944       q+=GetPixelChannels(image);
945     }
946     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
947       status=MagickFalse;
948     if (image->progress_monitor != (MagickProgressMonitor) NULL)
949       {
950         MagickBooleanType
951           proceed;
952
953 #if defined(MAGICKCORE_OPENMP_SUPPORT)
954   #pragma omp critical (MagickCore_TransparentPaintImage)
955 #endif
956         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
957           image->rows);
958         if (proceed == MagickFalse)
959           status=MagickFalse;
960       }
961   }
962   image_view=DestroyCacheView(image_view);
963   return(status);
964 }
965 \f
966 /*
967 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
968 %                                                                             %
969 %                                                                             %
970 %                                                                             %
971 %     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                   %
972 %                                                                             %
973 %                                                                             %
974 %                                                                             %
975 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
976 %
977 %  TransparentPaintImageChroma() changes the opacity value associated with any
978 %  pixel that matches color to the value defined by opacity.
979 %
980 %  As there is one fuzz value for the all the channels, TransparentPaintImage()
981 %  is not suitable for the operations like chroma, where the tolerance for
982 %  similarity of two color component (RGB) can be different. Thus we define
983 %  this method to take two target pixels (one low and one high) and all the
984 %  pixels of an image which are lying between these two pixels are made
985 %  transparent.
986 %
987 %  The format of the TransparentPaintImageChroma method is:
988 %
989 %      MagickBooleanType TransparentPaintImageChroma(Image *image,
990 %        const PixelInfo *low,const PixelInfo *high,const Quantum opacity,
991 %        const MagickBooleanType invert,ExceptionInfo *exception)
992 %
993 %  A description of each parameter follows:
994 %
995 %    o image: the image.
996 %
997 %    o low: the low target color.
998 %
999 %    o high: the high target color.
1000 %
1001 %    o opacity: the replacement opacity value.
1002 %
1003 %    o invert: paint any pixel that does not match the target color.
1004 %
1005 %    o exception: return any errors or warnings in this structure.
1006 %
1007 */
1008 MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1009   const PixelInfo *low,const PixelInfo *high,const Quantum opacity,
1010   const MagickBooleanType invert,ExceptionInfo *exception)
1011 {
1012 #define TransparentPaintImageTag  "Transparent/Image"
1013
1014   CacheView
1015     *image_view;
1016
1017   MagickBooleanType
1018     status;
1019
1020   MagickOffsetType
1021     progress;
1022
1023   ssize_t
1024     y;
1025
1026   assert(image != (Image *) NULL);
1027   assert(image->signature == MagickSignature);
1028   assert(high != (PixelInfo *) NULL);
1029   assert(low != (PixelInfo *) NULL);
1030   if (image->debug != MagickFalse)
1031     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1032   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1033     return(MagickFalse);
1034   if (image->matte == MagickFalse)
1035     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1036   /*
1037     Make image color transparent.
1038   */
1039   status=MagickTrue;
1040   progress=0;
1041   image_view=AcquireCacheView(image);
1042 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1043   #pragma omp parallel for schedule(dynamic,4) shared(progress,status)
1044 #endif
1045   for (y=0; y < (ssize_t) image->rows; y++)
1046   {
1047     MagickBooleanType
1048       match;
1049
1050     PixelInfo
1051       pixel;
1052
1053     register Quantum
1054       *restrict q;
1055
1056     register ssize_t
1057       x;
1058
1059     if (status == MagickFalse)
1060       continue;
1061     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1062     if (q == (Quantum *) NULL)
1063       {
1064         status=MagickFalse;
1065         continue;
1066       }
1067     GetPixelInfo(image,&pixel);
1068     for (x=0; x < (ssize_t) image->columns; x++)
1069     {
1070       GetPixelInfoPixel(image,q,&pixel);
1071       match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1072         (pixel.green >= low->green) && (pixel.green <= high->green) &&
1073         (pixel.blue  >= low->blue) && (pixel.blue <= high->blue)) ? MagickTrue :
1074         MagickFalse;
1075       if (match != invert)
1076         SetPixelAlpha(image,opacity,q);
1077       q+=GetPixelChannels(image);
1078     }
1079     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1080       status=MagickFalse;
1081     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1082       {
1083         MagickBooleanType
1084           proceed;
1085
1086 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1087   #pragma omp critical (MagickCore_TransparentPaintImageChroma)
1088 #endif
1089         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1090           image->rows);
1091         if (proceed == MagickFalse)
1092           status=MagickFalse;
1093       }
1094   }
1095   image_view=DestroyCacheView(image_view);
1096   return(status);
1097 }