blob: ac85b2a307c5f9cfb9646e971b940b702cc4a459 [file] [log] [blame]
// Copyright 2014 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 "config.h"
#include "platform/graphics/RecordingImageBufferSurface.h"
#include "platform/graphics/ExpensiveCanvasHeuristicParameters.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/ImageBuffer.h"
#include "public/platform/Platform.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "wtf/PassOwnPtr.h"
#include "wtf/PassRefPtr.h"
namespace blink {
RecordingImageBufferSurface::RecordingImageBufferSurface(const IntSize& size, PassOwnPtr<RecordingImageBufferFallbackSurfaceFactory> fallbackFactory, OpacityMode opacityMode)
: ImageBufferSurface(size, opacityMode)
, m_imageBuffer(0)
, m_currentFramePixelCount(0)
, m_previousFramePixelCount(0)
, m_frameWasCleared(true)
, m_didRecordDrawCommandsInCurrentFrame(false)
, m_currentFrameHasExpensiveOp(false)
, m_previousFrameHasExpensiveOp(false)
, m_fallbackFactory(fallbackFactory)
{
initializeCurrentFrame();
}
RecordingImageBufferSurface::~RecordingImageBufferSurface()
{ }
bool RecordingImageBufferSurface::initializeCurrentFrame()
{
static SkRTreeFactory rTreeFactory;
m_currentFrame = adoptPtr(new SkPictureRecorder);
m_currentFrame->beginRecording(size().width(), size().height(), &rTreeFactory);
if (m_imageBuffer) {
m_imageBuffer->resetCanvas(m_currentFrame->getRecordingCanvas());
}
m_didRecordDrawCommandsInCurrentFrame = false;
m_currentFrameHasExpensiveOp = false;
m_currentFramePixelCount = 0;
return true;
}
void RecordingImageBufferSurface::setImageBuffer(ImageBuffer* imageBuffer)
{
m_imageBuffer = imageBuffer;
if (m_currentFrame && m_imageBuffer) {
m_imageBuffer->resetCanvas(m_currentFrame->getRecordingCanvas());
}
if (m_fallbackSurface) {
m_fallbackSurface->setImageBuffer(imageBuffer);
}
}
bool RecordingImageBufferSurface::writePixels(const SkImageInfo& origInfo, const void* pixels, size_t rowBytes, int x, int y)
{
if (!m_fallbackSurface) {
if (x <= 0 && y <= 0 && x + origInfo.width() >= size().width() && y + origInfo.height() >= size().height()) {
willOverwriteCanvas();
}
fallBackToRasterCanvas();
}
return m_fallbackSurface->writePixels(origInfo, pixels, rowBytes, x, y);
}
void RecordingImageBufferSurface::fallBackToRasterCanvas()
{
if (m_fallbackSurface) {
ASSERT(!m_currentFrame);
return;
}
m_fallbackSurface = m_fallbackFactory->createSurface(size(), opacityMode());
m_fallbackSurface->setImageBuffer(m_imageBuffer);
if (m_previousFrame) {
m_previousFrame->playback(m_fallbackSurface->canvas());
m_previousFrame.clear();
}
if (m_currentFrame) {
RefPtr<SkPicture> currentPicture = adoptRef(m_currentFrame->endRecording());
currentPicture->playback(m_fallbackSurface->canvas());
m_currentFrame.clear();
}
if (m_imageBuffer) {
m_imageBuffer->resetCanvas(m_fallbackSurface->canvas());
}
}
PassRefPtr<SkImage> RecordingImageBufferSurface::newImageSnapshot()
{
if (!m_fallbackSurface)
fallBackToRasterCanvas();
return m_fallbackSurface->newImageSnapshot();
}
SkCanvas* RecordingImageBufferSurface::canvas()
{
if (m_fallbackSurface)
return m_fallbackSurface->canvas();
ASSERT(m_currentFrame->getRecordingCanvas());
return m_currentFrame->getRecordingCanvas();
}
void RecordingImageBufferSurface::disableDeferral()
{
if (!m_fallbackSurface)
fallBackToRasterCanvas();
}
PassRefPtr<SkPicture> RecordingImageBufferSurface::getPicture()
{
if (m_fallbackSurface)
return nullptr;
bool canUsePicture = finalizeFrameInternal();
m_imageBuffer->didFinalizeFrame();
if (canUsePicture) {
return m_previousFrame;
}
if (!m_fallbackSurface)
fallBackToRasterCanvas();
return nullptr;
}
void RecordingImageBufferSurface::finalizeFrame(const FloatRect &dirtyRect)
{
if (m_fallbackSurface) {
m_fallbackSurface->finalizeFrame(dirtyRect);
return;
}
if (!finalizeFrameInternal())
fallBackToRasterCanvas();
}
void RecordingImageBufferSurface::flush()
{
if (!m_fallbackSurface)
fallBackToRasterCanvas();
m_fallbackSurface->flush();
}
void RecordingImageBufferSurface::willOverwriteCanvas()
{
m_frameWasCleared = true;
m_previousFrame.clear();
m_previousFrameHasExpensiveOp = false;
m_previousFramePixelCount = 0;
if (m_didRecordDrawCommandsInCurrentFrame) {
// Discard previous draw commands
adoptRef(m_currentFrame->endRecording());
initializeCurrentFrame();
}
}
void RecordingImageBufferSurface::didDraw(const FloatRect& rect)
{
m_didRecordDrawCommandsInCurrentFrame = true;
IntRect pixelBounds = enclosingIntRect(rect);
m_currentFramePixelCount += pixelBounds.width() * pixelBounds.height();
}
bool RecordingImageBufferSurface::finalizeFrameInternal()
{
ASSERT(!m_fallbackSurface);
ASSERT(m_currentFrame);
ASSERT(m_currentFrame->getRecordingCanvas());
if (!m_imageBuffer->isDirty()) {
if (!m_previousFrame) {
// Create an initial blank frame
m_previousFrame = adoptRef(m_currentFrame->endRecording());
initializeCurrentFrame();
}
return m_currentFrame;
}
if (!m_frameWasCleared || m_currentFrame->getRecordingCanvas()->getSaveCount() > ExpensiveCanvasHeuristicParameters::ExpensiveRecordingStackDepth) {
return false;
}
m_previousFrame = adoptRef(m_currentFrame->endRecording());
m_previousFrameHasExpensiveOp = m_currentFrameHasExpensiveOp;
m_previousFramePixelCount = m_currentFramePixelCount;
if (!initializeCurrentFrame())
return false;
m_frameWasCleared = false;
return true;
}
void RecordingImageBufferSurface::draw(GraphicsContext* context, const FloatRect& destRect, const FloatRect& srcRect, SkXfermode::Mode op)
{
if (m_fallbackSurface) {
m_fallbackSurface->draw(context, destRect, srcRect, op);
return;
}
RefPtr<SkPicture> picture = getPicture();
if (picture) {
context->compositePicture(picture.get(), destRect, srcRect, op);
} else {
ImageBufferSurface::draw(context, destRect, srcRect, op);
}
}
bool RecordingImageBufferSurface::isExpensiveToPaint()
{
if (m_fallbackSurface)
return m_fallbackSurface->isExpensiveToPaint();
if (m_didRecordDrawCommandsInCurrentFrame) {
if (m_currentFrameHasExpensiveOp)
return true;
if (m_currentFramePixelCount >= (size().width() * size().height() * ExpensiveCanvasHeuristicParameters::ExpensiveOverdrawThreshold))
return true;
if (m_frameWasCleared)
return false; // early exit because previous frame is overdrawn
}
if (m_previousFrame) {
if (m_previousFrameHasExpensiveOp)
return true;
if (m_previousFramePixelCount >= (size().width() * size().height() * ExpensiveCanvasHeuristicParameters::ExpensiveOverdrawThreshold))
return true;
}
return false;
}
const SkBitmap& RecordingImageBufferSurface::deprecatedBitmapForOverwrite()
{
if (!m_fallbackSurface) {
willOverwriteCanvas();
fallBackToRasterCanvas();
}
return m_fallbackSurface->deprecatedBitmapForOverwrite();
}
// Fallback passthroughs
bool RecordingImageBufferSurface::restore()
{
if (m_fallbackSurface)
return m_fallbackSurface->restore();
return ImageBufferSurface::restore();
}
WebLayer* RecordingImageBufferSurface::layer() const
{
if (m_fallbackSurface)
return m_fallbackSurface->layer();
return ImageBufferSurface::layer();
}
bool RecordingImageBufferSurface::isAccelerated() const
{
if (m_fallbackSurface)
return m_fallbackSurface->isAccelerated();
return ImageBufferSurface::isAccelerated();
}
void RecordingImageBufferSurface::setIsHidden(bool hidden)
{
if (m_fallbackSurface)
m_fallbackSurface->setIsHidden(hidden);
else
ImageBufferSurface::setIsHidden(hidden);
}
} // namespace blink