I'm trying to use the rusttype crate to render text. So far it's been fantastic, but I am running into a wall when trying to correctly position individual glyphs.
I am rendering text by rendering each glyph to an RgbaImage (from the image crate, like in the rusttype image example) and rendering it to a quad mesh.
pub struct Glyph {
pub image: image::RgbaImage,
pub glyph: rusttype::PositionedGlyph<'static>,
pub vertical_offset: f32,
}
// &self.font is a rusttype::Font type
pub fn draw_glyph(&self, ch: char, font_size: f32) -> Option<Glyph> {
// Set size
let scale = rusttype::Scale::uniform(font_size);
let v_metrics = self.font.v_metrics(scale);
// Position and scale glyph
let offset = rusttype::point(0.0, 0.0 + v_metrics.ascent);
let glyph = self.font.glyph(ch).scaled(scale).positioned(offset);
// Get glyph dimensions
let bounds = glyph.pixel_bounding_box()?;
let glyph_height = (v_metrics.ascent - v_metrics.descent).ceil() as u32;
let glyph_width = (bounds.max.x - bounds.min.x) as u32;
// Try to align glyphs on a baseline
let offset_y = bounds.height() as f32 - v_metrics.ascent;
// Generate image
let mut image =
image::ImageBuffer::from_pixel(glyph_width, glyph_height, image::Rgba([200; 4]));
glyph.draw(|x, y, v| {
image.put_pixel(x, y, image::Rgba([0 + (v * 255.0) as u8; 4]));
});
Some(Glyph {
glyph: glyph,
image: image,
vertical_offset: offset_y,
})
}
However, without the vertical_offset calculation, I receive the following text:
As you can see, the characters are all over the place.
I need to take into account the ascent and descent size of the font. However, this is where I run into issues.
I can compensate for the ascent using the following calculation:
let bounds = glyph.pixel_bounding_box()?;
let offset_y = bounds.height() as f32 - v_metrics.ascent;
And then shifting the quad meshes downwards by offset_y.
This improves the output when there are no descenders like "g" in the text:
But does not help when there are descending characters involved:
I cannot figure out how the rusttype examples handle this. They use the Font::layout method, but this doesn't have any special vertical align code.
I'm definitely doing something wrong. Please help!
Try compensating for the descent as well:
let offset_y = bounds.height() as f32 - v_metrics.ascent + v_metrics.descent;
Related
what is Rust's best way to display and dynamically generated image?
I have
let image : ImageBuffer<Rgb<u8>, Vec<u8>> = ...
How do I show it in a GUI?
I tried with fltk, but didn't find anything usable for ImageBuffer.
use fltk::...
let app = App::default().with_scheme(AppScheme::Gleam);
let mut wind = Window::new(100, 100, 400, 300, "Hello from rust");
wind.set_image(Some(image));
won't work because:
the trait `ImageExt` is not implemented for image::buffer_::ImageBuffer<image::color::Rgb<u8>, Vec<u8>>`
Do I have to encode my ImageBuffer image to some specific format, so that the GUI library can decode it back and display it afterward?
Or maybe I should use some other UI library then fltk?
You can create an fltk RgbImage from an ImageBuffer (from the image crate) using:
let rgbimage = RgbImage::new(&image.to_rgb8(), image.width() as i32, image.height() as i32, ColorDepth::Rgb8).unwrap();
I was able to solve it with the help from mo_al_'s answear. I just had to use into_raw(), because to_rgb8() or into_rgb8() were not implemented for type.
let w = image.width() as i32;
let h = image.height() as i32;
let image_rgb = RgbImage::new(&domain_image.into_raw(), w, h, ColorDepth::Rgb8).unwrap();
...
let mut frame = Frame::new(0, 0, w, h, "");
frame.set_image(Some(image_rgb));
So I have been using the Convex Shape struct to create triangles using the rust bindings for sfml.
My code is as follows:
let mut shape2 = ConvexShape::new(3);
shape2.set_point(0, Vector2f::new(0.0, 0.0));
shape2.set_point(1, Vector2f::new(0.0, 100.0));
shape2.set_point(2, Vector2f::new(100.0, 0.0));
for point in shape2.points() {
println!("x:{} y:{}", point.x, point.y);
}
However, when looping through the points for the triangles sometimes I would get output like this:
x:0 y:100
x:100 y:0
x:434583.44 y:-0.000000000000000000000000000000000000000047353
I am not sure what is causing this problem; however, I assume it has to do with the f32 overflowing.
Are there any fixes for this problem or am I doing something wrong here?
This appears to be a bug in the iterator returned by .points(). You can resort to fetching directly by index instead (accessed via the Shape trait):
use sfml::graphics::{ConvexShape, Shape};
use sfml::system::Vector2f;
...
let mut shape2 = ConvexShape::new(3);
shape2.set_point(0, Vector2f::new(0.0, 0.0));
shape2.set_point(1, Vector2f::new(0.0, 100.0));
shape2.set_point(2, Vector2f::new(100.0, 0.0));
for i in 0..shape2.point_count() {
let point = shape2.point(i);
println!("x:{} y:{}", point.x, point.y);
}
Using wgpu-rs, I'm trying to get a 3x3 cgmath matrix into a shader (compiled using glsl-to-spirv). However, the resulting mat3 in the shader has incorrect data. When I replace the mat3 and Matrix3 with mat4 and Matrix4, everything works fine and the matrix has correct data.
Vertex Shader:
layout(set=0, binding=0) uniform Uniforms {
mat3 camera_transform;
};
Render Loop:
let mut encoder = self.device.create_command_encoder(
&wgpu::CommandEncoderDescriptor {
label: Some("update encoder"),
},
);
let staging_buffer = self.device.create_buffer_with_data(
bytemuck::cast_slice(&[self.uniforms]),
wgpu::BufferUsage::COPY_SRC,
);
encoder.copy_buffer_to_buffer(&staging_buffer, 0, &self.uniform_buffer, 0, std::mem::size_of::<Uniforms>() as wgpu::BufferAddress);
self.queue.submit(&[encoder.finish()]);
// ...
render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
Uniforms:
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Uniforms {
pub camera_transform: Matrix3::<f32>,
}
unsafe impl bytemuck::Pod for Uniforms {}
unsafe impl bytemuck::Zeroable for Uniforms {}
impl Uniforms {
pub fn new() -> Self {
Self {
camera_transform: Matrix3::identity(),
}
}
}
This is an open issue in wgpu-rs. Indeed the simplest workaround may be to make your mat3 into a mat4 until it is resolved.
The problem seems to be a mistake of alignment in generating SPIR-V. The actual alignment is:
If the member is a scalar consuming N basic machine units, the base alignment is N.
If the member is a two- or four-component vector with components consuming N basic machine units, the base alignment is 2N or 4N,
respectively.
If the member is a three-component vector with components consuming N basic machine units, the base alignment is 4N.
If the member is an array of scalars or vectors, the base alignment and array stride are set to match the base alignment of a single array
element, according to rules (1), (2), and (3), and rounded up to the
base alignment of a vec4. The array may have padding at the end; the
base offset of the member following the array is rounded up to the
next multiple of the base alignment.
You are in case 4. Having a mat4 should leave no extra padding on the end and not give any possibility for misalignment issues.
I need to take an image and get a list of RGB byte values. I am using the image crate. This is what I have:
extern crate image;
fn main() {
let im = image::open("wall.jpg").unwrap().to_rgb();
let data: Vec<[u8; 3]> = im.pixels().flat_map(|p| vec![p.data]).collect();
let rgb: Vec<&u8> = data.iter().flat_map(|p| p.iter()).collect();
println!("First Pixel: {} {} {}", rgb[0], rgb[1], rgb[2]);
}
This seems pretty ugly. I have to introduce an intermediate variable and I get a vector of pointers to the values I really need, so before I can do something else, I would have to map over it again to get the actual values.
All I want is a vector of u8. How do I get that?
As of 0.23.12, to_rgb has been deprecated use DynamicImage::to_rgb8 or DynamicImage::to_bytes instead:
let im = image::open("wall.jpg").unwrap().to_rgb8();
let rgb: Vec<u8> = im.into_raw();
// Alternatively
let bytes = image::open("wall.jpg").unwrap().to_bytes();
Prior to 0.23.12, if you just want the raw data itself, you can just call DynamicImage::raw_pixels:
let im = image::open("wall.jpg").unwrap().to_rgb();
let rgb: Vec<u8> = im.raw_pixels();
If all you're actually interested in is the first pixel though, I'd recommend calling GenericImage::get_pixel:
let im = image::open("wall.jpg").unwrap();
let first_pixel = im.get_pixel(0, 0);
Which you can then turn into a [u8; 3] array of RGB data:
let rgb = first_pixel.to_rbg();
println!("First Pixel: {} {} {}", rgb.data[0], rgb.data[1], rgb.data[2]);
I just started learning Rust and I really like to learn by building something 'real'. So I read through the Book, installed Rust, played around with the language and got cargo run working.
I then decided to try and read an image from disk and convert that image into a Vec. In this case, I want to detect the color of the pixels and store that somehow.
I broke it into multiple parts to learn Rust and its syntax:
Starting with:
Read an image from disk
Get the pixels of an image
Show colour code
This led me to the following code using the image crate:
extern crate image;
use std::path::Path;
use image::GenericImage;
fn main() {
let img = image::open(&Path::new("src/maze.gif")).unwrap();
let pixels = img.pixels();
for e in pixels {
let (_, _, color) = e;
println!("Pixel colour {:?}", color);
}
println!("Dimensions {:?}", img.dimensions());
}
So proud as I am, I see some information popping up:
* snip *
Pixel colour Rgba { data: [255, 255, 255, 255] }
Pixel colour Rgba { data: [0, 0, 0, 255] }
Pixel colour Rgba { data: [255, 255, 255, 255] }
* snip *
Now I want to store for each line of the image its pixel information. I would like to have a Vec (is that correct?) with the info. A PHP array would look like this:
$a = [
0 => [ Color, Color, Color, Color],
1 => [ Color, Color, Color, Color]
];
Therefore my assumption is to use read_scanline. This is where reading AND understanding the documentation fails me completely.
What I think I need to do is:
Get the decoder for the current opened image
Call read_scanline on that decoder
Iterate over the results
But how?!
The code reads this:
read_scanline(&mut self, buf: &mut [u8]) -> ImageResult<u32>
I break this down as follows:
First argument, an ImageDecoder object.
Second argument a mutable 8 bit value.
It returns an ImageResult object.
So I tried adapting the code slightly:
extern crate image;
use std::path::Path;
use image::GenericImage;
use image::ImageDecoder;
fn main() {
let img = image::open(&Path::new("src/maze.gif")).unwrap();
let pixels = img.pixels();
let something: &mut [u8];
let result = image::ImageDecoder::read_scanline(img, something);
for e in pixels {
let (_, _, color) = e;
println!("Pixel colour {:?}", color);
}
println!("Dimensions {:?}", img.dimensions());
}
Which, as you might have guessed fails miserably.
error: mismatched types:
expected `&mut _`,
found `image::dynimage::DynamicImage`
(expected &-ptr,
found enum `image::dynimage::DynamicImage`) [E0308]
src/main.rs:13 let result = image::ImageDecoder::read_scanline(img, something);
Obviously this is due to the fact I didn't pass an ImageDecoder object. But how can I? How should I read and understand the documentation. I think it is due to not understanding &mut self. I also do not understand how I should initiate this.
I hope someone can explain what it is I am missing and point me into the right direction.
Note I'm having trouble actually opening a GIF as each line says it is 0 bytes, so there's the possibility I've missed something important... I'll use a JPEG to demonstrate instead.
ImageDecoder is a trait. You need to use a concrete implementation of the trait. The documentation lists all known implementors of the trait, one of which is image::gif::Decoder, another is image::jpeg::JPEGDecoder.
read_scanline accepts a mutable self, which means that you normally will call it with method syntax: object.method(arg1). The first non-self argument is a mutable slice of bytes. The documentation states:
Reads one row from the image into buf and returns the row index
So the pixel data will be stored in the buffer. The next trick is to figure out how many bytes we need for a row and how many rows there are. ImageDecoder::row_len and ImageDecoder::dimensions address that respectively. Put together, we get something like this:
extern crate image;
use std::fs::File;
use image::jpeg;
use image::{GenericImage, ImageDecoder};
use image::{ColorType, Rgb, Pixel};
fn main() {
let f = File::open("/tmp/cat.jpg").unwrap();
let mut decoder = jpeg::JPEGDecoder::new(f);
let (width, height) = decoder.dimensions().unwrap();
let row_len = decoder.row_len().unwrap();
println!("{} x {}, {}", width, height, row_len);
let rows: Vec<_> = (0..height).map(|_| {
let mut row = vec![0; row_len];
decoder.read_scanline(&mut row).unwrap();
row
}).collect();
}
This loads the JPEG, row-by-row, into a Vec<Vec<u8>> - a vector of vectors of bytes.
To convert the raw data into pixels, we can use Pixel::from_slice, another trait method. In this case, I only handle one type of pixel - 8-bit RGB:
let colortype = decoder.colortype().unwrap();
assert_eq!(colortype, ColorType::RGB(8)); // Others are left to you!
let row_colors: Vec<Vec<_>> = rows.iter().map(|r| {
r.chunks(3).map(|p| Rgb::from_slice(p)).collect()
}).collect();
I'm not in love with this because of the hard-coded 3. There should be some way of knowing that this pixel type only takes 3 bytes, but I'm not seeing anything obvious.