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

Improve text rendering #40

Open
romgrk opened this issue Oct 30, 2020 · 13 comments
Open

Improve text rendering #40

romgrk opened this issue Oct 30, 2020 · 13 comments
Labels
gui Bugs or enhancement related to GUI elements help wanted Extra attention is needed

Comments

@romgrk
Copy link

romgrk commented Oct 30, 2020

Currently, text rendering seems to be ok only for specific font-sizes. Examples:

h12
Screenshot from 2020-10-30 16-08-46

h15
Screenshot from 2020-10-30 16-07-05

h16
Screenshot from 2020-10-30 16-10-42

The rendering is probably not handling non-integer pixel value incorrectly. It should probably round them to the neared integer value. h16 works because 16 / 1.333333.. == 12 (point to pixel conversion) but other values dont, eg 15 / 1.33333... == 11.25.

Edit: this seems to contain decent hints how to improve things: https://stackoverflow.com/questions/25956272/better-quality-text-in-webgl

@smolck
Copy link
Owner

smolck commented Oct 31, 2020

Interestingly, if you hover over the character, things are rendered correctly:

pic

@Breja Sorry to ping you, but I've been reading through the webgl code and yet cannot seem to figure out what causes the text to render correctly when the cursor is above it. Do you have any idea what could be causing the above to happen? It seems to be related to the background color, but other than that I'm lost as to what makes the text render right/wrong.

@ghost
Copy link

ghost commented Oct 31, 2020

The cursor char is rendered as a HTML/DOM node. It's a z-index thing because I was too lazy to implement the cursor in WebGL.

const cursorChar = document.createElement('span')

I think I might start looking in font-texture-atlas.ts to see if the text rendering looks correct there. Text rendering starts by painting text in a plain 2d canvas. Then that canvas is copied/loaded as a texture in WebGL. At that point WebGL is just blitting glyphs from the texture - not sure how much WebGL influences the text rendering at that point. Maybe some settings on how textures are loaded?

const loadCanvasTexture = (

@smolck
Copy link
Owner

smolck commented Oct 31, 2020

@Breja Thank you for the quick response! After taking a look at the code for the font atlas, it appears that ui.clip() was causing the issues: 6244541. Hopefully that doesn't break something else. Thanks again!

@romgrk If you get the chance, could you try 6244541 and see if it fixes the font issues for you?

@romgrk
Copy link
Author

romgrk commented Oct 31, 2020

Screenshot from 2020-10-31 15-38-48

It's no clipped anymore but it still doesn't feel right.

@smolck
Copy link
Owner

smolck commented Oct 31, 2020

What do you have guifont set to?

@romgrk
Copy link
Author

romgrk commented Oct 31, 2020

FantasqueSansMono Nerd Font Mono:h14

@smolck
Copy link
Owner

smolck commented Oct 31, 2020

Interesting, I can't seem to replicate that, looks fine on my system. Maybe it could be config-related somehow? I'm not sure.

@romgrk
Copy link
Author

romgrk commented Oct 31, 2020

Is the canvas rendering taking into account the device pixel ratio? Is it possible to test rendering to the canvas at higher resolutions?

@smolck
Copy link
Owner

smolck commented Oct 31, 2020

Is the canvas rendering taking into account the device pixel ratio?

Yep, here are the main places it's used: https://github.com/smolck/uivonim/search?q=devicePixelRatio

Is it possible to test rendering to the canvas at higher resolutions?

Not sure I understand the question, you mean for me to test it, or just in general?

@romgrk
Copy link
Author

romgrk commented Nov 1, 2020

Not sure I understand the question, you mean for me to test it, or just in general?

For me to check if it makes a difference. I guess I can set window.devicePixelRatio manually.

@romgrk
Copy link
Author

romgrk commented Nov 1, 2020

Setting manually window.devicePixelRatio = 2 seems to improve text resolution a lot on on my setup:

h15
Screenshot from 2020-10-31 20-10-15
h12
Screenshot from 2020-10-31 20-10-28

Zoomed in comparison (at h15). Previous shows rendering artifacts in-between letters that contribute to a degraded quality.

previous (dpr = 1.25) new (dpr = 2)
previous-zoom new-zoom

Increasing the ratio beyond 2 seems however to have adverse effects on quality, in this example with dpr=3, other kinds of rendering artifacts appear, the top curves of the "g" look different from each other.
too-much

@smolck
Copy link
Owner

smolck commented Nov 1, 2020

Interesting, thank you for the detailed response! As far as I know, it could be an issue with (a) the font texture atlas (so see src/render/font-texture-atlas.ts, which is what it sounds like; basically text is rendered onto a canvas and then webgl turns parts of that into textures to render text), or with (b) the settings used to store the texture/draw it, which can be found here as breja mentioned earlier:

const loadCanvasTexture = (
canvas: HTMLCanvasElement,
textureUnit = gl.TEXTURE0
) => {
gl.activeTexture(textureUnit)
gl.bindTexture(gl.TEXTURE_2D, gl.createTexture())
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
// @ts-ignore typings are wrong, see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/pixelStorei#Pixel_storage_parameters
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas)
}

