Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/gsimpath.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Image to outline conversion for Ghostscript library */
18
#include "gx.h"
19
#include "gserrors.h"
20
#include "gsmatrix.h"
21
#include "gspaint.h"    /* for gs_imagepath prototype */
22
#include "gsstate.h"
23
#include "gspath.h"
24
25
/* Define the state of the conversion process. */
26
typedef struct {
27
    /* The following are set at the beginning of the conversion. */
28
    gs_gstate *pgs;
29
    const byte *data;   /* image data */
30
    int width, height, raster;
31
    /* The following are updated dynamically. */
32
    int dx, dy;     /* X/Y increment of current run */
33
    int count;      /* # of steps in current run */
34
} status;
35
36
/* Define the scaling for the path tracer. */
37
/* It must be even. */
38
0
#define outline_scale 4
39
/* Define the length of the short strokes for turning corners. */
40
0
#define step 1
41
42
/* Forward declarations */
43
static int get_pixel(const status *, int, int);
44
static int trace_from(status *, int, int, int);
45
static int add_dxdy(status *, int, int, int);
46
47
#define add_deltas(s, dx, dy, n)\
48
0
  if ( (code = add_dxdy(s, dx, dy, n)) < 0 ) return code
49
/* Append an outline derived from an image to the current path. */
50
int
51
gs_imagepath(gs_gstate * pgs, int width, int height, const byte * data)
52
0
{
53
0
    status stat;
54
0
    status *out = &stat;
55
0
    int code, x, y;
56
57
    /* Initialize the state. */
58
0
    stat.pgs = pgs;
59
0
    stat.data = data;
60
0
    stat.width = width;
61
0
    stat.height = height;
62
0
    stat.raster = (width + 7) / 8;
63
    /* Trace the cells to form an outline.  The trace goes in clockwise */
64
    /* order, always starting by going west along a bottom edge. */
65
0
    for (y = height - 1; y >= 0; y--)
66
0
        for (x = width - 1; x >= 0; x--) {
67
0
            if (get_pixel(out, x, y) && !get_pixel(out, x, y - 1) &&
68
0
              (!get_pixel(out, x + 1, y) || get_pixel(out, x + 1, y - 1)) &&
69
0
                !trace_from(out, x, y, 1)
70
0
                ) {   /* Found a starting point */
71
0
                stat.count = 0;
72
0
                stat.dx = stat.dy = 0;
73
0
                if ((code = trace_from(out, x, y, 0)) < 0)
74
0
                    return code;
75
0
                add_deltas(out, 0, 0, 1); /* force out last segment */
76
0
                if ((code = gs_closepath(pgs)) < 0)
77
0
                    return code;
78
0
            }
79
0
        }
80
0
    return 0;
81
0
}
82
83
/* Get a pixel from the data.  Return 0 if outside the image. */
84
static int
85
get_pixel(register const status * out, int x, int y)
86
0
{
87
0
    if (x < 0 || x >= out->width || y < 0 || y >= out->height)
88
0
        return 0;
89
0
    return (out->data[y * out->raster + (x >> 3)] >> (~x & 7)) & 1;
90
0
}
91
92
/* Trace a path.  If detect is true, don't draw, just return 1 if we ever */
93
/* encounter a starting point whose x,y follows that of the initial point */
94
/* in x-then-y scan order; if detect is false, actually draw the outline. */
95
static int
96
trace_from(register status * out, int x0, int y0, int detect)
97
0
{
98
0
    int x = x0, y = y0;
99
0
    int dx = -1, dy = 0;  /* initially going west */
100
0
    int part = 0;   /* how far along edge we are; */
101
102
    /* initialized only to pacify gcc */
103
0
    int code;
104
105
0
    if (!detect) {
106
0
        part = (get_pixel(out, x + 1, y - 1) ?
107
0
                outline_scale - step : step);
108
0
        code = gs_moveto(out->pgs,
109
0
                         x + 1 - part / (float)outline_scale,
110
0
                         (float)y);
111
0
        if (code < 0)
112
0
            return code;
113
0
    }
114
0
    while (1) {     /* Relative to the current direction, */
115
        /* -dy,dx is at +90 degrees (counter-clockwise); */
116
        /* tx,ty is at +45 degrees; */
117
        /* ty,-tx is at -45 degrees (clockwise); */
118
        /* dy,-dx is at -90 degrees. */
119
0
        int tx = dx - dy, ty = dy + dx;
120
121
0
        if (get_pixel(out, x + tx, y + ty)) { /* Cell at 45 degrees is full, */
122
            /* go counter-clockwise. */
123
0
            if (!detect) { /* If this is a 90 degree corner set at a */
124
                /* 45 degree angle, avoid backtracking. */
125
0
                if (out->dx == ty && out->dy == -tx) {
126
0
#define half_scale (outline_scale / 2 - step)
127
0
                    out->count -= half_scale;
128
0
                    add_deltas(out, tx, ty, outline_scale / 2);
129
0
#undef half_scale
130
0
                } else {
131
0
                    add_deltas(out, dx, dy, step - part);
132
0
                    add_deltas(out, tx, ty, outline_scale - step);
133
0
                }
134
0
                part = outline_scale - step;
135
0
            }
136
0
            x += tx, y += ty;
137
0
            dx = -dy, dy += tx;
138
0
        } else if (!get_pixel(out, x + dx, y + dy)) { /* Cell straight ahead is empty, go clockwise. */
139
0
            if (!detect) {
140
0
                add_deltas(out, dx, dy, outline_scale - step - part);
141
0
                add_deltas(out, ty, -tx, step);
142
0
                part = step;
143
0
            }
144
0
            dx = dy, dy -= ty;
145
0
        } else {   /* Neither of the above, go in same direction. */
146
0
            if (!detect) {
147
0
                add_deltas(out, dx, dy, outline_scale);
148
0
            }
149
0
            x += dx, y += dy;
150
0
        }
151
0
        if (dx == -step && dy == 0 && !(tx == -step && ty == -step)) { /* We just turned a corner and are going west, */
152
            /* so the previous pixel is a starting point pixel. */
153
0
            if (x == x0 && y == y0)
154
0
                return 0;
155
0
            if (detect && (y > y0 || (y == y0 && x > x0)))
156
0
                return 1;
157
0
        }
158
0
    }
159
0
}
160
161
/* Add a (dx, dy) pair to the path being formed. */
162
/* Accumulate successive segments in the same direction. */
163
static int
164
add_dxdy(register status * out, int dx, int dy, int count)
165
0
{
166
0
    if (count != 0) {
167
0
        if (dx == out->dx && dy == out->dy)
168
0
            out->count += count;
169
0
        else {
170
0
            if (out->count != 0) {
171
0
                int code = gs_rlineto(out->pgs,
172
0
                                out->dx * out->count / (float)outline_scale,
173
0
                               out->dy * out->count / (float)outline_scale);
174
175
0
                if (code < 0)
176
0
                    return code;
177
0
            }
178
0
            out->dx = dx, out->dy = dy;
179
0
            out->count = count;
180
0
        }
181
0
    }
182
0
    return 0;
183
0
}