Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/devices/minftrsz.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
/* $Id */
17
/* Support functions for 1bpp Fax/Tiff devices */
18
#include "minftrsz.h"
19
#include "gserrors.h"
20
#include "gsmalloc.h"
21
#include "memory_.h"
22
23
/************************************************************************
24
 *
25
 * These functions implement raster data processing to ensure that the
26
 * dot size and line width are at least some minimum number of pixels.
27
 * This is needed for some printing technologies that cannot image pixels
28
 * (or small groups of pixels) but that rasterize at full resolution to
29
 * give better quality. The effect is to 'thicken' lines and expand
30
 * independent pixels (which sets a minimum on the lightest halftoned
31
 * gray value).
32
 *
33
 * While some of this can be done by setting a minimum on linewidth, this
34
 * would have no effect on features painted with narrow fills or with
35
 * monochrome images (sometimes pre-halftoned).
36
 *
37
 ************************************************************************/
38
39
typedef struct min_feature_data_s {
40
    gs_memory_t *memory;
41
    int min_size;
42
    int width;                  /* Pixels per line                      */
43
    int height;
44
    int cur_line;
45
    int bytes_per_line;
46
    byte *lines;                /* vertical history lines               */
47
    byte *lines_prev[8];        /* pointers to previous lines           */
48
                                /* lines_prev[0] is most recent         */
49
    byte remap_mid8[65536];     /* Horizontal pixel expansion map       */
50
    /* The h_remap array is indexed by the data byte with the 'extra'   */
51
    /* min_size bits to the left and right of the byte. Thus 16 bits    */
52
    /* needed for 8 bits plus the preceding and following 4 bits needed */
53
    /* for min_size of 4. This is probably adequate for current users   */
54
        /* the beginning and end of a line are special since they need  */
55
        /* to darken extra pixels within the line bounds                */
56
    byte remap_first4[256];
57
    byte remap_last4[256];
58
} min_feature_data_t;
59
60
static int
61
next_zero(int startbit, int val)
62
0
{
63
0
    int i;
64
65
0
    for (i=startbit-1; i>=0; i--)
66
0
        if ((val & 1<<i) == 0)
67
0
            return i;
68
0
    return -1;
69
0
}
70
71
static int
72
next_one(int startbit, int val)
73
0
{
74
0
    int i;
75
76
0
    for (i=startbit-1; i>=0; i--)
77
0
        if ((val & 1<<i) != 0)
78
0
            return i;
79
0
    return -1;
80
0
}
81
82
/* Note linewidth is number of pixels -- needed for the end of line */
83
int
84
min_feature_size_init(gs_memory_t *mem, int min_feature_size,
85
                      int width, int height, void **min_feature_data)
