Coverage Report

Created: 2026-01-20 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/paint.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2025 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                      PPPP    AAA   IIIII  N   N  TTTTT                      %
14
%                      P   P  A   A    I    NN  N    T                        %
15
%                      PPPP   AAAAA    I    N N N    T                        %
16
%                      P      A   A    I    N  NN    T                        %
17
%                      P      A   A  IIIII  N   N    T                        %
18
%                                                                             %
19
%                                                                             %
20
%                        Methods to Paint on an Image                         %
21
%                                                                             %
22
%                                                                             %
23
%                              Software Design                                %
24
%                                John Cristy                                  %
25
%                                 July 1998                                   %
26
%                                                                             %
27
%                                                                             %
28
%                                                                             %
29
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
30
%
31
%
32
*/
33

34
/*
35
 Include declarations.
36
*/
37
#include "magick/studio.h"
38
#include "magick/alpha_composite.h"
39
#include "magick/color.h"
40
#include "magick/monitor.h"
41
#include "magick/paint.h"
42
#include "magick/pixel_cache.h"
43
#include "magick/pixel_iterator.h"
44
#include "magick/render.h"
45
#include "magick/utility.h"
46

47
/*
48
  Define declarations.
49
*/
50
#define FuzzyOpacityMatch(color,target,fuzz) \
51
17.6M
  (((color)->opacity == (target)->opacity) && \
52
17.6M
   FuzzyColorMatch(color,target,fuzz))
53
2.24M
#define MaxStacksize  (((size_t) 1) << 15)
54
#define Push(up,left,right,delta)                                       \
55
2.24M
  {                                                                     \
56
2.24M
    if (s >= (segment_stack+MaxStacksize))                              \
57
2.24M
      {                                                                 \
58
6
        MagickFreeResourceLimitedMemory(unsigned char *,floodplane);    \
59
6
        MagickFreeResourceLimitedMemory(SegmentInfo *,segment_stack);   \
60
6
        ThrowBinaryException2(DrawError,"SegmentStackOverflow",         \
61
6
                              image->filename);                         \
62
0
      }                                                                 \
63
2.24M
    else                                                                \
64
2.24M
      {                                                                 \
65
2.24M
        if (((((ptrdiff_t)up)+((ptrdiff_t)delta)) >= 0) &&              \
66
2.24M
            ((((ptrdiff_t)up)+((ptrdiff_t)delta)) < (long) image->rows)) \
67
2.24M
          {                                                             \
68
2.04M
            s->y1=(up);                                                 \
69
2.04M
            s->x1=(left);                                               \
70
2.04M
            s->x2=(right);                                              \
71
2.04M
            s->y2=(delta);                                              \
72
2.04M
            s++;                                                        \
73
2.04M
          }                                                             \
74
2.24M
      }                                                                 \
75
2.24M
  }
76

77
/*
78
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79
%                                                                             %
80
%                                                                             %
81
%                                                                             %
82
%   C o l o r F l o o d f i l l I m a g e                                     %
83
%                                                                             %
84
%                                                                             %
85
%                                                                             %
86
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87
%
88
%  ColorFloodfill() changes the color value of any pixel that matches
89
%  target and is an immediate neighbor.  If the method FillToBorderMethod is
90
%  specified, the color value is changed for any neighbor pixel that does not
91
%  match the bordercolor member of image.
92
%
93
%  By default target must match a particular pixel color exactly.
94
%  However, in many cases two colors may differ by a small amount.  The
95
%  fuzz member of image defines how much tolerance is acceptable to
96
%  consider two colors as the same.  For example, set fuzz to 10 and the
97
%  color red at intensities of 100 and 102 respectively are now
98
%  interpreted as the same color for the purposes of the floodfill.
99
%
100
%  The format of the ColorFloodfillImage method is:
101
%
102
%      MagickPassFail ColorFloodfillImage(Image *image,
103
%        const DrawInfo *draw_info, const PixelPacket target,
104
%        const long x_offset,const long y_offset,
105
%        const PaintMethod method)
106
%
107
%  A description of each parameter follows:
108
%
109
%    o image: The image.
110
%
111
%    o draw_info: The draw info.
112
%
113
%    o target: The RGB value of the target color.
114
%
115
%    o x,y: The starting location of the operation.
116
%
117
%    o method: Choose either FloodfillMethod or FillToBorderMethod.
118
%
119
%
120
*/
121
122
MagickExport MagickPassFail ColorFloodfillImage(Image *image,
123
                                                const DrawInfo *draw_info,
124
                                                const PixelPacket target,
125
                                                const long x_offset,
126
                                                const long y_offset,
127
                                                const PaintMethod method)
