| // Copyright 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/scrollbar_layer.h" |
| |
| #include "base/basictypes.h" |
| #include "base/debug/trace_event.h" |
| #include "cc/caching_bitmap_content_layer_updater.h" |
| #include "cc/layer_painter.h" |
| #include "cc/layer_tree_host.h" |
| #include "cc/prioritized_resource.h" |
| #include "cc/resource_update_queue.h" |
| #include "cc/scrollbar_layer_impl.h" |
| #include "third_party/WebKit/Source/Platform/chromium/public/WebRect.h" |
| #include "ui/gfx/rect_conversions.h" |
| |
| namespace cc { |
| |
| scoped_ptr<LayerImpl> ScrollbarLayer::CreateLayerImpl( |
| LayerTreeImpl* tree_impl) { |
| return ScrollbarLayerImpl::Create( |
| tree_impl, |
| id(), |
| ScrollbarGeometryFixedThumb::create(make_scoped_ptr(geometry_->clone()))) |
| .PassAs<LayerImpl>(); |
| } |
| |
| scoped_refptr<ScrollbarLayer> ScrollbarLayer::Create( |
| scoped_ptr<WebKit::WebScrollbar> scrollbar, |
| scoped_ptr<ScrollbarThemePainter> painter, |
| scoped_ptr<WebKit::WebScrollbarThemeGeometry> geometry, |
| int scrollLayerId) { |
| return make_scoped_refptr(new ScrollbarLayer(scrollbar.Pass(), |
| painter.Pass(), |
| geometry.Pass(), |
| scrollLayerId)); |
| } |
| |
| ScrollbarLayer::ScrollbarLayer( |
| scoped_ptr<WebKit::WebScrollbar> scrollbar, |
| scoped_ptr<ScrollbarThemePainter> painter, |
| scoped_ptr<WebKit::WebScrollbarThemeGeometry> geometry, |
| int scrollLayerId) |
| : scrollbar_(scrollbar.Pass()), |
| painter_(painter.Pass()), |
| geometry_(geometry.Pass()), |
| scroll_layer_id_(scrollLayerId), |
| texture_format_(GL_INVALID_ENUM) { |
| if (!scrollbar_->isOverlay()) |
| SetShouldScrollOnMainThread(true); |
| } |
| |
| ScrollbarLayer::~ScrollbarLayer() {} |
| |
| void ScrollbarLayer::SetScrollLayerId(int id) { |
| if (id == scroll_layer_id_) |
| return; |
| |
| scroll_layer_id_ = id; |
| SetNeedsFullTreeSync(); |
| } |
| |
| WebKit::WebScrollbar::Orientation ScrollbarLayer::Orientation() const { |
| return scrollbar_->orientation(); |
| } |
| |
| int ScrollbarLayer::MaxTextureSize() { |
| DCHECK(layer_tree_host()); |
| return layer_tree_host()->GetRendererCapabilities().max_texture_size; |
| } |
| |
| float ScrollbarLayer::ClampScaleToMaxTextureSize(float scale) { |
| if (layer_tree_host()->settings().solidColorScrollbars) |
| return scale; |
| |
| // If the scaled content_bounds() is bigger than the max texture size of the |
| // device, we need to clamp it by rescaling, since content_bounds() is used |
| // below to set the texture size. |
| gfx::Size scaled_bounds = ComputeContentBoundsForScale(scale, scale); |
| if (scaled_bounds.width() > MaxTextureSize() || |
| scaled_bounds.height() > MaxTextureSize()) { |
| if (scaled_bounds.width() > scaled_bounds.height()) |
| return (MaxTextureSize() - 1) / static_cast<float>(bounds().width()); |
| else |
| return (MaxTextureSize() - 1) / static_cast<float>(bounds().height()); |
| } |
| return scale; |
| } |
| |
| void ScrollbarLayer::CalculateContentsScale(float ideal_contents_scale, |
| bool animating_transform_to_screen, |
| float* contents_scale_x, |
| float* contents_scale_y, |
| gfx::Size* contentBounds) { |
| ContentsScalingLayer::CalculateContentsScale( |
| ClampScaleToMaxTextureSize(ideal_contents_scale), |
| animating_transform_to_screen, |
| contents_scale_x, |
| contents_scale_y, |
| contentBounds); |
| DCHECK_LE(contentBounds->width(), MaxTextureSize()); |
| DCHECK_LE(contentBounds->height(), MaxTextureSize()); |
| } |
| |
| void ScrollbarLayer::PushPropertiesTo(LayerImpl* layer) { |
| ContentsScalingLayer::PushPropertiesTo(layer); |
| |
| ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer); |
| |
| scrollbar_layer->SetScrollbarData(scrollbar_.get()); |
| scrollbar_layer->SetThumbSize(thumb_size_); |
| |
| if (back_track_ && back_track_->texture()->haveBackingTexture()) { |
| scrollbar_layer->set_back_track_resource_id( |
| back_track_->texture()->resourceId()); |
| } else { |
| scrollbar_layer->set_back_track_resource_id(0); |
| } |
| |
| if (fore_track_ && fore_track_->texture()->haveBackingTexture()) { |
| scrollbar_layer->set_fore_track_resource_id( |
| fore_track_->texture()->resourceId()); |
| } else { |
| scrollbar_layer->set_fore_track_resource_id(0); |
| } |
| |
| if (thumb_ && thumb_->texture()->haveBackingTexture()) |
| scrollbar_layer->set_thumb_resource_id(thumb_->texture()->resourceId()); |
| else |
| scrollbar_layer->set_thumb_resource_id(0); |
| } |
| |
| ScrollbarLayer* ScrollbarLayer::ToScrollbarLayer() { |
| return this; |
| } |
| |
| class ScrollbarBackgroundPainter : public LayerPainter { |
| public: |
| static scoped_ptr<ScrollbarBackgroundPainter> Create( |
| WebKit::WebScrollbar* scrollbar, |
| ScrollbarThemePainter *painter, |
| WebKit::WebScrollbarThemeGeometry* geometry, |
| WebKit::WebScrollbar::ScrollbarPart trackPart) { |
| return make_scoped_ptr(new ScrollbarBackgroundPainter(scrollbar, |
| painter, |
| geometry, |
| trackPart)); |
| } |
| |
| virtual void Paint(SkCanvas* canvas, |
| gfx::Rect content_rect, |
| gfx::RectF* opaque) OVERRIDE { |
| // The following is a simplification of ScrollbarThemeComposite::paint. |
| painter_->PaintScrollbarBackground(canvas, content_rect); |
| |
| if (geometry_->hasButtons(scrollbar_)) { |
| gfx::Rect back_button_start_paint_rect = |
| geometry_->backButtonStartRect(scrollbar_); |
| painter_->PaintBackButtonStart(canvas, back_button_start_paint_rect); |
| |
| gfx::Rect back_button_end_paint_rect = |
| geometry_->backButtonEndRect(scrollbar_); |
| painter_->PaintBackButtonEnd(canvas, back_button_end_paint_rect); |
| |
| gfx::Rect forward_button_start_paint_rect = |
| geometry_->forwardButtonStartRect(scrollbar_); |
| painter_->PaintForwardButtonStart(canvas, |
| forward_button_start_paint_rect); |
| |
| gfx::Rect forward_button_end_paint_rect = |
| geometry_->forwardButtonEndRect(scrollbar_); |
| painter_->PaintForwardButtonEnd(canvas, forward_button_end_paint_rect); |
| } |
| |
| gfx::Rect track_paint_rect = geometry_->trackRect(scrollbar_); |
| painter_->PaintTrackBackground(canvas, track_paint_rect); |
| |
| bool thumb_present = geometry_->hasThumb(scrollbar_); |
| if (thumb_present) { |
| if (track_part_ == WebKit::WebScrollbar::ForwardTrackPart) |
| painter_->PaintForwardTrackPart(canvas, track_paint_rect); |
| else |
| painter_->PaintBackTrackPart(canvas, track_paint_rect); |
| } |
| |
| painter_->PaintTickmarks(canvas, track_paint_rect); |
| } |
| private: |
| ScrollbarBackgroundPainter(WebKit::WebScrollbar* scrollbar, |
| ScrollbarThemePainter *painter, |
| WebKit::WebScrollbarThemeGeometry* geometry, |
| WebKit::WebScrollbar::ScrollbarPart trackPart) |
| : scrollbar_(scrollbar), |
| painter_(painter), |
| geometry_(geometry), |
| track_part_(trackPart) {} |
| |
| WebKit::WebScrollbar* scrollbar_; |
| ScrollbarThemePainter* painter_; |
| WebKit::WebScrollbarThemeGeometry* geometry_; |
| WebKit::WebScrollbar::ScrollbarPart track_part_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScrollbarBackgroundPainter); |
| }; |
| |
| class ScrollbarThumbPainter : public LayerPainter { |
| public: |
| static scoped_ptr<ScrollbarThumbPainter> Create( |
| WebKit::WebScrollbar* scrollbar, |
| ScrollbarThemePainter* painter, |
| WebKit::WebScrollbarThemeGeometry* geometry) { |
| return make_scoped_ptr(new ScrollbarThumbPainter(scrollbar, |
| painter, |
| geometry)); |
| } |
| |
| virtual void Paint(SkCanvas* canvas, |
| gfx::Rect content_rect, |
| gfx::RectF* opaque) OVERRIDE { |
| // Consider the thumb to be at the origin when painting. |
| gfx::Rect thumb_rect = geometry_->thumbRect(scrollbar_); |
| painter_->PaintThumb(canvas, gfx::Rect(thumb_rect.size())); |
| } |
| |
| private: |
| ScrollbarThumbPainter(WebKit::WebScrollbar* scrollbar, |
| ScrollbarThemePainter* painter, |
| WebKit::WebScrollbarThemeGeometry* geometry) |
| : scrollbar_(scrollbar), |
| painter_(painter), |
| geometry_(geometry) {} |
| |
| WebKit::WebScrollbar* scrollbar_; |
| ScrollbarThemePainter* painter_; |
| WebKit::WebScrollbarThemeGeometry* geometry_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScrollbarThumbPainter); |
| }; |
| |
| void ScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) { |
| if (!host || host != layer_tree_host()) { |
| back_track_updater_ = NULL; |
| back_track_.reset(); |
| thumb_updater_ = NULL; |
| thumb_.reset(); |
| } |
| |
| ContentsScalingLayer::SetLayerTreeHost(host); |
| } |
| |
| void ScrollbarLayer::CreateUpdaterIfNeeded() { |
| if (layer_tree_host()->settings().solidColorScrollbars) |
| return; |
| |
| texture_format_ = |
| layer_tree_host()->GetRendererCapabilities().best_texture_format; |
| |
| if (!back_track_updater_) { |
| back_track_updater_ = CachingBitmapContentLayerUpdater::Create( |
| ScrollbarBackgroundPainter::Create( |
| scrollbar_.get(), |
| painter_.get(), |
| geometry_.get(), |
| WebKit::WebScrollbar::BackTrackPart).PassAs<LayerPainter>()); |
| } |
| if (!back_track_) { |
| back_track_ = back_track_updater_->createResource( |
| layer_tree_host()->contents_texture_manager()); |
| } |
| |
| // Only create two-part track if we think the two parts could be different in |
| // appearance. |
| if (scrollbar_->isCustomScrollbar()) { |
| if (!fore_track_updater_) { |
| fore_track_updater_ = CachingBitmapContentLayerUpdater::Create( |
| ScrollbarBackgroundPainter::Create( |
| scrollbar_.get(), |
| painter_.get(), |
| geometry_.get(), |
| WebKit::WebScrollbar::ForwardTrackPart).PassAs<LayerPainter>()); |
| } |
| if (!fore_track_) { |
| fore_track_ = fore_track_updater_->createResource( |
| layer_tree_host()->contents_texture_manager()); |
| } |
| } |
| |
| if (!thumb_updater_) { |
| thumb_updater_ = CachingBitmapContentLayerUpdater::Create( |
| ScrollbarThumbPainter::Create(scrollbar_.get(), |
| painter_.get(), |
| geometry_.get()).PassAs<LayerPainter>()); |
| } |
| if (!thumb_) { |
| thumb_ = thumb_updater_->createResource( |
| layer_tree_host()->contents_texture_manager()); |
| } |
| } |
| |
| void ScrollbarLayer::UpdatePart(CachingBitmapContentLayerUpdater* painter, |
| LayerUpdater::Resource* resource, |
| gfx::Rect rect, |
| ResourceUpdateQueue* queue, |
| RenderingStats* stats) { |
| if (layer_tree_host()->settings().solidColorScrollbars) |
| return; |
| |
| // Skip painting and uploading if there are no invalidations and |
| // we already have valid texture data. |
| if (resource->texture()->haveBackingTexture() && |
| resource->texture()->size() == rect.size() && |
| !is_dirty()) |
| return; |
| |
| // We should always have enough memory for UI. |
| DCHECK(resource->texture()->canAcquireBackingTexture()); |
| if (!resource->texture()->canAcquireBackingTexture()) |
| return; |
| |
| // Paint and upload the entire part. |
| gfx::Rect painted_opaque_rect; |
| painter->prepareToUpdate(rect, |
| rect.size(), |
| contents_scale_x(), |
| contents_scale_y(), |
| painted_opaque_rect, |
| stats); |
| if (!painter->pixelsDidChange() && |
| resource->texture()->haveBackingTexture()) { |
| TRACE_EVENT_INSTANT0("cc", |
| "ScrollbarLayer::updatePart no texture upload needed"); |
| return; |
| } |
| |
| bool partial_updates_allowed = |
| layer_tree_host()->settings().maxPartialTextureUpdates > 0; |
| if (!partial_updates_allowed) |
| resource->texture()->returnBackingTexture(); |
| |
| gfx::Vector2d dest_offset(0, 0); |
| resource->update(*queue, rect, dest_offset, partial_updates_allowed, stats); |
| } |
| |
| gfx::Rect ScrollbarLayer::ScrollbarLayerRectToContentRect( |
| gfx::Rect layer_rect) const { |
| // Don't intersect with the bounds as in LayerRectToContentRect() because |
| // layer_rect here might be in coordinates of the containing layer. |
| gfx::RectF content_rect = gfx::ScaleRect(layer_rect, |
| contents_scale_y(), |
| contents_scale_y()); |
| return gfx::ToEnclosingRect(content_rect); |
| } |
| |
| void ScrollbarLayer::SetTexturePriorities( |
| const PriorityCalculator& priority_calc) { |
| if (layer_tree_host()->settings().solidColorScrollbars) |
| return; |
| |
| if (content_bounds().IsEmpty()) |
| return; |
| DCHECK_LE(content_bounds().width(), MaxTextureSize()); |
| DCHECK_LE(content_bounds().height(), MaxTextureSize()); |
| |
| CreateUpdaterIfNeeded(); |
| |
| bool draws_to_root = !render_target()->parent(); |
| if (back_track_) { |
| back_track_->texture()->setDimensions(content_bounds(), texture_format_); |
| back_track_->texture()->setRequestPriority( |
| PriorityCalculator::uiPriority(draws_to_root)); |
| } |
| if (fore_track_) { |
| fore_track_->texture()->setDimensions(content_bounds(), texture_format_); |
| fore_track_->texture()->setRequestPriority( |
| PriorityCalculator::uiPriority(draws_to_root)); |
| } |
| if (thumb_) { |
| gfx::Rect thumb_layer_rect = geometry_->thumbRect(scrollbar_.get()); |
| gfx::Size thumb_size = |
| ScrollbarLayerRectToContentRect(thumb_layer_rect).size(); |
| thumb_->texture()->setDimensions(thumb_size, texture_format_); |
| thumb_->texture()->setRequestPriority( |
| PriorityCalculator::uiPriority(draws_to_root)); |
| } |
| } |
| |
| void ScrollbarLayer::Update(ResourceUpdateQueue* queue, |
| const OcclusionTracker* occlusion, |
| RenderingStats* stats) { |
| ContentsScalingLayer::Update(queue, occlusion, stats); |
| |
| dirty_rect_.Union(update_rect_); |
| if (content_bounds().IsEmpty()) |
| return; |
| if (visible_content_rect().IsEmpty()) |
| return; |
| |
| CreateUpdaterIfNeeded(); |
| |
| gfx::Rect content_rect = ScrollbarLayerRectToContentRect( |
| gfx::Rect(scrollbar_->location(), bounds())); |
| UpdatePart(back_track_updater_.get(), |
| back_track_.get(), |
| content_rect, |
| queue, |
| stats); |
| if (fore_track_ && fore_track_updater_) { |
| UpdatePart(fore_track_updater_.get(), |
| fore_track_.get(), |
| content_rect, |
| queue, |
| stats); |
| } |
| |
| // Consider the thumb to be at the origin when painting. |
| gfx::Rect thumb_rect = geometry_->thumbRect(scrollbar_.get()); |
| thumb_size_ = thumb_rect.size(); |
| gfx::Rect origin_thumb_rect = |
| ScrollbarLayerRectToContentRect(gfx::Rect(thumb_rect.size())); |
| if (!origin_thumb_rect.IsEmpty()) { |
| UpdatePart(thumb_updater_.get(), |
| thumb_.get(), |
| origin_thumb_rect, |
| queue, |
| stats); |
| } |
| |
| dirty_rect_ = gfx::RectF(); |
| } |
| |
| } // namespace cc |