@@ -2040,6 +2040,7 @@ class CanvasCompareTester {
2040
2040
const uint32_t * test_row = test_pixels->addr32 (0 , y);
2041
2041
for (int x = 0 ; x < test_pixels->width (); x++) {
2042
2042
if (ref_row[x] != test_row[x]) {
2043
+ // FML_LOG(ERROR) << std::hex << ref_row[x] << " != " << test_row[x];
2043
2044
pixels_different++;
2044
2045
}
2045
2046
}
@@ -3238,5 +3239,175 @@ TEST_F(DisplayListCanvas, DrawShadowDpr) {
3238
3239
CanvasCompareTester::DefaultTolerance.addBoundsPadding (3 , 3 ));
3239
3240
}
3240
3241
3242
+ TEST_F (DisplayListCanvas, SaveLayerConsolidation) {
3243
+ float commutable_color_matrix[]{
3244
+ // clang-format off
3245
+ 0 , 1 , 0 , 0 , 0 ,
3246
+ 0 , 0 , 1 , 0 , 0 ,
3247
+ 1 , 0 , 0 , 0 , 0 ,
3248
+ 0 , 0 , 0 , 1 , 0 ,
3249
+ // clang-format on
3250
+ };
3251
+ float non_commutable_color_matrix[]{
3252
+ // clang-format off
3253
+ 0 , 1 , 0 , .1 , 0 ,
3254
+ 0 , 0 , 1 , .1 , 0 ,
3255
+ 1 , 0 , 0 , .1 , 0 ,
3256
+ 0 , 0 , 0 , .7 , 0 ,
3257
+ // clang-format on
3258
+ };
3259
+ SkMatrix contract_matrix;
3260
+ contract_matrix.setScale (0 .9f , 0 .9f , kRenderCenterX , kRenderCenterY );
3261
+
3262
+ std::vector<SkScalar> opacities = {
3263
+ 0 ,
3264
+ 0 .5f ,
3265
+ SK_Scalar1,
3266
+ };
3267
+ std::vector<std::shared_ptr<DlColorFilter>> color_filters = {
3268
+ std::make_shared<DlBlendColorFilter>(DlColor::kCyan (),
3269
+ DlBlendMode::kSrcATop ),
3270
+ std::make_shared<DlMatrixColorFilter>(commutable_color_matrix),
3271
+ std::make_shared<DlMatrixColorFilter>(non_commutable_color_matrix),
3272
+ DlSrgbToLinearGammaColorFilter::instance,
3273
+ DlLinearToSrgbGammaColorFilter::instance,
3274
+ };
3275
+ std::vector<std::shared_ptr<DlImageFilter>> image_filters = {
3276
+ std::make_shared<DlBlurImageFilter>(5 .0f , 5 .0f , DlTileMode::kDecal ),
3277
+ std::make_shared<DlDilateImageFilter>(5 .0f , 5 .0f ),
3278
+ std::make_shared<DlErodeImageFilter>(5 .0f , 5 .0f ),
3279
+ std::make_shared<DlMatrixImageFilter>(contract_matrix,
3280
+ DlImageSampling::kLinear ),
3281
+ };
3282
+ RenderEnvironment env = RenderEnvironment::MakeN32 ();
3283
+
3284
+ auto render_content = [](DisplayListBuilder& builder) {
3285
+ builder.drawRect (
3286
+ SkRect{kRenderLeft , kRenderTop , kRenderCenterX , kRenderCenterY },
3287
+ DlPaint ().setColor (DlColor::kYellow ()));
3288
+ builder.drawRect (
3289
+ SkRect{kRenderCenterX , kRenderTop , kRenderRight , kRenderCenterY },
3290
+ DlPaint ().setColor (DlColor::kRed ()));
3291
+ builder.drawRect (
3292
+ SkRect{kRenderLeft , kRenderCenterY , kRenderCenterX , kRenderBottom },
3293
+ DlPaint ().setColor (DlColor::kBlue ()));
3294
+ builder.drawRect (
3295
+ SkRect{kRenderCenterX , kRenderCenterY , kRenderRight , kRenderBottom },
3296
+ DlPaint ().setColor (DlColor::kRed ().modulateOpacity (0 .5f )));
3297
+ };
3298
+
3299
+ // clang-format off
3300
+ auto test_attributes = [&env, render_content]
3301
+ (DlPaint& paint1, DlPaint& paint2, DlPaint paint_both,
3302
+ bool same, bool rev_same, std::string desc1, std::string desc2) {
3303
+ // clang-format on
3304
+ DisplayListBuilder nested_builder;
3305
+ nested_builder.saveLayer (&kTestBounds , &paint1);
3306
+ nested_builder.saveLayer (&kTestBounds , &paint2);
3307
+ render_content (nested_builder);
3308
+ auto nested_surface = env.MakeSurface ();
3309
+ nested_builder.Build ()->RenderTo (nested_surface->canvas ());
3310
+
3311
+ DisplayListBuilder reverse_builder;
3312
+ reverse_builder.saveLayer (&kTestBounds , &paint2);
3313
+ reverse_builder.saveLayer (&kTestBounds , &paint1);
3314
+ render_content (reverse_builder);
3315
+ auto reverse_surface = env.MakeSurface ();
3316
+ reverse_builder.Build ()->RenderTo (reverse_surface->canvas ());
3317
+
3318
+ DisplayListBuilder combined_builder;
3319
+ combined_builder.saveLayer (&kTestBounds , &paint_both);
3320
+ render_content (combined_builder);
3321
+ auto combined_surface = env.MakeSurface ();
3322
+ combined_builder.Build ()->RenderTo (combined_surface->canvas ());
3323
+
3324
+ // Set this boolean to true to test if combinations that are marked
3325
+ // as incompatible actually are compatible despite our predictions.
3326
+ // Some of the combinations that we treat as incompatible actually
3327
+ // are compatible with swapping the order of the operations, but
3328
+ // it would take a bit of new infrastructure to really identify
3329
+ // those combinations. The only hard constraint to test here is
3330
+ // when we claim that they are compatible and they aren't.
3331
+ const bool always = false ;
3332
+
3333
+ if (always || same) {
3334
+ CanvasCompareTester::quickCompareToReference (
3335
+ nested_surface->pixmap (), combined_surface->pixmap (), same,
3336
+ " nested " + desc1 + " then " + desc2);
3337
+ }
3338
+ if (always || rev_same) {
3339
+ CanvasCompareTester::quickCompareToReference (
3340
+ reverse_surface->pixmap (), combined_surface->pixmap (), rev_same,
3341
+ " nested " + desc2 + " then " + desc1);
3342
+ }
3343
+ };
3344
+
3345
+ // CF then Opacity should always work.
3346
+ // The reverse sometimes works.
3347
+ for (size_t cfi = 0 ; cfi < color_filters.size (); cfi++) {
3348
+ auto color_filter = color_filters[cfi];
3349
+ std::string cf_desc = " color filter #" + std::to_string (cfi + 1 );
3350
+ DlPaint nested_paint1 = DlPaint ().setColorFilter (color_filter);
3351
+
3352
+ for (size_t oi = 0 ; oi < opacities.size (); oi++) {
3353
+ SkScalar opacity = opacities[oi];
3354
+ std::string op_desc = " opacity " + std::to_string (opacity);
3355
+ DlPaint nested_paint2 = DlPaint ().setOpacity (opacity);
3356
+
3357
+ DlPaint combined_paint = nested_paint1;
3358
+ combined_paint.setOpacity (opacity);
3359
+
3360
+ bool op_then_cf_works = opacity <= 0.0 || opacity >= 1.0 ||
3361
+ color_filter->can_commute_with_opacity ();
3362
+
3363
+ test_attributes (nested_paint1, nested_paint2, combined_paint, true ,
3364
+ op_then_cf_works, cf_desc, op_desc);
3365
+ }
3366
+ }
3367
+
3368
+ // Opacity then IF should always work.
3369
+ // The reverse can also work for some values of opacity.
3370
+ // The reverse should also theoretically work for some IFs, but we
3371
+ // get some rounding errors that are more than just trivial.
3372
+ for (size_t oi = 0 ; oi < opacities.size (); oi++) {
3373
+ SkScalar opacity = opacities[oi];
3374
+ std::string op_desc = " opacity " + std::to_string (opacity);
3375
+ DlPaint nested_paint1 = DlPaint ().setOpacity (opacity);
3376
+
3377
+ for (size_t ifi = 0 ; ifi < image_filters.size (); ifi++) {
3378
+ auto image_filter = image_filters[ifi];
3379
+ std::string if_desc = " image filter #" + std::to_string (ifi + 1 );
3380
+ DlPaint nested_paint2 = DlPaint ().setImageFilter (image_filter);
3381
+
3382
+ DlPaint combined_paint = nested_paint1;
3383
+ combined_paint.setImageFilter (image_filter);
3384
+
3385
+ bool if_then_op_works = opacity <= 0.0 || opacity >= 1.0 ;
3386
+ test_attributes (nested_paint1, nested_paint2, combined_paint, true ,
3387
+ if_then_op_works, op_desc, if_desc);
3388
+ }
3389
+ }
3390
+
3391
+ // CF then IF should always work.
3392
+ // The reverse might work, but we lack the infrastructure to check it.
3393
+ for (size_t cfi = 0 ; cfi < color_filters.size (); cfi++) {
3394
+ auto color_filter = color_filters[cfi];
3395
+ std::string cf_desc = " color filter #" + std::to_string (cfi + 1 );
3396
+ DlPaint nested_paint1 = DlPaint ().setColorFilter (color_filter);
3397
+
3398
+ for (size_t ifi = 0 ; ifi < image_filters.size (); ifi++) {
3399
+ auto image_filter = image_filters[ifi];
3400
+ std::string if_desc = " image filter #" + std::to_string (ifi + 1 );
3401
+ DlPaint nested_paint2 = DlPaint ().setImageFilter (image_filter);
3402
+
3403
+ DlPaint combined_paint = nested_paint1;
3404
+ combined_paint.setImageFilter (image_filter);
3405
+
3406
+ test_attributes (nested_paint1, nested_paint2, combined_paint, true , false ,
3407
+ cf_desc, if_desc);
3408
+ }
3409
+ }
3410
+ }
3411
+
3241
3412
} // namespace testing
3242
3413
} // namespace flutter
0 commit comments