/src/mozilla-central/gfx/webrender_bindings/Moz2DImageRenderer.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
7 | | #include "gfxPrefs.h" |
8 | | #include "gfxUtils.h" |
9 | | #include "mozilla/Mutex.h" |
10 | | #include "mozilla/Range.h" |
11 | | #include "mozilla/gfx/2D.h" |
12 | | #include "mozilla/gfx/RectAbsolute.h" |
13 | | #include "mozilla/gfx/Logging.h" |
14 | | #include "mozilla/gfx/RecordedEvent.h" |
15 | | #include "mozilla/layers/WebRenderDrawEventRecorder.h" |
16 | | #include "WebRenderTypes.h" |
17 | | #include "webrender_ffi.h" |
18 | | |
19 | | #include <unordered_map> |
20 | | |
21 | | #ifdef XP_MACOSX |
22 | | #include "mozilla/gfx/UnscaledFontMac.h" |
23 | | #elif defined(XP_WIN) |
24 | | #include "mozilla/gfx/UnscaledFontDWrite.h" |
25 | | #else |
26 | | #include "mozilla/gfx/UnscaledFontFreeType.h" |
27 | | #endif |
28 | | |
29 | | namespace std { |
30 | | template <> struct hash<mozilla::wr::FontKey> { |
31 | 0 | size_t operator()(const mozilla::wr::FontKey& key) const { |
32 | 0 | return hash<size_t>()(mozilla::wr::AsUint64(key)); |
33 | 0 | } |
34 | | }; |
35 | | |
36 | | template <> struct hash<mozilla::wr::FontInstanceKey> { |
37 | 0 | size_t operator()(const mozilla::wr::FontInstanceKey& key) const { |
38 | 0 | return hash<size_t>()(mozilla::wr::AsUint64(key)); |
39 | 0 | } |
40 | | }; |
41 | | }; |
42 | | |
43 | | namespace mozilla { |
44 | | |
45 | | using namespace gfx; |
46 | | |
47 | | namespace wr { |
48 | | |
49 | | struct FontTemplate { |
50 | | const uint8_t* mData; |
51 | | size_t mSize; |
52 | | uint32_t mIndex; |
53 | | const VecU8* mVec; |
54 | | RefPtr<UnscaledFont> mUnscaledFont; |
55 | | |
56 | | FontTemplate() |
57 | | : mData(nullptr) |
58 | | , mSize(0) |
59 | | , mIndex(0) |
60 | | , mVec(nullptr) |
61 | 0 | {} |
62 | | |
63 | 0 | ~FontTemplate() { |
64 | 0 | if (mVec) { |
65 | 0 | wr_dec_ref_arc(mVec); |
66 | 0 | } |
67 | 0 | } |
68 | | }; |
69 | | |
70 | | struct FontInstanceData { |
71 | | WrFontKey mFontKey; |
72 | | float mSize; |
73 | | Maybe<FontInstanceOptions> mOptions; |
74 | | Maybe<FontInstancePlatformOptions> mPlatformOptions; |
75 | | UniquePtr<gfx::FontVariation[]> mVariations; |
76 | | size_t mNumVariations; |
77 | | RefPtr<ScaledFont> mScaledFont; |
78 | | |
79 | | FontInstanceData() |
80 | | : mSize(0) |
81 | | , mNumVariations(0) |
82 | 0 | {} |
83 | | }; |
84 | | |
85 | | StaticMutex sFontDataTableLock; |
86 | | std::unordered_map<WrFontKey, FontTemplate> sFontDataTable; |
87 | | std::unordered_map<WrFontInstanceKey, FontInstanceData> sBlobFontTable; |
88 | | |
89 | | // Fixed-size ring buffer logging font deletion events to aid debugging. |
90 | | static struct FontDeleteLog { |
91 | | static const size_t MAX_ENTRIES = 256; |
92 | | |
93 | | uint64_t mEntries[MAX_ENTRIES] = { 0 }; |
94 | | size_t mNextEntry = 0; |
95 | | |
96 | 0 | void AddEntry(uint64_t aEntry) { |
97 | 0 | mEntries[mNextEntry] = aEntry; |
98 | 0 | mNextEntry = (mNextEntry + 1) % MAX_ENTRIES; |
99 | 0 | } |
100 | | |
101 | 0 | void Add(WrFontKey aKey) { |
102 | 0 | AddEntry(AsUint64(aKey)); |
103 | 0 | } |
104 | | |
105 | | // Store namespace clears as font id 0, since this will never be allocated. |
106 | 0 | void Add(WrIdNamespace aNamespace) { |
107 | 0 | AddEntry(AsUint64(WrFontKey { aNamespace, 0 })); |
108 | 0 | } |
109 | | |
110 | 0 | void AddAll() { |
111 | 0 | AddEntry(~0); |
112 | 0 | } |
113 | | |
114 | | // Find a matching entry in the log, searching backwards starting at the newest |
115 | | // entry and finishing with the oldest entry. Returns a brief description of why |
116 | | // the font was deleted, if known. |
117 | 0 | const char* Find(WrFontKey aKey) { |
118 | 0 | uint64_t keyEntry = AsUint64(aKey); |
119 | 0 | uint64_t namespaceEntry = AsUint64(WrFontKey { aKey.mNamespace, 0 }); |
120 | 0 | size_t offset = mNextEntry; |
121 | 0 | do { |
122 | 0 | offset = (offset + MAX_ENTRIES - 1) % MAX_ENTRIES; |
123 | 0 | if (mEntries[offset] == keyEntry) { |
124 | 0 | return "deleted font"; |
125 | 0 | } else if (mEntries[offset] == namespaceEntry) { |
126 | 0 | return "cleared namespace"; |
127 | 0 | } else if (mEntries[offset] == (uint64_t)~0) { |
128 | 0 | return "cleared all"; |
129 | 0 | } |
130 | 0 | } while (offset != mNextEntry); |
131 | 0 | return "unknown font"; |
132 | 0 | } |
133 | | } sFontDeleteLog; |
134 | | |
135 | | void |
136 | 0 | ClearAllBlobImageResources() { |
137 | 0 | StaticMutexAutoLock lock(sFontDataTableLock); |
138 | 0 | sFontDeleteLog.AddAll(); |
139 | 0 | sBlobFontTable.clear(); |
140 | 0 | sFontDataTable.clear(); |
141 | 0 | } |
142 | | |
143 | | extern "C" { |
144 | | void |
145 | 0 | ClearBlobImageResources(WrIdNamespace aNamespace) { |
146 | 0 | StaticMutexAutoLock lock(sFontDataTableLock); |
147 | 0 | sFontDeleteLog.Add(aNamespace); |
148 | 0 | for (auto i = sBlobFontTable.begin(); i != sBlobFontTable.end();) { |
149 | 0 | if (i->first.mNamespace == aNamespace) { |
150 | 0 | i = sBlobFontTable.erase(i); |
151 | 0 | } else { |
152 | 0 | i++; |
153 | 0 | } |
154 | 0 | } |
155 | 0 | for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) { |
156 | 0 | if (i->first.mNamespace == aNamespace) { |
157 | 0 | i = sFontDataTable.erase(i); |
158 | 0 | } else { |
159 | 0 | i++; |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | | void |
165 | 0 | AddFontData(WrFontKey aKey, const uint8_t *aData, size_t aSize, uint32_t aIndex, const ArcVecU8 *aVec) { |
166 | 0 | StaticMutexAutoLock lock(sFontDataTableLock); |
167 | 0 | auto i = sFontDataTable.find(aKey); |
168 | 0 | if (i == sFontDataTable.end()) { |
169 | 0 | FontTemplate& font = sFontDataTable[aKey]; |
170 | 0 | font.mData = aData; |
171 | 0 | font.mSize = aSize; |
172 | 0 | font.mIndex = aIndex; |
173 | 0 | font.mVec = wr_add_ref_arc(aVec); |
174 | 0 | } |
175 | 0 | } |
176 | | |
177 | | void |
178 | 0 | AddNativeFontHandle(WrFontKey aKey, void* aHandle, uint32_t aIndex) { |
179 | 0 | StaticMutexAutoLock lock(sFontDataTableLock); |
180 | 0 | auto i = sFontDataTable.find(aKey); |
181 | 0 | if (i == sFontDataTable.end()) { |
182 | 0 | FontTemplate& font = sFontDataTable[aKey]; |
183 | | #ifdef XP_MACOSX |
184 | | font.mUnscaledFont = new UnscaledFontMac(reinterpret_cast<CGFontRef>(aHandle), false); |
185 | | #elif defined(XP_WIN) |
186 | | font.mUnscaledFont = new UnscaledFontDWrite(reinterpret_cast<IDWriteFontFace*>(aHandle), nullptr); |
187 | | #elif defined(ANDROID) |
188 | | font.mUnscaledFont = new UnscaledFontFreeType(reinterpret_cast<const char*>(aHandle), aIndex); |
189 | | #else |
190 | | font.mUnscaledFont = new UnscaledFontFontconfig(reinterpret_cast<const char*>(aHandle), aIndex); |
191 | 0 | #endif |
192 | 0 | } |
193 | 0 | } |
194 | | |
195 | | void |
196 | 0 | DeleteFontData(WrFontKey aKey) { |
197 | 0 | StaticMutexAutoLock lock(sFontDataTableLock); |
198 | 0 | sFontDeleteLog.Add(aKey); |
199 | 0 | auto i = sFontDataTable.find(aKey); |
200 | 0 | if (i != sFontDataTable.end()) { |
201 | 0 | sFontDataTable.erase(i); |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | | void |
206 | | AddBlobFont(WrFontInstanceKey aInstanceKey, |
207 | | WrFontKey aFontKey, |
208 | | float aSize, |
209 | | const FontInstanceOptions* aOptions, |
210 | | const FontInstancePlatformOptions* aPlatformOptions, |
211 | | const FontVariation* aVariations, |
212 | | size_t aNumVariations) |
213 | 0 | { |
214 | 0 | StaticMutexAutoLock lock(sFontDataTableLock); |
215 | 0 | auto i = sBlobFontTable.find(aInstanceKey); |
216 | 0 | if (i == sBlobFontTable.end()) { |
217 | 0 | FontInstanceData& font = sBlobFontTable[aInstanceKey]; |
218 | 0 | font.mFontKey = aFontKey; |
219 | 0 | font.mSize = aSize; |
220 | 0 | if (aOptions) { |
221 | 0 | font.mOptions = Some(*aOptions); |
222 | 0 | } |
223 | 0 | if (aPlatformOptions) { |
224 | 0 | font.mPlatformOptions = Some(*aPlatformOptions); |
225 | 0 | } |
226 | 0 | if (aNumVariations) { |
227 | 0 | font.mVariations.reset(new gfx::FontVariation[aNumVariations]); |
228 | 0 | PodCopy(font.mVariations.get(), reinterpret_cast<const gfx::FontVariation*>(aVariations), aNumVariations); |
229 | 0 | } |
230 | 0 | } |
231 | 0 | } |
232 | | |
233 | | void |
234 | | DeleteBlobFont(WrFontInstanceKey aKey) |
235 | 0 | { |
236 | 0 | StaticMutexAutoLock lock(sFontDataTableLock); |
237 | 0 | auto i = sBlobFontTable.find(aKey); |
238 | 0 | if (i != sBlobFontTable.end()) { |
239 | 0 | sBlobFontTable.erase(i); |
240 | 0 | } |
241 | 0 | } |
242 | | |
243 | | } // extern |
244 | | |
245 | | static RefPtr<UnscaledFont> |
246 | | GetUnscaledFont(Translator* aTranslator, WrFontKey aKey) |
247 | 0 | { |
248 | 0 | auto i = sFontDataTable.find(aKey); |
249 | 0 | if (i == sFontDataTable.end()) { |
250 | 0 | gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to get UnscaledFont entry for FontKey " << aKey.mHandle |
251 | 0 | << " because " << sFontDeleteLog.Find(aKey); |
252 | 0 | return nullptr; |
253 | 0 | } |
254 | 0 | FontTemplate &data = i->second; |
255 | 0 | if (data.mUnscaledFont) { |
256 | 0 | return data.mUnscaledFont; |
257 | 0 | } |
258 | 0 | MOZ_ASSERT(data.mData); |
259 | 0 | FontType type = |
260 | | #ifdef XP_MACOSX |
261 | | FontType::MAC; |
262 | | #elif defined(XP_WIN) |
263 | | FontType::DWRITE; |
264 | | #elif defined(ANDROID) |
265 | | FontType::FREETYPE; |
266 | | #else |
267 | | FontType::FONTCONFIG; |
268 | 0 | #endif |
269 | 0 | // makes a copy of the data |
270 | 0 | RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource((uint8_t*)data.mData, data.mSize, |
271 | 0 | aTranslator->GetReferenceDrawTarget()->GetBackendType(), |
272 | 0 | type, |
273 | 0 | aTranslator->GetFontContext()); |
274 | 0 | RefPtr<UnscaledFont> unscaledFont; |
275 | 0 | if (!fontResource) { |
276 | 0 | gfxDevCrash(LogReason::NativeFontResourceNotFound) << "Failed to create NativeFontResource for FontKey " << aKey.mHandle; |
277 | 0 | } else { |
278 | 0 | // Instance data is only needed for GDI fonts which webrender does not |
279 | 0 | // support. |
280 | 0 | unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0); |
281 | 0 | if (!unscaledFont) { |
282 | 0 | gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to create UnscaledFont for FontKey " << aKey.mHandle; |
283 | 0 | } |
284 | 0 | } |
285 | 0 | data.mUnscaledFont = unscaledFont; |
286 | 0 | return unscaledFont; |
287 | 0 | } |
288 | | |
289 | | static RefPtr<ScaledFont> |
290 | | GetScaledFont(Translator* aTranslator, WrFontInstanceKey aKey) |
291 | 0 | { |
292 | 0 | StaticMutexAutoLock lock(sFontDataTableLock); |
293 | 0 | auto i = sBlobFontTable.find(aKey); |
294 | 0 | if (i == sBlobFontTable.end()) { |
295 | 0 | gfxDevCrash(LogReason::ScaledFontNotFound) << "Failed to get ScaledFont entry for FontInstanceKey " << aKey.mHandle; |
296 | 0 | return nullptr; |
297 | 0 | } |
298 | 0 | FontInstanceData &data = i->second; |
299 | 0 | if (data.mScaledFont) { |
300 | 0 | return data.mScaledFont; |
301 | 0 | } |
302 | 0 | RefPtr<UnscaledFont> unscaled = GetUnscaledFont(aTranslator, data.mFontKey); |
303 | 0 | if (!unscaled) { |
304 | 0 | return nullptr; |
305 | 0 | } |
306 | 0 | RefPtr<ScaledFont> scaled = |
307 | 0 | unscaled->CreateScaledFontFromWRFont(data.mSize, |
308 | 0 | data.mOptions.ptrOr(nullptr), |
309 | 0 | data.mPlatformOptions.ptrOr(nullptr), |
310 | 0 | data.mVariations.get(), |
311 | 0 | data.mNumVariations); |
312 | 0 | if (!scaled) { |
313 | 0 | gfxDevCrash(LogReason::ScaledFontNotFound) << "Failed to create ScaledFont for FontKey " << aKey.mHandle; |
314 | 0 | } |
315 | 0 | data.mScaledFont = scaled; |
316 | 0 | return data.mScaledFont; |
317 | 0 | } |
318 | | |
319 | | static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob, |
320 | | gfx::IntSize aSize, |
321 | | gfx::SurfaceFormat aFormat, |
322 | | const uint16_t *aTileSize, |
323 | | const mozilla::wr::TileOffset *aTileOffset, |
324 | | const mozilla::wr::DeviceUintRect *aDirtyRect, |
325 | | Range<uint8_t> aOutput) |
326 | 0 | { |
327 | 0 | MOZ_ASSERT(aSize.width > 0 && aSize.height > 0); |
328 | 0 | if (aSize.width <= 0 || aSize.height <= 0) { |
329 | 0 | return false; |
330 | 0 | } |
331 | 0 | |
332 | 0 | auto stride = aSize.width * gfx::BytesPerPixel(aFormat); |
333 | 0 |
|
334 | 0 | if (aOutput.length() < static_cast<size_t>(aSize.height * stride)) { |
335 | 0 | return false; |
336 | 0 | } |
337 | 0 | |
338 | 0 | // In bindings.rs we allocate a buffer filled with opaque white. |
339 | 0 | bool uninitialized = false; |
340 | 0 |
|
341 | 0 | RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData( |
342 | 0 | gfx::BackendType::SKIA, |
343 | 0 | aOutput.begin().get(), |
344 | 0 | aSize, |
345 | 0 | stride, |
346 | 0 | aFormat, |
347 | 0 | uninitialized |
348 | 0 | ); |
349 | 0 |
|
350 | 0 | if (!dt) { |
351 | 0 | return false; |
352 | 0 | } |
353 | 0 | |
354 | 0 | auto origin = gfx::IntPoint(0, 0); |
355 | 0 | if (aTileOffset) { |
356 | 0 | origin = gfx::IntPoint(aTileOffset->x * *aTileSize, aTileOffset->y * *aTileSize); |
357 | 0 | dt = gfx::Factory::CreateOffsetDrawTarget(dt, origin); |
358 | 0 | } |
359 | 0 |
|
360 | 0 | auto bounds = gfx::IntRect(origin, aSize); |
361 | 0 |
|
362 | 0 | if (aDirtyRect) { |
363 | 0 | Rect dirty(aDirtyRect->origin.x, aDirtyRect->origin.y, aDirtyRect->size.width, aDirtyRect->size.height); |
364 | 0 | dt->PushClipRect(dirty); |
365 | 0 | bounds = bounds.Intersect(IntRect(aDirtyRect->origin.x, |
366 | 0 | aDirtyRect->origin.y, |
367 | 0 | aDirtyRect->size.width, |
368 | 0 | aDirtyRect->size.height)); |
369 | 0 | } |
370 | 0 |
|
371 | 0 | struct Reader { |
372 | 0 | const uint8_t *buf; |
373 | 0 | size_t len; |
374 | 0 | size_t pos; |
375 | 0 |
|
376 | 0 | Reader(const uint8_t *buf, size_t len) : buf(buf), len(len), pos(0) {} |
377 | 0 |
|
378 | 0 | size_t ReadSize() { |
379 | 0 | size_t ret; |
380 | 0 | MOZ_RELEASE_ASSERT(pos + sizeof(ret) <= len); |
381 | 0 | memcpy(&ret, buf + pos, sizeof(ret)); |
382 | 0 | pos += sizeof(ret); |
383 | 0 | return ret; |
384 | 0 | } |
385 | 0 | int ReadInt() { |
386 | 0 | int ret; |
387 | 0 | MOZ_RELEASE_ASSERT(pos + sizeof(ret) <= len); |
388 | 0 | memcpy(&ret, buf + pos, sizeof(ret)); |
389 | 0 | pos += sizeof(ret); |
390 | 0 | return ret; |
391 | 0 | } |
392 | 0 |
|
393 | 0 | IntRectAbsolute ReadBounds() { |
394 | 0 | MOZ_RELEASE_ASSERT(pos + sizeof(int32_t) * 4 <= len); |
395 | 0 | int32_t x1, y1, x2, y2; |
396 | 0 | memcpy(&x1, buf + pos + 0 * sizeof(int32_t), sizeof(x1)); |
397 | 0 | memcpy(&y1, buf + pos + 1 * sizeof(int32_t), sizeof(y1)); |
398 | 0 | memcpy(&x2, buf + pos + 2 * sizeof(int32_t), sizeof(x2)); |
399 | 0 | memcpy(&y2, buf + pos + 3 * sizeof(int32_t), sizeof(y2)); |
400 | 0 | pos += sizeof(int32_t) * 4; |
401 | 0 | return IntRectAbsolute(x1, y1, x2, y2); |
402 | 0 | } |
403 | 0 |
|
404 | 0 | layers::BlobFont ReadBlobFont() { |
405 | 0 | MOZ_RELEASE_ASSERT(pos + sizeof(layers::BlobFont) <= len); |
406 | 0 | layers::BlobFont ret; |
407 | 0 | memcpy(&ret, buf + pos, sizeof(ret)); |
408 | 0 | pos += sizeof(ret); |
409 | 0 | return ret; |
410 | 0 | } |
411 | 0 |
|
412 | 0 | }; |
413 | 0 |
|
414 | 0 | // We try hard to not have empty blobs but we can end up with |
415 | 0 | // them because of CompositorHitTestInfo and merging. |
416 | 0 | MOZ_RELEASE_ASSERT(aBlob.length() >= sizeof(size_t)); |
417 | 0 | size_t indexOffset = *(size_t*)(aBlob.end().get()-sizeof(size_t)); |
418 | 0 | MOZ_RELEASE_ASSERT(indexOffset <= aBlob.length() - sizeof(size_t)); |
419 | 0 | Reader reader(aBlob.begin().get()+indexOffset, aBlob.length()-sizeof(size_t)-indexOffset); |
420 | 0 |
|
421 | 0 | bool ret = true; |
422 | 0 | size_t offset = 0; |
423 | 0 | auto absBounds = IntRectAbsolute::FromRect(bounds); |
424 | 0 | while (reader.pos < reader.len) { |
425 | 0 | size_t end = reader.ReadSize(); |
426 | 0 | size_t extra_end = reader.ReadSize(); |
427 | 0 | auto combinedBounds = absBounds.Intersect(reader.ReadBounds()); |
428 | 0 | if (combinedBounds.IsEmpty()) { |
429 | 0 | offset = extra_end; |
430 | 0 | continue; |
431 | 0 | } |
432 | 0 | |
433 | 0 | layers::WebRenderTranslator translator(dt); |
434 | 0 |
|
435 | 0 | MOZ_RELEASE_ASSERT(extra_end >= end); |
436 | 0 | MOZ_RELEASE_ASSERT(extra_end < aBlob.length()); |
437 | 0 | Reader fontReader(aBlob.begin().get() + end, extra_end - end); |
438 | 0 | size_t count = fontReader.ReadSize(); |
439 | 0 | for (size_t i = 0; i < count; i++) { |
440 | 0 | layers::BlobFont blobFont = fontReader.ReadBlobFont(); |
441 | 0 | RefPtr<ScaledFont> scaledFont = GetScaledFont(&translator, blobFont.mFontInstanceKey); |
442 | 0 | translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont); |
443 | 0 | } |
444 | 0 |
|
445 | 0 | Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end); |
446 | 0 | ret = translator.TranslateRecording((char*)blob.begin().get(), blob.length()); |
447 | 0 | MOZ_RELEASE_ASSERT(ret); |
448 | 0 | offset = extra_end; |
449 | 0 | } |
450 | 0 |
|
451 | 0 | if (gfxPrefs::WebRenderBlobPaintFlashing()) { |
452 | 0 | dt->SetTransform(gfx::Matrix()); |
453 | 0 | float r = float(rand()) / RAND_MAX; |
454 | 0 | float g = float(rand()) / RAND_MAX; |
455 | 0 | float b = float(rand()) / RAND_MAX; |
456 | 0 | dt->FillRect(gfx::Rect(origin.x, origin.y, aSize.width, aSize.height), gfx::ColorPattern(gfx::Color(r, g, b, 0.5))); |
457 | 0 | } |
458 | 0 |
|
459 | 0 | if (aDirtyRect) { |
460 | 0 | dt->PopClip(); |
461 | 0 | } |
462 | 0 |
|
463 | | #if 0 |
464 | | static int i = 0; |
465 | | char filename[40]; |
466 | | sprintf(filename, "out%d.png", i++); |
467 | | gfxUtils::WriteAsPNG(dt, filename); |
468 | | #endif |
469 | |
|
470 | 0 | return ret; |
471 | 0 | } |
472 | | |
473 | | } // namespace |
474 | | } // namespace |
475 | | |
476 | | extern "C" { |
477 | | |
478 | | bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob, |
479 | | uint32_t width, uint32_t height, |
480 | | mozilla::wr::ImageFormat aFormat, |
481 | | const uint16_t *aTileSize, |
482 | | const mozilla::wr::TileOffset *aTileOffset, |
483 | | const mozilla::wr::DeviceUintRect *aDirtyRect, |
484 | | mozilla::wr::MutByteSlice output) |
485 | 0 | { |
486 | 0 | return mozilla::wr::Moz2DRenderCallback(mozilla::wr::ByteSliceToRange(blob), |
487 | 0 | mozilla::gfx::IntSize(width, height), |
488 | 0 | mozilla::wr::ImageFormatToSurfaceFormat(aFormat), |
489 | 0 | aTileSize, |
490 | 0 | aTileOffset, |
491 | 0 | aDirtyRect, |
492 | 0 | mozilla::wr::MutByteSliceToRange(output)); |
493 | 0 | } |
494 | | |
495 | | } // extern |
496 | | |