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