Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/base/gscoord.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
/* Coordinate system operators for Ghostscript library */
18
#include "math_.h"
19
#include "gx.h"
20
#include "gserrors.h"
21
#include "gsccode.h"    /* for gxfont.h */
22
#include "gxfarith.h"
23
#include "gxfixed.h"
24
#include "gxmatrix.h"
25
#include "gxfont.h"   /* for char_tm */
26
#include "gxpath.h"   /* for gx_path_translate */
27
#include "gzstate.h"
28
#include "gxcoord.h"    /* requires gsmatrix, gsstate */
29
#include "gxdevice.h"
30
31
/* Choose whether to enable the rounding code in update_ctm. */
32
#define ROUND_CTM_FIXED 0
33
34
/* Forward declarations */
35
#ifdef DEBUG
36
#define trace_ctm(pgs) trace_matrix_fixed((pgs)->memory, &(pgs)->ctm)
37
static void trace_matrix_fixed(const gs_memory_t *mem, const gs_matrix_fixed *);
38
static void trace_matrix(const gs_memory_t *mem, const gs_matrix *);
39
40
#endif
41
42
/* Macro for ensuring ctm_inverse is valid */
43
#ifdef DEBUG
44
#  define print_inverse(pgs)\
45
     if ( gs_debug_c('x') )\
46
       dmlprintf(pgs->memory, "[x]Inverting:\n"), trace_ctm(pgs), trace_matrix(pgs->memory, &pgs->ctm_inverse)
47
#else
48
586k
#  define print_inverse(pgs) DO_NOTHING
49
#endif
50
#define ensure_inverse_valid(pgs)\
51
594k
        if ( !pgs->ctm_inverse_valid )\
52
594k
           { int code = ctm_set_inverse(pgs);\
53
586k
                if ( code < 0 ) return code;\
54
586k
           }
55
56
static int
57
ctm_set_inverse(gs_gstate * pgs)
58
586k
{
59
586k
    int code = gs_matrix_invert(&ctm_only(pgs), &pgs->ctm_inverse);
60
61
586k
    print_inverse(pgs);
62
586k
    if (code < 0)
63
7.23k
        return code;
64
579k
    pgs->ctm_inverse_valid = true;
65
579k
    return 0;
66
586k
}
67
68
/* Machinery for updating fixed version of ctm. */
69
/*
70
 * We (conditionally) adjust the floating point translation
71
 * so that it exactly matches the (rounded) fixed translation.
72
 * This avoids certain unpleasant rounding anomalies, such as
73
 * 0 0 moveto currentpoint not returning 0 0, and () stringwidth
74
 * not returning 0 0.
75
 */
76
#if ROUND_CTM_FIXED
77
#  define update_t_fixed(mat, t, t_fixed, v)\
78
    (set_float2fixed_vars((mat).t_fixed, v),\
79
     set_fixed2float_var((mat).t, (mat).t_fixed))
80
#else /* !ROUND_CTM_FIXED */
81
#  define update_t_fixed(mat, t, t_fixed, v)\
82
135M
    ((mat).t = (v),\
83
135M
     set_float2fixed_vars((mat).t_fixed, (mat).t))
84
#endif /* (!)ROUND_CTM_FIXED */
85
137M
#define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
86
#define update_matrix_fixed(mat, xt, yt)\
87
69.1M
  ((mat).txy_fixed_valid = (f_fits_in_fixed(xt) && f_fits_in_fixed(yt) ?\
88
69.1M
                            (update_t_fixed(mat, tx, tx_fixed, xt),\
89
67.7M
                             update_t_fixed(mat, ty, ty_fixed, yt), true) :\
90
69.1M
                            ((mat).tx = (xt), (mat).ty = (yt), false)))
91
#define update_ctm(pgs, xt, yt)\
92
22.6M
  (pgs->ctm_inverse_valid = false,\
93
22.6M
   pgs->char_tm_valid = false,\
94
22.6M
   update_matrix_fixed(pgs->ctm, xt, yt))
