mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-20 16:50:04 +00:00
dwrite: Round centered alignment shift for compatible layouts.
This commit is contained in:
parent
a718a9a5ca
commit
c8b0c80365
|
@ -255,7 +255,7 @@ struct dwrite_textlayout {
|
||||||
DWRITE_MEASURING_MODE measuringmode;
|
DWRITE_MEASURING_MODE measuringmode;
|
||||||
|
|
||||||
/* gdi-compatible layout specifics */
|
/* gdi-compatible layout specifics */
|
||||||
FLOAT pixels_per_dip;
|
FLOAT ppdip;
|
||||||
DWRITE_MATRIX transform;
|
DWRITE_MATRIX transform;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -281,6 +281,11 @@ struct dwrite_typography {
|
||||||
UINT32 count;
|
UINT32 count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dwrite_vec {
|
||||||
|
FLOAT x;
|
||||||
|
FLOAT y;
|
||||||
|
};
|
||||||
|
|
||||||
static const IDWriteTextFormat1Vtbl dwritetextformatvtbl;
|
static const IDWriteTextFormat1Vtbl dwritetextformatvtbl;
|
||||||
|
|
||||||
static void release_format_data(struct dwrite_textformat_data *data)
|
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))
|
if (is_layout_gdi_compatible(layout))
|
||||||
hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap,
|
hr = IDWriteTextAnalyzer_GetGdiCompatibleGlyphPlacements(analyzer, run->descr.string, run->descr.clusterMap,
|
||||||
text_props, run->descr.stringLength, run->run.glyphIndices, glyph_props, run->glyphcount,
|
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,
|
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);
|
run->run.bidiLevel & 1, &run->sa, run->descr.localeName, NULL, NULL, 0, run->advances, run->offsets);
|
||||||
else
|
else
|
||||||
|
@ -845,7 +850,7 @@ static HRESULT layout_compute_runs(struct dwrite_textlayout *layout)
|
||||||
if (is_layout_gdi_compatible(layout)) {
|
if (is_layout_gdi_compatible(layout)) {
|
||||||
hr = IDWriteFontFace_GetGdiCompatibleMetrics(run->run.fontFace,
|
hr = IDWriteFontFace_GetGdiCompatibleMetrics(run->run.fontFace,
|
||||||
run->run.fontEmSize,
|
run->run.fontEmSize,
|
||||||
layout->pixels_per_dip,
|
layout->ppdip,
|
||||||
&layout->transform,
|
&layout->transform,
|
||||||
&fontmetrics);
|
&fontmetrics);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
|
@ -1063,7 +1068,7 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const
|
||||||
HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(
|
HRESULT hr = IDWriteFontFace_GetGdiCompatibleMetrics(
|
||||||
r->u.regular.run.fontFace,
|
r->u.regular.run.fontFace,
|
||||||
r->u.regular.run.fontEmSize,
|
r->u.regular.run.fontEmSize,
|
||||||
layout->pixels_per_dip,
|
layout->ppdip,
|
||||||
&layout->transform,
|
&layout->transform,
|
||||||
&metrics);
|
&metrics);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
|
@ -1166,6 +1171,43 @@ static FLOAT layout_get_line_width(struct dwrite_textlayout *layout,
|
||||||
return width;
|
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)
|
static void layout_apply_leading_alignment(struct dwrite_textlayout *layout)
|
||||||
{
|
{
|
||||||
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
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;
|
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)
|
static void layout_apply_centered_alignment(struct dwrite_textlayout *layout)
|
||||||
{
|
{
|
||||||
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
|
||||||
struct layout_effective_inline *inrun;
|
struct layout_effective_inline *inrun;
|
||||||
struct layout_effective_run *erun;
|
struct layout_effective_run *erun;
|
||||||
|
BOOL skiptransform;
|
||||||
UINT32 line;
|
UINT32 line;
|
||||||
|
FLOAT det;
|
||||||
|
|
||||||
erun = layout_get_next_erun(layout, NULL);
|
erun = layout_get_next_erun(layout, NULL);
|
||||||
inrun = layout_get_next_inline_run(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++) {
|
for (line = 0; line < layout->metrics.lineCount; line++) {
|
||||||
FLOAT width = layout_get_line_width(layout, erun, inrun, 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)
|
if (is_rtl)
|
||||||
shift *= -1.0;
|
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.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)))
|
(m.m12 * m.m21 != 0.0 && (m.m11 != 0.0 || m.m22 != 0.0)))
|
||||||
disabled = TRUE;
|
disabled = TRUE;
|
||||||
else {
|
else
|
||||||
det = m.m11 * m.m22 - m.m12 * m.m21;
|
skiptransform = should_skip_transform(&m, &det);
|
||||||
|
|
||||||
/* on certain conditions we can skip transform */
|
|
||||||
if (!memcmp(&m, &identity, sizeof(m)) || fabsf(det) <= 1e-10f)
|
|
||||||
skiptransform = TRUE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SNAP_COORD(x) renderer_apply_snapping((x), skiptransform, ppdip, det, &m)
|
#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->metrics.layoutHeight = maxheight;
|
||||||
layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
|
layout->measuringmode = DWRITE_MEASURING_MODE_NATURAL;
|
||||||
|
|
||||||
layout->pixels_per_dip = 0.0;
|
layout->ppdip = 0.0;
|
||||||
memset(&layout->transform, 0, sizeof(layout->transform));
|
memset(&layout->transform, 0, sizeof(layout->transform));
|
||||||
|
|
||||||
layout->str = heap_strdupnW(str, len);
|
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,
|
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;
|
struct dwrite_textlayout *layout;
|
||||||
HRESULT hr;
|
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;
|
layout->measuringmode = use_gdi_natural ? DWRITE_MEASURING_MODE_GDI_NATURAL : DWRITE_MEASURING_MODE_GDI_CLASSIC;
|
||||||
|
|
||||||
/* set gdi-specific properties */
|
/* set gdi-specific properties */
|
||||||
layout->pixels_per_dip = pixels_per_dip;
|
layout->ppdip = ppdip;
|
||||||
layout->transform = transform ? *transform : identity;
|
layout->transform = transform ? *transform : identity;
|
||||||
|
|
||||||
*ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout2_iface;
|
*ret = (IDWriteTextLayout*)&layout->IDWriteTextLayout2_iface;
|
||||||
|
|
|
@ -1402,6 +1402,7 @@ static void test_Draw(void)
|
||||||
IDWriteTextLayout *layout;
|
IDWriteTextLayout *layout;
|
||||||
DWRITE_TEXT_RANGE range;
|
DWRITE_TEXT_RANGE range;
|
||||||
IDWriteFactory *factory;
|
IDWriteFactory *factory;
|
||||||
|
DWRITE_TEXT_METRICS tm;
|
||||||
DWRITE_MATRIX m;
|
DWRITE_MATRIX m;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
|
@ -1516,6 +1517,24 @@ static void test_Draw(void)
|
||||||
hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
|
hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, 0.0, 0.0);
|
||||||
ok(hr == S_OK, "got 0x%08x\n", hr);
|
ok(hr == S_OK, "got 0x%08x\n", hr);
|
||||||
ok_sequence(sequences, RENDERER_ID, draw_single_run_seq, "draw test 7", FALSE);
|
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);
|
IDWriteTextLayout_Release(layout);
|
||||||
|
|
||||||
ctxt.gdicompat = TRUE;
|
ctxt.gdicompat = TRUE;
|
||||||
|
@ -1874,6 +1893,31 @@ todo_wine
|
||||||
|
|
||||||
IDWriteTextLayout_Release(layout);
|
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);
|
IDWriteInlineObject_Release(trimm);
|
||||||
IDWriteTextFormat_Release(format);
|
IDWriteTextFormat_Release(format);
|
||||||
IDWriteFactory_Release(factory);
|
IDWriteFactory_Release(factory);
|
||||||
|
|
Loading…
Reference in a new issue