solenum / msdf-c

Pure C multi-channel signed distance field generator
MIT License
2 stars 0 forks source link

Large amounts of "whitespace" around lowercase glyphs #2

Open mattparks opened 4 years ago

mattparks commented 4 years ago

I've noticed that msdf-c generates some glyphs, mostly lower-case glyphs, with more space around the main character data than msdfgen provides.

Here are some examples. Glyphs generated from Roboto Regular at 32x32, a range of 1, and with autoframe.

Chars: a, @, h, c, -

Chlumsky/msdfgen: image image image image image

exezin/msdf-c: image image image image image

256x256 Example: image image

solenum commented 4 years ago

Interesting, I'm currently using this in my other project (https://github.com/exezin/exengine) and the final rendering results seem correct.

It's been a while since I have touched this project but I suspect I could be pre-scaling things to be the appropriate scale for what it is, so you don't have to store meta-data and scale anything when rendering them out at the end.

I'll take a look at this :+1:

Edit;

Could this also be the result of using the 'autoframe' option? Could you also confirm that this actually presents issues when attempting to use the generated MSDF to render a line of text?

Here is my implementation.

mattparks commented 4 years ago

The rendering results are mostly correct, but with some glyphs a lot of texture space is underutilized; in comparison, msdfgen fits the glyph mutch closer to the edge of the bitmap.

Another variable that can effect quality is RANGE, your constant for range does not appear to produce the same results as pxrange in msdfgen: msdfgen msdf-c

msdfgen creates this glyph without autoframe (256x256): msdfgen

solenum commented 4 years ago

I'm actually struggling to find the right metrics for the scale (line 975 msdf.c) using stb_truetype.h, as far as I can tell stb_truetype only supplies two functions to get this metric and the current one used produces the a larger glyph than the other function.

As for the px_range value, does changing the iteration count here produce results that look closer to that of msdfgen?

~ main.c:121 ~
  for(int i=0; i<4; ++i)
    p += ((PX_RANGE*otf)/size)*size;
mattparks commented 4 years ago

The output only looks right when both RANGE from msdf.c and PX_RANGE in main.cpp match, otherwise the shape has little antialiasing.

Right now in main bitmap_sdf represents the actaual MSDF image, and bitmap represents the example rendered image using that sdf. I would suggest having these two images use a seperate size value (one for thge MSDF, one for the test render).

solenum commented 4 years ago

That's a valid point, I'll work on that soon and also have a look into the RANGE and PX_RANGE thing, though I'm really not sure what the issue is there.

solenum commented 4 years ago

Hi, as of commit https://github.com/exezin/msdf-c/commit/a2b0008c70e7e4093b170082f8d4a599832bc5e0 I have implemented auto-fit, this needs some testing though.

There's no way to enable it via the command-line yet, I hadn't really planned for this to become a tool, just code one could use in their own projects but perhaps that will change soon.

Anyway, change this line in main.c as shown here (last argument enables auto-fit) and give it a test, see how it works for you.

~ main.c:109 ~
   float *msdf = ex_msdf_glyph(&font, ex_utf8(c), size, size, &metrics, 1);

No auto-fit: msdf_out

With auto-fit: msdf_out

mattparks commented 4 years ago

Your auto-fit is working well for me, this is what my 256x256 example looks like with auto-fit=1. 26

I'm still not 100% sure how I will use RANGE and PX_RANGE in my font shader, but this is my shader:

uniform sampler2DArray msdf;

in vec3 vTexCoords;

layout(location = 0) out vec4 outColor;

float median(float r, float g, float b) {
    return max(min(r, g), min(max(r, g), b));
}

void main() {
    vec3 msdfSample = texture(msdf, vTexCoords).rgb;
    float sigDist = median(msdfSample.r, msdfSample.g, msdfSample.b);
    float w = fwidth(sigDist);
    float opacity = smoothstep(0.5 - w, 0.5 + w, sigDist);
    outColor = vec4(1.0, 1.0, 1.0, opacity);
}
solenum commented 4 years ago

Here's my text shader, I have to dynamically calculate the scale factor u_scale based on some values to keep the antialiasing consistent regardless of render size, its a bit strange but it works. I think the reason I need to do this is as you said before, there's something wrong with the RANGE and PX_RANGE aspects of the generator.

See here for how I calculate u_scale, it should also be noted that RANGE never actually touches my shader, those values are only used for generating.

#version 330 core

in vec2 uv;

out vec4 color;

uniform sampler2D u_texture;
uniform float     u_scale;

float median(float r, float g, float b) {
    return max(min(r, g), min(max(r, g), b));
}

void main()
{
  vec3 sample = texture(u_texture, uv).rgb;
  float dist = u_scale * (median(sample.r, sample.g, sample.b) - 0.5);
  float o = clamp(dist + 0.5, 0.0, 1.0);
  color = vec4(vec3(1.0), o);
}