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