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 "SourceSurfaceBlobImage.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 SourceSurfaceBlobImage::SourceSurfaceBlobImage(
24 image::SVGDocumentWrapper
* aSVGDocumentWrapper
,
25 const Maybe
<SVGImageContext
>& aSVGContext
,
26 const Maybe
<ImageIntRegion
>& aRegion
, const IntSize
& aSize
,
27 uint32_t aWhichFrame
, uint32_t aImageFlags
)
28 : mSVGDocumentWrapper(aSVGDocumentWrapper
),
29 mSVGContext(aSVGContext
),
32 mWhichFrame(aWhichFrame
),
33 mImageFlags(aImageFlags
) {
34 MOZ_ASSERT(mSVGDocumentWrapper
);
35 MOZ_ASSERT(aWhichFrame
<= imgIContainer::FRAME_MAX_VALUE
);
36 MOZ_ASSERT(aImageFlags
& imgIContainer::FLAG_RECORD_BLOB
);
39 SourceSurfaceBlobImage::~SourceSurfaceBlobImage() {
40 if (NS_IsMainThread()) {
45 NS_ReleaseOnMainThread("SourceSurfaceBlobImage::mSVGDocumentWrapper",
46 mSVGDocumentWrapper
.forget());
47 NS_DispatchToMainThread(
48 NS_NewRunnableFunction("SourceSurfaceBlobImage::DestroyKeys",
49 [keys
= std::move(mKeys
)] { DestroyKeys(keys
); }));
52 /* static */ void SourceSurfaceBlobImage::DestroyKeys(
53 const AutoTArray
<BlobImageKeyData
, 1>& aKeys
) {
54 for (const auto& entry
: aKeys
) {
55 if (!entry
.mManager
->IsDestroyed()) {
56 entry
.mManager
->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
62 Maybe
<wr::BlobImageKey
> SourceSurfaceBlobImage::UpdateKey(
63 WebRenderLayerManager
* aManager
, wr::IpcResourceUpdateQueue
& aResources
) {
64 MOZ_ASSERT(NS_IsMainThread());
66 Maybe
<wr::BlobImageKey
> key
;
67 auto i
= mKeys
.Length();
70 BlobImageKeyData
& entry
= mKeys
[i
];
71 if (entry
.mManager
->IsDestroyed()) {
72 mKeys
.RemoveElementAt(i
);
73 } else if (entry
.mManager
== aManager
) {
74 WebRenderBridgeChild
* wrBridge
= aManager
->WrBridge();
77 bool ownsKey
= wrBridge
->MatchesNamespace(entry
.mBlobKey
);
78 if (ownsKey
&& !entry
.mDirty
) {
79 key
.emplace(entry
.mBlobKey
);
83 // Even if the manager is the same, its underlying WebRenderBridgeChild
84 // can change state. Either our namespace differs, and our old key has
85 // already been discarded, or the blob has changed. Either way, we need
87 auto newEntry
= RecordDrawing(aManager
, aResources
,
88 ownsKey
? Some(entry
.mBlobKey
) : Nothing());
91 aManager
->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
94 mKeys
.RemoveElementAt(i
);
98 key
.emplace(newEntry
.ref().mBlobKey
);
99 entry
= std::move(newEntry
.ref());
100 MOZ_ASSERT(!entry
.mDirty
);
104 // We didn't find an entry. Attempt to record the blob with a new key.
106 auto newEntry
= RecordDrawing(aManager
, aResources
, Nothing());
108 key
.emplace(newEntry
.ref().mBlobKey
);
109 mKeys
.AppendElement(std::move(newEntry
.ref()));
116 void SourceSurfaceBlobImage::MarkDirty() {
117 MOZ_ASSERT(NS_IsMainThread());
119 auto i
= mKeys
.Length();
122 BlobImageKeyData
& entry
= mKeys
[i
];
123 if (entry
.mManager
->IsDestroyed()) {
124 mKeys
.RemoveElementAt(i
);
131 Maybe
<BlobImageKeyData
> SourceSurfaceBlobImage::RecordDrawing(
132 WebRenderLayerManager
* aManager
, wr::IpcResourceUpdateQueue
& aResources
,
133 Maybe
<wr::BlobImageKey
> aBlobKey
) {
134 MOZ_ASSERT(!aManager
->IsDestroyed());
136 if (mSVGDocumentWrapper
->IsDrawing()) {
140 // This is either our first pass, or we have a stale key requiring us to
141 // re-record the SVG image draw commands.
142 auto* rootManager
= aManager
->GetRenderRootStateManager();
143 auto* wrBridge
= aManager
->WrBridge();
146 mRegion
? mRegion
->Rect() : IntRect(IntPoint(0, 0), mSize
);
147 IntRect imageRectOrigin
= imageRect
- imageRect
.TopLeft();
149 std::vector
<RefPtr
<ScaledFont
>> fonts
;
150 bool validFonts
= true;
151 RefPtr
<WebRenderDrawEventRecorder
> recorder
=
152 MakeAndAddRef
<WebRenderDrawEventRecorder
>(
153 [&](MemStream
& aStream
,
154 std::vector
<RefPtr
<ScaledFont
>>& aScaledFonts
) {
155 auto count
= aScaledFonts
.size();
156 aStream
.write((const char*)&count
, sizeof(count
));
158 for (auto& scaled
: aScaledFonts
) {
159 Maybe
<wr::FontInstanceKey
> key
=
160 wrBridge
->GetFontKeyForScaledFont(scaled
, &aResources
);
161 if (key
.isNothing()) {
165 BlobFont font
= {key
.value(), scaled
};
166 aStream
.write((const char*)&font
, sizeof(font
));
169 fonts
= std::move(aScaledFonts
);
172 RefPtr
<DrawTarget
> dummyDt
= Factory::CreateDrawTarget(
173 BackendType::SKIA
, IntSize(1, 1), SurfaceFormat::OS_RGBA
);
174 RefPtr
<DrawTarget
> dt
=
175 Factory::CreateRecordingDrawTarget(recorder
, dummyDt
, imageRectOrigin
);
177 if (!dt
|| !dt
->IsValid()) {
181 bool contextPaint
= mSVGContext
&& mSVGContext
->GetContextPaint();
183 float animTime
= (mWhichFrame
== imgIContainer::FRAME_FIRST
)
185 : mSVGDocumentWrapper
->GetCurrentTimeAsFloat();
187 IntSize viewportSize
= mSize
;
189 auto cssViewportSize
= mSVGContext
->GetViewportSize();
190 if (cssViewportSize
) {
192 viewportSize
.SizeTo(cssViewportSize
->width
, cssViewportSize
->height
);
197 // Get (& sanity-check) the helper-doc's presShell
198 RefPtr
<PresShell
> presShell
= mSVGDocumentWrapper
->GetPresShell();
199 MOZ_ASSERT(presShell
, "GetPresShell returned null for an SVG image?");
201 nsPresContext
* presContext
= presShell
->GetPresContext();
202 MOZ_ASSERT(presContext
, "pres shell w/out pres context");
204 auto* doc
= presShell
->GetDocument();
205 [[maybe_unused
]] nsIURI
* uri
= doc
? doc
->GetDocumentURI() : nullptr;
206 AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
207 "SVG Image recording", GRAPHICS
,
208 nsPrintfCString("(%d,%d) %dx%d from %dx%d %s", imageRect
.x
, imageRect
.y
,
209 imageRect
.width
, imageRect
.height
, mSize
.width
,
211 uri
? uri
->GetSpecOrDefault().get() : "N/A"));
213 AutoRestoreSVGState
autoRestore(mSVGContext
, animTime
, mSVGDocumentWrapper
,
216 mSVGDocumentWrapper
->UpdateViewportBounds(viewportSize
);
217 mSVGDocumentWrapper
->FlushImageTransformInvalidation();
219 RefPtr
<gfxContext
> ctx
= gfxContext::CreateOrNull(dt
);
220 MOZ_ASSERT(ctx
); // Already checked the draw target above.
223 auto auPerDevPixel
= presContext
->AppUnitsPerDevPixel();
224 if (mSize
!= viewportSize
) {
225 auto scaleX
= double(mSize
.width
) / viewportSize
.width
;
226 auto scaleY
= double(mSize
.height
) / viewportSize
.height
;
227 ctx
->SetMatrix(Matrix::Scaling(float(scaleX
), float(scaleY
)));
229 auto scaledVisibleRect
= IntRectToRect(imageRect
);
230 scaledVisibleRect
.Scale(float(auPerDevPixel
/ scaleX
),
231 float(auPerDevPixel
/ scaleY
));
232 scaledVisibleRect
.Round();
234 int32_t(scaledVisibleRect
.x
), int32_t(scaledVisibleRect
.y
),
235 int32_t(scaledVisibleRect
.width
), int32_t(scaledVisibleRect
.height
));
237 auto scaledVisibleRect(imageRect
);
238 scaledVisibleRect
.Scale(auPerDevPixel
);
239 svgRect
.SetRect(scaledVisibleRect
.x
, scaledVisibleRect
.y
,
240 scaledVisibleRect
.width
, scaledVisibleRect
.height
);
243 RenderDocumentFlags renderDocFlags
=
244 RenderDocumentFlags::IgnoreViewportScrolling
;
245 if (!(mImageFlags
& imgIContainer::FLAG_SYNC_DECODE
)) {
246 renderDocFlags
|= RenderDocumentFlags::AsyncDecodeImages
;
248 if (mImageFlags
& imgIContainer::FLAG_HIGH_QUALITY_SCALING
) {
249 renderDocFlags
|= RenderDocumentFlags::UseHighQualityScaling
;
252 presShell
->RenderDocument(svgRect
, renderDocFlags
,
253 NS_RGBA(0, 0, 0, 0), // transparent
257 recorder
->FlushItem(imageRectOrigin
);
261 gfxCriticalNote
<< "Failed serializing fonts for blob vector image";
265 Range
<uint8_t> bytes((uint8_t*)recorder
->mOutputStream
.mData
,
266 recorder
->mOutputStream
.mLength
);
267 wr::BlobImageKey key
= aBlobKey
269 : wr::BlobImageKey
{wrBridge
->GetNextImageKey()};
270 wr::ImageDescriptor
descriptor(imageRect
.Size(), 0, SurfaceFormat::OS_RGBA
,
271 wr::OpacityType::HasAlphaChannel
);
273 auto visibleRect
= ImageIntRect::FromUnknownRect(imageRectOrigin
);
275 if (!aResources
.UpdateBlobImage(key
, descriptor
, bytes
, visibleRect
,
279 } else if (!aResources
.AddBlobImage(key
, descriptor
, bytes
, visibleRect
)) {
283 std::vector
<RefPtr
<SourceSurface
>> externalSurfaces
;
284 recorder
->TakeExternalSurfaces(externalSurfaces
);
286 for (auto& surface
: externalSurfaces
) {
287 // While we don't use the image key with the surface, because the blob image
288 // renderer doesn't have easy access to the resource set, we still want to
289 // ensure one is generated. That will ensure the surface remains alive until
290 // at least the last epoch which the blob image could be used in.
291 wr::ImageKey key
= {};
292 DebugOnly
<nsresult
> rv
=
293 SharedSurfacesChild::Share(surface
, rootManager
, aResources
, key
);
294 MOZ_ASSERT(rv
.value
!= NS_ERROR_NOT_IMPLEMENTED
);
297 return Some(BlobImageKeyData(aManager
, key
, std::move(fonts
),
298 std::move(externalSurfaces
)));
301 } // namespace mozilla::image