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