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
46.7k
#  define print_inverse(pgs) DO_NOTHING
49
#endif
50
#define ensure_inverse_valid(pgs)\
51
46.7k
        if ( !pgs->ctm_inverse_valid )\
52
46.7k
           { int code = ctm_set_inverse(pgs);\
53
46.7k
                if ( code < 0 ) return code;\
54
46.7k
           }
55
56
static int
57
ctm_set_inverse(gs_gstate * pgs)
58
46.7k
{
59
46.7k
    int code = gs_matrix_invert(&ctm_only(pgs), &pgs->ctm_inverse);
60
61
46.7k
    print_inverse(pgs);
62
46.7k
    if (code < 0)
63
43
        return code;
64
46.6k
    pgs->ctm_inverse_valid = true;
65
46.6k
    return 0;
66
46.7k
}
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
14.0M
    ((mat).t = (v),\
83
14.0M
     set_float2fixed_vars((mat).t_fixed, (mat).t))
84
#endif /* (!)ROUND_CTM_FIXED */
85
14.2M
#define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
86
#define update_matrix_fixed(mat, xt, yt)\
87
7.16M
  ((mat).txy_fixed_valid = (f_fits_in_fixed(xt) && f_fits_in_fixed(yt) ?\
88
7.16M
                            (update_t_fixed(mat, tx, tx_fixed, xt),\
89
7.03M
                             update_t_fixed(mat, ty, ty_fixed, yt), true) :\
90
7.16M
                            ((mat).tx = (xt), (mat).ty = (yt), false)))
91
#define update_ctm(pgs, xt, yt)\
92
1.93M
  (pgs->ctm_inverse_valid = false,\
93
1.93M
   pgs->char_tm_valid = false,\
94
1.93M
   update_matrix_fixed(pgs->ctm, xt, yt))
