Coverage Report

Created: 2025-06-10 07:06

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