dwrite: Implement pixel snapping in Draw().

oldstable
Nikolay Sivov 2015-07-12 23:22:50 +03:00 committed by Alexandre Julliard
parent 402caa632b
commit 66efbbbae2
2 changed files with 256 additions and 12 deletions

View File

@ -21,6 +21,7 @@
#define COBJMACROS
#include <stdarg.h>
#include <math.h>
#include "windef.h"
#include "winbase.h"
@ -2732,13 +2733,42 @@ static HRESULT WINAPI dwritetextlayout_layout_GetLocaleName(IDWriteTextLayout2 *
return get_string_attribute_value(This, LAYOUT_RANGE_ATTR_LOCALE, position, locale, length, r);
}
static inline FLOAT renderer_apply_snapping(FLOAT coord, BOOL skiptransform, FLOAT ppdip, FLOAT det,
const DWRITE_MATRIX *m)
{
FLOAT vec[2], vec2[2];
if (!skiptransform) {
/* apply transform */
vec[0] = 0.0;
vec[1] = coord;
vec2[0] = m->m11 * vec[0] + m->m12 * vec[1] + m->dx;
vec2[1] = m->m21 * vec[0] + m->m22 * vec[1] + m->dy;
/* snap */
vec2[0] = floorf(vec2[0] * ppdip + 0.5f) / ppdip;
vec2[1] = floorf(vec2[1] * ppdip + 0.5f) / ppdip;
/* apply inverted transform, we don't care about X component at this point */
vec[1] = (-m->m21 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det;
}
else
vec[1] = floorf(coord * ppdip + 0.5f) / ppdip;
return vec[1];
}
static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout2 *iface,
void *context, IDWriteTextRenderer* renderer, FLOAT origin_x, FLOAT origin_y)
{
struct dwrite_textlayout *This = impl_from_IDWriteTextLayout2(iface);
BOOL disabled = FALSE, skiptransform = FALSE;
struct layout_effective_inline *inlineobject;
struct layout_effective_run *run;
struct layout_strikethrough *s;
FLOAT det = 0.0, ppdip = 0.0;
DWRITE_MATRIX m = { 0 };
HRESULT hr;
TRACE("(%p)->(%p %p %.2f %.2f)\n", This, context, renderer, origin_x, origin_y);
@ -2747,6 +2777,34 @@ static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout2 *iface,
if (FAILED(hr))
return hr;
hr = IDWriteTextRenderer_IsPixelSnappingDisabled(renderer, context, &disabled);
if (FAILED(hr))
return hr;
if (!disabled) {
hr = IDWriteTextRenderer_GetPixelsPerDip(renderer, context, &ppdip);
if (FAILED(hr))
return hr;
hr = IDWriteTextRenderer_GetCurrentTransform(renderer, context, &m);
if (FAILED(hr))
return hr;
/* it's only allowed to have a diagonal/antidiagonal transform matrix */
if (ppdip <= 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)))
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;
}
}
#define SNAP_COORD(x) renderer_apply_snapping((x), skiptransform, ppdip, det, &m)
/* 1. Regular runs */
LIST_FOR_EACH_ENTRY(run, &This->eruns, struct layout_effective_run, entry) {
const struct regular_layout_run *regular = &run->run->u.regular;
@ -2776,7 +2834,7 @@ static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout2 *iface,
IDWriteTextRenderer_DrawGlyphRun(renderer,
context,
run->origin_x + run->align_dx + origin_x,
run->origin_y + origin_y,
disabled ? run->origin_y + origin_y : SNAP_COORD(run->origin_y + origin_y),
DWRITE_MEASURING_MODE_NATURAL,
&glyph_run,
&descr,
@ -2788,7 +2846,7 @@ static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout2 *iface,
IDWriteTextRenderer_DrawInlineObject(renderer,
context,
inlineobject->origin_x + inlineobject->align_dx + origin_x,
inlineobject->origin_y + origin_y,
disabled ? inlineobject->origin_y + origin_y : SNAP_COORD(inlineobject->origin_y + origin_y),
inlineobject->object,
inlineobject->is_sideways,
inlineobject->is_rtl,
@ -2802,10 +2860,11 @@ static HRESULT WINAPI dwritetextlayout_Draw(IDWriteTextLayout2 *iface,
IDWriteTextRenderer_DrawStrikethrough(renderer,
context,
s->run->origin_x,
s->run->origin_y,
disabled ? s->run->origin_y : SNAP_COORD(s->run->origin_y),
&s->s,
NULL);
}
#undef SNAP_COORD
return S_OK;
}

View File

@ -21,6 +21,7 @@
#define COBJMACROS
#include <assert.h>
#include <math.h>
#include "windows.h"
#include "dwrite.h"
@ -417,29 +418,44 @@ static ULONG WINAPI testrenderer_Release(IDWriteTextRenderer *iface)
return 1;
}
struct renderer_context {
BOOL snapping_disabled;
DWRITE_MATRIX m;
FLOAT ppdip;
FLOAT originX;
FLOAT originY;
};
static HRESULT WINAPI testrenderer_IsPixelSnappingDisabled(IDWriteTextRenderer *iface,
void *client_drawingcontext, BOOL *disabled)
void *context, BOOL *disabled)
{
*disabled = TRUE;
struct renderer_context *ctxt = (struct renderer_context*)context;
if (ctxt)
*disabled = ctxt->snapping_disabled;
else
*disabled = TRUE;
return S_OK;
}
static HRESULT WINAPI testrenderer_GetCurrentTransform(IDWriteTextRenderer *iface,
void *client_drawingcontext, DWRITE_MATRIX *transform)
void *context, DWRITE_MATRIX *m)
{
ok(0, "unexpected call\n");
return E_NOTIMPL;
struct renderer_context *ctxt = (struct renderer_context*)context;
ok(!ctxt->snapping_disabled, "expected enabled snapping\n");
*m = ctxt->m;
return S_OK;
}
static HRESULT WINAPI testrenderer_GetPixelsPerDip(IDWriteTextRenderer *iface,
void *client_drawingcontext, FLOAT *pixels_per_dip)
void *context, FLOAT *pixels_per_dip)
{
ok(0, "unexpected call\n");
return E_NOTIMPL;
struct renderer_context *ctxt = (struct renderer_context*)context;
*pixels_per_dip = ctxt->ppdip;
return S_OK;
}
static HRESULT WINAPI testrenderer_DrawGlyphRun(IDWriteTextRenderer *iface,
void* client_drawingcontext,
void *context,
FLOAT baselineOriginX,
FLOAT baselineOriginY,
DWRITE_MEASURING_MODE mode,
@ -447,9 +463,15 @@ static HRESULT WINAPI testrenderer_DrawGlyphRun(IDWriteTextRenderer *iface,
DWRITE_GLYPH_RUN_DESCRIPTION const *descr,
IUnknown *effect)
{
struct renderer_context *ctxt = (struct renderer_context*)context;
struct drawcall_entry entry;
DWRITE_SCRIPT_ANALYSIS sa;
if (ctxt) {
ctxt->originX = baselineOriginX;
ctxt->originY = baselineOriginY;
}
ok(descr->stringLength < sizeof(entry.string)/sizeof(WCHAR), "string is too long\n");
if (descr->stringLength && descr->stringLength < sizeof(entry.string)/sizeof(WCHAR)) {
memcpy(entry.string, descr->string, descr->stringLength*sizeof(WCHAR));
@ -3212,6 +3234,168 @@ static void test_SetReadingDirection(void)
IDWriteFactory_Release(factory);
}
static inline FLOAT get_scaled_font_metric(UINT32 metric, FLOAT emSize, const DWRITE_FONT_METRICS *metrics)
{
return (FLOAT)metric * emSize / (FLOAT)metrics->designUnitsPerEm;
}
static FLOAT snap_coord(const DWRITE_MATRIX *m, FLOAT ppdip, FLOAT coord)
{
FLOAT vec[2], det, vec2[2];
BOOL transform;
/* has to be a diagonal matrix */
if ((ppdip <= 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)))
return coord;
det = m->m11 * m->m22 - m->m12 * m->m21;
transform = fabsf(det) > 1e-10;
if (transform) {
/* apply transform */
vec[0] = 0.0;
vec[1] = coord;
vec2[0] = m->m11 * vec[0] + m->m12 * vec[1] + m->dx;
vec2[1] = m->m21 * vec[0] + m->m22 * vec[1] + m->dy;
/* snap */
vec2[0] = floorf(vec2[0] * ppdip + 0.5f) / ppdip;
vec2[1] = floorf(vec2[1] * ppdip + 0.5f) / ppdip;
/* apply inverted transform */
vec[1] = (-m->m21 * vec2[0] + m->m11 * vec2[1] - (m->m11 * m->dy - m->m12 * m->dx)) / det;
}
else
vec[1] = floorf(coord * ppdip + 0.5f) / ppdip;
return vec[1];
}
static inline BOOL float_eq(FLOAT left, FLOAT right)
{
return fabsf(left - right) < 1e-6f;
}
struct snapping_test {
DWRITE_MATRIX m;
FLOAT ppdip;
};
static struct snapping_test snapping_tests[] = {
{ { 0.0, 1.0, 2.0, 0.0, 0.2, 0.3 }, 1.0 },
{ { 0.0, 1.0, 2.0, 0.0, 0.0, 0.0 }, 1.0 },
{ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 1.0 }, /* identity transform */
{ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 0.9 },
{ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, -1.0 },
{ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 0.0 },
{ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.3 }, 1.0 }, /* simple Y shift */
{ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 10.0 }, /* identity, 10 ppdip */
{ { 1.0, 0.0, 0.0, 10.0, 0.0, 0.0 }, 10.0 },
{ { 0.0, 1.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 },
{ { 0.0, 2.0, 2.0, 0.0, 0.2, 0.6 }, 1.0 },
{ { 0.0, 0.5, -0.5, 0.0, 0.2, 0.6 }, 1.0 },
{ { 1.0, 2.0, 0.0, 1.0, 0.2, 0.6 }, 1.0 },
{ { 1.0, 1.0, 0.0, 1.0, 0.2, 0.6 }, 1.0 },
{ { 0.5, 0.5, -0.5, 0.5, 0.2, 0.6 }, 1.0 }, /* 45 degrees rotation */
{ { 0.5, 0.5, -0.5, 0.5, 0.0, 0.0 }, 100.0 }, /* 45 degrees rotation */
{ { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, 100.0 },
{ { 0.0, 1.0, -1.0, 0.0, 0.2, 0.6 }, 1.0 }, /* 90 degrees rotation */
{ { -1.0, 0.0, 0.0, -1.0, 0.2, 0.6 }, 1.0 }, /* 180 degrees rotation */
{ { 0.0, -1.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 }, /* 270 degrees rotation */
{ { 0.0, 2.0, 1.0, 0.0, 0.2, 0.6 }, 1.0 },
{ { 0.0, 0.0, 1.0, 0.0, 0.0, 0.0 }, 1.0 },
};
static DWRITE_MATRIX compattransforms[] = {
{ 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 },
{ 1.0, 0.0, 0.0, 1.0, 0.2, 0.3 },
{ 2.0, 0.0, 0.0, 2.0, 0.2, 0.3 },
{ 2.0, 1.0, 2.0, 2.0, 0.2, 0.3 },
};
static void test_pixelsnapping(void)
{
static const WCHAR strW[] = {'a',0};
IDWriteTextLayout *layout, *layout2;
struct renderer_context ctxt;
DWRITE_FONT_METRICS metrics;
IDWriteTextFormat *format;
IDWriteFontFace *fontface;
IDWriteFactory *factory;
FLOAT baseline, originX;
HRESULT hr;
int i, j;
factory = create_factory();
hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_NORMAL, DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL, 12.0, enusW, &format);
ok(hr == S_OK, "got 0x%08x\n", hr);
fontface = get_fontface_from_format(format);
IDWriteFontFace_GetMetrics(fontface, &metrics);
hr = IDWriteFactory_CreateTextLayout(factory, strW, 1, format, 500.0, 100.0, &layout);
ok(hr == S_OK, "got 0x%08x\n", hr);
/* disabled snapping */
ctxt.snapping_disabled = TRUE;
ctxt.ppdip = 1.0f;
memset(&ctxt.m, 0, sizeof(ctxt.m));
ctxt.m.m11 = ctxt.m.m22 = 1.0;
originX = 0.1;
hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
ok(hr == S_OK, "got 0x%08x\n", hr);
baseline = get_scaled_font_metric(metrics.ascent, 12.0, &metrics);
ok(ctxt.originX == originX, "got %f, originX %f\n", ctxt.originX, originX);
ok(ctxt.originY == baseline, "got %f, baseline %f\n", ctxt.originY, baseline);
ok(floor(baseline) != baseline, "got %f\n", baseline);
ctxt.snapping_disabled = FALSE;
for (i = 0; i < sizeof(snapping_tests)/sizeof(snapping_tests[0]); i++) {
struct snapping_test *ptr = &snapping_tests[i];
FLOAT expectedY;
ctxt.m = ptr->m;
ctxt.ppdip = ptr->ppdip;
ctxt.originX = 678.9;
ctxt.originY = 678.9;
expectedY = snap_coord(&ctxt.m, ctxt.ppdip, baseline);
hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
ok(ctxt.originX == originX, "%d: got %f, originX %f\n", i, ctxt.originX, originX);
ok(float_eq(ctxt.originY, expectedY), "%d: got %f, expected %f, baseline %f\n",
i, ctxt.originY, expectedY, baseline);
/* gdicompat layout transform doesn't affect snapping */
for (j = 0; j < sizeof(compattransforms)/sizeof(compattransforms[0]); j++) {
hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 1, format, 500.0, 100.0,
1.0, &compattransforms[j], FALSE, &layout2);
ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
expectedY = snap_coord(&ctxt.m, ctxt.ppdip, baseline);
hr = IDWriteTextLayout_Draw(layout, &ctxt, &testrenderer, originX, 0.0);
ok(hr == S_OK, "%d: got 0x%08x\n", i, hr);
ok(ctxt.originX == originX, "%d: got %f, originX %f\n", i, ctxt.originX, originX);
ok(float_eq(ctxt.originY, expectedY), "%d: got %f, expected %f, baseline %f\n",
i, ctxt.originY, expectedY, baseline);
IDWriteTextLayout_Release(layout2);
}
}
IDWriteTextLayout_Release(layout);
IDWriteTextFormat_Release(format);
IDWriteFontFace_Release(fontface);
IDWriteFactory_Release(factory);
}
START_TEST(layout)
{
static const WCHAR ctrlstrW[] = {0x202a,0};
@ -3255,6 +3439,7 @@ START_TEST(layout)
test_SetTextAlignment();
test_SetParagraphAlignment();
test_SetReadingDirection();
test_pixelsnapping();
IDWriteFactory_Release(factory);
}