Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spanish letters misaligned with font sizes smaller than ~22 #22

Closed
john01dav opened this issue Jun 21, 2020 · 8 comments
Closed

Spanish letters misaligned with font sizes smaller than ~22 #22

john01dav opened this issue Jun 21, 2020 · 8 comments

Comments

@john01dav
Copy link

john01dav commented Jun 21, 2020

Spanish uses some accented letters that don't exist in English. These include "í" "ó" and "á". Consider the sample string:

¿Cómo estás?

As can be seen when Github+Firefox render this string, the vertical alignment of the accented letters is fine. Yet, when I render this same string using fontdue, the letters are slightly lower than they should be, but only with smaller font sizes.

Font size 12:
image

Font size 17:
image

Font size 22:
image

This is the code that handles the rendering:

fn draw_string(string: &str, x: i32, y: i32, font_size: f32, buffer: &mut Bitmap){
    let font = include_bytes!("roboto/Roboto-Regular.ttf") as &[u8];
    let mut font = Font::from_bytes(font, FontSettings::default()).unwrap();

    let xf = x as f32;
    let yf = y as f32;

    let mut x_offset: f32 = 0.0;
    for c in string.chars(){
        let (metrics, bitmap) = font.rasterize(c, font_size);
        let xmin = metrics.bounds.xmin;
        let ymax = metrics.bounds.ymax;

        for cx in 0..metrics.width{
            for cy in 0..metrics.height{
                let intensity = bitmap[cy*metrics.width+cx];
                let x_pos = xf+x_offset+(cx as f32)+xmin;
                let y_pos = yf+(cy as f32)- ymax;
                if x_pos >= 0.0 && y_pos >= 0.0 {
                    buffer.set_pixel(x_pos as usize, y_pos as usize, Color::new(intensity, intensity, intensity));
                }
            }
        }
        x_offset += metrics.advance_width;
    }
}
@MarimeGui
Copy link
Contributor

Have you tried rounding your x_pos and y_pos variables before converting them to usize ?

@mooman219
Copy link
Owner

mooman219 commented Jun 25, 2020

I'm aware of the positioning challenges for smaller font sizes. Adding a proper layout utility in fontdue will happen before 1.0 and I'll address this issue.

@mooman219
Copy link
Owner

Turns out the issue was I'm aligning to the wrong baseline during rasterization. I'll have a fix out soonish, along with some more subpixel positioning options for people rolling their own layout before Fontdue's is finished.

@mooman219
Copy link
Owner

image

Yeah I have it fixed locally. There's some optimization work I need to do because it added 30ns to raster to calculate the alignment.

@mooman219
Copy link
Owner

I just pushed it,

@john01dav
Copy link
Author

@mooman219 I'm having some trouble using the new fix. Would you be willing to look at my rendering code briefly? I'm not a font expert, so I suspect that there's some error in my code wrt how I'm using your library. Thank you!

Output:
image

Code (the second function, render, is the important bit):

use fontdue::{Metrics, Font, FontSettings};
use crate::error::Result;
use crate::bitmap::Bitmap;
use crate::bitmap::Color;

pub struct TextData{
    font: Font,
    chars: Vec<CharData>,
    total_width: usize,
}

struct CharData{
    metrics: Metrics,
    bitmap: Vec<u8>,
}

impl TextData{

    pub fn rasterize_string(string: &str, font_size: f32) -> Result<TextData>{
        let font = include_bytes!("roboto/Roboto-Regular.ttf") as &[u8];
        let font = Font::from_bytes(font, FontSettings::default()).unwrap();

        let mut text_data = TextData{
            font, chars: Vec::new(), total_width: 0
        };

        for c in string.chars(){
            let (metrics, bitmap) = text_data.font.rasterize(c, font_size, 0.0);
            text_data.chars.push(CharData{
                metrics, bitmap
            });
            text_data.total_width += metrics.advance_width as usize;
        }

        Ok(text_data)
    }

    pub fn render(&self, bitmap: &mut Bitmap, x: usize, y: usize){
        let xf = x as f32;
        let yf = y as f32;
        let mut x_offset: f32 = 0.0;
        for char_data in &self.chars{
            let xmin = char_data.metrics.bounds.xmin;
            let ymax = char_data.metrics.bounds.ymax;

            for cx in 0..char_data.metrics.width{
                for cy in 0..char_data.metrics.height{
                    let intensity = char_data.bitmap[cy*char_data.metrics.width+cx];
                    let x_pos = xf+x_offset+(cx as f32)+xmin;
                    let y_pos = yf+(cy as f32)-ymax;
                    if x_pos >= 0.0 && y_pos >= 0.0 {
                        bitmap.set_pixel(x_pos as usize, y_pos as usize, Color::new(intensity, intensity, intensity));
                    }
                }
            }
            x_offset += char_data.metrics.advance_width;
        }
    }

}

@mooman219
Copy link
Owner

At a glance, I believe it's to do with not flooring your y positions at the right spot, but the root of the issue is that proper layout is actually just hard (several hundred lines and some state management). I'm implementing a proper layout utility for the next revision so Fontdue will handle it for you.

@john01dav
Copy link
Author

Thanks for your help! I have a few things to say/ask in response to what you said:

  • At what point should that flooring take place? In my code as it is, everything is kept as f32 until the last moment when it's rendered, which would seem to allow for maximum precision. There are many other places however, and none seem any better to me.
  • For layout, I'd like to communicate that I do need the ability to know where individual characters begin and end in my use case, so whatever you implement it would be very useful if that feature were available. I also need to allow things like word wrapping, etc.
  • Can you recommend any crates that handle layout that I can use right now?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants