sparse_strips: Fix clipping for inverse blurred_rounded_rects#1721
sparse_strips: Fix clipping for inverse blurred_rounded_rects#1721nicoburns wants to merge 1 commit into
Conversation
Signed-off-by: Nico Burns <nico@nicoburns.com>
|
This doesn't make a lot of sense to me, why are we expanding by the rectangle origin? Doesn't that mean the expansion depends on the exact position of the rectangle? I think it would be best to figure out what Skia does here and then try to emulate that. |
So, the API in this PR only works if you don't use the origin to encode the position. You have to use the transform to encode the position, and the use the origin the encode the "offset" of the shadow. (probably I should change that to have a less confusing API with a dedicated offset parameter?) |
|
I’m not gonna block on it, but it seems to me like the semantics of an inset box shadow are slightly different than a normal box shadow? So maybe it should be a separate method altogether instead of just a boolean flag. But again, I think it would be worth finding out how Skia exposes both of those functionalities so we can draw some inspiration. |
|
I think there are exactly two differences between an "inset" and a "outset" (regular) box-shadow:
In principle this is an exact inversion of the clip. In practice:
What I would ideally like to do is implement a method that correctly handles all of this clipping for the user. At which point, I think it would probably make sense to rename the method (I'm not sure if there are any use cases for filling a more general shape with the blurred_rounded_rect fill. We could expose that too if it's useful (but I suspect it's not)) |
|
For Skia, anyrender currently has the following: fn draw_box_shadow(
&mut self,
transform: kurbo::Affine,
rect: kurbo::Rect,
brush: peniko::Color,
radius: f64,
std_dev: f64,
inset: bool,
) {
self.set_matrix(transform);
self.reset_paint();
self.set_paint_brush(brush, None);
self.cache.paint.set_style(PaintStyle::Fill);
if std_dev > 0.0 {
self.cache.paint.set_mask_filter(
MaskFilter::blur(BlurStyle::Normal, std_dev as f32, false).unwrap(),
);
}
let rrect = RRect::new_nine_patch(
Rect::new(
rect.x0 as f32,
rect.y0 as f32,
rect.x1 as f32,
rect.y1 as f32,
),
radius as f32,
radius as f32,
radius as f32,
radius as f32,
);
if inset {
let mut path = Path::rrect(rrect, None);
path.set_fill_type(PathFillType::InverseWinding);
self.inner.draw_path(&path, &self.cache.paint);
} else {
self.inner.draw_rrect(rrect, &self.cache.paint);
}
}I'm not sure if that is a "fast shadow" or a filter-based one. But it does produce the same output as the Vello shadows. |
|
It does make me a bit uneasy implementing a primitive with such CSS specific semantics as a Vello primitive, but I think it makes sense and I could live with it. 😄 |
I would (personally) be more worried if it was a "deep" feature. But given that the CSS-specific bit will be contained to the top-level method it seems less problematic to me. It's sugar for sure, but should be easy to maintain. I'll try to code it up early next week (shouldn't be too hard I don't think), and then perhaps I can bring to Rendering Office hours when that next happens. |
|
I looked into building the full clipping into Vello, and am running into the problem that the shape I want for Blitz is rounded rect with eliptical radii which isn't available in Kurbo. And at some point I may want a superellipse. I'm now wondering if we ought to go the other way and allow an arbitrary Which also brings up the question of why |
|
I think that’s just what made the most sense to me back then. |


Follow-up fix for #1715 which has a bug where "hard" shadows (with blur
std_devof 0) don't render correctly. This is because the inverse shadows were still using the clip-region logic used for "regular" shadows (inflate rect by2.5 * std_dev) which happens to work if thestd_devis large enough, but in general clips far too aggressively if thestd_devis small and not aggressively enough when it is large.For inverse shadows, what we want to do is clip to the exact size of the user-specified rect, but possibly at a location that is offset (translated) relative to where the shadow itself should render (if the shadow has a offset specified). This PR uses a bit of hack to encode this in the origin of the Rect:
This results in correct rendering, but is arguably a bit of a hack. I could add a separate offset (Vec2) parameter if that's preferred.
This PR was entirely hand written.