1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "config.h" 6 7#include "platform/graphics/RecordingImageBufferSurface.h" 8 9#include "platform/graphics/GraphicsContext.h" 10#include "platform/graphics/ImageBuffer.h" 11#include "public/platform/Platform.h" 12#include "third_party/skia/include/core/SkCanvas.h" 13#include "third_party/skia/include/core/SkPictureRecorder.h" 14#include "wtf/PassOwnPtr.h" 15#include "wtf/PassRefPtr.h" 16 17namespace blink { 18 19RecordingImageBufferSurface::RecordingImageBufferSurface(const IntSize& size, OpacityMode opacityMode) 20 : ImageBufferSurface(size, opacityMode) 21 , m_imageBuffer(0) 22 , m_initialSaveCount(0) 23 , m_frameWasCleared(true) 24{ 25 initializeCurrentFrame(); 26} 27 28RecordingImageBufferSurface::~RecordingImageBufferSurface() 29{ } 30 31bool RecordingImageBufferSurface::initializeCurrentFrame() 32{ 33 StateStack stateStack; 34 bool saved = false; 35 if (m_currentFrame) { 36 saved = saveState(m_currentFrame->getRecordingCanvas(), &stateStack); 37 if (!saved) 38 return false; 39 } 40 41 static SkRTreeFactory rTreeFactory; 42 m_currentFrame = adoptPtr(new SkPictureRecorder); 43 m_currentFrame->beginRecording(size().width(), size().height(), &rTreeFactory); 44 m_initialSaveCount = m_currentFrame->getRecordingCanvas()->getSaveCount(); 45 if (m_imageBuffer) { 46 m_imageBuffer->context()->resetCanvas(m_currentFrame->getRecordingCanvas()); 47 m_imageBuffer->context()->setRegionTrackingMode(GraphicsContext::RegionTrackingOverwrite); 48 } 49 50 if (saved) 51 setCurrentState(m_currentFrame->getRecordingCanvas(), &stateStack); 52 53 return true; 54} 55 56void RecordingImageBufferSurface::setImageBuffer(ImageBuffer* imageBuffer) 57{ 58 m_imageBuffer = imageBuffer; 59 if (m_currentFrame && m_imageBuffer) { 60 m_imageBuffer->context()->setRegionTrackingMode(GraphicsContext::RegionTrackingOverwrite); 61 m_imageBuffer->context()->resetCanvas(m_currentFrame->getRecordingCanvas()); 62 } 63} 64 65void RecordingImageBufferSurface::willAccessPixels() 66{ 67 fallBackToRasterCanvas(); 68} 69 70void RecordingImageBufferSurface::fallBackToRasterCanvas() 71{ 72 if (m_rasterCanvas) { 73 ASSERT(!m_currentFrame); 74 return; 75 } 76 77 m_rasterCanvas = adoptPtr(SkCanvas::NewRasterN32(size().width(), size().height())); 78 79 if (m_previousFrame) { 80 m_previousFrame->draw(m_rasterCanvas.get()); 81 m_previousFrame.clear(); 82 } 83 if (m_currentFrame) { 84 RefPtr<SkPicture> currentPicture = adoptRef(m_currentFrame->endRecording()); 85 currentPicture->draw(m_rasterCanvas.get()); 86 m_currentFrame.clear(); 87 } 88 89 if (m_imageBuffer) { 90 m_imageBuffer->context()->setRegionTrackingMode(GraphicsContext::RegionTrackingDisabled); 91 m_imageBuffer->context()->resetCanvas(m_rasterCanvas.get()); 92 } 93} 94 95SkCanvas* RecordingImageBufferSurface::canvas() const 96{ 97 if (m_rasterCanvas) 98 return m_rasterCanvas.get(); 99 100 ASSERT(m_currentFrame->getRecordingCanvas()); 101 return m_currentFrame->getRecordingCanvas(); 102} 103 104PassRefPtr<SkPicture> RecordingImageBufferSurface::getPicture() 105{ 106 bool canUsePicture = finalizeFrameInternal(); 107 m_imageBuffer->didFinalizeFrame(); 108 109 if (canUsePicture) { 110 return m_previousFrame; 111 } 112 113 if (!m_rasterCanvas) 114 fallBackToRasterCanvas(); 115 return nullptr; 116} 117 118void RecordingImageBufferSurface::finalizeFrame(const FloatRect &) 119{ 120 if (!finalizeFrameInternal() && !m_rasterCanvas) { 121 fallBackToRasterCanvas(); 122 } 123} 124 125void RecordingImageBufferSurface::didClearCanvas() 126{ 127 m_frameWasCleared = true; 128} 129 130bool RecordingImageBufferSurface::finalizeFrameInternal() 131{ 132 if (!m_imageBuffer->isDirty()) { 133 if (m_currentFrame && !m_previousFrame) { 134 // Create an initial blank frame 135 m_previousFrame = adoptRef(m_currentFrame->endRecording()); 136 initializeCurrentFrame(); 137 } 138 return m_currentFrame; 139 } 140 141 if (!m_currentFrame) { 142 return false; 143 } 144 145 IntRect canvasRect(IntPoint(0, 0), size()); 146 if (!m_frameWasCleared && !m_imageBuffer->context()->opaqueRegion().asRect().contains(canvasRect)) { 147 return false; 148 } 149 150 m_previousFrame = adoptRef(m_currentFrame->endRecording()); 151 if (!initializeCurrentFrame()) 152 return false; 153 154 155 m_frameWasCleared = false; 156 return true; 157} 158 159bool RecordingImageBufferSurface::saveState(SkCanvas* srcCanvas, StateStack* stateStack) 160{ 161 StateRec state; 162 163 // we will remove all the saved state stack in endRecording anyway, so it makes no difference 164 while (srcCanvas->getSaveCount() > m_initialSaveCount) { 165 state.m_ctm = srcCanvas->getTotalMatrix(); 166 // FIXME: don't mess up the state in case we will have to fallback, crbug.com/408392 167 if (!srcCanvas->getClipDeviceBounds(&state.m_clip)) 168 return false; 169 stateStack->push(state); 170 srcCanvas->restore(); 171 } 172 173 state.m_ctm = srcCanvas->getTotalMatrix(); 174 // FIXME: don't mess up the state in case we will have to fallback, crbug.com/408392 175 if (!srcCanvas->getClipDeviceBounds(&state.m_clip)) 176 return false; 177 stateStack->push(state); 178 179 return true; 180} 181 182void RecordingImageBufferSurface::setCurrentState(SkCanvas* dstCanvas, StateStack* stateStack) 183{ 184 while (stateStack->size() > 1) { 185 dstCanvas->resetMatrix(); 186 dstCanvas->clipRect(SkRect::MakeFromIRect(stateStack->peek().m_clip)); 187 dstCanvas->setMatrix(stateStack->peek().m_ctm); 188 dstCanvas->save(); 189 stateStack->pop(); 190 } 191 192 dstCanvas->resetMatrix(); 193 dstCanvas->clipRect(SkRect::MakeFromIRect(stateStack->peek().m_clip)); 194 dstCanvas->setMatrix(stateStack->peek().m_ctm); 195} 196 197} // namespace blink 198