Distinguish an absent column from a defaulted one on the lane source

This commit is contained in:
Dennis Kobert
2026-08-26 15:48:01 +00:00
parent b685ca77f2
commit 2b06d27729
5 changed files with 28 additions and 21 deletions

View File

@@ -6,9 +6,13 @@ use crate::attribute::Attribute;
/// One marker's column on a source, resolved once so lane reads skip the key
/// lookup.
pub trait LaneColumn<'a, A: Attribute> {
/// The lane's value, or the marker's census default where the column is
/// absent.
fn get(&self, lane: usize) -> A::Value<'a>;
/// The lane's value, `None` where the source carries no such column.
fn try_get(&self, lane: usize) -> Option<A::Value<'a>>;
/// The lane's value, falling back to the marker's census default.
fn get(&self, lane: usize) -> A::Value<'a> {
self.try_get(lane).unwrap_or_else(A::default)
}
}
/// A source of lanes carrying an element and census attributes.
@@ -27,6 +31,11 @@ pub trait LaneSource {
fn attr<A: Attribute>(&self, lane: usize) -> A::Value<'_> {
self.column::<A>().get(lane)
}
/// Distinguishes an absent column from one holding the census default.
fn try_attr<A: Attribute>(&self, lane: usize) -> Option<A::Value<'_>> {
self.column::<A>().try_get(lane)
}
}
#[cfg(test)]

View File

@@ -1180,8 +1180,8 @@ pub struct ListColumn<'a, A: crate::attribute::Attribute> {
}
impl<'a, A: crate::attribute::Attribute> crate::lane::LaneColumn<'a, A> for ListColumn<'a, A> {
fn get(&self, lane: usize) -> A::Value<'a> {
self.stored.and_then(|column| column.get_any(lane)).and_then(A::from_stored).unwrap_or_else(A::default)
fn try_get(&self, lane: usize) -> Option<A::Value<'a>> {
self.stored.and_then(|column| column.get_any(lane)).and_then(A::from_stored)
}
}

View File

@@ -1888,13 +1888,10 @@ pub struct RunColumn<'a, A: crate::attribute::Attribute> {
}
impl<'a, A: crate::attribute::Attribute> crate::lane::LaneColumn<'a, A> for RunColumn<'a, A> {
fn get(&self, lane: usize) -> A::Value<'a> {
match self.offset {
// SAFETY: the offset comes from the item's own layout, whose field at
// this name holds this marker's value type by census registration.
Some(offset) => unsafe { self.item.lanes().get(lane).rec().ptr().add(offset).cast::<A::Value<'a>>().read() },
None => A::default(),
}
fn try_get(&self, lane: usize) -> Option<A::Value<'a>> {
// SAFETY: the offset comes from the item's own layout, whose field at
// this name holds this marker's value type by census registration.
self.offset.map(|offset| unsafe { self.item.lanes().get(lane).rec().ptr().add(offset).cast::<A::Value<'a>>().read() })
}
}

View File

@@ -2,8 +2,8 @@ use crate::render_ext::{PaintTarget, RenderExt};
use crate::to_peniko::{BlendModeExt, ToPenikoColor};
use core_types::CacheHash;
use core_types::attribute::{
Background as BackgroundAttr, BlendMode as BlendModeAttr, Clip, ClippingMask, Dimensions, EditorLayerPath, FontSize, LetterSpacing, LetterTilt, LineHeight, Location, MaxHeight, MaxWidth, Opacity,
OpacityFill, Transform,
Background as BackgroundAttr, BlendMode as BlendModeAttr, Clip, ClippingMask, Dimensions, EditorLayerPath, EditorTextFrame, FontSize, LetterSpacing, LetterTilt, LineHeight, Location, MaxHeight,
MaxWidth, Opacity, OpacityFill, Transform,
};
use core_types::blending::BlendMode;
use core_types::bounds::BoundingBox;
@@ -16,7 +16,7 @@ use core_types::math::quad::Quad;
use core_types::render_complexity::RenderComplexity;
use core_types::transform::Footprint;
use core_types::uuid::{NodeId, generate_uuid};
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_EDITOR_TEXT_FRAME, ATTR_TRANSFORM};
use core_types::{ATTR_EDITOR_LAYER_PATH, ATTR_TRANSFORM};
use dyn_any::DynAny;
use glam::{DAffine2, DMat2, DVec2};
use graphene_hash::CacheHashWrapper;
@@ -43,7 +43,8 @@ use std::ops::Deref;
use std::sync::{Arc, LazyLock};
use text_nodes::markers::{Font, TextAlign};
use vector_types::gradient::GradientSpreadMethod;
use vector_types::{ATTR_EDITOR_CLICK_TARGET, ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD};
use vector_types::markers::EditorClickTarget;
use vector_types::{ATTR_GRADIENT_TYPE, ATTR_SPREAD_METHOD};
use vello::*;
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -1588,7 +1589,7 @@ impl Render for List<Vector> {
}
// Use click-target override if the item provides one (e.g. 'Text' node's per-glyph bboxes)
let click_target_vector = self.attribute::<Vector>(ATTR_EDITOR_CLICK_TARGET, index).unwrap_or(source);
let click_target_vector = self.attr::<EditorClickTarget>(index).unwrap_or(source);
let item_relative_transform = item_zero_inverse * transform;
@@ -1617,7 +1618,7 @@ impl Render for List<Vector> {
}
// Surface `editor:text_frame` for the Text tool's drag cage
if let Some(&frame) = self.attribute::<DAffine2>(ATTR_EDITOR_TEXT_FRAME, index) {
if let Some(frame) = self.try_attr::<EditorTextFrame>(index) {
metadata.text_frames.entry(element_id).or_insert(frame);
}
}
@@ -1647,7 +1648,7 @@ impl Render for List<Vector> {
let transform: DAffine2 = self.attr::<Transform>(index);
// Use click-target override geometry if the item provides one (e.g. 'Text' node's per-glyph bounding boxes)
let vector = self.attribute::<Vector>(ATTR_EDITOR_CLICK_TARGET, index).unwrap_or(source);
let vector = self.attr::<EditorClickTarget>(index).unwrap_or(source);
extend_targets_from_vector(click_targets, self, index, vector, transform);
}