Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Level 2 sethalftone operator */ |
18 | | #include "memory_.h" |
19 | | #include "ghost.h" |
20 | | #include "oper.h" |
21 | | #include "gsstruct.h" |
22 | | #include "gxdevice.h" /* for gzht.h */ |
23 | | #include "gzht.h" |
24 | | #include "estack.h" |
25 | | #include "ialloc.h" |
26 | | #include "iddict.h" |
27 | | #include "idparam.h" |
28 | | #include "igstate.h" |
29 | | #include "icolor.h" |
30 | | #include "iht.h" |
31 | | #include "store.h" |
32 | | #include "iname.h" |
33 | | #include "zht2.h" |
34 | | #include "gxgstate.h" |
35 | | #include "gen_ordered.h" |
36 | | #include "gp.h" |
37 | | |
38 | | /* Forward references */ |
39 | | static int dict_spot_params(const ref *, gs_spot_halftone *, ref *, ref *, |
40 | | gs_memory_t *); |
41 | | static int dict_spot_results(i_ctx_t *, ref *, const gs_spot_halftone *); |
42 | | static int dict_threshold_params(const ref *, gs_threshold_halftone *, |
43 | | ref *); |
44 | | static int dict_threshold2_params(const ref *, gs_threshold2_halftone *, |
45 | | ref *, gs_memory_t *); |
46 | | |
47 | | /* |
48 | | * This routine translates a gs_separation_name value into a character string |
49 | | * pointer and a string length. |
50 | | */ |
51 | | int |
52 | | gs_get_colorname_string(gs_gstate *pgs, gs_separation_name colorname_index, |
53 | | unsigned char **ppstr, unsigned int *pname_size) |
54 | 0 | { |
55 | 0 | ref nref; |
56 | |
|
57 | 0 | name_index_ref(pgs->memory, colorname_index, &nref); |
58 | 0 | name_string_ref(pgs->memory, &nref, &nref); |
59 | 0 | return obj_string_data(pgs->memory, &nref, (const unsigned char**) ppstr, pname_size); |
60 | 0 | } |
61 | | |
62 | | /* Dummy spot function */ |
63 | | static float |
64 | | spot1_dummy(double x, double y) |
65 | 0 | { |
66 | 0 | return (x + y) / 2; |
67 | 0 | } |
68 | | |
69 | | static int |
70 | | ht_object_type_from_name(gs_ref_memory_t *mem, ref *pname, gs_HT_objtype_t *HTobjtype) |
71 | 1 | { |
72 | 1 | ref sref; |
73 | | |
74 | 1 | *HTobjtype = HT_OBJTYPE_DEFAULT; |
75 | 1 | name_string_ref(mem, pname, &sref); |
76 | 1 | if (r_size(&sref) <= 1) |
77 | 0 | return_error(gs_error_undefined); /* PDF allows zero length strings, but it can't match */ |
78 | | |
79 | 1 | switch (sref.value.const_bytes[0]) { |
80 | 0 | case 'D': |
81 | 0 | if (r_size(&sref) == 7 && strncmp((const char *)sref.value.const_bytes, "Default", 7) == 0) { |
82 | 0 | *HTobjtype = HT_OBJTYPE_DEFAULT; |
83 | 0 | break; |
84 | 0 | } |
85 | 0 | return_error(gs_error_undefined); |
86 | 0 | case 'V': |
87 | 0 | if (r_size(&sref) == 6 && strncmp((const char *)sref.value.const_bytes, "Vector", 6) == 0) { |
88 | 0 | *HTobjtype = HT_OBJTYPE_VECTOR; |
89 | 0 | break; |
90 | 0 | } |
91 | 0 | return_error(gs_error_undefined); |
92 | 0 | case 'I': |
93 | 0 | if (r_size(&sref) == 5 && strncmp((const char *)sref.value.const_bytes, "Image", 5) == 0) { |
94 | 0 | *HTobjtype = HT_OBJTYPE_IMAGE; |
95 | 0 | break; |
96 | 0 | } |
97 | 0 | return_error(gs_error_undefined); |
98 | 0 | case 'T': |
99 | 0 | if (r_size(&sref) == 4 && strncmp((const char *)sref.value.const_bytes, "Text", 4) == 0) { |
100 | 0 | *HTobjtype = HT_OBJTYPE_TEXT; |
101 | 0 | break; |
102 | 0 | } /* falls through to default if no match */ |
103 | 1 | default: |
104 | 1 | return_error(gs_error_undefined); |
105 | 1 | } |
106 | 0 | return 0; |
107 | 1 | } |
108 | | |
109 | | /* <dict> <dict5> .sethalftone5 - */ |
110 | | static int sethalftone_finish(i_ctx_t *); |
111 | | static int sethalftone_cleanup(i_ctx_t *); |
112 | | static int |
113 | | zsethalftone5(i_ctx_t *i_ctx_p) |
114 | 0 | { |
115 | 0 | os_ptr op = osp; |
116 | 0 | uint count; |
117 | 0 | gs_halftone_component *phtc = 0; |
118 | 0 | gs_halftone_component *pc; |
119 | 0 | int code = 0; |
120 | 0 | int j; |
121 | 0 | bool have_default; |
122 | 0 | gs_halftone *pht = 0; |
123 | 0 | gx_device_halftone *pdht = 0; |
124 | 0 | ref sprocs[GS_CLIENT_COLOR_MAX_COMPONENTS + 1]; |
125 | 0 | ref tprocs[GS_CLIENT_COLOR_MAX_COMPONENTS + 1]; |
126 | 0 | gs_memory_t *mem; |
127 | 0 | uint edepth = ref_stack_count(&e_stack); |
128 | 0 | int npop = 2; |
129 | 0 | int dict_enum; |
130 | 0 | ref rvalue[2]; |
131 | 0 | int cname, colorant_number; |
132 | 0 | byte * pname; |
133 | 0 | uint name_size; |
134 | 0 | int halftonetype, type = 0; |
135 | 0 | gs_HT_objtype_t objtype = HT_OBJTYPE_DEFAULT; |
136 | 0 | ref *pdval; |
137 | 0 | gs_gstate *pgs = igs; |
138 | 0 | int space_index; |
139 | |
|
140 | 0 | if (ref_stack_count(&o_stack) < 2) |
141 | 0 | return_error(gs_error_stackunderflow); |
142 | 0 | check_type(*op, t_dictionary); |
143 | 0 | check_type(*(op - 1), t_dictionary); |
144 | | |
145 | 0 | dict_enum = dict_first(op); |
146 | 0 | space_index = r_space_index(op - 1); |
147 | |
|
148 | 0 | mem = (gs_memory_t *) idmemory->spaces_indexed[space_index]; |
149 | |
|
150 | 0 | check_type(*op, t_dictionary); |
151 | 0 | check_dict_read(*op); |
152 | 0 | check_type(op[-1], t_dictionary); |
153 | 0 | check_dict_read(op[-1]); |
154 | | |
155 | | /* |
156 | | * We think that Type 2 and Type 4 halftones, like |
157 | | * screens set by setcolorscreen, adapt automatically to |
158 | | * the device color space, so we need to mark them |
159 | | * with a different internal halftone type. |
160 | | */ |
161 | 0 | code = dict_int_param(op - 1, "HalftoneType", 1, 100, 0, &type); |
162 | 0 | if (code < 0) |
163 | 0 | return code; |
164 | 0 | halftonetype = (type == 2 || type == 4) |
165 | 0 | ? ht_type_multiple_colorscreen |
166 | 0 | : ht_type_multiple; |
167 | | |
168 | | /* Check if this dict has the optional ObjectType parameter */ |
169 | 0 | if (dict_find_string(op - 1, "ObjectType", &pdval) > 0 && |
170 | 0 | r_has_type(pdval, t_name)) { |
171 | 0 | if ((code = ht_object_type_from_name(iimemory, pdval, &objtype)) < 0) |
172 | 0 | return code; |
173 | 0 | } |
174 | | |
175 | | /* Count how many components that we will actually use. */ |
176 | | |
177 | 0 | have_default = false; |
178 | 0 | for (count = 0; ;) { |
179 | | |
180 | | /* Move to next element in the dictionary */ |
181 | 0 | if ((dict_enum = dict_next(op, dict_enum, rvalue)) == -1) |
182 | 0 | break; |
183 | | /* |
184 | | * Verify that we have a valid component. We may have a |
185 | | * /HalfToneType entry. |
186 | | */ |
187 | 0 | if (!r_has_type(&rvalue[0], t_name)) |
188 | 0 | continue; |
189 | 0 | if (!r_has_type(&rvalue[1], t_dictionary)) |
190 | 0 | continue; |
191 | | |
192 | | /* Get the name of the component verify that we will use it. */ |
193 | 0 | cname = name_index(mem, &rvalue[0]); |
194 | 0 | code = gs_get_colorname_string(pgs, cname, &pname, &name_size); |
195 | 0 | if (code < 0) |
196 | 0 | break; |
197 | 0 | colorant_number = gs_cname_to_colorant_number(pgs, pname, name_size, |
198 | 0 | halftonetype); |
199 | 0 | if (colorant_number < 0) |
200 | 0 | continue; |
201 | 0 | else if (colorant_number == GX_DEVICE_COLOR_MAX_COMPONENTS) { |
202 | | /* If here then we have the "Default" component */ |
203 | 0 | if (have_default) |
204 | 0 | return_error(gs_error_rangecheck); |
205 | 0 | have_default = true; |
206 | 0 | } |
207 | | |
208 | 0 | count++; |
209 | | /* |
210 | | * Check to see if we have already reached the legal number of |
211 | | * components. |
212 | | */ |
213 | 0 | if (count > GS_CLIENT_COLOR_MAX_COMPONENTS + 1) { |
214 | 0 | code = gs_note_error(gs_error_rangecheck); |
215 | 0 | break; |
216 | 0 | } |
217 | 0 | } |
218 | 0 | if (count == 0 || (halftonetype == ht_type_multiple && ! have_default)) |
219 | 0 | code = gs_note_error(gs_error_rangecheck); |
220 | |
|
221 | 0 | if (code >= 0) { |
222 | 0 | check_estack(5); /* for sampling Type 1 screens */ |
223 | 0 | refset_null(sprocs, count); |
224 | 0 | refset_null(tprocs, count); |
225 | 0 | rc_alloc_struct_0(pht, gs_halftone, &st_halftone, |
226 | 0 | imemory, pht = 0, ".sethalftone5"); |
227 | 0 | phtc = gs_alloc_struct_array(mem, count, gs_halftone_component, |
228 | 0 | &st_ht_component_element, |
229 | 0 | ".sethalftone5"); |
230 | 0 | rc_alloc_struct_0(pdht, gx_device_halftone, &st_device_halftone, |
231 | 0 | imemory, pdht = 0, ".sethalftone5"); |
232 | 0 | if (pht == 0 || phtc == 0 || pdht == 0) { |
233 | 0 | j = 0; /* Quiet the compiler: |
234 | | gs_note_error isn't necessarily identity, |
235 | | so j could be left ununitialized. */ |
236 | 0 | code = gs_note_error(gs_error_VMerror); |
237 | 0 | } |
238 | 0 | } |
239 | 0 | if (code >= 0) { |
240 | 0 | dict_enum = dict_first(op); |
241 | 0 | for (j = 0, pc = phtc; ;) { |
242 | 0 | int type; |
243 | | |
244 | | /* Move to next element in the dictionary */ |
245 | 0 | if ((dict_enum = dict_next(op, dict_enum, rvalue)) == -1) |
246 | 0 | break; |
247 | | /* |
248 | | * Verify that we have a valid component. We may have a |
249 | | * /HalfToneType entry. |
250 | | */ |
251 | 0 | if (!r_has_type(&rvalue[0], t_name)) |
252 | 0 | continue; |
253 | 0 | if (!r_has_type(&rvalue[1], t_dictionary)) |
254 | 0 | continue; |
255 | | |
256 | | /* Get the name of the component */ |
257 | 0 | cname = name_index(mem, &rvalue[0]); |
258 | 0 | code = gs_get_colorname_string(pgs, cname, &pname, &name_size); |
259 | 0 | if (code < 0) |
260 | 0 | break; |
261 | 0 | colorant_number = gs_cname_to_colorant_number(pgs, pname, name_size, |
262 | 0 | halftonetype); |
263 | 0 | if (colorant_number < 0) |
264 | 0 | continue; /* Do not use this component */ |
265 | 0 | pc->cname = cname; |
266 | 0 | pc->comp_number = colorant_number; |
267 | | |
268 | | /* Now process the component dictionary */ |
269 | 0 | check_dict_read(rvalue[1]); |
270 | 0 | if (dict_int_param(&rvalue[1], "HalftoneType", 1, 7, 0, &type) < 0) { |
271 | 0 | code = gs_note_error(gs_error_typecheck); |
272 | 0 | break; |
273 | 0 | } |
274 | 0 | switch (type) { |
275 | 0 | default: |
276 | 0 | code = gs_note_error(gs_error_rangecheck); |
277 | 0 | break; |
278 | 0 | case 1: |
279 | 0 | code = dict_spot_params(&rvalue[1], &pc->params.spot, |
280 | 0 | sprocs + j, tprocs + j, mem); |
281 | 0 | pc->params.spot.screen.spot_function = spot1_dummy; |
282 | 0 | pc->type = ht_type_spot; |
283 | 0 | break; |
284 | 0 | case 3: |
285 | 0 | code = dict_threshold_params(&rvalue[1], &pc->params.threshold, |
286 | 0 | tprocs + j); |
287 | 0 | pc->type = ht_type_threshold; |
288 | 0 | break; |
289 | 0 | case 7: |
290 | 0 | code = dict_threshold2_params(&rvalue[1], &pc->params.threshold2, |
291 | 0 | tprocs + j, imemory); |
292 | 0 | pc->type = ht_type_threshold2; |
293 | 0 | break; |
294 | 0 | } |
295 | 0 | if (code < 0) |
296 | 0 | break; |
297 | 0 | pc++; |
298 | 0 | j++; |
299 | 0 | } |
300 | 0 | } |
301 | 0 | if (code >= 0) { |
302 | 0 | pht->type = halftonetype; |
303 | 0 | pht->objtype = objtype; |
304 | 0 | pht->params.multiple.components = phtc; |
305 | 0 | pht->params.multiple.num_comp = j; |
306 | 0 | pht->params.multiple.get_colorname_string = gs_get_colorname_string; |
307 | 0 | code = gs_sethalftone_prepare(igs, pht, pdht); |
308 | 0 | } |
309 | 0 | if (code >= 0) { |
310 | | /* |
311 | | * Put the actual frequency and angle in the spot function component dictionaries. |
312 | | */ |
313 | 0 | dict_enum = dict_first(op); |
314 | 0 | for (pc = phtc; ; ) { |
315 | | /* Move to next element in the dictionary */ |
316 | 0 | if ((dict_enum = dict_next(op, dict_enum, rvalue)) == -1) |
317 | 0 | break; |
318 | | |
319 | | /* Verify that we have a valid component */ |
320 | 0 | if (!r_has_type(&rvalue[0], t_name)) |
321 | 0 | continue; |
322 | 0 | if (!r_has_type(&rvalue[1], t_dictionary)) |
323 | 0 | continue; |
324 | | |
325 | | /* Get the name of the component and verify that we will use it. */ |
326 | 0 | cname = name_index(mem, &rvalue[0]); |
327 | 0 | code = gs_get_colorname_string(pgs, cname, &pname, &name_size); |
328 | 0 | if (code < 0) |
329 | 0 | break; |
330 | 0 | colorant_number = gs_cname_to_colorant_number(pgs, pname, name_size, |
331 | 0 | halftonetype); |
332 | 0 | if (colorant_number < 0) |
333 | 0 | continue; |
334 | | |
335 | 0 | if (pc->type == ht_type_spot) { |
336 | 0 | code = dict_spot_results(i_ctx_p, &rvalue[1], &pc->params.spot); |
337 | 0 | if (code < 0) |
338 | 0 | break; |
339 | 0 | } |
340 | 0 | pc++; |
341 | 0 | } |
342 | 0 | } |
343 | 0 | if (code >= 0) { |
344 | | /* |
345 | | * Schedule the sampling of any Type 1 screens, |
346 | | * and any (Type 1 or Type 3) TransferFunctions. |
347 | | * Save the stack depths in case we have to back out. |
348 | | */ |
349 | 0 | uint odepth = ref_stack_count(&o_stack); |
350 | 0 | ref odict, odict5; |
351 | |
|
352 | 0 | odict = op[-1]; |
353 | 0 | odict5 = *op; |
354 | 0 | ref_stack_pop(&o_stack, 2); |
355 | 0 | op = osp; |
356 | 0 | esp += 5; |
357 | 0 | make_mark_estack(esp - 4, es_other, sethalftone_cleanup); |
358 | 0 | esp[-3] = odict; |
359 | 0 | make_istruct(esp - 2, 0, pht); |
360 | 0 | make_istruct(esp - 1, 0, pdht); |
361 | 0 | make_op_estack(esp, sethalftone_finish); |
362 | 0 | for (j = 0; j < count; j++) { |
363 | 0 | gx_ht_order *porder = NULL; |
364 | |
|
365 | 0 | if (pdht->components == 0) |
366 | 0 | porder = &pdht->order; |
367 | 0 | else { |
368 | | /* Find the component in pdht that matches component j in |
369 | | the pht; gs_sethalftone_prepare() may permute these. */ |
370 | 0 | int k; |
371 | 0 | int comp_number = phtc[j].comp_number; |
372 | 0 | for (k = 0; k < count; k++) { |
373 | 0 | if (pdht->components[k].comp_number == comp_number) { |
374 | 0 | porder = &pdht->components[k].corder; |
375 | 0 | break; |
376 | 0 | } |
377 | 0 | } |
378 | 0 | } |
379 | 0 | switch (phtc[j].type) { |
380 | 0 | case ht_type_spot: |
381 | 0 | code = zscreen_enum_init(i_ctx_p, porder, |
382 | 0 | &phtc[j].params.spot.screen, |
383 | 0 | &sprocs[j], 0, 0, space_index); |
384 | 0 | if (code < 0) |
385 | 0 | break; |
386 | | /* falls through */ |
387 | 0 | case ht_type_threshold: |
388 | 0 | case ht_type_threshold2: |
389 | 0 | if (!r_has_type(tprocs + j, t__invalid)) { |
390 | | /* Schedule TransferFunction sampling. */ |
391 | | /****** check_xstack IS WRONG ******/ |
392 | 0 | check_ostack(zcolor_remap_one_ostack); |
393 | 0 | check_estack(zcolor_remap_one_estack); |
394 | 0 | code = zcolor_remap_one(i_ctx_p, tprocs + j, |
395 | 0 | porder->transfer, igs, |
396 | 0 | zcolor_remap_one_finish); |
397 | 0 | op = osp; |
398 | 0 | } |
399 | 0 | break; |
400 | 0 | default: /* not possible here, but to keep */ |
401 | | /* the compilers happy.... */ |
402 | 0 | ; |
403 | 0 | } |
404 | 0 | if (code < 0) { /* Restore the stack. */ |
405 | 0 | ref_stack_pop_to(&o_stack, odepth); |
406 | 0 | ref_stack_pop_to(&e_stack, edepth); |
407 | 0 | op = osp; |
408 | 0 | op[-1] = odict; |
409 | 0 | *op = odict5; |
410 | 0 | break; |
411 | 0 | } |
412 | 0 | npop = 0; |
413 | 0 | } |
414 | 0 | } |
415 | 0 | if (code < 0) { |
416 | 0 | gs_free_object(mem, pdht, ".sethalftone5"); |
417 | 0 | gs_free_object(mem, phtc, ".sethalftone5"); |
418 | 0 | gs_free_object(mem, pht, ".sethalftone5"); |
419 | 0 | return code; |
420 | 0 | } |
421 | 0 | pop(npop); |
422 | 0 | return (ref_stack_count(&e_stack) > edepth ? o_push_estack : 0); |
423 | 0 | } |
424 | | |
425 | | /* <dict> .genordered <string> */ |
426 | | /* array will have: width height turn_on_sequence.x turn_on_sequence.y ... */ |
427 | | /* total array length is 2 + (2 * width * height) */ |
428 | | static int |
429 | | zgenordered(i_ctx_t *i_ctx_p) |
430 | 12 | { |
431 | 12 | os_ptr op = osp; |
432 | 12 | int i, code = 0; |
433 | 12 | gs_memory_t *mem; |
434 | 12 | int space_index; |
435 | 12 | htsc_param_t params; |
436 | 12 | int S; |
437 | 12 | htsc_dig_grid_t final_mask; |
438 | 12 | float tmp_float; |
439 | 12 | gs_gstate *pgs = igs; |
440 | 12 | gx_device *currdevice = pgs->device; |
441 | 12 | output_format_type output_type = OUTPUT_PS; |
442 | 12 | ref *out_type_name; |
443 | | |
444 | 12 | if (ref_stack_count(&o_stack) < 1) |
445 | 4 | return_error(gs_error_stackunderflow); |
446 | 8 | check_type(*op, t_dictionary); |
447 | | |
448 | 0 | space_index = r_space_index(op); /* used to construct array that is returned */ |
449 | 0 | mem = (gs_memory_t *) idmemory->spaces_indexed[space_index]; |
450 | |
|
451 | 0 | check_type(*op, t_dictionary); |
452 | 0 | check_dict_read(*op); |
453 | | |
454 | 0 | htsc_set_default_params(¶ms); |
455 | | /* Modify the default HResolution and VResolution to be the device HWResolution */ |
456 | 0 | params.horiz_dpi = currdevice->HWResolution[0]; |
457 | 0 | params.vert_dpi = currdevice->HWResolution[1]; |
458 | 0 | final_mask.memory = mem->non_gc_memory; |
459 | 0 | final_mask.data = NULL; |
460 | |
|
461 | 0 | if ((code = dict_find_string(op, "OutputType", &out_type_name)) > 0) { |
462 | 0 | ref namestr; |
463 | |
|
464 | 0 | if (!r_has_type(out_type_name, t_name)) |
465 | 0 | return gs_error_typecheck; |
466 | 0 | name_string_ref(imemory, out_type_name, &namestr); |
467 | 0 | if (r_size(&namestr) == 8 && !memcmp(namestr.value.bytes, "TOSArray", 8)) |
468 | 0 | output_type = OUTPUT_TOS; |
469 | 0 | else if (r_size(&namestr) == 5 && !memcmp(namestr.value.bytes, "Type3", 5)) |
470 | 0 | output_type = OUTPUT_PS; |
471 | 0 | else if (r_size(&namestr) == 12 && !memcmp(namestr.value.bytes, "ThreshString", 12)) |
472 | 0 | output_type = OUTPUT_RAW; |
473 | 0 | else |
474 | 0 | return gs_error_undefined; |
475 | 0 | } |
476 | 0 | if ((code = dict_int_param(op, "Angle", 0, 360, 0, ¶ms.targ_scr_ang)) < 0) |
477 | 0 | return gs_error_undefined; |
478 | 0 | if ((code = dict_int_param(op, "Frequency", 1, 0x7fff, 75, ¶ms.targ_lpi)) < 0) |
479 | 0 | return gs_error_undefined; |
480 | 0 | if ((code = dict_float_param(op, "HResolution", 300., &tmp_float)) < 0) |
481 | 0 | return gs_error_undefined; |
482 | 0 | if (code == 0) |
483 | 0 | params.horiz_dpi = tmp_float; |
484 | 0 | if ((code = dict_float_param(op, "VResolution", 300., &tmp_float)) < 0) |
485 | 0 | return gs_error_undefined; |
486 | 0 | if (code == 0) |
487 | 0 | params.vert_dpi = tmp_float; |
488 | 0 | if ((code = dict_int_param(op, "Levels", 1, 0x7fff, 256, ¶ms.targ_quant)) < 0) |
489 | 0 | return gs_error_undefined; |
490 | 0 | if (code == 0) |
491 | 0 | params.targ_quant_spec = true; |
492 | 0 | if ((code = dict_int_param(op, "SuperCellSize", 1, 0x7fff, 1, ¶ms.targ_size)) < 0) |
493 | 0 | return gs_error_undefined; |
494 | 0 | if (code == 0) |
495 | 0 | params.targ_size_spec = true; |
496 | 0 | if ((code = dict_int_param(op, "DotShape", 0, CUSTOM - 1, 0, (int *)(¶ms.spot_type))) < 0) |
497 | 0 | return gs_error_undefined; |
498 | 0 | if ((code = dict_bool_param(op, "Holladay", false, ¶ms.holladay)) < 0) |
499 | 0 | return gs_error_undefined; |
500 | | |
501 | 0 | params.output_format = OUTPUT_TOS; /* we want this format */ |
502 | 0 | code = htsc_gen_ordered(params, &S, &final_mask, mem); |
503 | |
|
504 | | #if FINAL_SCREEN_DUMP |
505 | | if (code >= 0) { |
506 | | code = htsc_save_screen(&final_mask, params.holladay, S, params, mem); |
507 | | } |
508 | | #endif |
509 | |
|
510 | 0 | if (code < 0) |
511 | 0 | goto done; |
512 | | |
513 | 0 | switch (output_type) { |
514 | 0 | case OUTPUT_TOS: |
515 | | /* Now return the mask info in an array [ width height turn_on.x turn_on.y ... ] */ |
516 | 0 | code = ialloc_ref_array((ref *)op, a_all, 2 + (2 * final_mask.width * final_mask.height), "gen_ordered"); |
517 | 0 | if (code < 0) |
518 | 0 | goto done; |
519 | 0 | make_int(&(op->value.refs[0]), final_mask.width); |
520 | 0 | make_int(&(op->value.refs[1]), final_mask.height); |
521 | 0 | for (i=0; i < 2 * final_mask.width * final_mask.height; i++) |
522 | 0 | make_int(&(op->value.refs[i+2]), final_mask.data[i]); |
523 | 0 | break; |
524 | 0 | case OUTPUT_RAW: |
525 | 0 | case OUTPUT_PS: |
526 | | /* Return a threshold array string first two bytes are width (high byte first), |
527 | | * next two bytes are height, followed by the threshold array (one byte per cell) |
528 | | * PostScript can easily form a Type 3 Halftone Thresholds string from this |
529 | | * using "getinterval". |
530 | | */ |
531 | 0 | { |
532 | | /* Make a threshold array from the turn_on_sequence */ |
533 | 0 | int level; |
534 | 0 | int cur_pix = 0; |
535 | 0 | int width = final_mask.width; |
536 | 0 | int num_pix = width * final_mask.height; |
537 | 0 | double delta_value = 1.0 / (double)(num_pix); |
538 | 0 | double end_value, cur_value = 0.0; |
539 | 0 | byte *thresh; |
540 | 0 | ref rval, thresh_ref; |
541 | |
|
542 | 0 | code = gs_error_VMerror; /* in case allocation of thresh fails */ |
543 | 0 | if (output_type == OUTPUT_RAW) { |
544 | 0 | if ((thresh = ialloc_string(4 + num_pix, "gen_ordered")) == 0) |
545 | 0 | goto done; |
546 | 0 | *thresh++ = width >> 8; |
547 | 0 | *thresh++ = width & 0xff; |
548 | 0 | *thresh++ = final_mask.height >> 8; |
549 | 0 | *thresh++ = final_mask.height & 0xff; |
550 | 0 | } else if ((thresh = ialloc_string(num_pix, "gen_ordered")) == 0) |
551 | 0 | goto done; |
552 | | /* The following is adapted from thresh_remap with the default linear map */ |
553 | 0 | for (level=0; level<256; level++) { |
554 | 0 | end_value = (float)(1+level) / 255.; |
555 | 0 | if (end_value > 255.0) |
556 | 0 | end_value = 255.0; /* clamp in case of rounding errors */ |
557 | 0 | while (cur_value < (end_value - (delta_value * (1./256.))) || |
558 | 0 | (cur_pix + 1) == (num_pix / 2) ) { /* force 50% gray level */ |
559 | 0 | thresh[final_mask.data[2*cur_pix] + (width*final_mask.data[2*cur_pix+1])] = 255 - level; |
560 | 0 | cur_pix++; |
561 | 0 | if (cur_pix >= num_pix) |
562 | 0 | break; |
563 | 0 | cur_value += delta_value; |
564 | 0 | } |
565 | 0 | if (cur_pix >= num_pix) |
566 | 0 | break; |
567 | 0 | } |
568 | | /* now fill any remaining cells */ |
569 | 0 | for (; cur_pix < num_pix; cur_pix++) { |
570 | 0 | thresh[final_mask.data[2 * cur_pix] + (width*final_mask.data[2 * cur_pix + 1])] = 0; |
571 | 0 | } |
572 | | #if FINAL_SCREEN_DUMP |
573 | | { |
574 | | char file_name[FULL_FILE_NAME_LENGTH]; |
575 | | gp_file *fid; |
576 | | |
577 | | snprintf(file_name, FULL_FILE_NAME_LENGTH, "Screen_%dx%d.raw", width, final_mask.height); |
578 | | fid = gp_fopen(mem, file_name, "wb"); |
579 | | if (fid) { |
580 | | gp_fwrite(thresh, sizeof(unsigned char), num_pix, fid); |
581 | | gp_fclose(fid); |
582 | | } |
583 | | } |
584 | | #endif |
585 | 0 | if (output_type == OUTPUT_RAW) { |
586 | 0 | make_string(&thresh_ref, a_all | icurrent_space, 4 + num_pix, thresh-4); |
587 | 0 | *op = thresh_ref; |
588 | 0 | code = 0; |
589 | 0 | } else { |
590 | | /* output_type == OUTPUT_PS */ |
591 | | /* Return a HalftoneType 3 dictionary */ |
592 | 0 | code = dict_create(4, op); |
593 | 0 | if (code < 0) |
594 | 0 | goto done; |
595 | 0 | make_string(&thresh_ref, a_all | icurrent_space, num_pix, thresh); |
596 | 0 | if ((code = idict_put_string(op, "Thresholds", &thresh_ref)) < 0) |
597 | 0 | goto done; |
598 | 0 | make_int(&rval, final_mask.width); |
599 | 0 | if ((code = idict_put_string(op, "Width", &rval)) < 0) |
600 | 0 | goto done; |
601 | 0 | make_int(&rval, final_mask.height); |
602 | 0 | if ((code = idict_put_string(op, "Height", &rval)) < 0) |
603 | 0 | goto done; |
604 | 0 | make_int(&rval, 3); |
605 | 0 | if ((code = idict_put_string(op, "HalftoneType", &rval)) < 0) |
606 | 0 | goto done; |
607 | 0 | } |
608 | 0 | } |
609 | 0 | break; |
610 | 0 | default: |
611 | 0 | return gs_error_undefined; |
612 | 0 | } |
613 | | |
614 | 0 | done: |
615 | 0 | if (final_mask.data != NULL) |
616 | 0 | gs_free_object(mem->non_gc_memory, final_mask.data, ".genordered"); |
617 | |
|
618 | 0 | return (code < 0 ? gs_error_undefined : 0); |
619 | 0 | } |
620 | | |
621 | | /* Install the halftone after sampling. */ |
622 | | static int |
623 | | sethalftone_finish(i_ctx_t *i_ctx_p) |
624 | 0 | { |
625 | 0 | gx_device_halftone *pdht = r_ptr(esp, gx_device_halftone); |
626 | 0 | int code; |
627 | |
|
628 | 0 | if (pdht->components) |
629 | 0 | pdht->order = pdht->components[0].corder; |
630 | 0 | code = gx_ht_install(igs, r_ptr(esp - 1, gs_halftone), pdht); |
631 | 0 | if (code < 0) { |
632 | 0 | esp -= 4; |
633 | 0 | sethalftone_cleanup(i_ctx_p); |
634 | 0 | return code; |
635 | 0 | } |
636 | 0 | istate->halftone = esp[-2]; |
637 | 0 | esp -= 4; |
638 | 0 | sethalftone_cleanup(i_ctx_p); |
639 | 0 | return o_pop_estack; |
640 | 0 | } |
641 | | /* Clean up after installing the halftone. */ |
642 | | static int |
643 | | sethalftone_cleanup(i_ctx_t *i_ctx_p) |
644 | 0 | { |
645 | 0 | gx_device_halftone *pdht = r_ptr(&esp[4], gx_device_halftone); |
646 | 0 | gs_halftone *pht = r_ptr(&esp[3], gs_halftone); |
647 | |
|
648 | 0 | gs_free_object(pdht->rc.memory, pdht, |
649 | 0 | "sethalftone_cleanup(device halftone)"); |
650 | 0 | gs_free_object(pht->rc.memory, pht, |
651 | 0 | "sethalftone_cleanup(halftone)"); |
652 | | /* See bug #707007, explicitly freed structures on the stacks need to be made NULL */ |
653 | 0 | make_null(&esp[4]); |
654 | 0 | make_null(&esp[3]); |
655 | 0 | return 0; |
656 | 0 | } |
657 | | |
658 | | static int |
659 | | zsetobjtypeHT(i_ctx_t *i_ctx_p) /* <name> .setobjtypeHT - */ |
660 | | /* name is one of /Vector, /Image, or /Text */ |
661 | 11 | { |
662 | 11 | os_ptr op = osp; |
663 | 11 | int code = 0; |
664 | 11 | gs_HT_objtype_t HTobjtype = HT_OBJTYPE_DEFAULT; |
665 | | |
666 | 11 | if (ref_stack_count(&o_stack) < 1) |
667 | 3 | return_error(gs_error_stackunderflow); |
668 | 8 | check_type(*op, t_name); |
669 | | |
670 | 1 | if ((code = ht_object_type_from_name(iimemory, op, &HTobjtype)) < 0) |
671 | 1 | return code; |
672 | | |
673 | | /* If we made it this far, HTobjtype is valid */ |
674 | 0 | code = gx_gstate_dev_ht_copy_to_objtype(i_ctx_p->pgs, HTobjtype); |
675 | 0 | if (code < 0) |
676 | 0 | return code; |
677 | | |
678 | 0 | pop(1); |
679 | 0 | return 0; |
680 | 0 | } |
681 | | |
682 | | /* ------ Initialization procedure ------ */ |
683 | | |
684 | | const op_def zht2_l2_op_defs[] = |
685 | | { |
686 | | op_def_begin_level2(), |
687 | | {"2.sethalftone5", zsethalftone5}, |
688 | | {"1.genordered", zgenordered}, |
689 | | {"1.setobjtypeHT", zsetobjtypeHT}, |
690 | | /* Internal operators */ |
691 | | {"0%sethalftone_finish", sethalftone_finish}, |
692 | | op_def_end(0) |
693 | | }; |
694 | | |
695 | | /* ------ Internal routines ------ */ |
696 | | |
697 | | /* Extract frequency, angle, spot function, and accurate screens flag */ |
698 | | /* from a dictionary. */ |
699 | | static int |
700 | | dict_spot_params(const ref * pdict, gs_spot_halftone * psp, |
701 | | ref * psproc, ref * ptproc, gs_memory_t *mem) |
702 | 0 | { |
703 | 0 | int code; |
704 | |
|
705 | 0 | check_dict_read(*pdict); |
706 | 0 | if ((code = dict_float_param(pdict, "Frequency", 0.0, |
707 | 0 | &psp->screen.frequency)) != 0 || |
708 | 0 | (code = dict_float_param(pdict, "Angle", 0.0, |
709 | 0 | &psp->screen.angle)) != 0 || |
710 | 0 | (code = dict_proc_param(pdict, "SpotFunction", psproc, false)) != 0 || |
711 | 0 | (code = dict_bool_param(pdict, "AccurateScreens", |
712 | 0 | gs_currentaccuratescreens(mem), |
713 | 0 | &psp->accurate_screens)) < 0 || |
714 | 0 | (code = dict_proc_param(pdict, "TransferFunction", ptproc, false)) < 0 |
715 | 0 | ) |
716 | 0 | return (code < 0 ? code : gs_error_undefined); |
717 | 0 | psp->transfer = (code > 0 ? (gs_mapping_proc) 0 : gs_mapped_transfer); |
718 | 0 | psp->transfer_closure.proc = 0; |
719 | 0 | psp->transfer_closure.data = 0; |
720 | 0 | return 0; |
721 | 0 | } |
722 | | |
723 | | /* Set actual frequency and angle in a dictionary. */ |
724 | | static int |
725 | | dict_real_result(i_ctx_t *i_ctx_p, ref * pdict, const char *kstr, double val) |
726 | 0 | { |
727 | 0 | int code = 0; |
728 | 0 | ref *ignore; |
729 | |
|
730 | 0 | if (dict_find_string(pdict, kstr, &ignore) > 0) { |
731 | 0 | ref rval; |
732 | |
|
733 | 0 | check_dict_write(*pdict); |
734 | 0 | make_real(&rval, val); |
735 | 0 | code = idict_put_string(pdict, kstr, &rval); |
736 | 0 | } |
737 | 0 | return code; |
738 | 0 | } |
739 | | static int |
740 | | dict_spot_results(i_ctx_t *i_ctx_p, ref * pdict, const gs_spot_halftone * psp) |
741 | 0 | { |
742 | 0 | int code; |
743 | |
|
744 | 0 | code = dict_real_result(i_ctx_p, pdict, "ActualFrequency", |
745 | 0 | psp->screen.actual_frequency); |
746 | 0 | if (code < 0) |
747 | 0 | return code; |
748 | 0 | return dict_real_result(i_ctx_p, pdict, "ActualAngle", |
749 | 0 | psp->screen.actual_angle); |
750 | 0 | } |
751 | | |
752 | | /* Extract Width, Height, and TransferFunction from a dictionary. */ |
753 | | static int |
754 | | dict_threshold_common_params(const ref * pdict, |
755 | | gs_threshold_halftone_common * ptp, |
756 | | ref **pptstring, ref *ptproc) |
757 | 0 | { |
758 | 0 | int code; |
759 | |
|
760 | 0 | check_dict_read(*pdict); |
761 | 0 | if ((code = dict_int_param(pdict, "Width", 1, 0x7fff, -1, |
762 | 0 | &ptp->width)) < 0 || |
763 | 0 | (code = dict_int_param(pdict, "Height", 1, 0x7fff, -1, |
764 | 0 | &ptp->height)) < 0 || |
765 | 0 | (code = dict_find_string(pdict, "Thresholds", pptstring)) <= 0 || |
766 | 0 | (code = dict_proc_param(pdict, "TransferFunction", ptproc, false)) < 0 |
767 | 0 | ) |
768 | 0 | return (code < 0 ? code : gs_error_undefined); |
769 | 0 | ptp->transfer_closure.proc = 0; |
770 | 0 | ptp->transfer_closure.data = 0; |
771 | 0 | return code; |
772 | 0 | } |
773 | | |
774 | | /* Extract threshold common parameters + Thresholds. */ |
775 | | static int |
776 | | dict_threshold_params(const ref * pdict, gs_threshold_halftone * ptp, |
777 | | ref * ptproc) |
778 | 0 | { |
779 | 0 | ref *tstring; |
780 | 0 | int code = |
781 | 0 | dict_threshold_common_params(pdict, |
782 | 0 | (gs_threshold_halftone_common *)ptp, |
783 | 0 | &tstring, ptproc); |
784 | |
|
785 | 0 | if (code < 0) |
786 | 0 | return code; |
787 | 0 | check_read_type_only(*tstring, t_string); |
788 | 0 | if (r_size(tstring) != (long)ptp->width * ptp->height) |
789 | 0 | return_error(gs_error_rangecheck); |
790 | 0 | ptp->thresholds.data = tstring->value.const_bytes; |
791 | 0 | ptp->thresholds.size = r_size(tstring); |
792 | 0 | ptp->transfer = (code > 0 ? (gs_mapping_proc) 0 : gs_mapped_transfer); |
793 | 0 | return 0; |
794 | 0 | } |
795 | | |
796 | | /* Extract threshold common parameters + Thresholds, Width2, Height2, */ |
797 | | /* BitsPerSample. */ |
798 | | static int |
799 | | dict_threshold2_params(const ref * pdict, gs_threshold2_halftone * ptp, |
800 | | ref * ptproc, gs_memory_t *mem) |
801 | 0 | { |
802 | 0 | ref *tstring; |
803 | 0 | int code = |
804 | 0 | dict_threshold_common_params(pdict, |
805 | 0 | (gs_threshold_halftone_common *)ptp, |
806 | 0 | &tstring, ptproc); |
807 | 0 | int bps; |
808 | 0 | uint size; |
809 | 0 | int cw2, ch2; |
810 | |
|
811 | 0 | ptp->transfer = (code > 0 ? (gs_mapping_proc) 0 : gs_mapped_transfer); |
812 | |
|
813 | 0 | if (code < 0 || |
814 | 0 | (code = cw2 = dict_int_param(pdict, "Width2", 0, 0x7fff, 0, |
815 | 0 | &ptp->width2)) < 0 || |
816 | 0 | (code = ch2 = dict_int_param(pdict, "Height2", 0, 0x7fff, 0, |
817 | 0 | &ptp->height2)) < 0 || |
818 | 0 | (code = dict_int_param(pdict, "BitsPerSample", 8, 16, -1, &bps)) < 0 |
819 | 0 | ) |
820 | 0 | return code; |
821 | 0 | if ((bps != 8 && bps != 16) || cw2 != ch2 || |
822 | 0 | (!cw2 && (ptp->width2 == 0 || ptp->height2 == 0)) |
823 | 0 | ) |
824 | 0 | return_error(gs_error_rangecheck); |
825 | 0 | ptp->bytes_per_sample = bps / 8; |
826 | 0 | switch (r_type(tstring)) { |
827 | 0 | case t_string: |
828 | 0 | size = r_size(tstring); |
829 | 0 | gs_bytestring_from_string(&ptp->thresholds, tstring->value.const_bytes, |
830 | 0 | size); |
831 | 0 | break; |
832 | 0 | case t_astruct: |
833 | 0 | if (gs_object_type(mem, tstring->value.pstruct) != &st_bytes) |
834 | 0 | return_error(gs_error_typecheck); |
835 | 0 | size = gs_object_size(mem, tstring->value.pstruct); |
836 | 0 | gs_bytestring_from_bytes(&ptp->thresholds, r_ptr(tstring, byte), |
837 | 0 | 0, size); |
838 | 0 | break; |
839 | 0 | default: |
840 | 0 | return_error(gs_error_typecheck); |
841 | 0 | } |
842 | 0 | check_read(*tstring); |
843 | 0 | if (size != (ptp->width * ptp->height + ptp->width2 * ptp->height2) * |
844 | 0 | ptp->bytes_per_sample) |
845 | 0 | return_error(gs_error_rangecheck); |
846 | 0 | return 0; |
847 | 0 | } |