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