Skip to main content

yog_gfx/
draw2d.rs

1//! 2D drawing helpers — thin wrappers over the `draw2d_*` ABI functions.
2//!
3//! These use Minecraft's own rendering infrastructure (text renderer, texture
4//! manager) and are therefore only valid during `on_hud_render`.
5
6use yog_abi::YogStr;
7use yog_graphics::{Color, Rect, Vec2};
8
9use crate::GfxContext;
10
11/// 2D drawing helpers, valid only within `on_hud_render`.
12///
13/// Obtain via [`GfxContext::draw2d`].
14/// How text is drawn: its font (empty: the default), times its size (1 =
15/// the font's own), its colour, whether it has a shadow.
16#[derive(Copy, Clone, Debug, PartialEq)]
17pub struct TextStyle<'a> {
18    pub font: &'a str,
19    pub scale: f32,
20    pub color: Color,
21    pub shadow: bool,
22}
23
24pub struct Draw2D<'ctx>(&'ctx GfxContext);
25
26impl<'ctx> Draw2D<'ctx> {
27    pub(crate) fn new(ctx: &'ctx GfxContext) -> Self {
28        Self(ctx)
29    }
30
31    /// Draws `text` at GUI position `at`, a drop shadow under it if `shadow`.
32    pub fn text(&self, text: &str, at: Vec2, color: Color, shadow: bool) {
33        let a = self.0.api();
34        unsafe { (a.draw2d_text)(YogStr::from_str(text), at.x, at.y, color.0, shadow) }
35    }
36
37    /// [`Self::text`] in the font `font` (empty: the default one).
38    pub fn text_font(&self, text: &str, font: &str, at: Vec2, color: Color, shadow: bool) {
39        let a = self.0.api();
40        unsafe {
41            (a.draw2d_text_font)(
42                YogStr::from_str(text),
43                YogStr::from_str(font),
44                at.x,
45                at.y,
46                color.0,
47                shadow,
48            )
49        }
50    }
51
52    /// Width in GUI pixels of `text` in `font` (empty = default), measured by
53    /// the host's text renderer.
54    pub fn text_width(&self, text: &str, font: &str) -> f32 {
55        let a = self.0.api();
56        unsafe { (a.text_width)(YogStr::from_str(text), YogStr::from_str(font)) }
57    }
58
59    /// Fills `r` with a flat color.
60    pub fn rect(&self, r: Rect, color: Color) {
61        let a = self.0.api();
62        unsafe { (a.draw2d_rect)(r.x, r.y, r.right(), r.bottom(), color.0) }
63    }
64
65    /// Fills `r` with a vertical gradient, `top` to `bottom`.
66    pub fn gradient(&self, r: Rect, top: Color, bottom: Color) {
67        let a = self.0.api();
68        unsafe { (a.draw2d_gradient)(r.x, r.y, r.right(), r.bottom(), top.0, bottom.0) }
69    }
70
71    /// Renders an item stack (3D block models included) via Minecraft's item
72    /// renderer — like inventory slots or Patchouli's item icons: item `id`
73    /// (`minecraft:crafting_table`) at `at`, `size` GUI pixels big (16 = an
74    /// inventory icon).
75    pub fn item(&self, id: &str, at: Vec2, size: f32) {
76        let a = self.0.api();
77        unsafe { (a.draw2d_item)(YogStr::from_str(id), at.x, at.y, size) }
78    }
79
80    /// Draws the image `source` — a Yog image source such as `"item:apple"`,
81    /// `"block:stone"`, `"sprite:button"` or `"mymod:gui/logo"` (see
82    /// [`crate::image`]) — scaled into `r`. The host resolves it for its
83    /// Minecraft version.
84    pub fn image(&self, source: &str, r: Rect) {
85        self.image_region(source, r, None, Color::WHITE);
86    }
87
88    /// [`Self::image`] drawing only `region` of it (in the image's own
89    /// pixels; `None` = all of it) multiplied by `tint` (white = none).
90    /// Items and sprites are always drawn whole, and items ignore the tint.
91    pub fn image_region(&self, source: &str, r: Rect, region: Option<Rect>, tint: Color) {
92        let a = self.0.api();
93        let src = region.unwrap_or_default();
94        unsafe {
95            (a.draw2d_image)(
96                YogStr::from_str(source),
97                r.x,
98                r.y,
99                r.w,
100                r.h,
101                src.x,
102                src.y,
103                src.w,
104                src.h,
105                tint.0,
106            )
107        }
108    }
109
110    /// Draws the SVG document `data` scaled into `r` (sharp at any GUI
111    /// scale: the host rasterizes it at the screen's resolution). An `.svg`
112    /// file is an image source like any other — `mymod:gui/logo.svg` — for
113    /// [`Self::image`].
114    pub fn svg(&self, data: &str, r: Rect) {
115        let a = self.0.api();
116        unsafe { (a.draw2d_svg)(YogStr::from_str(data), r.x, r.y, r.w, r.h) }
117    }
118
119    /// `text` at `at` in `style`.
120    pub fn text_scaled(&self, text: &str, at: Vec2, style: &TextStyle<'_>) {
121        let a = self.0.api();
122        unsafe {
123            (a.draw2d_text_scaled)(
124                YogStr::from_str(text),
125                YogStr::from_str(style.font),
126                at.x,
127                at.y,
128                style.scale,
129                style.color.0,
130                style.shadow,
131            )
132        }
133    }
134
135    /// Draws blocks in 3D, as big as fits in `r` — a multiblock structure,
136    /// a block on display: each is `((x, y, z), state)` with a full block
137    /// state (`minecraft:furnace[facing=north]`), turned `yaw` degrees about
138    /// the vertical and tilted `pitch` degrees toward the viewer (30 looks
139    /// down on it like Patchouli does).
140    pub fn blocks(&self, blocks: &[((i32, i32, i32), &str)], r: Rect, yaw: f32, pitch: f32) {
141        let mut list = String::new();
142        for ((bx, by, bz), state) in blocks {
143            use std::fmt::Write as _;
144            let _ = writeln!(list, "{bx} {by} {bz} {state}");
145        }
146        let a = self.0.api();
147        unsafe { (a.draw2d_blocks)(YogStr::from_str(&list), r.x, r.y, r.w, r.h, yaw, pitch) }
148    }
149
150    /// Draws a living entity of type `entity` (`minecraft:zombie`,
151    /// `mymod:wisp`) standing in `r`, as big as fits, turned `yaw` degrees
152    /// (0 = facing the viewer) and tilted `pitch` degrees.
153    pub fn entity(&self, entity: &str, r: Rect, yaw: f32, pitch: f32) {
154        let a = self.0.api();
155        unsafe { (a.draw2d_entity)(YogStr::from_str(entity), r.x, r.y, r.w, r.h, yaw, pitch) }
156    }
157
158    /// Natural size in GUI pixels of the image `source` (`None` if the host
159    /// can't resolve it).
160    pub fn image_size(&self, source: &str) -> Option<Vec2> {
161        image_size_with(self.0.image_size_fn(), source)
162    }
163}
164
165/// Calls an [`ImageSizeFn`](crate::ImageSizeFn).
166pub fn image_size_with(f: crate::ImageSizeFn, source: &str) -> Option<Vec2> {
167    let (mut w, mut h) = (0.0f32, 0.0f32);
168    unsafe { f(YogStr::from_str(source), &mut w, &mut h) }.then_some(Vec2::new(w, h))
169}