imageproc/drawing/
text.rs

1use crate::definitions::{Clamp, Image};
2use crate::drawing::Canvas;
3use conv::ValueInto;
4use image::{GenericImage, ImageBuffer, Pixel};
5use std::f32;
6use std::i32;
7
8use crate::pixelops::weighted_sum;
9use rusttype::{point, Font, PositionedGlyph, Rect, Scale};
10use std::cmp::max;
11
12fn layout_glyphs(
13    scale: Scale,
14    font: &Font,
15    text: &str,
16    mut f: impl FnMut(PositionedGlyph, Rect<i32>),
17) -> (i32, i32) {
18    let v_metrics = font.v_metrics(scale);
19
20    let (mut w, mut h) = (0, 0);
21
22    for g in font.layout(text, scale, point(0.0, v_metrics.ascent)) {
23        if let Some(bb) = g.pixel_bounding_box() {
24            w = max(w, bb.max.x);
25            h = max(h, bb.max.y);
26            f(g, bb);
27        }
28    }
29
30    (w, h)
31}
32
33/// Get the width and height of the given text, rendered with the given font and scale.
34///
35/// Note that this function *does not* support newlines, you must do this manually.
36pub fn text_size(scale: Scale, font: &Font, text: &str) -> (i32, i32) {
37    layout_glyphs(scale, font, text, |_, _| {})
38}
39
40/// Draws colored text on an image in place.
41///
42/// `scale` is augmented font scaling on both the x and y axis (in pixels).
43///
44/// Note that this function *does not* support newlines, you must do this manually.
45pub fn draw_text_mut<'a, C>(
46    canvas: &'a mut C,
47    color: C::Pixel,
48    x: i32,
49    y: i32,
50    scale: Scale,
51    font: &'a Font<'a>,
52    text: &'a str,
53) where
54    C: Canvas,
55    <C::Pixel as Pixel>::Subpixel: ValueInto<f32> + Clamp<f32>,
56{
57    let image_width = canvas.width() as i32;
58    let image_height = canvas.height() as i32;
59
60    layout_glyphs(scale, font, text, |g, bb| {
61        g.draw(|gx, gy, gv| {
62            let gx = gx as i32 + bb.min.x;
63            let gy = gy as i32 + bb.min.y;
64
65            let image_x = gx + x;
66            let image_y = gy + y;
67
68            if (0..image_width).contains(&image_x) && (0..image_height).contains(&image_y) {
69                let pixel = canvas.get_pixel(image_x as u32, image_y as u32);
70                let weighted_color = weighted_sum(pixel, color, 1.0 - gv, gv);
71                canvas.draw_pixel(image_x as u32, image_y as u32, weighted_color);
72            }
73        })
74    });
75}
76
77/// Draws colored text on a new copy of an image.
78///
79/// `scale` is augmented font scaling on both the x and y axis (in pixels).
80///
81/// Note that this function *does not* support newlines, you must do this manually.
82#[must_use = "the function does not modify the original image"]
83pub fn draw_text<'a, I>(
84    image: &'a I,
85    color: I::Pixel,
86    x: i32,
87    y: i32,
88    scale: Scale,
89    font: &'a Font<'a>,
90    text: &'a str,
91) -> Image<I::Pixel>
92where
93    I: GenericImage,
94    <I::Pixel as Pixel>::Subpixel: ValueInto<f32> + Clamp<f32>,
95{
96    let mut out = ImageBuffer::new(image.width(), image.height());
97    out.copy_from(image, 0, 0).unwrap();
98    draw_text_mut(&mut out, color, x, y, scale, font, text);
99    out
100}