From e6cb2dbaca34c5830922ff104507e5fdbd03abac Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Tue, 5 Aug 2014 11:46:47 +0400 Subject: [PATCH] dwrite/tests: Some tests for drawing sequence for a layout. --- dlls/dwrite/tests/analyzer.c | 1 + dlls/dwrite/tests/layout.c | 340 +++++++++++++++++++++++++++++++++++ 2 files changed, 341 insertions(+) diff --git a/dlls/dwrite/tests/analyzer.c b/dlls/dwrite/tests/analyzer.c index e504a22b4fc..3b8a0747ba3 100644 --- a/dlls/dwrite/tests/analyzer.c +++ b/dlls/dwrite/tests/analyzer.c @@ -25,6 +25,7 @@ #include "initguid.h" #include "windows.h" #include "dwrite.h" +#include "dwrite_2.h" #include "wine/test.h" diff --git a/dlls/dwrite/tests/layout.c b/dlls/dwrite/tests/layout.c index 5ed2bf5be96..f4cd90339b4 100644 --- a/dlls/dwrite/tests/layout.c +++ b/dlls/dwrite/tests/layout.c @@ -20,8 +20,11 @@ #define COBJMACROS +#include + #include "windows.h" #include "dwrite.h" +#include "dwrite_2.h" #include "wine/test.h" @@ -38,6 +41,271 @@ static void _expect_ref(IUnknown* obj, ULONG ref, int line) ok_(__FILE__,line)(rc-1 == ref, "expected refcount %d, got %d\n", ref, rc-1); } +enum drawcall_kind { + DRAW_GLYPHRUN = 0, + DRAW_UNDERLINE, + DRAW_STRIKETHROUGH, + DRAW_INLINE, + DRAW_LAST_KIND +}; + +static const char *get_draw_kind_name(enum drawcall_kind kind) +{ + static const char *kind_names[] = { "GLYPH_RUN", "UNDERLINE", "STRIKETHROUGH", "INLINE", "END_OF_SEQ" }; + return kind > DRAW_LAST_KIND ? "unknown" : kind_names[kind]; +} + +struct drawcall_entry { + enum drawcall_kind kind; +}; + +struct drawcall_sequence +{ + int count; + int size; + struct drawcall_entry *sequence; +}; + +struct drawtestcontext { + enum drawcall_kind kind; + BOOL todo; + int *failcount; + const char *file; + int line; +}; + +#define NUM_CALL_SEQUENCES 1 +#define RENDERER_ID 0 +static struct drawcall_sequence *sequences[NUM_CALL_SEQUENCES]; +static struct drawcall_sequence *expected_seq[1]; + +static void add_call(struct drawcall_sequence **seq, int sequence_index, const struct drawcall_entry *call) +{ + struct drawcall_sequence *call_seq = seq[sequence_index]; + + if (!call_seq->sequence) { + call_seq->size = 10; + call_seq->sequence = HeapAlloc(GetProcessHeap(), 0, call_seq->size * sizeof (struct drawcall_entry)); + } + + if (call_seq->count == call_seq->size) { + call_seq->size *= 2; + call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0, + call_seq->sequence, + call_seq->size * sizeof (struct drawcall_entry)); + } + + assert(call_seq->sequence); + call_seq->sequence[call_seq->count++] = *call; +} + +static inline void flush_sequence(struct drawcall_sequence **seg, int sequence_index) +{ + struct drawcall_sequence *call_seq = seg[sequence_index]; + + HeapFree(GetProcessHeap(), 0, call_seq->sequence); + call_seq->sequence = NULL; + call_seq->count = call_seq->size = 0; +} + +static inline void flush_sequences(struct drawcall_sequence **seq, int n) +{ + int i; + for (i = 0; i < n; i++) + flush_sequence(seq, i); +} + +static void init_call_sequences(struct drawcall_sequence **seq, int n) +{ + int i; + + for (i = 0; i < n; i++) + seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct drawcall_sequence)); +} + +static void ok_sequence_(struct drawcall_sequence **seq, int sequence_index, + const struct drawcall_entry *expected, const char *context, BOOL todo, + const char *file, int line) +{ + static const struct drawcall_entry end_of_sequence = { DRAW_LAST_KIND }; + struct drawcall_sequence *call_seq = seq[sequence_index]; + const struct drawcall_entry *actual, *sequence; + int failcount = 0; + + add_call(seq, sequence_index, &end_of_sequence); + + sequence = call_seq->sequence; + actual = sequence; + + while (expected->kind != DRAW_LAST_KIND && actual->kind != DRAW_LAST_KIND) { + if (expected->kind != actual->kind) { + if (todo) { + failcount++; + todo_wine + ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n", + context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind)); + + flush_sequence(seq, sequence_index); + return; + } + else + ok_(file, line) (0, "%s: call %s was expected, but got call %s instead\n", + context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind)); + } + expected++; + actual++; + } + + if (todo) { + todo_wine { + if (expected->kind != DRAW_LAST_KIND || actual->kind != DRAW_LAST_KIND) { + failcount++; + ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n", + context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind)); + } + } + } + else if (expected->kind != DRAW_LAST_KIND || actual->kind != DRAW_LAST_KIND) + ok_(file, line) (0, "%s: the call sequence is not complete: expected %s - actual %s\n", + context, get_draw_kind_name(expected->kind), get_draw_kind_name(actual->kind)); + + if (todo && !failcount) /* succeeded yet marked todo */ + todo_wine + ok_(file, line)(1, "%s: marked \"todo_wine\" but succeeds\n", context); + + flush_sequence(seq, sequence_index); +} + +#define ok_sequence(seq, index, exp, contx, todo) \ + ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__) + +static HRESULT WINAPI testrenderer_QI(IDWriteTextRenderer *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IDWriteTextRenderer) || + IsEqualIID(riid, &IID_IDWritePixelSnapping) || + IsEqualIID(riid, &IID_IUnknown) + ) { + *obj = iface; + return S_OK; + } + + *obj = NULL; + + /* IDWriteTextRenderer1 overrides drawing calls, ignore for now */ + if (IsEqualIID(riid, &IID_IDWriteTextRenderer1)) + return E_NOINTERFACE; + + ok(0, "unexpected QI %s\n", wine_dbgstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI testrenderer_AddRef(IDWriteTextRenderer *iface) +{ + return 2; +} + +static ULONG WINAPI testrenderer_Release(IDWriteTextRenderer *iface) +{ + return 1; +} + +static HRESULT WINAPI testrenderer_IsPixelSnappingDisabled(IDWriteTextRenderer *iface, + void *client_drawingcontext, BOOL *disabled) +{ + *disabled = TRUE; + return S_OK; +} + +static HRESULT WINAPI testrenderer_GetCurrentTransform(IDWriteTextRenderer *iface, + void *client_drawingcontext, DWRITE_MATRIX *transform) +{ + transform->m11 = 1.0; + transform->m12 = 0.0; + transform->m21 = 0.0; + transform->m22 = 1.0; + transform->dx = 0.0; + transform->dy = 0.0; + return S_OK; +} + +static HRESULT WINAPI testrenderer_GetPixelsPerDip(IDWriteTextRenderer *iface, + void *client_drawingcontext, FLOAT *pixels_per_dip) +{ + *pixels_per_dip = 1.0; + return S_OK; +} + +static HRESULT WINAPI testrenderer_DrawGlyphRun(IDWriteTextRenderer *iface, + void* client_drawingcontext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_MEASURING_MODE mode, + DWRITE_GLYPH_RUN const *glyph_run, + DWRITE_GLYPH_RUN_DESCRIPTION const *run_descr, + IUnknown *drawing_effect) +{ + struct drawcall_entry entry; + entry.kind = DRAW_GLYPHRUN; + add_call(sequences, RENDERER_ID, &entry); + return S_OK; +} + +static HRESULT WINAPI testrenderer_DrawUnderline(IDWriteTextRenderer *iface, + void *client_drawingcontext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_UNDERLINE const* underline, + IUnknown *drawing_effect) +{ + struct drawcall_entry entry; + entry.kind = DRAW_UNDERLINE; + add_call(sequences, RENDERER_ID, &entry); + return S_OK; +} + +static HRESULT WINAPI testrenderer_DrawStrikethrough(IDWriteTextRenderer *iface, + void *client_drawingcontext, + FLOAT baselineOriginX, + FLOAT baselineOriginY, + DWRITE_STRIKETHROUGH const* strikethrough, + IUnknown *drawing_effect) +{ + struct drawcall_entry entry; + entry.kind = DRAW_STRIKETHROUGH; + add_call(sequences, RENDERER_ID, &entry); + return S_OK; +} + +static HRESULT WINAPI testrenderer_DrawInlineObject(IDWriteTextRenderer *iface, + void *client_drawingcontext, + FLOAT originX, + FLOAT originY, + IDWriteInlineObject *object, + BOOL is_sideways, + BOOL is_rtl, + IUnknown *drawing_effect) +{ + struct drawcall_entry entry; + entry.kind = DRAW_INLINE; + add_call(sequences, RENDERER_ID, &entry); + return S_OK; +} + +static const IDWriteTextRendererVtbl testrenderervtbl = { + testrenderer_QI, + testrenderer_AddRef, + testrenderer_Release, + testrenderer_IsPixelSnappingDisabled, + testrenderer_GetCurrentTransform, + testrenderer_GetPixelsPerDip, + testrenderer_DrawGlyphRun, + testrenderer_DrawUnderline, + testrenderer_DrawStrikethrough, + testrenderer_DrawInlineObject +}; + +static IDWriteTextRenderer testrenderer = { &testrenderervtbl }; + static void test_CreateTextLayout(void) { static const WCHAR strW[] = {'s','t','r','i','n','g',0}; @@ -405,6 +673,74 @@ todo_wine IDWriteTextFormat_Release(format); } +/* drawing calls sequence doesn't depend on run order, instead all runs are + drawn first, inline objects next and then underline/strikes */ +static const struct drawcall_entry draw_seq[] = { + { DRAW_GLYPHRUN }, + { DRAW_GLYPHRUN }, + { DRAW_GLYPHRUN }, + { DRAW_GLYPHRUN }, + { DRAW_INLINE }, + { DRAW_UNDERLINE }, + { DRAW_STRIKETHROUGH }, + { DRAW_LAST_KIND } +}; + +static void test_draw_sequence(void) +{ + static const WCHAR strW[] = {'s','t','r','i','n','g',0}; + static const WCHAR ruW[] = {'r','u',0}; + + IDWriteInlineObject *inlineobj; + IDWriteTextFormat *format; + IDWriteTextLayout *layout; + DWRITE_TEXT_RANGE range; + HRESULT hr; + + hr = IDWriteFactory_CreateTextFormat(factory, tahomaW, NULL, DWRITE_FONT_WEIGHT_BOLD, DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, 10.0, ruW, &format); + ok(hr == S_OK, "got 0x%08x\n", hr); + + hr = IDWriteFactory_CreateGdiCompatibleTextLayout(factory, strW, 6, format, 100.0, 100.0, 1.0, NULL, FALSE, &layout); + ok(hr == S_OK, "got 0x%08x\n", hr); + + hr = IDWriteFactory_CreateEllipsisTrimmingSign(factory, format, &inlineobj); + ok(hr == S_OK, "got 0x%08x\n", hr); + + range.startPosition = 5; + range.length = 1; + hr = IDWriteTextLayout_SetStrikethrough(layout, TRUE, range); +todo_wine + ok(hr == S_OK, "got 0x%08x\n", hr); + + range.startPosition = 1; + range.length = 1; + hr = IDWriteTextLayout_SetInlineObject(layout, inlineobj, range); +todo_wine + ok(hr == S_OK, "got 0x%08x\n", hr); + + range.startPosition = 4; + range.length = 1; + hr = IDWriteTextLayout_SetDrawingEffect(layout, (IUnknown*)inlineobj, range); +todo_wine + ok(hr == S_OK, "got 0x%08x\n", hr); + + range.startPosition = 0; + range.length = 1; + hr = IDWriteTextLayout_SetUnderline(layout, TRUE, range); +todo_wine + ok(hr == S_OK, "got 0x%08x\n", hr); + + flush_sequence(sequences, RENDERER_ID); + hr = IDWriteTextLayout_Draw(layout, NULL, &testrenderer, 0.0, 0.0); +todo_wine + ok(hr == S_OK, "got 0x%08x\n", hr); + ok_sequence(sequences, RENDERER_ID, draw_seq, "draw test", TRUE); + + IDWriteTextFormat_Release(format); + IDWriteTextLayout_Release(layout); +} + START_TEST(layout) { HRESULT hr; @@ -417,6 +753,9 @@ START_TEST(layout) return; } + init_call_sequences(sequences, NUM_CALL_SEQUENCES); + init_call_sequences(expected_seq, 1); + test_CreateTextLayout(); test_CreateGdiCompatibleTextLayout(); test_CreateTextFormat(); @@ -424,6 +763,7 @@ START_TEST(layout) test_CreateEllipsisTrimmingSign(); test_fontweight(); test_SetInlineObject(); + test_draw_sequence(); IDWriteFactory_Release(factory); }