Pixel Font and Nearest-neighbor interpolation

We’re definitely seeing a problem on OS X.  Just not on Windows.  So, this is currently leading us to believe that this is an OS X or Apple related issue only at the moment and it’s dependent on scale mode.  It’s on our “to investigate” list.

Regarding your last posted screenshot, I don’t see anything wrong with it.  Both of the ‘M’ characters look exactly the same to me.  And I don’t where glasses either.  I don’t know how we can help you there.

Okay. Our OS X users here are starting to think that this may be a *hinting* issue in your embedded font for you display.newText() sample code.  if you switch to using “native.systemFont”, the blurriness issue goes away.

>  I don’t see anything wrong with it.  Both of the ‘M’ characters look exactly the same to me.  

To make this clear, I am using image editor to zoom in the screenshot (click for the large image):

JNa0DDf.png

I should clarify, I mean displaying pixel font without subpixel artifacts, here is a reference rendering in another game engine compared with corona rendering. Both rendering at integer coordinate, and at designed size (24px)

Reference:

I4TAjod.png

Corona SDK (simulator zoom level doesn’t matter, Corona SDK show the same subpixel artifact)

avUvJOV.png

see display.setDefault for min/mag filters

i think your first post has it “backwards”, i think you want to set nearest filtering, instead of default linear

you’ll want to do this just before loading the assets so that its used in whatever image/sheet is created

(then likely will want to set it back right afterwards to restore the default for other uses)

> i think you want to set nearest filtering

Yes.

> see display.setDefault for min/mag filters

Thing is, I have already set them to ‘nearest’, I use pixel asset for tilemap so it’s the first thing I done, and I can confirm them work for image asset, but not font.

Our display.newText() APIs uses the operating system’s native text renderer, which renders your text to a bitmap *before* being submitted as a texture to OpenGL.  Meaning that the OS’ native text renderer is alpha blending the edges of the characters/glyphs, not OpenGL.

OpenGL will only blend the edges of a text object’s text image if that object was scaled.  And I mean scaling the object yourself via the :scale() function or its scaleX and scaleY properties.  The global content scaling won’t be an issue because Corona takes that into account and appropriately converts your given font size to pixels based on the global scale factor.

But let me ask you this.  Is your Corona Simulator currently zoomed?  As in it’s not at its 100% zoom level?  Because if it is zoomed, then you *will* see scaling artifacts (pixels lost when zoomed smaller) that won’t happen on device and are safe to ignore.  That is, the pixel resolution of the OpenGL viewport has a fixed size in the simulator, but it’s being rendered to a smaller or larger view.  I suspect this is the issue you are seeing.  This will never happen on device because our OpenGL viewport always matches the actual pixel resolution of the screen.

Also, since you’re showing a framerate counter, for best performance, you should use a bitmap font library instead.  The added benefit of using a bitmap font library is that you’re showing images of pre-generated characters, which avoids the native text renderer’s imposed alpha blending of character edges.  Although I suspect the real issue here is simple simulator zoom level scaling artifacts.

> Meaning that the OS’ native text renderer is alpha blending the edges of the characters/glyphs, not OpenGL.

Thx Joshua, that makes sense. I am on OS X, is it possible to prevent OS X from messing with rendering from a TTF font? I know they default to subpixel rendering.

> Is your Corona Simulator currently zoomed

Three things I checked:

  • Turn off Corona: “Automatically scale simulator to fit screen”

  • Check different zoom level

I am hoping my simulator launch at 100% zoom level, but there isn’t a reset resolution option in menu.

And because I suspect OS X subpixel rendering, I turned off

  • “Use LCD font smoothing when available”

Which will disable subpixel rendering in most scenario, but rendering in Corona SDK is still showing a “blend” edge.

Now to reproduce a few more examples, assume simulator launch at 100% zoom level.

at 12px, zoom 100%

1nGlZsT.png

at 12px, zoom 200% (I hope it’s what cmd/+ do)

dHVKmxr.png

at 48px, zoom 100%

GlAOCX4.png

at 48px, zoom 200%

jK1bK9w.png

Things I don’t understand:

  • Why the 12px 100% zoom level text appears to be “blurred” in the middle of a sentence.

  • Why do I get “blend” edge when rendering at multiple of designed size (intended to be multiple of 12px)

  • How did the other game engine pull this off, by using FreeType to force TTF into Bitmap instead of using native text rendering?

PS: the font can be found here, if you want to test it:

Oh I forget to mention, I do have bmGlyph, but generating Bitmap file for a Unicode TTF seem a bit overkill (the number of CJK chars are going to result in a huge imagesheet).

I could have pick a subset of this TTF for my stats rendering, but the same issue still exist for my Unicode text… So finding a solution where I can use TTF would be a better choice for me.

oops - my quick “drive by” answer had misread your problem as *bitmap font*

you might try experimenting with setting a fixed width and using left alignment

why?  as per Joshua’s reply re ttf rendered to texture, imagine two sheets of graph paper held up to light…

now align them so the grids match (this simulate you positioning the texture itself at integral coordinates)

now draw text on the upper one that is NOT aligned to the grid (this simulates ttf render within texture at potentially arbitrary coords)

end result is that the “pixels” of your rendered font don’t end up at integral positions

but unfortunately, and even if that analogy is correct, it could at most solve “half” the problem, cuz you still wouldn’t have any control of the y alignment.  (and i don’t know if Corona and/or the underlying OS uses the font’s baseline or descender or whatever else to figure out where to put y, but it might not match what your font’s design requires for pixel-perfect rendering)

