void load_fonts(void) { stbtt_pack_context pc; int i; FILE *f; char filename[256]; char *win = getenv("windir"); if (win == NULL) win = getenv("SystemRoot"); f = fopen(stb_wingraph_commandline, "rb"); if (!f) { if (win == NULL) sprintf(filename, "arial.ttf", win); else sprintf(filename, "%s/fonts/arial.ttf", win); f = fopen(filename, "rb"); if (!f) exit(0); } fread(ttf_buffer, 1, 1<<25, f); stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL); for (i=0; i < 2; ++i) { stbtt_PackSetOversampling(&pc, 1, 1); stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+0]+32); stbtt_PackSetOversampling(&pc, 2, 2); stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+1]+32); stbtt_PackSetOversampling(&pc, 3, 1); stbtt_PackFontRange(&pc, ttf_buffer, 0, scale[i], 32, 95, chardata[i*3+2]+32); } stbtt_PackEnd(&pc); glGenTextures(1, &font_tex); glBindTexture(GL_TEXTURE_2D, font_tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, BITMAP_W, BITMAP_H, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); }
void addGlyphsToTexture(const std::vector<char32_t>& codepoints) override { if(codepoints.empty()) { LOG_WARN("stb_impl::addGlyphsToTexture: no codepoints."); return; } int old_size = packed_char_.size(); auto ttf_buffer = reinterpret_cast<const unsigned char*>(font_data_.c_str()); if(font_size_ < 20.0f) { stbtt_PackSetOversampling(&pc_, 2, 2); } std::vector<stbtt_pack_range> ranges; char32_t last_cp = codepoints.front(); char32_t first_cp = codepoints.front(); int num_chars = 1; for(auto it = codepoints.begin() + 1; it != codepoints.end(); ++it) { char32_t cp = *it; if(cp == last_cp+1) { ++num_chars; } else { auto& packed_data = packed_char_[UnicodeRange(first_cp, first_cp+num_chars-1)]; packed_data.resize(num_chars); stbtt_pack_range range; range.num_chars_in_range = num_chars; range.chardata_for_range = packed_data.data(); range.font_size = font_size_; range.first_unicode_char_in_range = first_cp; ranges.emplace_back(range); num_chars = 1; first_cp = cp; } last_cp = cp; } auto& packed_data = packed_char_[UnicodeRange(first_cp, first_cp+num_chars-1)]; packed_data.resize(num_chars); stbtt_pack_range range; range.num_chars_in_range = num_chars; range.chardata_for_range = packed_data.data(); range.font_size = font_size_; range.first_unicode_char_in_range = first_cp; ranges.emplace_back(range); stbtt_PackFontRanges(&pc_, ttf_buffer, 0, ranges.data(), ranges.size()); font_texture_->update2D(0, 0, 0, surface_width, surface_height, surface_width, pixels_.data()); }
int main(int argc, char **argv) { stbtt_fontinfo font; unsigned char *bitmap; int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 34807), s = (argc > 2 ? atoi(argv[2]) : 32); //debug(); // @TODO: why is minglui.ttc failing? fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/mingliu.ttc", "rb")); //fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/x/DroidSansMono.ttf", "rb")); { static stbtt_pack_context pc; static stbtt_packedchar cd[256]; static unsigned char atlas[1024*1024]; stbtt_PackBegin(&pc, atlas, 1024,1024,1024,1,NULL); stbtt_PackFontRange(&pc, ttf_buffer, 0, 32.0, 0, 256, cd); stbtt_PackEnd(&pc); } #if 0 stbtt_BakeFontBitmap(ttf_buffer,stbtt_GetFontOffsetForIndex(ttf_buffer,0), 40.0, temp_bitmap[0],BITMAP_W,BITMAP_H, 32,96, cdata); // no guarantee this fits! stbi_write_png("fonttest1.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0); { stbtt_pack_context pc; stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL); stbtt_PackFontRange(&pc, ttf_buffer, 0, 20.0, 32, 95, pdata); stbtt_PackFontRange(&pc, ttf_buffer, 0, 20.0, 0xa0, 0x100-0xa0, pdata); stbtt_PackEnd(&pc); stbi_write_png("fonttest2.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0); } { stbtt_pack_context pc; stbtt_pack_range pr[2]; stbtt_PackBegin(&pc, temp_bitmap[0], BITMAP_W, BITMAP_H, 0, 1, NULL); pr[0].chardata_for_range = pdata; pr[0].first_unicode_char_in_range = 32; pr[0].num_chars_in_range = 95; pr[0].font_size = 20.0f; pr[1].chardata_for_range = pdata+256; pr[1].first_unicode_char_in_range = 0xa0; pr[1].num_chars_in_range = 0x100 - 0xa0; pr[1].font_size = 20.0f; stbtt_PackSetOversampling(&pc, 2, 2); stbtt_PackFontRanges(&pc, ttf_buffer, 0, pr, 2); stbtt_PackEnd(&pc); stbi_write_png("fonttest3.png", BITMAP_W, BITMAP_H, 1, temp_bitmap, 0); } return 0; #endif stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, (float)s), c, &w, &h, 0,0); for (j=0; j < h; ++j) { for (i=0; i < w; ++i) putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); putchar('\n'); } return 0; }
FontRenderer::FontRenderer(const string& fontPath, uint32_t texWidth, uint32_t texHeight, float fontSize, size_t numCharsPerBatch, TextureFiltering filtering) noexcept : mFontSize{fontSize}, mPackedChars{new (std::nothrow) stbtt_packedchar[CHAR_COUNT]}, mSpriteBatch{numCharsPerBatch, FONT_RENDERER_FRAGMENT_SHADER_SRC} { // This should be const, but MSVC12 doesn't support ini lists in constructor's ini list mPixelToUV = vec2{1.0f/static_cast<float>(texWidth), 1.0f/static_cast<float>(texHeight)}; uint8_t* tempBitmap = new uint8_t[texWidth*texHeight]; stbtt_pack_context packContext; if(stbtt_PackBegin(&packContext, tempBitmap, texWidth, texHeight, 0, 1, NULL) == 0) { std::cerr << "FontRenderer: Couldn't stbtt_PackBegin()" << std::endl; std::terminate(); } stbtt_PackSetOversampling(&packContext, 2, 2); std::vector<uint8_t> ttfBuffer = sfz::readBinaryFile(fontPath.c_str()); if (ttfBuffer.size() == 0) { std::cerr << "Couldn't open TTF file at: " << fontPath << std::endl; std::terminate(); } if (stbtt_PackFontRange(&packContext, ttfBuffer.data(), 0, mFontSize, FIRST_CHAR, CHAR_COUNT, reinterpret_cast<stbtt_packedchar*>(mPackedChars)) == 0) { std::cerr << "FontRenderer: Couldn't pack font, texture likely too small." << std::endl; std::terminate(); } stbtt_PackEnd(&packContext); glGenTextures(1, &mFontTexture); glBindTexture(GL_TEXTURE_2D, mFontTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, texWidth, texHeight, 0, GL_RED, GL_UNSIGNED_BYTE, tempBitmap); // Sets specified texture filtering, generating mipmaps if needed. switch (filtering) { case TextureFiltering::NEAREST: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); break; case TextureFiltering::BILINEAR: glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); break; case TextureFiltering::TRILINEAR: glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); break; case TextureFiltering::ANISOTROPIC_1: case TextureFiltering::ANISOTROPIC_2: case TextureFiltering::ANISOTROPIC_4: case TextureFiltering::ANISOTROPIC_8: case TextureFiltering::ANISOTROPIC_16: glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropicFactor(filtering)); break; } delete[] tempBitmap; }