xref: /plan9/sys/src/cmd/gs/src/gshtscr.c (revision 8deabd962e84f51c67a12f970084955d97d8a8f2)
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