95
96
/* ------ Coordinate system definition ------ */
97
98
int
99
gs_initmatrix(gs_gstate * pgs)
100
183k
{
101
183k
    gs_matrix imat;
102
103
183k
    gs_defaultmatrix(pgs, &imat);
104
183k
    update_ctm(pgs, imat.tx, imat.ty);
105
183k
    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
183k
    return 0;
111
183k
}
112
113
int
114
gs_defaultmatrix(const gs_gstate * pgs, gs_matrix * pmat)
115
225k
{
116
225k
    gx_device *dev;
117
118
225k
    if (pgs->ctm_default_set) { /* set after Install */
119
40.4k
        *pmat = pgs->ctm_default;
120
40.4k
        return 1;
121
40.4k
    }
122
185k
    dev = gs_currentdevice_inline(pgs);
123
185k
    gs_deviceinitialmatrix(dev, pmat);
124
    /* Add in the translation for the Margins. */
125
185k
    pmat->tx += dev->Margins[0];
126
185k
    pmat->ty += dev->Margins[1];
127
185k
    return 0;
128
225k
}
129
130
int
131
gs_setdefaultmatrix(gs_gstate * pgs, const gs_matrix * pmat)
132
30.9k
{
133
30.9k
    if (pmat == NULL) {
134
0
        pgs->ctm_default_set = false;
135
0
        pgs->ctm_initial_set = false;
136
30.9k
    } else {
137
30.9k
        gx_device *dev;
138
139
30.9k
        pgs->ctm_default = *pmat;
140
30.9k
        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
30.9k
        dev = gs_currentdevice_inline(pgs);
145
30.9k
        gs_deviceinitialmatrix(dev, &pgs->ctm_initial);
146
30.9k
        pgs->ctm_initial_set = 1;
147
30.9k
    }
148
30.9k
    return 0;
149
30.9k
}
150
151
int
152
gs_updatematrices(gs_gstate *pgs)
153
101k
{
154
101k
    gx_device *dev;
155
101k
    gs_matrix newdefault, init, t, inv, newctm;
156
101k
    int code;
157
#ifdef DEBUG
158
    gs_matrix *mat;
159
#endif
160
161
    /* Read the current device initial matrix. */
162
101k
    dev = gs_currentdevice_inline(pgs);
163
101k
    gs_deviceinitialmatrix(dev, &init);
164
165
#ifdef DEBUG
166
    if (gs_debug_c('x'))
167
        dlprintf("[x]updatematrices\n");
168
#endif
169
170
101k
    if (pgs->ctm_default_set == 0 ||
171
101k
        pgs->ctm_initial_set == 0) {
172
        /* If neither default or initial are set, then store them for the
173
         * first time. */
174
15.3k
        pgs->ctm_initial = init;
175
15.3k
        pgs->ctm_initial_set = 1;
176
15.3k
        pgs->ctm_default = init;
177
15.3k
        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
15.3k
        return 0;
185
15.3k
    }
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
86.1k
    if (init.xx == pgs->ctm_initial.xx &&
201
86.1k
        init.xy == pgs->ctm_initial.xy &&
202
86.1k
        init.yx == pgs->ctm_initial.yx &&
203
86.1k
        init.yy == pgs->ctm_initial.yy &&
204
86.1k
        init.tx == pgs->ctm_initial.tx &&
205
86.1k
        init.ty == pgs->ctm_initial.ty)
206
86.1k
        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
122k
{
279
122k
    *pmat = ctm_only(pgs);
280
122k
    return 0;
281
122k
}
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
886k
{
288
886k
    gs_matrix cmat;
289
886k
    int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &cmat);
290
291
886k
    if (code < 0)
292
0
        return code;
293
886k
    update_matrix_fixed(pgs->char_tm, cmat.tx, cmat.ty);
294
886k
    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
886k
    pgs->char_tm_valid = true;
300
886k
    return 0;
301
886k
}
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
3.31M
{
309
3.31M
    if (!pgs->char_tm_valid) {
310
752k
        int code;
311
312
752k
        if (!force)
313
0
            return_error(gs_error_undefinedresult);
314
752k
        code = gs_setcharmatrix(pgs, &pgs->font->FontMatrix);
315
752k
        if (code < 0)
316
0
            return code;
317
752k
    }
318
3.31M
    if (ptm != NULL)
319
0
        *ptm = char_tm_only(pgs);
320
3.31M
    return 0;
321
3.31M
}
322
323
int
324
gs_setmatrix(gs_gstate * pgs, const gs_matrix * pmat)
325
649k
{
326
649k
    update_ctm(pgs, pmat->tx, pmat->ty);
327
649k
    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
649k
    return 0;
333
649k
}
334
335
int
336
gs_gstate_setmatrix(gs_gstate * pgs, const gs_matrix * pmat)
337
4.33M
{
338
4.33M
    update_matrix_fixed(pgs->ctm, pmat->tx, pmat->ty);
339
4.33M
    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
4.33M
    return 0;
345
4.33M
}
346
347
int
348
gs_settocharmatrix(gs_gstate * pgs)
349
981k
{
350
981k
    if (pgs->char_tm_valid) {
351
981k
        pgs->ctm = pgs->char_tm;
352
981k
        pgs->ctm_inverse_valid = false;
353
981k
        return 0;
354
981k
    } else
355
0
        return_error(gs_error_undefinedresult);
356
981k
}
357
358
int
359
gs_translate(gs_gstate * pgs, double dx, double dy)
360
832k
{
361
832k
    gs_point pt;
362
832k
    int code;
363
364
832k
    if ((code = gs_distance_transform(dx, dy, &ctm_only(pgs), &pt)) < 0)
365
0
        return code;
366
832k
    pt.x = (float)pt.x + pgs->ctm.tx;
367
832k
    pt.y = (float)pt.y + pgs->ctm.ty;
368
832k
    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
832k
    return 0;
376
832k
}
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
8.58k
{
398
8.58k
    pgs->ctm.xx *= sx;
399
8.58k
    pgs->ctm.xy *= sx;
400
8.58k
    pgs->ctm.yx *= sy;
401
8.58k
    pgs->ctm.yy *= sy;
402
8.58k
    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
8.58k
    return 0;
408
8.58k
}
409
410
int
411
gs_rotate(gs_gstate * pgs, double ang)
412
690k
{
413
690k
    int code = gs_matrix_rotate(&ctm_only(pgs), ang,
414
690k
                                &ctm_only_writable(pgs));
415
416
690k
    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
690k
    return code;
422
690k
}
423
424
int
425
gs_concat(gs_gstate * pgs, const gs_matrix * pmat)
426
273k
{
427
273k
    gs_matrix cmat;
428
273k
    int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &cmat);
429
430
273k
    if (code < 0)
431
0
        return code;
432
273k
    update_ctm(pgs, cmat.tx, cmat.ty);
433
273k
    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
273k
    return code;
439
273k
}
440
441
/* ------ Coordinate transformation ------ */
442
443
1.43M
#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
0
{
448
0
    return gs_point_transform(x, y, &ctm_only(pgs), pt);
449
0
}
450
451
int
452
gs_dtransform(gs_gstate * pgs, double dx, double dy, gs_point * pt)
453
10.8k
{
454
10.8k
    return gs_distance_transform(dx, dy, &ctm_only(pgs), pt);
455
10.8k
}
456
457
int
458
gs_itransform(gs_gstate * pgs, double x, double y, gs_point * pt)
459
1.39M
{       /* If the matrix isn't skewed, we get more accurate results */
460
    /* by using transform_inverse than by using the inverse matrix. */
461
1.39M
    if (!is_skewed(&pgs->ctm)) {
462
1.35M
        return gs_point_transform_inverse(x, y, &ctm_only(pgs), pt);
463
1.35M
    } else {
464
46.7k
        ensure_inverse_valid(pgs);
465
46.6k
        return gs_point_transform(x, y, &pgs->ctm_inverse, pt);
466
46.7k
    }
467
1.39M
}
468
469
int
470
gs_idtransform(gs_gstate * pgs, double dx, double dy, gs_point * pt)
471
30.9k
{       /* If the matrix isn't skewed, we get more accurate results */
472
    /* by using transform_inverse than by using the inverse matrix. */
473
30.9k
    if (!is_skewed(&pgs->ctm)) {
474
30.9k
        return gs_distance_transform_inverse(dx, dy,
475
30.9k
                                             &ctm_only(pgs), pt);
476
30.9k
    } else {
477
0
        ensure_inverse_valid(pgs);
478
0
        return gs_distance_transform(dx, dy, &pgs->ctm_inverse, pt);
479
0
    }
480
30.9k
}
481
482
int
483
gs_gstate_idtransform(const gs_gstate * pgs, double dx, double dy,
484
                      gs_point * pt)
