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",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder-lite"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.11.1"
|
version = "1.11.1"
|
||||||
@@ -672,6 +678,7 @@ dependencies = [
|
|||||||
"fontdue",
|
"fontdue",
|
||||||
"hex",
|
"hex",
|
||||||
"hmac",
|
"hmac",
|
||||||
|
"image",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
@@ -1421,6 +1428,19 @@ dependencies = [
|
|||||||
"icu_properties",
|
"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]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.14.0"
|
version = "2.14.0"
|
||||||
@@ -1676,6 +1696,16 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"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]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.2.18"
|
version = "0.2.18"
|
||||||
@@ -1735,6 +1765,15 @@ dependencies = [
|
|||||||
"memoffset",
|
"memoffset",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num_enum"
|
name = "num_enum"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
@@ -1897,6 +1936,19 @@ dependencies = [
|
|||||||
"miniz_oxide",
|
"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]]
|
[[package]]
|
||||||
name = "polling"
|
name = "polling"
|
||||||
version = "3.11.0"
|
version = "3.11.0"
|
||||||
@@ -1969,6 +2021,12 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pxfm"
|
||||||
|
version = "0.1.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.39.2"
|
version = "0.39.2"
|
||||||
@@ -2641,7 +2699,7 @@ dependencies = [
|
|||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"log",
|
"log",
|
||||||
"png",
|
"png 0.17.16",
|
||||||
"tiny-skia-path",
|
"tiny-skia-path",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -62,3 +62,4 @@ winit = { version = "0.29", default-features = false, features = ["rwh_05"
|
|||||||
softbuffer = "0.3"
|
softbuffer = "0.3"
|
||||||
tiny-skia = "0.11"
|
tiny-skia = "0.11"
|
||||||
fontdue = "0.8"
|
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"
|
softbuffer = "0.3"
|
||||||
tiny-skia = "0.11"
|
tiny-skia = "0.11"
|
||||||
fontdue = "0.8"
|
fontdue = "0.8"
|
||||||
|
image = { workspace = true }
|
||||||
|
|||||||
@@ -4724,6 +4724,50 @@ fn dispatch_builtin(
|
|||||||
unreachable!()
|
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,
|
_ => BuiltinResult::NotBuiltin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,6 +278,7 @@ impl TypeEnv {
|
|||||||
env.functions.insert("canvas_events".into(), str_fn(vec![], s.clone()));
|
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_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_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_set".into(), str_fn(vec![s.clone(), s.clone()], Type::Void));
|
||||||
env.functions.insert("state_get".into(), str_fn(vec![s.clone()], s.clone()));
|
env.functions.insert("state_get".into(), str_fn(vec![s.clone()], s.clone()));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user