1 /* Copyright (C) 1989, 1995, 1996, 1998, 1999 artofcode LLC.  All rights reserved.
2 
3   This program is free software; you can redistribute it and/or modify it
4   under the terms of the GNU General Public License as published by the
5   Free Software Foundation; either version 2 of the License, or (at your
6   option) any later version.
7 
8   This program is distributed in the hope that it will be useful, but
9   WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   General Public License for more details.
12 
13   You should have received a copy of the GNU General Public License along
14   with this program; if not, write to the Free Software Foundation, Inc.,
15   59 Temple Place, Suite 330, Boston, MA, 02111-1307.
16 
17 */
18 
19 /*$Id: gsmatrix.c,v 1.2.6.1.2.1 2003/01/17 00:49:03 giles Exp $ */
20 /* Matrix operators for Ghostscript library */
21 #include "math_.h"
22 #include "memory_.h"
23 #include "gx.h"
24 #include "gserrors.h"
25 #include "gxfarith.h"
26 #include "gxfixed.h"
27 #include "gxmatrix.h"
28 #include "stream.h"
29 
30 /* The identity matrix */
31 private const gs_matrix gs_identity_matrix =
32 {identity_matrix_body};
33 
34 /* ------ Matrix creation ------ */
35 
36 /* Create an identity matrix */
37 void
gs_make_identity(gs_matrix * pmat)38 gs_make_identity(gs_matrix * pmat)
39 {
40     *pmat = gs_identity_matrix;
41 }
42 
43 /* Create a translation matrix */
44 int
gs_make_translation(floatp dx,floatp dy,gs_matrix * pmat)45 gs_make_translation(floatp dx, floatp dy, gs_matrix * pmat)
46 {
47     *pmat = gs_identity_matrix;
48     pmat->tx = dx;
49     pmat->ty = dy;
50     return 0;
51 }
52 
53 /* Create a scaling matrix */
54 int
gs_make_scaling(floatp sx,floatp sy,gs_matrix * pmat)55 gs_make_scaling(floatp sx, floatp sy, gs_matrix * pmat)
56 {
57     *pmat = gs_identity_matrix;
58     pmat->xx = sx;
59     pmat->yy = sy;
60     return 0;
61 }
62 
63 /* Create a rotation matrix. */
64 /* The angle is in degrees. */
65 int
gs_make_rotation(floatp ang,gs_matrix * pmat)66 gs_make_rotation(floatp ang, gs_matrix * pmat)
67 {
68     gs_sincos_t sincos;
69 
70     gs_sincos_degrees(ang, &sincos);
71     pmat->yy = pmat->xx = sincos.cos;
72     pmat->xy = sincos.sin;
73     pmat->yx = -sincos.sin;
74     pmat->tx = pmat->ty = 0.0;
75     return 0;
76 }
77 
78 /* ------ Matrix arithmetic ------ */
79 
80 /* Multiply two matrices.  We should check for floating exceptions, */
81 /* but for the moment it's just too awkward. */
82 /* Since this is used heavily, we check for shortcuts. */
83 int
gs_matrix_multiply(const gs_matrix * pm1,const gs_matrix * pm2,gs_matrix * pmr)84 gs_matrix_multiply(const gs_matrix * pm1, const gs_matrix * pm2, gs_matrix * pmr)
85 {
86     double xx1 = pm1->xx, yy1 = pm1->yy;
87     double tx1 = pm1->tx, ty1 = pm1->ty;
88     double xx2 = pm2->xx, yy2 = pm2->yy;
89     double xy2 = pm2->xy, yx2 = pm2->yx;
90 
91     if (is_xxyy(pm1)) {
92 	pmr->tx = tx1 * xx2 + pm2->tx;
93 	pmr->ty = ty1 * yy2 + pm2->ty;
94 	if (is_fzero(xy2))
95 	    pmr->xy = 0;
96 	else
97 	    pmr->xy = xx1 * xy2,
98 		pmr->ty += tx1 * xy2;
99 	pmr->xx = xx1 * xx2;
100 	if (is_fzero(yx2))
101 	    pmr->yx = 0;
102 	else
103 	    pmr->yx = yy1 * yx2,
104 		pmr->tx += ty1 * yx2;
105 	pmr->yy = yy1 * yy2;
106     } else {
107 	double xy1 = pm1->xy, yx1 = pm1->yx;
108 
109 	pmr->xx = xx1 * xx2 + xy1 * yx2;
110 	pmr->xy = xx1 * xy2 + xy1 * yy2;
111 	pmr->yy = yx1 * xy2 + yy1 * yy2;
112 	pmr->yx = yx1 * xx2 + yy1 * yx2;
113 	pmr->tx = tx1 * xx2 + ty1 * yx2 + pm2->tx;
114 	pmr->ty = tx1 * xy2 + ty1 * yy2 + pm2->ty;
115     }
116     return 0;
117 }
118 
119 /* Invert a matrix.  Return gs_error_undefinedresult if not invertible. */
120 int
gs_matrix_invert(const gs_matrix * pm,gs_matrix * pmr)121 gs_matrix_invert(const gs_matrix * pm, gs_matrix * pmr)
122 {				/* We have to be careful about fetch/store order, */
123     /* because pm might be the same as pmr. */
124     if (is_xxyy(pm)) {
125 	if (is_fzero(pm->xx) || is_fzero(pm->yy))
126 	    return_error(gs_error_undefinedresult);
127 	pmr->tx = -(pmr->xx = 1.0 / pm->xx) * pm->tx;
128 	pmr->xy = 0.0;
129 	pmr->yx = 0.0;
130 	pmr->ty = -(pmr->yy = 1.0 / pm->yy) * pm->ty;
131     } else {
132 	double det = pm->xx * pm->yy - pm->xy * pm->yx;
133 	double mxx = pm->xx, mtx = pm->tx;
134 
135 	if (det == 0)
136 	    return_error(gs_error_undefinedresult);
137 	pmr->xx = pm->yy / det;
138 	pmr->xy = -pm->xy / det;
139 	pmr->yx = -pm->yx / det;
140 	pmr->yy = mxx / det;	/* xx is already changed */
141 	pmr->tx = -(mtx * pmr->xx + pm->ty * pmr->yx);
142 	pmr->ty = -(mtx * pmr->xy + pm->ty * pmr->yy);	/* tx ditto */
143     }
144     return 0;
145 }
146 
147 /* Translate a matrix, possibly in place. */
148 int
gs_matrix_translate(const gs_matrix * pm,floatp dx,floatp dy,gs_matrix * pmr)149 gs_matrix_translate(const gs_matrix * pm, floatp dx, floatp dy, gs_matrix * pmr)
150 {
151     gs_point trans;
152     int code = gs_distance_transform(dx, dy, pm, &trans);
153 
154     if (code < 0)
155 	return code;
156     if (pmr != pm)
157 	*pmr = *pm;
158     pmr->tx += trans.x;
159     pmr->ty += trans.y;
160     return 0;
161 }
162 
163 /* Scale a matrix, possibly in place. */
164 int
gs_matrix_scale(const gs_matrix * pm,floatp sx,floatp sy,gs_matrix * pmr)165 gs_matrix_scale(const gs_matrix * pm, floatp sx, floatp sy, gs_matrix * pmr)
166 {
167     pmr->xx = pm->xx * sx;
168     pmr->xy = pm->xy * sx;
169     pmr->yx = pm->yx * sy;
170     pmr->yy = pm->yy * sy;
171     if (pmr != pm) {
172 	pmr->tx = pm->tx;
173 	pmr->ty = pm->ty;
174     }
175     return 0;
176 }
177 
178 /* Rotate a matrix, possibly in place.  The angle is in degrees. */
179 int
gs_matrix_rotate(const gs_matrix * pm,floatp ang,gs_matrix * pmr)180 gs_matrix_rotate(const gs_matrix * pm, floatp ang, gs_matrix * pmr)
181 {
182     double mxx, mxy;
183     gs_sincos_t sincos;
184 
185     gs_sincos_degrees(ang, &sincos);
186     mxx = pm->xx, mxy = pm->xy;
187     pmr->xx = sincos.cos * mxx + sincos.sin * pm->yx;
188     pmr->xy = sincos.cos * mxy + sincos.sin * pm->yy;
189     pmr->yx = sincos.cos * pm->yx - sincos.sin * mxx;
190     pmr->yy = sincos.cos * pm->yy - sincos.sin * mxy;
191     if (pmr != pm) {
192 	pmr->tx = pm->tx;
193 	pmr->ty = pm->ty;
194     }
195     return 0;
196 }
197 
198 /* ------ Coordinate transformations (floating point) ------ */
199 
200 /* Note that all the transformation routines take separate */
201 /* x and y arguments, but return their result in a point. */
202 
203 /* Transform a point. */
204 int
gs_point_transform(floatp x,floatp y,const gs_matrix * pmat,gs_point * ppt)205 gs_point_transform(floatp x, floatp y, const gs_matrix * pmat,
206 		   gs_point * ppt)
207 {
208     ppt->x = x * pmat->xx + pmat->tx;
209     ppt->y = y * pmat->yy + pmat->ty;
210     if (!is_fzero(pmat->yx))
211 	ppt->x += y * pmat->yx;
212     if (!is_fzero(pmat->xy))
213 	ppt->y += x * pmat->xy;
214     return 0;
215 }
216 
217 /* Inverse-transform a point. */
218 /* Return gs_error_undefinedresult if the matrix is not invertible. */
219 int
gs_point_transform_inverse(floatp x,floatp y,const gs_matrix * pmat,gs_point * ppt)220 gs_point_transform_inverse(floatp x, floatp y, const gs_matrix * pmat,
221 			   gs_point * ppt)
222 {
223     if (is_xxyy(pmat)) {
224 	if (is_fzero(pmat->xx) || is_fzero(pmat->yy))
225 	    return_error(gs_error_undefinedresult);
226 	ppt->x = (x - pmat->tx) / pmat->xx;
227 	ppt->y = (y - pmat->ty) / pmat->yy;
228 	return 0;
229     } else if (is_xyyx(pmat)) {
230 	if (is_fzero(pmat->xy) || is_fzero(pmat->yx))
231 	    return_error(gs_error_undefinedresult);
232 	ppt->x = (y - pmat->ty) / pmat->xy;
233 	ppt->y = (x - pmat->tx) / pmat->yx;
234 	return 0;
235     } else {			/* There are faster ways to do this, */
236 	/* but we won't implement one unless we have to. */
237 	gs_matrix imat;
238 	int code = gs_matrix_invert(pmat, &imat);
239 
240 	if (code < 0)
241 	    return code;
242 	return gs_point_transform(x, y, &imat, ppt);
243     }
244 }
245 
246 /* Transform a distance. */
247 int
gs_distance_transform(floatp dx,floatp dy,const gs_matrix * pmat,gs_point * pdpt)248 gs_distance_transform(floatp dx, floatp dy, const gs_matrix * pmat,
249 		      gs_point * pdpt)
250 {
251     pdpt->x = dx * pmat->xx;
252     pdpt->y = dy * pmat->yy;
253     if (!is_fzero(pmat->yx))
254 	pdpt->x += dy * pmat->yx;
255     if (!is_fzero(pmat->xy))
256 	pdpt->y += dx * pmat->xy;
257     return 0;
258 }
259 
260 /* Inverse-transform a distance. */
261 /* Return gs_error_undefinedresult if the matrix is not invertible. */
262 int
gs_distance_transform_inverse(floatp dx,floatp dy,const gs_matrix * pmat,gs_point * pdpt)263 gs_distance_transform_inverse(floatp dx, floatp dy,
264 			      const gs_matrix * pmat, gs_point * pdpt)
265 {
266     if (is_xxyy(pmat)) {
267 	if (is_fzero(pmat->xx) || is_fzero(pmat->yy))
268 	    return_error(gs_error_undefinedresult);
269 	pdpt->x = dx / pmat->xx;
270 	pdpt->y = dy / pmat->yy;
271     } else if (is_xyyx(pmat)) {
272 	if (is_fzero(pmat->xy) || is_fzero(pmat->yx))
273 	    return_error(gs_error_undefinedresult);
274 	pdpt->x = dy / pmat->xy;
275 	pdpt->y = dx / pmat->yx;
276     } else {
277 	double det = pmat->xx * pmat->yy - pmat->xy * pmat->yx;
278 
279 	if (det == 0)
280 	    return_error(gs_error_undefinedresult);
281 	pdpt->x = (dx * pmat->yy - dy * pmat->yx) / det;
282 	pdpt->y = (dy * pmat->xx - dx * pmat->xy) / det;
283     }
284     return 0;
285 }
286 
287 /* Compute the bounding box of 4 points. */
288 int
gs_points_bbox(const gs_point pts[4],gs_rect * pbox)289 gs_points_bbox(const gs_point pts[4], gs_rect * pbox)
290 {
291 #define assign_min_max(vmin, vmax, v0, v1)\
292   if ( v0 < v1 ) vmin = v0, vmax = v1; else vmin = v1, vmax = v0
293 #define assign_min_max_4(vmin, vmax, v0, v1, v2, v3)\
294   { double min01, max01, min23, max23;\
295     assign_min_max(min01, max01, v0, v1);\
296     assign_min_max(min23, max23, v2, v3);\
297     vmin = min(min01, min23);\
298     vmax = max(max01, max23);\
299   }
300     assign_min_max_4(pbox->p.x, pbox->q.x,
301 		     pts[0].x, pts[1].x, pts[2].x, pts[3].x);
302     assign_min_max_4(pbox->p.y, pbox->q.y,
303 		     pts[0].y, pts[1].y, pts[2].y, pts[3].y);
304 #undef assign_min_max
305 #undef assign_min_max_4
306     return 0;
307 }
308 
309 /* Transform or inverse-transform a bounding box. */
310 /* Return gs_error_undefinedresult if the matrix is not invertible. */
311 private int
bbox_transform_either_only(const gs_rect * pbox_in,const gs_matrix * pmat,gs_point pts[4],int (* point_xform)(P4 (floatp,floatp,const gs_matrix *,gs_point *)))312 bbox_transform_either_only(const gs_rect * pbox_in, const gs_matrix * pmat,
313 			   gs_point pts[4],
314      int (*point_xform) (P4(floatp, floatp, const gs_matrix *, gs_point *)))
315 {
316     int code;
317 
318     if ((code = (*point_xform) (pbox_in->p.x, pbox_in->p.y, pmat, &pts[0])) < 0 ||
319 	(code = (*point_xform) (pbox_in->p.x, pbox_in->q.y, pmat, &pts[1])) < 0 ||
320 	(code = (*point_xform) (pbox_in->q.x, pbox_in->p.y, pmat, &pts[2])) < 0 ||
321      (code = (*point_xform) (pbox_in->q.x, pbox_in->q.y, pmat, &pts[3])) < 0
322 	)
323 	DO_NOTHING;
324     return code;
325 }
326 
327 private int
bbox_transform_either(const gs_rect * pbox_in,const gs_matrix * pmat,gs_rect * pbox_out,int (* point_xform)(P4 (floatp,floatp,const gs_matrix *,gs_point *)))328 bbox_transform_either(const gs_rect * pbox_in, const gs_matrix * pmat,
329 		      gs_rect * pbox_out,
330      int (*point_xform) (P4(floatp, floatp, const gs_matrix *, gs_point *)))
331 {
332     int code;
333 
334     /*
335      * In principle, we could transform only one point and two
336      * distance vectors; however, because of rounding, we will only
337      * get fully consistent results if we transform all 4 points.
338      * We must compute the max and min after transforming,
339      * since a rotation may be involved.
340      */
341     gs_point pts[4];
342 
343     if ((code = bbox_transform_either_only(pbox_in, pmat, pts, point_xform)) < 0)
344 	return code;
345     return gs_points_bbox(pts, pbox_out);
346 }
347 int
gs_bbox_transform(const gs_rect * pbox_in,const gs_matrix * pmat,gs_rect * pbox_out)348 gs_bbox_transform(const gs_rect * pbox_in, const gs_matrix * pmat,
349 		  gs_rect * pbox_out)
350 {
351     return bbox_transform_either(pbox_in, pmat, pbox_out,
352 				 gs_point_transform);
353 }
354 int
gs_bbox_transform_only(const gs_rect * pbox_in,const gs_matrix * pmat,gs_point points[4])355 gs_bbox_transform_only(const gs_rect * pbox_in, const gs_matrix * pmat,
356 		       gs_point points[4])
357 {
358     return bbox_transform_either_only(pbox_in, pmat, points,
359 				      gs_point_transform);
360 }
361 int
gs_bbox_transform_inverse(const gs_rect * pbox_in,const gs_matrix * pmat,gs_rect * pbox_out)362 gs_bbox_transform_inverse(const gs_rect * pbox_in, const gs_matrix * pmat,
363 			  gs_rect * pbox_out)
364 {
365     return bbox_transform_either(pbox_in, pmat, pbox_out,
366 				 gs_point_transform_inverse);
367 }
368 
369 /* ------ Coordinate transformations (to fixed point) ------ */
370 
371 #define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
372 
373 /* Make a gs_matrix_fixed from a gs_matrix. */
374 int
gs_matrix_fixed_from_matrix(gs_matrix_fixed * pfmat,const gs_matrix * pmat)375 gs_matrix_fixed_from_matrix(gs_matrix_fixed *pfmat, const gs_matrix *pmat)
376 {
377     *(gs_matrix *)pfmat = *pmat;
378     if (f_fits_in_fixed(pmat->tx) && f_fits_in_fixed(pmat->ty)) {
379 	pfmat->tx = fixed2float(pfmat->tx_fixed = float2fixed(pmat->tx));
380 	pfmat->ty = fixed2float(pfmat->ty_fixed = float2fixed(pmat->ty));
381 	pfmat->txy_fixed_valid = true;
382     } else {
383 	pfmat->txy_fixed_valid = false;
384     }
385     return 0;
386 }
387 
388 /* Transform a point with a fixed-point result. */
389 int
gs_point_transform2fixed(const gs_matrix_fixed * pmat,floatp x,floatp y,gs_fixed_point * ppt)390 gs_point_transform2fixed(const gs_matrix_fixed * pmat,
391 			 floatp x, floatp y, gs_fixed_point * ppt)
392 {
393     fixed px, py, t;
394     double xtemp, ytemp;
395     int code;
396 
397     if (!pmat->txy_fixed_valid) {	/* The translation is out of range.  Do the */
398 	/* computation in floating point, and convert to */
399 	/* fixed at the end. */
400 	gs_point fpt;
401 
402 	gs_point_transform(x, y, (const gs_matrix *)pmat, &fpt);
403 	if (!(f_fits_in_fixed(fpt.x) && f_fits_in_fixed(fpt.y)))
404 	    return_error(gs_error_limitcheck);
405 	ppt->x = float2fixed(fpt.x);
406 	ppt->y = float2fixed(fpt.y);
407 	return 0;
408     }
409     if (!is_fzero(pmat->xy)) {	/* Hope for 90 degree rotation */
410 	if ((code = CHECK_DFMUL2FIXED_VARS(px, y, pmat->yx, xtemp)) < 0 ||
411 	    (code = CHECK_DFMUL2FIXED_VARS(py, x, pmat->xy, ytemp)) < 0
412 	    )
413 	    return code;
414 	FINISH_DFMUL2FIXED_VARS(px, xtemp);
415 	FINISH_DFMUL2FIXED_VARS(py, ytemp);
416 	if (!is_fzero(pmat->xx)) {
417 	    if ((code = CHECK_DFMUL2FIXED_VARS(t, x, pmat->xx, xtemp)) < 0)
418 		return code;
419 	    FINISH_DFMUL2FIXED_VARS(t, xtemp);
420 	    px += t;		/* should check for overflow */
421 	}
422 	if (!is_fzero(pmat->yy)) {
423 	    if ((code = CHECK_DFMUL2FIXED_VARS(t, y, pmat->yy, ytemp)) < 0)
424 		return code;
425 	    FINISH_DFMUL2FIXED_VARS(t, ytemp);
426 	    py += t;		/* should check for overflow */
427 	}
428     } else {
429 	if ((code = CHECK_DFMUL2FIXED_VARS(px, x, pmat->xx, xtemp)) < 0 ||
430 	    (code = CHECK_DFMUL2FIXED_VARS(py, y, pmat->yy, ytemp)) < 0
431 	    )
432 	    return code;
433 	FINISH_DFMUL2FIXED_VARS(px, xtemp);
434 	FINISH_DFMUL2FIXED_VARS(py, ytemp);
435 	if (!is_fzero(pmat->yx)) {
436 	    if ((code = CHECK_DFMUL2FIXED_VARS(t, y, pmat->yx, ytemp)) < 0)
437 		return code;
438 	    FINISH_DFMUL2FIXED_VARS(t, ytemp);
439 	    px += t;		/* should check for overflow */
440 	}
441     }
442     ppt->x = px + pmat->tx_fixed;	/* should check for overflow */
443     ppt->y = py + pmat->ty_fixed;	/* should check for overflow */
444     return 0;
445 }
446 
447 /* Transform a distance with a fixed-point result. */
448 int
gs_distance_transform2fixed(const gs_matrix_fixed * pmat,floatp dx,floatp dy,gs_fixed_point * ppt)449 gs_distance_transform2fixed(const gs_matrix_fixed * pmat,
450 			    floatp dx, floatp dy, gs_fixed_point * ppt)
451 {
452     fixed px, py, t;
453     double xtemp, ytemp;
454     int code;
455 
456     if ((code = CHECK_DFMUL2FIXED_VARS(px, dx, pmat->xx, xtemp)) < 0 ||
457 	(code = CHECK_DFMUL2FIXED_VARS(py, dy, pmat->yy, ytemp)) < 0
458 	)
459 	return code;
460     FINISH_DFMUL2FIXED_VARS(px, xtemp);
461     FINISH_DFMUL2FIXED_VARS(py, ytemp);
462     if (!is_fzero(pmat->yx)) {
463 	if ((code = CHECK_DFMUL2FIXED_VARS(t, dy, pmat->yx, ytemp)) < 0)
464 	    return code;
465 	FINISH_DFMUL2FIXED_VARS(t, ytemp);
466 	px += t;		/* should check for overflow */
467     }
468     if (!is_fzero(pmat->xy)) {
469 	if ((code = CHECK_DFMUL2FIXED_VARS(t, dx, pmat->xy, xtemp)) < 0)
470 	    return code;
471 	FINISH_DFMUL2FIXED_VARS(t, xtemp);
472 	py += t;		/* should check for overflow */
473     }
474     ppt->x = px;
475     ppt->y = py;
476     return 0;
477 }
478 
479 /* ------ Serialization ------ */
480 
481 /*
482  * For maximum conciseness in band lists, we write a matrix as a control
483  * byte followed by 0 to 6 values.  The control byte has the format
484  * AABBCD00.  AA and BB control (xx,yy) and (xy,yx) as follows:
485  *	00 = values are (0.0, 0.0)
486  *	01 = values are (V, V) [1 value follows]
487  *	10 = values are (V, -V) [1 value follows]
488  *	11 = values are (U, V) [2 values follow]
489  * C and D control tx and ty as follows:
490  *	0 = value is 0.0
491  *	1 = value follows
492  * The following code is the only place that knows this representation.
493  */
494 
495 /* Put a matrix on a stream. */
496 int
sput_matrix(stream * s,const gs_matrix * pmat)497 sput_matrix(stream *s, const gs_matrix *pmat)
498 {
499     byte buf[1 + 6 * sizeof(float)];
500     byte *cp = buf + 1;
501     byte b = 0;
502     float coeff[6];
503     int i;
504     uint ignore;
505 
506     coeff[0] = pmat->xx;
507     coeff[1] = pmat->xy;
508     coeff[2] = pmat->yx;
509     coeff[3] = pmat->yy;
510     coeff[4] = pmat->tx;
511     coeff[5] = pmat->ty;
512     for (i = 0; i < 4; i += 2) {
513 	float u = coeff[i], v = coeff[i ^ 3];
514 
515 	b <<= 2;
516 	if (u != 0 || v != 0) {
517 	    memcpy(cp, &u, sizeof(float));
518 	    cp += sizeof(float);
519 
520 	    if (v == u)
521 		b += 1;
522 	    else if (v == -u)
523 		b += 2;
524 	    else {
525 		b += 3;
526 		memcpy(cp, &v, sizeof(float));
527 		cp += sizeof(float);
528 	    }
529 	}
530     }
531     for (; i < 6; ++i) {
532 	float v = coeff[i];
533 
534 	b <<= 1;
535 	if (v != 0) {
536 	    ++b;
537 	    memcpy(cp, &v, sizeof(float));
538 	    cp += sizeof(float);
539 	}
540     }
541     buf[0] = b << 2;
542     return sputs(s, buf, cp - buf, &ignore);
543 }
544 
545 /* Get a matrix from a stream. */
546 int
sget_matrix(stream * s,gs_matrix * pmat)547 sget_matrix(stream *s, gs_matrix *pmat)
548 {
549     int b = sgetc(s);
550     float coeff[6];
551     int i;
552     int status;
553     uint nread;
554 
555     if (b < 0)
556 	return b;
557     for (i = 0; i < 4; i += 2, b <<= 2)
558 	if (!(b & 0xc0))
559 	    coeff[i] = coeff[i ^ 3] = 0.0;
560 	else {
561 	    float value;
562 
563 	    status = sgets(s, (byte *)&value, sizeof(value), &nread);
564 	    if (status < 0)
565 		return status;
566 	    coeff[i] = value;
567 	    switch ((b >> 6) & 3) {
568 		case 1:
569 		    coeff[i ^ 3] = value;
570 		    break;
571 		case 2:
572 		    coeff[i ^ 3] = -value;
573 		    break;
574 		case 3:
575 		    status = sgets(s, (byte *)&coeff[i ^ 3],
576 				   sizeof(coeff[0]), &nread);
577 		    if (status < 0)
578 			return status;
579 	    }
580 	}
581     for (; i < 6; ++i, b <<= 1)
582 	if (b & 0x80) {
583 	    status = sgets(s, (byte *)&coeff[i], sizeof(coeff[0]), &nread);
584 	    if (status < 0)
585 		return status;
586 	} else
587 	    coeff[i] = 0.0;
588     pmat->xx = coeff[0];
589     pmat->xy = coeff[1];
590     pmat->yx = coeff[2];
591     pmat->yy = coeff[3];
592     pmat->tx = coeff[4];
593     pmat->ty = coeff[5];
594     return 0;
595 }
596