Commit | Line | Data |
---|---|---|
9792d40a WJ |
1 | /* Copyright (C) 1991, 1992 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
19 | ||
20 | /* gxclist.c */ | |
21 | /* Command list 'device' for Ghostscript. */ | |
22 | #include "memory_.h" | |
23 | #include "gx.h" | |
24 | #include "gserrors.h" | |
25 | #include "gxdevice.h" | |
26 | #include "gxdevmem.h" /* must precede gxclist.h */ | |
27 | #include "gxclist.h" | |
28 | ||
29 | /* Patch a couple of things possibly missing from stdio.h. */ | |
30 | #ifndef SEEK_SET | |
31 | # define SEEK_SET 0 | |
32 | #endif | |
33 | #ifndef SEEK_END | |
34 | # define SEEK_END 2 | |
35 | #endif | |
36 | ||
37 | #define cdev ((gx_device_clist *)dev) | |
38 | ||
39 | /* Forward declarations of procedures */ | |
40 | private dev_proc_open_device(clist_open); | |
41 | private dev_proc_get_initial_matrix(clist_get_initial_matrix); | |
42 | private dev_proc_output_page(clist_output_page); | |
43 | private dev_proc_map_rgb_color(clist_map_rgb_color); | |
44 | private dev_proc_map_color_rgb(clist_map_color_rgb); | |
45 | private dev_proc_fill_rectangle(clist_fill_rectangle); | |
46 | private dev_proc_tile_rectangle(clist_tile_rectangle); | |
47 | private dev_proc_copy_mono(clist_copy_mono); | |
48 | private dev_proc_copy_color(clist_copy_color); | |
49 | private dev_proc_get_bits(clist_get_bits); | |
50 | private dev_proc_get_props(clist_get_props); | |
51 | private dev_proc_put_props(clist_put_props); | |
52 | ||
53 | /* The device descriptor */ | |
54 | private gx_device_procs clist_procs = | |
55 | { clist_open, | |
56 | clist_get_initial_matrix, | |
57 | gx_default_sync_output, | |
58 | clist_output_page, | |
59 | gx_default_close_device, | |
60 | clist_map_rgb_color, | |
61 | clist_map_color_rgb, | |
62 | clist_fill_rectangle, | |
63 | clist_tile_rectangle, | |
64 | clist_copy_mono, | |
65 | clist_copy_color, | |
66 | gx_default_draw_line, | |
67 | clist_get_bits, | |
68 | clist_get_props, | |
69 | clist_put_props | |
70 | }; | |
71 | gx_device_clist gs_clist_device = | |
72 | { sizeof(gx_device_clist), | |
73 | &clist_procs, | |
74 | "command list", | |
75 | 0, 0, 1, 1, no_margins, dci_black_and_white, 0, /* generic */ | |
76 | NULL, NULL, 0 | |
77 | }; | |
78 | ||
79 | /* ------ Define the command set and syntax ------ */ | |
80 | ||
81 | /* A command always consists of an operation followed by operands. */ | |
82 | /* The operands are implicit in the procedural code. */ | |
83 | typedef enum { | |
84 | cmd_op_misc = 0x00, /* (see below) */ | |
85 | cmd_opv_end_run = 0x00, /* (nothing) */ | |
86 | cmd_opv_set_tile_size = 0x01, /* width, height */ | |
87 | cmd_opv_set_tile_phase = 0x02, /* x, y */ | |
88 | cmd_op_set_color0 = 0x10, /* color+2 in op byte, [color] */ | |
89 | cmd_op_set_color1 = 0x20, /* color+2 in op byte, [color] */ | |
90 | cmd_op_set_tile_index = 0x30, /* index */ | |
91 | cmd_op_fill_rect = 0x40, /* rect */ | |
92 | cmd_op_fill_rect_short = 0x50, /* dh in op byte,dx,dw | rect_short */ | |
93 | cmd_op_fill_rect_tiny = 0x60, /* dw in op byte, rect_tiny */ | |
94 | cmd_op_tile_rect = 0x70, /* rect */ | |
95 | cmd_op_tile_rect_short = 0x80, /* dh in op byte,dx,dw | rect_short */ | |
96 | cmd_op_tile_rect_tiny = 0x90, /* dw in op byte, rect_tiny */ | |
97 | cmd_op_copy_mono = 0xa0, /* rect, data_x, raster | */ | |
98 | /* d_x+1 in op byte, x, y, w, h */ | |
99 | cmd_op_copy_color = 0xb0, /* rect, data_x, raster */ | |
100 | cmd_op_set_tile_bits = 0xc0, /* index, <bits> */ | |
101 | cmd_op_delta_tile_bits = 0xd0, /* n-1 in op byte, n x <offset, bits> */ | |
102 | cmd_op_end | |
103 | } gx_cmd_op; | |
104 | /* Define the size of the largest command, */ | |
105 | /* not counting any bitmap. */ | |
106 | #define w sizeof(short) /* size of coordinate */ | |
107 | #define d 3 /* size of tile delta */ | |
108 | private uint cmd_largest_size = | |
109 | max(1 + 6 * sizeof(short) /* copy_mono */, 2 + 16 * d /* delta_tile 15 */); | |
110 | #undef d | |
111 | #undef w | |
112 | #ifdef DEBUG | |
113 | private char *cmd_op_names[16] = { | |
114 | "misc", "set_color_0", "set_color_1", "set_tile", | |
115 | "fill_rect", "fill_rect_short", "fill_rect_tiny", "tile_rect", | |
116 | "tile_rect_short", "tile_rect_tiny", "copy_mono", "copy_color", | |
117 | "set_tile_bits", "delta_tile_bits", "?e0?", "?f0?" | |
118 | }; | |
119 | private char *cmd_misc_op_names[16] = { | |
120 | "end_run", "set_tile_size", "set_tile_phase", "?03?", | |
121 | "?04?", "?05?", "?06?", "?07?", | |
122 | "?08?", "?09?", "?0a?", "?0b?", | |
123 | "?0c?", "?0d?", "?0e?", "?0f?" | |
124 | }; | |
125 | private ulong cmd_op_counts[256]; | |
126 | private ulong cmd_tile_count, cmd_copy_count, cmd_delta_tile_count; | |
127 | private ulong cmd_tile_reset, cmd_tile_found, cmd_tile_added; | |
128 | private int | |
129 | count_op(int op) | |
130 | { ++cmd_op_counts[op]; | |
131 | if ( gs_debug['L'] ) | |
132 | dprintf2(", %s %d\n", cmd_op_names[op >> 4], op & 0xf), | |
133 | fflush(dstderr); | |
134 | return op; | |
135 | } | |
136 | # define count_add(v, n) (v += (n)) | |
137 | #else | |
138 | # define count_op(store_op) store_op | |
139 | # define count_add(v, n) 0 | |
140 | #endif | |
141 | #define count_add1(v) count_add(v, 1) | |
142 | ||
143 | typedef struct { | |
144 | short x, y, width, height; | |
145 | } gx_cmd_rect; | |
146 | typedef struct { | |
147 | byte dx, dwidth, dy, dheight; /* dy and dheight are optional */ | |
148 | } gx_cmd_rect_short; | |
149 | #define cmd_min_short (-128) | |
150 | #define cmd_max_short 127 | |
151 | typedef struct { | |
152 | unsigned dx : 4; | |
153 | unsigned dy : 4; | |
154 | } gx_cmd_rect_tiny; | |
155 | #define cmd_min_tiny (-8) | |
156 | #define cmd_max_tiny 7 | |
157 | ||
158 | /* Define the prefix on each command run in the writing buffer. */ | |
159 | typedef struct cmd_prefix_s cmd_prefix; | |
160 | struct cmd_prefix_s { | |
161 | cmd_prefix *next; | |
162 | uint size; | |
163 | }; | |
164 | ||
165 | /* Define the entries in the block file. */ | |
166 | typedef struct cmd_block_s { | |
167 | int band; | |
168 | long pos; /* starting position in cfile */ | |
169 | } cmd_block; | |
170 | ||
171 | /* Remember the current state of one band when writing or reading. */ | |
172 | struct gx_clist_state_s { | |
173 | gx_color_index color0, color1; /* most recent colors */ | |
174 | tile_slot *tile; /* most recent tile */ | |
175 | gs_int_point tile_phase; /* most recent tile phase */ | |
176 | gx_cmd_rect rect; /* most recent rectangle */ | |
177 | /* Following are only used when writing */ | |
178 | cmd_prefix *head, *tail; /* list of commands for band */ | |
179 | }; | |
180 | private tile_slot no_tile = { ~0L }; | |
181 | ||
182 | /* The initial values for a band state */ | |
183 | private gx_clist_state cls_initial = | |
184 | { gx_no_color_index, gx_no_color_index, &no_tile, | |
185 | { 0, 0 }, { 0, 0, 0, 0 }, | |
186 | 0, 0 | |
187 | }; | |
188 | ||
189 | /* Define the size of the command buffer used for reading. */ | |
190 | /* This is needed to split up very large copy_ operations. */ | |
191 | #define cbuf_size 500 | |
192 | ||
193 | /* Initialize the device state */ | |
194 | private void clist_init_tiles(P1(gx_device_clist *)); | |
195 | private int | |
196 | clist_open(gx_device *dev) | |
197 | { /* | |
198 | * The buffer area (data, data_size) holds a tile cache and a | |
199 | * set of block range bit masks when both writing and reading. | |
200 | * The rest of the space is used for | |
201 | * the command buffer and band state bookkeeping when writing, | |
202 | * and for the rendering buffer (image device) when reading. | |
203 | * For the moment, we divide the space up arbitrarily. | |
204 | */ | |
205 | byte *data = cdev->data; | |
206 | uint size = cdev->data_size; | |
207 | #define alloc_data(n) data += (n), size -= (n) | |
208 | gx_device *target = cdev->target; | |
209 | int raster, nbands, band; | |
210 | gx_clist_state *states; | |
211 | uint state_size; | |
212 | cdev->ymin = cdev->ymax = -1; /* render_init not done yet */ | |
213 | cdev->tile_data = data; | |
214 | cdev->tile_data_size = (size / 5) & -4; /* arbitrary! */ | |
215 | alloc_data(cdev->tile_data_size); | |
216 | raster = (((target->width * target->color_info.depth + 31) >> 5) << 2) + sizeof(byte *); | |
217 | cdev->band_height = size / (uint)raster; | |
218 | nbands = target->height / cdev->band_height + 1; | |
219 | cdev->nbands = nbands; | |
220 | #ifdef DEBUG | |
221 | if ( gs_debug['l'] | gs_debug['L'] ) | |
222 | dprintf4("[l]width=%d, raster=%d, band_height=%d, nbands=%d\n", | |
223 | target->width, raster, cdev->band_height, cdev->nbands); | |
224 | #endif | |
225 | state_size = nbands * sizeof(gx_clist_state); | |
226 | if ( state_size > size / 2 ) | |
227 | return -1; /* not enough room */ | |
228 | cdev->mdev.base = data; | |
229 | cdev->states = states = (gx_clist_state *)data; | |
230 | alloc_data(state_size); | |
231 | cdev->cbuf = data; | |
232 | cdev->cnext = data; | |
233 | cdev->cend = data + size; | |
234 | cdev->ccls = 0; | |
235 | for ( band = 0; band < nbands; band++, states++ ) | |
236 | *states = cls_initial; | |
237 | #undef alloc_data | |
238 | cdev->tile_band_mask_size = (nbands + 31) / 32 * 4; | |
239 | cdev->tile_max_size = cdev->tile_data_size - | |
240 | (sizeof(tile_hash) * 2 + sizeof(tile_slot) + | |
241 | cdev->tile_band_mask_size); | |
242 | clist_init_tiles(cdev); | |
243 | return 0; | |
244 | } | |
245 | ||
246 | /* (Re)initialize the tile cache. */ | |
247 | private void | |
248 | clist_init_tiles(register gx_device_clist *cldev) | |
249 | { gx_clist_state *pcls; | |
250 | int i, hc; | |
251 | cldev->tile_slot_size = | |
252 | sizeof(tile_slot) + cldev->tile_band_mask_size + | |
253 | cldev->tile.raster * cldev->tile.size.y; | |
254 | cldev->tile_max_count = cldev->tile_data_size / | |
255 | (sizeof(tile_hash) * 3 /*(worst case)*/ + cldev->tile_slot_size); | |
256 | hc = (cldev->tile_max_count - 1) * 2; | |
257 | while ( (hc + 1) & hc ) hc |= hc >> 1; /* make mask */ | |
258 | if ( hc >= cldev->tile_max_count * 3 ) hc >>= 1; | |
259 | if ( hc > 255 ) /* slot index in set_tile is only 1 byte */ | |
260 | { hc = 255; | |
261 | if ( cldev->tile_max_count > 200 ) | |
262 | cldev->tile_max_count = 200; | |
263 | } | |
264 | cldev->tile_hash_mask = hc; | |
265 | hc++; /* make actual size */ | |
266 | #ifdef DEBUG | |
267 | if ( gs_debug['l'] | gs_debug['L'] ) | |
268 | dprintf5("[l]tile.size=%dx%d, slot_size=%d, max_count=%d, hc=%d\n", | |
269 | cldev->tile.size.x, cldev->tile.size.y, | |
270 | cldev->tile_slot_size, cldev->tile_max_count, hc); | |
271 | #endif | |
272 | cldev->tile_hash_table = | |
273 | (tile_hash *)(cldev->tile_data + cldev->tile_data_size) - hc; | |
274 | cldev->tile_count = 0; | |
275 | memset(cldev->tile_data, 0, cldev->tile_data_size); | |
276 | memset(cldev->tile_hash_table, -1, hc * sizeof(tile_hash)); | |
277 | for ( i = 0, pcls = cldev->states; i < cldev->nbands; i++, pcls++ ) | |
278 | pcls->tile = &no_tile; | |
279 | count_add1(cmd_tile_reset); | |
280 | } | |
281 | ||
282 | /* Forward the non-displaying operations to the target device. */ | |
283 | private void | |
284 | clist_get_initial_matrix(gx_device *dev, gs_matrix *pmat) | |
285 | { (*cdev->target->procs->get_initial_matrix)(dev, pmat); | |
286 | } | |
287 | private gx_color_index | |
288 | clist_map_rgb_color(gx_device *dev, gx_color_value red, gx_color_value green, | |
289 | gx_color_value blue) | |
290 | { return (*cdev->target->procs->map_rgb_color)(dev, red, green, blue); | |
291 | } | |
292 | private int | |
293 | clist_map_color_rgb(gx_device *dev, gx_color_index color, | |
294 | gx_color_value rgb[3]) | |
295 | { return (*cdev->target->procs->map_color_rgb)(dev, color, rgb); | |
296 | } | |
297 | private int | |
298 | clist_get_props(gx_device *dev, gs_prop_item *plist) | |
299 | { gx_device *tdev = cdev->target; | |
300 | return (*tdev->procs->get_props)(tdev, plist); | |
301 | } | |
302 | private int | |
303 | clist_put_props(gx_device *dev, gs_prop_item *plist, int count) | |
304 | { gx_device *tdev = cdev->target; | |
305 | return (*tdev->procs->put_props)(tdev, plist, count); | |
306 | } | |
307 | ||
308 | /* Print a bitmap for tracing */ | |
309 | #ifdef DEBUG | |
310 | private void | |
311 | cmd_print_bits(byte *data, int height, int raster) | |
312 | { int i, j; | |
313 | for ( i = 0; i < height; i++ ) | |
314 | { byte *row = data + i * raster; | |
315 | dprintf("[L]"); | |
316 | for ( j = 0; j < raster; j++ ) | |
317 | dprintf1(" %02x", row[j]); | |
318 | dputc('\n'); | |
319 | } | |
320 | } | |
321 | #else | |
322 | # define cmd_print_bits(data, height, raster) | |
323 | #endif | |
324 | ||
325 | /* ------ Writing ------ */ | |
326 | ||
327 | /* Utilities */ | |
328 | ||
329 | #define cmd_set_rect(rect)\ | |
330 | ((rect).x = x, (rect).y = y,\ | |
331 | (rect).width = width, (rect).height = height) | |
332 | ||
333 | #define clist_write(f, str, len)\ | |
334 | fwrite(str, 1, len, f) | |
335 | ||
336 | /* Write out the buffered commands, and reset the buffer. */ | |
337 | private void | |
338 | cmd_write_buffer(gx_device_clist *cldev) | |
339 | { FILE *cfile = cldev->cfile; | |
340 | FILE *bfile = cldev->bfile; | |
341 | int nbands = cldev->nbands; | |
342 | gx_clist_state *pcls; | |
343 | int band; | |
344 | for ( band = 0, pcls = cldev->states; band < nbands; band++, pcls++ ) | |
345 | { cmd_prefix *cp = pcls->head; | |
346 | if ( cp != 0 ) | |
347 | { cmd_block cb; | |
348 | cb.band = band; | |
349 | cb.pos = ftell(cfile); | |
350 | #ifdef DEBUG | |
351 | if ( gs_debug['l'] | gs_debug['L'] ) | |
352 | dprintf2("[l]writing for band %d at %ld\n", | |
353 | band, cb.pos); | |
354 | #endif | |
355 | clist_write(bfile, (byte *)&cb, sizeof(cb)); | |
356 | for ( ; cp != 0; cp = cp->next ) | |
357 | clist_write(cfile, (byte *)(cp + 1), cp->size); | |
358 | pcls->head = pcls->tail = 0; | |
359 | fputc(cmd_opv_end_run, cfile); | |
360 | } | |
361 | } | |
362 | cldev->cnext = cldev->cbuf; | |
363 | cldev->ccls = 0; | |
364 | } | |
365 | ||
366 | /* Add a command to the appropriate band list, */ | |
367 | /* and allocate space for its data. */ | |
368 | /* Return the pointer to the data area. */ | |
369 | private byte * | |
370 | cmd_put_op(gx_device_clist *cldev, gx_clist_state *pcls, uint size) | |
371 | { byte *dp = cldev->cnext; | |
372 | #ifdef DEBUG | |
373 | if ( gs_debug['L'] ) | |
374 | dprintf3("[L]band %d: size=%u, left=%d", | |
375 | (int)(pcls - cldev->states), size, (int)(cldev->cend - dp)); | |
376 | #endif | |
377 | if ( size + (sizeof(cmd_prefix) + 4) > cldev->cend - dp ) | |
378 | { cmd_write_buffer(cldev); | |
379 | return cmd_put_op(cldev, pcls, size); | |
380 | } | |
381 | if ( cldev->ccls == pcls ) | |
382 | { /* We're adding another command for the same band. */ | |
383 | /* Tack it onto the end of the previous one. */ | |
384 | pcls->tail->size += size; | |
385 | } | |
386 | else | |
387 | { cmd_prefix *cp = (cmd_prefix *)(dp + (((byte *)0 - dp) & 3)); | |
388 | dp = (byte *)(cp + 1); | |
389 | if ( pcls->tail != 0 ) pcls->tail->next = cp; | |
390 | else pcls->head = cp; | |
391 | pcls->tail = cp; | |
392 | cldev->ccls = pcls; | |
393 | cp->next = 0; | |
394 | cp->size = size; | |
395 | } | |
396 | cldev->cnext = dp + size; | |
397 | return dp; | |
398 | } | |
399 | ||
400 | /* We store all short quantities little-endian. */ | |
401 | /* This is OK, because we read them back little-endian explicitly. */ | |
402 | #define cmd_putw(w, dp)\ | |
403 | (*dp = (w) & 0xff, dp[1] = (w) >> 8, dp += 2) | |
404 | ||
405 | /* Write a short bitmap. 1 <= bwidth <= 3. */ | |
406 | private void | |
407 | cmd_put_short_bits(register byte *dp, register byte *data, | |
408 | int raster, register int bwidth, register int height) | |
409 | { while ( --height >= 0 ) | |
410 | { switch ( bwidth ) | |
411 | { | |
412 | case 3: dp[2] = data[2]; | |
413 | case 2: dp[1] = data[1]; | |
414 | case 1: dp[0] = data[0]; | |
415 | } | |
416 | dp += bwidth, data += raster; | |
417 | } | |
418 | } | |
419 | ||
420 | private int | |
421 | cmd_write_rect_cmd(gx_device *dev, gx_clist_state *pcls, | |
422 | int op, int x, int y, int width, int height) | |
423 | { int dx = x - pcls->rect.x; | |
424 | int dy = y - pcls->rect.y; | |
425 | int dwidth = width - pcls->rect.width; | |
426 | int dheight = height - pcls->rect.height; | |
427 | #define check_ranges_1()\ | |
428 | ((unsigned)(dx - rmin) <= (rmax - rmin) &&\ | |
429 | (unsigned)(dy - rmin) <= (rmax - rmin) &&\ | |
430 | (unsigned)(dwidth - rmin) <= (rmax - rmin)) | |
431 | #define check_ranges()\ | |
432 | (check_ranges_1() &&\ | |
433 | (unsigned)(dheight - rmin) <= (rmax - rmin)) | |
434 | #define rmin cmd_min_tiny | |
435 | #define rmax cmd_max_tiny | |
436 | cmd_set_rect(pcls->rect); | |
437 | if ( dheight == 0 && check_ranges_1() ) | |
438 | { byte *dp = cmd_put_op(cdev, pcls, 2); | |
439 | count_op(*dp = op + 0x20 + dwidth - rmin); | |
440 | dp[1] = (dx << 4) + dy - (rmin * 0x11); | |
441 | } | |
442 | #undef rmin | |
443 | #undef rmax | |
444 | #define rmin cmd_min_short | |
445 | #define rmax cmd_max_short | |
446 | else if ( check_ranges() ) | |
447 | { int dh = dheight - cmd_min_tiny; | |
448 | byte *dp; | |
449 | if ( (unsigned)dh <= cmd_max_tiny - cmd_min_tiny && dh != 0 && | |
450 | dy == 0 | |
451 | ) | |
452 | { op += dh; | |
453 | dp = cmd_put_op(cdev, pcls, 3); | |
454 | } | |
455 | else | |
456 | { dp = cmd_put_op(cdev, pcls, 5); | |
457 | dp[3] = dy - rmin; | |
458 | dp[4] = dheight - rmin; | |
459 | } | |
460 | count_op(*dp = op + 0x10); | |
461 | dp[1] = dx - rmin; | |
462 | dp[2] = dwidth - rmin; | |
463 | } | |
464 | else | |
465 | { byte *dp = cmd_put_op(cdev, pcls, 1 + sizeof(pcls->rect)); | |
466 | count_op(*dp = op); | |
467 | memcpy(dp + 1, &pcls->rect, sizeof(pcls->rect)); | |
468 | } | |
469 | return 0; | |
470 | } | |
471 | ||
472 | private void | |
473 | cmd_put_color(gx_device *dev, gx_clist_state *pcls, | |
474 | int op, gx_color_index color) | |
475 | { if ( (long)color >= -1 && (long)color <= 13 ) | |
476 | count_op(*cmd_put_op(cdev, pcls, 1) = op + (int)color + 2); | |
477 | else | |
478 | { byte *dp = cmd_put_op(cdev, pcls, 1 + sizeof(color)); | |
479 | count_op(*dp = op); | |
480 | memcpy(dp + 1, &color, sizeof(color)); | |
481 | } | |
482 | } | |
483 | private void | |
484 | cmd_set_colors(gx_device *dev, gx_clist_state *pcls, | |
485 | gx_color_index color0, gx_color_index color1) | |
486 | { if ( color0 != pcls->color0 ) | |
487 | { cmd_put_color(dev, pcls, cmd_op_set_color0, color0); | |
488 | pcls->color0 = color0; | |
489 | } | |
490 | if ( color1 != pcls->color1 ) | |
491 | { cmd_put_color(dev, pcls, cmd_op_set_color1, color1); | |
492 | pcls->color1 = color1; | |
493 | } | |
494 | } | |
495 | ||
496 | /* Driver interface */ | |
497 | ||
498 | /* Macros for dividing up a single call into bands */ | |
499 | #define BEGIN_RECT\ | |
500 | { int yend = y + height;\ | |
501 | int band_height = cdev->band_height;\ | |
502 | do\ | |
503 | { int band = y / band_height;\ | |
504 | gx_clist_state *pcls = cdev->states + band;\ | |
505 | height = band_height - y % band_height;\ | |
506 | if ( yend - y < height ) height = yend - y;\ | |
507 | { | |
508 | #define END_RECT\ | |
509 | }\ | |
510 | y += height;\ | |
511 | }\ | |
512 | while ( y < yend );\ | |
513 | } | |
514 | ||
515 | private int | |
516 | clist_fill_rectangle(gx_device *dev, int x, int y, int width, int height, | |
517 | gx_color_index color) | |
518 | { BEGIN_RECT | |
519 | if ( color != pcls->color1 ) | |
520 | cmd_set_colors(dev, pcls, pcls->color0, color); | |
521 | cmd_write_rect_cmd(dev, pcls, cmd_op_fill_rect, x, y, width, height); | |
522 | END_RECT | |
523 | return 0; | |
524 | } | |
525 | ||
526 | /* Compare unequal tiles. Return -1 if unrelated, */ | |
527 | /* or 2<=N<=50 for the size of the delta encoding. */ | |
528 | private int | |
529 | tile_diff(byte *old_data, byte *new_data, uint tsize, byte _ss *delta) | |
530 | { register ushort *old2, *new2; | |
531 | register ushort diff; | |
532 | int count; | |
533 | register int i; | |
534 | byte _ss *pd; | |
535 | if ( tsize > 128 ) return -1; | |
536 | old2 = (ushort *)old_data; | |
537 | new2 = (ushort *)new_data; | |
538 | count = 0; | |
539 | pd = delta + 2; /* skip slot index */ | |
540 | for ( i = 0; i < tsize; i += 2, old2++, new2++ ) | |
541 | if ( (diff = *new2 ^ *old2) != 0 ) | |
542 | #if arch_is_big_endian | |
543 | # define i_hi 0 | |
544 | # define b_0(w) ((w) >> 8) | |
545 | # define b_1(w) ((byte)(w)) | |
546 | #else | |
547 | # define i_hi 1 | |
548 | # define b_0(w) ((byte)(w)) | |
549 | # define b_1(w) ((w) >> 8) | |
550 | #endif | |
551 | { if ( count == 16 ) return -1; | |
552 | if ( diff & 0xff00 ) | |
553 | { if ( diff & 0xff ) | |
554 | *pd++ = 0x80 + i, | |
555 | *pd++ = b_0(diff), | |
556 | *pd++ = b_1(diff); | |
557 | else | |
558 | *pd++ = i + i_hi, *pd++ = diff >> 8; | |
559 | } | |
560 | else /* know diff != 0 */ | |
561 | *pd++ = i + (1 - i_hi), *pd++ = (byte)diff; | |
562 | count++; | |
563 | } | |
564 | #undef b_0 | |
565 | #undef b_1 | |
566 | #undef i_hi | |
567 | delta[0] = (byte)cmd_op_delta_tile_bits + count - 1; | |
568 | return pd - delta; | |
569 | } | |
570 | ||
571 | /* Handle changing tiles for clist_tile_rectangle. */ | |
572 | /* We put this in a separate routine, even though it is called only once, */ | |
573 | /* to avoid cluttering up the main-line case of tile_rectangle. */ | |
574 | private int | |
575 | clist_change_tile(gx_device_clist *cldev, gx_clist_state *pcls, | |
576 | gx_bitmap *tile) | |
577 | { uint tile_size = tile->raster * tile->size.y; | |
578 | tile_slot *old_tile, *new_tile; | |
579 | int slot_index; | |
580 | /* Look up the tile in the cache. */ | |
581 | top: { gx_bitmap_id id = tile->id; | |
582 | uint probe = (uint)(id >> 16) + (uint)(id); | |
583 | old_tile = pcls->tile; | |
584 | for ( ; ; probe += 25 /* semi-random odd # */ ) | |
585 | { tile_hash *hptr = cldev->tile_hash_table + | |
586 | (probe & cldev->tile_hash_mask); | |
587 | if ( (slot_index = hptr->slot_index) < 0 ) /* empty entry */ | |
588 | { /* Must change tiles. Check whether the */ | |
589 | /* tile size has changed. */ | |
590 | if ( tile->size.x != cldev->tile.size.x || | |
591 | tile->size.y != cldev->tile.size.y | |
592 | ) | |
593 | { if ( tile->raster != | |
594 | ((tile->size.x + 31) >> 5) << 2 || | |
595 | tile_size > cldev->tile_max_size | |
596 | ) | |
597 | return -1; | |
598 | cldev->tile = *tile; /* reset size, raster */ | |
599 | clist_init_tiles(cldev); | |
600 | goto top; | |
601 | } | |
602 | if ( cldev->tile_count == cldev->tile_max_count ) | |
603 | { /* Punt. */ | |
604 | clist_init_tiles(cldev); | |
605 | goto top; | |
606 | } | |
607 | hptr->slot_index = slot_index = | |
608 | cldev->tile_count++; | |
609 | new_tile = tile_slot_ptr(cldev, slot_index); | |
610 | new_tile->id = id; | |
611 | memcpy(ts_bits(cldev, new_tile), tile->data, tile_size); | |
612 | count_add1(cmd_tile_added); | |
613 | #ifdef DEBUG | |
614 | if ( gs_debug['L'] ) | |
615 | dprintf3("[L]adding tile %d, hash=%d, id=%lx\n", | |
616 | slot_index, | |
617 | (int)(hptr - cldev->tile_hash_table), | |
618 | id); | |
619 | #endif | |
620 | break; | |
621 | } | |
622 | new_tile = tile_slot_ptr(cldev, slot_index); | |
623 | if ( new_tile->id == id ) | |
624 | { count_add1(cmd_tile_found); | |
625 | #ifdef DEBUG | |
626 | if ( gs_debug['L'] ) | |
627 | dprintf1("[L]found tile %d\n", slot_index); | |
628 | #endif | |
629 | break; | |
630 | } | |
631 | } | |
632 | } | |
633 | /* Check whether this band knows about this tile yet. */ | |
634 | { int band_index = pcls - cldev->states; | |
635 | byte pmask = 1 << (band_index & 7); | |
636 | byte *ppresent = ts_mask(new_tile) + (band_index >> 3); | |
637 | if ( *ppresent & pmask ) | |
638 | { /* Tile is known, just put out the index. */ | |
639 | byte *dp = cmd_put_op(cldev, pcls, 2); | |
640 | count_op(*dp = cmd_op_set_tile_index); | |
641 | dp[1] = slot_index; | |
642 | } | |
643 | else | |
644 | { /* Tile is not known, put out the bits. Use a */ | |
645 | /* delta encoding or a short encoding if possible. */ | |
646 | byte *new_data = ts_bits(cldev, new_tile); | |
647 | byte *dp; | |
648 | byte delta[2+16*3]; | |
649 | int diff; | |
650 | *ppresent |= pmask; | |
651 | if ( old_tile != &no_tile && | |
652 | (diff = tile_diff(ts_bits(cldev, old_tile), new_data, tile_size, delta)) >= 0 | |
653 | ) | |
654 | { /* Use delta representation */ | |
655 | dp = cmd_put_op(cldev, pcls, diff); | |
656 | count_op(delta[0]); | |
657 | delta[1] = slot_index; | |
658 | memcpy(dp, delta, diff); | |
659 | count_add(cmd_delta_tile_count, diff - 2); | |
660 | } | |
661 | else | |
662 | { if ( old_tile == &no_tile ) | |
663 | { byte *dp = cmd_put_op(cldev, pcls, | |
664 | 1 + sizeof(cldev->tile.size)); | |
665 | count_op(*dp = (byte)cmd_opv_set_tile_size); | |
666 | memcpy(dp + 1, &cldev->tile.size, | |
667 | sizeof(cldev->tile.size)); | |
668 | } | |
669 | if ( tile->size.x <= 16 ) | |
670 | { dp = cmd_put_op(cldev, pcls, 2 + (tile_size >> 1)); | |
671 | cmd_put_short_bits(dp + 2, new_data, tile->raster, 2, tile->size.y); | |
672 | count_add(cmd_tile_count, tile_size >> 1); | |
673 | } | |
674 | else | |
675 | { dp = cmd_put_op(cldev, pcls, 2 + tile_size); | |
676 | memcpy(dp + 2, new_data, tile_size); | |
677 | count_add(cmd_tile_count, tile_size); | |
678 | } | |
679 | count_op(*dp = (byte)cmd_op_set_tile_bits); | |
680 | dp[1] = slot_index; | |
681 | } | |
682 | } | |
683 | } | |
684 | pcls->tile = new_tile; | |
685 | return 0; | |
686 | } | |
687 | private int | |
688 | clist_tile_rectangle(gx_device *dev, gx_bitmap *tile, int x, int y, | |
689 | int width, int height, gx_color_index color0, gx_color_index color1, | |
690 | int px, int py) | |
691 | { BEGIN_RECT | |
692 | if ( tile->id != pcls->tile->id ) | |
693 | { if ( clist_change_tile(cdev, pcls, tile) < 0 ) | |
694 | return gx_default_tile_rectangle(dev, tile, x, y, width, height, color0, color1, px, py); | |
695 | } | |
696 | if ( color0 != pcls->color0 || color1 != pcls->color1 ) | |
697 | cmd_set_colors(dev, pcls, color0, color1); | |
698 | if ( px != pcls->tile_phase.x || py != pcls->tile_phase.y ) | |
699 | { byte *dp = cmd_put_op(cdev, pcls, 1 + sizeof(pcls->tile_phase)); | |
700 | count_op(*dp = (byte)cmd_opv_set_tile_phase); | |
701 | pcls->tile_phase.x = px; | |
702 | pcls->tile_phase.y = py; | |
703 | memcpy(dp + 1, &pcls->tile_phase, sizeof(pcls->tile_phase)); | |
704 | } | |
705 | cmd_write_rect_cmd(dev, pcls, cmd_op_tile_rect, x, y, width, height); | |
706 | END_RECT | |
707 | return 0; | |
708 | } | |
709 | ||
710 | private int | |
711 | clist_copy_mono(gx_device *dev, | |
712 | byte *data, int data_x, int raster, gx_bitmap_id id, | |
713 | int x, int y, int width, int height, | |
714 | gx_color_index color0, gx_color_index color1) | |
715 | { int y0 = y; | |
716 | BEGIN_RECT | |
717 | gx_cmd_rect rect; | |
718 | uint dsize; | |
719 | int bwidth; | |
720 | byte *row = data + (y - y0) * raster; | |
721 | byte *dp; | |
722 | if ( color0 != pcls->color0 || color1 != pcls->color1 ) | |
723 | cmd_set_colors(dev, pcls, color0, color1); | |
724 | cmd_set_rect(rect); | |
725 | if ( width >= 2 && (bwidth = (width + (data_x & 7) + 7) >> 3) <= 3 && | |
726 | height <= min(255, (cbuf_size - (1 + 2 * 2 + 2)) / bwidth) | |
727 | ) | |
728 | { dsize = height * bwidth; | |
729 | dp = cmd_put_op(cdev, pcls, 1 + 2 * 2 + 2 + dsize); | |
730 | count_op(*dp++ = (byte)cmd_op_copy_mono + (data_x & 7) + 1); | |
731 | cmd_putw(x, dp); | |
732 | cmd_putw(y, dp); | |
733 | *dp++ = width; | |
734 | *dp++ = height; | |
735 | row += data_x >> 3; | |
736 | cmd_put_short_bits(dp, row, raster, bwidth, height); | |
737 | } | |
738 | else | |
739 | { dsize = height * raster; | |
740 | if ( dsize > cbuf_size ) | |
741 | { /* We have to split it into pieces. */ | |
742 | if ( height > 1 ) | |
743 | { int h2 = height >> 1; | |
744 | clist_copy_mono(dev, data, data_x, raster, | |
745 | gx_no_bitmap_id, x, y, width, h2, | |
746 | color0, color1); | |
747 | return clist_copy_mono(dev, data + h2 * raster, | |
748 | data_x, raster, gx_no_bitmap_id, | |
749 | x, y + h2, width, height - h2, | |
750 | color0, color1); | |
751 | } | |
752 | /* Split a single (very long) row. */ | |
753 | { int w2 = width >> 1; | |
754 | clist_copy_mono(dev, data, data_x, raster, | |
755 | gx_no_bitmap_id, x, y, w2, 1, | |
756 | color0, color1); | |
757 | return clist_copy_mono(dev, data, data_x + w2, | |
758 | raster, gx_no_bitmap_id, x + w2, y, | |
759 | width - w2, 1, color0, color1); | |
760 | } | |
761 | } | |
762 | dp = cmd_put_op(cdev, pcls, 1 + sizeof(rect) + 4 + dsize); | |
763 | count_op(*dp++ = (byte)cmd_op_copy_mono); | |
764 | memcpy(dp, (byte *)&rect, sizeof(rect)); | |
765 | dp += sizeof(rect); | |
766 | cmd_putw(data_x, dp); | |
767 | cmd_putw(raster, dp); | |
768 | memcpy(dp, row, dsize); | |
769 | } | |
770 | pcls->rect = rect; | |
771 | count_add(cmd_copy_count, dsize); | |
772 | END_RECT | |
773 | return 0; | |
774 | } | |
775 | ||
776 | private int | |
777 | clist_copy_color(gx_device *dev, | |
778 | byte *data, int data_x, int raster, gx_bitmap_id id, | |
779 | int x, int y, int width, int height) | |
780 | { int y0 = y; | |
781 | BEGIN_RECT | |
782 | gx_cmd_rect rect; | |
783 | uint dsize = height * raster; | |
784 | byte *dp; | |
785 | if ( dsize > cbuf_size ) | |
786 | { /* We have to split it into pieces. */ | |
787 | if ( height > 1 ) | |
788 | { int h2 = height >> 1; | |
789 | clist_copy_color(dev, data, data_x, raster, | |
790 | gx_no_bitmap_id, x, y, width, h2); | |
791 | return clist_copy_color(dev, data + h2 * raster, gx_no_bitmap_id, | |
792 | data_x, raster, x, y + h2, width, height - h2); | |
793 | } | |
794 | /* Split a single (very long) row. */ | |
795 | { int w2 = width >> 1; | |
796 | clist_copy_color(dev, data, data_x, raster, | |
797 | gx_no_bitmap_id, x, y, w2, 1); | |
798 | return clist_copy_color(dev, data, data_x + w2, | |
799 | raster, gx_no_bitmap_id, x + w2, y, | |
800 | width - w2, 1); | |
801 | } | |
802 | ||
803 | } | |
804 | cmd_set_rect(rect); | |
805 | dp = cmd_put_op(cdev, pcls, 1 + sizeof(rect) + 4 + dsize); | |
806 | count_op(*dp++ = (byte)cmd_op_copy_color); | |
807 | memcpy(dp, (byte *)&rect, sizeof(rect)); | |
808 | pcls->rect = rect; | |
809 | dp += sizeof(rect); | |
810 | cmd_putw(data_x, dp); | |
811 | cmd_putw(raster, dp); | |
812 | memcpy(dp, data + (y - y0) * raster, dsize); | |
813 | END_RECT | |
814 | return 0; | |
815 | } | |
816 | ||
817 | /* ------ Reading/rendering ------ */ | |
818 | ||
819 | /* Clean up after rendering a page. */ | |
820 | private int | |
821 | clist_output_page(gx_device *dev, int num_copies, int flush) | |
822 | { if ( flush ) | |
823 | { rewind(cdev->cfile); | |
824 | rewind(cdev->bfile); | |
825 | cdev->bfile_end_pos = 0; | |
826 | } | |
827 | else | |
828 | { fseek(cdev->cfile, 0L, SEEK_END); | |
829 | fseek(cdev->bfile, 0L, SEEK_END); | |
830 | } | |
831 | return clist_open(dev); /* reinitialize */ | |
832 | } | |
833 | ||
834 | private int clist_render_init(P1(gx_device_clist *)); | |
835 | private int clist_render(P3(gx_device_clist *, gx_device *, int)); | |
836 | ||
837 | /* Copy scan lines to the client. This is where rendering gets done. */ | |
838 | private int | |
839 | clist_get_bits(gx_device *dev, int start_y, | |
840 | byte *str, uint size, int pad_to_word) | |
841 | { int y = start_y; | |
842 | byte *dest = str; | |
843 | gx_device_memory *mdev = &cdev->mdev; | |
844 | uint bytes_per_line; | |
845 | uint count, left; | |
846 | /* Initialize for rendering if we haven't done so yet. */ | |
847 | if ( cdev->ymin < 0 ) | |
848 | clist_render_init(cdev); | |
849 | bytes_per_line = gx_device_bytes_per_scan_line((gx_device *)mdev, | |
850 | pad_to_word); | |
851 | count = min(size / bytes_per_line, | |
852 | cdev->target->height - start_y); | |
853 | /* Render bands and copy them incrementally. */ | |
854 | for ( left = count; left; ) | |
855 | { int n; | |
856 | if ( !(y >= cdev->ymin && y < cdev->ymax) ) | |
857 | { int band = y / mdev->height; | |
858 | int code; | |
859 | rewind(cdev->bfile); | |
860 | (*mdev->procs->open_device)((gx_device *)mdev); /* reinitialize */ | |
861 | code = clist_render(cdev, (gx_device *)mdev, band); | |
862 | if ( code < 0 ) return code; | |
863 | cdev->ymin = band * mdev->height; | |
864 | cdev->ymax = cdev->ymin + mdev->height; | |
865 | } | |
866 | n = min(cdev->ymax - y, left); | |
867 | (*mdev->procs->get_bits)((gx_device *)mdev, | |
868 | y - cdev->ymin, dest, | |
869 | bytes_per_line * n, pad_to_word); | |
870 | y += n, dest += bytes_per_line * n, left -= n; | |
871 | } | |
872 | return count; | |
873 | } | |
874 | ||
875 | #undef cdev | |
876 | ||
877 | /* Initialize for reading. */ | |
878 | private int | |
879 | clist_render_init(gx_device_clist *cdev) | |
880 | { gx_device *target = cdev->target; | |
881 | byte *base = cdev->mdev.base; /* save */ | |
882 | int depth = target->color_info.depth; | |
883 | uint raster = ((target->width * depth + 31) >> 5) << 2; | |
884 | gx_device_memory *mdev = gdev_mem_device_for_bits(depth); | |
885 | if ( mdev == 0 ) | |
886 | return_error(gs_error_rangecheck); | |
887 | cmd_write_buffer(cdev); /* flush buffer */ | |
888 | /* Write the terminating entry in the block file. */ | |
889 | /* Note that because of copypage, there may be many such entries. */ | |
890 | { cmd_block cb; | |
891 | cb.band = -1; | |
892 | cb.pos = ftell(cdev->cfile); | |
893 | clist_write(cdev->bfile, (byte *)&cb, sizeof(cb)); | |
894 | cdev->bfile_end_pos = ftell(cdev->bfile); | |
895 | } | |
896 | cdev->mdev = *mdev; | |
897 | cdev->mdev.base = base; /* restore */ | |
898 | (*target->procs->get_initial_matrix)(target, &cdev->mdev.initial_matrix); | |
899 | cdev->mdev.width = target->width; | |
900 | cdev->mdev.height = cdev->band_height; | |
901 | cdev->mdev.raster = raster; | |
902 | cdev->ymin = cdev->ymax = 0; | |
903 | #ifdef DEBUG | |
904 | if ( gs_debug['l'] | gs_debug['L'] ) | |
905 | { int ci, cj; | |
906 | dprintf3("[l]counts: tile = %ld, copy = %ld, delta = %ld\n", | |
907 | cmd_tile_count, cmd_copy_count, cmd_delta_tile_count); | |
908 | dprintf3(" reset = %ld, found = %ld, added = %ld\n", | |
909 | cmd_tile_reset, cmd_tile_found, cmd_tile_added); | |
910 | for ( ci = 0; ci < 0x100; ci += 0x10 ) | |
911 | { dprintf1("[l] %s =", cmd_op_names[ci >> 4]); | |
912 | for ( cj = ci; cj < ci + 0x10; cj++ ) | |
913 | dprintf1(" %ld", cmd_op_counts[cj]); | |
914 | dputs("\n"); | |
915 | } | |
916 | } | |
917 | #endif | |
918 | return 0; | |
919 | } | |
920 | ||
921 | /* Render one band to a specified target device. */ | |
922 | #define assign_getw(var, p)\ | |
923 | (var = *p + ((uint)p[1] << 8), p += 2) | |
924 | typedef byte _ss *cb_ptr; | |
925 | private void clist_read(P3(FILE *, byte *, uint)); | |
926 | private cb_ptr clist_read_short_bits(P6(FILE *, byte *, int, int, cb_ptr, cb_ptr)); | |
927 | private int | |
928 | clist_render(gx_device_clist *cdev, gx_device *tdev, int band) | |
929 | { byte cbuf[cbuf_size]; | |
930 | byte bits[4 * 255]; /* for short copy_mono bits */ | |
931 | register cb_ptr cbp; | |
932 | cb_ptr cb_limit; | |
933 | cb_ptr cb_end; | |
934 | FILE *file = cdev->cfile; | |
935 | FILE *bfile = cdev->bfile; | |
936 | int y0 = band * cdev->band_height; | |
937 | gx_clist_state state; | |
938 | gx_bitmap state_tile; | |
939 | uint tile_bits_size; /* size of bits of each tile */ | |
940 | gs_int_point tile_phase; | |
941 | cmd_block b_this; | |
942 | long pos; | |
943 | uint left; | |
944 | #define cmd_read_var(ptr, cbp)\ | |
945 | memcpy(ptr, cbp, sizeof(*ptr)),\ | |
946 | cbp += sizeof(*ptr) | |
947 | #define cmd_read(ptr, vsize, cbp)\ | |
948 | if ( cb_end - cbp >= vsize )\ | |
949 | memcpy(ptr, cbp, vsize), cbp += vsize;\ | |
950 | else\ | |
951 | { uint cleft = cb_end - cbp;\ | |
952 | memcpy(ptr, cbp, cleft); vsize -= cleft;\ | |
953 | clist_read(file, ptr + cleft, vsize);\ | |
954 | cbp = cb_end;\ | |
955 | } | |
956 | #define cmd_read_short_bits(ptr, bw, ht, cbp)\ | |
957 | cbp = clist_read_short_bits(file, ptr, bw, ht, cbp, cb_end) | |
958 | state = cls_initial; | |
959 | state_tile.id = 0; | |
960 | tile_phase.x = tile_phase.y = 0; | |
961 | trd: clist_read(bfile, (byte *)&b_this, sizeof(b_this)); | |
962 | top: /* Find the next run of commands for this band. */ | |
963 | if ( b_this.band < 0 && ftell(bfile) == cdev->bfile_end_pos ) | |
964 | return 0; /* end of bfile */ | |
965 | if ( b_this.band != band ) goto trd; | |
966 | pos = b_this.pos; | |
967 | clist_read(bfile, (byte *)&b_this, sizeof(b_this)); | |
968 | fseek(file, pos, SEEK_SET); | |
969 | left = (uint)(b_this.pos - pos); | |
970 | cb_limit = cbuf + (cbuf_size - cmd_largest_size); | |
971 | cb_end = cbuf + cbuf_size; | |
972 | cbp = cb_end; | |
973 | for ( ; ; ) | |
974 | { int op; | |
975 | uint bytes; | |
976 | int data_x, raster; | |
977 | int code; | |
978 | cb_ptr source; | |
979 | gx_color_index _ss *pcolor; | |
980 | /* Make sure the buffer contains a full command. */ | |
981 | if ( cbp > cb_limit ) | |
982 | { uint nread; | |
983 | memcpy(cbuf, cbp, cb_end - cbp); | |
984 | cbp = cbuf + (cb_end - cbp); | |
985 | nread = cb_end - cbp; | |
986 | if ( nread > left ) nread = left; | |
987 | clist_read(file, cbp, nread); | |
988 | cb_end = cbp + nread; | |
989 | cbp = cbuf; | |
990 | left -= nread; | |
991 | if ( cb_limit > cb_end ) cb_limit = cb_end; | |
992 | } | |
993 | op = *cbp++; | |
994 | #ifdef DEBUG | |
995 | if ( gs_debug['L'] ) | |
996 | dprintf2("[L]%s %d:\n", cmd_op_names[op >> 4], op & 0xf); | |
997 | #endif | |
998 | switch ( op >> 4 ) | |
999 | { | |
1000 | case cmd_op_misc >> 4: | |
1001 | switch ( op ) | |
1002 | { | |
1003 | case cmd_opv_end_run: | |
1004 | goto top; | |
1005 | case cmd_opv_set_tile_size: | |
1006 | cmd_read_var(&state_tile.size, cbp); | |
1007 | state_tile.raster = ((state_tile.size.x + 31) >> 5) << 2; | |
1008 | /* We can't actually know the rep_size, */ | |
1009 | /* so we play it safe. */ | |
1010 | state_tile.rep_width = state_tile.size.x; | |
1011 | state_tile.rep_height = state_tile.size.y; | |
1012 | cdev->tile_slot_size = tile_bits_size = | |
1013 | state_tile.raster * state_tile.size.y; | |
1014 | break; | |
1015 | case cmd_opv_set_tile_phase: | |
1016 | cmd_read_var(&state.tile_phase, cbp); | |
1017 | break; | |
1018 | default: | |
1019 | goto bad_op; | |
1020 | } | |
1021 | tile_phase.x = state.tile_phase.x % state_tile.size.x; | |
1022 | tile_phase.y = (state.tile_phase.y + y0) % state_tile.size.y; | |
1023 | continue; | |
1024 | case cmd_op_set_color0 >> 4: | |
1025 | pcolor = &state.color0; | |
1026 | goto set_color; | |
1027 | case cmd_op_set_color1 >> 4: | |
1028 | pcolor = &state.color1; | |
1029 | set_color: if ( op & 0xf ) | |
1030 | *pcolor = (gx_color_index)(long)((op & 0xf) - 2); | |
1031 | else | |
1032 | cmd_read_var(pcolor, cbp); | |
1033 | continue; | |
1034 | case cmd_op_set_tile_index >> 4: | |
1035 | state_tile.data = (byte *)tile_slot_ptr(cdev, *cbp); | |
1036 | cbp++; | |
1037 | continue; | |
1038 | case cmd_op_copy_mono >> 4: | |
1039 | if ( op & 0xf ) | |
1040 | { assign_getw(state.rect.x, cbp); | |
1041 | assign_getw(state.rect.y, cbp); | |
1042 | state.rect.width = *cbp++; | |
1043 | state.rect.height = *cbp++; | |
1044 | break; | |
1045 | } | |
1046 | /* falls through */ | |
1047 | case cmd_op_fill_rect >> 4: | |
1048 | case cmd_op_tile_rect >> 4: | |
1049 | case cmd_op_copy_color >> 4: | |
1050 | cmd_read_var(&state.rect, cbp); | |
1051 | break; | |
1052 | case cmd_op_fill_rect_short >> 4: | |
1053 | case cmd_op_tile_rect_short >> 4: | |
1054 | state.rect.x += *cbp + cmd_min_short; | |
1055 | state.rect.width += cbp[1] + cmd_min_short; | |
1056 | if ( op & 0xf ) | |
1057 | { state.rect.height += (op & 0xf) + cmd_min_tiny; | |
1058 | cbp += 2; | |
1059 | } | |
1060 | else | |
1061 | { state.rect.y += cbp[2] + cmd_min_short; | |
1062 | state.rect.height += cbp[3] + cmd_min_short; | |
1063 | cbp += 4; | |
1064 | } | |
1065 | break; | |
1066 | case cmd_op_fill_rect_tiny >> 4: | |
1067 | case cmd_op_tile_rect_tiny >> 4: | |
1068 | { int txy = *cbp++; | |
1069 | state.rect.x += (txy >> 4) + cmd_min_tiny; | |
1070 | state.rect.y += (txy & 0xf) + cmd_min_tiny; | |
1071 | state.rect.width += (op & 0xf) + cmd_min_tiny; | |
1072 | } break; | |
1073 | case cmd_op_set_tile_bits >> 4: | |
1074 | state_tile.data = (byte *)tile_slot_ptr(cdev, *cbp); | |
1075 | cbp++; | |
1076 | if ( state_tile.size.x <= 16 ) | |
1077 | { cmd_read_short_bits(state_tile.data, 2, state_tile.size.y, cbp); | |
1078 | } | |
1079 | else | |
1080 | { bytes = tile_bits_size; | |
1081 | cmd_read(state_tile.data, bytes, cbp); | |
1082 | } | |
1083 | #ifdef DEBUG | |
1084 | if ( gs_debug['L'] ) | |
1085 | cmd_print_bits(state_tile.data, state_tile.size.y, | |
1086 | state_tile.raster); | |
1087 | #endif | |
1088 | continue; | |
1089 | case cmd_op_delta_tile_bits >> 4: | |
1090 | { byte *new_data = (byte *)tile_slot_ptr(cdev, *cbp); | |
1091 | cbp++; | |
1092 | memcpy(new_data, state_tile.data, tile_bits_size); | |
1093 | state_tile.data = new_data; | |
1094 | do | |
1095 | { uint offset = *cbp; | |
1096 | if ( offset < 0x80 ) | |
1097 | new_data[offset] ^= cbp[1], | |
1098 | cbp += 2; | |
1099 | else | |
1100 | offset -= 0x80, | |
1101 | new_data[offset] ^= cbp[1], | |
1102 | new_data[offset + 1] ^= cbp[2], | |
1103 | cbp += 3; | |
1104 | } | |
1105 | while ( op-- & 0xf ); | |
1106 | } continue; | |
1107 | default: | |
1108 | bad_op: printf/*lprintf5*/("Bad op %02x band %d file pos %ld buf pos %d/%d\n", | |
1109 | op, band, ftell(file), (int)(cbp - cbuf), (int)(cb_end - cbuf)); | |
1110 | { cb_ptr pp; | |
1111 | for ( pp = cbuf; pp < cb_end; pp += 10 ) | |
1112 | printf/*lprintf10*/(" %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", | |
1113 | pp[0], pp[1], pp[2], pp[3], pp[4], | |
1114 | pp[5], pp[6], pp[7], pp[8], pp[9]); | |
1115 | } | |
1116 | ||
1117 | gs_exit(1); | |
1118 | return -1; | |
1119 | } | |
1120 | #ifdef DEBUG | |
1121 | if ( gs_debug['L'] ) | |
1122 | dprintf4("[L] x=%d y=%d w=%d h=%d\n", | |
1123 | state.rect.x, state.rect.y, state.rect.width, | |
1124 | state.rect.height); | |
1125 | #endif | |
1126 | switch ( op >> 4 ) | |
1127 | { | |
1128 | case cmd_op_fill_rect >> 4: | |
1129 | case cmd_op_fill_rect_short >> 4: | |
1130 | case cmd_op_fill_rect_tiny >> 4: | |
1131 | code = (*tdev->procs->fill_rectangle) | |
1132 | (tdev, state.rect.x, state.rect.y - y0, | |
1133 | state.rect.width, state.rect.height, state.color1); | |
1134 | break; | |
1135 | case cmd_op_tile_rect >> 4: | |
1136 | case cmd_op_tile_rect_short >> 4: | |
1137 | case cmd_op_tile_rect_tiny >> 4: | |
1138 | code = (*tdev->procs->tile_rectangle) | |
1139 | (tdev, &state_tile, | |
1140 | state.rect.x, state.rect.y - y0, | |
1141 | state.rect.width, state.rect.height, | |
1142 | state.color0, state.color1, | |
1143 | tile_phase.x, tile_phase.y); | |
1144 | break; | |
1145 | case cmd_op_copy_mono >> 4: | |
1146 | if ( op & 0xf ) | |
1147 | { data_x = (op & 0xf) - 1; | |
1148 | raster = 4; | |
1149 | cmd_read_short_bits(bits, (data_x + state.rect.width + 7) >> 3, state.rect.height, cbp); | |
1150 | source = bits; | |
1151 | goto copy; | |
1152 | } | |
1153 | /* falls through */ | |
1154 | case cmd_op_copy_color >> 4: | |
1155 | assign_getw(data_x, cbp); | |
1156 | assign_getw(raster, cbp); | |
1157 | bytes = state.rect.height * raster; | |
1158 | /* copy_mono and copy_color have ensured that */ | |
1159 | /* the bits will fit in a single buffer. */ | |
1160 | cmd_read(cbuf, bytes, cbp); | |
1161 | source = cbuf; | |
1162 | copy: | |
1163 | #ifdef DEBUG | |
1164 | if ( gs_debug['L'] ) | |
1165 | { dprintf2("[L] data_x=%d raster=%d\n", | |
1166 | data_x, raster); | |
1167 | cmd_print_bits(source, state.rect.height, raster); | |
1168 | } | |
1169 | #endif | |
1170 | code = (op >> 4 == (byte)cmd_op_copy_mono >> 4 ? | |
1171 | (*tdev->procs->copy_mono) | |
1172 | (tdev, source, data_x, raster, gx_no_bitmap_id, | |
1173 | state.rect.x, state.rect.y - y0, | |
1174 | state.rect.width, state.rect.height, | |
1175 | state.color0, state.color1) : | |
1176 | (*tdev->procs->copy_color) | |
1177 | (tdev, source, data_x, raster, gx_no_bitmap_id, | |
1178 | state.rect.x, state.rect.y - y0, | |
1179 | state.rect.width, state.rect.height)); | |
1180 | break; | |
1181 | } | |
1182 | if ( code < 0 ) return_error(code); | |
1183 | } | |
1184 | } | |
1185 | /* The typical implementations of fread and fseek */ | |
1186 | /* are extremely inefficient for small counts, */ | |
1187 | /* so we use loops instead. */ | |
1188 | private void | |
1189 | clist_read(FILE *f, byte *str, uint len) | |
1190 | { switch ( len ) | |
1191 | { | |
1192 | default: fread(str, 1, len, f); break; | |
1193 | case 8: *str++ = (byte)getc(f); | |
1194 | case 7: *str++ = (byte)getc(f); | |
1195 | case 6: *str++ = (byte)getc(f); | |
1196 | case 5: *str++ = (byte)getc(f); | |
1197 | case 4: *str++ = (byte)getc(f); | |
1198 | case 3: *str++ = (byte)getc(f); | |
1199 | case 2: *str++ = (byte)getc(f); | |
1200 | case 1: *str = (byte)getc(f); | |
1201 | } | |
1202 | } | |
1203 | /* Read a short bitmap */ | |
1204 | private cb_ptr | |
1205 | clist_read_short_bits(FILE *file, byte *data, register int bwidth, int height, | |
1206 | cb_ptr cbp, cb_ptr cb_end) | |
1207 | { uint bytes = bwidth * height; | |
1208 | byte *pdata = data + bytes; | |
1209 | byte *udata = data + (height << 2); | |
1210 | cmd_read(data, bytes, cbp); | |
1211 | while ( --height > 0 ) /* first row is in place already */ | |
1212 | { udata -= 4, pdata -= bwidth; | |
1213 | switch ( bwidth ) | |
1214 | { | |
1215 | case 3: udata[2] = pdata[2]; | |
1216 | case 2: udata[1] = pdata[1]; | |
1217 | case 1: udata[0] = pdata[0]; | |
1218 | } | |
1219 | } | |
1220 | return cbp; | |
1221 | } |