alexheretic / glyph-brush

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

Clarify caching behavior #84

Closed chinedufn closed 4 years ago

chinedufn commented 4 years ago

Alright so I might be looking in the wrong places - but here's what I'm observing.

The Problem

The process_queued method documentation includes the following:

Processes all queued sections, calling texture update logic when necessary

However - I ran process_queued on a text section containing the text Hello World and saw some of the same glyphs repeated.

Screenshot Example

image Note the repeated o and l

The Question

Is it expected behavior for the texture updater to cache the same glyph multiple times? I might have expected a character to be cached once and then re-used.

If I'm understanding correctly the texture is being used to cache the entire section as documented in the process_queued method.

Is there a downside to caching the section's glyph rectangles in memory while only caching each character on the texture only once?

The advantage being that a smaller texture could fit more glyphs.

Thanks and cheers!!


Source Code

Some source code illustrating how I generated the image above.

// This derefs to my GlyphBrush
let mut text_font_res = TextFontResource::new_with_test_font();

text_font_res.resize_texture(512, 512);

let mut font_cache = image::DynamicImage::new_luma8(512, 512);

let text = OwnedSectionText {
    text: "Hello World".to_string(),
    scale: Scale::uniform(30.),
    color: [1., 1., 1., 1.],
    font_id: FontId(0),
};

let section = OwnedVariedSection {
    screen_position: (0., 0.),
    layout: Layout::default_single_line().h_align(HorizontalAlign::Left),
    text: vec![text],
    ..OwnedVariedSection::default()
};

text_font_res.queue(&section);

let brush_action = text_font_res
    .process_queued(
        |rect, pixels| {
            let glyph_width = rect.width();
            let glyph_height = rect.height();
            let pixel_count = glyph_width * glyph_height;

            let mut glyph = DynamicImage::new_luma8(glyph_width, glyph_height);

            for (idx, pixel) in pixels.iter().enumerate() {
                let idx = idx as u32;

                let x = idx % glyph_width;
                let y = idx / glyph_width;
                glyph.put_pixel(x, y, image::Rgba([*pixel, 0, 0, 0]));
            }

            image::imageops::overlay(
                &mut font_cache,
                &glyph,
                rect.min.x as u32,
                rect.min.y as u32,
            );
        },
        |glyph_vertex| {
            TextFontResource::glyph_vertex_to_ui_quad(glyph_vertex, 512, 512, UILayer::Zero)
        },
    )
    .unwrap();

font_cache.save("/tmp/font-cache.png");
alexheretic commented 4 years ago

The gpu cache/draw cache will indeed return multiple versions of the same glyph at different subpixel positions. You can adjust this by setting the position tolerance.

Right now this is required for high quality text as glyph positioning is always exactly as drawn. So if you have high position tolerance it means you only draw a single glyph (at a given scale) but it will potentially be drawn in the wrong subpixel position.

chinedufn commented 4 years ago

Ah thanks! Added that to the docs here https://github.com/chinedufn/glyph-brush/commit/68ff932c88e739009b36d7aace5187e2c4829dd9 in #83