alexheretic / glyph-brush

Fast GPU cached text rendering
Apache License 2.0
688 stars 52 forks source link

Layout text from bottom #123

Closed codepunkt closed 3 years ago

codepunkt commented 3 years ago

Hey there - awesome library, thanks for your work! 👍

 ________________________________
|                                |
|                                |
|                                |
|  |__ start here                |
|________________________________|

I can do this for a single line by using padding like 50 and <HEIGHT> - 50

 ________________________________
|                                |
|                                |
|                                |
|  Single line of text           |
|________________________________|

However, if i would try to use a string that doesn't fit the given width, it would split to multi-line and probably look like this, not respecting the padding of 50 that i would like it to have from the bottom:

 ________________________________
|                                |
|                                |
|                                |
|  Slightly longer text          |
|  rendered to multiple lines    |

Instead, i would like to keep the padding from the bottom intact and let multiple lines grow upwards like this:

 ________________________________
|                                |
|                                |
|  Slightly longer text          |
|  rendered to multiple lines    |
|________________________________|

Is it possible to layout text from the bottom like this?

I'm using calculate_glyphs and couldn't find a way - but i'm also completely new to rust and a little lost here. screen_position of a SectionGeometry seems to be in pixels from the top left.

alexheretic commented 3 years ago

Yes, this is a property of the Layout. By default it uses HorizontalAlign::Left, VerticalAlign::Top. And the position you declare is the top left corner.

If you use Layout::default().v_align(VerticalAlign::Bottom) the position you pass becomes the bottom left corner and the layout should behave as you are describing.

Also see the opengl example which shows left,top, center,center, right,bottom layouts.

And yes the screen geometry used is indeed measured from the top left.

codepunkt commented 3 years ago

@alexheretic that's awesome, thank you!

I'm a step further along the way - however, it seems like VerticalAlign::Bottom also aligns every individual character.

When i render the resulting image on a static PNG background with this code

#[wasm_bindgen]
pub fn layout_text(
    text: &str,
    width: i32,
    height: i32,
    font_size: i32,
    rgb: JsValue,
    font_file: Uint8Array,
) -> Vec<u8> {
    utils::set_panic_hook();
    let rgb_value: (u8, u8, u8) = serde_wasm_bindgen::from_value(rgb).unwrap();
    let fonts = vec![Font::from_bytes(font_file.to_vec()).expect("Error constructing Font")];

    let layout = Layout::default().v_align(VerticalAlign::Bottom);
    let glyphs: Vec<_> = layout.calculate_glyphs(
        &fonts,
        &SectionGeometry {
            screen_position: (50.0, 550.0),
            bounds: (650.0, 530.0),
        },
        &[SectionText {
            text: text,
            scale: Scale::uniform(font_size as f32),
            font_id: FontId(0),
            color: [0.0, 1.0, 0.0, 1.0],
        }],
    );

    // Create new RGBA image
    let mut image = DynamicImage::new_rgba8(width as u32, height as u32).to_rgba();

    // Draw glyphs
    for glyph in glyphs {
        if let Some(bounding_box) = glyph.0.pixel_bounding_box() {
            glyph.0.draw(|x, y, v| {
                image.put_pixel(
                    // Offset the position by the glyph bounding box
                    x + bounding_box.min.x as u32,
                    y + bounding_box.max.y as u32,
                    Rgba([rgb_value.0, rgb_value.1, rgb_value.2, (v * 255.0) as u8]),
                )
            });
        }
    }

    return image.to_vec();
}

it bottom-aligns to 50/550 correctly, but it somehow stops aligning individual characters on their baseline.

This looks like this: strange-alignment

alexheretic commented 3 years ago

At a glance your offset looks wrong: ~y + bounding_box.max.y as u32,~ y + bounding_box.min.y as u32,

codepunkt commented 3 years ago

Argh. Don't let me near a computer after i had to accept all the cookies 😂

Sorry, you are right. Thanks a lot!

alexheretic commented 3 years ago

Glad it's working for you now! :)