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/. */
11 # include "DrawTargetCairo.h"
12 # include "PathCairo.h"
13 # include "SourceSurfaceCairo.h"
16 #include "DrawTargetSkia.h"
18 #include "ScaledFontBase.h"
21 # include "ScaledFontWin.h"
22 # include "NativeFontResourceGDI.h"
23 # include "UnscaledFontGDI.h"
27 # include "ScaledFontMac.h"
28 # include "NativeFontResourceMac.h"
29 # include "UnscaledFontMac.h"
33 # include "ScaledFontFontconfig.h"
34 # include "NativeFontResourceFreeType.h"
35 # include "UnscaledFontFreeType.h"
38 #ifdef MOZ_WIDGET_ANDROID
39 # include "ScaledFontFreeType.h"
40 # include "NativeFontResourceFreeType.h"
41 # include "UnscaledFontFreeType.h"
45 # include "DrawTargetD2D1.h"
47 # include "ScaledFontDWrite.h"
48 # include "NativeFontResourceDWrite.h"
49 # include "UnscaledFontDWrite.h"
52 # include "HelpersD2D.h"
53 # include "DXVA2Manager.h"
54 # include "ImageContainer.h"
55 # include "mozilla/layers/LayersSurfaces.h"
56 # include "mozilla/layers/TextureD3D11.h"
57 # include "nsWindowsHelpers.h"
60 #include "DrawTargetOffset.h"
61 #include "DrawTargetRecording.h"
63 #include "SourceSurfaceRawData.h"
65 #include "mozilla/CheckedInt.h"
67 #ifdef MOZ_ENABLE_FREETYPE
68 # include "ft2build.h"
69 # include FT_FREETYPE_H
71 #include "mozilla/StaticPrefs_gfx.h"
73 #if defined(MOZ_LOGGING)
74 GFX2D_API
mozilla::LogModule
* GetGFX2DLog() {
75 static mozilla::LazyLogModule
sLog("gfx2d");
80 // The following code was largely taken from xpcom/glue/SSE.cpp and
81 // made a little simpler.
82 enum CPUIDRegister
{ eax
= 0, ebx
= 1, ecx
= 2, edx
= 3 };
86 # if !(defined(__SSE2__) || defined(_M_X64) || \
87 (defined(_M_IX86_FP) && _M_IX86_FP >= 2)) || \
89 // cpuid.h is available on gcc 4.3 and higher on i386 and x86_64
92 static inline bool HasCPUIDBit(unsigned int level
, CPUIDRegister reg
,
95 return __get_cpuid(level
, ®s
[0], ®s
[1], ®s
[2], ®s
[3]) &&
100 # define HAVE_CPU_DETECTION
103 # if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_AMD64))
104 // MSVC 2005 or later supports __cpuid by intrin.h
107 # define HAVE_CPU_DETECTION
108 # elif defined(__SUNPRO_CC) && (defined(__i386) || defined(__x86_64__))
110 // Define a function identical to MSVC function.
112 static void __cpuid(int CPUInfo
[4], int InfoType
) {
113 asm("xchg %esi, %ebx\n"
115 "movl %eax, (%edi)\n"
116 "movl %ebx, 4(%edi)\n"
117 "movl %ecx, 8(%edi)\n"
118 "movl %edx, 12(%edi)\n"
121 : "a"(InfoType
), // %eax
123 : "%ecx", "%edx", "%esi");
126 static void __cpuid(int CPUInfo
[4], int InfoType
) {
127 asm("xchg %rsi, %rbx\n"
129 "movl %eax, (%rdi)\n"
130 "movl %ebx, 4(%rdi)\n"
131 "movl %ecx, 8(%rdi)\n"
132 "movl %edx, 12(%rdi)\n"
135 : "a"(InfoType
), // %eax
137 : "%ecx", "%edx", "%rsi");
140 # define HAVE_CPU_DETECTION
144 # ifdef HAVE_CPU_DETECTION
145 static inline bool HasCPUIDBit(unsigned int level
, CPUIDRegister reg
,
147 // Check that the level in question is supported.
148 volatile int regs
[4];
149 __cpuid((int*)regs
, level
& 0x80000000u
);
150 if (unsigned(regs
[0]) < level
) return false;
151 __cpuid((int*)regs
, level
);
152 return !!(unsigned(regs
[reg
]) & bit
);
157 #ifdef MOZ_ENABLE_FREETYPE
160 void mozilla_AddRefSharedFTFace(void* aContext
) {
162 static_cast<mozilla::gfx::SharedFTFace
*>(aContext
)->AddRef();
166 void mozilla_ReleaseSharedFTFace(void* aContext
, void* aOwner
) {
168 auto* sharedFace
= static_cast<mozilla::gfx::SharedFTFace
*>(aContext
);
169 sharedFace
->ForgetLockOwner(aOwner
);
170 sharedFace
->Release();
174 void mozilla_ForgetSharedFTFaceLockOwner(void* aContext
, void* aOwner
) {
175 static_cast<mozilla::gfx::SharedFTFace
*>(aContext
)->ForgetLockOwner(aOwner
);
178 int mozilla_LockSharedFTFace(void* aContext
,
179 void* aOwner
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
180 return int(static_cast<mozilla::gfx::SharedFTFace
*>(aContext
)->Lock(aOwner
));
183 void mozilla_UnlockSharedFTFace(void* aContext
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
184 static_cast<mozilla::gfx::SharedFTFace
*>(aContext
)->Unlock();
187 FT_Error
mozilla_LoadFTGlyph(FT_Face aFace
, uint32_t aGlyphIndex
,
189 return mozilla::gfx::Factory::LoadFTGlyph(aFace
, aGlyphIndex
, aFlags
);
192 void mozilla_LockFTLibrary(FT_Library aFTLibrary
) {
193 mozilla::gfx::Factory::LockFTLibrary(aFTLibrary
);
196 void mozilla_UnlockFTLibrary(FT_Library aFTLibrary
) {
197 mozilla::gfx::Factory::UnlockFTLibrary(aFTLibrary
);
202 namespace mozilla::gfx
{
204 #ifdef MOZ_ENABLE_FREETYPE
205 FT_Library
Factory::mFTLibrary
= nullptr;
206 StaticMutex
Factory::mFTLock
;
208 already_AddRefed
<SharedFTFace
> FTUserFontData::CloneFace(int aFaceIndex
) {
210 RefPtr
<SharedFTFace
> face
= Factory::NewSharedFTFaceFromData(
211 nullptr, mFontData
, mLength
, aFaceIndex
, this);
213 (FT_Select_Charmap(face
->GetFace(), FT_ENCODING_UNICODE
) != FT_Err_Ok
&&
214 FT_Select_Charmap(face
->GetFace(), FT_ENCODING_MS_SYMBOL
) !=
218 return face
.forget();
220 FT_Face face
= Factory::NewFTFace(nullptr, mFilename
.c_str(), aFaceIndex
);
222 return MakeAndAddRef
<SharedFTFace
>(face
, this);
229 // Note: mDeviceLock must be held when mutating these values.
230 static uint32_t mDeviceSeq
= 0;
231 StaticRefPtr
<ID3D11Device
> Factory::mD3D11Device
;
232 StaticRefPtr
<ID2D1Device
> Factory::mD2D1Device
;
233 StaticRefPtr
<IDWriteFactory
> Factory::mDWriteFactory
;
234 StaticRefPtr
<ID2D1DeviceContext
> Factory::mMTDC
;
235 StaticRefPtr
<ID2D1DeviceContext
> Factory::mOffMTDC
;
236 bool Factory::mDWriteFactoryInitialized
= false;
237 StaticRefPtr
<IDWriteFontCollection
> Factory::mDWriteSystemFonts
;
238 StaticMutex
Factory::mDeviceLock
;
239 StaticMutex
Factory::mDTDependencyLock
;
242 bool Factory::mBGRSubpixelOrder
= false;
244 mozilla::gfx::Config
* Factory::sConfig
= nullptr;
246 void Factory::Init(const Config
& aConfig
) {
247 MOZ_ASSERT(!sConfig
);
248 sConfig
= new Config(aConfig
);
251 NativeFontResourceMac::RegisterMemoryReporter();
253 NativeFontResource::RegisterMemoryReporter();
257 void Factory::ShutDown() {
259 delete sConfig
->mLogForwarder
;
264 #ifdef MOZ_ENABLE_FREETYPE
265 mFTLibrary
= nullptr;
269 bool Factory::HasSSE2() {
270 #if defined(__SSE2__) || defined(_M_X64) || \
271 (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
272 // gcc with -msse2 (default on OSX and x86-64)
273 // cl.exe with -arch:SSE2 (default on x64 compiler)
275 #elif defined(HAVE_CPU_DETECTION)
280 } sDetectionState
= UNINITIALIZED
;
282 if (sDetectionState
== UNINITIALIZED
) {
283 sDetectionState
= HasCPUIDBit(1u, edx
, (1u << 26)) ? HAS_SSE2
: NO_SSE2
;
285 return sDetectionState
== HAS_SSE2
;
291 bool Factory::HasSSE4() {
292 #if defined(__SSE4__)
293 // gcc with -msse2 (default on OSX and x86-64)
294 // cl.exe with -arch:SSE2 (default on x64 compiler)
296 #elif defined(HAVE_CPU_DETECTION)
301 } sDetectionState
= UNINITIALIZED
;
303 if (sDetectionState
== UNINITIALIZED
) {
304 sDetectionState
= HasCPUIDBit(1u, ecx
, (1u << 19)) ? HAS_SSE4
: NO_SSE4
;
306 return sDetectionState
== HAS_SSE4
;
312 // If the size is "reasonable", we want gfxCriticalError to assert, so
313 // this is the option set up for it.
314 inline int LoggerOptionsBasedOnSize(const IntSize
& aSize
) {
315 return CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize
));
318 bool Factory::ReasonableSurfaceSize(const IntSize
& aSize
) {
319 return Factory::CheckSurfaceSize(aSize
, kReasonableSurfaceSize
);
322 bool Factory::AllowedSurfaceSize(const IntSize
& aSize
) {
324 return Factory::CheckSurfaceSize(aSize
, sConfig
->mMaxTextureSize
,
325 sConfig
->mMaxAllocSize
);
328 return CheckSurfaceSize(aSize
);
331 bool Factory::CheckSurfaceSize(const IntSize
& sz
, int32_t extentLimit
,
332 int32_t allocLimit
) {
333 if (sz
.width
<= 0 || sz
.height
<= 0) {
337 // reject images with sides bigger than limit
338 if (extentLimit
&& (sz
.width
> extentLimit
|| sz
.height
> extentLimit
)) {
339 gfxDebug() << "Surface size too large (exceeds extent limit)!";
343 // assuming 4 bytes per pixel, make sure the allocation size
344 // doesn't overflow a int32_t either
345 CheckedInt
<int32_t> stride
= GetAlignedStride
<16>(sz
.width
, 4);
346 if (!stride
.isValid() || stride
.value() == 0) {
347 gfxDebug() << "Surface size too large (stride overflows int32_t)!";
351 CheckedInt
<int32_t> numBytes
= stride
* sz
.height
;
352 if (!numBytes
.isValid()) {
354 << "Surface size too large (allocation size would overflow int32_t)!";
358 if (allocLimit
&& allocLimit
< numBytes
.value()) {
359 gfxDebug() << "Surface size too large (exceeds allocation limit)!";
366 already_AddRefed
<DrawTarget
> Factory::CreateDrawTarget(BackendType aBackend
,
367 const IntSize
& aSize
,
368 SurfaceFormat aFormat
) {
369 if (!AllowedSurfaceSize(aSize
)) {
370 gfxCriticalError(LoggerOptionsBasedOnSize(aSize
))
371 << "Failed to allocate a surface due to invalid size (CDT) " << aSize
;
375 RefPtr
<DrawTarget
> retVal
;
378 case BackendType::DIRECT2D1_1
: {
379 RefPtr
<DrawTargetD2D1
> newTarget
;
380 newTarget
= new DrawTargetD2D1();
381 if (newTarget
->Init(aSize
, aFormat
)) {
387 case BackendType::SKIA
: {
388 RefPtr
<DrawTargetSkia
> newTarget
;
389 newTarget
= new DrawTargetSkia();
390 if (newTarget
->Init(aSize
, aFormat
)) {
396 case BackendType::CAIRO
: {
397 RefPtr
<DrawTargetCairo
> newTarget
;
398 newTarget
= new DrawTargetCairo();
399 if (newTarget
->Init(aSize
, aFormat
)) {
411 gfxCriticalError(LoggerOptionsBasedOnSize(aSize
))
412 << "Failed to create DrawTarget, Type: " << int(aBackend
)
413 << " Size: " << aSize
;
416 return retVal
.forget();
419 already_AddRefed
<PathBuilder
> Factory::CreatePathBuilder(BackendType aBackend
,
420 FillRule aFillRule
) {
423 case BackendType::DIRECT2D1_1
:
424 return PathBuilderD2D::Create(aFillRule
);
426 case BackendType::SKIA
:
427 case BackendType::WEBGL
:
428 return PathBuilderSkia::Create(aFillRule
);
430 case BackendType::CAIRO
:
431 return PathBuilderCairo::Create(aFillRule
);
434 gfxCriticalNote
<< "Invalid PathBuilder type specified: "
440 already_AddRefed
<PathBuilder
> Factory::CreateSimplePathBuilder() {
441 return CreatePathBuilder(BackendType::SKIA
);
444 already_AddRefed
<DrawTarget
> Factory::CreateRecordingDrawTarget(
445 DrawEventRecorder
* aRecorder
, DrawTarget
* aDT
, IntRect aRect
) {
446 return MakeAndAddRef
<DrawTargetRecording
>(aRecorder
, aDT
, aRect
);
449 already_AddRefed
<DrawTarget
> Factory::CreateDrawTargetForData(
450 BackendType aBackend
, unsigned char* aData
, const IntSize
& aSize
,
451 int32_t aStride
, SurfaceFormat aFormat
, bool aUninitialized
) {
453 if (!AllowedSurfaceSize(aSize
)) {
454 gfxCriticalError(LoggerOptionsBasedOnSize(aSize
))
455 << "Failed to allocate a surface due to invalid size (DTD) " << aSize
;
459 RefPtr
<DrawTarget
> retVal
;
462 case BackendType::SKIA
: {
463 RefPtr
<DrawTargetSkia
> newTarget
;
464 newTarget
= new DrawTargetSkia();
465 if (newTarget
->Init(aData
, aSize
, aStride
, aFormat
, aUninitialized
)) {
471 case BackendType::CAIRO
: {
472 RefPtr
<DrawTargetCairo
> newTarget
;
473 newTarget
= new DrawTargetCairo();
474 if (newTarget
->Init(aData
, aSize
, aStride
, aFormat
)) {
475 retVal
= std::move(newTarget
);
481 gfxCriticalNote
<< "Invalid draw target type specified: "
487 gfxCriticalNote
<< "Failed to create DrawTarget, Type: " << int(aBackend
)
488 << " Size: " << aSize
<< ", Data: " << hexa((void*)aData
)
489 << ", Stride: " << aStride
;
492 return retVal
.forget();
495 already_AddRefed
<DrawTarget
> Factory::CreateOffsetDrawTarget(
496 DrawTarget
* aDrawTarget
, IntPoint aTileOrigin
) {
497 RefPtr
<DrawTargetOffset
> dt
= new DrawTargetOffset();
499 if (!dt
->Init(aDrawTarget
, aTileOrigin
)) {
506 bool Factory::DoesBackendSupportDataDrawtarget(BackendType aType
) {
508 case BackendType::DIRECT2D
:
509 case BackendType::DIRECT2D1_1
:
510 case BackendType::RECORDING
:
511 case BackendType::NONE
:
512 case BackendType::BACKEND_LAST
:
513 case BackendType::WEBRENDER_TEXT
:
514 case BackendType::WEBGL
:
516 case BackendType::CAIRO
:
517 case BackendType::SKIA
:
524 uint32_t Factory::GetMaxSurfaceSize(BackendType aType
) {
526 case BackendType::CAIRO
:
527 return DrawTargetCairo::GetMaxSurfaceSize();
528 case BackendType::SKIA
:
529 return DrawTargetSkia::GetMaxSurfaceSize();
531 case BackendType::DIRECT2D1_1
:
532 return DrawTargetD2D1::GetMaxSurfaceSize();
539 already_AddRefed
<NativeFontResource
> Factory::CreateNativeFontResource(
540 uint8_t* aData
, uint32_t aSize
, FontType aFontType
, void* aFontContext
) {
543 case FontType::DWRITE
:
544 return NativeFontResourceDWrite::Create(aData
, aSize
);
546 return NativeFontResourceGDI::Create(aData
, aSize
);
547 #elif defined(XP_DARWIN)
549 return NativeFontResourceMac::Create(aData
, aSize
);
550 #elif defined(MOZ_WIDGET_GTK)
551 case FontType::FONTCONFIG
:
552 return NativeFontResourceFontconfig::Create(
553 aData
, aSize
, static_cast<FT_Library
>(aFontContext
));
554 #elif defined(MOZ_WIDGET_ANDROID)
555 case FontType::FREETYPE
:
556 return NativeFontResourceFreeType::Create(
557 aData
, aSize
, static_cast<FT_Library
>(aFontContext
));
561 << "Unable to create requested font resource from truetype data";
566 already_AddRefed
<UnscaledFont
> Factory::CreateUnscaledFontFromFontDescriptor(
567 FontType aType
, const uint8_t* aData
, uint32_t aDataLength
,
571 case FontType::DWRITE
:
572 return UnscaledFontDWrite::CreateFromFontDescriptor(aData
, aDataLength
,
575 return UnscaledFontGDI::CreateFromFontDescriptor(aData
, aDataLength
,
577 #elif defined(XP_DARWIN)
579 return UnscaledFontMac::CreateFromFontDescriptor(aData
, aDataLength
,
581 #elif defined(MOZ_WIDGET_GTK)
582 case FontType::FONTCONFIG
:
583 return UnscaledFontFontconfig::CreateFromFontDescriptor(
584 aData
, aDataLength
, aIndex
);
585 #elif defined(MOZ_WIDGET_ANDROID)
586 case FontType::FREETYPE
:
587 return UnscaledFontFreeType::CreateFromFontDescriptor(aData
, aDataLength
,
591 gfxWarning() << "Invalid type specified for UnscaledFont font descriptor";
597 already_AddRefed
<ScaledFont
> Factory::CreateScaledFontForMacFont(
598 CGFontRef aCGFont
, const RefPtr
<UnscaledFont
>& aUnscaledFont
, Float aSize
,
599 bool aUseFontSmoothing
, bool aApplySyntheticBold
, bool aHasColorGlyphs
) {
600 return MakeAndAddRef
<ScaledFontMac
>(aCGFont
, aUnscaledFont
, aSize
, false,
601 aUseFontSmoothing
, aApplySyntheticBold
,
606 #ifdef MOZ_WIDGET_GTK
607 already_AddRefed
<ScaledFont
> Factory::CreateScaledFontForFontconfigFont(
608 const RefPtr
<UnscaledFont
>& aUnscaledFont
, Float aSize
,
609 RefPtr
<SharedFTFace
> aFace
, FcPattern
* aPattern
) {
610 return MakeAndAddRef
<ScaledFontFontconfig
>(std::move(aFace
), aPattern
,
611 aUnscaledFont
, aSize
);
615 #ifdef MOZ_WIDGET_ANDROID
616 already_AddRefed
<ScaledFont
> Factory::CreateScaledFontForFreeTypeFont(
617 const RefPtr
<UnscaledFont
>& aUnscaledFont
, Float aSize
,
618 RefPtr
<SharedFTFace
> aFace
, bool aApplySyntheticBold
) {
619 return MakeAndAddRef
<ScaledFontFreeType
>(std::move(aFace
), aUnscaledFont
,
620 aSize
, aApplySyntheticBold
);
624 void Factory::SetBGRSubpixelOrder(bool aBGR
) { mBGRSubpixelOrder
= aBGR
; }
626 bool Factory::GetBGRSubpixelOrder() { return mBGRSubpixelOrder
; }
628 #ifdef MOZ_ENABLE_FREETYPE
629 SharedFTFace::SharedFTFace(FT_Face aFace
, SharedFTFaceData
* aData
)
632 mLock("SharedFTFace::mLock"),
633 mLastLockOwner(nullptr) {
639 SharedFTFace::~SharedFTFace() {
640 Factory::ReleaseFTFace(mFace
);
642 mData
->ReleaseData();
646 void Factory::SetFTLibrary(FT_Library aFTLibrary
) { mFTLibrary
= aFTLibrary
; }
648 FT_Library
Factory::GetFTLibrary() {
649 MOZ_ASSERT(mFTLibrary
);
653 FT_Library
Factory::NewFTLibrary() {
655 if (FT_Init_FreeType(&library
) != FT_Err_Ok
) {
661 void Factory::ReleaseFTLibrary(FT_Library aFTLibrary
) {
662 FT_Done_FreeType(aFTLibrary
);
665 void Factory::LockFTLibrary(FT_Library aFTLibrary
)
666 MOZ_CAPABILITY_ACQUIRE(mFTLock
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
670 void Factory::UnlockFTLibrary(FT_Library aFTLibrary
)
671 MOZ_CAPABILITY_RELEASE(mFTLock
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
675 FT_Face
Factory::NewFTFace(FT_Library aFTLibrary
, const char* aFileName
,
677 StaticMutexAutoLock
lock(mFTLock
);
679 aFTLibrary
= mFTLibrary
;
682 if (FT_New_Face(aFTLibrary
, aFileName
, aFaceIndex
, &face
) != FT_Err_Ok
) {
688 already_AddRefed
<SharedFTFace
> Factory::NewSharedFTFace(FT_Library aFTLibrary
,
689 const char* aFilename
,
691 FT_Face face
= NewFTFace(aFTLibrary
, aFilename
, aFaceIndex
);
696 RefPtr
<FTUserFontData
> data
;
698 // If the font has variations, we may later need to "clone" it in
699 // UnscaledFontFreeType::CreateScaledFont. To support this, we attach an
700 // FTUserFontData that records the filename used to instantiate the face.
701 if (face
->face_flags
& FT_FACE_FLAG_MULTIPLE_MASTERS
) {
702 data
= new FTUserFontData(aFilename
);
705 return MakeAndAddRef
<SharedFTFace
>(face
, data
);
708 FT_Face
Factory::NewFTFaceFromData(FT_Library aFTLibrary
, const uint8_t* aData
,
709 size_t aDataSize
, int aFaceIndex
) {
710 StaticMutexAutoLock
lock(mFTLock
);
712 aFTLibrary
= mFTLibrary
;
715 if (FT_New_Memory_Face(aFTLibrary
, aData
, aDataSize
, aFaceIndex
, &face
) !=
722 already_AddRefed
<SharedFTFace
> Factory::NewSharedFTFaceFromData(
723 FT_Library aFTLibrary
, const uint8_t* aData
, size_t aDataSize
,
724 int aFaceIndex
, SharedFTFaceData
* aSharedData
) {
726 NewFTFaceFromData(aFTLibrary
, aData
, aDataSize
, aFaceIndex
)) {
727 return MakeAndAddRef
<SharedFTFace
>(face
, aSharedData
);
733 void Factory::ReleaseFTFace(FT_Face aFace
) {
734 StaticMutexAutoLock
lock(mFTLock
);
738 FT_Error
Factory::LoadFTGlyph(FT_Face aFace
, uint32_t aGlyphIndex
,
740 StaticMutexAutoLock
lock(mFTLock
);
741 return FT_Load_Glyph(aFace
, aGlyphIndex
, aFlags
);
745 AutoSerializeWithMoz2D::AutoSerializeWithMoz2D(BackendType aBackendType
) {
747 // We use a multi-threaded ID2D1Factory1, so that makes the calls through the
748 // Direct2D API thread-safe. However, if the Moz2D objects are using Direct3D
749 // resources we need to make sure that calls through the Direct3D or DXGI API
750 // use the Direct2D synchronization. It's possible that this should be pushed
751 // down into the TextureD3D11 objects, so that we always use this.
752 if (aBackendType
== BackendType::DIRECT2D1_1
||
753 aBackendType
== BackendType::DIRECT2D
) {
754 auto factory
= D2DFactory();
756 factory
->QueryInterface(
757 static_cast<ID2D1Multithread
**>(getter_AddRefs(mMT
)));
766 AutoSerializeWithMoz2D::~AutoSerializeWithMoz2D() {
775 already_AddRefed
<DrawTarget
> Factory::CreateDrawTargetForD3D11Texture(
776 ID3D11Texture2D
* aTexture
, SurfaceFormat aFormat
) {
777 MOZ_ASSERT(aTexture
);
779 RefPtr
<DrawTargetD2D1
> newTarget
;
781 newTarget
= new DrawTargetD2D1();
782 if (newTarget
->Init(aTexture
, aFormat
)) {
783 RefPtr
<DrawTarget
> retVal
= newTarget
;
784 return retVal
.forget();
787 gfxWarning() << "Failed to create draw target for D3D11 texture.";
793 bool Factory::SetDirect3D11Device(ID3D11Device
* aDevice
) {
794 MOZ_RELEASE_ASSERT(NS_IsMainThread());
796 // D2DFactory already takes the device lock, so we get the factory before
797 // entering the lock scope.
798 RefPtr
<ID2D1Factory1
> factory
= D2DFactory();
800 StaticMutexAutoLock
lock(mDeviceLock
);
802 mD3D11Device
= aDevice
;
805 mD2D1Device
= nullptr;
814 RefPtr
<IDXGIDevice
> device
;
815 aDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(device
));
817 RefPtr
<ID2D1Device
> d2dDevice
;
818 HRESULT hr
= factory
->CreateDevice(device
, getter_AddRefs(d2dDevice
));
821 << "[D2D1] Failed to create gfx factory's D2D1 device, code: "
824 mD3D11Device
= nullptr;
829 mD2D1Device
= d2dDevice
;
833 RefPtr
<ID3D11Device
> Factory::GetDirect3D11Device() {
834 StaticMutexAutoLock
lock(mDeviceLock
);
838 RefPtr
<ID2D1Device
> Factory::GetD2D1Device(uint32_t* aOutSeqNo
) {
839 StaticMutexAutoLock
lock(mDeviceLock
);
841 *aOutSeqNo
= mDeviceSeq
;
843 return mD2D1Device
.get();
846 bool Factory::HasD2D1Device() { return !!GetD2D1Device(); }
848 RefPtr
<IDWriteFactory
> Factory::GetDWriteFactory() {
849 StaticMutexAutoLock
lock(mDeviceLock
);
850 return mDWriteFactory
;
853 RefPtr
<IDWriteFactory
> Factory::EnsureDWriteFactory() {
854 StaticMutexAutoLock
lock(mDeviceLock
);
856 if (mDWriteFactoryInitialized
) {
857 return mDWriteFactory
;
860 mDWriteFactoryInitialized
= true;
862 HMODULE dwriteModule
= LoadLibrarySystem32(L
"dwrite.dll");
863 decltype(DWriteCreateFactory
)* createDWriteFactory
=
864 (decltype(DWriteCreateFactory
)*)GetProcAddress(dwriteModule
,
865 "DWriteCreateFactory");
867 if (!createDWriteFactory
) {
868 gfxWarning() << "Failed to locate DWriteCreateFactory function.";
873 createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED
, __uuidof(IDWriteFactory
),
874 reinterpret_cast<IUnknown
**>(&mDWriteFactory
));
877 gfxWarning() << "Failed to create DWrite Factory.";
880 return mDWriteFactory
;
883 RefPtr
<IDWriteFontCollection
> Factory::GetDWriteSystemFonts(bool aUpdate
) {
884 StaticMutexAutoLock
lock(mDeviceLock
);
886 if (mDWriteSystemFonts
&& !aUpdate
) {
887 return mDWriteSystemFonts
;
890 if (!mDWriteFactory
) {
891 if ((rand() & 0x3f) == 0) {
892 gfxCriticalError(int(gfx::LogOptions::AssertOnCall
))
893 << "Failed to create DWrite factory";
895 gfxWarning() << "Failed to create DWrite factory";
901 RefPtr
<IDWriteFontCollection
> systemFonts
;
903 mDWriteFactory
->GetSystemFontCollection(getter_AddRefs(systemFonts
));
904 if (FAILED(hr
) || !systemFonts
) {
905 // only crash some of the time so those experiencing this problem
906 // don't stop using Firefox
907 if ((rand() & 0x3f) == 0) {
908 gfxCriticalError(int(gfx::LogOptions::AssertOnCall
))
909 << "Failed to create DWrite system font collection";
911 gfxWarning() << "Failed to create DWrite system font collection";
915 mDWriteSystemFonts
= systemFonts
;
917 return mDWriteSystemFonts
;
920 RefPtr
<ID2D1DeviceContext
> Factory::GetD2DDeviceContext() {
921 StaticRefPtr
<ID2D1DeviceContext
>* ptr
;
923 if (NS_IsMainThread()) {
933 RefPtr
<ID2D1Device
> device
= GetD2D1Device();
939 RefPtr
<ID2D1DeviceContext
> dc
;
940 HRESULT hr
= device
->CreateDeviceContext(
941 D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS
,
945 gfxCriticalError() << "Failed to create global device context";
954 bool Factory::SupportsD2D1() { return !!D2DFactory(); }
956 BYTE sSystemTextQuality
= CLEARTYPE_QUALITY
;
957 void Factory::SetSystemTextQuality(uint8_t aQuality
) {
958 sSystemTextQuality
= aQuality
;
961 uint64_t Factory::GetD2DVRAMUsageDrawTarget() {
962 return DrawTargetD2D1::mVRAMUsageDT
;
965 uint64_t Factory::GetD2DVRAMUsageSourceSurface() {
966 return DrawTargetD2D1::mVRAMUsageSS
;
969 void Factory::D2DCleanup() {
970 StaticMutexAutoLock
lock(mDeviceLock
);
972 mD2D1Device
= nullptr;
974 DrawTargetD2D1::CleanupD2D();
977 already_AddRefed
<ScaledFont
> Factory::CreateScaledFontForDWriteFont(
978 IDWriteFontFace
* aFontFace
, const gfxFontStyle
* aStyle
,
979 const RefPtr
<UnscaledFont
>& aUnscaledFont
, float aSize
,
980 bool aUseEmbeddedBitmap
, bool aUseMultistrikeBold
, bool aGDIForced
) {
981 return MakeAndAddRef
<ScaledFontDWrite
>(
982 aFontFace
, aUnscaledFont
, aSize
, aUseEmbeddedBitmap
, aUseMultistrikeBold
,
986 already_AddRefed
<ScaledFont
> Factory::CreateScaledFontForGDIFont(
987 const void* aLogFont
, const RefPtr
<UnscaledFont
>& aUnscaledFont
,
989 return MakeAndAddRef
<ScaledFontWin
>(static_cast<const LOGFONT
*>(aLogFont
),
990 aUnscaledFont
, aSize
);
994 already_AddRefed
<DrawTarget
> Factory::CreateDrawTargetWithSkCanvas(
996 RefPtr
<DrawTargetSkia
> newTarget
= new DrawTargetSkia();
997 if (!newTarget
->Init(aCanvas
)) {
1000 return newTarget
.forget();
1003 void Factory::PurgeAllCaches() {}
1005 already_AddRefed
<DrawTarget
> Factory::CreateDrawTargetForCairoSurface(
1006 cairo_surface_t
* aSurface
, const IntSize
& aSize
, SurfaceFormat
* aFormat
) {
1007 if (!AllowedSurfaceSize(aSize
)) {
1008 gfxWarning() << "Allowing surface with invalid size (Cairo) " << aSize
;
1011 RefPtr
<DrawTarget
> retVal
;
1014 RefPtr
<DrawTargetCairo
> newTarget
= new DrawTargetCairo();
1016 if (newTarget
->Init(aSurface
, aSize
, aFormat
)) {
1020 return retVal
.forget();
1023 already_AddRefed
<SourceSurface
> Factory::CreateSourceSurfaceForCairoSurface(
1024 cairo_surface_t
* aSurface
, const IntSize
& aSize
, SurfaceFormat aFormat
) {
1025 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
1026 gfxWarning() << "Can't create a SourceSurface without a valid size";
1031 return MakeAndAddRef
<SourceSurfaceCairo
>(aSurface
, aSize
, aFormat
);
1037 already_AddRefed
<DataSourceSurface
> Factory::CreateWrappingDataSourceSurface(
1038 uint8_t* aData
, int32_t aStride
, const IntSize
& aSize
,
1039 SurfaceFormat aFormat
,
1040 SourceSurfaceDeallocator aDeallocator
/* = nullptr */,
1041 void* aClosure
/* = nullptr */) {
1042 // Just check for negative/zero size instead of the full AllowedSurfaceSize()
1043 // - since the data is already allocated we do not need to check for a
1044 // possible overflow - it already worked.
1045 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
1048 if (!aDeallocator
&& aClosure
) {
1054 RefPtr
<SourceSurfaceRawData
> newSurf
= new SourceSurfaceRawData();
1055 newSurf
->InitWrappingData(aData
, aSize
, aStride
, aFormat
, aDeallocator
,
1058 return newSurf
.forget();
1061 already_AddRefed
<DataSourceSurface
> Factory::CreateDataSourceSurface(
1062 const IntSize
& aSize
, SurfaceFormat aFormat
, bool aZero
) {
1063 if (!AllowedSurfaceSize(aSize
)) {
1064 gfxCriticalError(LoggerOptionsBasedOnSize(aSize
))
1065 << "Failed to allocate a surface due to invalid size (DSS) " << aSize
;
1069 // Skia doesn't support RGBX, so memset RGBX to 0xFF
1070 bool clearSurface
= aZero
|| aFormat
== SurfaceFormat::B8G8R8X8
;
1071 uint8_t clearValue
= aFormat
== SurfaceFormat::B8G8R8X8
? 0xFF : 0;
1073 RefPtr
<SourceSurfaceAlignedRawData
> newSurf
=
1074 new SourceSurfaceAlignedRawData();
1075 if (newSurf
->Init(aSize
, aFormat
, clearSurface
, clearValue
)) {
1076 return newSurf
.forget();
1079 gfxWarning() << "CreateDataSourceSurface failed in init";
1083 already_AddRefed
<DataSourceSurface
> Factory::CreateDataSourceSurfaceWithStride(
1084 const IntSize
& aSize
, SurfaceFormat aFormat
, int32_t aStride
, bool aZero
) {
1085 if (!AllowedSurfaceSize(aSize
) ||
1086 aStride
< aSize
.width
* BytesPerPixel(aFormat
)) {
1087 gfxCriticalError(LoggerOptionsBasedOnSize(aSize
))
1088 << "CreateDataSourceSurfaceWithStride failed with bad stride "
1089 << aStride
<< ", " << aSize
<< ", " << aFormat
;
1093 // Skia doesn't support RGBX, so memset RGBX to 0xFF
1094 bool clearSurface
= aZero
|| aFormat
== SurfaceFormat::B8G8R8X8
;
1095 uint8_t clearValue
= aFormat
== SurfaceFormat::B8G8R8X8
? 0xFF : 0;
1097 RefPtr
<SourceSurfaceAlignedRawData
> newSurf
=
1098 new SourceSurfaceAlignedRawData();
1099 if (newSurf
->Init(aSize
, aFormat
, clearSurface
, clearValue
, aStride
)) {
1100 return newSurf
.forget();
1103 gfxCriticalError(LoggerOptionsBasedOnSize(aSize
))
1104 << "CreateDataSourceSurfaceWithStride failed to initialize " << aSize
1105 << ", " << aFormat
<< ", " << aStride
<< ", " << aZero
;
1109 already_AddRefed
<DataSourceSurface
> Factory::CopyDataSourceSurface(
1110 DataSourceSurface
* aSource
) {
1111 // Don't worry too much about speed.
1112 MOZ_ASSERT(aSource
->GetFormat() == SurfaceFormat::R8G8B8A8
||
1113 aSource
->GetFormat() == SurfaceFormat::R8G8B8X8
||
1114 aSource
->GetFormat() == SurfaceFormat::B8G8R8A8
||
1115 aSource
->GetFormat() == SurfaceFormat::B8G8R8X8
);
1117 DataSourceSurface::ScopedMap
srcMap(aSource
, DataSourceSurface::READ
);
1118 if (NS_WARN_IF(!srcMap
.IsMapped())) {
1119 MOZ_ASSERT_UNREACHABLE("CopyDataSourceSurface: Failed to map surface.");
1123 IntSize size
= aSource
->GetSize();
1124 SurfaceFormat format
= aSource
->GetFormat();
1126 RefPtr
<DataSourceSurface
> dst
= CreateDataSourceSurfaceWithStride(
1127 size
, format
, srcMap
.GetStride(), /* aZero */ false);
1128 if (NS_WARN_IF(!dst
)) {
1132 DataSourceSurface::ScopedMap
dstMap(dst
, DataSourceSurface::WRITE
);
1133 if (NS_WARN_IF(!dstMap
.IsMapped())) {
1134 MOZ_ASSERT_UNREACHABLE("CopyDataSourceSurface: Failed to map surface.");
1138 SwizzleData(srcMap
.GetData(), srcMap
.GetStride(), format
, dstMap
.GetData(),
1139 dstMap
.GetStride(), format
, size
);
1140 return dst
.forget();
1143 void Factory::CopyDataSourceSurface(DataSourceSurface
* aSource
,
1144 DataSourceSurface
* aDest
) {
1145 // Don't worry too much about speed.
1146 MOZ_ASSERT(aSource
->GetSize() == aDest
->GetSize());
1147 MOZ_ASSERT(aSource
->GetFormat() == SurfaceFormat::R8G8B8A8
||
1148 aSource
->GetFormat() == SurfaceFormat::R8G8B8X8
||
1149 aSource
->GetFormat() == SurfaceFormat::B8G8R8A8
||
1150 aSource
->GetFormat() == SurfaceFormat::B8G8R8X8
);
1151 MOZ_ASSERT(aDest
->GetFormat() == SurfaceFormat::R8G8B8A8
||
1152 aDest
->GetFormat() == SurfaceFormat::R8G8B8X8
||
1153 aDest
->GetFormat() == SurfaceFormat::B8G8R8A8
||
1154 aDest
->GetFormat() == SurfaceFormat::B8G8R8X8
||
1155 aDest
->GetFormat() == SurfaceFormat::R5G6B5_UINT16
);
1157 DataSourceSurface::MappedSurface srcMap
;
1158 DataSourceSurface::MappedSurface destMap
;
1159 if (!aSource
->Map(DataSourceSurface::MapType::READ
, &srcMap
) ||
1160 !aDest
->Map(DataSourceSurface::MapType::WRITE
, &destMap
)) {
1161 MOZ_ASSERT(false, "CopyDataSourceSurface: Failed to map surface.");
1165 SwizzleData(srcMap
.mData
, srcMap
.mStride
, aSource
->GetFormat(), destMap
.mData
,
1166 destMap
.mStride
, aDest
->GetFormat(), aSource
->GetSize());
1175 already_AddRefed
<DataSourceSurface
>
1176 Factory::CreateBGRA8DataSourceSurfaceForD3D11Texture(
1177 ID3D11Texture2D
* aSrcTexture
, uint32_t aArrayIndex
) {
1178 D3D11_TEXTURE2D_DESC srcDesc
= {0};
1179 aSrcTexture
->GetDesc(&srcDesc
);
1181 RefPtr
<gfx::DataSourceSurface
> destTexture
=
1182 gfx::Factory::CreateDataSourceSurface(
1183 IntSize(srcDesc
.Width
, srcDesc
.Height
), gfx::SurfaceFormat::B8G8R8A8
);
1184 if (NS_WARN_IF(!destTexture
)) {
1187 if (!ReadbackTexture(destTexture
, aSrcTexture
, aArrayIndex
)) {
1190 return destTexture
.forget();
1193 /* static */ nsresult
Factory::CreateSdbForD3D11Texture(
1194 ID3D11Texture2D
* aSrcTexture
, const IntSize
& aSrcSize
,
1195 layers::SurfaceDescriptorBuffer
& aSdBuffer
,
1196 const std::function
<layers::MemoryOrShmem(uint32_t)>& aAllocate
) {
1197 D3D11_TEXTURE2D_DESC srcDesc
= {0};
1198 aSrcTexture
->GetDesc(&srcDesc
);
1199 if (srcDesc
.Width
!= uint32_t(aSrcSize
.width
) ||
1200 srcDesc
.Height
!= uint32_t(aSrcSize
.height
) ||
1201 srcDesc
.Format
!= DXGI_FORMAT_B8G8R8A8_UNORM
) {
1202 return NS_ERROR_NOT_IMPLEMENTED
;
1205 const auto format
= gfx::SurfaceFormat::B8G8R8A8
;
1206 uint8_t* buffer
= nullptr;
1208 nsresult rv
= layers::Image::AllocateSurfaceDescriptorBufferRgb(
1209 aSrcSize
, format
, buffer
, aSdBuffer
, stride
, aAllocate
);
1210 if (NS_WARN_IF(NS_FAILED(rv
))) {
1214 if (!ReadbackTexture(buffer
, stride
, aSrcTexture
)) {
1215 return NS_ERROR_FAILURE
;
1222 template <typename DestTextureT
>
1223 bool Factory::ConvertSourceAndRetryReadback(DestTextureT
* aDestCpuTexture
,
1224 ID3D11Texture2D
* aSrcTexture
,
1225 uint32_t aArrayIndex
) {
1226 RefPtr
<ID3D11Device
> device
;
1227 aSrcTexture
->GetDevice(getter_AddRefs(device
));
1229 gfxWarning() << "Failed to get D3D11 device from source texture";
1233 nsAutoCString error
;
1234 std::unique_ptr
<DXVA2Manager
> manager(
1235 DXVA2Manager::CreateD3D11DXVA(nullptr, error
, device
));
1237 gfxWarning() << "Failed to create DXVA2 manager!";
1241 RefPtr
<ID3D11Texture2D
> newSrcTexture
;
1242 HRESULT hr
= manager
->CopyToBGRATexture(aSrcTexture
, aArrayIndex
,
1243 getter_AddRefs(newSrcTexture
));
1245 gfxWarning() << "Failed to copy to BGRA texture.";
1249 return ReadbackTexture(aDestCpuTexture
, newSrcTexture
);
1253 bool Factory::ReadbackTexture(layers::TextureData
* aDestCpuTexture
,
1254 ID3D11Texture2D
* aSrcTexture
) {
1255 layers::MappedTextureData mappedData
;
1256 if (!aDestCpuTexture
->BorrowMappedData(mappedData
)) {
1257 gfxWarning() << "Could not access in-memory texture";
1261 D3D11_TEXTURE2D_DESC srcDesc
= {0};
1262 aSrcTexture
->GetDesc(&srcDesc
);
1264 // Special case: If the source and destination have different formats and the
1265 // destination is B8G8R8A8 then convert the source to B8G8R8A8 and readback.
1266 if ((srcDesc
.Format
!= DXGIFormat(mappedData
.format
)) &&
1267 (mappedData
.format
== SurfaceFormat::B8G8R8A8
)) {
1268 return ConvertSourceAndRetryReadback(aDestCpuTexture
, aSrcTexture
);
1271 if ((IntSize(srcDesc
.Width
, srcDesc
.Height
) != mappedData
.size
) ||
1272 (srcDesc
.Format
!= DXGIFormat(mappedData
.format
))) {
1273 gfxWarning() << "Attempted readback between incompatible textures";
1277 return ReadbackTexture(mappedData
.data
, mappedData
.stride
, aSrcTexture
);
1281 bool Factory::ReadbackTexture(DataSourceSurface
* aDestCpuTexture
,
1282 ID3D11Texture2D
* aSrcTexture
,
1283 uint32_t aArrayIndex
) {
1284 D3D11_TEXTURE2D_DESC srcDesc
= {0};
1285 aSrcTexture
->GetDesc(&srcDesc
);
1287 // Special case: If the source and destination have different formats and the
1288 // destination is B8G8R8A8 then convert the source to B8G8R8A8 and readback.
1289 if ((srcDesc
.Format
!= DXGIFormat(aDestCpuTexture
->GetFormat())) &&
1290 (aDestCpuTexture
->GetFormat() == SurfaceFormat::B8G8R8A8
)) {
1291 return ConvertSourceAndRetryReadback(aDestCpuTexture
, aSrcTexture
,
1295 if ((IntSize(srcDesc
.Width
, srcDesc
.Height
) != aDestCpuTexture
->GetSize()) ||
1296 (srcDesc
.Format
!= DXGIFormat(aDestCpuTexture
->GetFormat()))) {
1297 gfxWarning() << "Attempted readback between incompatible textures";
1301 gfx::DataSourceSurface::MappedSurface mappedSurface
;
1302 if (!aDestCpuTexture
->Map(gfx::DataSourceSurface::WRITE
, &mappedSurface
)) {
1306 MOZ_ASSERT(aArrayIndex
== 0);
1309 ReadbackTexture(mappedSurface
.mData
, mappedSurface
.mStride
, aSrcTexture
);
1310 aDestCpuTexture
->Unmap();
1315 bool Factory::ReadbackTexture(uint8_t* aDestData
, int32_t aDestStride
,
1316 ID3D11Texture2D
* aSrcTexture
) {
1317 MOZ_ASSERT(aDestData
&& aDestStride
&& aSrcTexture
);
1319 RefPtr
<ID3D11Device
> device
;
1320 aSrcTexture
->GetDevice(getter_AddRefs(device
));
1322 gfxWarning() << "Failed to get D3D11 device from source texture";
1326 RefPtr
<ID3D11DeviceContext
> context
;
1327 device
->GetImmediateContext(getter_AddRefs(context
));
1329 gfxWarning() << "Could not get an immediate D3D11 context";
1333 RefPtr
<IDXGIKeyedMutex
> mutex
;
1334 HRESULT hr
= aSrcTexture
->QueryInterface(__uuidof(IDXGIKeyedMutex
),
1335 (void**)getter_AddRefs(mutex
));
1336 if (SUCCEEDED(hr
) && mutex
) {
1337 hr
= mutex
->AcquireSync(0, 2000);
1339 gfxWarning() << "Could not acquire DXGI surface lock in 2 seconds";
1344 D3D11_TEXTURE2D_DESC srcDesc
= {0};
1345 aSrcTexture
->GetDesc(&srcDesc
);
1346 srcDesc
.CPUAccessFlags
= D3D11_CPU_ACCESS_READ
;
1347 srcDesc
.Usage
= D3D11_USAGE_STAGING
;
1348 srcDesc
.BindFlags
= 0;
1349 srcDesc
.MiscFlags
= 0;
1350 srcDesc
.MipLevels
= 1;
1351 RefPtr
<ID3D11Texture2D
> srcCpuTexture
;
1353 device
->CreateTexture2D(&srcDesc
, nullptr, getter_AddRefs(srcCpuTexture
));
1355 gfxWarning() << "Could not create source texture for mapping";
1357 mutex
->ReleaseSync(0);
1362 context
->CopyResource(srcCpuTexture
, aSrcTexture
);
1365 mutex
->ReleaseSync(0);
1369 D3D11_MAPPED_SUBRESOURCE srcMap
;
1370 hr
= context
->Map(srcCpuTexture
, 0, D3D11_MAP_READ
, 0, &srcMap
);
1372 gfxWarning() << "Could not map source texture";
1376 uint32_t width
= srcDesc
.Width
;
1377 uint32_t height
= srcDesc
.Height
;
1378 int bpp
= BytesPerPixel(gfx::ToPixelFormat(srcDesc
.Format
));
1379 for (uint32_t y
= 0; y
< height
; y
++) {
1380 memcpy(aDestData
+ aDestStride
* y
,
1381 (unsigned char*)(srcMap
.pData
) + srcMap
.RowPitch
* y
, width
* bpp
);
1384 context
->Unmap(srcCpuTexture
, 0);
1391 void CriticalLogger::OutputMessage(const std::string
& aString
, int aLevel
,
1393 if (Factory::GetLogForwarder()) {
1394 Factory::GetLogForwarder()->Log(aString
);
1397 BasicLogger::OutputMessage(aString
, aLevel
, aNoNewline
);
1400 void CriticalLogger::CrashAction(LogReason aReason
) {
1401 if (Factory::GetLogForwarder()) {
1402 Factory::GetLogForwarder()->CrashAction(aReason
);
1407 void LogWStr(const wchar_t* aWStr
, std::stringstream
& aOut
) {
1409 WideCharToMultiByte(CP_ACP
, 0, aWStr
, -1, nullptr, 0, nullptr, nullptr);
1411 std::vector
<char> str(n
);
1412 WideCharToMultiByte(CP_ACP
, 0, aWStr
, -1, str
.data(), n
, nullptr, nullptr);
1418 } // namespace mozilla::gfx