Bug 1651513 [wpt PR 24521] - HTML: Add tentative tests for speculative HTML parsing...
[gecko.git] / image / SourceSurfaceBlobImage.cpp
blobea4ca79d720d8840746146afcf902547831b0a20
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),
30 mRegion(aRegion),
31 mSize(aSize),
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()) {
41 DestroyKeys(mKeys);
42 return;
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(
57 entry.mBlobKey);
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();
68 while (i > 0) {
69 --i;
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();
75 MOZ_ASSERT(wrBridge);
77 bool ownsKey = wrBridge->MatchesNamespace(entry.mBlobKey);
78 if (ownsKey && !entry.mDirty) {
79 key.emplace(entry.mBlobKey);
80 continue;
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
86 // to rerecord it.
87 auto newEntry = RecordDrawing(aManager, aResources,
88 ownsKey ? Some(entry.mBlobKey) : Nothing());
89 if (!newEntry) {
90 if (ownsKey) {
91 aManager->GetRenderRootStateManager()->AddBlobImageKeyForDiscard(
92 entry.mBlobKey);
94 mKeys.RemoveElementAt(i);
95 continue;
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.
105 if (!key) {
106 auto newEntry = RecordDrawing(aManager, aResources, Nothing());
107 if (newEntry) {
108 key.emplace(newEntry.ref().mBlobKey);
109 mKeys.AppendElement(std::move(newEntry.ref()));
113 return key;
116 void SourceSurfaceBlobImage::MarkDirty() {
117 MOZ_ASSERT(NS_IsMainThread());
119 auto i = mKeys.Length();
120 while (i > 0) {
121 --i;
122 BlobImageKeyData& entry = mKeys[i];
123 if (entry.mManager->IsDestroyed()) {
124 mKeys.RemoveElementAt(i);
125 } else {
126 entry.mDirty = true;
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()) {
137 return Nothing();
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();
145 IntRect imageRect =
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()) {
162 validFonts = false;
163 break;
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()) {
178 return Nothing();
181 bool contextPaint = mSVGContext && mSVGContext->GetContextPaint();
183 float animTime = (mWhichFrame == imgIContainer::FRAME_FIRST)
184 ? 0.0f
185 : mSVGDocumentWrapper->GetCurrentTimeAsFloat();
187 IntSize viewportSize = mSize;
188 if (mSVGContext) {
189 auto cssViewportSize = mSVGContext->GetViewportSize();
190 if (cssViewportSize) {
191 // XXX losing unit
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,
210 mSize.height,
211 uri ? uri->GetSpecOrDefault().get() : "N/A"));
213 AutoRestoreSVGState autoRestore(mSVGContext, animTime, mSVGDocumentWrapper,
214 contextPaint);
216 mSVGDocumentWrapper->UpdateViewportBounds(viewportSize);
217 mSVGDocumentWrapper->FlushImageTransformInvalidation();
219 RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(dt);
220 MOZ_ASSERT(ctx); // Already checked the draw target above.
222 nsRect svgRect;
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();
233 svgRect.SetRect(
234 int32_t(scaledVisibleRect.x), int32_t(scaledVisibleRect.y),
235 int32_t(scaledVisibleRect.width), int32_t(scaledVisibleRect.height));
236 } else {
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
254 ctx);
257 recorder->FlushItem(imageRectOrigin);
258 recorder->Finish();
260 if (!validFonts) {
261 gfxCriticalNote << "Failed serializing fonts for blob vector image";
262 return Nothing();
265 Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData,
266 recorder->mOutputStream.mLength);
267 wr::BlobImageKey key = aBlobKey
268 ? aBlobKey.value()
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);
274 if (aBlobKey) {
275 if (!aResources.UpdateBlobImage(key, descriptor, bytes, visibleRect,
276 visibleRect)) {
277 return Nothing();
279 } else if (!aResources.AddBlobImage(key, descriptor, bytes, visibleRect)) {
280 return Nothing();
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