128
2.91k
{
129
2.91k
  Image
130
2.91k
    *pattern;
131
132
2.91k
  int
133
2.91k
    skip;
134
135
2.91k
  long
136
2.91k
    offset,
137
2.91k
    start,
138
2.91k
    x1,
139
2.91k
    x2,
140
2.91k
    y;
141
142
2.91k
  PixelPacket
143
2.91k
    color;
144
145
2.91k
  register long
146
2.91k
    x;
147
148
2.91k
  register PixelPacket
149
2.91k
    *q;
150
151
2.91k
  register SegmentInfo
152
2.91k
    *s;
153
154
2.91k
  SegmentInfo
155
2.91k
    *segment_stack;
156
157
2.91k
  unsigned char
158
2.91k
    *floodplane;
159
160
2.91k
  MagickPassFail
161
2.91k
    status=MagickPass;
162
163
  /*
164
    Check boundary conditions.
165
  */
166
2.91k
  assert(image != (Image *) NULL);
167
2.91k
  assert(image->signature == MagickSignature);
168
2.91k
  assert(draw_info != (DrawInfo *) NULL);
169
2.91k
  assert(draw_info->signature == MagickSignature);
170
2.91k
  if ((x_offset < 0) || (x_offset >= (long) image->columns))
171
72
    return(MagickFail);
172
2.84k
  if ((y_offset < 0) || (y_offset >= (long) image->rows))
173
59
    return(MagickFail);
174
  /*
175
    Set floodfill color.
176
  */
177
2.78k
  if (FuzzyColorMatch(&draw_info->fill,&target,image->fuzz))
178
7
    return(MagickFail);
179
  /*
180
    The flood-fill algorithm may lock-up if a clip-mask is present
181
    because the pixels just written may not be what is read later.
182
183
    Perhaps we will update the algorithm to respect the clip-mask later.
184
  */
185
2.77k
  if (*ImageGetClipMaskInlined(image))
186
15
    {
187
15
      ThrowException(&image->exception,DrawError,UnableToFloodFillImageDueToClipMask,image->filename);
188
15
      return(MagickFail);
189
15
    }
190
2.76k
  floodplane=MagickAllocateResourceLimitedClearedArray(unsigned char *,image->columns,image->rows);
191
2.76k
  segment_stack=MagickAllocateResourceLimitedArray(SegmentInfo *,MaxStacksize,sizeof(SegmentInfo));
192
2.76k
  if ((floodplane== (unsigned char *) NULL) ||
193
2.76k
      (segment_stack == (SegmentInfo *) NULL))
194
0
    {
195
0
      MagickFreeResourceLimitedMemory(unsigned char *, floodplane);
196
0
      MagickFreeResourceLimitedMemory(SegmentInfo *, segment_stack);
197
0
      ThrowBinaryException3(ResourceLimitError,MemoryAllocationFailed,
198
0
                            UnableToFloodfillImage);
199
0
    }
200
  /*
201
    Push initial segment on stack.
202
  */
203
2.76k
  image->storage_class=DirectClass;
204
2.76k
  x=x_offset;
205
2.76k
  y=y_offset;
206
2.76k
  start=0;
207
2.76k
  s=segment_stack;
208
2.76k
  Push(y,x,x,1);
209
2.76k
  Push((ptrdiff_t)y+1,x,x,-1);
210
1.26M
  while (s > segment_stack)
211
1.26M
    {
212
      /*
213
        Pop segment off stack.
214
      */
215
1.26M
      s--;
216
1.26M
      x1=(long) s->x1;
217
1.26M
      x2=(long) s->x2;
218
1.26M
      offset=(long) s->y2;
219
1.26M
      y=(long) s->y1+offset;
220
      /*
221
        Recolor neighboring pixels.
222
      */
223
224
      /*
225
        These two method loops are the same except for the fuzzy-match
226
        logic.
227
      */
228
1.26M
      if (method == FloodfillMethod)
229
1.16M
        {
230
1.16M
          q=GetImagePixels(image,0,y,x1+1,1);
231
1.16M
          if (q == (PixelPacket *) NULL)
232
0
            {
233
0
              status=MagickFail;
234
0
              break;
235
0
            }
236
1.16M
          q+=x1;
237
238
2.53M
          for (x=x1; x >= 0; x--)
239
1.53M
            {
240
1.53M
              if (!FuzzyColorMatch(q,&target,image->fuzz))
241
171k
                break;
242
1.36M
              floodplane[y*image->columns+x]=MagickTrue;
243
1.36M
              *q=draw_info->fill;
244
1.36M
              q--;
245
1.36M
            }
246
247
1.16M
          if (!SyncImagePixels(image))
248
0
            {
249
0
              status=MagickFail;
250
0
              break;
251
0
            }
252
1.16M
        }
253
96.4k
      else /* method == FillToBorderMethod */
254
96.4k
        {
255
96.4k
          q=GetImagePixels(image,0,y,x1+1,1);
256
96.4k
          if (q == (PixelPacket *) NULL)
257
0
            {
258
0
              status=MagickFail;
259
0
              break;
260
0
            }
261
96.4k
          q+=x1;
262
263
735k
          for (x=x1; x >= 0; x--)
264
648k
            {
265
648k
              if (FuzzyColorMatch(q,&target,image->fuzz) ||
266
648k
                  FuzzyColorMatch(q,&draw_info->fill,image->fuzz))
267
9.39k
                break;
268
639k
              floodplane[y*image->columns+x]=MagickTrue;
269
639k
              *q=draw_info->fill;
270
639k
              q--;
271
639k
            }
272
273
96.4k
          if (!SyncImagePixels(image))
274
0
            {
275
0
              status=MagickFail;
276
0
              break;
277
0
            }
278
96.4k
        }
279
280
1.26M
      skip=x >= x1;
281
1.26M
      if (!skip)
282
1.15M
        {
283
1.15M
          start=x+1;
284
1.15M
          if (start < x1)
285
1.15M
            Push(y,start,(ptrdiff_t)x1-1,-offset);
286
1.15M
          x=x1+1;
287
1.15M
        }
288
1.26M
      do
289
1.33M
        {
290
1.33M
          if (!skip)
291
1.22M
            {
292
1.22M
              if (x < (long) image->columns)
293
1.18M
                {
294
                  /*
295
                    These two method loops are the same except for the
296
                    fuzzy-match logic.
297
                  */
298
1.18M
                  if (method == FloodfillMethod)
299
1.09M
                    {
300
1.09M
                      q=GetImagePixels(image,x,y,image->columns-x,1);
301
1.09M
                      if (q == (PixelPacket *) NULL)
302
0
                        {
303
0
                          status=MagickFail;
304
0
                          break;
305
0
                        }
306
307
16.2M
                      for ( ; x < (long) image->columns; x++)
308
15.2M
                        {
309
15.2M
                          if (!FuzzyColorMatch(q,&target,image->fuzz))
310
35.7k
                            break;
311
15.1M
                          floodplane[y*image->columns+x]=MagickTrue;
312
15.1M
                          *q=draw_info->fill;
313
15.1M
                          q++;
314
15.1M
                        }
315
316
1.09M
                      if (!SyncImagePixels(image))
317
0
                        {
318
0
                          status=MagickFail;
319
0
                          break;
320
0
                        }
321
1.09M
                    }
322
91.9k
                  else /* method == FillToBorderMethod */
323
91.9k
                    {
324
91.9k
                      q=GetImagePixels(image,x,y,image->columns-x,1);
325
91.9k
                      if (q == (PixelPacket *) NULL)
326
0
                        {
327
0
                          status=MagickFail;
328
0
                          break;
329
0
                        }
330
331
57.4M
                      for ( ; x < (long) image->columns; x++)
332
57.3M
                        {
333
57.3M
                          if (FuzzyColorMatch(q,&target,image->fuzz) ||
334
57.3M
                              FuzzyColorMatch(q,&draw_info->fill,image->fuzz))
335
5.63k
                            break;
336
57.3M
                          floodplane[y*image->columns+x]=True;
337
57.3M
                          *q=draw_info->fill;
338
57.3M
                          q++;
339
57.3M
                        }
340
341
91.9k
                      if (!SyncImagePixels(image))
342
0
                        {
343
0
                          status=MagickFail;
344
0
                          break;
345
0
                        }
346
91.9k
                    }
347
1.18M
                }
348
1.22M
              Push(y,start, (ptrdiff_t)x-1,offset);
349
1.22M
              if (x > ((ptrdiff_t)x2+1))
350
1.22M
                Push(y, (ptrdiff_t)x2+1, (ptrdiff_t)x-1,-offset);
351
1.22M
            }
352
1.33M
          skip=False;
353
1.33M
          x++;
354
1.33M
          if (x <= x2)
355
75.2k
            {
356
              /*
357
                These two method loops are the same except for the
358
                fuzzy-match logic.
359
              */
360
75.2k
              if (method == FloodfillMethod)
361
69.0k
                {
362
69.0k
                  q=GetImagePixels(image,x,y,x2-x+1,1);
363
69.0k
                  if (q == (PixelPacket *) NULL)
364
0
                    {
365
0
                      status=MagickFail;
366
0
                      break;
367
0
                    }
368
369
88.6k
                  for ( ; x <= x2; x++)
370
85.6k
                    {
371
85.6k
                      if (FuzzyColorMatch(q,&target,image->fuzz))
372
66.0k
                        break;
373
19.5k
                      q++;
374
19.5k
                    }
375
69.0k
                }
376
6.18k
              else /* method == FillToBorderMethod */
377
6.18k
                {
378
6.18k
                  q=GetImagePixels(image,x,y,x2-x+1,1);
379
6.18k
                  if (q == (PixelPacket *) NULL)
380
0
                    {
381
0
                      status=MagickFail;
382
0
                      break;
383
0
                    }
384
385
1.06M
                  for ( ; x <= x2; x++)
386
1.05M
                    {
387
1.05M
                      if (!FuzzyColorMatch(q,&target,image->fuzz) &&
388
1.05M
                          !FuzzyColorMatch(q,&draw_info->fill,image->fuzz))
389
2.25k
                        break;
390
1.05M
                      q++;
391
1.05M
                    }
392
6.18k
                }
393
75.2k
            }
394
1.33M
          start=x;
395
1.33M
        } while (x <= x2);
396
1.26M
    }
397
2.75k
  pattern=draw_info->fill_pattern;
398
2.75k
  if (pattern == (Image *) NULL)
399
2.25k
    {
400
      /*
401
        Tile fill color onto floodplane.
402
      */
403
2.25k
      if (status == MagickPass)
404
2.25k
        {
405
#if defined(HAVE_OPENMP)
406
#  pragma omp parallel for schedule(static,64) shared(status)
407
#endif
408
194k
          for (y=0; y < (long) image->rows; y++)
409
191k
            {
410
191k
              PixelPacket
411
191k
                * restrict p;
412
413
191k
              if (status == MagickFail)
414
0
                continue;
415
416
191k
              p=GetImagePixels(image,0,y,image->columns,1);
417
191k
              if (p == (PixelPacket *) NULL)
418
0
                {
419
0
                  goto tile_color_fail;
420
0
                }
421
84.7M
              for (x=0; x < (long) image->columns; x++)
422
84.5M
                {
423
84.5M
                  if (floodplane[y*image->columns+x])
424
70.4M
                    *p=draw_info->fill;
425
84.5M
                  p++;
426
84.5M
                }
427
191k
              if (!SyncImagePixels(image))
428
0
                {
429
0
                  goto tile_color_fail;
430
0
                }
431
              /* Continue loop processing */
432
191k
              continue;
433
434
              /* There was a problem */
435
191k
            tile_color_fail:;
436
0
              status=MagickFail;
437
#if defined(HAVE_OPENMP)
438
#  pragma omp flush (status)
439
#endif
440
0
            }
441
2.25k
        }
442
2.25k
    }
443
499
  else
444
499
    {
445
      /*
446
        Tile image onto floodplane.
447
      */
448
499
      if (status == MagickPass)
449
499
        {
450
#if defined(HAVE_OPENMP)
451
#  pragma omp parallel for schedule(static,64) shared(status)
452
#endif
453
24.5k
          for (y=0; y < (long) image->rows; y++)
454
24.0k
            {
455
24.0k
              PixelPacket
456
24.0k
                * restrict p;
457
458
24.0k
              if (status == MagickFail)
459
0
                continue;
460
461
24.0k
              p=GetImagePixels(image,0,y,image->columns,1);
462
24.0k
              if (p == (PixelPacket *) NULL)
463
0
                goto tile_pattern_fail;
464
465
1.24M
              for (x=0; x < (long) image->columns; x++)
466
1.22M
                {
467
1.22M
                  if (floodplane[y*image->columns+x])
468
188k
                    {
469
188k
                      if (!AcquireOnePixelByReference(pattern,
470
188k
                                                      &color,
471
188k
                                                      (long) ((unsigned long)
472
188k
                                                              (x-pattern->tile_info.x) %
473
188k
                                                              pattern->columns),
474
188k
                                                      (long) ((unsigned long)
475
188k
                                                              (y-pattern->tile_info.y) %
476
188k
                                                              pattern->rows),
477
188k
                                                      &image->exception))
478
0
                        goto tile_pattern_fail;
479
188k
                      if (!pattern->matte)
480
0
                        color.opacity=OpaqueOpacity;
481
188k
                      if (color.opacity != TransparentOpacity)
482
185k
                        AlphaCompositePixel(p,&color,color.opacity,p,p->opacity);
483
188k
                    }
484
1.22M
                  p++;
485
1.22M
                } /* for x ... */
486
24.0k
              if (!SyncImagePixels(image))
487
0
                goto tile_pattern_fail;
488
489
              /* Continue loop processing */
490
24.0k
              continue;
491
492
              /* There was a problem */
493
24.0k
            tile_pattern_fail:;
494
0
              status=MagickFail;
495
#if defined(HAVE_OPENMP)
496
#  pragma omp flush (status)
497
#endif
498
0
            } /* for y ... */
499
499
        } /* if status ... */
500
499
    }
501
2.75k
  MagickFreeResourceLimitedMemory(SegmentInfo *, segment_stack);
502
2.75k
  MagickFreeResourceLimitedMemory(unsigned char *, floodplane);
503
2.75k
  return(status);
504
2.75k
}
505

