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

Add SDF HintingMode #1966

Open
wants to merge 7 commits into
base: 12.0-development
Choose a base branch
from

Conversation

Labrium
Copy link

@Labrium Labrium commented Sep 2, 2023

I was having trouble building this with the prebuilt FreeType macOS framework, I'm not sure why, so I had to build it myself to make sure this built correctly.

@slime73
Copy link
Member

slime73 commented Sep 10, 2023

I tried to apply and use this change on Windows with the default font (love.graphics.newFont(14, "sdf")) but I get this error at runtime:

main.lua:1: TrueType Font glyph error: FT_Glyph_To_Bitmap failed (0x13)

0x13 is FT_Err_Cannot_Render_Glyph. The version of FreeType in megasource's 12.x branch is 2.12.0. Does it need a newer version than that to work, or is there some other problem?

@Labrium
Copy link
Author

Labrium commented Sep 11, 2023

That's the main problem I was having with the prebuilt love-apple-dependencies on macOS. After building it myself from the official FreeType 2.12.0 source instead of from the megasource, it worked with no errors.

From FreeType's changelog they state that SDF support is available since 2.11.0, is there a reason the megasource's FreeType could be different from the official source?

@slime73
Copy link
Member

slime73 commented Sep 11, 2023

It's the same source code. Maybe it's an opt-in compile time feature when building the library? If that's the case and it's not enabled by default in Linux distros, this gets a lot more complicated to support because we can't guarantee that it's available in every copy of a given love version.

Speaking of Linux distros, I think Ubuntu 20.04 LTS may not provide 2.11+.

@Labrium
Copy link
Author

Labrium commented Sep 11, 2023

I've investigated it further, and it looks like the FT_Err_Cannot_Render_Glyph error is being caused by this commit.

Commenting out lines 303-309 in src/sdf/ftsdfrend.c makes it work correctly (restoring the same functionality as 2.11.0):

    /* the rows and pitch must be valid after presetting the */
    /* bitmap using outline                                  */
    //if ( !bitmap->rows || !bitmap->pitch )
    //{
    //  FT_ERROR(( "ft_sdf_render: failed to preset bitmap\n" ));

    //  error = FT_THROW( Cannot_Render_Glyph );
    //  goto Exit;
    //}

Would it be possible and/or favorable to modify the FreeType source within the megasource (assuming this small change does not cause any other problems)?

Also, I don't see how supporting Ubuntu 20.04 is relevant to this issue since the 12.x branch of the megasource already includes FreeType 2.12.0.

@slime73
Copy link
Member

slime73 commented Sep 11, 2023

Commenting out lines 303-309 in src/sdf/ftsdfrend.c

Yeah that'd do it, love creates a glyph of the space character in Font constructors. It might be possible to modify TrueTypeRasterizer.cpp so it detects glyphs which would have invisible/zero-size bitmaps and has an alternate codepath that doesn't try rasterizing those. Or sdf support could only be turned on at runtime when a newer version of freetype with that fix is used - although I'm hesitant about doing that because of the versions used in package managers.

Would it be possible and/or favorable to modify the FreeType source within the megasource (assuming this small change does not cause any other problems)?

I don't think that's a good idea, it adds a burden for us to maintain the patch and it would only apply to one platform out of 5 (see below).

Also, I don't see how supporting Ubuntu 20.04 is relevant to this issue since the 12.x branch of the megasource already includes FreeType 2.12.0.

As I said (and is said in the readmes), megasource is primarily for Windows. It's not used for official builds on other platforms. Linux AppImages use a different build process, and Linux distro-maintained love packages use shared system dependencies which are not under our control at all.

Copy link
Member

@slime73 slime73 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's working for me in Windows now, thanks!

I think the hinting stuff still needs to be figured out, but aside from that it looks good. It's too bad Freetype doesn't have an option for a more advanced SDF technique since single-value SDFs can have some noticeable visual issues around corners of letters.

