Coverage Report

Created: 2025-08-28 07:06

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