]> granicus.if.org Git - imagemagick/blob - MagickCore/paint.c
Support maximum and circle for gradient:extent define
[imagemagick] / MagickCore / paint.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                      PPPP    AAA   IIIII  N   N  TTTTT                      %
7 %                      P   P  A   A    I    NN  N    T                        %
8 %                      PPPP   AAAAA    I    N N N    T                        %
9 %                      P      A   A    I    N  NN    T                        %
10 %                      P      A   A  IIIII  N   N    T                        %
11 %                                                                             %
12 %                                                                             %
13 %                        Methods to Paint on an Image                         %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                 July 1998                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40  Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/channel.h"
45 #include "MagickCore/color.h"
46 #include "MagickCore/color-private.h"
47 #include "MagickCore/colorspace-private.h"
48 #include "MagickCore/composite.h"
49 #include "MagickCore/composite-private.h"
50 #include "MagickCore/draw.h"
51 #include "MagickCore/draw-private.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/gem.h"
55 #include "MagickCore/gem-private.h"
56 #include "MagickCore/monitor.h"
57 #include "MagickCore/monitor-private.h"
58 #include "MagickCore/option.h"
59 #include "MagickCore/paint.h"
60 #include "MagickCore/pixel-accessor.h"
61 #include "MagickCore/resource_.h"
62 #include "MagickCore/statistic.h"
63 #include "MagickCore/string_.h"
64 #include "MagickCore/string-private.h"
65 #include "MagickCore/thread-private.h"
66 \f
67 /*
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
69 %                                                                             %
70 %                                                                             %
71 %                                                                             %
72 %   F l o o d f i l l P a i n t I m a g e                                     %
73 %                                                                             %
74 %                                                                             %
75 %                                                                             %
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77 %
78 %  FloodfillPaintImage() changes the color value of any pixel that matches
79 %  target and is an immediate neighbor.  If the method FillToBorderMethod is
80 %  specified, the color value is changed for any neighbor pixel that does not
81 %  match the bordercolor member of image.
82 %
83 %  By default target must match a particular pixel color exactly.  However,
84 %  in many cases two colors may differ by a small amount.  The fuzz member of
85 %  image defines how much tolerance is acceptable to consider two colors as
86 %  the same.  For example, set fuzz to 10 and the color red at intensities of
87 %  100 and 102 respectively are now interpreted as the same color for the
88 %  purposes of the floodfill.
89 %
90 %  The format of the FloodfillPaintImage method is:
91 %
92 %      MagickBooleanType FloodfillPaintImage(Image *image,
93 %        const DrawInfo *draw_info,const PixelInfo target,
94 %        const ssize_t x_offset,const ssize_t y_offset,
95 %        const MagickBooleanType invert,ExceptionInfo *exception)
96 %
97 %  A description of each parameter follows:
98 %
99 %    o image: the image.
100 %
101 %    o draw_info: the draw info.
102 %
103 %    o target: the RGB value of the target color.
104 %
105 %    o x_offset,y_offset: the starting location of the operation.
106 %
107 %    o invert: paint any pixel that does not match the target color.
108 %
109 %    o exception: return any errors or warnings in this structure.
110 %
111 */
112 MagickExport MagickBooleanType FloodfillPaintImage(Image *image,
113   const DrawInfo *draw_info,const PixelInfo *target,const ssize_t x_offset,
114   const ssize_t y_offset,const MagickBooleanType invert,
115   ExceptionInfo *exception)
116 {
117 #define MaxStacksize  262144UL
118 #define PushSegmentStack(up,left,right,delta) \
119 { \
120   if (s >= (segment_stack+MaxStacksize)) \
121     ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \
122   else \
123     { \
124       if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (ssize_t) image->rows)) \
125         { \
126           s->x1=(double) (left); \
127           s->y1=(double) (up); \
128           s->x2=(double) (right); \
129           s->y2=(double) (delta); \
130           s++; \
131         } \
132     } \
133 }
134
135   CacheView
136     *floodplane_view,
137     *image_view;
138
139   Image
140     *floodplane_image;
141
142   MagickBooleanType
143     skip,
144     status;
145
146   MemoryInfo
147     *segment_info;
148
149   PixelInfo
150     fill_color,
151     pixel;
152
153   register SegmentInfo
154     *s;
155
156   SegmentInfo
157     *segment_stack;
158
159   ssize_t
160     offset,
161     start,
162     x1,
163     x2,
164     y;
165
166   /*
167     Check boundary conditions.
168   */
169   assert(image != (Image *) NULL);
170   assert(image->signature == MagickCoreSignature);
171   if (image->debug != MagickFalse)
172     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
173   assert(draw_info != (DrawInfo *) NULL);
174   assert(draw_info->signature == MagickCoreSignature);
175   if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
176     return(MagickFalse);
177   if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
178     return(MagickFalse);
179   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
180     return(MagickFalse);
181   if (IsGrayColorspace(image->colorspace) != MagickFalse)
182     (void) SetImageColorspace(image,sRGBColorspace,exception);
183   if ((image->alpha_trait == UndefinedPixelTrait) &&
184       (draw_info->fill.alpha_trait != UndefinedPixelTrait))
185     (void) SetImageAlpha(image,OpaqueAlpha,exception);
186   /*
187     Set floodfill state.
188   */
189   floodplane_image=CloneImage(image,image->columns,image->rows,MagickTrue,
190     exception);
191   if (floodplane_image == (Image *) NULL)
192     return(MagickFalse);
193   floodplane_image->alpha_trait=UndefinedPixelTrait;
194   floodplane_image->colorspace=GRAYColorspace;
195   (void) QueryColorCompliance("#000",AllCompliance,
196     &floodplane_image->background_color,exception);
197   (void) SetImageBackgroundColor(floodplane_image,exception);
198   segment_info=AcquireVirtualMemory(MaxStacksize,sizeof(*segment_stack));
199   if (segment_info == (MemoryInfo *) NULL)
200     {
201       floodplane_image=DestroyImage(floodplane_image);
202       ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
203         image->filename);
204     }
205   segment_stack=(SegmentInfo *) GetVirtualMemoryBlob(segment_info);
206   /*
207     Push initial segment on stack.
208   */
209   status=MagickTrue;
210   start=0;
211   s=segment_stack;
212   PushSegmentStack(y_offset,x_offset,x_offset,1);
213   PushSegmentStack(y_offset+1,x_offset,x_offset,-1);
214   GetPixelInfo(image,&pixel);
215   image_view=AcquireVirtualCacheView(image,exception);
216   floodplane_view=AcquireAuthenticCacheView(floodplane_image,exception);
217   while (s > segment_stack)
218   {
219     register const Quantum
220       *restrict p;
221
222     register Quantum
223       *restrict q;
224
225     register ssize_t
226       x;
227
228     /*
229       Pop segment off stack.
230     */
231     s--;
232     x1=(ssize_t) s->x1;
233     x2=(ssize_t) s->x2;
234     offset=(ssize_t) s->y2;
235     y=(ssize_t) s->y1+offset;
236     /*
237       Recolor neighboring pixels.
238     */
239     p=GetCacheViewVirtualPixels(image_view,0,y,(size_t) (x1+1),1,exception);
240     q=GetCacheViewAuthenticPixels(floodplane_view,0,y,(size_t) (x1+1),1,
241       exception);
242     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
243       break;
244     p+=x1*GetPixelChannels(image);
245     q+=x1*GetPixelChannels(floodplane_image);
246     for (x=x1; x >= 0; x--)
247     {
248       if (GetPixelGray(floodplane_image,q) != 0)
249         break;
250       GetPixelInfoPixel(image,p,&pixel);
251       if (IsFuzzyEquivalencePixelInfo(&pixel,target) == invert)
252         break;
253       SetPixelGray(floodplane_image,QuantumRange,q);
254       p-=GetPixelChannels(image);
255       q-=GetPixelChannels(floodplane_image);
256     }
257     if (SyncCacheViewAuthenticPixels(floodplane_view,exception) == MagickFalse)
258       break;
259     skip=x >= x1 ? MagickTrue : MagickFalse;
260     if (skip == MagickFalse)
261       {
262         start=x+1;
263         if (start < x1)
264           PushSegmentStack(y,start,x1-1,-offset);
265         x=x1+1;
266       }
267     do
268     {
269       if (skip == MagickFalse)
270         {
271           if (x < (ssize_t) image->columns)
272             {
273               p=GetCacheViewVirtualPixels(image_view,x,y,image->columns-x,1,
274                 exception);
275               q=GetCacheViewAuthenticPixels(floodplane_view,x,y,image->columns-
276                 x,1,exception);
277               if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
278                 break;
279               for ( ; x < (ssize_t) image->columns; x++)
280               {
281                 if (GetPixelGray(floodplane_image,q) != 0)
282                   break;
283                 GetPixelInfoPixel(image,p,&pixel);
284                 if (IsFuzzyEquivalencePixelInfo(&pixel,target) == invert)
285                   break;
286                 SetPixelGray(floodplane_image,QuantumRange,q);
287                 p+=GetPixelChannels(image);
288                 q+=GetPixelChannels(floodplane_image);
289               }
290               status=SyncCacheViewAuthenticPixels(floodplane_view,exception);
291               if (status == MagickFalse)
292                 break;
293             }
294           PushSegmentStack(y,start,x-1,offset);
295           if (x > (x2+1))
296             PushSegmentStack(y,x2+1,x-1,-offset);
297         }
298       skip=MagickFalse;
299       x++;
300       if (x <= x2)
301         {
302           p=GetCacheViewVirtualPixels(image_view,x,y,(size_t) (x2-x+1),1,
303             exception);
304           q=GetCacheViewAuthenticPixels(floodplane_view,x,y,(size_t) (x2-x+1),1,
305             exception);
306           if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
307             break;
308           for ( ; x <= x2; x++)
309           {
310             if (GetPixelGray(floodplane_image,q) != 0)
311               break;
312             GetPixelInfoPixel(image,p,&pixel);
313             if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert)
314               break;
315             p+=GetPixelChannels(image);
316             q+=GetPixelChannels(floodplane_image);
317           }
318         }
319       start=x;
320     } while (x <= x2);
321   }
322   status=MagickTrue;
323 #if defined(MAGICKCORE_OPENMP_SUPPORT)
324   #pragma omp parallel for schedule(static,4) shared(status) \
325     magick_threads(floodplane_image,image,floodplane_image->rows,1)
326 #endif
327   for (y=0; y < (ssize_t) image->rows; y++)
328   {
329     register const Quantum
330       *restrict p;
331
332     register Quantum
333       *restrict q;
334
335     register ssize_t
336       x;
337
338     /*
339       Tile fill color onto floodplane.
340     */
341     if (status == MagickFalse)
342       continue;
343     p=GetCacheViewVirtualPixels(floodplane_view,0,y,image->columns,1,exception);
344     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
345     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
346       {
347         status=MagickFalse;
348         continue;
349       }
350     for (x=0; x < (ssize_t) image->columns; x++)
351     {
352       if (GetPixelGray(floodplane_image,p) != 0)
353         {
354           (void) GetFillColor(draw_info,x,y,&fill_color,exception);
355           SetPixelViaPixelInfo(image,&fill_color,q);
356         }
357       p+=GetPixelChannels(floodplane_image);
358       q+=GetPixelChannels(image);
359     }
360     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
361       status=MagickFalse;
362   }
363   floodplane_view=DestroyCacheView(floodplane_view);
364   image_view=DestroyCacheView(image_view);
365   segment_info=RelinquishVirtualMemory(segment_info);
366   floodplane_image=DestroyImage(floodplane_image);
367   return(status);
368 }
369 \f
370 /*
371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
372 %                                                                             %
373 %                                                                             %
374 %                                                                             %
375 +     G r a d i e n t I m a g e                                               %
376 %                                                                             %
377 %                                                                             %
378 %                                                                             %
379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
380 %
381 %  GradientImage() applies a continuously smooth color transitions along a
382 %  vector from one color to another.
383 %
384 %  Note, the interface of this method will change in the future to support
385 %  more than one transistion.
386 %
387 %  The format of the GradientImage method is:
388 %
389 %      MagickBooleanType GradientImage(Image *image,const GradientType type,
390 %        const SpreadMethod method,const PixelInfo *start_color,
391 %        const PixelInfo *stop_color,ExceptionInfo *exception)
392 %
393 %  A description of each parameter follows:
394 %
395 %    o image: the image.
396 %
397 %    o type: the gradient type: linear or radial.
398 %
399 %    o spread: the gradient spread meathod: pad, reflect, or repeat.
400 %
401 %    o start_color: the start color.
402 %
403 %    o stop_color: the stop color.
404 %
405 %    o exception: return any errors or warnings in this structure.
406 %
407 */
408
409 static inline double MagickRound(double x)
410 {
411   /*
412     Round the fraction to nearest integer.
413   */
414   if ((x-floor(x)) < (ceil(x)-x))
415     return(floor(x));
416   return(ceil(x));
417 }
418
419 MagickExport MagickBooleanType GradientImage(Image *image,
420   const GradientType type,const SpreadMethod method,const StopInfo *stops,
421   const size_t number_stops,ExceptionInfo *exception)
422 {
423   const char
424     *artifact;
425
426   DrawInfo
427     *draw_info;
428
429   GradientInfo
430     *gradient;
431
432   MagickBooleanType
433     status;
434
435   /*
436     Set gradient start-stop end points.
437   */
438   assert(image != (const Image *) NULL);
439   assert(image->signature == MagickCoreSignature);
440   if (image->debug != MagickFalse)
441     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
442   assert(stops != (const StopInfo *) NULL);
443   assert(number_stops > 0);
444   draw_info=AcquireDrawInfo();
445   gradient=(&draw_info->gradient);
446   gradient->type=type;
447   gradient->bounding_box.width=image->columns;
448   gradient->bounding_box.height=image->rows;
449   artifact=GetImageArtifact(image,"gradient:bounding-box");
450   if (artifact != (const char *) NULL)
451     (void) ParseAbsoluteGeometry(artifact,&gradient->bounding_box);
452   gradient->gradient_vector.x2=(double) image->columns-1.0;
453   gradient->gradient_vector.y2=(double) image->rows-1.0;
454   artifact=GetImageArtifact(image,"gradient:direction");
455   if (artifact != (const char *) NULL)
456     {
457       GravityType
458         direction;
459
460       direction=(GravityType) ParseCommandOption(MagickGravityOptions,
461         MagickFalse,artifact);
462       switch (direction)
463       {
464         case NorthWestGravity:
465         {
466           gradient->gradient_vector.x1=(double) image->columns-1.0;
467           gradient->gradient_vector.y1=(double) image->rows-1.0;
468           gradient->gradient_vector.x2=0.0;
469           gradient->gradient_vector.y2=0.0;
470           break;
471         }
472         case NorthGravity:
473         {
474           gradient->gradient_vector.x1=0.0;
475           gradient->gradient_vector.y1=(double) image->rows-1.0;
476           gradient->gradient_vector.x2=0.0;
477           gradient->gradient_vector.y2=0.0;
478           break;
479         }
480         case NorthEastGravity:
481         {
482           gradient->gradient_vector.x1=0.0;
483           gradient->gradient_vector.y1=(double) image->rows-1.0;
484           gradient->gradient_vector.x2=(double) image->columns-1.0;
485           gradient->gradient_vector.y2=0.0;
486           break;
487         }
488         case WestGravity:
489         {
490           gradient->gradient_vector.x1=(double) image->columns-1.0;
491           gradient->gradient_vector.y1=0.0;
492           gradient->gradient_vector.x2=0.0;
493           gradient->gradient_vector.y2=0.0;
494           break;
495         }
496         case EastGravity:
497         {
498           gradient->gradient_vector.x1=0.0;
499           gradient->gradient_vector.y1=0.0;
500           gradient->gradient_vector.x2=(double) image->columns-1.0;
501           gradient->gradient_vector.y2=0.0;
502           break;
503         }
504         case SouthWestGravity:
505         {
506           gradient->gradient_vector.x1=(double) image->columns-1.0;
507           gradient->gradient_vector.y1=0.0;
508           gradient->gradient_vector.x2=0.0;
509           gradient->gradient_vector.y2=(double) image->rows-1.0;
510           break;
511         }
512         case SouthGravity:
513         {
514           gradient->gradient_vector.x1=0.0;
515           gradient->gradient_vector.y1=0.0;
516           gradient->gradient_vector.x2=0.0;
517           gradient->gradient_vector.y2=(double) image->columns-1.0;
518           break;
519         }
520         case SouthEastGravity:
521         {
522           gradient->gradient_vector.x1=0.0;
523           gradient->gradient_vector.y1=0.0;
524           gradient->gradient_vector.x2=(double) image->columns-1.0;
525           gradient->gradient_vector.y2=(double) image->rows-1.0;
526           break;
527         }
528         default:
529           break;
530       }
531     }
532   artifact=GetImageArtifact(image,"gradient:angle");
533   if (artifact != (const char *) NULL)
534     gradient->angle=StringToDouble(artifact,(char **) NULL);
535   artifact=GetImageArtifact(image,"gradient:vector");
536   if (artifact != (const char *) NULL)
537     (void) sscanf(artifact,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",
538       &gradient->gradient_vector.x1,&gradient->gradient_vector.y1,
539       &gradient->gradient_vector.x2,&gradient->gradient_vector.y2);
540   if ((GetImageArtifact(image,"gradient:angle") == (const char *) NULL) &&
541       (GetImageArtifact(image,"gradient:direction") == (const char *) NULL) &&
542       (GetImageArtifact(image,"gradient:extent") == (const char *) NULL) &&
543       (GetImageArtifact(image,"gradient:vector") == (const char *) NULL))
544     if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
545       gradient->gradient_vector.x2=0.0;
546   gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
547   gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
548   artifact=GetImageArtifact(image,"gradient:center");
549   if (artifact != (const char *) NULL)
550     (void) sscanf(artifact,"%lf%*[ ,]%lf",&gradient->center.x,
551       &gradient->center.y);
552   gradient->radii.x=MagickMax(gradient->center.x,gradient->center.y);
553   gradient->radii.y=gradient->radii.x;
554   artifact=GetImageArtifact(image,"gradient:radii");
555   if (artifact != (const char *) NULL)
556     (void) sscanf(artifact,"%lf%*[ ,]%lf",&gradient->radii.x,
557       &gradient->radii.y);
558   artifact=GetImageArtifact(image,"gradient:extent");
559   if (artifact != (const char *) NULL)
560     {
561       if (LocaleCompare(artifact,"Circle") == 0)
562         {
563           gradient->radii.x=(double) (MagickMax(image->columns,image->rows))/
564             2.0;
565           gradient->radii.y=gradient->radii.x;
566         }
567       if (LocaleCompare(artifact,"Diagonal") == 0)
568         {
569           gradient->radii.x=(double) (sqrt(image->columns*image->columns+
570             image->rows*image->rows))/2.0;
571           gradient->radii.y=gradient->radii.x;
572         }
573       if (LocaleCompare(artifact,"Ellipse") == 0)
574         {
575           gradient->radii.x=(double) image->columns/2.0;
576           gradient->radii.y=(double) image->rows/2.0;
577         }
578       if (LocaleCompare(artifact,"Maximum") == 0)
579         {
580           gradient->radii.x=(double) (MagickMax(image->columns,image->rows))/
581             2.0;
582           gradient->radii.y=gradient->radii.x;
583         }
584       if (LocaleCompare(artifact,"Mininum") == 0)
585         {
586           gradient->radii.x=(double) (MagickMin(image->columns,image->rows))/
587             2.0;
588           gradient->radii.y=gradient->radii.x;
589         }
590     }
591   gradient->radius=MagickMax(gradient->radii.x,gradient->radii.y);
592   gradient->spread=method;
593   /*
594     Define the gradient to fill between the stops.
595   */
596   gradient->number_stops=number_stops;
597   gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
598     sizeof(*gradient->stops));
599   if (gradient->stops == (StopInfo *) NULL)
600     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
601       image->filename);
602   (void) CopyMagickMemory(gradient->stops,stops,(size_t) number_stops*
603     sizeof(*stops));
604   /*
605     Draw a gradient on the image.
606   */
607   status=DrawGradientImage(image,draw_info,exception);
608   draw_info=DestroyDrawInfo(draw_info);
609   return(status);
610 }
611 \f
612 /*
613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
614 %                                                                             %
615 %                                                                             %
616 %                                                                             %
617 %     O i l P a i n t I m a g e                                               %
618 %                                                                             %
619 %                                                                             %
620 %                                                                             %
621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
622 %
623 %  OilPaintImage() applies a special effect filter that simulates an oil
624 %  painting.  Each pixel is replaced by the most frequent color occurring
625 %  in a circular region defined by radius.
626 %
627 %  The format of the OilPaintImage method is:
628 %
629 %      Image *OilPaintImage(const Image *image,const double radius,
630 %        const double sigma,ExceptionInfo *exception)
631 %
632 %  A description of each parameter follows:
633 %
634 %    o image: the image.
635 %
636 %    o radius: the radius of the circular neighborhood.
637 %
638 %    o sigma: the standard deviation of the Gaussian, in pixels.
639 %
640 %    o exception: return any errors or warnings in this structure.
641 %
642 */
643
644 static size_t **DestroyHistogramThreadSet(size_t **histogram)
645 {
646   register ssize_t
647     i;
648
649   assert(histogram != (size_t **) NULL);
650   for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
651     if (histogram[i] != (size_t *) NULL)
652       histogram[i]=(size_t *) RelinquishMagickMemory(histogram[i]);
653   histogram=(size_t **) RelinquishMagickMemory(histogram);
654   return(histogram);
655 }
656
657 static size_t **AcquireHistogramThreadSet(const size_t count)
658 {
659   register ssize_t
660     i;
661
662   size_t
663     **histogram,
664     number_threads;
665
666   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
667   histogram=(size_t **) AcquireQuantumMemory(number_threads,sizeof(*histogram));
668   if (histogram == (size_t **) NULL)
669     return((size_t **) NULL);
670   (void) ResetMagickMemory(histogram,0,number_threads*sizeof(*histogram));
671   for (i=0; i < (ssize_t) number_threads; i++)
672   {
673     histogram[i]=(size_t *) AcquireQuantumMemory(count,sizeof(**histogram));
674     if (histogram[i] == (size_t *) NULL)
675       return(DestroyHistogramThreadSet(histogram));
676   }
677   return(histogram);
678 }
679
680 MagickExport Image *OilPaintImage(const Image *image,const double radius,
681   const double sigma,ExceptionInfo *exception)
682 {
683 #define NumberPaintBins  256
684 #define OilPaintImageTag  "OilPaint/Image"
685
686   CacheView
687     *image_view,
688     *paint_view;
689
690   Image
691     *linear_image,
692     *paint_image;
693
694   MagickBooleanType
695     status;
696
697   MagickOffsetType
698     progress;
699
700   size_t
701     **histograms,
702     width;
703
704   ssize_t
705     center,
706     y;
707
708   /*
709     Initialize painted image attributes.
710   */
711   assert(image != (const Image *) NULL);
712   assert(image->signature == MagickCoreSignature);
713   if (image->debug != MagickFalse)
714     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
715   assert(exception != (ExceptionInfo *) NULL);
716   assert(exception->signature == MagickCoreSignature);
717   width=GetOptimalKernelWidth2D(radius,sigma);
718   linear_image=CloneImage(image,0,0,MagickTrue,exception);
719   paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
720   if ((linear_image == (Image *) NULL) || (paint_image == (Image *) NULL))
721     {
722       if (linear_image != (Image *) NULL)
723         linear_image=DestroyImage(linear_image);
724       if (paint_image != (Image *) NULL)
725         linear_image=DestroyImage(paint_image);
726       return((Image *) NULL);
727     }
728   if (SetImageStorageClass(paint_image,DirectClass,exception) == MagickFalse)
729     {
730       linear_image=DestroyImage(linear_image);
731       paint_image=DestroyImage(paint_image);
732       return((Image *) NULL);
733     }
734   histograms=AcquireHistogramThreadSet(NumberPaintBins);
735   if (histograms == (size_t **) NULL)
736     {
737       linear_image=DestroyImage(linear_image);
738       paint_image=DestroyImage(paint_image);
739       ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
740     }
741   /*
742     Oil paint image.
743   */
744   status=MagickTrue;
745   progress=0;
746   center=(ssize_t) GetPixelChannels(linear_image)*(linear_image->columns+width)*
747     (width/2L)+GetPixelChannels(linear_image)*(width/2L);
748   image_view=AcquireVirtualCacheView(linear_image,exception);
749   paint_view=AcquireAuthenticCacheView(paint_image,exception);
750 #if defined(MAGICKCORE_OPENMP_SUPPORT)
751   #pragma omp parallel for schedule(static,4) shared(progress,status) \
752     magick_threads(linear_image,paint_image,linear_image->rows,1)
753 #endif
754   for (y=0; y < (ssize_t) linear_image->rows; y++)
755   {
756     register const Quantum
757       *restrict p;
758
759     register Quantum
760       *restrict q;
761
762     register size_t
763       *histogram;
764
765     register ssize_t
766       x;
767
768     if (status == MagickFalse)
769       continue;
770     p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
771       (width/2L),linear_image->columns+width,width,exception);
772     q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
773       exception);
774     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
775       {
776         status=MagickFalse;
777         continue;
778       }
779     histogram=histograms[GetOpenMPThreadId()];
780     for (x=0; x < (ssize_t) linear_image->columns; x++)
781     {
782       register ssize_t
783         i,
784         u;
785
786       size_t
787         count;
788
789       ssize_t
790         j,
791         k,
792         n,
793         v;
794
795       /*
796         Assign most frequent color.
797       */
798       k=0;
799       j=0;
800       count=0;
801       (void) ResetMagickMemory(histogram,0,NumberPaintBins* sizeof(*histogram));
802       for (v=0; v < (ssize_t) width; v++)
803       {
804         for (u=0; u < (ssize_t) width; u++)
805         {
806           n=(ssize_t) ScaleQuantumToChar(ClampToQuantum(GetPixelIntensity(
807             linear_image,p+GetPixelChannels(linear_image)*(u+k))));
808           histogram[n]++;
809           if (histogram[n] > count)
810             {
811               j=k+u;
812               count=histogram[n];
813             }
814         }
815         k+=(ssize_t) (linear_image->columns+width);
816       }
817       for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
818       {
819         PixelChannel channel=GetPixelChannelChannel(linear_image,i);
820         PixelTrait traits=GetPixelChannelTraits(linear_image,channel);
821         PixelTrait paint_traits=GetPixelChannelTraits(paint_image,channel);
822         if ((traits == UndefinedPixelTrait) ||
823             (paint_traits == UndefinedPixelTrait))
824           continue;
825         if (((paint_traits & CopyPixelTrait) != 0) ||
826             (GetPixelReadMask(linear_image,p) == 0))
827           {
828             SetPixelChannel(paint_image,channel,p[center+i],q);
829             continue;
830           }
831         SetPixelChannel(paint_image,channel,p[j*GetPixelChannels(linear_image)+
832           i],q);
833       }
834       p+=GetPixelChannels(linear_image);
835       q+=GetPixelChannels(paint_image);
836     }
837     if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
838       status=MagickFalse;
839     if (linear_image->progress_monitor != (MagickProgressMonitor) NULL)
840       {
841         MagickBooleanType
842           proceed;
843
844 #if defined(MAGICKCORE_OPENMP_SUPPORT)
845         #pragma omp critical (MagickCore_OilPaintImage)
846 #endif
847         proceed=SetImageProgress(linear_image,OilPaintImageTag,progress++,
848           linear_image->rows);
849         if (proceed == MagickFalse)
850           status=MagickFalse;
851       }
852   }
853   paint_view=DestroyCacheView(paint_view);
854   image_view=DestroyCacheView(image_view);
855   histograms=DestroyHistogramThreadSet(histograms);
856   linear_image=DestroyImage(linear_image);
857   if (status == MagickFalse)
858     paint_image=DestroyImage(paint_image);
859   return(paint_image);
860 }
861 \f
862 /*
863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
864 %                                                                             %
865 %                                                                             %
866 %                                                                             %
867 %     O p a q u e P a i n t I m a g e                                         %
868 %                                                                             %
869 %                                                                             %
870 %                                                                             %
871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
872 %
873 %  OpaquePaintImage() changes any pixel that matches color with the color
874 %  defined by fill argument.
875 %
876 %  By default color must match a particular pixel color exactly.  However, in
877 %  many cases two colors may differ by a small amount.  Fuzz defines how much
878 %  tolerance is acceptable to consider two colors as the same.  For example,
879 %  set fuzz to 10 and the color red at intensities of 100 and 102 respectively
880 %  are now interpreted as the same color.
881 %
882 %  The format of the OpaquePaintImage method is:
883 %
884 %      MagickBooleanType OpaquePaintImage(Image *image,const PixelInfo *target,
885 %        const PixelInfo *fill,const MagickBooleanType invert,
886 %        ExceptionInfo *exception)
887 %
888 %  A description of each parameter follows:
889 %
890 %    o image: the image.
891 %
892 %    o target: the RGB value of the target color.
893 %
894 %    o fill: the replacement color.
895 %
896 %    o invert: paint any pixel that does not match the target color.
897 %
898 %    o exception: return any errors or warnings in this structure.
899 %
900 */
901 MagickExport MagickBooleanType OpaquePaintImage(Image *image,
902   const PixelInfo *target,const PixelInfo *fill,const MagickBooleanType invert,
903   ExceptionInfo *exception)
904 {
905 #define OpaquePaintImageTag  "Opaque/Image"
906
907   CacheView
908     *image_view;
909
910   MagickBooleanType
911     status;
912
913   MagickOffsetType
914     progress;
915
916   PixelInfo
917     conform_fill,
918     conform_target,
919     zero;
920
921   ssize_t
922     y;
923
924   assert(image != (Image *) NULL);
925   assert(image->signature == MagickCoreSignature);
926   assert(target != (PixelInfo *) NULL);
927   assert(fill != (PixelInfo *) NULL);
928   if (image->debug != MagickFalse)
929     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
930   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
931     return(MagickFalse);
932   ConformPixelInfo(image,fill,&conform_fill,exception);
933   ConformPixelInfo(image,target,&conform_target,exception);
934   /*
935     Make image color opaque.
936   */
937   status=MagickTrue;
938   progress=0;
939   GetPixelInfo(image,&zero);
940   image_view=AcquireAuthenticCacheView(image,exception);
941 #if defined(MAGICKCORE_OPENMP_SUPPORT)
942   #pragma omp parallel for schedule(static,4) shared(progress,status) \
943     magick_threads(image,image,image->rows,1)
944 #endif
945   for (y=0; y < (ssize_t) image->rows; y++)
946   {
947     PixelInfo
948       pixel;
949
950     register Quantum
951       *restrict q;
952
953     register ssize_t
954       x;
955
956     if (status == MagickFalse)
957       continue;
958     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
959     if (q == (Quantum *) NULL)
960       {
961         status=MagickFalse;
962         continue;
963       }
964     pixel=zero;
965     for (x=0; x < (ssize_t) image->columns; x++)
966     {
967       GetPixelInfoPixel(image,q,&pixel);
968       if (IsFuzzyEquivalencePixelInfo(&pixel,&conform_target) != invert)
969         SetPixelViaPixelInfo(image,&conform_fill,q);
970       q+=GetPixelChannels(image);
971     }
972     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
973       status=MagickFalse;
974     if (image->progress_monitor != (MagickProgressMonitor) NULL)
975       {
976         MagickBooleanType
977           proceed;
978
979 #if defined(MAGICKCORE_OPENMP_SUPPORT)
980         #pragma omp critical (MagickCore_OpaquePaintImage)
981 #endif
982         proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
983           image->rows);
984         if (proceed == MagickFalse)
985           status=MagickFalse;
986       }
987   }
988   image_view=DestroyCacheView(image_view);
989   return(status);
990 }
991 \f
992 /*
993 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
994 %                                                                             %
995 %                                                                             %
996 %                                                                             %
997 %     T r a n s p a r e n t P a i n t I m a g e                               %
998 %                                                                             %
999 %                                                                             %
1000 %                                                                             %
1001 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1002 %
1003 %  TransparentPaintImage() changes the opacity value associated with any pixel
1004 %  that matches color to the value defined by opacity.
1005 %
1006 %  By default color must match a particular pixel color exactly.  However, in
1007 %  many cases two colors may differ by a small amount.  Fuzz defines how much
1008 %  tolerance is acceptable to consider two colors as the same.  For example,
1009 %  set fuzz to 10 and the color red at intensities of 100 and 102 respectively
1010 %  are now interpreted as the same color.
1011 %
1012 %  The format of the TransparentPaintImage method is:
1013 %
1014 %      MagickBooleanType TransparentPaintImage(Image *image,
1015 %        const PixelInfo *target,const Quantum opacity,
1016 %        const MagickBooleanType invert,ExceptionInfo *exception)
1017 %
1018 %  A description of each parameter follows:
1019 %
1020 %    o image: the image.
1021 %
1022 %    o target: the target color.
1023 %
1024 %    o opacity: the replacement opacity value.
1025 %
1026 %    o invert: paint any pixel that does not match the target color.
1027 %
1028 %    o exception: return any errors or warnings in this structure.
1029 %
1030 */
1031 MagickExport MagickBooleanType TransparentPaintImage(Image *image,
1032   const PixelInfo *target,const Quantum opacity,const MagickBooleanType invert,
1033   ExceptionInfo *exception)
1034 {
1035 #define TransparentPaintImageTag  "Transparent/Image"
1036
1037   CacheView
1038     *image_view;
1039
1040   MagickBooleanType
1041     status;
1042
1043   MagickOffsetType
1044     progress;
1045
1046   PixelInfo
1047     zero;
1048
1049   ssize_t
1050     y;
1051
1052   assert(image != (Image *) NULL);
1053   assert(image->signature == MagickCoreSignature);
1054   assert(target != (PixelInfo *) NULL);
1055   if (image->debug != MagickFalse)
1056     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1057   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1058     return(MagickFalse);
1059   if (image->alpha_trait == UndefinedPixelTrait)
1060     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1061   /*
1062     Make image color transparent.
1063   */
1064   status=MagickTrue;
1065   progress=0;
1066   GetPixelInfo(image,&zero);
1067   image_view=AcquireAuthenticCacheView(image,exception);
1068 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1069   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1070     magick_threads(image,image,image->rows,1)
1071 #endif
1072   for (y=0; y < (ssize_t) image->rows; y++)
1073   {
1074     PixelInfo
1075       pixel;
1076
1077     register ssize_t
1078       x;
1079
1080     register Quantum
1081       *restrict q;
1082
1083     if (status == MagickFalse)
1084       continue;
1085     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1086     if (q == (Quantum *) NULL)
1087       {
1088         status=MagickFalse;
1089         continue;
1090       }
1091     pixel=zero;
1092     for (x=0; x < (ssize_t) image->columns; x++)
1093     {
1094       GetPixelInfoPixel(image,q,&pixel);
1095       if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert)
1096         SetPixelAlpha(image,opacity,q);
1097       q+=GetPixelChannels(image);
1098     }
1099     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1100       status=MagickFalse;
1101     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1102       {
1103         MagickBooleanType
1104           proceed;
1105
1106 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1107         #pragma omp critical (MagickCore_TransparentPaintImage)
1108 #endif
1109         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1110           image->rows);
1111         if (proceed == MagickFalse)
1112           status=MagickFalse;
1113       }
1114   }
1115   image_view=DestroyCacheView(image_view);
1116   return(status);
1117 }
1118 \f
1119 /*
1120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1121 %                                                                             %
1122 %                                                                             %
1123 %                                                                             %
1124 %     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                   %
1125 %                                                                             %
1126 %                                                                             %
1127 %                                                                             %
1128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1129 %
1130 %  TransparentPaintImageChroma() changes the opacity value associated with any
1131 %  pixel that matches color to the value defined by opacity.
1132 %
1133 %  As there is one fuzz value for the all the channels, TransparentPaintImage()
1134 %  is not suitable for the operations like chroma, where the tolerance for
1135 %  similarity of two color component (RGB) can be different. Thus we define
1136 %  this method to take two target pixels (one low and one high) and all the
1137 %  pixels of an image which are lying between these two pixels are made
1138 %  transparent.
1139 %
1140 %  The format of the TransparentPaintImageChroma method is:
1141 %
1142 %      MagickBooleanType TransparentPaintImageChroma(Image *image,
1143 %        const PixelInfo *low,const PixelInfo *high,const Quantum opacity,
1144 %        const MagickBooleanType invert,ExceptionInfo *exception)
1145 %
1146 %  A description of each parameter follows:
1147 %
1148 %    o image: the image.
1149 %
1150 %    o low: the low target color.
1151 %
1152 %    o high: the high target color.
1153 %
1154 %    o opacity: the replacement opacity value.
1155 %
1156 %    o invert: paint any pixel that does not match the target color.
1157 %
1158 %    o exception: return any errors or warnings in this structure.
1159 %
1160 */
1161 MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1162   const PixelInfo *low,const PixelInfo *high,const Quantum opacity,
1163   const MagickBooleanType invert,ExceptionInfo *exception)
1164 {
1165 #define TransparentPaintImageTag  "Transparent/Image"
1166
1167   CacheView
1168     *image_view;
1169
1170   MagickBooleanType
1171     status;
1172
1173   MagickOffsetType
1174     progress;
1175
1176   ssize_t
1177     y;
1178
1179   assert(image != (Image *) NULL);
1180   assert(image->signature == MagickCoreSignature);
1181   assert(high != (PixelInfo *) NULL);
1182   assert(low != (PixelInfo *) NULL);
1183   if (image->debug != MagickFalse)
1184     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1185   if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1186     return(MagickFalse);
1187   if (image->alpha_trait == UndefinedPixelTrait)
1188     (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1189   /*
1190     Make image color transparent.
1191   */
1192   status=MagickTrue;
1193   progress=0;
1194   image_view=AcquireAuthenticCacheView(image,exception);
1195 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1196   #pragma omp parallel for schedule(static,4) shared(progress,status) \
1197     magick_threads(image,image,image->rows,1)
1198 #endif
1199   for (y=0; y < (ssize_t) image->rows; y++)
1200   {
1201     MagickBooleanType
1202       match;
1203
1204     PixelInfo
1205       pixel;
1206
1207     register Quantum
1208       *restrict q;
1209
1210     register ssize_t
1211       x;
1212
1213     if (status == MagickFalse)
1214       continue;
1215     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1216     if (q == (Quantum *) NULL)
1217       {
1218         status=MagickFalse;
1219         continue;
1220       }
1221     GetPixelInfo(image,&pixel);
1222     for (x=0; x < (ssize_t) image->columns; x++)
1223     {
1224       GetPixelInfoPixel(image,q,&pixel);
1225       match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1226         (pixel.green >= low->green) && (pixel.green <= high->green) &&
1227         (pixel.blue  >= low->blue) && (pixel.blue <= high->blue)) ? MagickTrue :
1228         MagickFalse;
1229       if (match != invert)
1230         SetPixelAlpha(image,opacity,q);
1231       q+=GetPixelChannels(image);
1232     }
1233     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1234       status=MagickFalse;
1235     if (image->progress_monitor != (MagickProgressMonitor) NULL)
1236       {
1237         MagickBooleanType
1238           proceed;
1239
1240 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1241         #pragma omp critical (MagickCore_TransparentPaintImageChroma)
1242 #endif
1243         proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1244           image->rows);
1245         if (proceed == MagickFalse)
1246           status=MagickFalse;
1247       }
1248   }
1249   image_view=DestroyCacheView(image_view);
1250   return(status);
1251 }