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