/src/ghostpdl/base/gshtscr.c
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 | | /* Screen (Type 1) halftone processing for Ghostscript library */ |
18 | | #include "math_.h" |
19 | | #include "gx.h" |
20 | | #include "gserrors.h" |
21 | | #include "gsstruct.h" |
22 | | #include "gxarith.h" |
23 | | #include "gzstate.h" |
24 | | #include "gxdevice.h" /* for gzht.h */ |
25 | | #include "gzht.h" |
26 | | |
27 | | /* Define whether to force all halftones to be strip halftones, */ |
28 | | /* for debugging. */ |
29 | | static const bool FORCE_STRIP_HALFTONES = false; |
30 | | |
31 | | /* Structure descriptors */ |
32 | | private_st_gs_screen_enum(); |
33 | | |
34 | | /* GC procedures */ |
35 | | static |
36 | 0 | ENUM_PTRS_WITH(screen_enum_enum_ptrs, gs_screen_enum *eptr) |
37 | 0 | { |
38 | 0 | if (index < 1 + st_ht_order_max_ptrs) { |
39 | 0 | gs_ptr_type_t ret = |
40 | 0 | ENUM_USING(st_ht_order, &eptr->order, sizeof(eptr->order), |
41 | 0 | index - 1); |
42 | |
|
43 | 0 | if (ret == 0) /* don't stop early */ |
44 | 0 | ENUM_RETURN(0); |
45 | 0 | return ret; |
46 | 0 | } |
47 | 0 | return ENUM_USING(st_halftone, &eptr->halftone, sizeof(eptr->halftone), |
48 | 0 | index - (1 + st_ht_order_max_ptrs)); |
49 | 0 | } |
50 | 0 | ENUM_PTR(0, gs_screen_enum, pgs); |
51 | 0 | ENUM_PTRS_END |
52 | 0 | static RELOC_PTRS_WITH(screen_enum_reloc_ptrs, gs_screen_enum *eptr) |
53 | 0 | { |
54 | 0 | RELOC_PTR(gs_screen_enum, pgs); |
55 | 0 | RELOC_USING(st_halftone, &eptr->halftone, sizeof(gs_halftone)); |
56 | 0 | RELOC_USING(st_ht_order, &eptr->order, sizeof(gx_ht_order)); |
57 | 0 | } |
58 | 0 | RELOC_PTRS_END |
59 | | |
60 | | /* Default AccurateScreens control */ |
61 | | void |
62 | | gs_setaccuratescreens(gs_memory_t *mem, bool accurate) |
63 | 705k | { |
64 | 705k | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
65 | | |
66 | 705k | ctx->screen_accurate_screens = accurate; |
67 | 705k | } |
68 | | bool |
69 | | gs_currentaccuratescreens(gs_memory_t *mem) |
70 | 2.28M | { |
71 | 2.28M | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
72 | | |
73 | 2.28M | return ctx->screen_accurate_screens; |
74 | 2.28M | } |
75 | | |
76 | | void |
77 | | gs_setminscreenlevels(gs_memory_t *mem, uint levels) |
78 | 705k | { |
79 | 705k | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
80 | | |
81 | 705k | ctx->screen_min_screen_levels = levels; |
82 | 705k | } |
83 | | uint |
84 | | gs_currentminscreenlevels(gs_memory_t *mem) |
85 | 649k | { |
86 | 649k | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
87 | | |
88 | 649k | return ctx->screen_min_screen_levels; |
89 | 649k | } |
90 | | |
91 | | /* Initialize the screen control statics at startup. */ |
92 | | init_proc(gs_gshtscr_init); /* check prototype */ |
93 | | int |
94 | | gs_gshtscr_init(gs_memory_t *mem) |
95 | 162k | { |
96 | 162k | gs_setaccuratescreens(mem, false); |
97 | 162k | gs_setminscreenlevels(mem, 1); |
98 | 162k | return 0; |
99 | 162k | } |
100 | | |
101 | | /* |
102 | | * The following implementation notes complement the general discussion of |
103 | | * halftone tiles found in gxdht.h. |
104 | | * |
105 | | * Currently we allow R(') > 1 (i.e., multiple basic cells per multi-cell) |
106 | | * only if AccurateScreens is true or if B (the number of pixels in a basic |
107 | | * cell) < MinScreenLevels; if AccurateScreens is false and B >= |
108 | | * MinScreenLevels, multi-cells and basic cells are the same. |
109 | | * |
110 | | * To find the smallest super-cell for a given multi-cell size, i.e., the |
111 | | * smallest (absolute value) coordinates where the corners of multi-cells |
112 | | * lie on the coordinate axes, we compute the values of i and j that give |
113 | | * the minimum value of W by: |
114 | | * D = gcd(abs(M'), abs(N)), i = M'/D, j = N/D, W = C / D, |
115 | | * and similarly |
116 | | * D' = gcd(abs(M), abs(N')), i' = N'/D', j' = M/D', W' = C / D'. |
117 | | */ |
118 | | |
119 | | /* Compute the derived values of a halftone tile. */ |
120 | | void |
121 | | gx_compute_cell_values(gx_ht_cell_params_t * phcp) |
122 | 9.82M | { |
123 | 9.82M | const int M = phcp->M, N = phcp->N, M1 = phcp->M1, N1 = phcp->N1; |
124 | 9.82M | const uint m = any_abs(M), n = any_abs(N); |
125 | 9.82M | const uint m1 = any_abs(M1), n1 = any_abs(N1); |
126 | 9.82M | const ulong C = phcp->C = (ulong)m * m1 + (ulong)n * n1; |
127 | 9.82M | const int D = phcp->D = igcd(m1, n); |
128 | 9.82M | const int D1 = phcp->D1 = igcd(m, n1); |
129 | | |
130 | 9.82M | phcp->W = C / D, phcp->W1 = C / D1; |
131 | | /* Compute the shift value. */ |
132 | | /* If M1 or N is zero, the shift is zero. */ |
133 | 9.82M | if (M1 && N) { |
134 | 8.51M | int h = 0, k = 0, dy = 0; |
135 | 8.51M | int shift; |
136 | | |
137 | | /* |
138 | | * There may be a faster way to do this: see Knuth vol. 2, |
139 | | * section 4.5.2, Algorithm X (p. 302) and exercise 15 |
140 | | * (p. 315, solution p. 523). |
141 | | */ |
142 | 28.7M | while (dy != D) |
143 | 20.2M | if (dy > D) { |
144 | 9.78M | if (M1 > 0) |
145 | 9.78M | ++k; |
146 | 1.79k | else |
147 | 1.79k | --k; |
148 | 9.78M | dy -= m1; |
149 | 10.4M | } else { |
150 | 10.4M | if (N > 0) |
151 | 10.4M | ++h; |
152 | 480 | else |
153 | 480 | --h; |
154 | 10.4M | dy += n; |
155 | 10.4M | } |
156 | 8.51M | shift = h * M + k * N1; |
157 | | /* We just computed what amounts to a right shift; */ |
158 | | /* what we want is a left shift. */ |
159 | 8.51M | phcp->S = imod(-shift, phcp->W); |
160 | 8.51M | } else |
161 | 1.30M | phcp->S = 0; |
162 | 9.82M | if_debug12('h', "[h]MNR=(%d,%d)/%d, M'N'R'=(%d,%d)/%d => C=%lu, D=%d, D'=%d, W=%u, W'=%u, S=%d\n", |
163 | 9.82M | M, N, phcp->R, M1, N1, phcp->R1, |
164 | 9.82M | C, D, D1, phcp->W, phcp->W1, phcp->S); |
165 | 9.82M | } |
166 | | |
167 | | /* Forward references */ |
168 | | static int pick_cell_size(gs_screen_halftone * ph, |
169 | | const gs_matrix * pmat, ulong max_size, uint min_levels, bool accurate, |
170 | | gx_ht_cell_params_t * phcp); |
171 | | |
172 | | /* Allocate a screen enumerator. */ |
173 | | gs_screen_enum * |
174 | | gs_screen_enum_alloc(gs_memory_t * mem, client_name_t cname) |
175 | 1.63M | { |
176 | 1.63M | return gs_alloc_struct(mem, gs_screen_enum, &st_gs_screen_enum, cname); |
177 | 1.63M | } |
178 | | |
179 | | /* Set up for halftone sampling. */ |
180 | | int |
181 | | gs_screen_init(gs_screen_enum * penum, gs_gstate * pgs, |
182 | | gs_screen_halftone * phsp) |
183 | 0 | { |
184 | 0 | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(pgs->memory); |
185 | |
|
186 | 0 | return gs_screen_init_accurate(penum, pgs, phsp, |
187 | 0 | ctx->screen_accurate_screens); |
188 | 0 | } |
189 | | int |
190 | | gs_screen_init_memory(gs_screen_enum * penum, gs_gstate * pgs, |
191 | | gs_screen_halftone * phsp, bool accurate, gs_memory_t * mem) |
192 | 1.30M | { |
193 | 1.30M | int code = |
194 | 1.30M | gs_screen_order_init_memory(&penum->order, pgs, phsp, accurate, mem); |
195 | | |
196 | 1.30M | if (code < 0) |
197 | 0 | return code; |
198 | 1.30M | return |
199 | 1.30M | gs_screen_enum_init_memory(penum, &penum->order, pgs, phsp, mem); |
200 | 1.30M | } |
201 | | |
202 | | /* Allocate and initialize a spot screen. */ |
203 | | /* This is the first half of gs_screen_init_accurate. */ |
204 | | int |
205 | | gs_screen_order_alloc(gx_ht_order *porder, gs_memory_t *mem) |
206 | 1.64M | { |
207 | 1.64M | uint num_levels = porder->params.W * porder->params.D; |
208 | 1.64M | int code; |
209 | | |
210 | 1.64M | if (!FORCE_STRIP_HALFTONES && |
211 | 1.64M | ((ulong)porder->params.W1 * bitmap_raster(porder->params.W) + |
212 | 1.64M | (ulong)num_levels * sizeof(*porder->levels) + |
213 | 1.64M | (ulong)porder->params.W * porder->params.W1 * sizeof(gx_ht_bit)) <= |
214 | 1.64M | porder->screen_params.max_size) { |
215 | | /* |
216 | | * Allocate an order for the entire tile, but only sample one |
217 | | * strip. Note that this causes the order parameters to be |
218 | | * self-inconsistent until gx_ht_construct_spot_order fixes them |
219 | | * up: see gxdht.h for more information. |
220 | | */ |
221 | 1.64M | code = gx_ht_alloc_order(porder, porder->params.W, |
222 | 1.64M | porder->params.W1, 0, |
223 | 1.64M | num_levels, mem); |
224 | 1.64M | porder->height = porder->orig_height = porder->params.D; |
225 | 1.64M | porder->shift = porder->orig_shift = porder->params.S; |
226 | 1.64M | } else { |
227 | | /* Just allocate the order for a single strip. */ |
228 | 11 | code = gx_ht_alloc_order(porder, porder->params.W, |
229 | 11 | porder->params.D, porder->params.S, |
230 | 11 | num_levels, mem); |
231 | 11 | } |
232 | 1.64M | return code; |
233 | 1.64M | } |
234 | | int |
235 | | gs_screen_order_init_memory(gx_ht_order * porder, const gs_gstate * pgs, |
236 | | gs_screen_halftone * phsp, bool accurate, |
237 | | gs_memory_t * mem) |
238 | 1.63M | { |
239 | 1.63M | gs_matrix imat; |
240 | 1.63M | ulong max_size = gx_ht_cache_default_bits_size(); |
241 | 1.63M | int code; |
242 | 1.63M | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
243 | | |
244 | 1.63M | if (phsp->frequency < 0.1) |
245 | 1 | return_error(gs_error_rangecheck); |
246 | 1.63M | gs_deviceinitialmatrix(gs_currentdevice(pgs), &imat); |
247 | 1.63M | code = pick_cell_size(phsp, &imat, max_size, |
248 | 1.63M | ctx->screen_min_screen_levels, accurate, |
249 | 1.63M | &porder->params); |
250 | 1.63M | if (code < 0) |
251 | 2 | return code; |
252 | 1.63M | gx_compute_cell_values(&porder->params); |
253 | 1.63M | porder->screen_params.matrix = imat; |
254 | 1.63M | porder->screen_params.max_size = max_size; |
255 | 1.63M | return gs_screen_order_alloc(porder, mem); |
256 | 1.63M | } |
257 | | |
258 | | /* |
259 | | * Given a desired frequency, angle, and minimum number of levels, a maximum |
260 | | * cell size, and an AccurateScreens flag, pick values for M('), N('), and |
261 | | * R('). We want to get a good fit to the requested frequency and angle, |
262 | | * provide at least the requested minimum number of levels, and keep |
263 | | * rendering as fast as possible; trading these criteria off against each |
264 | | * other is what makes the code complicated. |
265 | | * |
266 | | * We compute trial values u and v from the original values of F and A. |
267 | | * Normally these will not be integers. We then examine the 4 pairs of |
268 | | * integers obtained by rounding each of u and v independently up or down, |
269 | | * and pick the pair U, V that yields the closest match to the requested |
270 | | * F and A values and doesn't require more than max_size storage for a |
271 | | * single tile. If no pair |
272 | | * yields an acceptably small W, we divide both u and v by 2 and try again. |
273 | | * Then we run the equations backward to obtain the actual F and A. |
274 | | * This is fairly easy given that we require either xx = yy = 0 or |
275 | | * xy = yx = 0. In the former case, we have |
276 | | * U = (72 / F * xx) * cos(A); |
277 | | * V = (72 / F * yy) * sin(A); |
278 | | * from which immediately |
279 | | * A = arctan((V / yy) / (U / xx)), |
280 | | * or equivalently |
281 | | * A = arctan((V * xx) / (U * yy)). |
282 | | * We can then obtain F as |
283 | | * F = (72 * xx / U) * cos(A), |
284 | | * or equivalently |
285 | | * F = (72 * yy / V) * sin(A). |
286 | | * For landscape devices, we replace xx by yx, yy by xy, and interchange |
287 | | * sin and cos, resulting in |
288 | | * A = arctan((U * xy) / (V * yx)) |
289 | | * and |
290 | | * F = (72 * yx / U) * sin(A) |
291 | | * or |
292 | | * F = (72 * xy / V) * cos(A). |
293 | | */ |
294 | | /* ph->frequency and ph->angle are input parameters; */ |
295 | | /* the routine sets ph->actual_frequency and ph->actual_angle. */ |
296 | | static int |
297 | | pick_cell_size(gs_screen_halftone * ph, const gs_matrix * pmat, ulong max_size, |
298 | | uint min_levels, bool accurate, gx_ht_cell_params_t * phcp) |
299 | 1.63M | { |
300 | 1.63M | const bool landscape = (pmat->xy != 0.0 || pmat->yx != 0.0); |
301 | | |
302 | | /* Account for a possibly reflected coordinate system. */ |
303 | | /* See gxstroke.c for the algorithm. */ |
304 | 1.63M | const bool reflected = pmat->xy * pmat->yx > pmat->xx * pmat->yy; |
305 | 1.63M | const int reflection = (reflected ? -1 : 1); |
306 | 1.63M | const int rotation = |
307 | 1.63M | (landscape ? (pmat->yx < 0 ? 90 : -90) : pmat->xx < 0 ? 180 : 0); |
308 | 1.63M | const double f0 = ph->frequency, a0 = ph->angle; |
309 | 1.63M | const double T = |
310 | 1.63M | fabs((landscape ? pmat->yx / pmat->xy : pmat->xx / pmat->yy)); |
311 | 1.63M | gs_point uv0; |
312 | | |
313 | 9.81M | #define u0 uv0.x |
314 | 8.50M | #define v0 uv0.y |
315 | 1.63M | int rt = 1; |
316 | 1.63M | double f = 0, a = 0; |
317 | 1.63M | double e_best = 1000; |
318 | 1.63M | bool better; |
319 | | |
320 | | /* |
321 | | * We need to find a vector in device space whose length is |
322 | | * 1 inch / ph->frequency and whose angle is ph->angle. |
323 | | * Because device pixels may not be square, we can't simply |
324 | | * map the length to device space and then rotate it; |
325 | | * instead, since we know that user space is uniform in X and Y, |
326 | | * we calculate the correct angle in user space before rotation. |
327 | | */ |
328 | | |
329 | | /* Compute trial values of u and v. */ |
330 | | |
331 | 1.63M | if (f0 == 0) |
332 | 0 | return_error(gs_error_rangecheck); |
333 | | |
334 | 1.63M | { |
335 | 1.63M | gs_matrix rmat; |
336 | | |
337 | 1.63M | gs_make_rotation(a0 * reflection + rotation, &rmat); |
338 | 1.63M | gs_distance_transform(72.0 / f0, 0.0, &rmat, &uv0); |
339 | 1.63M | gs_distance_transform(u0, v0, pmat, &uv0); |
340 | 1.63M | if_debug10('h', "[h]Requested: f=%g a=%g mat=[%g %g %g %g] max_size=%lu min_levels=%u =>\n u=%g v=%g\n", |
341 | 1.63M | ph->frequency, ph->angle, |
342 | 1.63M | pmat->xx, pmat->xy, pmat->yx, pmat->yy, |
343 | 1.63M | max_size, min_levels, u0, v0); |
344 | 1.63M | } |
345 | | |
346 | | /* Adjust u and v to reasonable values. */ |
347 | | |
348 | 1.63M | if (u0 == 0 && v0 == 0) |
349 | 0 | return_error(gs_error_rangecheck); |
350 | | |
351 | | /* We increment rt in a loop below until (u+v) * rt |
352 | | * is at least 4. Make sure that rt has enough range |
353 | | * to satisfy that calculation. If it doesn't then |
354 | | * give up (silly values). |
355 | | */ |
356 | 1.63M | if ((fabs(u0) + fabs(v0)) < ((double)5.0 / max_int)) |
357 | 2 | return_error(gs_error_rangecheck); |
358 | | |
359 | 1.63M | while ((fabs(u0) + fabs(v0)) * rt < 4) |
360 | 2 | ++rt; |
361 | 1.63M | phcp->C = 0; |
362 | | |
363 | 1.63M | try_size: |
364 | 1.63M | better = false; |
365 | 1.63M | { |
366 | 1.63M | double fm0 = u0 * rt; |
367 | 1.63M | double fn0 = v0 * rt; |
368 | 1.63M | int m0 = (int)floor(u0 * rt + 0.0001); |
369 | 1.63M | int n0 = (int)floor(v0 * rt + 0.0001); |
370 | 1.63M | gx_ht_cell_params_t p; |
371 | | |
372 | 1.63M | p.R = p.R1 = rt; |
373 | 4.90M | for (p.M = m0 + 1; p.M >= m0; p.M--) |
374 | 9.81M | for (p.N = n0 + 1; p.N >= n0; p.N--) { |
375 | 6.54M | long raster, wt; |
376 | | #ifdef DEBUG |
377 | | long wt_size; |
378 | | #endif |
379 | 6.54M | double fr, ar, ft, at, f_diff, a_diff, f_err, a_err; |
380 | | |
381 | 6.54M | p.M1 = (int)floor(p.M / T + 0.5); |
382 | 6.54M | p.N1 = (int)floor(p.N * T + 0.5); |
383 | | |
384 | 6.54M | if (p.M1 == 0 && p.N1 == 0) |
385 | 0 | return_error(gs_error_rangecheck); |
386 | | |
387 | 6.54M | gx_compute_cell_values(&p); |
388 | 6.54M | if_debug3('h', "[h]trying m=%d, n=%d, r=%d\n", p.M, p.N, rt); |
389 | 6.54M | wt = p.W; |
390 | 6.54M | if (wt >= max_short) |
391 | 13 | continue; |
392 | | /* Calculations below involve dividing by one or more of these values |
393 | | * make sure we can't get a divide by zero. |
394 | | */ |
395 | 6.54M | if (wt == 0 || p.D == 0 || (p.M == 0 && p.N == 0)) |
396 | 0 | continue; |
397 | | /* Check the strip size, not the full tile size, */ |
398 | | /* against max_size. */ |
399 | 6.54M | raster = bitmap_raster(wt); |
400 | 6.54M | if (raster > max_size / p.D || raster > max_long / wt) |
401 | 0 | continue; |
402 | | #ifdef DEBUG |
403 | | wt_size = raster * wt; |
404 | | #endif |
405 | | |
406 | | /* Compute the corresponding values of F and A. */ |
407 | | |
408 | 6.54M | if (landscape) |
409 | 0 | ar = atan2(p.M * (double)pmat->xy, |
410 | 0 | p.N * (double)pmat->yx), |
411 | 0 | fr = 72.0 * (p.M == 0 ? pmat->xy / p.N * cos(ar) : |
412 | 0 | pmat->yx / p.M * sin(ar)); |
413 | 6.54M | else |
414 | 6.54M | ar = atan2(p.N * (double)pmat->xx, |
415 | 6.54M | p.M * (double)pmat->yy), |
416 | 6.54M | fr = 72.0 * (p.M == 0 ? pmat->yy / p.N * sin(ar) : |
417 | 6.54M | pmat->xx / p.M * cos(ar)); |
418 | 6.54M | ft = fabs(fr) * rt; |
419 | | /* Normalize the angle to the requested quadrant. */ |
420 | 6.54M | at = (ar * radians_to_degrees - rotation) * reflection; |
421 | 6.54M | at -= floor(at / 180.0) * 180.0; |
422 | 6.54M | at += floor(a0 / 180.0) * 180.0; |
423 | 6.54M | f_diff = fabs(ft - f0); |
424 | 6.54M | a_diff = fabs(at - a0); |
425 | 6.54M | f_err = f_diff / fabs(f0); |
426 | | /* |
427 | | * We used to compute the percentage difference here: |
428 | | * a_err = (a0 == 0 ? a_diff : a_diff / fabs(a0)); |
429 | | * but using the angle difference makes more sense: |
430 | | */ |
431 | 6.54M | a_err = a_diff; |
432 | | |
433 | 6.54M | if_debug5('h', " ==> d=%d, wt=%ld, wt_size=%ld, f=%g, a=%g\n", |
434 | 6.54M | p.D, wt, bitmap_raster(wt) * wt, ft, at); |
435 | | |
436 | 6.54M | { |
437 | | /* |
438 | | * Compute the error in position between ideal location. |
439 | | * and the current integer location. |
440 | | */ |
441 | | |
442 | 6.54M | double error = |
443 | 6.54M | (fn0 - p.N) * (fn0 - p.N) + (fm0 - p.M) * (fm0 - p.M); |
444 | | /* |
445 | | * Adjust the error by the length of the vector. This gives |
446 | | * a slight bias toward larger cell sizzes. |
447 | | */ |
448 | 6.54M | error /= p.N * p.N + p.M * p.M; |
449 | 6.54M | error = sqrt(error); /* The previous calcs. gave value squared */ |
450 | 6.54M | if (error > e_best) |
451 | 654k | continue; |
452 | 5.89M | e_best = error; |
453 | 5.89M | } |
454 | 0 | *phcp = p; |
455 | 5.89M | f = ft, a = at; |
456 | 5.89M | better = true; |
457 | 5.89M | if_debug3('h', "*** best wt_size=%ld, f_diff=%g, a_diff=%g\n", |
458 | 5.89M | wt_size, f_diff, a_diff); |
459 | | /* |
460 | | * We want a maximum relative frequency error of 1% and a |
461 | | * maximum angle error of 1% (of 90 degrees). |
462 | | */ |
463 | 5.89M | if (f_err <= 0.01 && a_err <= 0.9 /*degrees*/) |
464 | 771 | goto done; |
465 | 5.89M | } |
466 | 1.63M | } |
467 | | /* It is possible (for ridiculous halftone screens) to reach this point without ever |
468 | | * having been able to find a suitable screen. In that case we will not have set |
469 | | * phcp and so phcp->C will be undefined. We can detect this condition by checking rt |
470 | | * is 1 (so its the first attempt to find a screen) and better is false (we didn't find |
471 | | * a better match). If that happens, exit with an error. |
472 | | */ |
473 | 1.63M | if (rt == 1 && !better) |
474 | 0 | return_error(gs_error_rangecheck); |
475 | | |
476 | 1.63M | if (phcp->C < min_levels) { /* We don't have enough levels yet. Keep going. */ |
477 | 0 | ++rt; |
478 | 0 | goto try_size; |
479 | 0 | } |
480 | 1.63M | if (better) { /* If we want accurate screens, continue till we fail. */ |
481 | 1.63M | if (accurate) { |
482 | 0 | ++rt; |
483 | 0 | goto try_size; |
484 | 0 | } |
485 | 1.63M | } else { /* |
486 | | * We couldn't find an acceptable M and N. If R > 1, |
487 | | * take what we've got; if R = 1, give up. |
488 | | */ |
489 | 0 | if (rt == 1) |
490 | 0 | return_error(gs_error_rangecheck); |
491 | 0 | } |
492 | | |
493 | | /* Deliver the results. */ |
494 | 1.63M | done: |
495 | 1.63M | if_debug5('h', "[h]Chosen: f=%g a=%g M=%d N=%d R=%d\n", |
496 | 1.63M | f, a, phcp->M, phcp->N, phcp->R); |
497 | 1.63M | ph->actual_frequency = f; |
498 | 1.63M | ph->actual_angle = a; |
499 | 1.63M | return 0; |
500 | 1.63M | #undef u0 |
501 | 1.63M | #undef v0 |
502 | 1.63M | } |
503 | | |
504 | | /* Prepare to sample a spot screen. */ |
505 | | /* This is the second half of gs_screen_init_accurate. */ |
506 | | int |
507 | | gs_screen_enum_init_memory(gs_screen_enum * penum, const gx_ht_order * porder, |
508 | | gs_gstate * pgs, const gs_screen_halftone * phsp, |
509 | | gs_memory_t * mem) |
510 | 2.96M | { |
511 | 2.96M | int code; |
512 | | |
513 | 2.96M | penum->pgs = pgs; /* ensure clean for GC */ |
514 | 2.96M | if (&penum->order != porder) /* Pacify Valgrind */ |
515 | 1.66M | penum->order = *porder; |
516 | 2.96M | penum->halftone.rc.memory = mem; |
517 | 2.96M | penum->halftone.type = ht_type_screen; |
518 | 2.96M | penum->halftone.params.screen = *phsp; |
519 | 2.96M | penum->x = penum->y = 0; |
520 | | |
521 | 2.96M | penum->strip = porder->num_levels / porder->width; |
522 | 2.96M | penum->shift = porder->shift; |
523 | | /* |
524 | | * We want a transformation matrix that maps the parallelogram |
525 | | * (0,0), (U,V), (U-V',V+U'), (-V',U') to the square (+/-1, +/-1). |
526 | | * If the coefficients are [a b c d e f] and we let |
527 | | * u = U = M/R, v = V = N/R, |
528 | | * r = -V' = -N'/R', s = U' = M'/R', |
529 | | * then we just need to solve the equations: |
530 | | * a*0 + c*0 + e = -1 b*0 + d*0 + f = -1 |
531 | | * a*u + c*v + e = 1 b*u + d*v + f = 1 |
532 | | * a*r + c*s + e = -1 b*r + d*s + f = 1 |
533 | | * This has the following solution: |
534 | | * Q = 2 / (M*M' + N*N') |
535 | | * a = Q * R * M' |
536 | | * b = -Q * R' * N |
537 | | * c = Q * R * N' |
538 | | * d = Q * R' * M |
539 | | * e = -1 |
540 | | * f = -1 |
541 | | */ |
542 | 2.96M | { |
543 | 2.96M | const int M = porder->params.M, N = porder->params.N, R = porder->params.R; |
544 | 2.96M | const int M1 = porder->params.M1, N1 = porder->params.N1, R1 = porder->params.R1; |
545 | 2.96M | double Q = 2.0 / ((long)M * M1 + (long)N * N1); |
546 | | |
547 | 2.96M | penum->mat.xx = Q * (R * M1); |
548 | 2.96M | penum->mat.xy = Q * (-R1 * N); |
549 | 2.96M | penum->mat.yx = Q * (R * N1); |
550 | 2.96M | penum->mat.yy = Q * (R1 * M); |
551 | 2.96M | penum->mat.tx = -1.0; |
552 | 2.96M | penum->mat.ty = -1.0; |
553 | 2.96M | code = gs_matrix_invert(&penum->mat, &penum->mat_inv); |
554 | 2.96M | } |
555 | 2.96M | if_debug7('h', "[h]Screen: (%dx%d)/%d [%f %f %f %f]\n", |
556 | 2.96M | porder->width, porder->height, porder->params.R, |
557 | 2.96M | penum->mat.xx, penum->mat.xy, |
558 | 2.96M | penum->mat.yx, penum->mat.yy); |
559 | 2.96M | return code; |
560 | 2.96M | } |
561 | | |
562 | | /* Report current point for sampling */ |
563 | | int |
564 | | gs_screen_currentpoint(gs_screen_enum * penum, gs_point * ppt) |
565 | 54.5M | { |
566 | 54.5M | gs_point pt; |
567 | 54.5M | int code; |
568 | 54.5M | double sx, sy; /* spot center in spot coords (integers) */ |
569 | 54.5M | gs_point spot_center; /* device coords */ |
570 | | |
571 | 54.5M | if (penum->y >= penum->strip) { /* all done */ |
572 | 2.96M | gx_ht_construct_spot_order(&penum->order); |
573 | 2.96M | return 1; |
574 | 2.96M | } |
575 | | /* We displace the sampled coordinates very slightly */ |
576 | | /* in order to reduce the likely number of points */ |
577 | | /* for which the spot function returns the same value. */ |
578 | 51.6M | if ((code = gs_point_transform(penum->x + 0.501, penum->y + 0.498, &penum->mat, &pt)) < 0) |
579 | 0 | return code; |
580 | | |
581 | | /* find the spot center in device coords : */ |
582 | 51.6M | sx = ceil( pt.x / 2 ) * 2; |
583 | 51.6M | sy = ceil( pt.y / 2 ) * 2; |
584 | 51.6M | if ((code = gs_point_transform(sx, sy, &penum->mat_inv, &spot_center)) < 0) |
585 | 0 | return code; |
586 | | |
587 | | /* shift the spot center to nearest pixel center : */ |
588 | 51.6M | spot_center.x = floor(spot_center.x) + 0.5; |
589 | 51.6M | spot_center.y = floor(spot_center.y) + 0.5; |
590 | | |
591 | | /* compute the spot function arguments for the shifted spot : */ |
592 | 51.6M | if ((code = gs_distance_transform(penum->x - spot_center.x + 0.501, |
593 | 51.6M | penum->y - spot_center.y + 0.498, |
594 | 51.6M | &penum->mat, &pt)) < 0) |
595 | 0 | return code; |
596 | 51.6M | pt.x += 1; |
597 | 51.6M | pt.y += 1; |
598 | | |
599 | 51.6M | if (pt.x < -1.0) |
600 | 2.66M | pt.x += ((int)(-ceil(pt.x)) + 1) & ~1; |
601 | 48.9M | else if (pt.x >= 1.0) |
602 | 5.41k | pt.x -= ((int)pt.x + 1) & ~1; |
603 | 51.6M | if (pt.y < -1.0) |
604 | 21.8k | pt.y += ((int)(-ceil(pt.y)) + 1) & ~1; |
605 | 51.5M | else if (pt.y >= 1.0) |
606 | 51 | pt.y -= ((int)pt.y + 1) & ~1; |
607 | 51.6M | *ppt = pt; |
608 | 51.6M | return 0; |
609 | 51.6M | } |
610 | | |
611 | | /* Record next halftone sample */ |
612 | | int |
613 | | gs_screen_next(gs_screen_enum * penum, double value) |
614 | 51.6M | { |
615 | 51.6M | ht_sample_t sample; |
616 | 51.6M | int width = penum->order.width; |
617 | 51.6M | gx_ht_bit *bits = (gx_ht_bit *)penum->order.bit_data; |
618 | | |
619 | 51.6M | if (value < -1.0 || value > 1.0) |
620 | 2 | return_error(gs_error_rangecheck); |
621 | 51.6M | sample = (ht_sample_t) ((value + 1) * max_ht_sample); |
622 | | #if defined(DEBUG) |
623 | | if (gs_debug_c('H')) { |
624 | | gs_point pt; |
625 | | |
626 | | gs_screen_currentpoint(penum, &pt); |
627 | | dlprintf6("[H]sample x=%d y=%d (%f,%f): %f -> %u\n", |
628 | | penum->x, penum->y, pt.x, pt.y, value, sample); |
629 | | } |
630 | | #endif |
631 | 51.6M | bits[penum->y * width + penum->x].mask = sample; |
632 | 51.6M | if (++(penum->x) >= width) |
633 | 6.94M | penum->x = 0, ++(penum->y); |
634 | 51.6M | return 0; |
635 | 51.6M | } |
636 | | |
637 | | /* Install a fully constructed screen in the gstate. */ |
638 | | int |
639 | | gs_screen_install(gs_screen_enum * penum) |
640 | 310k | { |
641 | 310k | gx_device_halftone dev_ht; |
642 | 310k | int code; |
643 | | |
644 | 310k | dev_ht.rc.memory = penum->halftone.rc.memory; |
645 | 310k | dev_ht.order = penum->order; |
646 | 310k | dev_ht.components = 0; |
647 | 310k | penum->halftone.objtype = HT_OBJTYPE_DEFAULT; |
648 | 310k | if ((code = gx_ht_install(penum->pgs, &penum->halftone, &dev_ht)) < 0) |
649 | 0 | gx_device_halftone_release(&dev_ht, dev_ht.rc.memory); |
650 | 310k | return code; |
651 | 310k | } |