Coverage Report

Created: 2026-08-08 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/base/gsmatrix.c
Line
Count
Source
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
/* Matrix operators for Ghostscript library */
18
#include "math_.h"
19
#include "memory_.h"
20
#include "gx.h"
21
#include "gserrors.h"
22
#include "gxfarith.h"
23
#include "gxfixed.h"
24
#include "gxmatrix.h"
25
#include "stream.h"
26
27
/* The identity matrix */
28
static const gs_matrix gs_identity_matrix =
29
{identity_matrix_body};
30
31
/* ------ Matrix creation ------ */
32
33
/* Create an identity matrix */
34
void
35
gs_make_identity(gs_matrix * pmat)
36
78.1M
{
37
78.1M
    *pmat = gs_identity_matrix;
38
78.1M
}
39
40
/* Create a translation matrix */
41
int
42
gs_make_translation(double dx, double dy, gs_matrix * pmat)
43
942k
{
44
942k
    *pmat = gs_identity_matrix;
45
942k
    pmat->tx = dx;
46
942k
    pmat->ty = dy;
47
942k
    return 0;
48
942k
}
49
50
/* Create a scaling matrix */
51
int
52
gs_make_scaling(double sx, double sy, gs_matrix * pmat)
53
6.10M
{
54
6.10M
    *pmat = gs_identity_matrix;
55
6.10M
    pmat->xx = sx;
56
6.10M
    pmat->yy = sy;
57
6.10M
    return 0;
58
6.10M
}
59
60
/* Create a rotation matrix. */
61
/* The angle is in degrees. */
62
int
63
gs_make_rotation(double ang, gs_matrix * pmat)
64
1.43M
{
65
1.43M
    gs_sincos_t sincos;
66
67
1.43M
    gs_sincos_degrees(ang, &sincos);
68
1.43M
    pmat->yy = pmat->xx = sincos.cos;
69
1.43M
    pmat->xy = sincos.sin;
70
1.43M
    pmat->yx = -sincos.sin;
71
1.43M
    pmat->tx = pmat->ty = 0.0;
72
1.43M
    return 0;
73
1.43M
}
74
75
/* ------ Matrix arithmetic ------ */
76
77
/* Multiply two matrices.  We should check for floating exceptions, */
78
/* but for the moment it's just too awkward. */
79
/* Since this is used heavily, we check for shortcuts. */
80
int
81
gs_matrix_multiply(const gs_matrix * pm1, const gs_matrix * pm2, gs_matrix * pmr)
82
54.2M
{
83
54.2M
    double xx1 = pm1->xx, yy1 = pm1->yy;
84
54.2M
    double tx1 = pm1->tx, ty1 = pm1->ty;
85
54.2M
    double xx2 = pm2->xx, yy2 = pm2->yy;
86
54.2M
    double xy2 = pm2->xy, yx2 = pm2->yx;
87
88
54.2M
    if (is_xxyy(pm1)) {
89
52.6M
        pmr->tx = tx1 * xx2 + pm2->tx;
90
52.6M
        pmr->ty = ty1 * yy2 + pm2->ty;
91
52.6M
        if (is_fzero(xy2))
92
51.3M
            pmr->xy = 0;
93
1.25M
        else
94
1.25M
            pmr->xy = xx1 * xy2,
95
1.25M
                pmr->ty += tx1 * xy2;
96
52.6M
        pmr->xx = xx1 * xx2;
97
52.6M
        if (is_fzero(yx2))
98
51.2M
            pmr->yx = 0;
99
1.32M
        else
100
1.32M
            pmr->yx = yy1 * yx2,
101
1.32M
                pmr->tx += ty1 * yx2;
102
52.6M
        pmr->yy = yy1 * yy2;
103
52.6M
    } else {
104
1.62M
        double xy1 = pm1->xy, yx1 = pm1->yx;
105
106
1.62M
        pmr->xx = xx1 * xx2 + xy1 * yx2;
107
1.62M
        pmr->xy = xx1 * xy2 + xy1 * yy2;
108
1.62M
        pmr->yy = yx1 * xy2 + yy1 * yy2;
109
1.62M
        pmr->yx = yx1 * xx2 + yy1 * yx2;
110
1.62M
        pmr->tx = tx1 * xx2 + ty1 * yx2 + pm2->tx;
111
1.62M
        pmr->ty = tx1 * xy2 + ty1 * yy2 + pm2->ty;
112
1.62M
    }
113
54.2M
    return 0;
114
54.2M
}
115
int
116
gs_matrix_multiply_double(const gs_matrix_double * pm1, const gs_matrix * pm2, gs_matrix_double * pmr)
117
1.68M
{
118
1.68M
    double xx1 = pm1->xx, yy1 = pm1->yy;
119
1.68M
    double tx1 = pm1->tx, ty1 = pm1->ty;
120
1.68M
    double xx2 = pm2->xx, yy2 = pm2->yy;
121
1.68M
    double xy2 = pm2->xy, yx2 = pm2->yx;
122
123
1.68M
    if (is_xxyy(pm1)) {
124
1.63M
        pmr->tx = tx1 * xx2 + pm2->tx;
125
1.63M
        pmr->ty = ty1 * yy2 + pm2->ty;
126
1.63M
        if (is_fzero(xy2))
127
1.37M
            pmr->xy = 0;
128
261k
        else
129
261k
            pmr->xy = xx1 * xy2,
130
261k
                pmr->ty += tx1 * xy2;
131
1.63M
        pmr->xx = xx1 * xx2;
132
1.63M
        if (is_fzero(yx2))
133
1.36M
            pmr->yx = 0;
134
272k
        else
135
272k
            pmr->yx = yy1 * yx2,
136
272k
                pmr->tx += ty1 * yx2;
137
1.63M
        pmr->yy = yy1 * yy2;
138
1.63M
    } else {
139
47.6k
        double xy1 = pm1->xy, yx1 = pm1->yx;
140
141
47.6k
        pmr->xx = xx1 * xx2 + xy1 * yx2;
142
47.6k
        pmr->xy = xx1 * xy2 + xy1 * yy2;
143
47.6k
        pmr->yy = yx1 * xy2 + yy1 * yy2;
144
47.6k
        pmr->yx = yx1 * xx2 + yy1 * yx2;
145
47.6k
        pmr->tx = tx1 * xx2 + ty1 * yx2 + pm2->tx;
146
47.6k
        pmr->ty = tx1 * xy2 + ty1 * yy2 + pm2->ty;
147
47.6k
    }
148
1.68M
    return 0;
149
1.68M
}
150
151
/* Invert a matrix.  Return gs_error_undefinedresult if not invertible. */
152
int
153
gs_matrix_invert(const gs_matrix * pm, gs_matrix * pmr)
154
28.7M
{       /* We have to be careful about fetch/store order, */
155
    /* because pm might be the same as pmr. */
156
28.7M
    if (is_xxyy(pm)) {
157
21.7M
        if (is_fzero(pm->xx) || is_fzero(pm->yy))
158
24.7k
            return_error(gs_error_undefinedresult);
159
21.7M
        pmr->tx = -(pmr->xx = 1.0 / pm->xx) * pm->tx;
160
21.7M
        pmr->xy = 0.0;
161
21.7M
        pmr->yx = 0.0;
162
21.7M
        pmr->ty = -(pmr->yy = 1.0 / pm->yy) * pm->ty;
163
21.7M
    } else {
164
6.97M
        float mxx = pm->xx, myy = pm->yy, mxy = pm->xy, myx = pm->yx;
165
6.97M
        float mtx = pm->tx, mty = pm->ty;
166
        /* we declare det as double since on at least some computer (i.e. peeves)
167
           declaring it as a float results in different values for pmr depending
168
           on whether or not optimization is turned on.  I believe this is caused
169
           by the compiler keeping the det value in an internal register when
170
           optimization is enable.  As evidence of this if you add a debugging
171
           statement to print out det the optimized code acts the same as the
172
           unoptimized code.  declearing det as double does not change the CET 10-09.ps
173
           output. */
174
6.97M
        double det = (float)(mxx * myy) - (float)(mxy * myx);
175
176
        /*
177
         * We are doing the math as floats instead of doubles to reproduce
178
         * the results in page 1 of CET 10-09.ps
179
         */
180
6.97M
        if (det == 0)
181
7.51k
            return_error(gs_error_undefinedresult);
182
6.96M
        pmr->xx = myy / det;
183
6.96M
        pmr->xy = -mxy / det;
184
6.96M
        pmr->yx = -myx / det;
185
6.96M
        pmr->yy = mxx / det;
186
6.96M
        pmr->tx = (((float)(mty * myx) - (float)(mtx * myy))) / det;
187
6.96M
        pmr->ty = (((float)(mtx * mxy) - (float)(mty * mxx))) / det;
188
6.96M
    }
189
28.6M
    return 0;
190
28.7M
}
191
int
192
gs_matrix_invert_to_double(const gs_matrix * pm, gs_matrix_double * pmr)
193
1.68M
{       /* We have to be careful about fetch/store order, */
194
    /* because pm might be the same as pmr. */
195
1.68M
    if (is_xxyy(pm)) {
196
1.63M
        if (is_fzero(pm->xx) || is_fzero(pm->yy))
197
114
            return_error(gs_error_undefinedresult);
198
1.63M
        pmr->tx = -(pmr->xx = 1.0 / pm->xx) * pm->tx;
199
1.63M
        pmr->xy = 0.0;
200
1.63M
        pmr->yx = 0.0;
201
1.63M
        pmr->ty = -(pmr->yy = 1.0 / pm->yy) * pm->ty;
202
1.63M
    } else {
203
47.6k
        double mxx = pm->xx, myy = pm->yy, mxy = pm->xy, myx = pm->yx;
204
47.6k
        double mtx = pm->tx, mty = pm->ty;
205
47.6k
        double det = (mxx * myy) - (mxy * myx);
206
207
        /*
208
         * We are doing the math as floats instead of doubles to reproduce
209
         * the results in page 1 of CET 10-09.ps
210
         */
211
47.6k
        if (det == 0)
212
5
            return_error(gs_error_undefinedresult);
213
47.6k
        pmr->xx = myy / det;
214
47.6k
        pmr->xy = -mxy / det;
215
47.6k
        pmr->yx = -myx / det;
216
47.6k
        pmr->yy = mxx / det;
217
47.6k
        pmr->tx = (((mty * myx) - (mtx * myy))) / det;
218
47.6k
        pmr->ty = (((mtx * mxy) - (mty * mxx))) / det;
219
47.6k
    }
220
1.68M
    return 0;
221
1.68M
}
222
223
/* Translate a matrix, possibly in place. */
224
int
225
gs_matrix_translate(const gs_matrix * pm, double dx, double dy, gs_matrix * pmr)
226
1.31M
{
227
1.31M
    gs_point trans;
228
1.31M
    int code = gs_distance_transform(dx, dy, pm, &trans);
229
230
1.31M
    if (code < 0)
231
0
        return code;
232
1.31M
    if (pmr != pm)
233
56.8k
        *pmr = *pm;
234
1.31M
    pmr->tx += trans.x;
235
1.31M
    pmr->ty += trans.y;
236
1.31M
    return 0;
237
1.31M
}
238
239
/* Scale a matrix, possibly in place. */
240
int
241
gs_matrix_scale(const gs_matrix * pm, double sx, double sy, gs_matrix * pmr)
242
3.56M
{
243
3.56M
    pmr->xx = pm->xx * sx;
244
3.56M
    pmr->xy = pm->xy * sx;
245
3.56M
    pmr->yx = pm->yx * sy;
246
3.56M
    pmr->yy = pm->yy * sy;
247
3.56M
    if (pmr != pm) {
248
137k
        pmr->tx = pm->tx;
249
137k
        pmr->ty = pm->ty;
250
137k
    }
251
3.56M
    return 0;
252
3.56M
}
253
254
/* Rotate a matrix, possibly in place.  The angle is in degrees. */
255
int
256
gs_matrix_rotate(const gs_matrix * pm, double ang, gs_matrix * pmr)
257
8.12M
{
258
8.12M
    double mxx, mxy;
259
8.12M
    gs_sincos_t sincos;
260
261
8.12M
    gs_sincos_degrees(ang, &sincos);
262
8.12M
    mxx = pm->xx, mxy = pm->xy;
263
8.12M
    pmr->xx = sincos.cos * mxx + sincos.sin * pm->yx;
264
8.12M
    pmr->xy = sincos.cos * mxy + sincos.sin * pm->yy;
265
8.12M
    pmr->yx = sincos.cos * pm->yx - sincos.sin * mxx;
266
8.12M
    pmr->yy = sincos.cos * pm->yy - sincos.sin * mxy;
267
8.12M
    if (pmr != pm) {
268
0
        pmr->tx = pm->tx;
269
0
        pmr->ty = pm->ty;
270
0
    }
271
8.12M
    return 0;
272
8.12M
}
273
274
/* ------ Coordinate transformations (floating point) ------ */
275
276
/* Note that all the transformation routines take separate */
277
/* x and y arguments, but return their result in a point. */
278
279
/* Transform a point. */
280
int
281
gs_point_transform(double x, double y, const gs_matrix * pmat,
282
                   gs_point * ppt)
