blob: ba7224cc5fa2d183df0f150bc8e406f482071415 [file] [log] [blame]
// 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 <stdint.h>
#include "cc/layers/picture_image_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/paint/paint_image.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/test/layer_tree_pixel_resource_test.h"
#include "cc/test/pixel_comparator.h"
#include "components/viz/test/test_layer_tree_frame_sink.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkSurface.h"
#if !defined(OS_ANDROID)
namespace cc {
namespace {
SkBlendMode const kBlendModes[] = {
SkBlendMode::kSrcOver, SkBlendMode::kScreen,
SkBlendMode::kOverlay, SkBlendMode::kDarken,
SkBlendMode::kLighten, SkBlendMode::kColorDodge,
SkBlendMode::kColorBurn, SkBlendMode::kHardLight,
SkBlendMode::kSoftLight, SkBlendMode::kDifference,
SkBlendMode::kExclusion, SkBlendMode::kMultiply,
SkBlendMode::kHue, SkBlendMode::kSaturation,
SkBlendMode::kColor, SkBlendMode::kLuminosity};
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));
}
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 = force_antialiasing_;
modified_renderer_settings.force_blending_with_shaders =
force_blending_with_shaders_;
return LayerTreeHostPixelResourceTest::CreateLayerTreeFrameSink(
modified_renderer_settings, refresh_rate, compositor_context_provider,
worker_context_provider);
}
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<SolidColorLayer> 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<SolidColorLayer> 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<SolidColorLayer> root =
CreateSolidColorLayer(gfx::Rect(kRootWidth, kRootHeight), kCSSBrown);
scoped_refptr<SolidColorLayer> 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<SolidColorLayer> 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<Layer> CreateColorfulBackdropLayer(int width, int height) {
// Draw the backdrop with horizontal lanes.
const int kLaneWidth = width;
const int kLaneHeight = height / kCSSTestColorsCount;
sk_sp<SkSurface> backing_store =
SkSurface::MakeRasterN32Premul(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<PictureImageLayer> layer = PictureImageLayer::Create();
layer->SetIsDrawable(true);
layer->SetBounds(gfx::Size(width, height));
layer->SetImage(PaintImageBuilder::WithDefault()
.set_id(PaintImage::GetNextId())
.set_image(backing_store->makeImageSnapshot(),
PaintImage::GetNextContentId())
.TakePaintImage(),
SkMatrix::I(), false);
return layer;
}
void SetupMaskLayer(scoped_refptr<Layer> layer) {
const int kMaskOffset = 2;
gfx::Size bounds = layer->bounds();
scoped_refptr<PictureImageLayer> mask = PictureImageLayer::Create();
mask->SetIsDrawable(true);
mask->SetLayerMaskType(Layer::LayerMaskType::MULTI_TEXTURE_MASK);
mask->SetBounds(bounds);
sk_sp<SkSurface> surface =
SkSurface::MakeRasterN32Premul(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);
mask->SetImage(PaintImageBuilder::WithDefault()
.set_id(PaintImage::GetNextId())
.set_image(surface->makeImageSnapshot(),
PaintImage::GetNextContentId())
.TakePaintImage(),
SkMatrix::I(), false);
layer->SetMaskLayer(mask.get());
}
void SetupColorMatrix(scoped_refptr<Layer> layer) {
FilterOperations filter_operations;
filter_operations.Append(FilterOperation::CreateSepiaFilter(.001f));
layer->SetFilters(filter_operations);
}
void CreateBlendingColorLayers(int lane_width,
int lane_height,
scoped_refptr<Layer> background,
RenderPassOptions flags) {
const int kLanesCount = kBlendModesCount + 4;
const SkColor kMiscOpaqueColor = 0xffc86464;
const SkColor kMiscTransparentColor = 0x80c86464;
const SkBlendMode kCoeffBlendMode = SkBlendMode::kScreen;
const SkBlendMode kShaderBlendMode = SkBlendMode::kColorBurn;
// 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);
SkBlendMode blend_mode = SkBlendMode::kSrcOver;
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<SolidColorLayer> lane =
CreateSolidColorLayer(child_rect, color);
lane->SetBlendMode(blend_mode);
lane->SetOpacity(opacity);
lane->SetForceRenderSurfaceForTesting(true);
// Layers with kDstIn blend mode with a mask is not supported.
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 kRootWidth = kLaneWidth * (kBlendModesCount + 4);
const int kRootHeight = kLaneWidth * kCSSTestColorsCount;
InitializeFromTestCase(type);
scoped_refptr<SolidColorLayer> root = CreateSolidColorLayer(
gfx::Rect(kRootWidth, kRootHeight), SK_ColorWHITE);
scoped_refptr<Layer> background =
CreateColorfulBackdropLayer(kRootWidth, kRootHeight);
background->SetIsRootForIsolatedGroup(true);
root->AddChild(background);
CreateBlendingColorLayers(kLaneWidth, kRootHeight, 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.2f;
// 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(ZERO_COPY);
}
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(ZERO_COPY);
scoped_refptr<SolidColorLayer> 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<SolidColorLayer> 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(ZERO_COPY);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithTransparent_Software) {
RunBlendingWithTransparentPixelTestType(SOFTWARE);
}
// Tests for render passes
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPass_GL) {
RunBlendingWithRenderPass(ZERO_COPY,
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(ZERO_COPY,
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(
ZERO_COPY, 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(ZERO_COPY,
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(ZERO_COPY,
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(ZERO_COPY,
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(ZERO_COPY,
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(ZERO_COPY,
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(
ZERO_COPY, FILE_PATH_LITERAL("blending_render_pass.png"), kForceShaders);
}
TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersAA_GL) {
RunBlendingWithRenderPass(ZERO_COPY,
FILE_PATH_LITERAL("blending_render_pass.png"),
kUseAntialiasing | kForceShaders);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersWithMask_GL) {
RunBlendingWithRenderPass(ZERO_COPY,
FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks | kForceShaders);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersWithMask_GL_TextureRect) {
RunBlendingWithRenderPass(ZERO_COPY,
FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks | kForceShaders);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersWithMaskAA_GL) {
RunBlendingWithRenderPass(ZERO_COPY,
FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks | kUseAntialiasing | kForceShaders);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersWithMaskAA_GL_TextureRect) {
RunBlendingWithRenderPass(ZERO_COPY,
FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks | kUseAntialiasing | kForceShaders);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersColorMatrix_GL) {
RunBlendingWithRenderPass(ZERO_COPY,
FILE_PATH_LITERAL("blending_render_pass.png"),
kUseColorMatrix | kForceShaders);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersColorMatrixAA_GL) {
RunBlendingWithRenderPass(ZERO_COPY,
FILE_PATH_LITERAL("blending_render_pass.png"),
kUseAntialiasing | kUseColorMatrix | kForceShaders);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersWithMaskColorMatrix_GL) {
RunBlendingWithRenderPass(ZERO_COPY,
FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks | kUseColorMatrix | kForceShaders);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersWithMaskColorMatrix_GL_TextureRect) {
RunBlendingWithRenderPass(ZERO_COPY,
FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks | kUseColorMatrix | kForceShaders);
}
TEST_F(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersWithMaskColorMatrixAA_GL) {
RunBlendingWithRenderPass(
ZERO_COPY, FILE_PATH_LITERAL("blending_render_pass_mask.png"),
kUseMasks | kUseAntialiasing | kUseColorMatrix | kForceShaders);
}
} // namespace
} // namespace cc
#endif // OS_ANDROID