/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx 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, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_IMPORTER #include "font_data.h" #include "debug.h" #include "compiler.h" #include "texture.h" /** * @brief creates a new Font Data. */ FontData::FontData() : texData(new TextureData) { this->glyphArray = NULL; this->renderStyle = TTF_STYLE_NORMAL; this->renderSize = FONT_DEFAULT_RENDER_SIZE; this->maxHeight = 0; this->maxAscent = 0; this->maxDescent = 0; } /** * @brief Destructor of a Font * * Frees Data, and deletes the fonts from GL */ FontData::~FontData() { // deleting all Glyphs if (this->glyphArray != NULL) { for (int i = 0; i < FONT_HIGHEST_KNOWN_CHAR; i++) { if (this->glyphArray[i] != NULL) delete this->glyphArray[i]; } delete[] this->glyphArray; } //! @todo check if we really do not need to delete the fastTextureID here. // if (this->fastTextureID != 0) // if(glIsTexture(this->fastTextureID)) // glDeleteTextures(1, &this->fastTextureID); } /** * @brief sets The Font. * @param fontFile The file containing the font. * @returns true if loaded, false if something went wrong, or if a font was loaded before. */ bool FontData::loadFontFromTTF(const std::string& fontFile, unsigned int renderSize) { TTF_Font* fontTTF; //this->setName(fontFile); fontTTF = TTF_OpenFont(fontFile.c_str(), renderSize); this->renderSize = renderSize; if(fontTTF != NULL) { this->maxHeight = TTF_FontHeight(fontTTF); this->maxAscent = TTF_FontAscent(fontTTF); this->maxDescent = TTF_FontDescent(fontTTF); this->setStyle(fontTTF, "c"); if (this->createFastTexture(fontTTF)) { TTF_CloseFont(fontTTF); return true; } else { TTF_CloseFont(fontTTF); PRINTF(1)("Unable to createa a Fast Texture fot %s\n", fontFile.c_str() ); return false; } } else { PRINTF(1)("TTF_OpenFont: %s for %s\n", TTF_GetError(), fontFile.c_str()); return false; } } /** * @brief loads a font From an XPM-array. * @param xpmArray the array of the XPM to load the font from. */ bool FontData::loadFontFromSDL_Surface(SDL_Surface* surface) { if(surface == NULL) return false; bool hasAlpha; SDL_Surface* newSurf = Texture::prepareSurface(surface, hasAlpha); if (newSurf != NULL) { this->texData->setSurface(newSurf); this->texData->setAlpha(hasAlpha); this->texData->setTexture(Texture::loadTexToGL(newSurf)); } else { return false; } // initializing the Glyphs. if (this->glyphArray == NULL) { float cx,cy; Glyph* tmpGlyph; this->glyphArray = new Glyph*[FONT_HIGHEST_KNOWN_CHAR]; for (int i = 0; i < FONT_HIGHEST_KNOWN_CHAR; i++) { tmpGlyph = this->glyphArray[i] = new Glyph; cx=(float)(i%16)/16.0f; // X Position Of Current Character cy=(float)(i/16)/16.0f; // Y Position Of Current Character tmpGlyph->texCoord[0] = cx; tmpGlyph->texCoord[1] = cx+0.0625f; tmpGlyph->texCoord[2] = cy; tmpGlyph->texCoord[3] = cy+0.0625f; tmpGlyph->minX = 0.0f; tmpGlyph->maxX = 1.0f; tmpGlyph->minY = 0.0f; tmpGlyph->maxY = 1.0f; tmpGlyph->width = 1.0f; tmpGlyph->advance = 1.0f; tmpGlyph->bearingX = 0.0f; tmpGlyph->bearingY = 0.0f; tmpGlyph->height = 1.0f; } } return true; } /** * @brief sets a specific data->renderStyle * @param data->renderStyle the Style to render: a string (char-array) containing: * i: italic, b: bold, u, underline */ void FontData::setStyle(TTF_Font* font, const std::string& renderStyle) { this->renderStyle = TTF_STYLE_NORMAL; for (unsigned int i = 0; i < renderStyle.size(); i++) { if (renderStyle[i] == 'b') this->renderStyle |= TTF_STYLE_BOLD; else if (renderStyle[i] == 'i') this->renderStyle |= TTF_STYLE_ITALIC; else if (renderStyle[i] == 'u') this->renderStyle |= TTF_STYLE_UNDERLINE; } /// !TODO REBUILD THE FONT if (likely(font != NULL)) TTF_SetFontStyle(font, this->renderStyle); // else // PRINTF(2)("Font was not initialized, please do so before setting the Font-Style.\n"); } /** * @param glyph: The Glyph to set the Parameters to. * @param character: The character to get info about. * @returns a Glyph struct of a character. This Glyph is a pointer, * and MUST be deleted by the user.. * * This only works for horizontal fonts. see * http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html * for more info about vertical Fonts */ bool FontData::getGlyphMetrics(TTF_Font* font, Glyph* glyph, Uint16 character) { glyph->character = character; if (likely (font != NULL)) { int miX, maX, miY, maY, adv; if (TTF_GlyphMetrics(font, glyph->character, &miX, &maX, &miY, &maY, &adv) == -1) return false; glyph->minX = (float)miX / (float)this->renderSize; glyph->maxX = (float)maX / (float)this->renderSize; glyph->minY = (float)miY / (float)this->renderSize; glyph->maxY = (float)maY / (float)this->renderSize; glyph->advance = (float)adv / (float)this->renderSize; // Calculate the Rest. glyph->height = glyph->maxY - glyph->minY; glyph->width = glyph->maxX - glyph->minX; glyph->bearingX = (glyph->advance - glyph->width) / 2; glyph->bearingY = glyph->maxY; //printf("%c:: %d %d %d %d %d\n", character, miX, maX, miY, maY, adv); return true; } return false; } /** * @brief creates a Fast-Texture of this Font */ bool FontData::createFastTexture(TTF_Font* fontTTF) { /* interesting GLYPHS: * 32: space * 33-47: Special Characters. * 48-57: 0-9 * 58-63: some more special chars (minor) * 65-90: A-Z * 97-122: a-z */ int numberOfGlyphs = 91; this->initGlyphs(fontTTF, 32, numberOfGlyphs); // this->glyphArray[32]->width = .5f; //!< @todo find out the real size of a Space int rectSize = this->findOptimalFastTextureSize(); // setting default values. (maybe not needed afterwards) SDL_Color tmpColor; tmpColor.r = tmpColor.g = tmpColor.b = 0; // Surface definition. SDL_Rect tmpRect; // this represents a Rectangle for blitting. SDL_Surface* tmpSurf = SDL_CreateRGBSurface(SDL_HWSURFACE, rectSize, rectSize, 32, #if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */ 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 #else 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF #endif ); tmpRect.x = 0; tmpRect.y = 0; tmpRect.w = tmpSurf->w; tmpRect.h = tmpSurf->h; SDL_SetClipRect(tmpSurf, &tmpRect); int maxLineHeight = this->getMaxHeight(); // all the interessting Glyphs for (int i = 0; i < 128; i++) { SDL_Surface* glyphSurf = NULL; Glyph* tmpGlyph; if ((tmpGlyph = this->glyphArray[i]) != NULL) { if (tmpGlyph->height*this->renderSize > maxLineHeight) maxLineHeight = (int)(tmpGlyph->height*this->renderSize); if (tmpRect.x+tmpGlyph->advance*this->renderSize > tmpSurf->w) { tmpRect.x = 0; tmpRect.y = tmpRect.y + maxLineHeight; } if (tmpRect.y + maxLineHeight > tmpSurf->h) { PRINTF(1)("Protection, so font cannot write over the boundraries (!!this should not heappen!!)\n"); break; } // reading in the new Glyph if (likely(fontTTF != NULL)) { SDL_Color white = {255, 255, 255}; glyphSurf = TTF_RenderGlyph_Blended(fontTTF, i, white); } if( glyphSurf != NULL ) { SDL_SetAlpha(glyphSurf, 0, 0); int tmpY = tmpRect.y; tmpRect.y += this->getMaxAscent()-(int)((float)tmpGlyph->bearingY*this->renderSize); SDL_BlitSurface(glyphSurf, NULL, tmpSurf, &tmpRect); tmpRect.y = tmpY; tmpGlyph->texCoord[0] = (float)((float)tmpRect.x )/(float)tmpSurf->w; tmpGlyph->texCoord[1] = (float)((float)tmpRect.x + tmpGlyph->width*(float)this->renderSize)/(float)tmpSurf->w; tmpGlyph->texCoord[2] = (float)(tmpRect.y)/(float)tmpSurf->w; tmpGlyph->texCoord[3] = (float)((float)tmpRect.y+(float)this->getMaxHeight())/(float)tmpSurf->w; SDL_FreeSurface(glyphSurf); tmpRect.x += (int)(tmpGlyph->width * this->renderSize) + 1; } } } // outputting the GLYPH-table // char outName[1024]; // sprintf( outName, "%s-glyphs.bmp", this->getName()); // SDL_SaveBMP(tmpSurf, outName); this->texData->setAlpha(true); if (this->texData->setSurface(tmpSurf)) this->texData->setTexture(Texture::loadTexToGL(tmpSurf)); return true; } /** * @brief stores Glyph Metrics in an Array. * @param from The Glyph to start from. * @param count The number of Glyphs to start From. */ void FontData::initGlyphs(TTF_Font* font, Uint16 from, Uint16 count) { /* initialize the Array, and set all its entries to NULL * only if the Glyph-array has not been initialized */ if (!this->glyphArray) { this->glyphArray = new Glyph*[FONT_HIGHEST_KNOWN_CHAR]; for (int i = 0; i < FONT_HIGHEST_KNOWN_CHAR; i++) this->glyphArray[i] = NULL; } Uint16 lastGlyph = from + count; for (int i = from; i <= lastGlyph; i++) { // setting up all the Glyphs we like. Glyph* newGlyph = new Glyph; if (getGlyphMetrics(font, newGlyph, i)) this->glyphArray[i] = newGlyph; else { delete newGlyph; this->glyphArray[i] = NULL; } } return; } /** * @returns the optimal size to use as the texture size * * @todo: this algorithm can be a lot faster, althought it does * not really matter within the init-context, and 128 glyphs. * * This function searches for a 2^n sizes texture-size, this is for * openGL-version < 1.2 compatibility ( and because it is realy easy like this :)) */ int FontData::findOptimalFastTextureSize() { if (this->glyphArray == NULL) return 0; unsigned int i; unsigned int x,y; // the counters int maxLineHeight = this->getMaxHeight(); unsigned int size = 32; // starting Value, we have to start somewhere 32 seems reasonable. (take any small enough 2^i number) bool sizeOK = false; Glyph* tmpGlyph; while (!sizeOK) { x = 0; y = 0; for (i = 0; i < FONT_HIGHEST_KNOWN_CHAR; i++) { if((tmpGlyph = this->glyphArray[i]) != NULL) { // getting the height of the highest Glyph in the Line. if (tmpGlyph->height*this->renderSize > maxLineHeight) maxLineHeight = (int)(tmpGlyph->height*this->renderSize); if (x + tmpGlyph->advance*this->renderSize > size) { x = 0; y = y + maxLineHeight; //maxLineHeight = 0; } if (y + maxLineHeight + 1 > size) break; x += (int)(tmpGlyph->advance*this->renderSize)+1; } } if (i >= FONT_HIGHEST_KNOWN_CHAR-1 || size > 8192) sizeOK = true; else size *= 2; } return size; }