/src/ghostpdl/base/gshtscr.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2022 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., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, 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 | 343k | { |
64 | 343k | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
65 | | |
66 | 343k | ctx->screen_accurate_screens = accurate; |
67 | 343k | } |
68 | | bool |
69 | | gs_currentaccuratescreens(gs_memory_t *mem) |
70 | 1.32M | { |
71 | 1.32M | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
72 | | |
73 | 1.32M | return ctx->screen_accurate_screens; |
74 | 1.32M | } |
75 | | |
76 | | void |
77 | | gs_setminscreenlevels(gs_memory_t *mem, uint levels) |
78 | 343k | { |
79 | 343k | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
80 | | |
81 | 343k | ctx->screen_min_screen_levels = levels; |
82 | 343k | } |
83 | | uint |
84 | | gs_currentminscreenlevels(gs_memory_t *mem) |
85 | 356k | { |
86 | 356k | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
87 | | |
88 | 356k | return ctx->screen_min_screen_levels; |
89 | 356k | } |
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 | 89.2k | { |
96 | 89.2k | gs_setaccuratescreens(mem, false); |
97 | 89.2k | gs_setminscreenlevels(mem, 1); |
98 | 89.2k | return 0; |
99 | 89.2k | } |
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 | 5.80M | { |
123 | 5.80M | const int M = phcp->M, N = phcp->N, M1 = phcp->M1, N1 = phcp->N1; |
124 | 5.80M | const uint m = any_abs(M), n = any_abs(N); |
125 | 5.80M | const uint m1 = any_abs(M1), n1 = any_abs(N1); |
126 | 5.80M | const ulong C = phcp->C = (ulong)m * m1 + (ulong)n * n1; |
127 | 5.80M | const int D = phcp->D = igcd(m1, n); |
128 | 5.80M | const int D1 = phcp->D1 = igcd(m, n1); |
129 | | |
130 | 5.80M | 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 | 5.80M | if (M1 && N) { |
134 | 5.05M | int h = 0, k = 0, dy = 0; |
135 | 5.05M | 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 | 16.8M | while (dy != D) |
143 | 11.8M | if (dy > D) { |
144 | 5.64M | if (M1 > 0) |
145 | 5.64M | ++k; |
146 | 449 | else |
147 | 449 | --k; |
148 | 5.64M | dy -= m1; |
149 | 6.19M | } else { |
150 | 6.19M | if (N > 0) |
151 | 6.19M | ++h; |
152 | 120 | else |
153 | 120 | --h; |
154 | 6.19M | dy += n; |
155 | 6.19M | } |
156 | 5.05M | shift = h * M + k * N1; |
157 | | /* We just computed what amounts to a right shift; */ |
158 | | /* what we want is a left shift. */ |
159 | 5.05M | phcp->S = imod(-shift, phcp->W); |
160 | 5.05M | } else |
161 | 748k | phcp->S = 0; |
162 | 5.80M | 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 | 5.80M | M, N, phcp->R, M1, N1, phcp->R1, |
164 | 5.80M | C, D, D1, phcp->W, phcp->W1, phcp->S); |
165 | 5.80M | } |
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 | 965k | { |
176 | 965k | return gs_alloc_struct(mem, gs_screen_enum, &st_gs_screen_enum, cname); |
177 | 965k | } |
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 | 744k | { |
193 | 744k | int code = |
194 | 744k | gs_screen_order_init_memory(&penum->order, pgs, phsp, accurate, mem); |
195 | | |
196 | 744k | if (code < 0) |
197 | 0 | return code; |
198 | 744k | return |
199 | 744k | gs_screen_enum_init_memory(penum, &penum->order, pgs, phsp, mem); |
200 | 744k | } |
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 | 976k | { |
207 | 976k | uint num_levels = porder->params.W * porder->params.D; |
208 | 976k | int code; |
209 | | |
210 | 976k | if (!FORCE_STRIP_HALFTONES && |
211 | 976k | ((ulong)porder->params.W1 * bitmap_raster(porder->params.W) + |
212 | 976k | (ulong)num_levels * sizeof(*porder->levels) + |
213 | 976k | (ulong)porder->params.W * porder->params.W1 * sizeof(gx_ht_bit)) <= |
214 | 976k | 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 | 976k | code = gx_ht_alloc_order(porder, porder->params.W, |
222 | 976k | porder->params.W1, 0, |
223 | 976k | num_levels, mem); |
224 | 976k | porder->height = porder->orig_height = porder->params.D; |
225 | 976k | porder->shift = porder->orig_shift = porder->params.S; |
226 | 976k | } else { |
227 | | /* Just allocate the order for a single strip. */ |
228 | 7 | code = gx_ht_alloc_order(porder, porder->params.W, |
229 | 7 | porder->params.D, porder->params.S, |
230 | 7 | num_levels, mem); |
231 | 7 | } |
232 | 976k | return code; |
233 | 976k | } |
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 | 965k | { |
239 | 965k | gs_matrix imat; |
240 | 965k | ulong max_size = gx_ht_cache_default_bits_size(); |
241 | 965k | int code; |
242 | 965k | gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem); |
243 | | |
244 | 965k | if (phsp->frequency < 0.1) |
245 | 1 | return_error(gs_error_rangecheck); |
246 | 965k | gs_deviceinitialmatrix(gs_currentdevice(pgs), &imat); |
247 | 965k | code = pick_cell_size(phsp, &imat, max_size, |
248 | 965k | ctx->screen_min_screen_levels, accurate, |
249 | 965k | &porder->params); |
250 | 965k | if (code < 0) |
251 | 2 | return code; |
252 | 965k | gx_compute_cell_values(&porder->params); |
253 | 965k | porder->screen_params.matrix = imat; |
254 | 965k | porder->screen_params.max_size = max_size; |
255 | 965k | return gs_screen_order_alloc(porder, mem); |
256 | 965k | } |
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 | 965k | { |
300 | 965k | 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 | 965k | const bool reflected = pmat->xy * pmat->yx > pmat->xx * pmat->yy; |
305 | 965k | const int reflection = (reflected ? -1 : 1); |
306 | 965k | const int rotation = |
307 | 965k | (landscape ? (pmat->yx < 0 ? 90 : -90) : pmat->xx < 0 ? 180 : 0); |
308 | 965k | const double f0 = ph->frequency, a0 = ph->angle; |
309 | 965k | const double T = |
310 | 965k | fabs((landscape ? pmat->yx / pmat->xy : pmat->xx / pmat->yy)); |
311 | 965k | gs_point uv0; |
312 | | |
313 | 5.79M | #define u0 uv0.x |
314 | 5.01M | #define v0 uv0.y |
315 | 965k | int rt = 1; |
316 | 965k | double f = 0, a = 0; |
317 | 965k | double e_best = 1000; |
318 | 965k | 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 | 965k | { |
332 | 965k | gs_matrix rmat; |
333 | | |
334 | 965k | gs_make_rotation(a0 * reflection + rotation, &rmat); |
335 | 965k | gs_distance_transform(72.0 / f0, 0.0, &rmat, &uv0); |
336 | 965k | gs_distance_transform(u0, v0, pmat, &uv0); |
337 | 965k | 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", |
338 | 965k | ph->frequency, ph->angle, |
339 | 965k | pmat->xx, pmat->xy, pmat->yx, pmat->yy, |
340 | 965k | max_size, min_levels, u0, v0); |
341 | 965k | } |
342 | | |
343 | | /* Adjust u and v to reasonable values. */ |
344 | | |
345 | 965k | if (u0 == 0 && v0 == 0) |
346 | 0 | return_error(gs_error_rangecheck); |
347 | | |
348 | | /* We increment rt in a loop below until (u+v) * rt |
349 | | * is at least 4. Make sure that rt has enough range |
350 | | * to satisfy that calculation. If it doesn't then |
351 | | * give up (silly values). |
352 | | */ |
353 | 965k | if ((fabs(u0) + fabs(v0)) < ((double)5.0 / max_int)) |
354 | 2 | return_error(gs_error_rangecheck); |
355 | | |
356 | 965k | while ((fabs(u0) + fabs(v0)) * rt < 4) |
357 | 4 | ++rt; |
358 | 965k | phcp->C = 0; |
359 | | |
360 | 965k | try_size: |
361 | 965k | better = false; |
362 | 965k | { |
363 | 965k | double fm0 = u0 * rt; |
364 | 965k | double fn0 = v0 * rt; |
365 | 965k | int m0 = (int)floor(u0 * rt + 0.0001); |
366 | 965k | int n0 = (int)floor(v0 * rt + 0.0001); |
367 | 965k | gx_ht_cell_params_t p; |
368 | | |
369 | 965k | p.R = p.R1 = rt; |
370 | 2.89M | for (p.M = m0 + 1; p.M >= m0; p.M--) |
371 | 5.79M | for (p.N = n0 + 1; p.N >= n0; p.N--) { |
372 | 3.86M | long raster, wt; |
373 | | #ifdef DEBUG |
374 | | long wt_size; |
375 | | #endif |
376 | 3.86M | double fr, ar, ft, at, f_diff, a_diff, f_err, a_err; |
377 | | |
378 | 3.86M | p.M1 = (int)floor(p.M / T + 0.5); |
379 | 3.86M | p.N1 = (int)floor(p.N * T + 0.5); |
380 | | |
381 | 3.86M | if (p.M1 == 0 && p.N1 == 0) |
382 | 0 | return_error(gs_error_rangecheck); |
383 | | |
384 | 3.86M | gx_compute_cell_values(&p); |
385 | 3.86M | if_debug3('h', "[h]trying m=%d, n=%d, r=%d\n", p.M, p.N, rt); |
386 | 3.86M | wt = p.W; |
387 | 3.86M | if (wt >= max_short) |
388 | 4 | continue; |
389 | | /* Check the strip size, not the full tile size, */ |
390 | | /* against max_size. */ |
391 | 3.86M | raster = bitmap_raster(wt); |
392 | 3.86M | if (raster > max_size / p.D || raster > max_long / wt) |
393 | 0 | continue; |
394 | | #ifdef DEBUG |
395 | | wt_size = raster * wt; |
396 | | #endif |
397 | | |
398 | | /* Compute the corresponding values of F and A. */ |
399 | | |
400 | 3.86M | if (landscape) |
401 | 0 | ar = atan2(p.M * (double)pmat->xy, |
402 | 0 | p.N * (double)pmat->yx), |
403 | 0 | fr = 72.0 * (p.M == 0 ? pmat->xy / p.N * cos(ar) : |
404 | 0 | pmat->yx / p.M * sin(ar)); |
405 | 3.86M | else |
406 | 3.86M | ar = atan2(p.N * (double)pmat->xx, |
407 | 3.86M | p.M * (double)pmat->yy), |
408 | 3.86M | fr = 72.0 * (p.M == 0 ? pmat->yy / p.N * sin(ar) : |
409 | 3.86M | pmat->xx / p.M * cos(ar)); |
410 | 3.86M | ft = fabs(fr) * rt; |
411 | | /* Normalize the angle to the requested quadrant. */ |
412 | 3.86M | at = (ar * radians_to_degrees - rotation) * reflection; |
413 | 3.86M | at -= floor(at / 180.0) * 180.0; |
414 | 3.86M | at += floor(a0 / 180.0) * 180.0; |
415 | 3.86M | f_diff = fabs(ft - f0); |
416 | 3.86M | a_diff = fabs(at - a0); |
417 | 3.86M | f_err = f_diff / fabs(f0); |
418 | | /* |
419 | | * We used to compute the percentage difference here: |
420 | | * a_err = (a0 == 0 ? a_diff : a_diff / fabs(a0)); |
421 | | * but using the angle difference makes more sense: |
422 | | */ |
423 | 3.86M | a_err = a_diff; |
424 | | |
425 | 3.86M | if_debug5('h', " ==> d=%d, wt=%ld, wt_size=%ld, f=%g, a=%g\n", |
426 | 3.86M | p.D, wt, bitmap_raster(wt) * wt, ft, at); |
427 | | |
428 | 3.86M | { |
429 | | /* |
430 | | * Compute the error in position between ideal location. |
431 | | * and the current integer location. |
432 | | */ |
433 | | |
434 | 3.86M | double error = |
435 | 3.86M | (fn0 - p.N) * (fn0 - p.N) + (fm0 - p.M) * (fm0 - p.M); |
436 | | /* |
437 | | * Adjust the error by the length of the vector. This gives |
438 | | * a slight bias toward larger cell sizzes. |
439 | | */ |
440 | 3.86M | error /= p.N * p.N + p.M * p.M; |
441 | 3.86M | error = sqrt(error); /* The previous calcs. gave value squared */ |
442 | 3.86M | if (error > e_best) |
443 | 373k | continue; |
444 | 3.48M | e_best = error; |
445 | 3.48M | } |
446 | 0 | *phcp = p; |
447 | 3.48M | f = ft, a = at; |
448 | 3.48M | better = true; |
449 | 3.48M | if_debug3('h', "*** best wt_size=%ld, f_diff=%g, a_diff=%g\n", |
450 | 3.48M | wt_size, f_diff, a_diff); |
451 | | /* |
452 | | * We want a maximum relative frequency error of 1% and a |
453 | | * maximum angle error of 1% (of 90 degrees). |
454 | | */ |
455 | 3.48M | if (f_err <= 0.01 && a_err <= 0.9 /*degrees*/) |
456 | 905 | goto done; |
457 | 3.48M | } |
458 | 965k | } |
459 | | /* It is possible (for ridiculous halftone screens) to reach this point without ever |
460 | | * having been able to find a suitable screen. In that case we will not have set |
461 | | * phcp and so phcp->C will be undefined. We can detect this condition by checking rt |
462 | | * is 1 (so its the first attempt to find a screen) and better is false (we didn't find |
463 | | * a better match). If that happens, exit with an error. |
464 | | */ |
465 | 964k | if (rt == 1 && !better) |
466 | 0 | return_error(gs_error_rangecheck); |
467 | | |
468 | 964k | if (phcp->C < min_levels) { /* We don't have enough levels yet. Keep going. */ |
469 | 0 | ++rt; |
470 | 0 | goto try_size; |
471 | 0 | } |
472 | 964k | if (better) { /* If we want accurate screens, continue till we fail. */ |
473 | 964k | if (accurate) { |
474 | 0 | ++rt; |
475 | 0 | goto try_size; |
476 | 0 | } |
477 | 964k | } else { /* |
478 | | * We couldn't find an acceptable M and N. If R > 1, |
479 | | * take what we've got; if R = 1, give up. |
480 | | */ |
481 | 0 | if (rt == 1) |
482 | 0 | return_error(gs_error_rangecheck); |
483 | 0 | } |
484 | | |
485 | | /* Deliver the results. */ |
486 | 965k | done: |
487 | 965k | if_debug5('h', "[h]Chosen: f=%g a=%g M=%d N=%d R=%d\n", |
488 | 965k | f, a, phcp->M, phcp->N, phcp->R); |
489 | 965k | ph->actual_frequency = f; |
490 | 965k | ph->actual_angle = a; |
491 | 965k | return 0; |
492 | 964k | #undef u0 |
493 | 964k | #undef v0 |
494 | 964k | } |
495 | | |
496 | | /* Prepare to sample a spot screen. */ |
497 | | /* This is the second half of gs_screen_init_accurate. */ |
498 | | int |
499 | | gs_screen_enum_init_memory(gs_screen_enum * penum, const gx_ht_order * porder, |
500 | | gs_gstate * pgs, const gs_screen_halftone * phsp, |
501 | | gs_memory_t * mem) |
502 | 1.75M | { |
503 | 1.75M | int code; |
504 | | |
505 | 1.75M | penum->pgs = pgs; /* ensure clean for GC */ |
506 | 1.75M | if (&penum->order != porder) /* Pacify Valgrind */ |
507 | 1.01M | penum->order = *porder; |
508 | 1.75M | penum->halftone.rc.memory = mem; |
509 | 1.75M | penum->halftone.type = ht_type_screen; |
510 | 1.75M | penum->halftone.params.screen = *phsp; |
511 | 1.75M | penum->x = penum->y = 0; |
512 | | |
513 | 1.75M | penum->strip = porder->num_levels / porder->width; |
514 | 1.75M | penum->shift = porder->shift; |
515 | | /* |
516 | | * We want a transformation matrix that maps the parallelogram |
517 | | * (0,0), (U,V), (U-V',V+U'), (-V',U') to the square (+/-1, +/-1). |
518 | | * If the coefficients are [a b c d e f] and we let |
519 | | * u = U = M/R, v = V = N/R, |
520 | | * r = -V' = -N'/R', s = U' = M'/R', |
521 | | * then we just need to solve the equations: |
522 | | * a*0 + c*0 + e = -1 b*0 + d*0 + f = -1 |
523 | | * a*u + c*v + e = 1 b*u + d*v + f = 1 |
524 | | * a*r + c*s + e = -1 b*r + d*s + f = 1 |
525 | | * This has the following solution: |
526 | | * Q = 2 / (M*M' + N*N') |
527 | | * a = Q * R * M' |
528 | | * b = -Q * R' * N |
529 | | * c = Q * R * N' |
530 | | * d = Q * R' * M |
531 | | * e = -1 |
532 | | * f = -1 |
533 | | */ |
534 | 1.75M | { |
535 | 1.75M | const int M = porder->params.M, N = porder->params.N, R = porder->params.R; |
536 | 1.75M | const int M1 = porder->params.M1, N1 = porder->params.N1, R1 = porder->params.R1; |
537 | 1.75M | double Q = 2.0 / ((long)M * M1 + (long)N * N1); |
538 | | |
539 | 1.75M | penum->mat.xx = Q * (R * M1); |
540 | 1.75M | penum->mat.xy = Q * (-R1 * N); |
541 | 1.75M | penum->mat.yx = Q * (R * N1); |
542 | 1.75M | penum->mat.yy = Q * (R1 * M); |
543 | 1.75M | penum->mat.tx = -1.0; |
544 | 1.75M | penum->mat.ty = -1.0; |
545 | 1.75M | code = gs_matrix_invert(&penum->mat, &penum->mat_inv); |
546 | 1.75M | } |
547 | 1.75M | if_debug7('h', "[h]Screen: (%dx%d)/%d [%f %f %f %f]\n", |
548 | 1.75M | porder->width, porder->height, porder->params.R, |
549 | 1.75M | penum->mat.xx, penum->mat.xy, |
550 | 1.75M | penum->mat.yx, penum->mat.yy); |
551 | 1.75M | return code; |
552 | 1.75M | } |
553 | | |
554 | | /* Report current point for sampling */ |
555 | | int |
556 | | gs_screen_currentpoint(gs_screen_enum * penum, gs_point * ppt) |
557 | 32.0M | { |
558 | 32.0M | gs_point pt; |
559 | 32.0M | int code; |
560 | 32.0M | double sx, sy; /* spot center in spot coords (integers) */ |
561 | 32.0M | gs_point spot_center; /* device coords */ |
562 | | |
563 | 32.0M | if (penum->y >= penum->strip) { /* all done */ |
564 | 1.75M | gx_ht_construct_spot_order(&penum->order); |
565 | 1.75M | return 1; |
566 | 1.75M | } |
567 | | /* We displace the sampled coordinates very slightly */ |
568 | | /* in order to reduce the likely number of points */ |
569 | | /* for which the spot function returns the same value. */ |
570 | 30.3M | if ((code = gs_point_transform(penum->x + 0.501, penum->y + 0.498, &penum->mat, &pt)) < 0) |
571 | 0 | return code; |
572 | | |
573 | | /* find the spot center in device coords : */ |
574 | 30.3M | sx = ceil( pt.x / 2 ) * 2; |
575 | 30.3M | sy = ceil( pt.y / 2 ) * 2; |
576 | 30.3M | if ((code = gs_point_transform(sx, sy, &penum->mat_inv, &spot_center)) < 0) |
577 | 0 | return code; |
578 | | |
579 | | /* shift the spot center to nearest pixel center : */ |
580 | 30.3M | spot_center.x = floor(spot_center.x) + 0.5; |
581 | 30.3M | spot_center.y = floor(spot_center.y) + 0.5; |
582 | | |
583 | | /* compute the spot function arguments for the shifted spot : */ |
584 | 30.3M | if ((code = gs_distance_transform(penum->x - spot_center.x + 0.501, |
585 | 30.3M | penum->y - spot_center.y + 0.498, |
586 | 30.3M | &penum->mat, &pt)) < 0) |
587 | 0 | return code; |
588 | 30.3M | pt.x += 1; |
589 | 30.3M | pt.y += 1; |
590 | | |
591 | 30.3M | if (pt.x < -1.0) |
592 | 1.59M | pt.x += ((int)(-ceil(pt.x)) + 1) & ~1; |
593 | 28.7M | else if (pt.x >= 1.0) |
594 | 259 | pt.x -= ((int)pt.x + 1) & ~1; |
595 | 30.3M | if (pt.y < -1.0) |
596 | 21.5k | pt.y += ((int)(-ceil(pt.y)) + 1) & ~1; |
597 | 30.2M | else if (pt.y >= 1.0) |
598 | 51 | pt.y -= ((int)pt.y + 1) & ~1; |
599 | 30.3M | *ppt = pt; |
600 | 30.3M | return 0; |
601 | 30.3M | } |
602 | | |
603 | | /* Record next halftone sample */ |
604 | | int |
605 | | gs_screen_next(gs_screen_enum * penum, double value) |
606 | 30.3M | { |
607 | 30.3M | ht_sample_t sample; |
608 | 30.3M | int width = penum->order.width; |
609 | 30.3M | gx_ht_bit *bits = (gx_ht_bit *)penum->order.bit_data; |
610 | | |
611 | 30.3M | if (value < -1.0 || value > 1.0) |
612 | 2 | return_error(gs_error_rangecheck); |
613 | 30.3M | sample = (ht_sample_t) ((value + 1) * max_ht_sample); |
614 | | #if defined(DEBUG) |
615 | | if (gs_debug_c('H')) { |
616 | | gs_point pt; |
617 | | |
618 | | gs_screen_currentpoint(penum, &pt); |
619 | | dlprintf6("[H]sample x=%d y=%d (%f,%f): %f -> %u\n", |
620 | | penum->x, penum->y, pt.x, pt.y, value, sample); |
621 | | } |
622 | | #endif |
623 | 30.3M | bits[penum->y * width + penum->x].mask = sample; |
624 | 30.3M | if (++(penum->x) >= width) |
625 | 4.12M | penum->x = 0, ++(penum->y); |
626 | 30.3M | return 0; |
627 | 30.3M | } |
628 | | |
629 | | /* Install a fully constructed screen in the gstate. */ |
630 | | int |
631 | | gs_screen_install(gs_screen_enum * penum) |
632 | 178k | { |
633 | 178k | gx_device_halftone dev_ht; |
634 | 178k | int code; |
635 | | |
636 | 178k | dev_ht.rc.memory = penum->halftone.rc.memory; |
637 | 178k | dev_ht.order = penum->order; |
638 | 178k | dev_ht.components = 0; |
639 | 178k | penum->halftone.objtype = HT_OBJTYPE_DEFAULT; |
640 | 178k | if ((code = gx_ht_install(penum->pgs, &penum->halftone, &dev_ht)) < 0) |
641 | 0 | gx_device_halftone_release(&dev_ht, dev_ht.rc.memory); |
642 | 178k | return code; |
643 | 178k | } |