I don't really know how I could test this on my system, so here's a diff you could try @romgrk (note that this is a complete shot in the dark, just a change to be more like code I saw in a tutorial on text rendering in webgl; I'm not really sure what it does, and I don't really expect it to fix anything):

Diff
diff --git a/src/render/webgl-utils.ts b/src/render/webgl-utils.ts
index b5743a75..b8c0a223 100644
--- a/src/render/webgl-utils.ts
+++ b/src/render/webgl-utils.ts
@@ -94,8 +94,7 @@ const create = (options?: WebGLContextAttributes) => {
   ) => {
     gl.activeTexture(textureUnit)
     gl.bindTexture(gl.TEXTURE_2D, gl.createTexture())
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
-    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
+    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
     gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
     // @ts-ignore typings are wrong, see https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/pixelStorei#Pixel_storage_parameters

I'm guessing it's more likely that something isn't right about how the font is scaled to the device pixel ratio; if I'm correct it'll probably be something in this code or in the code around it:

const regenAtlas = () => {
needToRegenAtlas = false
const width = cell.width * (getTableSize() + 127 - 32)
canvas.height = Math.floor(cell.height * window.devicePixelRatio)
canvas.width = Math.floor(width * window.devicePixelRatio)
ui.imageSmoothingEnabled = false
ui.font = `${font.size}px ${font.face}`
ui.scale(window.devicePixelRatio, window.devicePixelRatio)
ui.textBaseline = 'top'
ui.fillStyle = 'white'
for (let ix = 32; ix < 127; ix++) drawChar(String.fromCharCode(ix), ix - 32)
unicodeTable.forEach(({ index, width }, char) => drawChar(char, index, width))
}
const drawChar = (char: string, col: number, width = 1) => {
const charWidth = cell.width * width
ui.save()
ui.beginPath()
ui.rect(col * cell.width, 2, charWidth, cell.height)
ui.fillText(char, col * cell.width, 2, charWidth)
ui.restore()
}

But I really don't know what exactly the issue is here.

@smolck smolck added gui Bugs or enhancement related to GUI elements help wanted Extra attention is needed labels Nov 8, 2020
@smolck
Copy link
Owner

smolck commented Jun 2, 2021

I doubt #255 improved anything here (if anything it seems to have gotten worse on low-res monitors), but maybe?

Assuming not, my plan to fix this is to use MSDFs generated with msdf-atlas-gen (via a native node module). Using MSDFs, for one, would allow for proper anti-aliasing in the shader (instead of in the font atlas texture), hopefully improving the text quality (significantly?), and might remove the need to re-generate the atlas as much (since text can be scaled and not look blurry w/MSDFs iiuc).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
gui Bugs or enhancement related to GUI elements help wanted Extra attention is needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants