Vertical Metrics?
Any idea when you'll be implementing Vertical Metrics support in OpenFont?
@hexland,
It is in the plan.
My 'current' dev queue ...
-
[x] #10 (3/3: autohint)
-
[ ] #24
then I will revisit this #44 after #24 is closed
Thank you for your interest.
Welcome all pull-requests.
:)
#10 -> may fix this problem https://github.com/PaintLab/PixelFarm/issues/3
So, my problem is this...
I'm trying to implement an MSDF font renderer in my game engine. I'm using the Typography library to rasterize a TTF into a texture atlas. Simple enough.
I want the Atlas index to contain the UV of the glyph on the page, and the WH of the glyph on the page. Again, easy enough.
In order to render this at runtime, I need additional information that I can't seem to get from the library. I need an offset from the cursor position to the top left of the glyph texture on screen. Hortizontal offset is easy enough -- that's just the translate value, and I can grab the HortizonalAdvance for the glyph from the typeface.
I am currently approximating the Y offset with the following code
fYOffset = fAscender - (glyphHeight-((float)translate.y) + fDecender);
where fAscender and fDecender are the typeface ascender and decender values, converted to pixel space. translate.y is the border/translation value used in CreateMsdfImage glyphHeight is the pixel height of the glyph that was created by CreateMsdfImage
I also made a small change to MsdfGlyphGen where it calculates the border width and calculates the translate value. The original code uses border = w/5 as a margin for both width and height -- but this knocks off the glyph width/height aspect ratio. I modified it to calculate separate borderW and borderH for the translate - maintaining the glyph texture aspect ratio.
Anyway - the code I'm using to approximate the Y offset is almost correct, but it doesn't work in certain cases, and I'm at a loss as to why.
However, it would be so much easier if I could just access the bearingY from the vertical metrics for the TTF...
Alternatively - I'm probably missing something fundamental, and this is already dead easy :)
from
I need an offset from the cursor position to the top left of the glyph texture on screen.
and
it would be so much easier if I could just access the bearingY from the vertical metrics for the
then
I will look for that values again. :)
@hexland:
fYOffset = fAscender - (glyphHeight-((float)translate.y) + fDecender);
Where dose a glyphHeight value come from?, ... What do you choose between
- a) overall (average) glyphHeight from a font
or
- b) per-glyph glyphHeight (exact glyph's height)
Here's the guts of the loop I'm using to generate the glyphs.
I implemented my own Atlas class (BinaryTreeAtlas) that builds multiple atlas pages
` Bounds bounds = typeface.Bounds; float fBoundsLeft = (float)bounds.XMin; float fBoundsRight = (float)bounds.XMax; float fBoundsTop = (float)bounds.YMin; float fBoundsBottom = (float)bounds.YMax; float fWidth = (fBoundsRight - fBoundsLeft); float fHeight = (fBoundsBottom - fBoundsTop); float fLargest = Math.Max(fWidth, fHeight); float fTargetPixelSize = mGlyphSize; float fPixelScale = fTargetPixelSize / fLargest;
int iAscender = typeface.Ascender;
int iDecender = typeface.Descender;
float fAscender = (float)iAscender * fPixelScale;
float fDecender = (float)iDecender * fPixelScale;
foreach ( char c in mInputDigest )
{
ushort gindex = typeface.LookupIndex(c);
builder.BuildFromGlyphIndex(gindex, -1);
GlyphTranslatorToContour glyphToContour = new GlyphTranslatorToContour();
builder.ReadShapes(glyphToContour);
Msdfgen.Shape msdfShape = MsdfGlyphGen.CreateMsdfShape(glyphToContour, fPixelScale);
double left, bottom, right, top;
msdfShape.findBounds(out left, out bottom, out right, out top);
float glyphWidth = (float)Math.Ceiling((right - left));
float glyphHeight = (float)Math.Ceiling((top - bottom));
GlyphImage glyphImg = MsdfGlyphGen.CreateMsdfImage(msdfShape);
BinaryTreeAtlas.BTACacheGlyph cacheGlyph = atlas.AddGlyph(c, glyphImg);
int borderW = (int)((float)glyphWidth / 5f);
int borderH = (int)((float)glyphHeight / 5f);
var translate = new Msdfgen.Vector2(left < 0 ? -left + borderW : borderW,
bottom < 0 ? -bottom + borderH : borderH);
ushort advanceWidth = typeface.GetAdvanceWidth(c);
float fAdvanceWidth = (float)advanceWidth * fPixelScale;
cacheGlyph.HAdvance = fAdvanceWidth;
cacheGlyph.YOffset = fAscender - (glyphHeight-((float)translate.y) + fDecender);
}
`
I find the largest bounds of the typeface, and try to work out the offset of the glyph with respect to that bounding box - based on the width/height returned from the msdfShape.findBounds.
It gets a bit sticky with glyphs that extend below the baseline, and I'm pretty sure its entirely the wrong way to do it :)
Thank you.
When I look from the view of actual glyph matrics (from glyph's points and contours). It is easy to find the values that you want.
But from the Msdfgen lib's view, ...
double left, bottom, right, top;
msdfShape.findBounds(out left, out bottom, out right, out top);
(this version) not provide enough information to calculate the values you want
and
glyphs that extend below the baseline,
(yes, you are nearly to the answer!)
I will add method(s) that provide information for Msdfgen lib
@hexland "How to get an exact glyph bounds" => see https://github.com/LayoutFarm/Typography/commit/23f5cac4f1d5f49f9ad8d656a530054684b9844b
This is an early fix. please test it out, If this is suite for you or not? and let me know.
pic 1: test with 'j' , to demonstrate glyph that its lower bound is below the baseline
pic 2: get Glyph and its exact (original) bounds
pic3: scale it down, to the same scale that we want (1f/64 for msdfgen)
- from pic3, ymin < 0 that is because the lower bound of 'j' is below the baseline
- the glyph ymax is height of the part above the baseline
- total glyph height = ymax-ymin
For Vertical Matric features in OpenType
OpenType™ vertical fonts require both a vertical header table ('vhea') and the vertical metrics table (vmtx)
-
But some fonts don't have the vertical matrics related tables (vhea, vmtx). (eg. in my 'TestFonts folder', Tahoma, Century, Palentino, Segoe font etc, don't have).
So I think get the bounds of glyph in the way above is quite correct for most fonts.
-
I found the vmtx related tables in the CJK font (eg. msjh). I will implement the vmtx related features later, (it is in the plan!).
Thanks for that. I believe I have it working now. I was using the typeface.Bounds to calculate 'the largest bounding area' and then calculate the pixel scale value from there (e.g. I want to scale the font to a 64x64 glyph texture). When displaying a texture in game, the font system uses the Top Left as the cursor position, and font's grow downwards as they increase in size. It simpler at runtime, and simpler is generally faster in game terms :) Each glyph in the atlas stores the U,V, Width, Height, XOffset, YOffset and HAdvance to move to the next character, and correct for the translation that happens when rendering the glyph into the MSDF image.
double left, bottom, right, top;
msdfShape.findBounds(out left, out bottom, out right, out top);
float glyphWidth = (float)Math.Ceiling((right - left));
float glyphHeight = (float)Math.Ceiling((top - bottom));
float err = glyphHeight - (float)(top - bottom);
GlyphImage glyphImg = MsdfGlyphGen.CreateMsdfImage(msdfShape);
BinaryTreeAtlas.BTACacheGlyph cacheGlyph = atlas.AddGlyph(c, glyphImg);
ushort advanceWidth = typeface.GetAdvanceWidth(c);
float fAdvanceWidth = (float)advanceWidth * fPixelScale;
cacheGlyph.HAdvance = fAdvanceWidth;
cacheGlyph.XOffset = -(float)glyphImg.TextureOffsetX;
cacheGlyph.YOffset = fAscender - (float)(top + err + glyphImg.TextureOffsetY + bottom);
The 'err' value was needed because I was seeing some very very slight vertical alignment issues due to accruing rounding errors when dealing between fractional glyph coordinates and integer pixels when placing the glyph on the glyph texture.
The rendering code at runtime is relatively simple...
float left = currentX + (pCBox->fXOffset * sizeMult)*xScale;
float right = left + (pCBox->fwidth*sizeMult)*xScale;
float top = currentY + ((pCBox->fYOffset * sizeMult)*yScale);
float bot = top + ((pCBox->fheight * sizeMult) * yScale);
float len = ((pCBox->fHAdvance * sizeMult) * xScale);
currentX += len;
//
// Set up vertex array
//
Thanks so much for your help!
@hexland
With your current method,... Do you use each glyph.Bounds or use overall typeface.Bounds ?
It makes no difference at the end of the day - the values were pretty much the same. I think I was most of the way there already - I just needed to take a step back and really think about what I was trying to achieve.
What I have found is that I'm having some vertical alignment issues because I used the typeface bounds to work out the pixel scale -- this was causing issues at game runtime with vertical centering - the glyphs were always higher than where I expected.
I found that I had to calculate the typeface bounds by stepping through only the printable glyphs (or the subset of glyphs that I'm using for that language) and calculate the pixel scale from those bounds instead of the whole typeface... some of the ascender and descender values in the typeface were pretty large, and skewing the bounds results (resulting in my typeface being higher or lower in relation to where I expected it to be).
I know this is the wrong way to think about it, and I really should go back and rethink how I'm doing runtime rendering, but I'm slammed with other tasks right now. I'll deal with it later. (for example, if I were to add additional new glyphs that might have disproportionately large ascenders or descenders, it affects the vertical alignment of the whole font)
@hexland :+1:
Thank you!
:)