MagickCore  6.9.13-3
Convert, Edit, Or Compose Bitmap Images
 All Data Structures
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 % Cristy %
17 % July 1998 %
18 % %
19 % %
20 % Copyright 1999 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 % https://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 
39 /*
40  Include declarations.
41 */
42 #include "magick/studio.h"
43 #include "magick/artifact.h"
44 #include "magick/cache.h"
45 #include "magick/channel.h"
46 #include "magick/color-private.h"
47 #include "magick/colorspace-private.h"
48 #include "magick/composite.h"
49 #include "magick/composite-private.h"
50 #include "magick/draw.h"
51 #include "magick/draw-private.h"
52 #include "magick/exception.h"
53 #include "magick/exception-private.h"
54 #include "magick/gem.h"
55 #include "magick/monitor.h"
56 #include "magick/monitor-private.h"
57 #include "magick/option.h"
58 #include "magick/paint.h"
59 #include "magick/pixel-private.h"
60 #include "magick/resource_.h"
61 #include "magick/string_.h"
62 #include "magick/string-private.h"
63 #include "magick/thread-private.h"
64 
65 /*
66 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
67 % %
68 % %
69 % %
70 % F l o o d f i l l P a i n t I m a g e %
71 % %
72 % %
73 % %
74 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
75 %
76 % FloodfillPaintImage() changes the color value of any pixel that matches
77 % target and is an immediate neighbor. If the method FillToBorderMethod is
78 % specified, the color value is changed for any neighbor pixel that does not
79 % match the bordercolor member of image.
80 %
81 % By default target must match a particular pixel color exactly.
82 % However, in many cases two colors may differ by a small amount. The
83 % fuzz member of image defines how much tolerance is acceptable to
84 % consider two colors as the same. For example, set fuzz to 10 and the
85 % color red at intensities of 100 and 102 respectively are now
86 % interpreted as the same color for the purposes of the floodfill.
87 %
88 % The format of the FloodfillPaintImage method is:
89 %
90 % MagickBooleanType FloodfillPaintImage(Image *image,
91 % const ChannelType channel,const DrawInfo *draw_info,
92 % const MagickPixelPacket target,const ssize_t x_offset,
93 % const ssize_t y_offset,const MagickBooleanType invert)
94 %
95 % A description of each parameter follows:
96 %
97 % o image: the image.
98 %
99 % o channel: the channel(s).
100 %
101 % o draw_info: the draw info.
102 %
103 % o target: the RGB value of the target color.
104 %
105 % o x_offset,y_offset: the starting location of the operation.
106 %
107 % o invert: paint any pixel that does not match the target color.
108 %
109 */
110 MagickExport MagickBooleanType FloodfillPaintImage(Image *image,
111  const ChannelType channel,const DrawInfo *draw_info,
112  const MagickPixelPacket *target,const ssize_t x_offset,const ssize_t y_offset,
113  const MagickBooleanType invert)
114 {
115 #define MaxStacksize 524288UL
116 #define PushSegmentStack(up,left,right,delta) \
117 { \
118  if (s >= (segment_stack+MaxStacksize)) \
119  { \
120  segment_info=RelinquishVirtualMemory(segment_info); \
121  image_view=DestroyCacheView(image_view); \
122  floodplane_view=DestroyCacheView(floodplane_view); \
123  floodplane_image=DestroyImage(floodplane_image); \
124  ThrowBinaryException(DrawError,"SegmentStackOverflow",image->filename) \
125  } \
126  else \
127  { \
128  if ((((up)+(delta)) >= 0) && (((up)+(delta)) < (ssize_t) image->rows)) \
129  { \
130  s->x1=(double) (left); \
131  s->y1=(double) (up); \
132  s->x2=(double) (right); \
133  s->y2=(double) (delta); \
134  s++; \
135  } \
136  } \
137 }
138 
139  CacheView
140  *floodplane_view,
141  *image_view;
142 
144  *exception;
145 
146  Image
147  *floodplane_image;
148 
149  MagickBooleanType
150  skip;
151 
153  fill,
154  pixel;
155 
156  MemoryInfo
157  *segment_info;
158 
160  fill_color;
161 
163  *s;
164 
166  *segment_stack;
167 
168  ssize_t
169  offset,
170  start,
171  x,
172  x1,
173  x2,
174  y;
175 
176  /*
177  Check boundary conditions.
178  */
179  assert(image != (Image *) NULL);
180  assert(image->signature == MagickCoreSignature);
181  assert(draw_info != (DrawInfo *) NULL);
182  assert(draw_info->signature == MagickCoreSignature);
183  if (IsEventLogging() != MagickFalse)
184  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
185  if ((x_offset < 0) || (x_offset >= (ssize_t) image->columns))
186  return(MagickFalse);
187  if ((y_offset < 0) || (y_offset >= (ssize_t) image->rows))
188  return(MagickFalse);
189  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
190  return(MagickFalse);
191  exception=(&image->exception);
192  if (IsGrayColorspace(image->colorspace) != MagickFalse)
193  (void) SetImageColorspace(image,sRGBColorspace);
194  if ((image->matte == MagickFalse) &&
195  (draw_info->fill.opacity != OpaqueOpacity))
196  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
197  /*
198  Set floodfill state.
199  */
200  floodplane_image=CloneImage(image,0,0,MagickTrue,&image->exception);
201  if (floodplane_image == (Image *) NULL)
202  return(MagickFalse);
203  floodplane_image->matte=MagickFalse;
204  floodplane_image->colorspace=GRAYColorspace;
205  (void) QueryColorCompliance("#000",AllCompliance,
206  &floodplane_image->background_color,exception);
207  (void) SetImageBackgroundColor(floodplane_image);
208  segment_info=AcquireVirtualMemory(MaxStacksize,sizeof(*segment_stack));
209  if (segment_info == (MemoryInfo *) NULL)
210  {
211  floodplane_image=DestroyImage(floodplane_image);
212  ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
213  image->filename);
214  }
215  segment_stack=(SegmentInfo *) GetVirtualMemoryBlob(segment_info);
216  /*
217  Push initial segment on stack.
218  */
219  x=x_offset;
220  y=y_offset;
221  start=0;
222  s=segment_stack;
223  GetMagickPixelPacket(image,&fill);
224  GetMagickPixelPacket(image,&pixel);
225  image_view=AcquireVirtualCacheView(image,exception);
226  floodplane_view=AcquireAuthenticCacheView(floodplane_image,exception);
227  PushSegmentStack(y,x,x,1);
228  PushSegmentStack(y+1,x,x,-1);
229  while (s > segment_stack)
230  {
231  const IndexPacket
232  *magick_restrict indexes;
233 
234  const PixelPacket
235  *magick_restrict p;
236 
238  *magick_restrict q;
239 
240  ssize_t
241  x;
242 
243  /*
244  Pop segment off stack.
245  */
246  s--;
247  x1=(ssize_t) s->x1;
248  x2=(ssize_t) s->x2;
249  offset=(ssize_t) s->y2;
250  y=(ssize_t) s->y1+offset;
251  /*
252  Recolor neighboring pixels.
253  */
254  p=GetCacheViewVirtualPixels(image_view,0,y,(size_t) (x1+1),1,exception);
255  q=GetCacheViewAuthenticPixels(floodplane_view,0,y,(size_t) (x1+1),1,
256  exception);
257  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
258  break;
259  indexes=GetCacheViewVirtualIndexQueue(image_view);
260  p+=x1;
261  q+=x1;
262  for (x=x1; x >= 0; x--)
263  {
264  if (GetPixelGray(q) != 0)
265  break;
266  SetMagickPixelPacket(image,p,indexes+x,&pixel);
267  if (IsMagickColorSimilar(&pixel,target) == invert)
268  break;
269  SetPixelGray(q,QuantumRange);
270  p--;
271  q--;
272  }
273  if (SyncCacheViewAuthenticPixels(floodplane_view,exception) == MagickFalse)
274  break;
275  skip=x >= x1 ? MagickTrue : MagickFalse;
276  if (skip == MagickFalse)
277  {
278  start=x+1;
279  if (start < x1)
280  PushSegmentStack(y,start,x1-1,-offset);
281  x=x1+1;
282  }
283  do
284  {
285  if (skip == MagickFalse)
286  {
287  if (x < (ssize_t) image->columns)
288  {
289  p=GetCacheViewVirtualPixels(image_view,x,y,image->columns-x,1,
290  exception);
291  q=GetCacheViewAuthenticPixels(floodplane_view,x,y,
292  image->columns-x,1,exception);
293  if ((p == (const PixelPacket *) NULL) ||
294  (q == (PixelPacket *) NULL))
295  break;
296  indexes=GetCacheViewVirtualIndexQueue(image_view);
297  for ( ; x < (ssize_t) image->columns; x++)
298  {
299  if (GetPixelGray(q) != 0)
300  break;
301  SetMagickPixelPacket(image,p,indexes+x,&pixel);
302  if (IsMagickColorSimilar(&pixel,target) == invert)
303  break;
304  SetPixelGray(q,QuantumRange);
305  p++;
306  q++;
307  }
308  if (SyncCacheViewAuthenticPixels(floodplane_view,exception) == MagickFalse)
309  break;
310  }
311  PushSegmentStack(y,start,x-1,offset);
312  if (x > (x2+1))
313  PushSegmentStack(y,x2+1,x-1,-offset);
314  }
315  skip=MagickFalse;
316  x++;
317  if (x <= x2)
318  {
319  p=GetCacheViewVirtualPixels(image_view,x,y,(size_t) (x2-x+1),1,
320  exception);
321  q=GetCacheViewAuthenticPixels(floodplane_view,x,y,(size_t) (x2-x+1),1,
322  exception);
323  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
324  break;
325  indexes=GetCacheViewVirtualIndexQueue(image_view);
326  for ( ; x <= x2; x++)
327  {
328  if (GetPixelGray(q) != 0)
329  break;
330  SetMagickPixelPacket(image,p,indexes+x,&pixel);
331  if (IsMagickColorSimilar(&pixel,target) != invert)
332  break;
333  p++;
334  q++;
335  }
336  }
337  start=x;
338  } while (x <= x2);
339  }
340  for (y=0; y < (ssize_t) image->rows; y++)
341  {
342  const PixelPacket
343  *magick_restrict p;
344 
345  IndexPacket
346  *magick_restrict indexes;
347 
349  *magick_restrict q;
350 
351  ssize_t
352  x;
353 
354  /*
355  Tile fill color onto floodplane.
356  */
357  p=GetCacheViewVirtualPixels(floodplane_view,0,y,image->columns,1,
358  exception);
359  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
360  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
361  break;
362  indexes=GetCacheViewAuthenticIndexQueue(image_view);
363  for (x=0; x < (ssize_t) image->columns; x++)
364  {
365  if (GetPixelGray(p) != 0)
366  {
367  (void) GetFillColor(draw_info,x,y,&fill_color);
368  SetMagickPixelPacket(image,&fill_color,(IndexPacket *) NULL,&fill);
369  if (image->colorspace == CMYKColorspace)
370  ConvertRGBToCMYK(&fill);
371  if ((channel & RedChannel) != 0)
372  SetPixelRed(q,ClampToQuantum(fill.red));
373  if ((channel & GreenChannel) != 0)
374  SetPixelGreen(q,ClampToQuantum(fill.green));
375  if ((channel & BlueChannel) != 0)
376  SetPixelBlue(q,ClampToQuantum(fill.blue));
377  if (((channel & OpacityChannel) != 0) ||
378  (draw_info->fill.opacity != OpaqueOpacity))
379  SetPixelOpacity(q,ClampToQuantum(fill.opacity));
380  if (((channel & IndexChannel) != 0) &&
381  (image->colorspace == CMYKColorspace))
382  SetPixelIndex(indexes+x,ClampToQuantum(fill.index));
383  }
384  p++;
385  q++;
386  }
387  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
388  break;
389  }
390  floodplane_view=DestroyCacheView(floodplane_view);
391  image_view=DestroyCacheView(image_view);
392  segment_info=RelinquishVirtualMemory(segment_info);
393  floodplane_image=DestroyImage(floodplane_image);
394  return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
395 }
396 
397 /*
398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
399 % %
400 % %
401 % %
402 + G r a d i e n t I m a g e %
403 % %
404 % %
405 % %
406 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
407 %
408 % GradientImage() applies a continuously smooth color transitions along a
409 % vector from one color to another.
410 %
411 % Note, the interface of this method will change in the future to support
412 % more than one transition.
413 %
414 % The format of the GradientImage method is:
415 %
416 % MagickBooleanType GradientImage(Image *image,const GradientType type,
417 % const SpreadMethod method,const PixelPacket *start_color,
418 % const PixelPacket *stop_color)
419 %
420 % A description of each parameter follows:
421 %
422 % o image: the image.
423 %
424 % o type: the gradient type: linear or radial.
425 %
426 % o spread: the gradient spread method: pad, reflect, or repeat.
427 %
428 % o start_color: the start color.
429 %
430 % o stop_color: the stop color.
431 %
432 % This provides a good example of making use of the DrawGradientImage
433 % function and the gradient structure in draw_info.
434 %
435 */
436 MagickExport MagickBooleanType GradientImage(Image *image,
437  const GradientType type,const SpreadMethod method,
438  const PixelPacket *start_color,const PixelPacket *stop_color)
439 {
440  const char
441  *artifact;
442 
443  DrawInfo
444  *draw_info;
445 
447  *gradient;
448 
449  MagickBooleanType
450  status;
451 
452  ssize_t
453  i;
454 
455  /*
456  Set gradient start-stop end points.
457  */
458  assert(image != (const Image *) NULL);
459  assert(image->signature == MagickCoreSignature);
460  assert(start_color != (const PixelPacket *) NULL);
461  assert(stop_color != (const PixelPacket *) NULL);
462  if (IsEventLogging() != MagickFalse)
463  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
464  draw_info=AcquireDrawInfo();
465  gradient=(&draw_info->gradient);
466  gradient->type=type;
467  gradient->bounding_box.width=image->columns;
468  gradient->bounding_box.height=image->rows;
469  artifact=GetImageArtifact(image,"gradient:bounding-box");
470  if (artifact != (const char *) NULL)
471  (void) ParseAbsoluteGeometry(artifact,&gradient->bounding_box);
472  gradient->gradient_vector.x2=(double) image->columns-1;
473  gradient->gradient_vector.y2=(double) image->rows-1;
474  artifact=GetImageArtifact(image,"gradient:direction");
475  if (artifact != (const char *) NULL)
476  {
477  GravityType
478  direction;
479 
480  direction=(GravityType) ParseCommandOption(MagickGravityOptions,
481  MagickFalse,artifact);
482  switch (direction)
483  {
484  case NorthWestGravity:
485  {
486  gradient->gradient_vector.x1=(double) image->columns-1;
487  gradient->gradient_vector.y1=(double) image->rows-1;
488  gradient->gradient_vector.x2=0.0;
489  gradient->gradient_vector.y2=0.0;
490  break;
491  }
492  case NorthGravity:
493  {
494  gradient->gradient_vector.x1=0.0;
495  gradient->gradient_vector.y1=(double) image->rows-1;
496  gradient->gradient_vector.x2=0.0;
497  gradient->gradient_vector.y2=0.0;
498  break;
499  }
500  case NorthEastGravity:
501  {
502  gradient->gradient_vector.x1=0.0;
503  gradient->gradient_vector.y1=(double) image->rows-1;
504  gradient->gradient_vector.x2=(double) image->columns-1;
505  gradient->gradient_vector.y2=0.0;
506  break;
507  }
508  case WestGravity:
509  {
510  gradient->gradient_vector.x1=(double) image->columns-1;
511  gradient->gradient_vector.y1=0.0;
512  gradient->gradient_vector.x2=0.0;
513  gradient->gradient_vector.y2=0.0;
514  break;
515  }
516  case EastGravity:
517  {
518  gradient->gradient_vector.x1=0.0;
519  gradient->gradient_vector.y1=0.0;
520  gradient->gradient_vector.x2=(double) image->columns-1;
521  gradient->gradient_vector.y2=0.0;
522  break;
523  }
524  case SouthWestGravity:
525  {
526  gradient->gradient_vector.x1=(double) image->columns-1;
527  gradient->gradient_vector.y1=0.0;
528  gradient->gradient_vector.x2=0.0;
529  gradient->gradient_vector.y2=(double) image->rows-1;
530  break;
531  }
532  case SouthGravity:
533  {
534  gradient->gradient_vector.x1=0.0;
535  gradient->gradient_vector.y1=0.0;
536  gradient->gradient_vector.x2=0.0;
537  gradient->gradient_vector.y2=(double) image->columns-1;
538  break;
539  }
540  case SouthEastGravity:
541  {
542  gradient->gradient_vector.x1=0.0;
543  gradient->gradient_vector.y1=0.0;
544  gradient->gradient_vector.x2=(double) image->columns-1;
545  gradient->gradient_vector.y2=(double) image->rows-1;
546  break;
547  }
548  default:
549  break;
550  }
551  }
552  artifact=GetImageArtifact(image,"gradient:angle");
553  if (artifact != (const char *) NULL)
554  gradient->angle=(MagickRealType) StringToDouble(artifact,(char **) NULL);
555  artifact=GetImageArtifact(image,"gradient:vector");
556  if (artifact != (const char *) NULL)
557  (void) sscanf(artifact,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",
558  &gradient->gradient_vector.x1,&gradient->gradient_vector.y1,
559  &gradient->gradient_vector.x2,&gradient->gradient_vector.y2);
560  if ((GetImageArtifact(image,"gradient:angle") == (const char *) NULL) &&
561  (GetImageArtifact(image,"gradient:direction") == (const char *) NULL) &&
562  (GetImageArtifact(image,"gradient:extent") == (const char *) NULL) &&
563  (GetImageArtifact(image,"gradient:vector") == (const char *) NULL))
564  if ((type == LinearGradient) && (gradient->gradient_vector.y2 != 0.0))
565  gradient->gradient_vector.x2=0.0;
566  gradient->center.x=(double) gradient->gradient_vector.x2/2.0;
567  gradient->center.y=(double) gradient->gradient_vector.y2/2.0;
568  artifact=GetImageArtifact(image,"gradient:center");
569  if (artifact != (const char *) NULL)
570  (void) sscanf(artifact,"%lf%*[ ,]%lf",&gradient->center.x,
571  &gradient->center.y);
572  artifact=GetImageArtifact(image,"gradient:angle");
573  if ((type == LinearGradient) && (artifact != (const char *) NULL))
574  {
575  double
576  sine,
577  cosine,
578  distance;
579 
580  /*
581  Reference https://drafts.csswg.org/css-images-3/#linear-gradients.
582  */
583  sine=sin((double) DegreesToRadians(gradient->angle-90.0));
584  cosine=cos((double) DegreesToRadians(gradient->angle-90.0));
585  distance=fabs((double) (image->columns-1)*cosine)+
586  fabs((double) (image->rows-1)*sine);
587  gradient->gradient_vector.x1=0.5*((image->columns-1)-distance*cosine);
588  gradient->gradient_vector.y1=0.5*((image->rows-1)-distance*sine);
589  gradient->gradient_vector.x2=0.5*((image->columns-1)+distance*cosine);
590  gradient->gradient_vector.y2=0.5*((image->rows-1)+distance*sine);
591  }
592  gradient->radii.x=(double) MagickMax((image->columns-1),(image->rows-1))/2.0;
593  gradient->radii.y=gradient->radii.x;
594  artifact=GetImageArtifact(image,"gradient:extent");
595  if (artifact != (const char *) NULL)
596  {
597  if (LocaleCompare(artifact,"Circle") == 0)
598  {
599  gradient->radii.x=(double) (MagickMax((image->columns-1),
600  (image->rows-1)))/2.0;
601  gradient->radii.y=gradient->radii.x;
602  }
603  if (LocaleCompare(artifact,"Diagonal") == 0)
604  {
605  gradient->radii.x=(double) (sqrt((double) (image->columns-1)*
606  (image->columns-1)+(image->rows-1)*(image->rows-1)))/2.0;
607  gradient->radii.y=gradient->radii.x;
608  }
609  if (LocaleCompare(artifact,"Ellipse") == 0)
610  {
611  gradient->radii.x=(double) (image->columns-1)/2.0;
612  gradient->radii.y=(double) (image->rows-1)/2.0;
613  }
614  if (LocaleCompare(artifact,"Maximum") == 0)
615  {
616  gradient->radii.x=(double) MagickMax((image->columns-1),
617  (image->rows-1))/2.0;
618  gradient->radii.y=gradient->radii.x;
619  }
620  if (LocaleCompare(artifact,"Minimum") == 0)
621  {
622  gradient->radii.x=(double) MagickMin((image->columns-1),
623  (image->rows-1))/2.0;
624  gradient->radii.y=gradient->radii.x;
625  }
626  }
627  artifact=GetImageArtifact(image,"gradient:radii");
628  if (artifact != (const char *) NULL)
629  (void) sscanf(artifact,"%lf%*[ ,]%lf",&gradient->radii.x,
630  &gradient->radii.y);
631  gradient->radius=MagickMax(gradient->radii.x,gradient->radii.y);
632  gradient->spread=method;
633  /*
634  Define the gradient to fill between the stops.
635  */
636  gradient->number_stops=2;
637  gradient->stops=(StopInfo *) AcquireQuantumMemory(gradient->number_stops,
638  sizeof(*gradient->stops));
639  if (gradient->stops == (StopInfo *) NULL)
640  ThrowBinaryImageException(ResourceLimitError,"MemoryAllocationFailed",
641  image->filename);
642  (void) memset(gradient->stops,0,gradient->number_stops*
643  sizeof(*gradient->stops));
644  for (i=0; i < (ssize_t) gradient->number_stops; i++)
645  GetMagickPixelPacket(image,&gradient->stops[i].color);
646  SetMagickPixelPacket(image,start_color,(IndexPacket *) NULL,
647  &gradient->stops[0].color);
648  gradient->stops[0].offset=0.0;
649  SetMagickPixelPacket(image,stop_color,(IndexPacket *) NULL,
650  &gradient->stops[1].color);
651  gradient->stops[1].offset=1.0;
652  /*
653  Draw a gradient on the image.
654  */
655  status=DrawGradientImage(image,draw_info);
656  draw_info=DestroyDrawInfo(draw_info);
657  return(status);
658 }
659 
660 /*
661 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662 % %
663 % %
664 % %
665 % O i l P a i n t I m a g e %
666 % %
667 % %
668 % %
669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670 %
671 % OilPaintImage() applies a special effect filter that simulates an oil
672 % painting. Each pixel is replaced by the most frequent color occurring
673 % in a circular region defined by radius.
674 %
675 % The format of the OilPaintImage method is:
676 %
677 % Image *OilPaintImage(const Image *image,const double radius,
678 % ExceptionInfo *exception)
679 %
680 % A description of each parameter follows:
681 %
682 % o image: the image.
683 %
684 % o radius: the radius of the circular neighborhood.
685 %
686 % o exception: return any errors or warnings in this structure.
687 %
688 */
689 
690 static size_t **DestroyHistogramTLS(size_t **histogram)
691 {
692  ssize_t
693  i;
694 
695  assert(histogram != (size_t **) NULL);
696  for (i=0; i < (ssize_t) GetMagickResourceLimit(ThreadResource); i++)
697  if (histogram[i] != (size_t *) NULL)
698  histogram[i]=(size_t *) RelinquishMagickMemory(histogram[i]);
699  histogram=(size_t **) RelinquishMagickMemory(histogram);
700  return(histogram);
701 }
702 
703 static size_t **AcquireHistogramTLS(const size_t count)
704 {
705  ssize_t
706  i;
707 
708  size_t
709  **histogram,
710  number_threads;
711 
712  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
713  histogram=(size_t **) AcquireQuantumMemory(number_threads,
714  sizeof(*histogram));
715  if (histogram == (size_t **) NULL)
716  return((size_t **) NULL);
717  (void) memset(histogram,0,number_threads*sizeof(*histogram));
718  for (i=0; i < (ssize_t) number_threads; i++)
719  {
720  histogram[i]=(size_t *) AcquireQuantumMemory(count,
721  sizeof(**histogram));
722  if (histogram[i] == (size_t *) NULL)
723  return(DestroyHistogramTLS(histogram));
724  }
725  return(histogram);
726 }
727 
728 MagickExport Image *OilPaintImage(const Image *image,const double radius,
729  ExceptionInfo *exception)
730 {
731 #define NumberPaintBins 256
732 #define OilPaintImageTag "OilPaint/Image"
733 
734  CacheView
735  *image_view,
736  *paint_view;
737 
738  Image
739  *linear_image,
740  *paint_image;
741 
742  MagickBooleanType
743  status;
744 
745  MagickOffsetType
746  progress;
747 
748  size_t
749  **magick_restrict histograms,
750  width;
751 
752  ssize_t
753  y;
754 
755  /*
756  Initialize painted image attributes.
757  */
758  assert(image != (const Image *) NULL);
759  assert(image->signature == MagickCoreSignature);
760  assert(exception != (ExceptionInfo *) NULL);
761  assert(exception->signature == MagickCoreSignature);
762  if (IsEventLogging() != MagickFalse)
763  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
764  width=GetOptimalKernelWidth2D(radius,0.5);
765  linear_image=CloneImage(image,0,0,MagickTrue,exception);
766  paint_image=CloneImage(image,0,0,MagickTrue,exception);
767  if ((linear_image == (Image *) NULL) || (paint_image == (Image *) NULL))
768  {
769  if (linear_image != (Image *) NULL)
770  linear_image=DestroyImage(linear_image);
771  if (paint_image != (Image *) NULL)
772  linear_image=DestroyImage(paint_image);
773  return((Image *) NULL);
774  }
775  if (SetImageStorageClass(paint_image,DirectClass) == MagickFalse)
776  {
777  InheritException(exception,&paint_image->exception);
778  linear_image=DestroyImage(linear_image);
779  paint_image=DestroyImage(paint_image);
780  return((Image *) NULL);
781  }
782  histograms=AcquireHistogramTLS(NumberPaintBins);
783  if (histograms == (size_t **) NULL)
784  {
785  linear_image=DestroyImage(linear_image);
786  paint_image=DestroyImage(paint_image);
787  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
788  }
789  /*
790  Oil paint image.
791  */
792  status=MagickTrue;
793  progress=0;
794  image_view=AcquireVirtualCacheView(linear_image,exception);
795  paint_view=AcquireAuthenticCacheView(paint_image,exception);
796 #if defined(MAGICKCORE_OPENMP_SUPPORT)
797  #pragma omp parallel for schedule(static) shared(progress,status) \
798  magick_number_threads(linear_image,paint_image,linear_image->rows,1)
799 #endif
800  for (y=0; y < (ssize_t) linear_image->rows; y++)
801  {
802  const IndexPacket
803  *magick_restrict indexes;
804 
805  const PixelPacket
806  *magick_restrict p;
807 
808  IndexPacket
809  *magick_restrict paint_indexes;
810 
811  ssize_t
812  x;
813 
815  *magick_restrict q;
816 
817  size_t
818  *histogram;
819 
820  if (status == MagickFalse)
821  continue;
822  p=GetCacheViewVirtualPixels(image_view,-((ssize_t) width/2L),y-(ssize_t)
823  (width/2L),linear_image->columns+width,width,exception);
824  q=QueueCacheViewAuthenticPixels(paint_view,0,y,paint_image->columns,1,
825  exception);
826  if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
827  {
828  status=MagickFalse;
829  continue;
830  }
831  indexes=GetCacheViewVirtualIndexQueue(image_view);
832  paint_indexes=GetCacheViewAuthenticIndexQueue(paint_view);
833  histogram=histograms[GetOpenMPThreadId()];
834  for (x=0; x < (ssize_t) linear_image->columns; x++)
835  {
836  ssize_t
837  i,
838  u;
839 
840  size_t
841  count;
842 
843  ssize_t
844  j,
845  k,
846  v;
847 
848  /*
849  Assign most frequent color.
850  */
851  i=0;
852  j=0;
853  count=0;
854  (void) memset(histogram,0,NumberPaintBins*sizeof(*histogram));
855  for (v=0; v < (ssize_t) width; v++)
856  {
857  for (u=0; u < (ssize_t) width; u++)
858  {
859  k=(ssize_t) ScaleQuantumToChar(ClampToQuantum(GetPixelIntensity(
860  linear_image,p+u+i)));
861  histogram[k]++;
862  if (histogram[k] > count)
863  {
864  j=i+u;
865  count=histogram[k];
866  }
867  }
868  i+=(ssize_t) (linear_image->columns+width);
869  }
870  *q=(*(p+j));
871  if (linear_image->colorspace == CMYKColorspace)
872  SetPixelIndex(paint_indexes+x,GetPixelIndex(indexes+x+j));
873  p++;
874  q++;
875  }
876  if (SyncCacheViewAuthenticPixels(paint_view,exception) == MagickFalse)
877  status=MagickFalse;
878  if (image->progress_monitor != (MagickProgressMonitor) NULL)
879  {
880  MagickBooleanType
881  proceed;
882 
883 #if defined(MAGICKCORE_OPENMP_SUPPORT)
884  #pragma omp atomic
885 #endif
886  progress++;
887  proceed=SetImageProgress(image,OilPaintImageTag,progress,image->rows);
888  if (proceed == MagickFalse)
889  status=MagickFalse;
890  }
891  }
892  paint_view=DestroyCacheView(paint_view);
893  image_view=DestroyCacheView(image_view);
894  histograms=DestroyHistogramTLS(histograms);
895  linear_image=DestroyImage(linear_image);
896  if (status == MagickFalse)
897  paint_image=DestroyImage(paint_image);
898  return(paint_image);
899 }
900 
901 /*
902 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
903 % %
904 % %
905 % %
906 % O p a q u e P a i n t I m a g e %
907 % %
908 % %
909 % %
910 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
911 %
912 % OpaquePaintImage() changes any pixel that matches color with the color
913 % defined by fill.
914 %
915 % By default color must match a particular pixel color exactly. However,
916 % in many cases two colors may differ by a small amount. Fuzz defines
917 % how much tolerance is acceptable to consider two colors as the same.
918 % For example, set fuzz to 10 and the color red at intensities of 100 and
919 % 102 respectively are now interpreted as the same color.
920 %
921 % The format of the OpaquePaintImage method is:
922 %
923 % MagickBooleanType OpaquePaintImage(Image *image,
924 % const PixelPacket *target,const PixelPacket *fill,
925 % const MagickBooleanType invert)
926 % MagickBooleanType OpaquePaintImageChannel(Image *image,
927 % const ChannelType channel,const PixelPacket *target,
928 % const PixelPacket *fill,const MagickBooleanType invert)
929 %
930 % A description of each parameter follows:
931 %
932 % o image: the image.
933 %
934 % o channel: the channel(s).
935 %
936 % o target: the RGB value of the target color.
937 %
938 % o fill: the replacement color.
939 %
940 % o invert: paint any pixel that does not match the target color.
941 %
942 */
943 
944 MagickExport MagickBooleanType OpaquePaintImage(Image *image,
945  const MagickPixelPacket *target,const MagickPixelPacket *fill,
946  const MagickBooleanType invert)
947 {
948  return(OpaquePaintImageChannel(image,CompositeChannels,target,fill,invert));
949 }
950 
951 MagickExport MagickBooleanType OpaquePaintImageChannel(Image *image,
952  const ChannelType channel,const MagickPixelPacket *target,
953  const MagickPixelPacket *fill,const MagickBooleanType invert)
954 {
955 #define OpaquePaintImageTag "Opaque/Image"
956 
957  CacheView
958  *image_view;
959 
961  *exception;
962 
963  MagickBooleanType
964  status;
965 
966  MagickOffsetType
967  progress;
968 
970  conform_fill,
971  conform_target,
972  zero;
973 
974  ssize_t
975  y;
976 
977  assert(image != (Image *) NULL);
978  assert(image->signature == MagickCoreSignature);
979  assert(target != (MagickPixelPacket *) NULL);
980  assert(fill != (MagickPixelPacket *) NULL);
981  if (IsEventLogging() != MagickFalse)
982  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
983  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
984  return(MagickFalse);
985  exception=(&image->exception);
986  ConformMagickPixelPacket(image,fill,&conform_fill,exception);
987  ConformMagickPixelPacket(image,target,&conform_target,exception);
988  /*
989  Make image color opaque.
990  */
991  status=MagickTrue;
992  progress=0;
993  GetMagickPixelPacket(image,&zero);
994  image_view=AcquireAuthenticCacheView(image,exception);
995 #if defined(MAGICKCORE_OPENMP_SUPPORT)
996  #pragma omp parallel for schedule(static) shared(progress,status) \
997  magick_number_threads(image,image,image->rows,1)
998 #endif
999  for (y=0; y < (ssize_t) image->rows; y++)
1000  {
1002  pixel;
1003 
1004  IndexPacket
1005  *magick_restrict indexes;
1006 
1007  ssize_t
1008  x;
1009 
1010  PixelPacket
1011  *magick_restrict q;
1012 
1013  if (status == MagickFalse)
1014  continue;
1015  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1016  if (q == (PixelPacket *) NULL)
1017  {
1018  status=MagickFalse;
1019  continue;
1020  }
1021  indexes=GetCacheViewAuthenticIndexQueue(image_view);
1022  pixel=zero;
1023  for (x=0; x < (ssize_t) image->columns; x++)
1024  {
1025  SetMagickPixelPacket(image,q,indexes+x,&pixel);
1026  if (IsMagickColorSimilar(&pixel,&conform_target) != invert)
1027  {
1028  if ((channel & RedChannel) != 0)
1029  SetPixelRed(q,ClampToQuantum(conform_fill.red));
1030  if ((channel & GreenChannel) != 0)
1031  SetPixelGreen(q,ClampToQuantum(conform_fill.green));
1032  if ((channel & BlueChannel) != 0)
1033  SetPixelBlue(q,ClampToQuantum(conform_fill.blue));
1034  if ((channel & OpacityChannel) != 0)
1035  SetPixelOpacity(q,ClampToQuantum(conform_fill.opacity));
1036  if (((channel & IndexChannel) != 0) &&
1037  (image->colorspace == CMYKColorspace))
1038  SetPixelIndex(indexes+x,ClampToQuantum(conform_fill.index));
1039  }
1040  q++;
1041  }
1042  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1043  status=MagickFalse;
1044  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1045  {
1046  MagickBooleanType
1047  proceed;
1048 
1049 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1050  #pragma omp atomic
1051 #endif
1052  progress++;
1053  proceed=SetImageProgress(image,OpaquePaintImageTag,progress,
1054  image->rows);
1055  if (proceed == MagickFalse)
1056  status=MagickFalse;
1057  }
1058  }
1059  image_view=DestroyCacheView(image_view);
1060  return(status);
1061 }
1062 
1063 /*
1064 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1065 % %
1066 % %
1067 % %
1068 % T r a n s p a r e n t P a i n t I m a g e %
1069 % %
1070 % %
1071 % %
1072 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1073 %
1074 % TransparentPaintImage() changes the opacity value associated with any pixel
1075 % that matches color to the value defined by opacity.
1076 %
1077 % By default color must match a particular pixel color exactly. However,
1078 % in many cases two colors may differ by a small amount. Fuzz defines
1079 % how much tolerance is acceptable to consider two colors as the same.
1080 % For example, set fuzz to 10 and the color red at intensities of 100 and
1081 % 102 respectively are now interpreted as the same color.
1082 %
1083 % The format of the TransparentPaintImage method is:
1084 %
1085 % MagickBooleanType TransparentPaintImage(Image *image,
1086 % const MagickPixelPacket *target,const Quantum opacity,
1087 % const MagickBooleanType invert)
1088 %
1089 % A description of each parameter follows:
1090 %
1091 % o image: the image.
1092 %
1093 % o target: the target color.
1094 %
1095 % o opacity: the replacement opacity value.
1096 %
1097 % o invert: paint any pixel that does not match the target color.
1098 %
1099 */
1100 MagickExport MagickBooleanType TransparentPaintImage(Image *image,
1101  const MagickPixelPacket *target,const Quantum opacity,
1102  const MagickBooleanType invert)
1103 {
1104 #define TransparentPaintImageTag "Transparent/Image"
1105 
1106  CacheView
1107  *image_view;
1108 
1110  *exception;
1111 
1112  MagickBooleanType
1113  status;
1114 
1115  MagickOffsetType
1116  progress;
1117 
1119  zero;
1120 
1121  ssize_t
1122  y;
1123 
1124  assert(image != (Image *) NULL);
1125  assert(image->signature == MagickCoreSignature);
1126  assert(target != (MagickPixelPacket *) NULL);
1127  if (IsEventLogging() != MagickFalse)
1128  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1129  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1130  return(MagickFalse);
1131  if (image->matte == MagickFalse)
1132  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1133  /*
1134  Make image color transparent.
1135  */
1136  status=MagickTrue;
1137  progress=0;
1138  exception=(&image->exception);
1139  GetMagickPixelPacket(image,&zero);
1140  image_view=AcquireAuthenticCacheView(image,exception);
1141 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1142  #pragma omp parallel for schedule(static) shared(progress,status) \
1143  magick_number_threads(image,image,image->rows,1)
1144 #endif
1145  for (y=0; y < (ssize_t) image->rows; y++)
1146  {
1148  pixel;
1149 
1150  IndexPacket
1151  *magick_restrict indexes;
1152 
1153  ssize_t
1154  x;
1155 
1156  PixelPacket
1157  *magick_restrict q;
1158 
1159  if (status == MagickFalse)
1160  continue;
1161  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1162  if (q == (PixelPacket *) NULL)
1163  {
1164  status=MagickFalse;
1165  continue;
1166  }
1167  indexes=GetCacheViewAuthenticIndexQueue(image_view);
1168  pixel=zero;
1169  for (x=0; x < (ssize_t) image->columns; x++)
1170  {
1171  SetMagickPixelPacket(image,q,indexes+x,&pixel);
1172  if (IsMagickColorSimilar(&pixel,target) != invert)
1173  q->opacity=opacity;
1174  q++;
1175  }
1176  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1177  status=MagickFalse;
1178  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1179  {
1180  MagickBooleanType
1181  proceed;
1182 
1183 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1184  #pragma omp atomic
1185 #endif
1186  progress++;
1187  proceed=SetImageProgress(image,TransparentPaintImageTag,progress,
1188  image->rows);
1189  if (proceed == MagickFalse)
1190  status=MagickFalse;
1191  }
1192  }
1193  image_view=DestroyCacheView(image_view);
1194  return(status);
1195 }
1196 
1197 /*
1198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1199 % %
1200 % %
1201 % %
1202 % 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 %
1203 % %
1204 % %
1205 % %
1206 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1207 %
1208 % TransparentPaintImageChroma() changes the opacity value associated with any
1209 % pixel that matches color to the value defined by opacity.
1210 %
1211 % As there is one fuzz value for the all the channels, the
1212 % TransparentPaintImage() API is not suitable for the operations like chroma,
1213 % where the tolerance for similarity of two color component (RGB) can be
1214 % different, Thus we define this method take two target pixels (one
1215 % low and one hight) and all the pixels of an image which are lying between
1216 % these two pixels are made transparent.
1217 %
1218 % The format of the TransparentPaintImage method is:
1219 %
1220 % MagickBooleanType TransparentPaintImage(Image *image,
1221 % const MagickPixelPacket *low,const MagickPixelPacket *hight,
1222 % const Quantum opacity,const MagickBooleanType invert)
1223 %
1224 % A description of each parameter follows:
1225 %
1226 % o image: the image.
1227 %
1228 % o low: the low target color.
1229 %
1230 % o high: the high target color.
1231 %
1232 % o opacity: the replacement opacity value.
1233 %
1234 % o invert: paint any pixel that does not match the target color.
1235 %
1236 */
1237 MagickExport MagickBooleanType TransparentPaintImageChroma(Image *image,
1238  const MagickPixelPacket *low,const MagickPixelPacket *high,
1239  const Quantum opacity,const MagickBooleanType invert)
1240 {
1241 #define TransparentPaintImageTag "Transparent/Image"
1242 
1243  CacheView
1244  *image_view;
1245 
1247  *exception;
1248 
1249  MagickBooleanType
1250  status;
1251 
1252  MagickOffsetType
1253  progress;
1254 
1255  ssize_t
1256  y;
1257 
1258  assert(image != (Image *) NULL);
1259  assert(image->signature == MagickCoreSignature);
1260  assert(high != (MagickPixelPacket *) NULL);
1261  assert(low != (MagickPixelPacket *) NULL);
1262  if (IsEventLogging() != MagickFalse)
1263  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1264  if (SetImageStorageClass(image,DirectClass) == MagickFalse)
1265  return(MagickFalse);
1266  if (image->matte == MagickFalse)
1267  (void) SetImageAlphaChannel(image,ResetAlphaChannel);
1268  /*
1269  Make image color transparent.
1270  */
1271  status=MagickTrue;
1272  progress=0;
1273  exception=(&image->exception);
1274  image_view=AcquireAuthenticCacheView(image,exception);
1275 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1276  #pragma omp parallel for schedule(static) shared(progress,status) \
1277  magick_number_threads(image,image,image->rows,1)
1278 #endif
1279  for (y=0; y < (ssize_t) image->rows; y++)
1280  {
1281  MagickBooleanType
1282  match;
1283 
1285  pixel;
1286 
1287  IndexPacket
1288  *magick_restrict indexes;
1289 
1290  ssize_t
1291  x;
1292 
1293  PixelPacket
1294  *magick_restrict q;
1295 
1296  if (status == MagickFalse)
1297  continue;
1298  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1299  if (q == (PixelPacket *) NULL)
1300  {
1301  status=MagickFalse;
1302  continue;
1303  }
1304  indexes=GetCacheViewAuthenticIndexQueue(image_view);
1305  GetMagickPixelPacket(image,&pixel);
1306  for (x=0; x < (ssize_t) image->columns; x++)
1307  {
1308  SetMagickPixelPacket(image,q,indexes+x,&pixel);
1309  match=((pixel.red >= low->red) && (pixel.red <= high->red) &&
1310  (pixel.green >= low->green) && (pixel.green <= high->green) &&
1311  (pixel.blue >= low->blue) && (pixel.blue <= high->blue)) ? MagickTrue : MagickFalse;
1312  if (match != invert)
1313  q->opacity=opacity;
1314  q++;
1315  }
1316  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1317  status=MagickFalse;
1318  if (image->progress_monitor != (MagickProgressMonitor) NULL)
1319  {
1320  MagickBooleanType
1321  proceed;
1322 
1323 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1324  #pragma omp atomic
1325 #endif
1326  progress++;
1327  proceed=SetImageProgress(image,TransparentPaintImageTag,progress,
1328  image->rows);
1329  if (proceed == MagickFalse)
1330  status=MagickFalse;
1331  }
1332  }
1333  image_view=DestroyCacheView(image_view);
1334  return(status);
1335 }
Definition: image.h:152