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