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