Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 07325f1

Browse files
committed
[Impeller] Add mask blur style support to the RRect blur fast path.
1 parent c4b9afa commit 07325f1

File tree

3 files changed

+211
-19
lines changed

3 files changed

+211
-19
lines changed

impeller/aiks/aiks_unittests.cc

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,6 +1614,122 @@ TEST_P(AiksTest, SolidColorCirclesOvalsRRectsMaskBlurCorrectly) {
16141614
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
16151615
}
16161616

1617+
TEST_P(AiksTest,
1618+
SolidColorCirclesOvalsRRectsMaskBlurCorrectlyBlurStyleVariations) {
1619+
Canvas canvas;
1620+
canvas.Scale(GetContentScale());
1621+
canvas.Scale(Vector2{0.8f, 0.8f});
1622+
Paint paint;
1623+
paint.mask_blur_descriptor = Paint::MaskBlurDescriptor{
1624+
.style = FilterContents::BlurStyle::kNormal,
1625+
.sigma = Sigma{1},
1626+
};
1627+
1628+
canvas.DrawPaint({.color = Color::AntiqueWhite()});
1629+
1630+
struct MaskBlurTestConfig {
1631+
FilterContents::BlurStyle style = FilterContents::BlurStyle::kNormal;
1632+
Scalar sigma = 1.0f;
1633+
Scalar alpha = 1.0f;
1634+
std::shared_ptr<ImageFilter> image_filter;
1635+
bool invert_colors = false;
1636+
BlendMode blend_mode = BlendMode::kSourceOver;
1637+
};
1638+
1639+
std::vector<MaskBlurTestConfig> paint_variations = {
1640+
// 1. Normal style, translucent, zero sigma.
1641+
{.style = FilterContents::BlurStyle::kNormal,
1642+
.sigma = 0.0f,
1643+
.alpha = 0.5f},
1644+
// 2. Normal style, translucent.
1645+
{.style = FilterContents::BlurStyle::kNormal,
1646+
.sigma = 8.0f,
1647+
.alpha = 0.5f},
1648+
// 3. Solid style, translucent.
1649+
{.style = FilterContents::BlurStyle::kSolid,
1650+
.sigma = 8.0f,
1651+
.alpha = 0.5f},
1652+
// 4. Solid style, opaque.
1653+
{.style = FilterContents::BlurStyle::kSolid, .sigma = 8.0f},
1654+
// 5. Solid style, translucent, color & image filtered.
1655+
{.style = FilterContents::BlurStyle::kSolid,
1656+
.sigma = 8.0f,
1657+
.alpha = 0.5f,
1658+
.image_filter = ImageFilter::MakeBlur(Sigma{3}, Sigma{3},
1659+
FilterContents::BlurStyle::kNormal,
1660+
Entity::TileMode::kClamp),
1661+
.invert_colors = true},
1662+
// 6. Solid style, translucent, exclusion blended.
1663+
{.style = FilterContents::BlurStyle::kSolid,
1664+
.sigma = 8.0f,
1665+
.alpha = 0.5f,
1666+
.blend_mode = BlendMode::kExclusion},
1667+
// 7. Inner style, translucent.
1668+
{.style = FilterContents::BlurStyle::kInner,
1669+
.sigma = 8.0f,
1670+
.alpha = 0.5f},
1671+
// 8. Inner style, translucent, blurred.
1672+
{.style = FilterContents::BlurStyle::kInner,
1673+
.sigma = 8.0f,
1674+
.alpha = 0.5f,
1675+
.image_filter = ImageFilter::MakeBlur(Sigma{3}, Sigma{3},
1676+
FilterContents::BlurStyle::kNormal,
1677+
Entity::TileMode::kClamp)},
1678+
// 9. Outer style, translucent.
1679+
{.style = FilterContents::BlurStyle::kOuter,
1680+
.sigma = 8.0f,
1681+
.alpha = 0.5f},
1682+
// 10. Outer style, opaque, image filtered.
1683+
{.style = FilterContents::BlurStyle::kOuter,
1684+
.sigma = 8.0f,
1685+
.image_filter = ImageFilter::MakeBlur(Sigma{3}, Sigma{3},
1686+
FilterContents::BlurStyle::kNormal,
1687+
Entity::TileMode::kClamp)},
1688+
};
1689+
1690+
for (size_t i = 0; i < paint_variations.size(); i++) {
1691+
const MaskBlurTestConfig& config = paint_variations[i];
1692+
paint.mask_blur_descriptor->style = config.style;
1693+
paint.mask_blur_descriptor->sigma = Sigma{config.sigma};
1694+
paint.image_filter = config.image_filter;
1695+
paint.invert_colors = config.invert_colors;
1696+
paint.blend_mode = config.blend_mode;
1697+
1698+
const Scalar x = 50 + i * 80;
1699+
const Scalar radius = 20.0f;
1700+
const Scalar y_spacing = 100.0f;
1701+
1702+
Scalar y = y_spacing;
1703+
paint.color = Color::Crimson().WithAlpha(config.alpha);
1704+
canvas.DrawRect(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1705+
radius, 60.0f - radius),
1706+
paint);
1707+
1708+
y += y_spacing;
1709+
paint.color = Color::Blue().WithAlpha(config.alpha);
1710+
canvas.DrawCircle({x + 25, y + 25}, radius, paint);
1711+
1712+
y += y_spacing;
1713+
paint.color = Color::Green().WithAlpha(config.alpha);
1714+
canvas.DrawOval(Rect::MakeXYWH(x + 25 - radius / 2, y + radius / 2, //
1715+
radius, 60.0f - radius),
1716+
paint);
1717+
1718+
y += y_spacing;
1719+
paint.color = Color::Purple().WithAlpha(config.alpha);
1720+
canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
1721+
{radius, radius}, //
1722+
paint);
1723+
1724+
y += y_spacing;
1725+
paint.color = Color::Orange().WithAlpha(config.alpha);
1726+
canvas.DrawRRect(Rect::MakeXYWH(x, y, 60.0f, 60.0f), //
1727+
{radius, 5.0f}, paint);
1728+
}
1729+
1730+
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
1731+
}
1732+
16171733
TEST_P(AiksTest, FilledRoundRectPathsRenderCorrectly) {
16181734
Canvas canvas;
16191735
canvas.Scale(GetContentScale());

impeller/aiks/canvas.cc

Lines changed: 94 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -308,41 +308,117 @@ void Canvas::DrawPaint(const Paint& paint) {
308308
}
309309

310310
bool Canvas::AttemptDrawBlurredRRect(const Rect& rect,
311-
Size corner_radius,
311+
Size corner_radii,
312312
const Paint& paint) {
313313
if (paint.color_source.GetType() != ColorSource::Type::kColor ||
314314
paint.style != Paint::Style::kFill) {
315315
return false;
316316
}
317317

318-
if (!paint.mask_blur_descriptor.has_value() ||
319-
paint.mask_blur_descriptor->style != FilterContents::BlurStyle::kNormal) {
320-
return false;
321-
}
322318
// A blur sigma that is not positive enough should not result in a blur.
323319
if (paint.mask_blur_descriptor->sigma.sigma <= kEhCloseEnough) {
324320
return false;
325321
}
326322

327-
Paint new_paint = paint;
328-
329323
// For symmetrically mask blurred solid RRects, absorb the mask blur and use
330324
// a faster SDF approximation.
331325

332-
auto contents = std::make_shared<SolidRRectBlurContents>();
333-
contents->SetColor(new_paint.color);
334-
contents->SetSigma(new_paint.mask_blur_descriptor->sigma);
335-
contents->SetRRect(rect, corner_radius);
326+
Paint rrect_paint = paint;
327+
328+
// Absorb the color filter, if any.
329+
if (rrect_paint.HasColorFilter()) {
330+
rrect_paint.color =
331+
rrect_paint.GetColorFilter()->GetCPUColorFilterProc()(paint.color);
332+
rrect_paint.color_filter = nullptr;
333+
rrect_paint.invert_colors = false;
334+
}
335+
336+
// In some cases, we need to render the mask blur to a separate layer.
337+
//
338+
// 1. If the blur style is normal, we'll be drawing using one draw call and
339+
// no clips. And so we can just wrap the RRect contents with the
340+
// ImageFilter, which will get applied to the result as per usual.
341+
//
342+
// 2. If the blur style is solid, we combine the non-blurred RRect with the
343+
// blurred RRect via two separate draw calls, and so we need to defer any
344+
// fancy blending, translucency, or image filtering until after these two
345+
// draws have been combined in a separate layer.
346+
//
347+
// 3. If the blur style is outer or inner, we apply the blur style via a
348+
// clip. The ImageFilter needs to be applied to the mask blurred result.
349+
// And so if there's an ImageFilter, we need to defer applying it until
350+
// after the clipped RRect blur has been drawn to a separate texture.
351+
// However, since there's only one draw call that produces color, we
352+
// don't need to worry about the blend mode or translucency (unlike with
353+
// BlurStyle::kSolid).
354+
//
355+
if ((rrect_paint.mask_blur_descriptor->style !=
356+
FilterContents::BlurStyle::kNormal &&
357+
rrect_paint.image_filter) ||
358+
(rrect_paint.mask_blur_descriptor->style ==
359+
FilterContents::BlurStyle::kSolid &&
360+
(!rrect_paint.color.IsOpaque() ||
361+
rrect_paint.blend_mode != BlendMode::kSourceOver))) {
362+
// Defer the alpha, blend mode, and image filter to a separate layer.
363+
SaveLayer({.color = Color::White().WithAlpha(rrect_paint.color.alpha),
364+
.blend_mode = rrect_paint.blend_mode,
365+
.image_filter = rrect_paint.image_filter});
366+
rrect_paint.color = rrect_paint.color.WithAlpha(1);
367+
rrect_paint.blend_mode = BlendMode::kSourceOver;
368+
rrect_paint.image_filter = nullptr;
369+
} else {
370+
Save();
371+
}
336372

337-
new_paint.mask_blur_descriptor = std::nullopt;
373+
auto draw_blurred_rrect = [this, &rect, &corner_radii, &rrect_paint]() {
374+
auto contents = std::make_shared<SolidRRectBlurContents>();
338375

339-
Entity entity;
340-
entity.SetTransform(GetCurrentTransform());
341-
entity.SetClipDepth(GetClipDepth());
342-
entity.SetBlendMode(new_paint.blend_mode);
343-
entity.SetContents(new_paint.WithFilters(std::move(contents)));
376+
contents->SetColor(rrect_paint.color);
377+
contents->SetSigma(rrect_paint.mask_blur_descriptor->sigma);
378+
contents->SetRRect(rect, corner_radii);
344379

345-
AddEntityToCurrentPass(std::move(entity));
380+
Entity blurred_rrect_entity;
381+
blurred_rrect_entity.SetTransform(GetCurrentTransform());
382+
blurred_rrect_entity.SetClipDepth(GetClipDepth());
383+
blurred_rrect_entity.SetBlendMode(rrect_paint.blend_mode);
384+
385+
rrect_paint.mask_blur_descriptor = std::nullopt;
386+
blurred_rrect_entity.SetContents(
387+
rrect_paint.WithFilters(std::move(contents)));
388+
AddEntityToCurrentPass(std::move(blurred_rrect_entity));
389+
};
390+
391+
switch (rrect_paint.mask_blur_descriptor->style) {
392+
case FilterContents::BlurStyle::kNormal: {
393+
draw_blurred_rrect();
394+
break;
395+
}
396+
case FilterContents::BlurStyle::kSolid: {
397+
// First, draw the blurred RRect.
398+
draw_blurred_rrect();
399+
// Then, draw the non-blurred RRect on top.
400+
Entity entity;
401+
entity.SetTransform(GetCurrentTransform());
402+
entity.SetClipDepth(GetClipDepth());
403+
entity.SetBlendMode(rrect_paint.blend_mode);
404+
entity.SetContents(CreateContentsForGeometryWithFilters(
405+
rrect_paint, Geometry::MakeRoundRect(rect, corner_radii)));
406+
AddEntityToCurrentPass(std::move(entity));
407+
break;
408+
}
409+
case FilterContents::BlurStyle::kOuter: {
410+
ClipRRect(rect, corner_radii, Entity::ClipOperation::kDifference);
411+
draw_blurred_rrect();
412+
break;
413+
}
414+
case FilterContents::BlurStyle::kInner: {
415+
ClipRRect(rect, corner_radii, Entity::ClipOperation::kIntersect);
416+
draw_blurred_rrect();
417+
break;
418+
}
419+
}
420+
421+
Restore();
346422

347423
return true;
348424
}

impeller/aiks/canvas.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ class Canvas {
207207
void RestoreClip();
208208

209209
bool AttemptDrawBlurredRRect(const Rect& rect,
210-
Size corner_radius,
210+
Size corner_radii,
211211
const Paint& paint);
212212

213213
Canvas(const Canvas&) = delete;

0 commit comments

Comments
 (0)