feat(image): fetch remote rasters and clip them to their boundary
A raster image reference now resolves through one memoised path shared by the loader and the unresolved-reference placeholder: - http/https URLs are fetched natively (rustls, 8s timeout, 32MB cap); a local file is read as before. The web build never fetches. The cache is cleared when a new document opens. - Online, the fetched image displays; offline or on a broken/missing reference, the frame + saved path text still shows. One resolver keeps the two in agreement without fetching twice. The raster is also clipped to its boundary: the image draws as the triangulated clip polygon (ear-clipped) with per-vertex UVs instead of a full quad, so pixels appear only inside the clip. Clip-boundary Y is raster space (top-down), so it is flipped to the image's Y-up space in both the outline and the clip triangulation so they align. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1b974df505
commit
b3e5743808
5 changed files with 247 additions and 36 deletions
|
|
@ -62,6 +62,9 @@ struct ImageParams {
|
|||
|
||||
pub struct ImageGpu {
|
||||
pub vertex_buffer: wgpu::Buffer,
|
||||
/// Number of triangle vertices in `vertex_buffer` — 6 for a plain quad, or
|
||||
/// more when the raster is clipped to a triangulated polygon.
|
||||
pub vertex_count: u32,
|
||||
pub bind_group: wgpu::BindGroup,
|
||||
/// Mirrors `ImageModel.vp_scissor` — see `HatchGpu.vp_scissor`.
|
||||
pub vp_scissor: Option<[f32; 4]>,
|
||||
|
|
@ -157,19 +160,22 @@ impl ImageGpu {
|
|||
],
|
||||
});
|
||||
|
||||
// ── Vertex buffer — two triangles ─────────────────────────────────
|
||||
// corners: [p0=BL, p1=BR, p2=TR, p3=TL]
|
||||
// UV: BL=(0,1), BR=(1,1), TR=(1,0), TL=(0,0)
|
||||
let [p0, p1, p2, p3] = model.corners;
|
||||
let [l0, l1, l2, l3] = model.corners_low;
|
||||
let verts = [
|
||||
ImageVertex { pos: p0, uv: [0.0, 1.0], pos_low: l0 },
|
||||
ImageVertex { pos: p1, uv: [1.0, 1.0], pos_low: l1 },
|
||||
ImageVertex { pos: p2, uv: [1.0, 0.0], pos_low: l2 },
|
||||
ImageVertex { pos: p0, uv: [0.0, 1.0], pos_low: l0 },
|
||||
ImageVertex { pos: p2, uv: [1.0, 0.0], pos_low: l2 },
|
||||
ImageVertex { pos: p3, uv: [0.0, 0.0], pos_low: l3 },
|
||||
];
|
||||
// ── Vertex buffer — the image's visible triangles ─────────────────
|
||||
// `model.verts` already holds either the full quad or the triangulated
|
||||
// clip polygon, each vertex carrying an RTE-split position and its UV.
|
||||
let verts: Vec<ImageVertex> = model
|
||||
.verts
|
||||
.iter()
|
||||
.map(|v| ImageVertex {
|
||||
pos: v.pos,
|
||||
uv: v.uv,
|
||||
pos_low: v.pos_low,
|
||||
})
|
||||
.collect();
|
||||
if verts.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let vertex_count = verts.len() as u32;
|
||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("image.vbuf"),
|
||||
contents: bytemuck::cast_slice(&verts),
|
||||
|
|
@ -178,6 +184,7 @@ impl ImageGpu {
|
|||
|
||||
Some(Self {
|
||||
vertex_buffer,
|
||||
vertex_count,
|
||||
bind_group,
|
||||
vp_scissor: model.vp_scissor,
|
||||
_texture: texture,
|
||||
|
|
|
|||
|
|
@ -2177,7 +2177,7 @@ impl Pipeline {
|
|||
}
|
||||
pass.set_bind_group(1, &img.bind_group, &[]);
|
||||
pass.set_vertex_buffer(0, img.vertex_buffer.slice(..));
|
||||
pass.draw(0..6, 0..1);
|
||||
pass.draw(0..img.vertex_count, 0..1);
|
||||
}
|
||||
if scissor_active {
|
||||
pass.set_scissor_rect(0, 0, vp.width, vp.height);
|
||||
|
|
|
|||
Loading…
Reference in a new issue