RenderedGlyphContainer::RenderedGlyph RenderedGlyphContainer::renderGlyph(glyph_char glyph, void*fontptr, void*renderer, unsigned int size, int fontstyle, bool antialiasing) { TTF_Font* font = (TTF_Font*)fontptr; SDL_Color color = {255,255,255,255}; //white SDL_Surface* surface = nullptr; if(antialiasing) { surface = TTF_RenderGlyph_Blended(font, CharForceConvert<glyph_char, GameLibrary::Uint16>(glyph), color); } else { surface = TTF_RenderGlyph_Solid(font, CharForceConvert<glyph_char, GameLibrary::Uint16>(glyph), color); } if(surface == nullptr) { throw RenderGlyphException(TTF_GetError()); } SDL_Texture*texture = SDL_CreateTextureFromSurface((SDL_Renderer*)renderer, surface); SDL_FreeSurface(surface); if(texture == nullptr) { throw RenderGlyphException(SDL_GetError()); } RenderedGlyph renderedGlyph; renderedGlyph.texture = texture; renderedGlyph.size = size; renderedGlyph.fontstyle = fontstyle; renderedGlyph.antialias = antialiasing; return renderedGlyph; }
extern DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_Blended_p( TTF_Font *font, uint16_t glyph, SDL_Color *fg) { return TTF_RenderGlyph_Blended(font, glyph, *fg); }
SDL_Surface* SimKit::TTFVFont::render_font(const Uint32 ch, const SDL_Color textcol, const SimKit::IVFont::TransparencyType bg) { switch (bg) { case SimKit::IVFont::COLORKEY: return TTF_RenderGlyph_Solid(this->rfont, ch, textcol); case SimKit::IVFont::ALPHA: return TTF_RenderGlyph_Blended(this->rfont, ch, textcol); } };
void cache_glyphs() { int i; char title[800]; SDL_Color fg={0,0,0,255}; #if RENDER_MODE==1 SDL_Color bg={255,255,255,255}; #endif free_glyphs(); if(!font) return; if(style!=TTF_GetFontStyle(font)) TTF_SetFontStyle(font,style); if(kerning != !!TTF_GetFontKerning(font)) TTF_SetFontKerning(font,kerning); if(hinting != TTF_GetFontHinting(font)) TTF_SetFontHinting(font,hinting); if(outline != TTF_GetFontOutline(font)) TTF_SetFontOutline(font,outline); for(i=0; i<128; i++) { /* cache rendered surface */ #if RENDER_MODE==0 text[i]=TTF_RenderGlyph_Solid(font,i+start_glyph,fg); #elif RENDER_MODE==1 text[i]=TTF_RenderGlyph_Shaded(font,i+start_glyph,fg,bg); #elif RENDER_MODE==2 text[i]=TTF_RenderGlyph_Blended(font,i+start_glyph,fg); #endif if(!text[i]) { printf("TTF_RenderGlyph_Shaded: %s\n", TTF_GetError()); exit(4); } /* cache metrics */ TTF_GlyphMetrics(font, i+start_glyph, &gm[i].minx, &gm[i].maxx, &gm[i].miny, &gm[i].maxy, &gm[i].advance); } sprintf(title,"%s-%s:%d+0x%04x",TTF_FontFaceFamilyName(font), TTF_FontFaceStyleName(font),font_size,start_glyph); SDL_WM_SetCaption(title,"latin1"); }
texture *font_impl::get_glyph(const wchar_t ch) const { shared_pointer<texture> tex = glyph_textures[ch]; if (!tex.ptr()) { int minx, maxx, miny, maxy, advance; if (TTF_GlyphMetrics(static_cast<TTF_Font *>(ttf_font_ptr), ch, &minx, &maxx, &miny, &maxy, &advance) == -1) throw runtime_exception(L"Error getting glyph metrics: %hs", TTF_GetError()); SDL_Color c; c.r = static_cast<Uint8>(fg[color::COMPONENT_RED] * 255.0f); c.g = static_cast<Uint8>(fg[color::COMPONENT_GREEN] * 255.0f); c.b = static_cast<Uint8>(fg[color::COMPONENT_BLUE] * 255.0f); SDL_Surface *surf1 = TTF_RenderGlyph_Blended(static_cast<TTF_Font *>(ttf_font_ptr), ch, c); SDL_Surface *surf2 = SDL_CreateRGBSurface(SDL_SWSURFACE, nearest_power_2(minx + surf1->w), texture_height, 32, surf1->format->Rmask, surf1->format->Gmask, surf1->format->Bmask, surf1->format->Amask); if (!surf2) throw runtime_exception(L"Unable to create SDL surface: %hs", SDL_GetError()); SDL_Rect dest; dest.x = minx > 0 ? minx : 0; dest.y = (surf2->h - font_height) + (font_ascent - maxy); dest.w = surf1->w; dest.h = surf1->h; clear_pixel_alpha(surf2); src_alpha_blit(surf1, surf2, dest.x, dest.y); string glyph_id = string::format(L"%ls %d: %lc", face.w_string(), size, ch); tex = new texture(FONT_TEXTURE_CATEGORY, surf2, texture::TEXTURE_ENV_REPLACE | texture::TEXTURE_WRAP_CLAMP | texture::TEXTURE_FILTER_LINEAR, texture::TEXTURE_COLORMAP, glyph_id.w_string()); glyph_textures[ch] = tex; glyph_pct_x[ch] = static_cast<float>(advance) / static_cast<float>(surf2->w); glyph_pct_y[ch] = static_cast<float>(font_height) / static_cast<float>(surf2->h); glyph_widths[ch] = static_cast<float>(advance); SDL_FreeSurface(surf2); SDL_FreeSurface(surf1); } return tex.ptr(); } // font_impl::get_glyph()
static mrb_value mrb_sdl2_ttf_font_render_glyph_blended(mrb_state *mrb, mrb_value self) { mrb_value text; mrb_int r, g, b, a; SDL_Surface * c; SDL_Color color; mrb_get_args(mrb, "Siiii", &text, &r, &g, &b, &a); color.r = r; color.g = g; color.b = b; color.a = a; c = TTF_RenderGlyph_Blended(mrb_sdl2_font_get_ptr(mrb, self), RSTRING_PTR(text)[0], color); if (c == NULL) { mruby_sdl2_raise_error(mrb); return mrb_false_value(); } return mrb_sdl2_video_surface(mrb, c, 0); }
JNIEXPORT jlong JNICALL Java_sdljava_x_swig_SWIG_1SDLTTFJNI_TTF_1RenderGlyph_1Blended(JNIEnv *jenv, jclass jcls, jlong jarg1, jint jarg2, jlong jarg3) { jlong jresult = 0 ; TTF_Font *arg1 = (TTF_Font *) 0 ; Uint16 arg2 ; SDL_Color arg3 ; SDL_Surface *result; SDL_Color *argp3 ; (void)jenv; (void)jcls; arg1 = *(TTF_Font **)&jarg1; arg2 = (Uint16)jarg2; argp3 = *(SDL_Color **)&jarg3; if (!argp3) { SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, "Attempt to dereference null SDL_Color"); return 0; } arg3 = *argp3; result = (SDL_Surface *)TTF_RenderGlyph_Blended(arg1,arg2,arg3); *(SDL_Surface **)&jresult = result; return jresult; }
/* * create_font */ int create_font(char *filename, FONT **font, int size, int style, SDL_Color color) { TTF_Font* ttffont; SDL_Surface* character_surface; int ret_code = BUILD_FONT_OK; int rc; Uint16 ii; /* * Allocate the memory required for the font object. */ (*font) = (FONT *) DT_MALLOC(sizeof(FONT)); /* * Set the font attributes. */ memcpy(&((*font)->color), &color, sizeof(SDL_Color)); (*font)->pointsize = size; (*font)->style = style; /* * The font loading may fail if the file does not exist or permissions are * incorrect etc. */ ttffont = TTF_OpenFont(filename, (*font)->pointsize); if (ttffont == NULL) { DT_DEBUG_LOG("Error opening font (%s): %s\n", filename, TTF_GetError()); ret_code = BUILD_FONT_LOAD_FAIL; goto EXIT_LABEL; } /* * Set the style of the font to whatever we passed in. */ TTF_SetFontStyle(ttffont, (*font)->style); /* * Retrieve the font information to store in our font structure. */ (*font)->ascent = TTF_FontAscent(ttffont); (*font)->descent = TTF_FontDescent(ttffont); (*font)->height = TTF_FontHeight(ttffont); (*font)->lineskip = TTF_FontLineSkip(ttffont); /* * For each possible glyph, attempt to load the font character and create a * texture for it. If any of these fail we attempt to close down gracefully * and exit the function. */ for (ii = ' '; ii <= '~'; ii++) { character_surface = TTF_RenderGlyph_Blended(ttffont, ii, (*font)->color); if (NULL == character_surface) { DT_DEBUG_LOG("Error rendering glyph %c whilst creating font (%s): %s\n", (char) ii, filename, TTF_GetError()); ret_code = BUILD_FONT_RENDER_FAIL; goto EXIT_LABEL; } /* * Retrieve the metric info from the font object and store it in our local * structure. */ TTF_GlyphMetrics(ttffont, ii, &((*font)->glyphs[ii].minx), &((*font)->glyphs[ii].maxx), &((*font)->glyphs[ii].miny), &((*font)->glyphs[ii].maxy), &((*font)->glyphs[ii].advance)); /* * This function can fail if opengl cannot allocate any more memory for * textures. */ rc = SDL_GL_LoadTexture(character_surface, &((*font)->glyphs[ii].texid), false); if (LOAD_TEXTURE_OK != rc) { DT_DEBUG_LOG("Failed creating texture for glyph %c from font %s\n", (char) ii, TTF_GetError()); SDL_FreeSurface(character_surface); ret_code = BUILD_FONT_CREATE_TEXTURE_FAIL; goto EXIT_LABEL; } /* * Set the texture coordinates for the glyph (note this is the same for all * glyphs). */ (*font)->glyphs[ii].texcoord[0] = 0.0f; (*font)->glyphs[ii].texcoord[1] = 0.0f; (*font)->glyphs[ii].texcoord[2] = 1.0f; (*font)->glyphs[ii].texcoord[3] = 1.0f; /* * The surface used for that character is no longer required. */ SDL_FreeSurface(character_surface); } EXIT_LABEL: if (NULL != ttffont) { TTF_CloseFont(ttffont); } return ret_code; }
SDL_Texture* generateVerticalText(const char* text, SDL_Color color) { std::vector <SDL_Texture*> glyphs; int w=0,h=0; for(int i=0;text[i]!='\0';++i) { SDL_Surface* text_surface=TTF_RenderGlyph_Blended(monoFont, text[i], color); SDL_Texture *tex = SDL_CreateTextureFromSurface(render, text_surface); w=std::max(w, text_surface->w); int miny, maxy; if(text[i]=='_') {miny=1; maxy=11;} else TTF_GlyphMetrics(font, text[i], NULL, NULL, &miny, &maxy, NULL); h+=maxy-miny+3; SDL_FreeSurface(text_surface); glyphs.push_back(tex); } SDL_Texture *tex=SDL_CreateTexture(render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h); SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); SDL_SetRenderTarget(render, tex); SDL_SetRenderDrawColor(render, 0, 0, 0, 0); SDL_RenderClear(render); int p=0; for(int i=0;i<glyphs.size();++i) { int texW, texH; SDL_QueryTexture(glyphs[i], NULL, NULL, &texW, &texH); int miny, maxy; if(text[i]=='_') {miny=1; maxy=11;} else TTF_GlyphMetrics(font, text[i], NULL, NULL, &miny, &maxy, NULL); SDL_Rect glyphRect; glyphRect.y=p; glyphRect.x=0;//(w-texW)/2; glyphRect.w=texW; glyphRect.h=texH; p+=maxy-miny+3; if(text[i]!='_') SDL_RenderCopy(render, glyphs[i], NULL, &glyphRect); SDL_DestroyTexture(glyphs[i]); } SDL_SetRenderTarget(render, NULL); return tex; }
void Font_FT::init(const String &filepath) { TTF_Font *font = TTF_OpenFont(filepath.c_str(), int(get_text_height() * m_vratio + 0.5f)); if(!font) throw Font_Init_Failure(); /*** Set Style ***/ if(is_bold() && is_italic()) TTF_SetFontStyle(font, TTF_STYLE_BOLD | TTF_STYLE_ITALIC); else if(is_bold()) TTF_SetFontStyle(font, TTF_STYLE_BOLD); else if(is_italic()) TTF_SetFontStyle(font, TTF_STYLE_ITALIC); /*** Determine Width & Height ***/ float font_width = 0; float font_height = 0; SDL_Color color2 = {0xFF, 0xFF, 0xFF, 0xFF}; SDL_Surface *source[256] = {0}; for(unsigned char c = 1; c; ++c) { Core::assert_no_error(); //char t[2] = {c, '\0'}; //source[c] = TTF_RenderText_Blended(font, t, color2); source[c] = TTF_RenderGlyph_Blended(font, c, color2); Core::print_error(); font_width = std::max(font_width, float(source[c] ? source[c]->w : 0)); font_height = std::max(font_height, float(source[c] ? source[c]->h : 0)); } /*** Initialize Intermediate SDL Surface ***/ const int next_w = int(pow(2.0f, ceil(log(float(16 * font_width))/log(2.0f)))), next_h = int(pow(2.0f, ceil(log(float(16 * font_height))/log(2.0f)))); SDL_Surface *font_surface = SDL_CreateRGBSurface(SDL_SWSURFACE, next_w, next_h, 32, source[END_OF_TIME]->format->Rmask, source[END_OF_TIME]->format->Gmask, source[END_OF_TIME]->format->Bmask, source[END_OF_TIME]->format->Amask); if(!font_surface) { for(unsigned char c = 1; c; ++c) SDL_FreeSurface(source[c]); TTF_CloseFont(font); if(next_h > 1024) { m_vratio /= 2.0f; init(filepath); return; } else throw Font_Init_Failure(); } SDL_LockSurface(font_surface); SDL_FillRect(font_surface, 0, SDL_MapRGBA(font_surface->format, 0, 0, 0, SDL_ALPHA_TRANSPARENT)); SDL_UnlockSurface(font_surface); /*** Initialize Glyphs ***/ SDL_Rect dstrect = {0, 0, Uint16(font_width), Uint16(font_height)}; m_glyph[0] = 0; for(unsigned char c = 1; c; ++c) { dstrect.x = Sint16((c % 16) * font_width); dstrect.y = Sint16((c / 16) * font_height); m_glyph[c] = new Glyph(font, c, source[c], font_surface, dstrect, next_w, next_h, m_vratio); } /*** Correct Transparency ***/ const Uint32 transparent_white = font_surface->format->Rmask | font_surface->format->Gmask | font_surface->format->Bmask; for(int i = 0; i < font_surface->h; ++i) for(Uint32 * src = reinterpret_cast<Uint32 *>(font_surface->pixels) + i * font_surface->pitch / 4, * src_end = src + font_surface->w; src != src_end; ++src) if(*src & font_surface->format->Rmask) *src = transparent_white | ((*src & font_surface->format->Rmask) >> font_surface->format->Rshift << font_surface->format->Ashift); /*** Initialize Final Texture ***/ m_texture = get_Video().create_Texture(font_surface, false); TTF_CloseFont(font); }
void Font::ConvertToBitmapFont(Display* display, int numchar) { int textureW = size * sqrt(numchar); int textureH = size * sqrt(numchar); bitmapFont = SDL_CreateTexture(display->GetRenderer(), SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, textureW, textureH); if (bitmapFont == nullptr) { LOG_ERROR("Couldn't create texture for bitmap font creating! Error: " << SDL_GetError()); } if(SDL_SetTextureBlendMode(bitmapFont, SDL_BLENDMODE_BLEND) == -1) LOG_ERROR("Couldn't set blendmode for texture when creating bitmap font!"); if(SDL_SetRenderTarget(display->GetRenderer(), bitmapFont) == -1) LOG_ERROR("Couldn't set render target to texture when creating bitmap font!"); display->PushRenderColor(); display->SetRenderColor(255, 255, 255, 0); if(SDL_RenderClear(display->GetRenderer()) == -1) LOG_ERROR("Couldn't clear texture when creating bitmap font!"); display->PopRenderColor(); int x = 0; int y = 0; for (int i = 0; i < numchar; i++) { //If break line charater skip if (i == 10) { glyphPositions.push_back(Rect(0, 0, 0, 0)); continue; } SDL_Surface* surface = nullptr; surface = TTF_RenderGlyph_Blended(font, i, COLOR_WHITE.ToSDLColor()); if (surface == nullptr) { //Don't show error on the NULL character if (i != 0) { LOG_ERROR("Couldn't create glyph surface! Using letter " << (char)i); } glyphPositions.push_back(Rect(0, 0, 0, 0)); continue; } SDL_Texture* texture = nullptr; texture = SDL_CreateTextureFromSurface(display->GetRenderer(), surface); SDL_FreeSurface(surface); if (texture == nullptr) { LOG_ERROR("Couldn't create glyph texture! Using letter " << (char)i); glyphPositions.push_back(Rect(0, 0, 0, 0)); continue; } Rect pos; if (SDL_QueryTexture(texture, NULL, NULL, &pos.w, &pos.h) == -1) { LOG_ERROR("Couldn't query texture when creating bitmap font!"); break; } if (x + pos.GetW() > textureW) { x = 0; y++; } if (y * pos.h > textureH) { LOG_ERROR("Can't render anymore letters to the texture it's full! Font size: " << size); SDL_DestroyTexture(texture); break; } pos.x = x; pos.y = y * pos.h; height = pos.GetH(); glyphPositions.push_back(pos); x += pos.GetW(); //Don't render tab character if (i != 9) { if (SDL_RenderCopy(display->GetRenderer(), texture, NULL, pos.ToSDLRect()) == -1) LOG_ERROR("Couldn't draw on texture when creating bitmap font!"); } SDL_DestroyTexture(texture); } if(SDL_SetRenderTarget(display->GetRenderer(), NULL) == -1) LOG_ERROR("Couldn't set render target back to renderer when creating bitmap font!"); return; }
Font::Font(const string& fileName) { guiCam = new Camera(true); vertices = new vector<Vertex>(); indices = new vector<int>(); TTF_Font *font = TTF_OpenFont(fileName.c_str(), 64); if (!font) printf("Unable to load font %s-%s\n", fileName.c_str(), TTF_GetError()); //first of all, need to render out the text atlas to use for vertex rendering SDL_Surface *atlasSurf = SDL_CreateRGBSurface(0, width, height, 32, 0xFF000000, 0xFF0000, 0xFF00, 0xFF); //to track where to put next glyphs int x = 0, y = 0; //marks where the newline starts int newY = 0; //32 being space, 126 being ~, all visible characters for (uint glyph = 32; glyph < 127; glyph++) { SDL_Surface *glyphSurf = TTF_RenderGlyph_Blended(font, glyph, {255, 255, 255}); int w = glyphSurf->w, h = glyphSurf->h; if (x + w > width) //if we can't go right anymore { x = 0; //start from left y += newY; //on a new line newY = 0; //start looking for a new newline offset } SDL_Rect r = { x, y, w, h }; SDL_BlitSurface(glyphSurf, NULL, atlasSurf, &r); RECTF rect = { x, y, w, h - 4 }; //adding a 4pixel boundary from the bottom rects[glyph - 32] = rect; //saving the unnormalized UVs x += w; newY = glm::max(newY, h); SDL_FreeSurface(glyphSurf); } //now normalizing the UVs int count = 127 - 32; for (int i = 0; i < count; i++) { RECTF r = rects[i]; r.x /= (float)width; r.y /= (float)height; r.w /= (float)width; r.h /= (float)height; rects[i] = r; } //for debug SDL_SaveBMP(atlasSurf, "atlas.bmp"); GLuint atlasText = ConvertSDLSurfaceToTexture(atlasSurf); SDL_FreeSurface(atlasSurf); TTF_CloseFont(font); //saving the previous state GLint currActive; glGetIntegerv(GL_ACTIVE_TEXTURE, &currActive); //pushing our texture and setting standard settings glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, atlasText); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); //restoring it glActiveTexture(currActive); m = new Model(); m->SetUpAttrib(0, 3, GL_FLOAT, 0); m->SetUpAttrib(1, 2, GL_FLOAT, sizeof(vec3) + sizeof(vec4)); shader = new ShaderProgram(SHADER_PATH + "guiVS.glsl", SHADER_PATH + "guiFS.glsl"); shader->BindAttribLoc(0, "vertexPosition"); shader->BindAttribLoc(1, "uvs"); shader->Link(); t = new Texture(atlasText); renderer = new Renderer(); renderer->SetModel(m, GL_TRIANGLES); renderer->SetShaderProgram(shader); renderer->AddTexture(t); }
bool TextSupervisor::_RenderText(vt_utils::ustring &string, TextStyle &style, ImageMemory &buffer) { FontProperties *fp = _font_map[style.font]; TTF_Font *font = fp->ttf_font; if(font == NULL) { IF_PRINT_WARNING(VIDEO_DEBUG) << "font of TextStyle argument '" << style.font << "' was invalid" << std::endl; return false; } static const SDL_Color white_color = { 0xFF, 0xFF, 0xFF, 0xFF }; SDL_Surface *intermediary = NULL; // Width and height of each line of text int32 line_w, line_h; // Minimum Y value of the line int32 min_y = 0; // Calculated line width int32 calc_line_width = 0; // Pixels left of '0' the first character extends, if any int32 line_start_x = 0; if(TTF_SizeUNICODE(font, string.c_str(), &line_w, &line_h) == -1) { IF_PRINT_WARNING(VIDEO_DEBUG) << "call to TTF_SizeUNICODE() failed" << std::endl; return false; } _CacheGlyphs(string.c_str(), fp); // Calculate the width of the width and minimum y value of the text const uint16 *char_ptr; for(char_ptr = string.c_str(); *char_ptr != '\0'; ++char_ptr) { FontGlyph *glyphinfo = (*fp->glyph_cache)[*char_ptr]; if(glyphinfo->top_y < min_y) min_y = glyphinfo->top_y; calc_line_width += glyphinfo->advance; } // Subtract one pixel from the minimum y value (TODO: explain why) min_y -= 1; // Check if the first character starts left of pixel 0, and set // char_ptr = string.c_str(); if(*char_ptr) { FontGlyph *first_glyphinfo = (*fp->glyph_cache)[*char_ptr]; if(first_glyphinfo->min_x < 0) line_start_x = first_glyphinfo->min_x; } // TTF_SizeUNICODE can underestimate line width as a result of its micro positioning. // Check if this condition is true and if so, set the line width appropriately. if(calc_line_width > line_w) line_w = calc_line_width; // Adjust line dimensions by negative starting offsets if present line_w -= line_start_x; line_h -= min_y; // Allocate enough memory for the entire text surface to reside on uint8 *intermed_buf = static_cast<uint8 *>(calloc(line_w * line_h, 4)); intermediary = SDL_CreateRGBSurfaceFrom(intermed_buf, line_w, line_h, 32, line_w * 4, RMASK, GMASK, BMASK, AMASK); if(intermediary == NULL) { IF_PRINT_WARNING(VIDEO_DEBUG) << "call to SDL_CreateRGBSurfaceFrom() failed" << std::endl; return false; } // Go through the string and render each glyph one by one SDL_Rect surf_target; int32 xpos = -line_start_x; int32 ypos = -min_y; for(char_ptr = string.c_str(); *char_ptr != '\0'; ++char_ptr) { FontGlyph *glyphinfo = (*fp->glyph_cache)[*char_ptr]; // Render the glyph SDL_Surface* initial = TTF_RenderGlyph_Blended(font, *char_ptr, white_color); if(initial == NULL) { IF_PRINT_WARNING(VIDEO_DEBUG) << "call to TTF_RenderGlyph_Blended() failed" << std::endl; return false; } surf_target.x = xpos + glyphinfo->min_x; surf_target.y = ypos + glyphinfo->top_y; // Add the glyph to the end of the rendered string if(SDL_BlitSurface(initial, NULL, intermediary, &surf_target) < 0) { SDL_FreeSurface(initial); SDL_FreeSurface(intermediary); free(intermed_buf); IF_PRINT_WARNING(VIDEO_DEBUG) << "call to SDL_BlitSurface() failed, SDL error: " << SDL_GetError() << std::endl; return false; } SDL_FreeSurface(initial); xpos += glyphinfo->advance; } SDL_LockSurface(intermediary); uint8 color_mult[] = { static_cast<uint8>(style.color[0] * 0xFF), static_cast<uint8>(style.color[1] * 0xFF), static_cast<uint8>(style.color[2] * 0xFF) }; uint32 num_bytes = intermediary->w * intermediary->h * 4; for(uint32 j = 0; j < num_bytes; j += 4) { ((uint8 *)intermediary->pixels)[j + 3] = ((uint8 *)intermediary->pixels)[j + 2]; ((uint8 *)intermediary->pixels)[j + 0] = color_mult[0]; ((uint8 *)intermediary->pixels)[j + 1] = color_mult[1]; ((uint8 *)intermediary->pixels)[j + 2] = color_mult[2]; } buffer.width = line_w; buffer.height = line_h; buffer.pixels = intermed_buf; SDL_UnlockSurface(intermediary); SDL_FreeSurface(intermediary); return true; } // bool TextSupervisor::_RenderText(vt_utils::ustring& string, TextStyle& style, ImageMemory& buffer)
void TextSupervisor::_CacheGlyphs(const uint16 *text, FontProperties *fp) { if(fp == NULL) { IF_PRINT_WARNING(VIDEO_DEBUG) << "FontProperties argument was null" << std::endl; return; } // Empty string means there are no glyphs to cache if(*text == 0) { return; } static const SDL_Color glyph_color = { 0xFF, 0xFF, 0xFF, 0xFF }; // Opaque white color static const uint16 fall_back_glyph = '?'; // If we can't cache a particular glyph, we fall back to this one TTF_Font *font = fp->ttf_font; SDL_Surface *initial = NULL; SDL_Surface *intermediary = NULL; int32 w, h; GLuint texture; // Go through each character in the string and cache those glyphs that have not already been cached for(const uint16 *character_ptr = text; *character_ptr != 0; ++character_ptr) { // A reference for legibility const uint16 &character = *character_ptr; // Update the glyph cache when needed if(character >= fp->glyph_cache->size()) fp->glyph_cache->resize(character + 1, 0); // Check if the glyph is already cached. If so, move on to the next character if(fp->glyph_cache->at(character) != 0) continue; // Attempt to create the initial SDL_Surface that contains the rendered glyph initial = TTF_RenderGlyph_Blended(font, character, glyph_color); if(initial == NULL) { IF_PRINT_WARNING(VIDEO_DEBUG) << "call to TTF_RenderGlyph_Blended() failed, resorting to fall back glyph: '?'" << std::endl; initial = TTF_RenderGlyph_Blended(font, fall_back_glyph, glyph_color); if(initial == NULL) { IF_PRINT_WARNING(VIDEO_DEBUG) << "call to TTF_RenderGlyph_Blended() failed for fall back glyph, aborting glyph caching" << std::endl; return; } } w = RoundUpPow2(initial->w + 1); h = RoundUpPow2(initial->h + 1); intermediary = SDL_CreateRGBSurface(0, w, h, 32, RMASK, GMASK, BMASK, AMASK); if(intermediary == NULL) { SDL_FreeSurface(initial); IF_PRINT_WARNING(VIDEO_DEBUG) << "call to SDL_CreateRGBSurface() failed" << std::endl; return; } if(SDL_BlitSurface(initial, 0, intermediary, 0) < 0) { SDL_FreeSurface(initial); SDL_FreeSurface(intermediary); IF_PRINT_WARNING(VIDEO_DEBUG) << "call to SDL_BlitSurface() failed" << std::endl; return; } glGenTextures(1, &texture); TextureManager->_BindTexture(texture); SDL_LockSurface(intermediary); uint32 num_bytes = w * h * 4; for(uint32 j = 0; j < num_bytes; j += 4) { (static_cast<uint8 *>(intermediary->pixels))[j + 3] = (static_cast<uint8 *>(intermediary->pixels))[j + 2]; (static_cast<uint8 *>(intermediary->pixels))[j + 0] = 0xff; (static_cast<uint8 *>(intermediary->pixels))[j + 1] = 0xff; (static_cast<uint8 *>(intermediary->pixels))[j + 2] = 0xff; } glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, intermediary->pixels); SDL_UnlockSurface(intermediary); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if(VideoManager->CheckGLError()) { SDL_FreeSurface(initial); SDL_FreeSurface(intermediary); IF_PRINT_WARNING(VIDEO_DEBUG) << "an OpenGL error was detected: " << VideoManager->CreateGLErrorString() << std::endl; return; } int minx, maxx; int miny, maxy; int advance; if(TTF_GlyphMetrics(font, character, &minx, &maxx, &miny, &maxy, &advance) != 0) { SDL_FreeSurface(initial); SDL_FreeSurface(intermediary); IF_PRINT_WARNING(VIDEO_DEBUG) << "call to TTF_GlyphMetrics() failed" << std::endl; return; } FontGlyph *glyph = new FontGlyph; glyph->texture = texture; glyph->min_x = minx; glyph->min_y = miny; glyph->top_y = fp->ascent - maxy; glyph->width = initial->w + 1; glyph->height = initial->h + 1; glyph->max_x = static_cast<float>(initial->w + 1) / static_cast<float>(w); glyph->max_y = static_cast<float>(initial->h + 1) / static_cast<float>(h); glyph->advance = advance; (*fp->glyph_cache)[character] = glyph; SDL_FreeSurface(initial); SDL_FreeSurface(intermediary); } } // void TextSupervisor::_CacheGlyphs(const uint16* text, FontProperties* fp)
SpriteFont::SpriteFont(const char* font, int size, char cs, char ce) { // Initialize SDL_ttf if (!TTF_WasInit()) { TTF_Init(); } TTF_Font* f = TTF_OpenFont(font, size); if (f == nullptr) { fprintf(stderr, "Failed to open TTF font %s\n", font); fflush(stderr); throw 281; } _fontHeight = TTF_FontHeight(f); _regStart = cs; _regLength = ce - cs + 1; int padding = size / 8; // First neasure all the regions glm::ivec4* glyphRects = new glm::ivec4[_regLength]; int i = 0, advance; for (char c = cs; c <= ce; c++) { TTF_GlyphMetrics(f, c, &glyphRects[i].x, &glyphRects[i].z, &glyphRects[i].y, &glyphRects[i].w, &advance); glyphRects[i].z -= glyphRects[i].x; glyphRects[i].x = 0; glyphRects[i].w -= glyphRects[i].y; glyphRects[i].y = 0; i++; } // Find best partitioning of glyphs int rows = 1, w, h, bestWidth = 0, bestHeight = 0, area = MAX_TEXTURE_RES * MAX_TEXTURE_RES, bestRows = 0; std::vector<int>* bestPartition = nullptr; while (rows <= _regLength) { h = rows * (padding + _fontHeight) + padding; auto gr = createRows(glyphRects, _regLength, rows, padding, w); // Desire a power of 2 texture w = closestPow2(w); h = closestPow2(h); // A texture must be feasible if (w > MAX_TEXTURE_RES || h > MAX_TEXTURE_RES) { rows++; delete[] gr; continue; } // Check for minimal area if (area >= w * h) { if (bestPartition) delete[] bestPartition; bestPartition = gr; bestWidth = w; bestHeight = h; bestRows = rows; area = bestWidth * bestHeight; rows++; } else { delete[] gr; break; } } // Can a bitmap font be made? if (!bestPartition) { fprintf(stderr, "Failed to Map TTF font %s to texture. Try lowering resolution.\n", font); fflush(stderr); throw 282; } // Create the texture glGenTextures(1, &_texID); glBindTexture(GL_TEXTURE_2D, _texID); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bestWidth, bestHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); // Now draw all the glyphs SDL_Color fg = { 255, 255, 255, 255 }; int ly = padding; for (int ri = 0; ri < bestRows; ri++) { int lx = padding; for (size_t ci = 0; ci < bestPartition[ri].size(); ci++) { int gi = bestPartition[ri][ci]; SDL_Surface* glyphSurface = TTF_RenderGlyph_Blended(f, (char)(cs + gi), fg); // Pre-multiplication occurs here unsigned char* sp = (unsigned char*)glyphSurface->pixels; int cp = glyphSurface->w * glyphSurface->h * 4; for (int i = 0; i < cp; i += 4) { float a = sp[i + 3] / 255.0f; sp[i] = (unsigned char)((float)sp[i] * a); sp[i + 1] = sp[i]; sp[i + 2] = sp[i]; } // Save glyph image and update coordinates glTexSubImage2D(GL_TEXTURE_2D, 0, lx, bestHeight - ly - 1 - glyphSurface->h, glyphSurface->w, glyphSurface->h, GL_BGRA, GL_UNSIGNED_BYTE, glyphSurface->pixels); glyphRects[gi].x = lx; glyphRects[gi].y = ly; glyphRects[gi].z = glyphSurface->w; glyphRects[gi].w = glyphSurface->h; SDL_FreeSurface(glyphSurface); glyphSurface = nullptr; lx += glyphRects[gi].z + padding; } ly += _fontHeight + padding; } // Draw the unsupported glyph int rs = padding - 1; int* pureWhiteSquare = new int[rs * rs]; memset(pureWhiteSquare, 0xffffffff, rs * rs * sizeof(int)); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rs, rs, GL_RGBA, GL_UNSIGNED_BYTE, pureWhiteSquare); delete[] pureWhiteSquare; pureWhiteSquare = nullptr; // Set some texture parameters 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_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Create spriteBatch glyphs _glyphs = new CharGlyph[_regLength + 1]; for (i = 0; i < _regLength; i++) { _glyphs[i].character = (char)(cs + i); _glyphs[i].size = glm::vec2(glyphRects[i].z, glyphRects[i].w); _glyphs[i].uvRect = glm::vec4( (float)glyphRects[i].x / (float)bestWidth, (float)glyphRects[i].y / (float)bestHeight, (float)glyphRects[i].z / (float)bestWidth, (float)glyphRects[i].w / (float)bestHeight ); } _glyphs[_regLength].character = ' '; _glyphs[_regLength].size = _glyphs[0].size; _glyphs[_regLength].uvRect = glm::vec4(0, 0, (float)rs / (float)bestWidth, (float)rs / (float)bestHeight); glBindTexture(GL_TEXTURE_2D, 0); delete[] glyphRects; delete[] bestPartition; TTF_CloseFont(f); }
int FTinit(font_data *font, const char * fontname, int ptsize) { int i; SDL_Color white = { 0xFF, 0xFF, 0xFF, 0x00 }; SDL_Color black = { 0x00, 0x00, 0x00, 0 }; SDL_Color *forecol; SDL_Color *backcol; GLenum gl_error; texcoord_t texcoords; int minx = 0,miny = 0,maxx = 0,maxy = 0; /* We might support changing theese later */ /* Look for special rendering types */ renderstyle = TTF_STYLE_NORMAL; rendertype = RENDER_LATIN1; /* Default is black and white */ forecol = &white; backcol = &black; /* Initialize the TTF library */ /*if ( TTF_Init() < 0 ) { fprintf(stderr, "Couldn't initialize TTF: %s\n",SDL_GetError()); return(2); }*/ font->ttffont = TTF_OpenFont(fontname, ptsize); if ( font->ttffont == NULL ) { fprintf(stderr, "Couldn't load %d pt font from %s: %s\n", ptsize, fontname, SDL_GetError()); return(2); } TTF_SetFontStyle(font->ttffont, renderstyle); font->list_base=glGenLists(next_p2(NUMCHARS)); /* Get the recommended spacing between lines of text for this font */ font->linespacing = TTF_FontLineSkip(font->ttffont); font->h = ptsize; for( i = 0; i < NUMCHARS; i++ ) { SDL_Surface *glyph = NULL; GLuint height = 0; /* kps - added default value */ forecol = &white; glyph = TTF_RenderGlyph_Blended( font->ttffont, i, *forecol ); if(glyph) { glGetError(); font->textures[i] = SDL_GL_LoadTexture(glyph, &texcoords); if ( (gl_error = glGetError()) != GL_NO_ERROR ) printf("Warning: Couldn't create texture: 0x%x\n", gl_error); font->W[i] = glyph->w; height = glyph->h; TTF_GlyphMetrics( font->ttffont, i, &minx,&maxx,&miny,&maxy,NULL); } SDL_FreeSurface(glyph); glNewList(font->list_base+i,GL_COMPILE); glBindTexture(GL_TEXTURE_2D, font->textures[i]); glTranslatef(1,0,0); glPushMatrix(); glBegin(GL_TRIANGLE_STRIP); glTexCoord2f(texcoords.MinX, texcoords.MaxY); glVertex2i(0 , miny); glTexCoord2f(texcoords.MaxX, texcoords.MaxY); glVertex2i(font->W[i] , miny); glTexCoord2f(texcoords.MinX, texcoords.MinY); glVertex2i(0 ,miny+height ); glTexCoord2f(texcoords.MaxX, texcoords.MinY); glVertex2i(font->W[i] , miny+height); glEnd(); glPopMatrix(); glTranslatef((font->W[i]>3)?font->W[i]:(font->W[i] = 3) + 1,0,0); /*one would think this should be += 2... I guess they overlap or the edge * isn't painted */ font->W[i] += 1; glEndList(); } /*TTF_CloseFont(font->ttffont);*/ /*TTF_Quit();*/ return 0; }
Font::Font(const std::string& path, int size) { TTF_Font* font = TTF_OpenFont(path.c_str(), size); if (!font) { fatalError("Failed to open font '" + path + "'."); } SDL_Color color {255, 255, 255, 255}; SDL_Surface* surfaces [NUM_GLYPHS]; for (int i = 0; i < NUM_GLYPHS; i++) { char c = FIRST_CHAR + i; TTF_GlyphMetrics( font, c, &glyph_metrics_map_[i].min_x, &glyph_metrics_map_[i].max_x, &glyph_metrics_map_[i].min_y, &glyph_metrics_map_[i].max_y, &glyph_metrics_map_[i].advance); surfaces[i] = TTF_RenderGlyph_Blended(font, c, color); } int font_height = TTF_FontHeight(font); int font_offset = font_height - TTF_FontAscent(font); int num_glyphs_per_row = 16; int num_rows = (NUM_GLYPHS - 1) / num_glyphs_per_row + 1; int max_row_width = 0; for (int i = 0; i < num_rows; i++) { int row_width = 0; for (int j = 0; j < num_glyphs_per_row; j++) { int index = i * num_glyphs_per_row + j; if (index >= NUM_GLYPHS) break; row_width += surfaces[index]->w; } if (row_width > max_row_width) max_row_width = row_width; } int atlas_height = num_rows * font_height; int atlas_width = max_row_width; int cursor_x = 0; int cursor_y = 0; int bytes_per_pixel = 4; std::vector<uint8_t> atlas_pixels (bytes_per_pixel * atlas_height * atlas_width); for (int i = 0; i < num_rows; i++) { for (int j = 0; j < num_glyphs_per_row; j++) { int index = i * num_glyphs_per_row + j; if (index >= NUM_GLYPHS) break; SDL_Surface* surface = surfaces[index]; for (int k = 0; k < font_height; k++) { for (int l = 0; l < surface->w; l++) { int atlas_index = bytes_per_pixel * ((cursor_y + k) * atlas_width + cursor_x + l); int surface_index = bytes_per_pixel * ((surface->h - k - 1) * surface->w + l); for (int m = 0; m < bytes_per_pixel; m++) { atlas_pixels[atlas_index + m] = reinterpret_cast<uint8_t*>(surface->pixels)[surface_index + m]; } } } GlyphMetrics& metrics = glyph_metrics_map_[index]; GlyphBound& bound = glyph_bound_map_[index]; bound.min_s = double(cursor_x + metrics.min_x) / atlas_width; bound.max_s = double(cursor_x + metrics.max_x) / atlas_width; bound.min_t = double(cursor_y + font_offset + metrics.min_y) / atlas_height; bound.max_t = double(cursor_y + font_offset + metrics.max_y) / atlas_height; cursor_x += surface->w; } cursor_x = 0; cursor_y += font_height; } for (int i = 0; i < NUM_GLYPHS; i++) { SDL_FreeSurface(surfaces[i]); } TTF_CloseFont(font); glGenTextures(1, &atlas_); glBindTexture(GL_TEXTURE_2D, atlas_); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, atlas_width, atlas_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, atlas_pixels.data()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); }
bool Fonte::criarTexturaTrueType(TTF_Font* ttf_font, int tamanho, Uint16 primeiro_glifo, Uint16 ultimo_glifo, EnumQualidadeEscala qualidade_escala) { ascent = TTF_FontAscent(ttf_font); int larg_glifo = tamanho; int alt_glifo = TTF_FontHeight(ttf_font); if (ultimo_glifo < primeiro_glifo) ultimo_glifo = primeiro_glifo; Uint16 num_glifos = ultimo_glifo - primeiro_glifo + 1; Uint16 glifos_por_linha = sqrt(num_glifos); if (glifos_por_linha*glifos_por_linha != num_glifos) glifos_por_linha++; Uint16 glifos_por_coluna = ceilf((float)num_glifos / (float)glifos_por_linha); int larg = glifos_por_linha*larg_glifo; int alt = glifos_por_coluna*alt_glifo; glifos.resize(num_glifos); Uint32 rmask, gmask, bmask, amask; #if SDL_BYTEORDER == SDL_BIG_ENDIAN rmask = 0xff000000; gmask = 0x00ff0000; bmask = 0x0000ff00; amask = 0x000000ff; #else rmask = 0x000000ff; gmask = 0x0000ff00; bmask = 0x00ff0000; amask = 0xff000000; #endif SDL_Surface* surface = SDL_CreateRGBSurface(0, larg, alt, 32, rmask, gmask, bmask, amask); if (!surface) { return false; } SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND); SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, 0, 0, 0, 0)); SDL_Color cor_glifo = { 255, 255, 255, 255 }; SDL_Rect rect; Uint16 i_glifo = 0; Uint16 lin = 0, col = 0; Glifo* g; for (Uint16 i = primeiro_glifo; i <= ultimo_glifo; ++i) { g = &glifos[i_glifo]; g->caractere = i; SDL_Surface* surface_glifo = TTF_RenderGlyph_Blended(ttf_font, g->caractere, cor_glifo); if (surface_glifo) { SDL_SetSurfaceBlendMode(surface_glifo, SDL_BLENDMODE_BLEND); rect.x = col*larg_glifo; rect.y = lin*alt_glifo; rect.w = surface_glifo->w; rect.h = surface_glifo->h; SDL_BlitSurface(surface_glifo, NULL, surface, &rect); SDL_FreeSurface(surface_glifo); int minx, miny, maxx, maxy, av; TTF_GlyphMetrics(ttf_font, g->caractere, &minx, &maxx, &miny, &maxy, &av); g->avanco = av; g->quad.x = (col*larg_glifo); g->quad.y = (lin*alt_glifo) + ascent - maxy; g->quad.larg = rect.w; g->quad.alt = maxy - miny; g->dx = minx; g->alturaAcimaDaBase = maxy; } else { g->avanco = tamanho; g->quad.x = 0; g->quad.y = 0; g->quad.larg = 0; g->quad.alt = 0; g->dx = 0; g->alturaAcimaDaBase = 0; } ++i_glifo; ++col; if (col == glifos_por_linha) { col = 0; ++lin; } } bool r = tex.criarDaSDL_Surface(surface, qualidade_escala); SDL_FreeSurface(surface); if (r) { numGlifosX = glifos_por_linha; numGlifosY = glifos_por_coluna; } return r; }