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}