Bug 1614890 - Implement conic-gradient for WebRender graphics backend. r=gw,nical

Differential Revision: https://phabricator.services.mozilla.com/D61599

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Tim Nguyen 2020-02-12 08:50:06 +00:00
parent db9a041cfb
commit 02ff3004d9
27 changed files with 1056 additions and 15 deletions

View File

@ -1195,6 +1195,17 @@ void DisplayListBuilder::PushRadialGradient(
aStops.Length(), aExtendMode, aTileSize, aTileSpacing);
}
void DisplayListBuilder::PushConicGradient(
const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
bool aIsBackfaceVisible, const wr::LayoutPoint& aCenter, const float aAngle,
const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode,
const wr::LayoutSize aTileSize, const wr::LayoutSize aTileSpacing) {
wr_dp_push_conic_gradient(mWrState, aBounds, MergeClipLeaf(aClip),
aIsBackfaceVisible, &mCurrentSpaceAndClipChain,
aCenter, aAngle, aStops.Elements(), aStops.Length(),
aExtendMode, aTileSize, aTileSpacing);
}
void DisplayListBuilder::PushImage(
const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
bool aIsBackfaceVisible, wr::ImageRendering aFilter, wr::ImageKey aImage,
@ -1317,6 +1328,18 @@ void DisplayListBuilder::PushBorderRadialGradient(
aStops.Elements(), aStops.Length(), aExtendMode, aOutset);
}
void DisplayListBuilder::PushBorderConicGradient(
const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, bool aFill,
const wr::LayoutPoint& aCenter, const float aAngle,
const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode,
const wr::LayoutSideOffsets& aOutset) {
wr_dp_push_border_conic_gradient(
mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
&mCurrentSpaceAndClipChain, aWidths, aFill, aCenter, aAngle,
aStops.Elements(), aStops.Length(), aExtendMode, aOutset);
}
void DisplayListBuilder::PushText(const wr::LayoutRect& aBounds,
const wr::LayoutRect& aClip,
bool aIsBackfaceVisible,

View File

@ -493,6 +493,14 @@ class DisplayListBuilder final {
const wr::LayoutSize aTileSize,
const wr::LayoutSize aTileSpacing);
void PushConicGradient(const wr::LayoutRect& aBounds,
const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
const wr::LayoutPoint& aCenter, const float aAngle,
const nsTArray<wr::GradientStop>& aStops,
wr::ExtendMode aExtendMode,
const wr::LayoutSize aTileSize,
const wr::LayoutSize aTileSpacing);
void PushImage(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
bool aIsBackfaceVisible, wr::ImageRendering aFilter,
wr::ImageKey aImage, bool aPremultipliedAlpha = true,
@ -557,6 +565,13 @@ class DisplayListBuilder final {
const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode,
const wr::LayoutSideOffsets& aOutset);
void PushBorderConicGradient(
const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, bool aFill,
const wr::LayoutPoint& aCenter, const float aAngle,
const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode,
const wr::LayoutSideOffsets& aOutset);
void PushText(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
bool aIsBackfaceVisible, const wr::ColorF& aColor,
wr::FontInstanceKey aFontKey,

View File

@ -3364,6 +3364,69 @@ pub extern "C" fn wr_dp_push_border_radial_gradient(state: &mut WrState,
);
}
#[no_mangle]
pub extern "C" fn wr_dp_push_border_conic_gradient(state: &mut WrState,
rect: LayoutRect,
clip: LayoutRect,
is_backface_visible: bool,
parent: &WrSpaceAndClipChain,
widths: LayoutSideOffsets,
fill: bool,
center: LayoutPoint,
angle: f32,
stops: *const GradientStop,
stops_count: usize,
extend_mode: ExtendMode,
outset: LayoutSideOffsets) {
debug_assert!(unsafe { is_in_main_thread() });
let stops_slice = unsafe { make_slice(stops, stops_count) };
let stops_vector = stops_slice.to_owned();
let slice = SideOffsets2D::new(
widths.top as i32,
widths.right as i32,
widths.bottom as i32,
widths.left as i32,
);
let gradient = state.frame_builder.dl_builder.create_conic_gradient(
center.into(),
angle,
stops_vector,
extend_mode.into()
);
let border_details = BorderDetails::NinePatch(NinePatchBorder {
source: NinePatchBorderSource::ConicGradient(gradient),
width: rect.size.width as i32,
height: rect.size.height as i32,
slice,
fill,
outset: outset.into(),
repeat_horizontal: RepeatMode::Stretch,
repeat_vertical: RepeatMode::Stretch,
});
let space_and_clip = parent.to_webrender(state.pipeline_id);
let prim_info = CommonItemProperties {
clip_rect: clip,
clip_id: space_and_clip.clip_id,
spatial_id: space_and_clip.spatial_id,
flags: prim_flags(is_backface_visible),
hit_info: state.current_tag,
item_key: state.current_item_key,
};
state.frame_builder.dl_builder.push_border(
&prim_info,
rect,
widths.into(),
border_details,
);
}
#[no_mangle]
pub extern "C" fn wr_dp_push_linear_gradient(state: &mut WrState,
rect: LayoutRect,
@ -3453,6 +3516,50 @@ pub extern "C" fn wr_dp_push_radial_gradient(state: &mut WrState,
tile_spacing);
}
#[no_mangle]
pub extern "C" fn wr_dp_push_conic_gradient(state: &mut WrState,
rect: LayoutRect,
clip: LayoutRect,
is_backface_visible: bool,
parent: &WrSpaceAndClipChain,
center: LayoutPoint,
angle: f32,
stops: *const GradientStop,
stops_count: usize,
extend_mode: ExtendMode,
tile_size: LayoutSize,
tile_spacing: LayoutSize) {
debug_assert!(unsafe { is_in_main_thread() });
let stops_slice = unsafe { make_slice(stops, stops_count) };
let stops_vector = stops_slice.to_owned();
let gradient = state.frame_builder
.dl_builder
.create_conic_gradient(center.into(),
angle,
stops_vector,
extend_mode.into());
let space_and_clip = parent.to_webrender(state.pipeline_id);
let prim_info = CommonItemProperties {
clip_rect: clip,
clip_id: space_and_clip.clip_id,
spatial_id: space_and_clip.spatial_id,
flags: prim_flags(is_backface_visible),
hit_info: state.current_tag,
item_key: state.current_item_key,
};
state.frame_builder.dl_builder.push_conic_gradient(
&prim_info,
rect,
gradient,
tile_size,
tile_spacing);
}
#[no_mangle]
pub extern "C" fn wr_dp_push_box_shadow(state: &mut WrState,
_rect: LayoutRect,

View File

@ -89,6 +89,7 @@ FWD_DECLARE_VS_FUNCTION(blend_brush_vs)
FWD_DECLARE_VS_FUNCTION(mix_blend_brush_vs)
FWD_DECLARE_VS_FUNCTION(linear_gradient_brush_vs)
FWD_DECLARE_VS_FUNCTION(radial_gradient_brush_vs)
FWD_DECLARE_VS_FUNCTION(conic_gradient_brush_vs)
FWD_DECLARE_VS_FUNCTION(yuv_brush_vs)
FWD_DECLARE_VS_FUNCTION(opacity_brush_vs)
@ -258,6 +259,7 @@ Fragment blend_brush_fs();
Fragment mix_blend_brush_fs();
Fragment linear_gradient_brush_fs();
Fragment radial_gradient_brush_fs();
Fragment conic_gradient_brush_fs();
Fragment yuv_brush_fs();
Fragment opacity_brush_fs();
Fragment multi_brush_fs(int brush_kind);

View File

@ -0,0 +1,140 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#define VECS_PER_CONIC_GRADIENT_BRUSH 2
#define VECS_PER_SPECIFIC_BRUSH VECS_PER_CONIC_GRADIENT_BRUSH
#define WR_BRUSH_VS_FUNCTION conic_gradient_brush_vs
#define WR_BRUSH_FS_FUNCTION conic_gradient_brush_fs
#include shared,prim_shared,brush
#define V_GRADIENT_ADDRESS flat_varying_highp_int_address_0
#define V_CENTER flat_varying_vec4_0.xy
#define V_ANGLE flat_varying_vec4_0.z
// Size of the gradient pattern's rectangle, used to compute horizontal and vertical
// repetitions. Not to be confused with another kind of repetition of the pattern
// which happens along the gradient stops.
#define V_REPEATED_SIZE flat_varying_vec4_1.xy
// Repetition along the gradient stops.
#define V_GRADIENT_REPEAT flat_varying_vec4_1.z
#define V_POS varying_vec4_0.zw
#ifdef WR_FEATURE_ALPHA_PASS
#define V_LOCAL_POS varying_vec4_0.xy
#define V_TILE_REPEAT flat_varying_vec4_2.xy
#endif
#define PI 3.1415926538
#ifdef WR_VERTEX_SHADER
struct ConicGradient {
vec2 center_point;
float angle;
int extend_mode;
vec2 stretch_size;
};
ConicGradient fetch_gradient(int address) {
vec4 data[2] = fetch_from_gpu_cache_2(address);
return ConicGradient(
data[0].xy,
float(data[0].z),
int(data[1].x),
data[1].yz
);
}
void conic_gradient_brush_vs(
VertexInfo vi,
int prim_address,
RectWithSize local_rect,
RectWithSize segment_rect,
ivec4 prim_user_data,
int specific_resource_address,
mat4 transform,
PictureTask pic_task,
int brush_flags,
vec4 texel_rect
) {
ConicGradient gradient = fetch_gradient(prim_address);
if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) {
V_POS = (vi.local_pos - segment_rect.p0) / segment_rect.size;
V_POS = V_POS * (texel_rect.zw - texel_rect.xy) + texel_rect.xy;
V_POS = V_POS * local_rect.size;
} else {
V_POS = vi.local_pos - local_rect.p0;
}
V_CENTER = gradient.center_point;
V_ANGLE = gradient.angle;
vec2 tile_repeat = local_rect.size / gradient.stretch_size;
V_REPEATED_SIZE = gradient.stretch_size;
V_GRADIENT_ADDRESS = prim_user_data.x;
// Whether to repeat the gradient along the line instead of clamping.
V_GRADIENT_REPEAT = float(gradient.extend_mode != EXTEND_MODE_CLAMP);
#ifdef WR_FEATURE_ALPHA_PASS
V_TILE_REPEAT = tile_repeat;
V_LOCAL_POS = vi.local_pos;
#endif
}
#endif
#ifdef WR_FRAGMENT_SHADER
Fragment conic_gradient_brush_fs() {
#ifdef WR_FEATURE_ALPHA_PASS
// Handle top and left inflated edges (see brush_image).
vec2 local_pos = max(V_POS, vec2(0.0));
// Apply potential horizontal and vertical repetitions.
vec2 pos = mod(local_pos, V_REPEATED_SIZE);
vec2 prim_size = V_REPEATED_SIZE * V_TILE_REPEAT;
// Handle bottom and right inflated edges (see brush_image).
if (local_pos.x >= prim_size.x) {
pos.x = V_REPEATED_SIZE.x;
}
if (local_pos.y >= prim_size.y) {
pos.y = V_REPEATED_SIZE.y;
}
#else
// Apply potential horizontal and vertical repetitions.
vec2 pos = mod(V_POS, V_REPEATED_SIZE);
#endif
vec2 current_dir = pos - V_CENTER;
float current_angle = atan(current_dir.y, current_dir.x) + (PI / 2 - V_ANGLE);
float offset = mod(current_angle / (2 * PI), 1.0);
vec4 color = sample_gradient(V_GRADIENT_ADDRESS,
offset,
V_GRADIENT_REPEAT);
#ifdef WR_FEATURE_ALPHA_PASS
color *= init_transform_fs(V_LOCAL_POS);
#endif
return Fragment(color);
}
#endif
// Undef macro names that could be re-defined by other shaders.
#undef V_GRADIENT_ADDRESS
#undef V_START_POINT
#undef V_SCALE_DIR
#undef V_REPEATED_SIZE
#undef V_GRADIENT_REPEAT
#undef V_POS
#undef V_LOCAL_POS
#undef V_TILE_REPEAT

View File

@ -18,10 +18,11 @@
#define BRUSH_KIND_TEXT 3
#define BRUSH_KIND_LINEAR_GRADIENT 4
#define BRUSH_KIND_RADIAL_GRADIENT 5
#define BRUSH_KIND_BLEND 6
#define BRUSH_KIND_MIX_BLEND 7
#define BRUSH_KIND_YV 8
#define BRUSH_KIND_OPACITY 9
#define BRUSH_KIND_CONIC_GRADIENT 6
#define BRUSH_KIND_BLEND 7
#define BRUSH_KIND_MIX_BLEND 8
#define BRUSH_KIND_YV 9
#define BRUSH_KIND_OPACITY 10
int vecs_per_brush(int brush_kind);
@ -75,6 +76,14 @@ int vecs_per_brush(int brush_kind);
#undef WR_BRUSH_VS_FUNCTION
#undef WR_BRUSH_FS_FUNCTION
#ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH
#include brush_conic_gradient
#endif
#undef VECS_PER_SPECIFIC_BRUSH
#undef WR_BRUSH_VS_FUNCTION
#undef WR_BRUSH_FS_FUNCTION
#ifdef WR_FEATURE_OPACITY_BRUSH
#include brush_opacity
#endif
@ -109,6 +118,11 @@ int vecs_per_brush(int brush_kind) {
case BRUSH_KIND_RADIAL_GRADIENT: return VECS_PER_RADIAL_GRADIENT_BRUSH;
#endif
#ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH
case BRUSH_KIND_CONIC_GRADIENT: return VECS_PER_CONIC_GRADIENT_BRUSH;
#endif
#ifdef WR_FEATURE_OPACITY_BRUSH
case BRUSH_KIND_OPACITY: return VECS_PER_OPACITY_BRUSH;
#endif
@ -173,6 +187,12 @@ void multi_brush_vs(
break;
#endif
#ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH
case BRUSH_KIND_CONIC_GRADIENT:
conic_gradient_brush_vs(BRUSH_VS_PARAMS);
break;
#endif
#ifdef WR_FEATURE_OPACITY_BRUSH
case BRUSH_KIND_OPACITY:
opacity_brush_vs(BRUSH_VS_PARAMS);
@ -213,6 +233,10 @@ Fragment multi_brush_fs(int brush_kind) {
case BRUSH_KIND_RADIAL_GRADIENT: return radial_gradient_brush_fs();
#endif
#ifdef WR_FEATURE_CONIC_GRADIENT_BRUSH
case BRUSH_KIND_CONIC_GRADIENT: return conic_gradient_brush_fs();
#endif
#ifdef WR_FEATURE_OPACITY_BRUSH
case BRUSH_KIND_OPACITY: return opacity_brush_fs();
#endif

View File

@ -56,6 +56,7 @@ pub enum BrushBatchKind {
backdrop_id: RenderTaskId,
},
YuvImage(ImageBufferKind, YuvFormat, ColorDepth, YuvColorSpace, ColorRange),
ConicGradient,
RadialGradient,
LinearGradient,
Opacity,
@ -77,6 +78,7 @@ impl BatchKind {
BatchKind::Brush(BrushBatchKind::Image(..)) => BrushShaderKind::Image,
BatchKind::Brush(BrushBatchKind::LinearGradient) => BrushShaderKind::LinearGradient,
BatchKind::Brush(BrushBatchKind::RadialGradient) => BrushShaderKind::RadialGradient,
BatchKind::Brush(BrushBatchKind::ConicGradient) => BrushShaderKind::ConicGradient,
BatchKind::Brush(BrushBatchKind::Blend) => BrushShaderKind::Blend,
BatchKind::Brush(BrushBatchKind::MixBlend { .. }) => BrushShaderKind::MixBlend,
BatchKind::Brush(BrushBatchKind::YuvImage(..)) => BrushShaderKind::Yuv,
@ -2330,6 +2332,87 @@ impl BatchBuilder {
);
}
}
PrimitiveInstanceKind::ConicGradient { data_handle, ref visible_tiles_range, .. } => {
let prim_data = &ctx.data_stores.conic_grad[data_handle];
let specified_blend_mode = BlendMode::PremultipliedAlpha;
let mut prim_header = PrimitiveHeader {
local_rect: prim_rect,
local_clip_rect: prim_info.combined_local_clip_rect,
specific_prim_address: GpuCacheAddress::INVALID,
transform_id,
};
if visible_tiles_range.is_empty() {
let non_segmented_blend_mode = if !prim_data.opacity.is_opaque ||
prim_info.clip_task_index != ClipTaskIndex::INVALID ||
transform_kind == TransformedRectKind::Complex
{
specified_blend_mode
} else {
BlendMode::None
};
let batch_params = BrushBatchParameters::shared(
BrushBatchKind::ConicGradient,
BatchTextures::no_texture(),
[
prim_data.stops_handle.as_int(gpu_cache),
0,
0,
0,
],
0,
);
prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
let prim_header_index = prim_headers.push(
&prim_header,
z_id,
batch_params.prim_user_data,
);
let segments = if prim_data.brush_segments.is_empty() {
None
} else {
Some(prim_data.brush_segments.as_slice())
};
self.add_segmented_prim_to_batch(
segments,
prim_data.opacity,
&batch_params,
specified_blend_mode,
non_segmented_blend_mode,
batch_features,
prim_header_index,
bounding_rect,
transform_kind,
render_tasks,
z_id,
prim_info.clip_task_index,
prim_vis_mask,
ctx,
);
} else {
let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
self.add_gradient_tiles(
visible_tiles,
&prim_data.stops_handle,
BrushBatchKind::ConicGradient,
specified_blend_mode,
bounding_rect,
clip_task_address.unwrap(),
gpu_cache,
&prim_header,
prim_headers,
z_id,
prim_vis_mask,
);
}
}
PrimitiveInstanceKind::Backdrop { data_handle } => {
let prim_data = &ctx.data_stores.backdrop[data_handle];
let backdrop_pic_index = prim_data.kind.pic_index;

View File

@ -78,10 +78,11 @@ pub enum BrushShaderKind {
Text = 3,
LinearGradient = 4,
RadialGradient = 5,
Blend = 6,
MixBlend = 7,
Yuv = 8,
Opacity = 9,
ConicGradient = 6,
Blend = 7,
MixBlend = 8,
Yuv = 9,
Opacity = 10,
}
#[derive(Debug, Copy, Clone)]

View File

@ -122,7 +122,7 @@ use crate::prim_store::backdrop::Backdrop;
#[cfg(any(feature = "capture", feature = "replay"))]
use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
#[cfg(any(feature = "capture", feature = "replay"))]
use crate::prim_store::gradient::{LinearGradient, RadialGradient};
use crate::prim_store::gradient::{LinearGradient, RadialGradient, ConicGradient};
#[cfg(any(feature = "capture", feature = "replay"))]
use crate::prim_store::image::{Image, YuvImage};
#[cfg(any(feature = "capture", feature = "replay"))]
@ -2714,6 +2714,7 @@ impl TileCacheInstance {
PrimitiveInstanceKind::NormalBorder { .. } |
PrimitiveInstanceKind::LinearGradient { .. } |
PrimitiveInstanceKind::RadialGradient { .. } |
PrimitiveInstanceKind::ConicGradient { .. } |
PrimitiveInstanceKind::Backdrop { .. } => {
// These don't contribute dependencies
}

View File

@ -562,6 +562,222 @@ impl IsVisible for RadialGradient {
////////////////////////////////////////////////////////////////////////////////
/// Conic gradients
/// Hashable conic gradient parameters, for use during prim interning.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Clone, MallocSizeOf, PartialEq)]
pub struct ConicGradientAngle {
pub angle: f32,
}
impl Eq for ConicGradientAngle {}
impl hash::Hash for ConicGradientAngle {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.angle.to_bits().hash(state);
}
}
/// Identifying key for a line decoration.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Clone, Eq, PartialEq, Hash, MallocSizeOf)]
pub struct ConicGradientKey {
pub common: PrimKeyCommonData,
pub extend_mode: ExtendMode,
pub center: PointKey,
pub angle: ConicGradientAngle,
pub stretch_size: SizeKey,
pub stops: Vec<GradientStopKey>,
pub tile_spacing: SizeKey,
pub nine_patch: Option<Box<NinePatchDescriptor>>,
}
impl ConicGradientKey {
pub fn new(
flags: PrimitiveFlags,
prim_size: LayoutSize,
conic_grad: ConicGradient,
) -> Self {
ConicGradientKey {
common: PrimKeyCommonData {
flags,
prim_size: prim_size.into(),
},
extend_mode: conic_grad.extend_mode,
center: conic_grad.center,
angle: conic_grad.angle,
stretch_size: conic_grad.stretch_size,
stops: conic_grad.stops,
tile_spacing: conic_grad.tile_spacing,
nine_patch: conic_grad.nine_patch,
}
}
}
impl InternDebug for ConicGradientKey {}
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(MallocSizeOf)]
pub struct ConicGradientTemplate {
pub common: PrimTemplateCommonData,
pub extend_mode: ExtendMode,
pub center: LayoutPoint,
pub angle: ConicGradientAngle,
pub stretch_size: LayoutSize,
pub tile_spacing: LayoutSize,
pub brush_segments: Vec<BrushSegment>,
pub stops: Vec<GradientStop>,
pub stops_handle: GpuCacheHandle,
}
impl Deref for ConicGradientTemplate {
type Target = PrimTemplateCommonData;
fn deref(&self) -> &Self::Target {
&self.common
}
}
impl DerefMut for ConicGradientTemplate {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.common
}
}
impl From<ConicGradientKey> for ConicGradientTemplate {
fn from(item: ConicGradientKey) -> Self {
let common = PrimTemplateCommonData::with_key_common(item.common);
let mut brush_segments = Vec::new();
if let Some(ref nine_patch) = item.nine_patch {
brush_segments = nine_patch.create_segments(common.prim_size);
}
let stops = item.stops.iter().map(|stop| {
GradientStop {
offset: stop.offset,
color: stop.color.into(),
}
}).collect();
ConicGradientTemplate {
common,
center: item.center.into(),
extend_mode: item.extend_mode,
angle: item.angle,
stretch_size: item.stretch_size.into(),
tile_spacing: item.tile_spacing.into(),
brush_segments,
stops,
stops_handle: GpuCacheHandle::new(),
}
}
}
impl ConicGradientTemplate {
/// Update the GPU cache for a given primitive template. This may be called multiple
/// times per frame, by each primitive reference that refers to this interned
/// template. The initial request call to the GPU cache ensures that work is only
/// done if the cache entry is invalid (due to first use or eviction).
pub fn update(
&mut self,
frame_state: &mut FrameBuildingState,
) {
if let Some(mut request) =
frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
// write_prim_gpu_blocks
request.push([
self.center.x,
self.center.y,
self.angle.angle,
0.0,
]);
request.push([
pack_as_float(self.extend_mode as u32),
self.stretch_size.width,
self.stretch_size.height,
0.0,
]);
// write_segment_gpu_blocks
for segment in &self.brush_segments {
// has to match VECS_PER_SEGMENT
request.write_segment(
segment.local_rect,
segment.extra_data,
);
}
}
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) {
GradientGpuBlockBuilder::build(
false,
&mut request,
&self.stops,
);
}
self.opacity = PrimitiveOpacity::translucent();
}
}
pub type ConicGradientDataHandle = InternHandle<ConicGradient>;
#[derive(Debug, MallocSizeOf)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ConicGradient {
pub extend_mode: ExtendMode,
pub center: PointKey,
pub angle: ConicGradientAngle,
pub stretch_size: SizeKey,
pub stops: Vec<GradientStopKey>,
pub tile_spacing: SizeKey,
pub nine_patch: Option<Box<NinePatchDescriptor>>,
}
impl Internable for ConicGradient {
type Key = ConicGradientKey;
type StoreData = ConicGradientTemplate;
type InternData = PrimitiveSceneData;
}
impl InternablePrimitive for ConicGradient {
fn into_key(
self,
info: &LayoutPrimitiveInfo,
) -> ConicGradientKey {
ConicGradientKey::new(
info.flags,
info.rect.size,
self,
)
}
fn make_instance_kind(
_key: ConicGradientKey,
data_handle: ConicGradientDataHandle,
_prim_store: &mut PrimitiveStore,
_reference_frame_relative_offset: LayoutVector2D,
) -> PrimitiveInstanceKind {
PrimitiveInstanceKind::ConicGradient {
data_handle,
visible_tiles_range: GradientTileRange::empty(),
}
}
}
impl IsVisible for ConicGradient {
fn is_visible(&self) -> bool {
true
}
}
////////////////////////////////////////////////////////////////////////////////
// The gradient entry index for the first color stop
pub const GRADIENT_DATA_FIRST_STOP: usize = 0;
// The gradient entry index for the last color stop
@ -783,4 +999,8 @@ fn test_struct_sizes() {
assert_eq!(mem::size_of::<RadialGradient>(), 72, "RadialGradient size changed");
assert_eq!(mem::size_of::<RadialGradientTemplate>(), 120, "RadialGradientTemplate size changed");
assert_eq!(mem::size_of::<RadialGradientKey>(), 88, "RadialGradientKey size changed");
assert_eq!(mem::size_of::<ConicGradient>(), 72, "ConicGradient size changed");
assert_eq!(mem::size_of::<ConicGradientTemplate>(), 112, "ConicGradientTemplate size changed");
assert_eq!(mem::size_of::<ConicGradientKey>(), 80, "ConicGradientKey size changed");
}

View File

@ -8,7 +8,7 @@ pub use crate::prim_store::backdrop::Backdrop;
pub use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
pub use crate::prim_store::image::{Image, YuvImage};
pub use crate::prim_store::line_dec::{LineDecoration};
pub use crate::prim_store::gradient::{LinearGradient, RadialGradient};
pub use crate::prim_store::gradient::{LinearGradient, RadialGradient, ConicGradient};
pub use crate::prim_store::picture::Picture;
pub use crate::prim_store::text_run::TextRun;

View File

@ -32,7 +32,7 @@ use crate::picture::{PrimitiveList, RecordedDirtyRegion, SurfaceIndex, RetainedT
use crate::prim_store::backdrop::BackdropDataHandle;
use crate::prim_store::borders::{ImageBorderDataHandle, NormalBorderDataHandle};
use crate::prim_store::gradient::{GRADIENT_FP_STOPS, GradientCacheKey, GradientStopKey};
use crate::prim_store::gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle};
use crate::prim_store::gradient::{LinearGradientPrimitive, LinearGradientDataHandle, RadialGradientDataHandle, ConicGradientDataHandle};
use crate::prim_store::image::{ImageDataHandle, ImageInstance, VisibleImageTile, YuvImageDataHandle};
use crate::prim_store::line_dec::LineDecorationDataHandle;
use crate::prim_store::picture::PictureDataHandle;
@ -1426,6 +1426,11 @@ pub enum PrimitiveInstanceKind {
data_handle: RadialGradientDataHandle,
visible_tiles_range: GradientTileRange,
},
ConicGradient {
/// Handle to the common interned data for this primitive.
data_handle: ConicGradientDataHandle,
visible_tiles_range: GradientTileRange,
},
/// Clear out a rect, used for special effects.
Clear {
/// Handle to the common interned data for this primitive.
@ -1623,6 +1628,9 @@ impl PrimitiveInstance {
PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
data_handle.uid()
}
PrimitiveInstanceKind::ConicGradient { data_handle, .. } => {
data_handle.uid()
}
PrimitiveInstanceKind::TextRun { data_handle, .. } => {
data_handle.uid()
}
@ -2240,6 +2248,7 @@ impl PrimitiveStore {
PrimitiveInstanceKind::Image { .. } => debug_colors::BLUE,
PrimitiveInstanceKind::LinearGradient { .. } => debug_colors::PINK,
PrimitiveInstanceKind::RadialGradient { .. } => debug_colors::PINK,
PrimitiveInstanceKind::ConicGradient { .. } => debug_colors::PINK,
PrimitiveInstanceKind::Clear { .. } => debug_colors::CYAN,
PrimitiveInstanceKind::Backdrop { .. } => debug_colors::MEDIUMAQUAMARINE,
};
@ -2559,6 +2568,7 @@ impl PrimitiveStore {
PrimitiveInstanceKind::YuvImage { .. } |
PrimitiveInstanceKind::LinearGradient { .. } |
PrimitiveInstanceKind::RadialGradient { .. } |
PrimitiveInstanceKind::ConicGradient { .. } |
PrimitiveInstanceKind::PushClipChain |
PrimitiveInstanceKind::PopClipChain |
PrimitiveInstanceKind::LineDecoration { .. } |
@ -2702,6 +2712,7 @@ impl PrimitiveStore {
PrimitiveInstanceKind::Image { .. } |
PrimitiveInstanceKind::LinearGradient { .. } |
PrimitiveInstanceKind::RadialGradient { .. } |
PrimitiveInstanceKind::ConicGradient { .. } |
PrimitiveInstanceKind::PushClipChain |
PrimitiveInstanceKind::PopClipChain |
PrimitiveInstanceKind::Clear { .. } |
@ -3327,6 +3338,70 @@ impl PrimitiveStore {
// TODO(gw): Consider whether it's worth doing segment building
// for gradient primitives.
}
PrimitiveInstanceKind::ConicGradient { data_handle, ref mut visible_tiles_range, .. } => {
let prim_data = &mut data_stores.conic_grad[*data_handle];
if prim_data.stretch_size.width >= prim_data.common.prim_size.width &&
prim_data.stretch_size.height >= prim_data.common.prim_size.height {
// We are performing the decomposition on the CPU here, no need to
// have it in the shader.
prim_data.common.may_need_repetition = false;
}
// Update the template this instane references, which may refresh the GPU
// cache with any shared template data.
prim_data.update(frame_state);
if prim_data.tile_spacing != LayoutSize::zero() {
let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize];
let prim_rect = LayoutRect::new(
prim_instance.prim_origin,
prim_data.common.prim_size,
);
let map_local_to_world = SpaceMapper::new_with_target(
ROOT_SPATIAL_NODE_INDEX,
prim_spatial_node_index,
frame_context.global_screen_world_rect,
frame_context.spatial_tree,
);
prim_data.common.may_need_repetition = false;
*visible_tiles_range = decompose_repeated_primitive(
&prim_info.combined_local_clip_rect,
&prim_rect,
prim_info.clipped_world_rect,
&prim_data.stretch_size,
&prim_data.tile_spacing,
frame_state,
&mut scratch.gradient_tiles,
&map_local_to_world,
&mut |_, mut request| {
request.push([
prim_data.center.x,
prim_data.center.y,
prim_data.angle.angle,
0.0,
]);
request.push([
pack_as_float(prim_data.extend_mode as u32),
prim_data.stretch_size.width,
prim_data.stretch_size.height,
0.0,
]);
},
);
if visible_tiles_range.is_empty() {
prim_instance.visibility_info = PrimitiveVisibilityIndex::INVALID;
}
}
// TODO(gw): Consider whether it's worth doing segment building
// for gradient primitives.
}
PrimitiveInstanceKind::Picture { pic_index, segment_instance_index, data_handle, .. } => {
let pic = &mut self.pictures[pic_index.0];
let prim_info = &scratch.prim_info[prim_instance.visibility_info.0 as usize];
@ -3718,6 +3793,7 @@ impl PrimitiveInstance {
PrimitiveInstanceKind::Clear { .. } |
PrimitiveInstanceKind::LinearGradient { .. } |
PrimitiveInstanceKind::RadialGradient { .. } |
PrimitiveInstanceKind::ConicGradient { .. } |
PrimitiveInstanceKind::PushClipChain |
PrimitiveInstanceKind::PopClipChain |
PrimitiveInstanceKind::LineDecoration { .. } |
@ -3863,6 +3939,17 @@ impl PrimitiveInstance {
return false;
}
prim_data.brush_segments.as_slice()
}
PrimitiveInstanceKind::ConicGradient { data_handle, .. } => {
let prim_data = &data_stores.conic_grad[data_handle];
// TODO: This is quite messy - once we remove legacy primitives we
// can change this to be a tuple match on (instance, template)
if prim_data.brush_segments.is_empty() {
return false;
}
prim_data.brush_segments.as_slice()
}
};

View File

@ -805,6 +805,7 @@ impl BackendProfileCounters {
//TODO: generate this by a macro
intern: InternProfileCounters {
prim: ResourceProfileCounter::new("Interned primitives", None, None),
conic_grad: ResourceProfileCounter::new("Interned conic gradients", None, None),
image: ResourceProfileCounter::new("Interned images", None, None),
image_border: ResourceProfileCounter::new("Interned image borders", None, None),
line_decoration: ResourceProfileCounter::new("Interned line decorations", None, None),

View File

@ -309,6 +309,10 @@ impl DataStores {
let prim_data = &self.radial_grad[data_handle];
&prim_data.common
}
PrimitiveInstanceKind::ConicGradient { data_handle, .. } => {
let prim_data = &self.conic_grad[data_handle];
&prim_data.common
}
PrimitiveInstanceKind::TextRun { data_handle, .. } => {
let prim_data = &self.text_run[data_handle];
&prim_data.common

View File

@ -153,6 +153,10 @@ const GPU_TAG_BRUSH_RADIAL_GRADIENT: GpuProfileTag = GpuProfileTag {
label: "B_RadialGradient",
color: debug_colors::LIGHTPINK,
};
const GPU_TAG_BRUSH_CONIC_GRADIENT: GpuProfileTag = GpuProfileTag {
label: "B_ConicGradient",
color: debug_colors::GREEN,
};
const GPU_TAG_BRUSH_YUV_IMAGE: GpuProfileTag = GpuProfileTag {
label: "B_YuvImage",
color: debug_colors::DARKGREEN,
@ -255,6 +259,7 @@ impl BatchKind {
BrushBatchKind::Blend => "Brush (Blend)",
BrushBatchKind::MixBlend { .. } => "Brush (Composite)",
BrushBatchKind::YuvImage(..) => "Brush (YuvImage)",
BrushBatchKind::ConicGradient => "Brush (ConicGradient)",
BrushBatchKind::RadialGradient => "Brush (RadialGradient)",
BrushBatchKind::LinearGradient => "Brush (LinearGradient)",
BrushBatchKind::Opacity => "Brush (Opacity)",
@ -274,6 +279,7 @@ impl BatchKind {
BrushBatchKind::Blend => GPU_TAG_BRUSH_BLEND,
BrushBatchKind::MixBlend { .. } => GPU_TAG_BRUSH_MIXBLEND,
BrushBatchKind::YuvImage(..) => GPU_TAG_BRUSH_YUV_IMAGE,
BrushBatchKind::ConicGradient => GPU_TAG_BRUSH_CONIC_GRADIENT,
BrushBatchKind::RadialGradient => GPU_TAG_BRUSH_RADIAL_GRADIENT,
BrushBatchKind::LinearGradient => GPU_TAG_BRUSH_LINEAR_GRADIENT,
BrushBatchKind::Opacity => GPU_TAG_BRUSH_OPACITY,
@ -6865,6 +6871,7 @@ fn should_skip_batch(kind: &BatchKind, flags: DebugFlags) -> bool {
BatchKind::TextRun(_) => {
flags.contains(DebugFlags::DISABLE_TEXT_PRIMS)
}
BatchKind::Brush(BrushBatchKind::ConicGradient) |
BatchKind::Brush(BrushBatchKind::RadialGradient) |
BatchKind::Brush(BrushBatchKind::LinearGradient) => {
flags.contains(DebugFlags::DISABLE_GRADIENT_PRIMS)

View File

@ -17,7 +17,7 @@ use crate::internal_types::{FastHashMap, FastHashSet};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use crate::prim_store::backdrop::Backdrop;
use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
use crate::prim_store::gradient::{LinearGradient, RadialGradient};
use crate::prim_store::gradient::{LinearGradient, RadialGradient, ConicGradient};
use crate::prim_store::image::{Image, YuvImage};
use crate::prim_store::line_dec::LineDecoration;
use crate::prim_store::picture::Picture;

View File

@ -31,7 +31,7 @@ use crate::prim_store::{register_prim_chase_id, get_line_decoration_size};
use crate::prim_store::{SpaceSnapper};
use crate::prim_store::backdrop::Backdrop;
use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
use crate::prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams};
use crate::prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams, ConicGradient, ConicGradientAngle};
use crate::prim_store::image::{Image, YuvImage};
use crate::prim_store::line_dec::{LineDecoration, LineDecorationCacheKey};
use crate::prim_store::picture::{Picture, PictureCompositeKey, PictureKey};
@ -1311,6 +1311,37 @@ impl<'a> SceneBuilder<'a> {
prim_key_kind,
);
}
DisplayItem::ConicGradient(ref info) => {
let (layout, unsnapped_rect, clip_and_scroll) = self.process_common_properties_with_bounds(
&info.common,
&info.bounds,
apply_pipeline_clip,
);
let tile_size = process_repeat_size(
&layout.rect,
&unsnapped_rect,
info.tile_size,
);
let prim_key_kind = self.create_conic_gradient_prim(
&layout,
info.gradient.center,
info.gradient.angle,
item.gradient_stops(),
info.gradient.extend_mode,
tile_size,
info.tile_spacing,
None,
);
self.add_nonshadowable_primitive(
clip_and_scroll,
&layout,
Vec::new(),
prim_key_kind,
);
}
DisplayItem::BoxShadow(ref info) => {
let (layout, _, clip_and_scroll) = self.process_common_properties_with_bounds(
&info.common,
@ -2893,6 +2924,25 @@ impl<'a> SceneBuilder<'a> {
Some(Box::new(nine_patch)),
);
self.add_nonshadowable_primitive(
clip_and_scroll,
info,
Vec::new(),
prim,
);
}
NinePatchBorderSource::ConicGradient(gradient) => {
let prim = self.create_conic_gradient_prim(
&info,
gradient.center,
gradient.angle,
gradient_stops,
gradient.extend_mode,
LayoutSize::new(border.height as f32, border.width as f32),
LayoutSize::zero(),
Some(Box::new(nine_patch)),
);
self.add_nonshadowable_primitive(
clip_and_scroll,
info,
@ -3013,6 +3063,38 @@ impl<'a> SceneBuilder<'a> {
}
}
pub fn create_conic_gradient_prim(
&mut self,
info: &LayoutPrimitiveInfo,
center: LayoutPoint,
angle: f32,
stops: ItemRange<GradientStop>,
extend_mode: ExtendMode,
stretch_size: LayoutSize,
mut tile_spacing: LayoutSize,
nine_patch: Option<Box<NinePatchDescriptor>>,
) -> ConicGradient {
let mut prim_rect = info.rect;
simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
let stops = stops.iter().map(|stop| {
GradientStopKey {
offset: stop.offset,
color: stop.color.into(),
}
}).collect();
ConicGradient {
extend_mode,
center: center.into(),
angle: ConicGradientAngle { angle },
stretch_size: stretch_size.into(),
tile_spacing: tile_spacing.into(),
nine_patch,
stops,
}
}
pub fn add_text(
&mut self,
clip_and_scroll: ScrollNodeAndClipChain,

View File

@ -536,6 +536,7 @@ pub struct Shaders {
brush_blend: BrushShader,
brush_mix_blend: BrushShader,
brush_yuv_image: Vec<Option<BrushShader>>,
brush_conic_gradient: BrushShader,
brush_radial_gradient: BrushShader,
brush_linear_gradient: BrushShader,
brush_opacity: BrushShader,
@ -614,6 +615,20 @@ impl Shaders {
use_pixel_local_storage,
)?;
let brush_conic_gradient = BrushShader::new(
"brush_conic_gradient",
device,
if options.enable_dithering {
&[DITHERING_FEATURE]
} else {
&[]
},
options.precache_flags,
false /* advanced blend */,
false /* dual source */,
use_pixel_local_storage,
)?;
let brush_radial_gradient = BrushShader::new(
"brush_radial_gradient",
device,
@ -904,6 +919,7 @@ impl Shaders {
brush_blend,
brush_mix_blend,
brush_yuv_image,
brush_conic_gradient,
brush_radial_gradient,
brush_linear_gradient,
brush_opacity,
@ -953,6 +969,9 @@ impl Shaders {
BrushBatchKind::MixBlend { .. } => {
&mut self.brush_mix_blend
}
BrushBatchKind::ConicGradient => {
&mut self.brush_conic_gradient
}
BrushBatchKind::RadialGradient => {
&mut self.brush_radial_gradient
}
@ -990,6 +1009,7 @@ impl Shaders {
self.brush_solid.deinit(device);
self.brush_blend.deinit(device);
self.brush_mix_blend.deinit(device);
self.brush_conic_gradient.deinit(device);
self.brush_radial_gradient.deinit(device);
self.brush_linear_gradient.deinit(device);
self.brush_opacity.deinit(device);

View File

@ -1175,6 +1175,7 @@ macro_rules! enumerate_interners {
line_decoration: LineDecoration,
linear_grad: LinearGradient,
radial_grad: RadialGradient,
conic_grad: ConicGradient,
picture: Picture,
text_run: TextRun,
filter_data: FilterDataIntern,

View File

@ -136,6 +136,7 @@ pub enum DisplayItem {
PushShadow(PushShadowDisplayItem),
Gradient(GradientDisplayItem),
RadialGradient(RadialGradientDisplayItem),
ConicGradient(ConicGradientDisplayItem),
Image(ImageDisplayItem),
RepeatingImage(RepeatingImageDisplayItem),
YuvImage(YuvImageDisplayItem),
@ -183,6 +184,7 @@ pub enum DebugDisplayItem {
PushShadow(PushShadowDisplayItem),
Gradient(GradientDisplayItem),
RadialGradient(RadialGradientDisplayItem),
ConicGradient(ConicGradientDisplayItem),
Image(ImageDisplayItem),
RepeatingImage(RepeatingImageDisplayItem),
YuvImage(YuvImageDisplayItem),
@ -444,6 +446,7 @@ pub enum NinePatchBorderSource {
Image(ImageKey),
Gradient(Gradient),
RadialGradient(RadialGradient),
ConicGradient(ConicGradient),
}
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
@ -633,6 +636,15 @@ pub struct RadialGradient {
pub extend_mode: ExtendMode,
} // IMPLICIT stops: Vec<GradientStop>
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct ConicGradient {
pub center: LayoutPoint,
pub angle: f32,
pub start_offset: f32,
pub end_offset: f32,
pub extend_mode: ExtendMode,
} // IMPLICIT stops: Vec<GradientStop>
/// Just an abstraction for bundling up a bunch of clips into a "super clip".
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct ClipChainItem {
@ -652,6 +664,18 @@ pub struct RadialGradientDisplayItem {
pub tile_spacing: LayoutSize,
}
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct ConicGradientDisplayItem {
pub common: CommonItemProperties,
/// The area to tile the gradient over (first tile starts at origin of this rect)
// FIXME: this should ideally just be `tile_origin` here, with the clip_rect
// defining the bounds of the item. Needs non-trivial backend changes.
pub bounds: LayoutRect,
pub gradient: ConicGradient,
pub tile_size: LayoutSize,
pub tile_spacing: LayoutSize,
}
/// Renders a filtered region of its backdrop
#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Serialize, PeekPoke)]
pub struct BackdropFilterDisplayItem {
@ -1462,6 +1486,7 @@ impl DisplayItem {
DisplayItem::HitTest(..) => "hit_test",
DisplayItem::Clip(..) => "clip",
DisplayItem::ClipChain(..) => "clip_chain",
DisplayItem::ConicGradient(..) => "conic_gradient",
DisplayItem::Gradient(..) => "gradient",
DisplayItem::Iframe(..) => "iframe",
DisplayItem::Image(..) => "image",

View File

@ -684,6 +684,7 @@ impl Serialize for BuiltDisplayList {
Real::BoxShadow(v) => Debug::BoxShadow(v),
Real::Gradient(v) => Debug::Gradient(v),
Real::RadialGradient(v) => Debug::RadialGradient(v),
Real::ConicGradient(v) => Debug::ConicGradient(v),
Real::Iframe(v) => Debug::Iframe(v),
Real::PushReferenceFrame(v) => Debug::PushReferenceFrame(v),
Real::PushStackingContext(v) => Debug::PushStackingContext(v),
@ -790,6 +791,7 @@ impl<'de> Deserialize<'de> for BuiltDisplayList {
Debug::BoxShadow(v) => Real::BoxShadow(v),
Debug::Gradient(v) => Real::Gradient(v),
Debug::RadialGradient(v) => Real::RadialGradient(v),
Debug::ConicGradient(v) => Real::ConicGradient(v),
Debug::PushStackingContext(v) => Real::PushStackingContext(v),
Debug::PushShadow(v) => Real::PushShadow(v),
Debug::BackdropFilter(v) => Real::BackdropFilter(v),
@ -1205,6 +1207,21 @@ impl DisplayListBuilder {
gradient
}
/// NOTE: gradients must be pushed in the order they're created
/// because create_gradient stores the stops in anticipation.
pub fn create_conic_gradient(
&mut self,
center: LayoutPoint,
angle: f32,
stops: Vec<di::GradientStop>,
extend_mode: di::ExtendMode,
) -> di::ConicGradient {
let mut builder = GradientBuilder::with_stops(stops);
let gradient = builder.conic_gradient(center, angle, extend_mode);
self.push_stops(builder.stops());
gradient
}
pub fn push_border(
&mut self,
common: &di::CommonItemProperties,
@ -1302,6 +1319,28 @@ impl DisplayListBuilder {
self.push_item(&item);
}
/// Pushes a conic gradient to be displayed.
///
/// See [`push_gradient`](#method.push_gradient) for explanation.
pub fn push_conic_gradient(
&mut self,
common: &di::CommonItemProperties,
bounds: LayoutRect,
gradient: di::ConicGradient,
tile_size: LayoutSize,
tile_spacing: LayoutSize,
) {
let item = di::DisplayItem::ConicGradient(di::ConicGradientDisplayItem {
common: *common,
bounds,
gradient,
tile_size,
tile_spacing,
});
self.push_item(&item);
}
pub fn push_reference_frame(
&mut self,
origin: LayoutPoint,

View File

@ -99,6 +99,27 @@ impl GradientBuilder {
}
}
/// Produce a conic gradient, normalize the stops.
pub fn conic_gradient(
&mut self,
center: LayoutPoint,
angle: f32,
extend_mode: di::ExtendMode,
) -> di::ConicGradient {
// XXX(ntim): Possibly handle negative angles ?
let (start_offset, end_offset) =
self.normalize(extend_mode);
di::ConicGradient {
center,
angle,
start_offset,
end_offset,
extend_mode,
}
}
/// Gradients can be defined with stops outside the range of [0, 1]
/// when this happens the gradient needs to be normalized by adjusting
/// the gradient stops and gradient line into an equivalent gradient

View File

@ -0,0 +1,14 @@
---
root:
items:
- type: clip
bounds: [50, 50, 300, 300]
complex:
- rect: [50, 50, 300, 300]
radius: 300
items:
- type: conic-gradient
bounds: 50 50 300 300
center: 150 150
angle: 0.0
stops: [0.0, red, 0.16666, yellow, 0.33333, green, 0.5, [0,255,255,1], 0.66666, blue, 0.83333, [255,0,255,1], 1.0, red]

View File

@ -0,0 +1,14 @@
---
root:
items:
- type: conic-gradient
bounds: 50 50 300 300
center: 150 150
# angle: 6.283185307179586
# angle: 5.497787143782138
# angle: 4.71238898038469
# angle: 3.141592653589793
# angle: 1.5707963267948966
# angle: 0.7853981633974483
angle: 0.0
stops: [0.0, red, 1.0, yellow]

View File

@ -0,0 +1,8 @@
---
root:
items:
- type: conic-gradient
bounds: 50 50 300 300
center: 150 150
angle: 0.7853981633974483
stops: [0.0, red, 1.0, yellow]

View File

@ -925,6 +925,33 @@ impl YamlFrameReader {
dl.create_radial_gradient(center, radius, stops, extend_mode)
}
fn to_conic_gradient(&mut self, dl: &mut DisplayListBuilder, item: &Yaml) -> ConicGradient {
let center = item["center"].as_point().expect("conic gradient must have center");
let angle = item["angle"].as_force_f32().expect("conic gradient must have an angle");
let stops = item["stops"]
.as_vec()
.expect("conic gradient must have stops")
.chunks(2)
.map(|chunk| {
GradientStop {
offset: chunk[0]
.as_force_f32()
.expect("gradient stop offset is not f32"),
color: chunk[1]
.as_colorf()
.expect("gradient stop color is not color"),
}
})
.collect::<Vec<_>>();
let extend_mode = if item["repeat"].as_bool().unwrap_or(false) {
ExtendMode::Repeat
} else {
ExtendMode::Clamp
};
dl.create_conic_gradient(center, angle, stops, extend_mode)
}
fn handle_rect(
&mut self,
dl: &mut DisplayListBuilder,
@ -1090,6 +1117,33 @@ impl YamlFrameReader {
);
}
fn handle_conic_gradient(
&mut self,
dl: &mut DisplayListBuilder,
item: &Yaml,
info: &mut CommonItemProperties,
) {
let bounds_key = if item["type"].is_badvalue() {
"conic-gradient"
} else {
"bounds"
};
let bounds = item[bounds_key]
.as_rect()
.expect("conic gradient must have bounds");
let gradient = self.to_conic_gradient(dl, item);
let tile_size = item["tile-size"].as_size().unwrap_or(bounds.size);
let tile_spacing = item["tile-spacing"].as_size().unwrap_or(LayoutSize::zero());
dl.push_conic_gradient(
&info,
bounds,
gradient,
tile_size,
tile_spacing,
);
}
fn handle_border(
&mut self,
dl: &mut DisplayListBuilder,
@ -1168,7 +1222,7 @@ impl YamlFrameReader {
do_aa,
}))
}
"image" | "gradient" | "radial-gradient" => {
"image" | "gradient" | "radial-gradient" | "conic-gradient" => {
let image_width = item["image-width"]
.as_i64()
.unwrap_or(bounds.size.width as i64);
@ -1221,7 +1275,10 @@ impl YamlFrameReader {
"radial-gradient" => {
let gradient = self.to_radial_gradient(dl, item);
NinePatchBorderSource::RadialGradient(gradient)
}
"conic-gradient" => {
let gradient = self.to_conic_gradient(dl, item);
NinePatchBorderSource::ConicGradient(gradient)
}
_ => unreachable!("Unexpected border type"),
};
@ -1593,6 +1650,7 @@ impl YamlFrameReader {
"border",
"gradient",
"radial-gradient",
"conic-gradient"
];
for shorthand in shorthands.iter() {
@ -1699,6 +1757,7 @@ impl YamlFrameReader {
"border" => self.handle_border(dl, wrench, item, &mut info),
"gradient" => self.handle_gradient(dl, item, &mut info),
"radial-gradient" => self.handle_radial_gradient(dl, item, &mut info),
"conic-gradient" => self.handle_conic_gradient(dl, item, &mut info),
"box-shadow" => self.handle_box_shadow(dl, item, &mut info),
"iframe" => self.handle_iframe(dl, item, &mut info),
"stacking-context" => {

View File

@ -526,6 +526,29 @@ fn radial_gradient_to_yaml(
bool_node(table, "repeat", gradient.extend_mode == ExtendMode::Repeat);
}
fn conic_gradient_to_yaml(
table: &mut Table,
gradient: &webrender::api::ConicGradient,
stops_range: ItemRange<GradientStop>,
) {
point_node(table, "center", &gradient.center);
f32_node(table, "angle", gradient.angle);
let first_offset = gradient.start_offset;
let last_offset = gradient.end_offset;
let stops_delta = last_offset - first_offset;
assert!(first_offset <= last_offset);
let mut denormalized_stops = vec![];
for stop in stops_range {
let denormalized_stop = (stop.offset * stops_delta) + first_offset;
denormalized_stops.push(Yaml::Real(denormalized_stop.to_string()));
denormalized_stops.push(Yaml::String(color_to_string(stop.color)));
}
yaml_node(table, "stops", Yaml::Array(denormalized_stops));
bool_node(table, "repeat", gradient.extend_mode == ExtendMode::Repeat);
}
enum CachedFont {
Native(NativeFontHandle, Option<PathBuf>),
Raw(Option<Vec<u8>>, u32, Option<PathBuf>),
@ -1191,6 +1214,14 @@ impl YamlFrameWriter {
base.gradient_stops(),
);
}
NinePatchBorderSource::ConicGradient(gradient) => {
str_node(&mut v, "border-type", "conic-gradient");
conic_gradient_to_yaml(
&mut v,
&gradient,
base.gradient_stops(),
);
}
}
i32_node(&mut v, "image-width", details.width);
@ -1273,6 +1304,18 @@ impl YamlFrameWriter {
base.gradient_stops(),
);
}
DisplayItem::ConicGradient(item) => {
str_node(&mut v, "type", "conic-gradient");
rect_node(&mut v, "bounds", &item.bounds);
common_node(&mut v, clip_id_mapper, &item.common);
size_node(&mut v, "tile-size", &item.tile_size);
size_node(&mut v, "tile-spacing", &item.tile_spacing);
conic_gradient_to_yaml(
&mut v,
&item.gradient,
base.gradient_stops(),
);
}
DisplayItem::Iframe(item) => {
str_node(&mut v, "type", "iframe");
rect_node(&mut v, "bounds", &item.bounds);