diff --git a/render-wasm/src/wasm/fills/image.rs b/render-wasm/src/wasm/fills/image.rs index e6b2e634c5..72befd5e55 100644 --- a/render-wasm/src/wasm/fills/image.rs +++ b/render-wasm/src/wasm/fills/image.rs @@ -1,30 +1,67 @@ use crate::error::{Error, Result}; use crate::get_resources; use crate::mem; -use crate::shapes::Fill; +use crate::shapes::{Fill, Shape, TextContent, Type}; use crate::state::State; use crate::uuid::Uuid; use crate::with_state; use crate::{shapes::ImageFill, utils::uuid_from_u32_quartet}; use macros::wasm_error; +fn text_content_uses_image(content: &TextContent, image_id: Uuid) -> bool { + content.paragraphs().iter().any(|paragraph| { + paragraph.children().iter().any(|span| { + span.fills + .iter() + .any(|f| matches!(f, Fill::Image(i) if i.id() == image_id)) + }) + }) +} + +fn shape_uses_image(shape: &Shape, image_id: Uuid) -> bool { + if shape + .fills() + .any(|f| matches!(f, Fill::Image(i) if i.id() == image_id)) + || shape + .strokes + .iter() + .any(|s| matches!(&s.fill, Fill::Image(i) if i.id() == image_id)) + { + return true; + } + match &shape.shape_type { + Type::Text(content) => text_content_uses_image(content, image_id), + _ => false, + } +} + +fn rebuild_text_layout_for_image(shape: &mut Shape) { + let Type::Text(text_content) = &mut shape.shape_type else { + return; + }; + // Image shaders are baked into Skia paragraphs at layout time. When the + // media arrives after the first layout, force a rebuild so text fills + // pick up `get_image` (rect fills look up the image every paint). + text_content.force_next_layout_update(); + text_content.update_layout(shape.selrect); + shape.apply_deferred_batch_paint(); + shape.invalidate_extrect(); +} + fn touch_shapes_with_image(state: &mut State, image_id: Uuid) { - let ids: Vec = state + let ids: Vec<(Uuid, bool)> = state .shapes .iter() - .filter(|shape| { - shape - .fills() - .any(|f| matches!(f, Fill::Image(i) if i.id() == image_id)) - || shape - .strokes - .iter() - .any(|s| matches!(&s.fill, Fill::Image(i) if i.id() == image_id)) - }) - .map(|shape| shape.id) + .filter(|shape| shape_uses_image(shape, image_id)) + .map(|shape| (shape.id, matches!(shape.shape_type, Type::Text(_)))) .collect(); - for id in ids { + for (id, is_text) in ids { + if is_text { + if let Some(shape) = state.shapes.get_mut(&id) { + rebuild_text_layout_for_image(shape); + } + } state.touch_shape(id); } }