95
96
/* ------ Coordinate system definition ------ */
97
98
int
99
gs_initmatrix(gs_gstate * pgs)
100
3.71M
{
101
3.71M
    gs_matrix imat;
102
103
3.71M
    gs_defaultmatrix(pgs, &imat);
104
3.71M
    update_ctm(pgs, imat.tx, imat.ty);
105
3.71M
    set_ctm_only(pgs, imat);
106
#ifdef DEBUG
107
    if (gs_debug_c('x'))
108
        dmlprintf(pgs->memory, "[x]initmatrix:\n"), trace_ctm(pgs);
109
#endif
110
3.71M
    return 0;
111
3.71M
}
112
113
int
114
gs_defaultmatrix(const gs_gstate * pgs, gs_matrix * pmat)
115
4.28M
{
116
4.28M
    gx_device *dev;
117
118
4.28M
    if (pgs->ctm_default_set) { /* set after Install */
119
884k
        *pmat = pgs->ctm_default;
120
884k
        return 1;
121
884k
    }
122
3.39M
    dev = gs_currentdevice_inline(pgs);
123
3.39M
    gs_deviceinitialmatrix(dev, pmat);
124
    /* Add in the translation for the Margins. */
125
3.39M
    pmat->tx += dev->Margins[0];
126
3.39M
    pmat->ty += dev->Margins[1];
127
3.39M
    return 0;
128
4.28M
}
129
130
int
131
gs_setdefaultmatrix(gs_gstate * pgs, const gs_matrix * pmat)
132
766k
{
133
766k
    if (pmat == NULL) {
134
0
        pgs->ctm_default_set = false;
135
0
        pgs->ctm_initial_set = false;
136
766k
    } else {
137
766k
        gx_device *dev;
138
139
766k
        pgs->ctm_default = *pmat;
140
766k
        pgs->ctm_default_set = true;
141
142
        /* We also store the current 'initial' matrix, so we can spot
143
         * changes in this in future. */
144
766k
        dev = gs_currentdevice_inline(pgs);
145
766k
        gs_deviceinitialmatrix(dev, &pgs->ctm_initial);
146
766k
        pgs->ctm_initial_set = 1;
147
766k
    }
148
766k
    return 0;
149
766k
}
150
151
int
152
gs_updatematrices(gs_gstate *pgs)
153
2.14M
{
154
2.14M
    gx_device *dev;
155
2.14M
    gs_matrix newdefault, init, t, inv, newctm;
156
2.14M
    int code;
157
#ifdef DEBUG
158
    gs_matrix *mat;
159
#endif
160
161
    /* Read the current device initial matrix. */
162
2.14M
    dev = gs_currentdevice_inline(pgs);
163
2.14M
    gs_deviceinitialmatrix(dev, &init);
164
165
#ifdef DEBUG
166
    if (gs_debug_c('x'))
167
        dlprintf("[x]updatematrices\n");
168
#endif
169
170
2.14M
    if (pgs->ctm_default_set == 0 ||
171
2.14M
        pgs->ctm_initial_set == 0) {
172
        /* If neither default or initial are set, then store them for the
173
         * first time. */
174
241k
        pgs->ctm_initial = init;
175
241k
        pgs->ctm_initial_set = 1;
176
241k
        pgs->ctm_default = init;
177
241k
        pgs->ctm_default_set = 1;
178
#ifdef DEBUG
179
        if (gs_debug_c('x')) {
180
            mat = &pgs->ctm_initial;
181
            dlprintf6("storing initial/default = %g %g %g %g %g %g\n", mat->xx, mat->xy, mat->yx, mat->yy, mat->tx, mat->ty);
182
        }
183
#endif
184
241k
        return 0;
185
241k
    }
186
187
#ifdef DEBUG
188
    if (gs_debug_c('x')) {
189
        mat = &init;
190
        dlprintf6("initial        = %g %g %g %g %g %g\n", mat->xx, mat->xy, mat->yx, mat->yy, mat->tx, mat->ty);
191
        mat = &pgs->ctm_default;
192
        dlprintf6("default        = %g %g %g %g %g %g\n", mat->xx, mat->xy, mat->yx, mat->yy, mat->tx, mat->ty);
193
        mat = (gs_matrix *)&pgs->ctm;
194
        dlprintf6("ctm            = %g %g %g %g %g %g\n", mat->xx, mat->xy, mat->yx, mat->yy, mat->tx, mat->ty);
195
        mat = &pgs->ctm_initial;
196
        dlprintf6("stored initial = %g %g %g %g %g %g\n", mat->xx, mat->xy, mat->yx, mat->yy, mat->tx, mat->ty);
197
    }
198
#endif
199
    /* If no change, then nothing else to do here. */
200
1.90M
    if (init.xx == pgs->ctm_initial.xx &&
201
1.90M
        init.xy == pgs->ctm_initial.xy &&
202
1.90M
        init.yx == pgs->ctm_initial.yx &&
203
1.90M
        init.yy == pgs->ctm_initial.yy &&
204
1.90M
        init.tx == pgs->ctm_initial.tx &&
205
1.90M
        init.ty == pgs->ctm_initial.ty)
206
1.90M
        return 0;
207
208
    /* So, the initial matrix has changed from what it was
209
     * the last time the default matrix was set. The default
210
     * matrix is some modification of the initial matrix
211
     * (typically a scale, or a translation, or a flip or
212
     * some combination thereof). Now the initial matrix
213
     * has changed (possibly because of Nup, or because of
214
     * a device doing Duplex etc), the default matrix is
215
     * almost certainly wrong. We therefore adjust it here.*/
216
217
    /* So originally: old_default = modification.old_init
218
     * and we want:   new_default = modification.new_init
219
     *
220
     * So: modification = old_default.INV(old_init)
221
     *     new_default  = old_default.INV(old_init).new_init
222
     */
223
0
    code = gs_matrix_invert(&pgs->ctm_initial, &inv);
224
0
    if (code < 0)
225
0
        return code;
226
0
    code = gs_matrix_multiply(&pgs->ctm_default, &inv, &t);
227
0
    if (code < 0)
228
0
        return code;
229
0
    code = gs_matrix_multiply(&t, &init, &newdefault);
230
0
    if (code < 0)
231
0
        return code;
232
233
    /* Now, the current ctm is similarly derived from the
234
     * old default. We want to update it to be derived (in the
235
     * same way) from the new default.
236
     *
237
     * So:  old_ctm = modification.old_default
238
     *      old_ctm.INV(old_default) = modification
239
     * And: new_ctm = modification.new_default
240
     *              = old_ctm.INV(old_default).new_default
241
     */
242
0
    code = gs_matrix_invert(&pgs->ctm_default, &inv);
243
0
    if (code < 0)
244
0
        return code;
245
0
    code = gs_matrix_multiply((gs_matrix *)&pgs->ctm, &inv, &t);
246
0
    if (code < 0)
247
0
        return code;
248
0
    code = gs_matrix_multiply(&t, &newdefault, &newctm);
249
0
    if (code < 0)
250
0
        return code;
251
252
0
    pgs->ctm_initial = init;
253
0
    pgs->ctm_default = newdefault;
254
0
    gs_setmatrix(pgs, &newctm);
255
256
#ifdef DEBUG
257
    if (gs_debug_c('x')) {
258
        mat = &pgs->ctm_default;
259
        dlprintf6("new default    = %g %g %g %g %g %g\n", mat->xx, mat->xy, mat->yx, mat->yy, mat->tx, mat->ty);
260
        mat = (gs_matrix *)&pgs->ctm;
261
        dlprintf6("new ctm        = %g %g %g %g %g %g\n", mat->xx, mat->xy, mat->yx, mat->yy, mat->tx, mat->ty);
262
    }
263
#endif
264
265
    /* This is a bit nasty. This resets the clipping box to the page.
266
     * We need to do this, because otherwise the clipping box is
267
     * not updated with the ctm, and (typically) the entire contents
268
     * of the page end up clipped away. This will break usages where
269
     * we run 1 file (or set of postscript commands) to set the clipping
270
     * box, and then another file to actually draw stuff to be clipped.
271
     * Given this will only go wrong in the case where the device is
272
     * Nupping or Duplexing, we'll live with this for now. */
273
0
    return gs_initclip(pgs);
274
0
}
275
276
int
277
gs_currentmatrix(const gs_gstate * pgs, gs_matrix * pmat)
278
2.53M
{
279
2.53M
    *pmat = ctm_only(pgs);
280
2.53M
    return 0;
281
2.53M
}
282
283
/* Set the current transformation matrix for rendering text. */
284
/* Note that this may be based on a font other than the current font. */
285
int
286
gs_setcharmatrix(gs_gstate * pgs, const gs_matrix * pmat)
287
6.63M
{
288
6.63M
    gs_matrix cmat;
289
6.63M
    int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &cmat);
