painted_scrollbar_layer.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 device_scale_factor,
102    float page_scale_factor,
103    bool animating_transform_to_screen,
104    float* contents_scale_x,
105    float* contents_scale_y,
106    gfx::Size* content_bounds) {
107  ContentsScalingLayer::CalculateContentsScale(
108      ClampScaleToMaxTextureSize(ideal_contents_scale),
109      device_scale_factor,
110      page_scale_factor,
111      animating_transform_to_screen,
112      contents_scale_x,
113      contents_scale_y,
114      content_bounds);
115}
116
117void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
118  ContentsScalingLayer::PushPropertiesTo(layer);
119
120  PushScrollClipPropertiesTo(layer);
121
122  PaintedScrollbarLayerImpl* scrollbar_layer =
123      static_cast<PaintedScrollbarLayerImpl*>(layer);
124
125  scrollbar_layer->SetThumbThickness(thumb_thickness_);
126  scrollbar_layer->SetThumbLength(thumb_length_);
127  if (orientation() == HORIZONTAL) {
128    scrollbar_layer->SetTrackStart(
129        track_rect_.x() - location_.x());
130    scrollbar_layer->SetTrackLength(track_rect_.width());
131  } else {
132    scrollbar_layer->SetTrackStart(
133        track_rect_.y() - location_.y());
134    scrollbar_layer->SetTrackLength(track_rect_.height());
135  }
136
137  if (track_resource_.get())
138    scrollbar_layer->set_track_ui_resource_id(track_resource_->id());
139  if (thumb_resource_.get())
140    scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id());
141
142  scrollbar_layer->set_is_overlay_scrollbar(is_overlay_);
143}
144
145ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() {
146  return this;
147}
148
149void PaintedScrollbarLayer::PushScrollClipPropertiesTo(LayerImpl* layer) {
150  PaintedScrollbarLayerImpl* scrollbar_layer =
151      static_cast<PaintedScrollbarLayerImpl*>(layer);
152
153  scrollbar_layer->SetScrollLayerById(scroll_layer_id_);
154  scrollbar_layer->SetClipLayerById(clip_layer_id_);
155}
156
157void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
158  // When the LTH is set to null or has changed, then this layer should remove
159  // all of its associated resources.
160  if (!host || host != layer_tree_host()) {
161    track_resource_.reset();
162    thumb_resource_.reset();
163  }
164
165  ContentsScalingLayer::SetLayerTreeHost(host);
166}
167
168gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect(
169    const gfx::Rect& layer_rect) const {
170  // Don't intersect with the bounds as in LayerRectToContentRect() because
171  // layer_rect here might be in coordinates of the containing layer.
172  gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect(
173      layer_rect, contents_scale_y(), contents_scale_y());
174  // We should never return a rect bigger than the content_bounds().
175  gfx::Size clamped_size = expanded_rect.size();
176  clamped_size.SetToMin(content_bounds());
177  expanded_rect.set_size(clamped_size);
178  return expanded_rect;
179}
180
181gfx::Rect PaintedScrollbarLayer::OriginThumbRect() const {
182  gfx::Size thumb_size;
183  if (orientation() == HORIZONTAL) {
184    thumb_size =
185        gfx::Size(scrollbar_->ThumbLength(), scrollbar_->ThumbThickness());
186  } else {
187    thumb_size =
188        gfx::Size(scrollbar_->ThumbThickness(), scrollbar_->ThumbLength());
189  }
190  return ScrollbarLayerRectToContentRect(gfx::Rect(thumb_size));
191}
192
193void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
194  UpdateProperty(scrollbar_->TrackRect(), &track_rect_);
195  UpdateProperty(scrollbar_->Location(), &location_);
196  UpdateProperty(scrollbar_->IsOverlay(), &is_overlay_);
197  UpdateProperty(scrollbar_->HasThumb(), &has_thumb_);
198  if (has_thumb_) {
199    UpdateProperty(scrollbar_->ThumbThickness(), &thumb_thickness_);
200    UpdateProperty(scrollbar_->ThumbLength(), &thumb_length_);
201  }
202}
203
204bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue,
205                                   const OcclusionTracker* occlusion) {
206  UpdateThumbAndTrackGeometry();
207
208  gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect(
209      gfx::Rect(location_, bounds()));
210
211  if (track_rect_.IsEmpty() || scaled_track_rect.IsEmpty())
212    return false;
213
214  {
215    base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
216                                                  true);
217    ContentsScalingLayer::Update(queue, occlusion);
218  }
219
220  if (update_rect_.IsEmpty() && track_resource_)
221    return false;
222
223  track_resource_ = ScopedUIResource::Create(
224      layer_tree_host(), RasterizeScrollbarPart(scaled_track_rect, TRACK));
225
226  gfx::Rect thumb_rect = OriginThumbRect();
227  if (has_thumb_ && !thumb_rect.IsEmpty()) {
228    thumb_resource_ = ScopedUIResource::Create(
229        layer_tree_host(), RasterizeScrollbarPart(thumb_rect, THUMB));
230  }
231
232  // UI resources changed so push properties is needed.
233  SetNeedsPushProperties();
234  return true;
235}
236
237UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart(
238    const gfx::Rect& rect,
239    ScrollbarPart part) {
240  DCHECK(!rect.size().IsEmpty());
241
242  SkBitmap skbitmap;
243  skbitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height());
244  skbitmap.allocPixels();
245
246  SkCanvas skcanvas(skbitmap);
247  skcanvas.translate(SkFloatToScalar(-rect.x()), SkFloatToScalar(-rect.y()));
248  skcanvas.scale(SkFloatToScalar(contents_scale_x()),
249                 SkFloatToScalar(contents_scale_y()));
250
251  gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
252      rect, 1.f / contents_scale_x(), 1.f / contents_scale_y());
253  SkRect layer_skrect = RectToSkRect(layer_rect);
254  SkPaint paint;
255  paint.setAntiAlias(false);
256  paint.setXfermodeMode(SkXfermode::kClear_Mode);
257  skcanvas.drawRect(layer_skrect, paint);
258  skcanvas.clipRect(layer_skrect);
259
260  scrollbar_->PaintPart(&skcanvas, part, layer_rect);
261  // Make sure that the pixels are no longer mutable to unavoid unnecessary
262  // allocation and copying.
263  skbitmap.setImmutable();
264
265  return UIResourceBitmap(skbitmap);
266}
267
268}  // namespace cc
269