/src/ghostpdl/pcl/pxl/pximage.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2026 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 | | /* pximage.c */ |
18 | | /* PCL XL bitmap painting operators */ |
19 | | |
20 | | #include "std.h" |
21 | | #include "string_.h" |
22 | | #include "pxerrors.h" |
23 | | #include "pxoper.h" |
24 | | #include "pxstate.h" |
25 | | #include "gdebug.h" |
26 | | #include "gsrop.h" |
27 | | #include "gsrefct.h" |
28 | | #include "gsstruct.h" |
29 | | #include "gsstate.h" |
30 | | #include "gscoord.h" |
31 | | #include "gsimage.h" |
32 | | #include "gspaint.h" |
33 | | #include "gspath.h" |
34 | | #include "gspath2.h" |
35 | | #include "gsuid.h" /* for gxpcolor.h */ |
36 | | #include "gsutil.h" |
37 | | #include "gxbitmap.h" |
38 | | #include "gxcspace.h" |
39 | | #include "gxdevice.h" /* for gxpcolor.h */ |
40 | | #include "gxcolor2.h" |
41 | | #include "gxdcolor.h" |
42 | | #include "gxpcolor.h" |
43 | | #include "scommon.h" |
44 | | #include "strimpl.h" |
45 | | #include "srlx.h" |
46 | | #include "jpeglib_.h" /* for jpeg filter */ |
47 | | #include "sdct.h" |
48 | | #include "sjpeg.h" |
49 | | #include "pxptable.h" |
50 | | #include "gxgstate.h" |
51 | | |
52 | | /* Define the "freeing" procedure for patterns in a dictionary. */ |
53 | | void |
54 | | px_free_pattern(gs_memory_t * mem, void *vptr, client_name_t cname) |
55 | 170 | { |
56 | 170 | px_pattern_t *pattern = vptr; |
57 | | |
58 | 170 | rc_decrement(pattern, cname); |
59 | 170 | } |
60 | | /* Define the real freeing procedure for patterns. */ |
61 | | static void |
62 | | rc_free_px_pattern(gs_memory_t * mem, void *vptr, client_name_t cname) |
63 | 170 | { |
64 | 170 | px_pattern_t *pattern = vptr; |
65 | | |
66 | 170 | gs_free_string(mem, (void *)pattern->palette.data, pattern->palette.size, |
67 | 170 | cname); |
68 | 170 | gs_free_object(mem, pattern->data, cname); |
69 | 170 | gs_free_object(mem, pattern, cname); |
70 | 170 | } |
71 | | /* Define the purging procedure for the Pattern cache. */ |
72 | | /* The proc_data points to a pxePatternPersistence_t that specifies */ |
73 | | /* the maximum persistence level to purge. */ |
74 | | static bool |
75 | | px_pattern_purge_proc(gx_color_tile * ctile, void *proc_data) |
76 | 0 | { |
77 | 0 | return ctile->uid.id % pxePatternPersistence_next <= |
78 | 0 | *(pxePatternPersistence_t *) proc_data; |
79 | 0 | } |
80 | | void |
81 | | px_purge_pattern_cache(px_state_t * pxs, pxePatternPersistence_t max_persist) |
82 | 16.3k | { |
83 | 16.3k | gx_pattern_cache_winnow(gstate_pattern_cache(pxs->pgs), |
84 | 16.3k | px_pattern_purge_proc, (void *)&max_persist); |
85 | 16.3k | } |
86 | | |
87 | | /* active decompression types */ |
88 | | typedef enum |
89 | | { |
90 | | unset = 0, |
91 | | nocomp = 1, |
92 | | rle = 2, |
93 | | jpeg = 4, |
94 | | delta = 8, |
95 | | } decomp_init_t; |
96 | | |
97 | 0 | static inline bool nocomp_inited(uint comp) {return comp & nocomp;} |
98 | 0 | static inline bool rle_inited(uint comp) {return comp & rle;} |
99 | 0 | static inline bool jpeg_inited(uint comp) {return comp & jpeg;} |
100 | 0 | static inline bool delta_inited(uint comp) {return comp & delta;} |
101 | | |
102 | 0 | static inline void nocomp_set(uint *comp) {*comp |= nocomp;} |
103 | 0 | static inline void rle_set(uint *comp) {*comp |= rle;} |
104 | 0 | static inline void jpeg_set(uint *comp) {*comp |= jpeg;} |
105 | 0 | static inline void delta_set(uint *comp) {*comp |= delta;} |
106 | | |
107 | 0 | static inline void comp_unset(uint *comp) {*comp = unset;} |
108 | | |
109 | | /* pxl delta row decompression state machine states */ |
110 | | typedef enum |
111 | | { |
112 | | next_is_bytecount, |
113 | | partial_bytecount, |
114 | | next_is_cmd, |
115 | | partial_offset, |
116 | | partial_cnt |
117 | | } deltarow_parse_state_t; |
118 | | |
119 | | /* pxl delta row decompression state storage */ |
120 | | typedef struct deltarow_state_s |
121 | | { |
122 | | deltarow_parse_state_t state; |
123 | | uint row_byte_count; |
124 | | uint short_cnt; |
125 | | uint short_offset; |
126 | | byte *seedrow; |
127 | | uint rowwritten; |
128 | | } deltarow_state_t; |
129 | | |
130 | | /* Define the structure for enumerating a bitmap being downloaded. */ |
131 | | typedef struct px_bitmap_enum_s |
132 | | { |
133 | | gs_memory_t *mem; /* used only for the jpeg filter */ |
134 | | uint data_per_row; /* ditto minus possible trailing padding */ |
135 | | uint initialized; |
136 | | stream_RLD_state rld_stream_state; /* decompressor states */ |
137 | | stream_DCT_state dct_stream_state; |
138 | | jpeg_decompress_data jdd; |
139 | | deltarow_state_t deltarow_state; |
140 | | |
141 | | /* Entries to deal with 'rebuffering' - the idea of making |
142 | | * input image data appear a different (but equivalent) shape. |
143 | | * Currently just morphing '1xH' bitmaps into 'Hx1' ones. */ |
144 | | uint rebuffered_data_per_row; /* equal to data_per_row except when flipping */ |
145 | | bool rebuffered; |
146 | | uint rebuffered_width; |
147 | | uint rebuffered_height; |
148 | | int rebuffered_row_pos; |
149 | | bool grayscale; |
150 | | } px_bitmap_enum_t; |
151 | | |
152 | | typedef struct px_begin_image_args_s { |
153 | | uint width, height; |
154 | | int depth; |
155 | | pxeColorMapping_t mapping; |
156 | | real dest_width, dest_height; |
157 | | } px_bitmap_args_t; |
158 | | |
159 | | /* Define our image enumerator. */ |
160 | | #ifndef px_image_enum_DEFINED |
161 | | # define px_image_enum_DEFINED |
162 | | typedef struct px_image_enum_s px_image_enum_t; |
163 | | #endif |
164 | | struct px_image_enum_s |
165 | | { |
166 | | gs_image_t image; |
167 | | byte *row; /* buffer for one row of data */ |
168 | | gs_image_enum *ienum; /* state structure for driver */ |
169 | | px_bitmap_enum_t benum; |
170 | | px_bitmap_args_t bi_args; |
171 | | bool enum_started; |
172 | | }; |
173 | | |
174 | | gs_private_st_simple(st_px_image_enum, px_image_enum_t, "px_image_enum_t"); |
175 | | |
176 | | |
177 | | static int |
178 | | stream_error(stream_state * st, const char *str) |
179 | 0 | { |
180 | 0 | return_error(-1); |
181 | 0 | } |
182 | | |
183 | | static int |
184 | | px_jpeg_init(px_bitmap_enum_t * benum, px_bitmap_params_t * params, px_args_t * par) |
185 | 0 | { |
186 | 0 | stream_cursor_read r; |
187 | 0 | stream_cursor_write w; |
188 | 0 | uint used; |
189 | 0 | const byte *data = par->source.data; |
190 | 0 | stream_DCT_state *ss = (&benum->dct_stream_state); |
191 | 0 | jpeg_decompress_data *jddp = &(benum->jdd); |
192 | 0 | uint avail = par->source.available; |
193 | 0 | int code = 0; |
194 | |
|
195 | 0 | if (!jpeg_inited(benum->initialized)) { |
196 | 0 | s_init_state((stream_state *)ss, &s_DCTD_template, benum->mem); |
197 | 0 | ss->report_error = stream_error; |
198 | 0 | s_DCTD_template.set_defaults((stream_state *)ss); |
199 | |
|
200 | 0 | ss->jpeg_memory = benum->mem; |
201 | 0 | ss->data.decompress = jddp; |
202 | |
|
203 | 0 | jddp->templat = s_DCTD_template; |
204 | 0 | jddp->memory = benum->mem; |
205 | 0 | jddp->scanline_buffer = NULL; |
206 | 0 | jddp->PassThrough = 0; |
207 | 0 | jddp->PassThroughfn = 0; |
208 | 0 | jddp->device = (void *)0; |
209 | |
|
210 | 0 | if (gs_jpeg_create_decompress(ss) < 0) |
211 | 0 | return_error(errorInsufficientMemory); |
212 | | |
213 | 0 | (*s_DCTD_template.init) ((stream_state *)ss); |
214 | 0 | jpeg_set(&benum->initialized); |
215 | 0 | } |
216 | | |
217 | 0 | r.ptr = data - 1; |
218 | 0 | r.limit = r.ptr + avail; |
219 | | |
220 | | /* this seems to be the way to just get the header info */ |
221 | 0 | w.ptr = w.limit = 0; |
222 | 0 | code = |
223 | 0 | (*s_DCTD_template.process) ((stream_state *)ss, &r, &w, false); |
224 | |
|
225 | 0 | if (code < 0) |
226 | 0 | goto error; |
227 | | |
228 | 0 | used = r.ptr + 1 - data; |
229 | 0 | par->source.data = r.ptr + 1; |
230 | 0 | par->source.available = avail - used; |
231 | | |
232 | | /* Needs more data to complete reading the header */ |
233 | 0 | if (ss->phase < 2) |
234 | 0 | return pxNeedData; |
235 | | |
236 | 0 | params->width = jddp->dinfo.output_width; |
237 | 0 | params->height = jddp->dinfo.output_height; |
238 | 0 | { |
239 | 0 | int comps = jddp->dinfo.output_components; |
240 | 0 | if (comps == 1) { |
241 | 0 | params->color_space = eGray; |
242 | 0 | } else if (comps == 3) { |
243 | 0 | params->color_space = eRGB; |
244 | 0 | } |
245 | 0 | else { |
246 | 0 | code = -1; |
247 | 0 | goto error; |
248 | 0 | } |
249 | 0 | params->depth = 8; |
250 | 0 | return 0; |
251 | 0 | } |
252 | | |
253 | 0 | error: |
254 | 0 | gs_jpeg_destroy(ss); |
255 | 0 | return_error(code); |
256 | 0 | } |
257 | | |
258 | | /* Extract the parameters for reading a bitmap image or raster pattern. */ |
259 | | static int |
260 | | begin_bitmap(px_bitmap_params_t * params, px_bitmap_enum_t * benum, |
261 | | const px_bitmap_args_t * bar, const px_state_t * pxs, bool is_jpeg, px_args_t * par) |
262 | 191 | { |
263 | 191 | int depth; |
264 | 191 | int num_components; |
265 | 191 | px_gstate_t *pxgs = pxs->pxgs; |
266 | | |
267 | 191 | benum->mem = pxs->memory; |
268 | | |
269 | 191 | if (is_jpeg) { |
270 | 0 | int code = px_jpeg_init(benum, params, par); |
271 | 0 | if (code < 0 || code == pxNeedData) |
272 | 0 | return_error(code); |
273 | 0 | depth = params->depth; |
274 | 0 | num_components = (params->color_space == eGray ? 1 : 3); |
275 | 0 | if (num_components == 3 && pxgs->color_space == eGray) |
276 | 0 | benum->grayscale = true; |
277 | 191 | } else { |
278 | 191 | px_gstate_t *pxgs = pxs->pxgs; |
279 | 191 | depth = "\001\004\010"[bar->depth]; |
280 | 191 | num_components = (pxgs->color_space == eGray ? 1 : 3); |
281 | 191 | params->width = bar->width; |
282 | 191 | params->height = bar->height; |
283 | 191 | params->depth = depth; |
284 | 191 | params->color_space = pxgs->color_space; |
285 | 191 | } |
286 | | |
287 | 191 | if (bar->mapping == eIndexedPixel && !is_jpeg) { |
288 | 0 | if (pxgs->palette.data == 0) |
289 | 0 | return_error(errorMissingPalette); |
290 | 0 | if (pxgs->palette.size != (1 << depth) * num_components) |
291 | 0 | return_error(errorImagePaletteMismatch); |
292 | 0 | params->indexed = true; |
293 | 0 | num_components = 1; |
294 | 0 | } else |
295 | 191 | params->indexed = false; |
296 | | |
297 | 191 | params->dest_width = bar->dest_width; |
298 | 191 | params->dest_height = bar->dest_height; |
299 | 191 | benum->data_per_row = |
300 | 191 | ((params->width * params->depth * num_components) + 7) >> 3; |
301 | 191 | return 0; |
302 | 191 | } |
303 | | |
304 | | /* Extract the parameters for reading a bitmap image or raster pattern. */ |
305 | | /* Attributes: pxaColorMapping, pxaColorDepth, pxaSourceWidth, */ |
306 | | /* pxaSourceHeight, pxaDestinationSize. */ |
307 | | static int |
308 | | begin_rebuffered_bitmap(px_bitmap_params_t * params, px_bitmap_enum_t * benum, |
309 | | const px_bitmap_args_t * bar, const px_state_t * pxs, bool is_jpeg, px_args_t * par) |
310 | 0 | { |
311 | 0 | int code = begin_bitmap(params, benum, bar, pxs, is_jpeg, par); |
312 | |
|
313 | 0 | if (code < 0 || code == pxNeedData) |
314 | 0 | return_error(code); |
315 | | |
316 | 0 | if (params->width == 1 && params->height > 1) { |
317 | 0 | benum->rebuffered_data_per_row = benum->data_per_row * params->height; |
318 | 0 | benum->rebuffered = 1; |
319 | 0 | benum->rebuffered_width = params->height; |
320 | 0 | benum->rebuffered_height = params->width; |
321 | 0 | benum->rebuffered_row_pos = 0; |
322 | 0 | } else { |
323 | 0 | benum->rebuffered_data_per_row = benum->data_per_row; |
324 | 0 | benum->rebuffered = 0; |
325 | 0 | benum->rebuffered_width = params->width; |
326 | 0 | benum->rebuffered_height = params->height; |
327 | 0 | } |
328 | |
|
329 | 0 | return code; |
330 | 0 | } |
331 | | |
332 | | static int |
333 | | read_jpeg_bitmap_data(px_bitmap_enum_t * benum, byte ** pdata, |
334 | | px_args_t * par, bool last) |
335 | 0 | { |
336 | 0 | uint data_per_row = benum->data_per_row; /* jpeg doesn't pad */ |
337 | 0 | uint avail = par->source.available; |
338 | 0 | uint end_pos = data_per_row * par->pv[1]->value.i; |
339 | 0 | uint pos_in_row = par->source.position % data_per_row; |
340 | 0 | const byte *data = par->source.data; |
341 | 0 | stream_DCT_state *ss = (&benum->dct_stream_state); |
342 | 0 | stream_cursor_read r; |
343 | 0 | stream_cursor_write w; |
344 | 0 | uint used; |
345 | 0 | int code = -1; |
346 | | |
347 | | /* consumed all of the data */ |
348 | 0 | if ((par->source.position >= end_pos) && (ss->phase != 4) && (par->source.available == 0)) { |
349 | | /* shutdown jpeg filter if necessary */ |
350 | 0 | if (jpeg_inited(benum->initialized)) |
351 | 0 | gs_jpeg_destroy((&benum->dct_stream_state)); |
352 | 0 | return 0; |
353 | 0 | } |
354 | | |
355 | 0 | if (last) |
356 | 0 | return_error(errorIllegalDataLength); |
357 | | |
358 | 0 | if (!jpeg_inited(benum->initialized)) { |
359 | 0 | jpeg_decompress_data *jddp = &(benum->jdd); |
360 | | |
361 | | /* we do not allow switching from other compression schemes to jpeg */ |
362 | 0 | if (benum->initialized != unset) |
363 | 0 | return_error(errorIllegalAttributeValue); |
364 | | /* use the graphics library support for DCT streams */ |
365 | 0 | ss->memory = benum->mem; |
366 | 0 | ss->templat = &s_DCTD_template; |
367 | 0 | s_DCTD_template.set_defaults((stream_state *) ss); |
368 | 0 | ss->report_error = stream_error; |
369 | 0 | ss->data.decompress = jddp; |
370 | | /* set now for allocation */ |
371 | 0 | jddp->memory = ss->jpeg_memory = benum->mem; |
372 | | /* set this early for safe error exit */ |
373 | 0 | jddp->scanline_buffer = NULL; |
374 | 0 | jddp->PassThrough = 0; |
375 | 0 | jddp->PassThroughfn = 0; |
376 | 0 | jddp->device = (void *)0; |
377 | 0 | if (gs_jpeg_create_decompress(ss) < 0) |
378 | 0 | return_error(errorInsufficientMemory); |
379 | 0 | (*s_DCTD_template.init) ((stream_state *) ss); |
380 | 0 | jddp->templat = s_DCTD_template; |
381 | 0 | jpeg_set(&benum->initialized); |
382 | 0 | } |
383 | 0 | r.ptr = data - 1; |
384 | 0 | r.limit = r.ptr + avail; |
385 | 0 | if (pos_in_row < data_per_row) { |
386 | | /* Read more of the current row. */ |
387 | 0 | byte *data = *pdata; |
388 | |
|
389 | 0 | w.ptr = data + pos_in_row - 1; |
390 | 0 | w.limit = data + data_per_row - 1; |
391 | 0 | code = |
392 | 0 | (*s_DCTD_template.process) ((stream_state *) ss, &r, &w, false); |
393 | | /* code = num scanlines processed (0=need more data, -ve=error) */ |
394 | 0 | used = w.ptr + 1 - data - pos_in_row; |
395 | | |
396 | | /* the filter process returns 1 to indicate more room is |
397 | | * needed for the output data, which should not happen unless |
398 | | * the parameters are specified incorrectly. |
399 | | */ |
400 | |
|
401 | 0 | if (code == 1) |
402 | 0 | code = -1; |
403 | 0 | if ((code == EOFC) && (used > 0)) |
404 | 0 | code = 1; |
405 | 0 | pos_in_row += used; |
406 | 0 | par->source.position += used; |
407 | 0 | } |
408 | 0 | used = r.ptr + 1 - data; |
409 | 0 | par->source.data = r.ptr + 1; |
410 | 0 | par->source.available = avail - used; |
411 | | /* The spec for this function says: Return 0 if we've processed all the |
412 | | * data in the block, 1 if we have a complete scan line, pxNeedData for |
413 | | * an incomplete scan line, or <0 for an error condition. The only way |
414 | | * we can return 0 currently is if we get an EOF from the underlying |
415 | | * decoder. */ |
416 | 0 | if (code == 0) |
417 | 0 | return pxNeedData; |
418 | 0 | if (code == EOFC) |
419 | 0 | return 0; |
420 | 0 | if (code > 0) |
421 | 0 | return 1; |
422 | 0 | return code; |
423 | 0 | } |
424 | | |
425 | | static int |
426 | | read_uncompressed_bitmap_data(px_bitmap_enum_t * benum, byte ** pdata, |
427 | | px_args_t * par, bool last) |
428 | 1.82k | { |
429 | 1.82k | int code; |
430 | 1.82k | uint avail = par->source.available; |
431 | 1.82k | uint data_per_row = benum->data_per_row; |
432 | 1.82k | uint pad = 4; /* default padding */ |
433 | 1.82k | const byte *data = par->source.data; |
434 | 1.82k | uint pos_in_row, data_per_row_padded, used; |
435 | | |
436 | | /* overrided default padding */ |
437 | 1.82k | if (par->pv[3]) |
438 | 0 | pad = par->pv[3]->value.i; |
439 | | |
440 | 1.82k | data_per_row_padded = round_up(data_per_row, (int)pad); |
441 | 1.82k | pos_in_row = par->source.position % data_per_row_padded; |
442 | | |
443 | | /* consumed all of the data */ |
444 | 1.82k | if (par->pv[1]->value.i < 0 || |
445 | 1.82k | par->source.position >= data_per_row_padded * (ulong)par->pv[1]->value.i) |
446 | 182 | return 0; |
447 | | |
448 | 1.64k | if (last) |
449 | 0 | return_error(errorIllegalDataLength); |
450 | | |
451 | 1.64k | if (avail >= data_per_row_padded && pos_in_row == 0) { |
452 | | /* Use the data directly from the input buffer. */ |
453 | 1.45k | *pdata = (byte *) data; |
454 | 1.45k | used = data_per_row_padded; |
455 | 1.45k | code = 1; |
456 | 1.45k | } else { |
457 | 185 | used = min(avail, data_per_row_padded - pos_in_row); |
458 | 185 | if (pos_in_row < data_per_row) |
459 | 185 | memcpy(*pdata + pos_in_row, data, |
460 | 185 | min(used, data_per_row - pos_in_row)); |
461 | 185 | code = (pos_in_row + used < data_per_row_padded ? pxNeedData : 1); |
462 | 185 | } |
463 | 1.64k | par->source.position += used; |
464 | 1.64k | par->source.data = data + used; |
465 | 1.64k | par->source.available = avail - used; |
466 | 1.64k | return code; |
467 | 1.64k | } |
468 | | |
469 | | static int |
470 | | read_rle_bitmap_data(px_bitmap_enum_t * benum, byte ** pdata, px_args_t * par, bool last) |
471 | 0 | { |
472 | 0 | stream_RLD_state *ss = (&benum->rld_stream_state); |
473 | 0 | uint avail = par->source.available; |
474 | 0 | const byte *data = par->source.data; |
475 | 0 | uint pad = 4; |
476 | 0 | stream_cursor_read r; |
477 | 0 | stream_cursor_write w; |
478 | 0 | uint pos_in_row, data_per_row, data_per_row_padded, used; |
479 | | |
480 | | /* overrided default padding */ |
481 | 0 | if (par->pv[3]) |
482 | 0 | pad = par->pv[3]->value.i; |
483 | 0 | data_per_row = benum->data_per_row; |
484 | 0 | data_per_row_padded = round_up(benum->data_per_row, (int)pad); |
485 | 0 | pos_in_row = par->source.position % data_per_row_padded; |
486 | | |
487 | | /* consumed all of the data */ |
488 | 0 | if (par->pv[1]->value.i < 0 || |
489 | 0 | par->source.position >= data_per_row_padded * (ulong)par->pv[1]->value.i) |
490 | 0 | return 0; |
491 | | |
492 | 0 | if (last) |
493 | 0 | return_error(errorIllegalDataLength); |
494 | | |
495 | 0 | if (!rle_inited(benum->initialized)) { |
496 | 0 | ss->EndOfData = false; |
497 | 0 | ss->templat = &s_RLD_template; |
498 | 0 | s_RLD_init_inline(ss); |
499 | 0 | rle_set(&benum->initialized); |
500 | 0 | } |
501 | |
|
502 | 0 | stream_cursor_read_init(&r, (const byte *)data, avail); |
503 | |
|
504 | 0 | if (pos_in_row < data_per_row) { |
505 | | /* Read more of the current row. */ |
506 | 0 | byte *data = *pdata; |
507 | |
|
508 | 0 | stream_cursor_write_init(&w, (const byte *)(data + pos_in_row), data_per_row - pos_in_row); |
509 | 0 | (*s_RLD_template.process) ((stream_state *) ss, &r, &w, false); |
510 | 0 | used = w.ptr + 1 - data - pos_in_row; |
511 | 0 | pos_in_row += used; |
512 | 0 | par->source.position += used; |
513 | 0 | } |
514 | 0 | if (pos_in_row >= data_per_row && pos_in_row < data_per_row_padded) { /* We've read all the real data; skip the padding. */ |
515 | 0 | byte pad[3]; /* maximum padding per row */ |
516 | |
|
517 | 0 | stream_cursor_write_init(&w, (const byte *)pad, data_per_row_padded - pos_in_row); |
518 | 0 | (*s_RLD_template.process) ((stream_state *) ss, &r, &w, false); |
519 | 0 | used = w.ptr + 1 - pad; |
520 | 0 | pos_in_row += used; |
521 | 0 | par->source.position += used; |
522 | 0 | } |
523 | |
|
524 | 0 | used = r.ptr + 1 - data; |
525 | 0 | par->source.data = r.ptr + 1; |
526 | 0 | par->source.available = avail - used; |
527 | 0 | return (pos_in_row < data_per_row_padded ? pxNeedData : 1); |
528 | 0 | } |
529 | | |
530 | | /** PCL XL delta row decompression |
531 | | * |
532 | | * delta row Algorithm: |
533 | | * |
534 | | * Seed Row is initialized with zeros. |
535 | | * |
536 | | * lsb,msb row byte count |
537 | | * delta row data |
538 | | * repeat for each row |
539 | | * |
540 | | * if row byte count is zero duplicate previous row |
541 | | * if row byte count doesn't fill row duplicate remainder and end the row (undocumented) |
542 | | * |
543 | | * delta row data: command byte, optional extra offset bytes, delta raster snippit |
544 | | * command byte 7-5 delta raster length: 4-0 offset |
545 | | * |
546 | | * offset = bits 4-0; |
547 | | * if offset == 31 then do { add next byte } repeat while next byte was 0xFF |
548 | | * example offset = 31 + 255 + 255 + 255 + 4 |
549 | | * |
550 | | * delta length = bits 5-7 + 1; range 1 to 8 bytes. |
551 | | * |
552 | | * output raster is: |
553 | | * last position + offset; "copies" old data |
554 | | * copy delta length bytes from input to output |
555 | | * |
556 | | * Internal Algorithm: |
557 | | * |
558 | | * No row padding is used. |
559 | | * State is need since available data can be short at any time. |
560 | | * read = *pin++; // out of data? save state, return eNeedData |
561 | | * |
562 | | * deltarow.state maintains state between requests for more data |
563 | | * state : description |
564 | | * -> next state |
565 | | * --------------------------------------- |
566 | | * next_is_bytecount : lsb of row bytecount |
567 | | * -> partial_bytecount |
568 | | * partial_bytecount : msb of row bytecount |
569 | | * -> next_is_cmd |
570 | | * next_is_cmd : 1 byte cmd contains cnt and partial offset |
571 | | * -> partial_offset or partial_cnt |
572 | | * partial_offset : accumulates extra offset bytes, moves output by offset |
573 | | * -> partial_offset or partial_cnt |
574 | | * partial_cnt : copies cnt bytes one at a time from input |
575 | | * -> partial_cnt or next_is_cmd or (next_bytecount && end_of_row) |
576 | | * |
577 | | * RETURN values: |
578 | | * 0 == end of image // end of row returns, next call returns end of image. |
579 | | * 1 == end of row |
580 | | * eNeedData == on need more input |
581 | | */ |
582 | | static int |
583 | | read_deltarow_bitmap_data(px_bitmap_enum_t * benum, byte ** pdata, |
584 | | px_args_t * par, bool last) |
585 | 0 | { |
586 | 0 | deltarow_state_t *deltarow = &benum->deltarow_state; |
587 | 0 | uint avail = par->source.available; |
588 | 0 | const byte *pin = par->source.data; |
589 | 0 | byte *pout = *pdata + par->source.position % benum->data_per_row; |
590 | 0 | const byte *pout_start = pout; |
591 | 0 | bool end_of_row = false; |
592 | |
|
593 | 0 | if (delta_inited(benum->initialized) && deltarow->rowwritten == par->pv[1]->value.i) { |
594 | 0 | deltarow->rowwritten = 0; |
595 | 0 | return 0; |
596 | 0 | } |
597 | | |
598 | 0 | if (last) |
599 | 0 | return_error(errorIllegalDataLength); |
600 | | |
601 | | /* initialize at begin of image */ |
602 | 0 | if (!delta_inited(benum->initialized)) { |
603 | | /* zero seed row */ |
604 | 0 | deltarow->seedrow = |
605 | 0 | gs_alloc_bytes(benum->mem, benum->data_per_row, |
606 | 0 | "read_deltarow_bitmap_data"); |
607 | 0 | if (deltarow->seedrow == NULL) |
608 | 0 | return 0; |
609 | 0 | memset(deltarow->seedrow, 0, benum->data_per_row); |
610 | 0 | deltarow->row_byte_count = 0; |
611 | 0 | deltarow->short_cnt = 0; |
612 | 0 | deltarow->short_offset = 0; |
613 | 0 | deltarow->state = next_is_bytecount; |
614 | 0 | deltarow->rowwritten = 0; |
615 | 0 | delta_set(&benum->initialized); |
616 | 0 | } |
617 | | |
618 | 0 | if (deltarow->row_byte_count == 0) { |
619 | 0 | memcpy(*pdata, deltarow->seedrow, benum->data_per_row); |
620 | 0 | } |
621 | | |
622 | | /* one byte at a time until end of input or end of row */ |
623 | 0 | while (avail && !end_of_row) { |
624 | 0 | switch (deltarow->state) { |
625 | | |
626 | 0 | case next_is_bytecount:{ |
627 | 0 | deltarow->short_cnt = *pin++; |
628 | 0 | --avail; |
629 | 0 | deltarow->state = partial_bytecount; |
630 | 0 | break; |
631 | 0 | } |
632 | | |
633 | 0 | case partial_bytecount:{ |
634 | 0 | deltarow->row_byte_count = |
635 | 0 | deltarow->short_cnt + ((*pin++) << 8); |
636 | 0 | --avail; |
637 | |
|
638 | 0 | if (deltarow->row_byte_count == 0) { |
639 | | /* duplicate the row */ |
640 | 0 | deltarow->state = next_is_bytecount; |
641 | 0 | end_of_row = true; |
642 | 0 | } else |
643 | 0 | deltarow->state = next_is_cmd; |
644 | 0 | break; |
645 | 0 | } |
646 | | |
647 | 0 | case next_is_cmd:{ |
648 | 0 | uint val = *pin++; |
649 | |
|
650 | 0 | --avail; |
651 | |
|
652 | 0 | if (deltarow->row_byte_count == 0) { |
653 | | /* duplicate the row */ |
654 | 0 | deltarow->state = next_is_bytecount; |
655 | 0 | end_of_row = true; |
656 | 0 | } else { |
657 | 0 | deltarow->row_byte_count--; |
658 | 0 | deltarow->short_cnt = (val >> 5) + 1; /* 1 to 8 new bytes to copy */ |
659 | 0 | deltarow->short_offset = val & 0x1f; /* num to retain from last row, skip */ |
660 | 0 | if (deltarow->short_offset == 0x1f) |
661 | 0 | deltarow->state = partial_offset; /* accumulate more offset */ |
662 | 0 | else { |
663 | 0 | pout += deltarow->short_offset; /* skip keeps old data in row */ |
664 | 0 | deltarow->state = partial_cnt; /* done with offset do count */ |
665 | 0 | } |
666 | 0 | } |
667 | 0 | break; |
668 | 0 | } |
669 | | |
670 | 0 | case partial_offset:{ |
671 | 0 | uint offset = *pin++; |
672 | |
|
673 | 0 | avail--; |
674 | 0 | if (deltarow->row_byte_count == 0) { |
675 | | /* duplicate the row */ |
676 | 0 | deltarow->state = next_is_bytecount; |
677 | 0 | end_of_row = true; |
678 | 0 | } else { |
679 | 0 | deltarow->row_byte_count--; |
680 | |
|
681 | 0 | deltarow->short_offset += offset; |
682 | |
|
683 | 0 | if (offset == 0xff) |
684 | 0 | deltarow->state = partial_offset; /* 0x1f + ff ff ff ff ff + 1 */ |
685 | 0 | else { |
686 | 0 | pout += deltarow->short_offset; /* skip keeps old data in row */ |
687 | 0 | deltarow->state = partial_cnt; /* done with offset do count */ |
688 | 0 | } |
689 | 0 | } |
690 | 0 | break; |
691 | 0 | } |
692 | | |
693 | 0 | case partial_cnt:{ |
694 | | /* check for possible row overflow */ |
695 | 0 | if (pout >= *pdata + benum->data_per_row) |
696 | 0 | pin++; |
697 | 0 | else |
698 | 0 | *pout++ = *pin++; /* copy new data into row */ |
699 | 0 | avail--; |
700 | 0 | if (deltarow->row_byte_count == 0) { |
701 | 0 | end_of_row = true; |
702 | 0 | deltarow->state = next_is_bytecount; |
703 | 0 | } else { |
704 | 0 | deltarow->row_byte_count--; |
705 | 0 | deltarow->short_cnt--; |
706 | |
|
707 | 0 | if (deltarow->row_byte_count == 0) { |
708 | 0 | end_of_row = true; |
709 | 0 | deltarow->state = next_is_bytecount; |
710 | 0 | } else if (deltarow->short_cnt == 0) |
711 | 0 | deltarow->state = next_is_cmd; |
712 | | /* else more bytes to copy */ |
713 | 0 | } |
714 | 0 | break; |
715 | 0 | } |
716 | |
|
717 | 0 | } /* end switch */ |
718 | 0 | } /* end of while */ |
719 | | |
720 | 0 | par->source.available -= pin - par->source.data; /* subract comressed data used */ |
721 | 0 | par->source.data = pin; /* new compressed data position */ |
722 | |
|
723 | 0 | if (end_of_row) { |
724 | | /* uncompressed raster position */ |
725 | 0 | par->source.position = |
726 | 0 | (par->source.position / benum->data_per_row + |
727 | 0 | 1) * benum->data_per_row; |
728 | 0 | deltarow->rowwritten++; |
729 | 0 | memcpy(deltarow->seedrow, *pdata, benum->data_per_row); |
730 | 0 | return 1; |
731 | 0 | } |
732 | 0 | par->source.position += pout - pout_start; /* amount of raster written */ |
733 | 0 | return pxNeedData; /* not end of row so request more data */ |
734 | 0 | } |
735 | | |
736 | | /* |
737 | | * Read a (possibly partial) row of bitmap data. This is most of the |
738 | | * implementation of ReadImage and ReadRastPattern. We use source.position |
739 | | * to track the uncompressed input byte position within the current block. |
740 | | * Return 0 if we've processed all the data in the block, 1 if we have a |
741 | | * complete scan line, pxNeedData for an incomplete scan line, or <0 for |
742 | | * an error condition. *pdata must point to a scan line buffer; this |
743 | | * routine may reset it to point into the input buffer (if a complete |
744 | | * scan line is available). |
745 | | * Attributes: pxaStartLine (ignored), pxaBlockHeight, pxaCompressMode. |
746 | | */ |
747 | | static int |
748 | | read_bitmap(px_bitmap_enum_t * benum, byte ** pdata, px_args_t * par, bool last) |
749 | 1.82k | { |
750 | 1.82k | switch (par->pv[2]->value.i) { |
751 | 0 | case eRLECompression: |
752 | 0 | return read_rle_bitmap_data(benum, pdata, par, last); |
753 | 0 | case eJPEGCompression: |
754 | 0 | return read_jpeg_bitmap_data(benum, pdata, par, last); |
755 | 0 | case eDeltaRowCompression: |
756 | 0 | return read_deltarow_bitmap_data(benum, pdata, par, last); |
757 | 1.82k | case eNoCompression: |
758 | 1.82k | return read_uncompressed_bitmap_data(benum, pdata, par, last); |
759 | 0 | default: |
760 | 0 | break; |
761 | 1.82k | } |
762 | 0 | return -1; |
763 | 1.82k | } |
764 | | |
765 | | static int read_rebuffered_bitmap(px_bitmap_enum_t * benum, byte ** pdata, px_args_t * par) |
766 | 0 | { |
767 | 0 | int code; |
768 | |
|
769 | 0 | if (!benum->rebuffered) |
770 | 0 | return read_bitmap(benum, pdata, par, /* last */ false); |
771 | | |
772 | 0 | { |
773 | 0 | int w = benum->rebuffered_width; |
774 | 0 | byte *rowptr = *pdata; |
775 | 0 | byte *data2 = rowptr + benum->rebuffered_row_pos * benum->data_per_row; |
776 | 0 | for (; benum->rebuffered_row_pos < w; benum->rebuffered_row_pos++) { |
777 | 0 | byte *data3 = data2; |
778 | 0 | code = read_bitmap(benum, &data3, par, /* last */ false); |
779 | 0 | if (code == 0) { |
780 | 0 | return 0; |
781 | 0 | } else if (code == 1) { |
782 | | /* got a scanline! (well, 1 pixel really, cos we are flipping) */ |
783 | 0 | } else { |
784 | 0 | if (par->source.available == 0) |
785 | 0 | return pxNeedData; |
786 | 0 | return code; |
787 | 0 | } |
788 | 0 | if (data3 != data2) { |
789 | 0 | memcpy(data2, data3, benum->data_per_row); |
790 | 0 | } |
791 | 0 | data2 += benum->data_per_row; |
792 | 0 | } |
793 | 0 | benum->rebuffered_row_pos = 0; |
794 | 0 | } |
795 | 0 | return code; |
796 | 0 | } |
797 | | |
798 | | |
799 | | /* ---------------- Image operators ---------------- */ |
800 | | |
801 | | const byte apxBeginImage[] = { |
802 | | pxaColorMapping, pxaColorDepth, pxaSourceWidth, pxaSourceHeight, |
803 | | pxaDestinationSize, 0, 0 |
804 | | }; |
805 | | |
806 | | int |
807 | | pxBeginImage(px_args_t * par, px_state_t * pxs) |
808 | 0 | { |
809 | 0 | px_image_enum_t *pxenum; |
810 | 0 | px_bitmap_args_t bi_args; |
811 | |
|
812 | 0 | pxenum = |
813 | 0 | gs_alloc_struct(pxs->memory, px_image_enum_t, |
814 | 0 | &st_px_image_enum, "setup_bitmap(pxenum)"); |
815 | |
|
816 | 0 | if (pxenum == 0) |
817 | 0 | return_error(errorInsufficientMemory); |
818 | | |
819 | | /* |
820 | | * store a copy of the args. For JPEG these parameters may be |
821 | | * revised depending on the color parameters in the JPEG data. |
822 | | * We defer image set up until we read the image data. |
823 | | */ |
824 | | |
825 | 0 | bi_args.mapping = par->pv[0]->value.i; |
826 | 0 | bi_args.depth = par->pv[1]->value.i; |
827 | 0 | bi_args.width = par->pv[2]->value.i; |
828 | 0 | bi_args.height = par->pv[3]->value.i; |
829 | 0 | bi_args.dest_width = real_value(par->pv[4], 0); |
830 | 0 | bi_args.dest_height = real_value(par->pv[4], 1); |
831 | |
|
832 | 0 | pxenum->bi_args = bi_args; |
833 | 0 | pxenum->enum_started = false; |
834 | 0 | pxs->image_enum = pxenum; |
835 | 0 | memset(&pxenum->benum, 0, sizeof(pxenum->benum)); |
836 | 0 | return 0; |
837 | 0 | } |
838 | | |
839 | | static int |
840 | | px_begin_image(px_state_t * pxs, bool is_jpeg, px_args_t * par) |
841 | 0 | { |
842 | 0 | gs_point origin; |
843 | 0 | px_bitmap_params_t params; |
844 | 0 | gs_gstate *pgs = pxs->pgs; |
845 | 0 | px_gstate_t *pxgs = pxs->pxgs; |
846 | 0 | px_image_enum_t *pxenum = pxs->image_enum; |
847 | 0 | px_bitmap_enum_t *pbenum = &pxenum->benum; |
848 | 0 | int code; |
849 | |
|
850 | 0 | if (gs_currentpoint(pgs, &origin) < 0) |
851 | 0 | return_error(errorCurrentCursorUndefined); |
852 | | /* |
853 | | * If the current logical operation doesn't involve the texture, |
854 | | * don't set a null brush, which would cause the image not to |
855 | | * appear. |
856 | | */ |
857 | 0 | if (pxs->pxgs->brush.type == pxpNull && |
858 | 0 | !rop3_uses_T(gs_currentrasterop(pgs))) |
859 | 0 | code = gs_setgray(pgs, 0.0); |
860 | 0 | else |
861 | 0 | code = px_set_paint(&pxgs->brush, pxs); |
862 | |
|
863 | 0 | if (code < 0) |
864 | 0 | return code; |
865 | | /* |
866 | | * Make sure the proper halftone is current. |
867 | | */ |
868 | 0 | code = px_set_halftone(pxs); |
869 | 0 | if (code < 0) |
870 | 0 | return code; |
871 | 0 | code = begin_rebuffered_bitmap(¶ms, pbenum, &pxenum->bi_args, pxs, is_jpeg, par); |
872 | 0 | if (code < 0 || code == pxNeedData) |
873 | 0 | return_error(code); |
874 | | |
875 | 0 | pxenum->row = gs_alloc_byte_array(pxs->memory, 1, pbenum->rebuffered_data_per_row, |
876 | 0 | "pxReadImage(row)"); |
877 | 0 | if (pxenum->row == 0) |
878 | 0 | code = gs_note_error(errorInsufficientMemory); |
879 | 0 | else |
880 | 0 | code = |
881 | 0 | px_image_color_space(&pxenum->image, ¶ms, |
882 | 0 | (const gs_string *)&pxgs->palette, pgs); |
883 | |
|
884 | 0 | if (code < 0) |
885 | 0 | goto free_buffers_and_exit; |
886 | | |
887 | | /* Set up the image parameters. */ |
888 | 0 | pxenum->image.Width = pbenum->rebuffered_width; |
889 | 0 | pxenum->image.Height = pbenum->rebuffered_height; |
890 | 0 | { |
891 | 0 | gs_matrix imat, dmat; |
892 | | |
893 | | /* We need the cast because height is unsigned. */ |
894 | | /* We also need to account for the upside-down H-P */ |
895 | | /* coordinate system. */ |
896 | 0 | gs_make_scaling(params.width, params.height, &imat); |
897 | 0 | if (pbenum->rebuffered) { |
898 | 0 | imat.xy = imat.xx; imat.xx = 0; |
899 | 0 | imat.yx = imat.yy; imat.yy = 0; |
900 | 0 | } |
901 | 0 | gs_make_translation(origin.x, origin.y, &dmat); |
902 | 0 | gs_matrix_scale(&dmat, params.dest_width, params.dest_height, &dmat); |
903 | | /* The ImageMatrix is dmat' * imat. */ |
904 | 0 | code = gs_matrix_invert(&dmat, &dmat); |
905 | 0 | if (code < 0) |
906 | 0 | goto free_buffers_and_exit; |
907 | 0 | gs_matrix_multiply(&dmat, &imat, &pxenum->image.ImageMatrix); |
908 | 0 | } |
909 | 0 | pxenum->image.CombineWithColor = true; |
910 | 0 | pxenum->image.Interpolate = pxs->interpolate; |
911 | |
|
912 | 0 | pxenum->ienum = gs_image_enum_alloc(gs_gstate_memory(pgs), "px_begin_image"); |
913 | 0 | if (pxenum->ienum == NULL) { |
914 | 0 | code = gs_note_error(gs_error_VMerror); |
915 | 0 | goto free_buffers_and_exit; |
916 | 0 | } |
917 | 0 | code = gs_image_init(pxenum->ienum, &pxenum->image, |
918 | 0 | pxenum->image.ImageMask | pxenum->image.CombineWithColor, |
919 | 0 | false, pgs); |
920 | 0 | if (code < 0) { |
921 | 0 | gs_image_cleanup_and_free_enum(pxenum->ienum, pgs); |
922 | 0 | pxenum->ienum = NULL; |
923 | | /* This procedure will be re-invoked if we are remapping the |
924 | | color so don't free the resources */ |
925 | 0 | free_buffers_and_exit: |
926 | 0 | if (code != gs_error_Remap_Color) { |
927 | 0 | gs_free_object(pxs->memory, pxenum->row, "pxReadImage(row)"); |
928 | 0 | gs_free_object(pxs->memory, pxenum, "pxBeginImage(pxenum)"); |
929 | 0 | } |
930 | 0 | return code; |
931 | 0 | } |
932 | 0 | return 0; |
933 | |
|
934 | 0 | } |
935 | | |
936 | | const byte apxReadImage[] = { |
937 | | pxaStartLine, pxaBlockHeight, pxaCompressMode, 0, pxaPadBytesMultiple, |
938 | | pxaBlockByteLength, 0 |
939 | | }; |
940 | | |
941 | | int |
942 | | pxReadImage(px_args_t * par, px_state_t * pxs) |
943 | 0 | { |
944 | 0 | px_image_enum_t *pxenum = pxs->image_enum; |
945 | 0 | int code = 0; |
946 | |
|
947 | 0 | if (par->pv[1]->value.i == 0) |
948 | 0 | return 0; /* no data */ |
949 | 0 | if (par->pv[3] != NULL && (par->pv[3]->value.i < 1 || par->pv[3]->value.i > 4)) |
950 | 0 | return_error(gs_error_rangecheck); |
951 | | /* Make a quick check for the first call, when no data is available. */ |
952 | 0 | if (par->source.available == 0) |
953 | 0 | return pxNeedData; |
954 | 0 | if (!pxenum->enum_started) { |
955 | 0 | bool is_jpeg = par->pv[2]->value.i == eJPEGCompression; |
956 | 0 | code = px_begin_image(pxs, is_jpeg, par); |
957 | 0 | if (code < 0 || code == pxNeedData) |
958 | 0 | return code; |
959 | 0 | pxenum->enum_started = true; |
960 | 0 | } |
961 | 0 | for (;;) { |
962 | 0 | byte *data = pxenum->row; |
963 | 0 | uint used; |
964 | 0 | int code = read_rebuffered_bitmap(&pxenum->benum, &data, par); |
965 | 0 | if (code != 1) |
966 | 0 | return code; |
967 | | |
968 | 0 | if (pxenum->benum.grayscale) { |
969 | 0 | int i; |
970 | 0 | for (i = 2; i < pxenum->benum.rebuffered_data_per_row; i+=3) { |
971 | 0 | int r = data[i-2]; |
972 | 0 | int g = data[i-1]; |
973 | 0 | int b = data[i]; |
974 | | /* we think this simple conversion is commensurate |
975 | | with typical XL business graphics use cases. */ |
976 | 0 | int gray = (r + g + b) / 3; |
977 | 0 | data[i-2] = data[i-1] = data[i] = gray; |
978 | 0 | } |
979 | 0 | } |
980 | |
|
981 | 0 | code = gs_image_next(pxenum->ienum, data, |
982 | 0 | pxenum->benum.rebuffered_data_per_row, |
983 | 0 | &used); |
984 | 0 | if (code < 0) |
985 | 0 | return code; |
986 | | |
987 | 0 | pxs->have_page = true; |
988 | 0 | } |
989 | 0 | } |
990 | | |
991 | | const byte apxEndImage[] = { 0, 0 }; |
992 | | int |
993 | | pxEndImage(px_args_t * par, px_state_t * pxs) |
994 | 0 | { |
995 | 0 | px_image_enum_t *pxenum = pxs->image_enum; |
996 | 0 | px_bitmap_enum_t *pbenum = &pxenum->benum; |
997 | 0 | int code = gs_image_cleanup_and_free_enum(pxenum->ienum, pxs->pgs); |
998 | |
|
999 | 0 | gs_free_object(pxs->memory, pxenum->row, "pxEndImage(row)"); |
1000 | 0 | gs_free_object(pbenum->mem, pbenum->deltarow_state.seedrow, |
1001 | 0 | "pxEndImage(seedrow)"); |
1002 | 0 | if (pxenum->image.ColorSpace) |
1003 | 0 | rc_decrement(pxenum->image.ColorSpace, |
1004 | 0 | "pxEndImage(image.ColorSpace)"); |
1005 | 0 | gs_free_object(pxs->memory, pxenum, "pxEndImage(pxenum)"); |
1006 | 0 | pxs->image_enum = 0; |
1007 | 0 | return code; |
1008 | 0 | } |
1009 | | |
1010 | | /* ---------------- Raster pattern operators ---------------- */ |
1011 | | |
1012 | | /* Define the enumerator for downloading raster patterns. */ |
1013 | | #ifndef px_pattern_enum_DEFINED |
1014 | | # define px_pattern_enum_DEFINED |
1015 | | typedef struct px_pattern_enum_s px_pattern_enum_t; |
1016 | | #endif |
1017 | | struct px_pattern_enum_s |
1018 | | { |
1019 | | px_bitmap_enum_t benum; |
1020 | | int32_t pattern_id; |
1021 | | pxePatternPersistence_t persistence; |
1022 | | px_pattern_t *pattern; |
1023 | | int lines_rendered; |
1024 | | }; |
1025 | | |
1026 | | gs_private_st_simple(st_px_pattern_enum, px_pattern_enum_t, |
1027 | | "px_pattern_enum_t"); |
1028 | | |
1029 | | const byte apxBeginRastPattern[] = { |
1030 | | pxaColorMapping, pxaColorDepth, pxaSourceWidth, pxaSourceHeight, |
1031 | | pxaDestinationSize, pxaPatternDefineID, pxaPatternPersistence, 0, 0 |
1032 | | }; |
1033 | | int |
1034 | | pxBeginRastPattern(px_args_t * par, px_state_t * pxs) |
1035 | 191 | { |
1036 | 191 | gs_memory_t *mem = pxs->memory; |
1037 | 191 | px_bitmap_params_t params; |
1038 | 191 | px_pattern_t *pattern; |
1039 | 191 | px_pattern_enum_t *pxenum; |
1040 | 191 | px_bitmap_enum_t benum; |
1041 | 191 | px_bitmap_args_t bi_args; |
1042 | 191 | byte *data; |
1043 | 191 | uint psize; |
1044 | 191 | byte *pdata; |
1045 | 191 | int code; |
1046 | 191 | static const gs_memory_struct_type_t st_px_pattern = |
1047 | 191 | { sizeof(px_pattern_t), "", 0, 0, 0, 0, 0 }; |
1048 | | |
1049 | | |
1050 | 191 | bi_args.mapping = par->pv[0]->value.i; |
1051 | 191 | bi_args.depth = par->pv[1]->value.i; |
1052 | 191 | bi_args.width = par->pv[2]->value.i; |
1053 | 191 | bi_args.height = par->pv[3]->value.i; |
1054 | 191 | bi_args.dest_width = real_value(par->pv[4], 0); |
1055 | 191 | bi_args.dest_height = real_value(par->pv[4], 1); |
1056 | | |
1057 | 191 | memset(&benum, 0, sizeof(benum)); |
1058 | 191 | code = begin_bitmap(¶ms, &benum, &bi_args, pxs, false, par); |
1059 | | |
1060 | 191 | if (code < 0) |
1061 | 0 | return code; |
1062 | 191 | rc_alloc_struct_1(pattern, px_pattern_t, &st_px_pattern, mem, |
1063 | 191 | return_error(errorInsufficientMemory), |
1064 | 191 | "raster pattern"); |
1065 | 191 | pattern->rc.free = rc_free_px_pattern; |
1066 | 191 | data = gs_alloc_byte_array(mem, params.height, benum.data_per_row, |
1067 | 191 | "raster pattern data"); |
1068 | 191 | if (params.indexed) { |
1069 | 0 | psize = pxs->pxgs->palette.size; |
1070 | 0 | pdata = gs_alloc_string(mem, psize, "raster pattern palette"); |
1071 | 0 | if (pdata != 0) |
1072 | 0 | memcpy(pdata, pxs->pxgs->palette.data, psize); |
1073 | 191 | } else { |
1074 | 191 | psize = 0; |
1075 | 191 | pdata = 0; |
1076 | 191 | } |
1077 | 191 | pxenum = gs_alloc_struct(mem, px_pattern_enum_t, &st_px_pattern_enum, |
1078 | 191 | "raster pattern enum"); |
1079 | 191 | if (data == 0 || (params.indexed && pdata == 0) || pxenum == 0) { |
1080 | 0 | gs_free_object(mem, pxenum, "raster pattern enum"); |
1081 | 0 | gs_free_string(mem, pdata, psize, "raster pattern palette"); |
1082 | 0 | gs_free_object(mem, data, "raster pattern data"); |
1083 | 0 | gs_free_object(mem, pattern, "raster pattern"); |
1084 | 0 | return_error(errorInsufficientMemory); |
1085 | 0 | } |
1086 | 191 | pxenum->benum = benum; |
1087 | 191 | pxenum->pattern_id = par->pv[5]->value.i; |
1088 | 191 | pxenum->persistence = par->pv[6]->value.i; |
1089 | 191 | pxenum->lines_rendered = 0; |
1090 | 191 | pattern->params = params; |
1091 | 191 | pattern->palette.data = pdata; |
1092 | 191 | pattern->palette.size = psize; |
1093 | 191 | pattern->data = data; |
1094 | 191 | pattern->id = gs_next_ids(mem, 1); |
1095 | 191 | pxenum->pattern = pattern; |
1096 | 191 | pxs->pattern_enum = pxenum; |
1097 | 191 | return 0; |
1098 | 191 | } |
1099 | | |
1100 | | const byte apxReadRastPattern[] = { |
1101 | | pxaStartLine, pxaBlockHeight, pxaCompressMode, 0, pxaPadBytesMultiple, |
1102 | | pxaBlockByteLength, 0 |
1103 | | }; |
1104 | | |
1105 | | int |
1106 | | pxReadRastPattern(px_args_t * par, px_state_t * pxs) |
1107 | 369 | { |
1108 | 369 | px_pattern_enum_t *pxenum = pxs->pattern_enum; |
1109 | 369 | int code; |
1110 | 369 | byte *plimit = pxenum->pattern->data + |
1111 | 369 | (pxenum->benum.data_per_row * pxenum->pattern->params.height); |
1112 | | |
1113 | 369 | if (par->pv[1]->value.i == 0) |
1114 | 0 | return 0; /* no data */ |
1115 | | |
1116 | | /* first call */ |
1117 | 369 | if (par->source.available == 0) |
1118 | 186 | pxenum->lines_rendered = 0; |
1119 | | |
1120 | 1.82k | for (;;) { |
1121 | 1.82k | byte *data = pxenum->pattern->data + |
1122 | 1.82k | (par->pv[0]->value.i + pxenum->lines_rendered) * pxenum->benum.data_per_row; |
1123 | 1.82k | byte *rdata = data; |
1124 | | |
1125 | 1.82k | if (data > plimit) |
1126 | 2 | return_error(gs_error_rangecheck); |
1127 | | |
1128 | 1.82k | code = read_bitmap(&pxenum->benum, &rdata, par, /* last */ data == plimit); |
1129 | 1.82k | if (code != 1) |
1130 | 367 | break; |
1131 | | |
1132 | 1.45k | pxenum->lines_rendered++; |
1133 | | |
1134 | 1.45k | if (rdata != data) |
1135 | 1.45k | memcpy(data, rdata, pxenum->benum.data_per_row); |
1136 | 1.45k | } |
1137 | | |
1138 | 367 | return code; |
1139 | 369 | } |
1140 | | |
1141 | | const byte apxEndRastPattern[] = { 0, 0 }; |
1142 | | int |
1143 | | pxEndRastPattern(px_args_t * par, px_state_t * pxs) |
1144 | 170 | { |
1145 | 170 | px_pattern_enum_t *pxenum = pxs->pattern_enum; |
1146 | | /* We extract the key and value from the pattern_enum structure */ |
1147 | | /* and then free the structure, to encourage LIFO allocation. */ |
1148 | 170 | px_pattern_t *pattern = pxenum->pattern; |
1149 | 170 | int32_t id = pxenum->pattern_id; |
1150 | 170 | px_value_t key; |
1151 | 170 | px_dict_t *pdict; |
1152 | | |
1153 | 170 | switch (pxenum->persistence) { |
1154 | 0 | case eTempPattern: |
1155 | 0 | pdict = &pxs->pxgs->temp_pattern_dict; |
1156 | 0 | break; |
1157 | 0 | case ePagePattern: |
1158 | 0 | pdict = &pxs->page_pattern_dict; |
1159 | 0 | break; |
1160 | 170 | case eSessionPattern: |
1161 | 170 | pdict = &pxs->session_pattern_dict; |
1162 | 170 | break; |
1163 | 0 | default: /* can't happen */ |
1164 | 0 | return_error(errorIllegalAttributeValue); |
1165 | 170 | } |
1166 | 170 | key.type = pxd_array | pxd_ubyte; |
1167 | 170 | key.value.array.data = (byte *) & id; |
1168 | 170 | key.value.array.size = sizeof(id); |
1169 | 170 | gs_free_object(pxs->memory, pxenum, "pxEndRastPattern(pxenum)"); |
1170 | 170 | return px_dict_put(pdict, &key, pattern); |
1171 | 170 | } |
1172 | | |
1173 | | /* ---------------- Scan line operators ---------------- */ |
1174 | | |
1175 | | const byte apxBeginScan[] = { 0, 0 }; |
1176 | | int |
1177 | | pxBeginScan(px_args_t * par, px_state_t * pxs) |
1178 | 13 | { |
1179 | 13 | int code = px_set_paint(&pxs->pxgs->brush, pxs); |
1180 | | |
1181 | 13 | if (code < 0) |
1182 | 0 | return code; |
1183 | | /* We may as well reset the path now instead of at the end. */ |
1184 | 13 | return gs_newpath(pxs->pgs); |
1185 | 13 | } |
1186 | | |
1187 | | const byte apxEndScan[] = { 0, 0 }; |
1188 | | int |
1189 | | pxEndScan(px_args_t * par, px_state_t * pxs) |
1190 | 0 | { |
1191 | 0 | return 0; |
1192 | 0 | } |
1193 | | |
1194 | | const byte apxScanLineRel[] = { |
1195 | | 0, pxaNumberOfScanLines, 0 |
1196 | | }; |
1197 | | int |
1198 | | pxScanLineRel(px_args_t * par, px_state_t * pxs) |
1199 | 0 | { /* |
1200 | | * In order to keep the number of intermediate states down to a |
1201 | | * reasonable number, we require enough data to be present to be |
1202 | | * able to read the control information for each line, or an entire |
1203 | | * x-pair. Initially, source.position is zero. As soon as we have |
1204 | | * read the X/YStart type byte, we change it to: |
1205 | | * (X/YStart type) << 28 + (x-pair type << 24) + |
1206 | | * (# of full or partial scan lines left to process) + 1 |
1207 | | * We use the separate variable source.count to keep track of |
1208 | | * the number of x-pairs left in the scan line. |
1209 | | */ |
1210 | 0 | gs_gstate *pgs = pxs->pgs; |
1211 | 0 | bool big_endian = pxs->data_source_big_endian; |
1212 | 0 | const byte *data = par->source.data; |
1213 | 0 | pxeDataType_t |
1214 | 0 | xystart_type = (par->source.position >> 28) & 0xf, |
1215 | 0 | xpair_type = (par->source.position >> 24) & 0xf; |
1216 | 0 | int code = 0; |
1217 | 0 | int rcount; |
1218 | 0 | gs_rect rlist[20]; /* 20 is arbitrary */ |
1219 | | |
1220 | | /* High level pattern support kludge. We need to know if we are executing |
1221 | | * a high level pattern before we go consuming any of the input, because if |
1222 | | * we are we need to return to the interpreter, set up the high level |
1223 | | * pattern and then come back here. Obviously if we've consumed the data |
1224 | | * we can't come back and rerun it. So we do a 'no op' rectfill which will |
1225 | | * set the color, if it is a high level pattern then we will get an error |
1226 | | * which we return. |
1227 | | */ |
1228 | 0 | code = gs_rectfill(pgs, NULL, 0); |
1229 | 0 | if (code < 0) |
1230 | 0 | return code; |
1231 | | |
1232 | | /* Check for initial state. */ |
1233 | 0 | if (par->source.position == 0) { /* Read XStart/YStart data type. */ |
1234 | 0 | if (par->source.available < 1) |
1235 | 0 | return pxNeedData; |
1236 | 0 | xystart_type = data[0]; |
1237 | 0 | if (xystart_type != eSInt16) |
1238 | 0 | return_error(errorIllegalDataValue); |
1239 | 0 | par->source.position = |
1240 | 0 | ((ulong) xystart_type << 28) + |
1241 | 0 | (par->pv[0] ? par->pv[0]->value.i : 1) + 1; |
1242 | 0 | par->source.data = data += 1; |
1243 | 0 | par->source.available -= 1; |
1244 | 0 | par->source.count = 0; |
1245 | 0 | } |
1246 | 0 | for (rcount = 0;;) { |
1247 | | |
1248 | | /* Check for start of scan line. */ |
1249 | 0 | if (par->source.count == 0) { |
1250 | 0 | int ystart; |
1251 | |
|
1252 | 0 | if ((par->source.position & 0xffffff) == 1) { |
1253 | 0 | code = 0; |
1254 | 0 | break; |
1255 | 0 | } |
1256 | | /* Read XStart and YStart values. */ |
1257 | | /* We know that eSInt16 is the only possible data type. */ |
1258 | 0 | if (par->source.available < 7) { |
1259 | 0 | code = pxNeedData; |
1260 | 0 | break; |
1261 | 0 | } |
1262 | 0 | pxs->scan_point.x = sint16at(data, big_endian); |
1263 | 0 | ystart = sint16at(data + 2, big_endian); |
1264 | 0 | pxs->scan_point.y0 = ystart - 0.5; |
1265 | 0 | pxs->scan_point.y1 = ystart + 0.5; |
1266 | 0 | par->source.count = uint16at(data + 4, big_endian); |
1267 | 0 | if (par->source.count == 0) { |
1268 | 0 | code = gs_note_error(errorIllegalDataValue); |
1269 | 0 | break; |
1270 | 0 | } |
1271 | 0 | xpair_type = data[6]; |
1272 | 0 | par->source.position = |
1273 | 0 | (par->source.position & 0xf0ffffff) + |
1274 | 0 | ((ulong) xpair_type << 24); |
1275 | 0 | par->source.data = data += 7; |
1276 | 0 | par->source.available -= 7; |
1277 | 0 | } |
1278 | | /* Read and process one x-pair. */ |
1279 | 0 | { |
1280 | 0 | uint x0, x1; |
1281 | 0 | uint used; |
1282 | |
|
1283 | 0 | switch (xpair_type) { |
1284 | 0 | case eUByte: |
1285 | 0 | if (par->source.available < 2) { |
1286 | 0 | code = pxNeedData; |
1287 | 0 | goto out; /* 2-level break */ |
1288 | 0 | } |
1289 | 0 | x0 = data[0]; |
1290 | 0 | x1 = data[1]; |
1291 | 0 | used = 2; |
1292 | 0 | break; |
1293 | 0 | case eUInt16: |
1294 | 0 | if (par->source.available < 4) { |
1295 | 0 | code = pxNeedData; |
1296 | 0 | goto out; /* 2-level break */ |
1297 | 0 | } |
1298 | 0 | x0 = uint16at(data, big_endian); |
1299 | 0 | x1 = uint16at(data + 2, big_endian); |
1300 | 0 | used = 4; |
1301 | 0 | break; |
1302 | 0 | default: |
1303 | 0 | code = gs_note_error(errorIllegalDataValue); |
1304 | 0 | goto out; /* 2-level break; */ |
1305 | 0 | } |
1306 | 0 | if (rcount == countof(rlist)) { |
1307 | 0 | code = gs_rectfill(pgs, rlist, rcount); |
1308 | 0 | rcount = 0; |
1309 | 0 | pxs->have_page = true; |
1310 | 0 | if (code < 0) |
1311 | 0 | break; |
1312 | 0 | } |
1313 | 0 | { |
1314 | 0 | gs_rect *pr = &rlist[rcount++]; |
1315 | |
|
1316 | 0 | pr->p.x = pxs->scan_point.x += x0; |
1317 | 0 | pr->p.y = pxs->scan_point.y0; |
1318 | 0 | pr->q.x = pxs->scan_point.x += x1; |
1319 | 0 | pr->q.y = pxs->scan_point.y1; |
1320 | 0 | } |
1321 | 0 | par->source.data = data += used; |
1322 | 0 | par->source.available -= used; |
1323 | 0 | } |
1324 | 0 | if (!--(par->source.count)) |
1325 | 0 | --(par->source.position); |
1326 | 0 | } |
1327 | 0 | out: |
1328 | 0 | if (rcount > 0 && code >= 0) { |
1329 | 0 | int rcode = gs_rectfill(pgs, rlist, rcount); |
1330 | |
|
1331 | 0 | pxs->have_page = true; |
1332 | 0 | if (rcode < 0) |
1333 | 0 | code = rcode; |
1334 | 0 | } |
1335 | 0 | return code; |
1336 | 0 | } |