From 3bbe5ebcdb2a5aa6810dd05576c6ec031efdc1c7 Mon Sep 17 00:00:00 2001 From: Tengu712 Date: Wed, 7 Oct 2026 00:57:01 +0900 Subject: render image --- src/graphics/command.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++----- src/graphics/font.rs | 10 +++++----- src/graphics/image.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 src/graphics/image.rs (limited to 'src/graphics') diff --git a/src/graphics/command.rs b/src/graphics/command.rs index f0330cb..6c0b036 100644 --- a/src/graphics/command.rs +++ b/src/graphics/command.rs @@ -1,8 +1,10 @@ use super::{ InstanceData, font::{self, CharAtlas}, + image::ImageId, + texture::TextureWrapper, }; -use glam::Vec2; +use glam::{Mat2, Vec2}; use wgpu::Queue; pub enum HorizontalAlign { @@ -25,8 +27,18 @@ pub struct Text { pub v_align: VerticalAlign, } +pub struct Image { + pub id: ImageId, + pub origin: Vec2, + pub rotation: f32, + pub scale: Vec2, + pub uv_offset: Vec2, + pub uv_size: Vec2, +} + pub enum RenderCommand { Text(Text), + Image(Image), } impl RenderCommand { @@ -37,6 +49,7 @@ impl RenderCommand { /// * scale_factor - 画面拡大率 /// * queue - queue /// * char_atlas - 文字テクスチャアトラス + /// * images - 画像テクスチャ /// * buf - インスタンスバッファ pub fn build( &self, @@ -45,6 +58,7 @@ impl RenderCommand { scale_factor: f32, queue: &Queue, char_atlas: &mut CharAtlas, + images: &[TextureWrapper], buf: &mut Vec, ) { match self { @@ -57,6 +71,9 @@ impl RenderCommand { char_atlas, buf, ), + RenderCommand::Image(image) => { + build_image(image, screen_width, screen_height, images, buf) + } } } } @@ -108,12 +125,18 @@ fn build_text( let center_x = x + ci.x_offset + ci.width / 2.0; let center_y = y + ci.y_offset + ci.height / 2.0; buf.push(InstanceData { - position: Vec2::new( + transform: Mat2::from_diagonal(Vec2::new( + ci.width / screen_width * 2.0, + -ci.height / screen_height * 2.0, + )), + translation: Vec2::new( center_x / screen_width * 2.0 - 1.0, 1.0 - center_y / screen_height * 2.0, ), - scale: Vec2::new(ci.width / screen_width, ci.height / screen_height), - uv: ci.uv, + uv_offset: ci.uv_offset, + uv_size: ci.uv_size, + tex_id: u32::MAX, + _pad: 0, }); ox += font::h_advance(scale, ci.glyph_id); } @@ -128,9 +151,29 @@ fn build_text( let left = (left * scale_factor).round() / scale_factor; for instance in &mut buf[start..] { - instance.position.x += left / screen_width * 2.0; + instance.translation.x += left / screen_width * 2.0; } baseline += line_height; } } + +fn build_image( + image: &Image, + screen_width: f32, + screen_height: f32, + images: &[TextureWrapper], + buf: &mut Vec, +) { + let texture = &images[image.id as usize]; + let size = image.uv_size * Vec2::new(texture.width as f32, texture.height as f32) * image.scale; + let ndc = Vec2::new(2.0 / screen_width, -2.0 / screen_height); + buf.push(InstanceData { + transform: Mat2::from_diagonal(ndc) * Mat2::from_scale_angle(size, image.rotation), + translation: image.origin * ndc + Vec2::new(-1.0, 1.0), + uv_offset: image.uv_offset, + uv_size: image.uv_size, + tex_id: image.id as u32, + _pad: 0, + }); +} diff --git a/src/graphics/font.rs b/src/graphics/font.rs index c20e691..88163cc 100644 --- a/src/graphics/font.rs +++ b/src/graphics/font.rs @@ -1,7 +1,7 @@ use super::texture::TextureWrapper; use ab_glyph::*; use etagere::{AllocId, AtlasAllocator}; -use glam::Vec4; +use glam::Vec2; use lru::LruCache; use std::sync::LazyLock; use ttf_parser::{ @@ -103,7 +103,8 @@ pub struct CharInfo { pub height: f32, pub x_offset: f32, pub y_offset: f32, - pub uv: Vec4, + pub uv_offset: Vec2, + pub uv_size: Vec2, } pub struct CharAtlas { @@ -236,12 +237,11 @@ impl CharAtlas { height: height / self.scale_factor, x_offset: rasterized.x_offset / self.scale_factor, y_offset: rasterized.y_offset / self.scale_factor, - uv: Vec4::new( + uv_offset: Vec2::new( (allocated.rectangle.min.x as f32 + MARGIN as f32) / self.width, (allocated.rectangle.min.y as f32 + MARGIN as f32) / self.height, - width / self.width, - height / self.height, ), + uv_size: Vec2::new(width / self.width, height / self.height), }; self.registereds.push((c, scale.to_bits()), info); } diff --git a/src/graphics/image.rs b/src/graphics/image.rs new file mode 100644 index 0000000..26833c0 --- /dev/null +++ b/src/graphics/image.rs @@ -0,0 +1,51 @@ +// TODO: このファイルは好きな設計ではないので破壊する。 + +use super::texture::TextureWrapper; +use png::{BitDepth, ColorType, Decoder}; +use std::io::Cursor; +use wgpu::{Device, Queue, TextureFormat, TextureUsages}; + +#[derive(Clone, Copy)] +pub enum ImageId { + Image, +} + +static IMAGE_DATAS: [&[u8]; 1] = [include_bytes!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/assets/image.png" +))]; + +pub fn create_textures(device: &Device, queue: &Queue) -> Vec { + IMAGE_DATAS + .iter() + .map(|data| create_texture(device, queue, data)) + .collect() +} + +fn create_texture(device: &Device, queue: &Queue, data: &[u8]) -> TextureWrapper { + let mut reader = Decoder::new(Cursor::new(data)) + .read_info() + .expect("fatal: failed to load image"); + let mut buf = vec![ + 0x00; + reader + .output_buffer_size() + .expect("fatal: image is too large") + ]; + let info = reader + .next_frame(&mut buf) + .expect("fatal: failed to decode image"); + if info.color_type != ColorType::Rgba || info.bit_depth != BitDepth::Eight { + panic!("fatal: image is not 8-bit RGBA"); + } + + let texture = TextureWrapper::new( + device, + info.width, + info.height, + TextureFormat::Rgba8UnormSrgb, + TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, + ); + texture.write(queue, &buf[..info.buffer_size()]); + texture +} -- cgit v1.3.1