Commit | Line | Data |
---|---|---|
e4d7ed6d WJ |
1 | /* Copyright (C) 1989, 1990, 1991 Aladdin Enterprises. All rights reserved. |
2 | Distributed by Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of Ghostscript. | |
5 | ||
6 | Ghostscript is distributed in the hope that it will be useful, but | |
7 | WITHOUT ANY WARRANTY. No author or distributor accepts responsibility | |
8 | to anyone for the consequences of using it or for whether it serves any | |
9 | particular purpose or works at all, unless he says so in writing. Refer | |
10 | to the Ghostscript General Public License for full details. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | Ghostscript, but only under the conditions described in the Ghostscript | |
14 | General Public License. A copy of this license is supposed to have been | |
15 | given to you along with Ghostscript so you can know your rights and | |
16 | responsibilities. It should be in a file named COPYING. Among other | |
17 | things, the copyright notice and this notice must be preserved on all | |
18 | copies. */ | |
19 | ||
20 | /* gsmatrix.c */ | |
21 | /* Matrix operators for GhostScript library */ | |
22 | #include "math_.h" | |
23 | #include "gx.h" | |
24 | #include "gserrors.h" | |
25 | #include "gxfixed.h" | |
26 | #include "gxarith.h" | |
27 | #include "gxmatrix.h" | |
28 | ||
29 | /* The identity matrix */ | |
30 | /* This should be private (static), but the interpreter */ | |
31 | /* has to be able to fill in the "unused" words. */ | |
32 | /*static*/ gs_matrix gs_identity_matrix = | |
33 | { identity_matrix_body }; | |
34 | ||
35 | /* ------ Matrix creation ------ */ | |
36 | ||
37 | /* Create an identity matrix */ | |
38 | void | |
39 | gs_make_identity(gs_matrix *pmat) | |
40 | { *pmat = gs_identity_matrix; | |
41 | } | |
42 | ||
43 | /* Create a translation matrix */ | |
44 | int | |
45 | gs_make_translation(floatp dx, floatp dy, register gs_matrix *pmat) | |
46 | { *pmat = gs_identity_matrix; | |
47 | pmat->tx = dx; | |
48 | pmat->ty = dy; | |
49 | return 0; | |
50 | } | |
51 | ||
52 | /* Create a scaling matrix */ | |
53 | int | |
54 | gs_make_scaling(floatp sx, floatp sy, register gs_matrix *pmat) | |
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, register gs_matrix *pmat) | |
65 | { float theta = ang * (M_PI / 180.0); | |
66 | *pmat = gs_identity_matrix; | |
67 | pmat->xx = pmat->yy = cos(theta); | |
68 | pmat->yx = -(pmat->xy = sin(theta)); | |
69 | return 0; | |
70 | } | |
71 | ||
72 | /* ------ Matrix arithmetic ------ */ | |
73 | ||
74 | /* Multiply two matrices. We should check for floating exceptions, */ | |
75 | /* but for the moment it's just too awkward. */ | |
76 | /* Since this is used heavily, we check for shortcuts. */ | |
77 | int | |
78 | gs_matrix_multiply(const gs_matrix *pm1, const gs_matrix *pm2, register gs_matrix *pmr) | |
79 | { float xx1 = pm1->xx, yy1 = pm1->yy; | |
80 | float tx1 = pm1->tx, ty1 = pm1->ty; | |
81 | float xx2 = pm2->xx, yy2 = pm2->yy; | |
82 | float xy2 = pm2->xy, yx2 = pm2->yx; | |
83 | if ( !is_skewed(pm1) ) | |
84 | { pmr->tx = tx1 * xx2 + pm2->tx; | |
85 | pmr->ty = ty1 * yy2 + pm2->ty; | |
86 | if ( is_fzero(xy2) ) | |
87 | pmr->xy = 0; | |
88 | else | |
89 | pmr->xy = xx1 * xy2, | |
90 | pmr->ty += tx1 * xy2; | |
91 | pmr->xx = xx1 * xx2; | |
92 | if ( is_fzero(yx2) ) | |
93 | pmr->yx = 0; | |
94 | else | |
95 | pmr->yx = yy1 * yx2, | |
96 | pmr->tx += ty1 * yx2; | |
97 | pmr->yy = yy1 * yy2; | |
98 | } | |
99 | else | |
100 | { pmr->xx = xx1 * xx2 + pm1->xy * yx2; | |
101 | pmr->xy = xx1 * xy2 + pm1->xy * yy2; | |
102 | pmr->yy = pm1->yx * xy2 + yy1 * yy2; | |
103 | pmr->yx = pm1->yx * xx2 + yy1 * yx2; | |
104 | pmr->tx = tx1 * xx2 + ty1 * yx2 + pm2->tx; | |
105 | pmr->ty = tx1 * xy2 + ty1 * yy2 + pm2->ty; | |
106 | } | |
107 | return 0; | |
108 | } | |
109 | ||
110 | /* Invert a matrix. Return gs_error_undefinedresult if not invertible. */ | |
111 | int | |
112 | gs_matrix_invert(register const gs_matrix *pm, register gs_matrix *pmr) | |
113 | { /* We have to be careful about fetch/store order, */ | |
114 | /* because pm might be the same as pmr. */ | |
115 | if ( !is_skewed(pm) ) | |
116 | { if ( is_fzero(pm->xx) || is_fzero(pm->yy) ) | |
117 | return_error(gs_error_undefinedresult); | |
118 | pmr->tx = - (pmr->xx = 1.0 / pm->xx) * pm->tx; | |
119 | pmr->xy = 0.0; | |
120 | pmr->yx = 0.0; | |
121 | pmr->ty = - (pmr->yy = 1.0 / pm->yy) * pm->ty; | |
122 | } | |
123 | else | |
124 | { double det = pm->xx * pm->yy - pm->xy * pm->yx; | |
125 | float mxx = pm->xx, mtx = pm->tx; | |
126 | if ( det == 0 ) return_error(gs_error_undefinedresult); | |
127 | pmr->xx = pm->yy / det; | |
128 | pmr->xy = - pm->xy / det; | |
129 | pmr->yx = - pm->yx / det; | |
130 | pmr->yy = mxx / det; /* xx is already changed */ | |
131 | pmr->tx = - (mtx * pmr->xx + pm->ty * pmr->yx); | |
132 | pmr->ty = - (mtx * pmr->xy + pm->ty * pmr->yy); /* tx is already changed */ | |
133 | } | |
134 | return 0; | |
135 | } | |
136 | ||
137 | /* Rotate a matrix, possibly in place. The angle is in degrees. */ | |
138 | int | |
139 | gs_matrix_rotate(register const gs_matrix *pm, floatp ang, register gs_matrix *pmr) | |
140 | { float mxx, mxy; | |
141 | int quads; | |
142 | float tsin, tcos; | |
143 | /* We do some special checking for multiples of 90, */ | |
144 | /* so we don't get any rounding errors. */ | |
145 | if ( ang >= -360 && ang <= 360 && | |
146 | ang == (quads = (int)ang / 90) * 90 | |
147 | ) | |
148 | { int isin = 0, icos = 1, t; | |
149 | quads &= 3; | |
150 | while ( quads-- ) t = isin, isin = icos, icos = -t; | |
151 | tsin = isin, tcos = icos; | |
152 | } | |
153 | else | |
154 | { float theta = ang * (M_PI / 180.0); | |
155 | tsin = sin(theta); | |
156 | tcos = cos(theta); | |
157 | } | |
158 | mxx = pm->xx, mxy = pm->xy; | |
159 | pmr->xx = tcos * mxx + tsin * pm->yx; | |
160 | pmr->xy = tcos * mxy + tsin * pm->yy; | |
161 | pmr->yx = tcos * pm->yx - tsin * mxx; | |
162 | pmr->yy = tcos * pm->yy - tsin * mxy; | |
163 | pmr->tx = pm->tx; | |
164 | pmr->ty = pm->ty; | |
165 | return 0; | |
166 | } | |
167 | ||
168 | /* ------ Coordinate transformations (floating point) ------ */ | |
169 | ||
170 | /* Note that all the transformation routines take separate */ | |
171 | /* x and y arguments, but return their result in a point. */ | |
172 | ||
173 | /* Transform a point. */ | |
174 | int | |
175 | gs_point_transform(floatp x, floatp y, register const gs_matrix *pmat, | |
176 | register gs_point *ppt) | |
177 | { ppt->x = x * pmat->xx + pmat->tx; | |
178 | ppt->y = y * pmat->yy + pmat->ty; | |
179 | if ( !is_fzero(pmat->yx) ) | |
180 | ppt->x += y * pmat->yx; | |
181 | if ( !is_fzero(pmat->xy) ) | |
182 | ppt->y += x * pmat->xy; | |
183 | return 0; | |
184 | } | |
185 | ||
186 | /* Inverse-transform a point. */ | |
187 | /* Return gs_error_undefinedresult if the matrix is not invertible. */ | |
188 | int | |
189 | gs_point_transform_inverse(floatp x, floatp y, register const gs_matrix *pmat, | |
190 | register gs_point *ppt) | |
191 | { if ( !is_skewed(pmat) ) | |
192 | { if ( is_fzero(pmat->xx) || is_fzero(pmat->yy) ) | |
193 | return_error(gs_error_undefinedresult); | |
194 | ppt->x = (x - pmat->tx) / pmat->xx; | |
195 | ppt->y = (y - pmat->ty) / pmat->yy; | |
196 | return 0; | |
197 | } | |
198 | else | |
199 | { /* There are faster ways to do this, */ | |
200 | /* but we won't implement one unless we have to. */ | |
201 | gs_matrix imat; | |
202 | int code = gs_matrix_invert(pmat, &imat); | |
203 | if ( code < 0 ) return code; | |
204 | return gs_point_transform(x, y, &imat, ppt); | |
205 | } | |
206 | } | |
207 | ||
208 | /* Transform a distance. */ | |
209 | int | |
210 | gs_distance_transform(floatp dx, floatp dy, register const gs_matrix *pmat, | |
211 | register gs_point *pdpt) | |
212 | { pdpt->x = dx * pmat->xx; | |
213 | pdpt->y = dy * pmat->yy; | |
214 | if ( !is_fzero(pmat->yx) ) | |
215 | pdpt->x += dy * pmat->yx; | |
216 | if ( !is_fzero(pmat->xy) ) | |
217 | pdpt->y += dx * pmat->xy; | |
218 | return 0; | |
219 | } | |
220 | ||
221 | /* Inverse-transform a distance. */ | |
222 | /* Return gs_error_undefinedresult if the matrix is not invertible. */ | |
223 | int | |
224 | gs_distance_transform_inverse(floatp dx, floatp dy, | |
225 | register const gs_matrix *pmat, register gs_point *pdpt) | |
226 | { if ( !is_skewed(pmat) ) | |
227 | { if ( is_fzero(pmat->xx) || is_fzero(pmat->yy) ) | |
228 | return_error(gs_error_undefinedresult); | |
229 | pdpt->x = dx / pmat->xx; | |
230 | pdpt->y = dy / pmat->yy; | |
231 | } | |
232 | else | |
233 | { double det = pmat->xx * pmat->yy - pmat->xy * pmat->yx; | |
234 | if ( det == 0 ) return_error(gs_error_undefinedresult); | |
235 | pdpt->x = (dx * pmat->yy - dy * pmat->yx) / det; | |
236 | pdpt->y = (dy * pmat->xx - dx * pmat->xy) / det; | |
237 | } | |
238 | return 0; | |
239 | } | |
240 | ||
241 | /* Inverse-transform a bounding box. */ | |
242 | /* Return gs_error_undefinedresult if the matrix is not invertible. */ | |
243 | int | |
244 | gs_bbox_transform_inverse(gs_rect *pbox_in, gs_matrix *pmat, | |
245 | gs_rect *pbox_out) | |
246 | { int code; | |
247 | gs_point p, w, h; | |
248 | double xmin, ymin, xmax, ymax; /* gs_point uses double */ | |
249 | /* We must recompute the max and min after transforming, */ | |
250 | /* since a rotation may be involved. */ | |
251 | if ( (code = gs_point_transform_inverse(pbox_in->p.x, pbox_in->p.y, pmat, &p)) < 0 || | |
252 | (code = gs_distance_transform_inverse(pbox_in->q.x - pbox_in->p.x, 0.0, pmat, &w)) < 0 || | |
253 | (code = gs_distance_transform_inverse(0.0, pbox_in->q.y - pbox_in->p.y, pmat, &h)) < 0 | |
254 | ) | |
255 | return code; | |
256 | xmin = xmax = p.x; | |
257 | if ( w.x < 0 ) xmin += w.x; | |
258 | else xmax += w.x; | |
259 | if ( h.x < 0 ) xmin += h.x; | |
260 | else xmax += h.x; | |
261 | ymin = ymax = p.y; | |
262 | if ( w.y < 0 ) ymin += w.y; | |
263 | else ymax += w.y; | |
264 | if ( h.y < 0 ) ymin += h.y; | |
265 | else ymax += h.y; | |
266 | pbox_out->p.x = xmin, pbox_out->p.y = ymin; | |
267 | pbox_out->q.x = xmax, pbox_out->q.y = ymax; | |
268 | return 0; | |
269 | } | |
270 | ||
271 | /* ------ Coordinate transformations (to fixed point) ------ */ | |
272 | ||
273 | /* Transform a point with a fixed-point result. */ | |
274 | int | |
275 | gs_point_transform2fixed(register gs_matrix_fixed *pmat, | |
276 | floatp x, floatp y, gs_fixed_point *ppt) | |
277 | { ppt->x = float2fixed(x * pmat->xx) + pmat->tx_fixed; | |
278 | ppt->y = float2fixed(y * pmat->yy) + pmat->ty_fixed; | |
279 | if ( !is_fzero(pmat->yx) ) | |
280 | ppt->x += float2fixed(y * pmat->yx); | |
281 | if ( !is_fzero(pmat->xy) ) | |
282 | ppt->y += float2fixed(x * pmat->xy); | |
283 | return 0; | |
284 | } | |
285 | ||
286 | /* Transform a distance with a fixed-point result. */ | |
287 | int | |
288 | gs_distance_transform2fixed(register gs_matrix_fixed *pmat, | |
289 | floatp dx, floatp dy, gs_fixed_point *ppt) | |
290 | { ppt->x = float2fixed(dx * pmat->xx); | |
291 | ppt->y = float2fixed(dy * pmat->yy); | |
292 | if ( !is_fzero(pmat->yx) ) | |
293 | ppt->x += float2fixed(dy * pmat->yx); | |
294 | if ( !is_fzero(pmat->xy) ) | |
295 | ppt->y += float2fixed(dx * pmat->xy); | |
296 | return 0; | |
297 | } |