283
2.63G
{
284
    /*
285
     * The float casts are there to reproduce results in CET 10-01.ps
286
     * page 4.
287
     */
288
2.63G
    ppt->x = (float)(x * pmat->xx) + pmat->tx;
289
2.63G
    ppt->y = (float)(y * pmat->yy) + pmat->ty;
290
2.63G
    if (!is_fzero(pmat->yx))
291
113M
        ppt->x += (float)(y * pmat->yx);
292
2.63G
    if (!is_fzero(pmat->xy))
293
1.94G
        ppt->y += (float)(x * pmat->xy);
294
2.63G
    return 0;
295
2.63G
}
296
297
/* Inverse-transform a point. */
298
/* Return gs_error_undefinedresult if the matrix is not invertible. */
299
int
300
gs_point_transform_inverse(double x, double y, const gs_matrix * pmat,
301
                           gs_point * ppt)
302
140M
{
303
140M
    if (is_xxyy(pmat)) {
304
135M
        if (is_fzero(pmat->xx) || is_fzero(pmat->yy))
305
6.23k
            return_error(gs_error_undefinedresult);
306
135M
        ppt->x = (x - pmat->tx) / pmat->xx;
307
135M
        ppt->y = (y - pmat->ty) / pmat->yy;
308
135M
        return 0;
309
135M
    } else if (is_xyyx(pmat)) {
310
1.79M
        if (is_fzero(pmat->xy) || is_fzero(pmat->yx))
311
344
            return_error(gs_error_undefinedresult);
312
1.79M
        ppt->x = (y - pmat->ty) / pmat->xy;
313
1.79M
        ppt->y = (x - pmat->tx) / pmat->yx;
314
1.79M
        return 0;
315
3.55M
    } else {     /* There are faster ways to do this, */
316
        /* but we won't implement one unless we have to. */
317
3.55M
        gs_matrix imat;
318
3.55M
        int code = gs_matrix_invert(pmat, &imat);
319
320
3.55M
        if (code < 0)
321
524
            return code;
322
3.55M
        return gs_point_transform(x, y, &imat, ppt);
323
3.55M
    }
324
140M
}
325
326
/* Transform a distance. */
327
int
328
gs_distance_transform(double dx, double dy, const gs_matrix * pmat,
329
                      gs_point * pdpt)
