diff --git a/gfx/wr/webrender/src/picture.rs b/gfx/wr/webrender/src/picture.rs index 8c1307b72e8a..7110a1163fc5 100644 --- a/gfx/wr/webrender/src/picture.rs +++ b/gfx/wr/webrender/src/picture.rs @@ -2316,7 +2316,7 @@ impl PicturePrimitive { blur_std_deviation * scale_factors.1 ); let inflation_factor = frame_state.surfaces[raster_config.surface_index.0].inflation_factor; - let inflation_factor = (inflation_factor * device_pixel_scale.0).ceil() as i32; + let inflation_factor = (inflation_factor * device_pixel_scale.0).ceil(); // The clipped field is the part of the picture that is visible // on screen. The unclipped field is the screen-space rect of @@ -2326,10 +2326,20 @@ impl PicturePrimitive { // blur results, inflate that clipped area by the blur range, and // then intersect with the total screen rect, to minimize the // allocation size. - let mut device_rect = clipped + // We cast clipped to f32 instead of casting unclipped to i32 + // because unclipped can overflow an i32. + let device_rect = clipped.to_f32() .inflate(inflation_factor, inflation_factor) - .intersection(&unclipped.to_i32()) + .intersection(&unclipped) .unwrap(); + + let mut device_rect = match device_rect.try_cast::() { + Some(rect) => rect, + None => { + return None + } + }; + // Adjust the size to avoid introducing sampling errors during the down-scaling passes. // what would be even better is to rasterize the picture at the down-scaled size // directly. @@ -2379,10 +2389,21 @@ impl PicturePrimitive { } 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()) + let max_blur_range = (max_std_deviation * BLUR_SAMPLE_SCALE).ceil(); + // We cast clipped to f32 instead of casting unclipped to i32 + // because unclipped can overflow an i32. + let device_rect = clipped.to_f32() + .inflate(max_blur_range, max_blur_range) + .intersection(&unclipped) .unwrap(); + + let mut device_rect = match device_rect.try_cast::() { + Some(rect) => rect, + None => { + return None + } + }; + device_rect.size = RenderTask::adjusted_blur_source_size( device_rect.size, DeviceSize::new(max_std_deviation, max_std_deviation), diff --git a/gfx/wr/wrench/reftests/filters/filter-drop-shadow-transform-huge.yaml b/gfx/wr/wrench/reftests/filters/filter-drop-shadow-transform-huge.yaml new file mode 100644 index 000000000000..65c699f8fa39 --- /dev/null +++ b/gfx/wr/wrench/reftests/filters/filter-drop-shadow-transform-huge.yaml @@ -0,0 +1,17 @@ +# Don't crash on large blur radius with large transform! +--- +root: + items: + - type: stacking-context + bounds: [0, 0, 1000, 1000] + items: + - type: stacking-context + bounds: [0, 0, 1000, 1000] + transform: scale-y(999999.25) + items: + - type: stacking-context + bounds: [0, 0, 1000, 1000] + filters: drop-shadow([999999, 999999], 999999, [255, 0, 0, 1]) + items: + - image: checkerboard(2, 16, 16) + bounds: [0, 0, 1000, 1000] diff --git a/gfx/wr/wrench/reftests/filters/reftest.list b/gfx/wr/wrench/reftests/filters/reftest.list index 3f209c3289ab..b71c2a957079 100644 --- a/gfx/wr/wrench/reftests/filters/reftest.list +++ b/gfx/wr/wrench/reftests/filters/reftest.list @@ -42,6 +42,7 @@ skip_on(android) == filter-mix-blend-mode.yaml filter-mix-blend-mode-ref.yaml # != srgb-to-linear-2.yaml srgb-to-linear-ref.yaml != filter-blur-huge.yaml blank.yaml != filter-drop-shadow-huge.yaml blank.yaml +!= filter-drop-shadow-transform-huge.yaml blank.yaml == filter-blur-scaled.yaml filter-blur-scaled-ref.yaml skip_on(android) == filter-blur-scaled-xonly.yaml filter-blur-scaled-xonly.png # fails on Android emulator and Pixel2 skip_on(android,emulator) == svg-filter-component-transfer.yaml filter-component-transfer-ref.yaml # fails on Android emulator