1149 lines
36 KiB
C
1149 lines
36 KiB
C
#ifndef lint
|
|
static char sccsid[] = "@(#)charsubs.c 1.1 94/10/31 Copyr 1985-9 Sun Micro";
|
|
#endif lint
|
|
|
|
/*
|
|
* Copyright (c) 1985, 1986, 1987, 1988, 1989 by Sun Microsystems, Inc.
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose and without fee is hereby granted, provided that the above
|
|
* copyright notice appear in all copies and that both that copyright
|
|
* notice and this permission notice are retained, and that the name
|
|
* of Sun Microsystems, Inc., not be used in advertising or publicity
|
|
* pertaining to this software without specific, written prior permission.
|
|
* Sun Microsystems, Inc., makes no representations about the suitability
|
|
* of this software or the interface defined in this software for any
|
|
* purpose. It is provided "as is" without express or implied warranty.
|
|
*/
|
|
/*
|
|
* CGI Text functions
|
|
*/
|
|
|
|
/*
|
|
_cgi_inner_text
|
|
_cgi_get_glyph_polyline
|
|
_cgi_get_glyph
|
|
_cgi_free_text_cache
|
|
invalidate_old_cache_entry
|
|
_cgi_inq_font
|
|
_cgi_enter_font_info
|
|
cc_to_vdc
|
|
fcc_to_vdc
|
|
_cgi_rnd_flt_to_int
|
|
*/
|
|
|
|
#include "cgipriv.h"
|
|
#include <math.h>
|
|
|
|
#ifdef TEXT_CACHE
|
|
int _cgi_notextcache = 1;
|
|
#endif TEXT_CACHE
|
|
|
|
#define SCALE(n) ((n) << CC_VDC_SHIFT)
|
|
#define UNSCALE(n) n = (n < 0 ? \
|
|
-(-(n) >> CC_VDC_SHIFT) : \
|
|
(n) >> CC_VDC_SHIFT)
|
|
|
|
/* External variables and functions */
|
|
Gstate _cgi_state; /* CGI's global state */
|
|
View_surface *_cgi_vws; /* current view surface */
|
|
Outatt *_cgi_att; /* structure containing current attributes */
|
|
int _cgi_pix_mode; /* pixrect equivalent of drawing mode */
|
|
int _cgi_ttext;
|
|
Ccoor _cgi_00; /* coordinate pair (0,0) */
|
|
|
|
extern char *strcat(), *strcpy(); /* makes lint happy */
|
|
|
|
#define MAX_CHAR_PER_FONT 160
|
|
#define MIN(a,b) (((a)<(b))? (a):(b))
|
|
#define MAX(a,b) (((a)>(b))? (a):(b))
|
|
|
|
typedef struct
|
|
{
|
|
long gc_vdc_change_cnt; /* matches vdc_change_cnt */
|
|
long gc_text_change_cnt; /* matches text_change_cnt */
|
|
short gc_font; /* font this glyph is cached from */
|
|
unsigned gc_npts; /* length of gc_coors and gc_mvlist */
|
|
Ccoor *gc_coors; /* polyline that draws this glyph */
|
|
u_char *gc_mvlist; /* move list for gc_coors */
|
|
Ccoor gc_ll, gc_ur; /* pixel bounding box, for clipping */
|
|
Ccoor gc_left; /* left edge/baseline of glyph box */
|
|
Ccoor gc_right; /* right edge/baseline of glyph box */
|
|
} Cglyphcache;
|
|
|
|
typedef struct
|
|
{
|
|
unsigned gi_npts;
|
|
short gi_xmin;
|
|
short gi_xmax;
|
|
short gi_ymin;
|
|
short gi_ymax;
|
|
} Cglyphinfo;
|
|
|
|
typedef struct
|
|
{
|
|
Cglyphcache *g_cache; /* pointer to cached glyph information */
|
|
Ccoor g_ref; /* reference position this glyph was set at */
|
|
Ccoor g_off; /* pixel position to draw this glyph */
|
|
Ccoor g_left_box; /* left side of bounding box when set */
|
|
Ccoor g_right_box;/* right side of bounding box when set */
|
|
} Cglyph;
|
|
|
|
static Cfontinfo _cgi_fontinfo[SYMBOLS + 1];
|
|
static Cglyphcache _cgi_glyph_cache[MAX_CHAR_PER_FONT];
|
|
Cfontinfo _cgi_inq_font();
|
|
static Ccoor cc_to_vdc(), fcc_to_vdc(), cc_to_vdc_unscaled();
|
|
static Ccoor fcc_to_vdc_unscaled ();
|
|
|
|
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: _cgi_inner_text */
|
|
/* */
|
|
/* This function renders text on a view surface. It */
|
|
/* is the most important text routine. The first */
|
|
/* decision made is about character precision. If the */
|
|
/* character precision is STRING, the pixrect (pw) */
|
|
/* text is used, otherwise the text is drawn with vectors. */
|
|
/* Both vector and pixrect text can be aligned. Pixrect text */
|
|
/* path is restricted to RIGHT and LEFT. */
|
|
/* Vector text can be skewed. The first process */
|
|
/* attempts to take care of alignment. Skewing has already */
|
|
/* been done by the vector transform. When the text is actually being */
|
|
/* rendered, the path is taken into consideration. */
|
|
/* */
|
|
/****************************************************************************/
|
|
Cerror _cgi_inner_text(vws, origin, tstring, extent)
|
|
View_surface *vws; /* view surface to do text upon */
|
|
Ccoor *origin; /* VDC coordinates */
|
|
char *tstring; /* text to draw */
|
|
Ccoor *extent; /* if non-null, return extent box, don;t draw */
|
|
{
|
|
register Cglyph *glyphP;
|
|
register Cglyph *newglyphP;
|
|
register Outatt *attP = vws->att;
|
|
register View_surface *vwsP = vws;
|
|
register short cnt;
|
|
int nglyph;
|
|
Cerror err = NO_ERROR;
|
|
Cerror terr;
|
|
int txtop;
|
|
Cint fixed_font;
|
|
Cpathtype text_path;
|
|
int vertical = 0;
|
|
Cglyph glyph_list[MAXCHAR];
|
|
Chaligntype halign;
|
|
Cvaligntype valign;
|
|
struct pr_subregion bound;
|
|
Ccoor ref; /* reference for current character */
|
|
Ccoor off; /* offset for char box, and glyphs in it */
|
|
Ccoor concat; /* relative new concatentation point */
|
|
Ccoor lleft, uleft, uright; /* text extent box */
|
|
Cfontinfo font_info; /* basic metrics of current font */
|
|
Cfloat continuous; /* temp for continuous alignment value */
|
|
int loop;
|
|
|
|
txtop = _cgi_pix_mode | PIX_COLOR(attP->text.attr.color);
|
|
text_path = attP->text.attr.path;
|
|
if (text_path == DOWN || text_path == UP)
|
|
fixed_font = 1;
|
|
else
|
|
fixed_font = attP->text.fixed_font;
|
|
halign = attP->text.attr.halign;
|
|
valign = attP->text.attr.valign;
|
|
concat = _cgi_00;
|
|
off = *origin;
|
|
|
|
if (strlen(tstring) > MAXCHAR)
|
|
return (EMEMSPAC);
|
|
if (strlen(tstring) <= 0)
|
|
{
|
|
if (extent != (Ccoor *) 0)
|
|
{
|
|
extent[0] = _cgi_00;
|
|
extent[1] = _cgi_00;
|
|
extent[2] = _cgi_00;
|
|
}
|
|
return (err);
|
|
}
|
|
|
|
if (attP->text.attr.precision == STRING)
|
|
{ /* raster text */
|
|
char *fontname;
|
|
|
|
if (attP->text.attr.current_font != _cgi_state.open_font.num)
|
|
{
|
|
/* reset font if necessary */
|
|
if (_cgi_state.open_font.ptr != (struct pixfont *) 0)
|
|
{
|
|
(void) pf_close(_cgi_state.open_font.ptr);
|
|
_cgi_state.open_font.num = -1;
|
|
}
|
|
switch (attP->text.attr.current_font)
|
|
{
|
|
case (ROMAN):
|
|
fontname = "/usr/lib/fonts/fixedwidthfonts/gallant.r.10";
|
|
break;
|
|
case (SCRIPT):
|
|
fontname = "/usr/lib/fonts/fixedwidthfonts/sail.r.6";
|
|
break;
|
|
default:
|
|
/* load default font */
|
|
fontname = "/usr/lib/fonts/fixedwidthfonts/gacha.r.8";
|
|
break;
|
|
} /* font set */
|
|
_cgi_state.open_font.ptr = pf_open(fontname);
|
|
if (_cgi_state.open_font.ptr == (struct pixfont *) 0)
|
|
_cgi_perror(fontname);
|
|
else
|
|
_cgi_state.open_font.num = attP->text.attr.current_font;
|
|
}
|
|
|
|
if (_cgi_state.open_font.ptr == (struct pixfont *) 0)
|
|
return (EFILACC);
|
|
|
|
/* get concatenation point for raster text */
|
|
vertical = 0;
|
|
text_path = RIGHT;
|
|
|
|
/* generate values for font_info structure */
|
|
(void) pf_textbound(&bound, strlen("X"), _cgi_state.open_font.ptr, "X");
|
|
_cgi_rev_dev_scale_only(font_info.ft_topline.x, font_info.ft_topline.y,
|
|
0, bound.pos.y + 1);
|
|
_cgi_rev_dev_scale_only(font_info.ft_bottomline.x,
|
|
font_info.ft_bottomline.y,
|
|
0, bound.pos.y + bound.size.y);
|
|
font_info.ft_capline = font_info.ft_topline;
|
|
|
|
font_info.ft_height.x = 0;
|
|
font_info.ft_height.y = font_info.ft_topline.y
|
|
- font_info.ft_bottomline.y;
|
|
|
|
_cgi_rev_dev_scale_only(font_info.ft_left.x, font_info.ft_left.y,
|
|
bound.pos.x, 0);
|
|
_cgi_rev_dev_scale_only(font_info.ft_right.x, font_info.ft_right.y,
|
|
bound.pos.x + bound.size.x, 0);
|
|
|
|
(void) pf_textbound(&bound, strlen(tstring),
|
|
_cgi_state.open_font.ptr, tstring);
|
|
lleft.x = font_info.ft_left.x;
|
|
lleft.y = font_info.ft_bottomline.y;
|
|
uleft.x = font_info.ft_left.x;
|
|
uleft.y = font_info.ft_topline.y;
|
|
_cgi_rev_dev_scale_only(uright.x, uright.y,
|
|
bound.pos.x + bound.size.x, bound.pos.y + 1);
|
|
ref.x = uright.x - uleft.x;
|
|
ref.y = 0;
|
|
}
|
|
else
|
|
{ /* vector text */
|
|
/*
|
|
*PLEASE NOTE - PLEASE NOTE - PLEASE NOTE - PLEASE_NOTE
|
|
* from here to the end of this else block, the coordinate
|
|
* values are unscaled. This was done to get better rounding
|
|
* on integer valued. Also note that _cgi_get_glyph works
|
|
* in unscaled coordinates
|
|
*/
|
|
char *cp, *sp;
|
|
char ch, string_buf[MAXCHAR + 1];
|
|
Cfloat spfac;
|
|
Ccoor charwidth; /* vector for character width */
|
|
Ccoor space; /* vector for inter-character spacing */
|
|
Ccoor newspace; /* scaled space */
|
|
|
|
/*
|
|
* Iterate through the string getting pointers to the cached glyph
|
|
* information, and calculating offsets for each glyph within the
|
|
* string. RIGHT and LEFT text are both set to the RIGHT, but the
|
|
* contents of the string are reversed for LEFT path, with (0,0) being
|
|
* the left side and baseline of the leftmost character (ie. the first
|
|
* character set, the one at glyph_list[0]).
|
|
*
|
|
* DOWN and UP text are both set DOWN, but the contents of the string are
|
|
* reversed for UP path, with (0,0) being the center and baseline of
|
|
* the topmost character (ie. the first character set, the one at
|
|
* glyph_list[0]).
|
|
*
|
|
* After the entire string is set, 'off.x' and 'off.y' are calculated to
|
|
* offset the entire character box so that the concatenation point is
|
|
* aligned appropriately for the path and alignment in use.
|
|
*/
|
|
font_info = _cgi_inq_font(attP->text.attr.current_font);
|
|
ref = _cgi_00;
|
|
glyphP = glyph_list;
|
|
spfac = attP->text.attr.space * font_info.ft_cap;
|
|
|
|
switch (text_path)
|
|
{
|
|
case RIGHT:
|
|
space = fcc_to_vdc(spfac, 0.);
|
|
newspace = fcc_to_vdc_unscaled (spfac,0.);
|
|
cp = tstring;
|
|
break;
|
|
case DOWN:
|
|
vertical = 1;
|
|
space = fcc_to_vdc(0., -spfac);
|
|
newspace = fcc_to_vdc_unscaled(0., -spfac);
|
|
cp = tstring;
|
|
break;
|
|
case LEFT:
|
|
space = fcc_to_vdc(spfac, 0.);
|
|
newspace = fcc_to_vdc_unscaled(spfac, 0.);
|
|
|
|
/* Reverse the given string, and draw it RIGHT */
|
|
cp = &string_buf[strlen(tstring)];
|
|
*cp = 0;
|
|
for (sp = tstring; *sp;)
|
|
*--cp = *sp++;
|
|
break;
|
|
case UP:
|
|
vertical = 1;
|
|
space = fcc_to_vdc(0., -spfac);
|
|
newspace = fcc_to_vdc_unscaled(0., -spfac);
|
|
|
|
/* Reverse the given string, and draw it DOWN */
|
|
cp = &string_buf[strlen(tstring)];
|
|
*cp = 0;
|
|
for (sp = tstring; *sp;)
|
|
*--cp = *sp++;
|
|
break;
|
|
}
|
|
|
|
while ((ch = *cp++) != '\0')
|
|
{
|
|
/* Query font for character info and allocate space for vectors */
|
|
err = _cgi_get_glyph(attP->text.attr.current_font, ch,
|
|
&(glyphP->g_cache));
|
|
if (err)
|
|
return (err);
|
|
|
|
/* if variable width font use actual character size */
|
|
if (fixed_font)
|
|
{
|
|
/* MDH 880324: Didn't used to account for exp_factor here.
|
|
* Added, but note that this will also 'shift' the edges
|
|
* of the glyph box if they are both on the same side of the
|
|
* X axis. As far as I know, no glyph does this. */
|
|
glyphP->g_left_box.x = SCALE(_cgi_rnd_flt_to_int(
|
|
((Cfloat)font_info.ft_left.x) * attP->text.attr.exp_factor));
|
|
glyphP->g_right_box.x = SCALE(_cgi_rnd_flt_to_int(
|
|
((Cfloat)font_info.ft_right.x)* attP->text.attr.exp_factor));
|
|
glyphP->g_left_box.y = SCALE(font_info.ft_left.y);
|
|
glyphP->g_right_box.y = SCALE(font_info.ft_right.y);
|
|
}
|
|
else
|
|
{
|
|
glyphP->g_left_box = glyphP->g_cache->gc_left;
|
|
glyphP->g_right_box = glyphP->g_cache->gc_right;
|
|
}
|
|
charwidth.x = glyphP->g_right_box.x - glyphP->g_left_box.x;
|
|
charwidth.y = glyphP->g_right_box.y - glyphP->g_left_box.y;
|
|
glyphP->g_ref = ref;
|
|
|
|
/*
|
|
* Calculate character position information based on text path
|
|
*/
|
|
switch (text_path)
|
|
{
|
|
case RIGHT:
|
|
case LEFT:
|
|
glyphP->g_off.x = ref.x - glyphP->g_left_box.x;
|
|
glyphP->g_off.y = ref.y - glyphP->g_left_box.y;
|
|
ref.x += charwidth.x + newspace.x;
|
|
ref.y += charwidth.y + newspace.y;
|
|
break;
|
|
case UP:
|
|
case DOWN:
|
|
glyphP->g_off.x = ref.x;
|
|
glyphP->g_off.y = ref.y;
|
|
|
|
/*
|
|
* Subtract the height vector (move down), and add the spacing
|
|
* (already computed for downward movement).
|
|
*/
|
|
ref.x += SCALE(-font_info.ft_height.x) + newspace.x;
|
|
ref.y += SCALE(-font_info.ft_height.y) + newspace.y;
|
|
break;
|
|
}
|
|
glyphP++;
|
|
}
|
|
|
|
/*
|
|
* now scale g_left_box, g_right_box, g_ref, and g_off
|
|
*/
|
|
nglyph = glyphP - glyph_list;
|
|
newglyphP = glyph_list;
|
|
|
|
for (loop=0;loop<nglyph;loop++) {
|
|
UNSCALE(newglyphP->g_off.x);
|
|
UNSCALE(newglyphP->g_off.y);
|
|
UNSCALE(newglyphP->g_left_box.x);
|
|
UNSCALE(newglyphP->g_left_box.y);
|
|
UNSCALE(newglyphP->g_right_box.x);
|
|
UNSCALE(newglyphP->g_right_box.y);
|
|
UNSCALE(newglyphP->g_ref.x);
|
|
UNSCALE(newglyphP->g_ref.y);
|
|
newglyphP++;
|
|
}
|
|
UNSCALE(ref.x);
|
|
UNSCALE(ref.y);
|
|
|
|
--glyphP; /* leave it pointing at last glyph */
|
|
if (vertical)
|
|
{
|
|
lleft.x = glyphP->g_off.x
|
|
+ font_info.ft_left.x
|
|
+ font_info.ft_bottomline.x;
|
|
lleft.y = glyphP->g_off.y
|
|
+ font_info.ft_left.y
|
|
+ font_info.ft_bottomline.y;
|
|
uleft.x = glyph_list[0].g_off.x
|
|
+ font_info.ft_left.x
|
|
+ font_info.ft_topline.x;
|
|
uleft.y = glyph_list[0].g_off.y
|
|
+ font_info.ft_left.y
|
|
+ font_info.ft_topline.y;
|
|
uright.x = glyph_list[0].g_off.x
|
|
+ font_info.ft_right.x
|
|
+ font_info.ft_topline.x;
|
|
uright.y = glyph_list[0].g_off.y
|
|
+ font_info.ft_right.y
|
|
+ font_info.ft_topline.y;
|
|
|
|
/*
|
|
* The vertical cases (UP,DOWN) were set with the (0,0) reference
|
|
* at (center,baseline) of the glyph. Adjust this to
|
|
* (left,baseline) to match the horizontal cases so the alignment
|
|
* below can treat both the same.
|
|
*/
|
|
off.x -= font_info.ft_left.x;
|
|
off.y -= font_info.ft_left.y;
|
|
}
|
|
else
|
|
{
|
|
lleft.x = glyph_list[0].g_off.x
|
|
+ glyph_list[0].g_left_box.x
|
|
+ font_info.ft_bottomline.x;
|
|
lleft.y = glyph_list[0].g_off.y
|
|
+ glyph_list[0].g_left_box.y
|
|
+ font_info.ft_bottomline.y;
|
|
uleft.x = glyph_list[0].g_off.x
|
|
+ glyph_list[0].g_left_box.x
|
|
+ font_info.ft_topline.x;
|
|
uleft.y = glyph_list[0].g_off.y
|
|
+ glyph_list[0].g_left_box.y
|
|
+ font_info.ft_topline.y;
|
|
uright.x = glyphP->g_off.x
|
|
+ glyphP->g_right_box.x
|
|
+ font_info.ft_topline.x;
|
|
uright.y = glyphP->g_off.y
|
|
+ glyphP->g_right_box.y
|
|
+ font_info.ft_topline.y;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Align text: 1) Adjust halign and valign for NORMAL/NRMAL cases. 2) If
|
|
* path is UP or DOWN, set 'off' to adjust the (0,0) reference point from
|
|
* center/baseline to left/baseline, to make it match horizontal text, so
|
|
* alignment can work the same on both. 3) Do horizontal alignment: adjust
|
|
* 'off' 4) Do vertical alignment: adjust 'off'
|
|
*
|
|
* The text extent box (lleft, uleft, uright) is calculated to match the
|
|
* coordinates the glyph point lists are in, ie. 'off' must be added to
|
|
* them to get the actual pixel positions. This allows these coordinates to
|
|
* be treated the same as the vector information.
|
|
*
|
|
* The new concatentation point is relative to the current origin inde-
|
|
* pendent of off.x or off.y. See GKS standard, ANSI X3.124-1985, p 159.
|
|
*/
|
|
switch (text_path)
|
|
{
|
|
case RIGHT:
|
|
if (halign == NRMAL)
|
|
halign = LFT;
|
|
if (valign == NORMAL)
|
|
valign = BASE;
|
|
break;
|
|
case LEFT:
|
|
if (halign == NRMAL)
|
|
halign = RGHT;
|
|
if (valign == NORMAL)
|
|
valign = BASE;
|
|
break;
|
|
case UP:
|
|
if (halign == NRMAL)
|
|
halign = CNTER;
|
|
if (valign == NORMAL)
|
|
valign = BASE;
|
|
break;
|
|
case DOWN:
|
|
if (halign == NRMAL)
|
|
halign = CNTER;
|
|
if (valign == NORMAL)
|
|
valign = TOP;
|
|
break;
|
|
}
|
|
|
|
switch (halign)
|
|
{
|
|
case LFT:
|
|
if (!vertical)
|
|
concat = ref;
|
|
break;
|
|
case CNTER:
|
|
off.x -= (uright.x - uleft.x) / 2;
|
|
off.y -= (uright.y - uleft.y) / 2;
|
|
break;
|
|
case RGHT:
|
|
off.x -= uright.x - uleft.x;
|
|
off.y -= uright.y - uleft.y;
|
|
if (!vertical)
|
|
{
|
|
concat.x = -ref.x;
|
|
concat.y = -ref.y;
|
|
}
|
|
break;
|
|
case CNT:
|
|
continuous = attP->text.attr.hcalind;
|
|
if (text_path == LEFT)
|
|
continuous = 1.0 - continuous;
|
|
off.x -= (uright.x - uleft.x) * continuous;
|
|
off.y -= (uright.y - uleft.y) * continuous;
|
|
break;
|
|
}
|
|
|
|
switch (valign)
|
|
{
|
|
case TOP:
|
|
/* topline of topmost character */
|
|
off.x -= font_info.ft_topline.x;
|
|
off.y -= font_info.ft_topline.y;
|
|
if (vertical)
|
|
concat = ref;
|
|
break;
|
|
case CAP:
|
|
/* capline of topmost character */
|
|
off.x -= font_info.ft_capline.x;
|
|
off.y -= font_info.ft_capline.y;
|
|
if (vertical)
|
|
concat = ref;
|
|
break;
|
|
case HALF:
|
|
/*
|
|
* halfway between the halfline of the topmost character and the
|
|
* halfline of the bottommost character. Halfline is assumed to be at
|
|
* capline/2.
|
|
*/
|
|
off.x -= font_info.ft_capline.x / 2;
|
|
off.y -= font_info.ft_capline.y / 2;
|
|
if (vertical)
|
|
{
|
|
off.x -= glyphP->g_ref.x / 2;
|
|
off.y -= glyphP->g_ref.y / 2;
|
|
}
|
|
break;
|
|
case BOTTOM:
|
|
/* offset to bottomline of bottommost character */
|
|
off.x -= font_info.ft_bottomline.x;
|
|
off.y -= font_info.ft_bottomline.y;
|
|
/* falls through to BASE case */
|
|
case BASE:
|
|
/* baseline of bottommost character */
|
|
if (vertical)
|
|
{
|
|
off.x -= glyphP->g_ref.x;
|
|
off.y -= glyphP->g_ref.y;
|
|
concat.x = -ref.x;
|
|
concat.y = -ref.y;
|
|
}
|
|
break;
|
|
case CONT:
|
|
continuous = attP->text.attr.vcalind;
|
|
if (text_path == UP)
|
|
continuous = 1.0 - continuous;
|
|
off.x -= (uleft.x - lleft.x) * continuous;
|
|
off.y -= (uleft.y - lleft.y) * continuous;
|
|
break;
|
|
}
|
|
|
|
/* Update saved concatenation point */
|
|
attP->text.concat_pt.x = concat.x + origin->x;
|
|
attP->text.concat_pt.y = concat.y + origin->y;
|
|
|
|
/*
|
|
* Draw text, if requested
|
|
*/
|
|
if (extent == (Ccoor *) 0)
|
|
{
|
|
Ccoor dc_off;
|
|
|
|
if (attP->text.attr.precision == STRING)
|
|
{
|
|
_cgi_devscale(off.x, off.y, dc_off.x, dc_off.y);
|
|
if (_cgi_ttext)
|
|
/* draw transparent text */
|
|
pw_ttext(vwsP->sunview.pw, dc_off.x, dc_off.y, txtop,
|
|
_cgi_state.open_font.ptr, tstring);
|
|
else
|
|
/* draw solid text */
|
|
pw_text(vwsP->sunview.pw, dc_off.x, dc_off.y, txtop,
|
|
_cgi_state.open_font.ptr, tstring);
|
|
}
|
|
else
|
|
{
|
|
Ccoor saved_vdc_off, vdc_off;
|
|
Clinatt saved_line;
|
|
Cint saved_conv_width;
|
|
|
|
/*
|
|
* There ought to be a better way than setting these global
|
|
* variables... wcl 6/27/86
|
|
*/
|
|
saved_vdc_off.x = vwsP->xform.off.x;
|
|
saved_vdc_off.y = vwsP->xform.off.y;
|
|
saved_line = attP->line;
|
|
saved_conv_width = vwsP->conv.line_width;
|
|
attP->line.color = attP->text.attr.color;
|
|
attP->line.width = (Cfloat) 1.0;
|
|
vwsP->conv.line_width = 1;
|
|
attP->line.style = SOLID;
|
|
glyphP = glyph_list;
|
|
pw_lock(vwsP->sunview.pw, &vwsP->sunview.lock_rect);
|
|
for (cnt = nglyph; --cnt >= 0; glyphP++)
|
|
{
|
|
if (glyphP->g_cache->gc_npts >= 2)
|
|
{
|
|
vdc_off.x = off.x + glyphP->g_off.x;
|
|
vdc_off.y = off.y + glyphP->g_off.y;
|
|
vwsP->xform.off.x = saved_vdc_off.x;
|
|
vwsP->xform.off.y = saved_vdc_off.y;
|
|
_cgi_devscale(vdc_off.x, vdc_off.y, dc_off.x, dc_off.y);
|
|
vwsP->xform.off.x = dc_off.x;
|
|
vwsP->xform.off.y = dc_off.y;
|
|
terr = _cgi_polyline(vwsP, (int) glyphP->g_cache->gc_npts,
|
|
glyphP->g_cache->gc_coors,
|
|
glyphP->g_cache->gc_mvlist,
|
|
OFFSET_ONLY);
|
|
/* If any error occurs, save it, and continue */
|
|
if (!err)
|
|
err = terr;
|
|
}
|
|
}
|
|
pw_unlock(vwsP->sunview.pw);
|
|
vwsP->conv.line_width = saved_conv_width;
|
|
attP->line = saved_line;
|
|
vwsP->xform.off.x = saved_vdc_off.x;
|
|
vwsP->xform.off.y = saved_vdc_off.y;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Return the bounding box to cgipw_inquire_text_extent.
|
|
*/
|
|
extent[0].x = lleft.x + off.x;
|
|
extent[0].y = lleft.y + off.y;
|
|
extent[1].x = uleft.x + off.x;
|
|
extent[1].y = uleft.y + off.y;
|
|
extent[2].x = uright.x + off.x;
|
|
extent[2].y = uright.y + off.y;
|
|
}
|
|
return (err);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: _cgi_get_glyph_polyline */
|
|
/* */
|
|
/* Return a Ccoor pointlist, and a pw_polyline-compatible move list */
|
|
/* for a glyph in a font. The reference point in return values */
|
|
/* is X==0 at lateral center of character box, Y==0 at baseline */
|
|
/****************************************************************************/
|
|
_cgi_get_glyph_polyline(font, ch, polypts, mvlist, glyph_infoP)
|
|
int font;
|
|
char ch;
|
|
Ccoor *polypts;
|
|
u_char *mvlist;
|
|
Cglyphinfo *glyph_infoP;
|
|
{
|
|
register Ccoor *polyptP;
|
|
register u_char *mvP;
|
|
register char *ptP;
|
|
register short npts, penup, realpts;
|
|
char *pt_base;
|
|
int nglyphs;
|
|
int ymin, ymax;
|
|
|
|
if (font < 4)
|
|
_cgi_scribefont(font, ch, &pt_base, &nglyphs);
|
|
else
|
|
_cgi_stickfont(font - 4, ch, &pt_base, &nglyphs);
|
|
|
|
ptP = pt_base;
|
|
polyptP = polypts;
|
|
mvP = mvlist;
|
|
npts = *ptP++;
|
|
glyph_infoP->gi_xmin = *ptP++;
|
|
glyph_infoP->gi_xmax = *ptP++;
|
|
realpts = 0;
|
|
ymin = 0;
|
|
ymax = 0;
|
|
penup = 1;
|
|
while (--npts >= 0)
|
|
{
|
|
if (*ptP == 31)
|
|
{
|
|
penup = 1;
|
|
ptP += 2;
|
|
}
|
|
else
|
|
{
|
|
polyptP->x = *ptP++;
|
|
polyptP->y = *ptP++ + 9; /* move vertical ref. to baseline */
|
|
if (polyptP->y < ymin)
|
|
ymin = polyptP->y;
|
|
if (polyptP->y > ymax)
|
|
ymax = polyptP->y;
|
|
polyptP++;
|
|
*mvP++ = penup;
|
|
penup = 0;
|
|
realpts++;
|
|
}
|
|
}
|
|
mvlist[0] = 0;
|
|
glyph_infoP->gi_npts = realpts;
|
|
glyph_infoP->gi_ymin = ymin;
|
|
glyph_infoP->gi_ymax = ymax;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: _cgi_get_glyph */
|
|
/* */
|
|
/* Return glyph cache entry, building it if necessary. */
|
|
/****************************************************************************/
|
|
Cerror _cgi_get_glyph(font, ch, glyph_cacheP)
|
|
int font;
|
|
char ch;
|
|
Cglyphcache **glyph_cacheP;
|
|
{
|
|
register Ccoor *csptP;
|
|
register Ccoor *coorP;
|
|
register Cglyphcache *gcP;
|
|
register Conv_text *textP = &_cgi_vws->conv.text;
|
|
register short i;
|
|
register int sin_up, cos_base, cos_up, sin_base;
|
|
Cint xmin = 0x7fffffff;
|
|
Cint xmax = -0x7fffffff;
|
|
Cint ymin = 0x7fffffff;
|
|
Cint ymax = -0x7fffffff;
|
|
int rx, ry;
|
|
Ccoor polypts[MAXPTS];
|
|
u_char mvlist_temp[MAXPTS];
|
|
Cglyphinfo glyph_info;
|
|
|
|
#define X_CC_TO_DC(x,y) ((((x * cos_base) + (y * cos_up)) \
|
|
+ (1 << (CC_DC_SHIFT - 1))) >> CC_DC_SHIFT)
|
|
#define Y_CC_TO_DC(x,y) ((((x * sin_base) + (y * sin_up)) \
|
|
+ (1 << (CC_DC_SHIFT - 1))) >> CC_DC_SHIFT)
|
|
|
|
gcP = &_cgi_glyph_cache[(int) ch];
|
|
#ifdef TEXT_CACHE
|
|
if (_cgi_notextcache
|
|
|| gcP->gc_vdc_change_cnt != _cgi_state.vdc_change_cnt
|
|
|| gcP->gc_text_change_cnt != _cgi_state.text_change_cnt
|
|
|| gcP->gc_font != font)
|
|
{
|
|
#endif TEXT_CACHE
|
|
/*
|
|
* Invalidate old entry: free space associated with it, and sets its
|
|
* change_cnt values to 0. The latter is not necessary for the current
|
|
* routine, but allows invalidate_old_cache_entry() to be used
|
|
* elsewhere.
|
|
*/
|
|
invalidate_old_cache_entry(gcP);
|
|
|
|
/*
|
|
* Get new transform parameters; reset transform parameters stored in
|
|
* glyph cache structure; get character-space copy of point list.
|
|
*/
|
|
gcP->gc_vdc_change_cnt = _cgi_state.vdc_change_cnt;
|
|
gcP->gc_text_change_cnt = _cgi_state.text_change_cnt;
|
|
gcP->gc_font = font;
|
|
sin_base = textP->dc_sin_base;
|
|
cos_base = textP->dc_cos_base;
|
|
sin_up = textP->dc_sin_up;
|
|
cos_up = textP->dc_cos_up;
|
|
|
|
_cgi_get_glyph_polyline(font, ch, polypts, mvlist_temp, &glyph_info);
|
|
gcP->gc_npts = glyph_info.gi_npts;
|
|
|
|
if (glyph_info.gi_npts >= 2)
|
|
{
|
|
/*
|
|
* Allocate new space and copy temporary move list to it.
|
|
*/
|
|
gcP->gc_coors = (Ccoor *) malloc(gcP->gc_npts * sizeof(Ccoor));
|
|
if (gcP->gc_coors == (Ccoor *) 0)
|
|
return (EMEMSPAC);
|
|
gcP->gc_mvlist = (u_char *) malloc(gcP->gc_npts * sizeof(u_char));
|
|
if (gcP->gc_mvlist == (u_char *) 0)
|
|
{
|
|
free((char *) gcP->gc_coors);
|
|
return (EMEMSPAC);
|
|
}
|
|
bcopy((char *) mvlist_temp, (char *) gcP->gc_mvlist, (int) gcP->gc_npts);
|
|
/*
|
|
* Transform coordinates to DC and copy to allocated space.
|
|
*/
|
|
csptP = polypts;
|
|
coorP = gcP->gc_coors;
|
|
for (i = gcP->gc_npts; --i >= 0;)
|
|
{
|
|
rx = csptP->x;
|
|
ry = csptP++->y;
|
|
coorP->x = X_CC_TO_DC(rx, ry);
|
|
coorP->y = Y_CC_TO_DC(rx, ry);
|
|
xmin = MIN(xmin, coorP->x);
|
|
xmax = MAX(xmax, coorP->x);
|
|
ymin = MIN(ymin, coorP->y);
|
|
ymax = MAX(ymax, coorP->y);
|
|
coorP++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Cint xleft, yleft, xright, yright;
|
|
|
|
/*
|
|
* Glyphs without any vectors still have two 'points' that are
|
|
* implicit: the left and right side of the box. These should be
|
|
* transformed, and used to set xmin, xmax, ymin, ymax.
|
|
*/
|
|
xleft = X_CC_TO_DC(glyph_info.gi_xmin, 0);
|
|
yleft = Y_CC_TO_DC(glyph_info.gi_xmin, 0);
|
|
xright = X_CC_TO_DC(glyph_info.gi_xmax, 0);
|
|
yright = Y_CC_TO_DC(glyph_info.gi_xmax, 0);
|
|
xmin = MIN(xleft, xright);
|
|
ymin = MIN(yleft, yright);
|
|
xmax = MAX(xleft, xright);
|
|
ymax = MAX(yleft, yright);
|
|
}
|
|
gcP->gc_ll.x = xmin;
|
|
gcP->gc_ll.y = ymax;
|
|
gcP->gc_ur.x = xmax;
|
|
gcP->gc_ur.y = ymin;
|
|
gcP->gc_left = cc_to_vdc_unscaled(glyph_info.gi_xmin, 0);
|
|
gcP->gc_right = cc_to_vdc_unscaled(glyph_info.gi_xmax, 0);
|
|
#ifdef TEXT_CACHE
|
|
}
|
|
#endif TEXT_CACHE
|
|
|
|
*glyph_cacheP = gcP;
|
|
return (NO_ERROR);
|
|
|
|
#undef X_CC_TO_DC(x,y)
|
|
#undef Y_CC_TO_DC(x,y)
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: _cgi_free_text_cache */
|
|
/* */
|
|
/* Free all malloc'd space associated with glyph cache, and */
|
|
/* invalidate all entries. */
|
|
/* */
|
|
/****************************************************************************/
|
|
_cgi_free_text_cache()
|
|
{
|
|
register Cglyphcache *gcP;
|
|
register int cnt;
|
|
|
|
gcP = _cgi_glyph_cache;
|
|
for (cnt = MAX_CHAR_PER_FONT; --cnt >= 0; gcP++)
|
|
invalidate_old_cache_entry(gcP);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: invalidate_old_cache_entry */
|
|
/* */
|
|
/* Free malloc'd space associated with a single glyph cache entry, */
|
|
/* and invalidate it by setting change_cnt to 0. */
|
|
/* */
|
|
/****************************************************************************/
|
|
static invalidate_old_cache_entry(gcP)
|
|
register Cglyphcache *gcP;
|
|
{
|
|
if (gcP->gc_coors != (Ccoor *) NULL)
|
|
{
|
|
free((char *) gcP->gc_coors);
|
|
gcP->gc_coors = NULL;
|
|
}
|
|
if (gcP->gc_mvlist != (u_char *) NULL)
|
|
{
|
|
free((char *) gcP->gc_mvlist);
|
|
gcP->gc_mvlist = NULL;
|
|
}
|
|
gcP->gc_vdc_change_cnt = 0;
|
|
gcP->gc_text_change_cnt = 0;
|
|
gcP->gc_font = NO_FONT;
|
|
gcP->gc_npts = 0;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: _cgi_inq_font */
|
|
/* */
|
|
/* Returns a pointer to a Cfontinfo structure for the given font. */
|
|
/****************************************************************************/
|
|
Cfontinfo _cgi_inq_font(font)
|
|
int font;
|
|
{
|
|
register Cfontinfo *fontP;
|
|
|
|
fontP = &_cgi_fontinfo[font];
|
|
if (!fontP->ft_flag)
|
|
_cgi_enter_font_info(font);
|
|
|
|
#ifdef TEXT_CACHE
|
|
if (_cgi_notextcache
|
|
|| fontP->ft_vdc_change_cnt != _cgi_state.vdc_change_cnt
|
|
|| fontP->ft_text_change_cnt != _cgi_state.text_change_cnt)
|
|
{
|
|
#endif TEXT_CACHE
|
|
fontP->ft_vdc_change_cnt = _cgi_state.vdc_change_cnt;
|
|
fontP->ft_text_change_cnt = _cgi_state.text_change_cnt;
|
|
fontP->ft_topline = cc_to_vdc(0, fontP->ft_top);
|
|
fontP->ft_capline = cc_to_vdc(0, fontP->ft_cap);
|
|
fontP->ft_bottomline = cc_to_vdc(0, fontP->ft_bottom);
|
|
fontP->ft_height.x = fontP->ft_topline.x - fontP->ft_bottomline.x;
|
|
fontP->ft_height.y = fontP->ft_topline.y - fontP->ft_bottomline.y;
|
|
fontP->ft_left = cc_to_vdc(fontP->ft_xmin, 0);
|
|
fontP->ft_right = cc_to_vdc(fontP->ft_xmax, 0);
|
|
#ifdef TEXT_CACHE
|
|
}
|
|
#endif TEXT_CACHE
|
|
|
|
return (*fontP);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: _cgi_enter_font_info */
|
|
/* */
|
|
/* Fills in a Cfontinfo structure by reading the glyphs in the font. */
|
|
/****************************************************************************/
|
|
_cgi_enter_font_info(font)
|
|
int font;
|
|
{
|
|
register short i;
|
|
char ch;
|
|
int nglyphs;
|
|
char *pt_base;
|
|
int font_top = 0;
|
|
int font_cap = 0;
|
|
int font_bottom = 0;
|
|
int font_xmin = 0;
|
|
int font_xmax = 0;
|
|
Ccoor polypts[MAXPTS];
|
|
u_char mvlist[MAXPTS];
|
|
Cglyphinfo glyph_info;
|
|
|
|
/* Get number of glyphs in font */
|
|
ch = ' ';
|
|
if (font < 4)
|
|
_cgi_scribefont(font, ch, &pt_base, &nglyphs);
|
|
else
|
|
_cgi_stickfont(font - 4, ch, &pt_base, &nglyphs);
|
|
|
|
for (i = nglyphs; --i >= 0;)
|
|
{
|
|
_cgi_get_glyph_polyline(font, ch, polypts, mvlist, &glyph_info);
|
|
font_top = MAX(font_top, glyph_info.gi_ymax);
|
|
if ('A' <= ch && ch <= 'Z')
|
|
font_cap = MAX(font_cap, glyph_info.gi_ymax);
|
|
font_bottom = MIN(font_bottom, glyph_info.gi_ymin);
|
|
font_xmin = MIN(font_xmin, glyph_info.gi_xmin);
|
|
font_xmax = MAX(font_xmax, glyph_info.gi_xmax);
|
|
ch++;
|
|
}
|
|
|
|
_cgi_fontinfo[font].ft_top = font_top;
|
|
_cgi_fontinfo[font].ft_cap = font_cap;
|
|
_cgi_fontinfo[font].ft_bottom = font_bottom;
|
|
_cgi_fontinfo[font].ft_xmin = font_xmin;
|
|
_cgi_fontinfo[font].ft_xmax = font_xmax;
|
|
_cgi_fontinfo[font].ft_flag = 1;
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: cc_to_vdc */
|
|
/* */
|
|
/* Convert an x,y position in integer character coordinates */
|
|
/* to a Ccoor in VDC. Since this function is static, it needn't */
|
|
/* start with "_cgi_". This function is currently used to set up */
|
|
/* glyph and font extent information, which isn't done often. */
|
|
/* If it gets used more in the future, it should be turned into a */
|
|
/* macro. */
|
|
/****************************************************************************/
|
|
static Ccoor cc_to_vdc(x, y)
|
|
int x, y;
|
|
{
|
|
register int temp;
|
|
register int half = (1 << (CC_VDC_SHIFT - 1));
|
|
Ccoor vdc_coor;
|
|
|
|
/**
|
|
** The integer rounding may look funny, but it's correct. In
|
|
** particular, the expression
|
|
** (temp - half) >> CC_VDC_SHIFT [wrong]
|
|
** is NOT equivalent to
|
|
** -((-temp + half) >> CC_VDC_SHIFT) [correct]
|
|
**
|
|
** because the truncation of the fractional part of temp is
|
|
** always towards more negative numbers, rather than toward 0.
|
|
** Integer shifts of signed numbers tend toward -1 as the amount
|
|
** of shift increases. If temp were some negative number with
|
|
** small magnitude, say -0.001, and we subtracted 1/2 and
|
|
** truncated, we would get -1 as a result, rather than 0.
|
|
**/
|
|
|
|
/* Generate x VDC coordinate */
|
|
temp = ((x * _cgi_vws->conv.text.vdc_cos_base)
|
|
+ (y * _cgi_vws->conv.text.vdc_cos_up));
|
|
if (temp < 0)
|
|
vdc_coor.x = -((-temp + half) >> CC_VDC_SHIFT);
|
|
else
|
|
vdc_coor.x = (temp + half) >> CC_VDC_SHIFT;
|
|
|
|
/* Generate y VDC coordinate */
|
|
temp = ((x * _cgi_vws->conv.text.vdc_sin_base)
|
|
+ (y * _cgi_vws->conv.text.vdc_sin_up));
|
|
if (temp < 0)
|
|
vdc_coor.y = -((-temp + half) >> CC_VDC_SHIFT);
|
|
else
|
|
vdc_coor.y = (temp + half) >> CC_VDC_SHIFT;
|
|
|
|
return (vdc_coor);
|
|
}
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: cc_to_vdc_unscaled */
|
|
/* */
|
|
/* Convert an x,y position in integer character coordinates */
|
|
/* to a Ccoor in VDC. Since this function is static, it needn't */
|
|
/* start with "_cgi_". This function is currently used to set up */
|
|
/* glyph and font extent information, which isn't done often. */
|
|
/* If it gets used more in the future, it should be turned into a */
|
|
/* macro. */
|
|
/****************************************************************************/
|
|
static Ccoor cc_to_vdc_unscaled(x, y)
|
|
int x, y;
|
|
{
|
|
register int temp;
|
|
register int half = (1 << (CC_VDC_SHIFT - 1));
|
|
Ccoor vdc_coor;
|
|
|
|
/**
|
|
** The integer rounding may look funny, but it's correct. In
|
|
** particular, the expression
|
|
** (temp - half) >> CC_VDC_SHIFT [wrong]
|
|
** is NOT equivalent to
|
|
** -((-temp + half) >> CC_VDC_SHIFT) [correct]
|
|
**
|
|
** because the truncation of the fractional part of temp is
|
|
** always towards more negative numbers, rather than toward 0.
|
|
** Integer shifts of signed numbers tend toward -1 as the amount
|
|
** of shift increases. If temp were some negative number with
|
|
** small magnitude, say -0.001, and we subtracted 1/2 and
|
|
** truncated, we would get -1 as a result, rather than 0.
|
|
**/
|
|
|
|
/* Generate x VDC coordinate */
|
|
temp = ((x * _cgi_vws->conv.text.vdc_cos_base)
|
|
+ (y * _cgi_vws->conv.text.vdc_cos_up));
|
|
if (temp < 0)
|
|
vdc_coor.x = -(-temp + half);
|
|
else
|
|
vdc_coor.x = (temp + half);
|
|
|
|
/* Generate y VDC coordinate */
|
|
temp = ((x * _cgi_vws->conv.text.vdc_sin_base)
|
|
+ (y * _cgi_vws->conv.text.vdc_sin_up));
|
|
if (temp < 0)
|
|
vdc_coor.y = -(-temp + half);
|
|
else
|
|
vdc_coor.y = (temp + half) ;
|
|
|
|
return (vdc_coor);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: fcc_to_vdc */
|
|
/* */
|
|
/* Convert an x,y position in floating-point character coordinates */
|
|
/* to a Ccoor in VDC. Floating character coordinates are used when */
|
|
/* additional precision is necessary, such as for the amount of */
|
|
/* spacing. Since this function is static, it needn't start with */
|
|
/* "_cgi_". */
|
|
/****************************************************************************/
|
|
static Ccoor fcc_to_vdc(x, y)
|
|
Cfloat x, y;
|
|
{
|
|
register Cfloat temp;
|
|
static Cfloat scaling = (Cfloat) (1 << CC_VDC_SHIFT);
|
|
Ccoor vdc_coor; /* returned values go here */
|
|
|
|
/* Generate x VDC coordinate */
|
|
temp = (((x * _cgi_vws->conv.text.vdc_cos_base)
|
|
+ (y * _cgi_vws->conv.text.vdc_cos_up))) / scaling;
|
|
vdc_coor.x = _cgi_rnd_flt_to_int(temp);
|
|
|
|
/* Generate y VDC coordinate */
|
|
temp = (((x * _cgi_vws->conv.text.vdc_sin_base)
|
|
+ (y * _cgi_vws->conv.text.vdc_sin_up))) / scaling;
|
|
vdc_coor.y = _cgi_rnd_flt_to_int(temp);
|
|
|
|
return (vdc_coor);
|
|
}
|
|
static Ccoor fcc_to_vdc_unscaled(x, y)
|
|
Cfloat x, y;
|
|
{
|
|
register Cfloat temp;
|
|
static Cfloat scaling = (Cfloat) (1 << CC_VDC_SHIFT);
|
|
Ccoor vdc_coor; /* returned values go here */
|
|
|
|
/* Generate x VDC coordinate */
|
|
temp = (((x * _cgi_vws->conv.text.vdc_cos_base)
|
|
+ (y * _cgi_vws->conv.text.vdc_cos_up)));
|
|
vdc_coor.x = _cgi_rnd_flt_to_int(temp);
|
|
|
|
/* Generate y VDC coordinate */
|
|
temp = (((x * _cgi_vws->conv.text.vdc_sin_base)
|
|
+ (y * _cgi_vws->conv.text.vdc_sin_up)));
|
|
vdc_coor.y = _cgi_rnd_flt_to_int(temp);
|
|
|
|
return (vdc_coor);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/* */
|
|
/* FUNCTION: _cgi_rnd_flt_to_int */
|
|
/* */
|
|
/* DO the obvious rounding on a floating-point number. This is a */
|
|
/* function because it's done a lot, and the function call overhead */
|
|
/* is relatively cheap compared to the floating-point time, although */
|
|
/* if it were called a lot it should be turned into a macro. */
|
|
/****************************************************************************/
|
|
Cint _cgi_rnd_flt_to_int(fnum)
|
|
Cfloat fnum;
|
|
{
|
|
if (fnum < 0)
|
|
return ((Cint) (fnum - 0.5));
|
|
else
|
|
return ((Cint) (fnum + 0.5));
|
|
}
|