Coverage Report

Created: 2025-06-24 07:01

/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
705k
{
64
705k
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
65
66
705k
    ctx->screen_accurate_screens = accurate;
67
705k
}
68
bool
69
gs_currentaccuratescreens(gs_memory_t *mem)
70
2.28M
{
71
2.28M
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
72
73
2.28M
    return ctx->screen_accurate_screens;
74
2.28M
}
75
76
void
77
gs_setminscreenlevels(gs_memory_t *mem, uint levels)
78
705k
{
79
705k
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
80
81
705k
    ctx->screen_min_screen_levels = levels;
82
705k
}
83
uint
84
gs_currentminscreenlevels(gs_memory_t *mem)
85
649k
{
86
649k
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
87
88
649k
    return ctx->screen_min_screen_levels;
89
649k
}
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
162k
{
96
162k
    gs_setaccuratescreens(mem, false);
97
162k
    gs_setminscreenlevels(mem, 1);
98
162k
    return 0;
99
162k
}
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
9.82M
{
123
9.82M
    const int M = phcp->M, N = phcp->N, M1 = phcp->M1, N1 = phcp->N1;
124
9.82M
    const uint m = any_abs(M), n = any_abs(N);
125
9.82M
    const uint m1 = any_abs(M1), n1 = any_abs(N1);
126
9.82M
    const ulong C = phcp->C = (ulong)m * m1 + (ulong)n * n1;
127
9.82M
    const int D = phcp->D = igcd(m1, n);
128
9.82M
    const int D1 = phcp->D1 = igcd(m, n1);
129
130
9.82M
    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
9.82M
    if (M1 && N) {
134
8.51M
        int h = 0, k = 0, dy = 0;
135
8.51M
        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
28.7M
        while (dy != D)
143
20.2M
            if (dy > D) {
144
9.78M
                if (M1 > 0)
145
9.78M
                    ++k;
146
1.79k
                else
147
1.79k
                    --k;
148
9.78M
                dy -= m1;
149
10.4M
            } else {
150
10.4M
                if (N > 0)
151
10.4M
                    ++h;
152
480
                else
153
480
                    --h;
154
10.4M
                dy += n;
155
10.4M
            }
156
8.51M
        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.51M
        phcp->S = imod(-shift, phcp->W);
160
8.51M
    } else
161
1.30M
        phcp->S = 0;
162
9.82M
    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
9.82M
               M, N, phcp->R, M1, N1, phcp->R1,
164
9.82M
               C, D, D1, phcp->W, phcp->W1, phcp->S);
