2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % PPPP AAA IIIII N N TTTTT %
8 % PPPP AAAAA I N N N T %
13 % Methods to Paint on an Image %
20 % Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 #include "MagickCore/studio.h"
43 #include "MagickCore/artifact.h"
44 #include "MagickCore/channel.h"
45 #include "MagickCore/color.h"
46 #include "MagickCore/color-private.h"
47 #include "MagickCore/colorspace-private.h"
48 #include "MagickCore/composite.h"
49 #include "MagickCore/composite-private.h"
50 #include "MagickCore/draw.h"
51 #include "MagickCore/draw-private.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/gem.h"
55 #include "MagickCore/gem-private.h"
56 #include "MagickCore/monitor.h"
57 #include "MagickCore/monitor-private.h"
58 #include "MagickCore/option.h"
59 #include "MagickCore/paint.h"
60 #include "MagickCore/pixel-accessor.h"
61 #include "MagickCore/resource_.h"
62 #include "MagickCore/statistic.h"
63 #include "MagickCore/string_.h"
64 #include "MagickCore/string-private.h"
65 #include "MagickCore/thread-private.h"
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72 % F l o o d f i l l P a i n t I m a g e %
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78 % FloodfillPaintImage() changes the color value of any pixel that matches
79 % target and is an immediate neighbor. If the method FillToBorderMethod is
80 % specified, the color value is changed for any neighbor pixel that does not
81 % match the bordercolor member of image.
83 % By default target must match a particular pixel color exactly. However,
84 % in many cases two colors may differ by a small amount. The fuzz member of
85 % image defines how much tolerance is acceptable to consider two colors as
86 % the same. For example, set fuzz to 10 and the color red at intensities of
87 % 100 and 102 respectively are now interpreted as the same color for the
88 % purposes of the floodfill.
90 % The format of the FloodfillPaintImage method is:
92 % MagickBooleanType FloodfillPaintImage(Image *image,
93 % const DrawInfo *draw_info,const PixelInfo target,
94 % const ssize_t x_offset,const ssize_t y_offset,
95 % const MagickBooleanType invert,ExceptionInfo *exception)
97 % A description of each parameter follows:
101 % o draw_info: the draw info.
103 % o target: the RGB value of the target color.
105 % o x_offset,y_offset: the starting location of the operation.
107 % o invert: paint any pixel that does not match the target color.
109 % o exception: return any errors or warnings in this structure.
112 MagickExport MagickBooleanType FloodfillPaintImage(Image *image,
113 const DrawInfo *draw_info,const PixelInfo *target,const ssize_t x_offset,
114 const ssize_t y_offset,const MagickBooleanType invert,
115 ExceptionInfo *exception)
117 #define MaxStacksize 262144UL
118 #define PushSegmentStack(up,left,right,delta) \
120 if (s >= (segment_stack+MaxStacksize)) \
121 ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \
124 if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (ssize_t) image->rows)) \
126 s->x1=(double) (left); \
127 s->y1=(double) (up); \
128 s->x2=(double) (right); \
129 s->y2=(double) (delta); \
167 Check boundary conditions.
169 assert(image != (Image *) NULL);
170 assert(image->signature == MagickCoreSignature);
171 if (image->debug != MagickFalse)
172 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
173 assert(draw_info != (DrawInfo *) NULL);
174 assert(draw_info->signature == MagickCoreSignature);
175 if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
177 if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
179 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
181 if (IsGrayColorspace(image->colorspace) != MagickFalse)
182 (void) SetImageColorspace(image,sRGBColorspace,exception);
183 if ((image->alpha_trait == UndefinedPixelTrait) &&
184 (draw_info->fill.alpha_trait != UndefinedPixelTrait))
185 (void) SetImageAlpha(image,OpaqueAlpha,exception);
189 floodplane_image=CloneImage(image,image->columns,image->rows,MagickTrue,
191 if (floodplane_image == (Image *) NULL)
193 floodplane_image->alpha_trait=UndefinedPixelTrait;
194 floodplane_image->colorspace=GRAYColorspace;
195 (void) QueryColorCompliance("#000",AllCompliance,
196 &floodplane_image->background_color,exception);
197 (void) SetImageBackgroundColor(floodplane_image,exception);
198 segment_info=AcquireVirtualMemory(MaxStacksize,sizeof(*segment_stack));
199 if (segment_info == (MemoryInfo *) NULL)
201 floodplane_image=DestroyImage(floodplane_image);
202 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
205 segment_stack=(SegmentInfo *) GetVirtualMemoryBlob(segment_info);
207 Push initial segment on stack.
212 PushSegmentStack(y_offset,x_offset,x_offset,1);
213 PushSegmentStack(y_offset+1,x_offset,x_offset,-1);
214 GetPixelInfo(image,&pixel);
215 image_view=AcquireVirtualCacheView(image,exception);
216 floodplane_view=AcquireAuthenticCacheView(floodplane_image,exception);
217 while (s > segment_stack)
219 register const Quantum
229 Pop segment off stack.
234 offset=(ssize_t) s->y2;
235 y=(ssize_t) s->y1+offset;
237 Recolor neighboring pixels.
239 p=GetCacheViewVirtualPixels(image_view,0,y,(size_t) (x1+1),1,exception);
240 q=GetCacheViewAuthenticPixels(floodplane_view,0,y,(size_t) (x1+1),1,
242 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
244 p+=x1*GetPixelChannels(image);
245 q+=x1*GetPixelChannels(floodplane_image);
246 for (x=x1; x >= 0; x--)
248 if (GetPixelGray(floodplane_image,q) != 0)
250 GetPixelInfoPixel(image,p,&pixel);
251 if (IsFuzzyEquivalencePixelInfo(&pixel,target) == invert)
253 SetPixelGray(floodplane_image,QuantumRange,q);
254 p-=GetPixelChannels(image);
255 q-=GetPixelChannels(floodplane_image);
257 if (SyncCacheViewAuthenticPixels(floodplane_view,exception) == MagickFalse)
259 skip=x >= x1 ? MagickTrue : MagickFalse;
260 if (skip == MagickFalse)
264 PushSegmentStack(y,start,x1-1,-offset);
269 if (skip == MagickFalse)
271 if (x < (ssize_t) image->columns)
273 p=GetCacheViewVirtualPixels(image_view,x,y,image->columns-x,1,
275 q=GetCacheViewAuthenticPixels(floodplane_view,x,y,image->columns-
277 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
279 for ( ; x < (ssize_t) image->columns; x++)
281 if (GetPixelGray(floodplane_image,q) != 0)
283 GetPixelInfoPixel(image,p,&pixel);
284 if (IsFuzzyEquivalencePixelInfo(&pixel,target) == invert)
286 SetPixelGray(floodplane_image,QuantumRange,q);
287 p+=GetPixelChannels(image);
288 q+=GetPixelChannels(floodplane_image);
290 status=SyncCacheViewAuthenticPixels(floodplane_view,exception);
291 if (status == MagickFalse)
294 PushSegmentStack(y,start,x-1,offset);
296 PushSegmentStack(y,x2+1,x-1,-offset);
302 p=GetCacheViewVirtualPixels(image_view,x,y,(size_t) (x2-x+1),1,
304 q=GetCacheViewAuthenticPixels(floodplane_view,x,y,(size_t) (x2-x+1),1,
306 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
308 for ( ; x <= x2; x++)
310 if (GetPixelGray(floodplane_image,q) != 0)
312 GetPixelInfoPixel(image,p,&pixel);
313 if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert)
315 p+=GetPixelChannels(image);
316 q+=GetPixelChannels(floodplane_image);
323 #if defined(MAGICKCORE_OPENMP_SUPPORT)
324 #pragma omp parallel for schedule(static,4) shared(status) \
325 magick_threads(floodplane_image,image,floodplane_image->rows,1)
327 for (y=0; y < (ssize_t) image->rows; y++)
329 register const Quantum
339 Tile fill color onto floodplane.
341 if (status == MagickFalse)
343 p=GetCacheViewVirtualPixels(floodplane_view,0,y,image->columns,1,exception);
344 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
345 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
350 for (x=0; x < (ssize_t) image->columns; x++)
352 if (GetPixelGray(floodplane_image,p) != 0)
354 (void) GetFillColor(draw_info,x,y,&fill_color,exception);
355 SetPixelViaPixelInfo(image,&fill_color,q);
357 p+=GetPixelChannels(floodplane_image);
358 q+=GetPixelChannels(image);
360 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
363 floodplane_view=DestroyCacheView(floodplane_view);
364 image_view=DestroyCacheView(image_view);
365 segment_info=RelinquishVirtualMemory(segment_info);
366 floodplane_image=DestroyImage(floodplane_image);
371 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375 + G r a d i e n t I m a g e %
379 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
381 % GradientImage() applies a continuously smooth color transitions along a
382 % vector from one color to another.
384 % Note, the interface of this method will change in the future to support
385 % more than one transistion.
387 % The format of the GradientImage method is:
389 % MagickBooleanType GradientImage(Image *image,const GradientType type,
390 % const SpreadMethod method,const PixelInfo *start_color,
391 % const PixelInfo *stop_color,ExceptionInfo *exception)
393 % A description of each parameter follows:
395 % o image: the image.
397 % o type: the gradient type: linear or radial.
399 % o spread: the gradient spread meathod: pad, reflect, or repeat.
401 % o start_color: the start color.
403 % o stop_color: the stop color.
405 % o exception: return any errors or warnings in this structure.
409 static inline double MagickRound(double x)
412 Round the fraction to nearest integer.
414 if ((x-floor(x)) < (ceil(x)-x))
419 MagickExport MagickBooleanType GradientImage(Image *image,
420 const GradientType type,const SpreadMethod method,const StopInfo *stops,
421 const size_t number_stops,ExceptionInfo *exception)
436 Set gradient start-stop end points.
438 assert(image != (const Image *) NULL);
439 assert(image->signature == MagickCoreSignature);
440 if (image->debug != MagickFalse)
441 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
442 assert(stops != (const StopInfo *) NULL);
443 assert(number_stops > 0);
444 draw_info=AcquireDrawInfo();
445 gradient=(&draw_info->gradient);
447 gradient->bounding_box.width=image->columns;
448 gradient->bounding_box.height=image->rows;
449 artifact=GetImageArtifact(image,"gradient:bounding-box");
450 if (artifact != (const char *) NULL)
451 (void) ParseAbsoluteGeometry(artifact,&gradient->bounding_box);
452 gradient->gradient_vector.x2=(double) image->columns-1.0;
453 gradient->gradient_vector.y2=(double) image->rows-1.0;
454 artifact=GetImageArtifact(image,"gradient:direction");
455 if (artifact != (const char *) NULL)
460 direction=(GravityType) ParseCommandOption(MagickGravityOptions,
461 MagickFalse,artifact);
464 case NorthWestGravity:
466 gradient->gradient_vector.x1=(double) image->columns-1.0;
467 gradient->gradient_vector.y1=(double) image->rows-1.0;
468 gradient->gradient_vector.x2=0.0;
469 gradient->gradient_vector.y2=0.0;
474 gradient->gradient_vector.x1=0.0;
475 gradient->gradient_vector.y1=(double) image->rows-1.0;
476 gradient->gradient_vector.x2=0.0;
477 gradient->gradient_vector.y2=0.0;
480 case NorthEastGravity:
482 gradient->gradient_vector.x1=0.0;
483 gradient->gradient_vector.y1=(double) image->rows-1.0;
484 gradient->gradient_vector.x2=(double) image->columns-1.0;
485 gradient->gradient_vector.y2=0.0;
490 gradient->gradient_vector.x1=(double) image->columns-1.0;
491 gradient->gradient_vector.y1=0.0;
492 gradient->gradient_vector.x2=0.0;
493 gradient->gradient_vector.y2=0.0;
498 gradient->gradient_vector.x1=0.0;
499 gradient->gradient_vector.y1=0.0;
500 gradient->gradient_vector.x2=(double) image->columns-1.0;
501 gradient->gradient_vector.y2=0.0;
504 case SouthWestGravity:
506 gradient->gradient_vector.x1=(double) image->columns-1.0;
507 gradient->gradient_vector.y1=0.0;
508 gradient->gradient_vector.x2=0.0;
509 gradient->gradient_vector.y2=(double) image->rows-1.0;
514 gradient->gradient_vector.x1=0.0;
515 gradient->gradient_vector.y1=0.0;
516 gradient->gradient_vector.x2=0.0;
517 gradient->gradient_vector.y2=(double) image->columns-1.0;
520 case SouthEastGravity:
522 gradient->gradient_vector.x1=0.0;
523 gradient->gradient_vector.y1=0.0;
524 gradient->gradient_vector.x2=(double) image->columns-1.0;
525 gradient->gradient_vector.y2=(double) image->rows-1.0;
532 artifact=GetImageArtifact(image,"gradient:angle");
533 if (artifact != (const char *) NULL)
534 gradient->angle=StringToDouble(artifact,(char **) NULL);
535 artifact=GetImageArtifact(image,"gradient:vector");
536 if (artifact != (const char *) NULL)
537 (void) sscanf(artifact,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",
538 &gradient->gradient_vector.x1,&gradient->gradient_vector.y1,
539 &gradient->gradient_vector.x2,&gradient->gradient_vector.y2);
540 if ((GetImageArtifact(image,"gradient:angle") == (const char *) NULL) &&
541 (GetImageArtifact(image,"gradient:direction") == (const char *) NULL) &&
542 (GetImageArtifact(image,"gradient:extent") == (const char *) NULL) &&
543 (GetImageArtifact(image,"gradient:vector") == (const char *) NULL))
544 if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
545 gradient->gradient_vector.x2=0.0;
546 gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
547 gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
548 artifact=GetImageArtifact(image,"gradient:center");
549 if (artifact != (const char *) NULL)
550 (void) sscanf(artifact,"%lf%*[ ,]%lf",&gradient->center.x,
551 &gradient->center.y);
552 gradient->radii.x=MagickMax(gradient->center.x,gradient->center.y);
553 gradient->radii.y=gradient->radii.x;
554 artifact=GetImageArtifact(image,"gradient:radii");
555 if (artifact != (const char *) NULL)
556 (void) sscanf(artifact,"%lf%*[ ,]%lf",&gradient->radii.x,
558 artifact=GetImageArtifact(image,"gradient:extent");
559 if (artifact != (const char *) NULL)
561 if (LocaleCompare(artifact,"Circle") == 0)
563 gradient->radii.x=(double) (MagickMax(image->columns,image->rows))/
565 gradient->radii.y=gradient->radii.x;
567 if (LocaleCompare(artifact,"Diagonal") == 0)
569 gradient->radii.x=(double) (sqrt(image->columns*image->columns+
570 image->rows*image->rows))/2.0;
571 gradient->radii.y=gradient->radii.x;
573 if (LocaleCompare(artifact,"Ellipse") == 0)
575 gradient->radii.x=(double) image->columns/2.0;
576 gradient->radii.y=(double) image->rows/2.0;
578 if (LocaleCompare(artifact,"Maximum") == 0)
580 gradient->radii.x=(double) (MagickMax(image->columns,image->rows))/
582 gradient->radii.y=gradient->radii.x;
584 if (LocaleCompare(artifact,"Mininum") == 0)
586 gradient->radii.x=(double) (MagickMin(image->columns,image->rows))/
588 gradient->radii.y=gradient->radii.x;
591 gradient->radius=MagickMax(gradient->radii.x,gradient->radii.y);
592 gradient->spread=method;
594 Define the gradient to fill between the stops.
596 gradient->number_stops=number_stops;
597 gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
598 sizeof(*gradient->stops));
599 if (gradient->stops == (StopInfo *) NULL)
600 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
602 (void) CopyMagickMemory(gradient->stops,stops,(size_t) number_stops*
605 Draw a gradient on the image.
607 status=DrawGradientImage(image,draw_info,exception);
608 draw_info=DestroyDrawInfo(draw_info);
613 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
617 % O i l P a i n t I m a g e %
621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
623 % OilPaintImage() applies a special effect filter that simulates an oil
624 % painting. Each pixel is replaced by the most frequent color occurring
625 % in a circular region defined by radius.
627 % The format of the OilPaintImage method is:
629 % Image *OilPaintImage(const Image *image,const double radius,
630 % const double sigma,ExceptionInfo *exception)
632 % A description of each parameter follows:
634 % o image: the image.
636 % o radius: the radius of the circular neighborhood.
638 % o sigma: the standard deviation of the Gaussian, in pixels.
640 % o exception: return any errors or warnings in this structure.
644 static size_t **DestroyHistogramThreadSet(size_t **histogram)
649 assert(histogram != (size_t **) NULL);
650 for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
651 if (histogram[i] != (size_t *) NULL)
652 histogram[i]=(size_t *) RelinquishMagickMemory(histogram[i]);
653 histogram=(size_t **) RelinquishMagickMemory(histogram);
657 static size_t **AcquireHistogramThreadSet(const size_t count)
666 number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
667 histogram=(size_t **) AcquireQuantumMemory(number_threads,sizeof(*histogram));
668 if (histogram == (size_t **) NULL)
669 return((size_t **) NULL);
670 (void) ResetMagickMemory(histogram,0,number_threads*sizeof(*histogram));
671 for (i=0; i < (ssize_t) number_threads; i++)
673 histogram[i]=(size_t *) AcquireQuantumMemory(count,sizeof(**histogram));
674 if (histogram[i] == (size_t *) NULL)
675 return(DestroyHistogramThreadSet(histogram));
680 MagickExport Image *OilPaintImage(const Image *image,const double radius,
681 const double sigma,ExceptionInfo *exception)
683 #define NumberPaintBins 256
684 #define OilPaintImageTag "OilPaint/Image"
709 Initialize painted image attributes.
711 assert(image != (const Image *) NULL);
712 assert(image->signature == MagickCoreSignature);
713 if (image->debug != MagickFalse)
714 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
715 assert(exception != (ExceptionInfo *) NULL);
716 assert(exception->signature == MagickCoreSignature);
717 width=GetOptimalKernelWidth2D(radius,sigma);
718 linear_image=CloneImage(image,0,0,MagickTrue,exception);
719 paint_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception);
720 if ((linear_image == (Image *) NULL) || (paint_image == (Image *) NULL))
722 if (linear_image != (Image *) NULL)
723 linear_image=DestroyImage(linear_image);
724 if (paint_image != (Image *) NULL)
725 linear_image=DestroyImage(paint_image);
726 return((Image *) NULL);
728 if (SetImageStorageClass(paint_image,DirectClass,exception) == MagickFalse)
730 linear_image=DestroyImage(linear_image);
731 paint_image=DestroyImage(paint_image);
732 return((Image *) NULL);
734 histograms=AcquireHistogramThreadSet(NumberPaintBins);
735 if (histograms == (size_t **) NULL)
737 linear_image=DestroyImage(linear_image);
738 paint_image=DestroyImage(paint_image);
739 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
746 center=(ssize_t) GetPixelChannels(linear_image)*(linear_image->columns+width)*
747 (width/2L)+GetPixelChannels(linear_image)*(width/2L);
748 image_view=AcquireVirtualCacheView(linear_image,exception);
749 paint_view=AcquireAuthenticCacheView(paint_image,exception);
750 #if defined(MAGICKCORE_OPENMP_SUPPORT)
751 #pragma omp parallel for schedule(static,4) shared(progress,status) \
752 magick_threads(linear_image,paint_image,linear_image->rows,1)
754 for (y=0; y < (ssize_t) linear_image->rows; y++)
756 register const Quantum
768 if (status == MagickFalse)
770 p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
771 (width/2L),linear_image->columns+width,width,exception);
772 q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
774 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
779 histogram=histograms[GetOpenMPThreadId()];
780 for (x=0; x < (ssize_t) linear_image->columns; x++)
796 Assign most frequent color.
801 (void) ResetMagickMemory(histogram,0,NumberPaintBins* sizeof(*histogram));
802 for (v=0; v < (ssize_t) width; v++)
804 for (u=0; u < (ssize_t) width; u++)
806 n=(ssize_t) ScaleQuantumToChar(ClampToQuantum(GetPixelIntensity(
807 linear_image,p+GetPixelChannels(linear_image)*(u+k))));
809 if (histogram[n] > count)
815 k+=(ssize_t) (linear_image->columns+width);
817 for (i=0; i < (ssize_t) GetPixelChannels(linear_image); i++)
819 PixelChannel channel=GetPixelChannelChannel(linear_image,i);
820 PixelTrait traits=GetPixelChannelTraits(linear_image,channel);
821 PixelTrait paint_traits=GetPixelChannelTraits(paint_image,channel);
822 if ((traits == UndefinedPixelTrait) ||
823 (paint_traits == UndefinedPixelTrait))
825 if (((paint_traits & CopyPixelTrait) != 0) ||
826 (GetPixelReadMask(linear_image,p) == 0))
828 SetPixelChannel(paint_image,channel,p[center+i],q);
831 SetPixelChannel(paint_image,channel,p[j*GetPixelChannels(linear_image)+
834 p+=GetPixelChannels(linear_image);
835 q+=GetPixelChannels(paint_image);
837 if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
839 if (linear_image->progress_monitor != (MagickProgressMonitor) NULL)
844 #if defined(MAGICKCORE_OPENMP_SUPPORT)
845 #pragma omp critical (MagickCore_OilPaintImage)
847 proceed=SetImageProgress(linear_image,OilPaintImageTag,progress++,
849 if (proceed == MagickFalse)
853 paint_view=DestroyCacheView(paint_view);
854 image_view=DestroyCacheView(image_view);
855 histograms=DestroyHistogramThreadSet(histograms);
856 linear_image=DestroyImage(linear_image);
857 if (status == MagickFalse)
858 paint_image=DestroyImage(paint_image);
863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
867 % O p a q u e P a i n t I m a g e %
871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
873 % OpaquePaintImage() changes any pixel that matches color with the color
874 % defined by fill argument.
876 % By default color must match a particular pixel color exactly. However, in
877 % many cases two colors may differ by a small amount. Fuzz defines how much
878 % tolerance is acceptable to consider two colors as the same. For example,
879 % set fuzz to 10 and the color red at intensities of 100 and 102 respectively
880 % are now interpreted as the same color.
882 % The format of the OpaquePaintImage method is:
884 % MagickBooleanType OpaquePaintImage(Image *image,const PixelInfo *target,
885 % const PixelInfo *fill,const MagickBooleanType invert,
886 % ExceptionInfo *exception)
888 % A description of each parameter follows:
890 % o image: the image.
892 % o target: the RGB value of the target color.
894 % o fill: the replacement color.
896 % o invert: paint any pixel that does not match the target color.
898 % o exception: return any errors or warnings in this structure.
901 MagickExport MagickBooleanType OpaquePaintImage(Image *image,
902 const PixelInfo *target,const PixelInfo *fill,const MagickBooleanType invert,
903 ExceptionInfo *exception)
905 #define OpaquePaintImageTag "Opaque/Image"
924 assert(image != (Image *) NULL);
925 assert(image->signature == MagickCoreSignature);
926 assert(target != (PixelInfo *) NULL);
927 assert(fill != (PixelInfo *) NULL);
928 if (image->debug != MagickFalse)
929 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
930 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
932 ConformPixelInfo(image,fill,&conform_fill,exception);
933 ConformPixelInfo(image,target,&conform_target,exception);
935 Make image color opaque.
939 GetPixelInfo(image,&zero);
940 image_view=AcquireAuthenticCacheView(image,exception);
941 #if defined(MAGICKCORE_OPENMP_SUPPORT)
942 #pragma omp parallel for schedule(static,4) shared(progress,status) \
943 magick_threads(image,image,image->rows,1)
945 for (y=0; y < (ssize_t) image->rows; y++)
956 if (status == MagickFalse)
958 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
959 if (q == (Quantum *) NULL)
965 for (x=0; x < (ssize_t) image->columns; x++)
967 GetPixelInfoPixel(image,q,&pixel);
968 if (IsFuzzyEquivalencePixelInfo(&pixel,&conform_target) != invert)
969 SetPixelViaPixelInfo(image,&conform_fill,q);
970 q+=GetPixelChannels(image);
972 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
974 if (image->progress_monitor != (MagickProgressMonitor) NULL)
979 #if defined(MAGICKCORE_OPENMP_SUPPORT)
980 #pragma omp critical (MagickCore_OpaquePaintImage)
982 proceed=SetImageProgress(image,OpaquePaintImageTag,progress++,
984 if (proceed == MagickFalse)
988 image_view=DestroyCacheView(image_view);
993 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
997 % T r a n s p a r e n t P a i n t I m a g e %
1001 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1003 % TransparentPaintImage() changes the opacity value associated with any pixel
1004 % that matches color to the value defined by opacity.
1006 % By default color must match a particular pixel color exactly. However, in
1007 % many cases two colors may differ by a small amount. Fuzz defines how much
1008 % tolerance is acceptable to consider two colors as the same. For example,
1009 % set fuzz to 10 and the color red at intensities of 100 and 102 respectively
1010 % are now interpreted as the same color.
1012 % The format of the TransparentPaintImage method is:
1014 % MagickBooleanType TransparentPaintImage(Image *image,
1015 % const PixelInfo *target,const Quantum opacity,
1016 % const MagickBooleanType invert,ExceptionInfo *exception)
1018 % A description of each parameter follows:
1020 % o image: the image.
1022 % o target: the target color.
1024 % o opacity: the replacement opacity value.
1026 % o invert: paint any pixel that does not match the target color.
1028 % o exception: return any errors or warnings in this structure.
1031 MagickExport MagickBooleanType TransparentPaintImage(Image *image,
1032 const PixelInfo *target,const Quantum opacity,const MagickBooleanType invert,
1033 ExceptionInfo *exception)
1035 #define TransparentPaintImageTag "Transparent/Image"
1052 assert(image != (Image *) NULL);
1053 assert(image->signature == MagickCoreSignature);
1054 assert(target != (PixelInfo *) NULL);
1055 if (image->debug != MagickFalse)
1056 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1057 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1058 return(MagickFalse);
1059 if (image->alpha_trait == UndefinedPixelTrait)
1060 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1062 Make image color transparent.
1066 GetPixelInfo(image,&zero);
1067 image_view=AcquireAuthenticCacheView(image,exception);
1068 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1069 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1070 magick_threads(image,image,image->rows,1)
1072 for (y=0; y < (ssize_t) image->rows; y++)
1083 if (status == MagickFalse)
1085 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1086 if (q == (Quantum *) NULL)
1092 for (x=0; x < (ssize_t) image->columns; x++)
1094 GetPixelInfoPixel(image,q,&pixel);
1095 if (IsFuzzyEquivalencePixelInfo(&pixel,target) != invert)
1096 SetPixelAlpha(image,opacity,q);
1097 q+=GetPixelChannels(image);
1099 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1101 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1106 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1107 #pragma omp critical (MagickCore_TransparentPaintImage)
1109 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1111 if (proceed == MagickFalse)
1115 image_view=DestroyCacheView(image_view);
1120 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1124 % T r a n s p a r e n t P a i n t I m a g e C h r o m a %
1128 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1130 % TransparentPaintImageChroma() changes the opacity value associated with any
1131 % pixel that matches color to the value defined by opacity.
1133 % As there is one fuzz value for the all the channels, TransparentPaintImage()
1134 % is not suitable for the operations like chroma, where the tolerance for
1135 % similarity of two color component (RGB) can be different. Thus we define
1136 % this method to take two target pixels (one low and one high) and all the
1137 % pixels of an image which are lying between these two pixels are made
1140 % The format of the TransparentPaintImageChroma method is:
1142 % MagickBooleanType TransparentPaintImageChroma(Image *image,
1143 % const PixelInfo *low,const PixelInfo *high,const Quantum opacity,
1144 % const MagickBooleanType invert,ExceptionInfo *exception)
1146 % A description of each parameter follows:
1148 % o image: the image.
1150 % o low: the low target color.
1152 % o high: the high target color.
1154 % o opacity: the replacement opacity value.
1156 % o invert: paint any pixel that does not match the target color.
1158 % o exception: return any errors or warnings in this structure.
1161 MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1162 const PixelInfo *low,const PixelInfo *high,const Quantum opacity,
1163 const MagickBooleanType invert,ExceptionInfo *exception)
1165 #define TransparentPaintImageTag "Transparent/Image"
1179 assert(image != (Image *) NULL);
1180 assert(image->signature == MagickCoreSignature);
1181 assert(high != (PixelInfo *) NULL);
1182 assert(low != (PixelInfo *) NULL);
1183 if (image->debug != MagickFalse)
1184 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1185 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1186 return(MagickFalse);
1187 if (image->alpha_trait == UndefinedPixelTrait)
1188 (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
1190 Make image color transparent.
1194 image_view=AcquireAuthenticCacheView(image,exception);
1195 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1196 #pragma omp parallel for schedule(static,4) shared(progress,status) \
1197 magick_threads(image,image,image->rows,1)
1199 for (y=0; y < (ssize_t) image->rows; y++)
1213 if (status == MagickFalse)
1215 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1216 if (q == (Quantum *) NULL)
1221 GetPixelInfo(image,&pixel);
1222 for (x=0; x < (ssize_t) image->columns; x++)
1224 GetPixelInfoPixel(image,q,&pixel);
1225 match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1226 (pixel.green >= low->green) && (pixel.green <= high->green) &&
1227 (pixel.blue >= low->blue) && (pixel.blue <= high->blue)) ? MagickTrue :
1229 if (match != invert)
1230 SetPixelAlpha(image,opacity,q);
1231 q+=GetPixelChannels(image);
1233 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1235 if (image->progress_monitor != (MagickProgressMonitor) NULL)
1240 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1241 #pragma omp critical (MagickCore_TransparentPaintImageChroma)
1243 proceed=SetImageProgress(image,TransparentPaintImageTag,progress++,
1245 if (proceed == MagickFalse)
1249 image_view=DestroyCacheView(image_view);