| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stddef.h> |
| |
| #include "base/stl_util.h" |
| #include "build/build_config.h" |
| #include "cc/layers/content_layer_client.h" |
| #include "cc/layers/picture_image_layer.h" |
| #include "cc/layers/picture_layer.h" |
| #include "cc/layers/solid_color_layer.h" |
| #include "cc/paint/paint_flags.h" |
| #include "cc/paint/paint_image.h" |
| #include "cc/paint/paint_image_builder.h" |
| #include "cc/paint/paint_op_buffer.h" |
| #include "cc/test/fake_picture_layer.h" |
| #include "cc/test/layer_tree_pixel_resource_test.h" |
| #include "cc/test/pixel_comparator.h" |
| #include "cc/test/solid_color_content_layer_client.h" |
| #include "components/viz/test/test_layer_tree_frame_sink.h" |
| #include "third_party/skia/include/core/SkImage.h" |
| |
| #if !defined(OS_ANDROID) |
| |
| namespace cc { |
| namespace { |
| |
| using LayerTreeHostMasksPixelTest = ParameterizedPixelResourceTest; |
| |
| INSTANTIATE_PIXEL_RESOURCE_TEST_SUITE_P(LayerTreeHostMasksPixelTest); |
| |
| class MaskContentLayerClient : public ContentLayerClient { |
| public: |
| explicit MaskContentLayerClient(const gfx::Size& bounds) : bounds_(bounds) {} |
| ~MaskContentLayerClient() override = default; |
| |
| bool FillsBoundsCompletely() const override { return false; } |
| size_t GetApproximateUnsharedMemoryUsage() const override { return 0; } |
| |
| gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } |
| |
| scoped_refptr<DisplayItemList> PaintContentsToDisplayList( |
| PaintingControlSetting picture_control) override { |
| auto display_list = base::MakeRefCounted<DisplayItemList>(); |
| display_list->StartPaint(); |
| |
| display_list->push<SaveOp>(); |
| display_list->push<ClipRectOp>(gfx::RectToSkRect(PaintableRegion()), |
| SkClipOp::kIntersect, false); |
| SkColor color = SK_ColorTRANSPARENT; |
| display_list->push<DrawColorOp>(color, SkBlendMode::kSrc); |
| |
| PaintFlags flags; |
| flags.setStyle(PaintFlags::kStroke_Style); |
| flags.setStrokeWidth(SkIntToScalar(2)); |
| flags.setColor(SK_ColorWHITE); |
| |
| gfx::Rect inset_rect(bounds_); |
| while (!inset_rect.IsEmpty()) { |
| inset_rect.Inset(3, 3, 2, 2); |
| display_list->push<DrawRectOp>(gfx::RectToSkRect(inset_rect), flags); |
| inset_rect.Inset(3, 3, 2, 2); |
| } |
| |
| display_list->push<RestoreOp>(); |
| display_list->EndPaintOfUnpaired(PaintableRegion()); |
| display_list->Finalize(); |
| return display_list; |
| } |
| |
| private: |
| gfx::Size bounds_; |
| }; |
| |
| TEST_P(LayerTreeHostMasksPixelTest, MaskOfLayer) { |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); |
| |
| scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder( |
| gfx::Rect(25, 25, 50, 50), kCSSGreen, 1, SK_ColorBLACK); |
| background->AddChild(green); |
| |
| gfx::Size mask_bounds(50, 50); |
| MaskContentLayerClient client(mask_bounds); |
| scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetLayerMaskType(mask_type_); |
| green->SetMaskLayer(mask.get()); |
| |
| RunPixelResourceTest(background, |
| base::FilePath(FILE_PATH_LITERAL("mask_of_layer.png"))); |
| } |
| |
| class LayerTreeHostLayerListPixelTest : public ParameterizedPixelResourceTest { |
| void InitializeSettings(LayerTreeSettings* settings) override { |
| settings->use_layer_lists = true; |
| } |
| }; |
| |
| INSTANTIATE_PIXEL_RESOURCE_TEST_SUITE_P(LayerTreeHostLayerListPixelTest); |
| |
| TEST_P(LayerTreeHostLayerListPixelTest, MaskWithEffect) { |
| PropertyTrees property_trees; |
| scoped_refptr<Layer> root_layer; |
| InitializeForLayerListMode(&root_layer, &property_trees); |
| |
| EffectNode isolation_effect; |
| isolation_effect.clip_id = 1; |
| isolation_effect.stable_id = 2; |
| isolation_effect.has_render_surface = true; |
| isolation_effect.transform_id = 1; |
| property_trees.effect_tree.Insert(isolation_effect, 1); |
| |
| EffectNode mask_effect; |
| mask_effect.clip_id = 1; |
| mask_effect.stable_id = 2; |
| mask_effect.transform_id = 1; |
| mask_effect.blend_mode = SkBlendMode::kDstIn; |
| property_trees.effect_tree.Insert(mask_effect, 2); |
| |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); |
| background->set_property_tree_sequence_number(property_trees.sequence_number); |
| background->SetClipTreeIndex(1); |
| background->SetEffectTreeIndex(1); |
| background->SetScrollTreeIndex(1); |
| background->SetTransformTreeIndex(1); |
| root_layer->AddChild(background); |
| |
| scoped_refptr<SolidColorLayer> green = |
| CreateSolidColorLayer(gfx::Rect(25, 25, 50, 50), kCSSGreen); |
| green->set_property_tree_sequence_number(property_trees.sequence_number); |
| green->SetClipTreeIndex(1); |
| green->SetEffectTreeIndex(2); |
| green->SetScrollTreeIndex(1); |
| green->SetTransformTreeIndex(1); |
| |
| root_layer->AddChild(green); |
| |
| gfx::Size mask_bounds(50, 50); |
| MaskContentLayerClient client(mask_bounds); |
| |
| scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); |
| mask->SetOffsetToTransformParent(gfx::Vector2dF(25, 25)); |
| mask->set_property_tree_sequence_number(property_trees.sequence_number); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetClipTreeIndex(1); |
| mask->SetEffectTreeIndex(3); |
| mask->SetScrollTreeIndex(1); |
| mask->SetTransformTreeIndex(1); |
| root_layer->AddChild(mask); |
| |
| RunPixelResourceTestWithLayerList( |
| root_layer, base::FilePath(FILE_PATH_LITERAL("mask_with_effect.png")), |
| &property_trees); |
| } |
| |
| // Tests a situation in which there is no other content in the target |
| // render surface that the mask applies to. In this situation, the mask |
| // should have no effect on the rendered output. |
| TEST_P(LayerTreeHostLayerListPixelTest, MaskWithEffectNoContentToMask) { |
| PropertyTrees property_trees; |
| scoped_refptr<Layer> root_layer; |
| InitializeForLayerListMode(&root_layer, &property_trees); |
| |
| EffectNode isolation_effect; |
| isolation_effect.clip_id = 1; |
| isolation_effect.stable_id = 2; |
| isolation_effect.has_render_surface = true; |
| isolation_effect.transform_id = 1; |
| property_trees.effect_tree.Insert(isolation_effect, 1); |
| |
| EffectNode mask_effect; |
| mask_effect.clip_id = 1; |
| mask_effect.stable_id = 2; |
| mask_effect.transform_id = 1; |
| mask_effect.blend_mode = SkBlendMode::kDstIn; |
| property_trees.effect_tree.Insert(mask_effect, 2); |
| |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorRED); |
| background->set_property_tree_sequence_number(property_trees.sequence_number); |
| background->SetClipTreeIndex(1); |
| background->SetEffectTreeIndex(1); |
| background->SetScrollTreeIndex(1); |
| background->SetTransformTreeIndex(1); |
| root_layer->AddChild(background); |
| |
| gfx::Size mask_bounds(50, 50); |
| MaskContentLayerClient client(mask_bounds); |
| |
| scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); |
| mask->SetOffsetToTransformParent(gfx::Vector2dF(0, 0)); |
| mask->set_property_tree_sequence_number(property_trees.sequence_number); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetClipTreeIndex(1); |
| mask->SetEffectTreeIndex(3); |
| mask->SetScrollTreeIndex(1); |
| mask->SetTransformTreeIndex(1); |
| root_layer->AddChild(mask); |
| |
| RunPixelResourceTestWithLayerList( |
| root_layer, |
| base::FilePath(FILE_PATH_LITERAL("mask_with_effect_no_content.png")), |
| &property_trees); |
| } |
| |
| TEST_P(LayerTreeHostLayerListPixelTest, ScaledMaskWithEffect) { |
| PropertyTrees property_trees; |
| scoped_refptr<Layer> root_layer; |
| InitializeForLayerListMode(&root_layer, &property_trees); |
| |
| EffectNode isolation_effect; |
| isolation_effect.clip_id = 1; |
| isolation_effect.stable_id = 2; |
| isolation_effect.has_render_surface = true; |
| isolation_effect.transform_id = 1; |
| property_trees.effect_tree.Insert(isolation_effect, 1); |
| |
| EffectNode mask_effect; |
| mask_effect.clip_id = 1; |
| mask_effect.stable_id = 2; |
| mask_effect.transform_id = 2; |
| mask_effect.blend_mode = SkBlendMode::kDstIn; |
| property_trees.effect_tree.Insert(mask_effect, 2); |
| |
| // Scale the mask with a non-integral transform. This will trigger the |
| // AA path in the renderer. |
| TransformNode transform; |
| transform.local = gfx::Transform(); |
| transform.local.Scale(1.5, 1.5); |
| property_trees.transform_tree.Insert(transform, 1); |
| |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); |
| background->set_property_tree_sequence_number(property_trees.sequence_number); |
| background->SetClipTreeIndex(1); |
| background->SetEffectTreeIndex(1); |
| background->SetScrollTreeIndex(1); |
| background->SetTransformTreeIndex(1); |
| root_layer->AddChild(background); |
| |
| scoped_refptr<SolidColorLayer> green = |
| CreateSolidColorLayer(gfx::Rect(25, 25, 50, 50), kCSSGreen); |
| green->set_property_tree_sequence_number(property_trees.sequence_number); |
| green->SetClipTreeIndex(1); |
| green->SetEffectTreeIndex(2); |
| green->SetScrollTreeIndex(1); |
| green->SetTransformTreeIndex(1); |
| |
| root_layer->AddChild(green); |
| |
| gfx::Size mask_bounds(50, 50); |
| MaskContentLayerClient client(mask_bounds); |
| |
| scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); |
| mask->SetOffsetToTransformParent(gfx::Vector2dF(25, 25)); |
| mask->set_property_tree_sequence_number(property_trees.sequence_number); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetClipTreeIndex(1); |
| mask->SetEffectTreeIndex(3); |
| mask->SetScrollTreeIndex(1); |
| mask->SetTransformTreeIndex(2); |
| root_layer->AddChild(mask); |
| |
| float percentage_pixels_large_error = 2.5f; // 2.5%, ~250px / (100*100) |
| float percentage_pixels_small_error = 0.0f; |
| float average_error_allowed_in_bad_pixels = 100.0f; |
| int large_error_allowed = 256; |
| int small_error_allowed = 0; |
| pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( |
| true, // discard_alpha |
| percentage_pixels_large_error, percentage_pixels_small_error, |
| average_error_allowed_in_bad_pixels, large_error_allowed, |
| small_error_allowed); |
| |
| RunPixelResourceTestWithLayerList( |
| root_layer, |
| base::FilePath(FILE_PATH_LITERAL("scaled_mask_with_effect.png")), |
| &property_trees); |
| } |
| |
| TEST_P(LayerTreeHostLayerListPixelTest, MaskWithEffectDifferentSize) { |
| PropertyTrees property_trees; |
| scoped_refptr<Layer> root_layer; |
| InitializeForLayerListMode(&root_layer, &property_trees); |
| |
| EffectNode isolation_effect; |
| isolation_effect.clip_id = 1; |
| isolation_effect.stable_id = 2; |
| isolation_effect.has_render_surface = true; |
| isolation_effect.transform_id = 1; |
| property_trees.effect_tree.Insert(isolation_effect, 1); |
| |
| EffectNode mask_effect; |
| mask_effect.clip_id = 1; |
| mask_effect.stable_id = 2; |
| mask_effect.transform_id = 1; |
| mask_effect.blend_mode = SkBlendMode::kDstIn; |
| property_trees.effect_tree.Insert(mask_effect, 2); |
| |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); |
| background->set_property_tree_sequence_number(property_trees.sequence_number); |
| background->SetClipTreeIndex(1); |
| background->SetEffectTreeIndex(1); |
| background->SetScrollTreeIndex(1); |
| background->SetTransformTreeIndex(1); |
| root_layer->AddChild(background); |
| |
| scoped_refptr<SolidColorLayer> green = |
| CreateSolidColorLayer(gfx::Rect(25, 25, 50, 50), kCSSGreen); |
| green->set_property_tree_sequence_number(property_trees.sequence_number); |
| green->SetClipTreeIndex(1); |
| green->SetEffectTreeIndex(2); |
| green->SetScrollTreeIndex(1); |
| green->SetTransformTreeIndex(1); |
| |
| root_layer->AddChild(green); |
| |
| gfx::Size mask_bounds(25, 25); |
| MaskContentLayerClient client(mask_bounds); |
| |
| scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); |
| mask->SetOffsetToTransformParent(gfx::Vector2dF(25, 25)); |
| mask->set_property_tree_sequence_number(property_trees.sequence_number); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetClipTreeIndex(1); |
| mask->SetEffectTreeIndex(3); |
| mask->SetScrollTreeIndex(1); |
| mask->SetTransformTreeIndex(1); |
| root_layer->AddChild(mask); |
| |
| // The mask is half the size of thing it's masking. In layer-list mode, |
| // the mask is not automatically scaled to match the other layer. |
| RunPixelResourceTestWithLayerList( |
| root_layer, |
| base::FilePath(FILE_PATH_LITERAL("mask_with_effect_different_size.png")), |
| &property_trees); |
| } |
| |
| TEST_P(LayerTreeHostLayerListPixelTest, ImageMaskWithEffect) { |
| PropertyTrees property_trees; |
| scoped_refptr<Layer> root_layer; |
| InitializeForLayerListMode(&root_layer, &property_trees); |
| |
| EffectNode isolation_effect; |
| isolation_effect.clip_id = 1; |
| isolation_effect.stable_id = 2; |
| isolation_effect.has_render_surface = true; |
| isolation_effect.transform_id = 1; |
| property_trees.effect_tree.Insert(isolation_effect, 1); |
| |
| EffectNode mask_effect; |
| mask_effect.clip_id = 1; |
| mask_effect.stable_id = 2; |
| mask_effect.transform_id = 1; |
| mask_effect.blend_mode = SkBlendMode::kDstIn; |
| property_trees.effect_tree.Insert(mask_effect, 2); |
| |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); |
| background->set_property_tree_sequence_number(property_trees.sequence_number); |
| background->SetClipTreeIndex(1); |
| background->SetEffectTreeIndex(1); |
| background->SetScrollTreeIndex(1); |
| background->SetTransformTreeIndex(1); |
| root_layer->AddChild(background); |
| |
| scoped_refptr<SolidColorLayer> green = |
| CreateSolidColorLayer(gfx::Rect(25, 25, 50, 50), kCSSGreen); |
| green->set_property_tree_sequence_number(property_trees.sequence_number); |
| green->SetClipTreeIndex(1); |
| green->SetEffectTreeIndex(2); |
| green->SetScrollTreeIndex(1); |
| green->SetTransformTreeIndex(1); |
| |
| root_layer->AddChild(green); |
| |
| gfx::Size mask_bounds(50, 50); |
| MaskContentLayerClient client(mask_bounds); |
| |
| scoped_refptr<PictureImageLayer> mask = PictureImageLayer::Create(); |
| mask->SetOffsetToTransformParent(gfx::Vector2dF(25, 25)); |
| mask->set_property_tree_sequence_number(property_trees.sequence_number); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetClipTreeIndex(1); |
| mask->SetEffectTreeIndex(3); |
| mask->SetScrollTreeIndex(1); |
| mask->SetTransformTreeIndex(1); |
| |
| sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(200, 200); |
| SkCanvas* canvas = surface->getCanvas(); |
| canvas->scale(SkIntToScalar(4), SkIntToScalar(4)); |
| scoped_refptr<DisplayItemList> mask_display_list = |
| client.PaintContentsToDisplayList( |
| ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); |
| mask_display_list->Raster(canvas); |
| mask->SetImage(PaintImageBuilder::WithDefault() |
| .set_id(PaintImage::GetNextId()) |
| .set_image(surface->makeImageSnapshot(), |
| PaintImage::GetNextContentId()) |
| .TakePaintImage(), |
| SkMatrix::I(), false); |
| root_layer->AddChild(mask); |
| |
| // The mask is half the size of thing it's masking. In layer-list mode, |
| // the mask is not automatically scaled to match the other layer. |
| RunPixelResourceTestWithLayerList( |
| root_layer, |
| base::FilePath(FILE_PATH_LITERAL("image_mask_with_effect.png")), |
| &property_trees); |
| } |
| |
| TEST_P(LayerTreeHostMasksPixelTest, ImageMaskOfLayer) { |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); |
| |
| gfx::Size mask_bounds(50, 50); |
| |
| scoped_refptr<PictureImageLayer> mask = PictureImageLayer::Create(); |
| mask->SetIsDrawable(true); |
| mask->SetLayerMaskType(mask_type_); |
| mask->SetBounds(mask_bounds); |
| |
| sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(200, 200); |
| SkCanvas* canvas = surface->getCanvas(); |
| canvas->scale(SkIntToScalar(4), SkIntToScalar(4)); |
| MaskContentLayerClient client(mask_bounds); |
| scoped_refptr<DisplayItemList> mask_display_list = |
| client.PaintContentsToDisplayList( |
| ContentLayerClient::PAINTING_BEHAVIOR_NORMAL); |
| mask_display_list->Raster(canvas); |
| mask->SetImage(PaintImageBuilder::WithDefault() |
| .set_id(PaintImage::GetNextId()) |
| .set_image(surface->makeImageSnapshot(), |
| PaintImage::GetNextContentId()) |
| .TakePaintImage(), |
| SkMatrix::I(), false); |
| |
| scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder( |
| gfx::Rect(25, 25, 50, 50), kCSSGreen, 1, SK_ColorBLACK); |
| green->SetMaskLayer(mask.get()); |
| background->AddChild(green); |
| |
| RunPixelResourceTest( |
| background, base::FilePath(FILE_PATH_LITERAL("image_mask_of_layer.png"))); |
| } |
| |
| TEST_P(LayerTreeHostMasksPixelTest, MaskOfClippedLayer) { |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); |
| |
| // Clip to the top half of the green layer. |
| scoped_refptr<Layer> clip = Layer::Create(); |
| clip->SetPosition(gfx::PointF()); |
| clip->SetBounds(gfx::Size(100, 50)); |
| clip->SetMasksToBounds(true); |
| background->AddChild(clip); |
| |
| scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder( |
| gfx::Rect(25, 25, 50, 50), kCSSGreen, 1, SK_ColorBLACK); |
| clip->AddChild(green); |
| |
| gfx::Size mask_bounds(50, 50); |
| MaskContentLayerClient client(mask_bounds); |
| scoped_refptr<PictureLayer> mask = PictureLayer::Create(&client); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetLayerMaskType(mask_type_); |
| green->SetMaskLayer(mask.get()); |
| |
| RunPixelResourceTest( |
| background, |
| base::FilePath(FILE_PATH_LITERAL("mask_of_clipped_layer.png"))); |
| } |
| |
| TEST_P(LayerTreeHostMasksPixelTest, MaskOfLayerNonExactTextureSize) { |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(100, 100), SK_ColorWHITE); |
| |
| scoped_refptr<SolidColorLayer> green = CreateSolidColorLayerWithBorder( |
| gfx::Rect(0, 0, 100, 100), kCSSGreen, 1, SK_ColorBLACK); |
| background->AddChild(green); |
| |
| gfx::Size mask_bounds(100, 100); |
| MaskContentLayerClient client(mask_bounds); |
| scoped_refptr<FakePictureLayer> mask = FakePictureLayer::Create(&client); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetLayerMaskType(mask_type_); |
| mask->set_fixed_tile_size(gfx::Size(173, 135)); |
| green->SetMaskLayer(mask.get()); |
| |
| RunPixelResourceTest(background, |
| base::FilePath(FILE_PATH_LITERAL( |
| "mask_with_non_exact_texture_size.png"))); |
| } |
| |
| class CheckerContentLayerClient : public ContentLayerClient { |
| public: |
| CheckerContentLayerClient(const gfx::Size& bounds, |
| SkColor color, |
| bool vertical) |
| : bounds_(bounds), color_(color), vertical_(vertical) {} |
| ~CheckerContentLayerClient() override = default; |
| bool FillsBoundsCompletely() const override { return false; } |
| size_t GetApproximateUnsharedMemoryUsage() const override { return 0; } |
| gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } |
| scoped_refptr<DisplayItemList> PaintContentsToDisplayList( |
| PaintingControlSetting picture_control) override { |
| auto display_list = base::MakeRefCounted<DisplayItemList>(); |
| display_list->StartPaint(); |
| |
| display_list->push<SaveOp>(); |
| display_list->push<ClipRectOp>(gfx::RectToSkRect(PaintableRegion()), |
| SkClipOp::kIntersect, false); |
| SkColor color = SK_ColorTRANSPARENT; |
| display_list->push<DrawColorOp>(color, SkBlendMode::kSrc); |
| |
| PaintFlags flags; |
| flags.setStyle(PaintFlags::kStroke_Style); |
| flags.setStrokeWidth(SkIntToScalar(4)); |
| flags.setColor(color_); |
| if (vertical_) { |
| for (int i = 4; i < bounds_.width(); i += 16) { |
| gfx::PointF p1(i, 0.f); |
| gfx::PointF p2(i, bounds_.height()); |
| display_list->push<DrawLineOp>(p1.x(), p1.y(), p2.x(), p2.y(), flags); |
| } |
| } else { |
| for (int i = 4; i < bounds_.height(); i += 16) { |
| gfx::PointF p1(0.f, i); |
| gfx::PointF p2(bounds_.width(), i); |
| display_list->push<DrawLineOp>(p1.x(), p1.y(), p2.x(), p2.y(), flags); |
| } |
| } |
| |
| display_list->push<RestoreOp>(); |
| display_list->EndPaintOfUnpaired(PaintableRegion()); |
| display_list->Finalize(); |
| return display_list; |
| } |
| |
| private: |
| gfx::Size bounds_; |
| SkColor color_; |
| bool vertical_; |
| }; |
| |
| class CircleContentLayerClient : public ContentLayerClient { |
| public: |
| explicit CircleContentLayerClient(const gfx::Size& bounds) |
| : bounds_(bounds) {} |
| ~CircleContentLayerClient() override = default; |
| bool FillsBoundsCompletely() const override { return false; } |
| size_t GetApproximateUnsharedMemoryUsage() const override { return 0; } |
| gfx::Rect PaintableRegion() override { return gfx::Rect(bounds_); } |
| scoped_refptr<DisplayItemList> PaintContentsToDisplayList( |
| PaintingControlSetting picture_control) override { |
| auto display_list = base::MakeRefCounted<DisplayItemList>(); |
| display_list->StartPaint(); |
| |
| display_list->push<SaveOp>(); |
| display_list->push<ClipRectOp>(gfx::RectToSkRect(PaintableRegion()), |
| SkClipOp::kIntersect, false); |
| SkColor color = SK_ColorTRANSPARENT; |
| display_list->push<DrawColorOp>(color, SkBlendMode::kSrc); |
| |
| PaintFlags flags; |
| flags.setStyle(PaintFlags::kFill_Style); |
| flags.setColor(SK_ColorWHITE); |
| float radius = bounds_.width() / 4.f; |
| float circle_x = bounds_.width() / 2.f; |
| float circle_y = bounds_.height() / 2.f; |
| display_list->push<DrawOvalOp>( |
| SkRect::MakeLTRB(circle_x - radius, circle_y - radius, |
| circle_x + radius, circle_y + radius), |
| flags); |
| display_list->push<RestoreOp>(); |
| display_list->EndPaintOfUnpaired(PaintableRegion()); |
| display_list->Finalize(); |
| return display_list; |
| } |
| |
| private: |
| gfx::Size bounds_; |
| }; |
| |
| using LayerTreeHostMasksForBackdropFiltersPixelTest = |
| ParameterizedPixelResourceTest; |
| |
| INSTANTIATE_TEST_SUITE_P( |
| PixelResourceTest, |
| LayerTreeHostMasksForBackdropFiltersPixelTest, |
| ::testing::Combine( |
| ::testing::Values(SOFTWARE, GPU, ONE_COPY, ZERO_COPY), |
| ::testing::Values(Layer::LayerMaskType::SINGLE_TEXTURE_MASK, |
| Layer::LayerMaskType::MULTI_TEXTURE_MASK))); |
| |
| TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTest, |
| MaskOfLayerWithBackdropFilter) { |
| scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( |
| gfx::Rect(100, 100), SK_ColorWHITE); |
| |
| gfx::Size picture_bounds(100, 100); |
| CheckerContentLayerClient picture_client(picture_bounds, SK_ColorGREEN, true); |
| scoped_refptr<PictureLayer> picture = PictureLayer::Create(&picture_client); |
| picture->SetBounds(picture_bounds); |
| picture->SetIsDrawable(true); |
| |
| scoped_refptr<SolidColorLayer> blur = CreateSolidColorLayer( |
| gfx::Rect(100, 100), SK_ColorTRANSPARENT); |
| background->AddChild(picture); |
| background->AddChild(blur); |
| |
| FilterOperations filters; |
| filters.Append(FilterOperation::CreateGrayscaleFilter(1.0)); |
| gfx::RRectF backdrop_filter_bounds; |
| blur->SetBackdropFilters(filters); |
| blur->SetBackdropFilterBounds(backdrop_filter_bounds); |
| |
| gfx::Size mask_bounds(100, 100); |
| CircleContentLayerClient mask_client(mask_bounds); |
| scoped_refptr<PictureLayer> mask = PictureLayer::Create(&mask_client); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetLayerMaskType(mask_type_); |
| blur->SetMaskLayer(mask.get()); |
| CHECK_EQ(Layer::LayerMaskType::SINGLE_TEXTURE_MASK, mask->mask_type()); |
| |
| float percentage_pixels_large_error = 2.5f; // 2.5%, ~250px / (100*100) |
| float percentage_pixels_small_error = 0.0f; |
| float average_error_allowed_in_bad_pixels = 100.0f; |
| int large_error_allowed = 256; |
| int small_error_allowed = 0; |
| pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( |
| true, // discard_alpha |
| percentage_pixels_large_error, |
| percentage_pixels_small_error, |
| average_error_allowed_in_bad_pixels, |
| large_error_allowed, |
| small_error_allowed); |
| |
| base::FilePath image_name = |
| (test_case_ == GPU) |
| ? base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter_gpu.png")) |
| : base::FilePath(FILE_PATH_LITERAL("mask_of_backdrop_filter.png")); |
| RunPixelResourceTest(background, image_name); |
| } |
| |
| TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTest, MaskOfLayerWithBlend) { |
| scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( |
| gfx::Rect(128, 128), SK_ColorWHITE); |
| |
| gfx::Size picture_bounds(128, 128); |
| CheckerContentLayerClient picture_client_vertical( |
| picture_bounds, SK_ColorGREEN, true); |
| scoped_refptr<PictureLayer> picture_vertical = |
| PictureLayer::Create(&picture_client_vertical); |
| picture_vertical->SetBounds(picture_bounds); |
| picture_vertical->SetIsDrawable(true); |
| |
| CheckerContentLayerClient picture_client_horizontal( |
| picture_bounds, SK_ColorMAGENTA, false); |
| scoped_refptr<PictureLayer> picture_horizontal = |
| PictureLayer::Create(&picture_client_horizontal); |
| picture_horizontal->SetBounds(picture_bounds); |
| picture_horizontal->SetIsDrawable(true); |
| picture_horizontal->SetContentsOpaque(false); |
| picture_horizontal->SetBlendMode(SkBlendMode::kMultiply); |
| |
| background->AddChild(picture_vertical); |
| background->AddChild(picture_horizontal); |
| |
| gfx::Size mask_bounds(128, 128); |
| CircleContentLayerClient mask_client(mask_bounds); |
| scoped_refptr<PictureLayer> mask = PictureLayer::Create(&mask_client); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetLayerMaskType(mask_type_); |
| picture_horizontal->SetMaskLayer(mask.get()); |
| |
| float percentage_pixels_large_error = 0.04f; // 0.04%, ~6px / (128*128) |
| float percentage_pixels_small_error = 0.0f; |
| float average_error_allowed_in_bad_pixels = 256.0f; |
| int large_error_allowed = 256; |
| int small_error_allowed = 0; |
| pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( |
| true, // discard_alpha |
| percentage_pixels_large_error, |
| percentage_pixels_small_error, |
| average_error_allowed_in_bad_pixels, |
| large_error_allowed, |
| small_error_allowed); |
| |
| RunPixelResourceTest(background, |
| base::FilePath( |
| FILE_PATH_LITERAL("mask_of_layer_with_blend.png"))); |
| } |
| |
| class StaticPictureLayer : private ContentLayerClient, public PictureLayer { |
| public: |
| static scoped_refptr<StaticPictureLayer> Create( |
| scoped_refptr<DisplayItemList> display_list) { |
| return base::WrapRefCounted( |
| new StaticPictureLayer(std::move(display_list))); |
| } |
| |
| gfx::Rect PaintableRegion() override { return gfx::Rect(bounds()); } |
| scoped_refptr<DisplayItemList> PaintContentsToDisplayList( |
| PaintingControlSetting) override { |
| return display_list_; |
| } |
| bool FillsBoundsCompletely() const override { return false; } |
| size_t GetApproximateUnsharedMemoryUsage() const override { return 0; } |
| |
| protected: |
| explicit StaticPictureLayer(scoped_refptr<DisplayItemList> display_list) |
| : PictureLayer(this), display_list_(std::move(display_list)) {} |
| ~StaticPictureLayer() override = default; |
| |
| private: |
| scoped_refptr<DisplayItemList> display_list_; |
| }; |
| |
| class LayerTreeHostMaskAsBlendingPixelTest |
| : public LayerTreeHostPixelResourceTest, |
| public ::testing::WithParamInterface<int> { |
| public: |
| LayerTreeHostMaskAsBlendingPixelTest() |
| : LayerTreeHostPixelResourceTest( |
| GetParam() ? ZERO_COPY : SOFTWARE, |
| Layer::LayerMaskType::SINGLE_TEXTURE_MASK), |
| use_antialiasing_(GetParam() == 2 || GetParam() == 4), |
| force_shaders_(GetParam() == 3 || GetParam() == 4) { |
| float percentage_pixels_small_error = 0.f; |
| float percentage_pixels_error = 0.f; |
| float average_error_allowed_in_bad_pixels = 0.f; |
| int large_error_allowed = 0; |
| int small_error_allowed = 0; |
| if (use_antialiasing_) { |
| percentage_pixels_small_error = 0.9f; |
| percentage_pixels_error = 6.7f; |
| average_error_allowed_in_bad_pixels = 3.5f; |
| large_error_allowed = 15; |
| small_error_allowed = 1; |
| } else if (test_type_ != PIXEL_TEST_SOFTWARE) { |
| percentage_pixels_small_error = 0.9f; |
| percentage_pixels_error = 6.5f; |
| average_error_allowed_in_bad_pixels = 3.5f; |
| large_error_allowed = 15; |
| small_error_allowed = 1; |
| } else { |
| #if defined(ARCH_CPU_ARM64) |
| // Differences in floating point calculation on ARM means a small |
| // percentage of pixels will be off by 1. |
| percentage_pixels_error = 0.112f; |
| average_error_allowed_in_bad_pixels = 1.f; |
| large_error_allowed = 1; |
| #endif |
| } |
| |
| pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( |
| false, // discard_alpha |
| percentage_pixels_error, percentage_pixels_small_error, |
| average_error_allowed_in_bad_pixels, large_error_allowed, |
| small_error_allowed); |
| } |
| |
| static scoped_refptr<Layer> CreateCheckerboardLayer(const gfx::Size& bounds) { |
| constexpr int kGridSize = 8; |
| static const SkColor color_even = SkColorSetRGB(153, 153, 153); |
| static const SkColor color_odd = SkColorSetRGB(102, 102, 102); |
| |
| auto display_list = base::MakeRefCounted<DisplayItemList>(); |
| display_list->StartPaint(); |
| display_list->push<DrawColorOp>(color_even, SkBlendMode::kSrc); |
| PaintFlags flags; |
| flags.setColor(color_odd); |
| for (int j = 0; j < (bounds.height() + kGridSize - 1) / kGridSize; j++) { |
| for (int i = 0; i < (bounds.width() + kGridSize - 1) / kGridSize; i++) { |
| bool is_odd_grid = (i ^ j) & 1; |
| if (!is_odd_grid) |
| continue; |
| display_list->push<DrawRectOp>( |
| SkRect::MakeXYWH(i * kGridSize, j * kGridSize, kGridSize, |
| kGridSize), |
| flags); |
| } |
| } |
| display_list->EndPaintOfUnpaired(gfx::Rect(bounds)); |
| display_list->Finalize(); |
| |
| scoped_refptr<Layer> layer = |
| StaticPictureLayer::Create(std::move(display_list)); |
| layer->SetIsDrawable(true); |
| layer->SetBounds(bounds); |
| return layer; |
| } |
| |
| static scoped_refptr<Layer> CreateTestPatternLayer(const gfx::Size& bounds, |
| int grid_size) { |
| // Creates a layer consists of solid grids. The grids are in a mix of |
| // different transparency and colors (1 transparent, 3 semi-transparent, |
| // and 3 opaque). |
| static SkColor test_colors[7] = { |
| SkColorSetARGB(128, 255, 0, 0), SkColorSetARGB(255, 0, 0, 255), |
| SkColorSetARGB(128, 0, 255, 0), SkColorSetARGB(128, 0, 0, 255), |
| SkColorSetARGB(255, 0, 255, 0), SkColorSetARGB(0, 0, 0, 0), |
| SkColorSetARGB(255, 255, 0, 0)}; |
| |
| auto display_list = base::MakeRefCounted<DisplayItemList>(); |
| display_list->StartPaint(); |
| for (int j = 0; j < (bounds.height() + grid_size - 1) / grid_size; j++) { |
| for (int i = 0; i < (bounds.width() + grid_size - 1) / grid_size; i++) { |
| PaintFlags flags; |
| flags.setColor(test_colors[(i + j * 3) % base::size(test_colors)]); |
| display_list->push<DrawRectOp>( |
| SkRect::MakeXYWH(i * grid_size, j * grid_size, grid_size, |
| grid_size), |
| flags); |
| } |
| } |
| display_list->EndPaintOfUnpaired(gfx::Rect(bounds)); |
| display_list->Finalize(); |
| |
| scoped_refptr<Layer> layer = |
| StaticPictureLayer::Create(std::move(display_list)); |
| layer->SetIsDrawable(true); |
| layer->SetBounds(bounds); |
| return layer; |
| } |
| |
| protected: |
| std::unique_ptr<viz::TestLayerTreeFrameSink> CreateLayerTreeFrameSink( |
| const viz::RendererSettings& renderer_settings, |
| double refresh_rate, |
| scoped_refptr<viz::ContextProvider> compositor_context_provider, |
| scoped_refptr<viz::RasterContextProvider> worker_context_provider) |
| override { |
| viz::RendererSettings modified_renderer_settings = renderer_settings; |
| modified_renderer_settings.force_antialiasing = use_antialiasing_; |
| modified_renderer_settings.force_blending_with_shaders = force_shaders_; |
| return LayerTreeHostPixelResourceTest::CreateLayerTreeFrameSink( |
| modified_renderer_settings, refresh_rate, |
| std::move(compositor_context_provider), |
| std::move(worker_context_provider)); |
| } |
| |
| bool use_antialiasing_; |
| bool force_shaders_; |
| }; |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| LayerTreeHostMaskAsBlendingPixelTest, |
| ::testing::Range(0, 5)); |
| // Instantiate 5 test modes of the following: |
| // 0: SOFTWARE (golden sample) |
| // 1: GL |
| // 2: GL + AA |
| // 3: GL + Forced Shaders |
| // 4: GL + Forced Shaders + AA |
| |
| TEST_P(LayerTreeHostMaskAsBlendingPixelTest, PixelAlignedNoop) { |
| // This test verifies the degenerate case of a no-op mask doesn't affect |
| // the contents in any way. |
| scoped_refptr<Layer> root = CreateCheckerboardLayer(gfx::Size(400, 300)); |
| |
| scoped_refptr<Layer> mask_isolation = Layer::Create(); |
| mask_isolation->SetPosition(gfx::PointF(20, 20)); |
| mask_isolation->SetBounds(gfx::Size(350, 250)); |
| mask_isolation->SetMasksToBounds(true); |
| mask_isolation->SetIsRootForIsolatedGroup(true); |
| root->AddChild(mask_isolation); |
| |
| scoped_refptr<Layer> content = |
| CreateTestPatternLayer(gfx::Size(400, 300), 25); |
| content->SetPosition(gfx::PointF(-40, -40)); |
| mask_isolation->AddChild(content); |
| |
| scoped_refptr<Layer> mask_layer = |
| CreateSolidColorLayer(gfx::Rect(350, 250), kCSSBlack); |
| mask_layer->SetBlendMode(SkBlendMode::kDstIn); |
| mask_isolation->AddChild(mask_layer); |
| |
| RunPixelResourceTest( |
| root, base::FilePath(FILE_PATH_LITERAL("mask_as_blending_noop.png"))); |
| } |
| |
| TEST_P(LayerTreeHostMaskAsBlendingPixelTest, PixelAlignedClippedCircle) { |
| // This test verifies a simple pixel aligned mask applies correctly. |
| scoped_refptr<Layer> root = CreateCheckerboardLayer(gfx::Size(400, 300)); |
| |
| scoped_refptr<Layer> mask_isolation = Layer::Create(); |
| mask_isolation->SetPosition(gfx::PointF(20, 20)); |
| mask_isolation->SetBounds(gfx::Size(350, 250)); |
| mask_isolation->SetMasksToBounds(true); |
| mask_isolation->SetIsRootForIsolatedGroup(true); |
| root->AddChild(mask_isolation); |
| |
| scoped_refptr<Layer> content = |
| CreateTestPatternLayer(gfx::Size(400, 300), 25); |
| content->SetPosition(gfx::PointF(-40, -40)); |
| mask_isolation->AddChild(content); |
| |
| auto display_list = base::MakeRefCounted<DisplayItemList>(); |
| display_list->StartPaint(); |
| PaintFlags flags; |
| flags.setColor(kCSSBlack); |
| flags.setAntiAlias(true); |
| display_list->push<DrawOvalOp>(SkRect::MakeXYWH(-5, -55, 360, 360), flags); |
| display_list->EndPaintOfUnpaired(gfx::Rect(-5, -55, 360, 360)); |
| display_list->Finalize(); |
| scoped_refptr<Layer> mask_layer = |
| StaticPictureLayer::Create(std::move(display_list)); |
| mask_layer->SetIsDrawable(true); |
| mask_layer->SetBounds(gfx::Size(350, 250)); |
| mask_layer->SetBlendMode(SkBlendMode::kDstIn); |
| mask_isolation->AddChild(mask_layer); |
| |
| RunPixelResourceTest( |
| root, base::FilePath(FILE_PATH_LITERAL("mask_as_blending_circle.png"))); |
| } |
| |
| TEST_P(LayerTreeHostMaskAsBlendingPixelTest, |
| PixelAlignedClippedCircleUnderflow) { |
| // This test verifies a simple pixel aligned mask applies correctly when |
| // the content is smaller than the mask. |
| scoped_refptr<Layer> root = CreateCheckerboardLayer(gfx::Size(400, 300)); |
| |
| scoped_refptr<Layer> mask_isolation = Layer::Create(); |
| mask_isolation->SetPosition(gfx::PointF(20, 20)); |
| mask_isolation->SetBounds(gfx::Size(350, 250)); |
| mask_isolation->SetMasksToBounds(true); |
| mask_isolation->SetIsRootForIsolatedGroup(true); |
| root->AddChild(mask_isolation); |
| |
| scoped_refptr<Layer> content = |
| CreateTestPatternLayer(gfx::Size(330, 230), 25); |
| content->SetPosition(gfx::PointF(10, 10)); |
| mask_isolation->AddChild(content); |
| |
| auto display_list = base::MakeRefCounted<DisplayItemList>(); |
| display_list->StartPaint(); |
| PaintFlags flags; |
| flags.setColor(kCSSBlack); |
| flags.setAntiAlias(true); |
| display_list->push<DrawOvalOp>(SkRect::MakeXYWH(-5, -55, 360, 360), flags); |
| display_list->EndPaintOfUnpaired(gfx::Rect(-5, -55, 360, 360)); |
| display_list->Finalize(); |
| scoped_refptr<Layer> mask_layer = |
| StaticPictureLayer::Create(std::move(display_list)); |
| mask_layer->SetIsDrawable(true); |
| mask_layer->SetBounds(gfx::Size(350, 250)); |
| mask_layer->SetBlendMode(SkBlendMode::kDstIn); |
| mask_isolation->AddChild(mask_layer); |
| |
| RunPixelResourceTest(root, base::FilePath(FILE_PATH_LITERAL( |
| "mask_as_blending_circle_underflow.png"))); |
| } |
| |
| TEST_P(LayerTreeHostMaskAsBlendingPixelTest, RotatedClippedCircle) { |
| // This test verifies a simple pixel aligned mask that is not pixel aligned |
| // to its target surface is rendered correctly. |
| scoped_refptr<Layer> root = CreateCheckerboardLayer(gfx::Size(400, 300)); |
| |
| scoped_refptr<Layer> mask_isolation = Layer::Create(); |
| mask_isolation->SetPosition(gfx::PointF(20, 20)); |
| { |
| gfx::Transform rotate; |
| rotate.Rotate(5.f); |
| mask_isolation->SetTransform(rotate); |
| } |
| mask_isolation->SetBounds(gfx::Size(350, 250)); |
| mask_isolation->SetMasksToBounds(true); |
| mask_isolation->SetIsRootForIsolatedGroup(true); |
| root->AddChild(mask_isolation); |
| |
| scoped_refptr<Layer> content = |
| CreateTestPatternLayer(gfx::Size(400, 300), 25); |
| content->SetPosition(gfx::PointF(-40, -40)); |
| mask_isolation->AddChild(content); |
| |
| auto display_list = base::MakeRefCounted<DisplayItemList>(); |
| display_list->StartPaint(); |
| PaintFlags flags; |
| flags.setColor(kCSSBlack); |
| flags.setAntiAlias(true); |
| display_list->push<DrawOvalOp>(SkRect::MakeXYWH(-5, -55, 360, 360), flags); |
| display_list->EndPaintOfUnpaired(gfx::Rect(-5, -55, 360, 360)); |
| display_list->Finalize(); |
| scoped_refptr<Layer> mask_layer = |
| StaticPictureLayer::Create(std::move(display_list)); |
| mask_layer->SetIsDrawable(true); |
| mask_layer->SetBounds(gfx::Size(350, 250)); |
| mask_layer->SetBlendMode(SkBlendMode::kDstIn); |
| mask_isolation->AddChild(mask_layer); |
| |
| base::FilePath image_name = |
| (test_type_ == PIXEL_TEST_SOFTWARE) |
| ? base::FilePath( |
| FILE_PATH_LITERAL("mask_as_blending_rotated_circle.png")) |
| : base::FilePath( |
| FILE_PATH_LITERAL("mask_as_blending_rotated_circle_gl.png")); |
| RunPixelResourceTest(root, image_name); |
| } |
| |
| TEST_P(LayerTreeHostMaskAsBlendingPixelTest, RotatedClippedCircleUnderflow) { |
| // This test verifies a simple pixel aligned mask that is not pixel aligned |
| // to its target surface, and has the content smaller than the mask, is |
| // rendered correctly. |
| scoped_refptr<Layer> root = CreateCheckerboardLayer(gfx::Size(400, 300)); |
| |
| scoped_refptr<Layer> mask_isolation = Layer::Create(); |
| mask_isolation->SetPosition(gfx::PointF(20, 20)); |
| { |
| gfx::Transform rotate; |
| rotate.Rotate(5.f); |
| mask_isolation->SetTransform(rotate); |
| } |
| mask_isolation->SetBounds(gfx::Size(350, 250)); |
| mask_isolation->SetMasksToBounds(true); |
| mask_isolation->SetIsRootForIsolatedGroup(true); |
| root->AddChild(mask_isolation); |
| |
| scoped_refptr<Layer> content = |
| CreateTestPatternLayer(gfx::Size(330, 230), 25); |
| content->SetPosition(gfx::PointF(10, 10)); |
| mask_isolation->AddChild(content); |
| |
| auto display_list = base::MakeRefCounted<DisplayItemList>(); |
| display_list->StartPaint(); |
| PaintFlags flags; |
| flags.setColor(kCSSBlack); |
| flags.setAntiAlias(true); |
| display_list->push<DrawOvalOp>(SkRect::MakeXYWH(-5, -55, 360, 360), flags); |
| display_list->EndPaintOfUnpaired(gfx::Rect(-5, -55, 360, 360)); |
| display_list->Finalize(); |
| scoped_refptr<Layer> mask_layer = |
| StaticPictureLayer::Create(std::move(display_list)); |
| mask_layer->SetIsDrawable(true); |
| mask_layer->SetBounds(gfx::Size(350, 250)); |
| mask_layer->SetBlendMode(SkBlendMode::kDstIn); |
| mask_isolation->AddChild(mask_layer); |
| |
| base::FilePath image_name = |
| (test_type_ == PIXEL_TEST_SOFTWARE) |
| ? base::FilePath(FILE_PATH_LITERAL( |
| "mask_as_blending_rotated_circle_underflow.png")) |
| : base::FilePath(FILE_PATH_LITERAL( |
| "mask_as_blending_rotated_circle_underflow_gl.png")); |
| RunPixelResourceTest(root, image_name); |
| } |
| |
| TEST_P(LayerTreeHostMasksForBackdropFiltersPixelTest, |
| MaskOfLayerWithBackdropFilterAndBlend) { |
| scoped_refptr<SolidColorLayer> background = |
| CreateSolidColorLayer(gfx::Rect(128, 128), SK_ColorWHITE); |
| |
| gfx::Size picture_bounds(128, 128); |
| CheckerContentLayerClient picture_client_vertical(picture_bounds, |
| SK_ColorGREEN, true); |
| scoped_refptr<PictureLayer> picture_vertical = |
| PictureLayer::Create(&picture_client_vertical); |
| picture_vertical->SetBounds(picture_bounds); |
| picture_vertical->SetIsDrawable(true); |
| |
| CheckerContentLayerClient picture_client_horizontal(picture_bounds, |
| SK_ColorMAGENTA, false); |
| scoped_refptr<PictureLayer> picture_horizontal = |
| PictureLayer::Create(&picture_client_horizontal); |
| picture_horizontal->SetBounds(picture_bounds); |
| picture_horizontal->SetIsDrawable(true); |
| picture_horizontal->SetContentsOpaque(false); |
| picture_horizontal->SetBlendMode(SkBlendMode::kMultiply); |
| |
| FilterOperations filters; |
| filters.Append(FilterOperation::CreateGrayscaleFilter(1.0)); |
| picture_horizontal->SetBackdropFilters(filters); |
| gfx::RRectF backdrop_filter_bounds; |
| picture_horizontal->SetBackdropFilterBounds(backdrop_filter_bounds); |
| |
| background->AddChild(picture_vertical); |
| background->AddChild(picture_horizontal); |
| |
| gfx::Size mask_bounds(128, 128); |
| CircleContentLayerClient mask_client(mask_bounds); |
| scoped_refptr<PictureLayer> mask = PictureLayer::Create(&mask_client); |
| mask->SetBounds(mask_bounds); |
| mask->SetIsDrawable(true); |
| mask->SetLayerMaskType(mask_type_); |
| picture_horizontal->SetMaskLayer(mask.get()); |
| |
| float percentage_pixels_large_error = 0.062f; // 0.062%, ~10px / (128*128) |
| float percentage_pixels_small_error = 0.0f; |
| float average_error_allowed_in_bad_pixels = 200.0f; |
| int large_error_allowed = 256; |
| int small_error_allowed = 0; |
| pixel_comparator_ = std::make_unique<FuzzyPixelComparator>( |
| true, // discard_alpha |
| percentage_pixels_large_error, percentage_pixels_small_error, |
| average_error_allowed_in_bad_pixels, large_error_allowed, |
| small_error_allowed); |
| |
| RunPixelResourceTest(background, |
| base::FilePath(FILE_PATH_LITERAL( |
| "mask_of_backdrop_filter_and_blend.png"))); |
| } |
| |
| } // namespace |
| } // namespace cc |
| |
| #endif // !defined(OS_ANDROID) |