165
9.82M
}
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.63M
{
176
1.63M
    return gs_alloc_struct(mem, gs_screen_enum, &st_gs_screen_enum, cname);
177
1.63M
}
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.30M
{
193
1.30M
    int code =
194
1.30M
    gs_screen_order_init_memory(&penum->order, pgs, phsp, accurate, mem);
195
196
1.30M
    if (code < 0)
197
0
        return code;
198
1.30M
    return
199
1.30M
        gs_screen_enum_init_memory(penum, &penum->order, pgs, phsp, mem);
200
1.30M
}
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.64M
{
207
1.64M
    uint num_levels = porder->params.W * porder->params.D;
208
1.64M
    int code;
209
210
1.64M
    if (!FORCE_STRIP_HALFTONES &&
211
1.64M
        ((ulong)porder->params.W1 * bitmap_raster(porder->params.W) +
212
1.64M
           (ulong)num_levels * sizeof(*porder->levels) +
213
1.64M
           (ulong)porder->params.W * porder->params.W1 * sizeof(gx_ht_bit)) <=
214
1.64M
        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.64M
        code = gx_ht_alloc_order(porder, porder->params.W,
222
1.64M
                                 porder->params.W1, 0,
223
1.64M
                                 num_levels, mem);
224
1.64M
        porder->height = porder->orig_height = porder->params.D;
225
1.64M
        porder->shift = porder->orig_shift = porder->params.S;
226
1.64M
    } 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.64M
    return code;
233
1.64M
}
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.63M
{
239
1.63M
    gs_matrix imat;
240
1.63M
    ulong max_size = gx_ht_cache_default_bits_size();
241
1.63M
    int code;
242
1.63M
    gs_lib_ctx_t *ctx = gs_lib_ctx_get_interp_instance(mem);
243
244
1.63M
    if (phsp->frequency < 0.1)
245
1
        return_error(gs_error_rangecheck);
246
1.63M
    gs_deviceinitialmatrix(gs_currentdevice(pgs), &imat);
247
1.63M
    code = pick_cell_size(phsp, &imat, max_size,
248
1.63M
                          ctx->screen_min_screen_levels, accurate,
249
1.63M
                          &porder->params);
250
1.63M
    if (code < 0)
251
2
        return code;
252
1.63M
    gx_compute_cell_values(&porder->params);
253
1.63M
    porder->screen_params.matrix = imat;
254
1.63M
    porder->screen_params.max_size = max_size;
255
1.63M
    return gs_screen_order_alloc(porder, mem);
256
1.63M
}
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.63M
{
300
1.63M
    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.63M
    const bool reflected = pmat->xy * pmat->yx > pmat->xx * pmat->yy;
305
1.63M
    const int reflection = (reflected ? -1 : 1);
306
1.63M
    const int rotation =
307
1.63M
    (landscape ? (pmat->yx < 0 ? 90 : -90) : pmat->xx < 0 ? 180 : 0);
308
1.63M
    const double f0 = ph->frequency, a0 = ph->angle;
309
1.63M
    const double T =
310
1.63M
    fabs((landscape ? pmat->yx / pmat->xy : pmat->xx / pmat->yy));
311
1.63M
    gs_point uv0;
312
313
9.81M
#define u0 uv0.x
314
8.50M
#define v0 uv0.y
315
1.63M
    int rt = 1;
316
1.63M
    double f = 0, a = 0;
317
1.63M
    double e_best = 1000;
318
1.63M
    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
1.63M
    if (f0 == 0)
332
0
        return_error(gs_error_rangecheck);
333
334
1.63M
    {
335
1.63M
        gs_matrix rmat;
336
337
1.63M
        gs_make_rotation(a0 * reflection + rotation, &rmat);
338
1.63M
        gs_distance_transform(72.0 / f0, 0.0, &rmat, &uv0);
339
1.63M
        gs_distance_transform(u0, v0, pmat, &uv0);
340
1.63M
        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
1.63M
                   ph->frequency, ph->angle,
342
1.63M
                   pmat->xx, pmat->xy, pmat->yx, pmat->yy,
343
1.63M
                   max_size, min_levels, u0, v0);
344
1.63M
    }
345
346
    /* Adjust u and v to reasonable values. */
347
348
1.63M
    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
1.63M
    if ((fabs(u0) + fabs(v0)) < ((double)5.0 / max_int))
357
2
        return_error(gs_error_rangecheck);
358
359
1.63M
    while ((fabs(u0) + fabs(v0)) * rt < 4)
360
2
        ++rt;
361
1.63M
    phcp->C = 0;
362
363
1.63M
  try_size:
364
1.63M
    better = false;
