• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/auto_reset.h"
8 #include "base/basictypes.h"
9 #include "base/debug/trace_event.h"
10 #include "cc/layers/painted_scrollbar_layer_impl.h"
11 #include "cc/resources/ui_resource_bitmap.h"
12 #include "cc/trees/layer_tree_host.h"
13 #include "cc/trees/layer_tree_impl.h"
14 #include "skia/ext/platform_canvas.h"
15 #include "skia/ext/refptr.h"
16 #include "third_party/skia/include/core/SkBitmap.h"
17 #include "third_party/skia/include/core/SkCanvas.h"
18 #include "third_party/skia/include/core/SkSize.h"
19 #include "ui/gfx/skia_util.h"
20 
21 namespace cc {
22 
CreateLayerImpl(LayerTreeImpl * tree_impl)23 scoped_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl(
24     LayerTreeImpl* tree_impl) {
25   return PaintedScrollbarLayerImpl::Create(
26       tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>();
27 }
28 
Create(scoped_ptr<Scrollbar> scrollbar,int scroll_layer_id)29 scoped_refptr<PaintedScrollbarLayer> PaintedScrollbarLayer::Create(
30     scoped_ptr<Scrollbar> scrollbar,
31     int scroll_layer_id) {
32   return make_scoped_refptr(
33       new PaintedScrollbarLayer(scrollbar.Pass(), scroll_layer_id));
34 }
35 
PaintedScrollbarLayer(scoped_ptr<Scrollbar> scrollbar,int scroll_layer_id)36 PaintedScrollbarLayer::PaintedScrollbarLayer(scoped_ptr<Scrollbar> scrollbar,
37                                              int scroll_layer_id)
38     : scrollbar_(scrollbar.Pass()),
39       scroll_layer_id_(scroll_layer_id),
40       clip_layer_id_(Layer::INVALID_ID),
41       thumb_thickness_(scrollbar_->ThumbThickness()),
42       thumb_length_(scrollbar_->ThumbLength()),
43       is_overlay_(scrollbar_->IsOverlay()),
44       has_thumb_(scrollbar_->HasThumb()) {
45   if (!scrollbar_->IsOverlay())
46     SetShouldScrollOnMainThread(true);
47 }
48 
~PaintedScrollbarLayer()49 PaintedScrollbarLayer::~PaintedScrollbarLayer() {}
50 
ScrollLayerId() const51 int PaintedScrollbarLayer::ScrollLayerId() const {
52   return scroll_layer_id_;
53 }
54 
SetScrollLayer(int layer_id)55 void PaintedScrollbarLayer::SetScrollLayer(int layer_id) {
56   if (layer_id == scroll_layer_id_)
57     return;
58 
59   scroll_layer_id_ = layer_id;
60   SetNeedsFullTreeSync();
61 }
62 
SetClipLayer(int layer_id)63 void PaintedScrollbarLayer::SetClipLayer(int layer_id) {
64   if (layer_id == clip_layer_id_)
65     return;
66 
67   clip_layer_id_ = layer_id;
68   SetNeedsFullTreeSync();
69 }
70 
OpacityCanAnimateOnImplThread() const71 bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
72   return scrollbar_->IsOverlay();
73 }
74 
orientation() const75 ScrollbarOrientation PaintedScrollbarLayer::orientation() const {
76   return scrollbar_->Orientation();
77 }
78 
MaxTextureSize()79 int PaintedScrollbarLayer::MaxTextureSize() {
80   DCHECK(layer_tree_host());
81   return layer_tree_host()->GetRendererCapabilities().max_texture_size;
82 }
83 
ClampScaleToMaxTextureSize(float scale)84 float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale) {
85   // If the scaled content_bounds() is bigger than the max texture size of the
86   // device, we need to clamp it by rescaling, since content_bounds() is used
87   // below to set the texture size.
88   gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale);
89   if (scaled_bounds.width() > MaxTextureSize() ||
90       scaled_bounds.height() > MaxTextureSize()) {
91     if (scaled_bounds.width() > scaled_bounds.height())
92       return (MaxTextureSize() - 1) / static_cast<float>(bounds().width());
93     else
94       return (MaxTextureSize() - 1) / static_cast<float>(bounds().height());
95   }
96   return scale;
97 }
98 
CalculateContentsScale(float ideal_contents_scale,float * contents_scale_x,float * contents_scale_y,gfx::Size * content_bounds)99 void PaintedScrollbarLayer::CalculateContentsScale(
100     float ideal_contents_scale,
101     float* contents_scale_x,
102     float* contents_scale_y,
103     gfx::Size* content_bounds) {
104   ContentsScalingLayer::CalculateContentsScale(
105       ClampScaleToMaxTextureSize(ideal_contents_scale),
106       contents_scale_x,
107       contents_scale_y,
108       content_bounds);
109 }
110 
PushPropertiesTo(LayerImpl * layer)111 void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
112   ContentsScalingLayer::PushPropertiesTo(layer);
113 
114   PushScrollClipPropertiesTo(layer);
115 
116   PaintedScrollbarLayerImpl* scrollbar_layer =
117       static_cast<PaintedScrollbarLayerImpl*>(layer);
118 
119   scrollbar_layer->SetThumbThickness(thumb_thickness_);
120   scrollbar_layer->SetThumbLength(thumb_length_);
121   if (orientation() == HORIZONTAL) {
122     scrollbar_layer->SetTrackStart(
123         track_rect_.x() - location_.x());
124     scrollbar_layer->SetTrackLength(track_rect_.width());
125   } else {
126     scrollbar_layer->SetTrackStart(
127         track_rect_.y() - location_.y());
128     scrollbar_layer->SetTrackLength(track_rect_.height());
129   }
130 
131   if (track_resource_.get())
132     scrollbar_layer->set_track_ui_resource_id(track_resource_->id());
133   else
134     scrollbar_layer->set_track_ui_resource_id(0);
135   if (thumb_resource_.get())
136     scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id());
137   else
138     scrollbar_layer->set_thumb_ui_resource_id(0);
139 
140   scrollbar_layer->set_is_overlay_scrollbar(is_overlay_);
141 }
142 
ToScrollbarLayer()143 ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() {
144   return this;
145 }
146 
PushScrollClipPropertiesTo(LayerImpl * layer)147 void PaintedScrollbarLayer::PushScrollClipPropertiesTo(LayerImpl* layer) {
148   PaintedScrollbarLayerImpl* scrollbar_layer =
149       static_cast<PaintedScrollbarLayerImpl*>(layer);
150 
151   scrollbar_layer->SetScrollLayerAndClipLayerByIds(scroll_layer_id_,
152                                                    clip_layer_id_);
153 }
154 
SetLayerTreeHost(LayerTreeHost * host)155 void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
156   // When the LTH is set to null or has changed, then this layer should remove
157   // all of its associated resources.
158   if (!host || host != layer_tree_host()) {
159     track_resource_.reset();
160     thumb_resource_.reset();
161   }
162 
163   ContentsScalingLayer::SetLayerTreeHost(host);
164 }
165 
ScrollbarLayerRectToContentRect(const gfx::Rect & layer_rect) const166 gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect(
167     const gfx::Rect& layer_rect) const {
168   // Don't intersect with the bounds as in LayerRectToContentRect() because
169   // layer_rect here might be in coordinates of the containing layer.
170   gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect(
171       layer_rect, contents_scale_x(), contents_scale_y());
172   // We should never return a rect bigger than the content_bounds().
173   gfx::Size clamped_size = expanded_rect.size();
174   clamped_size.SetToMin(content_bounds());
175   expanded_rect.set_size(clamped_size);
176   return expanded_rect;
177 }
178 
OriginThumbRect() const179 gfx::Rect PaintedScrollbarLayer::OriginThumbRect() const {
180   gfx::Size thumb_size;
181   if (orientation() == HORIZONTAL) {
182     thumb_size =
183         gfx::Size(scrollbar_->ThumbLength(), scrollbar_->ThumbThickness());
184   } else {
185     thumb_size =
186         gfx::Size(scrollbar_->ThumbThickness(), scrollbar_->ThumbLength());
187   }
188   return gfx::Rect(thumb_size);
189 }
190 
UpdateThumbAndTrackGeometry()191 void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
192   UpdateProperty(scrollbar_->TrackRect(), &track_rect_);
193   UpdateProperty(scrollbar_->Location(), &location_);
194   UpdateProperty(scrollbar_->IsOverlay(), &is_overlay_);
195   UpdateProperty(scrollbar_->HasThumb(), &has_thumb_);
196   if (has_thumb_) {
197     UpdateProperty(scrollbar_->ThumbThickness(), &thumb_thickness_);
198     UpdateProperty(scrollbar_->ThumbLength(), &thumb_length_);
199   } else {
200     UpdateProperty(0, &thumb_thickness_);
201     UpdateProperty(0, &thumb_length_);
202   }
203 }
204 
Update(ResourceUpdateQueue * queue,const OcclusionTracker<Layer> * occlusion)205 bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue,
206                                    const OcclusionTracker<Layer>* occlusion) {
207   UpdateThumbAndTrackGeometry();
208 
209   gfx::Rect track_layer_rect = gfx::Rect(location_, bounds());
210   gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect(
211       track_layer_rect);
212 
213   bool updated = false;
214 
215   if (track_rect_.IsEmpty() || scaled_track_rect.IsEmpty()) {
216     if (track_resource_) {
217       track_resource_.reset();
218       if (thumb_resource_)
219         thumb_resource_.reset();
220       SetNeedsPushProperties();
221       updated = true;
222     }
223     return updated;
224   }
225 
226   if (!has_thumb_ && thumb_resource_) {
227     thumb_resource_.reset();
228     SetNeedsPushProperties();
229   }
230 
231   {
232     base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
233                                                   true);
234     ContentsScalingLayer::Update(queue, occlusion);
235   }
236 
237   if (update_rect_.IsEmpty() && track_resource_)
238     return updated;
239 
240   track_resource_ = ScopedUIResource::Create(
241       layer_tree_host(),
242       RasterizeScrollbarPart(track_layer_rect, scaled_track_rect, TRACK));
243 
244   gfx::Rect thumb_layer_rect = OriginThumbRect();
245   gfx::Rect scaled_thumb_rect =
246       ScrollbarLayerRectToContentRect(thumb_layer_rect);
247   if (has_thumb_ && !scaled_thumb_rect.IsEmpty()) {
248     thumb_resource_ = ScopedUIResource::Create(
249         layer_tree_host(),
250         RasterizeScrollbarPart(thumb_layer_rect, scaled_thumb_rect, THUMB));
251   }
252 
253   // UI resources changed so push properties is needed.
254   SetNeedsPushProperties();
255   updated = true;
256   return updated;
257 }
258 
RasterizeScrollbarPart(const gfx::Rect & layer_rect,const gfx::Rect & content_rect,ScrollbarPart part)259 UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart(
260     const gfx::Rect& layer_rect,
261     const gfx::Rect& content_rect,
262     ScrollbarPart part) {
263   DCHECK(!content_rect.size().IsEmpty());
264   DCHECK(!layer_rect.size().IsEmpty());
265 
266   SkBitmap skbitmap;
267   skbitmap.allocN32Pixels(content_rect.width(), content_rect.height());
268   SkCanvas skcanvas(skbitmap);
269 
270   float scale_x =
271       content_rect.width() / static_cast<float>(layer_rect.width());
272   float scale_y =
273       content_rect.height() / static_cast<float>(layer_rect.height());
274 
275   skcanvas.scale(SkFloatToScalar(scale_x),
276                  SkFloatToScalar(scale_y));
277   skcanvas.translate(SkFloatToScalar(-layer_rect.x()),
278                      SkFloatToScalar(-layer_rect.y()));
279 
280   SkRect layer_skrect = RectToSkRect(layer_rect);
281   SkPaint paint;
282   paint.setAntiAlias(false);
283   paint.setXfermodeMode(SkXfermode::kClear_Mode);
284   skcanvas.drawRect(layer_skrect, paint);
285   skcanvas.clipRect(layer_skrect);
286 
287   scrollbar_->PaintPart(&skcanvas, part, layer_rect);
288   // Make sure that the pixels are no longer mutable to unavoid unnecessary
289   // allocation and copying.
290   skbitmap.setImmutable();
291 
292   return UIResourceBitmap(skbitmap);
293 }
294 
295 }  // namespace cc
296