blob: 93dee1acd3d14267948be669e611bfab3741eb73 [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 "build/build_config.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/paint/render_surface_filters.h"
#include "cc/paint/skia_paint_canvas.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 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 ::testing::WithParamInterface<
::testing::tuple<PixelResourceTestCase, SkBlendMode>> {
public:
LayerTreeHostBlendingPixelTest()
: force_antialiasing_(false), force_blending_with_shaders_(false) {
pixel_comparator_.reset(new FuzzyPixelOffByOneComparator(true));
}
PixelResourceTestCase resource_type() const {
return ::testing::get<0>(GetParam());
}
SkBlendMode current_blend_mode() const {
return ::testing::get<1>(GetParam());
}
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);
}
sk_sp<SkSurface> CreateColorfulSurface(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);
}
return backing_store;
}
scoped_refptr<Layer> CreateColorfulBackdropLayer(int width, int height) {
sk_sp<SkSurface> backing_store = CreateColorfulSurface(width, height);
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) {
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);
// This layer is a long skinny layer of size 2, so have the mask
// cover the right half of it
canvas->drawRect(
SkRect::MakeXYWH(1, 0, bounds.width() - 1, bounds.height()), 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) {
gfx::Rect child_rect(lane_width, lane_height);
scoped_refptr<SolidColorLayer> lane =
CreateSolidColorLayer(child_rect, misc_opaque_color_);
lane->SetBlendMode(current_blend_mode());
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);
}
SkBitmap CreateBlendingWithRenderPassExpected(int width,
int height,
RenderPassOptions flags) {
// Should match RunBlendingWithRenderPass.
sk_sp<SkSurface> surface = CreateColorfulSurface(width, height);
SkPaint paint;
paint.setBlendMode(current_blend_mode());
paint.setColor(misc_opaque_color_);
SkRect rect;
if (flags & kUseMasks) {
rect = SkRect::MakeXYWH(1, 0, width - 1, height);
} else {
rect = SkRect::MakeWH(width, height);
}
surface->getCanvas()->drawRect(rect, paint);
SkBitmap expected;
expected.allocN32Pixels(width, height);
SkCanvas canvas(expected);
canvas.clear(SK_ColorWHITE);
canvas.drawImage(surface->makeImageSnapshot(), 0, 0);
return expected;
}
void RunBlendingWithRenderPass(RenderPassOptions flags) {
const int kRootWidth = 2;
const int kRootHeight = kRootWidth * kCSSTestColorsCount;
InitializeFromTestCase(resource_type());
// Force shaders only applies to gl renderer.
if (test_type_ != PIXEL_TEST_GL && flags & kForceShaders)
return;
SCOPED_TRACE(test_type_ == PIXEL_TEST_GL ? "GL" : "Software");
SCOPED_TRACE(SkBlendMode_Name(current_blend_mode()));
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(kRootWidth, kRootHeight, background.get(), flags);
this->force_antialiasing_ = (flags & kUseAntialiasing);
this->force_blending_with_shaders_ = (flags & kForceShaders);
if ((flags & kUseAntialiasing) && (test_type_ == PIXEL_TEST_GL)) {
// Blending results might differ with one pixel.
// Don't allow large errors here, only off by ones.
// However, large error still has to be specified to satisfy
// the pixel comparator so set it equivalent to small errors.
int large_error_allowed = 1;
int small_error_allowed = 1;
float percentage_pixels_small_error = 35.0f;
float percentage_pixels_error = 35.0f;
// 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, CreateBlendingWithRenderPassExpected(
kRootWidth, kRootHeight, flags));
}
bool force_antialiasing_;
bool force_blending_with_shaders_;
SkColor misc_opaque_color_ = 0xffc86464;
};
INSTANTIATE_TEST_CASE_P(B,
LayerTreeHostBlendingPixelTest,
::testing::Combine(::testing::Values(SOFTWARE,
ZERO_COPY),
::testing::ValuesIn(kBlendModes)));
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRoot) {
const int kRootWidth = 2;
const int kRootHeight = 2;
InitializeFromTestCase(resource_type());
scoped_refptr<SolidColorLayer> background =
CreateSolidColorLayer(gfx::Rect(kRootWidth, kRootHeight), kCSSOrange);
// Orange child layers will blend with the green background
gfx::Rect child_rect(0, 0, kRootWidth, kRootHeight);
scoped_refptr<SolidColorLayer> green_lane =
CreateSolidColorLayer(child_rect, kCSSGreen);
background->AddChild(green_lane);
green_lane->SetBlendMode(current_blend_mode());
SkBitmap expected;
expected.allocN32Pixels(kRootWidth, kRootHeight);
SkCanvas canvas(expected);
canvas.drawColor(kCSSOrange);
SkPaint paint;
paint.setBlendMode(current_blend_mode());
paint.setColor(kCSSGreen);
canvas.drawRect(SkRect::MakeWH(kRootWidth, kRootHeight), paint);
RunPixelResourceTest(background, expected);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithBackgroundFilter) {
const int kRootWidth = 2;
const int kRootHeight = 2;
InitializeFromTestCase(resource_type());
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
gfx::Rect child_rect(0, 0, kRootWidth, kRootHeight);
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(current_blend_mode());
SkBitmap expected;
expected.allocN32Pixels(kRootWidth, kRootHeight);
SkCanvas canvas(expected);
SkiaPaintCanvas paint_canvas(&canvas);
PaintFlags grayscale;
grayscale.setColor(kCSSOrange);
sk_sp<PaintFilter> paint_filter = RenderSurfaceFilters::BuildImageFilter(
filters, gfx::SizeF(kRootWidth, kRootHeight));
grayscale.setImageFilter(paint_filter);
paint_canvas.drawRect(SkRect::MakeWH(kRootWidth, kRootHeight), grayscale);
PaintFlags blend_green;
blend_green.setBlendMode(current_blend_mode());
blend_green.setColor(kCSSGreen);
paint_canvas.drawRect(SkRect::MakeWH(kRootWidth, kRootHeight), blend_green);
RunPixelResourceTest(background, expected);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithTransparent) {
const int kRootWidth = 2;
const int kRootHeight = 2;
InitializeFromTestCase(resource_type());
// Intermediate layer here that should be ignored because of the isolated
// group.
scoped_refptr<SolidColorLayer> root =
CreateSolidColorLayer(gfx::Rect(kRootWidth, kRootHeight), kCSSBrown);
scoped_refptr<SolidColorLayer> background =
CreateSolidColorLayer(gfx::Rect(kRootWidth, kRootHeight), kCSSOrange);
root->AddChild(background);
background->SetIsRootForIsolatedGroup(true);
// Orange child layers will blend with the green background
gfx::Rect child_rect(kRootWidth, kRootHeight);
scoped_refptr<SolidColorLayer> green_lane =
CreateSolidColorLayer(child_rect, kCSSGreen);
background->AddChild(green_lane);
green_lane->SetBlendMode(current_blend_mode());
SkBitmap expected;
expected.allocN32Pixels(kRootWidth, kRootHeight);
SkCanvas canvas(expected);
canvas.drawColor(kCSSOrange);
SkPaint paint;
paint.setBlendMode(current_blend_mode());
paint.setColor(kCSSGreen);
canvas.drawRect(SkRect::MakeWH(kRootWidth, kRootHeight), paint);
RunPixelResourceTest(root, expected);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRenderPass) {
RunBlendingWithRenderPass(0);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassAA) {
RunBlendingWithRenderPass(kUseAntialiasing);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrix) {
RunBlendingWithRenderPass(kUseColorMatrix);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMask) {
RunBlendingWithRenderPass(kUseMasks);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrixAA) {
RunBlendingWithRenderPass(kUseAntialiasing | kUseColorMatrix);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskAA) {
RunBlendingWithRenderPass(kUseMasks | kUseAntialiasing);
}
TEST_P(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassWithMaskColorMatrix) {
RunBlendingWithRenderPass(kUseMasks | kUseColorMatrix);
}
TEST_P(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassWithMaskColorMatrixAA) {
RunBlendingWithRenderPass(kUseMasks | kUseAntialiasing | kUseColorMatrix);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShaders) {
RunBlendingWithRenderPass(kForceShaders);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersAA) {
RunBlendingWithRenderPass(kUseAntialiasing | kForceShaders);
}
TEST_P(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassShadersWithMask) {
RunBlendingWithRenderPass(kUseMasks | kForceShaders);
}
TEST_P(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersWithMaskAA) {
RunBlendingWithRenderPass(kUseMasks | kUseAntialiasing | kForceShaders);
}
TEST_P(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersColorMatrix) {
RunBlendingWithRenderPass(kUseColorMatrix | kForceShaders);
}
TEST_P(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersColorMatrixAA) {
RunBlendingWithRenderPass(kUseAntialiasing | kUseColorMatrix | kForceShaders);
}
TEST_P(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersWithMaskColorMatrix) {
RunBlendingWithRenderPass(kUseMasks | kUseColorMatrix | kForceShaders);
}
TEST_P(LayerTreeHostBlendingPixelTest,
BlendingWithRenderPassShadersWithMaskColorMatrixAA) {
RunBlendingWithRenderPass(kUseMasks | kUseAntialiasing | kUseColorMatrix |
kForceShaders);
}
} // namespace
} // namespace cc
#endif // OS_ANDROID