290
291
6.63M
    if (code < 0)
292
0
        return code;
293
6.63M
    update_matrix_fixed(pgs->char_tm, cmat.tx, cmat.ty);
294
6.63M
    char_tm_only(pgs) = cmat;
295
#ifdef DEBUG
296
    if (gs_debug_c('x'))
297
        dmlprintf(pgs->memory, "[x]setting char_tm:"), trace_matrix_fixed(pgs->memory, &pgs->char_tm);
298
#endif
299
6.63M
    pgs->char_tm_valid = true;
300
6.63M
    return 0;
301
6.63M
}
302
303
/* Read (after possibly computing) the current transformation matrix */
304
/* for rendering text.  If force=true, update char_tm if it is invalid; */
305
/* if force=false, don't update char_tm, and return an error code. */
306
int
307
gs_currentcharmatrix(gs_gstate * pgs, gs_matrix * ptm, bool force)
308
42.7M
{
309
42.7M
    if (!pgs->char_tm_valid) {
310
5.06M
        int code;
311
312
5.06M
        if (!force)
313
0
            return_error(gs_error_undefinedresult);
314
5.06M
        code = gs_setcharmatrix(pgs, &pgs->font->FontMatrix);
315
5.06M
        if (code < 0)
316
0
            return code;
317
5.06M
    }
318
42.7M
    if (ptm != NULL)
319
0
        *ptm = char_tm_only(pgs);
320
42.7M
    return 0;
321
42.7M
}
322
323
int
324
gs_setmatrix(gs_gstate * pgs, const gs_matrix * pmat)
325
9.95M
{
326
9.95M
    update_ctm(pgs, pmat->tx, pmat->ty);
327
9.95M
    set_ctm_only(pgs, *pmat);
328
#ifdef DEBUG
329
    if (gs_debug_c('x'))
330
        dmlprintf(pgs->memory, "[x]setmatrix:\n"), trace_ctm(pgs);
331
#endif
332
9.95M
    return 0;
333
9.95M
}
334
335
int
336
gs_gstate_setmatrix(gs_gstate * pgs, const gs_matrix * pmat)
337
39.8M
{
338
39.8M
    update_matrix_fixed(pgs->ctm, pmat->tx, pmat->ty);
339
39.8M
    set_ctm_only(pgs, *pmat);
340
#ifdef DEBUG
341
    if (gs_debug_c('x'))
342
        dmlprintf(pgs->memory, "[x]imager_setmatrix:\n"), trace_ctm(pgs);
343
#endif
344
39.8M
    return 0;
345
39.8M
}
346
347
int
348
gs_settocharmatrix(gs_gstate * pgs)
349
15.6M
{
350
15.6M
    if (pgs->char_tm_valid) {
351
15.6M
        pgs->ctm = pgs->char_tm;
352
15.6M
        pgs->ctm_inverse_valid = false;
353
15.6M
        return 0;
354
15.6M
    } else
355
0
        return_error(gs_error_undefinedresult);
356
15.6M
}
357
358
int
359
gs_translate(gs_gstate * pgs, double dx, double dy)
360
4.60M
{
361
4.60M
    gs_point pt;
362
4.60M
    int code;
363
364
4.60M
    if ((code = gs_distance_transform(dx, dy, &ctm_only(pgs), &pt)) < 0)
365
0
        return code;
366
4.60M
    pt.x = (float)pt.x + pgs->ctm.tx;
367
4.60M
    pt.y = (float)pt.y + pgs->ctm.ty;
368
4.60M
    update_ctm(pgs, pt.x, pt.y);
369
#ifdef DEBUG
370
    if (gs_debug_c('x'))
371
        dmlprintf4(pgs->memory, "[x]translate: %f %f -> %f %f\n",
372
                  dx, dy, pt.x, pt.y),
373
            trace_ctm(pgs);
374
#endif
375
4.60M
    return 0;
376
4.60M
}
377
378
int
379
gs_translate_untransformed(gs_gstate * pgs, double dx, double dy)
380
0
{
381
0
    gs_point pt;
382
383
0
    pt.x = (float)dx + pgs->ctm.tx;
384
0
    pt.y = (float)dy + pgs->ctm.ty;
385
0
    update_ctm(pgs, pt.x, pt.y);
386
#ifdef DEBUG
387
    if (gs_debug_c('x'))
388
        dmlprintf4(pgs->memory, "[x]translate_untransformed: %f %f -> %f %f\n",
389
                  dx, dy, pt.x, pt.y),
390
            trace_ctm(pgs);
391
#endif
392
0
    return 0;
393
0
}
394
395
int
396
gs_scale(gs_gstate * pgs, double sx, double sy)
397
182k
{
398
182k
    pgs->ctm.xx *= sx;
399
182k
    pgs->ctm.xy *= sx;
400
182k
    pgs->ctm.yx *= sy;
401
182k
    pgs->ctm.yy *= sy;
402
182k
    pgs->ctm_inverse_valid = false, pgs->char_tm_valid = false;
403
#ifdef DEBUG
404
    if (gs_debug_c('x'))
405
        dmlprintf2(pgs->memory, "[x]scale: %f %f\n", sx, sy), trace_ctm(pgs);
406
#endif
407
182k
    return 0;
408
182k
}
409
410
int
411
gs_rotate(gs_gstate * pgs, double ang)
412
2.79M
{
413
2.79M
    int code = gs_matrix_rotate(&ctm_only(pgs), ang,
414
2.79M
                                &ctm_only_writable(pgs));
415
416
2.79M
    pgs->ctm_inverse_valid = false, pgs->char_tm_valid = false;
417
#ifdef DEBUG
418
    if (gs_debug_c('x'))
419
        dmlprintf1(pgs->memory, "[x]rotate: %f\n", ang), trace_ctm(pgs);
420
#endif
421
2.79M
    return code;
422
2.79M
}
423
424
int
425
gs_concat(gs_gstate * pgs, const gs_matrix * pmat)
426
4.34M
{
427
4.34M
    gs_matrix cmat;
428
4.34M
    int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &cmat);
