xref: /plan9/sys/src/cmd/gs/src/gscoord.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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
ctm_set_inverse(gs_state * pgs)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
gs_initmatrix(gs_state * pgs)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
gs_defaultmatrix(const gs_state * pgs,gs_matrix * pmat)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
gs_setdefaultmatrix(gs_state * pgs,const gs_matrix * pmat)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
gs_currentmatrix(const gs_state * pgs,gs_matrix * pmat)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
gs_setcharmatrix(gs_state * pgs,const gs_matrix * pmat)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
gs_currentcharmatrix(gs_state * pgs,gs_matrix * ptm,bool force)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
gs_setmatrix(gs_state * pgs,const gs_matrix * pmat)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
gs_imager_setmatrix(gs_imager_state * pis,const gs_matrix * pmat)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
gs_settocharmatrix(gs_state * pgs)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
gs_translate(gs_state * pgs,floatp dx,floatp dy)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
gs_scale(gs_state * pgs,floatp sx,floatp sy)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
gs_rotate(gs_state * pgs,floatp ang)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
gs_concat(gs_state * pgs,const gs_matrix * pmat)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
gs_transform(gs_state * pgs,floatp x,floatp y,gs_point * pt)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
gs_dtransform(gs_state * pgs,floatp dx,floatp dy,gs_point * pt)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
gs_itransform(gs_state * pgs,floatp x,floatp y,gs_point * pt)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
gs_idtransform(gs_state * pgs,floatp dx,floatp dy,gs_point * pt)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
gs_imager_idtransform(const gs_imager_state * pis,floatp dx,floatp dy,gs_point * pt)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
gx_translate_to_fixed(register gs_state * pgs,fixed px,fixed py)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
gx_scale_char_matrix(register gs_state * pgs,int sx,int sy)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
gx_matrix_to_fixed_coeff(const gs_matrix * pmat,register fixed_coeff * pfc,int max_bits)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
fixed_coeff_mult(fixed value,long coeff,const fixed_coeff * pfc,int maxb)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
trace_matrix_fixed(const gs_matrix_fixed * pmat)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
trace_matrix(register const gs_matrix * pmat)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
541