Implement content-addressed resource storage for raster images (#4148)

* Implement content-addressed resource storage

* Implement OPFS resource storage

* Remove ResourceStorage::read

* Review
This commit is contained in:
Timon
2026-05-21 19:31:07 +00:00
committed by GitHub
parent 21d2994059
commit 2770df7567
50 changed files with 1535 additions and 355 deletions

View File

@@ -13,7 +13,6 @@ use core_types::{ATTR_EDITOR_MERGED_LAYERS, ATTR_TRANSFORM, WasmNotSend};
use core_types::{Color, Ctx};
pub use graph_craft::application_io::*;
pub use graph_craft::document::value::RenderOutputType;
use graphene_application_io::ApplicationIo;
#[cfg(target_family = "wasm")]
pub use graphene_canvas_utils as canvas_utils;
#[cfg(target_family = "wasm")]
@@ -137,18 +136,24 @@ fn image_to_bytes(_: impl Ctx, image: List<Raster<CPU>>) -> List<u8> {
/// Loads binary from URLs and local asset paths. Returns a transparent placeholder if the resource fails to load, allowing rendering to continue.
#[node_macro::node(category("Web Request"))]
async fn load_resource<'a: 'n>(_: impl Ctx, _primary: (), #[scope("editor-api")] editor_resources: &'a PlatformEditorApi, #[name("URL")] url: String) -> Arc<[u8]> {
let Some(api) = editor_resources.application_io.as_ref() else {
return Arc::from(include_bytes!("../../../graph-craft/src/null.png").to_vec());
};
let Ok(data) = api.load_resource(url) else {
return Arc::from(include_bytes!("../../../graph-craft/src/null.png").to_vec());
};
let Ok(data) = data.await else {
return Arc::from(include_bytes!("../../../graph-craft/src/null.png").to_vec());
async fn load_resource<'a: 'n>(_: impl Ctx, _primary: (), #[scope("editor-api")] _editor: &'a PlatformEditorApi, #[name("URL")] url: String) -> Arc<[u8]> {
let placeholder = || -> Arc<[u8]> { Arc::from(Vec::<u8>::new()) };
let response = match reqwest::Client::new().get(&url).send().await {
Ok(response) => response,
Err(error) => {
log::error!("HTTP request for `{url}` failed: {error}");
return placeholder();
}
};
data
match response.bytes().await {
Ok(bytes) => Arc::from(bytes.to_vec()),
Err(error) => {
log::error!("Failed to read HTTP response for `{url}`: {error}");
placeholder()
}
}
}
/// Converts raw binary data to a raster image.
@@ -254,3 +259,11 @@ where
.with_attribute(ATTR_EDITOR_MERGED_LAYERS, upstream_graphic_list),
)
}
#[node_macro::node(category(""))]
pub async fn resource<'a: 'n>(_: impl Ctx, hash: ResourceHash, #[scope("editor-api")] editor_api: &'a PlatformEditorApi) -> Resource {
let application_io = editor_api.application_io.as_ref().expect("ApplicationIo must be available when using resources");
application_io.load_resource(hash).await.unwrap_or_else(|| {
panic!("Resource {hash} not found");
})
}

View File

@@ -3,7 +3,7 @@ use core_types::transform::{Footprint, Transform};
use core_types::uuid::generate_uuid;
use core_types::{CloneVarArgs, ExtractAll, ExtractVarArgs};
use core_types::{Color, Context, Ctx, ExtractFootprint, OwnedContextImpl, WasmNotSend};
pub use graph_craft::application_io::*;
use graph_craft::application_io::PlatformEditorApi;
use graph_craft::document::value::RenderOutput;
pub use graph_craft::document::value::RenderOutputType;
use graphene_application_io::{ApplicationIo, ExportFormat, RenderConfig};