1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "cc/layers/painted_scrollbar_layer.h"
6
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10
11 #include "base/auto_reset.h"
12 #include "cc/layers/painted_scrollbar_layer_impl.h"
13 #include "cc/paint/skia_paint_canvas.h"
14 #include "cc/trees/draw_property_utils.h"
15 #include "cc/trees/layer_tree_host.h"
16 #include "third_party/skia/include/core/SkBitmap.h"
17
18 namespace cc {
19
CreateLayerImpl(LayerTreeImpl * tree_impl)20 std::unique_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl(
21 LayerTreeImpl* tree_impl) {
22 return PaintedScrollbarLayerImpl::Create(tree_impl, id(), orientation(),
23 is_left_side_vertical_scrollbar(),
24 is_overlay_);
25 }
26
CreateOrReuse(scoped_refptr<Scrollbar> scrollbar,PaintedScrollbarLayer * existing_layer)27 scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::CreateOrReuse(
28 scoped_refptr<Scrollbar> scrollbar,
29 PaintedScrollbarLayer* existing_layer) {
30 if (existing_layer && existing_layer->scrollbar_->IsSame(*scrollbar))
31 return existing_layer;
32 return Create(std::move(scrollbar));
33 }
34
Create(scoped_refptr<Scrollbar> scrollbar)35 scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::Create(
36 scoped_refptr<Scrollbar> scrollbar) {
37 return base::WrapRefCounted(new PaintedScrollbarLayer(std::move(scrollbar)));
38 }
39
PaintedScrollbarLayer(scoped_refptr<Scrollbar> scrollbar)40 PaintedScrollbarLayer::PaintedScrollbarLayer(scoped_refptr<Scrollbar> scrollbar)
41 : ScrollbarLayerBase(scrollbar->Orientation(),
42 scrollbar->IsLeftSideVerticalScrollbar()),
43 scrollbar_(std::move(scrollbar)),
44 internal_contents_scale_(1.f),
45 painted_opacity_(scrollbar_->Opacity()),
46 has_thumb_(scrollbar_->HasThumb()),
47 jump_on_track_click_(scrollbar_->JumpOnTrackClick()),
48 supports_drag_snap_back_(scrollbar_->SupportsDragSnapBack()),
49 is_overlay_(scrollbar_->IsOverlay()) {}
50
51 PaintedScrollbarLayer::~PaintedScrollbarLayer() = default;
52
OpacityCanAnimateOnImplThread() const53 bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
54 return is_overlay_;
55 }
56
PushPropertiesTo(LayerImpl * layer)57 void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
58 ScrollbarLayerBase::PushPropertiesTo(layer);
59
60 PaintedScrollbarLayerImpl* scrollbar_layer =
61 static_cast<PaintedScrollbarLayerImpl*>(layer);
62
63 scrollbar_layer->set_internal_contents_scale_and_bounds(
64 internal_contents_scale_, internal_content_bounds_);
65
66 scrollbar_layer->SetJumpOnTrackClick(jump_on_track_click_);
67 scrollbar_layer->SetSupportsDragSnapBack(supports_drag_snap_back_);
68 scrollbar_layer->SetBackButtonRect(back_button_rect_);
69 scrollbar_layer->SetForwardButtonRect(forward_button_rect_);
70 scrollbar_layer->SetTrackRect(track_rect_);
71 if (orientation() == ScrollbarOrientation::HORIZONTAL) {
72 scrollbar_layer->SetThumbThickness(thumb_size_.height());
73 scrollbar_layer->SetThumbLength(thumb_size_.width());
74 } else {
75 scrollbar_layer->SetThumbThickness(thumb_size_.width());
76 scrollbar_layer->SetThumbLength(thumb_size_.height());
77 }
78
79 if (track_resource_.get())
80 scrollbar_layer->set_track_ui_resource_id(track_resource_->id());
81 else
82 scrollbar_layer->set_track_ui_resource_id(0);
83 if (thumb_resource_.get())
84 scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id());
85 else
86 scrollbar_layer->set_thumb_ui_resource_id(0);
87
88 scrollbar_layer->set_scrollbar_painted_opacity(painted_opacity_);
89
90 scrollbar_layer->set_is_overlay_scrollbar(is_overlay_);
91 }
92
SetLayerTreeHost(LayerTreeHost * host)93 void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
94 // When the LTH is set to null or has changed, then this layer should remove
95 // all of its associated resources.
96 if (!host || host != layer_tree_host()) {
97 track_resource_ = nullptr;
98 thumb_resource_ = nullptr;
99 }
100
101 ScrollbarLayerBase::SetLayerTreeHost(host);
102 }
103
LayerSizeToContentSize(const gfx::Size & layer_size) const104 gfx::Size PaintedScrollbarLayer::LayerSizeToContentSize(
105 const gfx::Size& layer_size) const {
106 gfx::Size content_size =
107 gfx::ScaleToCeiledSize(layer_size, internal_contents_scale_);
108 // We should never return a rect bigger than the content bounds.
109 content_size.SetToMin(internal_content_bounds_);
110 return content_size;
111 }
112
UpdateThumbAndTrackGeometry()113 bool PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
114 // These properties should never change.
115 DCHECK_EQ(supports_drag_snap_back_, scrollbar_->SupportsDragSnapBack());
116 DCHECK_EQ(is_left_side_vertical_scrollbar(),
117 scrollbar_->IsLeftSideVerticalScrollbar());
118 DCHECK_EQ(is_overlay_, scrollbar_->IsOverlay());
119 DCHECK_EQ(orientation(), scrollbar_->Orientation());
120
121 bool updated = false;
122 updated |=
123 UpdateProperty(scrollbar_->JumpOnTrackClick(), &jump_on_track_click_);
124 updated |= UpdateProperty(scrollbar_->TrackRect(), &track_rect_);
125 updated |= UpdateProperty(scrollbar_->BackButtonRect(), &back_button_rect_);
126 updated |=
127 UpdateProperty(scrollbar_->ForwardButtonRect(), &forward_button_rect_);
128 updated |= UpdateProperty(scrollbar_->HasThumb(), &has_thumb_);
129 if (has_thumb_) {
130 // Ignore ThumbRect's location because the PaintedScrollbarLayerImpl will
131 // compute it from scroll offset.
132 updated |= UpdateProperty(scrollbar_->ThumbRect().size(), &thumb_size_);
133 } else {
134 updated |= UpdateProperty(gfx::Size(), &thumb_size_);
135 }
136 return updated;
137 }
138
UpdateInternalContentScale()139 bool PaintedScrollbarLayer::UpdateInternalContentScale() {
140 gfx::Transform transform;
141 transform = draw_property_utils::ScreenSpaceTransform(
142 this, layer_tree_host()->property_trees()->transform_tree);
143
144 gfx::Vector2dF transform_scales = MathUtil::ComputeTransform2dScaleComponents(
145 transform, layer_tree_host()->device_scale_factor());
146 float scale = std::max(transform_scales.x(), transform_scales.y());
147 // Clamp minimum scale to 1 to avoid too low scale during scale animation.
148 // TODO(crbug.com/1009291): Move rasterization of scrollbars to the impl side
149 // to better handle scale changes.
150 scale = std::max(1.0f, scale);
151
152 bool updated = false;
153 updated |= UpdateProperty(scale, &internal_contents_scale_);
154 updated |=
155 UpdateProperty(gfx::ScaleToCeiledSize(bounds(), internal_contents_scale_),
156 &internal_content_bounds_);
157 return updated;
158 }
159
Update()160 bool PaintedScrollbarLayer::Update() {
161 bool updated = false;
162
163 updated |= ScrollbarLayerBase::Update();
164 updated |= UpdateInternalContentScale();
165 updated |= UpdateThumbAndTrackGeometry();
166
167 gfx::Size size = bounds();
168 gfx::Size scaled_size = internal_content_bounds_;
169
170 if (scaled_size.IsEmpty()) {
171 if (track_resource_) {
172 track_resource_ = nullptr;
173 thumb_resource_ = nullptr;
174 SetNeedsPushProperties();
175 updated = true;
176 }
177 return updated;
178 }
179
180 if (!has_thumb_ && thumb_resource_) {
181 thumb_resource_ = nullptr;
182 SetNeedsPushProperties();
183 updated = true;
184 }
185
186 if (!track_resource_ ||
187 scrollbar_->NeedsRepaintPart(ScrollbarPart::TRACK_BUTTONS_TICKMARKS)) {
188 track_resource_ = ScopedUIResource::Create(
189 layer_tree_host()->GetUIResourceManager(),
190 RasterizeScrollbarPart(size, scaled_size,
191 ScrollbarPart::TRACK_BUTTONS_TICKMARKS));
192 SetNeedsPushProperties();
193 updated = true;
194 }
195
196 gfx::Size scaled_thumb_size = LayerSizeToContentSize(thumb_size_);
197 if (has_thumb_ && !scaled_thumb_size.IsEmpty()) {
198 if (!thumb_resource_ ||
199 scrollbar_->NeedsRepaintPart(ScrollbarPart::THUMB) ||
200 scaled_thumb_size != thumb_resource_->GetBitmap(0, false).GetSize()) {
201 thumb_resource_ = ScopedUIResource::Create(
202 layer_tree_host()->GetUIResourceManager(),
203 RasterizeScrollbarPart(thumb_size_, scaled_thumb_size,
204 ScrollbarPart::THUMB));
205 SetNeedsPushProperties();
206 updated = true;
207 }
208 updated |= UpdateProperty(scrollbar_->Opacity(), &painted_opacity_);
209 }
210
211 return updated;
212 }
213
RasterizeScrollbarPart(const gfx::Size & size,const gfx::Size & requested_content_size,ScrollbarPart part)214 UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart(
215 const gfx::Size& size,
216 const gfx::Size& requested_content_size,
217 ScrollbarPart part) {
218 DCHECK(!requested_content_size.IsEmpty());
219 DCHECK(!size.IsEmpty());
220
221 gfx::Size content_size = requested_content_size;
222
223 // Pages can end up requesting arbitrarily large scrollbars. Prevent this
224 // from crashing due to OOM and try something smaller.
225 SkBitmap skbitmap;
226 bool allocation_succeeded =
227 skbitmap.tryAllocN32Pixels(content_size.width(), content_size.height());
228 // Assuming 4bpp, caps at 4M.
229 constexpr int kMinScrollbarDimension = 1024;
230 int dimension = std::max(content_size.width(), content_size.height()) / 2;
231 while (!allocation_succeeded && dimension >= kMinScrollbarDimension) {
232 content_size.SetToMin(gfx::Size(dimension, dimension));
233 allocation_succeeded =
234 skbitmap.tryAllocN32Pixels(content_size.width(), content_size.height());
235 if (!allocation_succeeded)
236 dimension = dimension / 2;
237 }
238 CHECK(allocation_succeeded)
239 << "Failed to allocate memory for scrollbar at dimension : " << dimension;
240
241 SkiaPaintCanvas canvas(skbitmap);
242 canvas.clear(SK_ColorTRANSPARENT);
243
244 float scale_x = content_size.width() / static_cast<float>(size.width());
245 float scale_y = content_size.height() / static_cast<float>(size.height());
246 canvas.scale(SkFloatToScalar(scale_x), SkFloatToScalar(scale_y));
247
248 scrollbar_->PaintPart(&canvas, part, gfx::Rect(size));
249 // Make sure that the pixels are no longer mutable to unavoid unnecessary
250 // allocation and copying.
251 skbitmap.setImmutable();
252
253 return UIResourceBitmap(skbitmap);
254 }
255
256 ScrollbarLayerBase::ScrollbarLayerType
GetScrollbarLayerType() const257 PaintedScrollbarLayer::GetScrollbarLayerType() const {
258 return kPainted;
259 }
260
261 } // namespace cc
262