blob: fbe73302a4128191d857f6e73d521af58258e9bc [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/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_initialSaveCount(0)
, m_frameWasCleared(true)
, m_fallbackFactory(fallbackFactory)
{
initializeCurrentFrame();
}
RecordingImageBufferSurface::~RecordingImageBufferSurface()
{ }
bool RecordingImageBufferSurface::initializeCurrentFrame()
{
StateStack stateStack;
bool saved = false;
if (m_currentFrame) {
saved = saveState(m_currentFrame->getRecordingCanvas(), &stateStack);
if (!saved)
return false;
}
static SkRTreeFactory rTreeFactory;
m_currentFrame = adoptPtr(new SkPictureRecorder);
m_currentFrame->beginRecording(size().width(), size().height(), &rTreeFactory);
m_initialSaveCount = m_currentFrame->getRecordingCanvas()->getSaveCount();
if (m_imageBuffer) {
m_imageBuffer->context()->resetCanvas(m_currentFrame->getRecordingCanvas());
m_imageBuffer->context()->setRegionTrackingMode(GraphicsContext::RegionTrackingOverwrite);
}
if (saved)
setCurrentState(m_currentFrame->getRecordingCanvas(), &stateStack);
return true;
}
void RecordingImageBufferSurface::setImageBuffer(ImageBuffer* imageBuffer)
{
m_imageBuffer = imageBuffer;
if (m_currentFrame && m_imageBuffer) {
m_imageBuffer->context()->setRegionTrackingMode(GraphicsContext::RegionTrackingOverwrite);
m_imageBuffer->context()->resetCanvas(m_currentFrame->getRecordingCanvas());
}
if (m_fallbackSurface) {
m_fallbackSurface->setImageBuffer(imageBuffer);
}
}
void RecordingImageBufferSurface::willAccessPixels()
{
if (m_fallbackSurface)
m_fallbackSurface->willAccessPixels();
else
fallBackToRasterCanvas();
}
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->draw(m_fallbackSurface->canvas());
m_previousFrame.clear();
}
if (m_currentFrame) {
bool savedState = false;
StateStack stateStack;
savedState = saveState(m_currentFrame->getRecordingCanvas(), &stateStack);
RefPtr<SkPicture> currentPicture = adoptRef(m_currentFrame->endRecording());
currentPicture->draw(m_fallbackSurface->canvas());
m_currentFrame.clear();
if (savedState)
setCurrentState(m_fallbackSurface->canvas(), &stateStack);
}
if (m_imageBuffer) {
m_imageBuffer->context()->setRegionTrackingMode(GraphicsContext::RegionTrackingDisabled);
m_imageBuffer->context()->resetCanvas(m_fallbackSurface->canvas());
m_imageBuffer->context()->setAccelerated(m_fallbackSurface->isAccelerated());
}
}
PassRefPtr<SkImage> RecordingImageBufferSurface::newImageSnapshot() const
{
if (!m_fallbackSurface)
const_cast<RecordingImageBufferSurface*>(this)->fallBackToRasterCanvas();
return m_fallbackSurface->newImageSnapshot();
}
SkCanvas* RecordingImageBufferSurface::canvas() const
{
if (m_fallbackSurface)
return m_fallbackSurface->canvas();
ASSERT(m_currentFrame->getRecordingCanvas());
return m_currentFrame->getRecordingCanvas();
}
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::didClearCanvas()
{
if (m_fallbackSurface) {
m_fallbackSurface->didClearCanvas();
return;
}
m_frameWasCleared = true;
}
bool RecordingImageBufferSurface::finalizeFrameInternal()
{
ASSERT(!m_fallbackSurface);
if (!m_imageBuffer->isDirty()) {
if (m_currentFrame && !m_previousFrame) {
// Create an initial blank frame
m_previousFrame = adoptRef(m_currentFrame->endRecording());
initializeCurrentFrame();
}
return m_currentFrame;
}
IntRect canvasRect(IntPoint(0, 0), size());
if (!m_frameWasCleared && !m_imageBuffer->context()->opaqueRegion().asRect().contains(canvasRect)) {
return false;
}
m_previousFrame = adoptRef(m_currentFrame->endRecording());
if (!initializeCurrentFrame())
return false;
m_frameWasCleared = false;
return true;
}
bool RecordingImageBufferSurface::saveState(SkCanvas* srcCanvas, StateStack* stateStack)
{
StateRec state;
// we will remove all the saved state stack in endRecording anyway, so it makes no difference
while (srcCanvas->getSaveCount() > m_initialSaveCount) {
state.m_ctm = srcCanvas->getTotalMatrix();
// FIXME: don't mess up the state in case we will have to fallback, crbug.com/408392
if (!srcCanvas->getClipDeviceBounds(&state.m_clip))
return false;
stateStack->push(state);
srcCanvas->restore();
}
state.m_ctm = srcCanvas->getTotalMatrix();
// FIXME: don't mess up the state in case we will have to fallback, crbug.com/408392
if (!srcCanvas->getClipDeviceBounds(&state.m_clip))
return false;
stateStack->push(state);
return true;
}
void RecordingImageBufferSurface::setCurrentState(SkCanvas* dstCanvas, StateStack* stateStack)
{
while (stateStack->size() > 1) {
dstCanvas->resetMatrix();
dstCanvas->clipRect(SkRect::MakeFromIRect(stateStack->peek().m_clip));
dstCanvas->setMatrix(stateStack->peek().m_ctm);
dstCanvas->save();
stateStack->pop();
}
dstCanvas->resetMatrix();
dstCanvas->clipRect(SkRect::MakeFromIRect(stateStack->peek().m_clip));
dstCanvas->setMatrix(stateStack->peek().m_ctm);
}
void RecordingImageBufferSurface::willDrawVideo()
{
// Video draws need to be synchronous
if (!m_fallbackSurface)
fallBackToRasterCanvas();
}
// Fallback passthroughs
const SkBitmap& RecordingImageBufferSurface::bitmap()
{
if (m_fallbackSurface)
return m_fallbackSurface->bitmap();
return ImageBufferSurface::bitmap();
}
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();
}
Platform3DObject RecordingImageBufferSurface::getBackingTexture() const
{
if (m_fallbackSurface)
return m_fallbackSurface->getBackingTexture();
return ImageBufferSurface::getBackingTexture();
}
bool RecordingImageBufferSurface::cachedBitmapEnabled() const
{
if (m_fallbackSurface)
return m_fallbackSurface->cachedBitmapEnabled();
return ImageBufferSurface::cachedBitmapEnabled();
}
const SkBitmap& RecordingImageBufferSurface::cachedBitmap() const
{
if (m_fallbackSurface)
return m_fallbackSurface->cachedBitmap();
return ImageBufferSurface::cachedBitmap();
}
void RecordingImageBufferSurface::invalidateCachedBitmap()
{
if (m_fallbackSurface)
m_fallbackSurface->invalidateCachedBitmap();
else
ImageBufferSurface::invalidateCachedBitmap();
}
void RecordingImageBufferSurface::updateCachedBitmapIfNeeded()
{
if (m_fallbackSurface)
m_fallbackSurface->updateCachedBitmapIfNeeded();
else
ImageBufferSurface::updateCachedBitmapIfNeeded();
}
void RecordingImageBufferSurface::setIsHidden(bool hidden)
{
if (m_fallbackSurface)
m_fallbackSurface->setIsHidden(hidden);
else
ImageBufferSurface::setIsHidden(hidden);
}
} // namespace blink