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.
5 #include "cc/layers/painted_scrollbar_layer.h"
9 #include "base/auto_reset.h"
10 #include "base/basictypes.h"
11 #include "base/trace_event/trace_event.h"
12 #include "cc/base/math_util.h"
13 #include "cc/layers/painted_scrollbar_layer_impl.h"
14 #include "cc/resources/ui_resource_bitmap.h"
15 #include "cc/trees/layer_tree_host.h"
16 #include "cc/trees/layer_tree_impl.h"
17 #include "skia/ext/platform_canvas.h"
18 #include "skia/ext/refptr.h"
19 #include "third_party/skia/include/core/SkBitmap.h"
20 #include "third_party/skia/include/core/SkCanvas.h"
21 #include "third_party/skia/include/core/SkSize.h"
22 #include "ui/gfx/geometry/size_conversions.h"
23 #include "ui/gfx/skia_util.h"
27 scoped_ptr
<LayerImpl
> PaintedScrollbarLayer::CreateLayerImpl(
28 LayerTreeImpl
* tree_impl
) {
29 return PaintedScrollbarLayerImpl::Create(
30 tree_impl
, id(), scrollbar_
->Orientation());
33 scoped_refptr
<PaintedScrollbarLayer
> PaintedScrollbarLayer::Create(
34 scoped_ptr
<Scrollbar
> scrollbar
,
35 int scroll_layer_id
) {
36 return make_scoped_refptr(
37 new PaintedScrollbarLayer(scrollbar
.Pass(), scroll_layer_id
));
40 PaintedScrollbarLayer::PaintedScrollbarLayer(scoped_ptr
<Scrollbar
> scrollbar
,
42 : scrollbar_(scrollbar
.Pass()),
43 scroll_layer_id_(scroll_layer_id
),
44 clip_layer_id_(Layer::INVALID_ID
),
45 internal_contents_scale_(0.f
),
46 thumb_thickness_(scrollbar_
->ThumbThickness()),
47 thumb_length_(scrollbar_
->ThumbLength()),
48 is_overlay_(scrollbar_
->IsOverlay()),
49 has_thumb_(scrollbar_
->HasThumb()) {
50 if (!scrollbar_
->IsOverlay())
51 SetShouldScrollOnMainThread(true);
54 PaintedScrollbarLayer::~PaintedScrollbarLayer() {}
56 int PaintedScrollbarLayer::ScrollLayerId() const {
57 return scroll_layer_id_
;
60 void PaintedScrollbarLayer::SetScrollLayer(int layer_id
) {
61 if (layer_id
== scroll_layer_id_
)
64 scroll_layer_id_
= layer_id
;
65 SetNeedsFullTreeSync();
68 void PaintedScrollbarLayer::SetClipLayer(int layer_id
) {
69 if (layer_id
== clip_layer_id_
)
72 clip_layer_id_
= layer_id
;
73 SetNeedsFullTreeSync();
76 bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
77 return scrollbar_
->IsOverlay();
80 ScrollbarOrientation
PaintedScrollbarLayer::orientation() const {
81 return scrollbar_
->Orientation();
84 int PaintedScrollbarLayer::MaxTextureSize() {
85 DCHECK(layer_tree_host());
86 return layer_tree_host()->GetRendererCapabilities().max_texture_size
;
89 float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale
) {
90 // If the scaled bounds() is bigger than the max texture size of the
91 // device, we need to clamp it by rescaling, since this is used
92 // below to set the texture size.
93 gfx::Size scaled_bounds
= gfx::ToCeiledSize(gfx::ScaleSize(bounds(), scale
));
94 if (scaled_bounds
.width() > MaxTextureSize() ||
95 scaled_bounds
.height() > MaxTextureSize()) {
96 if (scaled_bounds
.width() > scaled_bounds
.height())
97 return (MaxTextureSize() - 1) / static_cast<float>(bounds().width());
99 return (MaxTextureSize() - 1) / static_cast<float>(bounds().height());
104 void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl
* layer
) {
105 Layer::PushPropertiesTo(layer
);
107 PushScrollClipPropertiesTo(layer
);
109 PaintedScrollbarLayerImpl
* scrollbar_layer
=
110 static_cast<PaintedScrollbarLayerImpl
*>(layer
);
112 scrollbar_layer
->set_internal_contents_scale_and_bounds(
113 internal_contents_scale_
, internal_content_bounds_
);
115 scrollbar_layer
->SetThumbThickness(thumb_thickness_
);
116 scrollbar_layer
->SetThumbLength(thumb_length_
);
117 if (orientation() == HORIZONTAL
) {
118 scrollbar_layer
->SetTrackStart(
119 track_rect_
.x() - location_
.x());
120 scrollbar_layer
->SetTrackLength(track_rect_
.width());
122 scrollbar_layer
->SetTrackStart(
123 track_rect_
.y() - location_
.y());
124 scrollbar_layer
->SetTrackLength(track_rect_
.height());
127 if (track_resource_
.get())
128 scrollbar_layer
->set_track_ui_resource_id(track_resource_
->id());
130 scrollbar_layer
->set_track_ui_resource_id(0);
131 if (thumb_resource_
.get())
132 scrollbar_layer
->set_thumb_ui_resource_id(thumb_resource_
->id());
134 scrollbar_layer
->set_thumb_ui_resource_id(0);
136 scrollbar_layer
->set_is_overlay_scrollbar(is_overlay_
);
139 ScrollbarLayerInterface
* PaintedScrollbarLayer::ToScrollbarLayer() {
143 void PaintedScrollbarLayer::PushScrollClipPropertiesTo(LayerImpl
* layer
) {
144 PaintedScrollbarLayerImpl
* scrollbar_layer
=
145 static_cast<PaintedScrollbarLayerImpl
*>(layer
);
147 scrollbar_layer
->SetScrollLayerAndClipLayerByIds(scroll_layer_id_
,
151 void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost
* host
) {
152 // When the LTH is set to null or has changed, then this layer should remove
153 // all of its associated resources.
154 if (!host
|| host
!= layer_tree_host()) {
155 track_resource_
= nullptr;
156 thumb_resource_
= nullptr;
159 Layer::SetLayerTreeHost(host
);
162 gfx::Rect
PaintedScrollbarLayer::ScrollbarLayerRectToContentRect(
163 const gfx::Rect
& layer_rect
) const {
164 // Don't intersect with the bounds as in LayerRectToContentRect() because
165 // layer_rect here might be in coordinates of the containing layer.
166 gfx::Rect expanded_rect
= gfx::ScaleToEnclosingRect(
167 layer_rect
, internal_contents_scale_
, internal_contents_scale_
);
168 // We should never return a rect bigger than the content bounds.
169 gfx::Size clamped_size
= expanded_rect
.size();
170 clamped_size
.SetToMin(internal_content_bounds_
);
171 expanded_rect
.set_size(clamped_size
);
172 return expanded_rect
;
175 gfx::Rect
PaintedScrollbarLayer::OriginThumbRect() const {
176 gfx::Size thumb_size
;
177 if (orientation() == HORIZONTAL
) {
179 gfx::Size(scrollbar_
->ThumbLength(), scrollbar_
->ThumbThickness());
182 gfx::Size(scrollbar_
->ThumbThickness(), scrollbar_
->ThumbLength());
184 return gfx::Rect(thumb_size
);
187 void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
188 UpdateProperty(scrollbar_
->TrackRect(), &track_rect_
);
189 UpdateProperty(scrollbar_
->Location(), &location_
);
190 UpdateProperty(scrollbar_
->IsOverlay(), &is_overlay_
);
191 UpdateProperty(scrollbar_
->HasThumb(), &has_thumb_
);
193 UpdateProperty(scrollbar_
->ThumbThickness(), &thumb_thickness_
);
194 UpdateProperty(scrollbar_
->ThumbLength(), &thumb_length_
);
196 UpdateProperty(0, &thumb_thickness_
);
197 UpdateProperty(0, &thumb_length_
);
201 void PaintedScrollbarLayer::UpdateInternalContentScale() {
202 float scale
= layer_tree_host()->device_scale_factor();
203 if (layer_tree_host()
205 .layer_transforms_should_scale_layer_contents
) {
206 gfx::Vector2dF transform_scales
=
207 MathUtil::ComputeTransform2dScaleComponents(draw_transform(), scale
);
208 scale
= std::max(transform_scales
.x(), transform_scales
.y());
210 bool changed
= false;
211 changed
|= UpdateProperty(ClampScaleToMaxTextureSize(scale
),
212 &internal_contents_scale_
);
213 changed
|= UpdateProperty(
214 gfx::ToCeiledSize(gfx::ScaleSize(bounds(), internal_contents_scale_
)),
215 &internal_content_bounds_
);
217 // If the content scale or bounds change, repaint.
222 bool PaintedScrollbarLayer::Update(ResourceUpdateQueue
* queue
,
223 const OcclusionTracker
<Layer
>* occlusion
) {
225 base::AutoReset
<bool> ignore_set_needs_commit(&ignore_set_needs_commit_
,
227 Layer::Update(queue
, occlusion
);
228 UpdateInternalContentScale();
231 UpdateThumbAndTrackGeometry();
233 gfx::Rect track_layer_rect
= gfx::Rect(location_
, bounds());
234 gfx::Rect scaled_track_rect
= ScrollbarLayerRectToContentRect(
237 bool updated
= false;
239 if (track_rect_
.IsEmpty() || scaled_track_rect
.IsEmpty()) {
240 if (track_resource_
) {
241 track_resource_
= nullptr;
242 thumb_resource_
= nullptr;
243 SetNeedsPushProperties();
249 if (!has_thumb_
&& thumb_resource_
) {
250 thumb_resource_
= nullptr;
251 SetNeedsPushProperties();
255 if (update_rect_
.IsEmpty() && track_resource_
)
258 track_resource_
= ScopedUIResource::Create(
260 RasterizeScrollbarPart(track_layer_rect
, scaled_track_rect
, TRACK
));
262 gfx::Rect thumb_layer_rect
= OriginThumbRect();
263 gfx::Rect scaled_thumb_rect
=
264 ScrollbarLayerRectToContentRect(thumb_layer_rect
);
265 if (has_thumb_
&& !scaled_thumb_rect
.IsEmpty()) {
266 thumb_resource_
= ScopedUIResource::Create(
268 RasterizeScrollbarPart(thumb_layer_rect
, scaled_thumb_rect
, THUMB
));
271 // UI resources changed so push properties is needed.
272 SetNeedsPushProperties();
277 UIResourceBitmap
PaintedScrollbarLayer::RasterizeScrollbarPart(
278 const gfx::Rect
& layer_rect
,
279 const gfx::Rect
& content_rect
,
280 ScrollbarPart part
) {
281 DCHECK(!content_rect
.size().IsEmpty());
282 DCHECK(!layer_rect
.size().IsEmpty());
285 skbitmap
.allocN32Pixels(content_rect
.width(), content_rect
.height());
286 SkCanvas
skcanvas(skbitmap
);
289 content_rect
.width() / static_cast<float>(layer_rect
.width());
291 content_rect
.height() / static_cast<float>(layer_rect
.height());
293 skcanvas
.scale(SkFloatToScalar(scale_x
),
294 SkFloatToScalar(scale_y
));
295 skcanvas
.translate(SkFloatToScalar(-layer_rect
.x()),
296 SkFloatToScalar(-layer_rect
.y()));
298 SkRect layer_skrect
= RectToSkRect(layer_rect
);
300 paint
.setAntiAlias(false);
301 paint
.setXfermodeMode(SkXfermode::kClear_Mode
);
302 skcanvas
.drawRect(layer_skrect
, paint
);
303 skcanvas
.clipRect(layer_skrect
);
305 scrollbar_
->PaintPart(&skcanvas
, part
, layer_rect
);
306 // Make sure that the pixels are no longer mutable to unavoid unnecessary
307 // allocation and copying.
308 skbitmap
.setImmutable();
310 return UIResourceBitmap(skbitmap
);