mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 00:35:44 +00:00
Bug 1536021 - Shadow stack implementation. r=kvark
Differential Revision: https://phabricator.services.mozilla.com/D30620 --HG-- extra : source : e830b256419b53556e27aa2586f2ed8f715aa99e extra : intermediate-source : c223987f3de744979a0b08b336b46ac9f199e9bd
This commit is contained in:
parent
8308eb7ea1
commit
c7a54e68a1
@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
use api::{AlphaType, ClipMode, ExternalImageType, FilterOp, ImageRendering};
|
||||
use api::{AlphaType, ClipMode, ExternalImageType, ImageRendering};
|
||||
use api::{YuvColorSpace, YuvFormat, ColorDepth, PremultipliedColorF, RasterSpace};
|
||||
use api::units::*;
|
||||
use crate::clip::{ClipDataStore, ClipNodeFlags, ClipNodeRange, ClipItem, ClipStore, ClipNodeInstance};
|
||||
@ -13,7 +13,7 @@ use crate::gpu_types::{BrushFlags, BrushInstance, PrimitiveHeaders, ZBufferId, Z
|
||||
use crate::gpu_types::{ClipMaskInstance, SplitCompositeInstance, SnapOffsets};
|
||||
use crate::gpu_types::{PrimitiveInstanceData, RasterizationSpace, GlyphInstance};
|
||||
use crate::gpu_types::{PrimitiveHeader, PrimitiveHeaderIndex, TransformPaletteId, TransformPalette};
|
||||
use crate::internal_types::{FastHashMap, SavedTargetIndex, TextureSource};
|
||||
use crate::internal_types::{FastHashMap, SavedTargetIndex, TextureSource, Filter};
|
||||
use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive};
|
||||
use crate::prim_store::{DeferredResolve, EdgeAaSegmentMask, PrimitiveInstanceKind, PrimitiveVisibilityIndex};
|
||||
use crate::prim_store::{VisibleGradientTile, PrimitiveInstance, PrimitiveOpacity, SegmentInstanceIndex};
|
||||
@ -25,7 +25,6 @@ use crate::render_task::{RenderTaskAddress, RenderTaskId, RenderTaskTree, TileBl
|
||||
use crate::renderer::{BlendMode, ImageBufferKind, ShaderColorMode};
|
||||
use crate::renderer::{BLOCKS_PER_UV_RECT, MAX_VERTEX_TEXTURE_WIDTH};
|
||||
use crate::resource_cache::{CacheItem, GlyphFetchResult, ImageRequest, ResourceCache, ImageProperties};
|
||||
use crate::scene::FilterOpHelpers;
|
||||
use smallvec::SmallVec;
|
||||
use std::{f32, i32, usize};
|
||||
use crate::tiling::{RenderTargetContext};
|
||||
@ -636,6 +635,7 @@ impl BatchBuilder {
|
||||
let transform_kind = transform_id.transform_kind();
|
||||
let prim_info = &ctx.scratch.prim_info[prim_instance.visibility_info.0 as usize];
|
||||
let bounding_rect = &prim_info.clip_chain.pic_clip_rect;
|
||||
|
||||
let z_id = z_generator.next();
|
||||
|
||||
let prim_common_data = &ctx.data_stores.as_common_data(&prim_instance);
|
||||
@ -1287,7 +1287,7 @@ impl BatchBuilder {
|
||||
PictureCompositeMode::Filter(ref filter) => {
|
||||
assert!(filter.is_visible());
|
||||
match filter {
|
||||
FilterOp::Blur(..) => {
|
||||
Filter::Blur(..) => {
|
||||
let kind = BatchKind::Brush(
|
||||
BrushBatchKind::Image(ImageBufferKind::Texture2DArray)
|
||||
);
|
||||
@ -1323,7 +1323,105 @@ impl BatchBuilder {
|
||||
PrimitiveInstanceData::from(instance),
|
||||
);
|
||||
}
|
||||
FilterOp::DropShadow(offset, ..) => {
|
||||
Filter::DropShadowStack(shadows) => {
|
||||
// Draw an instance per shadow first, following by the content.
|
||||
|
||||
// The shadows and the content get drawn as a brush image.
|
||||
let kind = BatchKind::Brush(
|
||||
BrushBatchKind::Image(ImageBufferKind::Texture2DArray),
|
||||
);
|
||||
|
||||
// Gets the saved render task ID of the content, which is
|
||||
// deeper in the render task tree than the direct child.
|
||||
let secondary_id = picture.secondary_render_task_id.expect("no secondary!?");
|
||||
let saved_index = render_tasks[secondary_id].saved_index.expect("no saved index!?");
|
||||
debug_assert_ne!(saved_index, SavedTargetIndex::PENDING);
|
||||
|
||||
// Build BatchTextures for shadow/content
|
||||
let shadow_textures = BatchTextures::render_target_cache();
|
||||
let content_textures = BatchTextures {
|
||||
colors: [
|
||||
TextureSource::RenderTaskCache(saved_index),
|
||||
TextureSource::Invalid,
|
||||
TextureSource::Invalid,
|
||||
],
|
||||
};
|
||||
|
||||
// Build batch keys for shadow/content
|
||||
let shadow_key = BatchKey::new(kind, non_segmented_blend_mode, shadow_textures);
|
||||
let content_key = BatchKey::new(kind, non_segmented_blend_mode, content_textures);
|
||||
|
||||
// Retrieve the UV rect addresses for shadow/content.
|
||||
let cache_task_id = surface
|
||||
.expect("bug: surface must be allocated by now");
|
||||
let shadow_uv_rect_address = render_tasks[cache_task_id]
|
||||
.get_texture_address(gpu_cache)
|
||||
.as_int();
|
||||
let content_uv_rect_address = render_tasks[secondary_id]
|
||||
.get_texture_address(gpu_cache)
|
||||
.as_int();
|
||||
|
||||
for (shadow, shadow_gpu_data) in shadows.iter().zip(picture.extra_gpu_data_handles.iter()) {
|
||||
// Get the GPU cache address of the extra data handle.
|
||||
let shadow_prim_address = gpu_cache.get_address(shadow_gpu_data);
|
||||
|
||||
let shadow_rect = prim_header.local_rect.translate(&shadow.offset);
|
||||
|
||||
let shadow_prim_header = PrimitiveHeader {
|
||||
local_rect: shadow_rect,
|
||||
specific_prim_address: shadow_prim_address,
|
||||
..prim_header
|
||||
};
|
||||
|
||||
let shadow_prim_header_index = prim_headers.push(&shadow_prim_header, z_id, [
|
||||
ShaderColorMode::Alpha as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
|
||||
RasterizationSpace::Screen as i32,
|
||||
get_shader_opacity(1.0),
|
||||
0,
|
||||
]);
|
||||
|
||||
let shadow_instance = BrushInstance {
|
||||
prim_header_index: shadow_prim_header_index,
|
||||
clip_task_address,
|
||||
segment_index: INVALID_SEGMENT_INDEX,
|
||||
edge_flags: EdgeAaSegmentMask::empty(),
|
||||
brush_flags,
|
||||
user_data: shadow_uv_rect_address,
|
||||
};
|
||||
|
||||
batcher.current_batch_list().push_single_instance(
|
||||
shadow_key,
|
||||
bounding_rect,
|
||||
z_id,
|
||||
PrimitiveInstanceData::from(shadow_instance),
|
||||
);
|
||||
}
|
||||
let z_id_content = z_generator.next();
|
||||
|
||||
let content_prim_header_index = prim_headers.push(&prim_header, z_id_content, [
|
||||
ShaderColorMode::Image as i32 | ((AlphaType::PremultipliedAlpha as i32) << 16),
|
||||
RasterizationSpace::Screen as i32,
|
||||
get_shader_opacity(1.0),
|
||||
0,
|
||||
]);
|
||||
|
||||
let content_instance = BrushInstance {
|
||||
prim_header_index: content_prim_header_index,
|
||||
clip_task_address,
|
||||
segment_index: INVALID_SEGMENT_INDEX,
|
||||
edge_flags: EdgeAaSegmentMask::empty(),
|
||||
brush_flags,
|
||||
user_data: content_uv_rect_address,
|
||||
};
|
||||
|
||||
batcher.current_batch_list().push_single_instance(
|
||||
content_key,
|
||||
bounding_rect,
|
||||
z_id_content,
|
||||
PrimitiveInstanceData::from(content_instance),
|
||||
);
|
||||
}
|
||||
Filter::DropShadow(shadow) => {
|
||||
// Draw an instance of the shadow first, following by the content.
|
||||
|
||||
// Both the shadow and the content get drawn as a brush image.
|
||||
@ -1361,7 +1459,7 @@ impl BatchBuilder {
|
||||
.as_int();
|
||||
|
||||
// Get the GPU cache address of the extra data handle.
|
||||
let shadow_prim_address = gpu_cache.get_address(&picture.extra_gpu_data_handle);
|
||||
let shadow_prim_address = gpu_cache.get_address(&picture.extra_gpu_data_handles[0]);
|
||||
|
||||
let z_id_shadow = z_id;
|
||||
let z_id_content = z_generator.next();
|
||||
@ -1373,7 +1471,7 @@ impl BatchBuilder {
|
||||
0,
|
||||
]);
|
||||
|
||||
let shadow_rect = prim_header.local_rect.translate(&offset);
|
||||
let shadow_rect = prim_header.local_rect.translate(&shadow.offset);
|
||||
|
||||
let shadow_prim_header = PrimitiveHeader {
|
||||
local_rect: shadow_rect,
|
||||
@ -1423,47 +1521,49 @@ impl BatchBuilder {
|
||||
}
|
||||
_ => {
|
||||
let filter_mode = match filter {
|
||||
FilterOp::Identity => 1, // matches `Contrast(1)`
|
||||
FilterOp::Blur(..) => 0,
|
||||
FilterOp::Contrast(..) => 1,
|
||||
FilterOp::Grayscale(..) => 2,
|
||||
FilterOp::HueRotate(..) => 3,
|
||||
FilterOp::Invert(..) => 4,
|
||||
FilterOp::Saturate(..) => 5,
|
||||
FilterOp::Sepia(..) => 6,
|
||||
FilterOp::Brightness(..) => 7,
|
||||
FilterOp::Opacity(..) => 8,
|
||||
FilterOp::DropShadow(..) => 9,
|
||||
FilterOp::ColorMatrix(..) => 10,
|
||||
FilterOp::SrgbToLinear => 11,
|
||||
FilterOp::LinearToSrgb => 12,
|
||||
FilterOp::ComponentTransfer => unreachable!(),
|
||||
Filter::Identity => 1, // matches `Contrast(1)`
|
||||
Filter::Blur(..) => 0,
|
||||
Filter::Contrast(..) => 1,
|
||||
Filter::Grayscale(..) => 2,
|
||||
Filter::HueRotate(..) => 3,
|
||||
Filter::Invert(..) => 4,
|
||||
Filter::Saturate(..) => 5,
|
||||
Filter::Sepia(..) => 6,
|
||||
Filter::Brightness(..) => 7,
|
||||
Filter::Opacity(..) => 8,
|
||||
Filter::DropShadow(..) |
|
||||
Filter::DropShadowStack(..) => 9,
|
||||
Filter::ColorMatrix(..) => 10,
|
||||
Filter::SrgbToLinear => 11,
|
||||
Filter::LinearToSrgb => 12,
|
||||
Filter::ComponentTransfer => unreachable!(),
|
||||
};
|
||||
|
||||
let user_data = match filter {
|
||||
FilterOp::Identity => 0x10000i32, // matches `Contrast(1)`
|
||||
FilterOp::Contrast(amount) |
|
||||
FilterOp::Grayscale(amount) |
|
||||
FilterOp::Invert(amount) |
|
||||
FilterOp::Saturate(amount) |
|
||||
FilterOp::Sepia(amount) |
|
||||
FilterOp::Brightness(amount) |
|
||||
FilterOp::Opacity(_, amount) => {
|
||||
Filter::Identity => 0x10000i32, // matches `Contrast(1)`
|
||||
Filter::Contrast(amount) |
|
||||
Filter::Grayscale(amount) |
|
||||
Filter::Invert(amount) |
|
||||
Filter::Saturate(amount) |
|
||||
Filter::Sepia(amount) |
|
||||
Filter::Brightness(amount) |
|
||||
Filter::Opacity(_, amount) => {
|
||||
(amount * 65536.0) as i32
|
||||
}
|
||||
FilterOp::SrgbToLinear | FilterOp::LinearToSrgb => 0,
|
||||
FilterOp::HueRotate(angle) => {
|
||||
Filter::SrgbToLinear | Filter::LinearToSrgb => 0,
|
||||
Filter::HueRotate(angle) => {
|
||||
(0.01745329251 * angle * 65536.0) as i32
|
||||
}
|
||||
// Go through different paths
|
||||
FilterOp::Blur(..) |
|
||||
FilterOp::DropShadow(..) => {
|
||||
Filter::Blur(..) |
|
||||
Filter::DropShadowStack(..) |
|
||||
Filter::DropShadow(..) => {
|
||||
unreachable!();
|
||||
}
|
||||
FilterOp::ColorMatrix(_) => {
|
||||
picture.extra_gpu_data_handle.as_int(gpu_cache)
|
||||
Filter::ColorMatrix(_) => {
|
||||
picture.extra_gpu_data_handles[0].as_int(gpu_cache)
|
||||
}
|
||||
FilterOp::ComponentTransfer => unreachable!(),
|
||||
Filter::ComponentTransfer => unreachable!(),
|
||||
};
|
||||
|
||||
let (uv_rect_address, textures) = render_tasks.resolve_surface(
|
||||
|
@ -19,7 +19,7 @@ use crate::glyph_rasterizer::FontInstance;
|
||||
use crate::hit_test::{HitTestingItem, HitTestingScene};
|
||||
use crate::image::simplify_repeated_primitive;
|
||||
use crate::intern::Interner;
|
||||
use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo};
|
||||
use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo, Filter};
|
||||
use crate::picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PictureOptions};
|
||||
use crate::picture::{BlitReason, PrimitiveList, TileCache};
|
||||
use crate::prim_store::{PrimitiveInstance, PrimitiveSceneData};
|
||||
@ -1280,7 +1280,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
apply_pipeline_clip
|
||||
);
|
||||
|
||||
self.push_shadow(info.shadow, clip_and_scroll);
|
||||
self.push_shadow(info.shadow, clip_and_scroll, info.should_inflate);
|
||||
}
|
||||
DisplayItem::PopAllShadows => {
|
||||
self.pop_all_shadows();
|
||||
@ -1760,7 +1760,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
let filter = filter.sanitize();
|
||||
|
||||
let composite_mode = Some(match filter {
|
||||
FilterOp::ComponentTransfer => {
|
||||
Filter::ComponentTransfer => {
|
||||
let filter_data =
|
||||
&stacking_context.composite_ops.filter_datas[current_filter_data_index];
|
||||
let filter_data = filter_data.sanitize();
|
||||
@ -2098,12 +2098,14 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
&mut self,
|
||||
shadow: Shadow,
|
||||
clip_and_scroll: ScrollNodeAndClipChain,
|
||||
should_inflate: bool,
|
||||
) {
|
||||
// Store this shadow in the pending list, for processing
|
||||
// during pop_all_shadows.
|
||||
self.pending_shadow_items.push_back(ShadowItem::Shadow(PendingShadow {
|
||||
shadow,
|
||||
clip_and_scroll,
|
||||
should_inflate,
|
||||
}));
|
||||
}
|
||||
|
||||
@ -2202,7 +2204,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
// blur radius is 0, the code in Picture::prepare_for_render will
|
||||
// detect this and mark the picture to be drawn directly into the
|
||||
// parent picture, which avoids an intermediate surface and blur.
|
||||
let blur_filter = FilterOp::Blur(std_deviation).sanitize();
|
||||
let blur_filter = Filter::Blur(std_deviation).sanitize();
|
||||
let composite_mode = PictureCompositeMode::Filter(blur_filter);
|
||||
let composite_mode_key = Some(composite_mode.clone()).into();
|
||||
let is_backface_visible = true; //TODO: double check this
|
||||
@ -2210,7 +2212,7 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
// Pass through configuration information about whether WR should
|
||||
// do the bounding rect inflation for text shadows.
|
||||
let options = PictureOptions {
|
||||
inflate_if_required: pending_shadow.shadow.should_inflate,
|
||||
inflate_if_required: pending_shadow.should_inflate,
|
||||
};
|
||||
|
||||
// Create the primitive to draw the shadow picture into the scene.
|
||||
@ -2312,16 +2314,16 @@ impl<'a> DisplayListFlattener<'a> {
|
||||
// Offset the local rect and clip rect by the shadow offset.
|
||||
let mut info = pending_primitive.info.clone();
|
||||
info.rect = info.rect.translate(&pending_shadow.shadow.offset);
|
||||
info.clip_rect = info.clip_rect.translate(&pending_shadow.shadow.offset);
|
||||
info.clip_rect = info.clip_rect.translate(
|
||||
&pending_shadow.shadow.offset
|
||||
);
|
||||
|
||||
// Construct and add a primitive for the given shadow.
|
||||
let shadow_prim_instance = self.create_primitive(
|
||||
&info,
|
||||
pending_primitive.clip_and_scroll.clip_chain_id,
|
||||
pending_primitive.clip_and_scroll.spatial_node_index,
|
||||
pending_primitive.prim.create_shadow(
|
||||
&pending_shadow.shadow,
|
||||
),
|
||||
pending_primitive.prim.create_shadow(&pending_shadow.shadow),
|
||||
);
|
||||
|
||||
// Add the new primitive to the shadow picture.
|
||||
@ -3031,6 +3033,7 @@ pub struct PendingPrimitive<T> {
|
||||
/// shadows, and handled at once during pop_all_shadows.
|
||||
pub struct PendingShadow {
|
||||
shadow: Shadow,
|
||||
should_inflate: bool,
|
||||
clip_and_scroll: ScrollNodeAndClipChain,
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,9 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{DebugCommand, DocumentId, ExternalImageData, ExternalImageId};
|
||||
use api::{ImageFormat, ItemTag, NotificationRequest};
|
||||
use api::{ImageFormat, ItemTag, NotificationRequest, Shadow, FilterOp, MAX_BLUR_RADIUS};
|
||||
use api::units::*;
|
||||
use api;
|
||||
use crate::device::TextureFilter;
|
||||
use crate::renderer::PipelineInfo;
|
||||
use crate::gpu_cache::GpuCacheUpdateList;
|
||||
@ -30,6 +31,141 @@ pub type FastHashSet<K> = HashSet<K, BuildHasherDefault<FxHasher>>;
|
||||
/// A concrete plane splitter type used in WebRender.
|
||||
pub type PlaneSplitter = BspSplitter<f64, WorldPixel>;
|
||||
|
||||
/// An arbitrary number which we assume opacity is invisible below.
|
||||
const OPACITY_EPSILON: f32 = 0.001;
|
||||
|
||||
/// Equivalent to api::FilterOp with added internal information
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub enum Filter {
|
||||
Identity,
|
||||
Blur(f32),
|
||||
Brightness(f32),
|
||||
Contrast(f32),
|
||||
Grayscale(f32),
|
||||
HueRotate(f32),
|
||||
Invert(f32),
|
||||
Opacity(api::PropertyBinding<f32>, f32),
|
||||
Saturate(f32),
|
||||
Sepia(f32),
|
||||
DropShadow(Shadow),
|
||||
#[allow(dead_code)]
|
||||
DropShadowStack(Vec<Shadow>),
|
||||
ColorMatrix([f32; 20]),
|
||||
SrgbToLinear,
|
||||
LinearToSrgb,
|
||||
ComponentTransfer,
|
||||
}
|
||||
|
||||
impl Filter {
|
||||
/// Ensure that the parameters for a filter operation
|
||||
/// are sensible.
|
||||
pub fn sanitize(&self) -> Self {
|
||||
match self {
|
||||
Filter::Blur(radius) => Filter::Blur(radius.min(MAX_BLUR_RADIUS)),
|
||||
Filter::DropShadow(shadow) => Filter::DropShadow(Shadow {
|
||||
offset: shadow.offset,
|
||||
blur_radius: shadow.blur_radius.min(MAX_BLUR_RADIUS),
|
||||
color: shadow.color
|
||||
}),
|
||||
Filter::DropShadowStack(ref stack) => {
|
||||
let mut shadows = Vec::with_capacity(stack.len());
|
||||
for shadow in stack {
|
||||
shadows.push(Shadow {
|
||||
blur_radius: shadow.blur_radius.min(MAX_BLUR_RADIUS),
|
||||
..*shadow
|
||||
});
|
||||
}
|
||||
Filter::DropShadowStack(shadows)
|
||||
}
|
||||
filter => filter.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_visible(&self) -> bool {
|
||||
match *self {
|
||||
Filter::Identity |
|
||||
Filter::Blur(..) |
|
||||
Filter::Brightness(..) |
|
||||
Filter::Contrast(..) |
|
||||
Filter::Grayscale(..) |
|
||||
Filter::HueRotate(..) |
|
||||
Filter::Invert(..) |
|
||||
Filter::Saturate(..) |
|
||||
Filter::Sepia(..) |
|
||||
Filter::DropShadow(..) |
|
||||
Filter::DropShadowStack(..) |
|
||||
Filter::ColorMatrix(..) |
|
||||
Filter::SrgbToLinear |
|
||||
Filter::LinearToSrgb |
|
||||
Filter::ComponentTransfer => true,
|
||||
Filter::Opacity(_, amount) => {
|
||||
amount > OPACITY_EPSILON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_noop(&self) -> bool {
|
||||
match *self {
|
||||
Filter::Identity => false, // this is intentional
|
||||
Filter::Blur(length) => length == 0.0,
|
||||
Filter::Brightness(amount) => amount == 1.0,
|
||||
Filter::Contrast(amount) => amount == 1.0,
|
||||
Filter::Grayscale(amount) => amount == 0.0,
|
||||
Filter::HueRotate(amount) => amount == 0.0,
|
||||
Filter::Invert(amount) => amount == 0.0,
|
||||
Filter::Opacity(_, amount) => amount >= 1.0,
|
||||
Filter::Saturate(amount) => amount == 1.0,
|
||||
Filter::Sepia(amount) => amount == 0.0,
|
||||
Filter::DropShadowStack(ref shadows) => {
|
||||
for shadow in shadows {
|
||||
if shadow.offset.x != 0.0 || shadow.offset.y != 0.0 || shadow.blur_radius != 0.0 {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
Filter::DropShadow(shadow) => {
|
||||
shadow.offset.x == 0.0 && shadow.offset.y == 0.0 && shadow.blur_radius == 0.0
|
||||
},
|
||||
Filter::ColorMatrix(matrix) => {
|
||||
matrix == [1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0,
|
||||
0.0, 0.0, 0.0, 0.0]
|
||||
}
|
||||
Filter::SrgbToLinear |
|
||||
Filter::LinearToSrgb |
|
||||
Filter::ComponentTransfer => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FilterOp> for Filter {
|
||||
fn from(op: FilterOp) -> Self {
|
||||
match op {
|
||||
FilterOp::Identity => Filter::Identity,
|
||||
FilterOp::Blur(r) => Filter::Blur(r),
|
||||
FilterOp::Brightness(b) => Filter::Brightness(b),
|
||||
FilterOp::Contrast(c) => Filter::Contrast(c),
|
||||
FilterOp::Grayscale(g) => Filter::Grayscale(g),
|
||||
FilterOp::HueRotate(h) => Filter::HueRotate(h),
|
||||
FilterOp::Invert(i) => Filter::Invert(i),
|
||||
FilterOp::Opacity(binding, opacity) => Filter::Opacity(binding, opacity),
|
||||
FilterOp::Saturate(s) => Filter::Saturate(s),
|
||||
FilterOp::Sepia(s) => Filter::Sepia(s),
|
||||
FilterOp::DropShadow(shadow) => Filter::DropShadow(shadow),
|
||||
FilterOp::ColorMatrix(mat) => Filter::ColorMatrix(mat),
|
||||
FilterOp::SrgbToLinear => Filter::SrgbToLinear,
|
||||
FilterOp::LinearToSrgb => Filter::LinearToSrgb,
|
||||
FilterOp::ComponentTransfer => Filter::ComponentTransfer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An ID for a texture that is owned by the `texture_cache` module.
|
||||
///
|
||||
/// This can include atlases or standalone textures allocated via the texture
|
||||
|
@ -2,7 +2,7 @@
|
||||
* 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/. */
|
||||
|
||||
use api::{FilterOp, MixBlendMode, PipelineId, PremultipliedColorF};
|
||||
use api::{MixBlendMode, PipelineId, PremultipliedColorF};
|
||||
use api::{PropertyBinding, PropertyBindingId};
|
||||
use api::{DebugFlags, RasterSpace, ColorF, ImageKey, ClipMode};
|
||||
use api::units::*;
|
||||
@ -16,7 +16,7 @@ use euclid::{size2, vec3, TypedPoint2D, TypedScale, TypedSize2D, Vector2D};
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use crate::frame_builder::{FrameVisibilityContext, FrameVisibilityState};
|
||||
use crate::intern::ItemUid;
|
||||
use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter};
|
||||
use crate::internal_types::{FastHashMap, FastHashSet, PlaneSplitter, Filter};
|
||||
use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureState, PictureContext};
|
||||
use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
|
||||
use crate::gpu_types::UvRectKind;
|
||||
@ -28,9 +28,9 @@ use crate::prim_store::{OpacityBindingStorage, ImageInstanceStorage, OpacityBind
|
||||
use crate::print_tree::PrintTreePrinter;
|
||||
use crate::render_backend::DataStores;
|
||||
use crate::render_task::{ClearMode, RenderTask, TileBlit};
|
||||
use crate::render_task::{RenderTaskId, RenderTaskLocation};
|
||||
use crate::render_task::{RenderTaskId, RenderTaskLocation, BlurTaskCache};
|
||||
use crate::resource_cache::ResourceCache;
|
||||
use crate::scene::{FilterOpHelpers, SceneProperties};
|
||||
use crate::scene::SceneProperties;
|
||||
use crate::scene_builder::Interners;
|
||||
use smallvec::SmallVec;
|
||||
use std::{mem, u16};
|
||||
@ -1133,7 +1133,7 @@ impl TileCache {
|
||||
PrimitiveInstanceKind::Picture { pic_index,.. } => {
|
||||
// Pictures can depend on animated opacity bindings.
|
||||
let pic = &pictures[pic_index.0];
|
||||
if let Some(PictureCompositeMode::Filter(FilterOp::Opacity(binding, _))) = pic.requested_composite_mode {
|
||||
if let Some(PictureCompositeMode::Filter(Filter::Opacity(binding, _))) = pic.requested_composite_mode {
|
||||
opacity_bindings.push(binding.into());
|
||||
}
|
||||
|
||||
@ -1894,7 +1894,7 @@ pub enum PictureCompositeMode {
|
||||
/// Apply CSS mix-blend-mode effect.
|
||||
MixBlend(MixBlendMode),
|
||||
/// Apply a CSS filter (except component transfer).
|
||||
Filter(FilterOp),
|
||||
Filter(Filter),
|
||||
/// Apply a component transfer filter.
|
||||
ComponentTransferFilter(FilterDataHandle),
|
||||
/// Draw to intermediate surface, copy straight across. This
|
||||
@ -2188,10 +2188,10 @@ pub struct PicturePrimitive {
|
||||
// pages to a texture), this is the pipeline this
|
||||
// picture is the root of.
|
||||
pub frame_output_pipeline_id: Option<PipelineId>,
|
||||
// An optional cache handle for storing extra data
|
||||
// Optional cache handles for storing extra data
|
||||
// in the GPU cache, depending on the type of
|
||||
// picture.
|
||||
pub extra_gpu_data_handle: GpuCacheHandle,
|
||||
pub extra_gpu_data_handles: SmallVec<[GpuCacheHandle; 1]>,
|
||||
|
||||
/// The spatial node index of this picture when it is
|
||||
/// composited into the parent picture.
|
||||
@ -2268,7 +2268,7 @@ impl PicturePrimitive {
|
||||
match self.requested_composite_mode {
|
||||
Some(PictureCompositeMode::Filter(ref mut filter)) => {
|
||||
match *filter {
|
||||
FilterOp::Opacity(ref binding, ref mut value) => {
|
||||
Filter::Opacity(ref binding, ref mut value) => {
|
||||
*value = properties.resolve_float(binding);
|
||||
}
|
||||
_ => {}
|
||||
@ -2336,7 +2336,7 @@ impl PicturePrimitive {
|
||||
raster_config: None,
|
||||
context_3d,
|
||||
frame_output_pipeline_id,
|
||||
extra_gpu_data_handle: GpuCacheHandle::new(),
|
||||
extra_gpu_data_handles: SmallVec::new(),
|
||||
apply_local_clip_rect,
|
||||
is_backface_visible,
|
||||
pipeline_id,
|
||||
@ -2467,7 +2467,7 @@ impl PicturePrimitive {
|
||||
PictureCompositeMode::TileCache { .. } => {
|
||||
unreachable!();
|
||||
}
|
||||
PictureCompositeMode::Filter(FilterOp::Blur(blur_radius)) => {
|
||||
PictureCompositeMode::Filter(Filter::Blur(blur_radius)) => {
|
||||
let blur_std_deviation = blur_radius * device_pixel_scale.0;
|
||||
let scale_factors = scale_factors(&transform);
|
||||
let blur_std_deviation = DeviceSize::new(
|
||||
@ -2518,20 +2518,19 @@ impl PicturePrimitive {
|
||||
|
||||
let picture_task_id = frame_state.render_tasks.add(picture_task);
|
||||
|
||||
let blur_render_task = RenderTask::new_blur(
|
||||
let blur_render_task_id = RenderTask::new_blur(
|
||||
blur_std_deviation,
|
||||
picture_task_id,
|
||||
frame_state.render_tasks,
|
||||
RenderTargetKind::Color,
|
||||
ClearMode::Transparent,
|
||||
None,
|
||||
);
|
||||
|
||||
let render_task_id = frame_state.render_tasks.add(blur_render_task);
|
||||
|
||||
(render_task_id, picture_task_id)
|
||||
(blur_render_task_id, picture_task_id)
|
||||
}
|
||||
PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)) => {
|
||||
let blur_std_deviation = blur_radius * device_pixel_scale.0;
|
||||
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => {
|
||||
let blur_std_deviation = shadow.blur_radius * device_pixel_scale.0;
|
||||
let blur_range = (blur_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
|
||||
let rounded_std_dev = blur_std_deviation.round();
|
||||
let rounded_std_dev = DeviceSize::new(rounded_std_dev, rounded_std_dev);
|
||||
@ -2573,19 +2572,80 @@ impl PicturePrimitive {
|
||||
|
||||
let picture_task_id = frame_state.render_tasks.add(picture_task);
|
||||
|
||||
let blur_render_task = RenderTask::new_blur(
|
||||
let blur_render_task_id = RenderTask::new_blur(
|
||||
rounded_std_dev,
|
||||
picture_task_id,
|
||||
frame_state.render_tasks,
|
||||
RenderTargetKind::Color,
|
||||
ClearMode::Transparent,
|
||||
None,
|
||||
);
|
||||
|
||||
self.secondary_render_task_id = Some(picture_task_id);
|
||||
|
||||
let render_task_id = frame_state.render_tasks.add(blur_render_task);
|
||||
|
||||
(render_task_id, picture_task_id)
|
||||
(blur_render_task_id, picture_task_id)
|
||||
}
|
||||
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
|
||||
let mut max_std_deviation = 0.0;
|
||||
for shadow in shadows {
|
||||
// TODO(nical) presumably we should compute the clipped rect for each shadow
|
||||
// and compute the union of them to determine what we need to rasterize and blur?
|
||||
max_std_deviation = f32::max(max_std_deviation, shadow.blur_radius * device_pixel_scale.0);
|
||||
}
|
||||
|
||||
max_std_deviation = max_std_deviation.round();
|
||||
let max_blur_range = (max_std_deviation * BLUR_SAMPLE_SCALE).ceil() as i32;
|
||||
let mut device_rect = clipped.inflate(max_blur_range, max_blur_range)
|
||||
.intersection(&unclipped.to_i32())
|
||||
.unwrap();
|
||||
device_rect.size = RenderTask::adjusted_blur_source_size(
|
||||
device_rect.size,
|
||||
DeviceSize::new(max_std_deviation, max_std_deviation),
|
||||
);
|
||||
|
||||
let uv_rect_kind = calculate_uv_rect_kind(
|
||||
&pic_rect,
|
||||
&transform,
|
||||
&device_rect,
|
||||
device_pixel_scale,
|
||||
true,
|
||||
);
|
||||
|
||||
let mut picture_task = RenderTask::new_picture(
|
||||
RenderTaskLocation::Dynamic(None, device_rect.size),
|
||||
unclipped.size,
|
||||
pic_index,
|
||||
device_rect.origin,
|
||||
Vec::new(),
|
||||
uv_rect_kind,
|
||||
raster_spatial_node_index,
|
||||
device_pixel_scale,
|
||||
);
|
||||
picture_task.mark_for_saving();
|
||||
|
||||
let picture_task_id = frame_state.render_tasks.add(picture_task);
|
||||
|
||||
self.secondary_render_task_id = Some(picture_task_id);
|
||||
|
||||
let mut blur_tasks = BlurTaskCache::default();
|
||||
|
||||
self.extra_gpu_data_handles.resize(shadows.len(), GpuCacheHandle::new());
|
||||
|
||||
let mut blur_render_task_id = picture_task_id;
|
||||
for shadow in shadows {
|
||||
let std_dev = f32::round(shadow.blur_radius * device_pixel_scale.0);
|
||||
blur_render_task_id = RenderTask::new_blur(
|
||||
DeviceSize::new(std_dev, std_dev),
|
||||
picture_task_id,
|
||||
frame_state.render_tasks,
|
||||
RenderTargetKind::Color,
|
||||
ClearMode::Transparent,
|
||||
Some(&mut blur_tasks),
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(nical) the second one should to be the blur's task id but we have several blurs now
|
||||
(picture_task_id, blur_render_task_id)
|
||||
}
|
||||
PictureCompositeMode::MixBlend(..) if !frame_context.fb_config.gpu_supports_advanced_blend => {
|
||||
let uv_rect_kind = calculate_uv_rect_kind(
|
||||
@ -3002,7 +3062,7 @@ impl PicturePrimitive {
|
||||
|
||||
// This inflation factor is to be applied to all primitives within the surface.
|
||||
let inflation_factor = match composite_mode {
|
||||
PictureCompositeMode::Filter(FilterOp::Blur(blur_radius)) => {
|
||||
PictureCompositeMode::Filter(Filter::Blur(blur_radius)) => {
|
||||
// Only inflate if the caller hasn't already inflated
|
||||
// the bounding rects for this filter.
|
||||
if self.options.inflate_if_required {
|
||||
@ -3130,10 +3190,20 @@ impl PicturePrimitive {
|
||||
let surface = state.current_surface_mut();
|
||||
// Inflate the local bounding rect if required by the filter effect.
|
||||
// This inflaction factor is to be applied to the surface itsefl.
|
||||
// TODO: in prepare_for_render we round before multiplying with the
|
||||
// blur sample scale. Should we do this here as well?
|
||||
let inflation_size = match raster_config.composite_mode {
|
||||
PictureCompositeMode::Filter(FilterOp::Blur(_)) => surface.inflation_factor,
|
||||
PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)) =>
|
||||
(blur_radius * BLUR_SAMPLE_SCALE).ceil(),
|
||||
PictureCompositeMode::Filter(Filter::Blur(_)) => surface.inflation_factor,
|
||||
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => {
|
||||
(shadow.blur_radius * BLUR_SAMPLE_SCALE).ceil()
|
||||
}
|
||||
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
|
||||
let mut max = 0.0;
|
||||
for shadow in shadows {
|
||||
max = f32::max(max, shadow.blur_radius * BLUR_SAMPLE_SCALE);
|
||||
}
|
||||
max.ceil()
|
||||
}
|
||||
_ => 0.0,
|
||||
};
|
||||
surface.rect = surface.rect.inflate(inflation_size, inflation_size);
|
||||
@ -3161,10 +3231,20 @@ impl PicturePrimitive {
|
||||
|
||||
// Drop shadows draw both a content and shadow rect, so need to expand the local
|
||||
// rect of any surfaces to be composited in parent surfaces correctly.
|
||||
if let PictureCompositeMode::Filter(FilterOp::DropShadow(offset, ..)) = raster_config.composite_mode {
|
||||
let content_rect = surface_rect;
|
||||
let shadow_rect = surface_rect.translate(&offset);
|
||||
surface_rect = content_rect.union(&shadow_rect);
|
||||
match raster_config.composite_mode {
|
||||
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => {
|
||||
let content_rect = surface_rect;
|
||||
let shadow_rect = surface_rect.translate(&shadow.offset);
|
||||
surface_rect = content_rect.union(&shadow_rect);
|
||||
}
|
||||
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
|
||||
for shadow in shadows {
|
||||
let content_rect = surface_rect;
|
||||
let shadow_rect = surface_rect.translate(&shadow.offset);
|
||||
surface_rect = content_rect.union(&shadow_rect);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Propagate up to parent surface, now that we know this surface's static rect
|
||||
@ -3214,9 +3294,13 @@ impl PicturePrimitive {
|
||||
|
||||
match raster_config.composite_mode {
|
||||
PictureCompositeMode::TileCache { .. } => {}
|
||||
PictureCompositeMode::Filter(FilterOp::Blur(..)) => {}
|
||||
PictureCompositeMode::Filter(FilterOp::DropShadow(offset, _, color)) => {
|
||||
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
|
||||
PictureCompositeMode::Filter(Filter::Blur(..)) => {}
|
||||
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => {
|
||||
if self.extra_gpu_data_handles.is_empty() {
|
||||
self.extra_gpu_data_handles.push(GpuCacheHandle::new());
|
||||
}
|
||||
|
||||
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handles[0]) {
|
||||
// TODO(gw): This is very hacky code below! It stores an extra
|
||||
// brush primitive below for the special case of a
|
||||
// drop-shadow where we need a different local
|
||||
@ -3227,10 +3311,10 @@ impl PicturePrimitive {
|
||||
// Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
|
||||
// [brush specific data]
|
||||
// [segment_rect, segment data]
|
||||
let shadow_rect = self.snapped_local_rect.translate(&offset);
|
||||
let shadow_rect = self.snapped_local_rect.translate(&shadow.offset);
|
||||
|
||||
// ImageBrush colors
|
||||
request.push(color.premultiplied());
|
||||
request.push(shadow.color.premultiplied());
|
||||
request.push(PremultipliedColorF::WHITE);
|
||||
request.push([
|
||||
self.snapped_local_rect.size.width,
|
||||
@ -3244,10 +3328,38 @@ impl PicturePrimitive {
|
||||
request.push([0.0, 0.0, 0.0, 0.0]);
|
||||
}
|
||||
}
|
||||
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
|
||||
self.extra_gpu_data_handles.resize(shadows.len(), GpuCacheHandle::new());
|
||||
for (shadow, extra_handle) in shadows.iter().zip(self.extra_gpu_data_handles.iter_mut()) {
|
||||
if let Some(mut request) = frame_state.gpu_cache.request(extra_handle) {
|
||||
// Basic brush primitive header is (see end of prepare_prim_for_render_inner in prim_store.rs)
|
||||
// [brush specific data]
|
||||
// [segment_rect, segment data]
|
||||
let shadow_rect = self.snapped_local_rect.translate(&shadow.offset);
|
||||
|
||||
// ImageBrush colors
|
||||
request.push(shadow.color.premultiplied());
|
||||
request.push(PremultipliedColorF::WHITE);
|
||||
request.push([
|
||||
self.snapped_local_rect.size.width,
|
||||
self.snapped_local_rect.size.height,
|
||||
0.0,
|
||||
0.0,
|
||||
]);
|
||||
|
||||
// segment rect / extra data
|
||||
request.push(shadow_rect);
|
||||
request.push([0.0, 0.0, 0.0, 0.0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
PictureCompositeMode::MixBlend(..) if !frame_context.fb_config.gpu_supports_advanced_blend => {}
|
||||
PictureCompositeMode::Filter(ref filter) => {
|
||||
if let FilterOp::ColorMatrix(m) = *filter {
|
||||
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handle) {
|
||||
if let Filter::ColorMatrix(m) = *filter {
|
||||
if self.extra_gpu_data_handles.is_empty() {
|
||||
self.extra_gpu_data_handles.push(GpuCacheHandle::new());
|
||||
}
|
||||
if let Some(mut request) = frame_state.gpu_cache.request(&mut self.extra_gpu_data_handles[0]) {
|
||||
for i in 0..5 {
|
||||
request.push([m[i*4], m[i*4+1], m[i*4+2], m[i*4+3]]);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{BorderRadius, ClipMode, ColorF};
|
||||
use api::{FilterOp, ImageRendering, RepeatMode};
|
||||
use api::{ImageRendering, RepeatMode};
|
||||
use api::{PremultipliedColorF, PropertyBinding, Shadow, GradientStop};
|
||||
use api::{BoxShadowClipMode, LineStyle, LineOrientation};
|
||||
use api::{PrimitiveKeyKind, RasterSpace};
|
||||
@ -52,7 +52,7 @@ use crate::storage;
|
||||
use crate::texture_cache::TEXTURE_REGION_DIMENSIONS;
|
||||
use crate::util::{MatrixHelpers, MaxRect, Recycler};
|
||||
use crate::util::{clamp_to_scale_factor, pack_as_float, project_rect, raster_rect_to_device_pixels};
|
||||
use crate::internal_types::LayoutPrimitiveInfo;
|
||||
use crate::internal_types::{LayoutPrimitiveInfo, Filter};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
pub mod borders;
|
||||
@ -1852,7 +1852,15 @@ impl PrimitiveStore {
|
||||
Some(ref rc) => match rc.composite_mode {
|
||||
// If we have a drop shadow filter, we also need to include the shadow in
|
||||
// our local rect for the purpose of calculating the size of the picture.
|
||||
PictureCompositeMode::Filter(FilterOp::DropShadow(offset, ..)) => pic.snapped_local_rect.translate(&offset),
|
||||
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => pic.snapped_local_rect.translate(&shadow.offset),
|
||||
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
|
||||
let mut rect = LayoutRect::zero();
|
||||
for shadow in shadows {
|
||||
rect = rect.union(&pic.snapped_local_rect.translate(&shadow.offset));
|
||||
}
|
||||
|
||||
rect
|
||||
}
|
||||
_ => LayoutRect::zero(),
|
||||
}
|
||||
None => {
|
||||
@ -2132,9 +2140,17 @@ impl PrimitiveStore {
|
||||
// Inflate the local bounding rect if required by the filter effect.
|
||||
// This inflaction factor is to be applied to the surface itself.
|
||||
let inflation_size = match raster_config.composite_mode {
|
||||
PictureCompositeMode::Filter(FilterOp::Blur(_)) => surface.inflation_factor,
|
||||
PictureCompositeMode::Filter(FilterOp::DropShadow(_, blur_radius, _)) =>
|
||||
(blur_radius * BLUR_SAMPLE_SCALE).ceil(),
|
||||
PictureCompositeMode::Filter(Filter::Blur(_)) => surface.inflation_factor,
|
||||
PictureCompositeMode::Filter(Filter::DropShadow(shadow)) => {
|
||||
(shadow.blur_radius * BLUR_SAMPLE_SCALE).ceil()
|
||||
}
|
||||
PictureCompositeMode::Filter(Filter::DropShadowStack(ref shadows)) => {
|
||||
let mut max = 0.0;
|
||||
for shadow in shadows {
|
||||
max = f32::max(max, shadow.blur_radius * BLUR_SAMPLE_SCALE);
|
||||
}
|
||||
max.ceil()
|
||||
}
|
||||
_ => 0.0,
|
||||
};
|
||||
surface_rect = surface_rect.inflate(inflation_size, inflation_size);
|
||||
@ -2143,8 +2159,15 @@ impl PrimitiveStore {
|
||||
// perspective of its child primitives.
|
||||
let pic_local_rect = surface_rect * TypedScale::new(1.0);
|
||||
if pic.snapped_local_rect != pic_local_rect {
|
||||
if let PictureCompositeMode::Filter(FilterOp::DropShadow(..)) = raster_config.composite_mode {
|
||||
frame_state.gpu_cache.invalidate(&pic.extra_gpu_data_handle);
|
||||
match raster_config.composite_mode {
|
||||
PictureCompositeMode::Filter(Filter::DropShadow(..))
|
||||
| PictureCompositeMode::Filter(Filter::DropShadowStack(..))
|
||||
=> {
|
||||
for handle in &pic.extra_gpu_data_handles {
|
||||
frame_state.gpu_cache.invalidate(handle);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// Invalidate any segments built for this picture, since the local
|
||||
// rect has changed.
|
||||
@ -2397,7 +2420,7 @@ impl PrimitiveStore {
|
||||
) {
|
||||
// Only handle opacity filters for now.
|
||||
let binding = match self.pictures[pic_index.0].requested_composite_mode {
|
||||
Some(PictureCompositeMode::Filter(FilterOp::Opacity(binding, _))) => {
|
||||
Some(PictureCompositeMode::Filter(Filter::Opacity(binding, _))) => {
|
||||
binding
|
||||
}
|
||||
_ => {
|
||||
|
@ -3,14 +3,14 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{
|
||||
ColorU, FilterOp, MixBlendMode,
|
||||
ColorU, MixBlendMode,
|
||||
PropertyBinding, PropertyBindingId,
|
||||
};
|
||||
use api::units::{Au, LayoutSize, LayoutVector2D};
|
||||
use crate::intern::ItemUid;
|
||||
use crate::display_list_flattener::IsVisible;
|
||||
use crate::intern::{Internable, InternDebug, Handle as InternHandle};
|
||||
use crate::internal_types::LayoutPrimitiveInfo;
|
||||
use crate::internal_types::{LayoutPrimitiveInfo, Filter};
|
||||
use crate::picture::PictureCompositeMode;
|
||||
use crate::prim_store::{
|
||||
PrimKey, PrimKeyCommonData, PrimTemplate, PrimTemplateCommonData,
|
||||
@ -39,6 +39,7 @@ pub enum PictureCompositeKey {
|
||||
Saturate(Au),
|
||||
Sepia(Au),
|
||||
DropShadow(VectorKey, Au, ColorU),
|
||||
DropShadowStack(Vec<(VectorKey, Au, ColorU)>),
|
||||
ColorMatrix([Au; 20]),
|
||||
SrgbToLinear,
|
||||
LinearToSrgb,
|
||||
@ -87,21 +88,28 @@ impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
|
||||
}
|
||||
Some(PictureCompositeMode::Filter(op)) => {
|
||||
match op {
|
||||
FilterOp::Blur(value) => PictureCompositeKey::Blur(Au::from_f32_px(value)),
|
||||
FilterOp::Brightness(value) => PictureCompositeKey::Brightness(Au::from_f32_px(value)),
|
||||
FilterOp::Contrast(value) => PictureCompositeKey::Contrast(Au::from_f32_px(value)),
|
||||
FilterOp::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)),
|
||||
FilterOp::HueRotate(value) => PictureCompositeKey::HueRotate(Au::from_f32_px(value)),
|
||||
FilterOp::Invert(value) => PictureCompositeKey::Invert(Au::from_f32_px(value)),
|
||||
FilterOp::Saturate(value) => PictureCompositeKey::Saturate(Au::from_f32_px(value)),
|
||||
FilterOp::Sepia(value) => PictureCompositeKey::Sepia(Au::from_f32_px(value)),
|
||||
FilterOp::SrgbToLinear => PictureCompositeKey::SrgbToLinear,
|
||||
FilterOp::LinearToSrgb => PictureCompositeKey::LinearToSrgb,
|
||||
FilterOp::Identity => PictureCompositeKey::Identity,
|
||||
FilterOp::DropShadow(offset, radius, color) => {
|
||||
PictureCompositeKey::DropShadow(offset.into(), Au::from_f32_px(radius), color.into())
|
||||
Filter::Blur(value) => PictureCompositeKey::Blur(Au::from_f32_px(value)),
|
||||
Filter::Brightness(value) => PictureCompositeKey::Brightness(Au::from_f32_px(value)),
|
||||
Filter::Contrast(value) => PictureCompositeKey::Contrast(Au::from_f32_px(value)),
|
||||
Filter::Grayscale(value) => PictureCompositeKey::Grayscale(Au::from_f32_px(value)),
|
||||
Filter::HueRotate(value) => PictureCompositeKey::HueRotate(Au::from_f32_px(value)),
|
||||
Filter::Invert(value) => PictureCompositeKey::Invert(Au::from_f32_px(value)),
|
||||
Filter::Saturate(value) => PictureCompositeKey::Saturate(Au::from_f32_px(value)),
|
||||
Filter::Sepia(value) => PictureCompositeKey::Sepia(Au::from_f32_px(value)),
|
||||
Filter::SrgbToLinear => PictureCompositeKey::SrgbToLinear,
|
||||
Filter::LinearToSrgb => PictureCompositeKey::LinearToSrgb,
|
||||
Filter::Identity => PictureCompositeKey::Identity,
|
||||
Filter::DropShadowStack(ref shadows) => {
|
||||
PictureCompositeKey::DropShadowStack(
|
||||
shadows.iter().map(|shadow| {
|
||||
(shadow.offset.into(), Au::from_f32_px(shadow.blur_radius), shadow.color.into())
|
||||
}).collect()
|
||||
)
|
||||
}
|
||||
FilterOp::Opacity(binding, _) => {
|
||||
Filter::DropShadow(shadow) => {
|
||||
PictureCompositeKey::DropShadow(shadow.offset.into(), Au::from_f32_px(shadow.blur_radius), shadow.color.into())
|
||||
}
|
||||
Filter::Opacity(binding, _) => {
|
||||
match binding {
|
||||
PropertyBinding::Value(value) => {
|
||||
PictureCompositeKey::Opacity(Au::from_f32_px(value))
|
||||
@ -111,14 +119,14 @@ impl From<Option<PictureCompositeMode>> for PictureCompositeKey {
|
||||
}
|
||||
}
|
||||
}
|
||||
FilterOp::ColorMatrix(values) => {
|
||||
Filter::ColorMatrix(values) => {
|
||||
let mut quantized_values: [Au; 20] = [Au(0); 20];
|
||||
for (value, result) in values.iter().zip(quantized_values.iter_mut()) {
|
||||
*result = Au::from_f32_px(*value);
|
||||
}
|
||||
PictureCompositeKey::ColorMatrix(quantized_values)
|
||||
}
|
||||
FilterOp::ComponentTransfer => unreachable!(),
|
||||
Filter::ComponentTransfer => unreachable!(),
|
||||
}
|
||||
}
|
||||
Some(PictureCompositeMode::ComponentTransferFilter(handle)) => {
|
||||
|
@ -676,6 +676,32 @@ pub enum ClearMode {
|
||||
Transparent,
|
||||
}
|
||||
|
||||
/// In order to avoid duplicating the down-scaling and blur passes when a picture has several blurs,
|
||||
/// we use a local (primitive-level) cache of the render tasks generated for a single shadowed primitive
|
||||
/// in a single frame.
|
||||
pub type BlurTaskCache = FastHashMap<BlurTaskKey, RenderTaskId>;
|
||||
|
||||
/// Since we only use it within a single primitive, the key only needs to contain the down-scaling level
|
||||
/// and the blur std deviation.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum BlurTaskKey {
|
||||
DownScale(u32),
|
||||
Blur { downscale_level: u32, stddev_x: u32, stddev_y: u32 },
|
||||
}
|
||||
|
||||
impl BlurTaskKey {
|
||||
fn downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self {
|
||||
// Quantise the std deviations and store it as integers to work around
|
||||
// Eq and Hash's f32 allergy.
|
||||
// The blur radius is rounded before RenderTask::new_blur so we don't need
|
||||
// a lot of precision.
|
||||
const QUANTIZATION_FACTOR: f32 = 1024.0;
|
||||
let stddev_x = (blur_stddev.width * QUANTIZATION_FACTOR) as u32;
|
||||
let stddev_y = (blur_stddev.height * QUANTIZATION_FACTOR) as u32;
|
||||
BlurTaskKey::Blur { downscale_level, stddev_x, stddev_y }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
@ -905,15 +931,14 @@ impl RenderTask {
|
||||
let mask_task_id = render_tasks.add(mask_task);
|
||||
|
||||
// Blur it
|
||||
let blur_render_task = RenderTask::new_blur(
|
||||
RenderTask::new_blur(
|
||||
DeviceSize::new(blur_radius_dp, blur_radius_dp),
|
||||
mask_task_id,
|
||||
render_tasks,
|
||||
RenderTargetKind::Alpha,
|
||||
ClearMode::Zero,
|
||||
);
|
||||
|
||||
render_tasks.add(blur_render_task)
|
||||
None,
|
||||
)
|
||||
}
|
||||
));
|
||||
}
|
||||
@ -1023,7 +1048,8 @@ impl RenderTask {
|
||||
render_tasks: &mut RenderTaskTree,
|
||||
target_kind: RenderTargetKind,
|
||||
clear_mode: ClearMode,
|
||||
) -> Self {
|
||||
mut blur_cache: Option<&mut BlurTaskCache>,
|
||||
) -> RenderTaskId {
|
||||
// Adjust large std deviation value.
|
||||
let mut adjusted_blur_std_deviation = blur_std_deviation;
|
||||
let (blur_target_size, uv_rect_kind) = {
|
||||
@ -1033,6 +1059,7 @@ impl RenderTask {
|
||||
let mut adjusted_blur_target_size = blur_target_size;
|
||||
let mut downscaling_src_task_id = src_task_id;
|
||||
let mut scale_factor = 1.0;
|
||||
let mut n_downscales = 1;
|
||||
while adjusted_blur_std_deviation.width > MAX_BLUR_STD_DEVIATION &&
|
||||
adjusted_blur_std_deviation.height > MAX_BLUR_STD_DEVIATION {
|
||||
if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
|
||||
@ -1042,40 +1069,72 @@ impl RenderTask {
|
||||
adjusted_blur_std_deviation = adjusted_blur_std_deviation * 0.5;
|
||||
scale_factor *= 2.0;
|
||||
adjusted_blur_target_size = (blur_target_size.to_f32() / scale_factor).to_i32();
|
||||
let downscaling_task = RenderTask::new_scaling(
|
||||
downscaling_src_task_id,
|
||||
render_tasks,
|
||||
target_kind,
|
||||
adjusted_blur_target_size,
|
||||
);
|
||||
downscaling_src_task_id = render_tasks.add(downscaling_task);
|
||||
|
||||
let cached_task = match blur_cache {
|
||||
Some(ref mut cache) => cache.get(&BlurTaskKey::DownScale(n_downscales)).cloned(),
|
||||
None => None,
|
||||
};
|
||||
|
||||
downscaling_src_task_id = cached_task.unwrap_or_else(|| {
|
||||
let downscaling_task = RenderTask::new_scaling(
|
||||
downscaling_src_task_id,
|
||||
render_tasks,
|
||||
target_kind,
|
||||
adjusted_blur_target_size,
|
||||
);
|
||||
render_tasks.add(downscaling_task)
|
||||
});
|
||||
|
||||
if let Some(ref mut cache) = blur_cache {
|
||||
cache.insert(BlurTaskKey::DownScale(n_downscales), downscaling_src_task_id);
|
||||
}
|
||||
|
||||
n_downscales += 1;
|
||||
}
|
||||
|
||||
let blur_task_v = RenderTask::with_dynamic_location(
|
||||
adjusted_blur_target_size,
|
||||
vec![downscaling_src_task_id],
|
||||
RenderTaskKind::VerticalBlur(BlurTask {
|
||||
blur_std_deviation: adjusted_blur_std_deviation.height,
|
||||
target_kind,
|
||||
uv_rect_handle: GpuCacheHandle::new(),
|
||||
uv_rect_kind,
|
||||
}),
|
||||
clear_mode,
|
||||
);
|
||||
|
||||
let blur_task_v_id = render_tasks.add(blur_task_v);
|
||||
let blur_key = BlurTaskKey::downscale_and_blur(n_downscales, adjusted_blur_std_deviation);
|
||||
|
||||
RenderTask::with_dynamic_location(
|
||||
adjusted_blur_target_size,
|
||||
vec![blur_task_v_id],
|
||||
RenderTaskKind::HorizontalBlur(BlurTask {
|
||||
blur_std_deviation: adjusted_blur_std_deviation.width,
|
||||
target_kind,
|
||||
uv_rect_handle: GpuCacheHandle::new(),
|
||||
uv_rect_kind,
|
||||
}),
|
||||
clear_mode,
|
||||
)
|
||||
let cached_task = match blur_cache {
|
||||
Some(ref mut cache) => cache.get(&blur_key).cloned(),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let blur_task_id = cached_task.unwrap_or_else(|| {
|
||||
let blur_task_v = RenderTask::with_dynamic_location(
|
||||
adjusted_blur_target_size,
|
||||
vec![downscaling_src_task_id],
|
||||
RenderTaskKind::VerticalBlur(BlurTask {
|
||||
blur_std_deviation: adjusted_blur_std_deviation.height,
|
||||
target_kind,
|
||||
uv_rect_handle: GpuCacheHandle::new(),
|
||||
uv_rect_kind,
|
||||
}),
|
||||
clear_mode,
|
||||
);
|
||||
|
||||
let blur_task_v_id = render_tasks.add(blur_task_v);
|
||||
|
||||
let blur_task_h = RenderTask::with_dynamic_location(
|
||||
adjusted_blur_target_size,
|
||||
vec![blur_task_v_id],
|
||||
RenderTaskKind::HorizontalBlur(BlurTask {
|
||||
blur_std_deviation: adjusted_blur_std_deviation.width,
|
||||
target_kind,
|
||||
uv_rect_handle: GpuCacheHandle::new(),
|
||||
uv_rect_kind,
|
||||
}),
|
||||
clear_mode,
|
||||
);
|
||||
|
||||
render_tasks.add(blur_task_h)
|
||||
});
|
||||
|
||||
if let Some(ref mut cache) = blur_cache {
|
||||
cache.insert(blur_key, blur_task_id);
|
||||
}
|
||||
|
||||
blur_task_id
|
||||
}
|
||||
|
||||
pub fn new_border_segment(
|
||||
@ -1393,7 +1452,6 @@ impl RenderTask {
|
||||
if let Some(mut request) = gpu_cache.request(cache_handle) {
|
||||
let p0 = target_rect.origin.to_f32();
|
||||
let p1 = target_rect.bottom_right().to_f32();
|
||||
|
||||
let image_source = ImageSource {
|
||||
p0,
|
||||
p1,
|
||||
|
@ -6,7 +6,7 @@ use api::{BuiltDisplayList, ColorF, DynamicProperties, Epoch};
|
||||
use api::{FilterOp, TempFilterData, FilterData, ComponentTransferFuncType};
|
||||
use api::{PipelineId, PropertyBinding, PropertyBindingId, ItemRange, MixBlendMode, StackingContext};
|
||||
use api::units::{LayoutSize, LayoutTransform};
|
||||
use crate::internal_types::FastHashMap;
|
||||
use crate::internal_types::{FastHashMap, Filter};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Stores a map of the animated property bindings for the current display list. These
|
||||
@ -203,73 +203,13 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
|
||||
/// An arbitrary number which we assume opacity is invisible below.
|
||||
pub const OPACITY_EPSILON: f32 = 0.001;
|
||||
|
||||
pub trait FilterOpHelpers {
|
||||
fn is_visible(&self) -> bool;
|
||||
fn is_noop(&self) -> bool;
|
||||
}
|
||||
|
||||
impl FilterOpHelpers for FilterOp {
|
||||
fn is_visible(&self) -> bool {
|
||||
match *self {
|
||||
FilterOp::Identity |
|
||||
FilterOp::Blur(..) |
|
||||
FilterOp::Brightness(..) |
|
||||
FilterOp::Contrast(..) |
|
||||
FilterOp::Grayscale(..) |
|
||||
FilterOp::HueRotate(..) |
|
||||
FilterOp::Invert(..) |
|
||||
FilterOp::Saturate(..) |
|
||||
FilterOp::Sepia(..) |
|
||||
FilterOp::DropShadow(..) |
|
||||
FilterOp::ColorMatrix(..) |
|
||||
FilterOp::SrgbToLinear |
|
||||
FilterOp::LinearToSrgb |
|
||||
FilterOp::ComponentTransfer => true,
|
||||
FilterOp::Opacity(_, amount) => {
|
||||
amount > OPACITY_EPSILON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_noop(&self) -> bool {
|
||||
match *self {
|
||||
FilterOp::Identity => false, // this is intentional
|
||||
FilterOp::Blur(length) => length == 0.0,
|
||||
FilterOp::Brightness(amount) => amount == 1.0,
|
||||
FilterOp::Contrast(amount) => amount == 1.0,
|
||||
FilterOp::Grayscale(amount) => amount == 0.0,
|
||||
FilterOp::HueRotate(amount) => amount == 0.0,
|
||||
FilterOp::Invert(amount) => amount == 0.0,
|
||||
FilterOp::Opacity(_, amount) => amount >= 1.0,
|
||||
FilterOp::Saturate(amount) => amount == 1.0,
|
||||
FilterOp::Sepia(amount) => amount == 0.0,
|
||||
FilterOp::DropShadow(offset, blur, _) => {
|
||||
offset.x == 0.0 && offset.y == 0.0 && blur == 0.0
|
||||
},
|
||||
FilterOp::ColorMatrix(matrix) => {
|
||||
matrix == [1.0, 0.0, 0.0, 0.0,
|
||||
0.0, 1.0, 0.0, 0.0,
|
||||
0.0, 0.0, 1.0, 0.0,
|
||||
0.0, 0.0, 0.0, 1.0,
|
||||
0.0, 0.0, 0.0, 0.0]
|
||||
}
|
||||
FilterOp::SrgbToLinear |
|
||||
FilterOp::LinearToSrgb |
|
||||
FilterOp::ComponentTransfer => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StackingContextHelpers {
|
||||
fn mix_blend_mode_for_compositing(&self) -> Option<MixBlendMode>;
|
||||
fn filter_ops_for_compositing(
|
||||
&self,
|
||||
display_list: &BuiltDisplayList,
|
||||
input_filters: ItemRange<FilterOp>,
|
||||
) -> Vec<FilterOp>;
|
||||
) -> Vec<Filter>;
|
||||
fn filter_datas_for_compositing(
|
||||
&self,
|
||||
display_list: &BuiltDisplayList,
|
||||
@ -289,13 +229,13 @@ impl StackingContextHelpers for StackingContext {
|
||||
&self,
|
||||
display_list: &BuiltDisplayList,
|
||||
input_filters: ItemRange<FilterOp>,
|
||||
) -> Vec<FilterOp> {
|
||||
) -> Vec<Filter> {
|
||||
// TODO(gw): Now that we resolve these later on,
|
||||
// we could probably make it a bit
|
||||
// more efficient than cloning these here.
|
||||
let mut filters = vec![];
|
||||
for filter in display_list.get(input_filters) {
|
||||
filters.push(filter);
|
||||
filters.push(filter.into());
|
||||
}
|
||||
filters
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use api::{ColorF, BorderStyle, MixBlendMode, PipelineId, PremultipliedColorF};
|
||||
use api::{DocumentLayer, FilterData, FilterOp, ImageFormat, LineOrientation};
|
||||
use api::{DocumentLayer, FilterData, ImageFormat, LineOrientation};
|
||||
use api::units::*;
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use api::FontRenderMode;
|
||||
@ -18,7 +18,7 @@ use crate::frame_builder::FrameGlobalResources;
|
||||
use crate::gpu_cache::{GpuCache};
|
||||
use crate::gpu_types::{BorderInstance, BlurDirection, BlurInstance, PrimitiveHeaders, ScalingInstance};
|
||||
use crate::gpu_types::{TransformData, TransformPalette, ZBufferIdGenerator};
|
||||
use crate::internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex, TextureSource};
|
||||
use crate::internal_types::{CacheTextureId, FastHashMap, SavedTargetIndex, TextureSource, Filter};
|
||||
#[cfg(feature = "pathfinder")]
|
||||
use pathfinder_partitioner::mesh::Mesh;
|
||||
use crate::picture::{RecordedDirtyRegion, SurfaceInfo};
|
||||
@ -1160,7 +1160,7 @@ impl RenderPass {
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CompositeOps {
|
||||
// Requires only a single texture as input (e.g. most filters)
|
||||
pub filters: Vec<FilterOp>,
|
||||
pub filters: Vec<Filter>,
|
||||
pub filter_datas: Vec<FilterData>,
|
||||
|
||||
// Requires two source textures (e.g. mix-blend-mode)
|
||||
@ -1168,7 +1168,7 @@ pub struct CompositeOps {
|
||||
}
|
||||
|
||||
impl CompositeOps {
|
||||
pub fn new(filters: Vec<FilterOp>,
|
||||
pub fn new(filters: Vec<Filter>,
|
||||
filter_datas: Vec<FilterData>,
|
||||
mix_blend_mode: Option<MixBlendMode>) -> Self {
|
||||
CompositeOps {
|
||||
|
@ -521,6 +521,7 @@ pub struct BoxShadowDisplayItem {
|
||||
pub struct PushShadowDisplayItem {
|
||||
pub space_and_clip: SpaceAndClipInfo,
|
||||
pub shadow: Shadow,
|
||||
pub should_inflate: bool,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@ -529,7 +530,6 @@ pub struct Shadow {
|
||||
pub offset: LayoutVector2D,
|
||||
pub color: ColorF,
|
||||
pub blur_radius: f32,
|
||||
pub should_inflate: bool,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
@ -712,31 +712,13 @@ pub enum FilterOp {
|
||||
Opacity(PropertyBinding<f32>, f32),
|
||||
Saturate(f32),
|
||||
Sepia(f32),
|
||||
DropShadow(LayoutVector2D, f32, ColorF),
|
||||
DropShadow(Shadow),
|
||||
ColorMatrix([f32; 20]),
|
||||
SrgbToLinear,
|
||||
LinearToSrgb,
|
||||
ComponentTransfer,
|
||||
}
|
||||
|
||||
impl FilterOp {
|
||||
/// Ensure that the parameters for a filter operation
|
||||
/// are sensible.
|
||||
pub fn sanitize(&self) -> FilterOp {
|
||||
match self {
|
||||
FilterOp::Blur(radius) => {
|
||||
let radius = radius.min(MAX_BLUR_RADIUS);
|
||||
FilterOp::Blur(radius)
|
||||
}
|
||||
FilterOp::DropShadow(offset, radius, color) => {
|
||||
let radius = radius.min(MAX_BLUR_RADIUS);
|
||||
FilterOp::DropShadow(*offset, radius, *color)
|
||||
}
|
||||
filter => filter.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
|
||||
pub enum ComponentTransferFuncType {
|
||||
|
@ -1640,10 +1640,12 @@ impl DisplayListBuilder {
|
||||
&mut self,
|
||||
space_and_clip: &di::SpaceAndClipInfo,
|
||||
shadow: di::Shadow,
|
||||
should_inflate: bool,
|
||||
) {
|
||||
let item = di::DisplayItem::PushShadow(di::PushShadowDisplayItem {
|
||||
space_and_clip: *space_and_clip,
|
||||
shadow,
|
||||
should_inflate,
|
||||
});
|
||||
self.push_item(&item);
|
||||
}
|
||||
|
@ -952,8 +952,8 @@ impl<'a> RawtestHarness<'a> {
|
||||
offset: LayoutVector2D::new(1.0, 1.0),
|
||||
blur_radius: 1.0,
|
||||
color: ColorF::new(0.0, 0.0, 0.0, 1.0),
|
||||
should_inflate: true,
|
||||
},
|
||||
true,
|
||||
);
|
||||
let info = CommonItemProperties {
|
||||
clip_rect: rect(110., 110., 50., 2.),
|
||||
@ -1030,8 +1030,8 @@ impl<'a> RawtestHarness<'a> {
|
||||
offset: LayoutVector2D::new(1.0, 1.0),
|
||||
blur_radius: 1.0,
|
||||
color: shadow_color,
|
||||
should_inflate: true,
|
||||
},
|
||||
true,
|
||||
);
|
||||
let info = self.make_common_properties(rect(110., 110., 50., 2.));
|
||||
builder.push_line(
|
||||
|
@ -1696,8 +1696,8 @@ impl YamlFrameReader {
|
||||
blur_radius,
|
||||
offset,
|
||||
color,
|
||||
should_inflate: true,
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -238,6 +238,15 @@ fn write_reference_frame(
|
||||
usize_node(parent, "id", clip_id_mapper.add_spatial_id(reference_frame.id));
|
||||
}
|
||||
|
||||
fn shadow_parameters(shadow: &Shadow) -> String {
|
||||
format!(
|
||||
"[{},{}],{},[{}]",
|
||||
shadow.offset.x, shadow.offset.y,
|
||||
shadow.blur_radius,
|
||||
color_to_string(shadow.color)
|
||||
)
|
||||
}
|
||||
|
||||
fn write_stacking_context(
|
||||
parent: &mut Table,
|
||||
sc: &StackingContext,
|
||||
@ -279,11 +288,11 @@ fn write_stacking_context(
|
||||
}
|
||||
FilterOp::Saturate(x) => { filters.push(Yaml::String(format!("saturate({})", x))) }
|
||||
FilterOp::Sepia(x) => { filters.push(Yaml::String(format!("sepia({})", x))) }
|
||||
FilterOp::DropShadow(offset, blur, color) => {
|
||||
filters.push(Yaml::String(format!("drop-shadow([{},{}],{},[{}])",
|
||||
offset.x, offset.y,
|
||||
blur,
|
||||
color_to_string(color))))
|
||||
FilterOp::DropShadow(shadow) => {
|
||||
filters.push(Yaml::String(format!(
|
||||
"drop-shadow({})",
|
||||
shadow_parameters(&shadow)
|
||||
)))
|
||||
}
|
||||
FilterOp::ColorMatrix(matrix) => {
|
||||
filters.push(Yaml::String(format!("color-matrix({:?})", matrix)))
|
||||
|
@ -601,9 +601,11 @@ impl YamlHelper for Yaml {
|
||||
let str = format!("---\noffset: {}\nblur-radius: {}\ncolor: {}\n", args[0], args[1], args[2]);
|
||||
let mut yaml_doc = YamlLoader::load_from_str(&str).expect("Failed to parse drop-shadow");
|
||||
let yaml = yaml_doc.pop().unwrap();
|
||||
Some(FilterOp::DropShadow(yaml["offset"].as_vector().unwrap(),
|
||||
yaml["blur-radius"].as_f32().unwrap(),
|
||||
yaml["color"].as_colorf().unwrap()))
|
||||
Some(FilterOp::DropShadow(Shadow {
|
||||
offset: yaml["offset"].as_vector().unwrap(),
|
||||
blur_radius: yaml["blur-radius"].as_f32().unwrap(),
|
||||
color: yaml["color"].as_colorf().unwrap()
|
||||
}))
|
||||
}
|
||||
("color-matrix", ref args, _) if args.len() == 20 => {
|
||||
let m: Vec<f32> = args.iter().map(|f| f.parse().unwrap()).collect();
|
||||
|
Loading…
Reference in New Issue
Block a user