static const char *initFont(font_t *self, const void *data, int ptsize) { int i; /* Init font */ stbtt_fontinfo font; if ( !stbtt_InitFont(&font, data, 0) ) { return "could not load font"; } /* Get height and scale */ int ascent, descent, lineGap; stbtt_GetFontVMetrics(&font, &ascent, &descent, &lineGap); float scale = stbtt_ScaleForMappingEmToPixels(&font, ptsize); self->height = (ascent - descent + lineGap) * scale + 0.5; /* Init image */ int w = 128, h = 128; retry: image_initBlank(&self->image, w, h); /* Load glyphs */ float s = stbtt_ScaleForMappingEmToPixels(&font, 1) / stbtt_ScaleForPixelHeight(&font, 1); int res = stbtt_BakeFontBitmap( data, 0, ptsize * s, self->image.data, w, h, 0, 128, self->glyphs); /* Retry with a larger image buffer if the buffer wasn't large enough */ if (res < 0) { w <<= 1; h <<= 1; image_deinit(&self->image); goto retry; } /* Adjust glyph yoffsets */ int scaledAscent = ascent * scale + 0.5; for (i = 0; i < 128; i++) { self->glyphs[i].yoff += scaledAscent; } /* Init image data and mask */ for (i = 0; i < w * h; i++) { self->image.data[i] = (self->image.data[i] > 127) ? 1 : 0; self->image.mask[i] = (self->image.data[i] == 0) ? 0xff : 0; } /* Return NULL for no error */ return NULL; }
int FontRenderer::getKerning(const fm::Uint32 &leftCodePoint,const fm::Uint32 &rightCodePoint) const { if (!m_fileContent) return 0; return stbtt_ScaleForMappingEmToPixels((stbtt_fontinfo*)m_stbFontInfo, m_currentSize) * stbtt_GetCodepointKernAdvance((stbtt_fontinfo*)m_stbFontInfo, leftCodePoint,rightCodePoint); }
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)); }
static void *font_renderer_stb_init(const char *font_path, float font_size) { int ascent, descent, line_gap; stbtt_fontinfo info; uint8_t *font_data = NULL; stb_font_renderer_t *self = (stb_font_renderer_t*) calloc(1, sizeof(*self)); /* See https://github.com/nothings/stb/blob/master/stb_truetype.h#L539 */ font_size = STBTT_POINT_SIZE(font_size); if (!self) goto error; if (!filestream_read_file(font_path, (void**)&font_data, NULL)) goto error; if (!font_renderer_stb_create_atlas(self, font_data, font_size, 512, 512)) goto error; if (!stbtt_InitFont(&info, font_data, stbtt_GetFontOffsetForIndex(font_data, 0))) goto error; stbtt_GetFontVMetrics(&info, &ascent, &descent, &line_gap); self->line_height = ascent - descent; if (font_size < 0) self->line_height *= stbtt_ScaleForMappingEmToPixels(&info, -font_size); else self->line_height *= stbtt_ScaleForPixelHeight(&info, font_size); free(font_data); return self; error: if (font_data) free(font_data); if (self) font_renderer_stb_free(self); return NULL; }
const FontRenderer &FontRenderer::setCharacterSize(unsigned int size) const { if (m_currentSize == size) return *this; m_currentSize = size; if (!m_fileContent) return *this; stbtt_GetFontVMetrics((stbtt_fontinfo*)m_stbFontInfo,&m_metrics.maxH,&m_metrics.minH,&m_metrics.lineGap); float scl = stbtt_ScaleForMappingEmToPixels((stbtt_fontinfo*)m_stbFontInfo, m_currentSize); m_metrics.lineGap += m_metrics.maxH - m_metrics.minH; m_metrics.lineGap *= scl; m_metrics.minH *= scl; m_metrics.maxH *= scl; return *this; }
static void *font_renderer_stb_init(const char *font_path, float font_size) { uint8_t *font_data = NULL; int ascent, descent, line_gap; stbtt_fontinfo info; stb_font_renderer_t *self = (stb_font_renderer_t*) calloc(1, sizeof(*self)); /* prevent warnings */ (void)rect_width_compare; if (!self) goto error; if (!read_file(font_path, (void**)&font_data, NULL)) goto error; if (!font_renderer_stb_create_atlas(self, font_data, font_size)) goto error; if (!stbtt_InitFont(&info, font_data, stbtt_GetFontOffsetForIndex(font_data, 0))) goto error; stbtt_GetFontVMetrics(&info, &ascent, &descent, &line_gap); self->line_height = ascent - descent;// + line_gap; if (font_size < 0) self->line_height *= stbtt_ScaleForMappingEmToPixels(&info, -font_size); else self->line_height *= stbtt_ScaleForPixelHeight(&info, font_size); free(font_data); return self; error: if (font_data) free(font_data); font_renderer_stb_free(self); return NULL; }
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; }