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