Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Halftone definition operators */ |
18 | | #include "ghost.h" |
19 | | #include "memory_.h" |
20 | | #include "oper.h" |
21 | | #include "estack.h" |
22 | | #include "gsstruct.h" /* must precede igstate.h, */ |
23 | | /* because of #ifdef in gsht.h */ |
24 | | #include "ialloc.h" |
25 | | #include "igstate.h" |
26 | | #include "gsmatrix.h" |
27 | | #include "gxdevice.h" /* for gzht.h */ |
28 | | #include "gzht.h" |
29 | | #include "gsstate.h" |
30 | | #include "iht.h" /* prototypes */ |
31 | | #include "store.h" |
32 | | |
33 | | /* Forward references */ |
34 | | static int screen_sample(i_ctx_t *); |
35 | | static int set_screen_continue(i_ctx_t *); |
36 | | static int screen_cleanup(i_ctx_t *); |
37 | | |
38 | | /* - .currenthalftone <dict> 0 */ |
39 | | /* - .currenthalftone <frequency> <angle> <proc> 1 */ |
40 | | /* - .currenthalftone <red_freq> ... <gray_proc> 2 */ |
41 | | static int |
42 | | zcurrenthalftone(i_ctx_t *i_ctx_p) |
43 | 162k | { |
44 | 162k | os_ptr op = osp; |
45 | 162k | gs_halftone ht; |
46 | | |
47 | 162k | gs_currenthalftone(igs, &ht); |
48 | 162k | switch (ht.type) { |
49 | 107 | case ht_type_screen: |
50 | 107 | push(4); |
51 | 107 | make_real(op - 3, ht.params.screen.frequency); |
52 | 107 | make_real(op - 2, ht.params.screen.angle); |
53 | 107 | op[-1] = istate->screen_procs.gray; |
54 | 107 | make_int(op, 1); |
55 | 107 | break; |
56 | 53 | case ht_type_colorscreen: |
57 | 53 | push(13); |
58 | 53 | { |
59 | 53 | os_ptr opc = op - 12; |
60 | 53 | gs_screen_halftone *pht = |
61 | 53 | &ht.params.colorscreen.screens.colored.red; |
62 | | |
63 | 53 | make_real(opc, pht->frequency); |
64 | 53 | make_real(opc + 1, pht->angle); |
65 | 53 | opc[2] = istate->screen_procs.red; |
66 | | |
67 | 53 | opc = op - 9; |
68 | 53 | pht = &ht.params.colorscreen.screens.colored.green; |
69 | 53 | make_real(opc, pht->frequency); |
70 | 53 | make_real(opc + 1, pht->angle); |
71 | 53 | opc[2] = istate->screen_procs.green; |
72 | | |
73 | 53 | opc = op - 6; |
74 | 53 | pht = &ht.params.colorscreen.screens.colored.blue; |
75 | 53 | make_real(opc, pht->frequency); |
76 | 53 | make_real(opc + 1, pht->angle); |
77 | 53 | opc[2] = istate->screen_procs.blue; |
78 | | |
79 | 53 | opc = op - 3; |
80 | 53 | pht = &ht.params.colorscreen.screens.colored.gray; |
81 | 53 | make_real(opc, pht->frequency); |
82 | 53 | make_real(opc + 1, pht->angle); |
83 | 53 | opc[2] = istate->screen_procs.gray; |
84 | 53 | } |
85 | 53 | make_int(op, 2); |
86 | 53 | break; |
87 | 162k | default: /* Screen was set by sethalftone. */ |
88 | 162k | push(2); |
89 | 162k | op[-1] = istate->halftone; |
90 | 162k | make_int(op, 0); |
91 | 162k | break; |
92 | 162k | } |
93 | 162k | return 0; |
94 | 162k | } |
95 | | |
96 | | /* - .currentscreenlevels <int> */ |
97 | | static int |
98 | | zcurrentscreenlevels(i_ctx_t *i_ctx_p) |
99 | 0 | { |
100 | 0 | os_ptr op = osp; |
101 | |
|
102 | 0 | push(1); |
103 | 0 | make_int(op, gs_currentscreenlevels(igs)); |
104 | 0 | return 0; |
105 | 0 | } |
106 | | |
107 | | /* The setscreen operator is complex because it has to sample */ |
108 | | /* each pixel in the pattern cell, calling a procedure, and then */ |
109 | | /* sort the result into a whitening order. */ |
110 | | |
111 | | /* Layout of stuff pushed on estack: */ |
112 | | /* Control mark, */ |
113 | | /* [other stuff for other screen-setting operators], */ |
114 | | /* finishing procedure (or 0), */ |
115 | | /* spot procedure, */ |
116 | | /* enumeration structure (as bytes). */ |
117 | 3.23M | #define snumpush 4 |
118 | 31.6M | #define sproc esp[-1] |
119 | 59.4M | #define senum r_ptr(esp, gs_screen_enum) |
120 | | |
121 | | /* Forward references */ |
122 | | static int setscreen_finish(i_ctx_t *); |
123 | | |
124 | | /* <frequency> <angle> <proc> setscreen - */ |
125 | | static int |
126 | | zsetscreen(i_ctx_t *i_ctx_p) |
127 | 310k | { |
128 | 310k | os_ptr op = osp; |
129 | 310k | gs_screen_halftone screen = { 0 }; |
130 | 310k | gx_ht_order order; |
131 | 310k | int code = zscreen_params(op, &screen); |
132 | 310k | gs_memory_t *mem; |
133 | 310k | int space_index; |
134 | | |
135 | 310k | check_op(3); |
136 | 310k | space_index = r_space_index(op); |
137 | | |
138 | 310k | if (code < 0) |
139 | 14 | return code; |
140 | 310k | mem = (gs_memory_t *)idmemory->spaces_indexed[space_index]; |
141 | | /* |
142 | | * Allocate the halftone in the same VM space as the procedure. |
143 | | * This keeps the space relationships consistent. |
144 | | */ |
145 | 310k | code = gs_screen_order_init_memory(&order, igs, &screen, |
146 | 310k | gs_currentaccuratescreens(mem), mem); |
147 | 310k | if (code < 0) |
148 | 3 | return code; |
149 | 310k | return zscreen_enum_init(i_ctx_p, &order, &screen, op, 3, |
150 | 310k | setscreen_finish, space_index); |
151 | 310k | } |
152 | | |
153 | | /* We break out the body of this operator so it can be shared with */ |
154 | | /* the code for Type 1 halftones in sethalftone. */ |
155 | | int |
156 | | zscreen_enum_init(i_ctx_t *i_ctx_p, const gx_ht_order * porder, |
157 | | gs_screen_halftone * psp, ref * pproc, int npop, |
158 | | int (*finish_proc)(i_ctx_t *), int space_index) |
159 | 1.61M | { |
160 | 1.61M | gs_screen_enum *penum; |
161 | 1.61M | gs_memory_t * mem = (gs_memory_t *)idmemory->spaces_indexed[space_index]; |
162 | 1.61M | int code; |
163 | | |
164 | 1.61M | check_estack(snumpush + 1); |
165 | 1.61M | penum = gs_screen_enum_alloc(mem, "setscreen"); |
166 | 1.61M | if (penum == 0) |
167 | 0 | return_error(gs_error_VMerror); |
168 | 1.61M | make_struct(esp + snumpush, space_index << r_space_shift, penum); /* do early for screen_cleanup in case of error */ |
169 | 1.61M | code = gs_screen_enum_init_memory(penum, porder, igs, psp, mem); |
170 | 1.61M | if (code < 0) { |
171 | 0 | screen_cleanup(i_ctx_p); |
172 | 0 | return code; |
173 | 0 | } |
174 | | /* Push everything on the estack */ |
175 | 1.61M | make_mark_estack(esp + 1, es_other, screen_cleanup); |
176 | 1.61M | esp += snumpush; |
177 | 1.61M | make_op_estack(esp - 2, finish_proc); |
178 | 1.61M | sproc = *pproc; |
179 | 1.61M | push_op_estack(screen_sample); |
180 | 1.61M | pop(npop); |
181 | 1.61M | return o_push_estack; |
182 | 1.61M | } |
183 | | /* Set up the next sample */ |
184 | | static int |
185 | | screen_sample(i_ctx_t *i_ctx_p) |
186 | 30.3M | { |
187 | 30.3M | os_ptr op = osp; |
188 | 30.3M | gs_screen_enum *penum = senum; |
189 | 30.3M | gs_point pt; |
190 | 30.3M | int code = gs_screen_currentpoint(penum, &pt); |
191 | 30.3M | ref proc; |
192 | | |
193 | 30.3M | switch (code) { |
194 | 0 | default: |
195 | 0 | return code; |
196 | 1.61M | case 1: |
197 | | /* All done */ |
198 | 1.61M | if (real_opproc(esp - 2) != 0) |
199 | 310k | code = (*real_opproc(esp - 2)) (i_ctx_p); |
200 | 1.61M | esp -= snumpush; |
201 | 1.61M | screen_cleanup(i_ctx_p); |
202 | 1.61M | return (code < 0 ? code : o_pop_estack); |
203 | 28.7M | case 0: |
204 | 28.7M | ; |
205 | 30.3M | } |
206 | 30.3M | push(2); |
207 | 28.7M | make_real(op - 1, pt.x); |
208 | 28.7M | make_real(op, pt.y); |
209 | 28.7M | proc = sproc; |
210 | 28.7M | push_op_estack(set_screen_continue); |
211 | 28.7M | *++esp = proc; |
212 | 28.7M | return o_push_estack; |
213 | 28.7M | } |
214 | | /* Continuation procedure for processing sampled pixels. */ |
215 | | static int |
216 | | set_screen_continue(i_ctx_t *i_ctx_p) |
217 | 28.7M | { |
218 | 28.7M | os_ptr op = osp; |
219 | 28.7M | double value; |
220 | 28.7M | int code = real_param(op, &value); |
221 | | |
222 | 28.7M | if (code < 0) |
223 | 0 | return code; |
224 | 28.7M | code = gs_screen_next(senum, value); |
225 | 28.7M | if (code < 0) |
226 | 2 | return code; |
227 | 28.7M | ref_stack_pop(&o_stack, 1); |
228 | 28.7M | return screen_sample(i_ctx_p); |
229 | 28.7M | } |
230 | | /* Finish setscreen. */ |
231 | | static int |
232 | | setscreen_finish(i_ctx_t *i_ctx_p) |
233 | 310k | { |
234 | 310k | gs_screen_install(senum); |
235 | 310k | istate->screen_procs.red = sproc; |
236 | 310k | istate->screen_procs.green = sproc; |
237 | 310k | istate->screen_procs.blue = sproc; |
238 | 310k | istate->screen_procs.gray = sproc; |
239 | 310k | make_null(&istate->halftone); |
240 | 310k | return 0; |
241 | 310k | } |
242 | | /* Clean up after screen enumeration */ |
243 | | static int |
244 | | screen_cleanup(i_ctx_t *i_ctx_p) |
245 | 1.61M | { |
246 | 1.61M | gs_screen_enum *penum = r_ptr(esp + snumpush, gs_screen_enum); |
247 | | |
248 | 1.61M | gs_free_object(penum->halftone.rc.memory, penum, "screen_cleanup"); |
249 | 1.61M | return 0; |
250 | 1.61M | } |
251 | | |
252 | | /* ------ Utility procedures ------ */ |
253 | | |
254 | | /* Get parameters for a single screen. */ |
255 | | int |
256 | | zscreen_params(os_ptr op, gs_screen_halftone * phs) |
257 | 1.61M | { |
258 | 1.61M | double fa[2]; |
259 | 1.61M | int code = num_params(op - 1, 2, fa); |
260 | | |
261 | 1.61M | if (code < 0) |
262 | 5 | return code; |
263 | 1.61M | check_proc(*op); |
264 | 1.61M | phs->frequency = fa[0]; |
265 | 1.61M | phs->angle = fa[1]; |
266 | 1.61M | return 0; |
267 | 1.61M | } |
268 | | |
269 | | /* ------ Initialization procedure ------ */ |
270 | | |
271 | | const op_def zht_op_defs[] = |
272 | | { |
273 | | {"0.currenthalftone", zcurrenthalftone}, |
274 | | {"0.currentscreenlevels", zcurrentscreenlevels}, |
275 | | {"3setscreen", zsetscreen}, |
276 | | /* Internal operators */ |
277 | | {"0%screen_sample", screen_sample}, |
278 | | {"1%set_screen_continue", set_screen_continue}, |
279 | | {"0%setscreen_finish", setscreen_finish}, |
280 | | op_def_end(0) |
281 | | }; |