lvgl / lv_font_conv

Converts TTF/WOFF fonts to compact bitmap format
https://lvgl.io/tools/fontconverter
MIT License
167 stars 72 forks source link

Glyphs quality #15

Closed beibean closed 4 years ago

beibean commented 5 years ago

Hi @puzrin,

First of all, congratulations for the great job done with de font converter 😉.

I was migrating from LittlevGL v5.3 to LittlevGL v6.0 and found some issues discussed here.

I already understood more or less the stuff related with new paradigma. Whichever, the thing I am worry about is the quality of the glyphs with new font converter.

As I show in this post, there is a bit lost in glyph quality. I guess is not about the new font converter at all but for the method of getting the .ttf into image.

What do you think? It might be an special case, but is important on my project.

Do you know if is there any possibility to use the method used in LittlevGL v5.3 just for getting the glyphs images?

Thanks in advance

puzrin commented 5 years ago

Size can be a bit different, because new version calculate metrics exactly as is should.

Discussion of "render any TTF into OLED with any size and best quality" can be endless, because technically impossible. Instead, i'd suggest you to describe exactly what you really need (list of sizes, bpp, font family, ...) and we will try to find solution.

About missed _ - use --dump to check source chars. This may be problem not with font but with renderer.

Do you know if is there any possibility to use the method used in LittlevGL v5.3 just for getting the glyphs images?

Only if you rewrite convertor back to PHP and use built-in renderer :)

kisvegabor commented 5 years ago

The PHP version uses FreeType to render the fonts. Is it ported to JS too? If so, it might be worth a try.

puzrin commented 5 years ago

The PHP version uses FreeType to render the fonts.

  1. Nobody knows real options used. For example, hinting on/off. There may be difference between "readability" and "smoothness".
  2. Prior to use FreeType, php script calculated desired metrics (scale, for example), and it was far from perfect.

Is it ported to JS too? If so, it might be worth a try.

I declined use of native FreeType, because installation difficulties would make package not useable for end customers. JS renderer, used here is well known opentype.js, it's good enougth.

kisvegabor commented 5 years ago

I declined use of native FreeType, because installation difficulties would make package not useable for end customers. JS renderer, used here is well known opentype.js, it's good enougth.

So there no reasonable way to improve quality?

puzrin commented 5 years ago

So there no reasonable way to improve quality?

In general, problem of ideal write of ANY vector font to low resolution display has no technical solution. I'd say, proper font selection for specific size will give much better effect.

Attempt to drop hinting will be waste of time IMO. With some luck, you will exchange one problem to another - bold lines to blured contours.

I got no answer about _. That may be some bug about cropping line bottom (but i hope not in font). Anyway, all this need a sample repo with reproducible samples and comments where to look. Inspecting with --dump will quickly show all defects.

It may be possible to improve horisontal resolution 3x via subpixel smoothing, but i did not digged deep into. IMO spends in this direction are not reasonable (just use HiDPI display instead with much better result).

kisvegabor commented 5 years ago

Using (or at least trying) FreeType is really not possible direction?

Attempt to drop hinting will be waste of time IMO. With some luck, you will exchange one problem to another - bold lines to blured contours.

What does hinting mean in this context? Could explain it or link something to read?

IMO spends in this direction are not reasonable (just use HiDPI display instead with much better result).

Anyway, better quality is better. :slightly_smiling_face: It would be nice to achieve the quality of the PHP converter. It really used some scaling and other tricks but these were only to modify the font size. So there were no hacks on the rendering itself.

puzrin commented 5 years ago

Using (or at least trying) FreeType is really not possible direction?

Everything possible. And full rewrite to php possible too. But without me :)

What does hinting mean in this context? Could explain it or link something to read?

https://www.freetype.org/ttfautohint/ - see intro & docs. That's corrections for small sizes to improve visual quality

Anyway, better quality is better. :slightly_smiling_face:

It's better only until it costs nothing :). Subpixel smoothing will require at least 3x memory to increase source resolution. Also, dark color become more light. You can search algorythms in google, something exists, but i don't remember details. Bitmap fonts have limitation, after those they become not evffective. At some moment it's better switch to vector fonts instead of reinvent weel.

