diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c index 8e967629352..7e608a60010 100644 --- a/dlls/dwrite/layout.c +++ b/dlls/dwrite/layout.c @@ -255,7 +255,7 @@ struct dwrite_textlayout { DWRITE_MEASURING_MODE measuringmode; /* gdi-compatible layout specifics */ - FLOAT pixels_per_dip; + FLOAT ppdip; DWRITE_MATRIX transform; }; @@ -281,6 +281,11 @@ struct dwrite_typography { UINT32 count; }; +struct dwrite_vec { + FLOAT x; + FLOAT y; +}; + static const IDWriteTextFormat1Vtbl dwritetextformatvtbl; static void release_format_data(struct dwrite_textformat_data *data) @@ -816,7 +821,7 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout) if (is_layout_gdi_compatible(layout)) hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap, text_props, run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount, - run->run.fontFace, run->run.fontEmSize, layout->pixels_per_dip, &layout->transform, + run->run.fontFace, run->run.fontEmSize, layout->ppdip, &layout->transform, layout->measuringmode == DWRITE_MEASURING_MODE_GDI_NATURAL, run->run.isSideways, run->run.bidiLevel & 1, &run->sa, run->descr.localeName, NULL, NULL, 0, run->advances, run->offsets); else @@ -845,7 +850,7 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout) if (is_layout_gdi_compatible(layout)) { hr = IDWriteFontFace_GetGdiCompatibleMetrics(run->run.fontFace, run->run.fontEmSize, - layout->pixels_per_dip, + layout->ppdip, &layout->transform, &fontmetrics); if (FAILED(hr)) @@ -1063,7 +1068,7 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics( r->u.regular.run.fontFace, r->u.regular.run.fontEmSize, - layout->pixels_per_dip, + layout->ppdip, &layout->transform, &metrics); if (FAILED(hr)) @@ -1166,6 +1171,43 @@ static FLOAT layout_get_line_width(struct dwrite_textlayout *layout, return width; } +static inline BOOL should_skip_transform(const DWRITE_MATRIX *m, FLOAT *det) +{ + *det = m->m11 * m->m22 - m->m12 * m->m21; + /* on certain conditions we can skip transform */ + return (!memcmp(m, &identity, sizeof(*m)) || fabsf(*det) <= 1e-10f); +} + +static inline void layout_apply_snapping(struct dwrite_vec *vec, BOOL skiptransform, FLOAT ppdip, + const DWRITE_MATRIX *m, FLOAT det) +{ + if (!skiptransform) { + FLOAT vec2[2]; + + /* apply transform */ + vec->x *= ppdip; + vec->y *= ppdip; + + vec2[0] = m->m11 * vec->x + m->m21 * vec->y + m->dx; + vec2[1] = m->m12 * vec->x + m->m22 * vec->y + m->dy; + + /* snap */ + vec2[0] = floorf(vec2[0] + 0.5f); + vec2[1] = floorf(vec2[1] + 0.5f); + + /* apply inverted transform, we don't care about X component at this point */ + vec->x = (m->m22 * vec2[0] - m->m21 * vec2[1] + m->m21 * m->dy - m->m22 * m->dx) / det; + vec->x /= ppdip; + + vec->y = (-m->m12 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det; + vec->y /= ppdip; + } + else { + vec->x = floorf(vec->x * ppdip + 0.5f) / ppdip; + vec->y = floorf(vec->y * ppdip + 0.5f) / ppdip; + } +} + static void layout_apply_leading_alignment(struct dwrite_textlayout *layout) { BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; @@ -1219,19 +1261,35 @@ static void layout_apply_trailing_alignment(struct dwrite_textlayout *layout) layout->metrics.left = is_rtl ? 0.0 : layout->metrics.layoutWidth - layout->metrics.width; } +static inline FLOAT layout_get_centered_shift(struct dwrite_textlayout *layout, BOOL skiptransform, + FLOAT width, FLOAT det) +{ + if (is_layout_gdi_compatible(layout)) { + struct dwrite_vec vec = { layout->metrics.layoutWidth - width, 0.0 }; + layout_apply_snapping(&vec, skiptransform, layout->ppdip, &layout->transform, det); + return floorf(vec.x / 2.0f); + } + else + return (layout->metrics.layoutWidth - width) / 2.0f; +} + static void layout_apply_centered_alignment(struct dwrite_textlayout *layout) { BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT; struct layout_effective_inline *inrun; struct layout_effective_run *erun; + BOOL skiptransform; UINT32 line; + FLOAT det; erun = layout_get_next_erun(layout, NULL); inrun = layout_get_next_inline_run(layout, NULL); + skiptransform = should_skip_transform(&layout->transform, &det); + for (line = 0; line < layout->metrics.lineCount; line++) { FLOAT width = layout_get_line_width(layout, erun, inrun, line); - FLOAT shift = (layout->metrics.layoutWidth - width) / 2.0; + FLOAT shift = layout_get_centered_shift(layout, skiptransform, width, det); if (is_rtl) shift *= -1.0; @@ -2795,13 +2853,8 @@ static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout2 *iface, (m.m11 * m.m22 != 0.0 && (m.m12 != 0.0 || m.m21 != 0.0)) || (m.m12 * m.m21 != 0.0 && (m.m11 != 0.0 || m.m22 != 0.0))) disabled = TRUE; - else { - det = m.m11 * m.m22 - m.m12 * m.m21; - - /* on certain conditions we can skip transform */ - if (!memcmp(&m, &identity, sizeof(m)) || fabsf(det) <= 1e-10f) - skiptransform = TRUE; - } + else + skiptransform = should_skip_transform(&m, &det); } #define SNAP_COORD(x) renderer_apply_snapping((x), skiptransform, ppdip, det, &m) @@ -3901,7 +3954,7 @@ static HRESULT init_textlayout(const WCHAR *str, UINT32 len, IDWriteTextFormat * layout->metrics.layoutHeight = maxheight; layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL; - layout->pixels_per_dip = 0.0; + layout->ppdip = 0.0; memset(&layout->transform, 0, sizeof(layout->transform)); layout->str = heap_strdupnW(str, len); @@ -3956,7 +4009,7 @@ HRESULT create_textlayout(const WCHAR *str, UINT32 len, IDWriteTextFormat *forma } HRESULT create_gdicompat_textlayout(const WCHAR *str, UINT32 len, IDWriteTextFormat *format, FLOAT maxwidth, FLOAT maxheight, - FLOAT pixels_per_dip, const DWRITE_MATRIX *transform, BOOL use_gdi_natural, IDWriteTextLayout **ret) + FLOAT ppdip, const DWRITE_MATRIX *transform, BOOL use_gdi_natural, IDWriteTextLayout **ret) { struct dwrite_textlayout *layout; HRESULT hr; @@ -3971,7 +4024,7 @@ HRESULT create_gdicompat_textlayout(const WCHAR *str, UINT32 len, IDWriteTextFor layout->measuringmode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC; /* set gdi-specific properties */ - layout->pixels_per_dip = pixels_per_dip; + layout->ppdip = ppdip; layout->transform = transform ? *transform : identity; *ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout2_iface; diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 1494d599a5d..0e8404cb4c5 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -1402,6 +1402,7 @@ static void test_Draw(void) IDWriteTextLayout *layout; DWRITE_TEXT_RANGE range; IDWriteFactory *factory; + DWRITE_TEXT_METRICS tm; DWRITE_MATRIX m; HRESULT hr; @@ -1516,6 +1517,24 @@ static void test_Draw(void) hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); ok(hr == S_OK, "got 0x%08x\n", hr); ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE); + + /* text alignment keeps pixel-aligned origin */ + hr = IDWriteTextLayout_GetMetrics(layout, &tm); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(tm.width == floorf(tm.width), "got %f\n", tm.width); + + hr = IDWriteTextLayout_SetMaxWidth(layout, tm.width + 3.0); + ok(hr == S_OK, "got 0x%08x\n", hr); + hr = IDWriteTextLayout_SetTextAlignment(layout, DWRITE_TEXT_ALIGNMENT_CENTER); + ok(hr == S_OK, "got 0x%08x\n", hr); + + ctxt.originX = ctxt.originY = 0.0; + flush_sequence(sequences, RENDERER_ID); + hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE); + ok(ctxt.originX != 0.0 && ctxt.originX == floorf(ctxt.originX), "got %f\n", ctxt.originX); + IDWriteTextLayout_Release(layout); ctxt.gdicompat = TRUE; @@ -1874,6 +1893,31 @@ todo_wine IDWriteTextLayout_Release(layout); + /* compare natural cluster width with gdi layout */ + hr = IDWriteFactory_CreateTextLayout(factory, str4W, 1, format, 100.0, 100.0, &layout); + ok(hr == S_OK, "got 0x%08x\n", hr); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(count == 1, "got %u\n", count); + ok(metrics[0].width != floorf(metrics[0].width), "got %f\n", metrics[0].width); + + IDWriteTextLayout_Release(layout); + + hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, str4W, 1, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout); + ok(hr == S_OK, "got 0x%08x\n", hr); + + count = 0; + memset(metrics, 0, sizeof(metrics)); + hr = IDWriteTextLayout_GetClusterMetrics(layout, metrics, 1, &count); + ok(hr == S_OK, "got 0x%08x\n", hr); + ok(count == 1, "got %u\n", count); + ok(metrics[0].width == floorf(metrics[0].width), "got %f\n", metrics[0].width); + + IDWriteTextLayout_Release(layout); + IDWriteInlineObject_Release(trimm); IDWriteTextFormat_Release(format); IDWriteFactory_Release(factory);