painted_scrollbar_layer.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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_x(), 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 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<Layer>* occlusion) { 206 UpdateThumbAndTrackGeometry(); 207 208 gfx::Rect track_layer_rect = gfx::Rect(location_, bounds()); 209 gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect( 210 track_layer_rect); 211 212 if (track_rect_.IsEmpty() || scaled_track_rect.IsEmpty()) 213 return false; 214 215 { 216 base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_, 217 true); 218 ContentsScalingLayer::Update(queue, occlusion); 219 } 220 221 if (update_rect_.IsEmpty() && track_resource_) 222 return false; 223 224 track_resource_ = ScopedUIResource::Create( 225 layer_tree_host(), 226 RasterizeScrollbarPart(track_layer_rect, scaled_track_rect, TRACK)); 227 228 gfx::Rect thumb_layer_rect = OriginThumbRect(); 229 gfx::Rect scaled_thumb_rect = 230 ScrollbarLayerRectToContentRect(thumb_layer_rect); 231 if (has_thumb_ && !scaled_thumb_rect.IsEmpty()) { 232 thumb_resource_ = ScopedUIResource::Create( 233 layer_tree_host(), 234 RasterizeScrollbarPart(thumb_layer_rect, scaled_thumb_rect, THUMB)); 235 } 236 237 // UI resources changed so push properties is needed. 238 SetNeedsPushProperties(); 239 return true; 240} 241 242UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart( 243 const gfx::Rect& layer_rect, 244 const gfx::Rect& content_rect, 245 ScrollbarPart part) { 246 DCHECK(!content_rect.size().IsEmpty()); 247 DCHECK(!layer_rect.size().IsEmpty()); 248 249 SkBitmap skbitmap; 250 skbitmap.setConfig(SkBitmap::kARGB_8888_Config, 251 content_rect.width(), 252 content_rect.height()); 253 skbitmap.allocPixels(); 254 255 SkCanvas skcanvas(skbitmap); 256 257 float scale_x = 258 content_rect.width() / static_cast<float>(layer_rect.width()); 259 float scale_y = 260 content_rect.height() / static_cast<float>(layer_rect.height()); 261 262 skcanvas.scale(SkFloatToScalar(scale_x), 263 SkFloatToScalar(scale_y)); 264 skcanvas.translate(SkFloatToScalar(-layer_rect.x()), 265 SkFloatToScalar(-layer_rect.y())); 266 267 SkRect layer_skrect = RectToSkRect(layer_rect); 268 SkPaint paint; 269 paint.setAntiAlias(false); 270 paint.setXfermodeMode(SkXfermode::kClear_Mode); 271 skcanvas.drawRect(layer_skrect, paint); 272 skcanvas.clipRect(layer_skrect); 273 274 scrollbar_->PaintPart(&skcanvas, part, layer_rect); 275 // Make sure that the pixels are no longer mutable to unavoid unnecessary 276 // allocation and copying. 277 skbitmap.setImmutable(); 278 279 return UIResourceBitmap(skbitmap); 280} 281 282} // namespace cc 283