429
430
4.34M
    if (code < 0)
431
0
        return code;
432
4.34M
    update_ctm(pgs, cmat.tx, cmat.ty);
433
4.34M
    set_ctm_only(pgs, cmat);
434
#ifdef DEBUG
435
    if (gs_debug_c('x'))
436
        dmlprintf(pgs->memory, "[x]concat:\n"), trace_matrix(pgs->memory, pmat), trace_ctm(pgs);
437
#endif
438
4.34M
    return code;
439
4.34M
}
440
441
/* ------ Coordinate transformation ------ */
442
443
27.8M
#define is_skewed(pmat) (!(is_xxyy(pmat) || is_xyyx(pmat)))
444
445
int
446
gs_transform(gs_gstate * pgs, double x, double y, gs_point * pt)
447
254
{
448
254
    return gs_point_transform(x, y, &ctm_only(pgs), pt);
449
254
}
450
451
int
452
gs_dtransform(gs_gstate * pgs, double dx, double dy, gs_point * pt)
453
160k
{
454
160k
    return gs_distance_transform(dx, dy, &ctm_only(pgs), pt);
455
160k
}
456
457
int
458
gs_itransform(gs_gstate * pgs, double x, double y, gs_point * pt)
459
27.0M
{       /* If the matrix isn't skewed, we get more accurate results */
460
    /* by using transform_inverse than by using the inverse matrix. */
461
27.0M
    if (!is_skewed(&pgs->ctm)) {
462
26.4M
        return gs_point_transform_inverse(x, y, &ctm_only(pgs), pt);
463
26.4M
    } else {
464
594k
        ensure_inverse_valid(pgs);
465
587k
        return gs_point_transform(x, y, &pgs->ctm_inverse, pt);
466
594k
    }
467
27.0M
}
468
469
int
470
gs_idtransform(gs_gstate * pgs, double dx, double dy, gs_point * pt)
471
766k
{       /* If the matrix isn't skewed, we get more accurate results */
472
    /* by using transform_inverse than by using the inverse matrix. */
473
766k
    if (!is_skewed(&pgs->ctm)) {
474
766k
        return gs_distance_transform_inverse(dx, dy,
475
766k
                                             &ctm_only(pgs), pt);
476
766k
    } else {
477
0
        ensure_inverse_valid(pgs);
478
0
        return gs_distance_transform(dx, dy, &pgs->ctm_inverse, pt);
479
0
    }
480
766k
}
481
482
int
483
gs_gstate_idtransform(const gs_gstate * pgs, double dx, double dy,
484
                      gs_point * pt)
