1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "DrawTargetCapture.h"
8 #include "DrawCommand.h"
9 #include "DrawCommands.h"
10 #include "gfxPlatform.h"
11 #include "SourceSurfaceCapture.h"
12 #include "FilterNodeCapture.h"
13 #include "PathCapture.h"
14
15 namespace mozilla {
16 namespace gfx {
17
~DrawTargetCaptureImpl()18 DrawTargetCaptureImpl::~DrawTargetCaptureImpl() {
19 if (mSnapshot && !mSnapshot->hasOneRef()) {
20 mSnapshot->DrawTargetWillDestroy();
21 mSnapshot = nullptr;
22 }
23 }
24
DrawTargetCaptureImpl(gfx::DrawTarget * aTarget,size_t aFlushBytes)25 DrawTargetCaptureImpl::DrawTargetCaptureImpl(gfx::DrawTarget* aTarget,
26 size_t aFlushBytes)
27 : mSnapshot(nullptr),
28 mStride(0),
29 mSurfaceAllocationSize(0),
30 mFlushBytes(aFlushBytes) {
31 mSize = aTarget->GetSize();
32 mCurrentClipBounds.push(IntRect(IntPoint(0, 0), mSize));
33 mFormat = aTarget->GetFormat();
34 SetPermitSubpixelAA(aTarget->GetPermitSubpixelAA());
35
36 mRefDT = aTarget;
37 }
38
DrawTargetCaptureImpl(BackendType aBackend,const IntSize & aSize,SurfaceFormat aFormat)39 DrawTargetCaptureImpl::DrawTargetCaptureImpl(BackendType aBackend,
40 const IntSize& aSize,
41 SurfaceFormat aFormat)
42 : mSize(aSize),
43 mSnapshot(nullptr),
44 mStride(0),
45 mSurfaceAllocationSize(0),
46 mFlushBytes(0) {
47 RefPtr<DrawTarget> screenRefDT =
48 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
49
50 mCurrentClipBounds.push(IntRect(IntPoint(0, 0), aSize));
51 mFormat = aFormat;
52 SetPermitSubpixelAA(IsOpaque(mFormat));
53 if (aBackend == screenRefDT->GetBackendType()) {
54 mRefDT = screenRefDT;
55 } else {
56 // This situation can happen if a blur operation decides to
57 // use an unaccelerated path even if the system backend is
58 // Direct2D.
59 //
60 // We don't really want to encounter the reverse scenario:
61 // we shouldn't pick an accelerated backend if the system
62 // backend is skia.
63 if (aBackend == BackendType::DIRECT2D1_1) {
64 gfxWarning() << "Creating a RefDT in DrawTargetCapture.";
65 }
66
67 // Create a 1x1 size ref dt to create assets
68 // If we have to snapshot, we'll just create the real DT
69 IntSize size(1, 1);
70 mRefDT = Factory::CreateDrawTarget(aBackend, size, mFormat);
71 }
72 }
73
Init(const IntSize & aSize,DrawTarget * aRefDT)74 bool DrawTargetCaptureImpl::Init(const IntSize& aSize, DrawTarget* aRefDT) {
75 if (!aRefDT) {
76 return false;
77 }
78
79 mRefDT = aRefDT;
80
81 mSize = aSize;
82 mCurrentClipBounds.push(IntRect(IntPoint(0, 0), aSize));
83
84 mFormat = aRefDT->GetFormat();
85 SetPermitSubpixelAA(IsOpaque(mFormat));
86 return true;
87 }
88
InitForData(int32_t aStride,size_t aSurfaceAllocationSize)89 void DrawTargetCaptureImpl::InitForData(int32_t aStride,
90 size_t aSurfaceAllocationSize) {
91 MOZ_ASSERT(!mFlushBytes);
92 mStride = aStride;
93 mSurfaceAllocationSize = aSurfaceAllocationSize;
94 }
95
Snapshot()96 already_AddRefed<SourceSurface> DrawTargetCaptureImpl::Snapshot() {
97 if (!mSnapshot) {
98 mSnapshot = new SourceSurfaceCapture(this);
99 }
100
101 RefPtr<SourceSurface> surface = mSnapshot;
102 return surface.forget();
103 }
104
IntoLuminanceSource(LuminanceType aLuminanceType,float aOpacity)105 already_AddRefed<SourceSurface> DrawTargetCaptureImpl::IntoLuminanceSource(
106 LuminanceType aLuminanceType, float aOpacity) {
107 RefPtr<SourceSurface> surface =
108 new SourceSurfaceCapture(this, aLuminanceType, aOpacity);
109 return surface.forget();
110 }
111
OptimizeSourceSurface(SourceSurface * aSurface) const112 already_AddRefed<SourceSurface> DrawTargetCaptureImpl::OptimizeSourceSurface(
113 SourceSurface* aSurface) const {
114 // If the surface is a recording, make sure it gets resolved on the paint
115 // thread.
116 if (aSurface->GetType() == SurfaceType::CAPTURE) {
117 RefPtr<SourceSurface> surface = aSurface;
118 return surface.forget();
119 }
120 RefPtr<SourceSurfaceCapture> surface = new SourceSurfaceCapture(
121 const_cast<DrawTargetCaptureImpl*>(this), aSurface);
122 return surface.forget();
123 }
124
DetachAllSnapshots()125 void DrawTargetCaptureImpl::DetachAllSnapshots() { MarkChanged(); }
126
127 #define AppendCommand(arg) new (AppendToCommandList<arg>()) arg
128 #define ReuseOrAppendCommand(arg) new (ReuseOrAppendToCommandList<arg>()) arg
129
SetPermitSubpixelAA(bool aPermitSubpixelAA)130 void DrawTargetCaptureImpl::SetPermitSubpixelAA(bool aPermitSubpixelAA) {
131 // Save memory by eliminating state changes with no effect
132 if (mPermitSubpixelAA == aPermitSubpixelAA) {
133 return;
134 }
135
136 ReuseOrAppendCommand(SetPermitSubpixelAACommand)(aPermitSubpixelAA);
137
138 // Have to update mPermitSubpixelAA for this DT
139 // because some code paths query the current setting
140 // to determine subpixel AA eligibility.
141 DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
142 }
143
DrawSurface(SourceSurface * aSurface,const Rect & aDest,const Rect & aSource,const DrawSurfaceOptions & aSurfOptions,const DrawOptions & aOptions)144 void DrawTargetCaptureImpl::DrawSurface(SourceSurface* aSurface,
145 const Rect& aDest, const Rect& aSource,
146 const DrawSurfaceOptions& aSurfOptions,
147 const DrawOptions& aOptions) {
148 aSurface->GuaranteePersistance();
149 AppendCommand(DrawSurfaceCommand)(aSurface, aDest, aSource, aSurfOptions,
150 aOptions);
151 }
152
DrawSurfaceWithShadow(SourceSurface * aSurface,const Point & aDest,const DeviceColor & aColor,const Point & aOffset,Float aSigma,CompositionOp aOperator)153 void DrawTargetCaptureImpl::DrawSurfaceWithShadow(
154 SourceSurface* aSurface, const Point& aDest, const DeviceColor& aColor,
155 const Point& aOffset, Float aSigma, CompositionOp aOperator) {
156 aSurface->GuaranteePersistance();
157 AppendCommand(DrawSurfaceWithShadowCommand)(aSurface, aDest, aColor, aOffset,
158 aSigma, aOperator);
159 }
160
DrawFilter(FilterNode * aNode,const Rect & aSourceRect,const Point & aDestPoint,const DrawOptions & aOptions)161 void DrawTargetCaptureImpl::DrawFilter(FilterNode* aNode,
162 const Rect& aSourceRect,
163 const Point& aDestPoint,
164 const DrawOptions& aOptions) {
165 // @todo XXX - this won't work properly long term yet due to filternodes not
166 // being immutable.
167 AppendCommand(DrawFilterCommand)(aNode, aSourceRect, aDestPoint, aOptions);
168 }
169
ClearRect(const Rect & aRect)170 void DrawTargetCaptureImpl::ClearRect(const Rect& aRect) {
171 AppendCommand(ClearRectCommand)(aRect);
172 }
173
MaskSurface(const Pattern & aSource,SourceSurface * aMask,Point aOffset,const DrawOptions & aOptions)174 void DrawTargetCaptureImpl::MaskSurface(const Pattern& aSource,
175 SourceSurface* aMask, Point aOffset,
176 const DrawOptions& aOptions) {
177 aMask->GuaranteePersistance();
178 AppendCommand(MaskSurfaceCommand)(aSource, aMask, aOffset, aOptions);
179 }
180
CopySurface(SourceSurface * aSurface,const IntRect & aSourceRect,const IntPoint & aDestination)181 void DrawTargetCaptureImpl::CopySurface(SourceSurface* aSurface,
182 const IntRect& aSourceRect,
183 const IntPoint& aDestination) {
184 aSurface->GuaranteePersistance();
185 AppendCommand(CopySurfaceCommand)(aSurface, aSourceRect, aDestination);
186 }
187
CopyRect(const IntRect & aSourceRect,const IntPoint & aDestination)188 void DrawTargetCaptureImpl::CopyRect(const IntRect& aSourceRect,
189 const IntPoint& aDestination) {
190 AppendCommand(CopyRectCommand)(aSourceRect, aDestination);
191 }
192
FillRect(const Rect & aRect,const Pattern & aPattern,const DrawOptions & aOptions)193 void DrawTargetCaptureImpl::FillRect(const Rect& aRect, const Pattern& aPattern,
194 const DrawOptions& aOptions) {
195 AppendCommand(FillRectCommand)(aRect, aPattern, aOptions);
196 }
197
FillRoundedRect(const RoundedRect & aRect,const Pattern & aPattern,const DrawOptions & aOptions)198 void DrawTargetCaptureImpl::FillRoundedRect(const RoundedRect& aRect,
199 const Pattern& aPattern,
200 const DrawOptions& aOptions) {
201 AppendCommand(FillRoundedRectCommand)(aRect, aPattern, aOptions);
202 }
203
StrokeRect(const Rect & aRect,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)204 void DrawTargetCaptureImpl::StrokeRect(const Rect& aRect,
205 const Pattern& aPattern,
206 const StrokeOptions& aStrokeOptions,
207 const DrawOptions& aOptions) {
208 AppendCommand(StrokeRectCommand)(aRect, aPattern, aStrokeOptions, aOptions);
209 }
210
StrokeLine(const Point & aStart,const Point & aEnd,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)211 void DrawTargetCaptureImpl::StrokeLine(const Point& aStart, const Point& aEnd,
212 const Pattern& aPattern,
213 const StrokeOptions& aStrokeOptions,
214 const DrawOptions& aOptions) {
215 AppendCommand(StrokeLineCommand)(aStart, aEnd, aPattern, aStrokeOptions,
216 aOptions);
217 }
218
Stroke(const Path * aPath,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)219 void DrawTargetCaptureImpl::Stroke(const Path* aPath, const Pattern& aPattern,
220 const StrokeOptions& aStrokeOptions,
221 const DrawOptions& aOptions) {
222 AppendCommand(StrokeCommand)(aPath, aPattern, aStrokeOptions, aOptions);
223 }
224
Fill(const Path * aPath,const Pattern & aPattern,const DrawOptions & aOptions)225 void DrawTargetCaptureImpl::Fill(const Path* aPath, const Pattern& aPattern,
226 const DrawOptions& aOptions) {
227 AppendCommand(FillCommand)(aPath, aPattern, aOptions);
228 }
229
FillGlyphs(ScaledFont * aFont,const GlyphBuffer & aBuffer,const Pattern & aPattern,const DrawOptions & aOptions)230 void DrawTargetCaptureImpl::FillGlyphs(ScaledFont* aFont,
231 const GlyphBuffer& aBuffer,
232 const Pattern& aPattern,
233 const DrawOptions& aOptions) {
234 AppendCommand(FillGlyphsCommand)(aFont, aBuffer, aPattern, aOptions);
235 }
236
StrokeGlyphs(ScaledFont * aFont,const GlyphBuffer & aBuffer,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)237 void DrawTargetCaptureImpl::StrokeGlyphs(ScaledFont* aFont,
238 const GlyphBuffer& aBuffer,
239 const Pattern& aPattern,
240 const StrokeOptions& aStrokeOptions,
241 const DrawOptions& aOptions) {
242 AppendCommand(StrokeGlyphsCommand)(aFont, aBuffer, aPattern, aStrokeOptions,
243 aOptions);
244 }
245
Mask(const Pattern & aSource,const Pattern & aMask,const DrawOptions & aOptions)246 void DrawTargetCaptureImpl::Mask(const Pattern& aSource, const Pattern& aMask,
247 const DrawOptions& aOptions) {
248 AppendCommand(MaskCommand)(aSource, aMask, aOptions);
249 }
250
PushClip(const Path * aPath)251 void DrawTargetCaptureImpl::PushClip(const Path* aPath) {
252 // We need Pushes and Pops to match so instead of trying
253 // to compute the bounds of the path just repush the current
254 // bounds.
255 mCurrentClipBounds.push(mCurrentClipBounds.top());
256
257 AppendCommand(PushClipCommand)(aPath);
258 }
259
PushClipRect(const Rect & aRect)260 void DrawTargetCaptureImpl::PushClipRect(const Rect& aRect) {
261 IntRect deviceRect = RoundedOut(mTransform.TransformBounds(aRect));
262 mCurrentClipBounds.push(mCurrentClipBounds.top().Intersect(deviceRect));
263
264 AppendCommand(PushClipRectCommand)(aRect);
265 }
266
PushLayer(bool aOpaque,Float aOpacity,SourceSurface * aMask,const Matrix & aMaskTransform,const IntRect & aBounds,bool aCopyBackground)267 void DrawTargetCaptureImpl::PushLayer(bool aOpaque, Float aOpacity,
268 SourceSurface* aMask,
269 const Matrix& aMaskTransform,
270 const IntRect& aBounds,
271 bool aCopyBackground) {
272 // Have to update mPermitSubpixelAA for this DT
273 // because some code paths query the current setting
274 // to determine subpixel AA eligibility.
275 PushedLayer layer(GetPermitSubpixelAA());
276 mPushedLayers.push_back(layer);
277 DrawTarget::SetPermitSubpixelAA(aOpaque);
278
279 if (aMask) {
280 aMask->GuaranteePersistance();
281 }
282
283 AppendCommand(PushLayerCommand)(aOpaque, aOpacity, aMask, aMaskTransform,
284 aBounds, aCopyBackground);
285 }
286
PopLayer()287 void DrawTargetCaptureImpl::PopLayer() {
288 MOZ_ASSERT(mPushedLayers.size());
289 DrawTarget::SetPermitSubpixelAA(mPushedLayers.back().mOldPermitSubpixelAA);
290 mPushedLayers.pop_back();
291
292 AppendCommand(PopLayerCommand)();
293 }
294
PopClip()295 void DrawTargetCaptureImpl::PopClip() {
296 mCurrentClipBounds.pop();
297 AppendCommand(PopClipCommand)();
298 }
299
SetTransform(const Matrix & aTransform)300 void DrawTargetCaptureImpl::SetTransform(const Matrix& aTransform) {
301 // Save memory by eliminating state changes with no effect
302 if (mTransform.ExactlyEquals(aTransform)) {
303 return;
304 }
305
306 ReuseOrAppendCommand(SetTransformCommand)(aTransform);
307
308 // Have to update the transform for this DT
309 // because some code paths query the current transform
310 // to render specific things.
311 DrawTarget::SetTransform(aTransform);
312 }
313
Blur(const AlphaBoxBlur & aBlur)314 void DrawTargetCaptureImpl::Blur(const AlphaBoxBlur& aBlur) {
315 // gfxAlphaBoxBlur should not use this if it takes the accelerated path.
316 MOZ_ASSERT(GetBackendType() == BackendType::SKIA);
317
318 AppendCommand(BlurCommand)(aBlur);
319 }
320
PadEdges(const IntRegion & aRegion)321 void DrawTargetCaptureImpl::PadEdges(const IntRegion& aRegion) {
322 AppendCommand(PadEdgesCommand)(aRegion);
323 }
324
ReplayToDrawTarget(DrawTarget * aDT,const Matrix & aTransform)325 void DrawTargetCaptureImpl::ReplayToDrawTarget(DrawTarget* aDT,
326 const Matrix& aTransform) {
327 for (CaptureCommandList::iterator iter(mCommands); !iter.Done();
328 iter.Next()) {
329 DrawingCommand* cmd = iter.Get();
330 cmd->ExecuteOnDT(aDT, &aTransform);
331 }
332 }
333
MarkChanged()334 void DrawTargetCaptureImpl::MarkChanged() {
335 if (!mSnapshot) {
336 return;
337 }
338
339 if (mSnapshot->hasOneRef()) {
340 mSnapshot = nullptr;
341 return;
342 }
343
344 mSnapshot->DrawTargetWillChange();
345 mSnapshot = nullptr;
346 }
347
CreateSimilarDrawTarget(const IntSize & aSize,SurfaceFormat aFormat) const348 already_AddRefed<DrawTarget> DrawTargetCaptureImpl::CreateSimilarDrawTarget(
349 const IntSize& aSize, SurfaceFormat aFormat) const {
350 return MakeAndAddRef<DrawTargetCaptureImpl>(GetBackendType(), aSize, aFormat);
351 }
352
CreateClippedDrawTarget(const Rect & aBounds,SurfaceFormat aFormat)353 RefPtr<DrawTarget> DrawTargetCaptureImpl::CreateClippedDrawTarget(
354 const Rect& aBounds, SurfaceFormat aFormat) {
355 IntRect& bounds = mCurrentClipBounds.top();
356 auto dt = MakeRefPtr<DrawTargetCaptureImpl>(GetBackendType(), bounds.Size(),
357 aFormat);
358 RefPtr<DrawTarget> result =
359 gfx::Factory::CreateOffsetDrawTarget(dt, bounds.TopLeft());
360 result->SetTransform(mTransform);
361 return result;
362 }
363
CreateSimilarRasterTarget(const IntSize & aSize,SurfaceFormat aFormat) const364 RefPtr<DrawTarget> DrawTargetCaptureImpl::CreateSimilarRasterTarget(
365 const IntSize& aSize, SurfaceFormat aFormat) const {
366 MOZ_ASSERT(!mRefDT->IsCaptureDT());
367 return mRefDT->CreateSimilarDrawTarget(aSize, aFormat);
368 }
369
CreatePathBuilder(FillRule aFillRule) const370 already_AddRefed<PathBuilder> DrawTargetCaptureImpl::CreatePathBuilder(
371 FillRule aFillRule) const {
372 if (mRefDT->GetBackendType() == BackendType::DIRECT2D1_1) {
373 return MakeRefPtr<PathBuilderCapture>(aFillRule, mRefDT).forget();
374 }
375
376 return mRefDT->CreatePathBuilder(aFillRule);
377 }
378
CreateFilter(FilterType aType)379 already_AddRefed<FilterNode> DrawTargetCaptureImpl::CreateFilter(
380 FilterType aType) {
381 if (mRefDT->GetBackendType() == BackendType::DIRECT2D1_1) {
382 return MakeRefPtr<FilterNodeCapture>(aType).forget();
383 } else {
384 return mRefDT->CreateFilter(aType);
385 }
386 }
387
IsEmpty() const388 bool DrawTargetCaptureImpl::IsEmpty() const { return mCommands.IsEmpty(); }
389
Dump()390 void DrawTargetCaptureImpl::Dump() {
391 TreeLog<> output;
392 output << "DrawTargetCapture(" << (void*)(this) << ")\n";
393 TreeAutoIndent<> indent(output);
394 mCommands.Log(output);
395 output << "\n";
396 }
397
398 } // namespace gfx
399 } // namespace mozilla
400