506
/*
507
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
508
%                                                                             %
509
%                                                                             %
510
%                                                                             %
511
%   M a t t e F l o o d f i l l I m a g e                                     %
512
%                                                                             %
513
%                                                                             %
514
%                                                                             %
515
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
516
%
517
%  MatteFloodfill() changes the transparency value of any pixel that matches
518
%  target and is an immediate neighbor.  If the method FillToBorderMethod
519
%  is specified, the transparency value is changed for any neighbor pixel
520
%  that does not match the bordercolor member of image.
521
%
522
%  By default target must match a particular pixel transparency exactly.
523
%  However, in many cases two transparency values may differ by a
524
%  small amount.  The fuzz member of image defines how much tolerance is
525
%  acceptable to consider two transparency values as the same.  For example,
526
%  set fuzz to 10 and the opacity values of 100 and 102 respectively are
527
%  now interpreted as the same value for the purposes of the floodfill.
528
%
529
%  The format of the MatteFloodfillImage method is:
530
%
531
%      unsigned int MatteFloodfillImage(Image *image,const PixelPacket target,
532
%        const unsigned int opacity,const long x_offset,const long y_offset,
533
%        const PaintMethod method)
534
%
535
%  A description of each parameter follows:
536
%
537
%    o image: The image.
538
%
539
%    o target: The RGB value of the target color.
540
%
541
%    o opacity: The level of transparency: 0 is fully opaque and MaxRGB is
542
%      fully transparent.
543
%
544
%    o x,y: The starting location of the operation.
545
%
546
%    o method:  Choose either FloodfillMethod or FillToBorderMethod.
547
%
548
%
549
*/
550
MagickExport MagickPassFail MatteFloodfillImage(Image *image,
551
  const PixelPacket target,const unsigned int opacity,const long x_offset,
552
  const long y_offset,const PaintMethod method)
