mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-15 16:08:05 +02:00
Merge branch 'win32-thread-data' into 'master'
DWrite font backend thread-safety Closes #886, #952, and #953 See merge request cairo/cairo!650
This commit is contained in:
commit
0a547172dd
8 changed files with 673 additions and 276 deletions
|
|
@ -514,6 +514,19 @@ _cairo_atomic_init_once_leave(cairo_atomic_once_t *once)
|
|||
}
|
||||
}
|
||||
|
||||
static cairo_always_inline cairo_bool_t
|
||||
_cairo_atomic_init_once_check(cairo_atomic_once_t *once)
|
||||
{
|
||||
BOOL pending;
|
||||
|
||||
if (InitOnceBeginInitialize (once, INIT_ONCE_CHECK_ONLY, &pending, NULL)) {
|
||||
assert (!pending);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
typedef cairo_atomic_int_t cairo_atomic_once_t;
|
||||
|
|
@ -547,6 +560,19 @@ _cairo_atomic_init_once_leave(cairo_atomic_once_t *once)
|
|||
assert (0 && "incorrect use of _cairo_atomic_init_once API (once != CAIRO_ATOMIC_ONCE_INITIALIZING)");
|
||||
}
|
||||
|
||||
static cairo_always_inline cairo_bool_t
|
||||
_cairo_atomic_init_once_check(cairo_atomic_once_t *once)
|
||||
{
|
||||
int val = _cairo_atomic_int_get(once);
|
||||
|
||||
if (unlikely(val == CAIRO_ATOMIC_ONCE_INITIALIZING))
|
||||
assert (0 && "incorrect use of _cairo_atomic_init_check API (once == CAIRO_ATOMIC_ONCE_INITIALIZING)");
|
||||
|
||||
assert (val == CAIRO_ATOMIC_ONCE_UNINITIALIZED || val == CAIRO_ATOMIC_ONCE_INITIALIZED);
|
||||
|
||||
return val == CAIRO_ATOMIC_ONCE_INITIALIZED;
|
||||
}
|
||||
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
CAIRO_END_DECLS
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ cairo_feature_sources = {
|
|||
'win32/cairo-win32-surface.c',
|
||||
'win32/cairo-win32-display-surface.c',
|
||||
'win32/cairo-win32-printing-surface.c',
|
||||
'win32/cairo-win32-thread-data.c',
|
||||
],
|
||||
'cairo-win32-font': [
|
||||
'win32/cairo-win32-font.c',
|
||||
|
|
|
|||
|
|
@ -71,13 +71,6 @@
|
|||
* Since: 1.18
|
||||
**/
|
||||
|
||||
typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
|
||||
D2D1_FACTORY_TYPE factoryType,
|
||||
REFIID iid,
|
||||
CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
|
||||
void **factory
|
||||
);
|
||||
|
||||
#define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS
|
||||
|
||||
// Forward declarations
|
||||
|
|
@ -131,68 +124,44 @@ _cairo_dwrite_error (HRESULT hr, const char *context)
|
|||
class D2DFactory
|
||||
{
|
||||
public:
|
||||
static RefPtr<ID2D1Factory> Instance()
|
||||
static ID2D1Factory *
|
||||
Instance()
|
||||
{
|
||||
if (!mFactoryInstance) {
|
||||
/* According to MSDN, using independent, single-threaded D2D1 factories
|
||||
* in each thread is the most scalable solution.
|
||||
*/
|
||||
cairo_win32_thread_data_t *thread_data = cairo_win32_thread_data_get ();
|
||||
|
||||
if (!thread_data->d2d1_factory) {
|
||||
typedef HRESULT
|
||||
(WINAPI *pD2D1CreateFactory_t) (D2D1_FACTORY_TYPE factoryType,
|
||||
REFIID iid,
|
||||
CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
|
||||
void **factory);
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
#endif
|
||||
/* TODO */
|
||||
HMODULE d2d1 = _cairo_win32_load_library_from_system32 (L"d2d1.dll");
|
||||
D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc)
|
||||
GetProcAddress(d2d1, "D2D1CreateFactory");
|
||||
pD2D1CreateFactory_t pD2D1CreateFactory = (pD2D1CreateFactory_t)
|
||||
GetProcAddress (d2d1, "D2D1CreateFactory");
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
if (createD2DFactory) {
|
||||
D2D1_FACTORY_OPTIONS options;
|
||||
options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
|
||||
createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
|
||||
__uuidof(ID2D1Factory),
|
||||
&options,
|
||||
(void**)&mFactoryInstance);
|
||||
}
|
||||
}
|
||||
return mFactoryInstance;
|
||||
/* D2D1 is based on nano-COM (just like DWrite), so there's no need
|
||||
* to ensure an apartment with CoInitializeEx or the implicit MTA.
|
||||
*/
|
||||
D2D1_FACTORY_OPTIONS options { D2D1_DEBUG_LEVEL_NONE };
|
||||
HRESULT hr = pD2D1CreateFactory (D2D1_FACTORY_TYPE_SINGLE_THREADED,
|
||||
__uuidof (ID2D1Factory),
|
||||
&options,
|
||||
(void**) &thread_data->d2d1_factory);
|
||||
assert (SUCCEEDED (hr));
|
||||
}
|
||||
|
||||
return thread_data->d2d1_factory;
|
||||
}
|
||||
|
||||
static RefPtr<IDWriteFactory4> Instance4()
|
||||
{
|
||||
if (!mFactoryInstance4) {
|
||||
if (Instance()) {
|
||||
Instance()->QueryInterface(&mFactoryInstance4);
|
||||
}
|
||||
}
|
||||
return mFactoryInstance4;
|
||||
}
|
||||
|
||||
static RefPtr<ID2D1DCRenderTarget> RenderTarget()
|
||||
{
|
||||
if (!mRenderTarget) {
|
||||
if (!Instance()) {
|
||||
return NULL;
|
||||
}
|
||||
// Create a DC render target.
|
||||
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
|
||||
D2D1_RENDER_TARGET_TYPE_DEFAULT,
|
||||
D2D1::PixelFormat(
|
||||
DXGI_FORMAT_B8G8R8A8_UNORM,
|
||||
D2D1_ALPHA_MODE_PREMULTIPLIED),
|
||||
0,
|
||||
0,
|
||||
D2D1_RENDER_TARGET_USAGE_NONE,
|
||||
D2D1_FEATURE_LEVEL_DEFAULT
|
||||
);
|
||||
|
||||
Instance()->CreateDCRenderTarget(&props, &mRenderTarget);
|
||||
}
|
||||
return mRenderTarget;
|
||||
}
|
||||
|
||||
private:
|
||||
static RefPtr<ID2D1Factory> mFactoryInstance;
|
||||
static RefPtr<IDWriteFactory4> mFactoryInstance4;
|
||||
static RefPtr<ID2D1DCRenderTarget> mRenderTarget;
|
||||
};
|
||||
|
||||
class WICImagingFactory
|
||||
|
|
@ -200,41 +169,61 @@ class WICImagingFactory
|
|||
public:
|
||||
static RefPtr<IWICImagingFactory> Instance()
|
||||
{
|
||||
if (!mFactoryInstance) {
|
||||
CoInitialize(NULL);
|
||||
CoCreateInstance(CLSID_WICImagingFactory,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&mFactoryInstance));
|
||||
}
|
||||
return mFactoryInstance;
|
||||
HRESULT hr;
|
||||
|
||||
/* WIC is based on true COM, so we need to set this thread
|
||||
* on a COM apartment. Usually one calls CoInitialize and
|
||||
* call it a day, however this is used on threads we don't
|
||||
* own. We can use whatever apartment the user has already
|
||||
* initialized, but if there's no apartment we don't want
|
||||
* to force the thread to a specific apartment type. Turns
|
||||
* out implicit MTA is perfect for this; we have to take
|
||||
* a reference on the MTA however, otherwise it can disappear
|
||||
* at any time in the middle of our operations.
|
||||
*
|
||||
* Note: WICImagingFactory has threading model 'both', so
|
||||
* the object will be accessed directly (no marshaling)
|
||||
* regardless of the apartment type.
|
||||
*/
|
||||
cairo_win32_ensure_mta ();
|
||||
|
||||
IWICImagingFactory *wic_factory;
|
||||
hr = CoCreateInstance (CLSID_WICImagingFactory,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS (&wic_factory));
|
||||
if (FAILED (hr)) {
|
||||
assert (0 && "CoCreateInstance (CLSID_WICImagingFactory) failed");
|
||||
}
|
||||
|
||||
return wic_factory;
|
||||
}
|
||||
private:
|
||||
static RefPtr<IWICImagingFactory> mFactoryInstance;
|
||||
};
|
||||
|
||||
|
||||
RefPtr<IDWriteFactory> DWriteFactory::mFactoryInstance;
|
||||
RefPtr<IDWriteFactory1> DWriteFactory::mFactoryInstance1;
|
||||
RefPtr<IDWriteFactory2> DWriteFactory::mFactoryInstance2;
|
||||
RefPtr<IDWriteFactory3> DWriteFactory::mFactoryInstance3;
|
||||
RefPtr<IDWriteFactory4> DWriteFactory::mFactoryInstance4;
|
||||
RefPtr<IDWriteFactory8> DWriteFactory::mFactoryInstance8;
|
||||
|
||||
RefPtr<IWICImagingFactory> WICImagingFactory::mFactoryInstance;
|
||||
RefPtr<IDWriteFontCollection> DWriteFactory::mSystemCollection;
|
||||
RefPtr<IDWriteRenderingParams> DWriteFactory::mDefaultRenderingParams;
|
||||
|
||||
RefPtr<ID2D1Factory> D2DFactory::mFactoryInstance;
|
||||
RefPtr<ID2D1DCRenderTarget> D2DFactory::mRenderTarget;
|
||||
cairo_atomic_once_t DWriteFactory::mOnceFactories = CAIRO_ATOMIC_ONCE_INIT;
|
||||
IDWriteFactory *DWriteFactory::mFactoryInstance;
|
||||
IDWriteFactory1 *DWriteFactory::mFactoryInstance1;
|
||||
IDWriteFactory2 *DWriteFactory::mFactoryInstance2;
|
||||
IDWriteFactory3 *DWriteFactory::mFactoryInstance3;
|
||||
IDWriteFactory4 *DWriteFactory::mFactoryInstance4;
|
||||
IDWriteFactory8 *DWriteFactory::mFactoryInstance8;
|
||||
cairo_atomic_once_t DWriteFactory::mOnceSystemCollection = CAIRO_ATOMIC_ONCE_INIT;
|
||||
IDWriteFontCollection *DWriteFactory::mSystemCollection;
|
||||
|
||||
static RefPtr<IDWriteRenderingParams>
|
||||
_create_rendering_params(IDWriteRenderingParams *params,
|
||||
const cairo_font_options_t *options,
|
||||
cairo_antialias_t antialias)
|
||||
{
|
||||
if (!params)
|
||||
params = DWriteFactory::DefaultRenderingParams();
|
||||
RefPtr<IDWriteRenderingParams> default_rendering_params;
|
||||
HRESULT hr;
|
||||
|
||||
if (!params) {
|
||||
hr = DWriteFactory::Instance()->CreateRenderingParams(&default_rendering_params);
|
||||
assert(SUCCEEDED(hr));
|
||||
params = default_rendering_params.get();
|
||||
}
|
||||
|
||||
FLOAT gamma = params->GetGamma();
|
||||
FLOAT enhanced_contrast = params->GetEnhancedContrast();
|
||||
FLOAT clear_type_level = params->GetClearTypeLevel();
|
||||
|
|
@ -279,7 +268,6 @@ _create_rendering_params(IDWriteRenderingParams *params,
|
|||
if (!modified)
|
||||
return params;
|
||||
|
||||
HRESULT hr;
|
||||
RefPtr<IDWriteRenderingParams1> params1;
|
||||
hr = params->QueryInterface(¶ms1);
|
||||
if (FAILED(hr)) {
|
||||
|
|
@ -1161,7 +1149,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s
|
|||
if (scaled_font->base.options.palette_index < palette_count)
|
||||
palette_index = scaled_font->base.options.palette_index;
|
||||
|
||||
if (DWriteFactory::Instance8().get()) {
|
||||
if (DWriteFactory::Instance8()) {
|
||||
hr = DWriteFactory::Instance8()->TranslateColorGlyphRun(
|
||||
origin,
|
||||
&run,
|
||||
|
|
@ -1174,7 +1162,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s
|
|||
palette_index,
|
||||
&run_enumerator);
|
||||
}
|
||||
else if (DWriteFactory::Instance4().get()) {
|
||||
else if (DWriteFactory::Instance4()) {
|
||||
hr = DWriteFactory::Instance4()->TranslateColorGlyphRun(
|
||||
origin,
|
||||
&run,
|
||||
|
|
@ -2202,52 +2190,6 @@ _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface,
|
|||
return CAIRO_INT_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
cairo_int_status_t
|
||||
_dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface,
|
||||
DWRITE_MATRIX *transform,
|
||||
DWRITE_GLYPH_RUN *run,
|
||||
COLORREF color,
|
||||
const RECT &area)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
RefPtr<ID2D1DCRenderTarget> rt = D2DFactory::RenderTarget();
|
||||
|
||||
// XXX don't we need to set RenderingParams on this RenderTarget?
|
||||
|
||||
hr = rt->BindDC(surface->dc, &area);
|
||||
if (FAILED(hr))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
// D2D uses 0x00RRGGBB not 0x00BBGGRR like COLORREF.
|
||||
color = (color & 0xFF) << 16 |
|
||||
(color & 0xFF00) |
|
||||
(color & 0xFF0000) >> 16;
|
||||
RefPtr<ID2D1SolidColorBrush> brush;
|
||||
hr = rt->CreateSolidColorBrush(D2D1::ColorF(color, 1.0), &brush);
|
||||
if (FAILED(hr))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
if (transform) {
|
||||
rt->SetTransform(D2D1::Matrix3x2F(transform->m11,
|
||||
transform->m12,
|
||||
transform->m21,
|
||||
transform->m22,
|
||||
transform->dx,
|
||||
transform->dy));
|
||||
}
|
||||
rt->BeginDraw();
|
||||
rt->DrawGlyphRun(D2D1::Point2F(0, 0), run, brush);
|
||||
hr = rt->EndDraw();
|
||||
if (transform) {
|
||||
rt->SetTransform(D2D1::Matrix3x2F::Identity());
|
||||
}
|
||||
if (FAILED(hr))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
return CAIRO_INT_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* Surface helper function */
|
||||
cairo_int_status_t
|
||||
_cairo_dwrite_show_glyphs_on_surface(void *surface,
|
||||
|
|
@ -2321,25 +2263,7 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface,
|
|||
RECT copyArea, dstArea = { 0, 0, dst->extents.width, dst->extents.height };
|
||||
IntersectRect(©Area, &fontArea, &dstArea);
|
||||
|
||||
#ifdef CAIRO_TRY_D2D_TO_GDI
|
||||
status = _dwrite_draw_glyphs_to_gdi_surface_d2d(dst,
|
||||
mat,
|
||||
&run,
|
||||
color,
|
||||
copyArea);
|
||||
|
||||
if (status == (cairo_status_t)CAIRO_INT_STATUS_UNSUPPORTED) {
|
||||
#endif
|
||||
status = _dwrite_draw_glyphs_to_gdi_surface_gdi(dst,
|
||||
mat,
|
||||
&run,
|
||||
color,
|
||||
dwritesf,
|
||||
copyArea);
|
||||
|
||||
#ifdef CAIRO_TRY_D2D_TO_GDI
|
||||
}
|
||||
#endif
|
||||
status = _dwrite_draw_glyphs_to_gdi_surface_gdi(dst, mat, &run, color, dwritesf, copyArea);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
|
@ -2535,3 +2459,9 @@ _cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_
|
|||
*new_font = font;
|
||||
return CAIRO_INT_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
cairo_win32_dwrite_finalize ()
|
||||
{
|
||||
DWriteFactory::Finalize();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,13 +39,6 @@
|
|||
#include "dwrite-extra.hpp"
|
||||
#include "d2d1-extra.hpp"
|
||||
|
||||
// DirectWrite is not available on all platforms.
|
||||
typedef HRESULT (WINAPI*DWriteCreateFactoryFunc)(
|
||||
DWRITE_FACTORY_TYPE factoryType,
|
||||
REFIID iid,
|
||||
IUnknown **factory
|
||||
);
|
||||
|
||||
/* #cairo_scaled_font_t implementation */
|
||||
struct _cairo_dwrite_scaled_font {
|
||||
cairo_scaled_font_t base;
|
||||
|
|
@ -61,98 +54,71 @@ typedef struct _cairo_dwrite_scaled_font cairo_dwrite_scaled_font_t;
|
|||
class DWriteFactory
|
||||
{
|
||||
public:
|
||||
static RefPtr<IDWriteFactory> Instance()
|
||||
static IDWriteFactory *
|
||||
Instance()
|
||||
{
|
||||
if (!mFactoryInstance) {
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
#endif
|
||||
HMODULE dwrite = _cairo_win32_load_library_from_system32 (L"dwrite.dll");
|
||||
DWriteCreateFactoryFunc createDWriteFactory = (DWriteCreateFactoryFunc)
|
||||
GetProcAddress(dwrite, "DWriteCreateFactory");
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
if (createDWriteFactory) {
|
||||
HRESULT hr = createDWriteFactory(
|
||||
DWRITE_FACTORY_TYPE_SHARED,
|
||||
__uuidof(IDWriteFactory),
|
||||
reinterpret_cast<IUnknown**>(&mFactoryInstance));
|
||||
assert(SUCCEEDED(hr));
|
||||
}
|
||||
}
|
||||
return mFactoryInstance;
|
||||
InitializeFactories();
|
||||
return mFactoryInstance;
|
||||
}
|
||||
|
||||
static RefPtr<IDWriteFactory1> Instance1()
|
||||
static IDWriteFactory1 *
|
||||
Instance1()
|
||||
{
|
||||
if (!mFactoryInstance1) {
|
||||
if (Instance()) {
|
||||
Instance()->QueryInterface(&mFactoryInstance1);
|
||||
}
|
||||
}
|
||||
return mFactoryInstance1;
|
||||
InitializeFactories();
|
||||
return mFactoryInstance1;
|
||||
}
|
||||
|
||||
static RefPtr<IDWriteFactory2> Instance2()
|
||||
static IDWriteFactory2 *
|
||||
Instance2()
|
||||
{
|
||||
if (!mFactoryInstance2) {
|
||||
if (Instance()) {
|
||||
Instance()->QueryInterface(&mFactoryInstance2);
|
||||
}
|
||||
}
|
||||
return mFactoryInstance2;
|
||||
InitializeFactories();
|
||||
return mFactoryInstance2;
|
||||
}
|
||||
|
||||
static RefPtr<IDWriteFactory3> Instance3()
|
||||
static IDWriteFactory3 *
|
||||
Instance3()
|
||||
{
|
||||
if (!mFactoryInstance3) {
|
||||
if (Instance()) {
|
||||
Instance()->QueryInterface(&mFactoryInstance3);
|
||||
}
|
||||
}
|
||||
return mFactoryInstance3;
|
||||
InitializeFactories();
|
||||
return mFactoryInstance3;
|
||||
}
|
||||
|
||||
static RefPtr<IDWriteFactory4> Instance4()
|
||||
static IDWriteFactory4 *
|
||||
Instance4()
|
||||
{
|
||||
if (!mFactoryInstance4) {
|
||||
if (Instance()) {
|
||||
Instance()->QueryInterface(&mFactoryInstance4);
|
||||
}
|
||||
}
|
||||
return mFactoryInstance4;
|
||||
InitializeFactories();
|
||||
return mFactoryInstance4;
|
||||
}
|
||||
|
||||
static RefPtr<IDWriteFactory8> Instance8()
|
||||
static IDWriteFactory8 *
|
||||
Instance8()
|
||||
{
|
||||
if (!mFactoryInstance8) {
|
||||
if (Instance()) {
|
||||
Instance()->QueryInterface(&mFactoryInstance8);
|
||||
}
|
||||
}
|
||||
InitializeFactories();
|
||||
return mFactoryInstance8;
|
||||
}
|
||||
|
||||
static RefPtr<IDWriteFontCollection> SystemCollection()
|
||||
static IDWriteFontCollection *
|
||||
SystemCollection()
|
||||
{
|
||||
if (!mSystemCollection) {
|
||||
if (Instance()) {
|
||||
HRESULT hr = Instance()->GetSystemFontCollection(&mSystemCollection);
|
||||
assert(SUCCEEDED(hr));
|
||||
}
|
||||
}
|
||||
return mSystemCollection;
|
||||
/* The system font collection obtained from the shared factory
|
||||
* is a singleton object. This means that we can cache it
|
||||
* globally and use from any thread.
|
||||
*/
|
||||
|
||||
if (_cairo_atomic_init_once_enter (&mOnceSystemCollection)) {
|
||||
HRESULT hr = Instance()->GetSystemFontCollection(&mSystemCollection);
|
||||
assert(SUCCEEDED(hr));
|
||||
|
||||
_cairo_atomic_init_once_leave (&mOnceSystemCollection);
|
||||
}
|
||||
return mSystemCollection;
|
||||
}
|
||||
|
||||
static RefPtr<IDWriteFontFamily> FindSystemFontFamily(const WCHAR *aFamilyName)
|
||||
static RefPtr<IDWriteFontFamily>
|
||||
FindSystemFontFamily(const WCHAR *aFamilyName)
|
||||
{
|
||||
UINT32 idx;
|
||||
BOOL found;
|
||||
if (!SystemCollection()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SystemCollection()->FindFamilyName(aFamilyName, &idx, &found);
|
||||
if (!found) {
|
||||
return NULL;
|
||||
|
|
@ -163,25 +129,82 @@ public:
|
|||
return family;
|
||||
}
|
||||
|
||||
static RefPtr<IDWriteRenderingParams> DefaultRenderingParams()
|
||||
static void
|
||||
Finalize()
|
||||
{
|
||||
if (!mDefaultRenderingParams) {
|
||||
if (Instance()) {
|
||||
Instance()->CreateRenderingParams(&mDefaultRenderingParams);
|
||||
}
|
||||
}
|
||||
return mDefaultRenderingParams;
|
||||
/* Loader-lock-safe */
|
||||
|
||||
if (_cairo_atomic_init_once_check (&mOnceSystemCollection)) {
|
||||
cairo_win32_async_com_release (mSystemCollection);
|
||||
}
|
||||
|
||||
if (_cairo_atomic_init_once_check (&mOnceFactories)) {
|
||||
cairo_win32_async_com_release (mFactoryInstance);
|
||||
cairo_win32_async_com_release (mFactoryInstance1);
|
||||
cairo_win32_async_com_release (mFactoryInstance2);
|
||||
cairo_win32_async_com_release (mFactoryInstance3);
|
||||
cairo_win32_async_com_release (mFactoryInstance4);
|
||||
cairo_win32_async_com_release (mFactoryInstance8);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static RefPtr<IDWriteFactory> mFactoryInstance;
|
||||
static RefPtr<IDWriteFactory1> mFactoryInstance1;
|
||||
static RefPtr<IDWriteFactory2> mFactoryInstance2;
|
||||
static RefPtr<IDWriteFactory3> mFactoryInstance3;
|
||||
static RefPtr<IDWriteFactory4> mFactoryInstance4;
|
||||
static RefPtr<IDWriteFactory8> mFactoryInstance8;
|
||||
static RefPtr<IDWriteFontCollection> mSystemCollection;
|
||||
static RefPtr<IDWriteRenderingParams> mDefaultRenderingParams;
|
||||
static void
|
||||
InitializeFactories()
|
||||
{
|
||||
/* The shared IDWriteFactory is a singleton object (every call to
|
||||
* DWriteCreateFactory returns the same object) and thus is safe
|
||||
* for concurrent access.
|
||||
*/
|
||||
|
||||
if (_cairo_atomic_init_once_enter (&mOnceFactories)) {
|
||||
typedef HRESULT
|
||||
(WINAPI *pDWriteCreateFactory_t) (DWRITE_FACTORY_TYPE factoryType,
|
||||
REFIID iid,
|
||||
IUnknown **factory);
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
#endif
|
||||
HMODULE dwrite = _cairo_win32_load_library_from_system32 (L"dwrite.dll");
|
||||
pDWriteCreateFactory_t pDWriteCreateFactory = (pDWriteCreateFactory_t)
|
||||
GetProcAddress (dwrite, "DWriteCreateFactory");
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
/* DWrite is based on nano-COM, which is COM as binary interface
|
||||
* and call convention, but doesn't need the runtime library or
|
||||
* registered informations. There's no need to enter a COM
|
||||
* apartment here.
|
||||
*/
|
||||
HRESULT hr = pDWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
|
||||
__uuidof (IDWriteFactory),
|
||||
reinterpret_cast<IUnknown**>(&mFactoryInstance));
|
||||
assert(SUCCEEDED(hr));
|
||||
|
||||
mFactoryInstance->QueryInterface(&mFactoryInstance1);
|
||||
mFactoryInstance->QueryInterface(&mFactoryInstance2);
|
||||
mFactoryInstance->QueryInterface(&mFactoryInstance3);
|
||||
mFactoryInstance->QueryInterface(&mFactoryInstance4);
|
||||
mFactoryInstance->QueryInterface(&mFactoryInstance8);
|
||||
|
||||
_cairo_atomic_init_once_leave (&mOnceFactories);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static cairo_atomic_once_t mOnceFactories;
|
||||
static IDWriteFactory *mFactoryInstance;
|
||||
static IDWriteFactory1 *mFactoryInstance1;
|
||||
static IDWriteFactory2 *mFactoryInstance2;
|
||||
static IDWriteFactory3 *mFactoryInstance3;
|
||||
static IDWriteFactory4 *mFactoryInstance4;
|
||||
static IDWriteFactory8 *mFactoryInstance8;
|
||||
|
||||
static cairo_atomic_once_t mOnceSystemCollection;
|
||||
static IDWriteFontCollection *mSystemCollection;
|
||||
};
|
||||
|
||||
class AutoDWriteGlyphRun : public DWRITE_GLYPH_RUN
|
||||
|
|
|
|||
|
|
@ -141,21 +141,15 @@ _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font
|
|||
#define NEARLY_ZERO(d) (fabs(d) < (1. / 65536.))
|
||||
|
||||
static HDC
|
||||
_get_global_font_dc (void)
|
||||
_get_thread_font_dc (void)
|
||||
{
|
||||
static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT;
|
||||
static DWORD hdc_tls_index;
|
||||
HDC hdc;
|
||||
cairo_win32_thread_data_t *data = cairo_win32_thread_data_get ();
|
||||
|
||||
if (_cairo_atomic_init_once_enter (&once)) {
|
||||
hdc_tls_index = TlsAlloc ();
|
||||
assert (hdc_tls_index != TLS_OUT_OF_INDEXES);
|
||||
_cairo_atomic_init_once_leave (&once);
|
||||
}
|
||||
if (!data->hdc) {
|
||||
HDC hdc_screen = GetDC (NULL);
|
||||
HDC hdc;
|
||||
|
||||
hdc = TlsGetValue (hdc_tls_index);
|
||||
if (!hdc) {
|
||||
hdc = CreateCompatibleDC (NULL);
|
||||
hdc = CreateCompatibleDC (hdc_screen);
|
||||
if (!hdc) {
|
||||
fprintf (stderr, "%s:%s\n", __FUNCTION__, "CreateCompatibleDC");
|
||||
return NULL;
|
||||
|
|
@ -167,13 +161,19 @@ _get_global_font_dc (void)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (!TlsSetValue (hdc_tls_index, hdc)) {
|
||||
DeleteDC (hdc);
|
||||
return NULL;
|
||||
}
|
||||
data->hdc = hdc;
|
||||
/* From MSDN docs for CreateCompatibleDC:
|
||||
*
|
||||
* If [the reference] hdc is NULL, the thread that calls CreateCompatibleDC
|
||||
* owns the HDC that is created. When this thread is destroyed, the HDC is
|
||||
* no longer valid.
|
||||
*/
|
||||
data->free_hdc = (hdc_screen != NULL);
|
||||
|
||||
ReleaseDC (NULL, hdc_screen);
|
||||
}
|
||||
|
||||
return hdc;
|
||||
return data->hdc;
|
||||
}
|
||||
|
||||
static cairo_status_t
|
||||
|
|
@ -314,7 +314,7 @@ _win32_scaled_font_create (LOGFONTW *logfont,
|
|||
cairo_matrix_t scale;
|
||||
cairo_status_t status;
|
||||
|
||||
hdc = _get_global_font_dc ();
|
||||
hdc = _get_thread_font_dc ();
|
||||
if (hdc == NULL)
|
||||
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
|
||||
|
||||
|
|
@ -654,7 +654,7 @@ _cairo_win32_scaled_font_ucs4_to_index (void *abstract_font,
|
|||
HDC hdc = NULL;
|
||||
cairo_status_t status;
|
||||
|
||||
hdc = _get_global_font_dc ();
|
||||
hdc = _get_thread_font_dc ();
|
||||
assert (hdc != NULL);
|
||||
|
||||
status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
|
||||
|
|
@ -682,7 +682,7 @@ _cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font)
|
|||
TEXTMETRIC metrics = {0};
|
||||
HDC hdc;
|
||||
|
||||
hdc = _get_global_font_dc ();
|
||||
hdc = _get_thread_font_dc ();
|
||||
assert (hdc != NULL);
|
||||
|
||||
if (scaled_font->preserve_axes || scaled_font->base.options.hint_metrics == CAIRO_HINT_METRICS_OFF) {
|
||||
|
|
@ -764,7 +764,7 @@ _cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_f
|
|||
cairo_text_extents_t extents;
|
||||
HDC hdc;
|
||||
|
||||
hdc = _get_global_font_dc ();
|
||||
hdc = _get_thread_font_dc ();
|
||||
assert (hdc != NULL);
|
||||
|
||||
if (scaled_font->is_bitmap) {
|
||||
|
|
@ -899,7 +899,7 @@ _cairo_win32_scaled_font_glyph_bbox (void *abstract_font,
|
|||
cairo_status_t status;
|
||||
int i;
|
||||
|
||||
hdc = _get_global_font_dc ();
|
||||
hdc = _get_thread_font_dc ();
|
||||
assert (hdc != NULL);
|
||||
|
||||
status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
|
||||
|
|
@ -1143,7 +1143,7 @@ _cairo_win32_scaled_font_load_truetype_table (void *abstract_font,
|
|||
cairo_status_t status;
|
||||
DWORD ret;
|
||||
|
||||
hdc = _get_global_font_dc ();
|
||||
hdc = _get_thread_font_dc ();
|
||||
assert (hdc != NULL);
|
||||
|
||||
tag = (tag&0x000000ffu)<<24 | (tag&0x0000ff00)<<8 | (tag&0x00ff0000)>>8 | (tag&0xff000000)>>24;
|
||||
|
|
@ -1176,7 +1176,7 @@ _cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font,
|
|||
unsigned int i, j, num_glyphs;
|
||||
cairo_status_t status;
|
||||
|
||||
hdc = _get_global_font_dc ();
|
||||
hdc = _get_thread_font_dc ();
|
||||
assert (hdc != NULL);
|
||||
|
||||
status = cairo_win32_scaled_font_select_font (&scaled_font->base, hdc);
|
||||
|
|
@ -1471,7 +1471,7 @@ _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font
|
|||
if (scaled_font->is_bitmap)
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
hdc = _get_global_font_dc ();
|
||||
hdc = _get_thread_font_dc ();
|
||||
assert (hdc != NULL);
|
||||
|
||||
path = _cairo_path_fixed_create ();
|
||||
|
|
|
|||
|
|
@ -36,14 +36,17 @@
|
|||
#ifndef CAIRO_WIN32_PRIVATE_H
|
||||
#define CAIRO_WIN32_PRIVATE_H
|
||||
|
||||
#include "cairo-win32.h"
|
||||
|
||||
#include "cairoint.h"
|
||||
|
||||
#include "cairo-device-private.h"
|
||||
#include "cairo-surface-clipper-private.h"
|
||||
#include "cairo-surface-private.h"
|
||||
|
||||
#include "cairo-win32.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <unknwn.h>
|
||||
|
||||
#define WIN32_FONT_LOGICAL_SCALE 32
|
||||
|
||||
CAIRO_BEGIN_DECLS
|
||||
|
|
@ -178,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);
|
||||
|
||||
|
|
@ -236,6 +242,44 @@ cairo_win32_get_system_text_quality (void);
|
|||
HMODULE
|
||||
_cairo_win32_load_library_from_system32 (const wchar_t *name);
|
||||
|
||||
void
|
||||
cairo_win32_ensure_mta (void);
|
||||
|
||||
typedef DWORD (__stdcall *stdcall_free_func_t) (void *);
|
||||
|
||||
void
|
||||
cairo_win32_async_stdcall_free (stdcall_free_func_t func, void *data);
|
||||
|
||||
void
|
||||
cairo_win32_async_com_release (IUnknown *iface_ptr);
|
||||
|
||||
#if CAIRO_HAS_DWRITE_FONT
|
||||
interface ID2D1Factory;
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
HDC hdc;
|
||||
cairo_bool_t free_hdc;
|
||||
|
||||
#if CAIRO_HAS_DWRITE_FONT
|
||||
interface ID2D1Factory *d2d1_factory;
|
||||
#endif
|
||||
|
||||
cairo_bool_t added_to_list;
|
||||
} cairo_win32_thread_data_t;
|
||||
|
||||
void
|
||||
cairo_win32_thread_data_initialize (void);
|
||||
|
||||
void
|
||||
cairo_win32_thread_data_finalize (void);
|
||||
|
||||
cairo_win32_thread_data_t *
|
||||
cairo_win32_thread_data_get (void);
|
||||
|
||||
void
|
||||
cairo_win32_thread_data_free (void);
|
||||
|
||||
#if CAIRO_HAS_DWRITE_FONT
|
||||
|
||||
cairo_int_status_t
|
||||
|
|
@ -251,6 +295,9 @@ cairo_int_status_t
|
|||
_cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_font,
|
||||
cairo_scaled_font_t **new_font);
|
||||
|
||||
void
|
||||
cairo_win32_dwrite_finalize (void);
|
||||
|
||||
#endif /* CAIRO_HAS_DWRITE_FONT */
|
||||
|
||||
CAIRO_END_DECLS
|
||||
|
|
|
|||
|
|
@ -46,8 +46,31 @@
|
|||
|
||||
#include "cairoint.h"
|
||||
|
||||
#include "cairo-win32-private.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef HRESULT (__stdcall *pCoIncrementMTAUsage_t) (CO_MTA_USAGE_COOKIE*);
|
||||
typedef HRESULT (__stdcall *pCoDecrementMTAUsage_t) (CO_MTA_USAGE_COOKIE);
|
||||
|
||||
static struct {
|
||||
cairo_atomic_once_t once;
|
||||
|
||||
struct {
|
||||
CO_MTA_USAGE_COOKIE cookie;
|
||||
bool cookie_is_set;
|
||||
pCoDecrementMTAUsage_t pCoDecrementMTAUsage;
|
||||
} mta_usage;
|
||||
HANDLE thread;
|
||||
} mta =
|
||||
{
|
||||
CAIRO_ATOMIC_ONCE_INIT,
|
||||
{ 0, false, NULL },
|
||||
NULL,
|
||||
};
|
||||
|
||||
/**
|
||||
* _cairo_win32_print_api_error:
|
||||
* @context: context string to display along with the error
|
||||
|
|
@ -107,20 +130,146 @@ _cairo_win32_load_library_from_system32 (const wchar_t *name)
|
|||
return module_handle;
|
||||
}
|
||||
|
||||
#if CAIRO_MUTEX_IMPL_WIN32
|
||||
static DWORD __stdcall
|
||||
mta_thread_main (void *user_data)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
assert (SUCCEEDED (hr));
|
||||
|
||||
HANDLE event = (HANDLE) user_data;
|
||||
if (!SignalObjectAndWait (event, GetCurrentProcess (), INFINITE, FALSE)) {
|
||||
assert (0 && "SignalObjectAndWait failed");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cairo_win32_ensure_mta:
|
||||
*
|
||||
* Ensures that the MTA is initialized and keeps running in this
|
||||
* process. Helps for COM usage on threads that we don't own,
|
||||
* since we don't have to call CoInitializeEx.
|
||||
**/
|
||||
void
|
||||
cairo_win32_ensure_mta (void)
|
||||
{
|
||||
if (_cairo_atomic_init_once_enter (&mta.once)) {
|
||||
HMODULE ole32 = _cairo_win32_load_library_from_system32 (L"OLE32.DLL");
|
||||
|
||||
/* Windows 8+ */
|
||||
if (ole32) {
|
||||
pCoIncrementMTAUsage_t pCoIncrementMTAUsage =
|
||||
(pCoIncrementMTAUsage_t) GetProcAddress (ole32, "CoIncrementMTAUsage");
|
||||
pCoDecrementMTAUsage_t pCoDecrementMTAUsage =
|
||||
(pCoDecrementMTAUsage_t) GetProcAddress (ole32, "CoDecrementMTAUsage");
|
||||
|
||||
if (pCoIncrementMTAUsage && pCoDecrementMTAUsage &&
|
||||
SUCCEEDED (pCoIncrementMTAUsage (&mta.mta_usage.cookie)))
|
||||
{
|
||||
mta.mta_usage.cookie_is_set = true;
|
||||
mta.mta_usage.pCoDecrementMTAUsage = pCoDecrementMTAUsage;
|
||||
}
|
||||
}
|
||||
|
||||
/* Downlevel support for Windows 7 */
|
||||
if (!mta.mta_usage.cookie_is_set) {
|
||||
HANDLE event = CreateEvent (NULL, TRUE, FALSE, NULL);
|
||||
if (!event) {
|
||||
assert (0 && "CreateEvent failed");
|
||||
}
|
||||
|
||||
/* Since the UCRT _beginthreadex takes a reference on the "calling
|
||||
* HMODULE", which makes Cairo unloadable. Use CreateThread.
|
||||
*/
|
||||
mta.thread = CreateThread (NULL, 0, mta_thread_main, event, 0, NULL);
|
||||
if (!mta.thread) {
|
||||
assert (0 && "_beginthreadex failed");
|
||||
}
|
||||
|
||||
DWORD ret = WaitForSingleObject (event, INFINITE);
|
||||
if (ret != WAIT_OBJECT_0) {
|
||||
assert (0 && "WaitForSingleObject failed");
|
||||
}
|
||||
|
||||
CloseHandle (event);
|
||||
}
|
||||
|
||||
_cairo_atomic_init_once_leave (&mta.once);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cairo_win32_mta_finalize (void)
|
||||
{
|
||||
/* Loader-lock-safe */
|
||||
|
||||
if (_cairo_atomic_init_once_check (&mta.once)) {
|
||||
if (mta.mta_usage.cookie_is_set) {
|
||||
void *free_func = mta.mta_usage.pCoDecrementMTAUsage;
|
||||
cairo_win32_async_stdcall_free (free_func, mta.mta_usage.cookie);
|
||||
}
|
||||
else if (mta.thread) {
|
||||
/* Yeah, TerminateThread is generally unsafe. however, this is synchronized
|
||||
* with entering of kernel-mode (SignalObjectAndWait) and thus is completely
|
||||
* safe. Note also that TerminateThread is asynchronous, so it can be used
|
||||
* from DllMain.
|
||||
*/
|
||||
TerminateThread (mta.thread, 0);
|
||||
CloseHandle (mta.thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cairo_win32_async_stdcall_free (stdcall_free_func_t func, void *data)
|
||||
{
|
||||
QueueUserWorkItem (func, data, WT_EXECUTEDEFAULT);
|
||||
}
|
||||
|
||||
void
|
||||
cairo_win32_async_com_release (IUnknown *iface_ptr)
|
||||
{
|
||||
if (iface_ptr) {
|
||||
QueueUserWorkItem ((void *) iface_ptr->lpVtbl->Release,
|
||||
iface_ptr, WT_EXECUTEDEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cairo_win32_initialize (void)
|
||||
{
|
||||
CAIRO_MUTEX_INITIALIZE ();
|
||||
cairo_win32_thread_data_initialize ();
|
||||
}
|
||||
|
||||
static void
|
||||
cairo_win32_finalize (void)
|
||||
{
|
||||
cairo_win32_dwrite_finalize ();
|
||||
cairo_win32_thread_data_finalize ();
|
||||
cairo_win32_mta_finalize ();
|
||||
CAIRO_MUTEX_FINALIZE ();
|
||||
}
|
||||
|
||||
static void NTAPI
|
||||
cairo_win32_tls_callback (PVOID hinstance, DWORD dwReason, PVOID lpvReserved)
|
||||
{
|
||||
switch (dwReason) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
CAIRO_MUTEX_INITIALIZE ();
|
||||
cairo_win32_initialize ();
|
||||
break;
|
||||
|
||||
case DLL_THREAD_DETACH:
|
||||
cairo_win32_thread_data_free ();
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
if (lpvReserved == NULL) {
|
||||
CAIRO_MUTEX_FINALIZE ();
|
||||
}
|
||||
if (lpvReserved != NULL)
|
||||
break;
|
||||
cairo_win32_finalize ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -168,5 +317,3 @@ static const PIMAGE_TLS_CALLBACK _ptr_##func = func;
|
|||
#endif /* !_MSC_VER */
|
||||
|
||||
DEFINE_TLS_CALLBACK (cairo_win32_tls_callback);
|
||||
|
||||
#endif /* CAIRO_MUTEX_IMPL_WIN32 */
|
||||
|
|
|
|||
223
src/win32/cairo-win32-thread-data.c
Normal file
223
src/win32/cairo-win32-thread-data.c
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/* cairo - a vector graphics library with display and print output
|
||||
*
|
||||
* Copyright © 2026 Luca Bacci
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it either under the terms of the GNU Lesser General Public
|
||||
* License version 2.1 as published by the Free Software Foundation
|
||||
* (the "LGPL") or, at your option, under the terms of the Mozilla
|
||||
* Public License Version 1.1 (the "MPL"). If you do not alter this
|
||||
* notice, a recipient may use your version of this file under either
|
||||
* the MPL or the LGPL.
|
||||
*
|
||||
* You should have received a copy of the LGPL along with this library
|
||||
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
|
||||
* Foundation, Inc., 31 Milk Street, #960789 Boston, MA 02196, USA.
|
||||
* You should have received a copy of the MPL along with this library
|
||||
* in the file COPYING-MPL-1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License
|
||||
* Version 1.1 (the "License"); you may not use this file except in
|
||||
* compliance with the License. You may obtain a copy of the License at
|
||||
* https://www.mozilla.org/MPL/
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
|
||||
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
|
||||
* the specific language governing rights and limitations.
|
||||
*
|
||||
* The Original Code is the cairo graphics library.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Luca Bacci <luca.bacci@outlook.com>
|
||||
*/
|
||||
|
||||
#include "cairoint.h"
|
||||
|
||||
#include "cairo-array-private.h"
|
||||
#include "cairo-win32-private.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
#include <assert.h>
|
||||
|
||||
#if (defined (__GNUC__) && !defined (__clang__))
|
||||
/* Prefer explicit TLS for mingw-w64 GCC — EmuTLS has
|
||||
* some issues and adds a dependency on libwinpthreads:
|
||||
*
|
||||
* https://github.com/msys2/MINGW-packages/issues/22917
|
||||
* https://github.com/msys2/MINGW-packages/issues/2519#issuecomment-304155278
|
||||
* https://gitlab.freedesktop.org/pixman/pixman/-/merge_requests/61
|
||||
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80881
|
||||
*/
|
||||
#define USE_EXPLICIT_TLS
|
||||
#endif
|
||||
|
||||
#ifdef USE_EXPLICIT_TLS
|
||||
static DWORD
|
||||
tls_index = TLS_OUT_OF_INDEXES;
|
||||
#else
|
||||
static _Thread_local
|
||||
cairo_win32_thread_data_t thread_data;
|
||||
#endif
|
||||
|
||||
SRWLOCK thread_data_list_lock = SRWLOCK_INIT;
|
||||
cairo_array_t thread_data_list;
|
||||
|
||||
/* Could use hardware_destructive_interference_size in C++17
|
||||
* or query at runtime. A modicum value works anyway, and we
|
||||
* actually have init-only members in cairo_win32_thread_data
|
||||
*/
|
||||
const size_t CACHE_LINE_SIZE = 128;
|
||||
|
||||
#ifdef USE_EXPLICIT_TLS
|
||||
|
||||
static cairo_win32_thread_data_t *
|
||||
thread_data_allocation_new (void)
|
||||
{
|
||||
cairo_win32_thread_data_t *data;
|
||||
|
||||
data = _aligned_malloc (sizeof (cairo_win32_thread_data_t), CACHE_LINE_SIZE);
|
||||
assert (data != NULL);
|
||||
memset (data, 0, sizeof (cairo_win32_thread_data_t));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void
|
||||
thread_data_allocation_free (cairo_win32_thread_data_t *data)
|
||||
{
|
||||
_aligned_free (data);
|
||||
}
|
||||
|
||||
#endif /* USE_EXPLICIT_TLS */
|
||||
|
||||
static void
|
||||
thread_data_free (cairo_win32_thread_data_t *data)
|
||||
{
|
||||
/* Loader-lock-safe */
|
||||
|
||||
if (data->free_hdc) {
|
||||
/* Delete the HDC explicitly only if it was created via a non-NULL
|
||||
* reference HDC. Otherwise the system deletes it automatically on
|
||||
* thread-exit and our asynchronous delete would be racy. For more
|
||||
* informations, refer to the MSDN docs for CreateCompatibleDC.
|
||||
*/
|
||||
void *free_func = DeleteDC;
|
||||
cairo_win32_async_stdcall_free (free_func, data->hdc);
|
||||
}
|
||||
|
||||
#if CAIRO_HAS_DWRITE_FONT
|
||||
/* It's not clear if we can release DWrite objects from DllMain
|
||||
* or in general while holding the loader lock. For one, this
|
||||
* is not allowed for DXGI factories (refer to "DXGI responses
|
||||
* from DLLMain" in MSDN's "DXGI Overview"). Use an asynchronous
|
||||
* release to ensure safety.
|
||||
*/
|
||||
cairo_win32_async_com_release ((IUnknown*) data->d2d1_factory);
|
||||
#endif
|
||||
|
||||
#ifdef USE_EXPLICIT_TLS
|
||||
thread_data_allocation_free (data);
|
||||
#endif
|
||||
}
|
||||
|
||||
static cairo_win32_thread_data_t *
|
||||
thread_data_retrieve (cairo_bool_t ensure_allocation)
|
||||
{
|
||||
#ifdef USE_EXPLICIT_TLS
|
||||
cairo_win32_thread_data_t *data = TlsGetValue (tls_index);
|
||||
if (ensure_allocation && !data) {
|
||||
data = thread_data_allocation_new ();
|
||||
TlsSetValue (tls_index, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
#else
|
||||
return &thread_data;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
cairo_win32_thread_data_initialize (void)
|
||||
{
|
||||
#ifdef USE_EXPLICIT_TLS
|
||||
assert(tls_index == TLS_OUT_OF_INDEXES);
|
||||
|
||||
if ((tls_index = TlsAlloc ()) == TLS_OUT_OF_INDEXES)
|
||||
assert (0 && "TlsAlloc failed");
|
||||
#endif
|
||||
|
||||
_cairo_array_init (&thread_data_list, sizeof (cairo_win32_thread_data_t *));
|
||||
}
|
||||
|
||||
void
|
||||
cairo_win32_thread_data_finalize (void)
|
||||
{
|
||||
for (unsigned int i = 0; i < _cairo_array_num_elements (&thread_data_list); i++) {
|
||||
cairo_win32_thread_data_t **p_data = _cairo_array_index (&thread_data_list, i);
|
||||
thread_data_free (*p_data);
|
||||
}
|
||||
|
||||
_cairo_array_fini (&thread_data_list);
|
||||
|
||||
#ifdef USE_EXPLICIT_TLS
|
||||
TlsFree (tls_index);
|
||||
#endif
|
||||
}
|
||||
|
||||
cairo_win32_thread_data_t *
|
||||
cairo_win32_thread_data_get (void)
|
||||
{
|
||||
cairo_win32_thread_data_t *data = thread_data_retrieve (TRUE);
|
||||
|
||||
if (!data->added_to_list) {
|
||||
AcquireSRWLockExclusive (&thread_data_list_lock);
|
||||
|
||||
data->added_to_list = TRUE;
|
||||
if (_cairo_array_append (&thread_data_list, &data) != CAIRO_STATUS_SUCCESS)
|
||||
abort ();
|
||||
|
||||
ReleaseSRWLockExclusive (&thread_data_list_lock);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void
|
||||
cairo_win32_thread_data_free (void)
|
||||
{
|
||||
cairo_win32_thread_data_t *data = thread_data_retrieve (FALSE);
|
||||
|
||||
#ifdef USE_EXPLICIT_TLS
|
||||
if (!data)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (data->added_to_list) {
|
||||
cairo_win32_thread_data_t **iter;
|
||||
unsigned int num_elements;
|
||||
unsigned int i;
|
||||
|
||||
AcquireSRWLockExclusive (&thread_data_list_lock);
|
||||
|
||||
iter = (cairo_win32_thread_data_t **) _cairo_array_index (&thread_data_list, 0);
|
||||
num_elements = _cairo_array_num_elements (&thread_data_list);
|
||||
|
||||
for (i = 0; i < num_elements; i++) {
|
||||
if (iter[i] == data) {
|
||||
cairo_win32_thread_data_t *aux;
|
||||
|
||||
_cairo_array_pop_element (&thread_data_list, &aux);
|
||||
if (i < num_elements - 1)
|
||||
iter[i] = aux;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert (i < num_elements);
|
||||
|
||||
ReleaseSRWLockExclusive (&thread_data_list_lock);
|
||||
}
|
||||
|
||||
thread_data_free (data);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue