Coverage Report

Created: 2026-06-16 07:20

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