add canvas_image builtin for PNG rendering with alpha blending
Registers canvas_image(path, x, y, w, h) in the type system and implements it in the interpreter using the image crate — scales to exact dimensions via Lanczos3 and alpha-composites onto the pixmap.
This commit is contained in:
Generated
+59
-1
@@ -272,6 +272,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder-lite"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.11.1"
|
||||
@@ -672,6 +678,7 @@ dependencies = [
|
||||
"fontdue",
|
||||
"hex",
|
||||
"hmac",
|
||||
"image",
|
||||
"reqwest",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
@@ -1421,6 +1428,19 @@ dependencies = [
|
||||
"icu_properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
"moxcms",
|
||||
"num-traits",
|
||||
"png 0.18.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.14.0"
|
||||
@@ -1676,6 +1696,16 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moxcms"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"pxfm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.18"
|
||||
@@ -1735,6 +1765,15 @@ dependencies = [
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.6"
|
||||
@@ -1897,6 +1936,19 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "3.11.0"
|
||||
@@ -1969,6 +2021,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pxfm"
|
||||
version = "0.1.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.39.2"
|
||||
@@ -2641,7 +2699,7 @@ dependencies = [
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"log",
|
||||
"png",
|
||||
"png 0.17.16",
|
||||
"tiny-skia-path",
|
||||
]
|
||||
|
||||
|
||||
@@ -62,3 +62,4 @@ winit = { version = "0.29", default-features = false, features = ["rwh_05"
|
||||
softbuffer = "0.3"
|
||||
tiny-skia = "0.11"
|
||||
fontdue = "0.8"
|
||||
image = { version = "0.25", default-features = false, features = ["png"] }
|
||||
|
||||
@@ -38,3 +38,4 @@ winit = { version = "0.29", default-features = false, features = ["rwh_05"
|
||||
softbuffer = "0.3"
|
||||
tiny-skia = "0.11"
|
||||
fontdue = "0.8"
|
||||
image = { workspace = true }
|
||||
|
||||
@@ -4724,6 +4724,50 @@ fn dispatch_builtin(
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
"canvas_image" => {
|
||||
// canvas_image(path: String, x: Int, y: Int, w: Int, h: Int) -> Void
|
||||
// Draws a PNG image scaled to w×h at (x, y) with alpha blending.
|
||||
let draw_h = match stack.pop().unwrap_or(Value::Nil) { Value::Int(n) => n as i32, _ => 0 };
|
||||
let draw_w = match stack.pop().unwrap_or(Value::Nil) { Value::Int(n) => n as i32, _ => 0 };
|
||||
let dy = match stack.pop().unwrap_or(Value::Nil) { Value::Int(n) => n as i32, _ => 0 };
|
||||
let dx = match stack.pop().unwrap_or(Value::Nil) { Value::Int(n) => n as i32, _ => 0 };
|
||||
let path = match stack.pop().unwrap_or(Value::Nil) { Value::Str(s) => s, _ => { stack.push(Value::Nil); return BuiltinResult::Handled; } };
|
||||
|
||||
if draw_w <= 0 || draw_h <= 0 { stack.push(Value::Nil); return BuiltinResult::Handled; }
|
||||
|
||||
use image::GenericImageView;
|
||||
if let Ok(img) = image::open(&path) {
|
||||
let img = img.resize_exact(draw_w as u32, draw_h as u32, image::imageops::FilterType::Lanczos3);
|
||||
let rgba = img.to_rgba8();
|
||||
CANVAS.with(|cv| {
|
||||
let mut cv = cv.borrow_mut();
|
||||
if let Some(px) = cv.pixmap.as_mut() {
|
||||
let pw = px.width() as i32;
|
||||
let ph = px.height() as i32;
|
||||
let data = px.data_mut();
|
||||
for row in 0..draw_h {
|
||||
for col in 0..draw_w {
|
||||
let ix = dx + col;
|
||||
let iy = dy + row;
|
||||
if ix < 0 || iy < 0 || ix >= pw || iy >= ph { continue; }
|
||||
let p = rgba.get_pixel(col as u32, row as u32);
|
||||
let sa = p[3] as u32;
|
||||
if sa == 0 { continue; }
|
||||
let ia = 255 - sa;
|
||||
let idx = (iy as usize * pw as usize + ix as usize) * 4;
|
||||
data[idx] = ((ia * data[idx] as u32 + sa * p[0] as u32) / 255) as u8;
|
||||
data[idx + 1] = ((ia * data[idx+1] as u32 + sa * p[1] as u32) / 255) as u8;
|
||||
data[idx + 2] = ((ia * data[idx+2] as u32 + sa * p[2] as u32) / 255) as u8;
|
||||
data[idx + 3] = ((ia * data[idx+3] as u32 + sa * sa) / 255) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
stack.push(Value::Nil);
|
||||
BuiltinResult::Handled
|
||||
}
|
||||
|
||||
_ => BuiltinResult::NotBuiltin,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,6 +278,7 @@ impl TypeEnv {
|
||||
env.functions.insert("canvas_events".into(), str_fn(vec![], s.clone()));
|
||||
env.functions.insert("canvas_swap".into(), str_fn(vec![], Type::Void));
|
||||
env.functions.insert("canvas_run_loop".into(), str_fn(vec![s.clone()], Type::Void));
|
||||
env.functions.insert("canvas_image".into(), str_fn(vec![s.clone(), i.clone(), i.clone(), i.clone(), i.clone()], Type::Void));
|
||||
env.functions.insert("state_set".into(), str_fn(vec![s.clone(), s.clone()], Type::Void));
|
||||
env.functions.insert("state_get".into(), str_fn(vec![s.clone()], s.clone()));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user