553
2.01k
{
554
2.01k
  int
555
2.01k
    skip;
556
557
2.01k
  long
558
2.01k
    offset,
559
2.01k
    start,
560
2.01k
    x1,
561
2.01k
    x2,
562
2.01k
    y;
563
564
2.01k
  register long
565
2.01k
    x;
566
567
2.01k
  register PixelPacket
568
2.01k
    *q;
569
570
2.01k
  register SegmentInfo
571
2.01k
    *s;
572
573
2.01k
  SegmentInfo
574
2.01k
    *segment_stack;
575
576
2.01k
  void
577
2.01k
    *floodplane = (void *) NULL; /* Only to make common Push macro happy */
578
579
2.01k
  MagickPassFail
580
2.01k
    status=MagickPass;
581
582
  /*
583
    Check boundary conditions.
584
  */
585
2.01k
  assert(image != (Image *) NULL);
586
2.01k
  assert(image->signature == MagickSignature);
587
2.01k
  if ((x_offset < 0) || (x_offset >= (long) image->columns))
588
48
    return(MagickFail);
589
1.97k
  if ((y_offset < 0) || (y_offset >= (long) image->rows))
590
53
    return(MagickFail);
591
1.91k
  if (target.opacity == opacity)
592
4
    return(MagickFail);
593
1.91k
  q=GetImagePixels(image,x_offset,y_offset,1,1);
594
1.91k
  if (q == (PixelPacket *) NULL)
595
0
    return(MagickFail);
596
1.91k
  if (q->opacity == opacity)
597
4
    return(MagickFail);
598
  /*
599
    The flood-fill algorithm may lock-up if a clip-mask is present
600
    because the pixels just written may not be what is read later.
601
602
    Perhaps we will update the algorithm to respect the clip-mask later.
603
  */
604
1.90k
  if (*ImageGetClipMaskInlined(image))
605
1
    {
606
1
      ThrowException(&image->exception,DrawError,UnableToFloodFillImageDueToClipMask,image->filename);
607
1
      return(MagickFail);
608
1
    }
609
  /*
610
    Allocate segment stack.
611
  */
612
1.90k
  segment_stack=MagickAllocateResourceLimitedArray(SegmentInfo *,MaxStacksize,sizeof(SegmentInfo));
613
1.90k
  if (segment_stack == (SegmentInfo *) NULL)
614
0
    ThrowBinaryException3(ResourceLimitError,MemoryAllocationFailed,
615
1.90k
      UnableToFloodfillImage);
616
  /*
617
    Push initial segment on stack.
618
  */
619
1.90k
  (void) SetImageType(image,TrueColorMatteType);
620
1.90k
  x=x_offset;
621
1.90k
  y=y_offset;
622
1.90k
  start=0;
623
1.90k
  s=segment_stack;
624
1.90k
  Push(y,x,x,1);
625
1.90k
  Push((ptrdiff_t)y+1,x,x,-1);
626
585k
  while (s > segment_stack)
627
584k
  {
628
    /*
629
      Pop segment off stack.
630
    */
631
584k
    s--;
632
584k
    x1=(long) s->x1;
633
584k
    x2=(long) s->x2;
634
584k
    offset=(long) s->y2;
635
584k
    y=(long) s->y1+offset;
636
    /*
637
      Recolor neighboring points.
638
    */
639
584k
    q=GetImagePixels(image,0,y,image->columns,1);
640
584k
    if (q == (PixelPacket *) NULL)
641
0
      {
642
0
        status=MagickFail;
643
0
        break;
644
0
      }
645
584k
    q+=x1;
646
1.26M
    for (x=x1; x >= 0; x--)
647
706k
    {
648
706k
      if (method == FloodfillMethod)
649
688k
        {
650
688k
          if (!FuzzyOpacityMatch(q,&target,image->fuzz))
651
21.2k
            break;
652
688k
        }
653
18.0k
      else
654
18.0k
        if (FuzzyOpacityMatch(q,&target,image->fuzz) || (q->opacity == opacity))
655
5.53k
          break;
656
679k
      q->opacity=opacity;
657
679k
      q--;
658
679k
    }
659
584k
    if (!SyncImagePixels(image))
660
0
      {
661
0
        status=MagickFail;
662
0
        break;
663
0
      }
664
584k
    skip=x >= x1;
665
584k
    if (!skip)
666
570k
      {
667
570k
        start=x+1;
668
570k
        if (start < x1)
669
570k
          Push(y,start, (ptrdiff_t)x1-1,-offset);
670
570k
        x=x1+1;
671
570k
      }
672
584k
    do
673
590k
    {
674
590k
      if (!skip)
675
577k
        {
676
577k
          q=GetImagePixels(image,0,y,image->columns,1);
677
577k
          if (q == (PixelPacket *) NULL)
678
0
            {
679
0
              status=MagickFail;
680
0
              break;
681
0
            }
682
577k
          q+=x;
683
16.6M
          for ( ; x < (long) image->columns; x++)
684
16.1M
          {
685
16.1M
            if (method == FloodfillMethod)
686
15.3M
              {
687
15.3M
                if (!FuzzyOpacityMatch(q,&target,image->fuzz))
688
7.87k
                  break;
689
15.3M
              }
690
786k
            else
691
786k
              if (FuzzyOpacityMatch(q,&target,image->fuzz) ||
692
786k
                  (q->opacity == opacity))
693
3.85k
                break;
694
16.1M
            q->opacity=opacity;
695
16.1M
            q++;
696
16.1M
          }
697
577k
          if (!SyncImagePixels(image))
698
0
            {
699
0
              status=MagickFail;
700
0
              break;
701
0
            }
702
577k
          Push(y,start, (ptrdiff_t)x-1,offset);
703
577k
          if (x > ((ptrdiff_t)x2+1))
704
577k
            Push(y, (ptrdiff_t)x2+1, (ptrdiff_t)x-1,-offset);
705
577k
        }
706
590k
      skip=False;
707
590k
      q=GetImagePixels(image,0,y,image->columns,1);
708
590k
      if (q == (PixelPacket *) NULL)
709
0
        {
710
0
          status=MagickFail;
711
0
          break;
712
0
        }
713
590k
      q+=x;
714
612k
      for (x++; x <= x2; x++)
715
28.5k
      {
716
28.5k
        q++;
717
28.5k
        if (method == FloodfillMethod)
718
26.6k
          {
719
26.6k
            if (FuzzyOpacityMatch(q,&target,image->fuzz))
720
6.26k
              break;
721
26.6k
          }
722
1.89k
        else
723
1.89k
          if (!FuzzyOpacityMatch(q,&target,image->fuzz) &&
724
1.89k
              (q->opacity != opacity))
725
543
            break;
726
28.5k
      }
727
590k
      start=x;
728
590k
    } while (x <= x2);
729
584k
  }
730
1.90k
  MagickFreeResourceLimitedMemory(SegmentInfo*, segment_stack);
731
1.90k
  return(status);
732
1.90k
}
733

