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