485
56.4M
{
486
56.4M
    return gs_distance_transform_inverse(dx, dy, &ctm_only(pgs), pt);
487
56.4M
}
488
489
/* ------ For internal use only ------ */
490
491
/* Set the translation to a fixed value, and translate any existing path. */
492
/* Used by gschar.c to prepare for a BuildChar or BuildGlyph procedure. */
493
int
494
gx_translate_to_fixed(register gs_gstate * pgs, fixed px, fixed py)
495
21.6M
{
496
21.6M
    double fpx = fixed2float(px);
497
21.6M
    double fdx = fpx - pgs->ctm.tx;
498
21.6M
    double fpy = fixed2float(py);
499
21.6M
    double fdy = fpy - pgs->ctm.ty;
500
21.6M
    fixed dx, dy;
501
21.6M
    int code;
502
503
21.6M
    if (pgs->ctm.txy_fixed_valid) {
504
21.3M
        dx = float2fixed(fdx);
505
21.3M
        dy = float2fixed(fdy);
506
21.3M
        code = gx_path_translate(pgs->path, dx, dy);
507
21.3M
        if (code < 0)
508
0
            return code;
509
21.3M
        if (pgs->char_tm_valid && pgs->char_tm.txy_fixed_valid)
510
21.2M
            pgs->char_tm.tx_fixed += dx,
511
21.2M
                pgs->char_tm.ty_fixed += dy;
512
21.3M
    } else {
513
304k
        if (!gx_path_is_null(pgs->path))
514
0
            return_error(gs_error_limitcheck);
515
304k
    }
516
21.6M
    pgs->ctm.tx = fpx;
517
21.6M
    pgs->ctm.tx_fixed = px;
518
21.6M
    pgs->ctm.ty = fpy;
519
21.6M
    pgs->ctm.ty_fixed = py;
520
21.6M
    pgs->ctm.txy_fixed_valid = true;
521
21.6M
    pgs->ctm_inverse_valid = false;
522
21.6M
    if (pgs->char_tm_valid) { /* Update char_tm now, leaving it valid. */
523
21.5M
        pgs->char_tm.tx += fdx;
524
21.5M
        pgs->char_tm.ty += fdy;
525
21.5M
    }
526
#ifdef DEBUG
527
    if (gs_debug_c('x')) {
528
        dmlprintf2(pgs->memory, "[x]translate_to_fixed %g, %g:\n",
529
                   fixed2float(px), fixed2float(py));
530
        trace_ctm(pgs);
531
        dmlprintf(pgs->memory, "[x]   char_tm:\n");
532
        trace_matrix_fixed(pgs->memory, &pgs->char_tm);
533
    }
534
#endif
535
21.6M
    gx_setcurrentpoint(pgs, fixed2float(pgs->ctm.tx_fixed), fixed2float(pgs->ctm.ty_fixed));
536
21.6M
    pgs->current_point_valid = true;
537
21.6M
    return 0;
538
21.6M
}
539
540
/* Scale the CTM and character matrix for oversampling. */
541
int
542
gx_scale_char_matrix(register gs_gstate * pgs, int sx, int sy)
543
0
{
544
0
#define scale_cxy(s, vx, vy)\
545
0
  if ( s != 1 )\
546
0
   { pgs->ctm.vx *= s;\
547
0
        pgs->ctm.vy *= s;\
548
0
        pgs->ctm_inverse_valid = false;\
549
0
        if ( pgs->char_tm_valid )\
550
0
        { pgs->char_tm.vx *= s;\
551
0
                pgs->char_tm.vy *= s;\
552
0
        }\
553
0
   }
554
0
    scale_cxy(sx, xx, yx);
555
0
    scale_cxy(sy, xy, yy);
556
0
#undef scale_cxy
557
0
    if_debug2m('x', pgs->memory, "[x]char scale: %d %d\n", sx, sy);
558
0
    return 0;
559
0
}
560
561
/* Compute the coefficients for fast fixed-point distance transformations */
562
/* from a transformation matrix. */
563
/* We should cache the coefficients with the ctm.... */
564
int
565
gx_matrix_to_fixed_coeff(const gs_matrix * pmat, register fixed_coeff * pfc,
566
                         int max_bits)
