1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
use crate::*;

use riddle_font::ImgFont;
use riddle_math::{Rect, SpacialNumericConversion, Vector2};

/// An efficient [`riddle_font::ImgFont`] renderer.
///
/// Use [`crate::SpriteRenderArgs`] for access to all supported paramters when rendering
/// sprites.
///
/// # Example
///
/// ```no_run
/// # use riddle::{common::Color, image::*, platform::*, renderer::*, math::*, font::*, *};
/// # fn main() -> Result<(), RiddleError> {
/// # let rdl =  RiddleLib::new()?;
/// # let window = WindowBuilder::new().build(rdl.context())?;
/// let renderer = Renderer::new_from_window(&window)?;
///
/// // Load a TTFont, generate an ImgFont, and then construct the SpriteFont with it
/// let img_font: ImgFont = {
///     let font_bytes = include_bytes!("../../example_assets/Roboto-Regular.ttf");
///     let ttfont = TTFont::load(&font_bytes[..])?;
///     ImgFontGenerator::new("abcdefghijklmnopqrstuvwxyz ", 32).generate(&ttfont)?
/// };
/// let sprite_font = SpriteFont::new(&renderer, img_font)?;
///
/// // Render the sprite at the top left corner of the screen
/// renderer.render(|render_ctx| {
///     render_ctx.clear(Color::WHITE);
///     sprite_font.render(
///         render_ctx,
///         &SpriteRenderArgs::new([0.0, 0.0]).with_color(Color::BLACK),
///         "hello world",
///     )
/// })?;
/// # Ok(()) }
/// ```
pub struct SpriteFont<R: CommonRenderer> {
	sprite: R::Sprite,
	font: ImgFont,
}

impl<R: CommonRenderer> SpriteFont<R> {
	/// Build a SpriteFont from the ImgFont given. The ImgFont's image will get loaded in to a
	/// texture, and its glyph information will be used for layout.
	pub fn new(renderer: &R, font: ImgFont) -> Result<Self> {
		let sprite = R::Sprite::new_from_image(
			renderer,
			font.image(),
			&SpriteInitArgs {
				min_filter: FilterMode::Linear,
				mag_filter: FilterMode::Linear,
			},
		)?;

		Ok(Self { sprite, font })
	}

	pub fn render<Ctx: RenderContext<R> + ?Sized>(
		&self,
		render_ctx: &mut Ctx,
		render_args: &SpriteRenderArgs,
		text: &str,
	) -> std::result::Result<(), RendererError> {
		let mut parts: Vec<(Rect<f32>, Vector2<f32>)> = Vec::with_capacity(text.len());
		self.font.layout(text, |_, rect, location| {
			parts.push((rect.clone().convert(), location.convert()));
		});
		self.sprite
			.render_regions(render_ctx, render_args, &parts[..])
	}
}