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