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