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