Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/base/sidscale.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
/* Special Image downsample scaling filters for dithered devices */
18
#include "math_.h"
19
#include "memory_.h"
20
#include "stdio_.h"
21
#include "gdebug.h"
22
#include "gxfixed.h"    /* for gxdda.h */
23
#include "gxdda.h"
24
#include "gxfrac.h"
25
#include "strimpl.h"
26
#include "sidscale.h"
27
28
/* Temporary intermediate values */
29
typedef byte PixelTmp;
30
31
#define minPixelTmp 0
32
#define maxPixelTmp 255
33
#define unitPixelTmp 255
34
35
/* Max of all pixel sizes */
36
#define maxSizeofPixel 2
37
38
/* Auxiliary structures. */
39
40
/* ImageSpecialDownScaleEncode / ImageSpecialDownScaleDecode */
41
typedef struct stream_ISpecialDownScale_state_s {
42
    /* The client sets the params values before initialization. */
43
    stream_image_scale_state_common;  /* = state_common + params */
44
    /* The init procedure sets the following. */
45
    int sizeofPixelIn;    /* bytes per input value, 1 or 2 */
46
    int sizeofPixelOut;   /* bytes per output value, 1 or 2 */
47
    void /*PixelIn */  *src;
48
    void /*PixelOut */ *dst;
49
    void /*PixelIn */  *tmp;
50
    gx_dda_int_t dda_x_init;  /* initial setting of dda_x */
51
    /* The following are updated dynamically. */
52
    int dst_x;
53
    uint dst_offset, dst_size;
54
    gx_dda_int_t dda_x;   /* DDA for dest X in current scan line */
55
    int src_y;
56
    uint src_offset, src_size;
57
    int dst_y;
58
    gx_dda_int_t dda_y;   /* DDA for dest Y */
59
} stream_ISpecialDownScale_state;
60
61
gs_private_st_ptrs3(st_ISpecialDownScale_state, stream_ISpecialDownScale_state,
62
    "ImageSpecialDownScaleEncode/Decode state",
63
    isdscale_state_enum_ptrs, isdscale_state_reloc_ptrs,
64
    dst, src, tmp);