734
/*
735
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
736
%                                                                             %
737
%                                                                             %
738
%     O p a q u e I m a g e                                                   %
739
%                                                                             %
740
%                                                                             %
741
%                                                                             %
742
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
743
%
744
%  OpaqueImage() changes any pixel that matches color with the color
745
%  defined by fill.
746
%
747
%  By default color must match a particular pixel color exactly.  However,
748
%  in many cases two colors may differ by a small amount.  Fuzz defines
749
%  how much tolerance is acceptable to consider two colors as the same.
750
%  For example, set fuzz to 10 and the color red at intensities of 100 and
751
%  102 respectively are now interpreted as the same color.
752
%
753
%  The format of the OpaqueImage method is:
754
%
755
%      unsigned int OpaqueImage(Image *image,const PixelPacket target,
756
%        const PixelPacket fill)
757
%
758
%  A description of each parameter follows:
759
%
760
%    o image: The image.
761
%
762
%    o target: The RGB value of the target color.
763
%
764
%    o fill: The replacement color.
765
%
766
%
767
*/
768
typedef struct _OpaqueImageOptions_t
769
{
770
  double fuzz;
771
  PixelPacket fill;
772
  PixelPacket target;
773
} OpaqueImageOptions_t;
774
static MagickPassFail
775
OpaqueImageCallBack(void *mutable_data,         /* User provided mutable data */
776
                      const void *immutable_data, /* User provided immutable data */
777
                      Image * restrict image,               /* Modify image */
778
                      PixelPacket * restrict pixels,        /* Pixel row */
779
                      IndexPacket * restrict indexes,       /* Pixel row indexes */
780
                      const long npixels,         /* Number of pixels in row */
781
                      ExceptionInfo *exception)   /* Exception report */
