ryanisaacg / quicksilver

A simple framework for 2D games on desktop and web
https://ryanisaacg.github.io/quicksilver
Other
782 stars 77 forks source link

Text does not work great with Transform::scale #617

Open IgneousRed opened 4 years ago

IgneousRed commented 4 years ago

Describe the bug When your use Graphics::set_transform to increase the scale, text rendering breaks (see image). Images drawn with Graphics::draw_subimage_tinted() do not seem to be effected by this bug.

image

To Reproduce

use quicksilver::{
    geom::*,
    graphics::{Color, VectorFont},
    run, Graphics, Input, Result, Settings, Window,
};

fn main() {
    run(
        Settings {
            title: "Font Example",
            ..Settings::default()
        },
        app,
    );
}

async fn app(window: Window, mut gfx: Graphics, mut input: Input) -> Result<()> {
    gfx.set_transform(Transform::scale((40, 40)));
    let mut font = VectorFont::load("arialn.ttf").await?.to_renderer(&gfx, 12.0)?;
    gfx.clear(Color::WHITE);
    font.draw(
        &mut gfx,
        "Hello world!\nHello Quicksilver!",
        Color::BLACK,
        Vector::new(0.0, 10.0),
    )?;
    gfx.present(&window)?;
    loop {
        while let Some(_) = input.next_event().await {}
    }
}
ryanisaacg commented 4 years ago

This is the expected behavior. The problem you're seeing here is that text doesn't scale up very well; you're blowing up a 12px font to 40 times its size. If you want to render text at a scale of 480px per line, you'll need to pick a much larger font size.

IgneousRed commented 4 years ago

The problem is that, by picking a larger font, the size also increases, thus, it is still messy... the only way I know of is to temporarily scale the transform back to (1, 1), and than draw text, witch is not ideal to say the least... Also I am posting this because other gfx draws, namely fill and stroke still look great, as opposed to text.

lenscas commented 4 years ago

One possible solution (and, it is how unity does it if I am not mistaken) is to have a separate font size and character size.

That way, I can load a font and say its glyphs need to render as for example 40px but actual render them at 12px. This way the detail every glyph has gets separated from the draw size, making scaling like this possible without much if any problem.

To get the same effect now you would need to render these bigger glyphs first to a texture and then scale the texture appropriately yourself.

ryanisaacg commented 4 years ago

I like that approach @lenscas. I think I'll add a new method to make dealing with different "font" and "character" sizes optional, because for many use cases that's just additional complexity.

@IgneousRed Images will also scale far less well than stroke or fill of shapes, as an inherent property of scaling. When you scale a filled rectangle, the points of the rectangle change and the shape is filled on the GPU. When you scale an image, the points of the image's draw target change and the image pixels have to be stretched on the GPU. For performance reasons, text characters are rendered as images behind the scenes.

lenscas commented 4 years ago

Making that extra bit optional is indeed the best choice. I still remember the first time I tried to get my fonts to look nice on unity until I learned about that second value. And after I learned it, I just set it to 400 or something like that just so I didn't have to deal with it.

IgneousRed commented 4 years ago

I know the graphic basics, just think that, to use translate scale and text, you must reset before any text rendering is a bit messy...