Hmm… Dave got me thinking that perhaps your image object positions are landing inbetween pixels.  That is, the polygon/rectoid is landing on fractional coordinates, which forces the video card to blend a single texture pixel to multiple onscreen pixels.  I’m not an OpenGL expert, so I don’t know if GL_NEAREST will handle that specific case.
 
You *can* force Corona to position you object to a whole number pixel position, but you have to do the math yourself like the below…

textObject.anchorX = 0 textObject.anchorY = 0 local xInPixels = textObject.x \* display.contentScaleX local yInPixels = textObject.y \* display.contentScaleY local xOffset = (math.floor(xInPixels) - xInPixels) / display.contentScaleX local yOffset = (math.floor(yInPixels) - yInPixels) / display.contentScaleY textObject:translate(xOffset, yOffset)

 
The above would move an existing text object to a whole number pixel position.  The above works best for objects that are anchored to a corner, such as the top-left corner (0,0).  For objects whose anchor point is at their center (the default), it would probably only work if your object has an odd width and height in pixels (I haven’t tried it).

Edit:

I test the above with a display.newText() text object and the results looked good.  And I used a resizable desktop window (Corona built Win32 app) to test it out.  The above should work for image objects as well.

Thx both for the suggestions, but they are not working for me at the moment.

To Dave:

Settings explicit width doesn’t fix the problem, and I tried to move my text object x,y to other integer position, they render the same.

However, setting different text object width does affect which glyphs gets more “blurred”, it seems. Here is two examples where I attempt to render at 12 fontSize with 200 or 50 as width, both using a custom simulator device where contentScaleX will be 1 instead of iphone device’s 0.5.

 self.stats = display.newText({ text = 'loading' -- placeholder , font = 'fonts/zpix.ttf' , fontSize = 12 , width = 200 , align = 'left' , x = 20 , y = 20 })

PtcABuK.png

0FDC4Aq.png

To Joshua:

I tried yours too, but as you can see from my above code and comment, textObject.x is 20, and contentScaleX is 1, there isn’t something to be “floored”, so the translate offset is 0,0.

I actually prefer working with top-left anchor too, so this is my default.

display.setDefault('minTextureFilter', 'nearest') display.setDefault('magTextureFilter', 'nearest') display.setDefault('anchorX', 0) display.setDefault('anchorY', 0)

Also I find a font designed specifically to be pixel-crisp at multiple of 12 appear on subpixel region unlikely.

For reference, here is one where I put text object on (20.5, 20.5) to explicitly trigger anti-aliasing, adding your code works for this scenario, but not the one I mentioned above, they are not the same thing:

Na9HW8r.png

8EfT17n.png

At this point I want to say this is a bug, whether it’s on OS X only I don’t know.

It’s would be a minor issue, given iOS probably use retina screen to compensate this, but an issue nonetheless…

Well, I’m not seeing this issue on Windows.  And I’m not using a high resolution monitor either, making something like this much more noticeable.  And the solution I’ve posted above definitely works-around this issue when content scaling is enabled.

The only other things I can think of is that you’re running on a retina screen, but your Corona Simulator app is set up to “Open in Low Resolution” mode.  Or (perhaps less likely) you’ve set up your display to be scaled under System Preferences, although that would be noticeable in all apps.  However, odds are you are running the Corona Simulator on a non-retina display, right?

> Well, I’m not seeing this issue on Windows. 

OK, so it’s not reproduced on Windows? I gotta try and get back to you.

> However, odds are you are running the Corona Simulator on a non-retina display

Yeah, non-retina Macbook Air and iMac.

PS: I have tested on iPhone 6 running iOS 9, I don’t see this issue.

Can you send us a simple project to reproduce this issue please?

You can do so by clicking the “Report a Bug” at the top of this web page.

And I recommend that this project use display.newText(), since that’s a built-in Corona feature.  This is in case the bitmap font library you are using is exasperating the issue somehow, such as positioning character objects on fractional coordinates.

It would also help to post a link to this forum thread in your bug report.

After submitting it, post the bug number to this forum.  I’ll then poke the right people into looking into it.  Thanks!

I tested it on Windows, the same issue does exist.

But I should point out I am using TTF font and newText(), NOT bitmap font library, the whole “bitmap” thing refer to what you mentioned above: Corona uses native text renderer to rasterize glyph…

The bug number is  Case 46360

And for curious minds, this is the reproducing sample

I just ran your project via the Windows version of the Corona Simulator.  The text is definitely not blurry and appears fine.  And I’m running it on a non-retina screen (I tried it on 2 separate monitors).

Now, if I changed the simulator’s zoom level to something other than 100%, yes, the text will be blurry.  But that’s a simulator feature only and won’t happen on device.  I’m still thinking this is what’s happening to you.  Especially since no one else is mentioning this issue.

Note:  To show your simulator at 100% zoom level, just keep zooming-in (makes the window bigger) until you can’t zoom-in anymore.

I also just had someone test this out on Mac on a non-retina screen.  He was able to reproduce this issue.  But that said, he noticed that your “config.lua” has a typo in it.  You are attempting to use the “adaptive” scale mode, but that word is spelled wrong.  Fix the typo there and this text blurriness issue will go away.

That said, there does seem to be an OS X specific issue here where the text is blurry when no scaling is applied.  This appears to be a legit issue and will investigate further.  So, thanks for bringing this up.  But in the meantime, applying a content scale work-around this issue.