782
0
{
783
0
  const OpaqueImageOptions_t
784
0
    options = *((const OpaqueImageOptions_t *) immutable_data);
785
786
0
  register long
787
0
    i;
788
789
0
  ARG_NOT_USED(mutable_data);
790
0
  ARG_NOT_USED(image);
791
0
  ARG_NOT_USED(indexes);
792
0
  ARG_NOT_USED(exception);
793
794
0
  if (options.fuzz == 0.0)
795
0
    {
796
0
      for (i=0; i < npixels; i++)
797
0
        {
798
0
          if (ColorMatch(&pixels[i],&options.target))
799
0
            pixels[i]=options.fill;
800
0
        }
801
0
    }
802
0
  else
803
0
    {
804
0
      for (i=0; i < npixels; i++)
805
0
        {
806
0
          if (FuzzyColorMatch(&pixels[i],&options.target,options.fuzz))
807
0
            pixels[i]=options.fill;
808
0
        }
809
0
    }
810
811
0
  return MagickPass;
812
0
}
813
MagickExport MagickPassFail
814
OpaqueImage(Image *image,const PixelPacket target,const PixelPacket fill)
815
0
{
816
0
#define OpaqueImageText "[%s] Setting opaque color..."
817
818
0
  OpaqueImageOptions_t
819
0
    options;
820
821
0
  MagickPassFail
822
0
    status=MagickPass;
823
824
0
  MagickBool
825
0
    is_monochrome = image->is_monochrome,
826
0
    is_grayscale = image->is_grayscale;
827
828
0
  assert(image != (Image *) NULL);
829
0
  assert(image->signature == MagickSignature);
830
0
  options.fuzz=image->fuzz;
831
0
  options.fill=fill;
832
0
  options.target=target;
833
834
  /*
835
    Assure that image type is promoted as required based on color
836
  */
837
0
  if ((is_grayscale || IsGrayColorspace(image->colorspace)) && !IsGray(fill))
838
0
    {
839
0
      if ((is_monochrome) && !IsBlackPixel(fill) && !IsWhitePixel(fill))
840
0
        is_monochrome = MagickFalse;
841
0
      is_grayscale = MagickFalse;
842
0
    }
843
844
  /*
845
    Make image color opaque.
846
  */
847
0
  if (image->storage_class == PseudoClass)
848
0
    {
849
0
      assert(image->colormap != (PixelPacket *) NULL);
850
0
      (void) OpaqueImageCallBack(0,&options,image,image->colormap,
851
0
                                 (IndexPacket *) NULL,image->colors,
852
0
                                 &image->exception);
853
0
      status &= SyncImage(image);
854
0
    }
855
0
  else
856
0
    {
857
0
      status=PixelIterateMonoModify(OpaqueImageCallBack,NULL,
858
0
                                    OpaqueImageText,NULL,&options,0,0,
859
0
                                    image->columns,image->rows,
860
0
                                    image,&image->exception);
861
0
    }
862
863
0
  image->is_monochrome = is_monochrome;
864
0
  image->is_grayscale = is_grayscale;
865
866
0
  return(status);
867
0
}
868

