painted_scrollbar_layer.cc revision 3551c9c881056c480085172ff9840cab31610854
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(
37    scoped_ptr<Scrollbar> scrollbar,
38    int scroll_layer_id)
39    : scrollbar_(scrollbar.Pass()),
40      scroll_layer_id_(scroll_layer_id) {
41  if (!scrollbar_->IsOverlay())
42    SetShouldScrollOnMainThread(true);
43}
44
45PaintedScrollbarLayer::~PaintedScrollbarLayer() {}
46
47void PaintedScrollbarLayer::SetScrollLayerId(int id) {
48  if (id == scroll_layer_id_)
49    return;
50
51  scroll_layer_id_ = id;
52  SetNeedsFullTreeSync();
53}
54
55bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const {
56  return scrollbar_->IsOverlay();
57}
58
59ScrollbarOrientation PaintedScrollbarLayer::Orientation() const {
60  return scrollbar_->Orientation();
61}
62
63int PaintedScrollbarLayer::MaxTextureSize() {
64  DCHECK(layer_tree_host());
65  return layer_tree_host()->GetRendererCapabilities().max_texture_size;
66}
67
68float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale) {
69  if (layer_tree_host()->settings().solid_color_scrollbars)
70    return scale;
71
72  // If the scaled content_bounds() is bigger than the max texture size of the
73  // device, we need to clamp it by rescaling, since content_bounds() is used
74  // below to set the texture size.
75  gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale);
76  if (scaled_bounds.width() > MaxTextureSize() ||
77      scaled_bounds.height() > MaxTextureSize()) {
78    if (scaled_bounds.width() > scaled_bounds.height())
79      return (MaxTextureSize() - 1) / static_cast<float>(bounds().width());
80    else
81      return (MaxTextureSize() - 1) / static_cast<float>(bounds().height());
82  }
83  return scale;
84}
85
86void PaintedScrollbarLayer::CalculateContentsScale(
87    float ideal_contents_scale,
88    float device_scale_factor,
89    float page_scale_factor,
90    bool animating_transform_to_screen,
91    float* contents_scale_x,
92    float* contents_scale_y,
93    gfx::Size* content_bounds) {
94  ContentsScalingLayer::CalculateContentsScale(
95      ClampScaleToMaxTextureSize(ideal_contents_scale),
96      device_scale_factor,
97      page_scale_factor,
98      animating_transform_to_screen,
99      contents_scale_x,
100      contents_scale_y,
101      content_bounds);
102}
103
104void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
105  ContentsScalingLayer::PushPropertiesTo(layer);
106
107  PaintedScrollbarLayerImpl* scrollbar_layer =
108      static_cast<PaintedScrollbarLayerImpl*>(layer);
109
110  if (layer_tree_host() &&
111      layer_tree_host()->settings().solid_color_scrollbars) {
112    int thickness_override =
113        layer_tree_host()->settings().solid_color_scrollbar_thickness_dip;
114    if (thickness_override != -1) {
115      scrollbar_layer->SetThumbThickness(thickness_override);
116    } else {
117      if (Orientation() == HORIZONTAL)
118        scrollbar_layer->SetThumbThickness(bounds().height());
119      else
120        scrollbar_layer->SetThumbThickness(bounds().width());
121    }
122  } else {
123    scrollbar_layer->SetThumbThickness(thumb_thickness_);
124  }
125  scrollbar_layer->SetThumbLength(thumb_length_);
126  if (Orientation() == HORIZONTAL) {
127    scrollbar_layer->SetTrackStart(
128        track_rect_.x() - scrollbar_->Location().x());
129    scrollbar_layer->SetTrackLength(track_rect_.width());
130  } else {
131    scrollbar_layer->SetTrackStart(
132        track_rect_.y() - scrollbar_->Location().y());
133    scrollbar_layer->SetTrackLength(track_rect_.height());
134  }
135
136  if (track_resource_.get())
137    scrollbar_layer->set_track_ui_resource_id(track_resource_->id());
138  if (thumb_resource_.get())
139    scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id());
140
141  scrollbar_layer->set_is_overlay_scrollbar(scrollbar_->IsOverlay());
142
143  // PaintedScrollbarLayer must push properties every frame. crbug.com/259095
144  needs_push_properties_ = true;
145}
146
147PaintedScrollbarLayer* PaintedScrollbarLayer::ToScrollbarLayer() {
148  return this;
149}
150
151void 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_.reset();
156    thumb_resource_.reset();
157  }
158
159  ContentsScalingLayer::SetLayerTreeHost(host);
160}
161
162gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect(
163    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, contents_scale_y(), contents_scale_y());
168  // We should never return a rect bigger than the content_bounds().
169  gfx::Size clamped_size = expanded_rect.size();
170  clamped_size.SetToMin(content_bounds());
171  expanded_rect.set_size(clamped_size);
172  return expanded_rect;
173}
174
175gfx::Rect PaintedScrollbarLayer::OriginThumbRect() const {
176  gfx::Size thumb_size;
177  if (Orientation() == HORIZONTAL) {
178    thumb_size =
179        gfx::Size(scrollbar_->ThumbLength(), scrollbar_->ThumbThickness());
180  } else {
181    thumb_size =
182        gfx::Size(scrollbar_->ThumbThickness(), scrollbar_->ThumbLength());
183  }
184  return ScrollbarLayerRectToContentRect(gfx::Rect(thumb_size));
185}
186
187void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() {
188  track_rect_ = scrollbar_->TrackRect();
189  if (scrollbar_->HasThumb()) {
190    thumb_thickness_ = scrollbar_->ThumbThickness();
191    thumb_length_ = scrollbar_->ThumbLength();
192  }
193}
194
195bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue,
196                            const OcclusionTracker* occlusion) {
197  UpdateThumbAndTrackGeometry();
198
199  gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect(
200      gfx::Rect(scrollbar_->Location(), bounds()));
201
202  if (layer_tree_host()->settings().solid_color_scrollbars ||
203      track_rect_.IsEmpty() || scaled_track_rect.IsEmpty())
204    return false;
205
206  {
207    base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
208                                                  true);
209    ContentsScalingLayer::Update(queue, occlusion);
210  }
211
212  track_resource_ = ScopedUIResource::Create(
213      layer_tree_host(), RasterizeScrollbarPart(scaled_track_rect, TRACK));
214  gfx::Rect thumb_rect = OriginThumbRect();
215
216  if (scrollbar_->HasThumb() && !thumb_rect.IsEmpty()) {
217    thumb_resource_ = ScopedUIResource::Create(
218        layer_tree_host(), RasterizeScrollbarPart(thumb_rect, THUMB));
219  }
220
221  return true;
222}
223
224scoped_refptr<UIResourceBitmap> PaintedScrollbarLayer::RasterizeScrollbarPart(
225    gfx::Rect rect,
226    ScrollbarPart part) {
227  DCHECK(!layer_tree_host()->settings().solid_color_scrollbars);
228  DCHECK(!rect.size().IsEmpty());
229
230  scoped_refptr<UIResourceBitmap> bitmap =
231      UIResourceBitmap::Create(new uint8_t[rect.width() * rect.height() * 4],
232                               UIResourceBitmap::RGBA8,
233                               rect.size());
234
235  SkBitmap skbitmap;
236  skbitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height());
237  skbitmap.setPixels(bitmap->GetPixels());
238
239  SkCanvas skcanvas(skbitmap);
240  skcanvas.translate(SkFloatToScalar(-rect.x()), SkFloatToScalar(-rect.y()));
241  skcanvas.scale(SkFloatToScalar(contents_scale_x()),
242                 SkFloatToScalar(contents_scale_y()));
243
244  gfx::Rect layer_rect = gfx::ScaleToEnclosingRect(
245      rect, 1.f / contents_scale_x(), 1.f / contents_scale_y());
246  SkRect layer_skrect = RectToSkRect(layer_rect);
247  SkPaint paint;
248  paint.setAntiAlias(false);
249  paint.setXfermodeMode(SkXfermode::kClear_Mode);
250  skcanvas.drawRect(layer_skrect, paint);
251  skcanvas.clipRect(layer_skrect);
252
253  scrollbar_->PaintPart(&skcanvas, part, layer_rect);
254
255  return bitmap;
256}
257
258}  // namespace cc
259