Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vertical Metrics? #44

Open
hexland opened this issue May 2, 2017 · 15 comments
Open

Vertical Metrics? #44

hexland opened this issue May 2, 2017 · 15 comments

Comments

@hexland
Copy link

hexland commented May 2, 2017

Any idea when you'll be implementing Vertical Metrics support in OpenFont?

@prepare
Copy link
Member

prepare commented May 2, 2017

@hexland,

It is in the plan.

My 'current' dev queue ...

then I will revisit this #44 after #24 is closed


Thank you for your interest.

Welcome all pull-requests.

:)

@prepare
Copy link
Member

prepare commented May 3, 2017

#10 -> may fix this problem PaintLab/PixelFarm#3

@hexland
Copy link
Author

hexland commented May 13, 2017

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 :)

@prepare
Copy link
Member

prepare commented May 13, 2017

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. :)

@prepare
Copy link
Member

prepare commented May 13, 2017

@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)

@hexland
Copy link
Author

hexland commented May 13, 2017

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);
        }

`

@hexland
Copy link
Author

hexland commented May 13, 2017

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 :)

@prepare
Copy link
Member

prepare commented May 13, 2017

Thank you.

@prepare
Copy link
Member

prepare commented May 13, 2017

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

@prepare
Copy link
Member

prepare commented May 13, 2017

@hexland
"How to get an exact glyph bounds" => see 23f5cac

This is an early fix.
please test it out, If this is suite for you or not? and let me know.


msdf_1
pic 1: test with 'j' , to demonstrate glyph that its lower bound is below the baseline


msdf_2
pic 2: get Glyph and its exact (original) bounds


msdf_3
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

@prepare
Copy link
Member

prepare commented May 13, 2017

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!).

@hexland
Copy link
Author

hexland commented May 16, 2017

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!

@prepare
Copy link
Member

prepare commented May 16, 2017

@hexland

With your current method,...
Do you use each glyph.Bounds or use overall typeface.Bounds ?

@hexland
Copy link
Author

hexland commented May 16, 2017

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)

@prepare
Copy link
Member

prepare commented May 16, 2017

@hexland 👍

Thank you!

:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants