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