ondras / rot.js

ROguelike Toolkit in JavaScript. Cool dungeon-related stuff, interactive manual, documentation, tests!
https://ondras.github.io/rot.js/hp/
BSD 3-Clause "New" or "Revised" License
2.33k stars 254 forks source link

Combination of browser&OS anti-aliasing can cause rendering issues. #44

Closed daid closed 9 years ago

daid commented 9 years ago

Currently, with the default ROT.Display settings. I'm getting bad rendering results, where part of the character bottoms is rendered outside of the rendering square.

If ROT.Display.Rect.cache is true then part of the character is cut off. If ROT.Display.Rect.cache is false then I get a ghosting effect like this: http://i.imgur.com/RlMXAVj.png (Note that it's not just the @ that goes wrong, other letters show ghosting as well)

This seems to depend on the browser&OS configuration combination. As it seems to go wrong in Chrome on my laptop (Windows7) which has anti-aliasing enabled on fonts. Firefox on this same machine does not show issues, but it does show anti-aliasing on fonts. While my other (small&cheap) laptop (Windows Vista) has firefox without anti-aliasing and does not show problems. Did not try chrome on that machine yet.

Setting the display spacing to 1.001 seems to workaround it for now, as then the actual boundary box is increased by 1 pixel.

Actual fix might be to draw a character (or multiple) to a canvas, and actually measure the width/height that it used instead of assuming the point size is the same as the character height in pixels. If you think this could be a valuable contribution I can make a pull request which implements this solution, but you might have other ideas.

ondras commented 9 years ago

Hi,

I would say that using a spacing >1 is a regular way to overcome this issue (it is actually the main reason why the spacing is implemented!), not a hack. Also, based on my own experience, this behavior varies greatly when using other font sizes and/or families. Your mileage may vary, but try adjusting font size by +-1 or switch to a different font (there are LOTS of useful roguelike fonts out there; Droid Sans Mono and Inconsolata being some of my favorites).

Manual measuring of rendered chars is something I would certainly want to evade for as long as possible, for a multitude of reasons:

  1. there already is an API for doing so (measureText for width, font size option for height). If the values obtained do not correspond to real world observations, I would consider it a browser's bug.
  2. measuring a char is a complex and time-consuming task; you want to scan the per-pixel canvas data to find the first/last pixel partially covered by the char's color. This, by definition, seems to be an ugly task :-)
  3. you would need to measure several (all of them?) chars to get a decent size coverage. The "@" might be overflowing in a vertical direction, but other char/font can present a vertical overflow, unexpectedly.

Please let me know if some of the solutions suggested in my first paragraph helped in your issue. These correspond to things I do when I run into this problem.

daid commented 9 years ago

Wait? You can load custom supplied fonts in a browser? (Sorry, I'm not used to web development. One of the reasons I'm playing with rot.js right now) Guess I have some more reading to do...

But then it might be wise to adjust the defaults for the ROT.Display settings a bit so this bug does not happen with the defaults?

  1. I agree with you there. It does feel like a browser bug, but in my experience with C and the freetype library, boundaries of glyphs is a complex thing. The measureText API pretty much misses a lot of information that I would expect from glyph/text drawing. (Less an issue for monospace fonts, but pretty much hell for other fonts)
  2. Only needs to be done once. But it is a bit ugly, have to agree on you there. But it would solve the problem.
  3. Yes, but you could draw then on top of each other and only measure once. You currently already sort of do something "ugly" like this by assuming that the W is the widest glyph. (which is not always the case, but works for most monospace fonts)
ondras commented 9 years ago

Yeah, you can use custom fonts in web pages. http://www.google.com/fonts might be a suitable starting place. Also, https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face.

The only one (but major!) "catch" here is that when using custom fonts AND the HTML canvas, the font must be fully loaded prior to drawing. But there is no reasonable way to know if or when the font loaded :-) Please see http://ondras.zarovi.cz/games/trw/ - this game uses two custom fonts.

As for the defaults - there are no defaults! :-) The default "font" setting of ROT.Display is "monospace"; this is a special HTML/CSS keyword that instructs the browser to just use some (the browser picks) of the available monospaced fonts. As you may imagine, this varies greatly across platforms, versions, users and so on.

Finally, I am not assuming that "W" is the widest glyph. The "W" is there by chance; you shall be able to replace it with any other character. This is how monospaced fonts behave by definition; all glyphs shall be of the very same width. Drawing all chars on top of each other is also somewhat complex - we are free to use all available characters from Unicode's BMP (basic multilingual plane; the first 65536 Unicode characters - http://en.wikipedia.org/wiki/Plane_%28Unicode%29#Basic_Multilingual_Plane), as these are all valid in JavaScript strings.

daid commented 9 years ago

I was already playing with the @font-face now, thanks for that!

I think I also have a work around for the loading issue (which causes problems, even on localhost, in chrome). I added a

that shows some text in this font on the page. Then I use the "onload" event to fire the rest of the code. As the onload event apparently only fires after all resources have been loaded. The div forces that the font is needed, and thus the onload waits for the font to be loaded.

But, "monospace" is your default, and your default is causing issues with chrome right now. Up to you if you are fine with that.

Closing the issue as I managed to solve it for myself, and the problem is apparently known and a bit "by design". Stil, this discussion might help other people as well.

Cheers. (And if you want to see what I'm working on, this is the latest running code: http://daid.github.io/NightmareRL/ , does not do too much yet. But you might like seeing that other people use your handywork)

ondras commented 9 years ago

@daid, Firefox now ships with a very useful set of developer tools that allow you to get the exact font name used. You might want to try it to extract the real font name your browser uses when "font-face:monospace" is used (hit F12 to launch those tools, inspect the relevant node, click the "Fonts" tab in the right toolbox).

I would argue that you really do not want to use the default "monospace" at the end of the day - because this way you have very little control of what the end user sees. For all my rot.js-based projects, I pick a specific (monospaced, obviously) font and use that for ROT.Display.