ailloli_ui_render_wgpu/text/
glyph_upload.rs

1//! GPU upload helpers for glyph / atlas regions (`bytes_per_row` padding invariants).
2
3/// Writes a tight RGBA8 rectangle into a texture, padding rows to 256 bytes.
4///
5/// `rgba` should contain `w * h * 4` bytes. Short data is zero-padded only when
6/// row repacking is required; an already aligned short slice is passed directly
7/// to wgpu and fails validation. Zero width produces a zero row pitch, which is
8/// not a valid nonempty texture copy.
9///
10/// # Panics
11///
12/// Panics on arithmetic overflow, allocation failure, or a short source row
13/// during the padding copy.
14///
15/// # Examples
16///
17/// ```no_run
18/// use ailloli_ui_render_wgpu::text::glyph_upload::write_subtexture_rgba;
19/// fn upload(queue: &wgpu::Queue, texture: &wgpu::Texture) {
20///     write_subtexture_rgba(queue, texture, wgpu::Origin3d::ZERO,
21///         2, 2, &[255; 2 * 2 * 4]);
22/// }
23/// ```
24pub fn write_subtexture_rgba(
25    queue: &wgpu::Queue,
26    texture: &wgpu::Texture,
27    origin: wgpu::Origin3d,
28    w: u32,
29    h: u32,
30    rgba: &[u8],
31) {
32    let unpadded_bpr = w * 4;
33    let padded_bpr = unpadded_bpr.div_ceil(256) * 256;
34
35    let data: Vec<u8>;
36    let bytes = if padded_bpr == unpadded_bpr {
37        rgba
38    } else {
39        data = pad_rows(rgba, unpadded_bpr as usize, padded_bpr as usize, h as usize);
40        &data
41    };
42
43    queue.write_texture(
44        wgpu::ImageCopyTexture {
45            texture,
46            mip_level: 0,
47            origin,
48            aspect: wgpu::TextureAspect::All,
49        },
50        bytes,
51        wgpu::ImageDataLayout {
52            offset: 0,
53            bytes_per_row: Some(padded_bpr),
54            rows_per_image: Some(h),
55        },
56        wgpu::Extent3d {
57            width: w,
58            height: h,
59            depth_or_array_layers: 1,
60        },
61    );
62}
63
64/// Copies tight rows into a zero-filled wider row pitch.
65fn pad_rows(src: &[u8], src_bpr: usize, dst_bpr: usize, rows: usize) -> Vec<u8> {
66    let mut out = vec![0u8; dst_bpr * rows];
67    for row in 0..rows {
68        let src_off = row * src_bpr;
69        let dst_off = row * dst_bpr;
70        let src_end = (src_off + src_bpr).min(src.len());
71        out[dst_off..dst_off + (src_end - src_off)].copy_from_slice(&src[src_off..src_end]);
72    }
73    out
74}