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( 37 scoped_ptr<Scrollbar> scrollbar, 38 int scroll_layer_id) 39 : scrollbar_(scrollbar.Pass()), 40 scroll_layer_id_(scroll_layer_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::SetScrollLayerId(int id) { 56 if (id == scroll_layer_id_) 57 return; 58 59 scroll_layer_id_ = id; 60 SetNeedsFullTreeSync(); 61 } 62 63 bool PaintedScrollbarLayer::OpacityCanAnimateOnImplThread() const { 64 return scrollbar_->IsOverlay(); 65 } 66 67 ScrollbarOrientation PaintedScrollbarLayer::orientation() const { 68 return scrollbar_->Orientation(); 69 } 70 71 int PaintedScrollbarLayer::MaxTextureSize() { 72 DCHECK(layer_tree_host()); 73 return layer_tree_host()->GetRendererCapabilities().max_texture_size; 74 } 75 76 float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale) { 77 // If the scaled content_bounds() is bigger than the max texture size of the 78 // device, we need to clamp it by rescaling, since content_bounds() is used 79 // below to set the texture size. 80 gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale); 81 if (scaled_bounds.width() > MaxTextureSize() || 82 scaled_bounds.height() > MaxTextureSize()) { 83 if (scaled_bounds.width() > scaled_bounds.height()) 84 return (MaxTextureSize() - 1) / static_cast<float>(bounds().width()); 85 else 86 return (MaxTextureSize() - 1) / static_cast<float>(bounds().height()); 87 } 88 return scale; 89 } 90 91 void PaintedScrollbarLayer::CalculateContentsScale( 92 float ideal_contents_scale, 93 float device_scale_factor, 94 float page_scale_factor, 95 bool animating_transform_to_screen, 96 float* contents_scale_x, 97 float* contents_scale_y, 98 gfx::Size* content_bounds) { 99 ContentsScalingLayer::CalculateContentsScale( 100 ClampScaleToMaxTextureSize(ideal_contents_scale), 101 device_scale_factor, 102 page_scale_factor, 103 animating_transform_to_screen, 104 contents_scale_x, 105 contents_scale_y, 106 content_bounds); 107 } 108 109 void PaintedScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { 110 ContentsScalingLayer::PushPropertiesTo(layer); 111 112 PaintedScrollbarLayerImpl* scrollbar_layer = 113 static_cast<PaintedScrollbarLayerImpl*>(layer); 114 115 scrollbar_layer->SetThumbThickness(thumb_thickness_); 116 scrollbar_layer->SetThumbLength(thumb_length_); 117 if (orientation() == HORIZONTAL) { 118 scrollbar_layer->SetTrackStart( 119 track_rect_.x() - location_.x()); 120 scrollbar_layer->SetTrackLength(track_rect_.width()); 121 } else { 122 scrollbar_layer->SetTrackStart( 123 track_rect_.y() - location_.y()); 124 scrollbar_layer->SetTrackLength(track_rect_.height()); 125 } 126 127 if (track_resource_.get()) 128 scrollbar_layer->set_track_ui_resource_id(track_resource_->id()); 129 if (thumb_resource_.get()) 130 scrollbar_layer->set_thumb_ui_resource_id(thumb_resource_->id()); 131 132 scrollbar_layer->set_is_overlay_scrollbar(is_overlay_); 133 } 134 135 ScrollbarLayerInterface* PaintedScrollbarLayer::ToScrollbarLayer() { 136 return this; 137 } 138 139 void PaintedScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) { 140 // When the LTH is set to null or has changed, then this layer should remove 141 // all of its associated resources. 142 if (!host || host != layer_tree_host()) { 143 track_resource_.reset(); 144 thumb_resource_.reset(); 145 } 146 147 ContentsScalingLayer::SetLayerTreeHost(host); 148 } 149 150 gfx::Rect PaintedScrollbarLayer::ScrollbarLayerRectToContentRect( 151 gfx::Rect layer_rect) const { 152 // Don't intersect with the bounds as in LayerRectToContentRect() because 153 // layer_rect here might be in coordinates of the containing layer. 154 gfx::Rect expanded_rect = gfx::ScaleToEnclosingRect( 155 layer_rect, contents_scale_y(), contents_scale_y()); 156 // We should never return a rect bigger than the content_bounds(). 157 gfx::Size clamped_size = expanded_rect.size(); 158 clamped_size.SetToMin(content_bounds()); 159 expanded_rect.set_size(clamped_size); 160 return expanded_rect; 161 } 162 163 gfx::Rect PaintedScrollbarLayer::OriginThumbRect() const { 164 gfx::Size thumb_size; 165 if (orientation() == HORIZONTAL) { 166 thumb_size = 167 gfx::Size(scrollbar_->ThumbLength(), scrollbar_->ThumbThickness()); 168 } else { 169 thumb_size = 170 gfx::Size(scrollbar_->ThumbThickness(), scrollbar_->ThumbLength()); 171 } 172 return ScrollbarLayerRectToContentRect(gfx::Rect(thumb_size)); 173 } 174 175 void PaintedScrollbarLayer::UpdateThumbAndTrackGeometry() { 176 UpdateProperty(scrollbar_->TrackRect(), &track_rect_); 177 UpdateProperty(scrollbar_->Location(), &location_); 178 UpdateProperty(scrollbar_->IsOverlay(), &is_overlay_); 179 UpdateProperty(scrollbar_->HasThumb(), &has_thumb_); 180 if (has_thumb_) { 181 UpdateProperty(scrollbar_->ThumbThickness(), &thumb_thickness_); 182 UpdateProperty(scrollbar_->ThumbLength(), &thumb_length_); 183 } 184 } 185 186 bool PaintedScrollbarLayer::Update(ResourceUpdateQueue* queue, 187 const OcclusionTracker* occlusion) { 188 UpdateThumbAndTrackGeometry(); 189 190 gfx::Rect scaled_track_rect = ScrollbarLayerRectToContentRect( 191 gfx::Rect(location_, bounds())); 192 193 if (track_rect_.IsEmpty() || scaled_track_rect.IsEmpty()) 194 return false; 195 196 { 197 base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_, 198 true); 199 ContentsScalingLayer::Update(queue, occlusion); 200 } 201 202 if (update_rect_.IsEmpty() && track_resource_) 203 return false; 204 205 track_resource_ = ScopedUIResource::Create( 206 layer_tree_host(), RasterizeScrollbarPart(scaled_track_rect, TRACK)); 207 208 gfx::Rect thumb_rect = OriginThumbRect(); 209 if (has_thumb_ && !thumb_rect.IsEmpty()) { 210 thumb_resource_ = ScopedUIResource::Create( 211 layer_tree_host(), RasterizeScrollbarPart(thumb_rect, THUMB)); 212 } 213 214 // UI resources changed so push properties is needed. 215 SetNeedsPushProperties(); 216 return true; 217 } 218 219 UIResourceBitmap PaintedScrollbarLayer::RasterizeScrollbarPart( 220 gfx::Rect rect, 221 ScrollbarPart part) { 222 DCHECK(!rect.size().IsEmpty()); 223 224 SkBitmap skbitmap; 225 skbitmap.setConfig(SkBitmap::kARGB_8888_Config, rect.width(), rect.height()); 226 skbitmap.allocPixels(); 227 228 SkCanvas skcanvas(skbitmap); 229 skcanvas.translate(SkFloatToScalar(-rect.x()), SkFloatToScalar(-rect.y())); 230 skcanvas.scale(SkFloatToScalar(contents_scale_x()), 231 SkFloatToScalar(contents_scale_y())); 232 233 gfx::Rect layer_rect = gfx::ScaleToEnclosingRect( 234 rect, 1.f / contents_scale_x(), 1.f / contents_scale_y()); 235 SkRect layer_skrect = RectToSkRect(layer_rect); 236 SkPaint paint; 237 paint.setAntiAlias(false); 238 paint.setXfermodeMode(SkXfermode::kClear_Mode); 239 skcanvas.drawRect(layer_skrect, paint); 240 skcanvas.clipRect(layer_skrect); 241 242 scrollbar_->PaintPart(&skcanvas, part, layer_rect); 243 // Make sure that the pixels are no longer mutable to unavoid unnecessary 244 // allocation and copying. 245 skbitmap.setImmutable(); 246 247 return UIResourceBitmap(skbitmap); 248 } 249 250 } // namespace cc 251