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