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