/* Copyright (C) 1989, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gscoord.c,v 1.5.6.1.2.1 2003/01/17 00:49:02 giles Exp $ */ /* Coordinate system operators for Ghostscript library */ #include "math_.h" #include "gx.h" #include "gserrors.h" #include "gsccode.h" /* for gxfont.h */ #include "gxfarith.h" #include "gxfixed.h" #include "gxmatrix.h" #include "gxfont.h" /* for char_tm */ #include "gxpath.h" /* for gx_path_translate */ #include "gzstate.h" #include "gxcoord.h" /* requires gsmatrix, gsstate */ #include "gxdevice.h" /* Choose whether to enable the rounding code in update_ctm. */ #define ROUND_CTM_FIXED 1 /* Forward declarations */ #ifdef DEBUG #define trace_ctm(pgs) trace_matrix_fixed(&(pgs)->ctm) private void trace_matrix_fixed(P1(const gs_matrix_fixed *)); private void trace_matrix(P1(const gs_matrix *)); #endif /* Macro for ensuring ctm_inverse is valid */ #ifdef DEBUG # define print_inverse(pgs)\ if ( gs_debug_c('x') )\ dlprintf("[x]Inverting:\n"), trace_ctm(pgs), trace_matrix(&pgs->ctm_inverse) #else # define print_inverse(pgs) DO_NOTHING #endif #define ensure_inverse_valid(pgs)\ if ( !pgs->ctm_inverse_valid )\ { int code = ctm_set_inverse(pgs);\ if ( code < 0 ) return code;\ } private int ctm_set_inverse(gs_state * pgs) { int code = gs_matrix_invert(&ctm_only(pgs), &pgs->ctm_inverse); print_inverse(pgs); if (code < 0) return code; pgs->ctm_inverse_valid = true; return 0; } /* Machinery for updating fixed version of ctm. */ /* * We (conditionally) adjust the floating point translation * so that it exactly matches the (rounded) fixed translation. * This avoids certain unpleasant rounding anomalies, such as * 0 0 moveto currentpoint not returning 0 0, and () stringwidth * not returning 0 0. */ #if ROUND_CTM_FIXED # define update_t_fixed(mat, t, t_fixed, v)\ (set_float2fixed_vars((mat).t_fixed, v),\ set_fixed2float_var((mat).t, (mat).t_fixed)) #else /* !ROUND_CTM_FIXED */ # define update_t_fixed(mat, t, t_fixed, v)\ ((mat).t = (v),\ set_float2fixed_vars((mat).t_fixed, (mat).t)) #endif /* (!)ROUND_CTM_FIXED */ #define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits) #define update_matrix_fixed(mat, xt, yt)\ ((mat).txy_fixed_valid = (f_fits_in_fixed(xt) && f_fits_in_fixed(yt) ?\ (update_t_fixed(mat, tx, tx_fixed, xt),\ update_t_fixed(mat, ty, ty_fixed, yt), true) :\ ((mat).tx = (xt), (mat).ty = (yt), false))) #define update_ctm(pgs, xt, yt)\ (pgs->ctm_inverse_valid = false,\ pgs->char_tm_valid = false,\ update_matrix_fixed(pgs->ctm, xt, yt)) /* ------ Coordinate system definition ------ */ int gs_initmatrix(gs_state * pgs) { gs_matrix imat; gs_defaultmatrix(pgs, &imat); update_ctm(pgs, imat.tx, imat.ty); set_ctm_only(pgs, imat); #ifdef DEBUG if (gs_debug_c('x')) dlprintf("[x]initmatrix:\n"), trace_ctm(pgs); #endif return 0; } int gs_defaultmatrix(const gs_state * pgs, gs_matrix * pmat) { gx_device *dev; if (pgs->ctm_default_set) { /* set after Install */ *pmat = pgs->ctm_default; return 1; } dev = gs_currentdevice_inline(pgs); gs_deviceinitialmatrix(dev, pmat); /* Add in the translation for the Margins. */ pmat->tx += dev->Margins[0] * dev->HWResolution[0] / dev->MarginsHWResolution[0]; pmat->ty += dev->Margins[1] * dev->HWResolution[1] / dev->MarginsHWResolution[1]; return 0; } int gs_setdefaultmatrix(gs_state * pgs, const gs_matrix * pmat) { if (pmat == NULL) pgs->ctm_default_set = false; else { pgs->ctm_default = *pmat; pgs->ctm_default_set = true; } return 0; } int gs_currentmatrix(const gs_state * pgs, gs_matrix * pmat) { *pmat = ctm_only(pgs); return 0; } /* Set the current transformation matrix for rendering text. */ /* Note that this may be based on a font other than the current font. */ int gs_setcharmatrix(gs_state * pgs, const gs_matrix * pmat) { gs_matrix cmat; int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &cmat); if (code < 0) return code; update_matrix_fixed(pgs->char_tm, cmat.tx, cmat.ty); char_tm_only(pgs) = cmat; #ifdef DEBUG if (gs_debug_c('x')) dlprintf("[x]setting char_tm:"), trace_matrix_fixed(&pgs->char_tm); #endif pgs->char_tm_valid = true; return 0; } /* Read (after possibly computing) the current transformation matrix */ /* for rendering text. If force=true, update char_tm if it is invalid; */ /* if force=false, don't update char_tm, and return an error code. */ int gs_currentcharmatrix(gs_state * pgs, gs_matrix * ptm, bool force) { if (!pgs->char_tm_valid) { int code; if (!force) return_error(gs_error_undefinedresult); code = gs_setcharmatrix(pgs, &pgs->font->FontMatrix); if (code < 0) return code; } if (ptm != NULL) *ptm = char_tm_only(pgs); return 0; } int gs_setmatrix(gs_state * pgs, const gs_matrix * pmat) { update_ctm(pgs, pmat->tx, pmat->ty); set_ctm_only(pgs, *pmat); #ifdef DEBUG if (gs_debug_c('x')) dlprintf("[x]setmatrix:\n"), trace_ctm(pgs); #endif return 0; } int gs_imager_setmatrix(gs_imager_state * pis, const gs_matrix * pmat) { update_matrix_fixed(pis->ctm, pmat->tx, pmat->ty); set_ctm_only(pis, *pmat); #ifdef DEBUG if (gs_debug_c('x')) dlprintf("[x]imager_setmatrix:\n"), trace_ctm(pis); #endif return 0; } int gs_settocharmatrix(gs_state * pgs) { if (pgs->char_tm_valid) { pgs->ctm = pgs->char_tm; pgs->ctm_inverse_valid = false; return 0; } else return_error(gs_error_undefinedresult); } int gs_translate(gs_state * pgs, floatp dx, floatp dy) { gs_point pt; int code; if ((code = gs_distance_transform(dx, dy, &ctm_only(pgs), &pt)) < 0) return code; pt.x += pgs->ctm.tx; pt.y += pgs->ctm.ty; update_ctm(pgs, pt.x, pt.y); #ifdef DEBUG if (gs_debug_c('x')) dlprintf4("[x]translate: %f %f -> %f %f\n", dx, dy, pt.x, pt.y), trace_ctm(pgs); #endif return 0; } int gs_scale(gs_state * pgs, floatp sx, floatp sy) { pgs->ctm.xx *= sx; pgs->ctm.xy *= sx; pgs->ctm.yx *= sy; pgs->ctm.yy *= sy; pgs->ctm_inverse_valid = false, pgs->char_tm_valid = false; #ifdef DEBUG if (gs_debug_c('x')) dlprintf2("[x]scale: %f %f\n", sx, sy), trace_ctm(pgs); #endif return 0; } int gs_rotate(gs_state * pgs, floatp ang) { int code = gs_matrix_rotate(&ctm_only(pgs), ang, &ctm_only_writable(pgs)); pgs->ctm_inverse_valid = false, pgs->char_tm_valid = false; #ifdef DEBUG if (gs_debug_c('x')) dlprintf1("[x]rotate: %f\n", ang), trace_ctm(pgs); #endif return code; } int gs_concat(gs_state * pgs, const gs_matrix * pmat) { gs_matrix cmat; int code = gs_matrix_multiply(pmat, &ctm_only(pgs), &cmat); if (code < 0) return code; update_ctm(pgs, cmat.tx, cmat.ty); set_ctm_only(pgs, cmat); #ifdef DEBUG if (gs_debug_c('x')) dlprintf("[x]concat:\n"), trace_matrix(pmat), trace_ctm(pgs); #endif return code; } /* ------ Coordinate transformation ------ */ #define is_skewed(pmat) (!(is_xxyy(pmat) || is_xyyx(pmat))) int gs_transform(gs_state * pgs, floatp x, floatp y, gs_point * pt) { return gs_point_transform(x, y, &ctm_only(pgs), pt); } int gs_dtransform(gs_state * pgs, floatp dx, floatp dy, gs_point * pt) { return gs_distance_transform(dx, dy, &ctm_only(pgs), pt); } int gs_itransform(gs_state * pgs, floatp x, floatp y, gs_point * pt) { /* If the matrix isn't skewed, we get more accurate results */ /* by using transform_inverse than by using the inverse matrix. */ if (!is_skewed(&pgs->ctm)) { return gs_point_transform_inverse(x, y, &ctm_only(pgs), pt); } else { ensure_inverse_valid(pgs); return gs_point_transform(x, y, &pgs->ctm_inverse, pt); } } int gs_idtransform(gs_state * pgs, floatp dx, floatp dy, gs_point * pt) { /* If the matrix isn't skewed, we get more accurate results */ /* by using transform_inverse than by using the inverse matrix. */ if (!is_skewed(&pgs->ctm)) { return gs_distance_transform_inverse(dx, dy, &ctm_only(pgs), pt); } else { ensure_inverse_valid(pgs); return gs_distance_transform(dx, dy, &pgs->ctm_inverse, pt); } } int gs_imager_idtransform(const gs_imager_state * pis, floatp dx, floatp dy, gs_point * pt) { return gs_distance_transform_inverse(dx, dy, &ctm_only(pis), pt); } /* ------ For internal use only ------ */ /* Set the translation to a fixed value, and translate any existing path. */ /* Used by gschar.c to prepare for a BuildChar or BuildGlyph procedure. */ int gx_translate_to_fixed(register gs_state * pgs, fixed px, fixed py) { double fpx = fixed2float(px); double fdx = fpx - pgs->ctm.tx; double fpy = fixed2float(py); double fdy = fpy - pgs->ctm.ty; fixed dx, dy; int code; if (pgs->ctm.txy_fixed_valid) { dx = float2fixed(fdx); dy = float2fixed(fdy); code = gx_path_translate(pgs->path, dx, dy); if (code < 0) return code; if (pgs->char_tm_valid && pgs->char_tm.txy_fixed_valid) pgs->char_tm.tx_fixed += dx, pgs->char_tm.ty_fixed += dy; } else { if (!gx_path_is_null(pgs->path)) return_error(gs_error_limitcheck); } pgs->ctm.tx = fpx; pgs->ctm.tx_fixed = px; pgs->ctm.ty = fpy; pgs->ctm.ty_fixed = py; pgs->ctm.txy_fixed_valid = true; pgs->ctm_inverse_valid = false; if (pgs->char_tm_valid) { /* Update char_tm now, leaving it valid. */ pgs->char_tm.tx += fdx; pgs->char_tm.ty += fdy; } #ifdef DEBUG if (gs_debug_c('x')) { dlprintf2("[x]translate_to_fixed %g, %g:\n", fixed2float(px), fixed2float(py)); trace_ctm(pgs); dlprintf("[x] char_tm:\n"); trace_matrix_fixed(&pgs->char_tm); } #endif return 0; } /* Scale the CTM and character matrix for oversampling. */ int gx_scale_char_matrix(register gs_state * pgs, int sx, int sy) { #define scale_cxy(s, vx, vy)\ if ( s != 1 )\ { pgs->ctm.vx *= s;\ pgs->ctm.vy *= s;\ pgs->ctm_inverse_valid = false;\ if ( pgs->char_tm_valid )\ { pgs->char_tm.vx *= s;\ pgs->char_tm.vy *= s;\ }\ } scale_cxy(sx, xx, yx); scale_cxy(sy, xy, yy); #undef scale_cxy if_debug2('x', "[x]char scale: %d %d\n", sx, sy); return 0; } /* Compute the coefficients for fast fixed-point distance transformations */ /* from a transformation matrix. */ /* We should cache the coefficients with the ctm.... */ int gx_matrix_to_fixed_coeff(const gs_matrix * pmat, register fixed_coeff * pfc, int max_bits) { gs_matrix ctm; int scale = -10000; int expt, shift; ctm = *pmat; pfc->skewed = 0; if (!is_fzero(ctm.xx)) { discard(frexp(ctm.xx, &scale)); } if (!is_fzero(ctm.xy)) { discard(frexp(ctm.xy, &expt)); if (expt > scale) scale = expt; pfc->skewed = 1; } if (!is_fzero(ctm.yx)) { discard(frexp(ctm.yx, &expt)); if (expt > scale) scale = expt; pfc->skewed = 1; } if (!is_fzero(ctm.yy)) { discard(frexp(ctm.yy, &expt)); if (expt > scale) scale = expt; } /* * There are two multiplications in fixed_coeff_mult: one involves a * factor that may have max_bits significant bits, the other may have * fixed_fraction_bits (_fixed_shift) bits. Ensure that neither one * will overflow. */ if (max_bits < fixed_fraction_bits) max_bits = fixed_fraction_bits; scale = sizeof(long) * 8 - 1 - max_bits - scale; shift = scale - _fixed_shift; if (shift > 0) { pfc->shift = shift; pfc->round = (fixed) 1 << (shift - 1); } else { pfc->shift = 0; pfc->round = 0; scale -= shift; } #define SET_C(c)\ if ( is_fzero(ctm.c) ) pfc->c = 0;\ else pfc->c = (long)ldexp(ctm.c, scale) SET_C(xx); SET_C(xy); SET_C(yx); SET_C(yy); #undef SET_C #ifdef DEBUG if (gs_debug_c('x')) { dlprintf6("[x]ctm: [%6g %6g %6g %6g %6g %6g]\n", ctm.xx, ctm.xy, ctm.yx, ctm.yy, ctm.tx, ctm.ty); dlprintf6(" scale=%d fc: [0x%lx 0x%lx 0x%lx 0x%lx] shift=%d\n", scale, pfc->xx, pfc->xy, pfc->yx, pfc->yy, pfc->shift); } #endif pfc->max_bits = max_bits; return 0; } /* * Handle the case of a large value or a value with a fraction part. * See gxmatrix.h for more details. */ fixed fixed_coeff_mult(fixed value, long coeff, const fixed_coeff *pfc, int maxb) { int shift = pfc->shift; /* * Test if the value is too large for simple long math. */ if ((value + (fixed_1 << (maxb - 1))) & (-fixed_1 << maxb)) { /* The second argument of fixed_mult_quo must be non-negative. */ return (coeff < 0 ? -fixed_mult_quo(value, -coeff, fixed_1 << shift) : fixed_mult_quo(value, coeff, fixed_1 << shift)); } else { /* * The construction above guarantees that the multiplications * won't overflow the capacity of an int. */ return (fixed) arith_rshift(fixed2int_var(value) * coeff + fixed2int(fixed_fraction(value) * coeff) + pfc->round, shift); } } /* ------ Debugging printout ------ */ #ifdef DEBUG /* Print a matrix */ private void trace_matrix_fixed(const gs_matrix_fixed * pmat) { trace_matrix((const gs_matrix *)pmat); if (pmat->txy_fixed_valid) { dprintf2("\t\tt_fixed: [%6g %6g]\n", fixed2float(pmat->tx_fixed), fixed2float(pmat->ty_fixed)); } else { dputs("\t\tt_fixed not valid\n"); } } private void trace_matrix(register const gs_matrix * pmat) { dlprintf6("\t[%6g %6g %6g %6g %6g %6g]\n", pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty); } #endif