2006-03-20 16:43:15 +00:00
/*
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1998 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
2008-01-23 17:25:09 +00:00
/*
2006-03-20 16:43:15 +00:00
*
* Graphical routines for drawing fonts .
*
*/
# include <stdarg.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2008-01-03 10:31:02 +00:00
# ifndef macintosh
2006-03-20 16:43:15 +00:00
# include <fcntl.h>
2008-01-03 10:31:02 +00:00
# endif
2006-03-20 16:43:15 +00:00
# include "u_mem.h"
# include "gr.h"
# include "grdef.h"
# include "error.h"
# include "cfile.h"
# include "mono.h"
# include "byteswap.h"
2008-02-24 14:41:27 +00:00
# include "gamefont.h"
# ifdef OGL
# include "ogl_init.h"
2006-03-20 16:43:15 +00:00
# endif
2008-02-24 14:41:27 +00:00
# define FONTSCALE_X(x) ((x)*(FNTScaleX))
# define FONTSCALE_Y(x) ((x)*(FNTScaleY))
2006-03-20 16:43:15 +00:00
# define BITS_TO_BYTES(x) (((x)+7)>>3)
int gr_internal_string_clipped ( int x , int y , char * s ) ;
int gr_internal_string_clipped_m ( int x , int y , char * s ) ;
ubyte * find_kern_entry ( grs_font * font , ubyte first , ubyte second )
{
ubyte * p = font - > ft_kerndata ;
while ( * p ! = 255 )
if ( p [ 0 ] = = first & & p [ 1 ] = = second )
return p ;
else p + = 3 ;
return NULL ;
}
//takes the character AFTER being offset into font
2008-02-24 14:41:27 +00:00
# define INFONT(_c) ((_c >= 0) && (_c <= grd_curcanv->cv_font->ft_maxchar-grd_curcanv->cv_font->ft_minchar))
2006-03-20 16:43:15 +00:00
//takes the character BEFORE being offset into current font
void get_char_width ( ubyte c , ubyte c2 , int * width , int * spacing )
{
int letter ;
2008-02-24 14:41:27 +00:00
letter = c - grd_curcanv - > cv_font - > ft_minchar ;
2006-03-20 16:43:15 +00:00
if ( ! INFONT ( letter ) ) { //not in font, draw as space
* width = 0 ;
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_PROPORTIONAL )
* spacing = grd_curcanv - > cv_font - > ft_w / 2 ;
2006-03-20 16:43:15 +00:00
else
2008-02-24 14:41:27 +00:00
* spacing = grd_curcanv - > cv_font - > ft_w ;
2006-03-20 16:43:15 +00:00
return ;
}
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_PROPORTIONAL )
* width = FONTSCALE_X ( grd_curcanv - > cv_font - > ft_widths [ letter ] ) ;
2006-03-20 16:43:15 +00:00
else
2008-02-24 14:41:27 +00:00
* width = grd_curcanv - > cv_font - > ft_w ;
2006-03-20 16:43:15 +00:00
* spacing = * width ;
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_KERNED ) {
2006-03-20 16:43:15 +00:00
ubyte * p ;
if ( ! ( c2 = = 0 | | c2 = = ' \n ' ) ) {
2008-02-27 22:05:58 +00:00
int letter2 = c2 - grd_curcanv - > cv_font - > ft_minchar ;
2006-03-20 16:43:15 +00:00
if ( INFONT ( letter2 ) ) {
2008-02-24 14:41:27 +00:00
p = find_kern_entry ( grd_curcanv - > cv_font , ( ubyte ) letter , letter2 ) ;
2006-03-20 16:43:15 +00:00
if ( p )
2008-02-27 22:05:58 +00:00
* spacing = FONTSCALE_X ( p [ 2 ] ) ;
2006-03-20 16:43:15 +00:00
}
}
}
}
int get_centered_x ( char * s )
{
int w , w2 , s2 ;
for ( w = 0 ; * s ! = 0 & & * s ! = ' \n ' ; s + + ) {
if ( * s < = 0x06 ) {
if ( * s < = 0x03 )
s + + ;
continue ; //skip color codes.
}
get_char_width ( s [ 0 ] , s [ 1 ] , & w2 , & s2 ) ;
w + = s2 ;
}
return ( ( grd_curcanv - > cv_bitmap . bm_w - w ) / 2 ) ;
}
2006-10-09 21:44:02 +00:00
//hack to allow color codes to be embedded in strings -MPM
//note we subtract one from color, since 255 is "transparent" so it'll never be used, and 0 would otherwise end the string.
//function must already have orig_color var set (or they could be passed as args...)
//perhaps some sort of recursive orig_color type thing would be better, but that would be way too much trouble for little gain
2007-09-05 17:31:05 +00:00
int gr_message_color_level = 1 ;
2006-10-09 21:44:02 +00:00
# define CHECK_EMBEDDED_COLORS() if ((*text_ptr >= 0x01) && (*text_ptr <= 0x03)) { \
text_ptr + + ; \
if ( * text_ptr ) { \
2007-09-05 17:31:05 +00:00
if ( gr_message_color_level > = * ( text_ptr - 1 ) ) \
2008-02-24 14:41:27 +00:00
grd_curcanv - > cv_font_fg_color = * text_ptr ; \
2006-10-09 21:44:02 +00:00
text_ptr + + ; \
} \
} \
else if ( ( * text_ptr > = 0x04 ) & & ( * text_ptr < = 0x06 ) ) { \
2007-09-05 17:31:05 +00:00
if ( gr_message_color_level > = * text_ptr - 3 ) \
2008-02-24 14:41:27 +00:00
grd_curcanv - > cv_font_fg_color = orig_color ; \
2006-10-09 21:44:02 +00:00
text_ptr + + ; \
}
2006-03-20 16:43:15 +00:00
int gr_internal_string0 ( int x , int y , char * s )
{
unsigned char * fp ;
2007-06-10 16:21:53 +00:00
char * text_ptr , * next_row , * text_ptr1 ;
2006-03-20 16:43:15 +00:00
int r , BitMask , i , bits , width , spacing , letter , underline ;
unsigned int VideoOffset , VideoOffset1 ;
bits = 0 ;
VideoOffset1 = y * ROWSIZE + x ;
next_row = s ;
while ( next_row ! = NULL )
{
text_ptr1 = next_row ;
next_row = NULL ;
if ( x = = 0x8000 ) { //centered
int xx = get_centered_x ( text_ptr1 ) ;
VideoOffset1 = y * ROWSIZE + xx ;
}
2008-02-24 14:41:27 +00:00
for ( r = 0 ; r < grd_curcanv - > cv_font - > ft_h ; r + + )
2006-03-20 16:43:15 +00:00
{
text_ptr = text_ptr1 ;
VideoOffset = VideoOffset1 ;
while ( * text_ptr )
{
if ( * text_ptr = = ' \n ' )
{
next_row = & text_ptr [ 1 ] ;
break ;
}
underline = 0 ;
if ( * text_ptr = = ' & ' )
{
2008-02-24 14:41:27 +00:00
if ( ( r = = grd_curcanv - > cv_font - > ft_baseline + 2 ) | | ( r = = grd_curcanv - > cv_font - > ft_baseline + 3 ) )
2006-03-20 16:43:15 +00:00
underline = 1 ;
text_ptr + + ;
}
get_char_width ( text_ptr [ 0 ] , text_ptr [ 1 ] , & width , & spacing ) ;
2008-02-24 14:41:27 +00:00
letter = ( unsigned char ) * text_ptr - grd_curcanv - > cv_font - > ft_minchar ;
2006-03-20 16:43:15 +00:00
if ( ! INFONT ( letter ) ) { //not in font, draw as space
VideoOffset + = spacing ;
text_ptr + + ;
continue ;
}
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_PROPORTIONAL )
fp = grd_curcanv - > cv_font - > ft_chars [ letter ] ;
2006-03-20 16:43:15 +00:00
else
2008-02-24 14:41:27 +00:00
fp = grd_curcanv - > cv_font - > ft_data + letter * BITS_TO_BYTES ( width ) * grd_curcanv - > cv_font - > ft_h ;
2006-03-20 16:43:15 +00:00
if ( underline )
for ( i = 0 ; i < width ; i + + )
2008-02-24 14:41:27 +00:00
DATA [ VideoOffset + + ] = ( unsigned char ) grd_curcanv - > cv_font_fg_color ;
2006-03-20 16:43:15 +00:00
else
{
fp + = BITS_TO_BYTES ( width ) * r ;
BitMask = 0 ;
for ( i = 0 ; i < width ; i + + )
{
if ( BitMask = = 0 ) {
bits = * fp + + ;
BitMask = 0x80 ;
}
if ( bits & BitMask )
2008-02-24 14:41:27 +00:00
DATA [ VideoOffset + + ] = ( unsigned char ) grd_curcanv - > cv_font_fg_color ;
2006-03-20 16:43:15 +00:00
else
2008-02-24 14:41:27 +00:00
DATA [ VideoOffset + + ] = ( unsigned char ) grd_curcanv - > cv_font_bg_color ;
2006-03-20 16:43:15 +00:00
BitMask > > = 1 ;
}
}
VideoOffset + = spacing - width ; //for kerning
text_ptr + + ;
}
VideoOffset1 + = ROWSIZE ; y + + ;
}
}
return 0 ;
}
int gr_internal_string0m ( int x , int y , char * s )
{
unsigned char * fp ;
2007-06-10 16:21:53 +00:00
char * text_ptr , * next_row , * text_ptr1 ;
2006-03-20 16:43:15 +00:00
int r , BitMask , i , bits , width , spacing , letter , underline ;
unsigned int VideoOffset , VideoOffset1 ;
2008-02-24 14:41:27 +00:00
int orig_color = grd_curcanv - > cv_font_fg_color ; //to allow easy reseting to default string color with colored strings -MPM
2006-03-20 16:43:15 +00:00
bits = 0 ;
VideoOffset1 = y * ROWSIZE + x ;
next_row = s ;
while ( next_row ! = NULL )
{
text_ptr1 = next_row ;
next_row = NULL ;
if ( x = = 0x8000 ) { //centered
int xx = get_centered_x ( text_ptr1 ) ;
VideoOffset1 = y * ROWSIZE + xx ;
}
2008-02-24 14:41:27 +00:00
for ( r = 0 ; r < grd_curcanv - > cv_font - > ft_h ; r + + )
2006-03-20 16:43:15 +00:00
{
text_ptr = text_ptr1 ;
VideoOffset = VideoOffset1 ;
while ( * text_ptr )
{
if ( * text_ptr = = ' \n ' )
{
next_row = & text_ptr [ 1 ] ;
break ;
}
underline = 0 ;
if ( * text_ptr = = ' & ' )
{
2008-02-24 14:41:27 +00:00
if ( ( r = = grd_curcanv - > cv_font - > ft_baseline + 2 ) | | ( r = = grd_curcanv - > cv_font - > ft_baseline + 3 ) )
2006-03-20 16:43:15 +00:00
underline = 1 ;
text_ptr + + ;
}
get_char_width ( text_ptr [ 0 ] , text_ptr [ 1 ] , & width , & spacing ) ;
2008-02-24 14:41:27 +00:00
letter = ( unsigned char ) * text_ptr - grd_curcanv - > cv_font - > ft_minchar ;
2006-03-20 16:43:15 +00:00
2008-01-13 02:10:40 +00:00
if ( ! INFONT ( letter ) | | ( unsigned char ) * text_ptr < = 0x06 ) { //not in font, draw as space
2006-10-09 21:44:02 +00:00
CHECK_EMBEDDED_COLORS ( ) else {
VideoOffset + = spacing ;
text_ptr + + ;
}
2006-03-20 16:43:15 +00:00
continue ;
}
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_PROPORTIONAL )
fp = grd_curcanv - > cv_font - > ft_chars [ letter ] ;
2006-03-20 16:43:15 +00:00
else
2008-02-24 14:41:27 +00:00
fp = grd_curcanv - > cv_font - > ft_data + letter * BITS_TO_BYTES ( width ) * grd_curcanv - > cv_font - > ft_h ;
2006-03-20 16:43:15 +00:00
if ( underline )
for ( i = 0 ; i < width ; i + + )
2008-02-24 14:41:27 +00:00
DATA [ VideoOffset + + ] = ( unsigned char ) grd_curcanv - > cv_font_fg_color ;
2006-03-20 16:43:15 +00:00
else
{
fp + = BITS_TO_BYTES ( width ) * r ;
BitMask = 0 ;
for ( i = 0 ; i < width ; i + + )
{
if ( BitMask = = 0 ) {
bits = * fp + + ;
BitMask = 0x80 ;
}
if ( bits & BitMask )
2008-02-24 14:41:27 +00:00
DATA [ VideoOffset + + ] = ( unsigned char ) grd_curcanv - > cv_font_fg_color ;
2006-03-20 16:43:15 +00:00
else
VideoOffset + + ;
BitMask > > = 1 ;
}
}
text_ptr + + ;
VideoOffset + = spacing - width ;
}
VideoOffset1 + = ROWSIZE ;
y + + ;
}
}
return 0 ;
}
# ifndef OGL
//a bitmap for the character
grs_bitmap char_bm = {
0 , 0 , 0 , 0 , //x,y,w,h
BM_LINEAR , //type
BM_FLAG_TRANSPARENT , //flags
0 , //rowsize
NULL , //data
# ifdef BITMAP_SELECTOR
0 , //selector
# endif
0 , //avg_color
0 //unused
} ;
int gr_internal_color_string ( int x , int y , char * s )
{
unsigned char * fp ;
2007-06-11 09:06:14 +00:00
char * text_ptr , * next_row , * text_ptr1 ;
2006-03-20 16:43:15 +00:00
int width , spacing , letter ;
int xx , yy ;
2008-02-24 14:41:27 +00:00
char_bm . bm_h = grd_curcanv - > cv_font - > ft_h ; //set height for chars of this font
2006-03-20 16:43:15 +00:00
next_row = s ;
yy = y ;
while ( next_row ! = NULL )
{
text_ptr1 = next_row ;
next_row = NULL ;
text_ptr = text_ptr1 ;
xx = x ;
if ( xx = = 0x8000 ) //centered
xx = get_centered_x ( text_ptr ) ;
while ( * text_ptr )
{
if ( * text_ptr = = ' \n ' )
{
next_row = & text_ptr [ 1 ] ;
2008-02-24 14:41:27 +00:00
yy + = grd_curcanv - > cv_font - > ft_h ;
2006-03-20 16:43:15 +00:00
break ;
}
2008-02-24 14:41:27 +00:00
letter = ( unsigned char ) * text_ptr - grd_curcanv - > cv_font - > ft_minchar ;
2006-03-20 16:43:15 +00:00
get_char_width ( text_ptr [ 0 ] , text_ptr [ 1 ] , & width , & spacing ) ;
if ( ! INFONT ( letter ) ) { //not in font, draw as space
xx + = spacing ;
text_ptr + + ;
continue ;
}
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_PROPORTIONAL )
fp = grd_curcanv - > cv_font - > ft_chars [ letter ] ;
2006-03-20 16:43:15 +00:00
else
2008-02-24 14:41:27 +00:00
fp = grd_curcanv - > cv_font - > ft_data + letter * BITS_TO_BYTES ( width ) * grd_curcanv - > cv_font - > ft_h ;
2006-03-20 16:43:15 +00:00
2008-02-24 14:41:27 +00:00
gr_init_bitmap ( & char_bm , BM_LINEAR , 0 , 0 , width , grd_curcanv - > cv_font - > ft_h , width , fp ) ;
2006-03-20 16:43:15 +00:00
gr_bitmapm ( xx , yy , & char_bm ) ;
# ifdef D1XD3D
Win32_FreeTexture ( & char_bm ) ;
# endif
xx + = spacing ;
text_ptr + + ;
}
}
return 0 ;
}
# else //OGL
int pow2ize ( int x ) ; //from ogl.c
int get_font_total_width ( grs_font * font ) {
if ( font - > ft_flags & FT_PROPORTIONAL ) {
int i , w = 0 , c = font - > ft_minchar ;
for ( i = 0 ; c < = font - > ft_maxchar ; i + + , c + + ) {
if ( font - > ft_widths [ i ] < 0 )
Error ( " heh? \n " ) ;
w + = font - > ft_widths [ i ] ;
}
return w ;
} else {
return font - > ft_w * ( font - > ft_maxchar - font - > ft_minchar + 1 ) ;
}
}
void ogl_font_choose_size ( grs_font * font , int gap , int * rw , int * rh ) {
int nchars = font - > ft_maxchar - font - > ft_minchar + 1 ;
int r , x , y , nc = 0 , smallest = 999999 , smallr = - 1 , tries ;
int smallprop = 10000 ;
int h , w ;
for ( h = 32 ; h < = 256 ; h * = 2 ) {
// h=pow2ize(font->ft_h*rows+gap*(rows-1));
if ( font - > ft_h > h ) continue ;
r = ( h / ( font - > ft_h + gap ) ) ;
w = pow2ize ( ( get_font_total_width ( font ) + ( nchars - r ) * gap ) / r ) ;
tries = 0 ;
do {
if ( tries )
w = pow2ize ( w + 1 ) ;
if ( tries > 3 ) {
mprintf ( ( 0 , " failed to fit (%ix%i, %ic) \n " , w , h , nc ) ) ;
break ;
}
nc = 0 ;
y = 0 ;
while ( y + font - > ft_h < = h ) {
x = 0 ;
while ( x < w ) {
if ( nc = = nchars )
break ;
if ( font - > ft_flags & FT_PROPORTIONAL ) {
if ( x + font - > ft_widths [ nc ] + gap > w ) break ;
x + = font - > ft_widths [ nc + + ] + gap ;
} else {
if ( x + font - > ft_w + gap > w ) break ;
x + = font - > ft_w + gap ;
nc + + ;
}
}
if ( nc = = nchars )
break ;
y + = font - > ft_h + gap ;
}
tries + + ;
} while ( nc ! = nchars ) ;
if ( nc ! = nchars )
continue ;
mprintf ( ( 0 , " fit: %ix%i %i tries \n " , w , h , tries ) ) ;
if ( w * h = = smallest ) { //this gives squarer sizes priority (ie, 128x128 would be better than 512*32)
if ( w > = h ) {
if ( w / h < smallprop ) {
smallprop = w / h ;
smallest + + ; //hack
}
} else {
if ( h / w < smallprop ) {
smallprop = h / w ;
smallest + + ; //hack
}
}
}
if ( w * h < smallest ) {
smallr = 1 ;
smallest = w * h ;
* rw = w ;
* rh = h ;
}
}
if ( smallr < = 0 )
Error ( " couldn't fit font? \n " ) ;
mprintf ( ( 0 , " using %ix%i \n " , * rw , * rh ) ) ;
}
void ogl_init_font ( grs_font * font ) {
2007-06-10 16:21:53 +00:00
int oglflags = OGL_FLAG_ALPHA ;
2006-03-20 16:43:15 +00:00
int nchars = font - > ft_maxchar - font - > ft_minchar + 1 ;
int i , w , h , tw , th , x , y , curx = 0 , cury = 0 ;
2007-06-10 16:21:53 +00:00
unsigned char * fp ;
2006-03-20 16:43:15 +00:00
// char data[32*32*4];
2007-06-10 16:21:53 +00:00
ubyte * data ;
2006-03-20 16:43:15 +00:00
int gap = 0 ; //having a gap just wastes ram, since we don't filter text textures at all.
// char s[2];
ogl_font_choose_size ( font , gap , & tw , & th ) ;
2008-01-23 17:25:09 +00:00
data = d_malloc ( tw * th ) ;
2006-11-28 08:56:24 +00:00
memset ( data , 0 , tw * th ) ;
2006-03-20 16:43:15 +00:00
gr_init_bitmap ( & font - > ft_parent_bitmap , BM_LINEAR , 0 , 0 , tw , th , tw , data ) ;
2007-06-10 16:21:53 +00:00
gr_set_transparent ( & font - > ft_parent_bitmap , 1 ) ;
2006-03-20 16:43:15 +00:00
2007-06-10 16:21:53 +00:00
if ( ! ( font - > ft_flags & FT_COLOR ) )
oglflags | = OGL_FLAG_NOCOLOR ;
ogl_init_texture ( font - > ft_parent_bitmap . gltexture = ogl_get_free_texture ( ) , tw , th , oglflags ) ; // have to init the gltexture here so the subbitmaps will find it.
2006-03-20 16:43:15 +00:00
2008-01-23 17:25:09 +00:00
font - > ft_bitmaps = ( grs_bitmap * ) d_malloc ( nchars * sizeof ( grs_bitmap ) ) ;
2006-03-20 16:43:15 +00:00
mprintf ( ( 0 , " ogl_init_font %s, %s, nchars=%i, (%ix%i tex) \n " , ( font - > ft_flags & FT_PROPORTIONAL ) ? " proportional " : " fixedwidth " , ( font - > ft_flags & FT_COLOR ) ? " color " : " mono " , nchars , tw , th ) ) ;
// s[1]=0;
h = font - > ft_h ;
// sleep(5);
for ( i = 0 ; i < nchars ; i + + ) {
// s[0]=font->ft_minchar+i;
// gr_get_string_size(s,&w,&h,&aw);
if ( font - > ft_flags & FT_PROPORTIONAL )
w = font - > ft_widths [ i ] ;
else
w = font - > ft_w ;
// mprintf((0,"char %i(%ix%i): ",i,w,h));
if ( w < 1 | | w > 256 ) {
mprintf ( ( 0 , " grr \n " ) ) ; continue ;
}
if ( curx + w + gap > tw ) {
cury + = h + gap ;
curx = 0 ;
}
if ( cury + h > th )
Error ( " font doesn't really fit (%i/%i)? \n " , i , nchars ) ;
if ( font - > ft_flags & FT_COLOR ) {
if ( font - > ft_flags & FT_PROPORTIONAL )
fp = font - > ft_chars [ i ] ;
else
fp = font - > ft_data + i * w * h ;
for ( y = 0 ; y < h ; y + + )
for ( x = 0 ; x < w ; x + + ) {
font - > ft_parent_bitmap . bm_data [ curx + x + ( cury + y ) * tw ] = fp [ x + y * w ] ;
}
// gr_init_bitmap(&font->ft_bitmaps[i],BM_LINEAR,0,0,w,h,w,font->);
} else {
int BitMask , bits = 0 , white = gr_find_closest_color ( 63 , 63 , 63 ) ;
// if (w*h>sizeof(data))
// Error("ogl_init_font: toobig\n");
if ( font - > ft_flags & FT_PROPORTIONAL )
fp = font - > ft_chars [ i ] ;
else
fp = font - > ft_data + i * BITS_TO_BYTES ( w ) * h ;
for ( y = 0 ; y < h ; y + + ) {
BitMask = 0 ;
for ( x = 0 ; x < w ; x + + )
{
if ( BitMask = = 0 ) {
bits = * fp + + ;
BitMask = 0x80 ;
}
if ( bits & BitMask )
font - > ft_parent_bitmap . bm_data [ curx + x + ( cury + y ) * tw ] = white ;
else
font - > ft_parent_bitmap . bm_data [ curx + x + ( cury + y ) * tw ] = 255 ;
BitMask > > = 1 ;
}
}
}
gr_init_sub_bitmap ( & font - > ft_bitmaps [ i ] , & font - > ft_parent_bitmap , curx , cury , w , h ) ;
curx + = w + gap ;
}
2007-06-10 16:21:53 +00:00
ogl_loadbmtexture_f ( & font - > ft_parent_bitmap , oglflags ) ;
2006-03-20 16:43:15 +00:00
}
int ogl_internal_string ( int x , int y , char * s )
{
2007-06-10 16:21:53 +00:00
char * text_ptr , * next_row , * text_ptr1 ;
2006-03-20 16:43:15 +00:00
int width , spacing , letter ;
int xx , yy ;
2008-02-24 14:41:27 +00:00
int orig_color = grd_curcanv - > cv_font_fg_color ; //to allow easy reseting to default string color with colored strings -MPM
2006-03-20 16:43:15 +00:00
next_row = s ;
yy = y ;
if ( grd_curscreen - > sc_canvas . cv_bitmap . bm_type ! = BM_OGL )
Error ( " carp. \n " ) ;
while ( next_row ! = NULL )
{
text_ptr1 = next_row ;
next_row = NULL ;
text_ptr = text_ptr1 ;
xx = x ;
if ( xx = = 0x8000 ) //centered
xx = get_centered_x ( text_ptr ) ;
while ( * text_ptr )
{
if ( * text_ptr = = ' \n ' )
{
next_row = & text_ptr [ 1 ] ;
2008-02-24 14:41:27 +00:00
yy + = FONTSCALE_Y ( grd_curcanv - > cv_font - > ft_h ) ;
2006-03-20 16:43:15 +00:00
break ;
}
2008-02-24 14:41:27 +00:00
letter = ( unsigned char ) * text_ptr - grd_curcanv - > cv_font - > ft_minchar ;
2006-03-20 16:43:15 +00:00
get_char_width ( text_ptr [ 0 ] , text_ptr [ 1 ] , & width , & spacing ) ;
2008-01-13 02:10:40 +00:00
if ( ! INFONT ( letter ) | | ( unsigned char ) * text_ptr < = 0x06 ) { //not in font, draw as space
2006-10-09 21:44:02 +00:00
CHECK_EMBEDDED_COLORS ( ) else {
xx + = spacing ;
text_ptr + + ;
}
2006-03-20 16:43:15 +00:00
continue ;
}
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_COLOR )
ogl_ubitmapm_cs ( xx , yy , FONTSCALE_X ( grd_curcanv - > cv_font - > ft_widths [ letter ] ) , FONTSCALE_Y ( grd_curcanv - > cv_font - > ft_h ) , & grd_curcanv - > cv_font - > ft_bitmaps [ letter ] , - 1 , F1_0 ) ;
else {
if ( grd_curcanv - > cv_bitmap . bm_type = = BM_OGL )
ogl_ubitmapm_cs ( xx , yy , grd_curcanv - > cv_font - > ft_widths [ letter ] * ( FONTSCALE_X ( grd_curcanv - > cv_font - > ft_w ) / grd_curcanv - > cv_font - > ft_w ) , FONTSCALE_Y ( grd_curcanv - > cv_font - > ft_h ) , & grd_curcanv - > cv_font - > ft_bitmaps [ letter ] , grd_curcanv - > cv_font_fg_color , F1_0 ) ;
else
2006-03-20 16:43:15 +00:00
Error ( " ogl_internal_string: non-color string to non-ogl dest \n " ) ;
}
xx + = spacing ;
text_ptr + + ;
}
}
return 0 ;
}
int gr_internal_color_string ( int x , int y , char * s ) {
return ogl_internal_string ( x , y , s ) ;
}
# endif //OGL
int gr_string ( int x , int y , char * s )
{
int w , h , aw ;
int clipped = 0 ;
if ( x = = 0x8000 ) {
if ( y < 0 ) clipped | = 1 ;
gr_get_string_size ( s , & w , & h , & aw ) ;
// for x, since this will be centered, only look at
// width.
if ( w > grd_curcanv - > cv_bitmap . bm_w ) clipped | = 1 ;
if ( ( y + h ) > grd_curcanv - > cv_bitmap . bm_h ) clipped | = 1 ;
if ( ( y + h ) < 0 ) clipped | = 2 ;
if ( y > grd_curcanv - > cv_bitmap . bm_h ) clipped | = 2 ;
} else {
if ( ( x < 0 ) | | ( y < 0 ) ) clipped | = 1 ;
gr_get_string_size ( s , & w , & h , & aw ) ;
if ( ( x + w ) > grd_curcanv - > cv_bitmap . bm_w ) clipped | = 1 ;
if ( ( y + h ) > grd_curcanv - > cv_bitmap . bm_h ) clipped | = 1 ;
if ( ( x + w ) < 0 ) clipped | = 2 ;
if ( ( y + h ) < 0 ) clipped | = 2 ;
if ( x > grd_curcanv - > cv_bitmap . bm_w ) clipped | = 2 ;
if ( y > grd_curcanv - > cv_bitmap . bm_h ) clipped | = 2 ;
}
if ( ! clipped )
return gr_ustring ( x , y , s ) ;
if ( clipped & 2 ) {
// Completely clipped...
mprintf ( ( 1 , " Text '%s' at (%d,%d) is off screen! \n " , s , x , y ) ) ;
return 0 ;
}
if ( clipped & 1 ) {
// Partially clipped...
//mprintf( (0, "Text '%s' at (%d,%d) is getting clipped!\n", s, x, y ));
}
// Partially clipped...
# ifdef OGL
if ( TYPE = = BM_OGL )
return ogl_internal_string ( x , y , s ) ;
# endif
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_COLOR )
2006-03-20 16:43:15 +00:00
return gr_internal_color_string ( x , y , s ) ;
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font_bg_color = = - 1 )
2006-03-20 16:43:15 +00:00
return gr_internal_string_clipped_m ( x , y , s ) ;
return gr_internal_string_clipped ( x , y , s ) ;
}
int gr_ustring ( int x , int y , char * s )
{
# ifdef OGL
if ( TYPE = = BM_OGL )
return ogl_internal_string ( x , y , s ) ;
# endif
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_COLOR ) {
2006-03-20 16:43:15 +00:00
return gr_internal_color_string ( x , y , s ) ;
}
else
switch ( TYPE )
{
case BM_LINEAR :
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font_bg_color = = - 1 )
2006-03-20 16:43:15 +00:00
return gr_internal_string0m ( x , y , s ) ;
else
return gr_internal_string0 ( x , y , s ) ;
# ifdef __MSDOS__
case BM_SVGA :
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font_bg_color = = - 1 )
2006-03-20 16:43:15 +00:00
return gr_internal_string2m ( x , y , s ) ;
else
return gr_internal_string2 ( x , y , s ) ;
# endif
# ifdef D1XD3D
case BM_DIRECTX :
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font_bg_color = = - 1 )
2006-03-20 16:43:15 +00:00
return gr_internal_string_clipped_m ( x , y , s ) ;
else
return gr_internal_string_clipped ( x , y , s ) ;
# endif
}
return 0 ;
}
void gr_get_string_size ( char * s , int * string_width , int * string_height , int * average_width )
{
int i = 0 , longest_width = 0 ;
int width , spacing ;
2008-02-24 14:41:27 +00:00
* string_height = FONTSCALE_Y ( grd_curcanv - > cv_font - > ft_h ) ;
2006-03-20 16:43:15 +00:00
* string_width = 0 ;
2008-02-24 14:41:27 +00:00
* average_width = grd_curcanv - > cv_font - > ft_w ;
2006-03-20 16:43:15 +00:00
if ( s ! = NULL )
{
* string_width = 0 ;
while ( * s )
{
// if (*s == '&')
// s++;
while ( * s = = ' \n ' )
{
s + + ;
2008-02-24 14:41:27 +00:00
* string_height + = FONTSCALE_Y ( grd_curcanv - > cv_font - > ft_h ) ;
2006-03-20 16:43:15 +00:00
* string_width = 0 ;
}
if ( * s = = 0 ) break ;
get_char_width ( s [ 0 ] , s [ 1 ] , & width , & spacing ) ;
* string_width + = spacing ;
if ( * string_width > longest_width )
longest_width = * string_width ;
i + + ;
s + + ;
}
}
* string_width = longest_width ;
}
int gr_uprintf ( int x , int y , char * format , . . . )
{
char buffer [ 1000 ] ;
va_list args ;
va_start ( args , format ) ;
vsprintf ( buffer , format , args ) ;
return gr_ustring ( x , y , buffer ) ;
}
int gr_printf ( int x , int y , char * format , . . . )
{
char buffer [ 1000 ] ;
va_list args ;
va_start ( args , format ) ;
vsprintf ( buffer , format , args ) ;
return gr_string ( x , y , buffer ) ;
}
void gr_close_font ( grs_font * font )
{
if ( font )
{
if ( font - > ft_chars )
2008-01-23 17:25:09 +00:00
d_free ( font - > ft_chars ) ;
d_free ( font - > oldfont ) ;
2006-03-20 16:43:15 +00:00
# ifdef OGL
if ( font - > ft_bitmaps )
2008-01-23 17:25:09 +00:00
d_free ( font - > ft_bitmaps ) ;
2006-03-20 16:43:15 +00:00
gr_free_bitmap_data ( & font - > ft_parent_bitmap ) ;
// ogl_freebmtexture(&font->ft_parent_bitmap);
# endif
2008-01-23 17:25:09 +00:00
d_free ( font ) ;
2006-03-20 16:43:15 +00:00
}
}
void build_colormap_good ( ubyte * palette , ubyte * colormap , int * freq ) ;
# include "bitmap.h" // decode_data_asm
#if 0
void decode_data_asm ( ubyte * data , int num_pixels , ubyte * colormap , int * count ) ;
# pragma aux decode_data_asm parm [esi] [ecx] [edi] [ebx] modify exact [esi edi eax ebx ecx] = \
" again_ddn: " \
" xor eax,eax " \
" mov al,[esi] " \
" inc dword ptr [ebx+eax*4] " \
" mov al,[edi+eax] " \
" mov [esi],al " \
" inc esi " \
" dec ecx " \
" jne again_ddn "
# endif
grs_font * gr_init_font ( char * fontname )
{
old_grs_font * font ;
grs_font * newfont ;
int i ;
unsigned char * ptr ;
int nchars ;
CFILE * fontfile ;
2008-01-13 00:58:49 +00:00
char file_id [ 4 ] ;
2006-03-20 16:43:15 +00:00
int32_t datasize ; //size up to (but not including) palette
fontfile = cfopen ( fontname , " rb " ) ;
if ( ! fontfile )
Error ( " Can't open font file %s " , fontname ) ;
2008-01-13 00:58:49 +00:00
cfread ( file_id , 4 , 1 , fontfile ) ;
if ( ! strncmp ( file_id , " NFSP " , 4 ) ) {
mprintf ( ( 0 , " File %s is not a font file \n " , fontname ) ) ;
return NULL ;
}
datasize = cfile_read_int ( fontfile ) ;
2006-03-20 16:43:15 +00:00
2008-01-23 17:25:09 +00:00
font = ( old_grs_font * ) d_malloc ( datasize ) ;
newfont = ( grs_font * ) d_malloc ( sizeof ( grs_font ) ) ;
2006-03-20 16:43:15 +00:00
newfont - > oldfont = font ;
cfread ( font , 1 , datasize , fontfile ) ;
2008-01-13 00:58:49 +00:00
newfont - > ft_flags = INTEL_SHORT ( font - > ft_flags ) ;
2008-01-08 16:33:19 +00:00
newfont - > ft_w = INTEL_SHORT ( font - > ft_w ) ;
newfont - > ft_h = INTEL_SHORT ( font - > ft_h ) ;
newfont - > ft_baseline = INTEL_SHORT ( font - > ft_baseline ) ;
2006-03-20 16:43:15 +00:00
newfont - > ft_maxchar = font - > ft_maxchar ;
newfont - > ft_minchar = font - > ft_minchar ;
2008-01-08 16:33:19 +00:00
newfont - > ft_bytewidth = INTEL_SHORT ( font - > ft_bytewidth ) ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
nchars = newfont - > ft_maxchar - newfont - > ft_minchar + 1 ;
if ( newfont - > ft_flags & FT_PROPORTIONAL ) {
2008-01-08 16:33:19 +00:00
newfont - > ft_widths = ( short * ) ( INTEL_INT ( font - > ft_widths ) + ( ( ubyte * ) font ) ) ;
2008-01-13 00:58:49 +00:00
for ( i = 0 ; i < nchars ; i + + )
newfont - > ft_widths [ i ] = INTEL_SHORT ( newfont - > ft_widths [ i ] ) ;
2008-01-08 16:33:19 +00:00
newfont - > ft_data = ( INTEL_INT ( font - > ft_data ) ) + ( ( ubyte * ) font ) ;
2008-01-13 00:58:49 +00:00
2008-01-23 17:25:09 +00:00
newfont - > ft_chars = ( unsigned char * * ) d_malloc ( nchars * sizeof ( unsigned char * ) ) ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
ptr = newfont - > ft_data ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
for ( i = 0 ; i < nchars ; i + + ) {
newfont - > ft_chars [ i ] = ptr ;
if ( newfont - > ft_flags & FT_COLOR )
ptr + = newfont - > ft_widths [ i ] * newfont - > ft_h ;
else
ptr + = BITS_TO_BYTES ( newfont - > ft_widths [ i ] ) * newfont - > ft_h ;
}
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
} else {
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
newfont - > ft_data = ( ( unsigned char * ) font ) + sizeof ( * font ) ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
newfont - > ft_chars = NULL ;
newfont - > ft_widths = NULL ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
ptr = newfont - > ft_data + ( nchars * newfont - > ft_w * newfont - > ft_h ) ;
}
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
if ( newfont - > ft_flags & FT_KERNED )
2008-01-08 16:33:19 +00:00
newfont - > ft_kerndata = INTEL_INT ( font - > ft_kerndata ) + ( ( ubyte * ) font ) ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
if ( newfont - > ft_flags & FT_COLOR ) { //remap palette
ubyte palette [ 256 * 3 ] ;
ubyte colormap [ 256 ] ;
int freq [ 256 ] ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
cfread ( palette , 3 , 256 , fontfile ) ; //read the palette
2008-01-13 00:58:49 +00:00
2007-06-10 16:21:53 +00:00
build_colormap_good ( ( ubyte * ) & palette , colormap , freq ) ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
colormap [ 255 ] = 255 ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
decode_data_asm ( newfont - > ft_data , ptr - newfont - > ft_data , colormap , freq ) ;
}
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
cfclose ( fontfile ) ;
2008-01-13 00:58:49 +00:00
// memcpy(newfont,font,(ubyte*)&newfont->oldfont-(ubyte*)newfont);//fill in newfont data from oldfont struct
// mprintf((0,"%i %i %i\n",sizeof(grs_font),sizeof(old_grs_font),(ubyte*)&newfont->oldfont-(ubyte*)newfont));
2006-03-20 16:43:15 +00:00
//set curcanv vars
2008-01-13 00:58:49 +00:00
2008-02-24 14:41:27 +00:00
grd_curcanv - > cv_font = newfont ;
grd_curcanv - > cv_font_fg_color = 0 ;
grd_curcanv - > cv_font_bg_color = 0 ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
{
int x , y , aw ;
char tests [ ] = " abcdefghij1234.A " ;
gr_get_string_size ( tests , & x , & y , & aw ) ;
newfont - > ft_aw = x / ( float ) strlen ( tests ) ;
}
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
# ifdef OGL
ogl_init_font ( newfont ) ;
# endif
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
return newfont ;
2008-01-13 00:58:49 +00:00
2006-03-20 16:43:15 +00:00
}
2008-02-24 14:41:27 +00:00
void gr_set_curfont ( grs_font * new )
2006-03-20 16:43:15 +00:00
{
2008-02-24 14:41:27 +00:00
grd_curcanv - > cv_font = new ;
2006-03-20 16:43:15 +00:00
}
2008-02-24 14:41:27 +00:00
void gr_set_fontcolor ( int fg_color , int bg_color )
2006-03-20 16:43:15 +00:00
{
2008-02-24 14:41:27 +00:00
grd_curcanv - > cv_font_fg_color = fg_color ;
grd_curcanv - > cv_font_bg_color = bg_color ;
2006-03-20 16:43:15 +00:00
}
int gr_internal_string_clipped ( int x , int y , char * s )
{
unsigned char * fp ;
char * text_ptr , * next_row , * text_ptr1 ;
int r , BitMask , i , bits , width , spacing , letter , underline ;
int x1 = x , last_x ;
bits = 0 ;
next_row = s ;
while ( next_row ! = NULL )
{
text_ptr1 = next_row ;
next_row = NULL ;
x = x1 ;
if ( x = = 0x8000 ) //centered
x = get_centered_x ( text_ptr1 ) ;
last_x = x ;
2008-02-24 14:41:27 +00:00
for ( r = 0 ; r < grd_curcanv - > cv_font - > ft_h ; r + + ) {
2006-03-20 16:43:15 +00:00
text_ptr = text_ptr1 ;
x = last_x ;
while ( * text_ptr ) {
if ( * text_ptr = = ' \n ' ) {
next_row = & text_ptr [ 1 ] ;
break ;
}
underline = 0 ;
if ( * text_ptr = = ' & ' ) {
2008-02-24 14:41:27 +00:00
if ( ( r = = grd_curcanv - > cv_font - > ft_baseline + 2 ) | | ( r = = grd_curcanv - > cv_font - > ft_baseline + 3 ) )
2006-03-20 16:43:15 +00:00
underline = 1 ;
text_ptr + + ;
}
get_char_width ( text_ptr [ 0 ] , text_ptr [ 1 ] , & width , & spacing ) ;
2008-02-24 14:41:27 +00:00
letter = ( unsigned char ) * text_ptr - grd_curcanv - > cv_font - > ft_minchar ;
2006-03-20 16:43:15 +00:00
if ( ! INFONT ( letter ) ) { //not in font, draw as space
x + = spacing ;
text_ptr + + ;
continue ;
}
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_PROPORTIONAL )
fp = grd_curcanv - > cv_font - > ft_chars [ letter ] ;
2006-03-20 16:43:15 +00:00
else
2008-02-24 14:41:27 +00:00
fp = grd_curcanv - > cv_font - > ft_data + letter * BITS_TO_BYTES ( width ) * grd_curcanv - > cv_font - > ft_h ;
2006-03-20 16:43:15 +00:00
if ( underline ) {
for ( i = 0 ; i < width ; i + + ) {
2008-02-24 14:41:27 +00:00
gr_setcolor ( grd_curcanv - > cv_font_fg_color ) ;
2006-03-20 16:43:15 +00:00
gr_pixel ( x + + , y ) ;
}
} else {
fp + = BITS_TO_BYTES ( width ) * r ;
BitMask = 0 ;
for ( i = 0 ; i < width ; i + + ) {
if ( BitMask = = 0 ) {
bits = * fp + + ;
BitMask = 0x80 ;
}
if ( bits & BitMask )
2008-02-24 14:41:27 +00:00
gr_setcolor ( grd_curcanv - > cv_font_fg_color ) ;
2006-03-20 16:43:15 +00:00
else
2008-02-24 14:41:27 +00:00
gr_setcolor ( grd_curcanv - > cv_font_bg_color ) ;
2006-03-20 16:43:15 +00:00
gr_pixel ( x + + , y ) ;
BitMask > > = 1 ;
}
}
x + = spacing - width ; //for kerning
text_ptr + + ;
}
y + + ;
}
}
return 0 ;
}
int gr_internal_string_clipped_m ( int x , int y , char * s )
{
unsigned char * fp ;
char * text_ptr , * next_row , * text_ptr1 ;
int r , BitMask , i , bits , width , spacing , letter , underline ;
int x1 = x , last_x ;
bits = 0 ;
next_row = s ;
while ( next_row ! = NULL )
{
text_ptr1 = next_row ;
next_row = NULL ;
x = x1 ;
if ( x = = 0x8000 ) //centered
x = get_centered_x ( text_ptr1 ) ;
last_x = x ;
2008-02-24 14:41:27 +00:00
for ( r = 0 ; r < grd_curcanv - > cv_font - > ft_h ; r + + ) {
2006-03-20 16:43:15 +00:00
x = last_x ;
text_ptr = text_ptr1 ;
while ( * text_ptr ) {
if ( * text_ptr = = ' \n ' ) {
next_row = & text_ptr [ 1 ] ;
break ;
}
underline = 0 ;
if ( * text_ptr = = ' & ' ) {
2008-02-24 14:41:27 +00:00
if ( ( r = = grd_curcanv - > cv_font - > ft_baseline + 2 ) | | ( r = = grd_curcanv - > cv_font - > ft_baseline + 3 ) )
2006-03-20 16:43:15 +00:00
underline = 1 ;
text_ptr + + ;
}
get_char_width ( text_ptr [ 0 ] , text_ptr [ 1 ] , & width , & spacing ) ;
2008-02-24 14:41:27 +00:00
letter = ( unsigned char ) * text_ptr - grd_curcanv - > cv_font - > ft_minchar ;
2006-03-20 16:43:15 +00:00
if ( ! INFONT ( letter ) ) { //not in font, draw as space
x + = spacing ;
text_ptr + + ;
continue ;
}
2008-02-24 14:41:27 +00:00
if ( grd_curcanv - > cv_font - > ft_flags & FT_PROPORTIONAL )
fp = grd_curcanv - > cv_font - > ft_chars [ letter ] ;
2006-03-20 16:43:15 +00:00
else
2008-02-24 14:41:27 +00:00
fp = grd_curcanv - > cv_font - > ft_data + letter * BITS_TO_BYTES ( width ) * grd_curcanv - > cv_font - > ft_h ;
2006-03-20 16:43:15 +00:00
if ( underline ) {
for ( i = 0 ; i < width ; i + + ) {
2008-02-24 14:41:27 +00:00
gr_setcolor ( grd_curcanv - > cv_font_fg_color ) ;
2006-03-20 16:43:15 +00:00
gr_pixel ( x + + , y ) ;
}
} else {
fp + = BITS_TO_BYTES ( width ) * r ;
BitMask = 0 ;
for ( i = 0 ; i < width ; i + + ) {
if ( BitMask = = 0 ) {
bits = * fp + + ;
BitMask = 0x80 ;
}
if ( bits & BitMask ) {
2008-02-24 14:41:27 +00:00
gr_setcolor ( grd_curcanv - > cv_font_fg_color ) ;
2006-03-20 16:43:15 +00:00
gr_pixel ( x + + , y ) ;
} else {
x + + ;
}
BitMask > > = 1 ;
}
}
x + = spacing - width ; //for kerning
text_ptr + + ;
}
y + + ;
}
}
return 0 ;
}