869
/*
870
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
871
%                                                                             %
872
%                                                                             %
873
%     T r a n s p a r e n t I m a g e                                         %
874
%                                                                             %
875
%                                                                             %
876
%                                                                             %
877
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
878
%
879
%  TransparentImage() changes the opacity value associated with any pixel
880
%  that matches color to the value defined by opacity.
881
%
882
%  By default color must match a particular pixel color exactly.  However,
883
%  in many cases two colors may differ by a small amount.  Fuzz defines
884
%  how much tolerance is acceptable to consider two colors as the same.
885
%  For example, set fuzz to 10 and the color red at intensities of 100 and
886
%  102 respectively are now interpreted as the same color.
887
%
888
%  The format of the TransparentImage method is:
889
%
890
%      unsigned int TransparentImage(Image *image,const PixelPacket target,
891
%        const unsigned int opacity)
892
%
893
%  A description of each parameter follows:
894
%
895
%    o image: The image.
896
%
897
%    o target: The RGB value of the target color.
898
%
899
%    o opacity: The replacement opacity value.
900
%
901
%
902
*/
903
typedef struct _TransparentImageOptions_t
904
{
905
  double fuzz;
906
  PixelPacket target;
907
  unsigned int opacity;
908
} TransparentImageOptions_t;
909
static MagickPassFail
910
TransparentImageCallBack(void *mutable_data,         /* User provided mutable data */
911
                         const void *immutable_data, /* User provided immutable data */
912
                         Image * restrict image,               /* Modify image */
913
                         PixelPacket * restrict pixels,        /* Pixel row */
914
                         IndexPacket * restrict indexes,       /* Pixel row indexes */
915
                         const long npixels,         /* Number of pixels in row */
916
                         ExceptionInfo *exception)   /* Exception report */
