From b06a6c84ac4fda7032606731c4293a69a0e30402 Mon Sep 17 00:00:00 2001 From: Dylan Smith Date: Wed, 9 Mar 2011 08:26:42 -0500 Subject: [PATCH] d3dx9/tests: Added outline comparison tests for D3DXCreateText. --- dlls/d3dx9_36/tests/mesh.c | 759 +++++++++++++++++++++++++++++++++++++ 1 file changed, 759 insertions(+) diff --git a/dlls/d3dx9_36/tests/mesh.c b/dlls/d3dx9_36/tests/mesh.c index 476fb309154..74f61fa24de 100644 --- a/dlls/d3dx9_36/tests/mesh.c +++ b/dlls/d3dx9_36/tests/mesh.c @@ -2012,10 +2012,756 @@ static void D3DXCreateCylinderTest(void) DestroyWindow(wnd); } +struct dynamic_array +{ + int count, capacity; + void *items; +}; + +enum pointtype { + POINTTYPE_CURVE = 0, + POINTTYPE_CORNER, + POINTTYPE_CURVE_START, + POINTTYPE_CURVE_END, + POINTTYPE_CURVE_MIDDLE, +}; + +struct point2d +{ + D3DXVECTOR2 pos; + enum pointtype corner; +}; + +/* is a dynamic_array */ +struct outline +{ + int count, capacity; + struct point2d *items; +}; + +/* is a dynamic_array */ +struct outline_array +{ + int count, capacity; + struct outline *items; +}; + +struct glyphinfo +{ + struct outline_array outlines; + float offset_x; +}; + +static BOOL reserve(struct dynamic_array *array, int count, int itemsize) +{ + if (count > array->capacity) { + void *new_buffer; + int new_capacity; + if (array->items && array->capacity) { + new_capacity = max(array->capacity * 2, count); + new_buffer = HeapReAlloc(GetProcessHeap(), 0, array->items, new_capacity * itemsize); + } else { + new_capacity = max(16, count); + new_buffer = HeapAlloc(GetProcessHeap(), 0, new_capacity * itemsize); + } + if (!new_buffer) + return FALSE; + array->items = new_buffer; + array->capacity = new_capacity; + } + return TRUE; +} + +static struct point2d *add_point(struct outline *array) +{ + struct point2d *item; + + if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0]))) + return NULL; + + item = &array->items[array->count++]; + ZeroMemory(item, sizeof(*item)); + return item; +} + +static struct outline *add_outline(struct outline_array *array) +{ + struct outline *item; + + if (!reserve((struct dynamic_array *)array, array->count + 1, sizeof(array->items[0]))) + return NULL; + + item = &array->items[array->count++]; + ZeroMemory(item, sizeof(*item)); + return item; +} + +static inline D3DXVECTOR2 *convert_fixed_to_float(POINTFX *pt, int count, float emsquare) +{ + D3DXVECTOR2 *ret = (D3DXVECTOR2*)pt; + while (count--) { + D3DXVECTOR2 *pt_flt = (D3DXVECTOR2*)pt; + pt_flt->x = (pt->x.value + pt->x.fract / (float)0x10000) / emsquare; + pt_flt->y = (pt->y.value + pt->y.fract / (float)0x10000) / emsquare; + pt++; + } + return ret; +} + +static HRESULT add_bezier_points(struct outline *outline, const D3DXVECTOR2 *p1, + const D3DXVECTOR2 *p2, const D3DXVECTOR2 *p3, + float max_deviation) +{ + D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec; + float deviation; + + D3DXVec2Scale(&split1, D3DXVec2Add(&split1, p1, p2), 0.5f); + D3DXVec2Scale(&split2, D3DXVec2Add(&split2, p2, p3), 0.5f); + D3DXVec2Scale(&middle, D3DXVec2Add(&middle, &split1, &split2), 0.5f); + + deviation = D3DXVec2Length(D3DXVec2Subtract(&vec, &middle, p2)); + if (deviation < max_deviation) { + struct point2d *pt = add_point(outline); + if (!pt) return E_OUTOFMEMORY; + pt->pos = *p2; + pt->corner = POINTTYPE_CURVE; + /* the end point is omitted because the end line merges into the next segment of + * the split bezier curve, and the end of the split bezier curve is added outside + * this recursive function. */ + } else { + HRESULT hr = add_bezier_points(outline, p1, &split1, &middle, max_deviation); + if (hr != S_OK) return hr; + hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation); + if (hr != S_OK) return hr; + } + + return S_OK; +} + +static inline BOOL is_direction_similar(D3DXVECTOR2 *dir1, D3DXVECTOR2 *dir2, float cos_theta) +{ + /* dot product = cos(theta) */ + return D3DXVec2Dot(dir1, dir2) > cos_theta; +} + +static inline D3DXVECTOR2 *unit_vec2(D3DXVECTOR2 *dir, const D3DXVECTOR2 *pt1, const D3DXVECTOR2 *pt2) +{ + return D3DXVec2Normalize(D3DXVec2Subtract(dir, pt2, pt1), dir); +} + +static BOOL attempt_line_merge(struct outline *outline, + int pt_index, + const D3DXVECTOR2 *nextpt, + BOOL to_curve) +{ + D3DXVECTOR2 curdir, lastdir; + struct point2d *prevpt, *pt; + BOOL ret = FALSE; + const float cos_half = cos(D3DXToRadian(0.5f)); + + pt = &outline->items[pt_index]; + pt_index = (pt_index - 1 + outline->count) % outline->count; + prevpt = &outline->items[pt_index]; + + if (to_curve) + pt->corner = pt->corner != POINTTYPE_CORNER ? POINTTYPE_CURVE_MIDDLE : POINTTYPE_CURVE_START; + + if (outline->count < 2) + return FALSE; + + /* remove last point if the next line continues the last line */ + unit_vec2(&lastdir, &prevpt->pos, &pt->pos); + unit_vec2(&curdir, &pt->pos, nextpt); + if (is_direction_similar(&lastdir, &curdir, cos_half)) + { + outline->count--; + if (pt->corner == POINTTYPE_CURVE_END) + prevpt->corner = pt->corner; + if (prevpt->corner == POINTTYPE_CURVE_END && to_curve) + prevpt->corner = POINTTYPE_CURVE_MIDDLE; + pt = prevpt; + + ret = TRUE; + if (outline->count < 2) + return ret; + + pt_index = (pt_index - 1 + outline->count) % outline->count; + prevpt = &outline->items[pt_index]; + unit_vec2(&lastdir, &prevpt->pos, &pt->pos); + unit_vec2(&curdir, &pt->pos, nextpt); + } + return ret; +} + +static HRESULT create_outline(struct glyphinfo *glyph, void *raw_outline, int datasize, + float max_deviation, float emsquare) +{ + const float cos_45 = cos(D3DXToRadian(45.0f)); + const float cos_90 = cos(D3DXToRadian(90.0f)); + TTPOLYGONHEADER *header = (TTPOLYGONHEADER *)raw_outline; + + while ((char *)header < (char *)raw_outline + datasize) + { + TTPOLYCURVE *curve = (TTPOLYCURVE *)(header + 1); + struct point2d *lastpt, *pt; + D3DXVECTOR2 lastdir; + D3DXVECTOR2 *pt_flt; + int j; + struct outline *outline = add_outline(&glyph->outlines); + + if (!outline) + return E_OUTOFMEMORY; + + pt = add_point(outline); + if (!pt) + return E_OUTOFMEMORY; + pt_flt = convert_fixed_to_float(&header->pfxStart, 1, emsquare); + pt->pos = *pt_flt; + pt->corner = POINTTYPE_CORNER; + + if (header->dwType != TT_POLYGON_TYPE) + trace("Unknown header type %d\n", header->dwType); + + while ((char *)curve < (char *)header + header->cb) + { + D3DXVECTOR2 bezier_start = outline->items[outline->count - 1].pos; + BOOL to_curve = curve->wType != TT_PRIM_LINE && curve->cpfx > 1; + + if (!curve->cpfx) { + curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx]; + continue; + } + + pt_flt = convert_fixed_to_float(curve->apfx, curve->cpfx, emsquare); + + attempt_line_merge(outline, outline->count - 1, &pt_flt[0], to_curve); + + if (to_curve) + { + HRESULT hr; + int count = curve->cpfx; + j = 0; + + while (count > 2) + { + D3DXVECTOR2 bezier_end; + + D3DXVec2Scale(&bezier_end, D3DXVec2Add(&bezier_end, &pt_flt[j], &pt_flt[j+1]), 0.5f); + hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &bezier_end, max_deviation); + if (hr != S_OK) + return hr; + bezier_start = bezier_end; + count--; + j++; + } + hr = add_bezier_points(outline, &bezier_start, &pt_flt[j], &pt_flt[j+1], max_deviation); + if (hr != S_OK) + return hr; + + pt = add_point(outline); + if (!pt) + return E_OUTOFMEMORY; + j++; + pt->pos = pt_flt[j]; + pt->corner = POINTTYPE_CURVE_END; + } else { + for (j = 0; j < curve->cpfx; j++) + { + pt = add_point(outline); + if (!pt) + return E_OUTOFMEMORY; + pt->pos = pt_flt[j]; + pt->corner = POINTTYPE_CORNER; + } + } + + curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx]; + } + + /* remove last point if the next line continues the last line */ + if (outline->count >= 3) { + BOOL to_curve; + + lastpt = &outline->items[outline->count - 1]; + pt = &outline->items[0]; + if (pt->pos.x == lastpt->pos.x && pt->pos.y == lastpt->pos.y) { + if (lastpt->corner == POINTTYPE_CURVE_END) + { + if (pt->corner == POINTTYPE_CURVE_START) + pt->corner = POINTTYPE_CURVE_MIDDLE; + else + pt->corner = POINTTYPE_CURVE_END; + } + outline->count--; + lastpt = &outline->items[outline->count - 1]; + } else { + /* outline closed with a line from end to start point */ + attempt_line_merge(outline, outline->count - 1, &pt->pos, FALSE); + } + lastpt = &outline->items[0]; + to_curve = lastpt->corner != POINTTYPE_CORNER && lastpt->corner != POINTTYPE_CURVE_END; + if (lastpt->corner == POINTTYPE_CURVE_START) + lastpt->corner = POINTTYPE_CORNER; + pt = &outline->items[1]; + if (attempt_line_merge(outline, 0, &pt->pos, to_curve)) + *lastpt = outline->items[outline->count]; + } + + lastpt = &outline->items[outline->count - 1]; + pt = &outline->items[0]; + unit_vec2(&lastdir, &lastpt->pos, &pt->pos); + for (j = 0; j < outline->count; j++) + { + D3DXVECTOR2 curdir; + + lastpt = pt; + pt = &outline->items[(j + 1) % outline->count]; + unit_vec2(&curdir, &lastpt->pos, &pt->pos); + + switch (lastpt->corner) + { + case POINTTYPE_CURVE_START: + case POINTTYPE_CURVE_END: + if (!is_direction_similar(&lastdir, &curdir, cos_45)) + lastpt->corner = POINTTYPE_CORNER; + break; + case POINTTYPE_CURVE_MIDDLE: + if (!is_direction_similar(&lastdir, &curdir, cos_90)) + lastpt->corner = POINTTYPE_CORNER; + else + lastpt->corner = POINTTYPE_CURVE; + break; + default: + break; + } + lastdir = curdir; + } + + header = (TTPOLYGONHEADER *)((char *)header + header->cb); + } + return S_OK; +} + +static BOOL compute_text_mesh(struct mesh *mesh, HDC hdc, LPCSTR text, FLOAT deviation, FLOAT extrusion, FLOAT otmEMSquare) +{ + HRESULT hr = E_FAIL; + DWORD nb_vertices, nb_faces; + DWORD nb_corners, nb_outline_points; + int textlen = 0; + float offset_x; + char *raw_outline = NULL; + struct glyphinfo *glyphs = NULL; + GLYPHMETRICS gm; + int i; + struct vertex *vertex_ptr; + face *face_ptr; + + if (deviation == 0.0f) + deviation = 1.0f / otmEMSquare; + + textlen = strlen(text); + glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs)); + if (!glyphs) { + hr = E_OUTOFMEMORY; + goto error; + } + + offset_x = 0.0f; + for (i = 0; i < textlen; i++) + { + /* get outline points from data returned from GetGlyphOutline */ + const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; + int datasize; + + glyphs[i].offset_x = offset_x; + + datasize = GetGlyphOutline(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity); + if (datasize < 0) { + hr = E_FAIL; + goto error; + } + HeapFree(GetProcessHeap(), 0, raw_outline); + raw_outline = HeapAlloc(GetProcessHeap(), 0, datasize); + if (!glyphs) { + hr = E_OUTOFMEMORY; + goto error; + } + datasize = GetGlyphOutline(hdc, text[i], GGO_NATIVE, &gm, datasize, raw_outline, &identity); + + create_outline(&glyphs[i], raw_outline, datasize, deviation, otmEMSquare); + + offset_x += gm.gmCellIncX / (float)otmEMSquare; + } + + /* corner points need an extra vertex for the different side faces normals */ + nb_corners = 0; + nb_outline_points = 0; + for (i = 0; i < textlen; i++) + { + int j; + for (j = 0; j < glyphs[i].outlines.count; j++) + { + int k; + struct outline *outline = &glyphs[i].outlines.items[j]; + nb_outline_points += outline->count; + nb_corners++; /* first outline point always repeated as a corner */ + for (k = 1; k < outline->count; k++) + if (outline->items[k].corner) + nb_corners++; + } + } + + nb_vertices = (nb_outline_points + nb_corners) * 2 + textlen; + nb_faces = nb_outline_points * 2; + + if (!new_mesh(mesh, nb_vertices, nb_faces)) + goto error; + + /* convert 2D vertices and faces into 3D mesh */ + vertex_ptr = mesh->vertices; + face_ptr = mesh->faces; + for (i = 0; i < textlen; i++) + { + int j; + + /* side vertices and faces */ + for (j = 0; j < glyphs[i].outlines.count; j++) + { + struct vertex *outline_vertices = vertex_ptr; + struct outline *outline = &glyphs[i].outlines.items[j]; + int k; + struct point2d *prevpt = &outline->items[outline->count - 1]; + struct point2d *pt = &outline->items[0]; + + for (k = 1; k <= outline->count; k++) + { + struct vertex vtx; + struct point2d *nextpt = &outline->items[k % outline->count]; + WORD vtx_idx = vertex_ptr - mesh->vertices; + D3DXVECTOR2 vec; + + if (pt->corner == POINTTYPE_CURVE_START) + D3DXVec2Subtract(&vec, &pt->pos, &prevpt->pos); + else if (pt->corner) + D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos); + else + D3DXVec2Subtract(&vec, &nextpt->pos, &prevpt->pos); + D3DXVec2Normalize(&vec, &vec); + vtx.normal.x = -vec.y; + vtx.normal.y = vec.x; + vtx.normal.z = 0; + + vtx.position.x = pt->pos.x + glyphs[i].offset_x; + vtx.position.y = pt->pos.y; + vtx.position.z = 0; + *vertex_ptr++ = vtx; + + vtx.position.z = -extrusion; + *vertex_ptr++ = vtx; + + vtx.position.x = nextpt->pos.x + glyphs[i].offset_x; + vtx.position.y = nextpt->pos.y; + if (pt->corner && nextpt->corner && nextpt->corner != POINTTYPE_CURVE_END) { + vtx.position.z = -extrusion; + *vertex_ptr++ = vtx; + vtx.position.z = 0; + *vertex_ptr++ = vtx; + + (*face_ptr)[0] = vtx_idx; + (*face_ptr)[1] = vtx_idx + 2; + (*face_ptr)[2] = vtx_idx + 1; + face_ptr++; + + (*face_ptr)[0] = vtx_idx; + (*face_ptr)[1] = vtx_idx + 3; + (*face_ptr)[2] = vtx_idx + 2; + face_ptr++; + } else { + if (nextpt->corner) { + if (nextpt->corner == POINTTYPE_CURVE_END) { + struct point2d *nextpt2 = &outline->items[(k + 1) % outline->count]; + D3DXVec2Subtract(&vec, &nextpt2->pos, &nextpt->pos); + } else { + D3DXVec2Subtract(&vec, &nextpt->pos, &pt->pos); + } + D3DXVec2Normalize(&vec, &vec); + vtx.normal.x = -vec.y; + vtx.normal.y = vec.x; + + vtx.position.z = 0; + *vertex_ptr++ = vtx; + vtx.position.z = -extrusion; + *vertex_ptr++ = vtx; + } + + (*face_ptr)[0] = vtx_idx; + (*face_ptr)[1] = vtx_idx + 3; + (*face_ptr)[2] = vtx_idx + 1; + face_ptr++; + + (*face_ptr)[0] = vtx_idx; + (*face_ptr)[1] = vtx_idx + 2; + (*face_ptr)[2] = vtx_idx + 3; + face_ptr++; + } + + prevpt = pt; + pt = nextpt; + } + if (!pt->corner) { + *vertex_ptr++ = *outline_vertices++; + *vertex_ptr++ = *outline_vertices++; + } + } + + /* FIXME: compute expected faces */ + /* Add placeholder to seperate glyph outlines */ + vertex_ptr->position.x = 0; + vertex_ptr->position.y = 0; + vertex_ptr->position.z = 0; + vertex_ptr->normal.x = 0; + vertex_ptr->normal.y = 0; + vertex_ptr->normal.z = 1; + vertex_ptr++; + } + + hr = D3D_OK; +error: + if (glyphs) { + for (i = 0; i < textlen; i++) + { + int j; + for (j = 0; j < glyphs[i].outlines.count; j++) + HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items[j].items); + HeapFree(GetProcessHeap(), 0, glyphs[i].outlines.items); + } + HeapFree(GetProcessHeap(), 0, glyphs); + } + HeapFree(GetProcessHeap(), 0, raw_outline); + + return hr == D3D_OK; +} + +static void compare_text_outline_mesh(const char *name, ID3DXMesh *d3dxmesh, struct mesh *mesh, int textlen) +{ + HRESULT hr; + DWORD number_of_vertices, number_of_faces; + IDirect3DVertexBuffer9 *vertex_buffer = NULL; + IDirect3DIndexBuffer9 *index_buffer = NULL; + D3DVERTEXBUFFER_DESC vertex_buffer_description; + D3DINDEXBUFFER_DESC index_buffer_description; + struct vertex *vertices = NULL; + face *faces = NULL; + int expected, i; + int vtx_idx1, face_idx1, vtx_idx2, face_idx2; + + number_of_vertices = d3dxmesh->lpVtbl->GetNumVertices(d3dxmesh); + number_of_faces = d3dxmesh->lpVtbl->GetNumFaces(d3dxmesh); + + /* vertex buffer */ + hr = d3dxmesh->lpVtbl->GetVertexBuffer(d3dxmesh, &vertex_buffer); + ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr); + if (hr != D3D_OK) + { + skip("Couldn't get vertex buffers\n"); + goto error; + } + + hr = IDirect3DVertexBuffer9_GetDesc(vertex_buffer, &vertex_buffer_description); + ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr); + + if (hr != D3D_OK) + { + skip("Couldn't get vertex buffer description\n"); + } + else + { + ok(vertex_buffer_description.Format == D3DFMT_VERTEXDATA, "Test %s, result %x, expected %x (D3DFMT_VERTEXDATA)\n", + name, vertex_buffer_description.Format, D3DFMT_VERTEXDATA); + ok(vertex_buffer_description.Type == D3DRTYPE_VERTEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_VERTEXBUFFER)\n", + name, vertex_buffer_description.Type, D3DRTYPE_VERTEXBUFFER); + ok(vertex_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, vertex_buffer_description.Usage, 0); + ok(vertex_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_DEFAULT)\n", + name, vertex_buffer_description.Pool, D3DPOOL_DEFAULT); + ok(vertex_buffer_description.FVF == mesh->fvf, "Test %s, result %x, expected %x\n", + name, vertex_buffer_description.FVF, mesh->fvf); + if (mesh->fvf == 0) + { + expected = number_of_vertices * mesh->vertex_size; + } + else + { + expected = number_of_vertices * D3DXGetFVFVertexSize(mesh->fvf); + } + ok(vertex_buffer_description.Size == expected, "Test %s, result %x, expected %x\n", + name, vertex_buffer_description.Size, expected); + } + + hr = d3dxmesh->lpVtbl->GetIndexBuffer(d3dxmesh, &index_buffer); + ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr); + if (hr != D3D_OK) + { + skip("Couldn't get index buffer\n"); + goto error; + } + + hr = IDirect3DIndexBuffer9_GetDesc(index_buffer, &index_buffer_description); + ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr); + + if (hr != D3D_OK) + { + skip("Couldn't get index buffer description\n"); + } + else + { + ok(index_buffer_description.Format == D3DFMT_INDEX16, "Test %s, result %x, expected %x (D3DFMT_INDEX16)\n", + name, index_buffer_description.Format, D3DFMT_INDEX16); + ok(index_buffer_description.Type == D3DRTYPE_INDEXBUFFER, "Test %s, result %x, expected %x (D3DRTYPE_INDEXBUFFER)\n", + name, index_buffer_description.Type, D3DRTYPE_INDEXBUFFER); + todo_wine ok(index_buffer_description.Usage == 0, "Test %s, result %x, expected %x\n", name, index_buffer_description.Usage, 0); + ok(index_buffer_description.Pool == D3DPOOL_MANAGED, "Test %s, result %x, expected %x (D3DPOOL_DEFAULT)\n", + name, index_buffer_description.Pool, D3DPOOL_DEFAULT); + expected = number_of_faces * sizeof(WORD) * 3; + ok(index_buffer_description.Size == expected, "Test %s, result %x, expected %x\n", + name, index_buffer_description.Size, expected); + } + + /* specify offset and size to avoid potential overruns */ + hr = IDirect3DVertexBuffer9_Lock(vertex_buffer, 0, number_of_vertices * sizeof(D3DXVECTOR3) * 2, + (LPVOID *)&vertices, D3DLOCK_DISCARD); + ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr); + if (hr != D3D_OK) + { + skip("Couldn't lock vertex buffer\n"); + goto error; + } + hr = IDirect3DIndexBuffer9_Lock(index_buffer, 0, number_of_faces * sizeof(WORD) * 3, + (LPVOID *)&faces, D3DLOCK_DISCARD); + ok(hr == D3D_OK, "Test %s, result %x, expected 0 (D3D_OK)\n", name, hr); + if (hr != D3D_OK) + { + skip("Couldn't lock index buffer\n"); + goto error; + } + + face_idx1 = 0; + vtx_idx2 = 0; + face_idx2 = 0; + vtx_idx1 = 0; + for (i = 0; i < textlen; i++) + { + int nb_outline_vertices1, nb_outline_faces1; + int nb_outline_vertices2, nb_outline_faces2; + int first_vtx1, first_vtx2; + int first_face1, first_face2; + int j; + + first_vtx1 = vtx_idx1; + first_vtx2 = vtx_idx2; + for (; vtx_idx1 < number_of_vertices; vtx_idx1++) { + if (vertices[vtx_idx1].normal.z != 0) + break; + } + for (; vtx_idx2 < mesh->number_of_vertices; vtx_idx2++) { + if (mesh->vertices[vtx_idx2].normal.z != 0) + break; + } + nb_outline_vertices1 = vtx_idx1 - first_vtx1; + nb_outline_vertices2 = vtx_idx2 - first_vtx2; + ok(nb_outline_vertices1 == nb_outline_vertices2, + "Test %s, glyph %d, outline vertex count result %d, expected %d\n", name, i, + nb_outline_vertices1, nb_outline_vertices2); + + for (j = 0; j < min(nb_outline_vertices1, nb_outline_vertices2); j++) + { + vtx_idx1 = first_vtx1 + j; + vtx_idx2 = first_vtx2 + j; + ok(compare_vec3(vertices[vtx_idx1].position, mesh->vertices[vtx_idx2].position), + "Test %s, glyph %d, vertex position %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1, + vertices[vtx_idx1].position.x, vertices[vtx_idx1].position.y, vertices[vtx_idx1].position.z, + mesh->vertices[vtx_idx2].position.x, mesh->vertices[vtx_idx2].position.y, mesh->vertices[vtx_idx2].position.z); + ok(compare_vec3(vertices[vtx_idx1].normal, mesh->vertices[first_vtx2 + j].normal), + "Test %s, glyph %d, vertex normal %d, result (%g, %g, %g), expected (%g, %g, %g)\n", name, i, vtx_idx1, + vertices[vtx_idx1].normal.x, vertices[vtx_idx1].normal.y, vertices[vtx_idx1].normal.z, + mesh->vertices[vtx_idx2].normal.x, mesh->vertices[vtx_idx2].normal.y, mesh->vertices[vtx_idx2].normal.z); + } + vtx_idx1 = first_vtx1 + nb_outline_vertices1; + vtx_idx2 = first_vtx2 + nb_outline_vertices2; + + first_face1 = face_idx1; + first_face2 = face_idx2; + for (; face_idx1 < number_of_faces; face_idx1++) + { + if (faces[face_idx1][0] >= vtx_idx1 || + faces[face_idx1][1] >= vtx_idx1 || + faces[face_idx1][2] >= vtx_idx1) + break; + } + for (; face_idx2 < mesh->number_of_faces; face_idx2++) + { + if (mesh->faces[face_idx2][0] >= vtx_idx2 || + mesh->faces[face_idx2][1] >= vtx_idx2 || + mesh->faces[face_idx2][2] >= vtx_idx2) + break; + } + nb_outline_faces1 = face_idx1 - first_face1; + nb_outline_faces2 = face_idx2 - first_face2; + ok(nb_outline_faces1 == nb_outline_faces2, + "Test %s, glyph %d, outline face count result %d, expected %d\n", name, i, + nb_outline_faces1, nb_outline_faces2); + + for (j = 0; j < min(nb_outline_faces1, nb_outline_faces2); j++) + { + face_idx1 = first_face1 + j; + face_idx2 = first_face2 + j; + ok(faces[face_idx1][0] - first_vtx1 == mesh->faces[face_idx2][0] - first_vtx2 && + faces[face_idx1][1] - first_vtx1 == mesh->faces[face_idx2][1] - first_vtx2 && + faces[face_idx1][2] - first_vtx1 == mesh->faces[face_idx2][2] - first_vtx2, + "Test %s, glyph %d, face %d, result (%d, %d, %d), expected (%d, %d, %d)\n", name, i, face_idx1, + faces[face_idx1][0], faces[face_idx1][1], faces[face_idx1][2], + mesh->faces[face_idx2][0] - first_vtx2 + first_vtx1, + mesh->faces[face_idx2][1] - first_vtx2 + first_vtx1, + mesh->faces[face_idx2][2] - first_vtx2 + first_vtx1); + } + face_idx1 = first_face1 + nb_outline_faces1; + face_idx2 = first_face2 + nb_outline_faces2; + + /* skip to the outline for the next glyph */ + for (; vtx_idx1 < number_of_vertices; vtx_idx1++) { + if (vertices[vtx_idx1].normal.z == 0) + break; + } + for (; vtx_idx2 < mesh->number_of_vertices; vtx_idx2++) { + if (mesh->vertices[vtx_idx2].normal.z == 0) + break; + } + for (; face_idx1 < number_of_faces; face_idx1++) + { + if (faces[face_idx1][0] >= vtx_idx1 || + faces[face_idx1][1] >= vtx_idx1 || + faces[face_idx1][2] >= vtx_idx1) + break; + } + for (; face_idx2 < mesh->number_of_faces; face_idx2++) + { + if (mesh->faces[face_idx2][0] >= vtx_idx2 || + mesh->faces[face_idx2][1] >= vtx_idx2 || + mesh->faces[face_idx2][2] >= vtx_idx2) break; + } + } + +error: + if (vertices) IDirect3DVertexBuffer9_Unlock(vertex_buffer); + if (faces) IDirect3DIndexBuffer9_Unlock(index_buffer); + if (index_buffer) IDirect3DIndexBuffer9_Release(index_buffer); + if (vertex_buffer) IDirect3DVertexBuffer9_Release(vertex_buffer); +} + static void test_createtext(IDirect3DDevice9 *device, HDC hdc, LPCSTR text, FLOAT deviation, FLOAT extrusion) { HRESULT hr; ID3DXMesh *d3dxmesh; + struct mesh mesh; char name[256]; OUTLINETEXTMETRIC otm; GLYPHMETRICS gm; @@ -2065,6 +2811,19 @@ static void test_createtext(IDirect3DDevice9 *device, HDC hdc, LPCSTR text, FLOA compare_float(glyphmetrics_float[i].gmfCellIncY, gm.gmCellIncY / (float)otm.otmEMSquare); } + ZeroMemory(&mesh, sizeof(mesh)); + if (!compute_text_mesh(&mesh, hdc, text, deviation, extrusion, otm.otmEMSquare)) + { + skip("Couldn't create mesh\n"); + d3dxmesh->lpVtbl->Release(d3dxmesh); + return; + } + mesh.fvf = D3DFVF_XYZ | D3DFVF_NORMAL; + + compare_text_outline_mesh(name, d3dxmesh, &mesh, strlen(text)); + + free_mesh(&mesh); + d3dxmesh->lpVtbl->Release(d3dxmesh); SelectObject(hdc, oldfont); }