// 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 #include "cc/layers/picture_image_layer.h" #include "cc/layers/solid_color_layer.h" #include "cc/test/layer_tree_pixel_resource_test.h" #include "cc/test/pixel_comparator.h" #include "third_party/skia/include/core/SkImage.h" #include "third_party/skia/include/core/SkSurface.h" #if !defined(OS_ANDROID) namespace cc { namespace { SkXfermode::Mode const kBlendModes[] = { SkXfermode::kSrcOver_Mode, SkXfermode::kScreen_Mode, SkXfermode::kOverlay_Mode, SkXfermode::kDarken_Mode, SkXfermode::kLighten_Mode, SkXfermode::kColorDodge_Mode, SkXfermode::kColorBurn_Mode, SkXfermode::kHardLight_Mode, SkXfermode::kSoftLight_Mode, SkXfermode::kDifference_Mode, SkXfermode::kExclusion_Mode, SkXfermode::kMultiply_Mode, SkXfermode::kHue_Mode, SkXfermode::kSaturation_Mode, SkXfermode::kColor_Mode, SkXfermode::kLuminosity_Mode}; SkColor kCSSTestColors[] = { 0xffff0000, // red 0xff00ff00, // lime 0xff0000ff, // blue 0xff00ffff, // aqua 0xffff00ff, // fuchsia 0xffffff00, // yellow 0xff008000, // green 0xff800000, // maroon 0xff000080, // navy 0xff800080, // purple 0xff808000, // olive 0xff008080, // teal 0xfffa8072, // salmon 0xffc0c0c0, // silver 0xff000000, // black 0xff808080, // gray 0x80000000, // black with transparency 0xffffffff, // white 0x80ffffff, // white with transparency 0x00000000 // transparent }; const int kBlendModesCount = arraysize(kBlendModes); const int kCSSTestColorsCount = arraysize(kCSSTestColors); using RenderPassOptions = uint32_t; const uint32_t kUseMasks = 1 << 0; const uint32_t kUseAntialiasing = 1 << 1; const uint32_t kUseColorMatrix = 1 << 2; const uint32_t kForceShaders = 1 << 3; class LayerTreeHostBlendingPixelTest : public LayerTreeHostPixelResourceTest { public: LayerTreeHostBlendingPixelTest() : force_antialiasing_(false), force_blending_with_shaders_(false) { pixel_comparator_.reset(new FuzzyPixelOffByOneComparator(true)); } void InitializeSettings(LayerTreeSettings* settings) override { settings->renderer_settings.force_antialiasing = force_antialiasing_; settings->renderer_settings.force_blending_with_shaders = force_blending_with_shaders_; } protected: void RunBlendingWithRootPixelTestType(PixelResourceTestCase type) { const int kLaneWidth = 2; const int kLaneHeight = kLaneWidth; const int kRootWidth = (kBlendModesCount + 2) * kLaneWidth; const int kRootHeight = 2 * kLaneWidth + kLaneHeight; InitializeFromTestCase(type); scoped_refptr background = CreateSolidColorLayer(gfx::Rect(kRootWidth, kRootHeight), kCSSOrange); // Orange child layers will blend with the green background for (int i = 0; i < kBlendModesCount; ++i) { gfx::Rect child_rect( (i + 1) * kLaneWidth, kLaneWidth, kLaneWidth, kLaneHeight); scoped_refptr green_lane = CreateSolidColorLayer(child_rect, kCSSGreen); background->AddChild(green_lane); green_lane->SetBlendMode(kBlendModes[i]); } RunPixelResourceTest( background, base::FilePath(FILE_PATH_LITERAL("blending_with_root.png"))); } void RunBlendingWithTransparentPixelTestType(PixelResourceTestCase type) { const int kLaneWidth = 2; const int kLaneHeight = 3 * kLaneWidth; const int kRootWidth = (kBlendModesCount + 2) * kLaneWidth; const int kRootHeight = 2 * kLaneWidth + kLaneHeight; InitializeFromTestCase(type); scoped_refptr root = CreateSolidColorLayer(gfx::Rect(kRootWidth, kRootHeight), kCSSBrown); scoped_refptr background = CreateSolidColorLayer( gfx::Rect(0, kLaneWidth * 2, kRootWidth, kLaneWidth), kCSSOrange); root->AddChild(background); background->SetIsRootForIsolatedGroup(true); // Orange child layers will blend with the green background for (int i = 0; i < kBlendModesCount; ++i) { gfx::Rect child_rect( (i + 1) * kLaneWidth, -kLaneWidth, kLaneWidth, kLaneHeight); scoped_refptr green_lane = CreateSolidColorLayer(child_rect, kCSSGreen); background->AddChild(green_lane); green_lane->SetBlendMode(kBlendModes[i]); } RunPixelResourceTest( root, base::FilePath(FILE_PATH_LITERAL("blending_transparent.png"))); } scoped_refptr CreateColorfulBackdropLayer(int width, int height) { // Draw the backdrop with horizontal lanes. const int kLaneWidth = width; const int kLaneHeight = height / kCSSTestColorsCount; skia::RefPtr backing_store = skia::AdoptRef(SkSurface::NewRasterN32Premul(width, height)); SkCanvas* canvas = backing_store->getCanvas(); canvas->clear(SK_ColorTRANSPARENT); for (int i = 0; i < kCSSTestColorsCount; ++i) { SkPaint paint; paint.setColor(kCSSTestColors[i]); canvas->drawRect( SkRect::MakeXYWH(0, i * kLaneHeight, kLaneWidth, kLaneHeight), paint); } scoped_refptr layer = PictureImageLayer::Create(); layer->SetIsDrawable(true); layer->SetBounds(gfx::Size(width, height)); skia::RefPtr image = skia::AdoptRef(backing_store->newImageSnapshot()); layer->SetImage(std::move(image)); return layer; } void SetupMaskLayer(scoped_refptr layer) { const int kMaskOffset = 2; gfx::Size bounds = layer->bounds(); scoped_refptr mask = PictureImageLayer::Create(); mask->SetIsDrawable(true); mask->SetIsMask(true); mask->SetBounds(bounds); skia::RefPtr surface = skia::AdoptRef( SkSurface::NewRasterN32Premul(bounds.width(), bounds.height())); SkCanvas* canvas = surface->getCanvas(); SkPaint paint; paint.setColor(SK_ColorWHITE); canvas->clear(SK_ColorTRANSPARENT); canvas->drawRect(SkRect::MakeXYWH(kMaskOffset, kMaskOffset, bounds.width() - kMaskOffset * 2, bounds.height() - kMaskOffset * 2), paint); skia::RefPtr image = skia::AdoptRef(surface->newImageSnapshot()); mask->SetImage(std::move(image)); layer->SetMaskLayer(mask.get()); } void SetupColorMatrix(scoped_refptr layer) { FilterOperations filter_operations; filter_operations.Append(FilterOperation::CreateSepiaFilter(.001f)); layer->SetFilters(filter_operations); } void CreateBlendingColorLayers(int lane_width, int lane_height, scoped_refptr background, RenderPassOptions flags) { const int kLanesCount = kBlendModesCount + 4; const SkColor kMiscOpaqueColor = 0xffc86464; const SkColor kMiscTransparentColor = 0x80c86464; const SkXfermode::Mode kCoeffBlendMode = SkXfermode::kScreen_Mode; const SkXfermode::Mode kShaderBlendMode = SkXfermode::kColorBurn_Mode; // add vertical lanes with each of the blend modes for (int i = 0; i < kLanesCount; ++i) { gfx::Rect child_rect(i * lane_width, 0, lane_width, lane_height); SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode; float opacity = 1.f; SkColor color = kMiscOpaqueColor; if (i < kBlendModesCount) { blend_mode = kBlendModes[i]; } else if (i == kBlendModesCount) { blend_mode = kCoeffBlendMode; opacity = 0.5f; } else if (i == kBlendModesCount + 1) { blend_mode = kCoeffBlendMode; color = kMiscTransparentColor; } else if (i == kBlendModesCount + 2) { blend_mode = kShaderBlendMode; opacity = 0.5f; } else if (i == kBlendModesCount + 3) { blend_mode = kShaderBlendMode; color = kMiscTransparentColor; } scoped_refptr lane = CreateSolidColorLayer(child_rect, color); lane->SetBlendMode(blend_mode); lane->SetOpacity(opacity); lane->SetForceRenderSurface(true); if (flags & kUseMasks) SetupMaskLayer(lane); if (flags & kUseColorMatrix) { SetupColorMatrix(lane); } background->AddChild(lane); } } void RunBlendingWithRenderPass(PixelResourceTestCase type, const base::FilePath::CharType* expected_path, RenderPassOptions flags) { const int kLaneWidth = 8; const int kLaneHeight = kLaneWidth * kCSSTestColorsCount; const int kRootSize = kLaneHeight; InitializeFromTestCase(type); scoped_refptr root = CreateSolidColorLayer(gfx::Rect(kRootSize, kRootSize), SK_ColorWHITE); scoped_refptr background = CreateColorfulBackdropLayer(kRootSize, kRootSize); background->SetIsRootForIsolatedGroup(true); root->AddChild(background); CreateBlendingColorLayers(kLaneWidth, kLaneHeight, background.get(), flags); this->force_antialiasing_ = (flags & kUseAntialiasing); this->force_blending_with_shaders_ = (flags & kForceShaders); if ((flags & kUseAntialiasing) && (test_type_ == PIXEL_TEST_GL)) { // Anti aliasing causes differences up to 8 pixels at the edges. int large_error_allowed = 8; // Blending results might differ with one pixel. int small_error_allowed = 1; // Most of the errors are one pixel errors. float percentage_pixels_small_error = 13.1f; // Because of anti-aliasing, around 10% of pixels (at the edges) have // bigger errors (from small_error_allowed + 1 to large_error_allowed). float percentage_pixels_error = 22.5f; // The average error is still close to 1. float average_error_allowed_in_bad_pixels = 1.4f; pixel_comparator_.reset( new FuzzyPixelComparator(false, // discard_alpha percentage_pixels_error, percentage_pixels_small_error, average_error_allowed_in_bad_pixels, large_error_allowed, small_error_allowed)); } RunPixelResourceTest(root, base::FilePath(expected_path)); } bool force_antialiasing_; bool force_blending_with_shaders_; }; TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRoot_GL) { RunBlendingWithRootPixelTestType(GL_ZERO_COPY_RECT_DRAW); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRoot_Software) { RunBlendingWithRootPixelTestType(SOFTWARE); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithBackgroundFilter) { const int kLaneWidth = 2; const int kLaneHeight = kLaneWidth; const int kRootWidth = (kBlendModesCount + 2) * kLaneWidth; const int kRootHeight = 2 * kLaneWidth + kLaneHeight; InitializeFromTestCase(GL_ZERO_COPY_RECT_DRAW); scoped_refptr background = CreateSolidColorLayer(gfx::Rect(kRootWidth, kRootHeight), kCSSOrange); // Orange child layers have a background filter set and they will blend with // the green background for (int i = 0; i < kBlendModesCount; ++i) { gfx::Rect child_rect( (i + 1) * kLaneWidth, kLaneWidth, kLaneWidth, kLaneHeight); scoped_refptr green_lane = CreateSolidColorLayer(child_rect, kCSSGreen); background->AddChild(green_lane); FilterOperations filters; filters.Append(FilterOperation::CreateGrayscaleFilter(.75)); green_lane->SetBackgroundFilters(filters); green_lane->SetBlendMode(kBlendModes[i]); } RunPixelResourceTest( background, base::FilePath(FILE_PATH_LITERAL("blending_and_filter.png"))); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithTransparent_GL) { RunBlendingWithTransparentPixelTestType(GL_ZERO_COPY_RECT_DRAW); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithTransparent_Software) { RunBlendingWithTransparentPixelTestType(SOFTWARE); } // Tests for render passes TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPass_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass.png"), 0); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPass_Software) { RunBlendingWithRenderPass(SOFTWARE, FILE_PATH_LITERAL("blending_render_pass.png"), 0); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassAA_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass.png"), kUseAntialiasing); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassAA_Software) { RunBlendingWithRenderPass(SOFTWARE, FILE_PATH_LITERAL("blending_render_pass.png"), kUseAntialiasing); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMask_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMask_Software) { RunBlendingWithRenderPass( SOFTWARE, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskAA_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseAntialiasing); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskAA_Software) { RunBlendingWithRenderPass(SOFTWARE, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseAntialiasing); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrix_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass.png"), kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrix_Software) { RunBlendingWithRenderPass( SOFTWARE, FILE_PATH_LITERAL("blending_render_pass.png"), kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrixAA_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass.png"), kUseAntialiasing | kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrixAA_Software) { RunBlendingWithRenderPass(SOFTWARE, FILE_PATH_LITERAL("blending_render_pass.png"), kUseAntialiasing | kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskColorMatrix_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskColorMatrix_Software) { RunBlendingWithRenderPass(SOFTWARE, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskColorMatrixAA_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseAntialiasing | kUseColorMatrix); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskColorMatrixAA_Software) { RunBlendingWithRenderPass(SOFTWARE, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseAntialiasing | kUseColorMatrix); } // Tests for render passes forcing shaders for all the blend modes. TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShaders_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass.png"), kForceShaders); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersAA_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass.png"), kUseAntialiasing | kForceShaders); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersWithMask_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kForceShaders); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersWithMask_GL_TextureRect) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kForceShaders); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersWithMaskAA_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseAntialiasing | kForceShaders); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersWithMaskAA_GL_TextureRect) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseAntialiasing | kForceShaders); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersColorMatrix_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass.png"), kUseColorMatrix | kForceShaders); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersColorMatrixAA_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass.png"), kUseAntialiasing | kUseColorMatrix | kForceShaders); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersWithMaskColorMatrix_GL) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseColorMatrix | kForceShaders); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersWithMaskColorMatrix_GL_TextureRect) { RunBlendingWithRenderPass(GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseColorMatrix | kForceShaders); } TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersWithMaskColorMatrixAA_GL) { RunBlendingWithRenderPass( GL_ZERO_COPY_RECT_DRAW, FILE_PATH_LITERAL("blending_render_pass_mask.png"), kUseMasks | kUseAntialiasing | kUseColorMatrix | kForceShaders); } } // namespace } // namespace cc #endif // OS_ANDROID