65
66
/* Apply filter to downscale horizontally from src to tmp. */
67
static void
68
idownscale_x(void /* PixelIn */ * tmp, const void /* PixelIn */ *src, stream_ISpecialDownScale_state *const ss)
69
0
{
70
0
    int c, i;
71
0
    int Colors = ss->params.spp_interp;
72
0
    int WidthIn = ss->params.WidthIn;
73
0
    int prev_y;
74
0
    int cur_y;
75
0
    bool firstline;
76
0
    bool polarity_additive = ss->params.ColorPolarityAdditive;
77
78
0
    dda_previous_assign(ss->dda_y, prev_y);
79
0
    dda_next_assign(ss->dda_y, cur_y);
80
0
    firstline = prev_y != cur_y; /* at the start of a new group of lines */
81
    /* The following could be a macro, BUT macro's with control */
82
    /* are not good style and hard to debug */
83
0
    if (ss->sizeofPixelIn == 1) {
84
0
        for (c = 0; c < Colors; ++c) {
85
0
            byte *tp = (byte *)tmp + c;   /* destination */
86
0
            const byte *pp =  (const byte *)src + c;
87
88
0
            ss->dda_x = ss->dda_x_init;
89
0
            if_debug1m('W', ss->memory, "[W]idownscale_x color %d:", c);
90
91
0
            if (polarity_additive) {
92
0
                for ( i = 0; i < WidthIn; tp += Colors) {
93
0
                    int endx;
94
0
                    dda_next_assign(ss->dda_x, endx);
95
0
                    if (firstline || *pp < *tp)
96
0
                        *tp = *pp;
97
0
                    i++; pp += Colors;
98
0
                    while (i < endx) {
99
0
                       if (*pp < *tp)
100
0
                            *tp = *pp;
101
0
                       i++; pp += Colors;
102
0
                    }
103
0
                    if_debug1m('W', ss->memory, " %d", *tp);
104
0
                }
105
0
            } else {
106
0
                for ( i = 0; i < WidthIn; tp += Colors) {
107
0
                    int endx;
108
0
                    dda_next_assign(ss->dda_x, endx);
109
0
                    if (firstline || *pp > *tp)
110
0
                        *tp = *pp;
111
0
                    i++; pp += Colors;
112
0
                    while (i < endx) {
113
0
                        if (*pp > *tp)
114
0
                            *tp = *pp;
115
0
                        i++; pp += Colors;
116
0
                    }
117
0
                    if_debug1m('W', ss->memory, " %d", *tp);
118
0
                }
119
0
            }
120
0
            if_debug0m('W', ss->memory, "\n");
121
0
        }
122
0
    } else {   /* sizeofPixelIn == 2 */
123
0
        for (c = 0; c < Colors; ++c) {
124
0
            bits16 *tp = (bits16 *)tmp + c;   /* destination */
125
0
            const bits16 *pp =  (const bits16 *)src + c;
126
127
0
            ss->dda_x = ss->dda_x_init;
128
0
            if_debug1m('W', ss->memory, "[W]idownscale_x color %d:", c);
129
130
0
            if (polarity_additive) {
131
0
                for ( i = 0; i < WidthIn; tp += Colors) {
132
0
                    int endx;
133
0
                    dda_next_assign(ss->dda_x,endx);
134
0
                    if (firstline || *pp < *tp)
135
0
                        *tp = *pp;
136
0
                    i++; pp += Colors;
137
0
                    while (i < endx) {
138
0
                        if (*pp < *tp)
139
0
                            *tp = *pp;
140
0
                        i++; pp += Colors;
141
0
                    }
142
0
                    if_debug1m('W', ss->memory, " %d", *tp);
143
0
                }
144
0
            } else {
145
0
                for ( i = 0; i < WidthIn; tp += Colors) {
146
0
                    int endx;
147
0
                    dda_next_assign(ss->dda_x,endx);
148
0
                    if (firstline || *pp > *tp)
149
0
                        *tp = *pp;
150
0
                    i++; pp += Colors;
151
0
                    while (i < endx) {
152
0
                        if (*pp > *tp)
153
0
                            *tp = *pp;
154
0
                        i++; pp += Colors;
155
0
                    }
156
0
                    if_debug1m('W', ss->memory, " %d", *tp);
157
0
                }
158
0
            }
159
0
            if_debug0m('W', ss->memory, "\n");
160
0
        }
161
0
    }
162
0
}
163
164
/*
165
 * Copy from tmp to dst, taking into account PixelOut vs. PixelIn sizes
166
 * We do the conversion from PixelIn to PixelOut here (if needed)
167
 * since if the Y is scaled down we will convert less often.
168
 * This is simpler because we can treat all columns identically
169
 * without regard to the number of samples per pixel.
170
 */
171
static void
172
idownscale_y(void /*PixelOut */ *dst, const void /* PixelIn */ *tmp,
173
             stream_ISpecialDownScale_state *const ss)
