#include "Font.h" #ifdef WIN32 #include #endif #include #include #include #include "LogManager.h" namespace foundation { int round(double x) { return (int)(x + 0.5); } int powerOfTwo(int x) { double logbase2 = log((float)x) / log(2.0f); return round(pow(2,ceil(logbase2))); } void flipSurface(SDL_Surface * surface) { SDL_LockSurface(surface); int lineSize = surface->pitch; unsigned char* tempLine = new unsigned char[lineSize]; for (int i = 0; i < surface->h/2; i ++) { void* bottom = &((unsigned char* )surface->pixels)[(surface->h - 1 -i)*lineSize]; void* top = &((unsigned char* )surface->pixels)[i*lineSize]; memcpy(tempLine,bottom,lineSize); memcpy(bottom,top,lineSize); memcpy(top,tempLine,lineSize); } delete[] tempLine; SDL_UnlockSurface(surface); } // Create a texture from a surface. Set the alpha according // to the color key. Pixels that match the color key get an // alpha of zero while all other pixels get an alpha of // one. GLuint loadTextureColorKey(SDL_Surface *surface, GLfloat *texcoord, int ckr, int ckg, int ckb) { GLuint texture; int w, h; SDL_Surface *image; SDL_Rect area; Uint32 colorkey; // Use the surface width and height expanded to powers of 2 w = powerOfTwo(surface->w); h = powerOfTwo(surface->h); texcoord[0] = 0.0f; // Min X texcoord[1] = 0.0f; // Min Y texcoord[2] = (GLfloat)surface->w / w; // Max X texcoord[3] = (GLfloat)surface->h / h; // Max Y image = SDL_CreateRGBSurface( SDL_SWSURFACE, w, h, 32, #if SDL_BYTEORDER == SDL_LIL_ENDIAN // OpenGL RGBA masks 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 #else 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF #endif ); if (image == NULL) { return 0; } // Set up so that colorkey pixels become transparent colorkey = SDL_MapRGBA(image->format, ckr, ckg, ckb, 0); SDL_FillRect(image, NULL, colorkey); colorkey = SDL_MapRGBA(surface->format, ckr, ckg, ckb, 0); SDL_SetColorKey(surface, SDL_SRCCOLORKEY, colorkey); // Copy the surface into the GL texture image area.x = 0; area.y = 0; area.w = surface->w; area.h = surface->h; SDL_BlitSurface(surface, &area, image, &area); flipSurface(image); // Create an OpenGL texture for the image glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels); SDL_FreeSurface(image); // No longer needed return texture; } int Font::initCounter = 0; void Font::loadChar(unsigned char c) { GLfloat texcoord[4]; char letter[2] = {0, 0}; if ((minGlyph <= c) && (c <= maxGlyph) && (NULL == glyphs[((int)c)].pic)) { SDL_Surface *g0 = NULL; SDL_Surface *g1 = NULL; letter[0] = c; TTF_GlyphMetrics(ttfFont, (Uint16)c, &glyphs[((int)c)].minx, &glyphs[((int)c)].maxx, &glyphs[((int)c)].miny, &glyphs[((int)c)].maxy, &glyphs[((int)c)].advance); g0 = TTF_RenderText_Blended(ttfFont, letter, foreground); if (NULL != g0) { g1 = SDL_DisplayFormatAlpha(g0); SDL_FreeSurface(g0); } if (NULL != g1) { glyphs[((int)c)].pic = g1; glyphs[((int)c)].tex = loadTextureColorKey(g1, texcoord,0,0,0); glyphs[((int)c)].texMinX = texcoord[0]; glyphs[((int)c)].texMinY = texcoord[1]; glyphs[((int)c)].texMaxX = texcoord[2]; glyphs[((int)c)].texMaxY = texcoord[3]; } } } Font::Font(const char *fontName, int pointSize,int fontStyle, float fgRed, float fgGreen, float fgBlue): fontName(fontName), pointSize(pointSize),fontStyle(fontStyle), fgRed(fgRed), fgGreen(fgGreen), fgBlue(fgBlue), ttfFont(NULL),tabSize(4) { if (0 == initCounter) { if (TTF_Init() < 0) { LogManager::log(LOG_WARNING) << "Can't init sdl_ttf !?\n"; } } initCounter++; initFont(); } Font::~Font() { unload(); TTF_CloseFont(ttfFont); initCounter--; if (0 == initCounter) { TTF_Quit(); } } void Font::initFont() { int i; ttfFont = TTF_OpenFont(fontName.c_str(), pointSize); if (NULL == ttfFont) { LogManager::log(LOG_WARNING) << "Can't open font file " << fontName << "\n"; } foreground.r = (Uint8)(255 * fgRed); foreground.g = (Uint8)(255 * fgGreen); foreground.b = (Uint8)(255 * fgBlue); height = TTF_FontHeight(ttfFont); ascent = TTF_FontAscent(ttfFont); descent = TTF_FontDescent(ttfFont); lineSkip = TTF_FontLineSkip(ttfFont); TTF_SetFontStyle(ttfFont,fontStyle); for (i = minGlyph; i <= maxGlyph; i++) { glyphs[i].pic = NULL; glyphs[i].tex = 0; } } void Font::unload() { for (int i = minGlyph; i <= maxGlyph; i++) { glDeleteTextures(1,&glyphs[i].tex); glyphs[i].tex = 0; } } int Font::getLineSkip() { return lineSkip; } int Font::getHeight() { return height; } void Font::textSize(char *text, int& _width, int& _height) { int maxx = 0; int advance = 0; int minx = 0; int w_largest = 0; char lastchar = 0; _width = 0; _height = height; while (0 != *text) { unsigned char t = *text; if(*text < 0) t = 256 + *text; if ((minGlyph <= t) && (t <= maxGlyph)) { lastchar = t; if (t == '\n') { _height += lineSkip; _width = _width - advance + maxx; if (_width > w_largest) w_largest = _width; _width = 0; } else if (t == '\t') { _width += glyphs[((int)' ')].advance*tabSize; } else { loadChar(t); maxx = glyphs[((int)t)].maxx; advance = glyphs[((int)t)].advance; _width += advance; } } text++; } if (lastchar != '\n') { _width = _width - advance + maxx; if (_width > w_largest) w_largest = _width; } else { _height -= lineSkip; } if (w_largest > _width) _width = w_largest; } void Font::drawText( int x, int y,const char *text) { GLfloat left, right; GLfloat top, bottom; GLfloat texMinX, texMinY; GLfloat texMaxX, texMaxY; GLfloat minx; GLfloat baseleft = x; glPushAttrib(GL_ALL_ATTRIB_BITS); glEnable(GL_BLEND); //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR); glEnable(GL_TEXTURE_2D); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); while (0 != *text) { // on prend en compte la table EBCDIC pour avoir les caractères accentué de base unsigned char t = *text; if(*text < 0) t = 256 + *text; if (t == '\n') { x = baseleft; y += lineSkip; } else if (t == '\t') { x += glyphs[' '].advance*tabSize; } else if ((minGlyph <= t) && (t <= maxGlyph)) { loadChar(t); texMinX = glyphs[((int)t)].texMinX; texMinY = glyphs[((int)t)].texMinY; texMaxX = glyphs[((int)t)].texMaxX; texMaxY = glyphs[((int)t)].texMaxY; minx = glyphs[((int)t)].minx; left = x + minx; right = x + glyphs[((int)t)].pic->w + minx; top = y; bottom = y + glyphs[((int)t)].pic->h; glBindTexture(GL_TEXTURE_2D, glyphs[((int)t)].tex); glBegin(GL_TRIANGLE_STRIP); // utiliser ca quand l'origine est en haut à gauche glTexCoord2f(texMinX, texMinY); glVertex2f( left, bottom); glTexCoord2f(texMaxX, texMinY); glVertex2f(right, bottom); glTexCoord2f(texMinX, texMaxY); glVertex2f( left, top); glTexCoord2f(texMaxX, texMaxY); glVertex2f(right, top); // utilise ca quand l'origine est en bas à gauche //glTexCoord2f(texMinX, texMinY); glVertex2f( left, top); //glTexCoord2f(texMaxX, texMinY); glVertex2f(right, top); //glTexCoord2f(texMinX, texMaxY); glVertex2f( left, bottom); //glTexCoord2f(texMaxX, texMaxY); glVertex2f(right, bottom); glEnd(); x += glyphs[((int)t)].advance; } text++; } glPopAttrib(); } void Font::drawTextf( int x, int y,const char *text,...) { char fullText[512]; va_list argsPtr; va_start(argsPtr, text); vsprintf(fullText, text, argsPtr); va_end(argsPtr); drawText(x,y,fullText); } } // namespace foundation