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