blob: 95eb6384732819c75bded06789c56b6cb76ee506 [file] [log] [blame]
// 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/layers/scrollbar_layer.h"
#include "base/auto_reset.h"
#include "base/basictypes.h"
#include "base/debug/trace_event.h"
#include "cc/layers/scrollbar_layer_impl.h"
#include "cc/resources/caching_bitmap_content_layer_updater.h"
#include "cc/resources/layer_painter.h"
#include "cc/resources/prioritized_resource.h"
#include "cc/resources/resource_update_queue.h"
#include "cc/trees/layer_tree_host.h"
#include "ui/gfx/rect_conversions.h"
namespace cc {
scoped_ptr<LayerImpl> ScrollbarLayer::CreateLayerImpl(
LayerTreeImpl* tree_impl) {
return ScrollbarLayerImpl::Create(
tree_impl, id(), scrollbar_->Orientation()).PassAs<LayerImpl>();
}
scoped_refptr<ScrollbarLayer> ScrollbarLayer::Create(
scoped_ptr<Scrollbar> scrollbar,
int scroll_layer_id) {
return make_scoped_refptr(new ScrollbarLayer(scrollbar.Pass(),
scroll_layer_id));
}
ScrollbarLayer::ScrollbarLayer(
scoped_ptr<Scrollbar> scrollbar,
int scroll_layer_id)
: scrollbar_(scrollbar.Pass()),
scroll_layer_id_(scroll_layer_id),
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();
}
bool ScrollbarLayer::OpacityCanAnimateOnImplThread() const {
return scrollbar_->IsOverlay();
}
ScrollbarOrientation 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().solid_color_scrollbars)
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,
float device_scale_factor,
float page_scale_factor,
bool animating_transform_to_screen,
float* contents_scale_x,
float* contents_scale_y,
gfx::Size* content_bounds) {
ContentsScalingLayer::CalculateContentsScale(
ClampScaleToMaxTextureSize(ideal_contents_scale),
device_scale_factor,
page_scale_factor,
animating_transform_to_screen,
contents_scale_x,
contents_scale_y,
content_bounds);
}
void ScrollbarLayer::PushPropertiesTo(LayerImpl* layer) {
ContentsScalingLayer::PushPropertiesTo(layer);
ScrollbarLayerImpl* scrollbar_layer = static_cast<ScrollbarLayerImpl*>(layer);
if (layer_tree_host() &&
layer_tree_host()->settings().solid_color_scrollbars) {
int thickness_override =
layer_tree_host()->settings().solid_color_scrollbar_thickness_dip;
if (thickness_override != -1) {
scrollbar_layer->SetThumbThickness(thickness_override);
} else {
if (Orientation() == HORIZONTAL)
scrollbar_layer->SetThumbThickness(bounds().height());
else
scrollbar_layer->SetThumbThickness(bounds().width());
}
} else {
scrollbar_layer->SetThumbThickness(thumb_thickness_);
}
scrollbar_layer->SetThumbLength(thumb_length_);
if (Orientation() == HORIZONTAL) {
scrollbar_layer->SetTrackStart(track_rect_.x() - location_.x());
scrollbar_layer->SetTrackLength(track_rect_.width());
} else {
scrollbar_layer->SetTrackStart(track_rect_.y() - location_.y());
scrollbar_layer->SetTrackLength(track_rect_.height());
}
if (track_ && track_->texture()->have_backing_texture())
scrollbar_layer->set_track_resource_id(track_->texture()->resource_id());
else
scrollbar_layer->set_track_resource_id(0);
if (thumb_ && thumb_->texture()->have_backing_texture())
scrollbar_layer->set_thumb_resource_id(thumb_->texture()->resource_id());
else
scrollbar_layer->set_thumb_resource_id(0);
scrollbar_layer->set_is_overlay_scrollbar(scrollbar_->IsOverlay());
// ScrollbarLayer must push properties every frame. crbug.com/259095
needs_push_properties_ = true;
}
ScrollbarLayer* ScrollbarLayer::ToScrollbarLayer() {
return this;
}
void ScrollbarLayer::SetLayerTreeHost(LayerTreeHost* host) {
if (!host || host != layer_tree_host()) {
track_updater_ = NULL;
track_.reset();
thumb_updater_ = NULL;
thumb_.reset();
}
ContentsScalingLayer::SetLayerTreeHost(host);
}
class ScrollbarPartPainter : public LayerPainter {
public:
ScrollbarPartPainter(Scrollbar* scrollbar, ScrollbarPart part)
: scrollbar_(scrollbar),
part_(part) {}
virtual ~ScrollbarPartPainter() {}
// LayerPainter implementation
virtual void Paint(SkCanvas* canvas,
gfx::Rect content_rect,
gfx::RectF* opaque) OVERRIDE {
scrollbar_->PaintPart(canvas, part_, content_rect);
}
private:
Scrollbar* scrollbar_;
ScrollbarPart part_;
};
void ScrollbarLayer::CreateUpdaterIfNeeded() {
if (layer_tree_host()->settings().solid_color_scrollbars)
return;
texture_format_ =
layer_tree_host()->GetRendererCapabilities().best_texture_format;
if (!track_updater_.get()) {
track_updater_ = CachingBitmapContentLayerUpdater::Create(
scoped_ptr<LayerPainter>(
new ScrollbarPartPainter(scrollbar_.get(), TRACK))
.Pass(),
rendering_stats_instrumentation(),
id());
}
if (!track_) {
track_ = track_updater_->CreateResource(
layer_tree_host()->contents_texture_manager());
}
if (!thumb_updater_.get()) {
thumb_updater_ = CachingBitmapContentLayerUpdater::Create(
scoped_ptr<LayerPainter>(
new ScrollbarPartPainter(scrollbar_.get(), THUMB))
.Pass(),
rendering_stats_instrumentation(),
id());
}
if (!thumb_ && scrollbar_->HasThumb()) {
thumb_ = thumb_updater_->CreateResource(
layer_tree_host()->contents_texture_manager());
}
}
bool ScrollbarLayer::UpdatePart(CachingBitmapContentLayerUpdater* painter,
LayerUpdater::Resource* resource,
gfx::Rect rect,
ResourceUpdateQueue* queue) {
if (layer_tree_host()->settings().solid_color_scrollbars)
return false;
// Skip painting and uploading if there are no invalidations and
// we already have valid texture data.
if (resource->texture()->have_backing_texture() &&
resource->texture()->size() == rect.size() &&
!is_dirty())
return false;
// We should always have enough memory for UI.
DCHECK(resource->texture()->can_acquire_backing_texture());
if (!resource->texture()->can_acquire_backing_texture())
return false;
// 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);
if (!painter->pixels_did_change() &&
resource->texture()->have_backing_texture()) {
TRACE_EVENT_INSTANT0("cc",
"ScrollbarLayer::UpdatePart no texture upload needed",
TRACE_EVENT_SCOPE_THREAD);
return false;
}
bool partial_updates_allowed =
layer_tree_host()->settings().max_partial_texture_updates > 0;
if (!partial_updates_allowed)
resource->texture()->ReturnBackingTexture();
gfx::Vector2d dest_offset(0, 0);
resource->Update(queue, rect, dest_offset, partial_updates_allowed);
return true;
}
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::Rect expanded_rect = gfx::ScaleToEnclosingRect(
layer_rect, contents_scale_y(), contents_scale_y());
// We should never return a rect bigger than the content_bounds().
gfx::Size clamped_size = expanded_rect.size();
clamped_size.SetToMin(content_bounds());
expanded_rect.set_size(clamped_size);
return expanded_rect;
}
void ScrollbarLayer::SetTexturePriorities(
const PriorityCalculator& priority_calc) {
if (layer_tree_host()->settings().solid_color_scrollbars)
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 (track_) {
track_->texture()->SetDimensions(content_bounds(), texture_format_);
track_->texture()->set_request_priority(
PriorityCalculator::UIPriority(draws_to_root));
}
if (thumb_) {
gfx::Size thumb_size = OriginThumbRect().size();
thumb_->texture()->SetDimensions(thumb_size, texture_format_);
thumb_->texture()->set_request_priority(
PriorityCalculator::UIPriority(draws_to_root));
}
}
bool ScrollbarLayer::Update(ResourceUpdateQueue* queue,
const OcclusionTracker* occlusion) {
track_rect_ = scrollbar_->TrackRect();
location_ = scrollbar_->Location();
if (layer_tree_host()->settings().solid_color_scrollbars)
return false;
bool updated = false;
{
base::AutoReset<bool> ignore_set_needs_commit(&ignore_set_needs_commit_,
true);
updated = ContentsScalingLayer::Update(queue, occlusion);
}
dirty_rect_.Union(update_rect_);
if (content_bounds().IsEmpty())
return false;
if (visible_content_rect().IsEmpty())
return false;
CreateUpdaterIfNeeded();
gfx::Rect content_rect = ScrollbarLayerRectToContentRect(
gfx::Rect(scrollbar_->Location(), bounds()));
updated |= UpdatePart(track_updater_.get(), track_.get(), content_rect,
queue);
if (scrollbar_->HasThumb()) {
thumb_thickness_ = scrollbar_->ThumbThickness();
thumb_length_ = scrollbar_->ThumbLength();
gfx::Rect origin_thumb_rect = OriginThumbRect();
if (!origin_thumb_rect.IsEmpty()) {
updated |= UpdatePart(thumb_updater_.get(), thumb_.get(),
origin_thumb_rect, queue);
}
}
dirty_rect_ = gfx::RectF();
return updated;
}
gfx::Rect ScrollbarLayer::OriginThumbRect() const {
gfx::Size thumb_size;
if (Orientation() == HORIZONTAL) {
thumb_size = gfx::Size(scrollbar_->ThumbLength(),
scrollbar_->ThumbThickness());
} else {
thumb_size = gfx::Size(scrollbar_->ThumbThickness(),
scrollbar_->ThumbLength());
}
return ScrollbarLayerRectToContentRect(gfx::Rect(thumb_size));
}
} // namespace cc