86
0
{
87
0
    min_feature_data_t *data;
88
0
    int i, bytes_per_line;
89
90
0
    if (min_feature_size > 4)
91
0
        return_error(gs_error_limitcheck);
92
    /* allocate our data structure and vertical tracking buffer */
93
0
    if ((data = (min_feature_data_t *)gs_malloc(mem, 1, sizeof(min_feature_data_t),
94
0
                                "mem_feature_size(data)")) == NULL)
95
0
        return_error(gs_error_VMerror);
96
0
    bytes_per_line = (width+7)/8;
97
0
    if ((data->lines = (byte *)gs_malloc(mem, bytes_per_line, 2 * min_feature_size,
98
0
                                "mem_feature_size(lines)")) == NULL) {
99
0
        gs_free(mem, data, 1, sizeof(min_feature_data_t),
100
0
                "mem_feature_size(data)");
101
0
        return_error(gs_error_VMerror);
102
0
    }
103
0
    data->memory = mem;
104
0
    data->width = width;
105
0
    data->height = height;
106
0
    data->cur_line = -1;
107
0
    data->min_size = min_feature_size;
108
0
    data->bytes_per_line = bytes_per_line;
109
0
    memset(data->lines, 0, bytes_per_line * 2 * min_feature_size);
110
0
    for(i=0; i<2*min_feature_size; i++)
111
0
        data->lines_prev[i] = data->lines + (bytes_per_line * i);
112
113
0
#define bm(b)           /* bitmask(b) 0 is lsb */\
114
0
   (1<<(b))
115
116
    /* build the 'remap' data for the min_feature_size */
117
0
    for (i=0; i<256; i++) {
118
0
        int f = i, l = i, fd = 8, fw;
119
120
0
        do {
121
0
            fd = next_one(fd, f);       /* value == -1 if past bit_0 */
122
0
            if (fd < 0)
123
0
                break;
124
0
            fw = next_zero(fd, f);      /* value == -1 if past bit_0 */
125
0
            if ((fd - fw) < min_feature_size) {
126
                /* darken (set to 0) bits needed depending on min_feature_size  */
127
                /* 'first' and 'last' are different because we need to expand   */
128
                /* within the bits, i.e., right of bit 7 for first and left of  */
129
                /* bit 0 for last                                               */
130
                /* Note that we will only be using the left most bits of first  */
131
                /* and the right most bits of last.                             */
132
0
                switch (min_feature_size) {
133
0
                  case 2:
134
                    /* current feature size is 1, darken bit to the right       */
135
                    /* unless it is past the end of the byte in 'last'          */
136
0
                    if (fd > 0 && fw > 0) {
137
0
                        f |= bm(fw);
138
0
                        l |= bm(fw);
139
0
                    } else /* fd == 0 */
140
0
                        l |= 0x03;              /* darken to the left of lsb */
141
0
                    break;
142
0
                  case 3:
143
                    /* This case is more complicated -- we want to darken about */
144
                    /* the center if the current size is 1, else darken a bit   */
145
                    /* to the right, with the constraints of staying within the */
146
                    /* byte (bits 7::0). (fd-1) is to the right.                */
147
0
                    if ((fd < 7) && (fd > 1)) {
148
                        /* within byte, left and right */
149
                        /* darkening is referenced from 'fw' since that is the  */
150
                        /* white bit to the right of the 1 or 2 pixel wide area */
151
0
                        f |= bm(fw+2) | bm(fd-2);
152
0
                        l |= bm(fw+2) | bm(fd-2);
153
0
                    } else if (fd == 7) {
154
0
                        f |= 0xe0;              /* darken top three pixels */
155
0
                    } else {                    /* fd == 0 */
156
0
                        f |= 0x07;              /* darken bottom three pixels */
157
0
                        l |= 0x07;
158
0
                    }
159
0
                    break;
160
0
                  case 4:
161
                    /* like case 3, except we prefer to darken one to the left  */
162
                    /* and two to the right.                                    */
163
0
                    if ((fd < 7) && (fd > 1)) {
164
                        /* within byte, left and right */
165
0
                        f |= bm(fw+2) | bm(fd-1) | bm(fd-2);
166
0
                        l |= bm(fw+2) | bm(fd-1) | bm(fd-2);
167
0
                    } else if (fd == 7) {
168
                        /* darken high 4 bits */
169
0
                        f |= 0xfd;
170
0
                    } else {    /* fd <= 1 */
171
                        /* darken final 4 bits */
172
0
                        f |= 0x0f;
173
0
                        l |= 0x0f;
174
0
                    }
175
0
                    break;
176
0
                  default:      /* don't change the data (shouldn't be here) */
177
0
                    break;
178
0
                }
179
0
            }
180
0
            fd = next_zero(fd, f);      /* advance past this group of '1' bits  */
181
                                        /* we 'seek' again because data may     */
182
                                        /* have changed due to darkening right  */
183
0
        } while (fd >= 0);
184
        /* finished with the whole byte. Save the results */
185
0
        data->remap_first4[i] = f;
186
0
        data->remap_last4[i] = l;
187
0
    }
188
0
    for (i=0; i<65536; i++) {
189
0
        int d = i, fd = 16, fw;
190
191
0
        do {
192
0
            fd = next_one(fd, d);       /* value == -1 if past bit_0 */
193
0
            if (fd < 0)
194
0
                break;
195
0
            fw = next_zero(fd, d);      /* value == -1 if past bit_0 */
196
0
            if ((fd - fw) < min_feature_size) {
197
                /* darken (set to 0) bits needed depending on min_feature_size  */
198
                /* 'first' and 'last' are different because we need to expand   */
199
                /* within the bits, i.e., right of bit 7 for first and left of  */
200
                /* bit 0 for last                                               */
201
                /* Note that we will only be using the left most bits of first  */
202
                /* and the right most bits of last.                             */
203
0
                switch (min_feature_size) {
204
0
                  case 2:
205
                    /* current feature size is 1, darken bit to the right       */
206
0
                    if (fd > 0 && fw >= 0) {
207
0
                        d |= bm(fw);
208
0
                    } else /* fd == 0 */
209
0
                        d |= 0x0003;            /* two lsb's darkened   */
210
0
                    break;
211
0
                  case 3:
212
                    /* This case is more complicated -- we want to darken about */
213
                    /* the center but bias darkening to the right               */
214
0
                    if ((fd < 15) && (fd > 0)) {
215
                        /* within value, left and right */
216
0
                        d |= bm(fw+2) | bm(fd-1);
217
0
                    } else if (fd == 15) {
218
0
                        d |= 0xe000;            /* darken top three */
219
0
                    } else {    /* fd == 0 */
220
0
                        d |= 0x0007;            /* darken three lsb's */
221
0
                    }
222
0
                    break;
223
0
                  case 4:
224
                    /* like case 3, except we prefer to darken one to the left  */
225
                    /* and two to the right.                                    */
226
0
                    if ((fd < 15) && (fd > 1)) {
227
                        /* within byte, left and right */
228
0
                        d |= bm(fw+2) | bm(fd-1) | bm(fd-2);
229
0
                    } else if (fd == 15) {
230
0
                        d &= 0xf000;            /* darken top 4 pixels */
231
0
                    } else {    /* fd <= 1 */
232
0
                        d &= 0x000f;            /* darken last 4 pixels */
233
0
                    }
234
0
                    break;
235
0
                  default:      /* don't change the data (shouldn't be here) */
236
0
                    break;
237
0
                }
238
0
            }
239
0
            fd = next_zero(fd, d);      /* advance past this group of '1' bits  */
240
                                        /* we 'seek' again because data may     */
241
                                        /* have changed due to darkening right  */
242
0
        } while (fd >= 0);
243
        /* finished with the whole short. Save the byte in the middle */
244
0
        data->remap_mid8[i] = (d >> 4) & 0xff;
245
0
    }
246
0
    *min_feature_data = data;           /* give the caller our pointer */
247
0
    return 0;
248
0
}
249
250
int
251
min_feature_size_dnit(void *min_feature_data)
252
0
{
253
0
    min_feature_data_t *data = min_feature_data;
254
255
0
    if (data != NULL) {
256
0
        if (data->lines != NULL)
257
0
            gs_free(data->memory, data->lines, data->bytes_per_line,
258
0
                    2*data->min_size, "mem_feature_size(lines)");
259
0
        gs_free(data->memory, data, 1, sizeof(min_feature_data_t),
260
0
                "mem_feature_size(data)");
261
0
    }
262
0
    return 0;
263
0
}
264
265
/* Return number of byte available */
266
int
267
min_feature_size_process(byte *line, void *min_feature_data)
268
0
{
269
0
    min_feature_data_t *data = min_feature_data;
270
0
    ushort      d;
271
0
    byte        n, m, *btmp;
272
0
    int i, pad_bits = (8 - (data->width & 7)) & 7;      /* range 0 to 7 */
273
0
    int bytes_per_line = (data->width+7)/8, end = bytes_per_line - 2;
274
0
    int count_out = 0;          /* bytes_per_line if line is output */
275
276
0
    data->cur_line++;
277
0
    d = data->remap_first4[line[0]];    /* darken bits toward the right */
278
0
    d <<= 4;                            /* shift up to enter loop */
279
280
0
    for (i=0; i<=end; i++) {            /* stop short of 'end' */
281
0
        n = line[i+1];
282
0
        d |= n >> 4;
283
0
        m = data->remap_mid8[d];                /* middle byte with bits darkened */
284
0
        line[i] = m;
285
0
        d |= m << 4;
286
0
        d = ((d<<4) | n) << 4;
287
0
    }
288
    /* Final bits require alignment for the last 4 bits */
289
0
    d = (((line[i-1] << 8) | line[i]) >> pad_bits) & 0xff;
290
0
    m = data->remap_last4[d];
291
0
    line[i-1] |= m >> (8 - pad_bits);
292
0
    line[i] |= m << pad_bits;
293
294
    /* Now handle the vertical darkening */
295
    /* shift lines up by adjusting pointers */
296
0
    btmp = data->lines_prev[2*(data->min_size)-1];      /* oldest line */
297
0
    for (i=2*(data->min_size)-1; i>0; i--)
298
0
        data->lines_prev[i] = data->lines_prev[i-1];
299
0
    data->lines_prev[0] = btmp;
300
301
    /* Copy this input line into the most recent lines_prev: lines_prev[0] */
302
0
    memcpy(data->lines_prev[0], line, bytes_per_line);
303
304
0
    switch (data->min_size) {
305
0
      case 4:
306
                /**** TBI ****/
307
0
      case 3:
308
                /**** TBI ****/
309
310
0
      case 2:
311
0
        if (data->cur_line >= data->height - 1) {
312
0
            if (data->cur_line == data->height - 1) {
313
                /* end of page must darken next to last line*/
314
0
                for (i=0; i<bytes_per_line; i++)
315
0
                    line[i] = data->lines_prev[1][i] |= data->lines_prev[0][i];
316
0
            } else {
317
                /* very last line is unchanged, ignore input line in lines_prev[0] */
318
0
                for (i=0; i<bytes_per_line; i++)
319
0
                    line[i] = data->lines_prev[1][i];
320
0
            }
321
0
        } else {
322
            /* darken bits in lines_prev[1] */
323
            /* Since we are darkening in following lines, we only really need 3 lines */
324
0
            for (i=0; i<bytes_per_line; i++) {
325
0
                data->lines_prev[0][i] |= data->lines_prev[1][i] & ~(data->lines_prev[2][i]);
326
0
                line[i] = data->lines_prev[1][i];
327
0
            }
328
0
        }
329
0
        if (data->cur_line >= 1)
330
0
            count_out = bytes_per_line;
331
0
        break;
332
0
      default:
333
0
        break;
334
0
    }
335
0
    return count_out;
336
0
}
337
338
int
339
fax_adjusted_width(int width, int adjust_width)
340
24.1k
{
341
24.1k
    if (adjust_width <= 0)
342
16.7k
        return width;
343
7.34k
    if (adjust_width == 1) {
344
        /* Adjust the page width to a legal value for fax systems. */
345
7.34k
        if (width >= 1680 && width <= 1736)
346
            /* Adjust width for A4 paper. */
347
3.47k
            return 1728;
348
3.87k
        else if (width >= 2000 && width <= 2056)
349
            /* Adjust width for B4 paper. */
350
9
            return 2048;
351
3.86k
        else
352
3.86k
            return width;
353
7.34k
    } else {
354
        /* Adjust for the user-defined width. */
355
0
        return adjust_width;
356
0
    }
357
7.34k
}