/* 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/. */
#[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] #[derive(MallocSizeOf)] #[derive(Debug)] pubstruct RadialGradientTemplate { pub common: PrimTemplateCommonData, pub extend_mode: ExtendMode, pub params: RadialGradientParams, pub center: LayoutPoint, /// Per-axis fraction of `common.prim_size` covered by one tile of the /// gradient pattern. Multiply by `common.prim_size` at use to recover the /// absolute stretch_size. pub stretch_ratio: LayoutSize, pub tile_spacing: LayoutSize, pub border_nine_patch: Option<Box<NinePatchDescriptor>>, pub stops_opacity: PrimitiveOpacity, pub stops: Vec<GradientStop>,
}
impl PatternBuilder for RadialGradientTemplate { fn build(
&self,
_sub_rect: Option<DeviceRect>,
offset: LayoutVector2D,
ctx: &PatternBuilderContext,
state: &mut PatternBuilderState,
) -> Pattern { // The scaling parameter is used to compensate for when we reduce the size // of the render task for cached gradients. Here we aren't applying any. let no_scale = DeviceVector2D::one();
// RadialGradientTemplate stores the center point relative to the primitive // origin, but the shader works with start/end points in "proper" layout // coordinates (relative to the primitive's spatial node). let center = self.center.cast_unit() + ctx.prim_origin.to_vector() + offset;
impl From<RadialGradientKey> for RadialGradientTemplate { fn from(item: RadialGradientKey) -> Self { 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);
/// Avoid invoking the radial gradient shader on large areas where the color is /// constant. /// /// If the extend mode is set to clamp, the "interesting" part /// of the gradient is only in the bounds of the gradient's ellipse, and the rest /// is the color of the last gradient stop. /// /// Sometimes we run into radial gradient with a small radius compared to the /// primitive bounds, which means a large area of the primitive is a constant color /// This function tries to detect that, potentially shrink the gradient primitive to only /// the useful part and if needed insert solid color primitives around the gradient where /// parts of it have been removed. /// /// If the radial gradient is split into multiple primitives, we must prevent anti-aliasing /// from being appplied at the edges connecting these primitives to prevent seams. This is /// done by masking out sides in `aa_mask` for the central gradient primitive and providing /// an edge mask for each extracted solid primitive. pubfn optimize_radial_gradient(
prim_rect: &mut LayoutRect,
stretch_size: &mut LayoutSize,
center: &mut LayoutPoint,
tile_spacing: &mut LayoutSize,
aa_mask: &mut EdgeMask,
clip_rect: &LayoutRect,
radius: LayoutSize,
end_offset: f32,
extend_mode: ExtendMode,
stops: &[GradientStopKey],
solid_parts: &mutdyn FnMut(&LayoutRect, ColorU, EdgeMask),
) { let offset = apply_gradient_local_clip(
prim_rect,
stretch_size,
tile_spacing,
clip_rect
);
*center += offset;
if extend_mode != ExtendMode::Clamp || stops.is_empty() { return;
}
// Bounding box of the "interesting" part of the gradient. let min = prim_rect.min + center.to_vector() - radius.to_vector() * end_offset; let max = prim_rect.min + center.to_vector() + radius.to_vector() * end_offset;
// The (non-repeated) gradient primitive rect. let gradient_rect = LayoutRect::from_origin_and_size(
prim_rect.min,
*stretch_size,
);
// How much internal margin between the primitive bounds and the gradient's // bounding rect (areas that are a constant color). letmut l = (min.x - gradient_rect.min.x).max(0.0).floor(); letmut t = (min.y - gradient_rect.min.y).max(0.0).floor(); letmut r = (gradient_rect.max.x - max.x).max(0.0).floor(); letmut b = (gradient_rect.max.y - max.y).max(0.0).floor();
if bg_color.a != 0 && is_tiled { // If the primitive has repetitions, it's not enough to insert solid rects around it, // so bail out. return;
}
// If the background is fully transparent, shrinking the primitive bounds as much as possible // is always a win. If the background is not transparent, we have to insert solid rectangles // around the shrunk parts. // If the background is transparent and the primitive is tiled, the optimization may introduce // tile spacing which forces the tiling to be manually decomposed. // Either way, don't bother optimizing unless it saves a significant amount of pixels. if bg_color.a != 0 || (is_tiled && tile_spacing.is_empty()) { let threshold = 128.0; if l < threshold { l = 0.0 } if t < threshold { t = 0.0 } if r < threshold { r = 0.0 } if b < threshold { b = 0.0 }
}
if l + t + r + b == 0.0 { // No adjustment to make; return;
}
// Insert solid rectangles around the gradient, in the places where the primitive will be // shrunk. if bg_color.a != 0 { if l != 0.0 && t != 0.0 { let solid_rect = LayoutRect::from_origin_and_size(
gradient_rect.min,
size2(l, t),
);
solid_parts(&solid_rect, bg_color, EdgeMask::LEFT | EdgeMask::TOP);
}
if l != 0.0 && b != 0.0 { let solid_rect = LayoutRect::from_origin_and_size(
gradient_rect.bottom_left() - vec2(0.0, b),
size2(l, b),
);
solid_parts(&solid_rect, bg_color, EdgeMask::LEFT | EdgeMask::BOTTOM);
}
if t != 0.0 && r != 0.0 { let solid_rect = LayoutRect::from_origin_and_size(
gradient_rect.top_right() - vec2(r, 0.0),
size2(r, t),
);
solid_parts(&solid_rect, bg_color, EdgeMask::TOP | EdgeMask::RIGHT);
}
if r != 0.0 && b != 0.0 { let solid_rect = LayoutRect::from_origin_and_size(
gradient_rect.bottom_right() - vec2(r, b),
size2(r, b),
);
solid_parts(&solid_rect, bg_color, EdgeMask::RIGHT | EdgeMask::BOTTOM);
}
if l != 0.0 { let solid_rect = LayoutRect::from_origin_and_size(
gradient_rect.min + vec2(0.0, t),
size2(l, gradient_rect.height() - t - b),
); letmut solid_aa = EdgeMask::LEFT;
solid_aa.set(EdgeMask::TOP, t == 0.0);
solid_aa.set(EdgeMask::BOTTOM, b == 0.0);
solid_parts(&solid_rect, bg_color, solid_aa);
aa_mask.remove(EdgeMask::LEFT);
}
if r != 0.0 { let solid_rect = LayoutRect::from_origin_and_size(
gradient_rect.top_right() + vec2(-r, t),
size2(r, gradient_rect.height() - t - b),
); letmut solid_aa = EdgeMask::RIGHT;
solid_aa.set(EdgeMask::TOP, t == 0.0);
solid_aa.set(EdgeMask::BOTTOM, b == 0.0);
solid_parts(&solid_rect, bg_color, solid_aa);
aa_mask.remove(EdgeMask::RIGHT);
}
if t != 0.0 { let solid_rect = LayoutRect::from_origin_and_size(
gradient_rect.min + vec2(l, 0.0),
size2(gradient_rect.width() - l - r, t),
); letmut solid_aa = EdgeMask::TOP;
solid_aa.set(EdgeMask::LEFT, l == 0.0);
solid_aa.set(EdgeMask::RIGHT, r == 0.0);
solid_parts(&solid_rect, bg_color, solid_aa);
aa_mask.remove(EdgeMask::TOP);
}
if b != 0.0 { let solid_rect = LayoutRect::from_origin_and_size(
gradient_rect.bottom_left() + vec2(l, -b),
size2(gradient_rect.width() - l - r, b),
); letmut solid_aa = EdgeMask::BOTTOM;
solid_aa.set(EdgeMask::LEFT, l == 0.0);
solid_aa.set(EdgeMask::RIGHT, r == 0.0);
solid_parts(&solid_rect, bg_color, solid_aa);
aa_mask.remove(EdgeMask::BOTTOM);
}
}
// Shrink the gradient primitive.
prim_rect.min.x += l;
prim_rect.min.y += t;
stretch_size.width -= l + r;
stretch_size.height -= b + t;
center.x -= l;
center.y -= t;
tile_spacing.width += l + r;
tile_spacing.height += t + b;
}
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.