330
204M
{
331
204M
    pdpt->x = dx * pmat->xx;
332
204M
    pdpt->y = dy * pmat->yy;
333
204M
    if (!is_fzero(pmat->yx))
334
48.7M
        pdpt->x += dy * pmat->yx;
335
204M
    if (!is_fzero(pmat->xy))
336
48.0M
        pdpt->y += dx * pmat->xy;
337
204M
    return 0;
338
204M
}
339
340
/* Inverse-transform a distance. */
341
/* Return gs_error_undefinedresult if the matrix is not invertible. */
342
int
343
gs_distance_transform_inverse(double dx, double dy,
344
                              const gs_matrix * pmat, gs_point * pdpt)
345
39.6M
{
346
39.6M
    if (is_xxyy(pmat)) {
347
24.2M
        if (is_fzero(pmat->xx) || is_fzero(pmat->yy))
348
53.9k
            return_error(gs_error_undefinedresult);
349
24.2M
        pdpt->x = dx / pmat->xx;
350
24.2M
        pdpt->y = dy / pmat->yy;
351
24.2M
    } else if (is_xyyx(pmat)) {
352
316k
        if (is_fzero(pmat->xy) || is_fzero(pmat->yx))
353
23.7k
            return_error(gs_error_undefinedresult);
354
292k
        pdpt->x = dy / pmat->xy;
355
292k
        pdpt->y = dx / pmat->yx;
356
15.0M
    } else {
357
15.0M
        double det = pmat->xx * pmat->yy - pmat->xy * pmat->yx;
358
359
15.0M
        if (det == 0)
360
727k
            return_error(gs_error_undefinedresult);
361
14.3M
        pdpt->x = (dx * pmat->yy - dy * pmat->yx) / det;
362
14.3M
        pdpt->y = (dy * pmat->xx - dx * pmat->xy) / det;
363
14.3M
    }
364
38.8M
    return 0;
365
39.6M
}
366
367
/* Compute the bounding box of 4 points. */
368
int
369
gs_points_bbox(const gs_point pts[4], gs_rect * pbox)
370
30.3M
{
371
30.3M
#define assign_min_max(vmin, vmax, v0, v1)\
372
121M
  if ( v0 < v1 ) vmin = v0, vmax = v1; else vmin = v1, vmax = v0
373
30.3M
#define assign_min_max_4(vmin, vmax, v0, v1, v2, v3)\
374
60.6M
  { double min01, max01, min23, max23;\
375
60.6M
    assign_min_max(min01, max01, v0, v1);\
376
60.6M
    assign_min_max(min23, max23, v2, v3);\
377
60.6M
    vmin = min(min01, min23);\
378
60.6M
    vmax = max(max01, max23);\
379
60.6M
  }
380
30.3M
    assign_min_max_4(pbox->p.x, pbox->q.x,
381
30.3M
                     pts[0].x, pts[1].x, pts[2].x, pts[3].x);
382
30.3M
    assign_min_max_4(pbox->p.y, pbox->q.y,
383
30.3M
                     pts[0].y, pts[1].y, pts[2].y, pts[3].y);
384
30.3M
#undef assign_min_max
385
30.3M
#undef assign_min_max_4
386
30.3M
    return 0;
387
30.3M
}
388
389
/* Transform or inverse-transform a bounding box. */
390
/* Return gs_error_undefinedresult if the matrix is not invertible. */
391
static int
392
bbox_transform_either_only(const gs_rect * pbox_in, const gs_matrix * pmat,
393
                           gs_point pts[4],
394
     int (*point_xform) (double, double, const gs_matrix *, gs_point *))
