/* Rubygame -- Ruby code and bindings to SDL to facilitate game creation Copyright (C) 2004-2005 John 'jacius' Croisant This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "rubygame.h" #include "rubygame_ttf.h" #include "rubygame_surface.h" void Rubygame_Init_TTF(); VALUE cTTF; VALUE rbgm_ttf_version(VALUE); /* * call-seq: * version -> [Integer, Integer, Integer] * * Return the major, minor, and patch numbers for the version of SDL_ttf * that Rubygame was compiled with. If Rubygame was not compiled with SDL_ttf, * all version numbers will be 0 (zero), and you should not attempt to use * the TTF class. * * This allows for more flexible detection of the capabilities of the TTF * class' base library, SDL_ttf. See also #usable?. */ VALUE rbgm_ttf_version(VALUE module) { return rb_ary_new3(3, INT2NUM(SDL_TTF_MAJOR_VERSION), INT2NUM(SDL_TTF_MINOR_VERSION), INT2NUM(SDL_TTF_PATCHLEVEL)); } #ifdef HAVE_SDL_TTF_H VALUE rbgm_ttf_setup(VALUE); VALUE rbgm_ttf_quit(VALUE); VALUE rbgm_ttf_new(int, VALUE*, VALUE); VALUE rbgm_ttf_initialize(int, VALUE *, VALUE); VALUE rbgm_ttf_getbold(VALUE); VALUE rbgm_ttf_setbold(VALUE, VALUE); VALUE rbgm_ttf_getitalic(VALUE); VALUE rbgm_ttf_setitalic(VALUE, VALUE); VALUE rbgm_ttf_getunderline(VALUE); VALUE rbgm_ttf_setunderline(VALUE, VALUE); VALUE rbgm_ttf_height(VALUE); VALUE rbgm_ttf_ascent(VALUE); VALUE rbgm_ttf_descent(VALUE); VALUE rbgm_ttf_lineskip(VALUE); VALUE rbgm_ttf_render(int, VALUE*, VALUE); /* * call-seq: * setup -> nil * * Attempt to setup the TTF class for use by initializing SDL_ttf. * This *must* be called before the TTF class can be used. * Raises SDLError if there is a problem initializing SDL_ttf. */ VALUE rbgm_ttf_setup(VALUE module) { if(TTF_Init()!=0) rb_raise(eSDLError,"could not setup TTF class: %s",TTF_GetError()); return Qnil; } /* * call-seq: * quit -> nil * * Clean up and quit SDL_ttf, making the TTF class unusable as a result * (until it is setup again). This does not need to be called before Rubygame * exits, as it will be done automatically. */ VALUE rbgm_ttf_quit(VALUE module) { if(TTF_WasInit()) TTF_Quit(); return Qnil; } /* * call-seq: * new( file, size ) -> TTF * * Create a new TTF object, which can render text to a Surface with a * particular font style and size. * * This function takes these arguments: * file:: filename of the TrueType font to use. Should be a +TTF+ or * +FON+ file. * size:: point size (based on 72DPI). Or, the height in pixels from the * bottom of the descent to the top of the ascent. */ VALUE rbgm_ttf_new(int argc, VALUE *argv, VALUE class) { VALUE self; TTF_Font *font; if(!TTF_WasInit()) rb_raise(eSDLError,"Font module must be initialized before making new font."); font = TTF_OpenFont(StringValuePtr(argv[0]), NUM2INT(argv[1])); if(font == NULL) rb_raise(eSDLError,"could not load font: %s",TTF_GetError()); self = Data_Wrap_Struct(cTTF,0,TTF_CloseFont,font); rb_obj_call_init(self,argc,argv); return self; } VALUE rbgm_ttf_initialize(int argc, VALUE *argv, VALUE self) { return self; } /* call-seq: * bold -> Bool * * True if bolding is enabled for the font. */ VALUE rbgm_ttf_getbold(VALUE self) { TTF_Font *font; int style; Data_Get_Struct(self,TTF_Font,font); style = TTF_GetFontStyle(font); if((style & TTF_STYLE_BOLD) == TTF_STYLE_BOLD) return Qtrue; else return Qfalse; } /* call-seq: * bold = value -> Bool * * Set whether bolding is enabled for this font. Returns the old value. */ VALUE rbgm_ttf_setbold(VALUE self,VALUE bold) { TTF_Font *font; int style; Data_Get_Struct(self,TTF_Font,font); style = TTF_GetFontStyle(font); /* Font is currently bold, and we want it to be not bold. */ if((style & TTF_STYLE_BOLD) == TTF_STYLE_BOLD && !bold) { TTF_SetFontStyle(font,style^TTF_STYLE_BOLD); return Qtrue; /* The old value */ } /* Font is not currently bold, and we want it to be bold. */ else if(bold) { TTF_SetFontStyle(font,style|TTF_STYLE_BOLD); return Qfalse; /* The old value */ } /* No changes were necessary. */ return bold; /* Same as old value */ } /* call-seq: * italic -> Bool * * True if italicizing is enabled for the font. */ VALUE rbgm_ttf_getitalic(VALUE self) { TTF_Font *font; int style; Data_Get_Struct(self,TTF_Font,font); style = TTF_GetFontStyle(font); if((style & TTF_STYLE_ITALIC) == TTF_STYLE_ITALIC) return Qtrue; else return Qfalse; } /* call-seq: * italic = value -> Bool * * Set whether italicizing is enabled for this font. Returns the old value. */ VALUE rbgm_ttf_setitalic(VALUE self,VALUE italic) { TTF_Font *font; int style; Data_Get_Struct(self,TTF_Font,font); style = TTF_GetFontStyle(font); /* Font is currently italic, and we want it to be not italic. */ if((style & TTF_STYLE_ITALIC) == TTF_STYLE_ITALIC && !italic) { TTF_SetFontStyle(font,style^TTF_STYLE_ITALIC); return Qtrue; /* The old value */ } /* Font is not currently italic, and we want it to be italic. */ else if(italic) { TTF_SetFontStyle(font,style|TTF_STYLE_ITALIC); return Qfalse; /* The old value */ } /* No changes were necessary. */ return italic; /* Same as old value */ } /* call-seq: * underline -> Bool * * True if underlining is enabled for the font. */ VALUE rbgm_ttf_getunderline(VALUE self) { TTF_Font *font; int style; Data_Get_Struct(self,TTF_Font,font); style = TTF_GetFontStyle(font); if((style & TTF_STYLE_UNDERLINE) == TTF_STYLE_UNDERLINE) return Qtrue; else return Qfalse; } /* call-seq: * underline = value -> Bool * * Set whether underlining is enabled for this font. Returns the old value. */ VALUE rbgm_ttf_setunderline(VALUE self,VALUE underline) { TTF_Font *font; int style; Data_Get_Struct(self,TTF_Font,font); style = TTF_GetFontStyle(font); /* Font is currently underlined, and we want it to be not underlined. */ if((style & TTF_STYLE_UNDERLINE) == TTF_STYLE_UNDERLINE && !underline) { TTF_SetFontStyle(font,style^TTF_STYLE_UNDERLINE); return Qtrue; /* The old value */ } /* Font is not currently underlined, and we want it to be underlined. */ else if(underline) { TTF_SetFontStyle(font,style|TTF_STYLE_UNDERLINE); return Qfalse; /* The old value */ } /* No changes were necessary. */ return underline; /* Same as old value */ } /* call-seq: * height -> Integer * * Return the biggest height (bottom to top; in pixels) of all glyphs in the * font. */ VALUE rbgm_ttf_height(VALUE self) { TTF_Font *font; Data_Get_Struct(self,TTF_Font,font); return INT2NUM(TTF_FontHeight(font)); } /* call-seq: * ascent -> Integer * * Return the biggest ascent (baseline to top; in pixels) of all glyphs in * the font. */ VALUE rbgm_ttf_ascent(VALUE self) { TTF_Font *font; Data_Get_Struct(self,TTF_Font,font); return INT2NUM(TTF_FontAscent(font)); } /* call-seq: * descent -> Integer * * Return the biggest descent (baseline to bottom; in pixels) of all glyphs in * the font. */ VALUE rbgm_ttf_descent(VALUE self) { TTF_Font *font; Data_Get_Struct(self,TTF_Font,font); return INT2NUM(TTF_FontDescent(font)); } /* call-seq: * lineskip -> Integer * * Return the recommended distance (in pixels) from a point on a line of text * to the same point on the line of text below it. */ VALUE rbgm_ttf_lineskip(VALUE self) { TTF_Font *font; Data_Get_Struct(self,TTF_Font,font); return INT2NUM(TTF_FontLineSkip(font)); } /* call-seq: * render(string, aa, fg, bg) -> Surface * * Renders a string to a Surface with the font's style and the given color(s). * * This method takes these arguments: * string:: the text string to render * aa:: Use anti-aliasing if true. Enabling this makes the text * look much nicer (smooth curves), but is much slower. * fg:: the color to render the text, in the form [r,g,b] * bg:: the color to use as a background for the text. This option can * be omitted to have a transparent background. */ VALUE rbgm_ttf_render(int argc, VALUE *argv, VALUE self) { SDL_Surface *surf; TTF_Font *font; int antialias; SDL_Color fore, back; /* foreground and background colors */ if(argc<3) rb_raise(rb_eArgError,"wrong number of arguments (%d for 3)",argc); Data_Get_Struct(self,TTF_Font,font); antialias = argv[1]; fore.r = NUM2UINT(rb_ary_entry(argv[2],0)); fore.g = NUM2UINT(rb_ary_entry(argv[2],1)); fore.b = NUM2UINT(rb_ary_entry(argv[2],2)); if(argc>3) { back.r = NUM2UINT(rb_ary_entry(argv[3],0)); back.g = NUM2UINT(rb_ary_entry(argv[3],1)); back.b = NUM2UINT(rb_ary_entry(argv[3],2)); } if(antialias) /* anti-aliasing enabled */ { if(argc>3) /* background color provided */ surf = TTF_RenderText_Shaded(font,StringValuePtr(argv[0]),fore,back); else /* no background color */ surf = TTF_RenderText_Blended(font,StringValuePtr(argv[0]),fore); } else /* anti-aliasing not enabled */ { if(argc>3) /* background color provided */ { /* remove colorkey, set color index 0 to background color */ surf = TTF_RenderText_Solid(font,StringValuePtr(argv[0]),fore); SDL_SetColors(surf,&back,0,1); SDL_SetColorKey(surf,0,0); } else /* no background color */ { surf = TTF_RenderText_Solid(font,StringValuePtr(argv[0]),fore); } } if(surf==NULL) rb_raise(eSDLError,"could not render font object: %s",TTF_GetError()); return Data_Wrap_Struct(cSurface,0,SDL_FreeSurface,surf); } #endif /* HAVE_SDL_TTF_H */ /* * Document-class: Rubygame::TTF * * TTF provides an interface to SDL_ttf, allowing TrueType Font files to be * loaded and used to render text to Surfaces. * * The TTF class *must* be initialized with the #setup method before any * TTF objects can be created or used. * * This class is only usable if Rubygame was compiled with the SDL_ttf * library. You may test if this feature is available with the #usable? * method. If you need more flexibility, you can check the library version * that Rubygame was compiled against with the #version method. */ void Rubygame_Init_TTF() { #if 0 /* Pretend to define Rubygame module, so RDoc knows about it: */ mRubygame = rb_define_module("Rubygame"); #endif cTTF = rb_define_class_under(mRubygame,"TTF",rb_cObject); rb_define_singleton_method(cTTF,"version",rbgm_ttf_version,0); #ifdef HAVE_SDL_TTF_H /* SDL_TTF is available, so we will provide the real functions. */ rb_define_singleton_method(cTTF,"new",rbgm_ttf_new,-1); rb_define_singleton_method(cTTF,"usable?",rbgm_usable,0); // in rubygame.c rb_define_singleton_method(cTTF,"setup",rbgm_ttf_setup,0); rb_define_singleton_method(cTTF,"quit",rbgm_ttf_quit,0); rb_define_method(cTTF,"initialize",rbgm_ttf_initialize,-1); rb_define_method(cTTF,"bold",rbgm_ttf_getitalic,0); rb_define_method(cTTF,"bold=",rbgm_ttf_setitalic,1); rb_define_alias( cTTF,"b","bold"); rb_define_alias( cTTF,"b=","bold="); rb_define_method(cTTF,"italic",rbgm_ttf_getitalic,0); rb_define_method(cTTF,"italic=",rbgm_ttf_setitalic,1); rb_define_alias( cTTF,"i","italic"); rb_define_alias( cTTF,"i=","italic="); rb_define_method(cTTF,"underline",rbgm_ttf_getunderline,0); rb_define_method(cTTF,"underline=",rbgm_ttf_setunderline,1); rb_define_alias( cTTF,"u","underline"); rb_define_alias( cTTF,"u=","underline="); rb_define_method(cTTF,"height",rbgm_ttf_height,0); rb_define_method(cTTF,"ascent",rbgm_ttf_ascent,0); rb_define_method(cTTF,"descent",rbgm_ttf_descent,0); rb_define_method(cTTF,"line_skip",rbgm_ttf_lineskip,0); rb_define_method(cTTF,"render",rbgm_ttf_render,-1); #else /* SDL_TTF is NOT available, so we will provide dummy functions. */ rb_define_singleton_method(cTTF,"new",rbgm_dummy,-1); rb_define_singleton_method(cTTF,"usable?",rbgm_unusable,0); rb_define_singleton_method(cTTF,"setup",rbgm_dummy,-1); rb_define_singleton_method(cTTF,"quit",rbgm_dummy,-1); rb_define_method(cTTF,"initialize",rbgm_dummy,-1); /* We can skip the rest, because no TTF instances will ever be created. */ #endif }