Commit | Line | Data |
---|---|---|
6c6cb4be WJ |
1 | /* Copyright (C) 1989, 1992 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 | /* gscoord.c */ | |
21 | /* Coordinate system operators for Ghostscript library */ | |
22 | #include "math_.h" | |
23 | #include "gx.h" | |
24 | #include "gserrors.h" | |
25 | #include "gxarith.h" | |
26 | #include "gxfixed.h" | |
27 | #include "gxmatrix.h" | |
28 | #include "gzstate.h" | |
29 | #include "gzdevice.h" /* requires gsstate */ | |
30 | #include "gscoord.h" /* requires gsmatrix, gsstate */ | |
31 | ||
32 | /* Choose whether to enable the new rounding code in update_ctm_fixed. */ | |
33 | /* I'm pretty sure this is the right thing to do, but since this change */ | |
34 | /* is being made 1 day before releasing version 2.4, I'm feeling cautious. */ | |
35 | #define round_ctm_fixed 1 | |
36 | ||
37 | /* Forward declarations */ | |
38 | #ifdef DEBUG | |
39 | private void trace_ctm(P1(const gs_state *)); | |
40 | private void trace_matrix(P1(const gs_matrix *)); | |
41 | #endif | |
42 | ||
43 | /* Macro for ensuring ctm_inverse is valid */ | |
44 | #ifdef DEBUG | |
45 | #define print_inverse(pgs)\ | |
46 | if ( gs_debug['x'] )\ | |
47 | dprintf("[x]Inverting:\n"), trace_ctm(pgs), trace_matrix(&pgs->ctm_inverse) | |
48 | #else | |
49 | #define print_inverse(pgs) 0 | |
50 | #endif | |
51 | #define ensure_inverse_valid(pgs)\ | |
52 | if ( !pgs->inverse_valid )\ | |
53 | { int code = ctm_set_inverse(pgs);\ | |
54 | if ( code < 0 ) return code;\ | |
55 | } | |
56 | ||
57 | private int | |
58 | ctm_set_inverse(gs_state *pgs) | |
59 | { int code = gs_matrix_invert(&ctm_only(pgs), &pgs->ctm_inverse); | |
60 | print_inverse(pgs); | |
61 | if ( code < 0 ) return code; | |
62 | pgs->inverse_valid = 1; | |
63 | return 0; | |
64 | } | |
65 | ||
66 | /* Machinery for updating fixed version of ctm. */ | |
67 | /* | |
68 | * We (conditionally) adjust the floating point translation | |
69 | * so that it exactly matches the (rounded) fixed translation. | |
70 | * This avoids certain unpleasant rounding anomalies, such as | |
71 | * 0 0 moveto currentpoint not returning 0 0, and () stringwidth | |
72 | * not returning 0 0. | |
73 | */ | |
74 | #if round_ctm_fixed /* ****** NOTA BENE ****** */ | |
75 | # define update_t_fixed(mat, t, t_fixed)\ | |
76 | (mat).t = fixed2float((mat).t_fixed = float2fixed((mat).t)) | |
77 | #else /* !round_update_fixed */ | |
78 | # define update_t_fixed(mat, t, t_fixed)\ | |
79 | (mat).t_fixed = float2fixed((mat).t) | |
80 | #endif /* (!)round_update_fixed */ | |
81 | #define update_matrix_fixed(mat)\ | |
82 | update_t_fixed(mat, tx, tx_fixed),\ | |
83 | update_t_fixed(mat, ty, ty_fixed) | |
84 | #define update_ctm(pgs)\ | |
85 | update_matrix_fixed(pgs->ctm),\ | |
86 | pgs->inverse_valid = 0,\ | |
87 | pgs->char_tm_valid = 0 | |
88 | ||
89 | void | |
90 | gs_update_matrix_fixed(gs_matrix_fixed *pmat) | |
91 | { update_matrix_fixed(*pmat); | |
92 | } | |
93 | ||
94 | /* ------ Coordinate system definition ------ */ | |
95 | ||
96 | int | |
97 | gs_initmatrix(gs_state *pgs) | |
98 | { gx_device *dev = pgs->device->info; | |
99 | (*dev->procs->get_initial_matrix)(dev, &ctm_only(pgs)); | |
100 | update_ctm(pgs); | |
101 | #ifdef DEBUG | |
102 | if ( gs_debug['x'] ) | |
103 | dprintf("[x]initmatrix:\n"), trace_ctm(pgs); | |
104 | #endif | |
105 | return 0; | |
106 | } | |
107 | ||
108 | int | |
109 | gs_defaultmatrix(const gs_state *pgs, gs_matrix *pmat) | |
110 | { gx_device *dev = pgs->device->info; | |
111 | (*dev->procs->get_initial_matrix)(dev, pmat); | |
112 | return 0; | |
113 | } | |
114 | ||
115 | int | |
116 | gs_currentmatrix(const gs_state *pgs, gs_matrix *pmat) | |
117 | { *pmat = ctm_only(pgs); | |
118 | return 0; | |
119 | } | |
120 | ||
121 | int | |
122 | gs_setmatrix(gs_state *pgs, const gs_matrix *pmat) | |
123 | { ctm_only(pgs) = *pmat; | |
124 | update_ctm(pgs); | |
125 | #ifdef DEBUG | |
126 | if ( gs_debug['x'] ) | |
127 | dprintf("[x]setmatrix:\n"), trace_ctm(pgs); | |
128 | #endif | |
129 | return 0; | |
130 | } | |
131 | ||
132 | int | |
133 | gs_translate(gs_state *pgs, floatp dx, floatp dy) | |
134 | { gs_point pt; | |
135 | int code; | |
136 | if ( (code = gs_distance_transform(dx, dy, &ctm_only(pgs), &pt)) < 0 ) | |
137 | return code; | |
138 | pgs->ctm.tx += pt.x; | |
139 | pgs->ctm.ty += pt.y; | |
140 | update_ctm(pgs); | |
141 | #ifdef DEBUG | |
142 | if ( gs_debug['x'] ) | |
143 | dprintf4("[x]translate: %f %f -> %f %f\n", | |
144 | dx, dy, pt.x, pt.y), | |
145 | trace_ctm(pgs); | |
146 | #endif | |
147 | return 0; | |
148 | } | |
149 | ||
150 | int | |
151 | gs_scale(gs_state *pgs, floatp sx, floatp sy) | |
152 | { pgs->ctm.xx *= sx; | |
153 | pgs->ctm.xy *= sx; | |
154 | pgs->ctm.yx *= sy; | |
155 | pgs->ctm.yy *= sy; | |
156 | pgs->inverse_valid = 0, pgs->char_tm_valid = 0; | |
157 | #ifdef DEBUG | |
158 | if ( gs_debug['x'] ) | |
159 | dprintf2("[x]scale: %f %f\n", sx, sy), trace_ctm(pgs); | |
160 | #endif | |
161 | return 0; | |
162 | } | |
163 | ||
164 | int | |
165 | gs_rotate(gs_state *pgs, floatp ang) | |
166 | { int code = gs_matrix_rotate(&ctm_only(pgs), ang, &ctm_only(pgs)); | |
167 | pgs->inverse_valid = 0, pgs->char_tm_valid = 0; | |
168 | #ifdef DEBUG | |
169 | if ( gs_debug['x'] ) | |
170 | dprintf1("[x]rotate: %f\n", ang), trace_ctm(pgs); | |
171 | #endif | |
172 | return code; | |
173 | } | |
174 | ||
175 | int | |
176 | gs_concat(gs_state *pgs, const gs_matrix *pmat) | |
177 | { int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &ctm_only(pgs)); | |
178 | update_ctm(pgs); | |
179 | #ifdef DEBUG | |
180 | if ( gs_debug['x'] ) | |
181 | dprintf("[x]concat:\n"), trace_matrix(pmat), trace_ctm(pgs); | |
182 | #endif | |
183 | return code; | |
184 | } | |
185 | ||
186 | /* ------ Coordinate transformation ------ */ | |
187 | ||
188 | int | |
189 | gs_transform(gs_state *pgs, floatp x, floatp y, gs_point *pt) | |
190 | { return gs_point_transform(x, y, &ctm_only(pgs), pt); | |
191 | } | |
192 | ||
193 | int | |
194 | gs_dtransform(gs_state *pgs, floatp dx, floatp dy, gs_point *pt) | |
195 | { return gs_distance_transform(dx, dy, &ctm_only(pgs), pt); | |
196 | } | |
197 | ||
198 | int | |
199 | gs_itransform(gs_state *pgs, floatp x, floatp y, gs_point *pt) | |
200 | { /* If the matrix isn't skewed, we get more accurate results */ | |
201 | /* by using transform_inverse than by using the inverse matrix. */ | |
202 | if ( !is_skewed(&pgs->ctm) ) | |
203 | { return gs_point_transform_inverse(x, y, &ctm_only(pgs), pt); | |
204 | } | |
205 | else | |
206 | { ensure_inverse_valid(pgs); | |
207 | return gs_point_transform(x, y, &pgs->ctm_inverse, pt); | |
208 | } | |
209 | } | |
210 | ||
211 | int | |
212 | gs_idtransform(gs_state *pgs, floatp dx, floatp dy, gs_point *pt) | |
213 | { /* If the matrix isn't skewed, we get more accurate results */ | |
214 | /* by using transform_inverse than by using the inverse matrix. */ | |
215 | if ( !is_skewed(&pgs->ctm) ) | |
216 | { return gs_distance_transform_inverse(dx, dy, | |
217 | &ctm_only(pgs), pt); | |
218 | } | |
219 | else | |
220 | { ensure_inverse_valid(pgs); | |
221 | return gs_distance_transform(dx, dy, &pgs->ctm_inverse, pt); | |
222 | } | |
223 | } | |
224 | ||
225 | /* ------ For internal use only ------ */ | |
226 | ||
227 | /* Set the translation to a fixed value, */ | |
228 | /* and mark char_tm as valid. */ | |
229 | /* Used by gschar.c to prepare for a BuildChar procedure. */ | |
230 | int | |
231 | gs_translate_to_fixed(register gs_state *pgs, fixed px, fixed py) | |
232 | { pgs->ctm.tx = fixed2float(pgs->ctm.tx_fixed = px); | |
233 | pgs->ctm.ty = fixed2float(pgs->ctm.ty_fixed = py); | |
234 | pgs->inverse_valid = 0; | |
235 | pgs->char_tm_valid = 1; | |
236 | return 0; | |
237 | } | |
238 | ||
239 | /* Compute the coefficients for fast fixed-point distance transformations */ | |
240 | /* from a transformation matrix. */ | |
241 | /* We should cache the coefficients with the ctm.... */ | |
242 | int | |
243 | gx_matrix_to_fixed_coeff(const gs_matrix *pmat, register fixed_coeff *pfc, | |
244 | int max_bits) | |
245 | { gs_matrix ctm; | |
246 | int scale = -10000; | |
247 | int expt, shift; | |
248 | ctm = *pmat; | |
249 | pfc->skewed = 0; | |
250 | if ( !is_fzero(ctm.xx) ) | |
251 | { (void)frexp(ctm.xx, &scale); | |
252 | } | |
253 | if ( !is_fzero(ctm.xy) ) | |
254 | { (void)frexp(ctm.xy, &expt); | |
255 | if ( expt > scale ) scale = expt; | |
256 | pfc->skewed = 1; | |
257 | } | |
258 | if ( !is_fzero(ctm.yx) ) | |
259 | { (void)frexp(ctm.yx, &expt); | |
260 | if ( expt > scale ) scale = expt; | |
261 | pfc->skewed = 1; | |
262 | } | |
263 | if ( !is_fzero(ctm.yy) ) | |
264 | { (void)frexp(ctm.yy, &expt); | |
265 | if ( expt > scale ) scale = expt; | |
266 | } | |
267 | scale = sizeof(long) * 8 - 1 - max_bits - scale; | |
268 | shift = scale - _fixed_shift; | |
269 | if ( shift > 0 ) | |
270 | { pfc->shift = shift; | |
271 | pfc->round = (fixed)1 << (shift - 1); | |
272 | } | |
273 | else | |
274 | { pfc->shift = 0; | |
275 | pfc->round = 0; | |
276 | scale -= shift; | |
277 | } | |
278 | pfc->xx = (is_fzero(ctm.xx) ? 0 : (long)ldexp(ctm.xx, scale)); | |
279 | pfc->yy = (is_fzero(ctm.yy) ? 0 : (long)ldexp(ctm.yy, scale)); | |
280 | if ( pfc->skewed ) | |
281 | { pfc->xy = (is_fzero(ctm.xy) ? 0 : (long)ldexp(ctm.xy, scale)); | |
282 | pfc->yx = (is_fzero(ctm.yx) ? 0 : (long)ldexp(ctm.yx, scale)); | |
283 | } | |
284 | else | |
285 | pfc->xy = pfc->yx = 0; | |
286 | #ifdef DEBUG | |
287 | if ( gs_debug['x'] ) | |
288 | { dprintf7("[x]ctm: [%6g %6g %6g %6g ; %6g %6g] scale=%d\n", | |
289 | ctm.xx, ctm.xy, ctm.yx, ctm.yy, ctm.tx, ctm.ty, scale); | |
290 | dprintf5(" fc: [%lx %lx %lx %lx] shift=%d\n", | |
291 | pfc->xx, pfc->xy, pfc->yx, pfc->yy, | |
292 | pfc->shift); | |
293 | } | |
294 | #endif | |
295 | pfc->max_bits = max_bits; | |
296 | return 0; | |
297 | } | |
298 | ||
299 | /* ------ Debugging printout ------ */ | |
300 | ||
301 | #ifdef DEBUG | |
302 | ||
303 | /* Print a matrix */ | |
304 | private void | |
305 | trace_ctm(const gs_state *pgs) | |
306 | { const gs_matrix_fixed *pmat = &pgs->ctm; | |
307 | trace_matrix((gs_matrix *)pmat); | |
308 | dprintf2("\t\tt_fixed: [%6g %6g]\n", | |
309 | fixed2float(pmat->tx_fixed), fixed2float(pmat->ty_fixed)); | |
310 | } | |
311 | private void | |
312 | trace_matrix(register const gs_matrix *pmat) | |
313 | { dprintf6("\t[%6g %6g %6g %6g %6g %6g]\n", | |
314 | pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty); | |
315 | } | |
316 | ||
317 | #endif |