395
30.3M
{
396
30.3M
    int code;
397
398
30.3M
    if ((code = (*point_xform) (pbox_in->p.x, pbox_in->p.y, pmat, &pts[0])) < 0 ||
399
30.3M
        (code = (*point_xform) (pbox_in->p.x, pbox_in->q.y, pmat, &pts[1])) < 0 ||
400
30.3M
        (code = (*point_xform) (pbox_in->q.x, pbox_in->p.y, pmat, &pts[2])) < 0 ||
401
30.3M
     (code = (*point_xform) (pbox_in->q.x, pbox_in->q.y, pmat, &pts[3])) < 0
402
30.3M
        )
403
30.3M
        DO_NOTHING;
404
30.3M
    return code;
405
30.3M
}
406
407
static int
408
bbox_transform_either(const gs_rect * pbox_in, const gs_matrix * pmat,
409
                      gs_rect * pbox_out,
410
     int (*point_xform) (double, double, const gs_matrix *, gs_point *))
411
30.3M
{
412
30.3M
    int code;
413
414
    /*
415
     * In principle, we could transform only one point and two
416
     * distance vectors; however, because of rounding, we will only
417
     * get fully consistent results if we transform all 4 points.
418
     * We must compute the max and min after transforming,
419
     * since a rotation may be involved.
420
     */
421
30.3M
    gs_point pts[4];
422
423
30.3M
    if ((code = bbox_transform_either_only(pbox_in, pmat, pts, point_xform)) < 0)
424
772
        return code;
425
30.3M
    return gs_points_bbox(pts, pbox_out);
426
30.3M
}
427
int
428
gs_bbox_transform(const gs_rect * pbox_in, const gs_matrix * pmat,
429
                  gs_rect * pbox_out)
