mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 14:00:37 +00:00
🐛 Rebuild text layout when image fills finish loading (#12564)
Text image shaders are baked into Skia paragraphs at layout time. touch_shapes_with_image only scanned shape.fills/strokes, so text spans were never invalidated after store_image. Detect text content image fills and force a layout rebuild before touching tiles. Closes #12563
This commit is contained in:
parent
ce5276065e
commit
6508449fa0
@ -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<Uuid> = 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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user