diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index 298bbc581..21d008705 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -159,25 +159,135 @@ public: } }; +class COMWeakRegistration final + : public IInitializeSpy +{ +public: + explicit COMWeakRegistration (void (*cleanup)(void)) noexcept + : cleanup (cleanup) + { + HRESULT hr; + hr = CoRegisterInitializeSpy (this, ®istration_id); + if (FAILED (hr)) { + + } + else { + registered = true; + } + } + + void Register () + { + + } + + void Unregister () + { + + } + + void EnsureApartment () + { + HRESULT hr = CoInitializeEx (NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); + if (FAILED (hr)) { + if (hr != RPC_E_CHANGED_MODE) { + _cairo (); + } + + } + else { + } + } + +public: + IFACEMETHOD (QueryInterface) (REFIID riid, void** ppv) noexcept override + { + if (!ppv) + return E_POINTER; + + if (riid == IID_IUnknown || riid == IID_IInitializeSpy) { + *ppv = this; + AddRef (); + return S_OK; + } + + *ppv = nullptr; + return E_NOINTERFACE; + } + + IFACEMETHOD_(ULONG, AddRef) () noexcept override + { + return 2; + } + + IFACEMETHOD_(ULONG, Release) () noexcept override + { + return 1; + } + + IFACEMETHOD (PreInitialize) (DWORD dwCoInit, DWORD dwCurThreadAptRefs) noexcept override + { + bool user_requested_sta = !!(dwCoInit & COINIT_APARTMENTTHREADED); + bool cairo_holds_last_reference = (dwCurThreadAptRefs == 1); + + if (user_requested_sta && cairo_holds_last_reference) { + cleanup (); + CoUninitialize(); + } + + return S_OK; + } + + IFACEMETHOD (PreInitialize) (DWORD dwCoInit, DWORD dwCurThreadAptRefs) noexcept override + { + return S_OK; + } + + IFACEMETHOD (PreInitialize) (DWORD dwCoInit, DWORD dwCurThreadAptRefs) noexcept override + { + return S_OK; + } + + IFACEMETHOD (PreInitialize) (DWORD dwCoInit, DWORD dwCurThreadAptRefs) noexcept override + { + return S_OK; + } + +private: + void (* cleanup) (void); + ULARGE_INTEGER registration_id { 0 }; + bool registered { false }; + bool have_reference { false }; +}; + class WICImagingFactory { public: static RefPtr Instance() { - if (!mFactoryInstance) { - CoInitialize(NULL); - CoCreateInstance(CLSID_WICImagingFactory, - NULL, - CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&mFactoryInstance)); - } - return mFactoryInstance; - } -private: - static RefPtr mFactoryInstance; -}; + cairo_win32_thread_data_t *thread_data = cairo_win32_thread_data_get (); -RefPtr WICImagingFactory::mFactoryInstance; + if (!thread_data->wic_factory) { + if (!thread_data->com_observer) { + thread_data->com_observer = new (std::nothrow) COMObserver (); + } + thread_data->com_observer->EnsureApartment (); + + CoInitializeEx (NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); + hr = CoCreateInstance (CLSID_WICImagingFactory, + NULL, + CLSCTX_INPROC_SERVER, + IID_PPV_ARGS (&thread_data->wic_factory)); + if (FAILED (hr)) { + _cairo_win32_api_error_fatal_with_hr (hr, "%s (%s)", + "CoCreateInstance", + "CLSID_WICImagingFactory"); + } + } + + return thread_data->wic_factory; + } +}; cairo_atomic_once_t DWriteFactory::mOnceFactories = CAIRO_ATOMIC_ONCE_INIT; RefPtr DWriteFactory::mFactoryInstance; @@ -186,6 +296,7 @@ RefPtr DWriteFactory::mFactoryInstance2; RefPtr DWriteFactory::mFactoryInstance3; RefPtr DWriteFactory::mFactoryInstance4; RefPtr DWriteFactory::mFactoryInstance8; + cairo_atomic_once_t DWriteFactory::mOnceSystemCollection = CAIRO_ATOMIC_ONCE_INIT; RefPtr DWriteFactory::mSystemCollection; diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h index 4d11d57ab..d13f99a8d 100644 --- a/src/win32/cairo-win32-private.h +++ b/src/win32/cairo-win32-private.h @@ -181,6 +181,9 @@ _cairo_win32_gdi_compositor_get (void); cairo_status_t _cairo_win32_print_api_error (const char *context, const char *api); +cairo_status_t +_cairo_win32_api_error_fatal (const char *format, ...); + cairo_bool_t _cairo_surface_is_win32 (const cairo_surface_t *surface); @@ -249,6 +252,8 @@ cairo_win32_async_com_release (IUnknown *iface_ptr); #if CAIRO_HAS_DWRITE_FONT interface ID2D1Factory; +interface IWICImagingFactory; +struct COMObserver; #endif typedef struct { @@ -256,6 +261,8 @@ typedef struct { #if CAIRO_HAS_DWRITE_FONT interface ID2D1Factory *d2d1_factory; + interface IWICImagingFactory *wic_factory; + struct COMObserver *com_observer; #endif cairo_bool_t added_to_list; diff --git a/src/win32/cairo-win32-thread-data.c b/src/win32/cairo-win32-thread-data.c index 5025dd0fc..c7cf4f60b 100644 --- a/src/win32/cairo-win32-thread-data.c +++ b/src/win32/cairo-win32-thread-data.c @@ -100,6 +100,9 @@ thread_data_free (cairo_win32_thread_data_t *data) */ cairo_win32_async_stdcall_free ((stdcall_free_func_t) DeleteDC, data->hdc); + if (data->com_observer) + data->com_observer->Detach (); + #if CAIRO_HAS_DWRITE_FONT cairo_win32_async_com_release ((IUnknown*) data->d2d1_factory); #endif