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