diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp index 7b89d4b2a7e1..f77343ecac25 100644 --- a/gfx/webrender_bindings/WebRenderAPI.cpp +++ b/gfx/webrender_bindings/WebRenderAPI.cpp @@ -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& 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& 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, diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h index 1d1d3af1650f..702659a5ffeb 100644 --- a/gfx/webrender_bindings/WebRenderAPI.h +++ b/gfx/webrender_bindings/WebRenderAPI.h @@ -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& 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& 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& 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, diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs index 50eeb6e2801b..d1cf7643f82e 100644 --- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -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, diff --git a/gfx/wr/webrender/res/brush.glsl b/gfx/wr/webrender/res/brush.glsl index 5a0f09b419bc..034fe61609ad 100644 --- a/gfx/wr/webrender/res/brush.glsl +++ b/gfx/wr/webrender/res/brush.glsl @@ -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); diff --git a/gfx/wr/webrender/res/brush_conic_gradient.glsl b/gfx/wr/webrender/res/brush_conic_gradient.glsl new file mode 100644 index 000000000000..f35e62367b78 --- /dev/null +++ b/gfx/wr/webrender/res/brush_conic_gradient.glsl @@ -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 diff --git a/gfx/wr/webrender/res/brush_multi.glsl b/gfx/wr/webrender/res/brush_multi.glsl index 2d13bda798ca..8188c70da603 100644 --- a/gfx/wr/webrender/res/brush_multi.glsl +++ b/gfx/wr/webrender/res/brush_multi.glsl @@ -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 diff --git a/gfx/wr/webrender/src/batch.rs b/gfx/wr/webrender/src/batch.rs index 8835bd9d8f1f..9032f698750a 100644 --- a/gfx/wr/webrender/src/batch.rs +++ b/gfx/wr/webrender/src/batch.rs @@ -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; diff --git a/gfx/wr/webrender/src/gpu_types.rs b/gfx/wr/webrender/src/gpu_types.rs index 2c892b78dd35..3dffb0a40b67 100644 --- a/gfx/wr/webrender/src/gpu_types.rs +++ b/gfx/wr/webrender/src/gpu_types.rs @@ -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)] diff --git a/gfx/wr/webrender/src/picture.rs b/gfx/wr/webrender/src/picture.rs index a8b94fdb179b..5b61fac0855e 100644 --- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -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 } diff --git a/gfx/wr/webrender/src/prim_store/gradient.rs b/gfx/wr/webrender/src/prim_store/gradient.rs index faf66e5dab3e..2b5b328ba3d9 100644 --- a/gfx/wr/webrender/src/prim_store/gradient.rs +++ b/gfx/wr/webrender/src/prim_store/gradient.rs @@ -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(&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, + pub tile_spacing: SizeKey, + pub nine_patch: Option>, +} + +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, + pub stops: Vec, + 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 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; + +#[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, + pub tile_spacing: SizeKey, + pub nine_patch: Option>, +} + +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::(), 72, "RadialGradient size changed"); assert_eq!(mem::size_of::(), 120, "RadialGradientTemplate size changed"); assert_eq!(mem::size_of::(), 88, "RadialGradientKey size changed"); + + assert_eq!(mem::size_of::(), 72, "ConicGradient size changed"); + assert_eq!(mem::size_of::(), 112, "ConicGradientTemplate size changed"); + assert_eq!(mem::size_of::(), 80, "ConicGradientKey size changed"); } diff --git a/gfx/wr/webrender/src/prim_store/interned.rs b/gfx/wr/webrender/src/prim_store/interned.rs index 3cabbd70a920..50337103c315 100644 --- a/gfx/wr/webrender/src/prim_store/interned.rs +++ b/gfx/wr/webrender/src/prim_store/interned.rs @@ -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; diff --git a/gfx/wr/webrender/src/prim_store/mod.rs b/gfx/wr/webrender/src/prim_store/mod.rs index 12c3a0744f5e..dc7c9ecf4f00 100644 --- a/gfx/wr/webrender/src/prim_store/mod.rs +++ b/gfx/wr/webrender/src/prim_store/mod.rs @@ -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() } }; diff --git a/gfx/wr/webrender/src/profiler.rs b/gfx/wr/webrender/src/profiler.rs index 6ec8a9cef1b3..e28d70ccde70 100644 --- a/gfx/wr/webrender/src/profiler.rs +++ b/gfx/wr/webrender/src/profiler.rs @@ -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), diff --git a/gfx/wr/webrender/src/render_backend.rs b/gfx/wr/webrender/src/render_backend.rs index ba800e455af6..e3102d5adb29 100644 --- a/gfx/wr/webrender/src/render_backend.rs +++ b/gfx/wr/webrender/src/render_backend.rs @@ -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 diff --git a/gfx/wr/webrender/src/renderer.rs b/gfx/wr/webrender/src/renderer.rs index bfeeceaf51a9..4f5671ccba09 100644 --- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -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) diff --git a/gfx/wr/webrender/src/scene_builder_thread.rs b/gfx/wr/webrender/src/scene_builder_thread.rs index 62092977785f..186e62223375 100644 --- a/gfx/wr/webrender/src/scene_builder_thread.rs +++ b/gfx/wr/webrender/src/scene_builder_thread.rs @@ -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; diff --git a/gfx/wr/webrender/src/scene_building.rs b/gfx/wr/webrender/src/scene_building.rs index d0c973e1daaf..540b9974a163 100644 --- a/gfx/wr/webrender/src/scene_building.rs +++ b/gfx/wr/webrender/src/scene_building.rs @@ -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, + extend_mode: ExtendMode, + stretch_size: LayoutSize, + mut tile_spacing: LayoutSize, + nine_patch: Option>, + ) -> 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, diff --git a/gfx/wr/webrender/src/shade.rs b/gfx/wr/webrender/src/shade.rs index 84ab5856882e..e8a9fe5cacc9 100644 --- a/gfx/wr/webrender/src/shade.rs +++ b/gfx/wr/webrender/src/shade.rs @@ -536,6 +536,7 @@ pub struct Shaders { brush_blend: BrushShader, brush_mix_blend: BrushShader, brush_yuv_image: Vec>, + 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); diff --git a/gfx/wr/webrender_api/src/api.rs b/gfx/wr/webrender_api/src/api.rs index d18415948889..741d449ee14a 100644 --- a/gfx/wr/webrender_api/src/api.rs +++ b/gfx/wr/webrender_api/src/api.rs @@ -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, diff --git a/gfx/wr/webrender_api/src/display_item.rs b/gfx/wr/webrender_api/src/display_item.rs index c268d9d03e53..3a3e1d9ad237 100644 --- a/gfx/wr/webrender_api/src/display_item.rs +++ b/gfx/wr/webrender_api/src/display_item.rs @@ -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 +#[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 + /// 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", diff --git a/gfx/wr/webrender_api/src/display_list.rs b/gfx/wr/webrender_api/src/display_list.rs index c64a56e033e7..c2e596884450 100644 --- a/gfx/wr/webrender_api/src/display_list.rs +++ b/gfx/wr/webrender_api/src/display_list.rs @@ -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, + 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, diff --git a/gfx/wr/webrender_api/src/gradient_builder.rs b/gfx/wr/webrender_api/src/gradient_builder.rs index 1b75dd730214..ef3dec8d1645 100644 --- a/gfx/wr/webrender_api/src/gradient_builder.rs +++ b/gfx/wr/webrender_api/src/gradient_builder.rs @@ -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 diff --git a/gfx/wr/wrench/reftests/gradient/conic-color-wheel.yaml b/gfx/wr/wrench/reftests/gradient/conic-color-wheel.yaml new file mode 100644 index 000000000000..54145295729f --- /dev/null +++ b/gfx/wr/wrench/reftests/gradient/conic-color-wheel.yaml @@ -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] diff --git a/gfx/wr/wrench/reftests/gradient/conic-simple.yaml b/gfx/wr/wrench/reftests/gradient/conic-simple.yaml new file mode 100644 index 000000000000..b65ed2eede59 --- /dev/null +++ b/gfx/wr/wrench/reftests/gradient/conic-simple.yaml @@ -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] \ No newline at end of file diff --git a/gfx/wr/wrench/reftests/gradient/conic-start-angle.yaml b/gfx/wr/wrench/reftests/gradient/conic-start-angle.yaml new file mode 100644 index 000000000000..11a068c9e6b4 --- /dev/null +++ b/gfx/wr/wrench/reftests/gradient/conic-start-angle.yaml @@ -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] \ No newline at end of file diff --git a/gfx/wr/wrench/src/yaml_frame_reader.rs b/gfx/wr/wrench/src/yaml_frame_reader.rs index 99d8946d3b3b..60a6626fa9ed 100644 --- a/gfx/wr/wrench/src/yaml_frame_reader.rs +++ b/gfx/wr/wrench/src/yaml_frame_reader.rs @@ -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::>(); + 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" => { diff --git a/gfx/wr/wrench/src/yaml_frame_writer.rs b/gfx/wr/wrench/src/yaml_frame_writer.rs index 91804d10eaeb..4cadebce7f00 100644 --- a/gfx/wr/wrench/src/yaml_frame_writer.rs +++ b/gfx/wr/wrench/src/yaml_frame_writer.rs @@ -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, +) { + 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), Raw(Option>, u32, Option), @@ -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);