174
0
{
175
0
    int kn = ss->params.WidthOut * ss->params.spp_interp;
176
0
    int kc;
177
0
    float scale = (float) ss->params.MaxValueOut/255.0;
178
179
0
    if_debug0m('W', ss->memory, "[W]idownscale_y: ");
180
181
0
    if (ss->sizeofPixelOut == 1) {
182
0
        if (ss->sizeofPixelIn == 1) {
183
0
            const byte *pp = (byte *)tmp;
184
185
0
            for ( kc = 0; kc < kn; ++kc, pp++ ) {
186
0
                if_debug1m('W', ss->memory, " %d", *pp);
187
0
                ((byte *)dst)[kc] = *pp;
188
0
            }
189
0
        } else { /* sizeofPixelIn == 2 */
190
0
            const bits16 *pp = (bits16 *)tmp;
191
192
0
            for ( kc = 0; kc < kn; ++kc, pp++ ) {
193
0
                if_debug1m('W', ss->memory, " %d", *pp);
194
0
                ((byte *)dst)[kc] = frac2byte(*pp);
195
0
            }
196
0
        }
197
0
    } else {   /* sizeofPixelOut == 2 */
198
0
        if (ss->sizeofPixelIn == 1) {
199
0
            const byte *pp = (byte *)tmp;
200
201
0
            for ( kc = 0; kc < kn; ++kc, pp++ ) {
202
0
                if_debug1m('W', ss->memory, " %d", *pp);
203
0
                ((bits16 *)dst)[kc] = (bits16)((*pp)*scale);
204
0
            }
205
0
        } else { /* sizeofPixelIn == 2 */
206
0
            const bits16 *pp = (bits16 *)tmp;
207
208
0
            for ( kc = 0; kc < kn; ++kc, pp++ ) {
209
0
                if_debug1m('W', ss->memory, " %d", *pp);
210
0
                ((bits16 *)dst)[kc] = *pp;
211
0
            }
212
0
        }
213
0
    }
214
0
    if_debug0m('W', ss->memory, "n");
215
0
}
216
217
/* ------ Stream implementation ------ */
218
219
/* Forward references */
220
static void s_ISpecialDownScale_release(stream_state * st);
221
222
/* Set default parameter values (actually, just clear pointers). */
223
static void
224
s_ISpecialDownScale_set_defaults(stream_state * st)
225
0
{
226
0
    stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
227
228
0
    ss->src = 0;
229
0
    ss->dst = 0;
230
0
    ss->tmp = 0;
231
0
}
232
233
/* Initialize the filter. */
234
static int
235
s_ISpecialDownScale_init(stream_state * st)
236
0
{
237
0
    stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
238
0
    gs_memory_t *mem = ss->memory;
239
240
0
    ss->sizeofPixelIn = ss->params.BitsPerComponentIn / 8;
241
0
    ss->sizeofPixelOut = ss->params.BitsPerComponentOut / 8;
242
243
0
    ss->src_size =
244
0
        ss->params.WidthIn * ss->sizeofPixelIn * ss->params.spp_interp;
245
0
    ss->dst_size =
246
0
        ss->params.WidthOut * ss->sizeofPixelOut * ss->params.spp_interp;
247
248
    /* Initialize destination DDAs. */
249
0
    ss->dst_x = 0;
250
0
    ss->src_offset = ss->dst_offset = 0;
251
0
    dda_init(ss->dda_x, 0, ss->params.WidthIn, ss->params.WidthOut);
252
0
    ss->dda_x_init = ss->dda_x;
253
0
    ss->src_y = ss->dst_y = 0;
254
0
    dda_init(ss->dda_y, 0, ss->params.HeightOut, ss->params.HeightIn);
255
256
    /* create intermediate image to hold horizontal zoom */
257
0
    ss->tmp =
258
0
        gs_alloc_byte_array(mem, (size_t)ss->params.WidthOut * ss->params.spp_interp,
259
0
                            ss->sizeofPixelIn, "image_scale tmp");
260
    /* Allocate buffers for 1 row of source and destination. */
261
0
    ss->dst =
262
0
        gs_alloc_byte_array(mem, (size_t)ss->params.WidthOut * ss->params.spp_interp,
263
0
                            ss->sizeofPixelOut, "image_scale dst");
264
0
    ss->src =
265
0
        gs_alloc_byte_array(mem, (size_t)ss->params.WidthIn * ss->params.spp_interp,
266
0
                            ss->sizeofPixelIn, "image_scale src");
267
0
    if (ss->tmp == 0 || ss->dst == 0 || ss->src == 0) {
268
0
        s_ISpecialDownScale_release(st);
269
0
        return ERRC;
270
/****** WRONG ******/
271
0
    }
272
273
0
    return 0;
274
275
0
}
276
277
/* Process a buffer.  Note that this handles Encode and Decode identically. */
278
static int
279
s_ISpecialDownScale_process(stream_state * st, stream_cursor_read * pr,
280
                 stream_cursor_write * pw, bool last)
