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 "SVGDrawingParameters.h"
11 #include "SVGDrawingCallback.h"
12 #include "SVGDocumentWrapper.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()) {
182 bool contextPaint
= mSVGContext
&& mSVGContext
->GetContextPaint();
184 float animTime
= (mWhichFrame
== imgIContainer::FRAME_FIRST
)
186 : mSVGDocumentWrapper
->GetCurrentTimeAsFloat();
190 ? mRegion
->ToImageRegion()
191 : ImageRegion::Create(gfxRect(imageRect
.x
, imageRect
.y
,
192 imageRect
.width
, imageRect
.height
));
194 SVGDrawingParameters
params(nullptr, mSize
, mSize
, region
,
195 SamplingFilter::POINT
, mSVGContext
, animTime
,
198 AutoRestoreSVGState
autoRestore(params
, mSVGDocumentWrapper
, contextPaint
);
200 RefPtr
<gfxDrawingCallback
> cb
= new SVGDrawingCallback(
201 mSVGDocumentWrapper
, params
.viewportSize
, params
.size
, params
.flags
);
202 RefPtr
<gfxDrawable
> svgDrawable
= new gfxCallbackDrawable(cb
, params
.size
);
204 mSVGDocumentWrapper
->UpdateViewportBounds(params
.viewportSize
);
205 mSVGDocumentWrapper
->FlushImageTransformInvalidation();
207 // Draw using the drawable the caller provided.
208 RefPtr
<gfxContext
> ctx
= gfxContext::CreateOrNull(dt
);
209 MOZ_ASSERT(ctx
); // Already checked the draw target above.
212 ctx
->CurrentMatrix().PreTranslate(-imageRect
.x
, -imageRect
.y
));
214 gfxUtils::DrawPixelSnapped(ctx
, svgDrawable
, SizeDouble(mSize
), region
,
215 SurfaceFormat::OS_RGBA
, SamplingFilter::POINT
,
216 mImageFlags
, /* aUseOptimalFillOp */ false);
219 recorder
->FlushItem(imageRectOrigin
);
223 gfxCriticalNote
<< "Failed serializing fonts for blob vector image";
227 Range
<uint8_t> bytes((uint8_t*)recorder
->mOutputStream
.mData
,
228 recorder
->mOutputStream
.mLength
);
229 wr::BlobImageKey key
= aBlobKey
231 : wr::BlobImageKey
{wrBridge
->GetNextImageKey()};
232 wr::ImageDescriptor
descriptor(imageRect
.Size(), 0, SurfaceFormat::OS_RGBA
,
233 wr::OpacityType::HasAlphaChannel
);
235 auto visibleRect
= ImageIntRect::FromUnknownRect(imageRectOrigin
);
237 if (!aResources
.UpdateBlobImage(key
, descriptor
, bytes
, visibleRect
,
241 } else if (!aResources
.AddBlobImage(key
, descriptor
, bytes
, visibleRect
)) {
245 std::vector
<RefPtr
<SourceSurface
>> externalSurfaces
;
246 recorder
->TakeExternalSurfaces(externalSurfaces
);
248 for (auto& surface
: externalSurfaces
) {
249 // While we don't use the image key with the surface, because the blob image
250 // renderer doesn't have easy access to the resource set, we still want to
251 // ensure one is generated. That will ensure the surface remains alive until
252 // at least the last epoch which the blob image could be used in.
253 wr::ImageKey key
= {};
254 DebugOnly
<nsresult
> rv
=
255 SharedSurfacesChild::Share(surface
, rootManager
, aResources
, key
);
256 MOZ_ASSERT(rv
.value
!= NS_ERROR_NOT_IMPLEMENTED
);
259 return Some(BlobImageKeyData(aManager
, key
, std::move(fonts
),
260 std::move(externalSurfaces
)));
263 } // namespace mozilla::image