diff --git a/cc/BUILD.gn b/cc/BUILD.gn index a96618448240d4..e0d651336474b2 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn @@ -105,6 +105,10 @@ cc_component("cc") { "layers/nine_patch_layer_impl.cc", "layers/nine_patch_layer_impl.h", "layers/paint_properties.h", + "layers/painted_overlay_scrollbar_layer.cc", + "layers/painted_overlay_scrollbar_layer.h", + "layers/painted_overlay_scrollbar_layer_impl.cc", + "layers/painted_overlay_scrollbar_layer_impl.h", "layers/painted_scrollbar_layer.cc", "layers/painted_scrollbar_layer.h", "layers/painted_scrollbar_layer_impl.cc", @@ -282,6 +286,8 @@ cc_component("cc") { "quads/draw_quad.h", "quads/largest_draw_quad.cc", "quads/largest_draw_quad.h", + "quads/nine_patch_generator.cc", + "quads/nine_patch_generator.h", "quads/picture_draw_quad.cc", "quads/picture_draw_quad.h", "quads/render_pass.cc", @@ -807,6 +813,7 @@ cc_test("cc_unittests") { "playback/recording_source_unittest.cc", "quads/draw_polygon_unittest.cc", "quads/draw_quad_unittest.cc", + "quads/nine_patch_generator_unittest.cc", "quads/render_pass_unittest.cc", "raster/raster_buffer_provider_unittest.cc", "raster/scoped_gpu_raster_unittest.cc", diff --git a/cc/blink/scrollbar_impl.cc b/cc/blink/scrollbar_impl.cc index 761586b1163474..4ba11e33ee2343 100644 --- a/cc/blink/scrollbar_impl.cc +++ b/cc/blink/scrollbar_impl.cc @@ -73,6 +73,18 @@ bool ScrollbarImpl::NeedsPaintPart(cc::ScrollbarPart part) const { return painter_.trackNeedsRepaint(); } +bool ScrollbarImpl::UsesNinePatchThumbResource() const { + return painter_.usesNinePatchThumbResource(); +} + +gfx::Size ScrollbarImpl::NinePatchThumbCanvasSize() const { + return geometry_->ninePatchThumbCanvasSize(scrollbar_.get()); +} + +gfx::Rect ScrollbarImpl::NinePatchThumbAperture() const { + return geometry_->ninePatchThumbAperture(scrollbar_.get()); +} + void ScrollbarImpl::PaintPart(cc::PaintCanvas* canvas, cc::ScrollbarPart part, const gfx::Rect& content_rect) { diff --git a/cc/blink/scrollbar_impl.h b/cc/blink/scrollbar_impl.h index d5c3052c40728f..c0534d5b5e7f6a 100644 --- a/cc/blink/scrollbar_impl.h +++ b/cc/blink/scrollbar_impl.h @@ -41,6 +41,10 @@ class ScrollbarImpl : public cc::Scrollbar { cc::ScrollbarPart part, const gfx::Rect& content_rect) override; + bool UsesNinePatchThumbResource() const override; + gfx::Size NinePatchThumbCanvasSize() const override; + gfx::Rect NinePatchThumbAperture() const override; + private: std::unique_ptr scrollbar_; blink::WebScrollbarThemePainter painter_; diff --git a/cc/blink/web_compositor_support_impl.cc b/cc/blink/web_compositor_support_impl.cc index ac2b81589d6875..7bb783e571639f 100644 --- a/cc/blink/web_compositor_support_impl.cc +++ b/cc/blink/web_compositor_support_impl.cc @@ -70,6 +70,15 @@ WebCompositorSupportImpl::createScrollbarLayer( std::move(geometry)); } +std::unique_ptr +WebCompositorSupportImpl::createOverlayScrollbarLayer( + std::unique_ptr scrollbar, + WebScrollbarThemePainter painter, + std::unique_ptr geometry) { + return base::MakeUnique(std::move(scrollbar), painter, + std::move(geometry), true); +} + std::unique_ptr WebCompositorSupportImpl::createSolidColorScrollbarLayer( WebScrollbar::Orientation orientation, diff --git a/cc/blink/web_compositor_support_impl.h b/cc/blink/web_compositor_support_impl.h index 91a205a12a3005..a303b273122f18 100644 --- a/cc/blink/web_compositor_support_impl.h +++ b/cc/blink/web_compositor_support_impl.h @@ -31,6 +31,10 @@ class CC_BLINK_EXPORT WebCompositorSupportImpl std::unique_ptr scrollbar, blink::WebScrollbarThemePainter painter, std::unique_ptr) override; + std::unique_ptr createOverlayScrollbarLayer( + std::unique_ptr scrollbar, + blink::WebScrollbarThemePainter painter, + std::unique_ptr) override; std::unique_ptr createSolidColorScrollbarLayer( blink::WebScrollbar::Orientation orientation, int thumb_thickness, diff --git a/cc/blink/web_scrollbar_layer_impl.cc b/cc/blink/web_scrollbar_layer_impl.cc index b9c6df38b6c1db..b6a759c51471a7 100644 --- a/cc/blink/web_scrollbar_layer_impl.cc +++ b/cc/blink/web_scrollbar_layer_impl.cc @@ -10,10 +10,12 @@ #include "cc/blink/scrollbar_impl.h" #include "cc/blink/web_layer_impl.h" #include "cc/layers/layer.h" +#include "cc/layers/painted_overlay_scrollbar_layer.h" #include "cc/layers/painted_scrollbar_layer.h" #include "cc/layers/scrollbar_layer_interface.h" #include "cc/layers/solid_color_scrollbar_layer.h" +using cc::PaintedOverlayScrollbarLayer; using cc::PaintedScrollbarLayer; using cc::SolidColorScrollbarLayer; @@ -39,6 +41,17 @@ WebScrollbarLayerImpl::WebScrollbarLayerImpl( std::move(geometry)), 0))) {} +WebScrollbarLayerImpl::WebScrollbarLayerImpl( + std::unique_ptr scrollbar, + blink::WebScrollbarThemePainter painter, + std::unique_ptr geometry, + bool) + : layer_(new WebLayerImpl(PaintedOverlayScrollbarLayer::Create( + base::MakeUnique(std::move(scrollbar), + painter, + std::move(geometry)), + 0))) {} + WebScrollbarLayerImpl::WebScrollbarLayerImpl( blink::WebScrollbar::Orientation orientation, int thumb_thickness, diff --git a/cc/blink/web_scrollbar_layer_impl.h b/cc/blink/web_scrollbar_layer_impl.h index 3fd192efe843a2..ad203e9a9563e1 100644 --- a/cc/blink/web_scrollbar_layer_impl.h +++ b/cc/blink/web_scrollbar_layer_impl.h @@ -27,6 +27,11 @@ class WebScrollbarLayerImpl : public blink::WebScrollbarLayer { std::unique_ptr scrollbar, blink::WebScrollbarThemePainter painter, std::unique_ptr geometry); + CC_BLINK_EXPORT WebScrollbarLayerImpl( + std::unique_ptr scrollbar, + blink::WebScrollbarThemePainter painter, + std::unique_ptr geometry, + bool temp); CC_BLINK_EXPORT WebScrollbarLayerImpl( blink::WebScrollbar::Orientation orientation, int thumb_thickness, diff --git a/cc/input/scrollbar.h b/cc/input/scrollbar.h index 69fbd3262bcb83..d159dba7b17080 100644 --- a/cc/input/scrollbar.h +++ b/cc/input/scrollbar.h @@ -35,6 +35,9 @@ class Scrollbar { virtual void PaintPart(PaintCanvas* canvas, ScrollbarPart part, const gfx::Rect& content_rect) = 0; + virtual bool UsesNinePatchThumbResource() const = 0; + virtual gfx::Size NinePatchThumbCanvasSize() const = 0; + virtual gfx::Rect NinePatchThumbAperture() const = 0; }; } // namespace cc diff --git a/cc/layers/nine_patch_layer_impl.cc b/cc/layers/nine_patch_layer_impl.cc index 436905a448107f..c79a76dfb42a8e 100644 --- a/cc/layers/nine_patch_layer_impl.cc +++ b/cc/layers/nine_patch_layer_impl.cc @@ -10,30 +10,13 @@ #include "cc/quads/texture_draw_quad.h" #include "cc/trees/layer_tree_impl.h" #include "cc/trees/occlusion.h" +#include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/rect_f.h" namespace cc { -// Maximum number of patches that can be produced for one NinePatchLayer. -static const int kMaxOcclusionPatches = 12; -static const int kMaxPatches = 9; - -namespace { - -gfx::Rect ToRect(const gfx::RectF& rect) { - return gfx::Rect(rect.x(), rect.y(), rect.width(), rect.height()); -} - -} // namespace - -NinePatchLayerImpl::Patch::Patch(const gfx::RectF& image_rect, - const gfx::RectF& layer_rect) - : image_rect(image_rect), layer_rect(layer_rect) {} - NinePatchLayerImpl::NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id) - : UIResourceLayerImpl(tree_impl, id), - fill_center_(false), - nearest_neighbor_(false) {} + : UIResourceLayerImpl(tree_impl, id) {} NinePatchLayerImpl::~NinePatchLayerImpl() {} @@ -46,19 +29,7 @@ void NinePatchLayerImpl::PushPropertiesTo(LayerImpl* layer) { UIResourceLayerImpl::PushPropertiesTo(layer); NinePatchLayerImpl* layer_impl = static_cast(layer); - layer_impl->SetLayout(image_aperture_, border_, layer_occlusion_, - fill_center_, nearest_neighbor_); -} - -static gfx::RectF BoundsToRect(int x1, int y1, int x2, int y2) { - return gfx::RectF(x1, y1, x2 - x1, y2 - y1); -} - -static gfx::RectF NormalizedRect(const gfx::RectF& rect, - float total_width, - float total_height) { - return gfx::RectF(rect.x() / total_width, rect.y() / total_height, - rect.width() / total_width, rect.height() / total_height); + layer_impl->quad_generator_ = this->quad_generator_; } void NinePatchLayerImpl::SetLayout(const gfx::Rect& aperture, @@ -70,240 +41,18 @@ void NinePatchLayerImpl::SetLayout(const gfx::Rect& aperture, // exist before SetLayout can be called. DCHECK(ui_resource_id_); - if (image_aperture_ == aperture && border_ == border && - fill_center_ == fill_center && nearest_neighbor_ == nearest_neighbor && - layer_occlusion_ == layer_occlusion) + if (!quad_generator_.SetLayout(image_bounds_, bounds(), aperture, border, + layer_occlusion, fill_center, + nearest_neighbor)) return; - image_aperture_ = aperture; - border_ = border; - fill_center_ = fill_center; - nearest_neighbor_ = nearest_neighbor; - layer_occlusion_ = layer_occlusion; - NoteLayerPropertyChanged(); } -void NinePatchLayerImpl::CheckGeometryLimitations() { - // |border| is in layer space. It cannot exceed the bounds of the layer. - DCHECK_GE(bounds().width(), border_.width()); - DCHECK_GE(bounds().height(), border_.height()); - - // Sanity Check on |border| - DCHECK_LE(border_.x(), border_.width()); - DCHECK_LE(border_.y(), border_.height()); - DCHECK_GE(border_.x(), 0); - DCHECK_GE(border_.y(), 0); - - // |aperture| is in image space. It cannot exceed the bounds of the bitmap. - DCHECK(!image_aperture_.size().IsEmpty()); - DCHECK(gfx::Rect(image_bounds_).Contains(image_aperture_)) - << "image_bounds_ " << gfx::Rect(image_bounds_).ToString() - << " image_aperture_ " << image_aperture_.ToString(); - - // Sanity check on |layer_occlusion_|. It should always be within the - // border. - gfx::Rect border_rect(border_.x(), border_.y(), - bounds().width() - border_.width(), - bounds().height() - border_.height()); - DCHECK(layer_occlusion_.IsEmpty() || layer_occlusion_.Contains(border_rect)) - << "border_rect " << border_rect.ToString() << " layer_occlusion_ " - << layer_occlusion_.ToString(); -} - -std::vector -NinePatchLayerImpl::ComputeQuadsWithoutOcclusion() const { - float image_width = image_bounds_.width(); - float image_height = image_bounds_.height(); - float layer_width = bounds().width(); - float layer_height = bounds().height(); - gfx::RectF layer_aperture(border_.x(), border_.y(), - layer_width - border_.width(), - layer_height - border_.height()); - - std::vector patches; - patches.reserve(kMaxPatches); - - // Top-left. - patches.push_back( - Patch(BoundsToRect(0, 0, image_aperture_.x(), image_aperture_.y()), - BoundsToRect(0, 0, layer_aperture.x(), layer_aperture.y()))); - - // Top-right. - patches.push_back(Patch(BoundsToRect(image_aperture_.right(), 0, image_width, - image_aperture_.y()), - BoundsToRect(layer_aperture.right(), 0, layer_width, - layer_aperture.y()))); - - // Bottom-left. - patches.push_back(Patch(BoundsToRect(0, image_aperture_.bottom(), - image_aperture_.x(), image_height), - BoundsToRect(0, layer_aperture.bottom(), - layer_aperture.x(), layer_height))); - - // Bottom-right. - patches.push_back( - Patch(BoundsToRect(image_aperture_.right(), image_aperture_.bottom(), - image_width, image_height), - BoundsToRect(layer_aperture.right(), layer_aperture.bottom(), - layer_width, layer_height))); - - // Top. - patches.push_back( - Patch(BoundsToRect(image_aperture_.x(), 0, image_aperture_.right(), - image_aperture_.y()), - BoundsToRect(layer_aperture.x(), 0, layer_aperture.right(), - layer_aperture.y()))); - - // Left. - patches.push_back( - Patch(BoundsToRect(0, image_aperture_.y(), image_aperture_.x(), - image_aperture_.bottom()), - BoundsToRect(0, layer_aperture.y(), layer_aperture.x(), - layer_aperture.bottom()))); - - // Right. - patches.push_back( - Patch(BoundsToRect(image_aperture_.right(), image_aperture_.y(), - image_width, image_aperture_.bottom()), - BoundsToRect(layer_aperture.right(), layer_aperture.y(), - layer_width, layer_aperture.bottom()))); - - // Bottom. - patches.push_back( - Patch(BoundsToRect(image_aperture_.x(), image_aperture_.bottom(), - image_aperture_.right(), image_height), - BoundsToRect(layer_aperture.x(), layer_aperture.bottom(), - layer_aperture.right(), layer_height))); - - // Center. - if (fill_center_) { - patches.push_back( - Patch(BoundsToRect(image_aperture_.x(), image_aperture_.y(), - image_aperture_.right(), image_aperture_.bottom()), - BoundsToRect(layer_aperture.x(), layer_aperture.y(), - layer_aperture.right(), layer_aperture.bottom()))); - } - - return patches; -} - -std::vector -NinePatchLayerImpl::ComputeQuadsWithOcclusion() const { - float image_width = image_bounds_.width(), - image_height = image_bounds_.height(); - float layer_width = bounds().width(), layer_height = bounds().height(); - float layer_border_right = border_.width() - border_.x(), - layer_border_bottom = border_.height() - border_.y(); - float image_aperture_right = image_width - image_aperture_.right(), - image_aperture_bottom = image_height - image_aperture_.bottom(); - float layer_occlusion_right = layer_width - layer_occlusion_.right(), - layer_occlusion_bottom = layer_height - layer_occlusion_.bottom(); - gfx::RectF image_occlusion(BoundsToRect( - border_.x() == 0 ? 0 : (layer_occlusion_.x() * image_aperture_.x() / - border_.x()), - border_.y() == 0 ? 0 : (layer_occlusion_.y() * image_aperture_.y() / - border_.y()), - image_width - (layer_border_right == 0 ? 0 : layer_occlusion_right * - image_aperture_right / - layer_border_right), - image_height - (layer_border_bottom == 0 ? 0 : layer_occlusion_bottom * - image_aperture_bottom / - layer_border_bottom))); - gfx::RectF layer_aperture(border_.x(), border_.y(), - layer_width - border_.width(), - layer_height - border_.height()); - - std::vector patches; - patches.reserve(kMaxOcclusionPatches); - - // Top-left-left. - patches.push_back( - Patch(BoundsToRect(0, 0, image_occlusion.x(), image_aperture_.y()), - BoundsToRect(0, 0, layer_occlusion_.x(), layer_aperture.y()))); - - // Top-left-right. - patches.push_back( - Patch(BoundsToRect(image_occlusion.x(), 0, image_aperture_.x(), - image_occlusion.y()), - BoundsToRect(layer_occlusion_.x(), 0, layer_aperture.x(), - layer_occlusion_.y()))); - - // Top-center. - patches.push_back( - Patch(BoundsToRect(image_aperture_.x(), 0, image_aperture_.right(), - image_occlusion.y()), - BoundsToRect(layer_aperture.x(), 0, layer_aperture.right(), - layer_occlusion_.y()))); - - // Top-right-left. - patches.push_back( - Patch(BoundsToRect(image_aperture_.right(), 0, image_occlusion.right(), - image_occlusion.y()), - BoundsToRect(layer_aperture.right(), 0, layer_occlusion_.right(), - layer_occlusion_.y()))); - - // Top-right-right. - patches.push_back(Patch(BoundsToRect(image_occlusion.right(), 0, image_width, - image_aperture_.y()), - BoundsToRect(layer_occlusion_.right(), 0, layer_width, - layer_aperture.y()))); - - // Left-center. - patches.push_back( - Patch(BoundsToRect(0, image_aperture_.y(), image_occlusion.x(), - image_aperture_.bottom()), - BoundsToRect(0, layer_aperture.y(), layer_occlusion_.x(), - layer_aperture.bottom()))); - - // Right-center. - patches.push_back( - Patch(BoundsToRect(image_occlusion.right(), image_aperture_.y(), - image_width, image_aperture_.bottom()), - BoundsToRect(layer_occlusion_.right(), layer_aperture.y(), - layer_width, layer_aperture.bottom()))); - - // Bottom-left-left. - patches.push_back(Patch(BoundsToRect(0, image_aperture_.bottom(), - image_occlusion.x(), image_height), - BoundsToRect(0, layer_aperture.bottom(), - layer_occlusion_.x(), layer_height))); - - // Bottom-left-right. - patches.push_back( - Patch(BoundsToRect(image_occlusion.x(), image_occlusion.bottom(), - image_aperture_.x(), image_height), - BoundsToRect(layer_occlusion_.x(), layer_occlusion_.bottom(), - layer_aperture.x(), layer_height))); - - // Bottom-center. - patches.push_back( - Patch(BoundsToRect(image_aperture_.x(), image_occlusion.bottom(), - image_aperture_.right(), image_height), - BoundsToRect(layer_aperture.x(), layer_occlusion_.bottom(), - layer_aperture.right(), layer_height))); - - // Bottom-right-left. - patches.push_back( - Patch(BoundsToRect(image_aperture_.right(), image_occlusion.bottom(), - image_occlusion.right(), image_height), - BoundsToRect(layer_aperture.right(), layer_occlusion_.bottom(), - layer_occlusion_.right(), layer_height))); - - // Bottom-right-right. - patches.push_back( - Patch(BoundsToRect(image_occlusion.right(), image_aperture_.bottom(), - image_width, image_height), - BoundsToRect(layer_occlusion_.right(), layer_aperture.bottom(), - layer_width, layer_height))); - - return patches; -} - void NinePatchLayerImpl::AppendQuads( RenderPass* render_pass, AppendQuadsData* append_quads_data) { - CheckGeometryLimitations(); + quad_generator_.CheckGeometryLimitations(); SharedQuadState* shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); PopulateSharedQuadState(shared_quad_state); @@ -311,47 +60,17 @@ void NinePatchLayerImpl::AppendQuads( AppendDebugBorderQuad(render_pass, bounds(), shared_quad_state, append_quads_data); - if (!ui_resource_id_) - return; - - ResourceId resource = - layer_tree_impl()->ResourceIdForUIResource(ui_resource_id_); - - if (!resource) - return; - DCHECK(!bounds().IsEmpty()); - std::vector patches; + std::vector patches = + quad_generator_.GeneratePatches(); - if (layer_occlusion_.IsEmpty() || fill_center_) - patches = ComputeQuadsWithoutOcclusion(); - else - patches = ComputeQuadsWithOcclusion(); + for (auto& patch : patches) + patch.output_rect = + gfx::RectF(gfx::ToFlooredRectDeprecated(patch.output_rect)); - const float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; - const bool opaque = layer_tree_impl()->IsUIResourceOpaque(ui_resource_id_); - static const bool flipped = false; - static const bool premultiplied_alpha = true; - - for (const auto& patch : patches) { - gfx::Rect visible_rect = - draw_properties().occlusion_in_content_space.GetUnoccludedContentRect( - ToRect(patch.layer_rect)); - gfx::Rect opaque_rect = opaque ? visible_rect : gfx::Rect(); - if (!visible_rect.IsEmpty()) { - gfx::RectF image_rect(NormalizedRect( - patch.image_rect, image_bounds_.width(), image_bounds_.height())); - TextureDrawQuad* quad = - render_pass->CreateAndAppendDrawQuad(); - quad->SetNew(shared_quad_state, ToRect(patch.layer_rect), opaque_rect, - visible_rect, resource, premultiplied_alpha, - image_rect.origin(), image_rect.bottom_right(), - SK_ColorTRANSPARENT, vertex_opacity, flipped, - nearest_neighbor_, false); - ValidateQuadResources(quad); - } - } + quad_generator_.AppendQuads(this, ui_resource_id_, render_pass, + shared_quad_state, patches); } const char* NinePatchLayerImpl::LayerTypeAsString() const { @@ -360,30 +79,7 @@ const char* NinePatchLayerImpl::LayerTypeAsString() const { std::unique_ptr NinePatchLayerImpl::LayerTreeAsJson() { std::unique_ptr result = LayerImpl::LayerTreeAsJson(); - - base::ListValue* list = new base::ListValue; - list->AppendInteger(image_aperture_.origin().x()); - list->AppendInteger(image_aperture_.origin().y()); - list->AppendInteger(image_aperture_.size().width()); - list->AppendInteger(image_aperture_.size().height()); - result->Set("ImageAperture", list); - - list = new base::ListValue; - list->AppendInteger(image_bounds_.width()); - list->AppendInteger(image_bounds_.height()); - result->Set("ImageBounds", list); - - result->Set("Border", MathUtil::AsValue(border_).release()); - - result->SetBoolean("FillCenter", fill_center_); - - list = new base::ListValue; - list->AppendInteger(layer_occlusion_.x()); - list->AppendInteger(layer_occlusion_.y()); - list->AppendInteger(layer_occlusion_.width()); - list->AppendInteger(layer_occlusion_.height()); - result->Set("LayerOcclusion", list); - + quad_generator_.AsJson(result.get()); return result; } diff --git a/cc/layers/nine_patch_layer_impl.h b/cc/layers/nine_patch_layer_impl.h index c7a1c2e8b28010..69b039a54e7bbf 100644 --- a/cc/layers/nine_patch_layer_impl.h +++ b/cc/layers/nine_patch_layer_impl.h @@ -12,6 +12,7 @@ #include "cc/base/cc_export.h" #include "cc/layers/layer_impl.h" #include "cc/layers/ui_resource_layer_impl.h" +#include "cc/quads/nine_patch_generator.h" #include "cc/resources/resource_provider.h" #include "cc/resources/ui_resource_client.h" #include "ui/gfx/geometry/rect.h" @@ -31,52 +32,7 @@ class CC_EXPORT NinePatchLayerImpl : public UIResourceLayerImpl { } ~NinePatchLayerImpl() override; - // The bitmap stretches out the bounds of the layer. The following picture - // illustrates the parameters associated with the dimensions. - // - // Layer space layout - // - // -------------------------------- - // | : : | - // | J C | - // | : : | - // | ------------------ | - // | | : | | - // |~~~I~~| ------------ | | - // | | | | | | - // | | | | | | - // |~~~A~~|~~| |~~|~B~~~~| - // | | | | | | - // | L ------------ | | - // | | : | | - // | ---K-------------- | - // | D | - // | : | - // | : | - // -------------------------------- - // - // Bitmap space layout - // - // ~~~~~~~~~~ W ~~~~~~~~~~ - // : : | - // : Y | - // : : | - // :~~X~~------------ | - // : | : | - // : | : | - // H | Q | - // : | : | - // : ~~~~~P~~~~~ | - // : | - // : | - // : | - // ------------------------ - // - // |image_bounds| = (W, H) - // |image_aperture| = (X, Y, P, Q) - // |border| = (A, C, A + B, C + D) - // |occlusion_rectangle| = (I, J, K, L) - // |fill_center| indicates whether to draw the center quad or not. + // For parameter meanings, see the declaration of NinePatchGenerator. void SetLayout(const gfx::Rect& image_aperture, const gfx::Rect& border, const gfx::Rect& layer_occlusion, @@ -95,33 +51,9 @@ class CC_EXPORT NinePatchLayerImpl : public UIResourceLayerImpl { NinePatchLayerImpl(LayerTreeImpl* tree_impl, int id); private: - class Patch { - public: - Patch(const gfx::RectF& image_rect, const gfx::RectF& layer_rect); - - gfx::RectF image_rect; - gfx::RectF layer_rect; - }; - const char* LayerTypeAsString() const override; - void CheckGeometryLimitations(); - - std::vector ComputeQuadsWithOcclusion() const; - std::vector ComputeQuadsWithoutOcclusion() const; - - // The transparent center region that shows the parent layer's contents in - // image space. - gfx::Rect image_aperture_; - - // An inset border that the patches will be mapped to. - gfx::Rect border_; - - bool fill_center_; - - bool nearest_neighbor_; - - gfx::Rect layer_occlusion_; + NinePatchGenerator quad_generator_; DISALLOW_COPY_AND_ASSIGN(NinePatchLayerImpl); }; diff --git a/cc/layers/painted_overlay_scrollbar_layer.cc b/cc/layers/painted_overlay_scrollbar_layer.cc new file mode 100644 index 00000000000000..ab8643ecac019f --- /dev/null +++ b/cc/layers/painted_overlay_scrollbar_layer.cc @@ -0,0 +1,173 @@ +// Copyright 2016 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/painted_overlay_scrollbar_layer.h" + +#include + +#include "base/auto_reset.h" +#include "cc/base/math_util.h" +#include "cc/layers/painted_overlay_scrollbar_layer_impl.h" +#include "cc/resources/ui_resource_bitmap.h" +#include "cc/resources/ui_resource_manager.h" +#include "cc/trees/layer_tree_host.h" +#include "cc/trees/layer_tree_impl.h" +#include "skia/ext/platform_canvas.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkSize.h" +#include "ui/gfx/geometry/size_conversions.h" +#include "ui/gfx/skia_util.h" + +namespace cc { + +std::unique_ptr PaintedOverlayScrollbarLayer::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return PaintedOverlayScrollbarLayerImpl::Create( + tree_impl, id(), scrollbar_->Orientation(), + scrollbar_->IsLeftSideVerticalScrollbar()); +} + +scoped_refptr +PaintedOverlayScrollbarLayer::Create(std::unique_ptr scrollbar, + int scroll_layer_id) { + return make_scoped_refptr( + new PaintedOverlayScrollbarLayer(std::move(scrollbar), scroll_layer_id)); +} + +PaintedOverlayScrollbarLayer::PaintedOverlayScrollbarLayer( + std::unique_ptr scrollbar, + int scroll_layer_id) + : scrollbar_(std::move(scrollbar)), + scroll_layer_id_(scroll_layer_id), + thumb_thickness_(scrollbar_->ThumbThickness()), + thumb_length_(scrollbar_->ThumbLength()) { + DCHECK(scrollbar_->UsesNinePatchThumbResource()); +} + +PaintedOverlayScrollbarLayer::~PaintedOverlayScrollbarLayer() {} + +int PaintedOverlayScrollbarLayer::ScrollLayerId() const { + return scroll_layer_id_; +} + +void PaintedOverlayScrollbarLayer::SetScrollLayer(int layer_id) { + if (layer_id == scroll_layer_id_) + return; + + scroll_layer_id_ = layer_id; + SetNeedsFullTreeSync(); +} + +bool PaintedOverlayScrollbarLayer::OpacityCanAnimateOnImplThread() const { + return scrollbar_->IsOverlay(); +} + +bool PaintedOverlayScrollbarLayer::AlwaysUseActiveTreeOpacity() const { + return true; +} + +ScrollbarOrientation PaintedOverlayScrollbarLayer::orientation() const { + return scrollbar_->Orientation(); +} + +void PaintedOverlayScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { + Layer::PushPropertiesTo(layer); + + PaintedOverlayScrollbarLayerImpl* scrollbar_layer = + static_cast(layer); + + scrollbar_layer->SetScrollLayerId(scroll_layer_id_); + + scrollbar_layer->SetThumbThickness(thumb_thickness_); + scrollbar_layer->SetThumbLength(thumb_length_); + if (orientation() == HORIZONTAL) { + scrollbar_layer->SetTrackStart(track_rect_.x() - location_.x()); + scrollbar_layer->SetTrackLength(track_rect_.width()); + } else { + scrollbar_layer->SetTrackStart(track_rect_.y() - location_.y()); + scrollbar_layer->SetTrackLength(track_rect_.height()); + } + + if (thumb_resource_.get()) { + scrollbar_layer->SetImageBounds( + layer_tree_host()->GetUIResourceManager()->GetUIResourceSize( + thumb_resource_->id())); + scrollbar_layer->SetAperture(aperture_); + scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id()); + } else { + scrollbar_layer->SetImageBounds(gfx::Size()); + scrollbar_layer->SetAperture(gfx::Rect()); + scrollbar_layer->set_thumb_ui_resource_id(0); + } +} + +ScrollbarLayerInterface* PaintedOverlayScrollbarLayer::ToScrollbarLayer() { + return this; +} + +void PaintedOverlayScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) { + // When the LTH is set to null or has changed, then this layer should remove + // all of its associated resources. + if (host != layer_tree_host()) + thumb_resource_ = nullptr; + + Layer::SetLayerTreeHost(host); +} + +gfx::Rect PaintedOverlayScrollbarLayer::OriginThumbRectForPainting() const { + return gfx::Rect(gfx::Point(), scrollbar_->NinePatchThumbCanvasSize()); +} + +bool PaintedOverlayScrollbarLayer::Update() { + bool updated = false; + updated |= Layer::Update(); + + DCHECK(scrollbar_->HasThumb()); + DCHECK(scrollbar_->IsOverlay()); + DCHECK(scrollbar_->UsesNinePatchThumbResource()); + updated |= UpdateProperty(scrollbar_->TrackRect(), &track_rect_); + updated |= UpdateProperty(scrollbar_->Location(), &location_); + updated |= UpdateProperty(scrollbar_->ThumbThickness(), &thumb_thickness_); + updated |= UpdateProperty(scrollbar_->ThumbLength(), &thumb_length_); + updated |= PaintThumbIfNeeded(); + + return updated; +} + +bool PaintedOverlayScrollbarLayer::PaintThumbIfNeeded() { + if (!scrollbar_->NeedsPaintPart(THUMB)) + return false; + + gfx::Rect paint_rect = OriginThumbRectForPainting(); + aperture_ = scrollbar_->NinePatchThumbAperture(); + + DCHECK(!paint_rect.size().IsEmpty()); + DCHECK(paint_rect.origin().IsOrigin()); + + SkBitmap skbitmap; + skbitmap.allocN32Pixels(paint_rect.width(), paint_rect.height()); + SkCanvas skcanvas(skbitmap); + + SkRect content_skrect = RectToSkRect(paint_rect); + SkPaint paint; + paint.setAntiAlias(false); + paint.setBlendMode(SkBlendMode::kClear); + skcanvas.drawRect(content_skrect, paint); + skcanvas.clipRect(content_skrect); + + scrollbar_->PaintPart(&skcanvas, THUMB, paint_rect); + // Make sure that the pixels are no longer mutable to unavoid unnecessary + // allocation and copying. + skbitmap.setImmutable(); + + thumb_resource_ = ScopedUIResource::Create( + layer_tree_host()->GetUIResourceManager(), UIResourceBitmap(skbitmap)); + + SetNeedsPushProperties(); + + return true; +} + +} // namespace cc diff --git a/cc/layers/painted_overlay_scrollbar_layer.h b/cc/layers/painted_overlay_scrollbar_layer.h new file mode 100644 index 00000000000000..6888bec7ce6217 --- /dev/null +++ b/cc/layers/painted_overlay_scrollbar_layer.h @@ -0,0 +1,77 @@ +// Copyright 2016 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. + +#ifndef CC_LAYERS_PAINTED_OVERLAY_SCROLLBAR_LAYER_H_ +#define CC_LAYERS_PAINTED_OVERLAY_SCROLLBAR_LAYER_H_ + +#include "base/macros.h" +#include "cc/base/cc_export.h" +#include "cc/input/scrollbar.h" +#include "cc/layers/layer.h" +#include "cc/layers/scrollbar_layer_interface.h" +#include "cc/layers/scrollbar_theme_painter.h" +#include "cc/resources/scoped_ui_resource.h" + +namespace cc { + +class CC_EXPORT PaintedOverlayScrollbarLayer : public ScrollbarLayerInterface, + public Layer { + public: + std::unique_ptr CreateLayerImpl(LayerTreeImpl* tree_impl) override; + + static scoped_refptr Create( + std::unique_ptr scrollbar, + int scroll_layer_id); + + bool OpacityCanAnimateOnImplThread() const override; + bool AlwaysUseActiveTreeOpacity() const override; + ScrollbarLayerInterface* ToScrollbarLayer() override; + + // ScrollbarLayerInterface + int ScrollLayerId() const override; + void SetScrollLayer(int layer_id) override; + ScrollbarOrientation orientation() const override; + + // Layer interface + bool Update() override; + void SetLayerTreeHost(LayerTreeHost* host) override; + void PushPropertiesTo(LayerImpl* layer) override; + + protected: + PaintedOverlayScrollbarLayer(std::unique_ptr scrollbar, + int scroll_layer_id); + ~PaintedOverlayScrollbarLayer() override; + + private: + gfx::Rect OriginThumbRectForPainting() const; + + template + bool UpdateProperty(T value, T* prop) { + if (*prop == value) + return false; + *prop = value; + SetNeedsPushProperties(); + return true; + } + + bool PaintThumbIfNeeded(); + + std::unique_ptr scrollbar_; + int scroll_layer_id_; + + int thumb_thickness_; + int thumb_length_; + gfx::Point location_; + gfx::Rect track_rect_; + + gfx::Rect aperture_; + + std::unique_ptr thumb_resource_; + + DISALLOW_COPY_AND_ASSIGN(PaintedOverlayScrollbarLayer); +}; + +} // namespace cc + +#endif // CC_LAYERS_PAINTED_OVERLAY_SCROLLBAR_LAYER_H_ diff --git a/cc/layers/painted_overlay_scrollbar_layer_impl.cc b/cc/layers/painted_overlay_scrollbar_layer_impl.cc new file mode 100644 index 00000000000000..c14b93be585e51 --- /dev/null +++ b/cc/layers/painted_overlay_scrollbar_layer_impl.cc @@ -0,0 +1,167 @@ +// Copyright 2016 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/painted_overlay_scrollbar_layer_impl.h" + +namespace cc { + +std::unique_ptr +PaintedOverlayScrollbarLayerImpl::Create(LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + bool is_left_side_vertical_scrollbar) { + return base::WrapUnique(new PaintedOverlayScrollbarLayerImpl( + tree_impl, id, orientation, is_left_side_vertical_scrollbar)); +} + +PaintedOverlayScrollbarLayerImpl::PaintedOverlayScrollbarLayerImpl( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + bool is_left_side_vertical_scrollbar) + : ScrollbarLayerImplBase(tree_impl, + id, + orientation, + is_left_side_vertical_scrollbar, + true), + thumb_ui_resource_id_(0), + thumb_thickness_(0), + thumb_length_(0), + track_start_(0), + track_length_(0) {} + +PaintedOverlayScrollbarLayerImpl::~PaintedOverlayScrollbarLayerImpl() {} + +std::unique_ptr PaintedOverlayScrollbarLayerImpl::CreateLayerImpl( + LayerTreeImpl* tree_impl) { + return PaintedOverlayScrollbarLayerImpl::Create( + tree_impl, id(), orientation(), is_left_side_vertical_scrollbar()); +} + +void PaintedOverlayScrollbarLayerImpl::PushPropertiesTo(LayerImpl* layer) { + ScrollbarLayerImplBase::PushPropertiesTo(layer); + + PaintedOverlayScrollbarLayerImpl* scrollbar_layer = + static_cast(layer); + + scrollbar_layer->SetThumbThickness(thumb_thickness_); + scrollbar_layer->SetThumbLength(thumb_length_); + scrollbar_layer->SetTrackStart(track_start_); + scrollbar_layer->SetTrackLength(track_length_); + + scrollbar_layer->SetImageBounds(image_bounds_); + scrollbar_layer->SetAperture(aperture_); + + scrollbar_layer->set_thumb_ui_resource_id(thumb_ui_resource_id_); +} + +bool PaintedOverlayScrollbarLayerImpl::WillDraw( + DrawMode draw_mode, + ResourceProvider* resource_provider) { + DCHECK(draw_mode != DRAW_MODE_RESOURCELESS_SOFTWARE); + return LayerImpl::WillDraw(draw_mode, resource_provider); +} + +void PaintedOverlayScrollbarLayerImpl::AppendQuads( + RenderPass* render_pass, + AppendQuadsData* append_quads_data) { + // For overlay scrollbars, the border should match the inset of the aperture + // and be symmetrical. + gfx::Rect border(aperture_.x(), aperture_.y(), aperture_.x() * 2, + aperture_.y() * 2); + gfx::Rect thumb_quad_rect(ComputeThumbQuadRect()); + gfx::Rect layer_occlusion; + bool fill_center = true; + bool nearest_neighbor = false; + + quad_generator_.SetLayout(image_bounds_, thumb_quad_rect.size(), aperture_, + border, layer_occlusion, fill_center, + nearest_neighbor); + quad_generator_.CheckGeometryLimitations(); + + SharedQuadState* shared_quad_state = + render_pass->CreateAndAppendSharedQuadState(); + PopulateSharedQuadState(shared_quad_state); + + AppendDebugBorderQuad(render_pass, bounds(), shared_quad_state, + append_quads_data); + + std::vector patches = + quad_generator_.GeneratePatches(); + + gfx::Vector2dF offset = thumb_quad_rect.OffsetFromOrigin(); + for (auto& patch : patches) + patch.output_rect += offset; + + quad_generator_.AppendQuads(this, thumb_ui_resource_id_, render_pass, + shared_quad_state, patches); +} + +void PaintedOverlayScrollbarLayerImpl::SetThumbThickness(int thumb_thickness) { + if (thumb_thickness_ == thumb_thickness) + return; + thumb_thickness_ = thumb_thickness; + NoteLayerPropertyChanged(); +} + +int PaintedOverlayScrollbarLayerImpl::ThumbThickness() const { + return thumb_thickness_; +} + +void PaintedOverlayScrollbarLayerImpl::SetThumbLength(int thumb_length) { + if (thumb_length_ == thumb_length) + return; + thumb_length_ = thumb_length; + NoteLayerPropertyChanged(); +} + +int PaintedOverlayScrollbarLayerImpl::ThumbLength() const { + return thumb_length_; +} + +void PaintedOverlayScrollbarLayerImpl::SetTrackStart(int track_start) { + if (track_start_ == track_start) + return; + track_start_ = track_start; + NoteLayerPropertyChanged(); +} + +int PaintedOverlayScrollbarLayerImpl::TrackStart() const { + return track_start_; +} + +void PaintedOverlayScrollbarLayerImpl::SetTrackLength(int track_length) { + if (track_length_ == track_length) + return; + track_length_ = track_length; + NoteLayerPropertyChanged(); +} + +void PaintedOverlayScrollbarLayerImpl::SetImageBounds(const gfx::Size& bounds) { + if (image_bounds_ == bounds) + return; + image_bounds_ = bounds; + NoteLayerPropertyChanged(); +} + +void PaintedOverlayScrollbarLayerImpl::SetAperture(const gfx::Rect& aperture) { + if (aperture_ == aperture) + return; + aperture_ = aperture; + NoteLayerPropertyChanged(); +} + +float PaintedOverlayScrollbarLayerImpl::TrackLength() const { + return track_length_ + (orientation() == VERTICAL ? vertical_adjust() : 0); +} + +bool PaintedOverlayScrollbarLayerImpl::IsThumbResizable() const { + return false; +} + +const char* PaintedOverlayScrollbarLayerImpl::LayerTypeAsString() const { + return "cc::PaintedOverlayScrollbarLayerImpl"; +} + +} // namespace cc diff --git a/cc/layers/painted_overlay_scrollbar_layer_impl.h b/cc/layers/painted_overlay_scrollbar_layer_impl.h new file mode 100644 index 00000000000000..fef31208864c9c --- /dev/null +++ b/cc/layers/painted_overlay_scrollbar_layer_impl.h @@ -0,0 +1,82 @@ +// Copyright 2016 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. + +#ifndef CC_LAYERS_PAINTED_OVERLAY_SCROLLBAR_LAYER_IMPL_H_ +#define CC_LAYERS_PAINTED_OVERLAY_SCROLLBAR_LAYER_IMPL_H_ + +#include "base/macros.h" +#include "cc/base/cc_export.h" +#include "cc/input/scrollbar.h" +#include "cc/layers/scrollbar_layer_impl_base.h" +#include "cc/quads/nine_patch_generator.h" +#include "cc/resources/ui_resource_client.h" + +namespace cc { + +class LayerTreeImpl; + +class CC_EXPORT PaintedOverlayScrollbarLayerImpl + : public ScrollbarLayerImplBase { + public: + static std::unique_ptr Create( + LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + bool is_left_side_vertical_scrollbar); + ~PaintedOverlayScrollbarLayerImpl() override; + + // LayerImpl implementation. + std::unique_ptr CreateLayerImpl(LayerTreeImpl* tree_impl) override; + void PushPropertiesTo(LayerImpl* layer) override; + + bool WillDraw(DrawMode draw_mode, + ResourceProvider* resource_provider) override; + void AppendQuads(RenderPass* render_pass, + AppendQuadsData* append_quads_data) override; + + void SetThumbThickness(int thumb_thickness); + void SetThumbLength(int thumb_length); + void SetTrackStart(int track_start); + void SetTrackLength(int track_length); + + void SetImageBounds(const gfx::Size& bounds); + void SetAperture(const gfx::Rect& aperture); + + void set_thumb_ui_resource_id(UIResourceId uid) { + thumb_ui_resource_id_ = uid; + } + + protected: + PaintedOverlayScrollbarLayerImpl(LayerTreeImpl* tree_impl, + int id, + ScrollbarOrientation orientation, + bool is_left_side_vertical_scrollbar); + + // ScrollbarLayerImplBase implementation. + int ThumbThickness() const override; + int ThumbLength() const override; + float TrackLength() const override; + int TrackStart() const override; + bool IsThumbResizable() const override; + + private: + const char* LayerTypeAsString() const override; + + UIResourceId thumb_ui_resource_id_; + + int thumb_thickness_; + int thumb_length_; + int track_start_; + int track_length_; + + gfx::Size image_bounds_; + gfx::Rect aperture_; + + NinePatchGenerator quad_generator_; + + DISALLOW_COPY_AND_ASSIGN(PaintedOverlayScrollbarLayerImpl); +}; + +} // namespace cc +#endif // CC_LAYERS_PAINTED_OVERLAY_SCROLLBAR_LAYER_IMPL_H_ diff --git a/cc/quads/nine_patch_generator.cc b/cc/quads/nine_patch_generator.cc new file mode 100644 index 00000000000000..ea722ad47e281d --- /dev/null +++ b/cc/quads/nine_patch_generator.cc @@ -0,0 +1,396 @@ +// Copyright 2016 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/quads/nine_patch_generator.h" + +#include "cc/layers/draw_properties.h" +#include "cc/quads/render_pass.h" +#include "cc/quads/texture_draw_quad.h" +#include "cc/trees/layer_tree_impl.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gfx/geometry/rect_f.h" + +namespace cc { + +namespace { + +// Maximum number of patches that can be produced for one NinePatchLayer. +const int kMaxOcclusionPatches = 12; +const int kMaxPatches = 9; + +gfx::RectF BoundsToRect(int x1, int y1, int x2, int y2) { + return gfx::RectF(x1, y1, x2 - x1, y2 - y1); +} + +gfx::RectF NormalizedRect(const gfx::RectF& rect, + float total_width, + float total_height) { + return gfx::RectF(rect.x() / total_width, rect.y() / total_height, + rect.width() / total_width, rect.height() / total_height); +} + +} // namespace + +NinePatchGenerator::Patch::Patch(const gfx::RectF& image_rect, + const gfx::Size& total_image_bounds, + const gfx::RectF& output_rect) + : image_rect(image_rect), + normalized_image_rect(NormalizedRect(image_rect, + total_image_bounds.width(), + total_image_bounds.height())), + output_rect(output_rect) {} + +NinePatchGenerator::NinePatchGenerator() + : fill_center_(false), nearest_neighbor_(false) {} + +bool NinePatchGenerator::SetLayout(const gfx::Size& image_bounds, + const gfx::Size& output_bounds, + const gfx::Rect& aperture, + const gfx::Rect& border, + const gfx::Rect& output_occlusion, + bool fill_center, + bool nearest_neighbor) { + if (image_bounds_ == image_bounds && output_bounds_ == output_bounds && + image_aperture_ == aperture && border_ == border && + fill_center_ == fill_center && output_occlusion_ == output_occlusion && + nearest_neighbor_ == nearest_neighbor) + return false; + + image_bounds_ = image_bounds; + output_bounds_ = output_bounds; + image_aperture_ = aperture; + border_ = border; + fill_center_ = fill_center; + output_occlusion_ = output_occlusion; + nearest_neighbor_ = nearest_neighbor; + + return true; +} + +void NinePatchGenerator::CheckGeometryLimitations() { + // |border| is in layer space. It cannot exceed the bounds of the layer. + DCHECK_GE(output_bounds_.width(), border_.width()); + DCHECK_GE(output_bounds_.height(), border_.height()); + + // Sanity Check on |border| + DCHECK_LE(border_.x(), border_.width()); + DCHECK_LE(border_.y(), border_.height()); + DCHECK_GE(border_.x(), 0); + DCHECK_GE(border_.y(), 0); + + // |aperture| is in image space. It cannot exceed the bounds of the bitmap. + DCHECK(!image_aperture_.size().IsEmpty()); + DCHECK(gfx::Rect(image_bounds_).Contains(image_aperture_)) + << "image_bounds_ " << gfx::Rect(image_bounds_).ToString() + << " image_aperture_ " << image_aperture_.ToString(); + + // Sanity check on |output_occlusion_|. It should always be within the + // border. + gfx::Rect border_rect(border_.x(), border_.y(), + output_bounds_.width() - border_.width(), + output_bounds_.height() - border_.height()); + DCHECK(output_occlusion_.IsEmpty() || output_occlusion_.Contains(border_rect)) + << "border_rect " << border_rect.ToString() << " output_occlusion_ " + << output_occlusion_.ToString(); +} + +std::vector +NinePatchGenerator::ComputeQuadsWithoutOcclusion() const { + float image_width = image_bounds_.width(); + float image_height = image_bounds_.height(); + float output_width = output_bounds_.width(); + float output_height = output_bounds_.height(); + gfx::RectF output_aperture(border_.x(), border_.y(), + output_width - border_.width(), + output_height - border_.height()); + + std::vector patches; + patches.reserve(kMaxPatches); + + // Top-left. + patches.push_back( + Patch(BoundsToRect(0, 0, image_aperture_.x(), image_aperture_.y()), + image_bounds_, + BoundsToRect(0, 0, output_aperture.x(), output_aperture.y()))); + + // Top-right. + patches.push_back(Patch(BoundsToRect(image_aperture_.right(), 0, image_width, + image_aperture_.y()), + image_bounds_, + BoundsToRect(output_aperture.right(), 0, output_width, + output_aperture.y()))); + + // Bottom-left. + patches.push_back(Patch(BoundsToRect(0, image_aperture_.bottom(), + image_aperture_.x(), image_height), + image_bounds_, + BoundsToRect(0, output_aperture.bottom(), + output_aperture.x(), output_height))); + + // Bottom-right. + patches.push_back( + Patch(BoundsToRect(image_aperture_.right(), image_aperture_.bottom(), + image_width, image_height), + image_bounds_, + BoundsToRect(output_aperture.right(), output_aperture.bottom(), + output_width, output_height))); + + // Top. + patches.push_back( + Patch(BoundsToRect(image_aperture_.x(), 0, image_aperture_.right(), + image_aperture_.y()), + image_bounds_, + BoundsToRect(output_aperture.x(), 0, output_aperture.right(), + output_aperture.y()))); + + // Left. + patches.push_back( + Patch(BoundsToRect(0, image_aperture_.y(), image_aperture_.x(), + image_aperture_.bottom()), + image_bounds_, + BoundsToRect(0, output_aperture.y(), output_aperture.x(), + output_aperture.bottom()))); + + // Right. + patches.push_back( + Patch(BoundsToRect(image_aperture_.right(), image_aperture_.y(), + image_width, image_aperture_.bottom()), + image_bounds_, + BoundsToRect(output_aperture.right(), output_aperture.y(), + output_width, output_aperture.bottom()))); + + // Bottom. + patches.push_back( + Patch(BoundsToRect(image_aperture_.x(), image_aperture_.bottom(), + image_aperture_.right(), image_height), + image_bounds_, + BoundsToRect(output_aperture.x(), output_aperture.bottom(), + output_aperture.right(), output_height))); + + // Center. + if (fill_center_) { + patches.push_back( + Patch(BoundsToRect(image_aperture_.x(), image_aperture_.y(), + image_aperture_.right(), image_aperture_.bottom()), + image_bounds_, + BoundsToRect(output_aperture.x(), output_aperture.y(), + output_aperture.right(), output_aperture.bottom()))); + } + + return patches; +} + +std::vector +NinePatchGenerator::ComputeQuadsWithOcclusion() const { + float image_width = image_bounds_.width(); + float image_height = image_bounds_.height(); + + float output_width = output_bounds_.width(); + float output_height = output_bounds_.height(); + + float layer_border_right = border_.width() - border_.x(); + float layer_border_bottom = border_.height() - border_.y(); + + float image_aperture_right = image_width - image_aperture_.right(); + float image_aperture_bottom = image_height - image_aperture_.bottom(); + + float output_occlusion_right = output_width - output_occlusion_.right(); + float output_occlusion_bottom = output_height - output_occlusion_.bottom(); + + gfx::RectF image_occlusion(BoundsToRect( + border_.x() == 0 + ? 0 + : (output_occlusion_.x() * image_aperture_.x() / border_.x()), + border_.y() == 0 + ? 0 + : (output_occlusion_.y() * image_aperture_.y() / border_.y()), + image_width - (layer_border_right == 0 + ? 0 + : output_occlusion_right * image_aperture_right / + layer_border_right), + image_height - (layer_border_bottom == 0 + ? 0 + : output_occlusion_bottom * image_aperture_bottom / + layer_border_bottom))); + gfx::RectF output_aperture(border_.x(), border_.y(), + output_width - border_.width(), + output_height - border_.height()); + + std::vector patches; + patches.reserve(kMaxOcclusionPatches); + + // Top-left-left. + patches.push_back( + Patch(BoundsToRect(0, 0, image_occlusion.x(), image_aperture_.y()), + image_bounds_, + BoundsToRect(0, 0, output_occlusion_.x(), output_aperture.y()))); + + // Top-left-right. + patches.push_back( + Patch(BoundsToRect(image_occlusion.x(), 0, image_aperture_.x(), + image_occlusion.y()), + image_bounds_, + BoundsToRect(output_occlusion_.x(), 0, output_aperture.x(), + output_occlusion_.y()))); + + // Top-center. + patches.push_back( + Patch(BoundsToRect(image_aperture_.x(), 0, image_aperture_.right(), + image_occlusion.y()), + image_bounds_, + BoundsToRect(output_aperture.x(), 0, output_aperture.right(), + output_occlusion_.y()))); + + // Top-right-left. + patches.push_back( + Patch(BoundsToRect(image_aperture_.right(), 0, image_occlusion.right(), + image_occlusion.y()), + image_bounds_, + BoundsToRect(output_aperture.right(), 0, output_occlusion_.right(), + output_occlusion_.y()))); + + // Top-right-right. + patches.push_back(Patch(BoundsToRect(image_occlusion.right(), 0, image_width, + image_aperture_.y()), + image_bounds_, + BoundsToRect(output_occlusion_.right(), 0, + output_width, output_aperture.y()))); + + // Left-center. + patches.push_back( + Patch(BoundsToRect(0, image_aperture_.y(), image_occlusion.x(), + image_aperture_.bottom()), + image_bounds_, + BoundsToRect(0, output_aperture.y(), output_occlusion_.x(), + output_aperture.bottom()))); + + // Right-center. + patches.push_back( + Patch(BoundsToRect(image_occlusion.right(), image_aperture_.y(), + image_width, image_aperture_.bottom()), + image_bounds_, + BoundsToRect(output_occlusion_.right(), output_aperture.y(), + output_width, output_aperture.bottom()))); + + // Bottom-left-left. + patches.push_back(Patch(BoundsToRect(0, image_aperture_.bottom(), + image_occlusion.x(), image_height), + image_bounds_, + BoundsToRect(0, output_aperture.bottom(), + output_occlusion_.x(), output_height))); + + // Bottom-left-right. + patches.push_back( + Patch(BoundsToRect(image_occlusion.x(), image_occlusion.bottom(), + image_aperture_.x(), image_height), + image_bounds_, + BoundsToRect(output_occlusion_.x(), output_occlusion_.bottom(), + output_aperture.x(), output_height))); + + // Bottom-center. + patches.push_back( + Patch(BoundsToRect(image_aperture_.x(), image_occlusion.bottom(), + image_aperture_.right(), image_height), + image_bounds_, + BoundsToRect(output_aperture.x(), output_occlusion_.bottom(), + output_aperture.right(), output_height))); + + // Bottom-right-left. + patches.push_back( + Patch(BoundsToRect(image_aperture_.right(), image_occlusion.bottom(), + image_occlusion.right(), image_height), + image_bounds_, + BoundsToRect(output_aperture.right(), output_occlusion_.bottom(), + output_occlusion_.right(), output_height))); + + // Bottom-right-right. + patches.push_back( + Patch(BoundsToRect(image_occlusion.right(), image_aperture_.bottom(), + image_width, image_height), + image_bounds_, + BoundsToRect(output_occlusion_.right(), output_aperture.bottom(), + output_width, output_height))); + + return patches; +} + +std::vector NinePatchGenerator::GeneratePatches() + const { + DCHECK(!output_bounds_.IsEmpty()); + + std::vector patches; + + if (output_occlusion_.IsEmpty() || fill_center_) + patches = ComputeQuadsWithoutOcclusion(); + else + patches = ComputeQuadsWithOcclusion(); + + return patches; +} + +void NinePatchGenerator::AppendQuads(LayerImpl* layer_impl, + UIResourceId ui_resource_id, + RenderPass* render_pass, + SharedQuadState* shared_quad_state, + const std::vector& patches) { + if (!ui_resource_id) + return; + + ResourceId resource = + layer_impl->layer_tree_impl()->ResourceIdForUIResource(ui_resource_id); + + if (!resource) + return; + + const float vertex_opacity[] = {1.0f, 1.0f, 1.0f, 1.0f}; + const bool opaque = + layer_impl->layer_tree_impl()->IsUIResourceOpaque(ui_resource_id); + constexpr bool flipped = false; + constexpr bool premultiplied_alpha = true; + + for (const auto& patch : patches) { + gfx::Rect output_rect = gfx::ToEnclosingRect(patch.output_rect); + gfx::Rect visible_rect = + layer_impl->draw_properties() + .occlusion_in_content_space.GetUnoccludedContentRect(output_rect); + gfx::Rect opaque_rect = opaque ? visible_rect : gfx::Rect(); + if (!visible_rect.IsEmpty()) { + gfx::RectF image_rect = patch.normalized_image_rect; + TextureDrawQuad* quad = + render_pass->CreateAndAppendDrawQuad(); + quad->SetNew(shared_quad_state, output_rect, opaque_rect, visible_rect, + resource, premultiplied_alpha, image_rect.origin(), + image_rect.bottom_right(), SK_ColorTRANSPARENT, + vertex_opacity, flipped, nearest_neighbor_, false); + layer_impl->ValidateQuadResources(quad); + } + } +} + +void NinePatchGenerator::AsJson(base::DictionaryValue* dictionary) const { + base::ListValue* list = new base::ListValue; + list->AppendInteger(image_aperture_.origin().x()); + list->AppendInteger(image_aperture_.origin().y()); + list->AppendInteger(image_aperture_.size().width()); + list->AppendInteger(image_aperture_.size().height()); + dictionary->Set("ImageAperture", list); + + list = new base::ListValue; + list->AppendInteger(image_bounds_.width()); + list->AppendInteger(image_bounds_.height()); + dictionary->Set("ImageBounds", list); + + dictionary->Set("Border", MathUtil::AsValue(border_).release()); + + dictionary->SetBoolean("FillCenter", fill_center_); + + list = new base::ListValue; + list->AppendInteger(output_occlusion_.x()); + list->AppendInteger(output_occlusion_.y()); + list->AppendInteger(output_occlusion_.width()); + list->AppendInteger(output_occlusion_.height()); + dictionary->Set("OutputOcclusion", list); +} + +} // namespace cc diff --git a/cc/quads/nine_patch_generator.h b/cc/quads/nine_patch_generator.h new file mode 100644 index 00000000000000..140d361914e3a2 --- /dev/null +++ b/cc/quads/nine_patch_generator.h @@ -0,0 +1,130 @@ +// Copyright 2016 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. + +#ifndef CC_QUADS_NINE_PATCH_GENERATOR_H_ +#define CC_QUADS_NINE_PATCH_GENERATOR_H_ + +#include +#include + +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "cc/base/cc_export.h" +#include "cc/resources/ui_resource_client.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size.h" + +namespace base { +class DictionaryValue; +} + +namespace cc { + +class LayerImpl; +class RenderPass; +class SharedQuadState; + +class CC_EXPORT NinePatchGenerator { + public: + class Patch { + public: + Patch(const gfx::RectF& image_rect, + const gfx::Size& total_image_bounds, + const gfx::RectF& output_rect); + + gfx::RectF image_rect; + gfx::RectF normalized_image_rect; + gfx::RectF output_rect; + }; + + NinePatchGenerator(); + + // The bitmap stretches out the bounds of the layer. The following picture + // illustrates the parameters associated with the dimensions. + // + // Layer space layout + // + // -------------------------------- + // | : : | + // | J C | + // | : : | + // | ------------------ | + // | | : | | + // |~~~I~~| ------------ | | + // | | | | | | + // | | | | | | + // |~~~A~~|~~| |~~|~B~~~~| + // | | | | | | + // | L ------------ | | + // | | : | | + // | ---K-------------- | + // | D | + // | : | + // | : | + // -------------------------------- + // + // Bitmap space layout + // + // ~~~~~~~~~~ W ~~~~~~~~~~ + // : : | + // : Y | + // : : | + // :~~X~~------------ | + // : | : | + // : | : | + // H | Q | + // : | : | + // : ~~~~~P~~~~~ | + // : | + // : | + // : | + // ------------------------ + // + // |image_bounds| = (W, H) + // |image_aperture| = (X, Y, P, Q) + // |border| = (A, C, A + B, C + D) + // |occlusion_rectangle| = (I, J, K, L) + // |fill_center| indicates whether to draw the center quad or not. + bool SetLayout(const gfx::Size& image_bounds, + const gfx::Size& output_bounds, + const gfx::Rect& image_aperture, + const gfx::Rect& border, + const gfx::Rect& output_occlusion, + bool fill_center, + bool nearest_neighbor); + + std::vector GeneratePatches() const; + + void AppendQuads(LayerImpl* layer_impl, + UIResourceId ui_resource_id, + RenderPass* render_pass, + SharedQuadState* shared_quad_state, + const std::vector& patches); + + void AsJson(base::DictionaryValue* dictionary) const; + void CheckGeometryLimitations(); + + private: + std::vector ComputeQuadsWithOcclusion() const; + std::vector ComputeQuadsWithoutOcclusion() const; + + // The center patch in image space. + gfx::Rect image_aperture_; + + // An inset border that the patches will be mapped to. + gfx::Rect border_; + + gfx::Size image_bounds_; + gfx::Size output_bounds_; + + bool fill_center_; + bool nearest_neighbor_; + + gfx::Rect output_occlusion_; +}; + +} // namespace cc + +#endif // CC_QUADS_NINE_PATCH_GENERATOR_H_ diff --git a/cc/quads/nine_patch_generator_unittest.cc b/cc/quads/nine_patch_generator_unittest.cc new file mode 100644 index 00000000000000..a0625f2ce51d05 --- /dev/null +++ b/cc/quads/nine_patch_generator_unittest.cc @@ -0,0 +1,237 @@ +// Copyright 2017 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/quads/nine_patch_generator.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" + +namespace cc { +namespace { + +TEST(NinePatchGeneratorTest, SetLayoutReturnsChanged) { + NinePatchGenerator quad_generator; + + EXPECT_TRUE(quad_generator.SetLayout( + gfx::Size(10, 10), gfx::Size(10, 10), gfx::Rect(1, 1, 8, 8), + gfx::Rect(1, 1, 2, 2), gfx::Rect(), true, false)); + + EXPECT_FALSE(quad_generator.SetLayout( + gfx::Size(10, 10), gfx::Size(10, 10), gfx::Rect(1, 1, 8, 8), + gfx::Rect(1, 1, 2, 2), gfx::Rect(), true, false)); + + EXPECT_TRUE(quad_generator.SetLayout( + gfx::Size(10, 10), gfx::Size(10, 10), gfx::Rect(1, 1, 8, 8), + gfx::Rect(1, 1, 2, 2), gfx::Rect(), false, false)); + + EXPECT_FALSE(quad_generator.SetLayout( + gfx::Size(10, 10), gfx::Size(10, 10), gfx::Rect(1, 1, 8, 8), + gfx::Rect(1, 1, 2, 2), gfx::Rect(), false, false)); + + EXPECT_TRUE(quad_generator.SetLayout( + gfx::Size(12, 10), gfx::Size(10, 10), gfx::Rect(1, 1, 8, 8), + gfx::Rect(1, 1, 2, 2), gfx::Rect(), false, false)); +} + +TEST(NinePatchGeneratorTest, BasicGenerate) { + NinePatchGenerator quad_generator; + + quad_generator.SetLayout(gfx::Size(10, 10), gfx::Size(10, 10), + gfx::Rect(1, 1, 8, 8), gfx::Rect(1, 1, 2, 2), + gfx::Rect(), true, false); + + std::vector patches = + quad_generator.GeneratePatches(); + + ASSERT_EQ(9u, patches.size()); + + // Center. + EXPECT_EQ(gfx::RectF(1, 1, 8, 8), patches[8].image_rect); + EXPECT_EQ(gfx::RectF(1, 1, 8, 8), patches[8].output_rect); + EXPECT_EQ(gfx::RectF(0.1f, 0.1f, 0.8f, 0.8f), + patches[8].normalized_image_rect); + + // Bottom. + EXPECT_EQ(gfx::RectF(1, 9, 8, 1), patches[7].image_rect); + EXPECT_EQ(gfx::RectF(1, 9, 8, 1), patches[7].output_rect); + EXPECT_EQ(gfx::RectF(0.1f, 0.9f, 0.8f, 0.1f), + patches[7].normalized_image_rect); + + // Right. + EXPECT_EQ(gfx::RectF(9, 1, 1, 8), patches[6].image_rect); + EXPECT_EQ(gfx::RectF(9, 1, 1, 8), patches[6].output_rect); + EXPECT_EQ(gfx::RectF(0.9f, 0.1f, 0.1f, 0.8f), + patches[6].normalized_image_rect); + + // Left. + EXPECT_EQ(gfx::RectF(0, 1, 1, 8), patches[5].image_rect); + EXPECT_EQ(gfx::RectF(0, 1, 1, 8), patches[5].output_rect); + EXPECT_EQ(gfx::RectF(0.f, 0.1f, 0.1f, 0.8f), + patches[5].normalized_image_rect); + + // Top. + EXPECT_EQ(gfx::RectF(1, 0, 8, 1), patches[4].image_rect); + EXPECT_EQ(gfx::RectF(1, 0, 8, 1), patches[4].output_rect); + EXPECT_EQ(gfx::RectF(0.1f, 0.f, 0.8f, 0.1f), + patches[4].normalized_image_rect); + + // Bottom-right + EXPECT_EQ(gfx::RectF(9, 9, 1, 1), patches[3].image_rect); + EXPECT_EQ(gfx::RectF(9, 9, 1, 1), patches[3].output_rect); + EXPECT_EQ(gfx::RectF(0.9f, 0.9f, 0.1f, 0.1f), + patches[3].normalized_image_rect); + + // Bottom-left + EXPECT_EQ(gfx::RectF(0, 9, 1, 1), patches[2].image_rect); + EXPECT_EQ(gfx::RectF(0, 9, 1, 1), patches[2].output_rect); + EXPECT_EQ(gfx::RectF(0.f, 0.9f, 0.1f, 0.1f), + patches[2].normalized_image_rect); + + // Top-right + EXPECT_EQ(gfx::RectF(9, 0, 1, 1), patches[1].image_rect); + EXPECT_EQ(gfx::RectF(9, 0, 1, 1), patches[1].output_rect); + EXPECT_EQ(gfx::RectF(0.9f, 0.f, 0.1f, 0.1f), + patches[1].normalized_image_rect); + + // Top-left + EXPECT_EQ(gfx::RectF(0, 0, 1, 1), patches[0].image_rect); + EXPECT_EQ(gfx::RectF(0, 0, 1, 1), patches[0].output_rect); + EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.1f, 0.1f), patches[0].normalized_image_rect); +} + +TEST(NinePatchGeneratorTest, GenerateNonSymmetricAperture) { + NinePatchGenerator quad_generator; + + quad_generator.SetLayout(gfx::Size(10, 10), gfx::Size(10, 10), + gfx::Rect(3, 5, 2, 2), gfx::Rect(2, 4, 4, 8), + gfx::Rect(), true, false); + + std::vector patches = + quad_generator.GeneratePatches(); + + ASSERT_EQ(9u, patches.size()); + + // Center. + EXPECT_EQ(gfx::RectF(3, 5, 2, 2), patches[8].image_rect); + EXPECT_EQ(gfx::RectF(2, 4, 6, 2), patches[8].output_rect); + EXPECT_EQ(gfx::RectF(0.3f, 0.5f, 0.2f, 0.2f), + patches[8].normalized_image_rect); + + // Bottom. + EXPECT_EQ(gfx::RectF(3, 7, 2, 3), patches[7].image_rect); + EXPECT_EQ(gfx::RectF(2, 6, 6, 4), patches[7].output_rect); + EXPECT_EQ(gfx::RectF(0.3f, 0.7f, 0.2f, 0.3f), + patches[7].normalized_image_rect); + + // Right. + EXPECT_EQ(gfx::RectF(5, 5, 5, 2), patches[6].image_rect); + EXPECT_EQ(gfx::RectF(8, 4, 2, 2), patches[6].output_rect); + EXPECT_EQ(gfx::RectF(0.5f, 0.5f, 0.5f, 0.2f), + patches[6].normalized_image_rect); + + // Left. + EXPECT_EQ(gfx::RectF(0, 5, 3, 2), patches[5].image_rect); + EXPECT_EQ(gfx::RectF(0, 4, 2, 2), patches[5].output_rect); + EXPECT_EQ(gfx::RectF(0.f, 0.5f, 0.3f, 0.2f), + patches[5].normalized_image_rect); + + // Top. + EXPECT_EQ(gfx::RectF(3, 0, 2, 5), patches[4].image_rect); + EXPECT_EQ(gfx::RectF(2, 0, 6, 4), patches[4].output_rect); + EXPECT_EQ(gfx::RectF(0.3f, 0.f, 0.2f, 0.5f), + patches[4].normalized_image_rect); + + // Bottom-right + EXPECT_EQ(gfx::RectF(5, 7, 5, 3), patches[3].image_rect); + EXPECT_EQ(gfx::RectF(8, 6, 2, 4), patches[3].output_rect); + EXPECT_EQ(gfx::RectF(0.5f, 0.7f, 0.5f, 0.3f), + patches[3].normalized_image_rect); + + // Bottom-left + EXPECT_EQ(gfx::RectF(0, 7, 3, 3), patches[2].image_rect); + EXPECT_EQ(gfx::RectF(0, 6, 2, 4), patches[2].output_rect); + EXPECT_EQ(gfx::RectF(0.f, 0.7f, 0.3f, 0.3f), + patches[2].normalized_image_rect); + + // Top-right + EXPECT_EQ(gfx::RectF(5, 0, 5, 5), patches[1].image_rect); + EXPECT_EQ(gfx::RectF(8, 0, 2, 4), patches[1].output_rect); + EXPECT_EQ(gfx::RectF(0.5f, 0.f, 0.5f, 0.5f), + patches[1].normalized_image_rect); + + // Top-left + EXPECT_EQ(gfx::RectF(0, 0, 3, 5), patches[0].image_rect); + EXPECT_EQ(gfx::RectF(0, 0, 2, 4), patches[0].output_rect); + EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.3f, 0.5f), patches[0].normalized_image_rect); +} + +TEST(NinePatchGeneratorTest, LargerLayerGenerate) { + NinePatchGenerator quad_generator; + + quad_generator.SetLayout(gfx::Size(10, 10), gfx::Size(20, 15), + gfx::Rect(1, 1, 8, 8), gfx::Rect(1, 1, 2, 2), + gfx::Rect(), true, false); + + std::vector patches = + quad_generator.GeneratePatches(); + + ASSERT_EQ(9u, patches.size()); + + // Center. + EXPECT_EQ(gfx::RectF(1, 1, 8, 8), patches[8].image_rect); + EXPECT_EQ(gfx::RectF(1, 1, 18, 13), patches[8].output_rect); + EXPECT_EQ(gfx::RectF(0.1f, 0.1f, 0.8f, 0.8f), + patches[8].normalized_image_rect); + + // Bottom. + EXPECT_EQ(gfx::RectF(1, 9, 8, 1), patches[7].image_rect); + EXPECT_EQ(gfx::RectF(1, 14, 18, 1), patches[7].output_rect); + EXPECT_EQ(gfx::RectF(0.1f, 0.9f, 0.8f, 0.1f), + patches[7].normalized_image_rect); + + // Right. + EXPECT_EQ(gfx::RectF(9, 1, 1, 8), patches[6].image_rect); + EXPECT_EQ(gfx::RectF(19, 1, 1, 13), patches[6].output_rect); + EXPECT_EQ(gfx::RectF(0.9f, 0.1f, 0.1f, 0.8f), + patches[6].normalized_image_rect); + + // Left. + EXPECT_EQ(gfx::RectF(0, 1, 1, 8), patches[5].image_rect); + EXPECT_EQ(gfx::RectF(0, 1, 1, 13), patches[5].output_rect); + EXPECT_EQ(gfx::RectF(0.f, 0.1f, 0.1f, 0.8f), + patches[5].normalized_image_rect); + + // Top. + EXPECT_EQ(gfx::RectF(1, 0, 8, 1), patches[4].image_rect); + EXPECT_EQ(gfx::RectF(1, 0, 18, 1), patches[4].output_rect); + EXPECT_EQ(gfx::RectF(0.1f, 0.f, 0.8f, 0.1f), + patches[4].normalized_image_rect); + + // Bottom-right + EXPECT_EQ(gfx::RectF(9, 9, 1, 1), patches[3].image_rect); + EXPECT_EQ(gfx::RectF(19, 14, 1, 1), patches[3].output_rect); + EXPECT_EQ(gfx::RectF(0.9f, 0.9f, 0.1f, 0.1f), + patches[3].normalized_image_rect); + + // Bottom-left + EXPECT_EQ(gfx::RectF(0, 9, 1, 1), patches[2].image_rect); + EXPECT_EQ(gfx::RectF(0, 14, 1, 1), patches[2].output_rect); + EXPECT_EQ(gfx::RectF(0.f, 0.9f, 0.1f, 0.1f), + patches[2].normalized_image_rect); + + // Top-right + EXPECT_EQ(gfx::RectF(9, 0, 1, 1), patches[1].image_rect); + EXPECT_EQ(gfx::RectF(19, 0, 1, 1), patches[1].output_rect); + EXPECT_EQ(gfx::RectF(0.9f, 0.f, 0.1f, 0.1f), + patches[1].normalized_image_rect); + + // Top-left + EXPECT_EQ(gfx::RectF(0, 0, 1, 1), patches[0].image_rect); + EXPECT_EQ(gfx::RectF(0, 0, 1, 1), patches[0].output_rect); + EXPECT_EQ(gfx::RectF(0.f, 0.f, 0.1f, 0.1f), patches[0].normalized_image_rect); +} + +} // namespace +} // namespace cc diff --git a/cc/test/data/overlay_scrollbar_scaled_down.png b/cc/test/data/overlay_scrollbar_scaled_down.png new file mode 100644 index 00000000000000..b4497ba1e6cc62 Binary files /dev/null and b/cc/test/data/overlay_scrollbar_scaled_down.png differ diff --git a/cc/test/data/overlay_scrollbar_scaled_up.png b/cc/test/data/overlay_scrollbar_scaled_up.png new file mode 100644 index 00000000000000..f3ad6d22413c46 Binary files /dev/null and b/cc/test/data/overlay_scrollbar_scaled_up.png differ diff --git a/cc/test/fake_scrollbar.cc b/cc/test/fake_scrollbar.cc index 7ddafd486ce56c..57a6ceefa2de84 100644 --- a/cc/test/fake_scrollbar.cc +++ b/cc/test/fake_scrollbar.cc @@ -91,4 +91,16 @@ void FakeScrollbar::PaintPart(PaintCanvas* canvas, canvas->drawRect(rect, flags); } +bool FakeScrollbar::UsesNinePatchThumbResource() const { + return false; +} + +gfx::Size FakeScrollbar::NinePatchThumbCanvasSize() const { + return gfx::Size(); +} + +gfx::Rect FakeScrollbar::NinePatchThumbAperture() const { + return gfx::Rect(); +} + } // namespace cc diff --git a/cc/test/fake_scrollbar.h b/cc/test/fake_scrollbar.h index dff1feeec990fb..f5a3bd7a10f157 100644 --- a/cc/test/fake_scrollbar.h +++ b/cc/test/fake_scrollbar.h @@ -37,6 +37,9 @@ class FakeScrollbar : public Scrollbar { void PaintPart(PaintCanvas* canvas, ScrollbarPart part, const gfx::Rect& content_rect) override; + bool UsesNinePatchThumbResource() const override; + gfx::Size NinePatchThumbCanvasSize() const override; + gfx::Rect NinePatchThumbAperture() const override; void set_location(const gfx::Point& location) { location_ = location; } void set_track_rect(const gfx::Rect& track_rect) { track_rect_ = track_rect; } diff --git a/cc/trees/layer_tree_host_pixeltest_scrollbars.cc b/cc/trees/layer_tree_host_pixeltest_scrollbars.cc index 683a5d491a5b27..9e6c405beb3097 100644 --- a/cc/trees/layer_tree_host_pixeltest_scrollbars.cc +++ b/cc/trees/layer_tree_host_pixeltest_scrollbars.cc @@ -7,12 +7,14 @@ #include "base/memory/ptr_util.h" #include "build/build_config.h" #include "cc/input/scrollbar.h" +#include "cc/layers/painted_overlay_scrollbar_layer.h" #include "cc/layers/painted_scrollbar_layer.h" #include "cc/layers/solid_color_layer.h" #include "cc/paint/paint_canvas.h" #include "cc/paint/paint_flags.h" #include "cc/test/layer_tree_pixel_test.h" #include "cc/test/test_in_process_context_provider.h" +#include "cc/trees/layer_tree_impl.h" #include "gpu/command_buffer/client/gles2_interface.h" #if !defined(OS_ANDROID) @@ -67,6 +69,9 @@ class PaintedScrollbar : public Scrollbar { inset_rect.Inset(big, big, small, small); } } + bool UsesNinePatchThumbResource() const override { return false; } + gfx::Size NinePatchThumbCanvasSize() const override { return gfx::Size(); } + gfx::Rect NinePatchThumbAperture() const override { return gfx::Rect(); } void set_paint_scale(int scale) { paint_scale_ = scale; } @@ -168,6 +173,114 @@ TEST_F(LayerTreeHostScrollbarsPixelTest, HugeTransformScale) { base::FilePath(FILE_PATH_LITERAL("spiral_64_scale.png"))); } +class LayerTreeHostOverlayScrollbarsPixelTest + : public LayerTreeHostScrollbarsPixelTest { + protected: + LayerTreeHostOverlayScrollbarsPixelTest() = default; + + void DidActivateTreeOnThread(LayerTreeHostImpl* host_impl) override { + LayerImpl* layer = host_impl->active_tree()->LayerById(scrollbar_layer_id_); + ScrollbarLayerImplBase* scrollbar = layer->ToScrollbarLayer(); + scrollbar->SetThumbThicknessScaleFactor(thickness_scale_); + } + + int scrollbar_layer_id_; + float thickness_scale_; +}; + +class PaintedOverlayScrollbar : public PaintedScrollbar { + public: + ~PaintedOverlayScrollbar() override = default; + + int ThumbThickness() const override { return 15; } + int ThumbLength() const override { return 50; } + gfx::Rect TrackRect() const override { return gfx::Rect(0, 0, 15, 400); } + bool HasThumb() const override { return true; } + bool IsOverlay() const override { return true; } + void PaintPart(PaintCanvas* canvas, + ScrollbarPart part, + const gfx::Rect& content_rect) override { + // The outside of the rect will be painted with a 1 pixel black, red, then + // blue border. The inside will be solid blue. This will allow the test to + // ensure that scaling the thumb doesn't scale the border at all. Note + // that the inside of the border must be the same color as the center tile + // to prevent an interpolation from being applied. + PaintFlags flags; + flags.setStyle(PaintFlags::kFill_Style); + flags.setStrokeWidth(SkIntToScalar(1)); + flags.setColor(SK_ColorBLACK); + + gfx::Rect inset_rect = content_rect; + + canvas->drawRect(RectToSkRect(inset_rect), flags); + + flags.setColor(SK_ColorRED); + inset_rect.Inset(1, 1); + canvas->drawRect(RectToSkRect(inset_rect), flags); + + flags.setColor(SK_ColorBLUE); + inset_rect.Inset(1, 1); + canvas->drawRect(RectToSkRect(inset_rect), flags); + } + bool UsesNinePatchThumbResource() const override { return true; } + gfx::Size NinePatchThumbCanvasSize() const override { + return gfx::Size(7, 7); + } + gfx::Rect NinePatchThumbAperture() const override { + return gfx::Rect(3, 3, 1, 1); + } +}; + +// Simulate increasing the thickness of a painted overlay scrollbar. Ensure that +// the scrollbar border remains crisp. +TEST_F(LayerTreeHostOverlayScrollbarsPixelTest, NinePatchScrollbarScaledUp) { + scoped_refptr background = + CreateSolidColorLayer(gfx::Rect(400, 400), SK_ColorWHITE); + + auto scrollbar = base::MakeUnique(); + scoped_refptr layer = + PaintedOverlayScrollbarLayer::Create(std::move(scrollbar), + Layer::INVALID_ID); + + scrollbar_layer_id_ = layer->id(); + thickness_scale_ = 5.f; + + layer->SetIsDrawable(true); + layer->SetBounds(gfx::Size(10, 300)); + background->AddChild(layer); + + layer->SetPosition(gfx::PointF(185, 10)); + + RunPixelTest( + PIXEL_TEST_GL, background, + base::FilePath(FILE_PATH_LITERAL("overlay_scrollbar_scaled_up.png"))); +} + +// Simulate decreasing the thickness of a painted overlay scrollbar. Ensure that +// the scrollbar border remains crisp. +TEST_F(LayerTreeHostOverlayScrollbarsPixelTest, NinePatchScrollbarScaledDown) { + scoped_refptr background = + CreateSolidColorLayer(gfx::Rect(400, 400), SK_ColorWHITE); + + auto scrollbar = base::MakeUnique(); + scoped_refptr layer = + PaintedOverlayScrollbarLayer::Create(std::move(scrollbar), + Layer::INVALID_ID); + + scrollbar_layer_id_ = layer->id(); + thickness_scale_ = 0.4f; + + layer->SetIsDrawable(true); + layer->SetBounds(gfx::Size(10, 300)); + background->AddChild(layer); + + layer->SetPosition(gfx::PointF(185, 10)); + + RunPixelTest( + PIXEL_TEST_GL, background, + base::FilePath(FILE_PATH_LITERAL("overlay_scrollbar_scaled_down.png"))); +} + } // namespace } // namespace cc diff --git a/content/child/webthemeengine_impl_default.cc b/content/child/webthemeengine_impl_default.cc index 169f96952c08e7..6014d2b3072dc1 100644 --- a/content/child/webthemeengine_impl_default.cc +++ b/content/child/webthemeengine_impl_default.cc @@ -253,6 +253,21 @@ void WebThemeEngineImpl::getOverlayScrollbarStyle(ScrollbarStyle* style) { // NativeTheme so these fields are unused. } +bool WebThemeEngineImpl::supportsNinePatch(Part part) const { + return ui::NativeTheme::GetInstanceForWeb()->SupportsNinePatch( + NativeThemePart(part)); +} + +blink::WebSize WebThemeEngineImpl::ninePatchCanvasSize(Part part) const { + return ui::NativeTheme::GetInstanceForWeb()->GetNinePatchCanvasSize( + NativeThemePart(part)); +} + +blink::WebRect WebThemeEngineImpl::ninePatchAperture(Part part) const { + return ui::NativeTheme::GetInstanceForWeb()->GetNinePatchAperture( + NativeThemePart(part)); +} + #if defined(OS_WIN) // static void WebThemeEngineImpl::cacheScrollBarMetrics( diff --git a/content/child/webthemeengine_impl_default.h b/content/child/webthemeengine_impl_default.h index 6f72953fc82f5d..02d2337e0b2377 100644 --- a/content/child/webthemeengine_impl_default.h +++ b/content/child/webthemeengine_impl_default.h @@ -23,6 +23,9 @@ class WebThemeEngineImpl : public blink::WebThemeEngine { const blink::WebThemeEngine::ExtraParams* extra_params) override; void getOverlayScrollbarStyle( blink::WebThemeEngine::ScrollbarStyle*) override; + bool supportsNinePatch(Part part) const override; + blink::WebSize ninePatchCanvasSize(Part part) const override; + blink::WebRect ninePatchAperture(Part part) const override; #if defined(OS_WIN) // Caches the scrollbar metrics. These are retrieved in the browser and passed // to the renderer in RendererPreferences because the required Windows system diff --git a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp index 0c721230e8ad8f..970e26a12dd3a7 100644 --- a/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp +++ b/third_party/WebKit/Source/core/page/scrolling/ScrollingCoordinator.cpp @@ -345,9 +345,16 @@ static std::unique_ptr createScrollbarLayer( std::unique_ptr geometry( WebScrollbarThemeGeometryNative::create(theme)); - std::unique_ptr scrollbarLayer = - Platform::current()->compositorSupport()->createScrollbarLayer( - WebScrollbarImpl::create(&scrollbar), painter, std::move(geometry)); + std::unique_ptr scrollbarLayer; + if (theme.usesOverlayScrollbars() && theme.usesNinePatchThumbResource()) { + scrollbarLayer = + Platform::current()->compositorSupport()->createOverlayScrollbarLayer( + WebScrollbarImpl::create(&scrollbar), painter, std::move(geometry)); + } else { + scrollbarLayer = + Platform::current()->compositorSupport()->createScrollbarLayer( + WebScrollbarImpl::create(&scrollbar), painter, std::move(geometry)); + } GraphicsLayer::registerContentsLayer(scrollbarLayer->layer()); return scrollbarLayer; } diff --git a/third_party/WebKit/Source/platform/exported/WebScrollbarThemeGeometryNative.cpp b/third_party/WebKit/Source/platform/exported/WebScrollbarThemeGeometryNative.cpp index 157dacadef7af1..be7bd6954994ce 100644 --- a/third_party/WebKit/Source/platform/exported/WebScrollbarThemeGeometryNative.cpp +++ b/third_party/WebKit/Source/platform/exported/WebScrollbarThemeGeometryNative.cpp @@ -82,4 +82,18 @@ WebRect WebScrollbarThemeGeometryNative::forwardButtonEndRect( ForwardButtonEndPart, false); } +WebSize WebScrollbarThemeGeometryNative::ninePatchThumbCanvasSize( + WebScrollbar* scrollbar) { + DCHECK(m_theme.usesNinePatchThumbResource()); + return m_theme.ninePatchThumbCanvasSize( + WebScrollbarThemeClientImpl(*scrollbar)); +} + +WebRect WebScrollbarThemeGeometryNative::ninePatchThumbAperture( + WebScrollbar* scrollbar) { + DCHECK(m_theme.usesNinePatchThumbResource()); + return m_theme.ninePatchThumbAperture( + WebScrollbarThemeClientImpl(*scrollbar)); +} + } // namespace blink diff --git a/third_party/WebKit/Source/platform/exported/WebScrollbarThemeGeometryNative.h b/third_party/WebKit/Source/platform/exported/WebScrollbarThemeGeometryNative.h index 195e2b64147323..b2e9e6cff8af0d 100644 --- a/third_party/WebKit/Source/platform/exported/WebScrollbarThemeGeometryNative.h +++ b/third_party/WebKit/Source/platform/exported/WebScrollbarThemeGeometryNative.h @@ -55,6 +55,8 @@ class PLATFORM_EXPORT WebScrollbarThemeGeometryNative WebRect backButtonEndRect(WebScrollbar*) override; WebRect forwardButtonStartRect(WebScrollbar*) override; WebRect forwardButtonEndRect(WebScrollbar*) override; + WebSize ninePatchThumbCanvasSize(WebScrollbar*) override; + WebRect ninePatchThumbAperture(WebScrollbar*) override; private: explicit WebScrollbarThemeGeometryNative(ScrollbarTheme&); diff --git a/third_party/WebKit/Source/platform/exported/WebScrollbarThemePainter.cpp b/third_party/WebKit/Source/platform/exported/WebScrollbarThemePainter.cpp index 0f9a326966d4e4..7dce90e628cf7f 100644 --- a/third_party/WebKit/Source/platform/exported/WebScrollbarThemePainter.cpp +++ b/third_party/WebKit/Source/platform/exported/WebScrollbarThemePainter.cpp @@ -167,4 +167,8 @@ bool WebScrollbarThemePainter::thumbNeedsRepaint() const { return m_scrollbar->thumbNeedsRepaint(); } +bool WebScrollbarThemePainter::usesNinePatchThumbResource() const { + return m_theme->usesNinePatchThumbResource(); +} + } // namespace blink diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h b/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h index a7a5f702f1c91f..7460d9ebbdf7ff 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h +++ b/third_party/WebKit/Source/platform/scroll/ScrollbarTheme.h @@ -185,6 +185,24 @@ class PLATFORM_EXPORT ScrollbarTheme { virtual bool isMockTheme() const { return false; } + virtual bool usesNinePatchThumbResource() const { return false; } + + // For a nine-patch scrollbar, this defines the painting canvas size which the + // painting code will use to paint the scrollbar into. The actual scrollbar + // dimensions will be ignored for purposes of painting since the resource can + // be then resized without a repaint. + virtual IntSize ninePatchThumbCanvasSize(const ScrollbarThemeClient&) const { + NOTREACHED(); + return IntSize(); + } + + // For a nine-patch resource, the aperture defines the center patch that will + // be stretched out. + virtual IntRect ninePatchThumbAperture(const ScrollbarThemeClient&) const { + NOTREACHED(); + return IntRect(); + } + static ScrollbarTheme& theme(); static void setMockScrollbarsEnabled(bool flag); diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp index 9753dcc192827a..30ba2b9554699d 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp +++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.cpp @@ -236,4 +236,38 @@ ScrollbarThemeOverlay& ScrollbarThemeOverlay::mobileTheme() { return *theme; } +bool ScrollbarThemeOverlay::usesNinePatchThumbResource() const { + WebThemeEngine* engine = Platform::current()->themeEngine(); + if (!engine) + return false; + + // Thumb orientation doesn't matter here. + return engine->supportsNinePatch(WebThemeEngine::PartScrollbarVerticalThumb); +} + +IntSize ScrollbarThemeOverlay::ninePatchThumbCanvasSize( + const ScrollbarThemeClient& scrollbar) const { + DCHECK(usesNinePatchThumbResource()); + + WebThemeEngine::Part part = + scrollbar.orientation() == VerticalScrollbar + ? WebThemeEngine::PartScrollbarVerticalThumb + : WebThemeEngine::PartScrollbarHorizontalThumb; + + DCHECK(Platform::current()->themeEngine()); + return Platform::current()->themeEngine()->ninePatchCanvasSize(part); +} + +IntRect ScrollbarThemeOverlay::ninePatchThumbAperture( + const ScrollbarThemeClient& scrollbar) const { + DCHECK(usesNinePatchThumbResource()); + + WebThemeEngine::Part part = WebThemeEngine::PartScrollbarHorizontalThumb; + if (scrollbar.orientation() == VerticalScrollbar) + part = WebThemeEngine::PartScrollbarVerticalThumb; + + DCHECK(Platform::current()->themeEngine()); + return Platform::current()->themeEngine()->ninePatchAperture(part); +} + } // namespace blink diff --git a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h index 0772e2136ff5d4..811cb6a1b3fbc6 100644 --- a/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h +++ b/third_party/WebKit/Source/platform/scroll/ScrollbarThemeOverlay.h @@ -81,6 +81,10 @@ class PLATFORM_EXPORT ScrollbarThemeOverlay : public ScrollbarTheme { void paintThumb(GraphicsContext&, const Scrollbar&, const IntRect&) override; ScrollbarPart hitTest(const ScrollbarThemeClient&, const IntPoint&) override; + bool usesNinePatchThumbResource() const override; + IntSize ninePatchThumbCanvasSize(const ScrollbarThemeClient&) const override; + IntRect ninePatchThumbAperture(const ScrollbarThemeClient&) const override; + static ScrollbarThemeOverlay& mobileTheme(); private: diff --git a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp index 75d00bb44bdb3d..59ba9472484553 100644 --- a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp +++ b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.cpp @@ -124,6 +124,14 @@ TestingCompositorSupport::createScrollbarLayer( return nullptr; } +std::unique_ptr +TestingCompositorSupport::createOverlayScrollbarLayer( + std::unique_ptr, + WebScrollbarThemePainter, + std::unique_ptr) { + return nullptr; +} + std::unique_ptr TestingCompositorSupport::createSolidColorScrollbarLayer( WebScrollbar::Orientation, diff --git a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h index 71aeb9c85420b7..3c052d18d16f4b 100644 --- a/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h +++ b/third_party/WebKit/Source/platform/testing/TestingPlatformSupport.h @@ -77,6 +77,10 @@ class TestingCompositorSupport : public WebCompositorSupport { std::unique_ptr, WebScrollbarThemePainter, std::unique_ptr) override; + std::unique_ptr createOverlayScrollbarLayer( + std::unique_ptr, + WebScrollbarThemePainter, + std::unique_ptr) override; std::unique_ptr createSolidColorScrollbarLayer( WebScrollbar::Orientation, int thumbThickness, diff --git a/third_party/WebKit/public/platform/WebCompositorSupport.h b/third_party/WebKit/public/platform/WebCompositorSupport.h index 0ef1b3cae2ef24..5c3f706aaa1df1 100644 --- a/third_party/WebKit/public/platform/WebCompositorSupport.h +++ b/third_party/WebKit/public/platform/WebCompositorSupport.h @@ -70,6 +70,11 @@ class WebCompositorSupport { WebScrollbarThemePainter, std::unique_ptr) = 0; + virtual std::unique_ptr createOverlayScrollbarLayer( + std::unique_ptr, + WebScrollbarThemePainter, + std::unique_ptr) = 0; + virtual std::unique_ptr createSolidColorScrollbarLayer( WebScrollbar::Orientation, int thumbThickness, diff --git a/third_party/WebKit/public/platform/WebScrollbarThemeGeometry.h b/third_party/WebKit/public/platform/WebScrollbarThemeGeometry.h index a2c89794a39a6c..c6291071f83c19 100644 --- a/third_party/WebKit/public/platform/WebScrollbarThemeGeometry.h +++ b/third_party/WebKit/public/platform/WebScrollbarThemeGeometry.h @@ -27,6 +27,7 @@ #define WebScrollbarThemeGeometry_h #include "WebRect.h" +#include "WebSize.h" namespace blink { @@ -44,6 +45,8 @@ class BLINK_PLATFORM_EXPORT WebScrollbarThemeGeometry { virtual WebRect backButtonEndRect(WebScrollbar*) = 0; virtual WebRect forwardButtonStartRect(WebScrollbar*) = 0; virtual WebRect forwardButtonEndRect(WebScrollbar*) = 0; + virtual WebSize ninePatchThumbCanvasSize(WebScrollbar*) = 0; + virtual WebRect ninePatchThumbAperture(WebScrollbar*) = 0; }; } // namespace blink diff --git a/third_party/WebKit/public/platform/WebScrollbarThemePainter.h b/third_party/WebKit/public/platform/WebScrollbarThemePainter.h index 665e746c760580..b46df87ae6e077 100644 --- a/third_party/WebKit/public/platform/WebScrollbarThemePainter.h +++ b/third_party/WebKit/public/platform/WebScrollbarThemePainter.h @@ -28,13 +28,14 @@ #include "public/platform/WebCanvas.h" #include "public/platform/WebPrivatePtr.h" +#include "public/platform/WebRect.h" +#include "public/platform/WebSize.h" namespace blink { class ScrollbarTheme; class Scrollbar; class WebScrollbar; -struct WebRect; class WebScrollbarThemePainter { public: @@ -71,6 +72,8 @@ class WebScrollbarThemePainter { BLINK_PLATFORM_EXPORT bool trackNeedsRepaint() const; BLINK_PLATFORM_EXPORT bool thumbNeedsRepaint() const; + BLINK_PLATFORM_EXPORT bool usesNinePatchThumbResource() const; + #if INSIDE_BLINK BLINK_PLATFORM_EXPORT WebScrollbarThemePainter(ScrollbarTheme&, Scrollbar&, diff --git a/third_party/WebKit/public/platform/WebThemeEngine.h b/third_party/WebKit/public/platform/WebThemeEngine.h index 2387f462111bc2..51d6d1a5286f32 100644 --- a/third_party/WebKit/public/platform/WebThemeEngine.h +++ b/third_party/WebKit/public/platform/WebThemeEngine.h @@ -33,13 +33,12 @@ #include "WebCanvas.h" #include "WebColor.h" +#include "WebRect.h" #include "WebScrollbarOverlayColorTheme.h" #include "WebSize.h" namespace blink { -struct WebRect; - class WebThemeEngine { public: // The current state of the associated Part. @@ -159,6 +158,10 @@ class WebThemeEngine { // the track while the height will be the minimum height. virtual WebSize getSize(Part) { return WebSize(); } + virtual bool supportsNinePatch(Part) const { return false; } + virtual WebSize ninePatchCanvasSize(Part) const { return WebSize(); } + virtual WebRect ninePatchAperture(Part) const { return WebRect(); } + struct ScrollbarStyle { int thumbThickness; int scrollbarMargin; diff --git a/ui/native_theme/native_theme.h b/ui/native_theme/native_theme.h index b9a7b6969a3e8f..7aa469a50b537f 100644 --- a/ui/native_theme/native_theme.h +++ b/ui/native_theme/native_theme.h @@ -11,6 +11,8 @@ #include "cc/paint/paint_canvas.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/models/menu_separator_types.h" +#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/size.h" #include "ui/gfx/native_widget_types.h" #include "ui/native_theme/native_theme_export.h" @@ -267,6 +269,20 @@ class NATIVE_THEME_EXPORT NativeTheme { const gfx::Rect& rect, ScrollbarOverlayColorTheme theme) const {} + // Returns whether the theme uses a nine-patch resource for the given part. + // If true, calling code should always paint into a canvas the size of which + // can be gotten from GetNinePatchCanvasSize. + virtual bool SupportsNinePatch(Part part) const = 0; + + // If the part paints into a nine-patch resource, the size of the canvas + // which should be painted into. + virtual gfx::Size GetNinePatchCanvasSize(Part part) const = 0; + + // If the part paints into a nine-patch resource, the rect in the canvas + // which defines the center tile. This is the tile that should be resized out + // when the part is resized. + virtual gfx::Rect GetNinePatchAperture(Part part) const = 0; + // Supports theme specific colors. void SetScrollbarColors(unsigned inactive_color, unsigned active_color, diff --git a/ui/native_theme/native_theme_aura.cc b/ui/native_theme/native_theme_aura.cc index 0c3255cb18b674..76283878e06a13 100644 --- a/ui/native_theme/native_theme_aura.cc +++ b/ui/native_theme/native_theme_aura.cc @@ -35,6 +35,12 @@ namespace { constexpr int kOverlayScrollbarStrokeWidth = 1; constexpr int kOverlayScrollbarMinimumLength = 12; +// 2 pixel border with 1 pixel center patch. The border is 2 pixels despite the +// stroke width being 1 so that the inner pixel can match the center tile +// color. This prevents color interpolation between the patches. +constexpr int kOverlayScrollbarBorderPatchWidth = 2; +constexpr int kOverlayScrollbarCenterPatchSize = 1; + const SkColor kTrackColor = SkColorSetRGB(0xF1, 0xF1, 0xF1); } // namespace @@ -317,4 +323,27 @@ gfx::Size NativeThemeAura::GetPartSize(Part part, return NativeThemeBase::GetPartSize(part, state, extra); } +bool NativeThemeAura::SupportsNinePatch(Part part) const { + if (!IsOverlayScrollbarEnabled()) + return false; + + return part == kScrollbarHorizontalThumb || part == kScrollbarVerticalThumb; +} + +gfx::Size NativeThemeAura::GetNinePatchCanvasSize(Part part) const { + DCHECK(SupportsNinePatch(part)); + + return gfx::Size( + kOverlayScrollbarBorderPatchWidth * 2 + kOverlayScrollbarCenterPatchSize, + kOverlayScrollbarBorderPatchWidth * 2 + kOverlayScrollbarCenterPatchSize); +} + +gfx::Rect NativeThemeAura::GetNinePatchAperture(Part part) const { + DCHECK(SupportsNinePatch(part)); + + return gfx::Rect( + kOverlayScrollbarBorderPatchWidth, kOverlayScrollbarBorderPatchWidth, + kOverlayScrollbarCenterPatchSize, kOverlayScrollbarCenterPatchSize); +} + } // namespace ui diff --git a/ui/native_theme/native_theme_aura.h b/ui/native_theme/native_theme_aura.h index 6212613614830b..eeebd051e3c120 100644 --- a/ui/native_theme/native_theme_aura.h +++ b/ui/native_theme/native_theme_aura.h @@ -53,6 +53,9 @@ class NATIVE_THEME_EXPORT NativeThemeAura : public NativeThemeBase { gfx::Size GetPartSize(Part part, State state, const ExtraParams& extra) const override; + bool SupportsNinePatch(Part part) const override; + gfx::Size GetNinePatchCanvasSize(Part part) const override; + gfx::Rect GetNinePatchAperture(Part part) const override; private: bool use_overlay_scrollbars_; diff --git a/ui/native_theme/native_theme_base.cc b/ui/native_theme/native_theme_base.cc index e6a27a14bc27df..4a2fb957ed0f5e 100644 --- a/ui/native_theme/native_theme_base.cc +++ b/ui/native_theme/native_theme_base.cc @@ -251,6 +251,20 @@ void NativeThemeBase::Paint(cc::PaintCanvas* canvas, canvas->restore(); } +bool NativeThemeBase::SupportsNinePatch(Part part) const { + return false; +} + +gfx::Size NativeThemeBase::GetNinePatchCanvasSize(Part part) const { + NOTREACHED() << "NativeThemeBase doesn't support nine-patch resources."; + return gfx::Size(); +} + +gfx::Rect NativeThemeBase::GetNinePatchAperture(Part part) const { + NOTREACHED() << "NativeThemeBase doesn't support nine-patch resources."; + return gfx::Rect(); +} + NativeThemeBase::NativeThemeBase() : scrollbar_width_(kDefaultScrollbarWidth), scrollbar_button_length_(kDefaultScrollbarButtonLength) { diff --git a/ui/native_theme/native_theme_base.h b/ui/native_theme/native_theme_base.h index 00675ab211872f..0e349c5dee2b14 100644 --- a/ui/native_theme/native_theme_base.h +++ b/ui/native_theme/native_theme_base.h @@ -33,6 +33,10 @@ class NATIVE_THEME_EXPORT NativeThemeBase : public NativeTheme { const gfx::Rect& rect, const ExtraParams& extra) const override; + bool SupportsNinePatch(Part part) const override; + gfx::Size GetNinePatchCanvasSize(Part part) const override; + gfx::Rect GetNinePatchAperture(Part part) const override; + protected: NativeThemeBase(); ~NativeThemeBase() override; diff --git a/ui/native_theme/native_theme_win.cc b/ui/native_theme/native_theme_win.cc index e0d92f10cee022..8c4a19042cee0b 100644 --- a/ui/native_theme/native_theme_win.cc +++ b/ui/native_theme/native_theme_win.cc @@ -660,6 +660,22 @@ SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const { return GetAuraColor(color_id, this); } +bool NativeThemeWin::SupportsNinePatch(Part part) const { + // The only nine-patch resources currently supported (overlay scrollbar) are + // painted by NativeThemeAura on Windows. + return false; +} + +gfx::Size NativeThemeWin::GetNinePatchCanvasSize(Part part) const { + NOTREACHED() << "NativeThemeWin doesn't support nine-patch resources."; + return gfx::Size(); +} + +gfx::Rect NativeThemeWin::GetNinePatchAperture(Part part) const { + NOTREACHED() << "NativeThemeWin doesn't support nine-patch resources."; + return gfx::Rect(); +} + void NativeThemeWin::PaintIndirect(cc::PaintCanvas* destination_canvas, Part part, State state, diff --git a/ui/native_theme/native_theme_win.h b/ui/native_theme/native_theme_win.h index 2f83a07b5e30bc..d2585e41d057b1 100644 --- a/ui/native_theme/native_theme_win.h +++ b/ui/native_theme/native_theme_win.h @@ -109,6 +109,10 @@ class NATIVE_THEME_EXPORT NativeThemeWin : public NativeTheme, const ExtraParams& extra) const override; SkColor GetSystemColor(ColorId color_id) const override; + bool SupportsNinePatch(Part part) const override; + gfx::Size GetNinePatchCanvasSize(Part part) const override; + gfx::Rect GetNinePatchAperture(Part part) const override; + protected: friend class NativeTheme; // Gets our singleton instance. diff --git a/ui/views/view_unittest.cc b/ui/views/view_unittest.cc index 4753be7534ecfd..6fe827757635ba 100644 --- a/ui/views/view_unittest.cc +++ b/ui/views/view_unittest.cc @@ -4565,6 +4565,14 @@ class TestNativeTheme : public ui::NativeTheme { const gfx::Rect& rect, const ExtraParams& extra) const override {} + bool SupportsNinePatch(Part part) const override { return false; } + gfx::Size GetNinePatchCanvasSize(Part part) const override { + return gfx::Size(); + } + gfx::Rect GetNinePatchAperture(Part part) const override { + return gfx::Rect(); + } + private: DISALLOW_COPY_AND_ASSIGN(TestNativeTheme); };