Coverage Report

Created: 2025-11-16 07:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gshtscr.c
Line
Count
Source
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
851k
{
64
851k
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
65
66
851k
    ctx->screen_accurate_screens = accurate;
67
851k
}
68
bool
69
gs_currentaccuratescreens(gs_memory_t *mem)
70
2.59M
{
71
2.59M
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
72
73
2.59M
    return ctx->screen_accurate_screens;
74
2.59M
}
75
76
void
77
gs_setminscreenlevels(gs_memory_t *mem, uint levels)
78
851k
{
79
851k
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
80
81
851k
    ctx->screen_min_screen_levels = levels;
82
851k
}
83
uint
84
gs_currentminscreenlevels(gs_memory_t *mem)
85
770k
{
86
770k
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
87
88
770k
    return ctx->screen_min_screen_levels;
89
770k
}
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
192k
{
96
192k
    gs_setaccuratescreens(mem, false);
97
192k
    gs_setminscreenlevels(mem, 1);
98
192k
    return 0;
99
192k
}
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.9M
{
123
10.9M
    const int M = phcp->M, N = phcp->N, M1 = phcp->M1, N1 = phcp->N1;
124
10.9M
    const uint m = any_abs(M), n = any_abs(N);
125
10.9M
    const uint m1 = any_abs(M1), n1 = any_abs(N1);
126
10.9M
    const ulong C = phcp->C = (ulong)m * m1 + (ulong)n * n1;
127
10.9M
    const int D = phcp->D = igcd(m1, n);
128
10.9M
    const int D1 = phcp->D1 = igcd(m, n1);
129
130
10.9M
    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.9M
    if (M1 && N) {
134
9.47M
        int h = 0, k = 0, dy = 0;
135
9.47M
        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
32.0M
        while (dy != D)
143
22.5M
            if (dy > D) {
144
10.9M
                if (M1 > 0)
145
10.9M
                    ++k;
146
2.71k
                else
147
2.71k
                    --k;
148
10.9M
                dy -= m1;
149
11.6M
            } else {
150
11.6M
                if (N > 0)
151
11.6M
                    ++h;
152
744
                else
153
744
                    --h;
154
11.6M
                dy += n;
155
11.6M
            }
156
9.47M
        shift = h * M + k * N1;
157
        /* We just computed what amounts to a right shift; */
158
        /* what we want is a left shift. */
159
9.47M
        phcp->S = imod(-shift, phcp->W);
160
9.47M
    } else
161
1.47M
        phcp->S = 0;
162
10.9M
    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.9M
               M, N, phcp->R, M1, N1, phcp->R1,
164
10.9M
               C, D, D1, phcp->W, phcp->W1, phcp->S);