430
24.7M
{
431
24.7M
    return bbox_transform_either(pbox_in, pmat, pbox_out,
432
24.7M
                                 gs_point_transform);
433
24.7M
}
434
int
435
gs_bbox_transform_only(const gs_rect * pbox_in, const gs_matrix * pmat,
436
                       gs_point points[4])
437
20.4k
{
438
20.4k
    return bbox_transform_either_only(pbox_in, pmat, points,
439
20.4k
                                      gs_point_transform);
440
20.4k
}
441
int
442
gs_bbox_transform_inverse(const gs_rect * pbox_in, const gs_matrix * pmat,
443
                          gs_rect * pbox_out)
444
5.51M
{
445
5.51M
    int code = bbox_transform_either(pbox_in, pmat, pbox_out,
446
5.51M
                                 gs_point_transform_inverse);
447
448
5.51M
    return code;
449
5.51M
}
450
451
/* ------ Coordinate transformations (to fixed point) ------ */
452
453
33.7M
#define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
454
455
/* Make a gs_matrix_fixed from a gs_matrix. */
456
int
457
gs_matrix_fixed_from_matrix(gs_matrix_fixed *pfmat, const gs_matrix *pmat)
458
15.6M
{
459
15.6M
    *(gs_matrix *)pfmat = *pmat;
460
15.6M
    if (f_fits_in_fixed(pmat->tx) && f_fits_in_fixed(pmat->ty)) {
461
15.6M
        pfmat->tx = fixed2float(pfmat->tx_fixed = float2fixed(pmat->tx));
462
15.6M
        pfmat->ty = fixed2float(pfmat->ty_fixed = float2fixed(pmat->ty));
463
15.6M
        pfmat->txy_fixed_valid = true;
464
15.6M
    } else {
465
0
        pfmat->txy_fixed_valid = false;
466
0
    }
467
15.6M
    return 0;
468
15.6M
}
469
470
/* Transform a point with a fixed-point result. */
471
int
472
gs_point_transform2fixed(const gs_matrix_fixed * pmat,
473
                         double x, double y, gs_fixed_point * ppt)
