blob: 6fad23cb11481dd7b64811a269b10d5fe873779f [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/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* treeImpl)
{
return ScrollbarLayerImpl::create(treeImpl, id(), ScrollbarGeometryFixedThumb::create(make_scoped_ptr(m_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)
: m_scrollbar(scrollbar.Pass())
, m_painter(painter.Pass())
, m_geometry(geometry.Pass())
, m_scrollLayerId(scrollLayerId)
, m_textureFormat(GL_INVALID_ENUM)
{
if (!m_scrollbar->isOverlay())
setShouldScrollOnMainThread(true);
}
ScrollbarLayer::~ScrollbarLayer()
{
}
void ScrollbarLayer::setScrollLayerId(int id)
{
if (id == m_scrollLayerId)
return;
m_scrollLayerId = id;
setNeedsFullTreeSync();
}
WebKit::WebScrollbar::Orientation ScrollbarLayer::orientation() const
{
return m_scrollbar->orientation();
}
int ScrollbarLayer::maxTextureSize() {
DCHECK(layerTreeHost());
return layerTreeHost()->rendererCapabilities().maxTextureSize;
}
float ScrollbarLayer::clampScaleToMaxTextureSize(float scale) {
// If the scaled contentBounds() is bigger than the max texture size of the
// device, we need to clamp it by rescaling, since contentBounds() is used
// below to set the texture size.
gfx::Size scaledBounds = computeContentBoundsForScale(scale, scale);
if (scaledBounds.width() > maxTextureSize() || scaledBounds.height() > maxTextureSize()) {
if (scaledBounds.width() > scaledBounds.height())
return (maxTextureSize() - 1) / static_cast<float>(bounds().width());
else
return (maxTextureSize() - 1) / static_cast<float>(bounds().height());
}
return scale;
}
void ScrollbarLayer::calculateContentsScale(
float idealContentsScale,
bool animatingTransformToScreen,
float* contentsScaleX,
float* contentsScaleY,
gfx::Size* contentBounds)
{
ContentsScalingLayer::calculateContentsScale(
clampScaleToMaxTextureSize(idealContentsScale),
animatingTransformToScreen,
contentsScaleX,
contentsScaleY,
contentBounds);
DCHECK_LE(contentBounds->width(), maxTextureSize());
DCHECK_LE(contentBounds->height(), maxTextureSize());
}
void ScrollbarLayer::pushPropertiesTo(LayerImpl* layer)
{
ContentsScalingLayer::pushPropertiesTo(layer);
ScrollbarLayerImpl* scrollbarLayer = static_cast<ScrollbarLayerImpl*>(layer);
scrollbarLayer->setScrollbarData(m_scrollbar.get());
scrollbarLayer->setThumbSize(m_thumbSize);
if (m_backTrack && m_backTrack->texture()->haveBackingTexture())
scrollbarLayer->setBackTrackResourceId(m_backTrack->texture()->resourceId());
else
scrollbarLayer->setBackTrackResourceId(0);
if (m_foreTrack && m_foreTrack->texture()->haveBackingTexture())
scrollbarLayer->setForeTrackResourceId(m_foreTrack->texture()->resourceId());
else
scrollbarLayer->setForeTrackResourceId(0);
if (m_thumb && m_thumb->texture()->haveBackingTexture())
scrollbarLayer->setThumbResourceId(m_thumb->texture()->resourceId());
else
scrollbarLayer->setThumbResourceId(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 contentRect, gfx::RectF&) OVERRIDE
{
// The following is a simplification of ScrollbarThemeComposite::paint.
m_painter->PaintScrollbarBackground(canvas, contentRect);
if (m_geometry->hasButtons(m_scrollbar)) {
gfx::Rect backButtonStartPaintRect = m_geometry->backButtonStartRect(m_scrollbar);
m_painter->PaintBackButtonStart(canvas, backButtonStartPaintRect);
gfx::Rect backButtonEndPaintRect = m_geometry->backButtonEndRect(m_scrollbar);
m_painter->PaintBackButtonEnd(canvas, backButtonEndPaintRect);
gfx::Rect forwardButtonStartPaintRect = m_geometry->forwardButtonStartRect(m_scrollbar);
m_painter->PaintForwardButtonStart(canvas, forwardButtonStartPaintRect);
gfx::Rect forwardButtonEndPaintRect = m_geometry->forwardButtonEndRect(m_scrollbar);
m_painter->PaintForwardButtonEnd(canvas, forwardButtonEndPaintRect);
}
gfx::Rect trackPaintRect = m_geometry->trackRect(m_scrollbar);
m_painter->PaintTrackBackground(canvas, trackPaintRect);
bool thumbPresent = m_geometry->hasThumb(m_scrollbar);
if (thumbPresent) {
if (m_trackPart == WebKit::WebScrollbar::ForwardTrackPart)
m_painter->PaintForwardTrackPart(canvas, trackPaintRect);
else
m_painter->PaintBackTrackPart(canvas, trackPaintRect);
}
m_painter->PaintTickmarks(canvas, trackPaintRect);
}
private:
ScrollbarBackgroundPainter(WebKit::WebScrollbar* scrollbar, ScrollbarThemePainter *painter, WebKit::WebScrollbarThemeGeometry* geometry, WebKit::WebScrollbar::ScrollbarPart trackPart)
: m_scrollbar(scrollbar)
, m_painter(painter)
, m_geometry(geometry)
, m_trackPart(trackPart)
{
}
WebKit::WebScrollbar* m_scrollbar;
ScrollbarThemePainter* m_painter;
WebKit::WebScrollbarThemeGeometry* m_geometry;
WebKit::WebScrollbar::ScrollbarPart m_trackPart;
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 contentRect, gfx::RectF& opaque) OVERRIDE
{
// Consider the thumb to be at the origin when painting.
gfx::Rect thumbRect = m_geometry->thumbRect(m_scrollbar);
m_painter->PaintThumb(canvas, gfx::Rect(thumbRect.size()));
}
private:
ScrollbarThumbPainter(WebKit::WebScrollbar* scrollbar, ScrollbarThemePainter* painter, WebKit::WebScrollbarThemeGeometry* geometry)
: m_scrollbar(scrollbar)
, m_painter(painter)
, m_geometry(geometry)
{
}
WebKit::WebScrollbar* m_scrollbar;
ScrollbarThemePainter* m_painter;
WebKit::WebScrollbarThemeGeometry* m_geometry;
DISALLOW_COPY_AND_ASSIGN(ScrollbarThumbPainter);
};
void ScrollbarLayer::setLayerTreeHost(LayerTreeHost* host)
{
if (!host || host != layerTreeHost()) {
m_backTrackUpdater = NULL;
m_backTrack.reset();
m_thumbUpdater = NULL;
m_thumb.reset();
}
ContentsScalingLayer::setLayerTreeHost(host);
}
void ScrollbarLayer::createUpdaterIfNeeded()
{
m_textureFormat = layerTreeHost()->rendererCapabilities().bestTextureFormat;
if (!m_backTrackUpdater)
m_backTrackUpdater = CachingBitmapContentLayerUpdater::Create(ScrollbarBackgroundPainter::create(m_scrollbar.get(), m_painter.get(), m_geometry.get(), WebKit::WebScrollbar::BackTrackPart).PassAs<LayerPainter>());
if (!m_backTrack)
m_backTrack = m_backTrackUpdater->createResource(layerTreeHost()->contentsTextureManager());
// Only create two-part track if we think the two parts could be different in appearance.
if (m_scrollbar->isCustomScrollbar()) {
if (!m_foreTrackUpdater)
m_foreTrackUpdater = CachingBitmapContentLayerUpdater::Create(ScrollbarBackgroundPainter::create(m_scrollbar.get(), m_painter.get(), m_geometry.get(), WebKit::WebScrollbar::ForwardTrackPart).PassAs<LayerPainter>());
if (!m_foreTrack)
m_foreTrack = m_foreTrackUpdater->createResource(layerTreeHost()->contentsTextureManager());
}
if (!m_thumbUpdater)
m_thumbUpdater = CachingBitmapContentLayerUpdater::Create(ScrollbarThumbPainter::create(m_scrollbar.get(), m_painter.get(), m_geometry.get()).PassAs<LayerPainter>());
if (!m_thumb)
m_thumb = m_thumbUpdater->createResource(layerTreeHost()->contentsTextureManager());
}
void ScrollbarLayer::updatePart(CachingBitmapContentLayerUpdater* painter, LayerUpdater::Resource* resource, const gfx::Rect& rect, ResourceUpdateQueue& queue, RenderingStats* stats)
{
// 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() &&
!isDirty())
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 paintedOpaqueRect;
painter->prepareToUpdate(rect, rect.size(), contentsScaleX(), contentsScaleY(), paintedOpaqueRect, stats);
if (!painter->pixelsDidChange() && resource->texture()->haveBackingTexture()) {
TRACE_EVENT_INSTANT0("cc","ScrollbarLayer::updatePart no texture upload needed");
return;
}
bool partialUpdatesAllowed = layerTreeHost()->settings().maxPartialTextureUpdates > 0;
if (!partialUpdatesAllowed)
resource->texture()->returnBackingTexture();
gfx::Vector2d destOffset(0, 0);
resource->update(queue, rect, destOffset, partialUpdatesAllowed, stats);
}
gfx::Rect ScrollbarLayer::scrollbarLayerRectToContentRect(const gfx::Rect& layerRect) const
{
// Don't intersect with the bounds as in layerRectToContentRect() because
// layerRect here might be in coordinates of the containing layer.
gfx::RectF contentRect = gfx::ScaleRect(layerRect, contentsScaleX(), contentsScaleY());
return gfx::ToEnclosingRect(contentRect);
}
void ScrollbarLayer::setTexturePriorities(const PriorityCalculator&)
{
if (contentBounds().IsEmpty())
return;
DCHECK_LE(contentBounds().width(), maxTextureSize());
DCHECK_LE(contentBounds().height(), maxTextureSize());
createUpdaterIfNeeded();
bool drawsToRoot = !renderTarget()->parent();
if (m_backTrack) {
m_backTrack->texture()->setDimensions(contentBounds(), m_textureFormat);
m_backTrack->texture()->setRequestPriority(PriorityCalculator::uiPriority(drawsToRoot));
}
if (m_foreTrack) {
m_foreTrack->texture()->setDimensions(contentBounds(), m_textureFormat);
m_foreTrack->texture()->setRequestPriority(PriorityCalculator::uiPriority(drawsToRoot));
}
if (m_thumb) {
gfx::Size thumbSize = scrollbarLayerRectToContentRect(m_geometry->thumbRect(m_scrollbar.get())).size();
m_thumb->texture()->setDimensions(thumbSize, m_textureFormat);
m_thumb->texture()->setRequestPriority(PriorityCalculator::uiPriority(drawsToRoot));
}
}
void ScrollbarLayer::update(ResourceUpdateQueue& queue, const OcclusionTracker* occlusion, RenderingStats* stats)
{
ContentsScalingLayer::update(queue, occlusion, stats);
m_dirtyRect.Union(m_updateRect);
if (contentBounds().IsEmpty())
return;
if (visibleContentRect().IsEmpty())
return;
createUpdaterIfNeeded();
gfx::Rect contentRect = scrollbarLayerRectToContentRect(gfx::Rect(m_scrollbar->location(), bounds()));
updatePart(m_backTrackUpdater.get(), m_backTrack.get(), contentRect, queue, stats);
if (m_foreTrack && m_foreTrackUpdater)
updatePart(m_foreTrackUpdater.get(), m_foreTrack.get(), contentRect, queue, stats);
// Consider the thumb to be at the origin when painting.
gfx::Rect thumbRect = m_geometry->thumbRect(m_scrollbar.get());
m_thumbSize = thumbRect.size();
gfx::Rect originThumbRect = scrollbarLayerRectToContentRect(gfx::Rect(thumbRect.size()));
if (!originThumbRect.IsEmpty())
updatePart(m_thumbUpdater.get(), m_thumb.get(), originThumbRect, queue, stats);
m_dirtyRect = gfx::RectF();
}
} // namespace cc