165
10.9M
}
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.82M
{
176
1.82M
    return gs_alloc_struct(mem, gs_screen_enum, &st_gs_screen_enum, cname);
177
1.82M
}
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.46M
{
193
1.46M
    int code =
194
1.46M
    gs_screen_order_init_memory(&penum->order, pgs, phsp, accurate, mem);
195
196
1.46M
    if (code < 0)
197
0
        return code;
198
1.46M
    return
199
1.46M
        gs_screen_enum_init_memory(penum, &penum->order, pgs, phsp, mem);
200
1.46M
}
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.83M
{
207
1.83M
    uint num_levels = porder->params.W * porder->params.D;
208
1.83M
    int code;
209
210
1.83M
    if (!FORCE_STRIP_HALFTONES &&
211
1.83M
        ((ulong)porder->params.W1 * bitmap_raster(porder->params.W) +
212
1.83M
           (ulong)num_levels * sizeof(*porder->levels) +
213
1.83M
           (ulong)porder->params.W * porder->params.W1 * sizeof(gx_ht_bit)) <=
214
1.83M
        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.83M
        code = gx_ht_alloc_order(porder, porder->params.W,
222
1.83M
                                 porder->params.W1, 0,
223
1.83M
                                 num_levels, mem);
224
1.83M
        porder->height = porder->orig_height = porder->params.D;
225
1.83M
        porder->shift = porder->orig_shift = porder->params.S;
226
1.83M
    } else {
227
        /* Just allocate the order for a single strip. */
228
17
        code = gx_ht_alloc_order(porder, porder->params.W,
229
17
                                 porder->params.D, porder->params.S,
230
17
                                 num_levels, mem);
231
17
    }
232
1.83M
    return code;
233
1.83M
}
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.82M
{
239
1.82M
    gs_matrix imat;
240
1.82M
    ulong max_size = gx_ht_cache_default_bits_size();
241
1.82M
    int code;
242
1.82M
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
243
244
1.82M
    if (phsp->frequency < 0.1)
245
1
        return_error(gs_error_rangecheck);
246
1.82M
    gs_deviceinitialmatrix(gs_currentdevice(pgs), &imat);
247
1.82M
    code = pick_cell_size(phsp, &imat, max_size,
248
1.82M
                          ctx->screen_min_screen_levels, accurate,
249
1.82M
                          &porder->params);
250
1.82M
    if (code < 0)
251
4
        return code;
252
1.82M
    gx_compute_cell_values(&porder->params);
253
1.82M
    porder->screen_params.matrix = imat;
254
1.82M
    porder->screen_params.max_size = max_size;
255
1.82M
    return gs_screen_order_alloc(porder, mem);
256
1.82M
}
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.82M
{
300
1.82M
    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.82M
    const bool reflected = pmat->xy * pmat->yx > pmat->xx * pmat->yy;
305
1.82M
    const int reflection = (reflected ? -1 : 1);
306
1.82M
    const int rotation =
307
1.82M
    (landscape ? (pmat->yx < 0 ? 90 : -90) : pmat->xx < 0 ? 180 : 0);
308
1.82M
    const double f0 = ph->frequency, a0 = ph->angle;
309
1.82M
    double T = 1.0;
310
1.82M
    gs_point uv0;
311
312
10.9M
#define u0 uv0.x
313
9.48M
#define v0 uv0.y
314
1.82M
    int rt = 1;
315
1.82M
    double f = 0, a = 0;
316
1.82M
    double e_best = 1000;
317
1.82M
    bool better;
318
319
1.82M
    if (landscape) {
320
4
        if (pmat->xy)
321
4
            T = fabs(pmat->yx / pmat->xy);
322
1.82M
    } else {
323
1.82M
        if (pmat->yy)
324
1.82M
            T = fabs(pmat->xx / pmat->yy);
325
1.82M
    }
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.82M
    if (f0 == 0)
339
0
        return_error(gs_error_rangecheck);
340
341
1.82M
    {
342
1.82M
        gs_matrix rmat;
343
344
1.82M
        gs_make_rotation(a0 * reflection + rotation, &rmat);
345
1.82M
        gs_distance_transform(72.0 / f0, 0.0, &rmat, &uv0);
346
1.82M
        gs_distance_transform(u0, v0, pmat, &uv0);
347
1.82M
        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.82M
                   ph->frequency, ph->angle,
349
1.82M
                   pmat->xx, pmat->xy, pmat->yx, pmat->yy,
350
1.82M
                   max_size, min_levels, u0, v0);
351
1.82M
    }
352
353
    /* Adjust u and v to reasonable values. */
354
355
1.82M
    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.82M
    if ((fabs(u0) + fabs(v0)) < ((double)5.0 / max_int))
364
4
        return_error(gs_error_rangecheck);
365
366
1.82M
    while ((fabs(u0) + fabs(v0)) * rt < 4)
367
1.31k
        ++rt;
368
1.82M
    phcp->C = 0;
369
370
1.82M
  try_size:
371
1.82M
    better = false;