917
31.1k
{
918
31.1k
  const TransparentImageOptions_t
919
31.1k
    options = *((const TransparentImageOptions_t *) immutable_data);
920
921
31.1k
  const MagickBool
922
31.1k
    clear_matte = (!image->matte);
923
924
31.1k
  register long
925
31.1k
    i;
926
927
31.1k
  ARG_NOT_USED(mutable_data);
928
31.1k
  ARG_NOT_USED(image);
929
31.1k
  ARG_NOT_USED(indexes);
930
31.1k
  ARG_NOT_USED(exception);
931
932
31.1k
  if (options.fuzz == 0.0)
933
31.1k
    {
934
14.6M
      for (i=0; i < npixels; i++)
935
14.6M
        {
936
14.6M
          if (ColorMatch(&pixels[i],&options.target))
937
4.62M
            pixels[i].opacity=options.opacity;
938
10.0M
          else if (clear_matte)
939
9.68M
            pixels[i].opacity=OpaqueOpacity;
940
14.6M
        }
941
31.1k
    }
942
0
  else
943
0
    {
944
0
      for (i=0; i < npixels; i++)
945
0
        {
946
0
          if (FuzzyColorMatch(&pixels[i],&options.target,options.fuzz))
947
0
            pixels[i].opacity=options.opacity;
948
0
          else if (clear_matte)
949
0
            pixels[i].opacity=OpaqueOpacity;
950
0
        }
951
0
    }
952
953
31.1k
  return MagickPass;
954
31.1k
}
955
MagickExport MagickPassFail
956
TransparentImage(Image *image,const PixelPacket target,
957
                 const unsigned int opacity)
958
1.26k
{
959
1.26k
#define TransparentImageText "[%s] Setting transparent color...  "
960
961
1.26k
  TransparentImageOptions_t
962
1.26k
    options;
963
964
1.26k
  MagickPassFail
965
1.26k
    status=MagickPass;
966
967
  /*
968
    Make image color transparent.
969
  */
970
1.26k
  assert(image != (Image *) NULL);
971
1.26k
  assert(image->signature == MagickSignature);
972
1.26k
  options.fuzz=image->fuzz;
973
1.26k
  options.opacity=opacity;
974
1.26k
  options.target=target;
975
1.26k
  if (image->storage_class == PseudoClass)
976
685
    {
977
685
      assert(image->colormap != (PixelPacket *) NULL);
978
685
      (void) TransparentImageCallBack(0,&options,image,image->colormap,
979
685
                                 (IndexPacket *) NULL,image->colors,
980
685
                                 &image->exception);
981
685
      status &= SyncImage(image);
982
685
    }
983
579
  else
984
579
    {
985
579
      status=PixelIterateMonoModify(TransparentImageCallBack,NULL,
986
579
                                    TransparentImageText,NULL,&options,0,0,
987
579
                                    image->columns,image->rows,
988
579
                                    image,&image->exception);
989
579
    }
990
1.26k
  image->matte=MagickTrue;
991
992
1.26k
  return(status);
993
1.26k
}