281
0
{
282
0
    stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
283
0
    uint cur_y = dda_current(ss->dda_y);
284
285
    /* Check whether we need to deliver any output. */
286
287
0
top:
288
0
    ss->params.Active = (ss->src_y >= ss->params.TopMarginIn &&
289
0
                         ss->src_y <= ss->params.TopMarginIn + ss->params.PatchHeightIn);
290
291
0
    if (cur_y > ss->dst_y) {
292
        /* Deliver some or all of the current scaled row. */
293
        /* to generate a vertically scaled output row. */
294
0
        uint wleft = pw->limit - pw->ptr;
295
296
0
        if (ss->dst_y == ss->params.HeightOut)
297
0
            return EOFC;
298
0
        if (wleft == 0)
299
0
            return 1;
300
0
        if (ss->dst_offset == 0) {
301
0
            byte *row;
302
303
0
            if (wleft >= ss->dst_size) { /* We can scale the row directly into the output. */
304
0
                row = pw->ptr + 1;
305
0
                pw->ptr += ss->dst_size;
306
0
            } else {   /* We'll have to buffer the row. */
307
0
                row = ss->dst;
308
0
            }
309
            /* Apply filter to zoom vertically from tmp to dst. */
310
0
            if (ss->params.Active)
311
0
                idownscale_y(row, ss->tmp, ss);
312
            /* Idiotic C coercion rules allow T* and void* to be */
313
            /* inter-assigned freely, but not compared! */
314
0
            if ((void *)row != ss->dst)   /* no buffering */
315
0
                goto adv;
316
0
        } {     /* We're delivering a buffered output row. */
317
0
            uint wcount = ss->dst_size - ss->dst_offset;
318
0
            uint ncopy = min(wleft, wcount);
319
320
0
            if (ss->params.Active)
321
0
                memcpy(pw->ptr + 1, (byte *) ss->dst + ss->dst_offset, ncopy);
322
0
            pw->ptr += ncopy;
323
0
            ss->dst_offset += ncopy;
324
0
            if (ncopy != wcount)
325
0
                return 1;
326
0
            ss->dst_offset = 0;
327
0
        }
328
        /* Advance to the next output row. */
329
0
adv:  ++(ss->dst_y);
330
0
    }
331
332
    /* Read input data and scale horizontally into tmp. */
333
334
0
    {
335
0
        uint rleft = pr->limit - pr->ptr;
336
0
        uint rcount = ss->src_size - ss->src_offset;
337
338
0
        if (rleft == 0)
339
0
            return 0;   /* need more input */
340
0
        if (ss->src_y >= ss->params.HeightIn)
341
0
            return ERRC;
342
0
        if (rleft >= rcount) { /* We're going to fill up a row. */
343
0
            const byte *row;
344
345
0
            if (ss->src_offset == 0) { /* We have a complete row.  Read the data */
346
                /* directly from the input. */
347
0
                row = pr->ptr + 1;
348
0
            } else {   /* We're buffering a row in src. */
349
0
                row = ss->src;
350
0
                if (ss->params.Active)
351
0
                    memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1,
352
0
                           rcount);
353
0
                ss->src_offset = 0;
354
0
            }
355
            /* Apply filter to zoom horizontally from src to tmp. */
356
0
            if_debug2m('w', ss->memory, "[w]idownscale_x y = %d to tmp row %d\n",
357
0
                       ss->src_y, (ss->src_y % MAX_ISCALE_SUPPORT));
358
0
            if (ss->params.Active)
359
0
                idownscale_x(ss->tmp, row, ss);
360
0
            pr->ptr += rcount;
361
0
            ++(ss->src_y);
362
0
            dda_next_assign(ss->dda_y,cur_y);
363
0
            goto top;
364
0
        } else {   /* We don't have a complete row.  Copy data to src buffer. */
365
0
            if (ss->params.Active)
366
0
                memcpy((byte *) ss->src + ss->src_offset, pr->ptr + 1, rleft);
367
0
            ss->src_offset += rleft;
368
0
            pr->ptr += rleft;
369
0
            return 0;
370
0
        }
371
0
    }
372
0
}
373
374
/* Release the filter's storage. */
375
static void
376
s_ISpecialDownScale_release(stream_state * st)
377
0
{
378
0
    stream_ISpecialDownScale_state *const ss = (stream_ISpecialDownScale_state *) st;
379
0
    gs_memory_t *mem = ss->memory;
380
381
0
    gs_free_object(mem, (void *)ss->src, "image_scale src");  /* no longer const */
382
0
    ss->src = 0;
383
0
    gs_free_object(mem, ss->dst, "image_scale dst");
384
0
    ss->dst = 0;
385
0
    gs_free_object(mem, ss->tmp, "image_scale tmp");
386
0
    ss->tmp = 0;
387
0
}
388
389
/* Stream template */
390
const stream_template s_ISpecialDownScale_template = {
391
    &st_ISpecialDownScale_state, s_ISpecialDownScale_init, s_ISpecialDownScale_process, 1, 1,
392
    s_ISpecialDownScale_release, s_ISpecialDownScale_set_defaults
393
};