diff options
author | rosca@adobe.com <rosca@adobe.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-26 10:27:22 +0000 |
---|---|---|
committer | rosca@adobe.com <rosca@adobe.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-26 10:27:22 +0000 |
commit | 7bbeaf4e5b9ad540bf6d3496470778ed74737766 (patch) | |
tree | 63d6549481b2a660eb39e3bfef8e0c42d28b75f1 /cc | |
parent | cc6a5a0804b5138079708e5c2a32081e078af605 (diff) | |
download | chromium_src-7bbeaf4e5b9ad540bf6d3496470778ed74737766.zip chromium_src-7bbeaf4e5b9ad540bf6d3496470778ed74737766.tar.gz chromium_src-7bbeaf4e5b9ad540bf6d3496470778ed74737766.tar.bz2 |
The blink part of this implementation is uploaded at https://codereview.chromium.org/23511004/
The spec for mix-blend-mode is http://dev.w3.org/fxtf/compositing-1/#mix-blend-mode
BUG=243223
Review URL: https://codereview.chromium.org/23455060
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237295 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc')
29 files changed, 743 insertions, 115 deletions
diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp index 1699577..5b318fa27 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp @@ -84,6 +84,7 @@ 'trees/layer_sorter_unittest.cc', 'trees/layer_tree_host_common_unittest.cc', 'trees/layer_tree_host_impl_unittest.cc', + 'trees/layer_tree_host_pixeltest_blending.cc', 'trees/layer_tree_host_pixeltest_filters.cc', 'trees/layer_tree_host_pixeltest_masks.cc', 'trees/layer_tree_host_pixeltest_on_demand_raster.cc', diff --git a/cc/layers/compositing_reasons.h b/cc/layers/compositing_reasons.h index 02354a0..6331a90 100644 --- a/cc/layers/compositing_reasons.h +++ b/cc/layers/compositing_reasons.h @@ -53,6 +53,8 @@ const uint64 kCompositingReasonLayerForBackground = GG_UINT64_C(1) << 30; const uint64 kCompositingReasonLayerForMask = GG_UINT64_C(1) << 31; const uint64 kCompositingReasonOverflowScrollingParent = GG_UINT64_C(1) << 32; const uint64 kCompositingReasonOutOfFlowClipping = GG_UINT64_C(1) << 33; +const uint64 kCompositingReasonIsolateCompositedDescendants = + GG_UINT64_C(1) << 35; typedef uint64 CompositingReasons; diff --git a/cc/layers/delegated_renderer_layer.cc b/cc/layers/delegated_renderer_layer.cc index a92b701..a940903 100644 --- a/cc/layers/delegated_renderer_layer.cc +++ b/cc/layers/delegated_renderer_layer.cc @@ -107,10 +107,13 @@ void DelegatedRendererLayer::SetDisplaySize(gfx::Size size) { SetNeedsCommit(); } -static bool FrameDataHasFilter(DelegatedFrameData* frame) { +static bool FrameDataRequiresFilterContext(const DelegatedFrameData* frame) { for (size_t i = 0; i < frame->render_pass_list.size(); ++i) { const QuadList& quad_list = frame->render_pass_list[i]->quad_list; for (size_t j = 0; j < quad_list.size(); ++j) { + if (quad_list[j]->shared_quad_state->blend_mode != + SkXfermode::kSrcOver_Mode) + return true; if (quad_list[j]->material != DrawQuad::RENDER_PASS) continue; const RenderPassDrawQuad* render_pass_quad = @@ -133,9 +136,9 @@ bool DelegatedRendererLayer::Update(ResourceUpdateQueue* queue, frame_provider_->GetFrameDataAndRefResources(this, &frame_damage_); should_collect_new_frame_ = false; - // If any quad has a filter operation, then we need a filter context to draw - // this layer's content. - if (FrameDataHasFilter(frame_data_) && layer_tree_host()) + // If any quad has a filter operation or a blend mode other than normal, + // then we need an offscreen context to draw this layer's content. + if (FrameDataRequiresFilterContext(frame_data_)) layer_tree_host()->set_needs_filter_context(); return true; diff --git a/cc/layers/delegated_renderer_layer_impl_unittest.cc b/cc/layers/delegated_renderer_layer_impl_unittest.cc index c8811a2..8cb0f3e 100644 --- a/cc/layers/delegated_renderer_layer_impl_unittest.cc +++ b/cc/layers/delegated_renderer_layer_impl_unittest.cc @@ -529,13 +529,13 @@ class DelegatedRendererLayerImplTestTransform AppendQuadsData data(pass->id); SharedQuadState* shared_quad_state = quad_sink.UseSharedQuadState( SharedQuadState::Create()); - shared_quad_state->SetAll( - child_pass_transform, - child_pass_content_bounds, - child_pass_rect, - child_pass_clip_rect, - child_pass_clipped, - 1.f); + shared_quad_state->SetAll(child_pass_transform, + child_pass_content_bounds, + child_pass_rect, + child_pass_clip_rect, + child_pass_clipped, + 1.f, + SkXfermode::kSrcOver_Mode); scoped_ptr<SolidColorDrawQuad> color_quad; color_quad = SolidColorDrawQuad::Create(); @@ -564,13 +564,13 @@ class DelegatedRendererLayerImplTestTransform AppendQuadsData data(pass->id); SharedQuadState* shared_quad_state = quad_sink.UseSharedQuadState(SharedQuadState::Create()); - shared_quad_state->SetAll( - root_pass_transform, - root_pass_content_bounds, - root_pass_rect, - root_pass_clip_rect, - root_pass_clipped, - 1.f); + shared_quad_state->SetAll(root_pass_transform, + root_pass_content_bounds, + root_pass_rect, + root_pass_clip_rect, + root_pass_clipped, + 1.f, + SkXfermode::kSrcOver_Mode); scoped_ptr<RenderPassDrawQuad> render_pass_quad = RenderPassDrawQuad::Create(); @@ -933,13 +933,13 @@ class DelegatedRendererLayerImplTestClip AppendQuadsData data(pass->id); SharedQuadState* shared_quad_state = quad_sink.UseSharedQuadState(SharedQuadState::Create()); - shared_quad_state->SetAll( - child_pass_transform, - child_pass_content_bounds, - child_pass_rect, - child_pass_clip_rect, - child_pass_clipped, - 1.f); + shared_quad_state->SetAll(child_pass_transform, + child_pass_content_bounds, + child_pass_rect, + child_pass_clip_rect, + child_pass_clipped, + 1.f, + SkXfermode::kSrcOver_Mode); scoped_ptr<SolidColorDrawQuad> color_quad; color_quad = SolidColorDrawQuad::Create(); @@ -971,7 +971,8 @@ class DelegatedRendererLayerImplTestClip root_pass_rect, root_pass_clip_rect, root_pass_clipped, - 1.f); + 1.f, + SkXfermode::kSrcOver_Mode); scoped_ptr<RenderPassDrawQuad> render_pass_quad = RenderPassDrawQuad::Create(); diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc index e0ffb5c..7790719 100644 --- a/cc/layers/layer.cc +++ b/cc/layers/layer.cc @@ -47,6 +47,8 @@ Layer::Layer() background_color_(0), compositing_reasons_(kCompositingReasonUnknown), opacity_(1.f), + blend_mode_(SkXfermode::kSrcOver_Mode), + is_root_for_isolated_group_(false), anchor_point_z_(0.f), is_container_for_fixed_position_layers_(false), is_drawable_(false), @@ -125,8 +127,7 @@ void Layer::SetLayerTreeHost(LayerTreeHost* host) { if (host && layer_animation_controller_->has_any_animation()) host->SetNeedsCommit(); - if (host && (!filters_.IsEmpty() || !background_filters_.IsEmpty())) - layer_tree_host_->set_needs_filter_context(); + SetNeedsFilterContextIfNeeded(); } void Layer::SetNeedsUpdate() { @@ -160,6 +161,15 @@ void Layer::SetNextCommitWaitsForActivation() { layer_tree_host_->SetNextCommitWaitsForActivation(); } +void Layer::SetNeedsFilterContextIfNeeded() { + if (!layer_tree_host_) + return; + + if (!filters_.IsEmpty() || !background_filters_.IsEmpty() || + !uses_default_blend_mode()) + layer_tree_host_->set_needs_filter_context(); +} + void Layer::SetNeedsPushProperties() { if (needs_push_properties_) return; @@ -471,8 +481,7 @@ void Layer::SetFilters(const FilterOperations& filters) { return; filters_ = filters; SetNeedsCommit(); - if (!filters.IsEmpty() && layer_tree_host_) - layer_tree_host_->set_needs_filter_context(); + SetNeedsFilterContextIfNeeded(); } bool Layer::FilterIsAnimating() const { @@ -485,8 +494,7 @@ void Layer::SetBackgroundFilters(const FilterOperations& filters) { return; background_filters_ = filters; SetNeedsCommit(); - if (!filters.IsEmpty() && layer_tree_host_) - layer_tree_host_->set_needs_filter_context(); + SetNeedsFilterContextIfNeeded(); } void Layer::SetOpacity(float opacity) { @@ -505,6 +513,64 @@ bool Layer::OpacityCanAnimateOnImplThread() const { return false; } +void Layer::SetBlendMode(SkXfermode::Mode blend_mode) { + DCHECK(IsPropertyChangeAllowed()); + if (blend_mode_ == blend_mode) + return; + + // Allowing only blend modes that are defined in the CSS Compositing standard: + // http://dev.w3.org/fxtf/compositing-1/#blending + switch (blend_mode) { + case SkXfermode::kSrcOver_Mode: + case SkXfermode::kScreen_Mode: + case SkXfermode::kOverlay_Mode: + case SkXfermode::kDarken_Mode: + case SkXfermode::kLighten_Mode: + case SkXfermode::kColorDodge_Mode: + case SkXfermode::kColorBurn_Mode: + case SkXfermode::kHardLight_Mode: + case SkXfermode::kSoftLight_Mode: + case SkXfermode::kDifference_Mode: + case SkXfermode::kExclusion_Mode: + case SkXfermode::kMultiply_Mode: + case SkXfermode::kHue_Mode: + case SkXfermode::kSaturation_Mode: + case SkXfermode::kColor_Mode: + case SkXfermode::kLuminosity_Mode: + // supported blend modes + break; + case SkXfermode::kClear_Mode: + case SkXfermode::kSrc_Mode: + case SkXfermode::kDst_Mode: + case SkXfermode::kDstOver_Mode: + case SkXfermode::kSrcIn_Mode: + case SkXfermode::kDstIn_Mode: + case SkXfermode::kSrcOut_Mode: + case SkXfermode::kDstOut_Mode: + case SkXfermode::kSrcATop_Mode: + case SkXfermode::kDstATop_Mode: + case SkXfermode::kXor_Mode: + case SkXfermode::kPlus_Mode: + case SkXfermode::kModulate_Mode: + // Porter Duff Compositing Operators are not yet supported + // http://dev.w3.org/fxtf/compositing-1/#porterduffcompositingoperators + NOTREACHED(); + return; + } + + blend_mode_ = blend_mode; + SetNeedsCommit(); + SetNeedsFilterContextIfNeeded(); +} + +void Layer::SetIsRootForIsolatedGroup(bool root) { + DCHECK(IsPropertyChangeAllowed()); + if (is_root_for_isolated_group_ == root) + return; + is_root_for_isolated_group_ = root; + SetNeedsCommit(); +} + void Layer::SetContentsOpaque(bool opaque) { DCHECK(IsPropertyChangeAllowed()); if (contents_opaque_ == opaque) @@ -832,6 +898,8 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { if (!layer->OpacityIsAnimatingOnImplOnly() && !OpacityIsAnimating()) layer->SetOpacity(opacity_); DCHECK(!(OpacityIsAnimating() && layer->OpacityIsAnimatingOnImplOnly())); + layer->SetBlendMode(blend_mode_); + layer->SetIsRootForIsolatedGroup(is_root_for_isolated_group_); layer->SetPosition(position_); layer->SetIsContainerForFixedPositionLayers( IsContainerForFixedPositionLayers()); diff --git a/cc/layers/layer.h b/cc/layers/layer.h index b552a35..9af3dea 100644 --- a/cc/layers/layer.h +++ b/cc/layers/layer.h @@ -29,6 +29,7 @@ #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkImageFilter.h" #include "third_party/skia/include/core/SkPicture.h" +#include "third_party/skia/include/core/SkXfermode.h" #include "ui/gfx/rect.h" #include "ui/gfx/rect_f.h" #include "ui/gfx/transform.h" @@ -126,6 +127,22 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, bool OpacityIsAnimating() const; virtual bool OpacityCanAnimateOnImplThread() const; + void SetBlendMode(SkXfermode::Mode blend_mode); + SkXfermode::Mode blend_mode() const { return blend_mode_; } + + bool uses_default_blend_mode() const { + return blend_mode_ == SkXfermode::kSrcOver_Mode; + } + + // A layer is root for an isolated group when it and all its descendants are + // drawn over a black and fully transparent background, creating an isolated + // group. It should be used along with SetBlendMode(), in order to restrict + // layers within the group to blend with layers outside this group. + void SetIsRootForIsolatedGroup(bool root); + bool is_root_for_isolated_group() const { + return is_root_for_isolated_group_; + } + void SetFilters(const FilterOperations& filters); const FilterOperations& filters() const { return filters_; } bool FilterIsAnimating() const; @@ -459,6 +476,9 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, // unused resources on the impl thread are returned before commit completes. void SetNextCommitWaitsForActivation(); + // Called when the blend mode or filters have been changed. + void SetNeedsFilterContextIfNeeded(); + void SetNeedsPushProperties(); void AddDependentNeedsPushProperties(); void RemoveDependentNeedsPushProperties(); @@ -552,6 +572,8 @@ class CC_EXPORT Layer : public base::RefCounted<Layer>, SkColor background_color_; CompositingReasons compositing_reasons_; float opacity_; + SkXfermode::Mode blend_mode_; + bool is_root_for_isolated_group_; FilterOperations filters_; FilterOperations background_filters_; float anchor_point_z_; diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index 99f9502..f8882291 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc @@ -53,6 +53,8 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id) masks_to_bounds_(false), contents_opaque_(false), opacity_(1.0), + blend_mode_(SkXfermode::kSrcOver_Mode), + is_root_for_isolated_group_(false), preserves_3d_(false), use_parent_backface_visibility_(false), draw_checkerboard_for_missing_tiles_(false), @@ -246,7 +248,8 @@ scoped_ptr<SharedQuadState> LayerImpl::CreateSharedQuadState() const { draw_properties_.visible_content_rect, draw_properties_.clip_rect, draw_properties_.is_clipped, - draw_properties_.opacity); + draw_properties_.opacity, + blend_mode_); return state.Pass(); } @@ -524,6 +527,8 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { layer->SetTouchEventHandlerRegion(touch_event_handler_region_); layer->SetContentsOpaque(contents_opaque_); layer->SetOpacity(opacity_); + layer->SetBlendMode(blend_mode_); + layer->SetIsRootForIsolatedGroup(is_root_for_isolated_group_); layer->SetPosition(position_); layer->SetIsContainerForFixedPositionLayers( is_container_for_fixed_position_layers_); @@ -880,6 +885,21 @@ bool LayerImpl::OpacityIsAnimatingOnImplOnly() const { return opacity_animation && opacity_animation->is_impl_only(); } +void LayerImpl::SetBlendMode(SkXfermode::Mode blend_mode) { + if (blend_mode_ == blend_mode) + return; + + blend_mode_ = blend_mode; + NoteLayerPropertyChangedForSubtree(); +} + +void LayerImpl::SetIsRootForIsolatedGroup(bool root) { + if (is_root_for_isolated_group_ == root) + return; + + is_root_for_isolated_group_ = root; +} + void LayerImpl::SetPosition(gfx::PointF position) { if (position_ == position) return; @@ -1281,6 +1301,9 @@ CompositingReasonsAsValue(CompositingReasons reasons) { if (reasons & kCompositingReasonOutOfFlowClipping) reason_list->AppendString("Has clipping ancestor"); + if (reasons & kCompositingReasonIsolateCompositedDescendants) + reason_list->AppendString("Should isolate composited descendants"); + return reason_list.PassAs<base::Value>(); } diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h index 6d06b6d2..1969b74 100644 --- a/cc/layers/layer_impl.h +++ b/cc/layers/layer_impl.h @@ -213,6 +213,17 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { bool OpacityIsAnimating() const; bool OpacityIsAnimatingOnImplOnly() const; + void SetBlendMode(SkXfermode::Mode); + SkXfermode::Mode blend_mode() const { return blend_mode_; } + bool uses_default_blend_mode() const { + return blend_mode_ == SkXfermode::kSrcOver_Mode; + } + + void SetIsRootForIsolatedGroup(bool root); + bool is_root_for_isolated_group() const { + return is_root_for_isolated_group_; + } + void SetPosition(gfx::PointF position); gfx::PointF position() const { return position_; } @@ -571,6 +582,8 @@ class CC_EXPORT LayerImpl : LayerAnimationValueObserver { bool masks_to_bounds_; bool contents_opaque_; float opacity_; + SkXfermode::Mode blend_mode_; + bool is_root_for_isolated_group_; gfx::PointF position_; bool preserves_3d_; bool use_parent_backface_visibility_; diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc index 2d3305c..c43adff 100644 --- a/cc/layers/layer_impl_unittest.cc +++ b/cc/layers/layer_impl_unittest.cc @@ -40,6 +40,13 @@ namespace { EXPECT_FALSE(child->LayerPropertyChanged()); \ EXPECT_FALSE(grand_child->LayerPropertyChanged()); +#define EXECUTE_AND_VERIFY_ONLY_DESCENDANTS_CHANGED(code_to_test) \ + root->ResetAllChangeTrackingForSubtree(); \ + code_to_test; \ + EXPECT_FALSE(root->LayerPropertyChanged()); \ + EXPECT_TRUE(child->LayerPropertyChanged()); \ + EXPECT_TRUE(grand_child->LayerPropertyChanged()); + #define VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(code_to_test) \ root->ResetAllChangeTrackingForSubtree(); \ host_impl.ForcePrepareToDraw(); \ @@ -91,6 +98,7 @@ TEST(LayerImplTest, VerifyLayerChangesAreTrackedProperly) { arbitrary_transform.Scale3d(0.1f, 0.2f, 0.3f); FilterOperations arbitrary_filters; arbitrary_filters.Append(FilterOperation::CreateOpacityFilter(0.5f)); + SkXfermode::Mode arbitrary_blend_mode = SkXfermode::kMultiply_Mode; // These properties are internal, and should not be considered "change" when // they are used. @@ -119,6 +127,7 @@ TEST(LayerImplTest, VerifyLayerChangesAreTrackedProperly) { EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetScrollOffset(arbitrary_vector2d)); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetHideLayerAndSubtree(true)); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetOpacity(arbitrary_number)); + EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetBlendMode(arbitrary_blend_mode)); EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetTransform(arbitrary_transform)); // Changing these properties only affects the layer itself. @@ -131,13 +140,10 @@ TEST(LayerImplTest, VerifyLayerChangesAreTrackedProperly) { EXECUTE_AND_VERIFY_ONLY_LAYER_CHANGED( root->SetBackgroundFilters(arbitrary_filters)); - // Special case: check that sublayer transform changes all layer's - // descendants, but not the layer itself. - root->ResetAllChangeTrackingForSubtree(); - root->SetSublayerTransform(arbitrary_transform); - EXPECT_FALSE(root->LayerPropertyChanged()); - EXPECT_TRUE(child->LayerPropertyChanged()); - EXPECT_TRUE(grand_child->LayerPropertyChanged()); + // Changing these properties affects all layer's descendants, + // but not the layer itself. + EXECUTE_AND_VERIFY_ONLY_DESCENDANTS_CHANGED( + root->SetSublayerTransform(arbitrary_transform)); // Special case: check that SetBounds changes behavior depending on // masksToBounds. @@ -148,6 +154,9 @@ TEST(LayerImplTest, VerifyLayerChangesAreTrackedProperly) { // changed. EXECUTE_AND_VERIFY_SUBTREE_CHANGED(root->SetBounds(arbitrary_size)); + EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE( + root->SetIsRootForIsolatedGroup(true)); + // After setting all these properties already, setting to the exact same // values again should not cause any change. EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE( @@ -172,6 +181,10 @@ TEST(LayerImplTest, VerifyLayerChangesAreTrackedProperly) { root->SetContentsScale(arbitrary_number, arbitrary_number)); EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetContentsOpaque(true)); EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetOpacity(arbitrary_number)); + EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE( + root->SetBlendMode(arbitrary_blend_mode)); + EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE( + root->SetIsRootForIsolatedGroup(true)); EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE(root->SetDrawsContent(true)); EXECUTE_AND_VERIFY_SUBTREE_DID_NOT_CHANGE( root->SetSublayerTransform(arbitrary_transform)); @@ -199,6 +212,7 @@ TEST(LayerImplTest, VerifyNeedsUpdateDrawProperties) { arbitrary_transform.Scale3d(0.1f, 0.2f, 0.3f); FilterOperations arbitrary_filters; arbitrary_filters.Append(FilterOperation::CreateOpacityFilter(0.5f)); + SkXfermode::Mode arbitrary_blend_mode = SkXfermode::kMultiply_Mode; // Related filter functions. VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetFilters(arbitrary_filters)); @@ -243,6 +257,7 @@ TEST(LayerImplTest, VerifyNeedsUpdateDrawProperties) { VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES( root->SetBackgroundFilters(arbitrary_filters)); VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetOpacity(arbitrary_number)); + VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetBlendMode(arbitrary_blend_mode)); VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetTransform(arbitrary_transform)); VERIFY_NEEDS_UPDATE_DRAW_PROPERTIES( root->SetSublayerTransform(arbitrary_transform)); @@ -251,6 +266,7 @@ TEST(LayerImplTest, VerifyNeedsUpdateDrawProperties) { // Unrelated functions, set to the same values, no needs update. VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES( root->SetAnchorPointZ(arbitrary_number)); + VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetIsRootForIsolatedGroup(true)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetFilters(arbitrary_filters)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetMasksToBounds(true)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetContentsOpaque(true)); @@ -269,6 +285,9 @@ TEST(LayerImplTest, VerifyNeedsUpdateDrawProperties) { root->SetBackgroundFilters(arbitrary_filters)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetOpacity(arbitrary_number)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES( + root->SetBlendMode(arbitrary_blend_mode)); + VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES(root->SetIsRootForIsolatedGroup(true)); + VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES( root->SetTransform(arbitrary_transform)); VERIFY_NO_NEEDS_UPDATE_DRAW_PROPERTIES( root->SetSublayerTransform(arbitrary_transform)); diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc index 0414acb..a77a5fb 100644 --- a/cc/layers/layer_unittest.cc +++ b/cc/layers/layer_unittest.cc @@ -545,6 +545,8 @@ TEST_F(LayerTest, CheckPropertyChangeCausesCorrectBehavior) { EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBackgroundColor(SK_ColorLTGRAY)); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetMasksToBounds(true)); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetOpacity(0.5f)); + EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetBlendMode(SkXfermode::kHue_Mode)); + EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetIsRootForIsolatedGroup(true)); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetContentsOpaque(true)); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetPosition(gfx::PointF(4.f, 9.f))); EXPECT_SET_NEEDS_COMMIT(1, test_layer->SetSublayerTransform( diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc index 660f5e4..53e60c7 100644 --- a/cc/layers/render_surface_impl.cc +++ b/cc/layers/render_surface_impl.cc @@ -156,7 +156,8 @@ void RenderSurfaceImpl::AppendQuads(QuadSink* quad_sink, content_rect_, clip_rect_, is_clipped_, - draw_opacity_); + draw_opacity_, + owning_layer_->blend_mode()); if (owning_layer_->ShowDebugBorders()) { SkColor color = for_replica ? diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc index c0cf138..cc9a752 100644 --- a/cc/output/gl_renderer.cc +++ b/cc/output/gl_renderer.cc @@ -576,11 +576,129 @@ static SkBitmap ApplyImageFilter(GLRenderer* renderer, return device.accessBitmap(false); } -scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters( +static SkBitmap ApplyBlendModeWithBackdrop( + GLRenderer* renderer, + ContextProvider* offscreen_contexts, + SkBitmap source_bitmap_with_filters, + ScopedResource* source_texture_resource, + ScopedResource* background_texture_resource, + SkXfermode::Mode blend_mode) { + if (!offscreen_contexts || !offscreen_contexts->GrContext()) + return source_bitmap_with_filters; + + DCHECK(background_texture_resource); + DCHECK(source_texture_resource); + + gfx::Size source_size = source_texture_resource->size(); + gfx::Size background_size = background_texture_resource->size(); + + DCHECK_LE(background_size.width(), source_size.width()); + DCHECK_LE(background_size.height(), source_size.height()); + + int source_texture_with_filters_id; + scoped_ptr<ResourceProvider::ScopedReadLockGL> lock; + if (source_bitmap_with_filters.getTexture()) { + DCHECK_EQ(source_size.width(), source_bitmap_with_filters.width()); + DCHECK_EQ(source_size.height(), source_bitmap_with_filters.height()); + GrTexture* texture = + reinterpret_cast<GrTexture*>(source_bitmap_with_filters.getTexture()); + source_texture_with_filters_id = texture->getTextureHandle(); + } else { + lock.reset(new ResourceProvider::ScopedReadLockGL( + renderer->resource_provider(), source_texture_resource->id())); + source_texture_with_filters_id = lock->texture_id(); + } + + ResourceProvider::ScopedReadLockGL lock_background( + renderer->resource_provider(), background_texture_resource->id()); + + // Flush the compositor context to ensure that textures there are available + // in the shared context. Do this after locking/creating the compositor + // texture. + renderer->resource_provider()->Flush(); + + // Make sure skia uses the correct GL context. + offscreen_contexts->Context3d()->makeContextCurrent(); + + // Wrap the source texture in a Ganesh platform texture. + GrBackendTextureDesc backend_texture_description; + backend_texture_description.fConfig = kSkia8888_GrPixelConfig; + backend_texture_description.fOrigin = kBottomLeft_GrSurfaceOrigin; + + backend_texture_description.fWidth = source_size.width(); + backend_texture_description.fHeight = source_size.height(); + backend_texture_description.fTextureHandle = source_texture_with_filters_id; + skia::RefPtr<GrTexture> source_texture = + skia::AdoptRef(offscreen_contexts->GrContext()->wrapBackendTexture( + backend_texture_description)); + + backend_texture_description.fWidth = background_size.width(); + backend_texture_description.fHeight = background_size.height(); + backend_texture_description.fTextureHandle = lock_background.texture_id(); + skia::RefPtr<GrTexture> background_texture = + skia::AdoptRef(offscreen_contexts->GrContext()->wrapBackendTexture( + backend_texture_description)); + + // Place the platform texture inside an SkBitmap. + SkBitmap source; + source.setConfig( + SkBitmap::kARGB_8888_Config, source_size.width(), source_size.height()); + skia::RefPtr<SkGrPixelRef> source_pixel_ref = + skia::AdoptRef(new SkGrPixelRef(source_texture.get())); + source.setPixelRef(source_pixel_ref.get()); + + SkBitmap background; + background.setConfig(SkBitmap::kARGB_8888_Config, + background_size.width(), + background_size.height()); + skia::RefPtr<SkGrPixelRef> background_pixel_ref = + skia::AdoptRef(new SkGrPixelRef(background_texture.get())); + background.setPixelRef(background_pixel_ref.get()); + + // Create a scratch texture for backing store. + GrTextureDesc desc; + desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; + desc.fSampleCnt = 0; + desc.fWidth = source.width(); + desc.fHeight = source.height(); + desc.fConfig = kSkia8888_GrPixelConfig; + desc.fOrigin = kBottomLeft_GrSurfaceOrigin; + GrAutoScratchTexture scratch_texture( + offscreen_contexts->GrContext(), desc, GrContext::kExact_ScratchTexMatch); + skia::RefPtr<GrTexture> backing_store = + skia::AdoptRef(scratch_texture.detach()); + + // Create a device and canvas using that backing store. + SkGpuDevice device(offscreen_contexts->GrContext(), backing_store.get()); + SkCanvas canvas(&device); + + // Draw the source bitmap through the filter to the canvas. + canvas.clear(SK_ColorTRANSPARENT); + canvas.drawSprite(background, 0, 0); + SkPaint paint; + paint.setXfermodeMode(blend_mode); + canvas.drawSprite(source, 0, 0, &paint); + + // Flush skia context so that all the rendered stuff appears on the + // texture. + offscreen_contexts->GrContext()->flush(); + + // Flush the GL context so rendering results from this context are + // visible in the compositor's context. + offscreen_contexts->Context3d()->flush(); + + // Use the compositor's GL context again. + renderer->Context()->makeContextCurrent(); + + return device.accessBitmap(false); +} + +scoped_ptr<ScopedResource> GLRenderer::GetBackgroundWithFilters( DrawingFrame* frame, const RenderPassDrawQuad* quad, const gfx::Transform& contents_device_transform, - const gfx::Transform& contents_device_transform_inverse) { + const gfx::Transform& contents_device_transform_inverse, + bool* background_changed) { // This method draws a background filter, which applies a filter to any pixels // behind the quad and seen through its background. The algorithm works as // follows: @@ -607,14 +725,14 @@ scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters( // TODO(danakj): We only allow background filters on an opaque render surface // because other surfaces may contain translucent pixels, and the contents // behind those translucent pixels wouldn't have the filter applied. - if (frame->current_render_pass->has_transparent_background) - return scoped_ptr<ScopedResource>(); + bool apply_background_filters = + !frame->current_render_pass->has_transparent_background; DCHECK(!frame->current_texture); // TODO(ajuma): Add support for reference filters once // FilterOperations::GetOutsets supports reference filters. - if (quad->background_filters.HasReferenceFilter()) - return scoped_ptr<ScopedResource>(); + if (apply_background_filters && quad->background_filters.HasReferenceFilter()) + apply_background_filters = false; // TODO(danakj): Do a single readback for both the surface and replica and // cache the filtered results (once filter textures are not reused). @@ -649,18 +767,28 @@ scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters( skia::RefPtr<SkImageFilter> filter = RenderSurfaceFilters::BuildImageFilter( quad->background_filters, device_background_texture->size()); - SkBitmap filtered_device_background = - ApplyImageFilter(this, - frame->offscreen_context_provider, - quad->rect.origin(), - filter.get(), - device_background_texture.get()); - if (!filtered_device_background.getTexture()) - return scoped_ptr<ScopedResource>(); + SkBitmap filtered_device_background; + if (apply_background_filters) { + filtered_device_background = + ApplyImageFilter(this, + frame->offscreen_context_provider, + quad->rect.origin(), + filter.get(), + device_background_texture.get()); + } + *background_changed = (filtered_device_background.getTexture() != NULL); - GrTexture* texture = - reinterpret_cast<GrTexture*>(filtered_device_background.getTexture()); - int filtered_device_background_texture_id = texture->getTextureHandle(); + int filtered_device_background_texture_id = 0; + scoped_ptr<ResourceProvider::ScopedReadLockGL> lock; + if (filtered_device_background.getTexture()) { + GrTexture* texture = + reinterpret_cast<GrTexture*>(filtered_device_background.getTexture()); + filtered_device_background_texture_id = texture->getTextureHandle(); + } else { + lock.reset(new ResourceProvider::ScopedReadLockGL( + resource_provider_, device_background_texture->id())); + filtered_device_background_texture_id = lock->texture_id(); + } scoped_ptr<ScopedResource> background_texture = ScopedResource::create(resource_provider_); @@ -731,19 +859,24 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, if (!contents_device_transform.GetInverse(&contents_device_transform_inverse)) return; + bool need_background_texture = + quad->shared_quad_state->blend_mode != SkXfermode::kSrcOver_Mode || + !quad->background_filters.IsEmpty(); + bool background_changed = false; scoped_ptr<ScopedResource> background_texture; - if (!quad->background_filters.IsEmpty()) { + if (need_background_texture) { // The pixels from the filtered background should completely replace the // current pixel values. bool disable_blending = blend_enabled(); if (disable_blending) SetBlendEnabled(false); - background_texture = DrawBackgroundFilters( - frame, - quad, - contents_device_transform, - contents_device_transform_inverse); + background_texture = + GetBackgroundWithFilters(frame, + quad, + contents_device_transform, + contents_device_transform_inverse, + &background_changed); if (disable_blending) SetBlendEnabled(true); @@ -782,8 +915,19 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, } } - // Draw the background texture if there is one. - if (background_texture) { + if (quad->shared_quad_state->blend_mode != SkXfermode::kSrcOver_Mode && + background_texture) { + filter_bitmap = + ApplyBlendModeWithBackdrop(this, + frame->offscreen_context_provider, + filter_bitmap, + contents_texture, + background_texture.get(), + quad->shared_quad_state->blend_mode); + } + + // Draw the background texture if it has some filters applied. + if (background_texture && background_changed) { DCHECK(background_texture->size() == quad->rect.size()); ResourceProvider::ScopedReadLockGL lock(resource_provider_, background_texture->id()); diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h index def9a25..d582769 100644 --- a/cc/output/gl_renderer.h +++ b/cc/output/gl_renderer.h @@ -151,11 +151,12 @@ class CC_EXPORT GLRenderer : public DirectRenderer { const CheckerboardDrawQuad* quad); void DrawDebugBorderQuad(const DrawingFrame* frame, const DebugBorderDrawQuad* quad); - scoped_ptr<ScopedResource> DrawBackgroundFilters( + scoped_ptr<ScopedResource> GetBackgroundWithFilters( DrawingFrame* frame, const RenderPassDrawQuad* quad, const gfx::Transform& contents_device_transform, - const gfx::Transform& contents_device_transformInverse); + const gfx::Transform& contents_device_transformInverse, + bool* background_changed); void DrawRenderPassQuad(DrawingFrame* frame, const RenderPassDrawQuad* quad); void DrawSolidColorQuad(const DrawingFrame* frame, const SolidColorDrawQuad* quad); diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc index 3de12ac..2bf5152 100644 --- a/cc/output/renderer_pixeltest.cc +++ b/cc/output/renderer_pixeltest.cc @@ -50,13 +50,15 @@ scoped_ptr<SharedQuadState> CreateTestSharedQuadState( const gfx::Rect clip_rect = rect; const bool is_clipped = false; const float opacity = 1.0f; + const SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode; scoped_ptr<SharedQuadState> shared_state = SharedQuadState::Create(); shared_state->SetAll(content_to_target_transform, content_bounds, visible_content_rect, clip_rect, is_clipped, - opacity); + opacity, + blend_mode); return shared_state.Pass(); } @@ -68,13 +70,15 @@ scoped_ptr<SharedQuadState> CreateTestSharedQuadStateClipped( const gfx::Rect visible_content_rect = clip_rect; const bool is_clipped = true; const float opacity = 1.0f; + const SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode; scoped_ptr<SharedQuadState> shared_state = SharedQuadState::Create(); shared_state->SetAll(content_to_target_transform, content_bounds, visible_content_rect, clip_rect, is_clipped, - opacity); + opacity, + blend_mode); return shared_state.Pass(); } diff --git a/cc/output/software_renderer_unittest.cc b/cc/output/software_renderer_unittest.cc index ef9f43f..33e311a 100644 --- a/cc/output/software_renderer_unittest.cc +++ b/cc/output/software_renderer_unittest.cc @@ -79,8 +79,13 @@ TEST_F(SoftwareRendererTest, SolidColorQuad) { InitializeRenderer(make_scoped_ptr(new SoftwareOutputDevice)); scoped_ptr<SharedQuadState> shared_quad_state = SharedQuadState::Create(); - shared_quad_state->SetAll( - gfx::Transform(), outer_size, outer_rect, outer_rect, false, 1.0); + shared_quad_state->SetAll(gfx::Transform(), + outer_size, + outer_rect, + outer_rect, + false, + 1.0, + SkXfermode::kSrcOver_Mode); RenderPass::Id root_render_pass_id = RenderPass::Id(1, 1); scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create(); root_render_pass->SetNew( @@ -162,8 +167,13 @@ TEST_F(SoftwareRendererTest, TileQuad) { gfx::Rect root_rect = DeviceViewport(); scoped_ptr<SharedQuadState> shared_quad_state = SharedQuadState::Create(); - shared_quad_state->SetAll( - gfx::Transform(), outer_size, outer_rect, outer_rect, false, 1.0); + shared_quad_state->SetAll(gfx::Transform(), + outer_size, + outer_rect, + outer_rect, + false, + 1.0, + SkXfermode::kSrcOver_Mode); RenderPass::Id root_render_pass_id = RenderPass::Id(1, 1); scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create(); root_render_pass->SetNew( @@ -241,8 +251,13 @@ TEST_F(SoftwareRendererTest, TileQuadVisibleRect) { gfx::Rect root_rect = DeviceViewport(); scoped_ptr<SharedQuadState> shared_quad_state = SharedQuadState::Create(); - shared_quad_state->SetAll( - gfx::Transform(), tile_size, tile_rect, tile_rect, false, 1.0); + shared_quad_state->SetAll(gfx::Transform(), + tile_size, + tile_rect, + tile_rect, + false, + 1.0, + SkXfermode::kSrcOver_Mode); RenderPass::Id root_render_pass_id = RenderPass::Id(1, 1); scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create(); root_render_pass->SetNew( diff --git a/cc/quads/draw_quad_unittest.cc b/cc/quads/draw_quad_unittest.cc index 8180898..1a99184 100644 --- a/cc/quads/draw_quad_unittest.cc +++ b/cc/quads/draw_quad_unittest.cc @@ -36,6 +36,7 @@ TEST(DrawQuadTest, CopySharedQuadState) { gfx::Rect clip_rect(19, 21, 23, 25); bool is_clipped = true; float opacity = 0.25f; + SkXfermode::Mode blend_mode = SkXfermode::kMultiply_Mode; scoped_ptr<SharedQuadState> state(SharedQuadState::Create()); state->SetAll(quad_transform, @@ -43,7 +44,8 @@ TEST(DrawQuadTest, CopySharedQuadState) { visible_content_rect, clip_rect, is_clipped, - opacity); + opacity, + blend_mode); scoped_ptr<SharedQuadState> copy(state->Copy()); EXPECT_EQ(quad_transform, copy->content_to_target_transform); @@ -51,6 +53,7 @@ TEST(DrawQuadTest, CopySharedQuadState) { EXPECT_EQ(opacity, copy->opacity); EXPECT_RECT_EQ(clip_rect, copy->clip_rect); EXPECT_EQ(is_clipped, copy->is_clipped); + EXPECT_EQ(blend_mode, copy->blend_mode); } scoped_ptr<SharedQuadState> CreateSharedQuadState() { @@ -60,6 +63,7 @@ scoped_ptr<SharedQuadState> CreateSharedQuadState() { gfx::Rect clip_rect(19, 21, 23, 25); bool is_clipped = false; float opacity = 1.f; + SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode; scoped_ptr<SharedQuadState> state(SharedQuadState::Create()); state->SetAll(quad_transform, @@ -67,7 +71,8 @@ scoped_ptr<SharedQuadState> CreateSharedQuadState() { visible_content_rect, clip_rect, is_clipped, - opacity); + opacity, + blend_mode); return state.Pass(); } diff --git a/cc/quads/render_pass_unittest.cc b/cc/quads/render_pass_unittest.cc index 394a3ec..e70a33dd 100644 --- a/cc/quads/render_pass_unittest.cc +++ b/cc/quads/render_pass_unittest.cc @@ -80,8 +80,13 @@ TEST(RenderPassTest, CopyShouldBeIdenticalExceptIdAndQuads) { // Stick a quad in the pass, this should not get copied. scoped_ptr<SharedQuadState> shared_state = SharedQuadState::Create(); - shared_state->SetAll( - gfx::Transform(), gfx::Size(), gfx::Rect(), gfx::Rect(), false, 1); + shared_state->SetAll(gfx::Transform(), + gfx::Size(), + gfx::Rect(), + gfx::Rect(), + false, + 1, + SkXfermode::kSrcOver_Mode); pass->AppendSharedQuadState(shared_state.Pass()); scoped_ptr<CheckerboardDrawQuad> checkerboard_quad = @@ -126,8 +131,13 @@ TEST(RenderPassTest, CopyAllShouldBeIdentical) { // Two quads using one shared state. scoped_ptr<SharedQuadState> shared_state1 = SharedQuadState::Create(); - shared_state1->SetAll( - gfx::Transform(), gfx::Size(1, 1), gfx::Rect(), gfx::Rect(), false, 1); + shared_state1->SetAll(gfx::Transform(), + gfx::Size(1, 1), + gfx::Rect(), + gfx::Rect(), + false, + 1, + SkXfermode::kSrcOver_Mode); pass->AppendSharedQuadState(shared_state1.Pass()); scoped_ptr<CheckerboardDrawQuad> checkerboard_quad1 = @@ -144,8 +154,13 @@ TEST(RenderPassTest, CopyAllShouldBeIdentical) { // And two quads using another shared state. scoped_ptr<SharedQuadState> shared_state2 = SharedQuadState::Create(); - shared_state2->SetAll( - gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), gfx::Rect(), false, 1); + shared_state2->SetAll(gfx::Transform(), + gfx::Size(2, 2), + gfx::Rect(), + gfx::Rect(), + false, + 1, + SkXfermode::kSrcOver_Mode); pass->AppendSharedQuadState(shared_state2.Pass()); scoped_ptr<CheckerboardDrawQuad> checkerboard_quad3 = @@ -176,8 +191,13 @@ TEST(RenderPassTest, CopyAllShouldBeIdentical) { contrib_has_transparent_background); scoped_ptr<SharedQuadState> contrib_shared_state = SharedQuadState::Create(); - contrib_shared_state->SetAll( - gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), gfx::Rect(), false, 1); + contrib_shared_state->SetAll(gfx::Transform(), + gfx::Size(2, 2), + gfx::Rect(), + gfx::Rect(), + false, + 1, + SkXfermode::kSrcOver_Mode); contrib->AppendSharedQuadState(contrib_shared_state.Pass()); scoped_ptr<CheckerboardDrawQuad> contrib_quad = @@ -228,8 +248,13 @@ TEST(RenderPassTest, CopyAllWithCulledQuads) { // A shared state with a quad. scoped_ptr<SharedQuadState> shared_state1 = SharedQuadState::Create(); - shared_state1->SetAll( - gfx::Transform(), gfx::Size(1, 1), gfx::Rect(), gfx::Rect(), false, 1); + shared_state1->SetAll(gfx::Transform(), + gfx::Size(1, 1), + gfx::Rect(), + gfx::Rect(), + false, + 1, + SkXfermode::kSrcOver_Mode); pass->AppendSharedQuadState(shared_state1.Pass()); scoped_ptr<CheckerboardDrawQuad> checkerboard_quad1 = @@ -240,20 +265,35 @@ TEST(RenderPassTest, CopyAllWithCulledQuads) { // A shared state with no quads, they were culled. scoped_ptr<SharedQuadState> shared_state2 = SharedQuadState::Create(); - shared_state2->SetAll( - gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), gfx::Rect(), false, 1); + shared_state2->SetAll(gfx::Transform(), + gfx::Size(2, 2), + gfx::Rect(), + gfx::Rect(), + false, + 1, + SkXfermode::kSrcOver_Mode); pass->AppendSharedQuadState(shared_state2.Pass()); // A second shared state with no quads. scoped_ptr<SharedQuadState> shared_state3 = SharedQuadState::Create(); - shared_state3->SetAll( - gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), gfx::Rect(), false, 1); + shared_state3->SetAll(gfx::Transform(), + gfx::Size(2, 2), + gfx::Rect(), + gfx::Rect(), + false, + 1, + SkXfermode::kSrcOver_Mode); pass->AppendSharedQuadState(shared_state3.Pass()); // A last shared state with a quad again. scoped_ptr<SharedQuadState> shared_state4 = SharedQuadState::Create(); - shared_state4->SetAll( - gfx::Transform(), gfx::Size(2, 2), gfx::Rect(), gfx::Rect(), false, 1); + shared_state4->SetAll(gfx::Transform(), + gfx::Size(2, 2), + gfx::Rect(), + gfx::Rect(), + false, + 1, + SkXfermode::kSrcOver_Mode); pass->AppendSharedQuadState(shared_state4.Pass()); scoped_ptr<CheckerboardDrawQuad> checkerboard_quad2 = diff --git a/cc/quads/shared_quad_state.cc b/cc/quads/shared_quad_state.cc index 6a53d9f..56584577 100644 --- a/cc/quads/shared_quad_state.cc +++ b/cc/quads/shared_quad_state.cc @@ -10,7 +10,8 @@ namespace cc { -SharedQuadState::SharedQuadState() : is_clipped(false), opacity(0.f) {} +SharedQuadState::SharedQuadState() + : is_clipped(false), opacity(0.f), blend_mode(SkXfermode::kSrcOver_Mode) {} SharedQuadState::~SharedQuadState() { TRACE_EVENT_OBJECT_DELETED_WITH_ID( @@ -26,19 +27,20 @@ scoped_ptr<SharedQuadState> SharedQuadState::Copy() const { return make_scoped_ptr(new SharedQuadState(*this)); } -void SharedQuadState::SetAll( - const gfx::Transform& content_to_target_transform, - gfx::Size content_bounds, - gfx::Rect visible_content_rect, - gfx::Rect clip_rect, - bool is_clipped, - float opacity) { +void SharedQuadState::SetAll(const gfx::Transform& content_to_target_transform, + gfx::Size content_bounds, + gfx::Rect visible_content_rect, + gfx::Rect clip_rect, + bool is_clipped, + float opacity, + SkXfermode::Mode blend_mode) { this->content_to_target_transform = content_to_target_transform; this->content_bounds = content_bounds; this->visible_content_rect = visible_content_rect; this->clip_rect = clip_rect; this->is_clipped = is_clipped; this->opacity = opacity; + this->blend_mode = blend_mode; } scoped_ptr<base::Value> SharedQuadState::AsValue() const { @@ -52,6 +54,7 @@ scoped_ptr<base::Value> SharedQuadState::AsValue() const { value->SetBoolean("is_clipped", is_clipped); value->Set("clip_rect", MathUtil::AsValue(clip_rect).release()); value->SetDouble("opacity", opacity); + value->SetString("blend_mode", SkXfermode::ModeName(blend_mode)); TracedValue::MakeDictIntoImplicitSnapshotWithCategory( TRACE_DISABLED_BY_DEFAULT("cc.debug.quads"), value.get(), "cc::SharedQuadState", this); diff --git a/cc/quads/shared_quad_state.h b/cc/quads/shared_quad_state.h index 79bd09b..b70d4a1 100644 --- a/cc/quads/shared_quad_state.h +++ b/cc/quads/shared_quad_state.h @@ -7,6 +7,7 @@ #include "base/memory/scoped_ptr.h" #include "cc/base/cc_export.h" +#include "third_party/skia/include/core/SkXfermode.h" #include "ui/gfx/rect.h" #include "ui/gfx/transform.h" @@ -28,7 +29,8 @@ class CC_EXPORT SharedQuadState { gfx::Rect visible_content_rect, gfx::Rect clip_rect, bool is_clipped, - float opacity); + float opacity, + SkXfermode::Mode blend_mode); scoped_ptr<base::Value> AsValue() const; // Transforms from quad's original content space to its target content space. @@ -41,6 +43,7 @@ class CC_EXPORT SharedQuadState { gfx::Rect clip_rect; bool is_clipped; float opacity; + SkXfermode::Mode blend_mode; private: SharedQuadState(); diff --git a/cc/test/data/blending_and_filter.png b/cc/test/data/blending_and_filter.png Binary files differnew file mode 100644 index 0000000..07e7fea --- /dev/null +++ b/cc/test/data/blending_and_filter.png diff --git a/cc/test/data/blending_transparent.png b/cc/test/data/blending_transparent.png Binary files differnew file mode 100644 index 0000000..dc20808 --- /dev/null +++ b/cc/test/data/blending_transparent.png diff --git a/cc/test/data/blending_with_root.png b/cc/test/data/blending_with_root.png Binary files differnew file mode 100644 index 0000000..519c642 --- /dev/null +++ b/cc/test/data/blending_with_root.png diff --git a/cc/test/render_pass_test_common.cc b/cc/test/render_pass_test_common.cc index 35bc561..4b69a7e 100644 --- a/cc/test/render_pass_test_common.cc +++ b/cc/test/render_pass_test_common.cc @@ -90,7 +90,8 @@ void TestRenderPass::AppendOneOfEveryQuadType( rect, rect, false, - 1); + 1, + SkXfermode::kSrcOver_Mode); scoped_ptr<cc::CheckerboardDrawQuad> checkerboard_quad = cc::CheckerboardDrawQuad::Create(); @@ -204,7 +205,13 @@ void TestRenderPass::AppendOneOfEveryQuadType( AppendQuad(transformed_tile_quad.PassAs<DrawQuad>()); scoped_ptr<cc::SharedQuadState> shared_state2 = cc::SharedQuadState::Create(); - shared_state->SetAll(gfx::Transform(), rect.size(), rect, rect, false, 1); + shared_state->SetAll(gfx::Transform(), + rect.size(), + rect, + rect, + false, + 1, + SkXfermode::kSrcOver_Mode); scoped_ptr<cc::TileDrawQuad> tile_quad = cc::TileDrawQuad::Create(); tile_quad->SetNew(shared_state2.get(), diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc index b2045d7..32e6f48 100644 --- a/cc/test/render_pass_test_utils.cc +++ b/cc/test/render_pass_test_utils.cc @@ -36,7 +36,13 @@ SolidColorDrawQuad* AddQuad(TestRenderPass* pass, AppendQuadsData data(pass->id); SharedQuadState* shared_state = quad_sink.UseSharedQuadState(SharedQuadState::Create()); - shared_state->SetAll(gfx::Transform(), rect.size(), rect, rect, false, 1); + shared_state->SetAll(gfx::Transform(), + rect.size(), + rect, + rect, + false, + 1, + SkXfermode::kSrcOver_Mode); scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); quad->SetNew(shared_state, rect, color, false); SolidColorDrawQuad* quad_ptr = quad.get(); @@ -51,7 +57,13 @@ SolidColorDrawQuad* AddClippedQuad(TestRenderPass* pass, AppendQuadsData data(pass->id); SharedQuadState* shared_state = quad_sink.UseSharedQuadState(SharedQuadState::Create()); - shared_state->SetAll(gfx::Transform(), rect.size(), rect, rect, true, 1); + shared_state->SetAll(gfx::Transform(), + rect.size(), + rect, + rect, + true, + 1, + SkXfermode::kSrcOver_Mode); scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); quad->SetNew(shared_state, rect, color, false); SolidColorDrawQuad* quad_ptr = quad.get(); @@ -67,7 +79,8 @@ SolidColorDrawQuad* AddTransformedQuad(TestRenderPass* pass, AppendQuadsData data(pass->id); SharedQuadState* shared_state = quad_sink.UseSharedQuadState(SharedQuadState::Create()); - shared_state->SetAll(transform, rect.size(), rect, rect, false, 1); + shared_state->SetAll( + transform, rect.size(), rect, rect, false, 1, SkXfermode::kSrcOver_Mode); scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); quad->SetNew(shared_state, rect, color, false); SolidColorDrawQuad* quad_ptr = quad.get(); @@ -83,8 +96,13 @@ void AddRenderPassQuad(TestRenderPass* to_pass, gfx::Rect output_rect = contributing_pass->output_rect; SharedQuadState* shared_state = quad_sink.UseSharedQuadState(SharedQuadState::Create()); - shared_state->SetAll( - gfx::Transform(), output_rect.size(), output_rect, output_rect, false, 1); + shared_state->SetAll(gfx::Transform(), + output_rect.size(), + output_rect, + output_rect, + false, + 1, + SkXfermode::kSrcOver_Mode); scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create(); quad->SetNew(shared_state, output_rect, contributing_pass->id, false, 0, output_rect, gfx::RectF(), FilterOperations(), @@ -103,8 +121,13 @@ void AddRenderPassQuad(TestRenderPass* to_pass, gfx::Rect output_rect = contributing_pass->output_rect; SharedQuadState* shared_state = quad_sink.UseSharedQuadState(SharedQuadState::Create()); - shared_state->SetAll( - transform, output_rect.size(), output_rect, output_rect, false, 1); + shared_state->SetAll(transform, + output_rect.size(), + output_rect, + output_rect, + false, + 1, + SkXfermode::kSrcOver_Mode); scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create(); quad->SetNew(shared_state, output_rect, contributing_pass->id, false, mask_resource_id, output_rect, gfx::RectF(), filters, diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc index 784b3db..453a6f3 100644 --- a/cc/trees/layer_tree_host_common.cc +++ b/cc/trees/layer_tree_host_common.cc @@ -587,6 +587,19 @@ static bool SubtreeShouldRenderToSeparateSurface( return true; } + // If the layer has blending. + // TODO(rosca): this is temporary, until blending is implemented for other + // types of quads than RenderPassDrawQuad. Layers having descendants that draw + // content will still create a separate rendering surface. + if (!layer->uses_default_blend_mode()) { + TRACE_EVENT_INSTANT0( + "cc", + "LayerTreeHostCommon::SubtreeShouldRenderToSeparateSurface blending", + TRACE_EVENT_SCOPE_THREAD); + DCHECK(!is_root); + return true; + } + // If the layer clips its descendants but it is not axis-aligned with respect // to its parent. bool layer_clips_external_content = @@ -629,6 +642,19 @@ static bool SubtreeShouldRenderToSeparateSurface( // be used as a contributing surface in order to apply correctly. // + // If the layer has isolation. + // TODO(rosca): to be optimized - create separate rendering surface only when + // the blending descendants might have access to the content behind this layer + // (layer has transparent background or descendants overflow). + // https://code.google.com/p/chromium/issues/detail?id=301738 + if (layer->is_root_for_isolated_group()) { + TRACE_EVENT_INSTANT0( + "cc", + "LayerTreeHostCommon::SubtreeShouldRenderToSeparateSurface isolation", + TRACE_EVENT_SCOPE_THREAD); + return true; + } + // If we force it. if (layer->force_render_surface()) return true; @@ -2034,6 +2060,16 @@ static void CalculateDrawPropertiesInternal( return; } + // Layers having a non-default blend mode will blend with the content + // inside its parent's render target. This render target should be + // either root_for_isolated_group, or the root of the layer tree. + // Otherwise, this layer will use an incomplete backdrop, limited to its + // render target and the blending result will be incorrect. + DCHECK(layer->uses_default_blend_mode() || IsRootLayer(layer) || + !layer->parent()->render_target() || + IsRootLayer(layer->parent()->render_target()) || + layer->parent()->render_target()->is_root_for_isolated_group()); + render_surface->SetContentRect(clipped_content_rect); // The owning layer's screen_space_transform has a scale from content to diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 9485c72..8a01b80 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -626,7 +626,8 @@ static void AppendQuadsToFillScreen( root_target_rect, root_target_rect, false, - opacity); + opacity, + SkXfermode::kSrcOver_Mode); AppendQuadsData append_quads_data; diff --git a/cc/trees/layer_tree_host_pixeltest_blending.cc b/cc/trees/layer_tree_host_pixeltest_blending.cc new file mode 100644 index 0000000..7a492a1 --- /dev/null +++ b/cc/trees/layer_tree_host_pixeltest_blending.cc @@ -0,0 +1,120 @@ +// 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 "cc/layers/solid_color_layer.h" +#include "cc/layers/texture_layer.h" +#include "cc/test/layer_tree_pixel_test.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}; + +const int kBlendModesCount = arraysize(kBlendModes); + +class LayerTreeHostBlendingPixelTest : public LayerTreePixelTest { + protected: + void RunBlendingWithRootPixelTestType(PixelTestType type) { + const int kLaneWidth = 15; + const int kLaneHeight = kBlendModesCount * kLaneWidth; + const int kRootSize = (kBlendModesCount + 2) * kLaneWidth; + + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(kRootSize, kRootSize), 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]); + } + + RunPixelTest(type, + background, + base::FilePath(FILE_PATH_LITERAL("blending_with_root.png"))); + } + + void RunBlendingWithTransparentPixelTestType(PixelTestType type) { + const int kLaneWidth = 15; + const int kLaneHeight = kBlendModesCount * kLaneWidth; + const int kRootSize = (kBlendModesCount + 2) * kLaneWidth; + + scoped_refptr<SolidColorLayer> root = + CreateSolidColorLayer(gfx::Rect(kRootSize, kRootSize), kCSSBrown); + + scoped_refptr<SolidColorLayer> background = CreateSolidColorLayer( + gfx::Rect(0, kLaneWidth * 2, kRootSize, 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]); + } + + RunPixelTest(type, + root, + base::FilePath(FILE_PATH_LITERAL("blending_transparent.png"))); + } +}; + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRoot_GL) { + RunBlendingWithRootPixelTestType(GL_WITH_BITMAP); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithBackgroundFilter) { + const int kLaneWidth = 15; + const int kLaneHeight = kBlendModesCount * kLaneWidth; + const int kRootSize = (kBlendModesCount + 2) * kLaneWidth; + + scoped_refptr<SolidColorLayer> background = + CreateSolidColorLayer(gfx::Rect(kRootSize, kRootSize), 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]); + } + + RunPixelTest(GL_WITH_BITMAP, + background, + base::FilePath(FILE_PATH_LITERAL("blending_and_filter.png"))); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithTransparent_GL) { + RunBlendingWithTransparentPixelTestType(GL_WITH_BITMAP); +} + +} // namespace +} // namespace cc + +#endif // OS_ANDROID diff --git a/cc/trees/layer_tree_host_unittest_occlusion.cc b/cc/trees/layer_tree_host_unittest_occlusion.cc index 9300f30..621d23d 100644 --- a/cc/trees/layer_tree_host_unittest_occlusion.cc +++ b/cc/trees/layer_tree_host_unittest_occlusion.cc @@ -346,6 +346,74 @@ class LayerTreeHostOcclusionTestOcclusionOpacityBelowOcclusion SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostOcclusionTestOcclusionOpacityBelowOcclusion); +class LayerTreeHostOcclusionTestOcclusionBlending + : public LayerTreeHostOcclusionTest { + public: + virtual void SetupTree() OVERRIDE { + // If the child layer has a blend mode, then it shouldn't + // contribute to occlusion on stuff below it + SetLayerPropertiesForTesting( + root_.get(), NULL, identity_matrix_, + gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); + SetLayerPropertiesForTesting( + child2_.get(), root_.get(), identity_matrix_, + gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); + SetLayerPropertiesForTesting( + child_.get(), root_.get(), identity_matrix_, + gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); + SetLayerPropertiesForTesting( + grand_child_.get(), child_.get(), identity_matrix_, + gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); + + child_->SetMasksToBounds(true); + child_->SetBlendMode(SkXfermode::kMultiply_Mode); + child_->SetForceRenderSurface(true); + + child_->set_expected_occlusion(gfx::Rect(0, 0, 10, 190)); + root_->set_expected_occlusion(gfx::Rect(20, 10, 10, 190)); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeTest::SetupTree(); + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F(LayerTreeHostOcclusionTestOcclusionBlending); + +class LayerTreeHostOcclusionTestOcclusionBlendingBelowOcclusion + : public LayerTreeHostOcclusionTest { + public: + virtual void SetupTree() OVERRIDE { + // If the child layer with a blend mode is below child2, then + // child2 should contribute to occlusion on everything, and child shouldn't + // contribute to the root_. + SetLayerPropertiesForTesting( + root_.get(), NULL, identity_matrix_, + gfx::PointF(0.f, 0.f), gfx::Size(200, 200), true); + SetLayerPropertiesForTesting( + child_.get(), root_.get(), identity_matrix_, + gfx::PointF(10.f, 10.f), gfx::Size(500, 500), true); + SetLayerPropertiesForTesting( + grand_child_.get(), child_.get(), identity_matrix_, + gfx::PointF(-10.f, -10.f), gfx::Size(20, 500), true); + SetLayerPropertiesForTesting( + child2_.get(), root_.get(), identity_matrix_, + gfx::PointF(20.f, 10.f), gfx::Size(10, 500), true); + + child_->SetMasksToBounds(true); + child_->SetBlendMode(SkXfermode::kMultiply_Mode); + + grand_child_->set_expected_occlusion(gfx::Rect(10, 0, 10, 190)); + child_->set_expected_occlusion(gfx::Rect(0, 0, 20, 190)); + root_->set_expected_occlusion(gfx::Rect(20, 10, 10, 190)); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeTest::SetupTree(); + } +}; + +SINGLE_AND_MULTI_THREAD_TEST_F( + LayerTreeHostOcclusionTestOcclusionBlendingBelowOcclusion); + class LayerTreeHostOcclusionTestOcclusionOpacityFilter : public LayerTreeHostOcclusionTest { public: diff --git a/cc/trees/occlusion_tracker.cc b/cc/trees/occlusion_tracker.cc index 09fc114..1c6c8a0 100644 --- a/cc/trees/occlusion_tracker.cc +++ b/cc/trees/occlusion_tracker.cc @@ -224,9 +224,9 @@ void OcclusionTrackerBase<LayerType, RenderSurfaceType>::FinishedRenderTarget( // If the occlusion within the surface can not be applied to things outside of // the surface's subtree, then clear the occlusion here so it won't be used. - if (finished_target->mask_layer() || - !SurfaceOpacityKnown(surface) || + if (finished_target->mask_layer() || !SurfaceOpacityKnown(surface) || surface->draw_opacity() < 1 || + !finished_target->uses_default_blend_mode() || target_is_only_for_copy_request || finished_target->filters().HasFilterThatAffectsOpacity()) { stack_.back().occlusion_from_outside_target.Clear(); @@ -405,6 +405,9 @@ void OcclusionTrackerBase<LayerType, RenderSurfaceType>:: if (!LayerOpacityKnown(layer) || layer->draw_opacity() < 1) return; + if (!layer->uses_default_blend_mode()) + return; + if (LayerIsInUnsorted3dRenderingContext(layer)) return; |