/src/ghostpdl/base/gxclbits.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 | | /* Halftone and bitmap writing for command lists */ |
18 | | #include "memory_.h" |
19 | | #include "gx.h" |
20 | | #include "gpcheck.h" |
21 | | #include "gserrors.h" |
22 | | #include "gsbitops.h" |
23 | | #include "gxdevice.h" |
24 | | #include "gxdevmem.h" /* must precede gxcldev.h */ |
25 | | #include "gdevprn.h" /* for BLS_force_memory */ |
26 | | #include "gxcldev.h" |
27 | | #include "gxfmap.h" |
28 | | #include "gxpcolor.h" /* for gx_device_is_pattern_clist */ |
29 | | |
30 | | /* |
31 | | * Define when, if ever, to write character bitmaps in all bands. |
32 | | * Set this to: |
33 | | * 0 to always write in all bands; |
34 | | * N to write in all bands when the character has been seen in N+1 |
35 | | * bands on a page; |
36 | | * max_ushort to never write in all bands. |
37 | | */ |
38 | 17.1M | #define CHAR_ALL_BANDS_COUNT max_ushort |
39 | | |
40 | | /* ------ Writing ------ */ |
41 | | |
42 | | /* |
43 | | * Determine the (possibly unpadded) width in bytes for writing a bitmap, |
44 | | * per the algorithm in gxcldev.h. If compression_mask has any of the |
45 | | * cmd_mask_compress_any bits set, we assume the bitmap will be compressed. |
46 | | * Return the total size of the bitmap. |
47 | | */ |
48 | | uint |
49 | | clist_bitmap_bytes(uint width_bits, uint height, int compression_mask, |
50 | | uint * width_bytes, uint * raster) |
51 | 66.8M | { |
52 | 66.8M | uint full_raster = *raster = bitmap_raster(width_bits); |
53 | 66.8M | uint short_raster = (width_bits + 7) >> 3; |
54 | 66.8M | uint width_bytes_last; |
55 | | |
56 | 66.8M | if (compression_mask & cmd_mask_compress_any) |
57 | 17.9M | *width_bytes = width_bytes_last = full_raster; |
58 | 48.9M | else if (short_raster <= cmd_max_short_width_bytes || |
59 | 48.9M | height <= 1 || |
60 | 48.9M | (compression_mask & decompress_spread) != 0 |
61 | 48.9M | ) |
62 | 48.5M | *width_bytes = width_bytes_last = short_raster; |
63 | 418k | else |
64 | 418k | *width_bytes = full_raster, width_bytes_last = short_raster; |
65 | 66.8M | return |
66 | 66.8M | (height == 0 ? 0 : *width_bytes * (height - 1) + width_bytes_last); |
67 | 66.8M | } |
68 | | |
69 | | /* |
70 | | * Compress a bitmap, skipping extra padding bytes at the end of each row if |
71 | | * necessary. We require height >= 1, raster >= bitmap_raster(width_bits). |
72 | | */ |
73 | | static int |
74 | | go_process(stream_state * st, stream_cursor_read *pr, stream_cursor_write *pw, bool end) |
75 | 189M | { |
76 | 189M | int status = (*st->templat->process) (st, pr, pw, end); |
77 | 189M | if (status) |
78 | 1.15M | return status; |
79 | | /* We don't attempt to handle compressors that */ |
80 | | /* require >1 input byte to make progress. */ |
81 | 188M | if (pr->ptr != pr->limit) |
82 | 0 | return -1; |
83 | 188M | return 0; |
84 | 188M | } |
85 | | static byte zeros[1<<align_bitmap_mod] = { 0, }; |
86 | | static int |
87 | | cmd_compress_bitmap(stream_state * st, const byte * data, uint width_bits, |
88 | | uint raster, uint height, stream_cursor_write * pw) |
89 | 3.16M | { |
90 | 3.16M | uint width_bytes = bitmap_raster(width_bits); |
91 | 3.16M | int status = 0; |
92 | 3.16M | stream_cursor_read r; |
93 | 3.16M | stream_cursor_read r2; |
94 | 3.16M | uint whole_bytes = width_bits>>3; |
95 | 3.16M | uint mask = (0xff00>>(width_bits & 7)) & 0xff; |
96 | 3.16M | uint padding = width_bytes - ((width_bits+7)>>3); |
97 | | |
98 | 3.16M | if (raster == whole_bytes) { |
99 | 11.0k | stream_cursor_read_init(&r, data, raster * (size_t)height); |
100 | 11.0k | status = (*st->templat->process) (st, &r, pw, true); |
101 | 3.15M | } else { /* Compress row-by-row. */ |
102 | 3.15M | uint y; |
103 | | |
104 | 3.15M | stream_cursor_read_init(&r, data, whole_bytes); |
105 | | |
106 | 65.7M | for (y = height-1; (r.limit = r.ptr + whole_bytes), y > 0; y--) { |
107 | 62.8M | status = go_process(st, &r, pw, false); |
108 | 62.8M | if (status) |
109 | 101k | break; |
110 | 62.7M | if (mask) { |
111 | 57.1M | byte b = r.ptr[1] & mask; |
112 | | |
113 | 57.1M | stream_cursor_read_init(&r2, &b, 1); |
114 | 57.1M | status = go_process(st, &r2, pw, false); |
115 | 57.1M | if (status) |
116 | 2.28k | break; |
117 | 57.1M | } |
118 | 62.7M | if (padding) { |
119 | 61.2M | stream_cursor_read_init(&r2, zeros, padding); |
120 | 61.2M | status = go_process(st, &r2, pw, false); |
121 | 61.2M | if (status) |
122 | 89.1k | break; |
123 | 61.2M | } |
124 | 62.6M | r.ptr += (int)(raster - whole_bytes); |
125 | 62.6M | } |
126 | 3.15M | if (status == 0) { |
127 | 2.96M | status = go_process(st, &r, pw, padding == 0 && mask == 0); |
128 | 2.96M | if (status == 0 && mask) { |
129 | 2.55M | byte b = r.ptr[1] & mask; |
130 | | |
131 | 2.55M | stream_cursor_read_init(&r2, &b, 1); |
132 | 2.55M | status = go_process(st, &r2, pw, padding == 0); |
133 | 2.55M | } |
134 | 2.96M | if (status == 0 && padding) { |
135 | 2.71M | stream_cursor_read_init(&r2, zeros, padding); |
136 | 2.71M | status = go_process(st, &r2, pw, true); |
137 | 2.71M | } |
138 | 2.96M | } |
139 | 3.15M | } |
140 | 3.16M | if (st->templat->release) |
141 | 2.29M | (*st->templat->release) (st); |
142 | 3.16M | return status; |
143 | 3.16M | } |
144 | | |
145 | | /* |
146 | | * Put a bitmap in the buffer, compressing if appropriate. |
147 | | * pcls == 0 means put the bitmap in all bands. |
148 | | * Return <0 if error, otherwise the compression method. |
149 | | * A return value of gs_error_limitcheck means that the bitmap was too big |
150 | | * to fit in the command reading buffer. |
151 | | * This won't happen if the compression_mask has allow_large_bitmap set. |
152 | | * Note that this leaves room for the command and initial arguments, |
153 | | * but doesn't fill them in. |
154 | | */ |
155 | | int |
156 | | cmd_put_bits(gx_device_clist_writer * cldev, gx_clist_state * pcls, |
157 | | const byte * data, uint width_bits, uint height, uint raster, int op_size, |
158 | | int compression_mask, byte ** pdp, uint * psize) |
159 | 23.0M | { |
160 | 23.0M | uint short_raster, full_raster; |
161 | 23.0M | uint short_size = clist_bitmap_bytes(width_bits, height, |
162 | 23.0M | compression_mask & ~cmd_mask_compress_any, |
163 | 23.0M | &short_raster, &full_raster); |
164 | 23.0M | uint uncompressed_raster; |
165 | 23.0M | uint uncompressed_size = clist_bitmap_bytes(width_bits, height, compression_mask, |
166 | 23.0M | &uncompressed_raster, &full_raster); |
167 | 23.0M | uint max_size = (compression_mask & allow_large_bitmap) ? 0x7fffffff : |
168 | 23.0M | data_bits_size - op_size; |
169 | 23.0M | gs_memory_t *mem = cldev->memory; |
170 | 23.0M | byte *dp; |
171 | 23.0M | int compress = 0; |
172 | 23.0M | int code; |
173 | | |
174 | | /* |
175 | | * See if compressing the bits is possible and worthwhile. |
176 | | * Currently we can't compress if the compressed data won't fit in |
177 | | * the command reading buffer, or if the decompressed data won't fit |
178 | | * in the buffer and decompress_elsewhere isn't set. |
179 | | */ |
180 | 23.0M | if (short_size >= 50 && |
181 | 23.0M | (compression_mask & ((1<<cmd_compress_rle) | (1<<cmd_compress_cfe))) != 0 && |
182 | 23.0M | (uncompressed_size <= max_size || |
183 | 3.17M | (compression_mask & decompress_elsewhere) != 0) |
184 | 23.0M | ) { |
185 | 3.16M | union ss_ { |
186 | 3.16M | stream_state ss; |
187 | 3.16M | stream_CFE_state cf; |
188 | 3.16M | stream_RLE_state rl; |
189 | 3.16M | } sstate; |
190 | 3.16M | int try_size = op_size + min(uncompressed_size, max_size); |
191 | | |
192 | 3.16M | *psize = try_size; |
193 | 3.16M | code = (pcls != 0 ? |
194 | 3.16M | set_cmd_put_op(&dp, cldev, pcls, 0, try_size) : |
195 | 3.16M | set_cmd_put_all_op(&dp, cldev, 0, try_size)); |
196 | 3.16M | if (code < 0) |
197 | 0 | return code; |
198 | 3.16M | cmd_uncount_op(0, try_size); |
199 | | /* |
200 | | * Note that we currently keep all the padding if we are |
201 | | * compressing. This is ridiculous, but it's too hard to |
202 | | * change right now. |
203 | | */ |
204 | 3.16M | if (compression_mask & (1 << cmd_compress_cfe)) { |
205 | | /* Try CCITTFax compression. */ |
206 | 2.29M | clist_cfe_init(&sstate.cf, |
207 | 2.29M | uncompressed_raster << 3 /*width_bits*/, |
208 | 2.29M | mem); |
209 | 2.29M | compress = cmd_compress_cfe; |
210 | 2.29M | } else if (compression_mask & (1 << cmd_compress_rle)) { |
211 | | /* Try RLE compression. */ |
212 | 871k | clist_rle_init(&sstate.rl); |
213 | 871k | compress = cmd_compress_rle; |
214 | 871k | } |
215 | 3.16M | if (compress) { |
216 | 3.16M | byte *wbase = dp + (op_size - 1); |
217 | 3.16M | stream_cursor_write w; |
218 | | |
219 | | /* |
220 | | * We can give up on compressing if we generate too much |
221 | | * output to fit in the command reading buffer, or too |
222 | | * much to make compression worthwhile. |
223 | | */ |
224 | 3.16M | uint wmax = min(uncompressed_size, max_size); |
225 | 3.16M | int status; |
226 | | |
227 | 3.16M | w.ptr = wbase; |
228 | 3.16M | w.limit = w.ptr + min(wmax, short_size >> 1); |
229 | 3.16M | status = cmd_compress_bitmap((stream_state *) & sstate, data, |
230 | 3.16M | width_bits, /* was uncompressed_raster << 3, but this overruns. */ |
231 | 3.16M | raster, height, &w); |
232 | 3.16M | if (status == 0) { /* Use compressed representation. */ |
233 | 2.00M | uint wcount = w.ptr - wbase; |
234 | | |
235 | 2.00M | cmd_shorten_list_op(cldev, |
236 | 2.00M | (pcls ? &pcls->list : cldev->band_range_list), |
237 | 2.00M | try_size - (op_size + wcount)); |
238 | 2.00M | *psize = op_size + wcount; |
239 | 2.00M | goto out; |
240 | 2.00M | } |
241 | 3.16M | } |
242 | 1.15M | if (uncompressed_size > max_size) { |
243 | | /* Shorten to zero, erasing the operation altogether */ |
244 | 0 | if_debug1m('L', cldev->memory, |
245 | 0 | "[L]Uncompressed bits %u too large for buffer\n", |
246 | 0 | uncompressed_size); |
247 | 0 | cmd_shorten_list_op(cldev, |
248 | 0 | (pcls ? &pcls->list : cldev->band_range_list), |
249 | 0 | try_size); |
250 | 0 | return_error(gs_error_limitcheck); |
251 | 0 | } |
252 | 1.15M | if (uncompressed_size != short_size) { |
253 | 1.13M | if_debug2m('L',cldev->memory,"[L]Shortening bits from %u to %u\n", |
254 | 1.13M | try_size, op_size + short_size); |
255 | 1.13M | cmd_shorten_list_op(cldev, |
256 | 1.13M | (pcls ? &pcls->list : cldev->band_range_list), |
257 | 1.13M | try_size - (op_size + short_size)); |
258 | 1.13M | *psize = op_size + short_size; |
259 | 1.13M | } |
260 | 1.15M | compress = 0; |
261 | 19.8M | } else if (uncompressed_size > max_size) |
262 | 6.71k | return_error(gs_error_limitcheck); |
263 | 19.8M | else { |
264 | 19.8M | *psize = op_size + short_size; |
265 | 19.8M | code = (pcls != 0 ? |
266 | 19.8M | set_cmd_put_op(&dp, cldev, pcls, 0, *psize) : |
267 | 19.8M | set_cmd_put_all_op(&dp, cldev, 0, *psize)); |
268 | 19.8M | if (code < 0) |
269 | 0 | return code; |
270 | 19.8M | cmd_uncount_op(0, *psize); |
271 | 19.8M | } |
272 | 21.0M | if ((compression_mask & (1 << cmd_compress_const)) && |
273 | 21.0M | (code = bytes_rectangle_is_const(data, raster, uncompressed_raster << 3, height)) >= 0) { |
274 | 0 | cmd_shorten_list_op(cldev, |
275 | 0 | (pcls ? &pcls->list : cldev->band_range_list), |
276 | 0 | *psize - (op_size + 1)); |
277 | 0 | *psize = op_size + 1; |
278 | 0 | dp[op_size] = code; |
279 | 0 | compress = cmd_compress_const; |
280 | 21.0M | } else { |
281 | 21.0M | uint copy_bytes = (width_bits + 7) >> 3; |
282 | 21.0M | bytes_copy_rectangle_zero_padding_last_short( |
283 | 21.0M | dp + op_size, short_raster, data, raster, |
284 | 21.0M | copy_bytes, height); |
285 | 21.0M | } |
286 | 23.0M | out: |
287 | 23.0M | *pdp = dp; |
288 | 23.0M | return compress; |
289 | 21.0M | } |
290 | | |
291 | | /* Add a command to set the tile size and depth. */ |
292 | | static uint |
293 | | cmd_size_tile_params(const gx_strip_bitmap * tile, bool for_pattern) |
294 | 5.05k | { |
295 | 5.05k | return 2 + (for_pattern ? cmd_size_w(tile->id) : 0) + |
296 | 5.05k | cmd_size_w(tile->rep_width) + cmd_size_w(tile->rep_height) + |
297 | 5.05k | (tile->rep_width == tile->size.x ? 0 : |
298 | 5.05k | cmd_size_w(tile->size.x / tile->rep_width)) + |
299 | 5.05k | (tile->rep_height == tile->size.y ? 0 : |
300 | 5.05k | cmd_size_w(tile->size.y / tile->rep_height)) + |
301 | 5.05k | (tile->rep_shift == 0 ? 0 : cmd_size_w(tile->rep_shift)) + |
302 | 5.05k | (tile->num_planes == 1 ? 0 : 1); |
303 | 5.05k | } |
304 | | static void |
305 | | cmd_store_tile_params(byte * dp, const gx_strip_bitmap * tile, int depth, |
306 | | uint csize, bool for_pattern, const gs_memory_t *mem) |
307 | 5.05k | { |
308 | 5.05k | byte *p = dp + 2; |
309 | 5.05k | byte bd = cmd_depth_to_code(depth); |
310 | | |
311 | 5.05k | *dp = cmd_count_op(cmd_opv_set_tile_size, csize, mem); |
312 | 5.05k | if (for_pattern) |
313 | 0 | p = cmd_put_w(tile->id, p); |
314 | 5.05k | p = cmd_put_w(tile->rep_width, p); |
315 | 5.05k | p = cmd_put_w(tile->rep_height, p); |
316 | 5.05k | if (tile->rep_width != tile->size.x) { |
317 | 4.99k | p = cmd_put_w(tile->size.x / tile->rep_width, p); |
318 | 4.99k | bd |= 0x20; |
319 | 4.99k | } |
320 | 5.05k | if (tile->rep_height != tile->size.y) { |
321 | 1.63k | p = cmd_put_w(tile->size.y / tile->rep_height, p); |
322 | 1.63k | bd |= 0x40; |
323 | 1.63k | } |
324 | 5.05k | if (tile->rep_shift != 0) { |
325 | 0 | p = cmd_put_w(tile->rep_shift, p); |
326 | 0 | bd |= 0x80; |
327 | 0 | } |
328 | 5.05k | if (tile->num_planes != 1) { |
329 | 0 | *p++ = (byte)tile->num_planes; |
330 | 0 | bd |= 0x10; |
331 | 0 | } |
332 | 5.05k | dp[1] = bd; |
333 | 5.05k | } |
334 | | |
335 | | /* Add a command to set the tile index. */ |
336 | | /* This is a relatively high-frequency operation, so we declare it `inline'. */ |
337 | | static inline int |
338 | | cmd_put_tile_index(gx_device_clist_writer *cldev, gx_clist_state *pcls, |
339 | | uint indx) |
340 | 32.2M | { |
341 | 32.2M | int idelta = indx - pcls->tile_index + 8; |
342 | 32.2M | byte *dp; |
343 | 32.2M | int code; |
344 | | |
345 | 32.2M | if (!(idelta & ~15)) { |
346 | 10.5M | code = set_cmd_put_op(&dp, cldev, pcls, |
347 | 10.5M | cmd_op_delta_tile_index + idelta, 1); |
348 | 10.5M | if (code < 0) |
349 | 0 | return code; |
350 | 21.7M | } else { |
351 | 21.7M | code = set_cmd_put_op(&dp, cldev, pcls, |
352 | 21.7M | cmd_op_set_tile_index + (indx >> 8), 2); |
353 | 21.7M | if (code < 0) |
354 | 0 | return code; |
355 | 21.7M | dp[1] = indx & 0xff; |
356 | 21.7M | } |
357 | 32.2M | if_debug2m('L', cldev->memory, "[L]writing index=%u, offset=%lu\n", |
358 | 32.2M | indx, cldev->tile_table[indx].offset); |
359 | 32.2M | return 0; |
360 | 32.2M | } |
361 | | |
362 | | /* If necessary, write out data for a single color map. */ |
363 | | int |
364 | | cmd_put_color_map(gx_device_clist_writer * cldev, cmd_map_index map_index, |
365 | | int comp_num, const gx_transfer_map * map, gs_id * pid) |
366 | 437k | { |
367 | 437k | byte *dp; |
368 | 437k | int code; |
369 | | |
370 | 437k | if (map == 0) { |
371 | 0 | if (pid && *pid == gs_no_id) |
372 | 0 | return 0; /* no need to write */ |
373 | 0 | code = set_cmd_put_all_op(&dp, cldev, cmd_opv_set_misc, 3); |
374 | 0 | if (code < 0) |
375 | 0 | return code; |
376 | 0 | dp[1] = cmd_set_misc_map + (cmd_map_none << 4) + map_index; |
377 | 0 | dp[2] = comp_num; |
378 | 0 | if (pid) |
379 | 0 | *pid = gs_no_id; |
380 | 437k | } else { |
381 | 437k | if (pid && map->id == *pid) |
382 | 375k | return 0; /* no need to write */ |
383 | 61.4k | if (map->proc == gs_identity_transfer) { |
384 | 10.5k | code = set_cmd_put_all_op(&dp, cldev, cmd_opv_set_misc, 3); |
385 | 10.5k | if (code < 0) |
386 | 0 | return code; |
387 | 10.5k | dp[1] = cmd_set_misc_map + (cmd_map_identity << 4) + map_index; |
388 | 10.5k | dp[2] = comp_num; |
389 | 50.8k | } else { |
390 | 50.8k | code = set_cmd_put_all_op(&dp, cldev, cmd_opv_set_misc, |
391 | 50.8k | 3 + sizeof(map->values)); |
392 | 50.8k | if (code < 0) |
393 | 0 | return code; |
394 | 50.8k | dp[1] = cmd_set_misc_map + (cmd_map_other << 4) + map_index; |
395 | 50.8k | dp[2] = comp_num; |
396 | 50.8k | memcpy(dp + 3, map->values, sizeof(map->values)); |
397 | 50.8k | } |
398 | 61.4k | if (pid) |
399 | 61.4k | *pid = map->id; |
400 | 61.4k | } |
401 | 61.4k | return 0; |
402 | 437k | } |
403 | | |
404 | | /* ------ Tile cache management ------ */ |
405 | | |
406 | | /* We want consecutive ids to map to consecutive hash slots if possible, */ |
407 | | /* so we can use a delta representation when setting the index. */ |
408 | | /* NB that we cannot emit 'delta' style tile indices if VM error recovery */ |
409 | | /* is in effect, since reader & writer's tile indices may get out of phase */ |
410 | | /* as a consequence of error recovery occurring. */ |
411 | 61.1M | #define tile_id_hash(id) (id) |
412 | 2.79M | #define tile_hash_next(index) ((index) + 413) /* arbitrary large odd # */ |
413 | | typedef struct tile_loc_s { |
414 | | uint index; |
415 | | tile_slot *tile; |
416 | | } tile_loc; |
417 | | |
418 | | /* Look up a tile or character in the cache. If found, set the index and */ |
419 | | /* pointer; if not, set the index to the insertion point. */ |
420 | | static bool |
421 | | clist_find_bits(gx_device_clist_writer * cldev, gx_bitmap_id id, tile_loc * ploc) |
422 | 61.1M | { |
423 | 61.1M | uint index = tile_id_hash(id); |
424 | 61.1M | const tile_hash *table = cldev->tile_table; |
425 | 61.1M | uint mask = cldev->tile_hash_mask; |
426 | 61.1M | ulong offset; |
427 | | |
428 | 61.5M | for (; (offset = table[index &= mask].offset) != 0; |
429 | 61.1M | index = tile_hash_next(index) |
430 | 61.1M | ) { |
431 | 50.5M | tile_slot *tile = (tile_slot *) (cldev->data + offset); |
432 | | |
433 | 50.5M | if (tile->id == id) { |
434 | 50.1M | ploc->index = index; |
435 | 50.1M | ploc->tile = tile; |
436 | 50.1M | return true; |
437 | 50.1M | } |
438 | 50.5M | } |
439 | 11.0M | ploc->index = index; |
440 | 11.0M | return false; |
441 | 61.1M | } |
442 | | |
443 | | /* Delete a tile from the cache. */ |
444 | | static void |
445 | | clist_delete_tile(gx_device_clist_writer * cldev, tile_slot * slot) |
446 | 1.68M | { |
447 | 1.68M | tile_hash *table = cldev->tile_table; |
448 | 1.68M | uint mask = cldev->tile_hash_mask; |
449 | 1.68M | uint index = slot->index; |
450 | 1.68M | ulong offset; |
451 | | |
452 | 1.68M | if_debug2m('L', cldev->memory, "[L]deleting index=%u, offset=%lu\n", |
453 | 1.68M | index, (ulong) ((byte *) slot - cldev->data)); |
454 | 1.68M | gx_bits_cache_free(&cldev->bits, (gx_cached_bits_head *) slot, |
455 | 1.68M | cldev->cache_chunk); |
456 | 1.68M | table[index].offset = 0; |
457 | | /* Delete the entry from the hash table. */ |
458 | | /* We'd like to move up any later entries, so that we don't need */ |
459 | | /* a deleted mark, but it's too difficult to note this in the */ |
460 | | /* band list, so instead, we just delete any entries that */ |
461 | | /* would need to be moved. */ |
462 | 2.40M | while ((offset = table[index = tile_hash_next(index) & mask].offset) != 0) { |
463 | 719k | tile_slot *tile = (tile_slot *) (cldev->data + offset); |
464 | 719k | tile_loc loc; |
465 | | |
466 | 719k | if (!clist_find_bits(cldev, tile->id, &loc)) { /* We didn't find it, so it should be moved into a slot */ |
467 | | /* that we just vacated; instead, delete it. */ |
468 | 18.2k | if_debug2m('L', cldev->memory, |
469 | 18.2k | "[L]move-deleting index=%u, offset=%lu\n", |
470 | 18.2k | index, offset); |
471 | 18.2k | gx_bits_cache_free(&cldev->bits, |
472 | 18.2k | (gx_cached_bits_head *) (cldev->data + offset), |
473 | 18.2k | cldev->cache_chunk); |
474 | 18.2k | table[index].offset = 0; |
475 | 18.2k | } |
476 | 719k | } |
477 | 1.68M | } |
478 | | |
479 | | /* Add a tile to the cache. */ |
480 | | /* tile->raster holds the raster for the replicated tile; */ |
481 | | /* we pass the raster of the actual data separately. */ |
482 | | static int |
483 | | clist_add_tile(gx_device_clist_writer * cldev, const gx_strip_bitmap * tiles, |
484 | | uint sraster, int depth) |
485 | 5.50M | { |
486 | 5.50M | uint raster = tiles->raster; |
487 | 5.50M | uint size_bytes = raster * tiles->size.y * tiles->num_planes; |
488 | 5.50M | uint tsize = |
489 | 5.50M | sizeof(tile_slot) + cldev->tile_band_mask_size + size_bytes; |
490 | 5.50M | tile_slot *slot; |
491 | | |
492 | 5.50M | if (cldev->bits.csize == cldev->tile_max_count) { /* Don't let the hash table get too full: delete an entry. */ |
493 | | /* Since gx_bits_cache_alloc returns an entry to delete when */ |
494 | | /* it fails, just force it to fail. */ |
495 | 0 | gx_bits_cache_alloc(&cldev->bits, (ulong) cldev->cache_chunk->size, |
496 | 0 | (gx_cached_bits_head **)&slot); |
497 | 0 | if (slot == NULL) { /* Wrap around and retry. */ |
498 | 0 | cldev->bits.cnext = 0; |
499 | 0 | gx_bits_cache_alloc(&cldev->bits, (ulong) cldev->cache_chunk->size, |
500 | 0 | (gx_cached_bits_head **)&slot); |
501 | | #ifdef DEBUG |
502 | | if (slot == NULL) { |
503 | | lprintf("No entry to delete!\n"); |
504 | | return_error(gs_error_Fatal); |
505 | | } |
506 | | #endif |
507 | 0 | } |
508 | 0 | clist_delete_tile(cldev, slot); |
509 | 0 | } |
510 | | /* Allocate the space for the new entry, deleting entries as needed. */ |
511 | 7.20M | while (gx_bits_cache_alloc(&cldev->bits, (ulong) tsize, (gx_cached_bits_head **)&slot) < 0) { |
512 | 1.69M | if (slot == NULL) { /* Wrap around. */ |
513 | 7.56k | if (cldev->bits.cnext == 0) { /* Too big to fit. We should probably detect this */ |
514 | | /* sooner, since if we get here, we've cleared the */ |
515 | | /* cache. */ |
516 | 0 | return_error(gs_error_limitcheck); |
517 | 0 | } |
518 | 7.56k | cldev->bits.cnext = 0; |
519 | 7.56k | } else |
520 | 1.68M | clist_delete_tile(cldev, slot); |
521 | 1.69M | } |
522 | | /* Fill in the entry. */ |
523 | 5.50M | slot->head.depth = depth; |
524 | 5.50M | slot->raster = raster; |
525 | 5.50M | slot->width = tiles->rep_width; |
526 | 5.50M | slot->height = tiles->rep_height; |
527 | 5.50M | slot->shift = slot->rep_shift = tiles->rep_shift; |
528 | 5.50M | slot->x_reps = slot->y_reps = 1; |
529 | 5.50M | slot->id = tiles->id; |
530 | 5.50M | slot->num_planes = (byte)tiles->num_planes; |
531 | 5.50M | if (slot->num_planes != 1) |
532 | 0 | depth /= slot->num_planes; |
533 | 5.50M | memset(ts_mask(slot), 0, cldev->tile_band_mask_size); |
534 | 5.50M | bytes_copy_rectangle_zero_padding(ts_bits(cldev, slot), raster, |
535 | 5.50M | tiles->data, sraster, |
536 | 5.50M | (tiles->rep_width * depth + 7) >> 3, |
537 | 5.50M | tiles->rep_height * slot->num_planes); |
538 | | /* Make the hash table entry. */ |
539 | 5.50M | { |
540 | 5.50M | tile_loc loc; |
541 | | |
542 | | #ifdef DEBUG |
543 | | if (clist_find_bits(cldev, tiles->id, &loc)) |
544 | | lprintf1("clist_find_bits(0x%lx) should have failed!\n", |
545 | | (ulong) tiles->id); |
546 | | #else |
547 | 5.50M | clist_find_bits(cldev, tiles->id, &loc); /* always fails */ |
548 | 5.50M | #endif |
549 | 5.50M | slot->index = loc.index; |
550 | 5.50M | cldev->tile_table[loc.index].offset = |
551 | 5.50M | (byte *) slot - cldev->data; |
552 | 5.50M | if_debug2m('L', cldev->memory, "[L]adding index=%u, offset=%lu\n", |
553 | 5.50M | loc.index, cldev->tile_table[loc.index].offset); |
554 | 5.50M | } |
555 | 5.50M | slot->num_bands = 0; |
556 | 5.50M | return 0; |
557 | 5.50M | } |
558 | | |
559 | | /* ------ Driver procedure support ------ */ |
560 | | |
561 | | /* Change the tile parameters (size and depth). */ |
562 | | /* Currently we do this for all bands at once. */ |
563 | | static void |
564 | | clist_new_tile_params(gx_strip_bitmap * new_tile, const gx_strip_bitmap * tiles, |
565 | | int depth, const gx_device_clist_writer * cldev) |
566 | 7.96k | { /* |
567 | | * Adjust the replication factors. If we can, we replicate |
568 | | * the tile in X up to 32 bytes, and then in Y up to 4 copies, |
569 | | * as long as we don't exceed a total tile size of 256 bytes, |
570 | | * or more than 255 repetitions in X or Y, or make the tile so |
571 | | * large that not all possible tiles will fit in the cache. |
572 | | * Also, don't attempt Y replication if shifting is required, |
573 | | * or if num_planes != 1. |
574 | | */ |
575 | 7.96k | #define max_tile_reps_x 255 |
576 | 7.96k | #define max_tile_bytes_x 32 |
577 | 7.96k | #define max_tile_reps_y 4 |
578 | 11.6k | #define max_tile_bytes 256 |
579 | 7.96k | uint rep_width = tiles->rep_width; |
580 | 7.96k | uint rep_height = tiles->rep_height; |
581 | 7.96k | uint rep_width_bits; |
582 | 7.96k | uint tile_overhead = |
583 | 7.96k | sizeof(tile_slot) + cldev->tile_band_mask_size; |
584 | 7.96k | uint max_bytes; |
585 | | |
586 | 7.96k | if (tiles->num_planes != 1) |
587 | 0 | depth /= tiles->num_planes; |
588 | 7.96k | rep_width_bits = rep_width * depth; |
589 | 7.96k | max_bytes = cldev->cache_chunk->size / (rep_width_bits * rep_height); |
590 | | |
591 | 7.96k | max_bytes -= min(max_bytes, tile_overhead); |
592 | 7.96k | if (max_bytes > max_tile_bytes) |
593 | 3.72k | max_bytes = max_tile_bytes; |
594 | 7.96k | *new_tile = *tiles; |
595 | 7.96k | { |
596 | 7.96k | uint max_bits_x = max_bytes * 8 / rep_height; |
597 | 7.96k | uint reps_x = |
598 | 7.96k | min(max_bits_x, max_tile_bytes_x * 8) / rep_width_bits; |
599 | 7.96k | uint reps_y; |
600 | | |
601 | 7.96k | while (reps_x > max_tile_reps_x) |
602 | 0 | reps_x >>= 1; |
603 | 7.96k | new_tile->size.x = max(reps_x, 1) * rep_width; |
604 | 7.96k | new_tile->raster = bitmap_raster(new_tile->size.x * depth); |
605 | 7.96k | if (tiles->shift != 0 || tiles->num_planes != 1) |
606 | 0 | reps_y = 1; |
607 | 7.96k | else { |
608 | 7.96k | reps_y = max_bytes / (new_tile->raster * rep_height); |
609 | 7.96k | if (reps_y > max_tile_reps_y) |
610 | 0 | reps_y = max_tile_reps_y; |
611 | 7.96k | else if (reps_y < 1) |
612 | 4.28k | reps_y = 1; |
613 | 7.96k | } |
614 | 7.96k | new_tile->size.y = reps_y * rep_height; |
615 | 7.96k | } |
616 | 7.96k | #undef max_tile_reps_x |
617 | 7.96k | #undef max_tile_bytes_x |
618 | 7.96k | #undef max_tile_reps_y |
619 | 7.96k | #undef max_tile_bytes |
620 | 7.96k | } |
621 | | |
622 | | extern dev_proc_open_device(pattern_clist_open_device); |
623 | | |
624 | | /* Change tile for clist_tile_rectangle. */ |
625 | | int |
626 | | clist_change_tile(gx_device_clist_writer * cldev, gx_clist_state * pcls, |
627 | | const gx_strip_bitmap * tiles, int depth) |
628 | 857k | { |
629 | 857k | tile_loc loc; |
630 | 857k | int code; |
631 | | |
632 | 857k | #define tile_params_differ(cldev, tiles, depth)\ |
633 | 857k | ((tiles)->rep_width != (cldev)->tile_params.rep_width ||\ |
634 | 35.8k | (tiles)->rep_height != (cldev)->tile_params.rep_height ||\ |
635 | 35.8k | (tiles)->rep_shift != (cldev)->tile_params.rep_shift ||\ |
636 | 54.9k | (depth) != (cldev)->tile_depth) |
637 | | |
638 | 874k | top:if (clist_find_bits(cldev, tiles->id, &loc)) { /* The bitmap is in the cache. Check whether this band */ |
639 | | /* knows about it. */ |
640 | 857k | int band_index = pcls - cldev->states; |
641 | 857k | byte *bptr = ts_mask(loc.tile) + (band_index >> 3); |
642 | 857k | byte bmask = 1 << (band_index & 7); |
643 | 857k | bool for_pattern = gx_device_is_pattern_clist((gx_device *)cldev); |
644 | | |
645 | 857k | if (*bptr & bmask) { /* Already known. Just set the index. */ |
646 | 838k | if (pcls->tile_index == loc.index) |
647 | 0 | return 0; |
648 | 838k | if ((code = cmd_put_tile_index(cldev, pcls, loc.index)) < 0) |
649 | 0 | return code; |
650 | 838k | } else { |
651 | 19.1k | uint extra = 0; |
652 | | |
653 | 19.1k | if (tile_params_differ(cldev, tiles, depth) || |
654 | 19.1k | for_pattern) { /* |
655 | | * We have a cached tile whose parameters differ from |
656 | | * the current ones. Because of the way tile IDs are |
657 | | * managed, this is currently only possible when mixing |
658 | | * Patterns and halftones, but if we didn't generate new |
659 | | * IDs each time the main halftone cache needed to be |
660 | | * refreshed, this could also happen simply from |
661 | | * switching screens. |
662 | | */ |
663 | 4.16k | int band; |
664 | | |
665 | 4.16k | clist_new_tile_params(&cldev->tile_params, tiles, depth, |
666 | 4.16k | cldev); |
667 | 4.16k | cldev->tile_depth = depth; |
668 | | /* No band knows about the new parameters. */ |
669 | 4.16k | for (band = cldev->tile_known_min; |
670 | 6.94k | band <= cldev->tile_known_max; |
671 | 4.16k | ++band |
672 | 4.16k | ) |
673 | 2.78k | cldev->states[band].known &= ~tile_params_known; |
674 | 4.16k | cldev->tile_known_min = cldev->nbands; |
675 | 4.16k | cldev->tile_known_max = -1; |
676 | 4.16k | } |
677 | 19.1k | if (!(pcls->known & tile_params_known)) { /* We're going to have to write the tile parameters. */ |
678 | 5.05k | extra = cmd_size_tile_params(&cldev->tile_params, for_pattern); |
679 | 5.05k | } { /* |
680 | | * This band doesn't know this tile yet, so output the |
681 | | * bits. Note that the offset we write is the one used by |
682 | | * the reading phase, not the writing phase. Note also |
683 | | * that the size of the cached and written tile may differ |
684 | | * from that of the client's tile. Finally, note that |
685 | | * this tile's size parameters are guaranteed to be |
686 | | * compatible with those stored in the device |
687 | | * (cldev->tile_params). |
688 | | */ |
689 | 19.1k | ulong offset = (byte *) loc.tile - cldev->cache_chunk->data; |
690 | 19.1k | uint rsize = |
691 | 19.1k | extra + 1 + cmd_size_w(loc.index) + cmd_size_w(offset); |
692 | 19.1k | byte *dp; |
693 | 19.1k | uint csize; |
694 | 19.1k | int code; |
695 | 19.1k | int pdepth = depth; |
696 | 19.1k | if (tiles->num_planes != 1) |
697 | 0 | pdepth /= tiles->num_planes; |
698 | | |
699 | | /* put the bits, but don't restrict to a single buffer */ |
700 | 19.1k | code = cmd_put_bits(cldev, pcls, ts_bits(cldev, loc.tile), |
701 | 19.1k | tiles->rep_width * pdepth, |
702 | 19.1k | tiles->rep_height * tiles->num_planes, |
703 | 19.1k | loc.tile->raster, rsize, |
704 | 19.1k | allow_large_bitmap | |
705 | 19.1k | (cldev->tile_params.size.x > tiles->rep_width ? |
706 | 19.0k | decompress_elsewhere | decompress_spread : |
707 | 19.1k | decompress_elsewhere), |
708 | 19.1k | &dp, &csize); |
709 | | |
710 | 19.1k | if (code < 0) |
711 | 0 | return code; |
712 | 19.1k | if (extra) { /* Write the tile parameters before writing the bits. */ |
713 | 5.05k | if_debug1m('L', cldev->memory, |
714 | 5.05k | "[L] fake end_run: really set_tile_size[%d]\n", extra); |
715 | 5.05k | cmd_store_tile_params(dp, &cldev->tile_params, depth, |
716 | 5.05k | extra, for_pattern, cldev->memory); |
717 | 5.05k | dp += extra; |
718 | | /* This band now knows the parameters. */ |
719 | 5.05k | pcls->known |= tile_params_known; |
720 | 5.05k | if (band_index < cldev->tile_known_min) |
721 | 4.25k | cldev->tile_known_min = band_index; |
722 | 5.05k | if (band_index > cldev->tile_known_max) |
723 | 4.86k | cldev->tile_known_max = band_index; |
724 | 5.05k | } |
725 | 19.1k | if_debug1m('L', cldev->memory, |
726 | 19.1k | "[L] fake end_run: really set_tile_bits[%d]\n", csize-extra); |
727 | 19.1k | *dp = cmd_count_op(cmd_opv_set_tile_bits, csize - extra, cldev->memory); |
728 | 19.1k | dp++; |
729 | 19.1k | dp = cmd_put_w(loc.index, dp); |
730 | 19.1k | cmd_put_w(offset, dp); |
731 | 19.1k | *bptr |= bmask; |
732 | 19.1k | loc.tile->num_bands++; |
733 | 19.1k | } |
734 | 19.1k | } |
735 | 857k | pcls->tile_index = loc.index; |
736 | 857k | pcls->tile_id = loc.tile->id; |
737 | 857k | return 0; |
738 | 857k | } |
739 | | /* The tile is not in the cache, add it. */ |
740 | 16.7k | { |
741 | 16.7k | gx_strip_bitmap new_tile; |
742 | 16.7k | gx_strip_bitmap *ptile; |
743 | | |
744 | | /* Ensure that the tile size is compatible. */ |
745 | 16.7k | if (tile_params_differ(cldev, tiles, depth)) { /* We'll reset cldev->tile_params when we write the bits. */ |
746 | 3.80k | clist_new_tile_params(&new_tile, tiles, depth, cldev); |
747 | 3.80k | ptile = &new_tile; |
748 | 12.9k | } else { |
749 | 12.9k | cldev->tile_params.id = tiles->id; |
750 | 12.9k | cldev->tile_params.data = tiles->data; |
751 | 12.9k | ptile = &cldev->tile_params; |
752 | 12.9k | } |
753 | 16.7k | code = clist_add_tile(cldev, ptile, tiles->raster, depth); |
754 | 16.7k | if (code < 0) |
755 | 0 | return code; |
756 | 16.7k | } |
757 | 16.7k | goto top; |
758 | 16.7k | #undef tile_params_differ |
759 | 16.7k | } |
760 | | |
761 | | /* Change "tile" for clist_copy_*. tiles->[rep_]shift must be zero. */ |
762 | | int |
763 | | clist_change_bits(gx_device_clist_writer * cldev, gx_clist_state * pcls, |
764 | | const gx_strip_bitmap * tiles, int depth) |
765 | 48.5M | { |
766 | 48.5M | tile_loc loc; |
767 | 48.5M | int code; |
768 | 48.5M | uint band_index = pcls - cldev->states; |
769 | 48.5M | byte bmask = 1 << (band_index & 7); |
770 | 48.5M | byte *bptr; |
771 | | |
772 | 54.0M | while (!clist_find_bits(cldev, tiles->id, &loc)) { |
773 | | /* The tile is not in the cache. */ |
774 | 5.48M | code = clist_add_tile(cldev, tiles, tiles->raster, depth); |
775 | 5.48M | if (code < 0) |
776 | 0 | return code; |
777 | 5.48M | } |
778 | | |
779 | | /* The bitmap is in the cache. Check whether this band */ |
780 | | /* knows about it. */ |
781 | 48.5M | bptr = ts_mask(loc.tile) + (band_index >> 3); |
782 | | |
783 | 48.5M | if (*bptr & bmask) { /* Already known. Just set the index. */ |
784 | 31.4M | if (pcls->tile_index == loc.index) |
785 | 0 | return 0; |
786 | 31.4M | cmd_put_tile_index(cldev, pcls, loc.index); |
787 | 31.4M | } else { /* Not known yet. Output the bits. */ |
788 | | /* Note that the offset we write is the one used by */ |
789 | | /* the reading phase, not the writing phase. */ |
790 | 17.1M | ulong offset = (byte *) loc.tile - cldev->cache_chunk->data; |
791 | 17.1M | uint rsize = 2 + cmd_size_w(loc.tile->width) + |
792 | 17.1M | cmd_size_w(loc.tile->height) + |
793 | 17.1M | (loc.tile->num_planes > 1 ? 1 : 0) + |
794 | 17.1M | cmd_size_w(loc.index) + |
795 | 17.1M | cmd_size_w(offset); |
796 | 17.1M | byte *dp; |
797 | 17.1M | uint csize; |
798 | 17.1M | gx_clist_state *bit_pcls = pcls; |
799 | 17.1M | int pdepth = depth; |
800 | | |
801 | 17.1M | if (tiles->num_planes != 1) |
802 | 0 | pdepth /= loc.tile->num_planes; |
803 | 17.1M | if (loc.tile->num_bands == CHAR_ALL_BANDS_COUNT) |
804 | 0 | bit_pcls = NULL; |
805 | | /* put the bits, but don't restrict to a single buffer */ |
806 | 17.1M | code = cmd_put_bits(cldev, bit_pcls, ts_bits(cldev, loc.tile), |
807 | 17.1M | loc.tile->width * pdepth, |
808 | 17.1M | loc.tile->height * loc.tile->num_planes, loc.tile->raster, |
809 | 17.1M | rsize, |
810 | 17.1M | decompress_elsewhere | |
811 | 17.1M | (cldev->target->BLS_force_memory ? (1 << cmd_compress_cfe) : 0), |
812 | 17.1M | &dp, &csize); |
813 | | |
814 | 17.1M | if (code < 0) |
815 | 45 | return code; |
816 | 17.1M | if_debug1m('L', cldev->memory, |
817 | 17.1M | "[L] fake end_run: really set_bits[%d]\n", csize); |
818 | 17.1M | *dp = cmd_count_op(loc.tile->num_planes > 1 ? cmd_opv_set_bits_planar : cmd_opv_set_bits, |
819 | 17.1M | csize, cldev->memory); |
820 | 17.1M | dp[1] = (depth << 2) + code; |
821 | 17.1M | dp += 2; |
822 | 17.1M | dp = cmd_put_w(loc.tile->width, dp); |
823 | 17.1M | dp = cmd_put_w(loc.tile->height, dp); |
824 | 17.1M | if (loc.tile->num_planes > 1) |
825 | 0 | *dp++ = loc.tile->num_planes; |
826 | 17.1M | dp = cmd_put_w(loc.index, dp); |
827 | 17.1M | cmd_put_w(offset, dp); |
828 | 17.1M | if_debug7m('L', cldev->memory, " compress=%d depth=%d size=(%d,%d) planes=%d index=%d offset=%ld\n", |
829 | 17.1M | code, depth, loc.tile->width, loc.tile->height, loc.tile->num_planes, loc.index, offset); |
830 | 17.1M | if (bit_pcls == NULL) { |
831 | 0 | memset(ts_mask(loc.tile), 0xff, |
832 | 0 | cldev->tile_band_mask_size); |
833 | 0 | loc.tile->num_bands = cldev->nbands; |
834 | 17.1M | } else { |
835 | 17.1M | *bptr |= bmask; |
836 | 17.1M | loc.tile->num_bands++; |
837 | 17.1M | } |
838 | 17.1M | } |
839 | 48.5M | pcls->tile_index = loc.index; |
840 | 48.5M | pcls->tile_id = loc.tile->id; |
841 | 48.5M | return 0; |
842 | 48.5M | } |