365
1.63M
    {
366
1.63M
        double fm0 = u0 * rt;
367
1.63M
        double fn0 = v0 * rt;
368
1.63M
        int m0 = (int)floor(u0 * rt + 0.0001);
369
1.63M
        int n0 = (int)floor(v0 * rt + 0.0001);
370
1.63M
        gx_ht_cell_params_t p;
371
372
1.63M
        p.R = p.R1 = rt;
373
4.90M
        for (p.M = m0 + 1; p.M >= m0; p.M--)
374
9.81M
            for (p.N = n0 + 1; p.N >= n0; p.N--) {
375
6.54M
                long raster, wt;
376
#ifdef DEBUG
377
                long wt_size;
378
#endif
379
6.54M
                double fr, ar, ft, at, f_diff, a_diff, f_err, a_err;
380
381
6.54M
                p.M1 = (int)floor(p.M / T + 0.5);
382
6.54M
                p.N1 = (int)floor(p.N * T + 0.5);
383
384
6.54M
                if (p.M1 == 0 && p.N1 == 0)
385
0
                    return_error(gs_error_rangecheck);
386
387
6.54M
                gx_compute_cell_values(&p);
388
6.54M
                if_debug3('h', "[h]trying m=%d, n=%d, r=%d\n", p.M, p.N, rt);
389
6.54M
                wt = p.W;
390
6.54M
                if (wt >= max_short)
391
13
                    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
6.54M
                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
6.54M
                raster = bitmap_raster(wt);
400
6.54M
                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
6.54M
                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
6.54M
                else
414
6.54M
                    ar = atan2(p.N * (double)pmat->xx,
415
6.54M
                               p.M * (double)pmat->yy),
416
6.54M
                        fr = 72.0 * (p.M == 0 ? pmat->yy / p.N * sin(ar) :
417
6.54M
                                     pmat->xx / p.M * cos(ar));
418
6.54M
                ft = fabs(fr) * rt;
419
                /* Normalize the angle to the requested quadrant. */
420
6.54M
                at = (ar * radians_to_degrees - rotation) * reflection;
421
6.54M
                at -= floor(at / 180.0) * 180.0;
422
6.54M
                at += floor(a0 / 180.0) * 180.0;
423
6.54M
                f_diff = fabs(ft - f0);
424
6.54M
                a_diff = fabs(at - a0);
425
6.54M
                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
6.54M
                a_err = a_diff;
432
433
6.54M
                if_debug5('h', " ==> d=%d, wt=%ld, wt_size=%ld, f=%g, a=%g\n",
434
6.54M
                          p.D, wt, bitmap_raster(wt) * wt, ft, at);
435
436
6.54M
                {
437
                    /*
438
                     * Compute the error in position between ideal location.
439
                     * and the current integer location.
440
                     */
441
442
6.54M
                    double error =
443
6.54M
                        (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
6.54M
                    error /= p.N * p.N + p.M * p.M;
449
6.54M
                    error = sqrt(error); /* The previous calcs. gave value squared */
450
6.54M
                    if (error > e_best)
451
654k
                        continue;
452
5.89M
                    e_best = error;
453
5.89M
                }
454
0
                *phcp = p;
455
5.89M
                f = ft, a = at;
456
5.89M
                better = true;
457
5.89M
                if_debug3('h', "*** best wt_size=%ld, f_diff=%g, a_diff=%g\n",
458
5.89M
                          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
5.89M
                if (f_err <= 0.01 && a_err <= 0.9 /*degrees*/)
464
771
                    goto done;
465
5.89M
            }
466
1.63M
    }
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
1.63M
    if (rt == 1 && !better)
474
0
        return_error(gs_error_rangecheck);
475
476
1.63M
    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
1.63M
    if (better) {               /* If we want accurate screens, continue till we fail. */
481
1.63M
        if (accurate) {
482
0
            ++rt;
483
0
            goto try_size;
484
0
        }
485
1.63M
    } 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
1.63M
  done:
495
1.63M
    if_debug5('h', "[h]Chosen: f=%g a=%g M=%d N=%d R=%d\n",
496
1.63M
              f, a, phcp->M, phcp->N, phcp->R);
497
1.63M
    ph->actual_frequency = f;
498
1.63M
    ph->actual_angle = a;
499
1.63M
    return 0;
500
1.63M
#undef u0
501
1.63M
#undef v0
502
1.63M
}
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
2.96M
{
511
2.96M
    int code;
512
513
2.96M
    penum->pgs = pgs;           /* ensure clean for GC */
514
2.96M
    if (&penum->order != porder) /* Pacify Valgrind */
515
1.66M
        penum->order = *porder;
516
2.96M
    penum->halftone.rc.memory = mem;
517
2.96M
    penum->halftone.type = ht_type_screen;
518
2.96M
    penum->halftone.params.screen = *phsp;
519
2.96M
    penum->x = penum->y = 0;
520
521
2.96M
    penum->strip = porder->num_levels / porder->width;
522
2.96M
    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
2.96M
    {
543
2.96M
        const int M = porder->params.M, N = porder->params.N, R = porder->params.R;
544
2.96M
        const int M1 = porder->params.M1, N1 = porder->params.N1, R1 = porder->params.R1;
545
2.96M
        double Q = 2.0 / ((long)M * M1 + (long)N * N1);
546
547
2.96M
        penum->mat.xx = Q * (R * M1);
548
2.96M
        penum->mat.xy = Q * (-R1 * N);
549
2.96M
        penum->mat.yx = Q * (R * N1);
550
2.96M
        penum->mat.yy = Q * (R1 * M);
551
2.96M
        penum->mat.tx = -1.0;
552
2.96M
        penum->mat.ty = -1.0;
553
2.96M
        code = gs_matrix_invert(&penum->mat, &penum->mat_inv);
554
2.96M
    }
555
2.96M
    if_debug7('h', "[h]Screen: (%dx%d)/%d [%f %f %f %f]\n",
556
2.96M
              porder->width, porder->height, porder->params.R,
557
2.96M
              penum->mat.xx, penum->mat.xy,
558
2.96M
              penum->mat.yx, penum->mat.yy);
559
2.96M
    return code;
560
2.96M
}
561
562
/* Report current point for sampling */
563
int
564
gs_screen_currentpoint(gs_screen_enum * penum, gs_point * ppt)
565
54.5M
{
566
54.5M
    gs_point pt;
567
54.5M
    int code;
568
54.5M
    double sx, sy; /* spot center in spot coords (integers) */
569
54.5M
    gs_point spot_center; /* device coords */
570
571
54.5M
    if (penum->y >= penum->strip) {     /* all done */
572
2.96M
        gx_ht_construct_spot_order(&penum->order);
573
2.96M
        return 1;
574
2.96M
    }
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
51.6M
    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
51.6M
    sx = ceil( pt.x / 2 ) * 2;
583
51.6M
    sy = ceil( pt.y / 2 ) * 2;
584
51.6M
    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
51.6M
    spot_center.x = floor(spot_center.x) + 0.5;
589
51.6M
    spot_center.y = floor(spot_center.y) + 0.5;
590
591
    /* compute the spot function arguments for the shifted spot : */
592
51.6M
    if ((code = gs_distance_transform(penum->x - spot_center.x + 0.501,
593
51.6M
                                      penum->y - spot_center.y + 0.498,
594
51.6M
                                      &penum->mat, &pt)) < 0)
595
0
        return code;
596
51.6M
    pt.x += 1;
597
51.6M
    pt.y += 1;
598
599
51.6M
    if (pt.x < -1.0)
600
2.66M
        pt.x += ((int)(-ceil(pt.x)) + 1) & ~1;
601
48.9M
    else if (pt.x >= 1.0)
602
5.41k
        pt.x -= ((int)pt.x + 1) & ~1;
603
51.6M
    if (pt.y < -1.0)
604
21.8k
        pt.y += ((int)(-ceil(pt.y)) + 1) & ~1;
605
51.5M
    else if (pt.y >= 1.0)
606
51
        pt.y -= ((int)pt.y + 1) & ~1;
607
51.6M
    *ppt = pt;
608
51.6M
    return 0;
609
51.6M
}
610
611
/* Record next halftone sample */
612
int
613
gs_screen_next(gs_screen_enum * penum, double value)
614
51.6M
{
615
51.6M
    ht_sample_t sample;
616
51.6M
    int width = penum->order.width;
617
51.6M
    gx_ht_bit *bits = (gx_ht_bit *)penum->order.bit_data;
618
619
51.6M
    if (value < -1.0 || value > 1.0)
620
2
        return_error(gs_error_rangecheck);
621
51.6M
    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
51.6M
    bits[penum->y * width + penum->x].mask = sample;
632
51.6M
    if (++(penum->x) >= width)
633
6.94M
        penum->x = 0, ++(penum->y);
634
51.6M
    return 0;
635
51.6M
}
636
637
/* Install a fully constructed screen in the gstate. */
638
int
639
gs_screen_install(gs_screen_enum * penum)
640
310k
{
641
310k
    gx_device_halftone dev_ht;
642
310k
    int code;
643
644
310k
    dev_ht.rc.memory = penum->halftone.rc.memory;
645
310k
    dev_ht.order = penum->order;
646
310k
    dev_ht.components = 0;
647
310k
    penum->halftone.objtype = HT_OBJTYPE_DEFAULT;
648
310k
    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
310k
    return code;
651
310k
}