It would be nice to achieve the quality of the PHP converter. It really used some scaling and other tricks but these were only to modify the font size. So there were no hacks on the rendering itself.

It would be nice to have test repo with reproducible samples first, prior to discuss anything. Convertor has --dump mode, and it's easy to check what it writes to font file.

If anyone wish to check hinting difference, hinting can be disabled here in option.

embeddedt commented 5 years ago

(just use HiDPI display instead with much better result)

We can't expect all embedded systems to have HiDPI displays.

puzrin commented 5 years ago

I this our problem we need spend time to discuss? Problem is trivial - author need to create test repo with font file, and put into readme list of minimal CLI commands to reproduce results.

puzrin commented 5 years ago

I did dump P of roboto and some freesans from internet with this command (set appropriate font path):

$ ./lv_font_conv.js --size 14 --bpp 4 --format dump --font ./node_modules/roboto-fontface/fonts/roboto/Roboto-Regular.woff --symbols P -o 1111

Снимок экрана от 2019-07-13 17-34-47 Снимок экрана от 2019-07-13 17-35-56

Effect is caused by location of vertical line between pixels (as a result, in freesans it's more gray).

Could not find difference with and without hinting, and it's strange. Tried to enforce hinting via ttfautohint tool, but renderer crashed with error

$ ./lv_font_conv.js --size 14 --bpp 4 --format dump --font freesans_ttfa_hinted.ttf --symbols P -o 1111
Hinting error:Error: unknown instruction: 0x78
Note: further hinting errors are silenced

Probably, built-in hints are for very small sizes, and freetype may use autohinter for bigger size. Will ask opentype.js guys why renderer crashes.

Anyway, i'd suggest to use pixel-perfect fonts for specific size if you need best quality.

puzrin commented 5 years ago

Reported https://github.com/opentypejs/opentype.js/issues/395, this should be fixed prior next attempt.

kisvegabor commented 5 years ago

Reported opentypejs/opentype.js#395, this should be fixed prior next attempt.

Awesome!

puzrin commented 5 years ago

For info. Seems it should be possible to assemble and use freetype2 via emscripten for use under node.js. But existing state of npm modules is very outdated:

Also take into account:

embeddedt commented 5 years ago

Freetype has GPL license.

Are you sure? The website says that it is dual licensed under the FreeType License and the GPL: https://www.freetype.org/license.html

puzrin commented 5 years ago

@embeddedt you are right. It should not become a fatal problem.

If you find a way to build freetype as node.js lib (with JS-callable API) - kick me to take a look. Webassembly internals would be preferable, but not critical.

In last try i remenber, we did manual wrapper and that looked ugly https://github.com/fontello/wawoff2/blob/master/compress.js. I think, emscripten devs should have something better for us.

beibean commented 5 years ago

Hi,

I've been out of the lvgl project, and will go on holidays soon.

I will test in a few weeks with other fonts to see if they are rendered better (roboto is a good candidate) and fit the project and investigate using --dump as puzrin suggests.

puzrin commented 5 years ago

No good news about opentype.js. Hinting processor had no support of conditional jumps. Adding this support was trivial, but resulting images become broken. I suspect, there are more bugs in previously implemented parts - fixing those needs completely different spends.

I would not recommend to wait solution from opentype.js in realistic future. It would be better, if someone could investigate how to build freetype for node.js via emscripten. In this case i could replace renderer.

embeddedt commented 5 years ago

@puzrin FreeType is already ported to Emscripten. The question is how you want to expose the APIs to node.js.

puzrin commented 5 years ago

@embeddedt Under "build for node.js" i mean providing appropriate API-s.

As far as i understand from doc, emscripten has some support https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html. But i can't understand how is this expected to be initialized? Because as far as i know, init process is async (at least, webassembly init is async and this can't be avoided).

embeddedt commented 5 years ago

