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