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