567
2.23M
{
568
2.23M
    gs_matrix ctm;
569
2.23M
    int scale = -10000;
570
2.23M
    int expt, shift;
571
572
2.23M
    ctm = *pmat;
573
2.23M
    pfc->skewed = 0;
574
2.23M
    if (!is_fzero(ctm.xx)) {
575
2.23M
        discard(frexp(ctm.xx, &scale));
576
2.23M
    }
577
2.23M
    if (!is_fzero(ctm.xy)) {
578
6
        discard(frexp(ctm.xy, &expt));
579
6
        if (expt > scale)
580
6
            scale = expt;
581
6
        pfc->skewed = 1;
582
6
    }
583
2.23M
    if (!is_fzero(ctm.yx)) {
584
576
        discard(frexp(ctm.yx, &expt));
585
576
        if (expt > scale)
586
0
            scale = expt;
587
576
        pfc->skewed = 1;
588
576
    }
589
2.23M
    if (!is_fzero(ctm.yy)) {
590
2.23M
        discard(frexp(ctm.yy, &expt));
591
2.23M
        if (expt > scale)
592
10
            scale = expt;
593
2.23M
    }
594
    /*
595
     * There are two multiplications in fixed_coeff_mult: one involves a
596
     * factor that may have max_bits significant bits, the other may have
597
     * fixed_fraction_bits (_fixed_shift) bits.  Ensure that neither one
598
     * will overflow.
599
     */
600
2.23M
    if (max_bits < fixed_fraction_bits)
601
0
        max_bits = fixed_fraction_bits;
602
2.23M
    scale = sizeof(long) * 8 - 1 - max_bits - scale;
603
604
2.23M
    shift = scale - _fixed_shift;
605
2.23M
    if (shift > 0) {
606
2.23M
        pfc->shift = shift;
607
2.23M
        pfc->round = (fixed) 1 << (shift - 1);
608
2.23M
    } else {
609
0
        pfc->shift = 0;
610
0
        pfc->round = 0;
611
0
        scale -= shift;
612
0
    }
613
2.23M
#define SET_C(c)\
614
8.94M
  if ( is_fzero(ctm.c) ) pfc->c = 0;\
615
8.94M
  else pfc->c = (long)ldexp(ctm.c, scale)
616
2.23M
    SET_C(xx);
617
2.23M
    SET_C(xy);
618
2.23M
    SET_C(yx);
619
2.23M
    SET_C(yy);
620
2.23M
#undef SET_C
621
#ifdef DEBUG
622
    if (gs_debug_c('x')) {
623
        dlprintf6("[x]ctm: [%6g %6g %6g %6g %6g %6g]\n",
624
                  ctm.xx, ctm.xy, ctm.yx, ctm.yy, ctm.tx, ctm.ty);
625
        dlprintf6("   scale=%d fc: [0x%lx 0x%lx 0x%lx 0x%lx] shift=%d\n",
626
                  scale, pfc->xx, pfc->xy, pfc->yx, pfc->yy,
627
                  pfc->shift);
628
    }
629
#endif
630
2.23M
    pfc->max_bits = max_bits;
631
2.23M
    return 0;
632
2.23M
}
633
634
/*
635
 * Handle the case of a large value or a value with a fraction part.
636
 * See gxmatrix.h for more details.
637
 */
