/src/ghostpdl/base/simscale.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2024 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 mask interpolation filter */ |
18 | | |
19 | | #include "memory_.h" |
20 | | #include "gserrors.h" |
21 | | #include "strimpl.h" |
22 | | #include "sisparam.h" |
23 | | #include "simscale.h" |
24 | | #include "simscale_foo.h" |
25 | | |
26 | | gs_private_st_ptrs2(st_imscale_state, stream_imscale_state, |
27 | | "ImscaleDecode state", |
28 | | imscale_state_enum_ptrs, imscale_state_reloc_ptrs, |
29 | | window, dst); |
30 | | |
31 | | static void |
32 | | s_imscale_release(stream_state *st) |
33 | 0 | { |
34 | 0 | stream_imscale_state *const ss = (stream_imscale_state *) st; |
35 | 0 | gs_memory_t *mem = ss->memory; |
36 | |
|
37 | 0 | gs_free_object(mem, (void *)ss->window, "imscale window"); |
38 | 0 | ss->window = 0; |
39 | 0 | gs_free_object(mem, (void *)ss->dst, "imscale dst"); |
40 | 0 | ss->dst = 0; |
41 | 0 | } |
42 | | |
43 | | static int |
44 | | s_imscale_init(stream_state *st) |
45 | 0 | { |
46 | 0 | stream_imscale_state *const ss = (stream_imscale_state *) st; |
47 | 0 | gs_memory_t *mem = ss->memory; |
48 | 0 | int bytesin = (ss->params.WidthIn + 7) >> 3; |
49 | 0 | int bytesout = (ss->params.WidthIn + 1) >> 1; |
50 | |
|
51 | 0 | ss->src_y = 0; |
52 | 0 | ss->src_offset = 0; |
53 | 0 | ss->src_size = bytesin; |
54 | 0 | ss->src_line_padded = bytesin + 10; |
55 | |
|
56 | 0 | ss->dst_line_padded = bytesout + 10; /* to compensate for overshoots in zoom() */ |
57 | 0 | ss->dst_line_size = bytesout; |
58 | 0 | ss->dst_size = bytesout*4; |
59 | 0 | ss->dst_offset = ss->dst_size; |
60 | 0 | ss->dst_togo = (long long)ss->dst_size * ss->params.HeightIn; |
61 | 0 | ss->window = (byte *)gs_alloc_byte_array(mem, ss->src_line_padded, 5, "imscale window"); |
62 | 0 | ss->dst = (byte *)gs_alloc_bytes(mem, ss->dst_line_padded * 4, "imscale dst"); |
63 | 0 | if (ss->window == NULL || ss->dst == NULL) |
64 | 0 | return_error(gs_error_VMerror); |
65 | 0 | memset(ss->window, 0xff, ss->src_line_padded * 5); |
66 | 0 | return 0; |
67 | 0 | } |
68 | | |
69 | | static void |
70 | 0 | zoom_line(stream_imscale_state *ss) { |
71 | | /* src_y is 2 scan lines ahead of dst_y/4, although the latter counter is implicit. |
72 | | * For instance, during the 1st call to this function, src_y == 2, dst_y == 0. |
73 | | * (src_y + 3) % 5 == 0 and points to the beginning of the window. |
74 | | * (src_y + 4) % 5 == 1 and points to the next line. |
75 | | * (src_y ) % 5 == 2 and points to the last scanned line. |
76 | | * The next 2 lines in the window correspond to the blank lines above the first |
77 | | * line of the image. |
78 | | */ |
79 | 0 | unsigned char * const p0 = ss->window + (ss->src_line_padded * ((ss->src_y + 1) % 5)); |
80 | 0 | unsigned char * const p1 = ss->window + (ss->src_line_padded * ((ss->src_y + 2) % 5)); |
81 | 0 | unsigned char * const p2 = ss->window + (ss->src_line_padded * ((ss->src_y + 3) % 5)); |
82 | 0 | unsigned char * const p3 = ss->window + (ss->src_line_padded * ((ss->src_y + 4) % 5)); |
83 | 0 | unsigned char * const p4 = ss->window + (ss->src_line_padded * ((ss->src_y ) % 5)); |
84 | | |
85 | | /* Pointers to the lines in the destination buffer. */ |
86 | 0 | unsigned char * const dst0 = ss->dst; |
87 | 0 | unsigned char * const dst1 = ss->dst + ss->dst_line_padded; |
88 | 0 | unsigned char * const dst2 = ss->dst + 2*ss->dst_line_padded; |
89 | 0 | unsigned char * const dst3 = ss->dst + 3*ss->dst_line_padded; |
90 | 0 | unsigned int i; |
91 | | |
92 | | /* r0..r4 are shift registers that contain 5x5 bit matrix and serve |
93 | | * as buffers for byte-based access to memory. The registers have |
94 | | * the following structure and initial content. |
95 | | * r0: ........ ........ ......11 XXXxxxxx |
96 | | * r1: ........ ........ .11XXXxx xxx00000 |
97 | | * r2: ........ ....11XX Xxxxxxyy yyyyyy00 |
98 | | * r3: .......1 1XXXxxxx xyyyyyyy y0000000 |
99 | | * r3: ..11XXXx xxxxyyyy yyyyzzzz zzzz0000 |
100 | | * where |
101 | | * '.' denotes an unused bit |
102 | | * '1' denotes the initial blank bits that precede leading bits of every line |
103 | | * 'X' denotes leading bits of the 1st byte. '1' and 'X' belong to 5x5 bit matrix |
104 | | * 'x' denotes remaining bits of the 1st byte |
105 | | * 'y','z' denote the positions of the following bytes |
106 | | * '0' denotes the initial empty bits |
107 | | */ |
108 | 0 | uint32_t r0 = 0x300 | p0[0]; |
109 | 0 | uint32_t r1 = 0x6000 | p1[0] << 5; |
110 | 0 | uint32_t r2 = 0xc0000 | p2[0] << 10 | p2[1] << 2; |
111 | 0 | uint32_t r3 = 0x1800000 | p3[0] << 15 | p3[1] << 7; |
112 | 0 | uint32_t r4 = 0x30000000 | p4[0] << 20 | p4[1] << 12 | p4[2] << 4; |
113 | |
|
114 | 0 | #define ZOOM(r0, r1, r2, r3, r4) imscale_foo((r0 & 0x3e0) | (r1 & 0x7c00) | (r2 & 0xf8000) | (r3 & 0x1f00000) | (r4 & 0x3e000000)) |
115 | 0 | #define SHIFT(r0, r1, r2, r3, r4) r0 <<= 1, r1 <<= 1, r2 <<= 1, r3 <<= 1, r4 <<= 1 |
116 | 0 | #define LOAD(n,i) r##n |= p##n[i] |
117 | 0 | #define STORE(i) dst0[i] = out, dst1[i] = out >> 8, dst2[i] = out >> 16, dst3[i] = out >> 24 |
118 | | |
119 | | /* Possible improvement: buffer output in a 64-bit accumulator and write 16-bit chunks. */ |
120 | | /* Unfortunately in this case big- and little-endian systems need different code. */ |
121 | 0 | for (i=0; i < ss->src_size; i++) { |
122 | 0 | uint32_t out; /* 0 5 2 7 4 : number of empty bits in r0..r4 */ |
123 | |
|
124 | 0 | out = ZOOM(r0, r1, r2, r3, r4) << 4; |
125 | 0 | SHIFT(r0, r1, r2, r3, r4); /* 1 6 3 8 5 : every counter increases by 1 */ |
126 | 0 | LOAD(3, i+2); /* 1 6 3 0 5 : load r3 because it has 8 empty bits */ |
127 | 0 | out |= ZOOM(r0, r1, r2, r3, r4); |
128 | 0 | SHIFT(r0, r1, r2, r3, r4); /* 2 7 4 1 6 : and so on */ |
129 | 0 | STORE(4*i); |
130 | 0 | out = ZOOM(r0, r1, r2, r3, r4) << 4; |
131 | 0 | SHIFT(r0, r1, r2, r3, r4); /* 3 8 5 2 7 */ |
132 | 0 | LOAD(1, i+1); /* 3 0 5 2 7 */ |
133 | 0 | out |= ZOOM(r0, r1, r2, r3, r4); |
134 | 0 | SHIFT(r0, r1, r2, r3, r4); /* 4 1 6 3 8 */ |
135 | 0 | STORE(4*i+1); |
136 | 0 | LOAD(4, i+3); /* 4 1 6 3 0 */ |
137 | 0 | out = ZOOM(r0, r1, r2, r3, r4) << 4; |
138 | 0 | SHIFT(r0, r1, r2, r3, r4); /* 5 2 7 4 1 */ |
139 | 0 | out |= ZOOM(r0, r1, r2, r3, r4); |
140 | 0 | STORE(4*i+2); |
141 | 0 | SHIFT(r0, r1, r2, r3, r4); /* 6 3 8 5 2 */ |
142 | 0 | LOAD(2, i+2); /* 6 3 0 5 2 */ |
143 | 0 | out = ZOOM(r0, r1, r2, r3, r4) << 4; |
144 | 0 | SHIFT(r0, r1, r2, r3, r4); /* 7 4 1 6 3 */ |
145 | 0 | out |= ZOOM(r0, r1, r2, r3, r4); |
146 | 0 | STORE(4*i+3); |
147 | 0 | SHIFT(r0, r1, r2, r3, r4); /* 8 5 2 7 4 */ |
148 | 0 | LOAD(0, i+1); /* 0 5 2 7 4 */ |
149 | 0 | } |
150 | 0 | #undef ZOOM |
151 | 0 | #undef SHIFT |
152 | 0 | #undef LOAD |
153 | 0 | #undef STORE |
154 | 0 | } |
155 | | |
156 | | |
157 | | static int |
158 | | s_imscale_process(stream_state *st, stream_cursor_read *pr, |
159 | | stream_cursor_write *pw, bool last) |
160 | 0 | { |
161 | 0 | stream_imscale_state *const ss = (stream_imscale_state *) st; |
162 | |
|
163 | 0 | while (1) { |
164 | 0 | if (ss->dst_togo <= 0) |
165 | 0 | return EOFC; |
166 | | /* deliver data from dst buffer */ |
167 | 0 | if (ss->dst_offset < ss->dst_size) { |
168 | 0 | uint ncopy = min(pw->limit - pw->ptr, ss->dst_size - ss->dst_offset); |
169 | |
|
170 | 0 | if (ncopy == 0) |
171 | 0 | return 1; |
172 | 0 | ss->dst_togo -= ncopy; |
173 | |
|
174 | 0 | while (ncopy) { |
175 | 0 | int line = ss->dst_offset / ss->dst_line_size; |
176 | 0 | int offset = ss->dst_offset % ss->dst_line_size; |
177 | 0 | int linecopy = min(ncopy, ss->dst_line_size - offset); |
178 | |
|
179 | 0 | memcpy(pw->ptr + 1, (byte *)ss->dst + line * ss->dst_line_padded + offset, linecopy); |
180 | 0 | pw->ptr += linecopy; |
181 | 0 | ss->dst_offset += linecopy; |
182 | 0 | ncopy -= linecopy; |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | | /* output a row, if possible */ |
187 | 0 | if (ss->dst_offset == ss->dst_size && /* dst is empty */ |
188 | 0 | ss->src_offset == ss->src_size) { /* src is full */ |
189 | 0 | if (ss->src_y >= 2) { |
190 | 0 | zoom_line(ss); |
191 | 0 | ss->dst_offset = 0; |
192 | 0 | } |
193 | 0 | ss->src_offset = 0; |
194 | 0 | ss->src_y += 1; |
195 | 0 | } |
196 | | |
197 | | /* input into window */ |
198 | 0 | if (ss->src_offset < ss->src_size) { |
199 | 0 | uint rleft = pr->limit - pr->ptr; |
200 | 0 | uint ncopy = min(rleft, ss->src_size - ss->src_offset); |
201 | |
|
202 | 0 | if (ss->src_y >= ss->params.HeightIn) { |
203 | 0 | last = true; |
204 | 0 | ncopy = 0; |
205 | 0 | } |
206 | 0 | if (rleft == 0 && !last) |
207 | 0 | return 0; /* need more input */ |
208 | 0 | if (ncopy) { |
209 | 0 | memcpy(ss->window + ss->src_line_padded * (ss->src_y % 5) + ss->src_offset, pr->ptr + 1, ncopy); |
210 | 0 | ss->src_offset += ncopy; |
211 | 0 | pr->ptr += ncopy; |
212 | 0 | } else { |
213 | 0 | memset(ss->window + ss->src_line_padded * (ss->src_y % 5) + ss->src_offset, 0xff, ss->src_size - ss->src_offset); |
214 | 0 | ss->src_offset = ss->src_size; |
215 | 0 | } |
216 | 0 | } |
217 | 0 | } |
218 | 0 | } |
219 | | |
220 | | const stream_template s_imscale_template = { |
221 | | &st_imscale_state, s_imscale_init, s_imscale_process, 1, 1, |
222 | | s_imscale_release |
223 | | }; |