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
17 namespace blink {
18
RecordingImageBufferSurface(const IntSize & size,OpacityMode opacityMode)19 RecordingImageBufferSurface::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
~RecordingImageBufferSurface()28 RecordingImageBufferSurface::~RecordingImageBufferSurface()
29 { }
30
initializeCurrentFrame()31 bool 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
setImageBuffer(ImageBuffer * imageBuffer)56 void 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
willAccessPixels()65 void RecordingImageBufferSurface::willAccessPixels()
66 {
67 fallBackToRasterCanvas();
68 }
69
fallBackToRasterCanvas()70 void 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
canvas() const95 SkCanvas* 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
getPicture()104 PassRefPtr<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
finalizeFrame(const FloatRect &)118 void RecordingImageBufferSurface::finalizeFrame(const FloatRect &)
119 {
120 if (!finalizeFrameInternal() && !m_rasterCanvas) {
121 fallBackToRasterCanvas();
122 }
123 }
124
didClearCanvas()125 void RecordingImageBufferSurface::didClearCanvas()
126 {
127 m_frameWasCleared = true;
128 }
129
finalizeFrameInternal()130 bool 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
saveState(SkCanvas * srcCanvas,StateStack * stateStack)159 bool 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
setCurrentState(SkCanvas * dstCanvas,StateStack * stateStack)182 void 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