Because as far as i know, init process is async (at least, webassembly init is async and this can't be avoided).

Right. I think the rest of the font convertor would have to accommodate that.

puzrin commented 5 years ago

That's not a big problem (i will do), if anyone can create a transpiled freetype package for node.js (if that's technically possible).

Draft will be ok too (build script & info how to init/call from node). I'm ready to help with node-specific details, how to properly arrange & publish package.

embeddedt commented 5 years ago

if anyone can create a transpiled freetype package for node.js (if that's technically possible).

Can you explain exactly what the steps would be to create that package, beyond simply including FreeType into an Emscripten program? Would I essentially have to bring out the FreeType APIs to JavaScript?

puzrin commented 5 years ago

If you have some time, i'd suggest to look at https://github.com/fontello/wawoff2 as [bad] example. Just to be sure that's possible. Problem is, that solution uses manual wrappers & all functions done async to hide init.

As far as i understand, embind is intended to generate JS glue automatically. But:

So, i see this steps:

  1. Try to understand, does emscripten really supports compilation to node.js modules & how they expect init to be done
  2. Provide makefile or instruction, how to build that (no need to test, just build something "looking like useable from node")
  3. From this point i can do all the rest about node (or help if anyone wish to learn it).
embeddedt commented 5 years ago

It seems possible to create node.js modules when compiling to WebAssembly (see the first half of this page): https://developers.google.com/web/updates/2019/01/emscripten-npm

puzrin commented 5 years ago

Well, as far as i see from ems faq, they suggest promise-like loading interface with -s MODULARIZE=1. That's fine.

Also, according to your link, docker should simplify builds.

What is not clear - how to create interface to call freetype2 methods and pass structs OR typed pointers. Google example use primitive types, those are not enough.

embeddedt commented 5 years ago

@puzrin It would be simplest, IMO, to build the renderer component in C, and create an API that is used by the higher-level components of the font convertor. That way, there would be a very reduced amount of binding code that would need to be written.

that solution uses manual wrappers

Unfortunately, I think that's unavoidable when trying to bind two very different languages.

embind is intended to generate JS glue automatically

I'm not so sure about this. It looks like embind is intended to generate JS glue when told information about the functions. Look at the super-simple C++ example: https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#classes

puzrin commented 5 years ago

Ok. C renderer will be useable too. I think, we could continue use opentype.js to work with metrics data, and use FreeType2 only to draw single glyph.

Will you have a time to do such "draft" with build script for docker, useable for cwrap call? With some minimal "caching" to not parse font very time for each glyph.

embeddedt commented 5 years ago

@puzrin I can get it to the point where you can build a module with FreeType and include it in the font convertor. Beyond that, I have little experience with FreeType, so I won't be of much use. I think you (or someone else) will have to write the relevant C routines for that.

puzrin commented 5 years ago

I'm not strong in C (can do some simple things, when no choice) and not familiar with freetype too. I could try to describe desired C interface, if that helps.

kisvegabor commented 5 years ago

@puzrin IT'd be a good starting point. I'm also not familiar with Freetype but let's see the required interface. Generating the bitmap shouldn't be too complicated.

puzrin commented 5 years ago

Something like this:

// seems FT can autodetect font type
int create_fontface(Uint8Array font, uint16 size, &fontface_data);

int free_fontface(&fontface_data);

int render_glyph(&fontface_data, uint32 glyph_code, Uint8Array &rgba_buffer, uint32 uint32 buf_size_x, uint32 buf_size_y, uint32 glyph_pos_x, uint32 glyph_pos_y);

I think this should be testable without JS at all.

Or, as altermative, without cache (for first attempt):

int render_glyph(Uint8Array font, uint16 size, uint32 glyph_code, Uint8Array &rgba_buffer, uint32 uint32 buf_size_x, uint32 buf_size_y, uint32 glyph_pos_x, uint32 glyph_pos_y);
kisvegabor commented 5 years ago

I think this should be testable without JS at all.

Sure creating byte array with the rendered image should be that simple. Do you need some assistance with that?

puzrin commented 5 years ago

Do you need some assistance with that?

Yes.

Under example i mean "read font from file, render char P and dump result array to console".

kisvegabor commented 5 years ago

I've followed this tutorial and made it work without Docker.

freetype_test.zip

Steps:

puzrin commented 5 years ago

Thank you! Could you do this changes:

kisvegabor commented 5 years ago

I've created a git repo for it and updated with requested changes: https://github.com/kisvegabor/freetype_test

puzrin commented 5 years ago

Thanks a lot. Give me several days to setup build scripts (never used docker before), then will try to use FT as backend. That should not take too much time, i'll keep you informed on progress.

kisvegabor commented 5 years ago

Great, thank you!

kisvegabor commented 4 years ago

@puzrin Is there any news with FreeType?

puzrin commented 4 years ago

Not yet, sorry. Have to do other projects. But i remember about this issue.

kisvegabor commented 4 years ago

Ok, thank you for the feedback.

puzrin commented 4 years ago

I've tagged last stable version and commited API rewrite to be async https://github.com/littlevgl/lv_font_conv/commit/6bd7c74b18d76a15e973ed0a35c5c28b50b05f6b

That does not add "new features", but allows to call async init for emscripten-compiled libraries.

puzrin commented 4 years ago

@kisvegabor, @embeddedt

There are some progress (see branches). Need help with freetype lib build. Current one (default) does NOT support woff (ttf is ok). Here is Dockerfile

Could you help with improving this? I mean:

To run via npm:

# build docker image, with emscripten & precompiled freetype
npm run build:dockerimage

# compile renderer glue for js (run shell file in our docker image)
npm run build:freetype

Probably need extra libs, more options & better paths instead of emmake make install in docker config.

PS. please post samples to separate branches, don't touch existing ones.

puzrin commented 4 years ago

Note, woff (not woff2) is "ttf + gzip". Probably, freetype was misconfigured somehow. I have no ideas why woff loading can fail, except decompression problems. From the other hand, FreeType has built-in zlib and i don't know why this may fail.

kisvegabor commented 4 years ago

I've tested Roboto-Regular.woff.zip with my freetype_test repo and it worked well.

puzrin commented 4 years ago

@kisvegabor

You used preinstalled system library, while we build it from sources. Problem is with configure, it disables zlib (and built-in zlib too), required for woff. Note, is we use built-in ported freetype, woff works, but version is very old and should not be used for good hinting.

We updated branches with almost working new renderer, see freetype. It still misalign glyphs, BUT ready to test everything else:

How to play:

git checkout freetype
# once
npm run build:dockerimage

# after checjout + Every time after built config or C source updated
npm run build:freetype

Note, steps above need because we did not commited wasm build to avoid multiple updates of big blobs. Will do that when all issues resolved.

It would be very nice is someone more experienced could help with emscripten. FreeType build issue does not depend on JS at all. What is needed:

puzrin commented 4 years ago

Quality progress

Tested FreeSans.

  1. Currently, there are no difference between opentype and current freetype. Probably, build issues or some missed flags to force hinting.
  2. After ttfautohint result is better (is forcec more hinting instructions, to describe all sizes)

opentype:

P_opentype

freetype current:

P_freetype

ttfautohunt + freetype:

P_ttfa_freetype

embeddedt commented 4 years ago

someone more experienced could help with emscripten.

I will look into it on the weekend.

puzrin commented 4 years ago

https://github.com/littlevgl/lv_font_conv/commit/60e35b57d2159f5fc323a817859e44cdd408712f

Fixed woff support via kludge, until done properly + with harfbuzz. Idea is force it use system zlib and then add -s USE_ZLIB=1 to emcc options. Ugly but works.

puzrin commented 4 years ago

I don't know why freetype does not fallback to autohinter when hinting info not exists for specific size. So, forced autohinter to be always on.

FreeSans before (FT_LOAD_DEFAULT flag):

P_FT_LOAD_DEFAULT

FreeSans after (FT_LOAD_FORCE_AUTOHINT flag):

P_FT_LOAD_FORCE_AUTOHINT

I don't know if additional option needed to disable autohinting. Will add later, if bugs reported.