int pack_begin(int width, int height, int stride_in_bytes, int padding) { pack_pixels = new unsigned char[width * height]; spc = new stbtt_pack_context(); int r = stbtt_PackBegin(spc, pack_pixels, width, height, stride_in_bytes, padding, NULL); return r; }
static bool font_renderer_stb_create_atlas(stb_font_renderer_t *self, uint8_t *font_data, float font_size) { int i; stbtt_pack_context pc = {NULL}; stbtt_packedchar chardata[256]; self->atlas.width = self->atlas.height = 512; alloc_atlas: self->atlas.buffer = (uint8_t*)calloc(self->atlas.height, self->atlas.width); if (!self->atlas.buffer) return false; stbtt_PackBegin(&pc, self->atlas.buffer, self->atlas.width, self->atlas.height, self->atlas.width, 1, NULL); stbtt_PackFontRange(&pc, font_data, 0, font_size, 0, 256, chardata); stbtt_PackEnd(&pc); for (i = 0; i < 256; ++i) { struct font_glyph *g = &self->glyphs[i]; stbtt_packedchar *c = &chardata[i]; g->advance_x = c->xadvance; g->atlas_offset_x = c->x0; g->atlas_offset_y = c->y0; g->draw_offset_x = c->xoff; g->draw_offset_y = c->yoff; g->width = c->x1 - c->x0; g->height = c->y1 - c->y0; /* make sure important characters fit */ if (isprint(i) && !isspace(i) && (!g->width || !g->height)) { /* increase atlas by 20% in all directions */ self->atlas.width *= 1.2; self->atlas.height *= 1.2; free(self->atlas.buffer); goto alloc_atlas; break; } } return true; }
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); }
FontAtlas::FontAtlas(int bitmapSize, FontRangeMap &map, unsigned char *fontData) : bitmapSize(bitmapSize), fontRangeMap(std::move(map)) { ASSERT(bitmapSize > 0, "Invalid bitmap size"); UPtr<unsigned char[]> bitmap(new unsigned char[bitmapSize * bitmapSize]); stbtt_pack_context packContext; int res = stbtt_PackBegin(&packContext, bitmap.get(), bitmapSize, bitmapSize, 0, 1, nullptr); ASSERT(res, "stbtt_PackBegin failed"); std::vector<stbtt_pack_range> packRanges; for (std::pair<const FontType, FontRange> &item : fontRangeMap) { FontRange &fontRange = item.second; stbtt_pack_range packRange; fontRange.charData.resize(fontRange.numGlyphs); packRange.font_size = fontRange.fontSize; packRange.first_unicode_char_in_range = fontRange.firstGlyph; packRange.num_chars_in_range = fontRange.numGlyphs; packRange.chardata_for_range = fontRange.charData.data(); packRanges.push_back(packRange); } res = stbtt_PackFontRanges(&packContext, fontData, 0, packRanges.data(), packRanges.size()); ASSERT(res, "stbtt_PackFontRanges failed"); stbtt_PackEnd(&packContext); texture = std::make_shared<Texture>(GL_TEXTURE_2D); texture->bind(); glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, bitmapSize, bitmapSize, 0, GL_RED, GL_UNSIGNED_BYTE, bitmap.get()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); texture->unbind(); }
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; }
bool Font::load(std::string path, AssetManager& manager) { mName = getFileNameFromPath(path); mPath = getPathFromFilePath(path); FILE* file = fopen(path.c_str(), "rb"); size_t fSize = 0; U8* buffer = nullptr; if (!file) return false; fseek(file, 0, SEEK_END); fSize = ftell(file); rewind(file); buffer = new U8[fSize]; const U32 TATLAS_SIZE = 512; size_t result = fread(buffer, sizeof(U8), fSize, file); if (result != fSize) return false; stbtt_fontinfo fontInfo; stbtt_InitFont(&fontInfo, buffer, stbtt_GetFontOffsetForIndex(buffer, 0)); U8* img = new U8[TATLAS_SIZE * TATLAS_SIZE]; U8* rgbaImg = new U8[TATLAS_SIZE * TATLAS_SIZE * 4]; stbtt_pack_context pack; stbtt_packedchar pcdata[96]; stbtt_packedchar tabData; int packSuccess = stbtt_PackBegin(&pack, img, TATLAS_SIZE, TATLAS_SIZE, 0, 1, NULL); int rangeSuc = stbtt_PackFontRange(&pack, buffer, 0, mSize, 32, 95, pcdata); int tabSuc = stbtt_PackFontRange(&pack, buffer, 0, mSize, '\t', 1, &tabData); stbtt_PackEnd(&pack); I32 largestY = 0; I32 tabSize = 0; mTabWidth = tabData.xadvance; for (int i = 0; i < 96; ++i) { mGlyphs[i].x0 = pcdata[i].x0; mGlyphs[i].y0 = pcdata[i].y0; mGlyphs[i].x1 = pcdata[i].x1; mGlyphs[i].y1 = pcdata[i].y1; mGlyphs[i].xoff = pcdata[i].xoff; mGlyphs[i].yoff = pcdata[i].yoff; mGlyphs[i].xoff2 = pcdata[i].xoff2; mGlyphs[i].yoff2 = pcdata[i].yoff2; mGlyphs[i].xadvance = pcdata[i].xadvance; vec2 tl = GET_UV(mGlyphs[i].x0, TATLAS_SIZE, mGlyphs[i].y0, TATLAS_SIZE); vec2 br = GET_UV(mGlyphs[i].x1, TATLAS_SIZE, mGlyphs[i].y1, TATLAS_SIZE); mGlyphs[i].u0 = tl.x(); mGlyphs[i].v0 = tl.y(); mGlyphs[i].u1 = br.x(); mGlyphs[i].v1 = br.y(); mGlyphs[i].w = mGlyphs[i].x1 - mGlyphs[i].x0; mGlyphs[i].h = abs(mGlyphs[i].y1 - mGlyphs[i].y0); if (mGlyphs[i].y1 - mGlyphs[i].y0 > largestY) largestY = (mGlyphs[i].y1 - mGlyphs[i].y0); } mMaxGlyphHeight = largestY; for (int i = 0; i < TATLAS_SIZE * TATLAS_SIZE; ++i) { rgbaImg[(i * 4) + 0] = 255; //r rgbaImg[(i * 4) + 1] = 255; //g rgbaImg[(i * 4) + 2] = 255; //b rgbaImg[(i * 4) + 3] = img[i]; //a } glGenTextures(1, &glTex); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, glTex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TATLAS_SIZE, TATLAS_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaImg); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); fclose(file); delete[] buffer; delete[] img; delete[] rgbaImg; return true; }
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; }
static bool font_renderer_stb_create_atlas(stb_font_renderer_t *self, uint8_t *font_data, float font_size, unsigned width, unsigned height) { int i; stbtt_pack_context pc = {NULL}; stbtt_packedchar chardata[256]; if (width > 2048 || height > 2048) { RARCH_WARN("[stb] Font atlas too big: %ux%u\n", width, height); goto error; } if (self->atlas.buffer) free(self->atlas.buffer); self->atlas.buffer = (uint8_t*)calloc(height, width); self->atlas.width = width; self->atlas.height = height; if (!self->atlas.buffer) goto error; stbtt_PackBegin(&pc, self->atlas.buffer, self->atlas.width, self->atlas.height, self->atlas.width, 1, NULL); stbtt_PackFontRange(&pc, font_data, 0, font_size, 0, 256, chardata); stbtt_PackEnd(&pc); for (i = 0; i < 256; ++i) { struct font_glyph *g = &self->glyphs[i]; stbtt_packedchar *c = &chardata[i]; g->advance_x = c->xadvance; g->atlas_offset_x = c->x0; g->atlas_offset_y = c->y0; g->draw_offset_x = c->xoff; g->draw_offset_y = c->yoff; g->width = c->x1 - c->x0; g->height = c->y1 - c->y0; /* Make sure important characters fit */ if (isalnum(i) && (!g->width || !g->height)) { int new_width = width * 1.2; int new_height = height * 1.2; /* Limit growth to 2048x2048 unless we already reached that */ if (width < 2048 || height < 2048) { new_width = MIN(new_width, 2048); new_height = MIN(new_height, 2048); } return font_renderer_stb_create_atlas(self, font_data, font_size, new_width, new_height); } } return true; error: self->atlas.width = self->atlas.height = 0; if (self->atlas.buffer) free(self->atlas.buffer); self->atlas.buffer = NULL; return false; }