no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / gfx / 2d / DrawTargetRecording.cpp
blob6edc8cdf91408232097fea25e9f2dde1b55f6343
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/. */
7 #include "DrawTargetRecording.h"
8 #include "DrawTargetSkia.h"
9 #include "PathRecording.h"
10 #include <stdio.h>
12 #include "Logging.h"
13 #include "Tools.h"
14 #include "Filters.h"
15 #include "mozilla/gfx/DataSurfaceHelpers.h"
16 #include "mozilla/layers/CanvasDrawEventRecorder.h"
17 #include "mozilla/layers/RecordedCanvasEventImpl.h"
18 #include "mozilla/layers/SourceSurfaceSharedData.h"
19 #include "mozilla/UniquePtr.h"
20 #include "nsXULAppAPI.h" // for XRE_IsContentProcess()
21 #include "RecordingTypes.h"
22 #include "RecordedEventImpl.h"
24 namespace mozilla {
25 namespace gfx {
27 struct RecordingSourceSurfaceUserData {
28 void* refPtr;
29 RefPtr<DrawEventRecorderPrivate> recorder;
31 // The optimized surface holds a reference to our surface, for GetDataSurface
32 // calls, so we must hold a weak reference to avoid circular dependency.
33 ThreadSafeWeakPtr<SourceSurface> optimizedSurface;
36 static void RecordingSourceSurfaceUserDataFunc(void* aUserData) {
37 RecordingSourceSurfaceUserData* userData =
38 static_cast<RecordingSourceSurfaceUserData*>(aUserData);
40 if (NS_IsMainThread()) {
41 userData->recorder->RecordSourceSurfaceDestruction(userData->refPtr);
42 delete userData;
43 return;
46 userData->recorder->AddPendingDeletion([userData]() -> void {
47 userData->recorder->RecordSourceSurfaceDestruction(userData->refPtr);
48 delete userData;
49 });
52 static bool EnsureSurfaceStoredRecording(DrawEventRecorderPrivate* aRecorder,
53 SourceSurface* aSurface,
54 const char* reason) {
55 // It's important that TryAddStoredObject is called first because that will
56 // run any pending processing required by recorded objects that have been
57 // deleted off the main thread.
58 if (!aRecorder->TryAddStoredObject(aSurface)) {
59 // Surface is already stored.
60 return false;
62 aRecorder->StoreSourceSurfaceRecording(aSurface, reason);
63 aRecorder->AddSourceSurface(aSurface);
65 RecordingSourceSurfaceUserData* userData = new RecordingSourceSurfaceUserData;
66 userData->refPtr = aSurface;
67 userData->recorder = aRecorder;
68 aSurface->AddUserData(reinterpret_cast<UserDataKey*>(aRecorder), userData,
69 &RecordingSourceSurfaceUserDataFunc);
70 return true;
73 class SourceSurfaceRecording : public SourceSurface {
74 public:
75 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceRecording, override)
77 SourceSurfaceRecording(IntSize aSize, SurfaceFormat aFormat,
78 DrawEventRecorderPrivate* aRecorder,
79 SourceSurface* aOriginalSurface = nullptr)
80 : mSize(aSize),
81 mFormat(aFormat),
82 mRecorder(aRecorder),
83 mOriginalSurface(aOriginalSurface) {
84 mRecorder->AddStoredObject(this);
87 ~SourceSurfaceRecording() {
88 mRecorder->RemoveStoredObject(this);
89 mRecorder->RecordEvent(
90 RecordedSourceSurfaceDestruction(ReferencePtr(this)));
93 SurfaceType GetType() const override { return SurfaceType::RECORDING; }
94 IntSize GetSize() const override { return mSize; }
95 SurfaceFormat GetFormat() const override { return mFormat; }
96 already_AddRefed<DataSourceSurface> GetDataSurface() override {
97 if (mOriginalSurface) {
98 return mOriginalSurface->GetDataSurface();
101 return nullptr;
104 already_AddRefed<SourceSurface> ExtractSubrect(const IntRect& aRect) override;
106 IntSize mSize;
107 SurfaceFormat mFormat;
108 RefPtr<DrawEventRecorderPrivate> mRecorder;
109 // If a SourceSurfaceRecording is returned from an OptimizeSourceSurface call
110 // we need GetDataSurface to work, so we hold the original surface we
111 // optimized to return its GetDataSurface.
112 RefPtr<SourceSurface> mOriginalSurface;
115 class GradientStopsRecording : public GradientStops {
116 public:
117 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsRecording, override)
119 explicit GradientStopsRecording(DrawEventRecorderPrivate* aRecorder)
120 : mRecorder(aRecorder) {
121 mRecorder->AddStoredObject(this);
124 virtual ~GradientStopsRecording() {
125 mRecorder->RemoveStoredObject(this);
126 mRecorder->RecordEvent(
127 RecordedGradientStopsDestruction(ReferencePtr(this)));
130 BackendType GetBackendType() const override { return BackendType::RECORDING; }
132 RefPtr<DrawEventRecorderPrivate> mRecorder;
135 class FilterNodeRecording : public FilterNode {
136 public:
137 MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeRecording, override)
138 using FilterNode::SetAttribute;
140 explicit FilterNodeRecording(DrawEventRecorderPrivate* aRecorder)
141 : mRecorder(aRecorder) {
142 mRecorder->AddStoredObject(this);
145 virtual ~FilterNodeRecording() {
146 mRecorder->RemoveStoredObject(this);
147 mRecorder->RecordEvent(RecordedFilterNodeDestruction(ReferencePtr(this)));
150 void SetInput(uint32_t aIndex, SourceSurface* aSurface) override {
151 EnsureSurfaceStoredRecording(mRecorder, aSurface, "SetInput");
153 mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aSurface));
155 void SetInput(uint32_t aIndex, FilterNode* aFilter) override {
156 MOZ_ASSERT(mRecorder->HasStoredObject(aFilter));
158 mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aFilter));
161 #define FORWARD_SET_ATTRIBUTE(type, argtype) \
162 void SetAttribute(uint32_t aIndex, type aValue) override { \
163 mRecorder->RecordEvent(RecordedFilterNodeSetAttribute( \
164 this, aIndex, aValue, \
165 RecordedFilterNodeSetAttribute::ARGTYPE_##argtype)); \
168 FORWARD_SET_ATTRIBUTE(bool, BOOL);
169 FORWARD_SET_ATTRIBUTE(uint32_t, UINT32);
170 FORWARD_SET_ATTRIBUTE(Float, FLOAT);
171 FORWARD_SET_ATTRIBUTE(const Size&, SIZE);
172 FORWARD_SET_ATTRIBUTE(const IntSize&, INTSIZE);
173 FORWARD_SET_ATTRIBUTE(const IntPoint&, INTPOINT);
174 FORWARD_SET_ATTRIBUTE(const Rect&, RECT);
175 FORWARD_SET_ATTRIBUTE(const IntRect&, INTRECT);
176 FORWARD_SET_ATTRIBUTE(const Point&, POINT);
177 FORWARD_SET_ATTRIBUTE(const Matrix&, MATRIX);
178 FORWARD_SET_ATTRIBUTE(const Matrix5x4&, MATRIX5X4);
179 FORWARD_SET_ATTRIBUTE(const Point3D&, POINT3D);
180 FORWARD_SET_ATTRIBUTE(const DeviceColor&, COLOR);
182 #undef FORWARD_SET_ATTRIBUTE
184 void SetAttribute(uint32_t aIndex, const Float* aFloat,
185 uint32_t aSize) override {
186 mRecorder->RecordEvent(
187 RecordedFilterNodeSetAttribute(this, aIndex, aFloat, aSize));
190 FilterBackend GetBackendType() override { return FILTER_BACKEND_RECORDING; }
192 RefPtr<DrawEventRecorderPrivate> mRecorder;
195 DrawTargetRecording::DrawTargetRecording(
196 layers::CanvasDrawEventRecorder* aRecorder, int64_t aTextureId,
197 const layers::RemoteTextureOwnerId& aTextureOwnerId, DrawTarget* aDT,
198 const IntSize& aSize)
199 : mRecorder(static_cast<DrawEventRecorderPrivate*>(aRecorder)),
200 mFinalDT(aDT),
201 mRect(IntPoint(0, 0), aSize) {
202 mRecorder->RecordEvent(layers::RecordedCanvasDrawTargetCreation(
203 this, aTextureId, aTextureOwnerId, mFinalDT->GetBackendType(), aSize,
204 mFinalDT->GetFormat()));
205 mFormat = mFinalDT->GetFormat();
206 DrawTarget::SetPermitSubpixelAA(IsOpaque(mFormat));
209 DrawTargetRecording::DrawTargetRecording(DrawEventRecorder* aRecorder,
210 DrawTarget* aDT, IntRect aRect,
211 bool aHasData)
212 : mRecorder(static_cast<DrawEventRecorderPrivate*>(aRecorder)),
213 mFinalDT(aDT),
214 mRect(aRect) {
215 MOZ_DIAGNOSTIC_ASSERT(aRecorder->GetRecorderType() != RecorderType::CANVAS);
216 RefPtr<SourceSurface> snapshot = aHasData ? mFinalDT->Snapshot() : nullptr;
217 mRecorder->RecordEvent(
218 RecordedDrawTargetCreation(this, mFinalDT->GetBackendType(), mRect,
219 mFinalDT->GetFormat(), aHasData, snapshot));
220 mFormat = mFinalDT->GetFormat();
221 DrawTarget::SetPermitSubpixelAA(IsOpaque(mFormat));
224 DrawTargetRecording::DrawTargetRecording(const DrawTargetRecording* aDT,
225 IntRect aRect, SurfaceFormat aFormat)
226 : mRecorder(aDT->mRecorder), mFinalDT(aDT->mFinalDT), mRect(aRect) {
227 mFormat = aFormat;
228 DrawTarget::SetPermitSubpixelAA(IsOpaque(mFormat));
231 DrawTargetRecording::~DrawTargetRecording() {
232 mRecorder->RecordEvent(RecordedDrawTargetDestruction(ReferencePtr(this)));
233 mRecorder->ClearDrawTarget(this);
236 void DrawTargetRecording::Link(const char* aDestination, const Rect& aRect) {
237 MarkChanged();
239 mRecorder->RecordEvent(this, RecordedLink(aDestination, aRect));
242 void DrawTargetRecording::Destination(const char* aDestination,
243 const Point& aPoint) {
244 MarkChanged();
246 mRecorder->RecordEvent(this, RecordedDestination(aDestination, aPoint));
249 void DrawTargetRecording::FillRect(const Rect& aRect, const Pattern& aPattern,
250 const DrawOptions& aOptions) {
251 MarkChanged();
253 EnsurePatternDependenciesStored(aPattern);
255 mRecorder->RecordEvent(this, RecordedFillRect(aRect, aPattern, aOptions));
258 void DrawTargetRecording::StrokeRect(const Rect& aRect, const Pattern& aPattern,
259 const StrokeOptions& aStrokeOptions,
260 const DrawOptions& aOptions) {
261 MarkChanged();
263 EnsurePatternDependenciesStored(aPattern);
265 mRecorder->RecordEvent(
266 this, RecordedStrokeRect(aRect, aPattern, aStrokeOptions, aOptions));
269 void DrawTargetRecording::StrokeLine(const Point& aBegin, const Point& aEnd,
270 const Pattern& aPattern,
271 const StrokeOptions& aStrokeOptions,
272 const DrawOptions& aOptions) {
273 MarkChanged();
275 EnsurePatternDependenciesStored(aPattern);
277 mRecorder->RecordEvent(this, RecordedStrokeLine(aBegin, aEnd, aPattern,
278 aStrokeOptions, aOptions));
281 void DrawTargetRecording::Fill(const Path* aPath, const Pattern& aPattern,
282 const DrawOptions& aOptions) {
283 if (!aPath) {
284 return;
287 MarkChanged();
289 if (aPath->GetBackendType() == BackendType::RECORDING) {
290 const PathRecording* path = static_cast<const PathRecording*>(aPath);
291 auto circle = path->AsCircle();
292 if (circle) {
293 EnsurePatternDependenciesStored(aPattern);
294 mRecorder->RecordEvent(
295 this, RecordedFillCircle(circle.value(), aPattern, aOptions));
296 return;
300 RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
301 EnsurePatternDependenciesStored(aPattern);
303 mRecorder->RecordEvent(this, RecordedFill(pathRecording, aPattern, aOptions));
306 struct RecordingFontUserData {
307 void* refPtr;
308 void* unscaledFont;
309 RefPtr<DrawEventRecorderPrivate> recorder;
312 static void RecordingFontUserDataDestroyFunc(void* aUserData) {
313 RecordingFontUserData* userData =
314 static_cast<RecordingFontUserData*>(aUserData);
316 userData->recorder->RecordEvent(
317 RecordedScaledFontDestruction(ReferencePtr(userData->refPtr)));
318 userData->recorder->RemoveScaledFont((ScaledFont*)userData->refPtr);
319 userData->recorder->DecrementUnscaledFontRefCount(userData->unscaledFont);
320 delete userData;
323 void DrawTargetRecording::DrawGlyphs(ScaledFont* aFont,
324 const GlyphBuffer& aBuffer,
325 const Pattern& aPattern,
326 const DrawOptions& aOptions,
327 const StrokeOptions* aStrokeOptions) {
328 if (!aFont) {
329 return;
332 MarkChanged();
334 EnsurePatternDependenciesStored(aPattern);
336 UserDataKey* userDataKey = reinterpret_cast<UserDataKey*>(mRecorder.get());
337 if (mRecorder->WantsExternalFonts()) {
338 mRecorder->AddScaledFont(aFont);
339 } else if (!aFont->GetUserData(userDataKey)) {
340 UnscaledFont* unscaledFont = aFont->GetUnscaledFont();
341 if (mRecorder->IncrementUnscaledFontRefCount(unscaledFont) == 0) {
342 // Prefer sending the description, if we can create one. This ensures
343 // we don't record the data of system fonts which saves time and can
344 // prevent duplicate copies from accumulating in the OS cache during
345 // playback.
346 RecordedFontDescriptor fontDesc(unscaledFont);
347 if (fontDesc.IsValid()) {
348 mRecorder->RecordEvent(fontDesc);
349 } else {
350 RecordedFontData fontData(unscaledFont);
351 RecordedFontDetails fontDetails;
352 if (fontData.GetFontDetails(fontDetails)) {
353 // Try to serialise the whole font, just in case this is a web font
354 // that is not present on the system.
355 if (!mRecorder->HasStoredFontData(fontDetails.fontDataKey)) {
356 mRecorder->RecordEvent(fontData);
357 mRecorder->AddStoredFontData(fontDetails.fontDataKey);
359 mRecorder->RecordEvent(
360 RecordedUnscaledFontCreation(unscaledFont, fontDetails));
361 } else {
362 gfxWarning() << "DrawTargetRecording::FillGlyphs failed to serialise "
363 "UnscaledFont";
367 mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, unscaledFont));
368 RecordingFontUserData* userData = new RecordingFontUserData;
369 userData->refPtr = aFont;
370 userData->unscaledFont = unscaledFont;
371 userData->recorder = mRecorder;
372 aFont->AddUserData(userDataKey, userData,
373 &RecordingFontUserDataDestroyFunc);
374 userData->recorder->AddScaledFont(aFont);
377 if (aStrokeOptions) {
378 mRecorder->RecordEvent(
379 this, RecordedStrokeGlyphs(aFont, aPattern, *aStrokeOptions, aOptions,
380 aBuffer.mGlyphs, aBuffer.mNumGlyphs));
381 } else {
382 mRecorder->RecordEvent(
383 this, RecordedFillGlyphs(aFont, aPattern, aOptions, aBuffer.mGlyphs,
384 aBuffer.mNumGlyphs));
388 void DrawTargetRecording::FillGlyphs(ScaledFont* aFont,
389 const GlyphBuffer& aBuffer,
390 const Pattern& aPattern,
391 const DrawOptions& aOptions) {
392 DrawGlyphs(aFont, aBuffer, aPattern, aOptions);
395 void DrawTargetRecording::StrokeGlyphs(ScaledFont* aFont,
396 const GlyphBuffer& aBuffer,
397 const Pattern& aPattern,
398 const StrokeOptions& aStrokeOptions,
399 const DrawOptions& aOptions) {
400 DrawGlyphs(aFont, aBuffer, aPattern, aOptions, &aStrokeOptions);
403 void DrawTargetRecording::Mask(const Pattern& aSource, const Pattern& aMask,
404 const DrawOptions& aOptions) {
405 MarkChanged();
407 EnsurePatternDependenciesStored(aSource);
408 EnsurePatternDependenciesStored(aMask);
410 mRecorder->RecordEvent(this, RecordedMask(aSource, aMask, aOptions));
413 void DrawTargetRecording::MaskSurface(const Pattern& aSource,
414 SourceSurface* aMask, Point aOffset,
415 const DrawOptions& aOptions) {
416 if (!aMask) {
417 return;
420 MarkChanged();
422 EnsurePatternDependenciesStored(aSource);
423 EnsureSurfaceStoredRecording(mRecorder, aMask, "MaskSurface");
425 mRecorder->RecordEvent(
426 this, RecordedMaskSurface(aSource, aMask, aOffset, aOptions));
429 void DrawTargetRecording::Stroke(const Path* aPath, const Pattern& aPattern,
430 const StrokeOptions& aStrokeOptions,
431 const DrawOptions& aOptions) {
432 MarkChanged();
434 if (aPath->GetBackendType() == BackendType::RECORDING) {
435 const PathRecording* path = static_cast<const PathRecording*>(aPath);
436 auto circle = path->AsCircle();
437 if (circle && circle->closed) {
438 EnsurePatternDependenciesStored(aPattern);
439 mRecorder->RecordEvent(
440 this, RecordedStrokeCircle(circle.value(), aPattern, aStrokeOptions,
441 aOptions));
442 return;
445 auto line = path->AsLine();
446 if (line) {
447 EnsurePatternDependenciesStored(aPattern);
448 mRecorder->RecordEvent(
449 this, RecordedStrokeLine(line->origin, line->destination, aPattern,
450 aStrokeOptions, aOptions));
451 return;
455 RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
456 EnsurePatternDependenciesStored(aPattern);
458 mRecorder->RecordEvent(
459 this, RecordedStroke(pathRecording, aPattern, aStrokeOptions, aOptions));
462 void DrawTargetRecording::DrawShadow(const Path* aPath, const Pattern& aPattern,
463 const ShadowOptions& aShadow,
464 const DrawOptions& aOptions,
465 const StrokeOptions* aStrokeOptions) {
466 MarkChanged();
468 RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
469 EnsurePatternDependenciesStored(aPattern);
471 mRecorder->RecordEvent(
472 this, RecordedDrawShadow(pathRecording, aPattern, aShadow, aOptions,
473 aStrokeOptions));
476 void DrawTargetRecording::MarkChanged() { mIsDirty = true; }
478 already_AddRefed<SourceSurface> DrawTargetRecording::Snapshot() {
479 RefPtr<SourceSurface> retSurf =
480 new SourceSurfaceRecording(mRect.Size(), mFormat, mRecorder);
482 mRecorder->RecordEvent(this, RecordedSnapshot(ReferencePtr(retSurf)));
484 return retSurf.forget();
487 already_AddRefed<SourceSurface> DrawTargetRecording::IntoLuminanceSource(
488 LuminanceType aLuminanceType, float aOpacity) {
489 RefPtr<SourceSurface> retSurf =
490 new SourceSurfaceRecording(mRect.Size(), SurfaceFormat::A8, mRecorder);
492 mRecorder->RecordEvent(
493 this, RecordedIntoLuminanceSource(retSurf, aLuminanceType, aOpacity));
495 return retSurf.forget();
498 already_AddRefed<SourceSurface> SourceSurfaceRecording::ExtractSubrect(
499 const IntRect& aRect) {
500 if (aRect.IsEmpty() || !GetRect().Contains(aRect)) {
501 return nullptr;
504 RefPtr<SourceSurface> subSurf =
505 new SourceSurfaceRecording(aRect.Size(), mFormat, mRecorder);
506 mRecorder->RecordEvent(RecordedExtractSubrect(subSurf, this, aRect));
507 return subSurf.forget();
510 void DrawTargetRecording::Flush() {
511 mRecorder->RecordEvent(this, RecordedFlush());
514 void DrawTargetRecording::DetachAllSnapshots() {
515 mRecorder->RecordEvent(this, RecordedDetachAllSnapshots());
518 void DrawTargetRecording::DrawSurface(SourceSurface* aSurface,
519 const Rect& aDest, const Rect& aSource,
520 const DrawSurfaceOptions& aSurfOptions,
521 const DrawOptions& aOptions) {
522 if (!aSurface) {
523 return;
526 MarkChanged();
528 EnsureSurfaceStoredRecording(mRecorder, aSurface, "DrawSurface");
530 mRecorder->RecordEvent(this, RecordedDrawSurface(aSurface, aDest, aSource,
531 aSurfOptions, aOptions));
534 void DrawTargetRecording::DrawDependentSurface(uint64_t aId,
535 const Rect& aDest) {
536 MarkChanged();
538 mRecorder->AddDependentSurface(aId);
539 mRecorder->RecordEvent(this, RecordedDrawDependentSurface(aId, aDest));
542 void DrawTargetRecording::DrawSurfaceWithShadow(SourceSurface* aSurface,
543 const Point& aDest,
544 const ShadowOptions& aShadow,
545 CompositionOp aOp) {
546 if (!aSurface) {
547 return;
550 MarkChanged();
552 EnsureSurfaceStoredRecording(mRecorder, aSurface, "DrawSurfaceWithShadow");
554 mRecorder->RecordEvent(
555 this, RecordedDrawSurfaceWithShadow(aSurface, aDest, aShadow, aOp));
558 void DrawTargetRecording::DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
559 const Point& aDestPoint,
560 const DrawOptions& aOptions) {
561 if (!aNode) {
562 return;
565 MarkChanged();
567 MOZ_ASSERT(mRecorder->HasStoredObject(aNode));
569 mRecorder->RecordEvent(
570 this, RecordedDrawFilter(aNode, aSourceRect, aDestPoint, aOptions));
573 already_AddRefed<FilterNode> DrawTargetRecording::CreateFilter(
574 FilterType aType) {
575 RefPtr<FilterNode> retNode = new FilterNodeRecording(mRecorder);
577 mRecorder->RecordEvent(this, RecordedFilterNodeCreation(retNode, aType));
579 return retNode.forget();
582 void DrawTargetRecording::ClearRect(const Rect& aRect) {
583 MarkChanged();
585 mRecorder->RecordEvent(this, RecordedClearRect(aRect));
588 void DrawTargetRecording::CopySurface(SourceSurface* aSurface,
589 const IntRect& aSourceRect,
590 const IntPoint& aDestination) {
591 if (!aSurface) {
592 return;
595 MarkChanged();
597 EnsureSurfaceStoredRecording(mRecorder, aSurface, "CopySurface");
599 mRecorder->RecordEvent(
600 this, RecordedCopySurface(aSurface, aSourceRect, aDestination));
603 void DrawTargetRecording::PushClip(const Path* aPath) {
604 if (!aPath) {
605 return;
608 // The canvas doesn't have a clipRect API so we always end up in the generic
609 // path. The D2D backend doesn't have a good way of specializing rectangular
610 // clips so we take advantage of the fact that aPath is usually backed by a
611 // SkiaPath which implements AsRect() and specialize it here.
612 auto rect = aPath->AsRect();
613 if (rect.isSome()) {
614 PushClipRect(rect.value());
615 return;
618 RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath);
620 mRecorder->RecordEvent(this, RecordedPushClip(ReferencePtr(pathRecording)));
623 void DrawTargetRecording::PushClipRect(const Rect& aRect) {
624 mRecorder->RecordEvent(this, RecordedPushClipRect(aRect));
627 void DrawTargetRecording::PopClip() {
628 mRecorder->RecordEvent(this, RecordedPopClip());
631 void DrawTargetRecording::PushLayer(bool aOpaque, Float aOpacity,
632 SourceSurface* aMask,
633 const Matrix& aMaskTransform,
634 const IntRect& aBounds,
635 bool aCopyBackground) {
636 if (aMask) {
637 EnsureSurfaceStoredRecording(mRecorder, aMask, "PushLayer");
640 mRecorder->RecordEvent(
641 this, RecordedPushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds,
642 aCopyBackground));
644 PushedLayer layer(GetPermitSubpixelAA());
645 mPushedLayers.push_back(layer);
646 DrawTarget::SetPermitSubpixelAA(aOpaque);
649 void DrawTargetRecording::PushLayerWithBlend(bool aOpaque, Float aOpacity,
650 SourceSurface* aMask,
651 const Matrix& aMaskTransform,
652 const IntRect& aBounds,
653 bool aCopyBackground,
654 CompositionOp aCompositionOp) {
655 if (aMask) {
656 EnsureSurfaceStoredRecording(mRecorder, aMask, "PushLayer");
659 mRecorder->RecordEvent(this, RecordedPushLayerWithBlend(
660 aOpaque, aOpacity, aMask, aMaskTransform,
661 aBounds, aCopyBackground, aCompositionOp));
663 PushedLayer layer(GetPermitSubpixelAA());
664 mPushedLayers.push_back(layer);
665 DrawTarget::SetPermitSubpixelAA(aOpaque);
668 void DrawTargetRecording::PopLayer() {
669 MarkChanged();
671 mRecorder->RecordEvent(this, RecordedPopLayer());
673 const PushedLayer& layer = mPushedLayers.back();
674 DrawTarget::SetPermitSubpixelAA(layer.mOldPermitSubpixelAA);
675 mPushedLayers.pop_back();
678 already_AddRefed<SourceSurface>
679 DrawTargetRecording::CreateSourceSurfaceFromData(unsigned char* aData,
680 const IntSize& aSize,
681 int32_t aStride,
682 SurfaceFormat aFormat) const {
683 RefPtr<SourceSurface> surface = CreateDataSourceSurfaceWithStrideFromData(
684 aSize, aFormat, aStride, aData, aStride);
685 if (!surface) {
686 return nullptr;
689 return OptimizeSourceSurface(surface);
692 already_AddRefed<SourceSurface> DrawTargetRecording::OptimizeSourceSurface(
693 SourceSurface* aSurface) const {
694 // See if we have a previously optimized surface available. We have to do this
695 // check before the SurfaceType::RECORDING below, because aSurface might be a
696 // SurfaceType::RECORDING from another recorder we have previously optimized.
697 auto* userData = static_cast<RecordingSourceSurfaceUserData*>(
698 aSurface->GetUserData(reinterpret_cast<UserDataKey*>(mRecorder.get())));
699 if (userData) {
700 RefPtr<SourceSurface> strongRef(userData->optimizedSurface);
701 if (strongRef) {
702 return do_AddRef(strongRef);
704 } else {
705 if (!EnsureSurfaceStoredRecording(mRecorder, aSurface,
706 "OptimizeSourceSurface")) {
707 // Surface was already stored, but doesn't have UserData so must be one
708 // of our recording surfaces.
709 MOZ_ASSERT(aSurface->GetType() == SurfaceType::RECORDING);
710 return do_AddRef(aSurface);
713 userData = static_cast<RecordingSourceSurfaceUserData*>(
714 aSurface->GetUserData(reinterpret_cast<UserDataKey*>(mRecorder.get())));
715 MOZ_ASSERT(userData,
716 "User data should always have been set by "
717 "EnsureSurfaceStoredRecording.");
720 RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording(
721 aSurface->GetSize(), aSurface->GetFormat(), mRecorder, aSurface);
722 mRecorder->RecordEvent(const_cast<DrawTargetRecording*>(this),
723 RecordedOptimizeSourceSurface(aSurface, retSurf));
724 userData->optimizedSurface = retSurf;
726 return retSurf.forget();
729 already_AddRefed<SourceSurface>
730 DrawTargetRecording::CreateSourceSurfaceFromNativeSurface(
731 const NativeSurface& aSurface) const {
732 MOZ_ASSERT(false);
733 return nullptr;
736 already_AddRefed<DrawTarget>
737 DrawTargetRecording::CreateSimilarDrawTargetWithBacking(
738 const IntSize& aSize, SurfaceFormat aFormat) const {
739 RefPtr<DrawTarget> similarDT;
740 if (mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat)) {
741 // If the requested similar draw target is too big, then we should try to
742 // rasterize on the content side to avoid duplicating the effort when a
743 // blob image gets tiled. If we fail somehow to produce it, we can fall
744 // back to recording.
745 constexpr int32_t kRasterThreshold = 256 * 256 * 4;
746 int32_t stride = aSize.width * BytesPerPixel(aFormat);
747 int32_t surfaceBytes = aSize.height * stride;
748 if (surfaceBytes >= kRasterThreshold) {
749 auto surface = MakeRefPtr<SourceSurfaceSharedData>();
750 if (surface->Init(aSize, stride, aFormat)) {
751 auto dt = MakeRefPtr<DrawTargetSkia>();
752 if (dt->Init(std::move(surface))) {
753 return dt.forget();
754 } else {
755 MOZ_ASSERT_UNREACHABLE("Skia should initialize given surface!");
761 return CreateSimilarDrawTarget(aSize, aFormat);
764 already_AddRefed<DrawTarget> DrawTargetRecording::CreateSimilarDrawTarget(
765 const IntSize& aSize, SurfaceFormat aFormat) const {
766 RefPtr<DrawTarget> similarDT;
767 if (mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat)) {
768 similarDT =
769 new DrawTargetRecording(this, IntRect(IntPoint(0, 0), aSize), aFormat);
770 mRecorder->RecordEvent(
771 const_cast<DrawTargetRecording*>(this),
772 RecordedCreateSimilarDrawTarget(similarDT.get(), aSize, aFormat));
773 } else if (XRE_IsContentProcess()) {
774 // Crash any content process that calls this function with arguments that
775 // would fail to create a similar draw target. We do this to root out bad
776 // callers. We don't want to crash any important processes though so for
777 // for those we'll just gracefully return nullptr.
778 MOZ_CRASH(
779 "Content-process DrawTargetRecording can't create requested similar "
780 "drawtarget");
782 return similarDT.forget();
785 bool DrawTargetRecording::CanCreateSimilarDrawTarget(
786 const IntSize& aSize, SurfaceFormat aFormat) const {
787 return mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat);
790 RefPtr<DrawTarget> DrawTargetRecording::CreateClippedDrawTarget(
791 const Rect& aBounds, SurfaceFormat aFormat) {
792 RefPtr<DrawTarget> similarDT;
793 similarDT = new DrawTargetRecording(this, mRect, aFormat);
794 mRecorder->RecordEvent(
795 this, RecordedCreateClippedDrawTarget(similarDT.get(), aBounds, aFormat));
796 similarDT->SetTransform(mTransform);
797 return similarDT;
800 already_AddRefed<DrawTarget>
801 DrawTargetRecording::CreateSimilarDrawTargetForFilter(
802 const IntSize& aMaxSize, SurfaceFormat aFormat, FilterNode* aFilter,
803 FilterNode* aSource, const Rect& aSourceRect, const Point& aDestPoint) {
804 RefPtr<DrawTarget> similarDT;
805 if (mFinalDT->CanCreateSimilarDrawTarget(aMaxSize, aFormat)) {
806 similarDT = new DrawTargetRecording(this, IntRect(IntPoint(0, 0), aMaxSize),
807 aFormat);
808 mRecorder->RecordEvent(
809 this, RecordedCreateDrawTargetForFilter(similarDT.get(), aMaxSize,
810 aFormat, aFilter, aSource,
811 aSourceRect, aDestPoint));
812 } else if (XRE_IsContentProcess()) {
813 // See CreateSimilarDrawTarget
814 MOZ_CRASH(
815 "Content-process DrawTargetRecording can't create requested clipped "
816 "drawtarget");
818 return similarDT.forget();
821 already_AddRefed<PathBuilder> DrawTargetRecording::CreatePathBuilder(
822 FillRule aFillRule) const {
823 return MakeAndAddRef<PathBuilderRecording>(mFinalDT->GetBackendType(),
824 aFillRule);
827 already_AddRefed<GradientStops> DrawTargetRecording::CreateGradientStops(
828 GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
829 RefPtr<GradientStops> retStops = new GradientStopsRecording(mRecorder);
831 mRecorder->RecordEvent(
832 const_cast<DrawTargetRecording*>(this),
833 RecordedGradientStopsCreation(retStops, aStops, aNumStops, aExtendMode));
835 return retStops.forget();
838 void DrawTargetRecording::SetTransform(const Matrix& aTransform) {
839 if (mTransform.ExactlyEquals(aTransform)) {
840 return;
842 DrawTarget::SetTransform(aTransform);
843 mRecorder->RecordEvent(this, RecordedSetTransform(aTransform));
846 void DrawTargetRecording::SetPermitSubpixelAA(bool aPermitSubpixelAA) {
847 if (aPermitSubpixelAA == mPermitSubpixelAA) {
848 return;
850 DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA);
851 mRecorder->RecordEvent(this, RecordedSetPermitSubpixelAA(aPermitSubpixelAA));
854 already_AddRefed<PathRecording> DrawTargetRecording::EnsurePathStored(
855 const Path* aPath) {
856 RefPtr<PathRecording> pathRecording;
857 if (aPath->GetBackendType() == BackendType::RECORDING) {
858 pathRecording =
859 const_cast<PathRecording*>(static_cast<const PathRecording*>(aPath));
860 if (!mRecorder->TryAddStoredObject(pathRecording)) {
861 // Path is already stored.
862 return pathRecording.forget();
864 } else {
865 MOZ_ASSERT(!mRecorder->HasStoredObject(aPath));
866 FillRule fillRule = aPath->GetFillRule();
867 RefPtr<PathBuilderRecording> builderRecording =
868 new PathBuilderRecording(mFinalDT->GetBackendType(), fillRule);
869 aPath->StreamToSink(builderRecording);
870 pathRecording = builderRecording->Finish().downcast<PathRecording>();
871 mRecorder->AddStoredObject(pathRecording);
874 // It's important that AddStoredObject or TryAddStoredObject is called before
875 // this because that will run any pending processing required by recorded
876 // objects that have been deleted off the main thread.
877 mRecorder->RecordEvent(this, RecordedPathCreation(pathRecording.get()));
878 pathRecording->mStoredRecorders.push_back(mRecorder);
880 return pathRecording.forget();
883 // This should only be called on the 'root' DrawTargetRecording.
884 // Calling it on a child DrawTargetRecordings will cause confusion.
885 void DrawTargetRecording::FlushItem(const IntRect& aBounds) {
886 mRecorder->FlushItem(aBounds);
887 // Reinitialize the recorder (FlushItem will write a new recording header)
888 // Tell the new recording about our draw target
889 // This code should match what happens in the DrawTargetRecording constructor.
890 MOZ_DIAGNOSTIC_ASSERT(mRecorder->GetRecorderType() != RecorderType::CANVAS);
891 mRecorder->RecordEvent(
892 RecordedDrawTargetCreation(this, mFinalDT->GetBackendType(), mRect,
893 mFinalDT->GetFormat(), false, nullptr));
894 // Add the current transform to the new recording
895 mRecorder->RecordEvent(this,
896 RecordedSetTransform(DrawTarget::GetTransform()));
899 void DrawTargetRecording::EnsurePatternDependenciesStored(
900 const Pattern& aPattern) {
901 switch (aPattern.GetType()) {
902 case PatternType::COLOR:
903 // No dependencies here.
904 return;
905 case PatternType::LINEAR_GRADIENT: {
906 MOZ_ASSERT_IF(
907 static_cast<const LinearGradientPattern*>(&aPattern)->mStops,
908 mRecorder->HasStoredObject(
909 static_cast<const LinearGradientPattern*>(&aPattern)->mStops));
910 return;
912 case PatternType::RADIAL_GRADIENT: {
913 MOZ_ASSERT_IF(
914 static_cast<const RadialGradientPattern*>(&aPattern)->mStops,
915 mRecorder->HasStoredObject(
916 static_cast<const RadialGradientPattern*>(&aPattern)->mStops));
917 return;
919 case PatternType::CONIC_GRADIENT: {
920 MOZ_ASSERT_IF(
921 static_cast<const ConicGradientPattern*>(&aPattern)->mStops,
922 mRecorder->HasStoredObject(
923 static_cast<const ConicGradientPattern*>(&aPattern)->mStops));
924 return;
926 case PatternType::SURFACE: {
927 const SurfacePattern* pat = static_cast<const SurfacePattern*>(&aPattern);
928 EnsureSurfaceStoredRecording(mRecorder, pat->mSurface,
929 "EnsurePatternDependenciesStored");
930 return;
935 } // namespace gfx
936 } // namespace mozilla