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 "BlobSurfaceProvider.h"
8 #include "AutoRestoreSVGState.h"
9 #include "ImageRegion.h"
10 #include "SVGDocumentWrapper.h"
11 #include "mozilla/PresShell.h"
12 #include "mozilla/dom/Document.h"
13 #include "mozilla/gfx/2D.h"
14 #include "mozilla/layers/IpcResourceUpdateQueue.h"
15 #include "mozilla/layers/WebRenderBridgeChild.h"
16 #include "mozilla/layers/WebRenderDrawEventRecorder.h"
18 using namespace mozilla::gfx
;
19 using namespace mozilla::layers
;
21 namespace mozilla::image
{
23 BlobSurfaceProvider::BlobSurfaceProvider(
24 const ImageKey aImageKey
, const SurfaceKey
& aSurfaceKey
,
25 image::SVGDocumentWrapper
* aSVGDocumentWrapper
, uint32_t aImageFlags
)
26 : ISurfaceProvider(aImageKey
, aSurfaceKey
,
27 AvailabilityState::StartAvailable()),
28 mSVGDocumentWrapper(aSVGDocumentWrapper
),
29 mImageFlags(aImageFlags
) {
30 MOZ_ASSERT(mSVGDocumentWrapper
);
31 MOZ_ASSERT(aImageFlags
& imgIContainer::FLAG_RECORD_BLOB
);
34 BlobSurfaceProvider::~BlobSurfaceProvider() {
35 if (NS_IsMainThread()) {
40 NS_ReleaseOnMainThread("SourceSurfaceBlobImage::mSVGDocumentWrapper",
41 mSVGDocumentWrapper
.forget());
42 NS_DispatchToMainThread(
43 NS_NewRunnableFunction("SourceSurfaceBlobImage::DestroyKeys",
44 [keys
= std::move(mKeys
)] { DestroyKeys(keys
); }));
47 /* static */ void BlobSurfaceProvider::DestroyKeys(
48 const AutoTArray
<BlobImageKeyData
, 1>& aKeys
) {
49 for (const auto& entry
: aKeys
) {
50 if (!entry
.mManager
->IsDestroyed()) {
51 entry
.mManager
->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
57 nsresult
BlobSurfaceProvider::UpdateKey(
58 layers::RenderRootStateManager
* aManager
,
59 wr::IpcResourceUpdateQueue
& aResources
, wr::ImageKey
& aKey
) {
60 MOZ_ASSERT(NS_IsMainThread());
62 layers::WebRenderLayerManager
* manager
= aManager
->LayerManager();
65 Maybe
<wr::BlobImageKey
> key
;
66 auto i
= mKeys
.Length();
69 BlobImageKeyData
& entry
= mKeys
[i
];
70 if (entry
.mManager
->IsDestroyed()) {
71 mKeys
.RemoveElementAt(i
);
72 } else if (entry
.mManager
== manager
) {
73 WebRenderBridgeChild
* wrBridge
= manager
->WrBridge();
76 bool ownsKey
= wrBridge
->MatchesNamespace(entry
.mBlobKey
);
77 if (ownsKey
&& !entry
.mDirty
) {
78 key
.emplace(entry
.mBlobKey
);
82 // Even if the manager is the same, its underlying WebRenderBridgeChild
83 // can change state. Either our namespace differs, and our old key has
84 // already been discarded, or the blob has changed. Either way, we need
86 auto newEntry
= RecordDrawing(manager
, aResources
,
87 ownsKey
? Some(entry
.mBlobKey
) : Nothing());
90 aManager
->AddBlobImageKeyForDiscard(entry
.mBlobKey
);
92 mKeys
.RemoveElementAt(i
);
96 key
.emplace(newEntry
.ref().mBlobKey
);
97 entry
= std::move(newEntry
.ref());
98 MOZ_ASSERT(!entry
.mDirty
);
102 // We didn't find an entry. Attempt to record the blob with a new key.
104 auto newEntry
= RecordDrawing(manager
, aResources
, Nothing());
106 key
.emplace(newEntry
.ref().mBlobKey
);
107 mKeys
.AppendElement(std::move(newEntry
.ref()));
112 aKey
= wr::AsImageKey(key
.value());
116 return NS_ERROR_FAILURE
;
119 void BlobSurfaceProvider::InvalidateRecording() {
120 MOZ_ASSERT(NS_IsMainThread());
122 auto i
= mKeys
.Length();
125 BlobImageKeyData
& entry
= mKeys
[i
];
126 if (entry
.mManager
->IsDestroyed()) {
127 mKeys
.RemoveElementAt(i
);
134 Maybe
<BlobImageKeyData
> BlobSurfaceProvider::RecordDrawing(
135 WebRenderLayerManager
* aManager
, wr::IpcResourceUpdateQueue
& aResources
,
136 Maybe
<wr::BlobImageKey
> aBlobKey
) {
137 MOZ_ASSERT(!aManager
->IsDestroyed());
139 if (mSVGDocumentWrapper
->IsDrawing()) {
143 // This is either our first pass, or we have a stale key requiring us to
144 // re-record the SVG image draw commands.
145 auto* rootManager
= aManager
->GetRenderRootStateManager();
146 auto* wrBridge
= aManager
->WrBridge();
148 const auto& size
= GetSurfaceKey().Size();
149 const auto& region
= GetSurfaceKey().Region();
150 const auto& svgContext
= GetSurfaceKey().SVGContext();
152 IntRect imageRect
= region
? region
->Rect() : IntRect(IntPoint(0, 0), size
);
153 IntRect imageRectOrigin
= imageRect
- imageRect
.TopLeft();
155 std::vector
<RefPtr
<ScaledFont
>> fonts
;
156 bool validFonts
= true;
157 RefPtr
<WebRenderDrawEventRecorder
> recorder
=
158 MakeAndAddRef
<WebRenderDrawEventRecorder
>(
159 [&](MemStream
& aStream
,
160 std::vector
<RefPtr
<ScaledFont
>>& aScaledFonts
) {
161 auto count
= aScaledFonts
.size();
162 aStream
.write((const char*)&count
, sizeof(count
));
164 for (auto& scaled
: aScaledFonts
) {
165 Maybe
<wr::FontInstanceKey
> key
=
166 wrBridge
->GetFontKeyForScaledFont(scaled
, aResources
);
167 if (key
.isNothing()) {
171 BlobFont font
= {key
.value(), scaled
};
172 aStream
.write((const char*)&font
, sizeof(font
));
175 fonts
= std::move(aScaledFonts
);
178 RefPtr
<DrawTarget
> dummyDt
=
179 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
180 RefPtr
<DrawTarget
> dt
=
181 Factory::CreateRecordingDrawTarget(recorder
, dummyDt
, imageRectOrigin
);
183 if (!dt
|| !dt
->IsValid()) {
187 bool contextPaint
= svgContext
&& svgContext
->GetContextPaint();
189 float animTime
= (GetSurfaceKey().Playback() == PlaybackType::eStatic
)
191 : mSVGDocumentWrapper
->GetCurrentTimeAsFloat();
193 IntSize viewportSize
= size
;
195 auto cssViewportSize
= svgContext
->GetViewportSize();
196 if (cssViewportSize
) {
198 viewportSize
.SizeTo(cssViewportSize
->width
, cssViewportSize
->height
);
203 // Get (& sanity-check) the helper-doc's presShell
204 RefPtr
<PresShell
> presShell
= mSVGDocumentWrapper
->GetPresShell();
205 MOZ_ASSERT(presShell
, "GetPresShell returned null for an SVG image?");
207 nsPresContext
* presContext
= presShell
->GetPresContext();
208 MOZ_ASSERT(presContext
, "pres shell w/out pres context");
210 auto* doc
= presShell
->GetDocument();
211 [[maybe_unused
]] nsIURI
* uri
= doc
? doc
->GetDocumentURI() : nullptr;
212 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
213 "SVG Image recording", GRAPHICS
,
214 nsPrintfCString("(%d,%d) %dx%d from %dx%d %s", imageRect
.x
, imageRect
.y
,
215 imageRect
.width
, imageRect
.height
, size
.width
,
217 uri
? uri
->GetSpecOrDefault().get() : "N/A"));
219 AutoRestoreSVGState
autoRestore(svgContext
, animTime
, mSVGDocumentWrapper
,
222 mSVGDocumentWrapper
->UpdateViewportBounds(viewportSize
);
223 mSVGDocumentWrapper
->FlushImageTransformInvalidation();
225 RefPtr
<gfxContext
> ctx
= gfxContext::CreateOrNull(dt
);
226 MOZ_ASSERT(ctx
); // Already checked the draw target above.
229 auto auPerDevPixel
= presContext
->AppUnitsPerDevPixel();
230 if (size
!= viewportSize
) {
231 auto scaleX
= double(size
.width
) / viewportSize
.width
;
232 auto scaleY
= double(size
.height
) / viewportSize
.height
;
233 ctx
->SetMatrix(Matrix::Scaling(float(scaleX
), float(scaleY
)));
235 auto scaledVisibleRect
= IntRectToRect(imageRect
);
236 scaledVisibleRect
.Scale(float(auPerDevPixel
/ scaleX
),
237 float(auPerDevPixel
/ scaleY
));
238 scaledVisibleRect
.Round();
240 int32_t(scaledVisibleRect
.x
), int32_t(scaledVisibleRect
.y
),
241 int32_t(scaledVisibleRect
.width
), int32_t(scaledVisibleRect
.height
));
243 auto scaledVisibleRect(imageRect
);
244 scaledVisibleRect
.Scale(auPerDevPixel
);
245 svgRect
.SetRect(scaledVisibleRect
.x
, scaledVisibleRect
.y
,
246 scaledVisibleRect
.width
, scaledVisibleRect
.height
);
249 RenderDocumentFlags renderDocFlags
=
250 RenderDocumentFlags::IgnoreViewportScrolling
;
251 if (!(mImageFlags
& imgIContainer::FLAG_SYNC_DECODE
)) {
252 renderDocFlags
|= RenderDocumentFlags::AsyncDecodeImages
;
254 if (mImageFlags
& imgIContainer::FLAG_HIGH_QUALITY_SCALING
) {
255 renderDocFlags
|= RenderDocumentFlags::UseHighQualityScaling
;
258 presShell
->RenderDocument(svgRect
, renderDocFlags
,
259 NS_RGBA(0, 0, 0, 0), // transparent
263 recorder
->FlushItem(imageRectOrigin
);
267 gfxCriticalNote
<< "Failed serializing fonts for blob vector image";
271 Range
<uint8_t> bytes((uint8_t*)recorder
->mOutputStream
.mData
,
272 recorder
->mOutputStream
.mLength
);
273 wr::BlobImageKey key
= aBlobKey
275 : wr::BlobImageKey
{wrBridge
->GetNextImageKey()};
276 wr::ImageDescriptor
descriptor(imageRect
.Size(), 0, SurfaceFormat::OS_RGBA
,
277 wr::OpacityType::HasAlphaChannel
);
279 auto visibleRect
= ImageIntRect::FromUnknownRect(imageRectOrigin
);
281 if (!aResources
.UpdateBlobImage(key
, descriptor
, bytes
, visibleRect
,
285 } else if (!aResources
.AddBlobImage(key
, descriptor
, bytes
, visibleRect
)) {
289 std::vector
<RefPtr
<SourceSurface
>> externalSurfaces
;
290 recorder
->TakeExternalSurfaces(externalSurfaces
);
292 for (auto& surface
: externalSurfaces
) {
293 // While we don't use the image key with the surface, because the blob image
294 // renderer doesn't have easy access to the resource set, we still want to
295 // ensure one is generated. That will ensure the surface remains alive until
296 // at least the last epoch which the blob image could be used in.
297 wr::ImageKey key
= {};
298 DebugOnly
<nsresult
> rv
=
299 SharedSurfacesChild::Share(surface
, rootManager
, aResources
, key
);
300 MOZ_ASSERT(rv
.value
!= NS_ERROR_NOT_IMPLEMENTED
);
303 return Some(BlobImageKeyData(aManager
, key
, std::move(fonts
),
304 std::move(externalSurfaces
)));
307 } // namespace mozilla::image