/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// Perform a few optimizations to the gradient that are relevant to scene building. /// /// Mutates `prim_rect`, `tile_size`, `start`, `end` to bake in the simplifications /// (repeated-tile collapse, equivalent-to-stretching on either axis, clip-induced /// offsets). Decomposition into per-segment quads is no longer done here -- the /// caller emits a single `LinearGradient` prim and prepare-time runs /// [`decompose_axis_aligned_gradient`] against the snapped prim_rect when the /// gradient is eligible. Doing the decomposition at frame-build keeps adjacent /// segments phase-aligned with the snapped outer prim, even when the frame-time /// snap pass nudges the outer rect. pubfn optimize_linear_gradient(
prim_rect: &mut LayoutRect,
tile_size: &mut LayoutSize, mut tile_spacing: LayoutSize,
clip_rect: &LayoutRect,
start: &mut LayoutPoint,
end: &mut LayoutPoint,
) {
simplify_repeated_primitive(&tile_size, &mut tile_spacing, prim_rect);
let vertical = start.x.approx_eq(&end.x); let horizontal = start.y.approx_eq(&end.y);
let horizontally_tiled = prim_rect.width() > tile_size.width; let vertically_tiled = prim_rect.height() > tile_size.height;
// Check whether the tiling is equivalent to stretching on either axis. // Stretching the gradient is more efficient than repeating it. if vertically_tiled && horizontal && tile_spacing.height == 0.0 {
tile_size.height = prim_rect.height();
}
let offset = apply_gradient_local_clip(
prim_rect,
&tile_size,
&tile_spacing,
&clip_rect
);
// The size of gradient render tasks depends on the tile_size. No need to generate // large stretch sizes that will be clipped to the bounds of the primitive.
tile_size.width = tile_size.width.min(prim_rect.width());
tile_size.height = tile_size.height.min(prim_rect.height());
*start += offset;
*end += offset;
}
/// Whether a linear gradient is eligible for the fast-path two-stop-per-segment /// decomposition at prepare time. Inputs are the values produced by /// `optimize_linear_gradient` (i.e. already simplified and clip-adjusted). pubfn linear_gradient_decomposes(
prim_rect: &LayoutRect,
tile_size: LayoutSize,
tile_spacing: LayoutSize,
start: LayoutPoint,
end: LayoutPoint,
extend_mode: ExtendMode,
stops: &[GradientStop],
enable_dithering: bool,
) -> bool { if extend_mode != ExtendMode::Clamp || stops.is_empty() { returnfalse;
}
let vertical = start.x.approx_eq(&end.x); let horizontal = start.y.approx_eq(&end.y);
if !vertical && !horizontal { returnfalse;
}
if vertical && horizontal { returnfalse;
}
if !tile_spacing.is_empty() { returnfalse;
}
let horizontally_tiled = prim_rect.width() > tile_size.width; let vertically_tiled = prim_rect.height() > tile_size.height; if vertically_tiled || horizontally_tiled { returnfalse;
}
/// Decompose an axis-aligned linear gradient into a sequence of two-stop /// segments that tile end-to-end across `prim_rect`. Each callback invocation /// is one segment, ready to be rendered as its own quad via the fast-path /// gradient shader. Run at frame-build (against the snapped prim_rect) so /// adjacent segments share a snapped boundary and tile without phase drift. /// /// Caller must have verified eligibility via [`linear_gradient_decomposes`]. pubfn decompose_axis_aligned_gradient(
prim_rect: &LayoutRect,
tile_size: LayoutSize,
start: LayoutPoint,
end: LayoutPoint,
stops: &[GradientStop],
clip_rect: &LayoutRect, mut callback: impl FnMut(&LayoutRect, LayoutPoint, LayoutPoint, [GradientStop; 2], EdgeMask),
) {
debug_assert!(!stops.is_empty());
let vertical = start.x.approx_eq(&end.x);
// Flip x/y when the gradient is vertical so the remaining math treats it // as horizontal; un-flip per-segment outputs at the end. let adjust_rect = &mut |rect: &mut LayoutRect| { if vertical {
swap(&mut rect.min.x, &mut rect.min.y);
swap(&mut rect.max.x, &mut rect.max.y);
}
}; let adjust_size = &mut |size: &mut LayoutSize| { if vertical { swap(&mut size.width, &mut size.height); }
}; let adjust_point = &mut |p: &mut LayoutPoint| { if vertical { swap(&mut p.x, &mut p.y); }
};
let clip_rect = match clip_rect.intersection(prim_rect) {
Some(clip) => clip,
None => return,
};
// `clip_rect` stays in the original (un-swapped) space — segment_rect // gets `adjust_rect` applied twice (once implicitly via the prim_rect // copy, once explicitly after computing per-segment extent) and lands // back in original space before this intersection.
let length = (end.x - start.x).abs();
// Match the pre-refactor optimiser: when the gradient line points in // decreasing-x (post-axis-swap), swap start/end and walk the stop list // in reverse, so the loop always processes stops in increasing-x // order. The pre-refactor code did this via `stops.reverse()` in // place; we can't mutate the template's stops here, so use a reversed // iterator and swap which end of the slice supplies the fake-stop // colour accordingly. let reverse_stops = start.x > end.x; if reverse_stops {
swap(&mut start, &mut end);
}
let (first_stop, last_stop) = if reverse_stops {
(*stops.last().unwrap(), *stops.first().unwrap())
} else {
(*stops.first().unwrap(), *stops.last().unwrap())
};
letmut is_first = true; let last_offset = last.offset;
// Iterate stops in increasing-x order. When reverse_stops is set, walk the // backing slice in reverse instead of mutating it. let stops_iter: Box<dyn Iterator<Item = &GradientStop>> = if reverse_stops { Box::new(stops.iter().rev())
} else { Box::new(stops.iter())
};
for stop in stops_iter.chain(std::iter::once(&last)) { let prev_stop = prev;
prev = *stop;
let prev_offset = if reverse_stops { 1.0 - prev_stop.offset } else { prev_stop.offset }; let offset = if reverse_stops { 1.0 - stop.offset } else { stop.offset };
// Segment_start and segment_end are in the gradient's pre-flip space // (relative to the prim's origin); the adjust_* helpers below restore // axis orientation when emitting. let segment_start = start.x + prev_offset * length; let segment_end = start.x + offset * length; let segment_length = segment_end - segment_start;
let common = PrimTemplateCommonData::with_key_common(item.common);
let (stops, min_alpha) = stops_and_min_alpha(&item.stops);
// Save opacity of the stops for use in // selecting which pass this gradient // should be drawn in. let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha);
let start_point = LayoutPoint::new(item.start_point.x, item.start_point.y); let end_point = LayoutPoint::new(item.end_point.x, item.end_point.y); let tile_spacing: LayoutSize = item.tile_spacing.into(); let stretch_ratio: LayoutSize = item.stretch_ratio.into();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.