2 |
- |
1 |
/* Copyright (C) 1989, 2000 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: gscoord.c,v 1.9 2004/03/13 18:28:52 igor Exp $ */
|
|
|
18 |
/* Coordinate system operators for Ghostscript library */
|
|
|
19 |
#include "math_.h"
|
|
|
20 |
#include "gx.h"
|
|
|
21 |
#include "gserrors.h"
|
|
|
22 |
#include "gsccode.h" /* for gxfont.h */
|
|
|
23 |
#include "gxfarith.h"
|
|
|
24 |
#include "gxfixed.h"
|
|
|
25 |
#include "gxmatrix.h"
|
|
|
26 |
#include "gxfont.h" /* for char_tm */
|
|
|
27 |
#include "gxpath.h" /* for gx_path_translate */
|
|
|
28 |
#include "gzstate.h"
|
|
|
29 |
#include "gxcoord.h" /* requires gsmatrix, gsstate */
|
|
|
30 |
#include "gxdevice.h"
|
|
|
31 |
|
|
|
32 |
/* Choose whether to enable the rounding code in update_ctm. */
|
|
|
33 |
#define ROUND_CTM_FIXED 1
|
|
|
34 |
|
|
|
35 |
/* Forward declarations */
|
|
|
36 |
#ifdef DEBUG
|
|
|
37 |
#define trace_ctm(pgs) trace_matrix_fixed(&(pgs)->ctm)
|
|
|
38 |
private void trace_matrix_fixed(const gs_matrix_fixed *);
|
|
|
39 |
private void trace_matrix(const gs_matrix *);
|
|
|
40 |
|
|
|
41 |
#endif
|
|
|
42 |
|
|
|
43 |
/* Macro for ensuring ctm_inverse is valid */
|
|
|
44 |
#ifdef DEBUG
|
|
|
45 |
# define print_inverse(pgs)\
|
|
|
46 |
if ( gs_debug_c('x') )\
|
|
|
47 |
dlprintf("[x]Inverting:\n"), trace_ctm(pgs), trace_matrix(&pgs->ctm_inverse)
|
|
|
48 |
#else
|
|
|
49 |
# define print_inverse(pgs) DO_NOTHING
|
|
|
50 |
#endif
|
|
|
51 |
#define ensure_inverse_valid(pgs)\
|
|
|
52 |
if ( !pgs->ctm_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 |
{
|
|
|
60 |
int code = gs_matrix_invert(&ctm_only(pgs), &pgs->ctm_inverse);
|
|
|
61 |
|
|
|
62 |
print_inverse(pgs);
|
|
|
63 |
if (code < 0)
|
|
|
64 |
return code;
|
|
|
65 |
pgs->ctm_inverse_valid = true;
|
|
|
66 |
return 0;
|
|
|
67 |
}
|
|
|
68 |
|
|
|
69 |
/* Machinery for updating fixed version of ctm. */
|
|
|
70 |
/*
|
|
|
71 |
* We (conditionally) adjust the floating point translation
|
|
|
72 |
* so that it exactly matches the (rounded) fixed translation.
|
|
|
73 |
* This avoids certain unpleasant rounding anomalies, such as
|
|
|
74 |
* 0 0 moveto currentpoint not returning 0 0, and () stringwidth
|
|
|
75 |
* not returning 0 0.
|
|
|
76 |
*/
|
|
|
77 |
#if ROUND_CTM_FIXED
|
|
|
78 |
# define update_t_fixed(mat, t, t_fixed, v)\
|
|
|
79 |
(set_float2fixed_vars((mat).t_fixed, v),\
|
|
|
80 |
set_fixed2float_var((mat).t, (mat).t_fixed))
|
|
|
81 |
#else /* !ROUND_CTM_FIXED */
|
|
|
82 |
# define update_t_fixed(mat, t, t_fixed, v)\
|
|
|
83 |
((mat).t = (v),\
|
|
|
84 |
set_float2fixed_vars((mat).t_fixed, (mat).t))
|
|
|
85 |
#endif /* (!)ROUND_CTM_FIXED */
|
|
|
86 |
#define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits)
|
|
|
87 |
#define update_matrix_fixed(mat, xt, yt)\
|
|
|
88 |
((mat).txy_fixed_valid = (f_fits_in_fixed(xt) && f_fits_in_fixed(yt) ?\
|
|
|
89 |
(update_t_fixed(mat, tx, tx_fixed, xt),\
|
|
|
90 |
update_t_fixed(mat, ty, ty_fixed, yt), true) :\
|
|
|
91 |
((mat).tx = (xt), (mat).ty = (yt), false)))
|
|
|
92 |
#define update_ctm(pgs, xt, yt)\
|
|
|
93 |
(pgs->ctm_inverse_valid = false,\
|
|
|
94 |
pgs->char_tm_valid = false,\
|
|
|
95 |
update_matrix_fixed(pgs->ctm, xt, yt))
|
|
|
96 |
|
|
|
97 |
/* ------ Coordinate system definition ------ */
|
|
|
98 |
|
|
|
99 |
int
|
|
|
100 |
gs_initmatrix(gs_state * pgs)
|
|
|
101 |
{
|
|
|
102 |
gs_matrix imat;
|
|
|
103 |
|
|
|
104 |
gs_defaultmatrix(pgs, &imat);
|
|
|
105 |
update_ctm(pgs, imat.tx, imat.ty);
|
|
|
106 |
set_ctm_only(pgs, imat);
|
|
|
107 |
#ifdef DEBUG
|
|
|
108 |
if (gs_debug_c('x'))
|
|
|
109 |
dlprintf("[x]initmatrix:\n"), trace_ctm(pgs);
|
|
|
110 |
#endif
|
|
|
111 |
return 0;
|
|
|
112 |
}
|
|
|
113 |
|
|
|
114 |
int
|
|
|
115 |
gs_defaultmatrix(const gs_state * pgs, gs_matrix * pmat)
|
|
|
116 |
{
|
|
|
117 |
gx_device *dev;
|
|
|
118 |
|
|
|
119 |
if (pgs->ctm_default_set) { /* set after Install */
|
|
|
120 |
*pmat = pgs->ctm_default;
|
|
|
121 |
return 1;
|
|
|
122 |
}
|
|
|
123 |
dev = gs_currentdevice_inline(pgs);
|
|
|
124 |
gs_deviceinitialmatrix(dev, pmat);
|
|
|
125 |
/* Add in the translation for the Margins. */
|
|
|
126 |
pmat->tx += dev->Margins[0] *
|
|
|
127 |
dev->HWResolution[0] / dev->MarginsHWResolution[0];
|
|
|
128 |
pmat->ty += dev->Margins[1] *
|
|
|
129 |
dev->HWResolution[1] / dev->MarginsHWResolution[1];
|
|
|
130 |
return 0;
|
|
|
131 |
}
|
|
|
132 |
|
|
|
133 |
int
|
|
|
134 |
gs_setdefaultmatrix(gs_state * pgs, const gs_matrix * pmat)
|
|
|
135 |
{
|
|
|
136 |
if (pmat == NULL)
|
|
|
137 |
pgs->ctm_default_set = false;
|
|
|
138 |
else {
|
|
|
139 |
pgs->ctm_default = *pmat;
|
|
|
140 |
pgs->ctm_default_set = true;
|
|
|
141 |
}
|
|
|
142 |
return 0;
|
|
|
143 |
}
|
|
|
144 |
|
|
|
145 |
int
|
|
|
146 |
gs_currentmatrix(const gs_state * pgs, gs_matrix * pmat)
|
|
|
147 |
{
|
|
|
148 |
*pmat = ctm_only(pgs);
|
|
|
149 |
return 0;
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
/* Set the current transformation matrix for rendering text. */
|
|
|
153 |
/* Note that this may be based on a font other than the current font. */
|
|
|
154 |
int
|
|
|
155 |
gs_setcharmatrix(gs_state * pgs, const gs_matrix * pmat)
|
|
|
156 |
{
|
|
|
157 |
gs_matrix cmat;
|
|
|
158 |
int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &cmat);
|
|
|
159 |
|
|
|
160 |
if (code < 0)
|
|
|
161 |
return code;
|
|
|
162 |
update_matrix_fixed(pgs->char_tm, cmat.tx, cmat.ty);
|
|
|
163 |
char_tm_only(pgs) = cmat;
|
|
|
164 |
#ifdef DEBUG
|
|
|
165 |
if (gs_debug_c('x'))
|
|
|
166 |
dlprintf("[x]setting char_tm:"), trace_matrix_fixed(&pgs->char_tm);
|
|
|
167 |
#endif
|
|
|
168 |
pgs->char_tm_valid = true;
|
|
|
169 |
return 0;
|
|
|
170 |
}
|
|
|
171 |
|
|
|
172 |
/* Read (after possibly computing) the current transformation matrix */
|
|
|
173 |
/* for rendering text. If force=true, update char_tm if it is invalid; */
|
|
|
174 |
/* if force=false, don't update char_tm, and return an error code. */
|
|
|
175 |
int
|
|
|
176 |
gs_currentcharmatrix(gs_state * pgs, gs_matrix * ptm, bool force)
|
|
|
177 |
{
|
|
|
178 |
if (!pgs->char_tm_valid) {
|
|
|
179 |
int code;
|
|
|
180 |
|
|
|
181 |
if (!force)
|
|
|
182 |
return_error(gs_error_undefinedresult);
|
|
|
183 |
code = gs_setcharmatrix(pgs, &pgs->font->FontMatrix);
|
|
|
184 |
if (code < 0)
|
|
|
185 |
return code;
|
|
|
186 |
}
|
|
|
187 |
if (ptm != NULL)
|
|
|
188 |
*ptm = char_tm_only(pgs);
|
|
|
189 |
return 0;
|
|
|
190 |
}
|
|
|
191 |
|
|
|
192 |
int
|
|
|
193 |
gs_setmatrix(gs_state * pgs, const gs_matrix * pmat)
|
|
|
194 |
{
|
|
|
195 |
update_ctm(pgs, pmat->tx, pmat->ty);
|
|
|
196 |
set_ctm_only(pgs, *pmat);
|
|
|
197 |
#ifdef DEBUG
|
|
|
198 |
if (gs_debug_c('x'))
|
|
|
199 |
dlprintf("[x]setmatrix:\n"), trace_ctm(pgs);
|
|
|
200 |
#endif
|
|
|
201 |
return 0;
|
|
|
202 |
}
|
|
|
203 |
|
|
|
204 |
int
|
|
|
205 |
gs_imager_setmatrix(gs_imager_state * pis, const gs_matrix * pmat)
|
|
|
206 |
{
|
|
|
207 |
update_matrix_fixed(pis->ctm, pmat->tx, pmat->ty);
|
|
|
208 |
set_ctm_only(pis, *pmat);
|
|
|
209 |
#ifdef DEBUG
|
|
|
210 |
if (gs_debug_c('x'))
|
|
|
211 |
dlprintf("[x]imager_setmatrix:\n"), trace_ctm(pis);
|
|
|
212 |
#endif
|
|
|
213 |
return 0;
|
|
|
214 |
}
|
|
|
215 |
|
|
|
216 |
int
|
|
|
217 |
gs_settocharmatrix(gs_state * pgs)
|
|
|
218 |
{
|
|
|
219 |
if (pgs->char_tm_valid) {
|
|
|
220 |
pgs->ctm = pgs->char_tm;
|
|
|
221 |
pgs->ctm_inverse_valid = false;
|
|
|
222 |
return 0;
|
|
|
223 |
} else
|
|
|
224 |
return_error(gs_error_undefinedresult);
|
|
|
225 |
}
|
|
|
226 |
|
|
|
227 |
int
|
|
|
228 |
gs_translate(gs_state * pgs, floatp dx, floatp dy)
|
|
|
229 |
{
|
|
|
230 |
gs_point pt;
|
|
|
231 |
int code;
|
|
|
232 |
|
|
|
233 |
if ((code = gs_distance_transform(dx, dy, &ctm_only(pgs), &pt)) < 0)
|
|
|
234 |
return code;
|
|
|
235 |
pt.x += pgs->ctm.tx;
|
|
|
236 |
pt.y += pgs->ctm.ty;
|
|
|
237 |
update_ctm(pgs, pt.x, pt.y);
|
|
|
238 |
#ifdef DEBUG
|
|
|
239 |
if (gs_debug_c('x'))
|
|
|
240 |
dlprintf4("[x]translate: %f %f -> %f %f\n",
|
|
|
241 |
dx, dy, pt.x, pt.y),
|
|
|
242 |
trace_ctm(pgs);
|
|
|
243 |
#endif
|
|
|
244 |
return 0;
|
|
|
245 |
}
|
|
|
246 |
|
|
|
247 |
int
|
|
|
248 |
gs_scale(gs_state * pgs, floatp sx, floatp sy)
|
|
|
249 |
{
|
|
|
250 |
pgs->ctm.xx *= sx;
|
|
|
251 |
pgs->ctm.xy *= sx;
|
|
|
252 |
pgs->ctm.yx *= sy;
|
|
|
253 |
pgs->ctm.yy *= sy;
|
|
|
254 |
pgs->ctm_inverse_valid = false, pgs->char_tm_valid = false;
|
|
|
255 |
#ifdef DEBUG
|
|
|
256 |
if (gs_debug_c('x'))
|
|
|
257 |
dlprintf2("[x]scale: %f %f\n", sx, sy), trace_ctm(pgs);
|
|
|
258 |
#endif
|
|
|
259 |
return 0;
|
|
|
260 |
}
|
|
|
261 |
|
|
|
262 |
int
|
|
|
263 |
gs_rotate(gs_state * pgs, floatp ang)
|
|
|
264 |
{
|
|
|
265 |
int code = gs_matrix_rotate(&ctm_only(pgs), ang,
|
|
|
266 |
&ctm_only_writable(pgs));
|
|
|
267 |
|
|
|
268 |
pgs->ctm_inverse_valid = false, pgs->char_tm_valid = false;
|
|
|
269 |
#ifdef DEBUG
|
|
|
270 |
if (gs_debug_c('x'))
|
|
|
271 |
dlprintf1("[x]rotate: %f\n", ang), trace_ctm(pgs);
|
|
|
272 |
#endif
|
|
|
273 |
return code;
|
|
|
274 |
}
|
|
|
275 |
|
|
|
276 |
int
|
|
|
277 |
gs_concat(gs_state * pgs, const gs_matrix * pmat)
|
|
|
278 |
{
|
|
|
279 |
gs_matrix cmat;
|
|
|
280 |
int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &cmat);
|
|
|
281 |
|
|
|
282 |
if (code < 0)
|
|
|
283 |
return code;
|
|
|
284 |
update_ctm(pgs, cmat.tx, cmat.ty);
|
|
|
285 |
set_ctm_only(pgs, cmat);
|
|
|
286 |
#ifdef DEBUG
|
|
|
287 |
if (gs_debug_c('x'))
|
|
|
288 |
dlprintf("[x]concat:\n"), trace_matrix(pmat), trace_ctm(pgs);
|
|
|
289 |
#endif
|
|
|
290 |
return code;
|
|
|
291 |
}
|
|
|
292 |
|
|
|
293 |
/* ------ Coordinate transformation ------ */
|
|
|
294 |
|
|
|
295 |
#define is_skewed(pmat) (!(is_xxyy(pmat) || is_xyyx(pmat)))
|
|
|
296 |
|
|
|
297 |
int
|
|
|
298 |
gs_transform(gs_state * pgs, floatp x, floatp y, gs_point * pt)
|
|
|
299 |
{
|
|
|
300 |
return gs_point_transform(x, y, &ctm_only(pgs), pt);
|
|
|
301 |
}
|
|
|
302 |
|
|
|
303 |
int
|
|
|
304 |
gs_dtransform(gs_state * pgs, floatp dx, floatp dy, gs_point * pt)
|
|
|
305 |
{
|
|
|
306 |
return gs_distance_transform(dx, dy, &ctm_only(pgs), pt);
|
|
|
307 |
}
|
|
|
308 |
|
|
|
309 |
int
|
|
|
310 |
gs_itransform(gs_state * pgs, floatp x, floatp y, gs_point * pt)
|
|
|
311 |
{ /* If the matrix isn't skewed, we get more accurate results */
|
|
|
312 |
/* by using transform_inverse than by using the inverse matrix. */
|
|
|
313 |
if (!is_skewed(&pgs->ctm)) {
|
|
|
314 |
return gs_point_transform_inverse(x, y, &ctm_only(pgs), pt);
|
|
|
315 |
} else {
|
|
|
316 |
ensure_inverse_valid(pgs);
|
|
|
317 |
return gs_point_transform(x, y, &pgs->ctm_inverse, pt);
|
|
|
318 |
}
|
|
|
319 |
}
|
|
|
320 |
|
|
|
321 |
int
|
|
|
322 |
gs_idtransform(gs_state * pgs, floatp dx, floatp dy, gs_point * pt)
|
|
|
323 |
{ /* If the matrix isn't skewed, we get more accurate results */
|
|
|
324 |
/* by using transform_inverse than by using the inverse matrix. */
|
|
|
325 |
if (!is_skewed(&pgs->ctm)) {
|
|
|
326 |
return gs_distance_transform_inverse(dx, dy,
|
|
|
327 |
&ctm_only(pgs), pt);
|
|
|
328 |
} else {
|
|
|
329 |
ensure_inverse_valid(pgs);
|
|
|
330 |
return gs_distance_transform(dx, dy, &pgs->ctm_inverse, pt);
|
|
|
331 |
}
|
|
|
332 |
}
|
|
|
333 |
|
|
|
334 |
int
|
|
|
335 |
gs_imager_idtransform(const gs_imager_state * pis, floatp dx, floatp dy,
|
|
|
336 |
gs_point * pt)
|
|
|
337 |
{
|
|
|
338 |
return gs_distance_transform_inverse(dx, dy, &ctm_only(pis), pt);
|
|
|
339 |
}
|
|
|
340 |
|
|
|
341 |
/* ------ For internal use only ------ */
|
|
|
342 |
|
|
|
343 |
/* Set the translation to a fixed value, and translate any existing path. */
|
|
|
344 |
/* Used by gschar.c to prepare for a BuildChar or BuildGlyph procedure. */
|
|
|
345 |
int
|
|
|
346 |
gx_translate_to_fixed(register gs_state * pgs, fixed px, fixed py)
|
|
|
347 |
{
|
|
|
348 |
double fpx = fixed2float(px);
|
|
|
349 |
double fdx = fpx - pgs->ctm.tx;
|
|
|
350 |
double fpy = fixed2float(py);
|
|
|
351 |
double fdy = fpy - pgs->ctm.ty;
|
|
|
352 |
fixed dx, dy;
|
|
|
353 |
int code;
|
|
|
354 |
|
|
|
355 |
if (pgs->ctm.txy_fixed_valid) {
|
|
|
356 |
dx = float2fixed(fdx);
|
|
|
357 |
dy = float2fixed(fdy);
|
|
|
358 |
code = gx_path_translate(pgs->path, dx, dy);
|
|
|
359 |
if (code < 0)
|
|
|
360 |
return code;
|
|
|
361 |
if (pgs->char_tm_valid && pgs->char_tm.txy_fixed_valid)
|
|
|
362 |
pgs->char_tm.tx_fixed += dx,
|
|
|
363 |
pgs->char_tm.ty_fixed += dy;
|
|
|
364 |
} else {
|
|
|
365 |
if (!gx_path_is_null(pgs->path))
|
|
|
366 |
return_error(gs_error_limitcheck);
|
|
|
367 |
}
|
|
|
368 |
pgs->ctm.tx = fpx;
|
|
|
369 |
pgs->ctm.tx_fixed = px;
|
|
|
370 |
pgs->ctm.ty = fpy;
|
|
|
371 |
pgs->ctm.ty_fixed = py;
|
|
|
372 |
pgs->ctm.txy_fixed_valid = true;
|
|
|
373 |
pgs->ctm_inverse_valid = false;
|
|
|
374 |
if (pgs->char_tm_valid) { /* Update char_tm now, leaving it valid. */
|
|
|
375 |
pgs->char_tm.tx += fdx;
|
|
|
376 |
pgs->char_tm.ty += fdy;
|
|
|
377 |
}
|
|
|
378 |
#ifdef DEBUG
|
|
|
379 |
if (gs_debug_c('x')) {
|
|
|
380 |
dlprintf2("[x]translate_to_fixed %g, %g:\n",
|
|
|
381 |
fixed2float(px), fixed2float(py));
|
|
|
382 |
trace_ctm(pgs);
|
|
|
383 |
dlprintf("[x] char_tm:\n");
|
|
|
384 |
trace_matrix_fixed(&pgs->char_tm);
|
|
|
385 |
}
|
|
|
386 |
#endif
|
|
|
387 |
gx_setcurrentpoint(pgs, fixed2float(pgs->ctm.tx_fixed), fixed2float(pgs->ctm.ty_fixed));
|
|
|
388 |
pgs->current_point_valid = true;
|
|
|
389 |
return 0;
|
|
|
390 |
}
|
|
|
391 |
|
|
|
392 |
/* Scale the CTM and character matrix for oversampling. */
|
|
|
393 |
int
|
|
|
394 |
gx_scale_char_matrix(register gs_state * pgs, int sx, int sy)
|
|
|
395 |
{
|
|
|
396 |
#define scale_cxy(s, vx, vy)\
|
|
|
397 |
if ( s != 1 )\
|
|
|
398 |
{ pgs->ctm.vx *= s;\
|
|
|
399 |
pgs->ctm.vy *= s;\
|
|
|
400 |
pgs->ctm_inverse_valid = false;\
|
|
|
401 |
if ( pgs->char_tm_valid )\
|
|
|
402 |
{ pgs->char_tm.vx *= s;\
|
|
|
403 |
pgs->char_tm.vy *= s;\
|
|
|
404 |
}\
|
|
|
405 |
}
|
|
|
406 |
scale_cxy(sx, xx, yx);
|
|
|
407 |
scale_cxy(sy, xy, yy);
|
|
|
408 |
#undef scale_cxy
|
|
|
409 |
if_debug2('x', "[x]char scale: %d %d\n", sx, sy);
|
|
|
410 |
return 0;
|
|
|
411 |
}
|
|
|
412 |
|
|
|
413 |
/* Compute the coefficients for fast fixed-point distance transformations */
|
|
|
414 |
/* from a transformation matrix. */
|
|
|
415 |
/* We should cache the coefficients with the ctm.... */
|
|
|
416 |
int
|
|
|
417 |
gx_matrix_to_fixed_coeff(const gs_matrix * pmat, register fixed_coeff * pfc,
|
|
|
418 |
int max_bits)
|
|
|
419 |
{
|
|
|
420 |
gs_matrix ctm;
|
|
|
421 |
int scale = -10000;
|
|
|
422 |
int expt, shift;
|
|
|
423 |
|
|
|
424 |
ctm = *pmat;
|
|
|
425 |
pfc->skewed = 0;
|
|
|
426 |
if (!is_fzero(ctm.xx)) {
|
|
|
427 |
discard(frexp(ctm.xx, &scale));
|
|
|
428 |
}
|
|
|
429 |
if (!is_fzero(ctm.xy)) {
|
|
|
430 |
discard(frexp(ctm.xy, &expt));
|
|
|
431 |
if (expt > scale)
|
|
|
432 |
scale = expt;
|
|
|
433 |
pfc->skewed = 1;
|
|
|
434 |
}
|
|
|
435 |
if (!is_fzero(ctm.yx)) {
|
|
|
436 |
discard(frexp(ctm.yx, &expt));
|
|
|
437 |
if (expt > scale)
|
|
|
438 |
scale = expt;
|
|
|
439 |
pfc->skewed = 1;
|
|
|
440 |
}
|
|
|
441 |
if (!is_fzero(ctm.yy)) {
|
|
|
442 |
discard(frexp(ctm.yy, &expt));
|
|
|
443 |
if (expt > scale)
|
|
|
444 |
scale = expt;
|
|
|
445 |
}
|
|
|
446 |
/*
|
|
|
447 |
* There are two multiplications in fixed_coeff_mult: one involves a
|
|
|
448 |
* factor that may have max_bits significant bits, the other may have
|
|
|
449 |
* fixed_fraction_bits (_fixed_shift) bits. Ensure that neither one
|
|
|
450 |
* will overflow.
|
|
|
451 |
*/
|
|
|
452 |
if (max_bits < fixed_fraction_bits)
|
|
|
453 |
max_bits = fixed_fraction_bits;
|
|
|
454 |
scale = sizeof(long) * 8 - 1 - max_bits - scale;
|
|
|
455 |
|
|
|
456 |
shift = scale - _fixed_shift;
|
|
|
457 |
if (shift > 0) {
|
|
|
458 |
pfc->shift = shift;
|
|
|
459 |
pfc->round = (fixed) 1 << (shift - 1);
|
|
|
460 |
} else {
|
|
|
461 |
pfc->shift = 0;
|
|
|
462 |
pfc->round = 0;
|
|
|
463 |
scale -= shift;
|
|
|
464 |
}
|
|
|
465 |
#define SET_C(c)\
|
|
|
466 |
if ( is_fzero(ctm.c) ) pfc->c = 0;\
|
|
|
467 |
else pfc->c = (long)ldexp(ctm.c, scale)
|
|
|
468 |
SET_C(xx);
|
|
|
469 |
SET_C(xy);
|
|
|
470 |
SET_C(yx);
|
|
|
471 |
SET_C(yy);
|
|
|
472 |
#undef SET_C
|
|
|
473 |
#ifdef DEBUG
|
|
|
474 |
if (gs_debug_c('x')) {
|
|
|
475 |
dlprintf6("[x]ctm: [%6g %6g %6g %6g %6g %6g]\n",
|
|
|
476 |
ctm.xx, ctm.xy, ctm.yx, ctm.yy, ctm.tx, ctm.ty);
|
|
|
477 |
dlprintf6(" scale=%d fc: [0x%lx 0x%lx 0x%lx 0x%lx] shift=%d\n",
|
|
|
478 |
scale, pfc->xx, pfc->xy, pfc->yx, pfc->yy,
|
|
|
479 |
pfc->shift);
|
|
|
480 |
}
|
|
|
481 |
#endif
|
|
|
482 |
pfc->max_bits = max_bits;
|
|
|
483 |
return 0;
|
|
|
484 |
}
|
|
|
485 |
|
|
|
486 |
/*
|
|
|
487 |
* Handle the case of a large value or a value with a fraction part.
|
|
|
488 |
* See gxmatrix.h for more details.
|
|
|
489 |
*/
|
|
|
490 |
fixed
|
|
|
491 |
fixed_coeff_mult(fixed value, long coeff, const fixed_coeff *pfc, int maxb)
|
|
|
492 |
{
|
|
|
493 |
int shift = pfc->shift;
|
|
|
494 |
|
|
|
495 |
/*
|
|
|
496 |
* Test if the value is too large for simple long math.
|
|
|
497 |
*/
|
|
|
498 |
if ((value + (fixed_1 << (maxb - 1))) & (-fixed_1 << maxb)) {
|
|
|
499 |
/* The second argument of fixed_mult_quo must be non-negative. */
|
|
|
500 |
return
|
|
|
501 |
(coeff < 0 ?
|
|
|
502 |
-fixed_mult_quo(value, -coeff, fixed_1 << shift) :
|
|
|
503 |
fixed_mult_quo(value, coeff, fixed_1 << shift));
|
|
|
504 |
} else {
|
|
|
505 |
/*
|
|
|
506 |
* The construction above guarantees that the multiplications
|
|
|
507 |
* won't overflow the capacity of an int.
|
|
|
508 |
*/
|
|
|
509 |
return (fixed)
|
|
|
510 |
arith_rshift(fixed2int_var(value) * coeff
|
|
|
511 |
+ fixed2int(fixed_fraction(value) * coeff)
|
|
|
512 |
+ pfc->round, shift);
|
|
|
513 |
}
|
|
|
514 |
}
|
|
|
515 |
|
|
|
516 |
/* ------ Debugging printout ------ */
|
|
|
517 |
|
|
|
518 |
#ifdef DEBUG
|
|
|
519 |
|
|
|
520 |
/* Print a matrix */
|
|
|
521 |
private void
|
|
|
522 |
trace_matrix_fixed(const gs_matrix_fixed * pmat)
|
|
|
523 |
{
|
|
|
524 |
trace_matrix((const gs_matrix *)pmat);
|
|
|
525 |
if (pmat->txy_fixed_valid) {
|
|
|
526 |
dprintf2("\t\tt_fixed: [%6g %6g]\n",
|
|
|
527 |
fixed2float(pmat->tx_fixed),
|
|
|
528 |
fixed2float(pmat->ty_fixed));
|
|
|
529 |
} else {
|
|
|
530 |
dputs("\t\tt_fixed not valid\n");
|
|
|
531 |
}
|
|
|
532 |
}
|
|
|
533 |
private void
|
|
|
534 |
trace_matrix(register const gs_matrix * pmat)
|
|
|
535 |
{
|
|
|
536 |
dlprintf6("\t[%6g %6g %6g %6g %6g %6g]\n",
|
|
|
537 |
pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
|
|
|
538 |
}
|
|
|
539 |
|
|
|
540 |
#endif
|