painted_scrollbar_layer.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
21namespace cc {
22
23scoped_ptr<LayerImpl> PaintedScrollbarLayer::CreateLayerImpl(
24    LayerTreeImpl* tree_impl) {
25  return PaintedScrollbarLayerImpl::Create(
26      tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>();
27}
28
29scoped_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
36PaintedScrollbarLayer::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
49PaintedScrollbarLayer::~PaintedScrollbarLayer() {}
50
51int PaintedScrollbarLayer::ScrollLayerId() const {
52  return scroll_layer_id_;
53}
54
55void 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
63void 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
71bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
72  return scrollbar_->IsOverlay();
73}
74
75ScrollbarOrientation PaintedScrollbarLayer::orientation() const {
76  return scrollbar_->Orientation();
77}
78
79int PaintedScrollbarLayer::MaxTextureSize() {
80  DCHECK(layer_tree_host());
81  return layer_tree_host()->GetRendererCapabilities().max_texture_size;
82}
83
84float 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
99void 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
111void 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
143ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() {
144  return this;
145}
146
147void 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
155void 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
166gfx::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
179gfx::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
191void 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
205bool 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
259UIResourceBitmap 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