// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // Copyright (C) 2011 by Callum Dickinson. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. //----------------------------------------------------------------------------- /// \file /// \brief SDL_ttf interface code. Necessary for platforms with no framebuffer console systems. #if defined(HAVE_SDL) && defined(HAVE_TTF) #include "SDL.h" #include "SDL_ttf.h" #include "../doomdef.h" #include "../doomstat.h" #include "../d_netfil.h" #include "../filesrch.h" #include "i_ttf.h" // Search directories to find aforementioned TTF file. #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) #define FONTSEARCHPATH1 "/usr/share/fonts" #define FONTSEARCHPATH2 "/usr/local/share/fonts" #define FONTSEARCHPATH3 "/usr/games/SRB2" #define FONTSEARCHPATH4 "/usr/local/games/SRB2" #define FONTSEARCHPATH5 "/usr/local/share/games/SRB2" #else #define FONTSEARCHPATH1 "." #endif #define FONTHANDLE -1 // Renduring surfaces. SDL_Surface *TTFSurface = NULL; SDL_Surface *TTFRendSurface = NULL; // Text box. SDL_Rect TTFRect; // Temporary storage for the new TTFRect, used to check for // line wrapping. SDL_Rect TTFRectCheck; // Text rendering resolution. videoResolution res; // Text storage buffer, the contents get printed to the SDL surface. char textbuffer[8192]; // look for default ttf file in given directory static char *searchFont(const char *fontsearchDir) { static char tempsw[256] = ""; filestatus_t fstemp; strcpy(tempsw, FONTFILE); fstemp = filesearch(tempsw, fontsearchDir, NULL, true, 20); if (fstemp == FS_FOUND) { return tempsw; } return NULL; } // Load TTF font from file. INT32 I_TTFLoadFont(const char *file, UINT32 ptsize) { TTF_Font *tmpfont = NULL; float fontsize; // If a font is currently loaded, unload it. if (currentfont) { TTF_CloseFont(currentfont); } // Scale the specified font point size for the current resolution. fontsize = (ptsize * 0.005f) * (res.width - res.height); tmpfont = TTF_OpenFont(file, fontsize); if (!tmpfont) return FONTHANDLE; // set pointer for current font currentfont = tmpfont; // set current font point size currentfontpoint = ptsize; // get font properties, and set them currentfontstyle = TTF_GetFontStyle(currentfont); TTF_SetFontStyle(currentfont, currentfontstyle); // these functions only exist in SDL_ttf 2.0.10 onwards #if SDL_TTF_VERSION_ATLEAST(2,0,10) currentfontkerning = TTF_GetFontKerning(currentfont); TTF_SetFontKerning(currentfont, currentfontkerning); currentfonthinting = TTF_GetFontHinting(currentfont); TTF_SetFontHinting(currentfont, currentfonthinting); currentfontoutline = TTF_GetFontOutline(currentfont); TTF_SetFontOutline(currentfont, currentfontoutline); #endif return 0; } static void I_TTFRendSurface(const char *textmsg, TTF_Font *font, TextQuality quality, SDL_Color fontfgcolor, SDL_Color fontbgcolor) { // Print text in the buffer. // SDL_ttf has three modes to draw text. // Solid rendering is quick, but dirty. Use it if you need speed more than quality. switch (quality) { case solid: TTFRendSurface = TTF_RenderText_Solid(font, textmsg, fontfgcolor); break; // Shaded rendering adds a background to the rendered text. Because of this, I_TTFDrawText // takes an extra color more than the other styles to be a background color. // Shaded is supposedly as fast as solid rendering and about as good quality as blended. case shaded: TTFRendSurface = TTF_RenderText_Shaded(font, textmsg, fontfgcolor, fontbgcolor); break; // Blended rendering is the opposite of solid. Good quality, but slow. case blended: TTFRendSurface = TTF_RenderText_Blended(font, textmsg, fontfgcolor); break; } // Get SDL to update the main surface. SDL_BlitSurface(TTFRendSurface, NULL, TTFSurface, &TTFRect); SDL_Flip(TTFSurface); } // Draw text to screen. It will accept four colour vales (red, green, blue and alpha) // with foreground for draw modes Solid and Blended, and an extra four values for background // colour with draw type Shaded. void I_TTFDrawText(TTF_Font *font, TextQuality quality, INT32 fgR, INT32 fgG, INT32 fgB, INT32 fgA, INT32 bgR, INT32 bgG, INT32 bgB, INT32 bgA, const char *textmsg) { // Temporary small buffer to store character to process. // NULL pointer to prevc to kill warning char c, prevc = 0x0; // hack to allow TTF_SizeText to work properly. char linebuffer[2]; // Don't need h, but TTF_SizeText needs a height parameter INT32 w, h; // Globally declare foreground and background text colours, // text drawing mode and the font to draw. SDL_Color fontfgcolor = {fgR, fgG, fgB, fgA}; SDL_Color fontbgcolor = {bgR, bgG, bgB, bgA}; // Keep on processing until the null terminator in the text buffer is reached. while (*textmsg != '\0') { // Copy pointer for current character into the temporary buffer. c = *textmsg; // If c is a newline, move to the next available line. if (c == '\n') { TTFRectCheck.x = 0; TTFRectCheck.y += (currentfontpoint + 1); } // Otherwise... else { // If the previous character was a newline, actually move to the next line. if (prevc == '\n') { if (textbuffer != NULL) { // Render cached text to the SDL surface. I_TTFRendSurface(textbuffer, font, quality, fontfgcolor, fontbgcolor); // Empty text buffer. memset(textbuffer, '\0', 1); } TTFRect.x = TTFRectCheck.x; TTFRect.y = TTFRectCheck.y; } // Copy the character to the text buffer. sprintf(textbuffer, "%s%c", textbuffer, c); // Hack to allow TTF_SizeText to work properly. sprintf(linebuffer, "%c", c); // If we have reached the end of the screen, move to the next available line. TTF_SizeText(currentfont, linebuffer, &w, &h); TTFRectCheck.x += w; if (TTFRectCheck.x >= res.width) { // Render cached text to the SDL surface. I_TTFRendSurface(textbuffer, font, quality, fontfgcolor, fontbgcolor); // Empty text buffer. memset(textbuffer, '\0', 1); // Move to the next line. TTFRectCheck.x = 0; TTFRectCheck.y += (currentfontpoint + 1); // Set stored co-ordinates for next line. TTFRect.x = TTFRectCheck.x; TTFRect.y = TTFRectCheck.y; } } // Add 1 to the pointer reference for the character to process. textmsg++; // Copy contents of the now-old buffer to somewhere else, so it can be referenced in next loop. prevc = c; } // If the buffer was previously emptied by a line wrapping operation and // no text came after that, don't print anything. Otherwise, print everything // still in the buffer. if (textbuffer != NULL) { // Render cached text to the SDL surface. I_TTFRendSurface(textbuffer, font, quality, fontfgcolor, fontbgcolor); // Empty text buffer. memset(textbuffer, '\0', 1); // Set stored co-ordinates for next line. TTFRect.x = TTFRectCheck.x; TTFRect.y = TTFRectCheck.y; } } // Initialise SDL_ttf. void I_StartupTTF(UINT32 fontpointsize, Uint32 initflags, Uint32 vidmodeflags) { char *fontpath = NULL; INT32 fontstatus = -1; res.width = 320; res.height = 200; bitsperpixel = 8; // what's the point of trying to display an error? // SDL_ttf is not started, can't display anything to screen (presumably)... if (SDL_InitSubSystem(initflags) < 0) I_Error("Couldn't initialize SDL: %s\n", SDL_GetError()); TTFSurface = SDL_SetVideoMode(res.width, res.height, bitsperpixel, vidmodeflags); if (!TTFSurface) I_Error("Couldn't set SDL Video resolution: %s\n", SDL_GetError()); if (TTF_Init() < 0) I_Error("Couldn't start SDL_ttf: %s\n", TTF_GetError()); // look for default font in many directories #ifdef FONTSEARCHPATH1 fontpath = searchFont(FONTSEARCHPATH1); if (fontpath) fontstatus = I_TTFLoadFont(fontpath, fontpointsize); #endif #ifdef FONTSEARCHPATH2 if (fontstatus < 0) { fontpath = searchFont(FONTSEARCHPATH2); if (fontpath) fontstatus = I_TTFLoadFont(fontpath, fontpointsize); } #endif #ifdef FONTSEARCHPATH3 if (fontstatus < 0) { fontpath = searchFont(FONTSEARCHPATH3); if (fontpath) fontstatus = I_TTFLoadFont(fontpath, fontpointsize); } #endif #ifdef FONTSEARCHPATH4 if (fontstatus < 0) { fontpath = searchFont(FONTSEARCHPATH4); if (fontpath) fontstatus = I_TTFLoadFont(fontpath, fontpointsize); } #endif #ifdef FONTSEARCHPATH5 if (fontstatus < 0) { fontpath = searchFont(FONTSEARCHPATH5); if (fontpath) fontstatus = I_TTFLoadFont(fontpath, fontpointsize); } #endif #ifdef FONTSEARCHPATH6 if (fontstatus < 0) { fontpath = searchFont(FONTSEARCHPATH6); if (fontpath) fontstatus = I_TTFLoadFont(fontpath, fontpointsize); } #endif #ifdef FONTSEARCHPATH7 if (fontstatus < 0) { fontpath = searchFont(FONTSEARCHPATH7); if (fontpath) fontstatus = I_TTFLoadFont(fontpath, fontpointsize); } #endif // argh! no font file found! disable SDL_ttf code if (fontstatus < 0) { I_ShutdownTTF(); CONS_Printf("Unable to find default font files! Not loading SDL_ttf\n"); } else { // Get SDL_ttf compiled and linked version SDL_version TTFcompiled; const SDL_version *TTFlinked; SDL_TTF_VERSION(&TTFcompiled); TTFlinked = TTF_Linked_Version(); // Display it on screen CONS_Printf("Compiled for SDL_ttf version: %d.%d.%d\n", TTFcompiled.major, TTFcompiled.minor, TTFcompiled.patch); CONS_Printf("Linked with SDL_ttf version: %d.%d.%d\n", TTFlinked->major, TTFlinked->minor, TTFlinked->patch); } } void I_ShutdownTTF(void) { // close current font TTF_CloseFont(currentfont); // shutdown SDL_ttf TTF_Quit(); // Free TTF rendering surfaces. SDL_FreeSurface(TTFSurface); SDL_FreeSurface(TTFRendSurface); } #endif