485
32.9M
{
486
32.9M
    return gs_distance_transform_inverse(dx, dy, &ctm_only(pgs), pt);
487
32.9M
}
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
1.54M
{
496
1.54M
    double fpx = fixed2float(px);
497
1.54M
    double fdx = fpx - pgs->ctm.tx;
498
1.54M
    double fpy = fixed2float(py);
499
1.54M
    double fdy = fpy - pgs->ctm.ty;
500
1.54M
    fixed dx, dy;
501
1.54M
    int code;
502
503
1.54M
    if (pgs->ctm.txy_fixed_valid) {
504
1.50M
        dx = float2fixed(fdx);
505
1.50M
        dy = float2fixed(fdy);
506
1.50M
        code = gx_path_translate(pgs->path, dx, dy);
507
1.50M
        if (code < 0)
508
0
            return code;
509
1.50M
        if (pgs->char_tm_valid && pgs->char_tm.txy_fixed_valid)
510
1.48M
            pgs->char_tm.tx_fixed += dx,
511
1.48M
                pgs->char_tm.ty_fixed += dy;
512
1.50M
    } else {
513
45.0k
        if (!gx_path_is_null(pgs->path))
514
0
            return_error(gs_error_limitcheck);
515
45.0k
    }
516
1.54M
    pgs->ctm.tx = fpx;
517
1.54M
    pgs->ctm.tx_fixed = px;
518
1.54M
    pgs->ctm.ty = fpy;
519
1.54M
    pgs->ctm.ty_fixed = py;
520
1.54M
    pgs->ctm.txy_fixed_valid = true;
521
1.54M
    pgs->ctm_inverse_valid = false;
522
1.54M
    if (pgs->char_tm_valid) { /* Update char_tm now, leaving it valid. */
523
1.54M
        pgs->char_tm.tx += fdx;
524
1.54M
        pgs->char_tm.ty += fdy;
525
1.54M
    }
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
1.54M
    gx_setcurrentpoint(pgs, fixed2float(pgs->ctm.tx_fixed), fixed2float(pgs->ctm.ty_fixed));
536
1.54M
    pgs->current_point_valid = true;
537
1.54M
    return 0;
538
1.54M
}
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
0
{
568
0
    gs_matrix ctm;
569
0
    int scale = -10000;
570
0
    int expt, shift;
571
572
0
    ctm = *pmat;
573
0
    pfc->skewed = 0;
574
0
    if (!is_fzero(ctm.xx)) {
575
0
        discard(frexp(ctm.xx, &scale));
576
0
    }
577
0
    if (!is_fzero(ctm.xy)) {
578
0
        discard(frexp(ctm.xy, &expt));
579
0
        if (expt > scale)
580
0
            scale = expt;
581
0
        pfc->skewed = 1;
582
0
    }
583
0
    if (!is_fzero(ctm.yx)) {
584
0
        discard(frexp(ctm.yx, &expt));
585
0
        if (expt > scale)
586
0
            scale = expt;
587
0
        pfc->skewed = 1;
588
0
    }
589
0
    if (!is_fzero(ctm.yy)) {
590
0
        discard(frexp(ctm.yy, &expt));
591
0
        if (expt > scale)
592
0
            scale = expt;
593
0
    }
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
0
    if (max_bits < fixed_fraction_bits)
601
0
        max_bits = fixed_fraction_bits;
602
0
    scale = sizeof(long) * 8 - 1 - max_bits - scale;
603
604
0
    shift = scale - _fixed_shift;
605
0
    if (shift > 0) {
606
0
        pfc->shift = shift;
607
0
        pfc->round = (fixed) 1 << (shift - 1);
608
0
    } else {
609
0
        pfc->shift = 0;
610
0
        pfc->round = 0;
611
0
        scale -= shift;
612
0
    }
613
0
#define SET_C(c)\
614
0
  if ( is_fzero(ctm.c) ) pfc->c = 0;\
615
0
  else pfc->c = (long)ldexp(ctm.c, scale)
616
0
    SET_C(xx);
617
0
    SET_C(xy);
618
0
    SET_C(yx);
619
0
    SET_C(yy);
620
0
#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
0
    pfc->max_bits = max_bits;
631
0
    return 0;
632
0
}
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