638
fixed
639
fixed_coeff_mult(fixed value, long coeff, const fixed_coeff *pfc, int maxb)
640
0
{
641
0
    int shift = pfc->shift;
642
643
    /*
644
     * Test if the value is too large for simple long math.
645
     */
646
0
    if ((value + (fixed_1 << (maxb - 1))) & (-fixed_1 << maxb)) {
647
        /* The second argument of fixed_mult_quo must be non-negative. */
648
0
        return
649
0
            (coeff < 0 ?
650
0
             -fixed_mult_quo(value, -coeff, fixed_1 << shift) :
651
0
             fixed_mult_quo(value, coeff, fixed_1 << shift));
652
0
    } else {
653
        /*
654
         * The construction above guarantees that the multiplications
655
         * won't overflow the capacity of an int.
656
         */
657
0
        return (fixed)
658
0
            arith_rshift(fixed2int_var(value) * coeff
659
0
                         + fixed2int(fixed_fraction(value) * coeff)
660
0
                         + pfc->round, shift);
661
0
    }
662
0
}
663
664
/* ------ Debugging printout ------ */
665
666
#ifdef DEBUG
667
668
/* Print a matrix */
669
static void
670
trace_matrix_fixed(const gs_memory_t *mem, const gs_matrix_fixed * pmat)
671
{
672
    trace_matrix(mem, (const gs_matrix *)pmat);
673
    if (pmat->txy_fixed_valid) {
674
        dmprintf2(mem, "\t\tt_fixed: [%6g %6g]\n",
675
                  fixed2float(pmat->tx_fixed),
676
                  fixed2float(pmat->ty_fixed));
677
    } else {
678
        dmputs(mem, "\t\tt_fixed not valid\n");
679
    }
680
}
681
static void
682
trace_matrix(const gs_memory_t *mem, register const gs_matrix * pmat)
683
{
684
    dmlprintf6(mem, "\t[%6g %6g %6g %6g %6g %6g]\n",
685
               pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
686
}
687
688
#endif