struct TEXT_FONT_GLYPH *textRenderGlyphToCache(TEXT_FONT *font, unsigned int glyph) { int glyph_index, x1, y1, x2, y2; struct TEXT_FONT_CACHE *next; next = font->cache; glyph_index = stbtt_FindGlyphIndex(&font->face, glyph); /* Just in case the coords aren't reported correctly o_O */ x1 = y1 = x2 = y2 = 0; // stbtt_GetCodepointBox(&font->face, glyph_index, &x1, &y1, &x2, &y2); stbtt_GetCodepointBitmapBox(&font->face, glyph, font->scale, font->scale, &x1, &y1, &x2, &y2); #if 0 x1 = font->scale * x1; y1 = font->scale * y1; x2 = font->scale * x2; y2 = font->scale * y2; #endif while (next != NULL) { if (textWillGlyphFit(next, x2 - x1, y2 - y1) == 0) break; next = next->next; } if (next == NULL) if ((next = textAppendCache(font, font->cache, font->tex_w, font->tex_h)) == NULL) return NULL; if (font->cache == NULL) font->cache = next; return textRenderGlyph(next, glyph, glyph_index, font); }
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; } }
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_getCodepointBitmapBox(JNIEnv* env, jclass clazz, jlong info, jint codePoint, jfloat scaleX, jfloat scaleY, jintArray obj_box) { int* box = (int*)env->GetPrimitiveArrayCritical(obj_box, 0); //@line:147 int x0, y0, x1, y1; x0 = y0 = x1 = y1 = 0; stbtt_GetCodepointBitmapBox((stbtt_fontinfo*)info, codePoint, scaleX, scaleY, &x0, &y0, &x1, &y1); box[0] = x0; box[1] = y0; box[2] = x1; box[3] = y1; env->ReleasePrimitiveArrayCritical(obj_box, box, 0); }
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 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; } }
fg::Image FontRenderer::renderGlyph(const fm::Uint32 &letter,Glyph::Style style,fm::vec2 *leftDown) const { if (!m_fileContent) return fg::Image(); if (letter == ' ') { float scl = stbtt_ScaleForMappingEmToPixels((stbtt_fontinfo*)m_stbFontInfo, m_currentSize); int ix0,ix1,iy0,iy1; stbtt_GetCodepointBitmapBox((stbtt_fontinfo*)m_stbFontInfo, 'w', scl, scl, &ix0, &iy0, &ix1, &iy1); return fg::Image(fm::vec2s(ix1-ix0,iy1-iy0),fg::Color(255,255,255,0)); } unsigned char *bitmap; fm::vec2i size,offset; bitmap = stbtt_GetCodepointBitmap((stbtt_fontinfo*)m_stbFontInfo, 0,stbtt_ScaleForMappingEmToPixels((stbtt_fontinfo*)m_stbFontInfo, m_currentSize), letter, &size.w, &size.h, &offset.x, &offset.y); if (leftDown) *leftDown = offset; fg::Image img; img.create(size); Cv(size) img.set(p,fg::Color(255,bitmap[p.x + p.y*size.w])); stbtt_FreeBitmap(bitmap,0); return img; (void)style; /* // if rendering upper/lower index decrease size unsigned int originalSize = m_currentSize; if ((style & Glyph::Subscript) xor (style & Glyph::Superscript )) setCharacterSize(m_currentSize*.7); // get glyph index FT_UInt glyphIndex = FT_Get_Char_Index(*((FT_Face*)m_ftFaceData),letter); // load glyph data int error = FT_Load_Glyph(*((FT_Face*)m_ftFaceData),glyphIndex,FT_LOAD_FORCE_AUTOHINT | FT_LOAD_DEFAULT); if (error) return fg::Image(); FT_Face face = (*((FT_Face*)m_ftFaceData)); // embolden and italicise if (style & fg::Glyph::Bold) FT_GlyphSlot_Embolden(face->glyph); if (style & fg::Glyph::Italic) FT_GlyphSlot_Oblique(face->glyph); // render glyph image error = FT_Render_Glyph( face->glyph,FT_RENDER_MODE_NORMAL); if (error) return fg::Image(); // get bitmap details fm::Size width = face->glyph->bitmap.width; fm::Size height = face->glyph->bitmap.rows; std::vector<fm::Uint8> bitmap(face->glyph->bitmap.buffer,face->glyph->bitmap.buffer+width*height); float offsetx = (face->glyph->metrics.horiBearingX>>6); float offsety = (face->glyph->metrics.horiBearingY>>6)-float(height); // manually create upper index if requested if (style & fg::Glyph::Superscript && !(style & fg::Glyph::Subscript)) { fm::Size deltaH = height*0.4f; bitmap.resize(width*(height+deltaH),0); offsety+=deltaH; height+=deltaH; } // manually create lower index if requested if (style & fg::Glyph::Subscript && !(style & fg::Glyph::Superscript)) { fm::Size deltaH = height*0.4f; bitmap.resize(width*(height+deltaH),0); offsety-=deltaH; Cxy(width,height) { fm::Uint8 tmp = bitmap[x+int(height+deltaH-y-1)*width]; bitmap[x+int(height+deltaH-y-1)*width] = bitmap[x+int(height-y-1)*width]; bitmap[x+int(height-y-1)*width] = tmp; } height+=height/2.f; } // reset the size back if needed if ((style & fg::Glyph::Subscript) xor (style & fg::Glyph::Superscript)) setCharacterSize(originalSize); // manually strike through and/or underline if requested for (int i=0;i<bool(style & fg::Glyph::Crossed)+bool(style & fg::Glyph::Underlined);i++) { bool crossed = ((style & fg::Glyph::Crossed) && i==0); int ymax = offsety+height; float &ymin = offsety; // if the line would be out of the image (take account the y offset like in '~' as the leeter might not "sit" on the baseline) int lineW = int(m_metrics.maxH/15.f); int lineP = int(m_metrics.maxH*(crossed ? 0.2 : 0.0) ); if (ymin > lineP) { bitmap.resize(width*(height+int(ymin-lineP)),0); ymin = lineP; height = ymax-ymin; } else if (ymax < lineW+lineP) { bitmap.resize(width*(height+(lineW+lineP-ymax)),0); ymax = lineW+lineP; height = ymax-ymin; } // simply set the value to one for every pixel in the line Cx(width) Cy((unsigned int)(lineW)) bitmap[x+(height-1-y+int(ymin)-lineP)*width] = 255; } // manually create outline if requested if (style & fg::Glyph::Outline) { std::vector<fm::Uint8> oldData = bitmap; bitmap.resize((width+2)*(height+2)); // get a bigger bitmap Cxy(width+2,height+2) { int db=0,sum=0; int ax=int(x)-1,ay=int(y)-1; int curVal = fm::rect2i(0,0,width-1,height-1).contains(fm::vec2i(ax,ay)) ? oldData[ax+ay*width] : 0; // this algorithm uses the difference between the neighbour pixels (the bigger the difference the bigger the output will be) for(int x1=-1;x1<=1;x1++) for(int y1=-1;y1<=1;y1++) { if (fm::vec2(x1,y1).LENGTH()>=1) { int deltaVal=(fm::rect2i(0,0,width-1,height-1).contains(fm::vec2i(ax+x1,ay+y1))) ? oldData[(ax+x1)+(ay+y1)*width]-curVal : -curVal; deltaVal*=(deltaVal > 0 ? .9 : 1.4); sum+=(deltaVal < 0 ? -deltaVal : deltaVal), db++; } } // do some scaling on the value and clamp it to [0;255] bitmap[x+y*(width+2)] = fm::math::min(255.0,db ? sum/db*1.6 : 0.0); } // update glyph image details width+=2,height+=2;offsetx--,offsety--; } Cxy(width,height) { float f = bitmap[y*width + x] / 255.0; bitmap[y*width + x] = fm::math::sqrt3(f) * 255.0; } if (leftDown) leftDown->x = offsetx, leftDown->y = offsety; // convert the bitmap to fg::Image fg::Image img; img.create(width,height); Cxy(width,height) img.setPixel(x,y,fg::Color(255,255,255,bitmap[y*width + x])); */ return img; }
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; }
void Font::drawStringToBuffer(int x, int y, const std::wstring& str, Color color, unsigned char* buffer, int buffer_width, int buffer_height, int bitsperpixel, int max_width) { std::vector<unsigned char> char_raster; int bx, by, bw, bh; int ascent, descent, lineGap; int sx = 0, sy = 0; int width, height; stbtt_GetFontVMetrics(&m_info, &ascent, &descent, &lineGap); ascent *= m_scale; descent *= m_scale; lineGap *= m_scale; this->measureText(str, width, height, max_width); for (unsigned 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 char_width = bw - bx; int char_height = bh - by; int oy = ascent + by; if(max_width > 0 && sx + char_width > max_width) { sy += ascent - descent + lineGap; sx = 0; } char_raster.resize(char_width * char_height); stbtt_MakeCodepointBitmap(&m_info, &char_raster[0], char_width, char_height, char_width, m_scale, m_scale, str[i]); for (int ix = 0; ix < char_width; ix++) { for (int iy = 0; iy < char_height; iy++) { int xpos = x + sx + ix; int ypos = buffer_height - (y + sy + oy + iy) -1; if (char_raster[ix + iy * char_width] != 0 && xpos < buffer_width && ypos < buffer_height) { unsigned int alpha = char_raster[ix + iy * char_width]; unsigned int inv_alpha = 255 - alpha; if (bitsperpixel == 24) { unsigned char bg_r = buffer[3 * (xpos + ypos * buffer_width) + 0]; unsigned char bg_g = buffer[3 * (xpos + ypos * buffer_width) + 1]; unsigned char bg_b = buffer[3 * (xpos + ypos * buffer_width) + 2]; unsigned char r = (unsigned char)((alpha * color.r + inv_alpha * bg_r) >> 8); unsigned char g = (unsigned char)((alpha * color.g + inv_alpha * bg_g) >> 8); unsigned char b = (unsigned char)((alpha * color.b + inv_alpha * bg_b) >> 8); buffer[3 * (xpos + ypos * buffer_width) + 0] = b; buffer[3 * (xpos + ypos * buffer_width) + 1] = g; buffer[3 * (xpos + ypos * buffer_width) + 2] = r; } else { unsigned char bg_r = buffer[4 * (xpos + ypos * buffer_width) + 0]; unsigned char bg_g = buffer[4 * (xpos + ypos * buffer_width) + 1]; unsigned char bg_b = buffer[4 * (xpos + ypos * buffer_width) + 2]; unsigned char bg_a = buffer[4 * (xpos + ypos * buffer_width) + 3]; unsigned char a; if(alpha == 255 || bg_a == 255) { a = 255; } else { a = alpha + (inv_alpha * bg_a) / 256; } unsigned int r,g,b; if(a == 0) { r = 0; g = 0; b = 0; } else { r = (alpha * color.r + (inv_alpha * bg_a * bg_r) / 256) / a; b = (alpha * color.b + (inv_alpha * bg_a * bg_b) / 256) / a; g = (alpha * color.g + (inv_alpha * bg_a * bg_g) / 256) / a; } buffer[4 * (xpos + ypos * buffer_width) + 0] = b > 255 ? 255 : b; buffer[4 * (xpos + ypos * buffer_width) + 1] = g > 255 ? 255 : g; buffer[4 * (xpos + ypos * buffer_width) + 2] = r > 255 ? 255 : r; buffer[4 * (xpos + ypos * buffer_width) + 3] = a; } } }
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; }