372
1.82M
    {
373
1.82M
        double fm0 = u0 * rt;
374
1.82M
        double fn0 = v0 * rt;
375
1.82M
        int m0 = (int)floor(u0 * rt + 0.0001);
376
1.82M
        int n0 = (int)floor(v0 * rt + 0.0001);
377
1.82M
        gx_ht_cell_params_t p;
378
379
1.82M
        p.R = p.R1 = rt;
380
5.46M
        for (p.M = m0 + 1; p.M >= m0; p.M--)
381
10.9M
            for (p.N = n0 + 1; p.N >= n0; p.N--) {
382
7.29M
                long raster, wt;
383
#ifdef DEBUG
384
                long wt_size;
385
#endif
386
7.29M
                double fr, ar, ft, at, f_diff, a_diff, f_err, a_err;
387
388
7.29M
                p.M1 = (int)floor(p.M / T + 0.5);
389
7.29M
                p.N1 = (int)floor(p.N * T + 0.5);
390
391
7.29M
                if (p.M1 == 0 && p.N1 == 0)
392
0
                    return_error(gs_error_rangecheck);
393
394
7.29M
                gx_compute_cell_values(&p);
395
7.29M
                if_debug3('h', "[h]trying m=%d, n=%d, r=%d\n", p.M, p.N, rt);
396
7.29M
                wt = p.W;
397
7.29M
                if (wt >= max_short)
398
20
                    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
7.29M
                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
7.29M
                raster = bitmap_raster(wt);
407
7.29M
                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
7.29M
                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
7.29M
                else
421
7.29M
                    ar = atan2(p.N * (double)pmat->xx,
422
7.29M
                               p.M * (double)pmat->yy),
423
7.29M
                        fr = 72.0 * (p.M == 0 ? pmat->yy / p.N * sin(ar) :
424
7.29M
                                     pmat->xx / p.M * cos(ar));
425
7.29M
                ft = fabs(fr) * rt;
426
                /* Normalize the angle to the requested quadrant. */
427
7.29M
                at = (ar * radians_to_degrees - rotation) * reflection;
428
7.29M
                at -= floor(at / 180.0) * 180.0;
429
7.29M
                at += floor(a0 / 180.0) * 180.0;
430
7.29M
                f_diff = fabs(ft - f0);
431
7.29M
                a_diff = fabs(at - a0);
432
7.29M
                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
7.29M
                a_err = a_diff;
439
440
7.29M
                if_debug5('h', " ==> d=%d, wt=%ld, wt_size=%ld, f=%g, a=%g\n",
441
7.29M
                          p.D, wt, bitmap_raster(wt) * wt, ft, at);
442
443
7.29M
                {
444
                    /*
445
                     * Compute the error in position between ideal location.
446
                     * and the current integer location.
447
                     */
448
449
7.29M
                    double error =
450
7.29M
                        (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
7.29M
                    error /= p.N * p.N + p.M * p.M;
456
7.29M
                    error = sqrt(error); /* The previous calcs. gave value squared */
457
7.29M
                    if (error > e_best)
458
733k
                        continue;
459
6.55M
                    e_best = error;
460
6.55M
                }
461
0
                *phcp = p;
462
6.55M
                f = ft, a = at;
463
6.55M
                better = true;
464
6.55M
                if_debug3('h', "*** best wt_size=%ld, f_diff=%g, a_diff=%g\n",
465
6.55M
                          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.55M
                if (f_err <= 0.01 && a_err <= 0.9 /*degrees*/)
471
1.45k
                    goto done;
472
6.55M
            }
473
1.82M
    }
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.82M
    if (rt == 1 && !better)
481
0
        return_error(gs_error_rangecheck);
482
483
1.82M
    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.82M
    if (better) {               /* If we want accurate screens, continue till we fail. */
488
1.82M
        if (accurate) {
489
0
            ++rt;
490
0
            goto try_size;
491
0
        }
492
1.82M
    } 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.82M
  done:
502
1.82M
    if_debug5('h', "[h]Chosen: f=%g a=%g M=%d N=%d R=%d\n",
503
1.82M
              f, a, phcp->M, phcp->N, phcp->R);
504
1.82M
    ph->actual_frequency = f;
505
1.82M
    ph->actual_angle = a;
506
1.82M
    return 0;
507
1.82M
#undef u0
508
1.82M
#undef v0
509
1.82M
}
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.31M
{
518
3.31M
    int code;
519
520
3.31M
    penum->pgs = pgs;           /* ensure clean for GC */
521
3.31M
    if (&penum->order != porder) /* Pacify Valgrind */
522
1.85M
        penum->order = *porder;
523
3.31M
    penum->halftone.rc.memory = mem;
524
3.31M
    penum->halftone.type = ht_type_screen;
525
3.31M
    penum->halftone.params.screen = *phsp;
526
3.31M
    penum->x = penum->y = 0;
527
528
3.31M
    penum->strip = porder->num_levels / porder->width;
529
3.31M
    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.31M
    {
550
3.31M
        const int M = porder->params.M, N = porder->params.N, R = porder->params.R;
551
3.31M
        const int M1 = porder->params.M1, N1 = porder->params.N1, R1 = porder->params.R1;
552
3.31M
        double Q = 2.0 / ((long)M * M1 + (long)N * N1);
553
554
3.31M
        penum->mat.xx = Q * (R * M1);
555
3.31M
        penum->mat.xy = Q * (-R1 * N);
556
3.31M
        penum->mat.yx = Q * (R * N1);
557
3.31M
        penum->mat.yy = Q * (R1 * M);
558
3.31M
        penum->mat.tx = -1.0;
559
3.31M
        penum->mat.ty = -1.0;
560
3.31M
        code = gs_matrix_invert(&penum->mat, &penum->mat_inv);
561
3.31M
    }
562
3.31M
    if_debug7('h', "[h]Screen: (%dx%d)/%d [%f %f %f %f]\n",
563
3.31M
              porder->width, porder->height, porder->params.R,
564
3.31M
              penum->mat.xx, penum->mat.xy,
565
3.31M
              penum->mat.yx, penum->mat.yy);
566
3.31M
    return code;
567
3.31M
}
568
569
/* Report current point for sampling */
570
int
571
gs_screen_currentpoint(gs_screen_enum * penum, gs_point * ppt)
572
61.3M
{
573
61.3M
    gs_point pt;
574
61.3M
    int code;
575
61.3M
    double sx, sy; /* spot center in spot coords (integers) */
576
61.3M
    gs_point spot_center; /* device coords */
577
578
61.3M
    if (penum->y >= penum->strip) {     /* all done */
579
3.31M
        gx_ht_construct_spot_order(&penum->order);
580
3.31M
        return 1;
581
3.31M
    }
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
58.0M
    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
58.0M
    sx = ceil( pt.x / 2 ) * 2;
590
58.0M
    sy = ceil( pt.y / 2 ) * 2;
591
58.0M
    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
58.0M
    spot_center.x = floor(spot_center.x) + 0.5;
596
58.0M
    spot_center.y = floor(spot_center.y) + 0.5;
597
598
    /* compute the spot function arguments for the shifted spot : */
599
58.0M
    if ((code = gs_distance_transform(penum->x - spot_center.x + 0.501,
600
58.0M
                                      penum->y - spot_center.y + 0.498,
601
58.0M
                                      &penum->mat, &pt)) < 0)
602
0
        return code;
603
58.0M
    pt.x += 1;
604
58.0M
    pt.y += 1;
605
606
58.0M
    if (pt.x < -1.0)
607
3.02M
        pt.x += ((int)(-ceil(pt.x)) + 1) & ~1;
608
55.0M
    else if (pt.x >= 1.0)
609
10.7k
        pt.x -= ((int)pt.x + 1) & ~1;
610
58.0M
    if (pt.y < -1.0)
611
30.1k
        pt.y += ((int)(-ceil(pt.y)) + 1) & ~1;
612
58.0M
    else if (pt.y >= 1.0)
613
55
        pt.y -= ((int)pt.y + 1) & ~1;
614
58.0M
    *ppt = pt;
615
58.0M
    return 0;
616
58.0M
}
617
618
/* Record next halftone sample */
619
int
620
gs_screen_next(gs_screen_enum * penum, double value)
621
58.0M
{
622
58.0M
    ht_sample_t sample;
623
58.0M
    int width = penum->order.width;
624
58.0M
    gx_ht_bit *bits = (gx_ht_bit *)penum->order.bit_data;
625
626
58.0M
    if (value < -1.0 || value > 1.0)
627
2
        return_error(gs_error_rangecheck);
628
58.0M
    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
58.0M
    bits[penum->y * width + penum->x].mask = sample;
639
58.0M
    if (++(penum->x) >= width)
640
7.74M
        penum->x = 0, ++(penum->y);
641
58.0M
    return 0;
642
58.0M
}
643
644
/* Install a fully constructed screen in the gstate. */
645
int
646
gs_screen_install(gs_screen_enum * penum)
647
316k
{
648
316k
    gx_device_halftone dev_ht;
649
316k
    int code;
650
651
316k
    dev_ht.rc.memory = penum->halftone.rc.memory;
652
316k
    dev_ht.order = penum->order;
653
316k
    dev_ht.components = 0;
654
316k
    penum->halftone.objtype = HT_OBJTYPE_DEFAULT;
655
316k
    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
316k
    return code;
658
316k
}