[PATCH 1/7] dwrite: Implement pixel snapping in Draw() (try2)
Nikolay Sivov
nsivov at codeweavers.com
Sun Jul 12 15:34:43 CDT 2015
try2: this time actually handle renderer transform
-------------- next part --------------
>From 1954ca25f50cf6a9d49247a6153f9276aac1e418 Mon Sep 17 00:00:00 2001
From: Nikolay Sivov <nsivov at codeweavers.com>
Date: Sun, 12 Jul 2015 23:22:50 +0300
Subject: [PATCH 1/7] dwrite: Implement pixel snapping in Draw()
---
dlls/dwrite/layout.c | 65 ++++++++++++++-
dlls/dwrite/tests/layout.c | 203 +++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 256 insertions(+), 12 deletions(-)
diff --git a/dlls/dwrite/layout.c b/dlls/dwrite/layout.c
index b07aec0..8b4c2cd 100644
--- a/dlls/dwrite/layout.c
+++ b/dlls/dwrite/layout.c
@@ -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;
}
diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c
index 0fc101b..76bcdff 100644
--- a/dlls/dwrite/tests/layout.c
+++ b/dlls/dwrite/tests/layout.c
@@ -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);
}
--
2.1.4
More information about the wine-patches
mailing list