cvkg_render_gpu/
surtr_util.rs1use crate::renderer::SurtrRenderer;
3use cvkg_core::{Rect, Renderer};
4
5impl SurtrRenderer {
6 pub fn load_image_to_heim(&mut self, name: &str, data: &[u8]) {
9 if self.image_uv_registry.contains(name) {
10 log::info!("[Surtr] load_image_to_heim: '{}' already in registry, skipping", name);
11 return;
12 }
13 log::info!("[Surtr] load_image_to_heim: decoding '{}' ({} bytes)", name, data.len());
14 let img_result = image::load_from_memory(data);
15 let img = match img_result {
16 Ok(img) => {
17 log::info!("[Surtr] decode OK: {}x{}", img.width(), img.height());
18 img.to_rgba8()
19 }
20 Err(e) => {
21 log::error!("[Surtr] Failed to load image {} to heim: {}", name, e);
22 return;
23 }
24 };
25 let (width, height) = img.dimensions();
26
27 if let Some((x, y)) = self.heim_packer.pack(width, height) {
29 let tex_w = self.mega_heim_tex.width() as f32;
30 let tex_h = self.mega_heim_tex.height() as f32;
31 let uv_rect = Rect {
32 x: x as f32 / tex_w,
33 y: y as f32 / tex_h,
34 width: width as f32 / tex_w,
35 height: height as f32 / tex_h,
36 };
37
38 self.queue.write_texture(
40 wgpu::TexelCopyTextureInfo {
41 texture: &self.mega_heim_tex,
42 mip_level: 0,
43 origin: wgpu::Origin3d { x, y, z: 0 },
44 aspect: wgpu::TextureAspect::All,
45 },
46 &img,
47 wgpu::TexelCopyBufferLayout {
48 offset: 0,
49 bytes_per_row: Some(4 * width),
50 rows_per_image: Some(height),
51 },
52 wgpu::Extent3d {
53 width,
54 height,
55 depth_or_array_layers: 1,
56 },
57 );
58
59 self.image_uv_registry.put(name.to_string(), uv_rect);
60 self.texture_registry.put(name.to_string(), 0);
62 log::info!("[Surtr] Packed '{}' into Mega-Heim at ({}, {})", name, x, y);
63 log::info!("[Surtr] Registry now contains '{}'", name);
64 } else {
65 log::warn!(
66 "HEIM_FULL: Failed to pack '{}' into Mega-Heim. Falling back to Texture Array.",
67 name
68 );
69 self.load_image(name, data);
70 }
71 }
72
73 pub(crate) fn shape_text_with_stack(
98 &mut self,
99 text: &str,
100 size: f32,
101 ) -> std::sync::Arc<cvkg_runic_text::ShapedText> {
102 let cache_key = (text.to_string(), (size * 100.0) as u32);
103 if let Some(shaped) = self.text.shaped_cache.get(&cache_key) {
104 return shaped.clone();
105 }
106
107 let mut style = cvkg_runic_text::TextStyle::new("Jupiteroid", size);
108 style.fallback_families = vec![
109 "sans-serif".to_string(),
110 "DejaVu Sans".to_string(),
111 "Cantarell".to_string(),
112 "Liberation Sans".to_string(),
113 "Noto Sans".to_string(),
114 "Adwaita Sans".to_string(),
115 "SF Pro".to_string(),
116 "SF Pro Text".to_string(),
117 "Inter".to_string(),
118 "Helvetica Neue".to_string(),
119 "Helvetica".to_string(),
120 "Arial".to_string(),
121 ];
122 style.render_mode = cvkg_runic_text::RenderMode::Grayscale;
123 let spans = vec![cvkg_runic_text::TextSpan::new(text, style)];
124 let shaped = self
125 .text
126 .engine
127 .shape_layout(
128 &spans,
129 None,
130 cvkg_runic_text::TextAlign::Start,
131 cvkg_runic_text::TextOverflow::WordWrap,
132 )
133 .unwrap_or_else(|_| cvkg_runic_text::ShapedText {
134 glyphs: Vec::new(),
135 lines: Vec::new(),
136 width: 0.0,
137 height: 0.0,
138 text: text.to_string(),
139 spans: Vec::new(),
140 has_rtl: false,
141 ascent: 0.0,
142 descent: 0.0,
143 line_gap: 0.0,
144 grapheme_boundaries: vec![],
145 });
146
147 let arc = std::sync::Arc::new(shaped);
148 self.text.shaped_cache.put(cache_key, arc.clone());
149 arc
150 }
151}