Improve modern rendering and output parity

This commit is contained in:
SGavrl committed 2026-08-03 19:46:24 +00:00
1 parent f428aa37fa
commit c8a5477296
25 files changed
+12885 -5053

No files matched your search

File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+111 -38
View File
@@ -14,7 +14,7 @@
use taffy::prelude::*;
pub mod css;
pub use css::Stylesheet;
pub use css::{Stylesheet, StylesheetCache};
pub mod style;
pub use style::compute_style;
@@ -37,7 +37,10 @@ pub use dom::{
/// are ASCII case-insensitive. Keep this list aligned with the `image` crate
/// features in `Cargo.toml` plus the SVG path in `paint.rs`.
pub(crate) fn source_type_supported(value: &str) -> bool {
let essence = value.split_once(';').map_or(value, |(essence, _)| essence).trim();
let essence = value
.split_once(';')
.map_or(value, |(essence, _)| essence)
.trim();
[
"image/apng",
"image/bmp",
@@ -94,11 +97,12 @@ mod paint;
#[cfg(feature = "paint")]
pub use paint::{
paint_dom, paint_dom_scrolled, paint_prepared, paint_prepared_region_with_scroll,
paint_prepared_with_scroll, prepare_dom, prepare_dom_with_dynamic_fonts, screenshot_png,
screenshot_png_scrolled, screenshot_prepared, screenshot_prepared_region_with_scroll,
screenshot_prepared_with_scroll, CaptureError, CaptureRegion, DynamicFontFace,
ElementScrollMetrics, PreparedRender, RenderResourceCache, RenderResourceLoader,
ResolvedScrollState, SelectedImage, MAX_CAPTURE_DIMENSION, MAX_CAPTURE_PIXELS,
paint_prepared_with_scroll, prepare_dom, prepare_dom_with_dynamic_fonts,
prepare_dom_with_dynamic_fonts_and_stylesheet_cache, screenshot_png, screenshot_png_scrolled,
screenshot_prepared, screenshot_prepared_region_with_scroll, screenshot_prepared_with_scroll,
validate_capture_region, CaptureError, CaptureRegion, DynamicFontFace, ElementScrollMetrics,
PreparedRender, RenderResourceCache, RenderResourceLoader, ResolvedScrollState, SelectedImage,
MAX_CAPTURE_DIMENSION, MAX_CAPTURE_PIXELS,
};
// Real inline text layout (cosmic-text) lives behind the paint feature; the
@@ -122,7 +126,12 @@ pub mod inline {
/// Layout-only builds have no shaper, so no container is ever treated
/// as a cosmic-text inline formatting context: the word-split path
/// handles text geometry for `getBoundingClientRect`.
pub fn try_build(&mut self, _tree: &DomTree, _id: NodeId, _styles: &HashMap<NodeId, crate::LayoutStyle>) -> Option<usize> {
pub fn try_build(
&mut self,
_tree: &DomTree,
_id: NodeId,
_styles: &HashMap<NodeId, crate::LayoutStyle>,
) -> Option<usize> {
None
}
/// See `try_build`: inline runs likewise fall back to word-split
@@ -224,16 +233,31 @@ impl Affine2 {
}
pub fn translate(x: f32, y: f32) -> Self {
Self { e: x, f: y, ..Self::IDENTITY }
Self {
e: x,
f: y,
..Self::IDENTITY
}
}
pub fn scale(x: f32, y: f32) -> Self {
Self { a: x, d: y, ..Self::IDENTITY }
Self {
a: x,
d: y,
..Self::IDENTITY
}
}
pub fn rotate(degrees: f32) -> Self {
let (sin, cos) = degrees.to_radians().sin_cos();
Self { a: cos, b: sin, c: -sin, d: cos, e: 0.0, f: 0.0 }
Self {
a: cos,
b: sin,
c: -sin,
d: cos,
e: 0.0,
f: 0.0,
}
}
pub fn skew(x_degrees: f32, y_degrees: f32) -> Self {
@@ -254,7 +278,10 @@ impl Affine2 {
}
pub fn map_point(self, x: f32, y: f32) -> (f32, f32) {
(self.a * x + self.c * y + self.e, self.b * x + self.d * y + self.f)
(
self.a * x + self.c * y + self.e,
self.b * x + self.d * y + self.f,
)
}
pub fn map_rect(self, rect: Rect) -> Rect {
@@ -264,10 +291,22 @@ impl Affine2 {
self.map_point(rect.x, rect.y + rect.height),
self.map_point(rect.x + rect.width, rect.y + rect.height),
];
let left = points.iter().map(|point| point.0).fold(f32::INFINITY, f32::min);
let top = points.iter().map(|point| point.1).fold(f32::INFINITY, f32::min);
let right = points.iter().map(|point| point.0).fold(f32::NEG_INFINITY, f32::max);
let bottom = points.iter().map(|point| point.1).fold(f32::NEG_INFINITY, f32::max);
let left = points
.iter()
.map(|point| point.0)
.fold(f32::INFINITY, f32::min);
let top = points
.iter()
.map(|point| point.1)
.fold(f32::INFINITY, f32::min);
let right = points
.iter()
.map(|point| point.0)
.fold(f32::NEG_INFINITY, f32::max);
let bottom = points
.iter()
.map(|point| point.1)
.fold(f32::NEG_INFINITY, f32::max);
Rect {
x: left,
y: top,
@@ -317,7 +356,10 @@ pub struct TransformLength {
impl TransformLength {
pub fn px(value: f32) -> Self {
Self { value: Dimension::Px(value), expression: None }
Self {
value: Dimension::Px(value),
expression: None,
}
}
}
@@ -343,7 +385,12 @@ impl Rect {
let x1 = (self.x + self.width).min(other.x + other.width);
let y1 = (self.y + self.height).min(other.y + other.height);
if x1 > x0 && y1 > y0 {
Some(Rect { x: x0, y: y0, width: x1 - x0, height: y1 - y0 })
Some(Rect {
x: x0,
y: y0,
width: x1 - x0,
height: y1 - y0,
})
} else {
None
}
@@ -356,7 +403,12 @@ impl Rect {
let y0 = self.y.min(other.y);
let x1 = (self.x + self.width).max(other.x + other.width);
let y1 = (self.y + self.height).max(other.y + other.height);
Rect { x: x0, y: y0, width: x1 - x0, height: y1 - y0 }
Rect {
x: x0,
y: y0,
width: x1 - x0,
height: y1 - y0,
}
}
}
@@ -380,11 +432,7 @@ pub fn quantize_scroll_value(value: f32, device_scale_factor: f32) -> f32 {
(value * scale).round() / scale
}
pub(crate) fn quantized_scroll_range(
content: f32,
client: f32,
device_scale_factor: f32,
) -> f32 {
pub(crate) fn quantized_scroll_range(content: f32, client: f32, device_scale_factor: f32) -> f32 {
(quantize_scroll_value(content, device_scale_factor)
- quantize_scroll_value(client, device_scale_factor))
.max(0.0)
@@ -532,8 +580,7 @@ impl BackgroundPositionAxis {
pub(crate) fn interpolate(self, other: Self, position: f32) -> Self {
Self {
length: self.length + (other.length - self.length) * position,
percentage: self.percentage
+ (other.percentage - self.percentage) * position,
percentage: self.percentage + (other.percentage - self.percentage) * position,
}
}
}
@@ -816,8 +863,7 @@ pub struct LayoutStyle {
/// First `radial-gradient(...)` layer: center in box-relative fractions
/// and color stops. It is painted below the first linear layer, matching
/// the common `linear-gradient(...), radial-gradient(...)` hero pattern.
pub background_radial_gradient:
Option<((f32, f32), Vec<([u8; 4], Option<f32>)>)>,
pub background_radial_gradient: Option<((f32, f32), Vec<([u8; 4], Option<f32>)>)>,
/// `conic-gradient(...)` background. The angle is the CSS `from` angle,
/// the center is a fraction of the border box, and stops are normalized
/// during paint. Conic gradients commonly provide the color source for a
@@ -1536,7 +1582,11 @@ pub struct LayoutNode {
impl LayoutNode {
pub fn leaf(style: LayoutStyle) -> Self {
LayoutNode { style, text: None, children: Vec::new() }
LayoutNode {
style,
text: None,
children: Vec::new(),
}
}
}
@@ -1568,9 +1618,9 @@ fn build_node(tree: &mut TaffyTree, node: &LayoutNode) -> NodeId {
if node.children.is_empty() {
tree.new_leaf(style).expect("taffy new_leaf")
} else {
let child_ids: Vec<NodeId> =
node.children.iter().map(|c| build_node(tree, c)).collect();
tree.new_with_children(style, &child_ids).expect("taffy new_with_children")
let child_ids: Vec<NodeId> = node.children.iter().map(|c| build_node(tree, c)).collect();
tree.new_with_children(style, &child_ids)
.expect("taffy new_with_children")
}
}
@@ -1609,7 +1659,10 @@ pub(crate) fn to_taffy_style(style: &LayoutStyle) -> Style {
// `text_align` is separate from real flex/grid `align-items`, so a
// text-align declaration never changes how flex children are sized.
let promote_for_alignment = style.display == Display::Block
&& matches!(style.text_align, Some(taffy::AlignItems::CENTER) | Some(taffy::AlignItems::FLEX_END));
&& matches!(
style.text_align,
Some(taffy::AlignItems::CENTER) | Some(taffy::AlignItems::FLEX_END)
);
s.display = match style.display {
Display::Block if promote_for_alignment => taffy::style::Display::Flex,
@@ -1816,7 +1869,9 @@ fn dimension(v: Dimension) -> taffy::style::Dimension {
// rather than panicking.
Dimension::Em(v) | Dimension::Rem(v) => taffy::style::Dimension::length(v * 16.0),
Dimension::Ex(v) => taffy::style::Dimension::length(v * 16.0 * 0.528_320_3),
Dimension::Vw(v) | Dimension::Vh(v) | Dimension::Vmin(v) | Dimension::Vmax(v) => taffy::style::Dimension::length(v),
Dimension::Vw(v) | Dimension::Vh(v) | Dimension::Vmin(v) | Dimension::Vmax(v) => {
taffy::style::Dimension::length(v)
}
}
}
@@ -1866,7 +1921,12 @@ mod tests {
use super::*;
fn make_box(display: Display, w: f32, h: f32) -> LayoutStyle {
LayoutStyle { display, width: Dimension::Px(w), height: Dimension::Px(h), ..Default::default() }
LayoutStyle {
display,
width: Dimension::Px(w),
height: Dimension::Px(h),
..Default::default()
}
}
#[test]
@@ -1909,7 +1969,10 @@ mod tests {
display: Display::Block,
width: Dimension::Px(200.0),
height: Dimension::Px(40.0),
margin: Edges { right: 50.0, ..Default::default() },
margin: Edges {
right: 50.0,
..Default::default()
},
margin_auto: [false, false, false, true],
..Default::default()
};
@@ -1957,7 +2020,12 @@ mod tests {
#[test]
fn flex_row_lays_out_horizontally() {
let root = LayoutNode {
style: LayoutStyle { display: Display::Flex, width: Dimension::Px(600.0), height: Dimension::Px(100.0), ..Default::default() },
style: LayoutStyle {
display: Display::Flex,
width: Dimension::Px(600.0),
height: Dimension::Px(100.0),
..Default::default()
},
text: None,
children: vec![
LayoutNode::leaf(make_box(Display::Block, 200.0, 100.0)),
@@ -1983,7 +2051,12 @@ mod tests {
display: Display::Block,
width: Dimension::Px(100.0),
height: Dimension::Px(100.0),
padding: Edges { top: 10.0, right: 10.0, bottom: 10.0, left: 10.0 },
padding: Edges {
top: 10.0,
right: 10.0,
bottom: 10.0,
left: 10.0,
},
..Default::default()
},
text: None,
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff