int __stdcall get_width(char *text, char *buffer, int height) { stbtt_fontinfo font; int i,j,ascent,baseline,ch,xpos=0; float scale=0; baseline=10; if (buffer==-1) {stbtt_InitFont(&font, pdf_font_DroidSans,stbtt_GetFontOffsetForIndex(pdf_font_DroidSans,0) );} else {stbtt_InitFont(&font, buffer,stbtt_GetFontOffsetForIndex(buffer,0) );kol_board_puts("Font loaded..\n");} kol_board_puts("Engine init..\n"); //kol_board_puti(&screen); scale = stbtt_ScaleForPixelHeight(&font, height*3/4); int advance,lsb; while (dos2utf(text[ch])) { // kol_board_puts("new symbol...\n"); stbtt_GetCodepointHMetrics(&font, dos2utf(text[ch]), &advance, &lsb); xpos += (advance * scale); if (text[ch+1]) xpos += scale*stbtt_GetCodepointKernAdvance(&font, dos2utf(text[ch]),dos2utf(text[ch+1])); ++ch; } stbtt_GetCodepointHMetrics(&font, dos2utf(text[ch]), &advance, &lsb); xpos += (advance * scale); return xpos; }
float Font::measureText(const std::string& text) { float length = 0.0f; float lineLength = 0.0f; if(isLoaded()) { for(unsigned int i = 0; i < text.length(); i++) { if(text[i] == '\n') { if(lineLength > length) { length = lineLength; } lineLength = 0.0f; continue; } int advance = 0; stbtt_GetCodepointHMetrics(&m_info, text[i], &advance, 0); lineLength += advance; } if(lineLength > length) length = lineLength; } return length; }
int cellFontGetCharGlyphMetrics(mem_ptr_t<CellFont> font, u32 code, mem_ptr_t<CellFontGlyphMetrics> metrics) { cellFont->Log("cellFontGetCharGlyphMetrics(font_addr=0x%x, code=0x%x, metrics_addr=0x%x", font.GetAddr(), code, metrics.GetAddr()); if (!font.IsGood() || metrics.IsGood()) return CELL_FONT_ERROR_INVALID_PARAMETER; int x0, y0, x1, y1; int advanceWidth, leftSideBearing; float scale = stbtt_ScaleForPixelHeight(&(font->stbfont), font->scale_y); stbtt_GetCodepointBox(&(font->stbfont), code, &x0, &y0, &x1, &y1); stbtt_GetCodepointHMetrics(&(font->stbfont), code, &advanceWidth, &leftSideBearing); // TODO: Add the rest of the information metrics->width = (x1-x0) * scale; metrics->height = (y1-y0) * scale; metrics->Horizontal.bearingX = (float)leftSideBearing * scale; metrics->Horizontal.bearingY = 0; metrics->Horizontal.advance = (float)advanceWidth * scale; metrics->Vertical.bearingX = 0; metrics->Vertical.bearingY = 0; metrics->Vertical.advance = 0; return CELL_FONT_OK; }
void UniFont::CacheGlyphDefault(const unsigned int* str, int index, int len, int cellCols, int cellRows, Texture* tex, bool* outIsDirty) { int codePoint = str[index]; int charSize = fontScale + 2; if (GetInfoForCodepoint(codePoint) == nullptr) { stbtt_fontinfo* font = nullptr; unsigned char* cBmp = nullptr; int cW = 0, cH = 0; for (int j = 0; j < fontInfos.count; j++) { if (fontInfos.data[j].low <= codePoint && codePoint <= fontInfos.data[j].high) { float pixelScale = stbtt_ScaleForPixelHeight(&fontInfos.data[j].info, fontScale); int glyphIdx = stbtt_FindGlyphIndex(&fontInfos.data[j].info, codePoint); cBmp = stbtt_GetCodepointBitmap(&fontInfos.data[j].info, 0, pixelScale, codePoint, &cW, &cH, 0, 0); if (cBmp) { font = &fontInfos.data[j].info; break; } } } if (font != nullptr) { *outIsDirty = true; int ascent, descent, lineGap; stbtt_GetFontVMetrics(font, &ascent, &descent, &lineGap); float pixelScale = stbtt_ScaleForPixelHeight(font, fontScale); int cellY = cacheCursor / cellCols; int cellX = cacheCursor % cellCols; int startX = cellX * charSize; int startY = cellY * charSize; GlyphBlit(tex->texMem, tex->width, tex->height, startX, startY, cBmp, cW, cH); free(cBmp); int advanceWidth = 0, leftSideBearing = 0; stbtt_GetCodepointHMetrics(font, codePoint, &advanceWidth, &leftSideBearing); int x0, y0, x1, y1; stbtt_GetCodepointBitmapBox(font, codePoint, pixelScale, pixelScale, &x0, &y0, &x1, &y1); CodepointInfo pointInfo = {}; pointInfo.codepoint = codePoint; pointInfo.x = startX; pointInfo.y = startY; pointInfo.w = cW; pointInfo.h = cH; pointInfo.xOffset = x0; pointInfo.yOffset = y0; pointInfo.xAdvance = pixelScale * advanceWidth; codepointListing.EnsureCapacity(cacheCursor + 1); codepointListing.count = BNS_MAX(codepointListing.count, cacheCursor + 1); codepointListing.data[cacheCursor] = pointInfo; cacheCursor = (cacheCursor + 1) % (cellCols * cellRows); } } }
void Font::measureText(const std::wstring str, int& width, int& height, int max_width) { if(!isLoaded()) { return NULL; } Image char_render; std::vector<unsigned char> char_raster; int bx, by, bw, bh; int ascent, descent, lineGap; int sx = 0, sy = 0; stbtt_GetFontVMetrics(&m_info, &ascent, &descent, &lineGap); ascent *= m_scale; descent *= m_scale; lineGap *= m_scale; width = 0; height = 0; for(int i = 0; i < str.length(); i++) { if(str[i] == L'\n') { sy += ascent - descent + lineGap; sx = 0; continue; } stbtt_GetCodepointBitmapBox(&m_info, str[i], m_scale, m_scale, &bx, &by, &bw, &bh); int cwidth = bw - bx; int cheight = bh - by; int oy = ascent + by; if(max_width > 0 && sx + cwidth > max_width) { sy += ascent - descent + lineGap; sx = 0; } int advance; stbtt_GetCodepointHMetrics(&m_info, str[i], &advance, 0); if(sy + oy + cheight > height) { height = sy + oy + cheight; } if(sx + cwidth > width) { width = sx + cwidth; } sx += advance * m_scale; int kerning = stbtt_GetCodepointKernAdvance(&m_info, str[i], str[i + 1]); sx += kerning * m_scale; } }
int __stdcall picture(char *text, char *buffer, char *screen1, int width, int height) { //unsigned char *screen; //screen=zmalloc(20*78); //kol_board_puts(screen); //kol_board_puts("It was text\n"); stbtt_fontinfo font; int i,j,ascent,baseline,descent,ch=0; float scale, xpos=0; //baseline=10; kol_board_puts("Font address:\n"); //kol_board_puti(buffer); if (buffer==-1) {stbtt_InitFont(&font, pdf_font_DroidSans,stbtt_GetFontOffsetForIndex(pdf_font_DroidSans,0) );kol_board_puts("default font\n");} else {stbtt_InitFont(&font, buffer,stbtt_GetFontOffsetForIndex(buffer,0) );kol_board_puts("Font loaded..\n");} //kol_board_puti(&screen); scale = stbtt_ScaleForPixelHeight(&font, height*3/4); //stbtt_GetFontVMetrics(&font, &ascent,0,0); stbtt_GetFontVMetrics(&font, &ascent,&descent,0); //lev //baseline = (int) (ascent*scale); baseline = (int) ((ascent-descent)*scale); //lev //kol_board_puts("Text render:\n"); while (dos2utf(text[ch])) { //kol_board_puts("new symbol...\n"); int advance,lsb,x0,y0,x1,y1; //float x_shift = xpos - (float) i_floor(xpos); // kol_board_puts("floor called!\n"); stbtt_GetCodepointHMetrics(&font, dos2utf(text[ch]), &advance, &lsb); stbtt_GetCodepointBitmapBoxSubpixel(&font, dos2utf(text[ch]), scale,scale,0,0, &x0,&y0,&x1,&y1); //10= y0, 20=y1-y0 or so stbtt_MakeCodepointBitmapSubpixel(&font, &screen1[(baseline + y0)*width+ (int)xpos + x0], x1-x0,y1-y0, width, scale,scale,0,0, dos2utf(text[ch])); // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong // because this API is really for baking character bitmaps into textures. if you want to do this, // you need to render the bitmap to a temp buffer, then\n\t"alpha blend" that into the working buffer xpos += (advance * scale); if (text[ch+1]) xpos += scale*stbtt_GetCodepointKernAdvance(&font, dos2utf(text[ch]),dos2utf(text[ch+1])); ++ch; } //zmemcpy(screen1,bitmap,20*20); //kol_board_puts("finished...\n"); return 0; }
fm::rect2i FontRenderer::getGlyphRect(const fm::Uint32 &letter,Glyph::Style /* style */) const { float scale = stbtt_ScaleForMappingEmToPixels((stbtt_fontinfo*)m_stbFontInfo, m_currentSize); int ix0,ix1,iy0,iy1; stbtt_GetCodepointBitmapBox((stbtt_fontinfo*)m_stbFontInfo, letter, scale, scale, &ix0, &iy0, &ix1, &iy1); int leftSideBearing, advanceWidth; stbtt_GetCodepointHMetrics((stbtt_fontinfo*)m_stbFontInfo, letter, &advanceWidth, &leftSideBearing); return fm::rect2i(fm::vec2i(leftSideBearing*scale,iy0),fm::vec2i(ix1-ix0,iy1-iy0)); }
JNIEXPORT void JNICALL Java_com_badlogic_gdx_graphics_g2d_stbtt_StbTrueType_getCodepointHMetrics(JNIEnv* env, jclass clazz, jlong info, jint codePoint, jintArray obj_metrics) { int* metrics = (int*)env->GetPrimitiveArrayCritical(obj_metrics, 0); //@line:67 int advanceWidth = 0; int leftSideBearing = 0; stbtt_GetCodepointHMetrics((stbtt_fontinfo*)info, codePoint, &advanceWidth, &leftSideBearing); metrics[0] = advanceWidth; metrics[1] = leftSideBearing; env->ReleasePrimitiveArrayCritical(obj_metrics, metrics, 0); }
long calculateCharAdvance(char32_t cp) override { //int advance = 0; //int bearing = 0; //stbtt_GetCodepointHMetrics(&font_handle_, cp, &advance, &bearing); //return static_cast<int>(advance * scale_ * 65536.0f); auto it = packed_char_.find(UnicodeRange(cp)); if(it == packed_char_.end()) { int advance = 0; int bearing = 0; stbtt_GetCodepointHMetrics(&font_handle_, cp, &advance, &bearing); return static_cast<int>(advance * scale_ * 65536.0f); } stbtt_packedchar *b = it->second.data() + cp - it->first.first; return static_cast<int>(b->xadvance * 65536.0f); }
s32 cellFontGetCharGlyphMetrics(vm::ptr<CellFont> font, u32 code, vm::ptr<CellFontGlyphMetrics> metrics) { cellFont.Log("cellFontGetCharGlyphMetrics(font=*0x%x, code=0x%x, metrics=*0x%x)", font, code, metrics); s32 x0, y0, x1, y1; s32 advanceWidth, leftSideBearing; float scale = stbtt_ScaleForPixelHeight(font->stbfont, font->scale_y); stbtt_GetCodepointBox(font->stbfont, code, &x0, &y0, &x1, &y1); stbtt_GetCodepointHMetrics(font->stbfont, code, &advanceWidth, &leftSideBearing); // TODO: Add the rest of the information metrics->width = (x1-x0) * scale; metrics->height = (y1-y0) * scale; metrics->Horizontal.bearingX = (float)leftSideBearing * scale; metrics->Horizontal.bearingY = 0.f; metrics->Horizontal.advance = (float)advanceWidth * scale; metrics->Vertical.bearingX = 0.f; metrics->Vertical.bearingY = 0.f; metrics->Vertical.advance = 0.f; return CELL_OK; }
bool TruetypeFont::renderGlyph(char32_t c) { bool result = false; GlyphPtr glyph = char2glyph[c]; if(!glyph) { result = true; glyph.reset(new Glyph); // glyph->render(face, size, c); int advance, lsb; stbtt_GetCodepointHMetrics(_fontinfo, c, &advance, &lsb); int ix0, iy0, ix1, iy1; stbtt_GetCodepointBitmapBox(_fontinfo, c, _vscale, _vscale, &ix0, &iy0, &ix1, &iy1); int width = ix1-ix0; int height = iy1-iy0; unsigned char* bmpmem = (unsigned char*)malloc(width*height); stbtt_MakeCodepointBitmapSubpixel(_fontinfo, bmpmem, width, height, width, _vscale, _vscale, .0, .0, c); BitmapPtr result(new Bitmap(width, height, GL_RGBA, GL_ALPHA, bmpmem)); result->flip(); free(bmpmem); glyph->bitmap = result; glyph->advance = floorf(advance*_vscale); glyph->xoffset = floorf(lsb*_vscale); glyph->yoffset = -iy1; char2glyph[c] = glyph; glyphs.push_back(glyph); } return result; }
void DynamicFontAtSize::_update_char(CharType p_char) { if (char_map.has(p_char)) return; font->lock(); int w,h,xofs,yofs; unsigned char * cpbitmap = stbtt_GetCodepointBitmap(&font->info, scale, scale, p_char, &w, &h, &xofs, &yofs ); if (!cpbitmap) { //no glyph int advance; stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0); //print_line("char has no bitmap: "+itos(p_char)+" but advance is "+itos(advance*scale)); Character ch; ch.texture_idx=-1; ch.advance=advance*scale; ch.h_align=0; ch.v_align=0; char_map[p_char]=ch; font->unlock(); return; } int mw=w+rect_margin*2; int mh=h+rect_margin*2; if (mw>4096 || mh>4096) { stbtt_FreeBitmap(cpbitmap,NULL); font->unlock(); ERR_FAIL_COND(mw>4096); ERR_FAIL_COND(mh>4096); } //find a texture to fit this... int tex_index=-1; int tex_x=0; int tex_y=0; for(int i=0;i<textures.size();i++) { CharTexture &ct=textures[i]; if (mw > ct.texture_size || mh > ct.texture_size) //too big for this texture continue; tex_y=0x7FFFFFFF; tex_x=0; for(int j=0;j<ct.texture_size-mw;j++) { int max_y=0; for(int k=j;k<j+mw;k++) { int y = ct.offsets[k]; if (y>max_y) max_y=y; } if (max_y<tex_y) { tex_y=max_y; tex_x=j; } } if (tex_y==0x7FFFFFFF || tex_y+mh > ct.texture_size) continue; //fail, could not fit it here tex_index=i; break; } // print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" X: "+itos(tex_x)+" Y: "+itos(tex_y)); if (tex_index==-1) { //could not find texture to fit, create one int texsize = MAX(size*8,256); if (mw>texsize) texsize=mw; //special case, adapt to it? if (mh>texsize) texsize=mh; //special case, adapt to it? texsize=nearest_power_of_2(texsize); texsize=MIN(texsize,4096); CharTexture tex; tex.texture_size=texsize; tex.imgdata.resize(texsize*texsize*2); //grayscale alpha { //zero texture DVector<uint8_t>::Write w = tex.imgdata.write(); for(int i=0;i<texsize*texsize*2;i++) { w[i]=0; } } tex.offsets.resize(texsize); for(int i=0;i<texsize;i++) //zero offsets tex.offsets[i]=0; textures.push_back(tex); tex_index=textures.size()-1; } //fit character in char texture CharTexture &tex=textures[tex_index]; { DVector<uint8_t>::Write wr = tex.imgdata.write(); for(int i=0;i<h;i++) { for(int j=0;j<w;j++) { int ofs = ( (i+tex_y+rect_margin)*tex.texture_size+j+tex_x+rect_margin)*2; wr[ofs+0]=255; //grayscale as 1 wr[ofs+1]=cpbitmap[i*w+j]; //alpha as 0 } } } //blit to image and texture { Image img(tex.texture_size,tex.texture_size,0,Image::FORMAT_GRAYSCALE_ALPHA,tex.imgdata); if (tex.texture.is_null()) { tex.texture.instance(); tex.texture->create_from_image(img,Texture::FLAG_FILTER); } else { tex.texture->set_data(img); //update } } // update height array for(int k=tex_x;k<tex_x+mw;k++) { tex.offsets[k]=tex_y+mh; } int advance; stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0); Character chr; chr.h_align=xofs; chr.v_align=yofs + get_ascent(); chr.advance=advance*scale; chr.texture_idx=tex_index; chr.rect=Rect2(tex_x+rect_margin,tex_y+rect_margin,w,h); //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" RECT: "+chr.rect+" X OFS: "+itos(xofs)+" Y OFS: "+itos(yofs)); char_map[p_char]=chr; stbtt_FreeBitmap(cpbitmap,NULL); font->unlock(); }
void Font::drawStringUnicode(int x, int y, const std::wstring& str, Color color, bool top_screen, bool side) { if(!isLoaded()) { return; } Image char_render; std::vector<unsigned char> char_raster; int bx, by, bw, bh; int ascent, descent, lineGap; int sx = 0, sy = 0; stbtt_GetFontVMetrics(&m_info, &ascent, &descent, &lineGap); ascent *= m_scale; descent *= m_scale; for (unsigned int i = 0; i < str.length(); i++) { if(str[i + 1] == L'\n') { sy += lineGap * m_scale; sx = 0; continue; } stbtt_GetCodepointBitmapBox(&m_info, str[i], m_scale, m_scale, &bx, &by, &bw, &bh); int width = bw - bx; int height = bh - by; int oy = ascent + by; char_raster.resize(width * height); stbtt_MakeCodepointBitmap(&m_info, &char_raster[0], width, height, width, m_scale, m_scale, str[i]); char_render.create(width, height, Color(0, 0, 0, 0)); for (int ix = 0; ix < width; ix++) { for (int iy = 0; iy < height; iy++) { if (char_raster[ix + iy * width] != 0) char_render.putPixel(ix, iy, Color(color.r, color.g, color.b, char_raster[ix + iy * width])); } } char_render.draw(sx + x, sy + y + oy, top_screen, side); int advance; stbtt_GetCodepointHMetrics(&m_info, str[i], &advance, 0); sx += advance * m_scale; int kerning = stbtt_GetCodepointKernAdvance(&m_info, str[i], str[i + 1]); sx += kerning * m_scale; } }
void TruetypeFont::Render(const std::string &In, const Vec2 &Position, const Mat4 &Transform) { const char* Text = In.c_str(); int Line = 0; glm::vec3 vOffs(Position.x, Position.y + scale, 0); if (!IsValid) return; UpdateWindowScale(); SetBlendingMode(BLEND_ALPHA); SetShaderParameters(false, false, false, false, false, true); WindowFrame.SetUniform(U_COLOR, Red, Green, Blue, Alpha); SetPrimitiveQuadVBO(); try { utf8::iterator<const char*> it(Text, Text, Text + In.length()); utf8::iterator<const char*> itend(Text + In.length(), Text, Text + In.length()); for (; it != itend; ++it) { CheckCodepoint(*it); // Force a regeneration of this if necessary codepdata &cp = GetTexFromCodepoint(*it); unsigned char* tx = cp.tex; glm::vec3 trans = vOffs + glm::vec3(cp.xofs, cp.yofs, 0); glm::mat4 dx; if (*it == 10) // utf-32 line feed { Line++; vOffs.x = Position.x; vOffs.y = Position.y + scale * (Line + 1); continue; } dx = Transform * glm::translate(Mat4(), trans) * glm::scale(Mat4(), glm::vec3(cp.w, cp.h, 1)); // do the actual draw? if (cp.gltx == 0) { glGenTextures(1, &cp.gltx); glBindTexture(GL_TEXTURE_2D, cp.gltx); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, cp.tw, cp.th, 0, GL_ALPHA, GL_UNSIGNED_BYTE, tx); } else glBindTexture(GL_TEXTURE_2D, cp.gltx); WindowFrame.SetUniform(U_MVP, &(dx[0][0])); DoQuadDraw(); utf8::iterator<const char*> next = it; next++; if (next != itend) { float aW = stbtt_GetCodepointKernAdvance(info.get(), *it, *next); int bW; stbtt_GetCodepointHMetrics(info.get(), *it, &bW, NULL); vOffs.x += aW * virtualscale + bW * virtualscale; } } } #ifndef NDEBUG catch (utf8::exception &ex) { Utility::DebugBreak(); Log::Logf("Invalid UTF-8 string %s was passed. Error type: %s\n", ex.what()); } #else catch (...) { // nothing } #endif FinalizeDraw(); Image::ForceRebind(); }
void Text::setText(const char* text) { int length = strlen(text); if(length > 255) length = 255; memset(pTextBuffer, 0x00, 256); memcpy(pTextBuffer, text, length); const char* p; int width=0,height=0; /* calculate font scaling */ LOCK_ACQUIRE(gFtLock); //float scale = stbtt_ScaleForPixelHeight(gFontInfo, pPixelSize); float scale = stbtt_ScaleForMappingEmToPixels(gFontInfo, pPixelSize); int ascent, descent, lineGap; stbtt_GetFontVMetrics(gFontInfo, &ascent, &descent, &lineGap); ascent *= scale; descent *= scale; height = ascent; // calculate bitmap size for (p = pTextBuffer; *p; p++) { /* how wide is this character */ int ax; stbtt_GetCodepointHMetrics(gFontInfo, p[0], &ax, 0); width += ax * scale; /* add kerning */ int kern; kern = stbtt_GetCodepointKernAdvance(gFontInfo, p[0], p[1]); width += kern * scale; } //check if old bitmap exists, and delete it uint8_t* oldBitmap = pBitmap; pBitmap = (uint8_t*)malloc(width*height); if(oldBitmap) { free(oldBitmap); } memset(pBitmap,0,width*height); pBitmapWidth = width; pBitmapHeight = height; setSizeN( 2.f*((float)pBitmapWidth)/GetCore()->screen_width, 2.f*((float)pBitmapHeight)/GetCore()->screen_width ); int x=0,y=0; // render text to buffer for (p = pTextBuffer; *p; p++) { /* get bounding box for character (may be offset to account for chars that dip above or below the line */ int c_x1, c_y1, c_x2, c_y2; stbtt_GetCodepointBitmapBox(gFontInfo, p[0], scale, scale, &c_x1, &c_y1, &c_x2, &c_y2); /* compute y (different characters have different heights */ y = ascent + c_y1; /* render character (stride and offset is important here) */ int byteOffset = x + (y * width); stbtt_MakeCodepointBitmap(gFontInfo, pBitmap + byteOffset, c_x2 - c_x1, c_y2 - c_y1, width, scale, scale, p[0]); /* how wide is this character */ int ax; stbtt_GetCodepointHMetrics(gFontInfo, p[0], &ax, 0); x += ax * scale; /* add kerning */ int kern; kern = stbtt_GetCodepointKernAdvance(gFontInfo, p[0], p[1]); x += kern * scale; } LOCK_RELEASE(gFtLock); updateBitmap = true; }
Bytes& StbFontRenderer::RenderText( const FontPtr& data, const FontProperties& properties, cstring text, Bytes& output, Size& imageSize) { using irect = rect<int>; auto stb_data = &data->info; auto scale = properties.scale; Bytes stringData = Bytes::CreateString(text); szptr bit_w = 0; int x = 0; /* We use this to find kerning character */ auto shadowChar = stringData.begin(); for(auto c : stringData) { irect bbox = {}; stbtt_GetCodepointBitmapBox( stb_data, c, scale, scale, &bbox.x, &bbox.y, &bbox.w, &bbox.h); int y = properties.ascent + bbox.y, ax = 0, kern = 0; stbtt_GetCodepointHMetrics(stb_data, c, &ax, nullptr); kern = stbtt_GetCodepointKernAdvance(stb_data, c, *shadowChar); x += scale * (ax + kern); imageSize.w = CMath::max<u32>(imageSize.w, x + (bbox.w - bbox.x)); imageSize.h = CMath::max<u32>(imageSize.h, y + (bbox.h - bbox.y)); } bit_w = C_FCAST<szptr>(imageSize.w); output = Bytes::Alloc(imageSize.area()); shadowChar = stringData.begin(); x = 0; for(auto c : stringData) { ++shadowChar; irect bbox = {}; stbtt_GetCodepointBitmapBox( stb_data, c, scale, scale, &bbox.x, &bbox.y, &bbox.w, &bbox.h); int y = properties.ascent + bbox.y, ax = 0, kern = 0; szptr offset = C_FCAST<szptr>(x + y * bit_w); stbtt_MakeCodepointBitmap( stb_data, &output[offset], (bbox.w - bbox.x), (bbox.h - bbox.y), bit_w, scale, scale, c); stbtt_GetCodepointHMetrics(stb_data, c, &ax, nullptr); kern = stbtt_GetCodepointKernAdvance(stb_data, c, *shadowChar); x += scale * (ax + kern); } return output; }