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