@@ -308,41 +308,117 @@ void Canvas::DrawPaint(const Paint& paint) {
308
308
}
309
309
310
310
bool Canvas::AttemptDrawBlurredRRect (const Rect & rect,
311
- Size corner_radius ,
311
+ Size corner_radii ,
312
312
const Paint& paint) {
313
313
if (paint.color_source .GetType () != ColorSource::Type::kColor ||
314
314
paint.style != Paint::Style ::kFill ) {
315
315
return false ;
316
316
}
317
317
318
- if (!paint.mask_blur_descriptor .has_value () ||
319
- paint.mask_blur_descriptor ->style != FilterContents::BlurStyle::kNormal ) {
320
- return false ;
321
- }
322
318
// A blur sigma that is not positive enough should not result in a blur.
323
319
if (paint.mask_blur_descriptor ->sigma .sigma <= kEhCloseEnough ) {
324
320
return false ;
325
321
}
326
322
327
- Paint new_paint = paint;
328
-
329
323
// For symmetrically mask blurred solid RRects, absorb the mask blur and use
330
324
// a faster SDF approximation.
331
325
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
+ }
336
372
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>();
338
375
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);
344
379
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 ();
346
422
347
423
return true ;
348
424
}
0 commit comments