474
8.39M
{
475
8.39M
    fixed px, py, t;
476
8.39M
    double xtemp, ytemp;
477
8.39M
    int code;
478
479
8.39M
    if (!pmat->txy_fixed_valid) { /* The translation is out of range.  Do the */
480
        /* computation in floating point, and convert to */
481
        /* fixed at the end. */
482
231k
        gs_point fpt;
483
484
231k
        gs_point_transform(x, y, (const gs_matrix *)pmat, &fpt);
485
231k
        if (!(f_fits_in_fixed(fpt.x) && f_fits_in_fixed(fpt.y)))
486
31
            return_error(gs_error_limitcheck);
487
231k
        ppt->x = float2fixed(fpt.x);
488
231k
        ppt->y = float2fixed(fpt.y);
489
231k
        return 0;
490
231k
    }
491
8.16M
    if (!is_fzero(pmat->xy)) { /* Hope for 90 degree rotation */
492
32.1k
        if ((code = CHECK_DFMUL2FIXED_VARS(px, y, pmat->yx, xtemp)) < 0 ||
493
32.1k
            (code = CHECK_DFMUL2FIXED_VARS(py, x, pmat->xy, ytemp)) < 0
494
32.1k
            )
495
7
            return code;
496
32.1k
        FINISH_DFMUL2FIXED_VARS(px, xtemp);
497
32.1k
        FINISH_DFMUL2FIXED_VARS(py, ytemp);
498
32.1k
        if (!is_fzero(pmat->xx)) {
499
25.9k
            if ((code = CHECK_DFMUL2FIXED_VARS(t, x, pmat->xx, xtemp)) < 0)
500
1
                return code;
501
25.9k
            FINISH_DFMUL2FIXED_VARS(t, xtemp);
502
25.9k
            if ((code = CHECK_SET_FIXED_SUM(px, px, t)) < 0)
503
0
                return code;
504
25.9k
        }
505
32.1k
        if (!is_fzero(pmat->yy)) {
506
25.8k
            if ((code = CHECK_DFMUL2FIXED_VARS(t, y, pmat->yy, ytemp)) < 0)
507
0
                return code;
508
25.8k
            FINISH_DFMUL2FIXED_VARS(t, ytemp);
509
25.8k
            if ((code = CHECK_SET_FIXED_SUM(py, py, t)) < 0)
510
0
                return code;
511
25.8k
        }
512
8.12M
    } else {
513
8.12M
        if ((code = CHECK_DFMUL2FIXED_VARS(px, x, pmat->xx, xtemp)) < 0 ||
514
8.12M
            (code = CHECK_DFMUL2FIXED_VARS(py, y, pmat->yy, ytemp)) < 0
515
8.12M
            )
516
6.36k
            return code;
517
8.12M
        FINISH_DFMUL2FIXED_VARS(px, xtemp);
518
8.12M
        FINISH_DFMUL2FIXED_VARS(py, ytemp);
519
8.12M
        if (!is_fzero(pmat->yx)) {
520
4
            if ((code = CHECK_DFMUL2FIXED_VARS(t, y, pmat->yx, ytemp)) < 0)
521
0
                return code;
522
4
            FINISH_DFMUL2FIXED_VARS(t, ytemp);
523
4
            if ((code = CHECK_SET_FIXED_SUM(px, px, t)) < 0)
524
0
                return code;
525
4
        }
526
8.12M
    }
527
8.15M
    if (((code = CHECK_SET_FIXED_SUM(ppt->x, px, pmat->tx_fixed)) < 0) ||
528
8.15M
        ((code = CHECK_SET_FIXED_SUM(ppt->y, py, pmat->ty_fixed)) < 0) )
529
136
        return code;
530
8.15M
    return 0;
531
8.15M
}
532
533
#if PRECISE_CURRENTPOINT
534
/* Transform a point with a fixed-point result. */
535
/* Used for the best precision of the current point,
536
   see comment in clamp_point_aux. */