@@ -236,6 +247,7 @@ FT_UInt TrueTypeRasterizer::hintingToLoadOption(Hinting hint)
return FT_LOAD_TARGET_LIGHT;
case HINTING_MONO:
return FT_LOAD_TARGET_MONO;
case HINTING_SDF:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SDF freetype glyphs are still affected by whatever hinting load mode is being used (if I change the code to use different ones than NO_HINTING here I get different visual results).

I don't know whether it makes sense to expose SDF versus normal glyph generation as a separate option from the hinting mode, but either way making it always use NO_HINTING probably isn't desirable since it can stop things from looking like how the font author intended.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conceptually the hinting mode is a different thing from SDF versus bitmap rasterized glyphs, too.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed that because SDF rendering is made to be scalable, hinting would become irrelevant since the source and target pixel grids will almost certainly not match up.

Taking this into account, I chose to use NO_HINTING because it preserves the exact shape of the text, which is optimal for dynamic scaling.

Also, the resulting shape of the text (and appearance of "visual issues") depends mostly on the texture filtering settings and the original size the font was initialized at.

I understand that there are other methods more advanced than pure SDF rendering, such as MSDF which preserves sharp corners in fonts, but I think that's out of the scope of this PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because SDF rendering is made to be scalable, hinting would become irrelevant

I think it's still relevant because it combines with the input font size to affect the look of the SDF glyphs. Maybe some people might even want mono hinting with it. In this code example you can see the 'e' renders differently with normal hinting (red) versus SDF + no hinting (green), whereas SDF + normal hinting matches the non-SDF version more closely.

font1 = love.graphics.newFont(20, "sdf")
font2 = love.graphics.newFont(20)

shader = love.graphics.newShader[[
vec4 effect(vec4 vcolor, Image tex, vec2 tc, vec2 pc)
{
	float dist = Texel(tex, tc).a;
	float edgeWidth = 1 * length(vec2(dFdx(dist), dFdy(dist)));
	float edgeDistance = 0.5;
	float opacity = smoothstep(edgeDistance - edgeWidth, edgeDistance + edgeWidth, dist);
	vcolor.a *= opacity;
	return vcolor;
}
]]

function love.draw()
	love.graphics.setShader()
	love.graphics.setColor(1, 0, 0)
	love.graphics.print("Hello World", font2, 10, 10, 0, 1)

	love.graphics.setShader(shader)
	love.graphics.setColor(0, 1, 0)
	love.graphics.print("Hello World", font1, 10, 10, 0, 1)
end

Capture
Capture2

I think that's out of the scope of this PR.

I agree, I was just commenting that it's not a totally perfect solution to a general SDF font request (the original github issue), although it's still a nice feature to have.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it combines with the input font size to affect the look of the SDF glyphs. Maybe some people might even want mono hinting with it.

I hadn't even tested that before. Then instead of adding "sdf" as a HintingMode, what do you suggest? Combining it with the existing HintingModes ("sdfnormal", "sdfmono", etc.)? Adding it as another parameter to love.graphics.newFont? Or something else?

Copy link
Member

@slime73 slime73 Oct 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably add it as a separate parameter. However the parameter list is getting kind of long so maybe it should be turned into a settings table (like what newImage etc. have) instead. Since that's sort of a separate change I could do that without the sdf support part some time this week.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the parameter list is getting kind of long so maybe it should be turned into a settings table (like what newImage etc. have) instead.

I've done this in 5a0a6a1.

For the sdf setting, I think you'll just want to have a sdf boolean in the Settings struct (defaulted to false), and the table part of luax_checktruetypesettings would be modified to check for a sdf boolean field (also defaulted to false).

@slime73
Copy link
Member

slime73 commented Feb 10, 2024

The 12.0-development branch has been merged into main now, you'll need to change the base branch of this PR to main as well.

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

Successfully merging this pull request may close these issues.

None yet

2 participants