From gitlab-mirror at kemper.freedesktop.org Fri Feb 21 17:32:39 2025 From: gitlab-mirror at kemper.freedesktop.org (GitLab Mirror) Date: Fri, 21 Feb 2025 17:32:39 +0000 (UTC) Subject: 29 commits - boilerplate/cairo-boilerplate-win32-printing.c meson.build meson.options meson_options.txt README.md src/win32 Message-ID: <20250221173239.EADAF76194@kemper.freedesktop.org> README.md | 2 boilerplate/cairo-boilerplate-win32-printing.c | 47 - meson.build | 3 src/win32/cairo-dwrite-font.cpp | 773 ++++++++++++++++++++----- src/win32/cairo-dwrite-private.hpp | 6 src/win32/cairo-win32-device.c | 46 - src/win32/cairo-win32-display-surface.c | 34 - src/win32/cairo-win32-font.c | 117 ++- src/win32/cairo-win32-gdi-compositor.c | 36 - src/win32/cairo-win32-printing-surface.c | 59 + src/win32/cairo-win32-private.h | 32 - src/win32/cairo-win32-surface.c | 40 - src/win32/cairo-win32-system.c | 126 +++- 13 files changed, 884 insertions(+), 437 deletions(-) New commits: commit bdd12408a72f1825818fba141b45987389ddfc0c Merge: 3bfe1eed0 4d141f08f Author: Emmanuele Bassi Date: Fri Feb 21 17:32:28 2025 +0000 Merge branch 'dwrite-coverage-map' into 'master' DWrite: Get glyph coverage using IDWriteGlyphRunAnalysis See merge request cairo/cairo!602 commit 4d141f08fb55a71efd6fb194a08169122edb7b47 Author: Luca Bacci Date: Wed Jan 8 10:32:37 2025 +0100 DWrite: Add fallback for OS versions up to Windows 8 IDWriteGlyphRunAnalysis supports gray-scale antialiasing only when created via IDWriteFactory2 (and newer), introduced in Windows 8.1 Fallback to using DIrect2D on A8 targets when IDWriteFactory2 is not available. diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index 3e83a59ee..bf5191967 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -1319,6 +1319,81 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s return CAIRO_INT_STATUS_SUCCESS; } +// Helper for OS versions up to Windows 8 +static cairo_int_status_t +init_glyph_surface_fallback_a8 (cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph, + int width, + int height, + double x1, + double y1, + DWRITE_MATRIX *matrix, + DWRITE_GLYPH_RUN *run) +{ + RefPtr bitmap; + HRESULT hr; + + hr = WICImagingFactory::Instance()->CreateBitmap ((UINT)width, + (UINT)height, + GUID_WICPixelFormat8bppAlpha, + WICBitmapCacheOnLoad, + &bitmap); + if (FAILED(hr)) + return _cairo_dwrite_error (hr, "CreateBitmap failed"); + + D2D1_RENDER_TARGET_PROPERTIES properties = D2D1::RenderTargetProperties( + D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat( + DXGI_FORMAT_A8_UNORM, + D2D1_ALPHA_MODE_PREMULTIPLIED), + 0, + 0, + D2D1_RENDER_TARGET_USAGE_NONE, + D2D1_FEATURE_LEVEL_DEFAULT); + + RefPtr rt; + hr = D2DFactory::Instance()->CreateWicBitmapRenderTarget (bitmap, properties, &rt); + if (FAILED(hr)) + return _cairo_dwrite_error (hr, "CreateWicBitmapRenderTarget failed"); + + RefPtr brush; + hr = rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black, 1.0), &brush); + + rt->BeginDraw(); + + rt->SetTransform(*(D2D1_MATRIX_3X2_F*)matrix); + + rt->DrawGlyphRun({0, 0}, run, brush, scaled_font->measuring_mode); + + hr = rt->EndDraw(); + if (FAILED(hr)) + return _cairo_dwrite_error (hr, "EndDraw failed"); + + // TODO: rt->Flush()? + + cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); + if (cairo_surface_status (surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + // Tell pixman that it should use component alpha blending when the surface is + // used as a source + pixman_image_set_component_alpha (((cairo_image_surface_t*)surface)->pixman_image, TRUE); + + int stride = cairo_image_surface_get_stride (surface); + WICRect rect = { 0, 0, width, height }; + bitmap->CopyPixels(&rect, + stride, + height * stride, + cairo_image_surface_get_data (surface)); + cairo_surface_mark_dirty (surface); + cairo_surface_set_device_offset (surface, -x1, -y1); + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t*)surface); + + return CAIRO_INT_STATUS_SUCCESS; +} + static cairo_int_status_t _cairo_dwrite_scaled_font_init_glyph_surface (cairo_dwrite_scaled_font_t *scaled_font, cairo_scaled_glyph_t *scaled_glyph) @@ -1481,6 +1556,14 @@ _cairo_dwrite_scaled_font_init_glyph_surface (cairo_dwrite_scaled_font_t *scaled &dwrite_glyph_run_analysis); } else { + if (antialias == ANTIALIAS_GRAY) { + // IDWriteGlyphRunAnalysis supports gray-scale antialiasing only when + // created from IDWriteFactory2 or later. If we have IDWriteFactory + // only, fallback to rendering with Direct2D on A8 targets. + return init_glyph_surface_fallback_a8 (scaled_font, scaled_glyph, + width, height, x1, y1, &matrix, &run); + } + hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, 1, &matrix, rendering_mode, commit 78fca779ba3af806d48dd8ebbf6633f566734328 Author: Luca Bacci Date: Wed Nov 13 13:51:53 2024 +0100 DWrite: Get glyph coverage using IDWriteGlyphRunAnalysis diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index 4e8970fdc..3e83a59ee 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -47,6 +47,7 @@ #include "cairo-scaled-font-subsets-private.h" #include "cairo-dwrite.h" +#include #include #include @@ -226,21 +227,6 @@ RefPtr DWriteFactory::mDefaultRenderingParams; RefPtr D2DFactory::mFactoryInstance; RefPtr D2DFactory::mRenderTarget; -static int -_quality_from_antialias_mode(cairo_antialias_t antialias) -{ - switch (antialias) { - case CAIRO_ANTIALIAS_NONE: - return NONANTIALIASED_QUALITY; - case CAIRO_ANTIALIAS_FAST: - case CAIRO_ANTIALIAS_GRAY: - return ANTIALIASED_QUALITY; - default: - break; - } - return CLEARTYPE_QUALITY; -} - static RefPtr _create_rendering_params(IDWriteRenderingParams *params, const cairo_font_options_t *options, @@ -1334,60 +1320,41 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s } static cairo_int_status_t -_cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_font, - cairo_scaled_glyph_t *scaled_glyph) +_cairo_dwrite_scaled_font_init_glyph_surface (cairo_dwrite_scaled_font_t *scaled_font, + cairo_scaled_glyph_t *scaled_glyph) { - cairo_int_status_t status; - cairo_win32_surface_t *surface; - cairo_t *cr; - cairo_surface_t *image; - int width, height; - double x1, y1, x2, y2; + HRESULT hr; - x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); - y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); - x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); - y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); - width = (int)(x2 - x1); - height = (int)(y2 - y1); + double x1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.x); + double y1 = _cairo_fixed_integer_floor (scaled_glyph->bbox.p1.y); + double x2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.x); + double y2 = _cairo_fixed_integer_ceil (scaled_glyph->bbox.p2.y); + int width = (int)(x2 - x1); + int height = (int)(y2 - y1); + + if (width <= 0) + width = 1; + if (height <= 0) + height = 1; - DWRITE_GLYPH_RUN run; FLOAT advance = 0; UINT16 index = (UINT16)_cairo_scaled_glyph_index (scaled_glyph); DWRITE_GLYPH_OFFSET offset; double x = -x1 + .25 * _cairo_scaled_glyph_xphase (scaled_glyph); double y = -y1 + .25 * _cairo_scaled_glyph_yphase (scaled_glyph); - RECT area; - DWRITE_MATRIX matrix; - - surface = (cairo_win32_surface_t *) - cairo_win32_surface_create_with_dib (CAIRO_FORMAT_RGB24, width, height); - cr = cairo_create (&surface->base); - cairo_set_source_rgb (cr, 1, 1, 1); - cairo_paint (cr); - status = (cairo_int_status_t)cairo_status (cr); - cairo_destroy(cr); - if (status) - goto FAIL; - - /* - * We transform by the inverse transformation here. This will put our glyph + /* We transform by the inverse transformation here. This will put our glyph * locations in the space in which we draw. Which is later transformed by * the transformation matrix that we use. This will transform the * glyph positions back to where they were before when drawing, but the - * glyph shapes will be transformed by the transformation matrix. - */ + * glyph shapes will be transformed by the transformation matrix. */ cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); offset.advanceOffset = (FLOAT)x; - /* Y-axis is inverted */ - offset.ascenderOffset = -(FLOAT)y; + offset.ascenderOffset = -(FLOAT)y; /* Y axis is inverted */ - area.top = 0; - area.bottom = height; - area.left = 0; - area.right = width; + DWRITE_MATRIX matrix = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); + DWRITE_GLYPH_RUN run; run.glyphCount = 1; run.glyphAdvances = &advance; run.fontFace = scaled_font->dwriteface; @@ -1397,29 +1364,370 @@ _cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_ run.isSideways = FALSE; run.glyphOffsets = &offset; - matrix = _cairo_dwrite_matrix_from_matrix(&scaled_font->mat); + // Reduce the many Cairo antialias values to the + // three we actually care about: NONE, GRAY, RGB + enum { + ANTIALIAS_NONE, + ANTIALIAS_GRAY, + ANTIALIAS_CLEARTYPE, + } antialias = ANTIALIAS_CLEARTYPE; + + switch (scaled_font->antialias_mode) { + case CAIRO_ANTIALIAS_NONE: + antialias = ANTIALIAS_NONE; + break; + case CAIRO_ANTIALIAS_FAST: + case CAIRO_ANTIALIAS_GRAY: + antialias = ANTIALIAS_GRAY; + break; + case CAIRO_ANTIALIAS_DEFAULT: + case CAIRO_ANTIALIAS_GOOD: + case CAIRO_ANTIALIAS_BEST: + case CAIRO_ANTIALIAS_SUBPIXEL: + antialias = ANTIALIAS_CLEARTYPE; + break; + } + + // Set DWrite rendering options + + DWRITE_RENDERING_MODE rendering_mode; + DWRITE_RENDERING_MODE1 rendering_mode1; + DWRITE_TEXT_ANTIALIAS_MODE text_antialias_mode; + DWRITE_TEXTURE_TYPE texture_type; - status = _dwrite_draw_glyphs_to_gdi_surface_gdi (surface, &matrix, &run, - RGB(0,0,0), scaled_font, area); - if (status) - goto FAIL; + switch (antialias) { + case ANTIALIAS_NONE: + rendering_mode1 = DWRITE_RENDERING_MODE1_ALIASED; + rendering_mode = DWRITE_RENDERING_MODE_ALIASED; + text_antialias_mode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE; + texture_type = DWRITE_TEXTURE_ALIASED_1x1; + break; + case ANTIALIAS_GRAY: + rendering_mode1 = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; + rendering_mode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; + text_antialias_mode = DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE; + texture_type = DWRITE_TEXTURE_ALIASED_1x1; + break; + case ANTIALIAS_CLEARTYPE: + rendering_mode1 = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC; + rendering_mode = DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; + text_antialias_mode = DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE; + texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1; + break; + } + + DWRITE_GRID_FIT_MODE grid_fit_mode = DWRITE_GRID_FIT_MODE_DEFAULT; + + switch (cairo_font_options_get_hint_style (&scaled_font->base.options)) { + case CAIRO_HINT_STYLE_DEFAULT: + grid_fit_mode = DWRITE_GRID_FIT_MODE_DEFAULT; + case CAIRO_HINT_STYLE_NONE: + grid_fit_mode = DWRITE_GRID_FIT_MODE_DISABLED; + case CAIRO_HINT_STYLE_SLIGHT: + case CAIRO_HINT_STYLE_MEDIUM: + case CAIRO_HINT_STYLE_FULL: + grid_fit_mode = DWRITE_GRID_FIT_MODE_ENABLED; + } + + cairo_subpixel_order_t subpixel_order; + subpixel_order = cairo_font_options_get_subpixel_order (&scaled_font->base.options); + + bool subpixel_order_is_vertical = false; + if (antialias == ANTIALIAS_CLEARTYPE) { + switch (subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + case CAIRO_SUBPIXEL_ORDER_BGR: + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + case CAIRO_SUBPIXEL_ORDER_VBGR: + subpixel_order_is_vertical = true; + break; + } + } - GdiFlush(); + if (subpixel_order_is_vertical) { + // DirectWrite does not support vertical pixel geometries. + // As a workaround, apply a simmetry which swaps x and y + // coordinates, then re-swap while copying the back into + // the image surface + + // swap the two rows + std::swap (matrix.m11, matrix.m21); + std::swap (matrix.m12, matrix.m22); + } + + RefPtr dwrite_glyph_run_analysis; + if (DWriteFactory::Instance3()) { + hr = DWriteFactory::Instance3()->CreateGlyphRunAnalysis(&run, + &matrix, + rendering_mode1, + scaled_font->measuring_mode, + grid_fit_mode, + text_antialias_mode, + 0, // baselineOriginX, + 0, // baselineOriginY, + &dwrite_glyph_run_analysis); + } + else if (DWriteFactory::Instance2()) { + hr = DWriteFactory::Instance2()->CreateGlyphRunAnalysis(&run, + &matrix, + rendering_mode, + scaled_font->measuring_mode, + grid_fit_mode, + text_antialias_mode, + 0, // baselineOriginX, + 0, // baselineOriginY, + &dwrite_glyph_run_analysis); + } + else { + hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, 1, + &matrix, + rendering_mode, + scaled_font->measuring_mode, + 0, // baselineOriginX, + 0, // baselineOriginY, + &dwrite_glyph_run_analysis); + } + if (FAILED(hr)) + return CAIRO_INT_STATUS_UNSUPPORTED; - image = _cairo_compute_glyph_mask (&surface->base, _quality_from_antialias_mode(scaled_font->antialias_mode)); - status = (cairo_int_status_t)image->status; - if (status) - goto FAIL; + cairo_format_t surface_format = antialias == ANTIALIAS_NONE ? CAIRO_FORMAT_A1 : + antialias == ANTIALIAS_GRAY ? CAIRO_FORMAT_A8 : + CAIRO_FORMAT_ARGB32; - cairo_surface_set_device_offset (image, -x1, -y1); - _cairo_scaled_glyph_set_surface (scaled_glyph, - &scaled_font->base, - (cairo_image_surface_t *) image); + cairo_surface_t *surface = cairo_image_surface_create (surface_format, width, height); + if (cairo_surface_status (surface)) + return CAIRO_INT_STATUS_UNSUPPORTED; - FAIL: - cairo_surface_destroy (&surface->base); + // Tell pixman that it should use component alpha blending when the surface is + // used as a source + pixman_image_set_component_alpha (((cairo_image_surface_t*)surface)->pixman_image, TRUE); - return status; + // That's probably not needed right after creation + cairo_surface_flush (surface); + unsigned char *surface_data = cairo_image_surface_get_data (surface); + int surface_stride = cairo_image_surface_get_stride (surface); + + UINT32 dwrite_data_size; + BYTE *dwrite_data; + RECT dwrite_rect = { + 0, // left + 0, // top + width, // right + height // bottom + }; + + if (subpixel_order_is_vertical) { + std::swap (dwrite_rect.right, + dwrite_rect.bottom); + } + + // Whether IDWriteGlyphRunAnalysis::CreateAlphaTexture() can render directly + // on the cairo image surface (because the pixel formats match) or a separate + // buffer is needed + bool render_is_direct; + + switch (antialias) { + case ANTIALIAS_NONE: + { + dwrite_data_size = width * height; //TODO: check overflow + + dwrite_data = (BYTE*) _cairo_malloc (dwrite_data_size); + if (!dwrite_data) + return CAIRO_INT_STATUS_UNSUPPORTED; + + render_is_direct = false; + break; + } + case ANTIALIAS_GRAY: + { + // The image surface may have a stride that's bigger than width- + // account for that by passing stride as width to DWrite. Note: + // stride is a byte-size, but here pixel-size is exactly 1 byte. + dwrite_rect.right = cairo_image_surface_get_stride (surface); + + dwrite_data_size = dwrite_rect.right * height; //TODO: check overflow + dwrite_data = static_cast(surface_data); + + render_is_direct = true; + break; + } + case ANTIALIAS_CLEARTYPE: + { + dwrite_data_size = 3 * width * height; //TODO: check overflow + + dwrite_data = (BYTE*) _cairo_malloc (dwrite_data_size); + if (!dwrite_data) + return CAIRO_INT_STATUS_UNSUPPORTED; + + render_is_direct = false; + break; + } + } + + hr = dwrite_glyph_run_analysis->CreateAlphaTexture(texture_type, &dwrite_rect, dwrite_data, dwrite_data_size); + if (FAILED (hr)) + return CAIRO_INT_STATUS_UNSUPPORTED; + + // Most of the code here was copied and adapted from cairoft-font.c + switch (antialias) { + case ANTIALIAS_NONE: + { + unsigned char *src = static_cast(dwrite_data); + unsigned char *dst = surface_data; + + for (int i = 0; i < height; i++) { + unsigned char *d = dst; + + for (int j = 0; j < width / 8; j++) { + *d = (src[0] ? (1 << 0) : 0) + + (src[1] ? (1 << 1) : 0) + + (src[2] ? (1 << 2) : 0) + + (src[3] ? (1 << 3) : 0) + + (src[4] ? (1 << 4) : 0) + + (src[5] ? (1 << 5) : 0) + + (src[6] ? (1 << 6) : 0) + + (src[7] ? (1 << 7) : 0); + + d++; + src += 8; + } + + if (width % 8 != 0) { + *d = 0; + + for (int k = 0; k < width % 8; k++) { + *d += (src[k] ? (1 << k) : 0); + } + + d++; + src += (width % 8); + } + + dst += surface_stride; + } + break; + } + case ANTIALIAS_GRAY: + { + // Nothing to do + break; + } + case ANTIALIAS_CLEARTYPE: + { + unsigned char *src = static_cast(dwrite_data); + unsigned char *dst = surface_data; + + // The alpha channel is unused for component-alpha blending. + // Here we set the alpha channel anyway so that things work + // even in case of normal blending (but one likely gets some + // color fringing) + + switch (subpixel_order) { + case CAIRO_SUBPIXEL_ORDER_DEFAULT: + case CAIRO_SUBPIXEL_ORDER_RGB: + { + for (int i = 0; i < height; i++) { + UINT32 *d = reinterpret_cast(dst); + + for (int j = 0; j < width; j++) { + // CAIRO_FORMAT_ARGB32 is BGRA on little-endian + *d = (src[2] << 0) + + (src[1] << 8) + + (src[0] << 16) + + (src[1] << 24); // unused + + d++; + src += 3; + } + + dst += surface_stride; + } + } + break; + case CAIRO_SUBPIXEL_ORDER_BGR: + { + for (int i = 0; i < height; i++) { + UINT32 *d = reinterpret_cast(dst); + + for (int j = 0; j < width; j++) { + *d = (src[0] << 0) + + (src[1] << 8) + + (src[2] << 16) + + (src[1] << 24); // unused + + d++; + src += 3; + } + + dst += surface_stride; + } + } + break; + case CAIRO_SUBPIXEL_ORDER_VRGB: + { + size_t p; + + for (int i = 0; i < height; i++) { + UINT32 *d = reinterpret_cast(dst); + + p = i * 3; + for (int j = 0; j < width; j++) { + *d = (src[p + 2] << 0) + + (src[p + 1] << 8) + + (src[p + 0] << 16) + + (src[p + 1] << 24); // unused + + d++; + p += 3 * height; + } + + dst += surface_stride; + } + } + break; + case CAIRO_SUBPIXEL_ORDER_VBGR: + { + size_t p; + + for (int i = 0; i < height; i++) { + UINT32 *d = reinterpret_cast(dst); + + p = i * 3; + for (int j = 0; j < width; j++) { + *d = (src[p + 0] << 0) + + (src[p + 1] << 8) + + (src[p + 2] << 16) + + (src[p + 1] << 24); // unused + + d++; + p += 3 * height; + } + + dst += surface_stride; + } + } + break; + } + + break; + } + } + + // That's probably not needed. cairoft-font.c doesn't do that + cairo_surface_mark_dirty (surface); + + if (!render_is_direct) { + free (dwrite_data); + } + + cairo_surface_set_device_offset (surface, -x1, -y1); + _cairo_scaled_glyph_set_surface (scaled_glyph, + &scaled_font->base, + (cairo_image_surface_t*)surface); + + return CAIRO_INT_STATUS_SUCCESS; } static cairo_int_status_t @@ -1828,6 +2136,7 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface, cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); cairo_win32_surface_t *dst = reinterpret_cast(surface); cairo_int_status_t status; + /* We can only handle dwrite fonts */ if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) return CAIRO_INT_STATUS_UNSUPPORTED; diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h index ae8dc52c3..a9eda8fdb 100644 --- a/src/win32/cairo-win32-private.h +++ b/src/win32/cairo-win32-private.h @@ -191,10 +191,6 @@ cairo_bool_t _cairo_win32_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle); -cairo_surface_t * -_cairo_compute_glyph_mask (cairo_surface_t *surface, - int quality); - uint32_t _cairo_win32_flags_for_dc (HDC dc, cairo_format_t format); commit 3bfe1eed026bc7ed60c77c58257fead23654ad55 Merge: fe87f1c3f af4f14905 Author: Emmanuele Bassi Date: Thu Jan 30 09:31:59 2025 +0000 Merge branch 'meson' into 'master' Minor Meson cleanups See merge request cairo/cairo!584 commit fe87f1c3fb5b8fb031285c38250ff187f3bab0fc Merge: e2a52c087 864ede8e1 Author: Emmanuele Bassi Date: Thu Jan 30 09:29:01 2025 +0000 Merge branch 'win32-cleanup' into 'master' Win32 cleanup See merge request cairo/cairo!605 commit e2a52c0870ad8752002e5de4173c677c60bbb508 Merge: ed7b0a54b b84b3542d Author: Emmanuele Bassi Date: Thu Jan 30 09:26:49 2025 +0000 Merge branch 'dwrite-font-variations' into 'master' DWrite: Add support for font variations Closes #877 See merge request cairo/cairo!601 commit ed7b0a54b9e86f3aea04a42ca99ab4526027f2ed Merge: 727966dfc e0287e09f Author: Emmanuele Bassi Date: Thu Jan 30 09:25:57 2025 +0000 Merge branch 'dwrite-fix-warnings' into 'master' DWrite: Fix warnings See merge request cairo/cairo!603 commit e0287e09f41fc7d063d2e3d707a5e5a5521b6f85 Author: Luca Bacci Date: Thu Jan 16 12:13:39 2025 +0100 DWrite/GeometryRecorder: Add final specifier diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index a69adec0b..92d2980cd 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -838,7 +838,8 @@ _cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_ * Used to determine the path of the glyphs. */ -class GeometryRecorder : public IDWriteGeometrySink +class GeometryRecorder final + : public IDWriteGeometrySink { public: GeometryRecorder(cairo_path_fixed_t *aCairoPath, const cairo_matrix_t &matrix) commit 46153a04087d1bef8f2ef3da3cb14e1b97bf4a6c Author: Luca Bacci Date: Thu Jan 16 12:02:27 2025 +0100 DWrite/GeometryRecorder: Complete implementation of QueryInterface ...by checking for IUnknown. This makes GeometryRecorder::QueryInterface compliant with the rules of COM. QueryInterface for IUnknown has a special meaning in COM: it's used to check whether two interface pointers refer to the same object. diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index 36d6b5c7b..a69adec0b 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -845,15 +845,18 @@ public: : mCairoPath(aCairoPath) , mMatrix(matrix) {} - // IUnknown interface IFACEMETHOD (QueryInterface)(IID const& iid, OUT void** ppObject) noexcept override { - if (iid != __uuidof(IDWriteGeometrySink)) - return E_NOINTERFACE; - - *ppObject = static_cast(this); - - return S_OK; + if (iid == __uuidof (IUnknown) || + iid == __uuidof (IDWriteGeometrySink)) + { + AddRef(); + *ppObject = this; + return S_OK; + } + + *ppObject = nullptr; + return E_NOINTERFACE; } IFACEMETHOD_(ULONG, AddRef)() noexcept override commit 59197e779106985fb14625159318b0d48afb9882 Author: Luca Bacci Date: Thu Jan 16 11:52:52 2025 +0100 DWrite/GeometryRecorder: Add noexcept specifier STDMETHOD / IFACEMETHOD macros already add __declspec(nothrow), but noexcept is better. From MSDN [1]: We recommend that all new code use the noexcept operator rather than __declspec(nothrow). This attribute tells the compiler that the declared function and the functions it calls never throw an exception. However, it does not enforce the directive. In other words, it never causes std::terminate to be invoked, unlike noexcept, or in std:c++17 mode (Visual Studio 2017 version 15.5 and later), throw(). See also [2]: Non-throwing functions are permitted to call potentially-throwing functions. Whenever an exception is thrown and the search for a handler encounters the outermost block of a non-throwing function, the function std::terminate is called: extern void f(); // potentially-throwing void g() noexcept { f(); // valid, even if f throws throw 42; // valid, effectively a call to std::terminate } References: [1] https://learn.microsoft.com/en-us/cpp/cpp/nothrow-cpp?view=msvc-170 [2] https://en.cppreference.com/w/cpp/language/noexcept_spec diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index f20df6de9..36d6b5c7b 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -846,7 +846,7 @@ public: , mMatrix(matrix) {} // IUnknown interface - IFACEMETHOD (QueryInterface)(IID const& iid, OUT void** ppObject) override + IFACEMETHOD (QueryInterface)(IID const& iid, OUT void** ppObject) noexcept override { if (iid != __uuidof(IDWriteGeometrySink)) return E_NOINTERFACE; @@ -856,34 +856,34 @@ public: return S_OK; } - IFACEMETHOD_(ULONG, AddRef)() override + IFACEMETHOD_(ULONG, AddRef)() noexcept override { return 1; } - IFACEMETHOD_(ULONG, Release)() override + IFACEMETHOD_(ULONG, Release)() noexcept override { return 1; } - IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode) override + IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode) noexcept override { return; } - IFACEMETHOD (Close)() override + IFACEMETHOD (Close)() noexcept override { return S_OK; } - IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags) override + IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags) noexcept override { return; } IFACEMETHOD_(void, BeginFigure)( D2D1_POINT_2F startPoint, - D2D1_FIGURE_BEGIN figureBegin) override + D2D1_FIGURE_BEGIN figureBegin) noexcept override { double x = startPoint.x; double y = startPoint.y; @@ -897,7 +897,7 @@ public: } IFACEMETHOD_(void, EndFigure)( - D2D1_FIGURE_END figureEnd) override + D2D1_FIGURE_END figureEnd) noexcept override { if (figureEnd == D2D1_FIGURE_END_CLOSED) { cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, @@ -909,7 +909,7 @@ public: IFACEMETHOD_(void, AddBeziers)( const D2D1_BEZIER_SEGMENT *beziers, - UINT beziersCount) override + UINT beziersCount) noexcept override { for (unsigned int i = 0; i < beziersCount; i++) { double x1 = beziers[i].point1.x; @@ -934,7 +934,7 @@ public: IFACEMETHOD_(void, AddLines)( const D2D1_POINT_2F *points, - UINT pointsCount) override + UINT pointsCount) noexcept override { for (unsigned int i = 0; i < pointsCount; i++) { double x = points[i].x; commit 8107086c6a8d52a2b86352e86b32799885a3b746 Author: Luca Bacci Date: Thu Jan 16 11:37:39 2025 +0100 DWrite/GeometryRecorder: Add override specifier IFACEMETHOD already adds the __override / __allowed(on_function) SAL annotation (only on Windows SDK, not mingw-w64), which is understood by some code analysis tools [1]. Since we're compiling in C++11 mode, we can add the override specifier, so that the compiler is informed as well. [1] https://devblogs.microsoft.com/oldnewthing/20200911-00/?p=104205 diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index bcff2d42a..f20df6de9 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -846,7 +846,7 @@ public: , mMatrix(matrix) {} // IUnknown interface - IFACEMETHOD (QueryInterface)(IID const& iid, OUT void** ppObject) + IFACEMETHOD (QueryInterface)(IID const& iid, OUT void** ppObject) override { if (iid != __uuidof(IDWriteGeometrySink)) return E_NOINTERFACE; @@ -856,34 +856,34 @@ public: return S_OK; } - IFACEMETHOD_(ULONG, AddRef)() + IFACEMETHOD_(ULONG, AddRef)() override { return 1; } - IFACEMETHOD_(ULONG, Release)() + IFACEMETHOD_(ULONG, Release)() override { return 1; } - IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode) + IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode) override { return; } - IFACEMETHOD (Close)() + IFACEMETHOD (Close)() override { return S_OK; } - IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags) + IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags) override { return; } IFACEMETHOD_(void, BeginFigure)( D2D1_POINT_2F startPoint, - D2D1_FIGURE_BEGIN figureBegin) + D2D1_FIGURE_BEGIN figureBegin) override { double x = startPoint.x; double y = startPoint.y; @@ -897,7 +897,7 @@ public: } IFACEMETHOD_(void, EndFigure)( - D2D1_FIGURE_END figureEnd) + D2D1_FIGURE_END figureEnd) override { if (figureEnd == D2D1_FIGURE_END_CLOSED) { cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, @@ -909,7 +909,7 @@ public: IFACEMETHOD_(void, AddBeziers)( const D2D1_BEZIER_SEGMENT *beziers, - UINT beziersCount) + UINT beziersCount) override { for (unsigned int i = 0; i < beziersCount; i++) { double x1 = beziers[i].point1.x; @@ -934,7 +934,7 @@ public: IFACEMETHOD_(void, AddLines)( const D2D1_POINT_2F *points, - UINT pointsCount) + UINT pointsCount) override { for (unsigned int i = 0; i < pointsCount; i++) { double x = points[i].x; commit bdac72997480b9e71e1f341d8e09388703f1bde3 Author: Luca Bacci Date: Wed Jan 8 11:20:08 2025 +0100 DWrite/GeometryRecorder: Use IFACEMETHOD consistently Fixes the following warnings on CLang: ../cairo/src/win32/cairo-dwrite-font.cpp:869:27: warning: exception specification of overriding function is more lax than base version [-Wmicrosoft-exception-spec] 869 | IFACEMETHODIMP_(void) SetFillMode(D2D1_FILL_MODE fillMode) | ^ D:/msys64/clang64/include/d2d1.h:1491:22: note: overridden virtual function is here 1491 | STDMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode) PURE; | COM objects are usually implemented like that: 1. The class is defined with only method declarations. For that, one should use IFACEMETHOD macros. 2. Then methods are implemented (defined), outside of the class definition. For that, one should use the IFACEMETHODIMP macros If one really wants to provide inline method definitions (that is, inside the class definition), then IFACEMETHOD macros should be used (and not IFACEMETHODIMP, though it's a definition / implementation). diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index ba1695259..bcff2d42a 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -846,7 +846,7 @@ public: , mMatrix(matrix) {} // IUnknown interface - IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) + IFACEMETHOD (QueryInterface)(IID const& iid, OUT void** ppObject) { if (iid != __uuidof(IDWriteGeometrySink)) return E_NOINTERFACE; @@ -866,22 +866,22 @@ public: return 1; } - IFACEMETHODIMP_(void) SetFillMode(D2D1_FILL_MODE fillMode) + IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode) { return; } - STDMETHODIMP Close() + IFACEMETHOD (Close)() { return S_OK; } - IFACEMETHODIMP_(void) SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) + IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags) { return; } - IFACEMETHODIMP_(void) BeginFigure( + IFACEMETHOD_(void, BeginFigure)( D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) { @@ -896,7 +896,7 @@ public: (void)status; /* squelch warning */ } - IFACEMETHODIMP_(void) EndFigure( + IFACEMETHOD_(void, EndFigure)( D2D1_FIGURE_END figureEnd) { if (figureEnd == D2D1_FIGURE_END_CLOSED) { @@ -907,7 +907,7 @@ public: } } - IFACEMETHODIMP_(void) AddBeziers( + IFACEMETHOD_(void, AddBeziers)( const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) { @@ -932,7 +932,7 @@ public: } } - IFACEMETHODIMP_(void) AddLines( + IFACEMETHOD_(void, AddLines)( const D2D1_POINT_2F *points, UINT pointsCount) { commit 864ede8e1a8e9205d7c75d3a6ea3360282121ba5 Author: Luca Bacci Date: Thu Jan 16 18:08:55 2025 +0100 Win32: Load system DLLs from System32 diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index 74d12c6a0..bc8177fd9 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -137,8 +137,9 @@ public: #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-function-type" #endif + HMODULE d2d1 = _cairo_win32_load_library_from_system32 (L"d2d1.dll"); D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc) - GetProcAddress(LoadLibraryW(L"d2d1.dll"), "D2D1CreateFactory"); + GetProcAddress(d2d1, "D2D1CreateFactory"); #ifdef __GNUC__ #pragma GCC diagnostic pop #endif diff --git a/src/win32/cairo-dwrite-private.hpp b/src/win32/cairo-dwrite-private.hpp index c7a24822a..3e0d9a14e 100644 --- a/src/win32/cairo-dwrite-private.hpp +++ b/src/win32/cairo-dwrite-private.hpp @@ -83,8 +83,9 @@ public: #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(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"); + GetProcAddress(dwrite, "DWriteCreateFactory"); #ifdef __GNUC__ #pragma GCC diagnostic pop #endif diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h index 6dd60f1f0..ae8dc52c3 100644 --- a/src/win32/cairo-win32-private.h +++ b/src/win32/cairo-win32-private.h @@ -237,6 +237,9 @@ _cairo_win32_scaled_font_is_bitmap (cairo_scaled_font_t *scaled_font); cairo_public BYTE cairo_win32_get_system_text_quality (void); +HMODULE +_cairo_win32_load_library_from_system32 (const wchar_t *name); + #if CAIRO_HAS_DWRITE_FONT cairo_int_status_t diff --git a/src/win32/cairo-win32-system.c b/src/win32/cairo-win32-system.c index 6ea14ebed..c5ef24e8d 100644 --- a/src/win32/cairo-win32-system.c +++ b/src/win32/cairo-win32-system.c @@ -79,6 +79,34 @@ _cairo_win32_print_api_error (const char *context, const char *api) return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); } +/** + * _cairo_win32_load_library_from_system32: + * @name: name of the module to load from System32 + * + * Helper function to load system modules in the System32 + * folder. + * + * Return value: An module HANDLE, NULL on error. + **/ +HMODULE +_cairo_win32_load_library_from_system32 (const wchar_t *name) +{ + HMODULE module_handle; + + module_handle = LoadLibraryExW (name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (module_handle == NULL) { + DWORD code = GetLastError(); + if (code == ERROR_INVALID_PARAMETER) { + /* Support for flag LOAD_LIBRARY_SEARCH_SYSTEM32 was backported + * to Windows Vista / 7 with Update KB2533623. If the flag is + * not supported, simply use LoadLibrary */ + return LoadLibraryW (name); + } + } + + return module_handle; +} + #if CAIRO_MUTEX_IMPL_WIN32 static void NTAPI commit 995c09ba67f47e17b883ac0dc4f5a849f100f600 Author: Luca Bacci Date: Tue Jan 21 11:29:05 2025 +0100 Win32: Avoid library finalization on process termination Library finalization should be done only for explicit library unloads (dlclose / FreeLibrary), not for process termination. diff --git a/src/win32/cairo-win32-system.c b/src/win32/cairo-win32-system.c index ae1489ac6..6ea14ebed 100644 --- a/src/win32/cairo-win32-system.c +++ b/src/win32/cairo-win32-system.c @@ -90,7 +90,9 @@ cairo_win32_tls_callback (PVOID hinstance, DWORD dwReason, PVOID lpvReserved) break; case DLL_PROCESS_DETACH: - CAIRO_MUTEX_FINALIZE (); + if (lpvReserved == NULL) { + CAIRO_MUTEX_FINALIZE (); + } break; } } commit 1e5740c75631310adb83992ed61438a08df914bd Author: Luca Bacci Date: Tue Jan 21 11:25:06 2025 +0100 Revert "win32: Initialize mutexes for static builds for win32" This reverts commit 5f90f0a706206ea3b940392bf1492fead832d99b. This is not needed anymore now that we have TLS callbacks. diff --git a/src/win32/cairo-win32-device.c b/src/win32/cairo-win32-device.c index c55b0789e..91ef74588 100644 --- a/src/win32/cairo-win32-device.c +++ b/src/win32/cairo-win32-device.c @@ -81,8 +81,6 @@ _cairo_win32_device_get (void) { cairo_win32_device_t *device; - CAIRO_MUTEX_INITIALIZE (); - if (__cairo_win32_device) return cairo_device_reference (__cairo_win32_device); commit 3921eb96be5071405f898ee7cd36f50a96d15a5c Author: Luca Bacci Date: Thu Jan 16 18:01:07 2025 +0100 Win32: Use TLS callback for module initialization and finalization TLS callbacks are pretty much like DllMain but work also in static builds. diff --git a/src/win32/cairo-win32-system.c b/src/win32/cairo-win32-system.c index 7163d4470..ae1489ac6 100644 --- a/src/win32/cairo-win32-system.c +++ b/src/win32/cairo-win32-system.c @@ -80,20 +80,11 @@ _cairo_win32_print_api_error (const char *context, const char *api) } #if CAIRO_MUTEX_IMPL_WIN32 -#if !CAIRO_WIN32_STATIC_BUILD - -/* declare to avoid "no previous prototype for 'DllMain'" warning */ -BOOL WINAPI -DllMain (HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved); - -BOOL WINAPI -DllMain (HINSTANCE hinstDLL, - DWORD fdwReason, - LPVOID lpvReserved) + +static void NTAPI +cairo_win32_tls_callback (PVOID hinstance, DWORD dwReason, PVOID lpvReserved) { - switch (fdwReason) { + switch (dwReason) { case DLL_PROCESS_ATTACH: CAIRO_MUTEX_INITIALIZE (); break; @@ -102,9 +93,50 @@ DllMain (HINSTANCE hinstDLL, CAIRO_MUTEX_FINALIZE (); break; } - - return TRUE; } +#ifdef _MSC_VER + +#ifdef _M_IX86 +# define SYMBOL_PREFIX "_" +#else +# define SYMBOL_PREFIX "" #endif + +#ifdef __cplusplus +# define EXTERN_C_BEGIN extern "C" { +# define EXTERN_C_END } +# define EXTERN_CONST extern const +#else +# define EXTERN_C_BEGIN +# define EXTERN_C_END +# define EXTERN_CONST const #endif + +#define DEFINE_TLS_CALLBACK(func) \ +__pragma (section (".CRT$XLD", long, read)) \ + \ +static void NTAPI func (PVOID, DWORD, PVOID); \ + \ +EXTERN_C_BEGIN \ +__declspec (allocate (".CRT$XLD")) \ +EXTERN_CONST PIMAGE_TLS_CALLBACK _ptr_##func = func; \ +EXTERN_C_END \ + \ +__pragma (comment (linker, "/INCLUDE:" SYMBOL_PREFIX "_tls_used")) \ +__pragma (comment (linker, "/INCLUDE:" SYMBOL_PREFIX "_ptr_" #func)) + +#else /* _MSC_VER */ + +#define DEFINE_TLS_CALLBACK(func) \ +static void NTAPI func (PVOID, DWORD, PVOID); \ + \ +__attribute__ ((used, section (".CRT$XLD"))) \ +static const PIMAGE_TLS_CALLBACK _ptr_##func = func; + + +#endif /* !_MSC_VER */ + +DEFINE_TLS_CALLBACK (cairo_win32_tls_callback); + +#endif /* CAIRO_MUTEX_IMPL_WIN32 */ commit 3bc6b616a46655ded95a6f5e016f43ca30be96d7 Author: Luca Bacci Date: Thu Jan 16 17:33:52 2025 +0100 Win32: Rename _cairo_win32_print_gdi_error function ...to _cairo_win32_print_api_error, since it should not be used with most GDI functions. Also move the function definition to cairo-win32-system.c and change argument signature. diff --git a/src/win32/cairo-win32-display-surface.c b/src/win32/cairo-win32-display-surface.c index 8be03c03c..7f0770b4f 100644 --- a/src/win32/cairo-win32-display-surface.c +++ b/src/win32/cairo-win32-display-surface.c @@ -210,7 +210,7 @@ _create_dc_and_bitmap (cairo_win32_display_surface_t *surface, &bits, NULL, 0); if (!surface->bitmap) { - _cairo_win32_print_gdi_error ("_create_dc_and_bitmap:CreateDIBSection"); + _cairo_win32_print_api_error (__FUNCTION__, "CreateDIBSection"); goto FAIL; } @@ -549,7 +549,7 @@ _cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags) fallback->win32.dc, surface->win32.extents.x, surface->win32.extents.y, SRCCOPY)) - status = _cairo_win32_print_gdi_error ("_cairo_win32_display_surface_flush:BitBlt"); + status = _cairo_win32_print_api_error (__FUNCTION__, "BitBlt"); } else if (damage->region) { int n = cairo_region_num_rectangles (damage->region), i; for (i = 0; i < n; i++) { @@ -566,7 +566,7 @@ _cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags) fallback->win32.dc, rect.x, rect.y, SRCCOPY)) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_display_surface_flush:BitBlt"); + status = _cairo_win32_print_api_error (__FUNCTION__, "BitBlt"); break; } } diff --git a/src/win32/cairo-win32-font.c b/src/win32/cairo-win32-font.c index d1ad087f9..1412c8bbc 100644 --- a/src/win32/cairo-win32-font.c +++ b/src/win32/cairo-win32-font.c @@ -258,7 +258,7 @@ _have_cleartype_quality (void) version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (!GetVersionEx (&version_info)) { - _cairo_win32_print_gdi_error ("_have_cleartype_quality"); + _cairo_win32_print_api_error (__FUNCTION__, "GetVersionEx"); return FALSE; } @@ -274,7 +274,7 @@ cairo_win32_get_system_text_quality (void) UINT smoothing_type; if (!SystemParametersInfo (SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { - _cairo_win32_print_gdi_error ("_get_system_quality"); + _cairo_win32_print_api_error (__FUNCTION__, "SystemParametersInfo"); return DEFAULT_QUALITY; } @@ -282,7 +282,7 @@ cairo_win32_get_system_text_quality (void) if (_have_cleartype_quality ()) { if (!SystemParametersInfo (SPI_GETFONTSMOOTHINGTYPE, 0, &smoothing_type, 0)) { - _cairo_win32_print_gdi_error ("_get_system_quality"); + _cairo_win32_print_api_error (__FUNCTION__, "SystemParametersInfo"); return DEFAULT_QUALITY; } diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h index 4e180a81e..6dd60f1f0 100644 --- a/src/win32/cairo-win32-private.h +++ b/src/win32/cairo-win32-private.h @@ -176,7 +176,7 @@ const cairo_compositor_t * _cairo_win32_gdi_compositor_get (void); cairo_status_t -_cairo_win32_print_gdi_error (const char *context); +_cairo_win32_print_api_error (const char *context, const char *api); cairo_bool_t _cairo_surface_is_win32 (const cairo_surface_t *surface); diff --git a/src/win32/cairo-win32-surface.c b/src/win32/cairo-win32-surface.c index 8e22996da..e1ac51558 100644 --- a/src/win32/cairo-win32-surface.c +++ b/src/win32/cairo-win32-surface.c @@ -79,41 +79,6 @@ * Since: 1.0 **/ -/** - * _cairo_win32_print_gdi_error: - * @context: context string to display along with the error - * - * Helper function to dump out a human readable form of the - * current error code. - * - * Return value: A cairo status code for the error code - **/ -cairo_status_t -_cairo_win32_print_gdi_error (const char *context) -{ - void *lpMsgBuf; - DWORD last_error = GetLastError (); - - if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - last_error, - MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPWSTR) &lpMsgBuf, - 0, NULL)) { - fprintf (stderr, "%s: Unknown GDI error", context); - } else { - fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf); - - LocalFree (lpMsgBuf); - } - - fflush (stderr); - - return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); -} - cairo_bool_t _cairo_win32_surface_get_extents (void *abstract_surface, cairo_rectangle_int_t *rectangle) diff --git a/src/win32/cairo-win32-system.c b/src/win32/cairo-win32-system.c index 01bbe89df..7163d4470 100644 --- a/src/win32/cairo-win32-system.c +++ b/src/win32/cairo-win32-system.c @@ -44,14 +44,44 @@ * And no other function should live here. */ - #include "cairoint.h" +#include + +/** + * _cairo_win32_print_api_error: + * @context: context string to display along with the error + * @api: name of the failing api + * + * Helper function to dump out a human readable form of the + * current error code. + * + * Return value: A cairo status code for the error code + **/ +cairo_status_t +_cairo_win32_print_api_error (const char *context, const char *api) +{ + const DWORD lang_id = MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT); + const DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM; + const DWORD last_error = GetLastError (); + void *lpMsgBuf = NULL; + + if (!FormatMessageW (flags, NULL, last_error, lang_id, (LPWSTR) &lpMsgBuf, 0, NULL)) { + fprintf (stderr, "%s: %s failed with error code %lu\n", context, api, last_error); + } + else { + fprintf (stderr, "%s: %s failed - %S\n", context, api, (wchar_t *)lpMsgBuf); + LocalFree (lpMsgBuf); + } + + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); +} + #if CAIRO_MUTEX_IMPL_WIN32 #if !CAIRO_WIN32_STATIC_BUILD -#include - /* declare to avoid "no previous prototype for 'DllMain'" warning */ BOOL WINAPI DllMain (HINSTANCE hinstDLL, commit 85f308f690f522e2f69e489c3f54587cf20d2c01 Author: Luca Bacci Date: Thu Jan 16 14:44:01 2025 +0100 Win32: Avoid checking the last error for GDI functions that don't set it Most GDI functions do not set the last error, so GetLastError() returns unrelated error codes. There are some exceptions, however, like BitBlt and CreateDIBSection. Whether a GDI function sets the last error is stated in the reference documentation on MSDN. diff --git a/boilerplate/cairo-boilerplate-win32-printing.c b/boilerplate/cairo-boilerplate-win32-printing.c index a156deb24..9177480fc 100644 --- a/boilerplate/cairo-boilerplate-win32-printing.c +++ b/boilerplate/cairo-boilerplate-win32-printing.c @@ -36,36 +36,6 @@ #include -static cairo_status_t -_cairo_win32_print_gdi_error (const char *context) -{ - void *lpMsgBuf; - DWORD last_error = GetLastError (); - - if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS | - FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - last_error, - MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPWSTR) &lpMsgBuf, - 0, NULL)) { - fprintf (stderr, "%s: Unknown GDI error", context); - } else { - fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf); - - LocalFree (lpMsgBuf); - } - - fflush (stderr); - - /* We should switch off of last_status, but we'd either return - * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there - * is no CAIRO_STATUS_UNKNOWN_ERROR. - */ - return CAIRO_STATUS_NO_MEMORY; -} - static cairo_user_data_key_t win32_closure_key; typedef struct _win32_target_closure { @@ -159,7 +129,7 @@ create_printer_dc (win32_target_closure_t *ptc) xform.eDx = 0; xform.eDy = printable_height - ptc->height*y_dpi/72.0; if (!SetWorldTransform (ptc->dc, &xform)) { - _cairo_win32_print_gdi_error ("cairo-boilerplate-win32-printing:SetWorldTransform"); + fprintf (stderr, "%s:%s\n", "cairo-boilerplate-win32-printing", "SetWorldTransform"); return; } diff --git a/src/win32/cairo-win32-display-surface.c b/src/win32/cairo-win32-display-surface.c index 4f4e8f847..8be03c03c 100644 --- a/src/win32/cairo-win32-display-surface.c +++ b/src/win32/cairo-win32-display-surface.c @@ -95,8 +95,6 @@ _create_dc_and_bitmap (cairo_win32_display_surface_t *surface, unsigned char **bits_out, int *rowstride_out) { - cairo_status_t status; - BITMAPINFO *bitmap_info = NULL; struct { BITMAPINFOHEADER bmiHeader; @@ -201,16 +199,20 @@ _create_dc_and_bitmap (cairo_win32_display_surface_t *surface, } surface->win32.dc = CreateCompatibleDC (original_dc); - if (!surface->win32.dc) + if (!surface->win32.dc) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "CreateCompatibleDC"); goto FAIL; + } surface->bitmap = CreateDIBSection (surface->win32.dc, bitmap_info, DIB_RGB_COLORS, &bits, NULL, 0); - if (!surface->bitmap) + if (!surface->bitmap) { + _cairo_win32_print_gdi_error ("_create_dc_and_bitmap:CreateDIBSection"); goto FAIL; + } surface->is_dib = TRUE; @@ -218,8 +220,10 @@ _create_dc_and_bitmap (cairo_win32_display_surface_t *surface, surface->saved_dc_bitmap = SelectObject (surface->win32.dc, surface->bitmap); - if (!surface->saved_dc_bitmap) + if (!surface->saved_dc_bitmap) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SelectObject"); goto FAIL; + } if (bitmap_info && num_palette > 2) free (bitmap_info); @@ -256,8 +260,6 @@ _create_dc_and_bitmap (cairo_win32_display_surface_t *surface, return CAIRO_STATUS_SUCCESS; FAIL: - status = _cairo_win32_print_gdi_error (__FUNCTION__); - if (bitmap_info && num_palette > 2) free (bitmap_info); @@ -276,7 +278,7 @@ _create_dc_and_bitmap (cairo_win32_display_surface_t *surface, surface->win32.dc = NULL; } - return status; + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); } static cairo_surface_t * @@ -547,7 +549,7 @@ _cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags) fallback->win32.dc, surface->win32.extents.x, surface->win32.extents.y, SRCCOPY)) - status = _cairo_win32_print_gdi_error (__FUNCTION__); + status = _cairo_win32_print_gdi_error ("_cairo_win32_display_surface_flush:BitBlt"); } else if (damage->region) { int n = cairo_region_num_rectangles (damage->region), i; for (i = 0; i < n; i++) { @@ -564,7 +566,7 @@ _cairo_win32_display_surface_flush (void *abstract_surface, unsigned flags) fallback->win32.dc, rect.x, rect.y, SRCCOPY)) { - status = _cairo_win32_print_gdi_error (__FUNCTION__); + status = _cairo_win32_print_gdi_error ("_cairo_win32_display_surface_flush:BitBlt"); break; } } @@ -615,7 +617,7 @@ _cairo_win32_save_initial_clip (HDC hdc, cairo_win32_display_surface_t *surface) clipBoxType = GetClipBox (hdc, &rect); if (clipBoxType == ERROR) { - _cairo_win32_print_gdi_error (__FUNCTION__); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetClipBox"); SetGraphicsMode (hdc, gm); /* XXX: Can we make a more reasonable guess at the error cause here? */ return _cairo_error (CAIRO_STATUS_DEVICE_ERROR); @@ -748,8 +750,10 @@ _cairo_win32_display_surface_set_clip (cairo_win32_display_surface_t *surface, /* AND the new region into our DC */ status = CAIRO_STATUS_SUCCESS; - if (ExtSelectClipRgn (surface->win32.dc, gdi_region, RGN_AND) == ERROR) - status = _cairo_win32_print_gdi_error (__FUNCTION__); + if (ExtSelectClipRgn (surface->win32.dc, gdi_region, RGN_AND) == ERROR) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "ExtSelectClipRgn"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } DeleteObject (gdi_region); diff --git a/src/win32/cairo-win32-font.c b/src/win32/cairo-win32-font.c index 265f93294..d1ad087f9 100644 --- a/src/win32/cairo-win32-font.c +++ b/src/win32/cairo-win32-font.c @@ -159,12 +159,12 @@ _get_global_font_dc (void) if (!hdc) { hdc = CreateCompatibleDC (NULL); if (!hdc) { - _cairo_win32_print_gdi_error ("_get_global_font_dc"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "CreateCompatibleDC"); return NULL; } if (!SetGraphicsMode (hdc, GM_ADVANCED)) { - _cairo_win32_print_gdi_error ("_get_global_font_dc"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SetGraphicsMode"); DeleteDC (hdc); return NULL; } @@ -409,8 +409,10 @@ _win32_scaled_font_set_world_transform (cairo_win32_scaled_font_t *scaled_font, _cairo_matrix_to_win32_xform (&scaled_font->logical_to_device, &xform); - if (!SetWorldTransform (hdc, &xform)) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_world_transform"); + if (!SetWorldTransform (hdc, &xform)) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SetWorldTransform"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } return CAIRO_STATUS_SUCCESS; } @@ -418,8 +420,10 @@ _win32_scaled_font_set_world_transform (cairo_win32_scaled_font_t *scaled_font, static cairo_status_t _win32_scaled_font_set_identity_transform (HDC hdc) { - if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_set_identity_transform"); + if (!ModifyWorldTransform (hdc, NULL, MWT_IDENTITY)) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "ModifyWorldTransform"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } return CAIRO_STATUS_SUCCESS; } @@ -437,8 +441,10 @@ _win32_scaled_font_get_scaled_hfont (cairo_win32_scaled_font_t *scaled_font, logfont.lfQuality = scaled_font->quality; scaled_font->scaled_hfont = CreateFontIndirectW (&logfont); - if (!scaled_font->scaled_hfont) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_scaled_hfont"); + if (!scaled_font->scaled_hfont) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "CreateFontIndirect"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } } *hfont_out = scaled_font->scaled_hfont; @@ -462,25 +468,30 @@ _win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font, if (status) return status; - if (! SelectObject (hdc, scaled_hfont)) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:SelectObject"); + if (! SelectObject (hdc, scaled_hfont)) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SelectObject"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } otm_size = GetOutlineTextMetrics (hdc, 0, NULL); - if (! otm_size) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); + if (! otm_size) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetOutlineTextMetrics"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } otm = _cairo_malloc (otm_size); if (otm == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (! GetOutlineTextMetrics (hdc, otm_size, otm)) { - status = _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:GetOutlineTextMetrics"); - free (otm); - return status; + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetOutlineTextMetrics"); + free (otm); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); } scaled_font->em_square = otm->otmEMSquare; free (otm); + otm = NULL; logfont = scaled_font->logfont; logfont.lfHeight = -scaled_font->em_square; @@ -490,8 +501,10 @@ _win32_scaled_font_get_unscaled_hfont (cairo_win32_scaled_font_t *scaled_font, logfont.lfQuality = scaled_font->quality; scaled_font->unscaled_hfont = CreateFontIndirectW (&logfont); - if (! scaled_font->unscaled_hfont) - return _cairo_win32_print_gdi_error ("_win32_scaled_font_get_unscaled_hfont:CreateIndirect"); + if (! scaled_font->unscaled_hfont) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "CreateFontIndirect"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } } *hfont_out = scaled_font->unscaled_hfont; @@ -511,8 +524,10 @@ _cairo_win32_scaled_font_select_unscaled_font (cairo_scaled_font_t *scaled_font, return status; old_hfont = SelectObject (hdc, hfont); - if (!old_hfont) - return _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_select_unscaled_font"); + if (!old_hfont) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "CreateSolidBrush"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } status = _win32_scaled_font_set_identity_transform (hdc); if (status) { @@ -651,7 +666,7 @@ _cairo_win32_scaled_font_ucs4_to_index (void *abstract_font, unicode[0] = ucs4; unicode[1] = 0; if (GetGlyphIndicesW (hdc, unicode, 1, &glyph_index, 0) == GDI_ERROR) { - _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_ucs4_to_index:GetGlyphIndicesW"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetGlyphIndices"); glyph_index = 0; } @@ -681,7 +696,8 @@ _cairo_win32_scaled_font_set_metrics (cairo_win32_scaled_font_t *scaled_font) return status; if (!GetTextMetrics (hdc, &metrics)) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_set_metrics:GetTextMetrics"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetTextMetrics"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); } cairo_win32_scaled_font_done_font (&scaled_font->base); @@ -766,7 +782,8 @@ _cairo_win32_scaled_font_init_glyph_metrics (cairo_win32_scaled_font_t *scaled_f return status; if (!GetCharWidth32(hdc, charIndex, charIndex, &width)) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_init_glyph_metrics:GetCharWidth32"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetCharWidth32"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); width = 0; } cairo_win32_scaled_font_done_font (&scaled_font->base); @@ -965,7 +982,8 @@ _flush_glyphs (cairo_glyph_state_t *state) elements, state->glyphs.num_elements, dx_elements)) { - return _cairo_win32_print_gdi_error ("_flush_glyphs"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "CreateSolidBrush"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); } _cairo_array_truncate (&state->glyphs, 0); @@ -1045,8 +1063,10 @@ _draw_glyphs_on_surface (cairo_win32_surface_t *surface, cairo_status_t status, status2; int i; - if (!SaveDC (surface->dc)) - return _cairo_win32_print_gdi_error ("_draw_glyphs_on_surface:SaveDC"); + if (!SaveDC (surface->dc)) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SaveDC"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } status = cairo_win32_scaled_font_select_font (&scaled_font->base, surface->dc); if (status) @@ -1167,8 +1187,8 @@ _cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font, res = GetFontUnicodeRanges(hdc, NULL); if (res == 0) { - status = _cairo_win32_print_gdi_error ( - "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetFontUnicodeRanges"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto exit1; } @@ -1180,8 +1200,8 @@ _cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font, res = GetFontUnicodeRanges(hdc, glyph_set); if (res == 0) { - status = _cairo_win32_print_gdi_error ( - "_cairo_win32_scaled_font_index_to_ucs4:GetFontUnicodeRanges"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetFontUnicodeRanges"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto exit1; } @@ -1206,8 +1226,8 @@ _cairo_win32_scaled_font_index_to_ucs4 (void *abstract_font, utf16[j] = 0; if (GetGlyphIndicesW (hdc, utf16, num_glyphs, glyph_indices, 0) == GDI_ERROR) { - status = _cairo_win32_print_gdi_error ( - "_cairo_win32_scaled_font_index_to_ucs4:GetGlyphIndicesW"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetGlyphIndices"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto exit2; } @@ -1476,7 +1496,8 @@ _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font &metrics, 0, NULL, &matrix); if (bytesGlyph == GDI_ERROR) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetGlyphOutline"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto CLEANUP_FONT; } @@ -1489,7 +1510,8 @@ _cairo_win32_scaled_font_init_glyph_path (cairo_win32_scaled_font_t *scaled_font if (GetGlyphOutlineW (hdc, _cairo_scaled_glyph_index (scaled_glyph), GGO_NATIVE | GGO_GLYPH_INDEX, &metrics, bytesGlyph, buffer, &matrix) == GDI_ERROR) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_scaled_font_glyph_path"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GetGlyphOutline"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto CLEANUP_BUFFER; } @@ -1987,14 +2009,16 @@ cairo_win32_scaled_font_select_font (cairo_scaled_font_t *scaled_font, return status; old_hfont = SelectObject (hdc, hfont); - if (!old_hfont) - return _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SelectObject"); + if (!old_hfont) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SelectObject"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } old_mode = SetGraphicsMode (hdc, GM_ADVANCED); if (!old_mode) { - status = _cairo_win32_print_gdi_error ("cairo_win32_scaled_font_select_font:SetGraphicsMode"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SetGraphicsMode"); SelectObject (hdc, old_hfont); - return status; + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); } status = _win32_scaled_font_set_world_transform ((cairo_win32_scaled_font_t *)scaled_font, hdc); diff --git a/src/win32/cairo-win32-gdi-compositor.c b/src/win32/cairo-win32-gdi-compositor.c index 268ca0100..347cd7ed5 100644 --- a/src/win32/cairo-win32-gdi-compositor.c +++ b/src/win32/cairo-win32-gdi-compositor.c @@ -166,8 +166,10 @@ fill_boxes (cairo_win32_display_surface_t *dst, fb.dc = dst->win32.dc; fb.brush = CreateSolidBrush (color_to_rgb(color)); - if (!fb.brush) - return _cairo_win32_print_gdi_error (__FUNCTION__); + if (!fb.brush) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "CreateSolidBrush"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } if (! _cairo_boxes_for_each_box (boxes, fill_box, &fb)) status = CAIRO_INT_STATUS_UNSUPPORTED; diff --git a/src/win32/cairo-win32-printing-surface.c b/src/win32/cairo-win32-printing-surface.c index 8305ede9c..c5b5f5cab 100644 --- a/src/win32/cairo-win32-printing-surface.c +++ b/src/win32/cairo-win32-printing-surface.c @@ -477,8 +477,10 @@ _cairo_win32_printing_surface_select_solid_brush (cairo_win32_printing_surface_t color = _cairo_win32_printing_surface_flatten_transparency (surface, &pattern->color); surface->brush = CreateSolidBrush (color); - if (!surface->brush) - return _cairo_win32_print_gdi_error ("_cairo_win32_surface_select_solid_brush(CreateSolidBrush)"); + if (!surface->brush) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "CreateSolidBrush"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } surface->old_brush = SelectObject (surface->win32.dc, surface->brush); return CAIRO_STATUS_SUCCESS; @@ -501,13 +503,17 @@ _cairo_win32_printing_surface_get_ctm_clip_box (cairo_win32_printing_surface_t * XFORM xform; _cairo_matrix_to_win32_xform (&surface->ctm, &xform); - if (!ModifyWorldTransform (surface->win32.dc, &xform, MWT_LEFTMULTIPLY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:ModifyWorldTransform"); + if (!ModifyWorldTransform (surface->win32.dc, &xform, MWT_LEFTMULTIPLY)) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "ModifyWorldTransform"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } GetClipBox (surface->win32.dc, clip); _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); - if (!SetWorldTransform (surface->win32.dc, &xform)) - return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_get_clip_box:SetWorldTransform"); + if (!SetWorldTransform (surface->win32.dc, &xform)) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SetWorldTransform"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } return CAIRO_STATUS_SUCCESS; } @@ -887,7 +893,8 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_printing_surface_ _cairo_matrix_to_win32_xform (&m, &xform); if (! SetWorldTransform (surface->win32.dc, &xform)) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SetWorldTransform"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto CLEANUP_OPAQUE_IMAGE; } @@ -922,7 +929,8 @@ _cairo_win32_printing_surface_paint_image_pattern (cairo_win32_printing_surface_ DIB_RGB_COLORS, SRCCOPY)) { - status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint(StretchDIBits)"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "StretchDIBits"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto CLEANUP_OPAQUE_IMAGE; } } @@ -996,8 +1004,10 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_printing_surface _cairo_matrix_to_win32_xform (&mat, &xform); - if (!SetWorldTransform (surface->win32.dc, &xform)) - return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:SetWorldTransform2"); + if (!SetWorldTransform (surface->win32.dc, &xform)) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SetWorldTransform"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } GetClipBox (surface->win32.dc, &clip); @@ -1086,7 +1096,10 @@ _cairo_win32_printing_surface_paint_linear_pattern (cairo_win32_printing_surface vert, total_verts, rect, total_rects, GRADIENT_FILL_RECT_H)) - return _cairo_win32_print_gdi_error ("_win32_printing_surface_paint_linear_pattern:GradientFill"); + { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "GradientFill"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } free (rect); free (vert); @@ -1556,13 +1569,15 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, style->num_dashes, dash_array); if (pen == NULL) { - status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:ExtCreatePen"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "ExtCreatePen"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto cleanup_composite; } obj = SelectObject (surface->win32.dc, pen); if (obj == NULL) { - status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectObject"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SelectObject"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto cleanup_composite; } @@ -1582,7 +1597,8 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, xform.eDy = 0.0f; if (!ModifyWorldTransform (surface->win32.dc, &xform, MWT_LEFTMULTIPLY)) { - status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "ModifyWorldTransform"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto cleanup_composite; } @@ -1590,18 +1606,21 @@ _cairo_win32_printing_surface_stroke (void *abstract_surface, StrokePath (surface->win32.dc); } else { if (!WidenPath (surface->win32.dc)) { - status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "WidenPath"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto cleanup_composite; } if (!SelectClipPath (surface->win32.dc, RGN_AND)) { - status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SelectClipPath"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto cleanup_composite; } /* Return to device space to paint the pattern */ _cairo_matrix_to_win32_xform (&surface->gdi_ctm, &xform); if (!SetWorldTransform (surface->win32.dc, &xform)) { - status = _cairo_win32_print_gdi_error ("_win32_surface_stroke:ModifyWorldTransform"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "SetWorldTransform"); + status = _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); goto cleanup_composite; } status = _cairo_win32_printing_surface_paint_pattern (surface, source, &extents.bounded); @@ -2102,8 +2121,10 @@ _cairo_win32_printing_surface_start_page (void *abstract_surface) surface->ctm.x0 = xform.eDx; surface->ctm.y0 = xform.eDy; cairo_matrix_init_identity (&surface->gdi_ctm); - if (!ModifyWorldTransform (surface->win32.dc, NULL, MWT_IDENTITY)) - return _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_start_page:ModifyWorldTransform"); + if (!ModifyWorldTransform (surface->win32.dc, NULL, MWT_IDENTITY)) { + fprintf (stderr, "%s:%s\n", __FUNCTION__, "ModifyWorldTransform"); + return _cairo_error (CAIRO_STATUS_WIN32_GDI_ERROR); + } } surface->has_ctm = !_cairo_matrix_is_identity (&surface->ctm); diff --git a/src/win32/cairo-win32-surface.c b/src/win32/cairo-win32-surface.c index 6862e2090..8e22996da 100644 --- a/src/win32/cairo-win32-surface.c +++ b/src/win32/cairo-win32-surface.c @@ -319,7 +319,7 @@ _cairo_win32_surface_emit_glyphs (cairo_win32_surface_t *dst, num_glyphs, dxy_buf); if (!win_result) { - _cairo_win32_print_gdi_error("_cairo_win32_surface_show_glyphs(ExtTextOutW failed)"); + fprintf (stderr, "%s:%s\n", __FUNCTION__, "ExtTextOut"); } RestoreDC(dst->dc, -1); commit f289bea1d28718c94e8451a80db607b88d7a3692 Author: Luca Bacci Date: Thu Jan 16 15:31:23 2025 +0100 Readme: Update Windows OS requirements to Windows Vista diff --git a/README.md b/README.md index ebe3f99e1..2bd26068b 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ system pixman. #### Windows backend -- Microsoft Windows 2000 or newer. +- Microsoft Windows Vista or newer. #### XCB backend commit e10adb1c2b81c8b3f1e280b7d32ff63bbfe56c71 Author: Luca Bacci Date: Thu Jan 16 14:33:58 2025 +0100 Win32: Pass FORMAT_MESSAGE_IGNORE_INSERTS This should always be passed when retreving messages from third parties (unless the third party documents format strings) See https://devblogs.microsoft.com/oldnewthing/20071128-00/?p=24353 diff --git a/boilerplate/cairo-boilerplate-win32-printing.c b/boilerplate/cairo-boilerplate-win32-printing.c index a3802d2e9..a156deb24 100644 --- a/boilerplate/cairo-boilerplate-win32-printing.c +++ b/boilerplate/cairo-boilerplate-win32-printing.c @@ -43,6 +43,7 @@ _cairo_win32_print_gdi_error (const char *context) DWORD last_error = GetLastError (); if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, last_error, diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index f7d1c4767..74d12c6a0 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -110,6 +110,7 @@ _cairo_dwrite_error (HRESULT hr, const char *context) void *lpMsgBuf; if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, hr, diff --git a/src/win32/cairo-win32-surface.c b/src/win32/cairo-win32-surface.c index 6e82638ff..6862e2090 100644 --- a/src/win32/cairo-win32-surface.c +++ b/src/win32/cairo-win32-surface.c @@ -95,6 +95,7 @@ _cairo_win32_print_gdi_error (const char *context) DWORD last_error = GetLastError (); if (!FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, NULL, last_error, commit d0ee67a14285788f3b423df5b87ab81c08f50c8f Author: Luca Bacci Date: Thu Jan 16 14:26:04 2025 +0100 Win32: Remove unused code and defines for old toolchains diff --git a/boilerplate/cairo-boilerplate-win32-printing.c b/boilerplate/cairo-boilerplate-win32-printing.c index e8fcdcef5..a3802d2e9 100644 --- a/boilerplate/cairo-boilerplate-win32-printing.c +++ b/boilerplate/cairo-boilerplate-win32-printing.c @@ -36,22 +36,6 @@ #include -#if !defined(POSTSCRIPT_IDENTIFY) -# define POSTSCRIPT_IDENTIFY 0x1015 -#endif - -#if !defined(PSIDENT_GDICENTRIC) -# define PSIDENT_GDICENTRIC 0x0000 -#endif - -#if !defined(GET_PS_FEATURESETTING) -# define GET_PS_FEATURESETTING 0x1019 -#endif - -#if !defined(FEATURESETTING_PSLEVEL) -# define FEATURESETTING_PSLEVEL 0x0002 -#endif - static cairo_status_t _cairo_win32_print_gdi_error (const char *context) { diff --git a/src/win32/cairo-win32-device.c b/src/win32/cairo-win32-device.c index 575239352..c55b0789e 100644 --- a/src/win32/cairo-win32-device.c +++ b/src/win32/cairo-win32-device.c @@ -76,20 +76,6 @@ static const cairo_device_backend_t _cairo_win32_device_backend = { _cairo_win32_device_destroy, }; -#if 0 -D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat( - DXGI_FORMAT_B8G8R8A8_UNORM, - D2D1_ALPHA_MODE_IGNORE), - 0, - 0, - D2D1_RENDER_TARGET_USAGE_NONE, - D2D1_FEATURE_LEVEL_DEFAULT - ); - -hr = m_pD2DFactory->CreateDCRenderTarget(&props, &device->d2d); -#endif - cairo_device_t * _cairo_win32_device_get (void) { diff --git a/src/win32/cairo-win32-display-surface.c b/src/win32/cairo-win32-display-surface.c index 2800052bc..4f4e8f847 100644 --- a/src/win32/cairo-win32-display-surface.c +++ b/src/win32/cairo-win32-display-surface.c @@ -56,10 +56,6 @@ #include #include -#if defined(__MINGW32__) && !defined(ETO_PDY) -# define ETO_PDY 0x2000 -#endif - #define PELS_72DPI ((LONG)(72. / 0.0254)) /** diff --git a/src/win32/cairo-win32-font.c b/src/win32/cairo-win32-font.c index 3ad4f7ff4..265f93294 100644 --- a/src/win32/cairo-win32-font.c +++ b/src/win32/cairo-win32-font.c @@ -45,19 +45,6 @@ #include -#ifndef SPI_GETFONTSMOOTHINGTYPE -#define SPI_GETFONTSMOOTHINGTYPE 0x200a -#endif -#ifndef FE_FONTSMOOTHINGCLEARTYPE -#define FE_FONTSMOOTHINGCLEARTYPE 2 -#endif -#ifndef CLEARTYPE_QUALITY -#define CLEARTYPE_QUALITY 5 -#endif -#ifndef TT_PRIM_CSPLINE -#define TT_PRIM_CSPLINE 3 -#endif - #define CMAP_TAG 0x70616d63 /** diff --git a/src/win32/cairo-win32-gdi-compositor.c b/src/win32/cairo-win32-gdi-compositor.c index 08e5abfb3..268ca0100 100644 --- a/src/win32/cairo-win32-gdi-compositor.c +++ b/src/win32/cairo-win32-gdi-compositor.c @@ -55,23 +55,6 @@ #include "cairo-surface-inline.h" #include "cairo-surface-offset-private.h" -#if !defined(AC_SRC_OVER) -#define AC_SRC_OVER 0x00 -#pragma pack(1) -typedef struct { - BYTE BlendOp; - BYTE BlendFlags; - BYTE SourceConstantAlpha; - BYTE AlphaFormat; -}BLENDFUNCTION; -#pragma pack() -#endif - -/* for compatibility with VC++ 6 */ -#ifndef AC_SRC_ALPHA -#define AC_SRC_ALPHA 0x01 -#endif - #define PELS_72DPI ((LONG)(72. / 0.0254)) /* the low-level interface */ diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h index 594e2b085..4e180a81e 100644 --- a/src/win32/cairo-win32-private.h +++ b/src/win32/cairo-win32-private.h @@ -44,13 +44,6 @@ #include "cairo-surface-clipper-private.h" #include "cairo-surface-private.h" -#ifndef SHADEBLENDCAPS -#define SHADEBLENDCAPS 120 -#endif -#ifndef SB_NONE -#define SB_NONE 0 -#endif - #define WIN32_FONT_LOGICAL_SCALE 32 CAIRO_BEGIN_DECLS diff --git a/src/win32/cairo-win32-surface.c b/src/win32/cairo-win32-surface.c index ca5c9d823..6e82638ff 100644 --- a/src/win32/cairo-win32-surface.c +++ b/src/win32/cairo-win32-surface.c @@ -53,10 +53,6 @@ #include #include -#if defined(__MINGW32__) && !defined(ETO_PDY) -# define ETO_PDY 0x2000 -#endif - /** * SECTION:cairo-win32 * @Title: Win32 Surfaces commit 361240657c2d668a719cdf4e999867025c8caeb1 Author: Luca Bacci Date: Thu Jan 16 14:19:55 2025 +0100 Win32: Do not load msimg32.dll at runtime This commit drops support for Windows 98 diff --git a/src/win32/cairo-win32-device.c b/src/win32/cairo-win32-device.c index 42f013392..575239352 100644 --- a/src/win32/cairo-win32-device.c +++ b/src/win32/cairo-win32-device.c @@ -90,18 +90,6 @@ D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_T hr = m_pD2DFactory->CreateDCRenderTarget(&props, &device->d2d); #endif -static void * -_cairo_win32_device_get_alpha_blend (cairo_win32_device_t *device) -{ - void *func = NULL; - - device->msimg32_dll = LoadLibraryW (L"msimg32"); - if (device->msimg32_dll) - func = GetProcAddress (device->msimg32_dll, "AlphaBlend"); - - return func; -} - cairo_device_t * _cairo_win32_device_get (void) { @@ -118,9 +106,6 @@ _cairo_win32_device_get (void) device->compositor = _cairo_win32_gdi_compositor_get (); - device->msimg32_dll = NULL; - device->alpha_blend = _cairo_win32_device_get_alpha_blend (device); - if (_cairo_atomic_ptr_cmpxchg ((cairo_atomic_intptr_t *)&__cairo_win32_device, NULL, device)) return cairo_device_reference(&device->base); diff --git a/src/win32/cairo-win32-gdi-compositor.c b/src/win32/cairo-win32-gdi-compositor.c index bc1f69e70..08e5abfb3 100644 --- a/src/win32/cairo-win32-gdi-compositor.c +++ b/src/win32/cairo-win32-gdi-compositor.c @@ -105,7 +105,6 @@ struct copy_box { int tx, ty; HDC dst, src; BLENDFUNCTION bf; - cairo_win32_alpha_blend_func_t alpha_blend; }; static cairo_bool_t copy_box (cairo_box_t *box, void *closure) @@ -131,9 +130,9 @@ static cairo_bool_t alpha_box (cairo_box_t *box, void *closure) int height = _cairo_fixed_integer_part (box->p2.y - box->p1.y); TRACE ((stderr, "%s\n", __FUNCTION__)); - return cb->alpha_blend (cb->dst, x, y, width, height, - cb->src, x + cb->tx, y + cb->ty, width, height, - cb->bf); + return AlphaBlend (cb->dst, x, y, width, height, + cb->src, x + cb->tx, y + cb->ty, width, height, + cb->bf); } struct upload_box { @@ -382,7 +381,6 @@ alpha_blend_boxes (cairo_win32_display_surface_t *dst, cb.bf.BlendFlags = 0; cb.bf.SourceConstantAlpha = alpha; cb.bf.AlphaFormat = (src->win32.format == CAIRO_FORMAT_ARGB32) ? AC_SRC_ALPHA : 0; - cb.alpha_blend = to_win32_device(dst->win32.base.device)->alpha_blend; cb.tx += cb.limit.x; cb.ty += cb.limit.y; @@ -397,10 +395,7 @@ alpha_blend_boxes (cairo_win32_display_surface_t *dst, static cairo_bool_t can_alpha_blend (cairo_win32_display_surface_t *dst) { - if ((dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND) == 0) - return FALSE; - - return to_win32_device(dst->win32.base.device)->alpha_blend != NULL; + return (dst->win32.flags & CAIRO_WIN32_SURFACE_CAN_ALPHABLEND) != 0; } static cairo_status_t diff --git a/src/win32/cairo-win32-private.h b/src/win32/cairo-win32-private.h index 6af09c0e1..594e2b085 100644 --- a/src/win32/cairo-win32-private.h +++ b/src/win32/cairo-win32-private.h @@ -168,26 +168,10 @@ typedef struct _cairo_win32_printing_surface { } cairo_win32_printing_surface_t; #define to_win32_printing_surface(S) ((cairo_win32_printing_surface_t *)(S)) -typedef BOOL (WINAPI *cairo_win32_alpha_blend_func_t) (HDC hdcDest, - int nXOriginDest, - int nYOriginDest, - int nWidthDest, - int hHeightDest, - HDC hdcSrc, - int nXOriginSrc, - int nYOriginSrc, - int nWidthSrc, - int nHeightSrc, - BLENDFUNCTION blendFunction); - typedef struct _cairo_win32_device { cairo_device_t base; - HMODULE msimg32_dll; - const cairo_compositor_t *compositor; - - cairo_win32_alpha_blend_func_t alpha_blend; } cairo_win32_device_t; #define to_win32_device(D) ((cairo_win32_device_t *)(D)) #define to_win32_device_from_surface(S) to_win32_device(((cairo_surface_t *)(S))->device) commit 613df92660903f5a5fa31199ba568b6d99fee9bd Author: Luca Bacci Date: Thu Jan 16 14:12:34 2025 +0100 Win32: Remove check for Windows 98 diff --git a/src/win32/cairo-win32-device.c b/src/win32/cairo-win32-device.c index e000b11f6..42f013392 100644 --- a/src/win32/cairo-win32-device.c +++ b/src/win32/cairo-win32-device.c @@ -90,26 +90,11 @@ D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(D2D1_RENDER_T hr = m_pD2DFactory->CreateDCRenderTarget(&props, &device->d2d); #endif -static cairo_bool_t is_win98 (void) -{ - OSVERSIONINFO os; - - os.dwOSVersionInfoSize = sizeof (os); - GetVersionEx (&os); - - return (VER_PLATFORM_WIN32_WINDOWS == os.dwPlatformId && - os.dwMajorVersion == 4 && - os.dwMinorVersion == 10); -} - static void * _cairo_win32_device_get_alpha_blend (cairo_win32_device_t *device) { void *func = NULL; - if (is_win98 ()) - return NULL; - device->msimg32_dll = LoadLibraryW (L"msimg32"); if (device->msimg32_dll) func = GetProcAddress (device->msimg32_dll, "AlphaBlend"); commit dfd06ab3884d79de3b5bac782d1e8b2a0bd791ab Author: Luca Bacci Date: Thu Jan 16 16:48:12 2025 +0100 Meson: Require C++11 diff --git a/meson.build b/meson.build index 162b2927c..de89fb235 100644 --- a/meson.build +++ b/meson.build @@ -2,6 +2,7 @@ project('cairo', 'c', meson_version: '>= 1.3.0', version: run_command(find_program('version.py'), check: true).stdout().strip(), default_options: ['c_std=gnu11,c11', + 'cpp_std=gnu++11,c++11', 'warning_level=2'], ) commit 56b8933ede912a7a679157811009f87d1fac6ce7 Author: Luca Bacci Date: Wed Jan 8 11:05:01 2025 +0100 DWrite: Remove unused variables diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index f7d1c4767..ba1695259 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -1689,7 +1689,6 @@ _dwrite_draw_glyphs_to_gdi_surface_d2d(cairo_win32_surface_t *surface, if (FAILED(hr)) return CAIRO_INT_STATUS_UNSUPPORTED; - float x = 0, y = 0; if (transform) { rt->SetTransform(D2D1::Matrix3x2F(transform->m11, transform->m12, @@ -1722,7 +1721,6 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface, { // TODO: Check font & surface for types. cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); - cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->font_face); cairo_win32_surface_t *dst = reinterpret_cast(surface); cairo_int_status_t status; /* We can only handle dwrite fonts */ @@ -1747,10 +1745,6 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface, AutoDWriteGlyphRun run; run.allocate(num_glyphs); - UINT16 *indices = const_cast(run.glyphIndices); - FLOAT *advances = const_cast(run.glyphAdvances); - DWRITE_GLYPH_OFFSET *offsets = const_cast(run.glyphOffsets); - BOOL transform = FALSE; _cairo_dwrite_glyph_run_from_glyphs(glyphs, num_glyphs, dwritesf, &run, &transform); commit b84b3542d153e7255e911d6bfe43a184a19df2db Author: Luca Bacci Date: Fri Nov 8 11:52:10 2024 +0100 DWrite: Add support for font variations Fixes https://gitlab.freedesktop.org/cairo/cairo/-/issues/877 diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index 489131c11..62ad7a1a1 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -626,6 +626,54 @@ do_grayscale(IDWriteFontFace *dwface, unsigned int ppem) return true; } +static void +set_dwrite_axes_from_string (const char *string, + DWRITE_FONT_AXIS_VALUE *dwrite_axes, + UINT32 count) +{ + const char *p = string; + + while (p && *p) { + const char *start; + const char *end, *end2; + DWRITE_FONT_AXIS_TAG tag; + double value; + + while (_cairo_isspace (*p)) p++; + + start = p; + end = strchr (p, ','); + if (end && (end - p < 6)) + goto skip; + + tag = DWRITE_MAKE_FONT_AXIS_TAG (p[0], p[1], p[2], p[3]); + + p += 4; + while (_cairo_isspace (*p)) p++; + if (*p == '=') p++; + + if (p - start < 5) + goto skip; + + value = _cairo_strtod (p, (char **) &end2); + + while (end2 && _cairo_isspace (*end2)) end2++; + + if (end2 && (*end2 != ',' && *end2 != '\0')) + goto skip; + + for (UINT32 i = 0; i < count; i++) { + if (dwrite_axes[i].axisTag == tag) { + dwrite_axes[i].value = value; + break; + } + } + +skip: + p = end ? end + 1 : NULL; + } +} + static cairo_status_t _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, const cairo_matrix_t *font_matrix, @@ -654,8 +702,59 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, return status; } - dwrite_font->dwriteface->AddRef (); - dwrite_font->dwriteface = dwrite_font->dwriteface; + if (options->variations) { + RefPtr dwriteface5; + + /* Since Windows 10 20348 */ + if (SUCCEEDED (font_face->dwriteface->QueryInterface(&dwriteface5))) { + RefPtr dwritefontresource; + + if (dwriteface5->HasVariations () && + SUCCEEDED (dwriteface5->GetFontResource (&dwritefontresource))) + { + UINT32 count = MIN (dwriteface5->GetFontAxisValueCount (), 500); + DWRITE_FONT_AXIS_VALUE *dwrite_axes = new DWRITE_FONT_AXIS_VALUE[count]; + UINT32 variables_count = 0; + + /* Sort variable axes first */ + for (UINT32 i = 0; i < count; i++) { + if (dwritefontresource->GetFontAxisAttributes (i) & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE) { + if (variables_count != i) { + DWRITE_FONT_AXIS_VALUE swap_aux = dwrite_axes[variables_count]; + dwrite_axes[variables_count] = dwrite_axes[i]; + dwrite_axes[i] = swap_aux; + } + + variables_count++; + } + } + + if (SUCCEEDED (dwriteface5->GetFontAxisValues(dwrite_axes, count))) { + RefPtr dwriteface_new5; + + set_dwrite_axes_from_string (options->variations, dwrite_axes, variables_count); + + /* Can't use constexpr with mingw-w64 headers */ + const DWRITE_FONT_SIMULATIONS all_simulations = DWRITE_FONT_SIMULATIONS_BOLD | + DWRITE_FONT_SIMULATIONS_OBLIQUE; + if (SUCCEEDED (dwritefontresource->CreateFontFace(all_simulations, + dwrite_axes, + count, + &dwriteface_new5))) + { + dwrite_font->dwriteface = dwriteface_new5.forget().drop(); + } + } + + delete[] dwrite_axes; + } + } + } + + if (!dwrite_font->dwriteface) { + font_face->dwriteface->AddRef (); + dwrite_font->dwriteface = font_face->dwriteface; + } dwrite_font->mat = dwrite_font->base.ctm; cairo_matrix_multiply(&dwrite_font->mat, &dwrite_font->mat, font_matrix); commit 6cd0a7d3952953040f9b891806fc69b7aa7be12e Author: Luca Bacci Date: Wed Nov 6 23:42:04 2024 +0100 DWrite: Add IDWriteFontFace field in the scaled font subclass ...and make the code use that instead of the original IDWriteFontFace in the font face subclass. We do that because to apply a few settings (font variations) a new IDWriteFontFace must be created out of the original one. For now the IDWriteFontFace in the scaled font is a copy of the one in the font face. In the next commit we'll add code to create a different object. diff --git a/src/win32/cairo-dwrite-font.cpp b/src/win32/cairo-dwrite-font.cpp index b6931b367..489131c11 100644 --- a/src/win32/cairo-dwrite-font.cpp +++ b/src/win32/cairo-dwrite-font.cpp @@ -507,7 +507,7 @@ _cairo_dwrite_font_face_destroy (void *font_face) { cairo_dwrite_font_face_t *dwrite_font_face = static_cast(font_face); if (dwrite_font_face->dwriteface) - dwrite_font_face->dwriteface->Release(); + dwrite_font_face->dwriteface->Release(); if (dwrite_font_face->rendering_params) dwrite_font_face->rendering_params->Release(); return TRUE; @@ -532,10 +532,8 @@ _cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs, FLOAT *advances = const_cast(run->glyphAdvances); DWRITE_GLYPH_OFFSET *offsets = const_cast(run->glyphOffsets); - cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->base.font_face); - run->bidiLevel = 0; - run->fontFace = dwriteff->dwriteface; + run->fontFace = scaled_font->dwriteface; run->glyphCount = num_glyphs; run->isSideways = FALSE; @@ -656,6 +654,9 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, return status; } + dwrite_font->dwriteface->AddRef (); + dwrite_font->dwriteface = dwrite_font->dwriteface; + dwrite_font->mat = dwrite_font->base.ctm; cairo_matrix_multiply(&dwrite_font->mat, &dwrite_font->mat, font_matrix); dwrite_font->mat_inverse = dwrite_font->mat; @@ -667,9 +668,9 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, if (dwrite_font->measuring_mode == DWRITE_MEASURING_MODE_GDI_CLASSIC || dwrite_font->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL) { DWRITE_MATRIX transform = _cairo_dwrite_matrix_from_matrix (&dwrite_font->mat); - font_face->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &metrics); + dwrite_font->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &metrics); } else { - font_face->dwriteface->GetMetrics(&metrics); + dwrite_font->dwriteface->GetMetrics(&metrics); } extents.ascent = (FLOAT)metrics.ascent / metrics.designUnitsPerEm; @@ -698,7 +699,7 @@ _cairo_dwrite_font_face_scaled_font_create (void *abstract_face, } if (default_quality == CAIRO_ANTIALIAS_GRAY) { - if (!do_grayscale(font_face->dwriteface, (unsigned int)_cairo_round(font_matrix->yy))) { + if (!do_grayscale(dwrite_font->dwriteface, (unsigned int)_cairo_round(font_matrix->yy))) { default_quality = CAIRO_ANTIALIAS_NONE; } } @@ -720,6 +721,9 @@ static void _cairo_dwrite_scaled_font_fini(void *scaled_font) { cairo_dwrite_scaled_font_t *dwrite_font = static_cast(scaled_font); + + dwrite_font->dwriteface->Release (); + if (dwrite_font->rendering_params) dwrite_font->rendering_params->Release(); } @@ -765,10 +769,9 @@ _cairo_dwrite_ucs4_to_index(void *scaled_font, uint32_t ucs4) { cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); - cairo_dwrite_font_face_t *face = reinterpret_cast(dwritesf->base.font_face); + UINT16 index = 0; - UINT16 index; - face->dwriteface->GetGlyphIndicesA(&ucs4, 1, &index); + dwritesf->dwriteface->GetGlyphIndicesA(&ucs4, 1, &index); return index; } @@ -787,12 +790,12 @@ _cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_ if (font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_CLASSIC || font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL) { DWRITE_MATRIX transform = _cairo_dwrite_matrix_from_matrix (&scaled_font->mat); - font_face->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &fontMetrics); + scaled_font->dwriteface->GetGdiCompatibleMetrics(1, 1, &transform, &fontMetrics); BOOL natural = font_face->measuring_mode == DWRITE_MEASURING_MODE_GDI_NATURAL; - hr = font_face->dwriteface->GetGdiCompatibleGlyphMetrics (1, 1, &transform, natural, &charIndex, 1, &metrics, FALSE); + hr = scaled_font->dwriteface->GetGdiCompatibleGlyphMetrics (1, 1, &transform, natural, &charIndex, 1, &metrics, FALSE); } else { - font_face->dwriteface->GetMetrics(&fontMetrics); - hr = font_face->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics); + scaled_font->dwriteface->GetMetrics(&fontMetrics); + hr = scaled_font->dwriteface->GetDesignGlyphMetrics(&charIndex, 1, &metrics); } if (FAILED(hr)) { return CAIRO_INT_STATUS_UNSUPPORTED; @@ -968,16 +971,15 @@ _cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_fon offset.ascenderOffset = 0; UINT16 glyphId = (UINT16)_cairo_scaled_glyph_index(scaled_glyph); FLOAT advance = 0.0; - cairo_dwrite_font_face_t *dwriteff = (cairo_dwrite_font_face_t*)scaled_font->base.font_face; - - HRESULT hr = dwriteff->dwriteface->GetGlyphRunOutline(1, - &glyphId, - &advance, - &offset, - 1, - FALSE, - FALSE, - &recorder); + + HRESULT hr = scaled_font->dwriteface->GetGlyphRunOutline(1, + &glyphId, + &advance, + &offset, + 1, + FALSE, + FALSE, + &recorder); if (!SUCCEEDED(hr)) return _cairo_dwrite_error (hr, "GetGlyphRunOutline failed"); @@ -1035,7 +1037,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s /* Y-axis is inverted */ offset.ascenderOffset = -(FLOAT)y; - run.fontFace = dwrite_font_face->dwriteface; + run.fontFace = scaled_font->dwriteface; run.fontEmSize = 1; run.glyphCount = 1; run.glyphIndices = &index; @@ -1057,7 +1059,7 @@ _cairo_dwrite_scaled_font_init_glyph_color_surface(cairo_dwrite_scaled_font_t *s RefPtr fontFace2; UINT32 palette_count = 0; - if (SUCCEEDED(dwrite_font_face->dwriteface->QueryInterface(&fontFace2))) + if (SUCCEEDED(scaled_font->dwriteface->QueryInterface(&fontFace2))) palette_count = fontFace2->GetColorPaletteCount(); UINT32 palette_index = CAIRO_COLOR_PALETTE_DEFAULT; @@ -1283,7 +1285,7 @@ _cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_ run.glyphCount = 1; run.glyphAdvances = &advance; - run.fontFace = ((cairo_dwrite_font_face_t*)scaled_font->base.font_face)->dwriteface; + run.fontFace = scaled_font->dwriteface; run.fontEmSize = 1.0f; run.bidiLevel = 0; run.glyphIndices = &index; @@ -1323,18 +1325,17 @@ _cairo_dwrite_load_truetype_table(void *scaled_font, unsigned long *length) { cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); - cairo_dwrite_font_face_t *face = reinterpret_cast(dwritesf->base.font_face); const void *data; UINT32 size; void *tableContext; BOOL exists; HRESULT hr; - hr = face->dwriteface->TryGetFontTable (be32_to_cpu (tag), - &data, - &size, - &tableContext, - &exists); + hr = dwritesf->dwriteface->TryGetFontTable (be32_to_cpu (tag), + &data, + &size, + &tableContext, + &exists); if (FAILED(hr)) return _cairo_dwrite_error (hr, "TryGetFontTable failed"); @@ -1349,7 +1350,7 @@ _cairo_dwrite_load_truetype_table(void *scaled_font, *length = size; if (tableContext) { - face->dwriteface->ReleaseFontTable(tableContext); + dwritesf->dwriteface->ReleaseFontTable(tableContext); } return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; } @@ -1359,17 +1360,16 @@ _cairo_dwrite_is_synthetic(void *scaled_font, cairo_bool_t *is_synthetic) { cairo_dwrite_scaled_font_t *dwritesf = static_cast(scaled_font); - cairo_dwrite_font_face_t *face = reinterpret_cast(dwritesf->base.font_face); HRESULT hr; cairo_int_status_t status; - if (face->dwriteface->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) { + if (dwritesf->dwriteface->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) { *is_synthetic = FALSE; return CAIRO_INT_STATUS_SUCCESS; } RefPtr fontFace5; - if (FAILED(face->dwriteface->QueryInterface(&fontFace5))) { + if (FAILED(dwritesf->dwriteface->QueryInterface(&fontFace5))) { /* If IDWriteFontFace5 is not available, assume this version of * DirectWrite does not support variations. */ @@ -1722,7 +1722,6 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface, { // TODO: Check font & surface for types. cairo_dwrite_scaled_font_t *dwritesf = reinterpret_cast(scaled_font); - cairo_dwrite_font_face_t *dwriteff = reinterpret_cast(scaled_font->font_face); cairo_win32_surface_t *dst = reinterpret_cast(surface); cairo_int_status_t status; /* We can only handle dwrite fonts */ @@ -1827,10 +1826,10 @@ compare_font_tables (cairo_dwrite_font_face_t *dwface, HRESULT hr; hr = dwface->dwriteface->TryGetFontTable(be32_to_cpu (tag), - &dw_data, - &dw_size, - &dw_tableContext, - &dw_exists); + &dw_data, + &dw_size, + &dw_tableContext, + &dw_exists); if (FAILED(hr)) return _cairo_dwrite_error (hr, "TryGetFontTable failed"); @@ -1866,7 +1865,7 @@ compare_font_tables (cairo_dwrite_font_face_t *dwface, cleanup: free (buffer); if (dw_tableContext) - dwface->dwriteface->ReleaseFontTable(dw_tableContext); + dwface->dwriteface->ReleaseFontTable(dw_tableContext); return status; } @@ -1931,6 +1930,8 @@ _cairo_dwrite_scaled_font_create_win32_scaled_font (cairo_scaled_font_t *scaled_ return CAIRO_INT_STATUS_UNSUPPORTED; } + /* TODO: use scaled font? */ + LOGFONTW logfont; if (FAILED(gdiInterop->ConvertFontFaceToLOGFONT (dwface->dwriteface, &logfont))) { return CAIRO_INT_STATUS_UNSUPPORTED; diff --git a/src/win32/cairo-dwrite-private.hpp b/src/win32/cairo-dwrite-private.hpp index c7a24822a..19c666da5 100644 --- a/src/win32/cairo-dwrite-private.hpp +++ b/src/win32/cairo-dwrite-private.hpp @@ -68,7 +68,8 @@ struct _cairo_dwrite_scaled_font { cairo_matrix_t mat; cairo_matrix_t mat_inverse; cairo_antialias_t antialias_mode; - IDWriteRenderingParams *rendering_params; + IDWriteFontFace *dwriteface; /* Can't use RefPtr because this struct is malloc'd. */ + IDWriteRenderingParams *rendering_params; /* Can't use RefPtr because this struct is malloc'd. */ DWRITE_MEASURING_MODE measuring_mode; }; typedef struct _cairo_dwrite_scaled_font cairo_dwrite_scaled_font_t; commit af4f149057f3484ea64ffbfddea0c77bdb3fc9bc Author: Benjamin Gilbert Date: Wed Sep 4 21:46:04 2024 -0700 meson: Don't require C++ compiler for build machine on Windows We only build C++ code for the host. Fixes Meson warning: meson.build:486: WARNING: add_languages is missing native:, assuming languages are wanted for both host and build. diff --git a/meson.build b/meson.build index 4051a7c11..553314d2b 100644 --- a/meson.build +++ b/meson.build @@ -483,7 +483,7 @@ if host_machine.system() == 'darwin' and not get_option('quartz').disabled() endif if host_machine.system() == 'windows' - add_languages('cpp') + add_languages('cpp', native: false) add_project_arguments('-DWIN32_LEAN_AND_MEAN', '-DNOMINMAX', language: ['c', 'cpp']) commit 3e97e1f16b4ebe5516c0afc092122a224b5c13eb Author: Benjamin Gilbert Date: Wed Sep 4 20:58:30 2024 -0700 meson: Rename meson_options.txt to meson.options The new name is a bit cleaner and is supported by all versions of Meson that we support. diff --git a/meson_options.txt b/meson.options similarity index 100% rename from meson_options.txt rename to meson.options