537
int
538
gs_point_transform2fixed_rounding(const gs_matrix_fixed * pmat,
539
                         double x, double y, gs_fixed_point * ppt)
540
941k
{
541
941k
    gs_point fpt;
542
543
941k
    gs_point_transform(x, y, (const gs_matrix *)pmat, &fpt);
544
941k
    if (!(f_fits_in_fixed(fpt.x) && f_fits_in_fixed(fpt.y)))
545
60
        return_error(gs_error_limitcheck);
546
941k
    ppt->x = float2fixed_rounded(fpt.x);
547
941k
    ppt->y = float2fixed_rounded(fpt.y);
548
941k
    return 0;
549
941k
}
550
#endif
551
552
/* Transform a distance with a fixed-point result. */
553
int
554
gs_distance_transform2fixed(const gs_matrix_fixed * pmat,
555
                            double dx, double dy, gs_fixed_point * ppt)
556
54.9M
{
557
54.9M
    fixed px, py, t;
558
54.9M
    double xtemp, ytemp;
559
54.9M
    int code;
560
561
54.9M
    if ((code = CHECK_DFMUL2FIXED_VARS(px, dx, pmat->xx, xtemp)) < 0 ||
562
54.8M
        (code = CHECK_DFMUL2FIXED_VARS(py, dy, pmat->yy, ytemp)) < 0
563
54.9M
        )
564
157k
        return code;
565
54.8M
    FINISH_DFMUL2FIXED_VARS(px, xtemp);
566
54.8M
    FINISH_DFMUL2FIXED_VARS(py, ytemp);
567
54.8M
    if (!is_fzero(pmat->yx)) {
568
2.06M
        if ((code = CHECK_DFMUL2FIXED_VARS(t, dy, pmat->yx, ytemp)) < 0)
569
9.46k
            return code;
570
2.05M
        FINISH_DFMUL2FIXED_VARS(t, ytemp);
571
2.05M
        if ((code = CHECK_SET_FIXED_SUM(px, px, t)) < 0)
572
12
            return code;
573
2.05M
    }
574
54.8M
    if (!is_fzero(pmat->xy)) {
575
1.89M
        if ((code = CHECK_DFMUL2FIXED_VARS(t, dx, pmat->xy, xtemp)) < 0)
576
10.6k
            return code;
577
1.88M
        FINISH_DFMUL2FIXED_VARS(t, xtemp);
578
1.88M
        if ((code = CHECK_SET_FIXED_SUM(py, py, t)) < 0)
579
0
            return code;
580
1.88M
    }
581
54.8M
    ppt->x = px;
582
54.8M
    ppt->y = py;
583
54.8M
    return 0;
584
54.8M
}
585
586
/* ------ Serialization ------ */
587
588
/*
589
 * For maximum conciseness in band lists, we write a matrix as a control
590
 * byte followed by 0 to 6 values.  The control byte has the format
591
 * AABBCD00.  AA and BB control (xx,yy) and (xy,yx) as follows:
592
 *  00 = values are (0.0, 0.0)
593
 *  01 = values are (V, V) [1 value follows]
594
 *  10 = values are (V, -V) [1 value follows]
595
 *  11 = values are (U, V) [2 values follow]
596
 * C and D control tx and ty as follows:
597
 *  0 = value is 0.0
598
 *  1 = value follows
599
 * The following code is the only place that knows this representation.
600
 */
