]> granicus.if.org Git - imagemagick/blob - MagickCore/paint.c
Update web pages
[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-2015 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  262144UL
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 == MagickCoreSignature);
169   if (image->debug != MagickFalse)
170     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
171   assert(draw_info != (DrawInfo *) NULL);
172   assert(draw_info->signature == MagickCoreSignature);
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 == UndefinedPixelTrait) &&
182       (draw_info->fill.alpha_trait != UndefinedPixelTrait))
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   status=MagickTrue;
323 #if defined(MAGICKCORE_OPENMP_SUPPORT)
324   #pragma omp parallel for schedule(static,4) shared(status) \
325     magick_threads(floodplane_image,image,floodplane_image->rows,1)
326 #endif
327   for (y=0; y < (ssize_t) image->rows; y++)
328   {
329     register const Quantum
330       *restrict p;
331
332     register Quantum
333       *restrict q;
334
335     register ssize_t
336       x;
337
338     /*
339       Tile fill color onto floodplane.
340     */
341     if (status == MagickFalse)
342       continue;
343     p=GetCacheViewVirtualPixels(floodplane_view,0,y,image->columns,1,exception);
344     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
345     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
346       {
347         status=MagickFalse;
348         continue;
349       }
350     for (x=0; x < (ssize_t) image->columns; x++)
351     {
352       if (GetPixelGray(floodplane_image,p) != 0)
353         {
354           (void) GetFillColor(draw_info,x,y,&fill_color,exception);
355           SetPixelViaPixelInfo(image,&fill_color,q);
356         }
357       p+=GetPixelChannels(floodplane_image);
358       q+=GetPixelChannels(image);
359     }
360     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
361       status=MagickFalse;
362   }
363   floodplane_view=DestroyCacheView(floodplane_view);
364   image_view=DestroyCacheView(image_view);
365   segment_info=RelinquishVirtualMemory(segment_info);
366   floodplane_image=DestroyImage(floodplane_image);
367   return(status);
368 }
369 \f
370 /*
371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
372 %                                                                             %
373 %                                                                             %
374 %                                                                             %
375 +     G r a d i e n t I m a g e                                               %
376 %                                                                             %
377 %                                                                             %
378 %                                                                             %
379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
380 %
381 %  GradientImage() applies a continuously smooth color transitions along a
382 %  vector from one color to another.
383 %
384 %  Note, the interface of this method will change in the future to support
385 %  more than one transistion.
386 %
387 %  The format of the GradientImage method is:
388 %
389 %      MagickBooleanType GradientImage(Image *image,const GradientType type,
390 %        const SpreadMethod method,const PixelInfo *start_color,
391 %        const PixelInfo *stop_color,ExceptionInfo *exception)
392 %
393 %  A description of each parameter follows:
394 %
395 %    o image: the image.
396 %
397 %    o type: the gradient type: linear or radial.
398 %
399 %    o spread: the gradient spread meathod: pad, reflect, or repeat.
400 %
401 %    o start_color: the start color.
402 %
403 %    o stop_color: the stop color.
404 %
405 %    o exception: return any errors or warnings in this structure.
406 %
407 */
408
409 MagickExport MagickBooleanType GradientImage(Image *image,
410   const GradientType type,const SpreadMethod method,
411   const StopInfo *stops,const size_t number_stops,
412   ExceptionInfo *exception)
413 {
414   DrawInfo
415     *draw_info;
416
417   GradientInfo
418     *gradient;
419
420   MagickBooleanType
421     status;
422
423   /*
424     Set gradient start-stop end points.
425   */
426   assert(image != (const Image *) NULL);
427   assert(image->signature == MagickCoreSignature);
428   if (image->debug != MagickFalse)
429     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
430   assert(stops != (const StopInfo *) NULL);
431   assert(number_stops > 0);
432   draw_info=AcquireDrawInfo();
433   gradient=(&draw_info->gradient);
434   gradient->type=type;
435   gradient->bounding_box.width=image->columns;
436   gradient->bounding_box.height=image->rows;
437   gradient->gradient_vector.x2=(double) image->columns-1.0;
438   gradient->gradient_vector.y2=(double) image->rows-1.0;
439   if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
440     gradient->gradient_vector.x2=0.0;
441   gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
442   gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
443   gradient->radius=MagickMax(gradient->center.x,gradient->center.y);
444   gradient->spread=method;
445   /*
446     Define the gradient to fill between the stops.
447   */
448   gradient->number_stops=number_stops;
449   gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
450     sizeof(*gradient->stops));
451   if (gradient->stops == (StopInfo *) NULL)
452     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
453       image->filename);
454   (void) CopyMagickMemory(gradient->stops,stops,(size_t) number_stops*
455     sizeof(*stops));
456   /*
457     Draw a gradient on the image.
458   */
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 == MagickCoreSignature);
565   if (image->debug != MagickFalse)
566     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
567   assert(exception != (ExceptionInfo *) NULL);
568   assert(exception->signature == MagickCoreSignature);
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 (SetImageStorageClass(paint_image,DirectClass,exception) == MagickFalse)
581     {
582       linear_image=DestroyImage(linear_image);
583       paint_image=DestroyImage(paint_image);
584       return((Image *) NULL);
585     }
586   histograms=AcquireHistogramThreadSet(NumberPaintBins);
587   if (histograms == (size_t **) NULL)
588     {
589       linear_image=DestroyImage(linear_image);
590       paint_image=DestroyImage(paint_image);
591       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
592     }
593   /*
594     Oil paint image.
595   */
596   status=MagickTrue;
597   progress=0;
598   center=(ssize_t) GetPixelChannels(linear_image)*(linear_image->columns+width)*
599     (width/2L)+GetPixelChannels(linear_image)*(width/2L);
600   image_view=AcquireVirtualCacheView(linear_image,exception);
601   paint_view=AcquireAuthenticCacheView(paint_image,exception);
602 #if defined(MAGICKCORE_OPENMP_SUPPORT)
603   #pragma omp parallel for schedule(static,4) shared(progress,status) \
604     magick_threads(linear_image,paint_image,linear_image->rows,1)
605 #endif
606   for (y=0; y < (ssize_t) linear_image->rows; y++)
607   {
608     register const Quantum
609       *restrict p;
610
611     register Quantum
612       *restrict q;
613
614     register size_t
615       *histogram;
616
617     register ssize_t
618       x;
619
620     if (status == MagickFalse)
621       continue;
622     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
623       (width/2L),linear_image->columns+width,width,exception);
624     q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
625       exception);
626     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
627       {
628         status=MagickFalse;
629         continue;
630       }
631     histogram=histograms[GetOpenMPThreadId()];
632     for (x=0; x < (ssize_t) linear_image->columns; x++)
633     {
634       register ssize_t
635         i,
636         u;
637
638       size_t
639         count;
640
641       ssize_t
642         j,
643         k,
644         n,
645         v;
646
647       /*
648         Assign most frequent color.
649       */
650       k=0;
651       j=0;
652       count=0;
653       (void) ResetMagickMemory(histogram,0,NumberPaintBins* sizeof(*histogram));
654       for (v=0; v < (ssize_t) width; v++)
655       {
656         for (u=0; u < (ssize_t) width; u++)
657         {
658           n=(ssize_t) ScaleQuantumToChar(ClampToQuantum(GetPixelIntensity(
659             linear_image,p+GetPixelChannels(linear_image)*(u+k))));
660           histogram[n]++;
661           if (histogram[n] > count)
662             {
663               j=k+u;
664               count=histogram[n];
665             }
666         }
667         k+=(ssize_t) (linear_image->columns+width);
668       }
669       for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
670       {
671         PixelChannel channel=GetPixelChannelChannel(linear_image,i);
672         PixelTrait traits=GetPixelChannelTraits(linear_image,channel);
673         PixelTrait paint_traits=GetPixelChannelTraits(paint_image,channel);
674         if ((traits == UndefinedPixelTrait) ||
675             (paint_traits == UndefinedPixelTrait))
676           continue;
677         if (((paint_traits & CopyPixelTrait) != 0) ||
678             (GetPixelReadMask(linear_image,p) == 0))
679           {
680             SetPixelChannel(paint_image,channel,p[center+i],q);
681             continue;
682           }
683         SetPixelChannel(paint_image,channel,p[j*GetPixelChannels(linear_image)+
684           i],q);
685       }
686       p+=GetPixelChannels(linear_image);
687       q+=GetPixelChannels(paint_image);
688     }
689     if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
690       status=MagickFalse;
691     if (linear_image->progress_monitor != (MagickProgressMonitor) NULL)
692       {
693         MagickBooleanType
694           proceed;
695
696 #if defined(MAGICKCORE_OPENMP_SUPPORT)
697         #pragma omp critical (MagickCore_OilPaintImage)
698 #endif
699         proceed=SetImageProgress(linear_image,OilPaintImageTag,progress++,
700           linear_image->rows);
701         if (proceed == MagickFalse)
702           status=MagickFalse;
703       }
704   }
705   paint_view=DestroyCacheView(paint_view);
706   image_view=DestroyCacheView(image_view);
707   histograms=DestroyHistogramThreadSet(histograms);
708   linear_image=DestroyImage(linear_image);
709   if (status == MagickFalse)
710     paint_image=DestroyImage(paint_image);
711   return(paint_image);
712 }
713 \f
714 /*
715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
716 %                                                                             %
717 %                                                                             %
718 %                                                                             %
719 %     O p a q u e P a i n t I m a g e                                         %
720 %                                                                             %
721 %                                                                             %
722 %                                                                             %
723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
724 %
725 %  OpaquePaintImage() changes any pixel that matches color with the color
726 %  defined by fill argument.
727 %
728 %  By default color must match a particular pixel color exactly.  However, in
729 %  many cases two colors may differ by a small amount.  Fuzz defines how much
730 %  tolerance is acceptable to consider two colors as the same.  For example,
731 %  set fuzz to 10 and the color red at intensities of 100 and 102 respectively
732 %  are now interpreted as the same color.
733 %
734 %  The format of the OpaquePaintImage method is:
735 %
736 %      MagickBooleanType OpaquePaintImage(Image *image,const PixelInfo *target,
737 %        const PixelInfo *fill,const MagickBooleanType invert,
738 %        ExceptionInfo *exception)
739 %
740 %  A description of each parameter follows:
741 %
742 %    o image: the image.
743 %
744 %    o target: the RGB value of the target color.
745 %
746 %    o fill: the replacement color.
747 %
748 %    o invert: paint any pixel that does not match the target color.
749 %
750 %    o exception: return any errors or warnings in this structure.
751 %
752 */
753 MagickExport MagickBooleanType OpaquePaintImage(Image *image,
754   const PixelInfo *target,const PixelInfo *fill,const MagickBooleanType invert,
755   ExceptionInfo *exception)
756 {
757 #define OpaquePaintImageTag  "Opaque/Image"
758
759   CacheView
760     *image_view;
761
762   MagickBooleanType
763     status;
764
765   MagickOffsetType
766     progress;
767
768   PixelInfo
769     conform_fill,
770     conform_target,
771     zero;
772
773   ssize_t
774     y;
775
776   assert(image != (Image *) NULL);
777   assert(image->signature == MagickCoreSignature);
778   assert(target != (PixelInfo *) NULL);
779   assert(fill != (PixelInfo *) NULL);
780   if (image->debug != MagickFalse)
781     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
782   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
783     return(MagickFalse);
784   ConformPixelInfo(image,fill,&conform_fill,exception);
785   ConformPixelInfo(image,target,&conform_target,exception);
786   /*
787     Make image color opaque.
788   */
789   status=MagickTrue;
790   progress=0;
791   GetPixelInfo(image,&zero);
792   image_view=AcquireAuthenticCacheView(image,exception);
793 #if defined(MAGICKCORE_OPENMP_SUPPORT)
794   #pragma omp parallel for schedule(static,4) shared(progress,status) \
795     magick_threads(image,image,image->rows,1)
796 #endif
797   for (y=0; y < (ssize_t) image->rows; y++)
798   {
799     PixelInfo
800       pixel;
801
802     register Quantum
803       *restrict q;
804
805     register ssize_t
806       x;
807
808     if (status == MagickFalse)
809       continue;
810     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
811     if (q == (Quantum *) NULL)
812       {
813         status=MagickFalse;
814         continue;
815       }
816     pixel=zero;
817     for (x=0; x < (ssize_t) image->columns; x++)
818     {
819       GetPixelInfoPixel(image,q,&pixel);
820       if (IsFuzzyEquivalencePixelInfo(&pixel,&conform_target) != invert)
821         SetPixelViaPixelInfo(image,&conform_fill,q);
822       q+=GetPixelChannels(image);
823     }
824     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
825       status=MagickFalse;
826     if (image->progress_monitor != (MagickProgressMonitor) NULL)
827       {
828         MagickBooleanType
829           proceed;
830
831 #if defined(MAGICKCORE_OPENMP_SUPPORT)
832         #pragma omp critical (MagickCore_OpaquePaintImage)
833 #endif
834         proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
835           image->rows);
836         if (proceed == MagickFalse)
837           status=MagickFalse;
838       }
839   }
840   image_view=DestroyCacheView(image_view);
841   return(status);
842 }
843 \f
844 /*
845 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846 %                                                                             %
847 %                                                                             %
848 %                                                                             %
849 %     T r a n s p a r e n t P a i n t I m a g e                               %
850 %                                                                             %
851 %                                                                             %
852 %                                                                             %
853 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
854 %
855 %  TransparentPaintImage() changes the opacity value associated with any pixel
856 %  that matches color to the value defined by opacity.
857 %
858 %  By default color must match a particular pixel color exactly.  However, in
859 %  many cases two colors may differ by a small amount.  Fuzz defines how much
860 %  tolerance is acceptable to consider two colors as the same.  For example,
861 %  set fuzz to 10 and the color red at intensities of 100 and 102 respectively
862 %  are now interpreted as the same color.
863 %
864 %  The format of the TransparentPaintImage method is:
865 %
866 %      MagickBooleanType TransparentPaintImage(Image *image,
867 %        const PixelInfo *target,const Quantum opacity,
868 %        const MagickBooleanType invert,ExceptionInfo *exception)
869 %
870 %  A description of each parameter follows:
871 %
872 %    o image: the image.
873 %
874 %    o target: the target color.
875 %
876 %    o opacity: the replacement opacity value.
877 %
878 %    o invert: paint any pixel that does not match the target color.
879 %
880 %    o exception: return any errors or warnings in this structure.
881 %
882 */
883 MagickExport MagickBooleanType TransparentPaintImage(Image *image,
884   const PixelInfo *target,const Quantum opacity,const MagickBooleanType invert,
885   ExceptionInfo *exception)
886 {
887 #define TransparentPaintImageTag  "Transparent/Image"
888
889   CacheView
890     *image_view;
891
892   MagickBooleanType
893     status;
894
895   MagickOffsetType
896     progress;
897
898   PixelInfo
899     zero;
900
901   ssize_t
902     y;
903
904   assert(image != (Image *) NULL);
905   assert(image->signature == MagickCoreSignature);
906   assert(target != (PixelInfo *) NULL);
907   if (image->debug != MagickFalse)
908     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
909   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
910     return(MagickFalse);
911   if (image->alpha_trait == UndefinedPixelTrait)
912     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
913   /*
914     Make image color transparent.
915   */
916   status=MagickTrue;
917   progress=0;
918   GetPixelInfo(image,&zero);
919   image_view=AcquireAuthenticCacheView(image,exception);
920 #if defined(MAGICKCORE_OPENMP_SUPPORT)
921   #pragma omp parallel for schedule(static,4) shared(progress,status) \
922     magick_threads(image,image,image->rows,1)
923 #endif
924   for (y=0; y < (ssize_t) image->rows; y++)
925   {
926     PixelInfo
927       pixel;
928
929     register ssize_t
930       x;
931
932     register Quantum
933       *restrict q;
934
935     if (status == MagickFalse)
936       continue;
937     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
938     if (q == (Quantum *) NULL)
939       {
940         status=MagickFalse;
941         continue;
942       }
943     pixel=zero;
944     for (x=0; x < (ssize_t) image->columns; x++)
945     {
946       GetPixelInfoPixel(image,q,&pixel);
947       if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert)
948         SetPixelAlpha(image,opacity,q);
949       q+=GetPixelChannels(image);
950     }
951     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
952       status=MagickFalse;
953     if (image->progress_monitor != (MagickProgressMonitor) NULL)
954       {
955         MagickBooleanType
956           proceed;
957
958 #if defined(MAGICKCORE_OPENMP_SUPPORT)
959         #pragma omp critical (MagickCore_TransparentPaintImage)
960 #endif
961         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
962           image->rows);
963         if (proceed == MagickFalse)
964           status=MagickFalse;
965       }
966   }
967   image_view=DestroyCacheView(image_view);
968   return(status);
969 }
970 \f
971 /*
972 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
973 %                                                                             %
974 %                                                                             %
975 %                                                                             %
976 %     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                   %
977 %                                                                             %
978 %                                                                             %
979 %                                                                             %
980 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
981 %
982 %  TransparentPaintImageChroma() changes the opacity value associated with any
983 %  pixel that matches color to the value defined by opacity.
984 %
985 %  As there is one fuzz value for the all the channels, TransparentPaintImage()
986 %  is not suitable for the operations like chroma, where the tolerance for
987 %  similarity of two color component (RGB) can be different. Thus we define
988 %  this method to take two target pixels (one low and one high) and all the
989 %  pixels of an image which are lying between these two pixels are made
990 %  transparent.
991 %
992 %  The format of the TransparentPaintImageChroma method is:
993 %
994 %      MagickBooleanType TransparentPaintImageChroma(Image *image,
995 %        const PixelInfo *low,const PixelInfo *high,const Quantum opacity,
996 %        const MagickBooleanType invert,ExceptionInfo *exception)
997 %
998 %  A description of each parameter follows:
999 %
1000 %    o image: the image.
1001 %
1002 %    o low: the low target color.
1003 %
1004 %    o high: the high target color.
1005 %
1006 %    o opacity: the replacement opacity value.
1007 %
1008 %    o invert: paint any pixel that does not match the target color.
1009 %
1010 %    o exception: return any errors or warnings in this structure.
1011 %
1012 */
1013 MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1014   const PixelInfo *low,const PixelInfo *high,const Quantum opacity,
1015   const MagickBooleanType invert,ExceptionInfo *exception)
1016 {
1017 #define TransparentPaintImageTag  "Transparent/Image"
1018
1019   CacheView
1020     *image_view;
1021
1022   MagickBooleanType
1023     status;
1024
1025   MagickOffsetType
1026     progress;
1027
1028   ssize_t
1029     y;
1030
1031   assert(image != (Image *) NULL);
1032   assert(image->signature == MagickCoreSignature);
1033   assert(high != (PixelInfo *) NULL);
1034   assert(low != (PixelInfo *) NULL);
1035   if (image->debug != MagickFalse)
1036     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1037   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1038     return(MagickFalse);
1039   if (image->alpha_trait == UndefinedPixelTrait)
1040     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1041   /*
1042     Make image color transparent.
1043   */
1044   status=MagickTrue;
1045   progress=0;
1046   image_view=AcquireAuthenticCacheView(image,exception);
1047 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1048   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1049     magick_threads(image,image,image->rows,1)
1050 #endif
1051   for (y=0; y < (ssize_t) image->rows; y++)
1052   {
1053     MagickBooleanType
1054       match;
1055
1056     PixelInfo
1057       pixel;
1058
1059     register Quantum
1060       *restrict q;
1061
1062     register ssize_t
1063       x;
1064
1065     if (status == MagickFalse)
1066       continue;
1067     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1068     if (q == (Quantum *) NULL)
1069       {
1070         status=MagickFalse;
1071         continue;
1072       }
1073     GetPixelInfo(image,&pixel);
1074     for (x=0; x < (ssize_t) image->columns; x++)
1075     {
1076       GetPixelInfoPixel(image,q,&pixel);
1077       match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1078         (pixel.green >= low->green) && (pixel.green <= high->green) &&
1079         (pixel.blue  >= low->blue) && (pixel.blue <= high->blue)) ? MagickTrue :
1080         MagickFalse;
1081       if (match != invert)
1082         SetPixelAlpha(image,opacity,q);
1083       q+=GetPixelChannels(image);
1084     }
1085     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1086       status=MagickFalse;
1087     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1088       {
1089         MagickBooleanType
1090           proceed;
1091
1092 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1093         #pragma omp critical (MagickCore_TransparentPaintImageChroma)
1094 #endif
1095         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1096           image->rows);
1097         if (proceed == MagickFalse)
1098           status=MagickFalse;
1099       }
1100   }
1101   image_view=DestroyCacheView(image_view);
1102   return(status);
1103 }