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