601
602
/* Put a matrix on a stream. */
603
int
604
sput_matrix(stream *s, const gs_matrix *pmat)
605
10.8M
{
606
10.8M
    byte buf[1 + 6 * sizeof(float)];
607
10.8M
    byte *cp = buf + 1;
608
10.8M
    byte b = 0;
609
10.8M
    float coeff[6];
610
10.8M
    int i;
611
10.8M
    uint ignore;
612
613
10.8M
    coeff[0] = pmat->xx;
614
10.8M
    coeff[1] = pmat->xy;
615
10.8M
    coeff[2] = pmat->yx;
616
10.8M
    coeff[3] = pmat->yy;
617
10.8M
    coeff[4] = pmat->tx;
618
10.8M
    coeff[5] = pmat->ty;
619
32.4M
    for (i = 0; i < 4; i += 2) {
620
21.6M
        float u = coeff[i], v = coeff[i ^ 3];
621
622
21.6M
        b <<= 2;
623
21.6M
        if (u != 0 || v != 0) {
624
16.8M
            memcpy(cp, &u, sizeof(float));
625
16.8M
            cp += sizeof(float);
626
627
16.8M
            if (v == u)
628
462k
                b += 1;
629
16.3M
            else if (v == -u)
630
3.09M
                b += 2;
631
13.2M
            else {
632
13.2M
                b += 3;
633
13.2M
                memcpy(cp, &v, sizeof(float));
634
13.2M
                cp += sizeof(float);
635
13.2M
            }
636
16.8M
        }
637
21.6M
    }
638
32.4M
    for (; i < 6; ++i) {
639
21.6M
        float v = coeff[i];
640
641
21.6M
        b <<= 1;
642
21.6M
        if (v != 0) {
643
20.6M
            ++b;
644
20.6M
            memcpy(cp, &v, sizeof(float));
645
20.6M
            cp += sizeof(float);
646
20.6M
        }
647
21.6M
    }
648
10.8M
    buf[0] = b << 2;
649
10.8M
    return sputs(s, buf, cp - buf, &ignore);
650
10.8M
}
651
652
/* Get a matrix from a stream. */
653
int
654
sget_matrix(stream *s, gs_matrix *pmat)
655
11.3M
{
656
11.3M
    int b = sgetc(s);
657
11.3M
    float coeff[6];
658
11.3M
    int i;
659
11.3M
    int status;
660
11.3M
    uint nread;
661
662
11.3M
    if (b < 0)
663
0
        return b;
664
34.1M
    for (i = 0; i < 4; i += 2, b <<= 2)
665
22.7M
        if (!(b & 0xc0))
666
8.37M
            coeff[i] = coeff[i ^ 3] = 0.0;
667
14.3M
        else {
668
14.3M
            float value;
669
670
14.3M
            status = sgets(s, (byte *)&value, sizeof(value), &nread);
671
14.3M
            if (status < 0 && status != EOFC)
672
0
                return_error(gs_error_ioerror);
673
14.3M
            coeff[i] = value;
674
14.3M
            switch ((b >> 6) & 3) {
675
946k
                case 1:
676
946k
                    coeff[i ^ 3] = value;
677
946k
                    break;
678
5.81M
                case 2:
679
5.81M
                    coeff[i ^ 3] = -value;
680
5.81M
                    break;
681
7.61M
                case 3:
682
7.61M
                    status = sgets(s, (byte *)&coeff[i ^ 3],
683
7.61M
                                   sizeof(coeff[0]), &nread);
684
7.61M
                    if (status < 0 && status != EOFC)
685
0
                        return_error(gs_error_ioerror);
686
14.3M
            }
687
14.3M
        }
688
34.1M
    for (; i < 6; ++i, b <<= 1)
689
22.7M
        if (b & 0x80) {
690
19.2M
            status = sgets(s, (byte *)&coeff[i], sizeof(coeff[0]), &nread);
691
19.2M
            if (status < 0 && status != EOFC)
692
0
                return_error(gs_error_ioerror);
693
19.2M
        } else
694
3.48M
            coeff[i] = 0.0;
695
11.3M
    pmat->xx = coeff[0];
696
11.3M
    pmat->xy = coeff[1];
697
11.3M
    pmat->yx = coeff[2];
698
11.3M
    pmat->yy = coeff[3];
699
11.3M
    pmat->tx = coeff[4];
700
11.3M
    pmat->ty = coeff[5];
701
11.3M
    return 0;
702
11.3M
}
703
704
/* Compare two matrices */
705
int
706
13.6M
gs_matrix_compare(const gs_matrix *pmat1, const gs_matrix *pmat2) {
707
13.6M
  if (pmat1->xx != pmat2->xx)
708
162k
    return(1);
709
13.5M
  if (pmat1->xy != pmat2->xy)
710
1.61k
    return(1);
711
13.5M
  if (pmat1->yx != pmat2->yx)
712
126
    return(1);
713
13.5M
  if (pmat1->yy != pmat2->yy)
714
6.76k
    return(1);
715
13.5M
  if (pmat1->tx != pmat2->tx)
716
10.5M
    return(1);
717
2.95M
  if (pmat1->ty != pmat2->ty)
718
58.1k
    return(1);
719
2.90M
  return(0);
720
2.95M
}