[1/5] d3dx9: D3DXCreateText implementation for simple glyphs. (resend)

Dylan Smith dylan.ah.smith at gmail.com
Wed Mar 9 07:25:56 CST 2011


This path implements D3DXCreateText, but only supports single outline
glyphs that can use a triangle fan for their front and back faces. The
native implementation uses this triangulation method for simple glyphs.
---
 dlls/d3dx9_36/mesh.c |  795 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 790 insertions(+), 5 deletions(-)

diff --git a/dlls/d3dx9_36/mesh.c b/dlls/d3dx9_36/mesh.c
index 0ccced6..814866e 100644
--- a/dlls/d3dx9_36/mesh.c
+++ b/dlls/d3dx9_36/mesh.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2006 Ivan Gyurdiev
  * Copyright (C) 2009 David Adam
  * Copyright (C) 2010 Tony Wasserka
+ * Copyright (C) 2011 Dylan Smith
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -29,6 +30,7 @@
 #include "wingdi.h"
 #include "d3dx9.h"
 #include "wine/debug.h"
+#include "wine/unicode.h"
 #include "d3dx9_36_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(d3dx);
@@ -1506,20 +1508,803 @@ HRESULT WINAPI D3DXCreateTextA(LPDIRECT3DDEVICE9 device,
                                LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
                                LPGLYPHMETRICSFLOAT glyphmetrics)
 {
-    FIXME("(%p, %p, %s, %f, %f, %p, %p, %p): stub\n", device, hdc,
+    HRESULT hr;
+    int len;
+    LPWSTR textW;
+
+    TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
           debugstr_a(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
 
+    if (!text)
+        return D3DERR_INVALIDCALL;
+
+    len = MultiByteToWideChar(CP_ACP, 0, text, -1, NULL, 0);
+    textW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    MultiByteToWideChar(CP_ACP, 0, text, -1, textW, len);
+
+    hr = D3DXCreateTextW(device, hdc, textW, deviation, extrusion,
+                         mesh, adjacency, glyphmetrics);
+    HeapFree(GetProcessHeap(), 0, textW);
+
+    return hr;
+}
+
+enum pointtype {
+    POINTTYPE_CURVE = 0,
+    POINTTYPE_CORNER,
+    POINTTYPE_CURVE_START,
+    POINTTYPE_CURVE_END,
+    POINTTYPE_CURVE_MIDDLE,
+};
+
+struct point2d
+{
+    D3DXVECTOR2 pos;
+    enum pointtype corner;
+};
+
+struct dynamic_array
+{
+    int count, capacity;
+    void *items;
+};
+
+/* 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 face_array
+{
+    int count;
+    face *items;
+};
+
+struct point2d_index
+{
+    struct outline *outline;
+    int vertex;
+};
+
+struct point2d_index_array
+{
+    int count;
+    struct point2d_index *items;
+};
+
+struct glyphinfo
+{
+    struct outline_array outlines;
+    struct face_array faces;
+    struct point2d_index_array ordered_vertices;
+    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_points(struct outline *array, int num)
+{
+    struct point2d *item;
+
+    if (!reserve((struct dynamic_array *)array, array->count + num, sizeof(array->items[0])))
+        return NULL;
+
+    item = &array->items[array->count];
+    array->count += num;
+    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;
+}
+
+/* assume fixed point numbers can be converted to float point in place */
+C_ASSERT(sizeof(FIXED) == sizeof(float));
+C_ASSERT(sizeof(POINTFX) == sizeof(D3DXVECTOR2));
+
+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_sq)
+{
+    D3DXVECTOR2 split1 = {0, 0}, split2 = {0, 0}, middle, vec;
+    float deviation_sq;
+
+    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_sq = D3DXVec2LengthSq(D3DXVec2Subtract(&vec, &middle, p2));
+    if (deviation_sq < max_deviation_sq) {
+        struct point2d *pt = add_points(outline, 1);
+        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_sq);
+        if (hr != S_OK) return hr;
+        hr = add_bezier_points(outline, &middle, &split2, p3, max_deviation_sq);
+        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);
+}
+
+struct cos_table
+{
+    float cos_half;
+    float cos_45;
+    float cos_90;
+};
+
+static BOOL attempt_line_merge(struct outline *outline,
+                               int pt_index,
+                               const D3DXVECTOR2 *nextpt,
+                               BOOL to_curve,
+                               const struct cos_table *table)
+{
+    D3DXVECTOR2 curdir, lastdir;
+    struct point2d *prevpt, *pt;
+    BOOL ret = FALSE;
+
+    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, table->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_sq, float emsquare, const struct cos_table *cos_table)
+{
+    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_points(outline, 1);
+        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)
+            FIXME("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, cos_table);
+
+            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_sq);
+                    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_sq);
+                if (hr != S_OK)
+                    return hr;
+
+                pt = add_points(outline, 1);
+                if (!pt)
+                    return E_OUTOFMEMORY;
+                j++;
+                pt->pos = pt_flt[j];
+                pt->corner = POINTTYPE_CURVE_END;
+            } else {
+                pt = add_points(outline, curve->cpfx);
+                if (!pt)
+                    return E_OUTOFMEMORY;
+                for (j = 0; j < curve->cpfx; j++)
+                {
+                    pt->pos = pt_flt[j];
+                    pt->corner = POINTTYPE_CORNER;
+                    pt++;
+                }
+            }
+
+            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, cos_table);
+            }
+            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, cos_table))
+                *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_table->cos_45))
+                        lastpt->corner = POINTTYPE_CORNER;
+                    break;
+                case POINTTYPE_CURVE_MIDDLE:
+                    if (!is_direction_similar(&lastdir, &curdir, cos_table->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 D3DXVECTOR2 *get_indexed_point(struct point2d_index *pt_idx)
+{
+    return &pt_idx->outline->items[pt_idx->vertex].pos;
+}
+
+static D3DXVECTOR2 *get_ordered_vertex(struct glyphinfo *glyph, WORD index)
+{
+    return get_indexed_point(&glyph->ordered_vertices.items[index]);
+}
+
+static HRESULT triangulate(struct glyphinfo *glyph)
+{
+    int nb_vertices = 0;
+    int i;
+    struct point2d_index *idx_ptr;
+
+    for (i = 0; i < glyph->outlines.count; i++)
+        nb_vertices += glyph->outlines.items[i].count;
+
+    glyph->ordered_vertices.items = HeapAlloc(GetProcessHeap(), 0,
+            nb_vertices * sizeof(*glyph->ordered_vertices.items));
+    if (!glyph->ordered_vertices.items)
+        return E_OUTOFMEMORY;
+
+    idx_ptr = glyph->ordered_vertices.items;
+    for (i = 0; i < glyph->outlines.count; i++)
+    {
+        struct outline *outline = &glyph->outlines.items[i];
+        int j;
+
+        idx_ptr->outline = outline;
+        idx_ptr->vertex = 0;
+        idx_ptr++;
+        for (j = outline->count - 1; j > 0; j--)
+        {
+            idx_ptr->outline = outline;
+            idx_ptr->vertex = j;
+            idx_ptr++;
+        }
+    }
+    glyph->ordered_vertices.count = nb_vertices;
+
+    /* Native implementation seems to try to create a triangle fan from
+     * the first outline point if the glyph only has one outline. */
+    if (glyph->outlines.count == 1)
+    {
+        struct outline *outline = glyph->outlines.items;
+        D3DXVECTOR2 *base = &outline->items[0].pos;
+        D3DXVECTOR2 *last = &outline->items[1].pos;
+        float ccw = 0;
+
+        for (i = 2; i < outline->count; i++)
+        {
+            D3DXVECTOR2 *next = &outline->items[i].pos;
+            D3DXVECTOR2 v1 = {0.0f, 0.0f};
+            D3DXVECTOR2 v2 = {0.0f, 0.0f};
+
+            D3DXVec2Subtract(&v1, base, last);
+            D3DXVec2Subtract(&v2, last, next);
+            ccw = D3DXVec2CCW(&v1, &v2);
+            if (ccw > 0.0f)
+                break;
+
+            last = next;
+        }
+        if (ccw <= 0)
+        {
+            glyph->faces.items = HeapAlloc(GetProcessHeap(), 0,
+                    (outline->count - 2) * sizeof(glyph->faces.items[0]));
+            if (!glyph->faces.items)
+                return E_OUTOFMEMORY;
+
+            glyph->faces.count = outline->count - 2;
+            for (i = 0; i < glyph->faces.count; i++)
+            {
+                glyph->faces.items[i][0] = 0;
+                glyph->faces.items[i][1] = i + 1;
+                glyph->faces.items[i][2] = i + 2;
+            }
+            return S_OK;
+        }
+    }
+
+    FIXME("triangulation of complex glyphs not yet implemented for D3DXCreateText.\n");
+
     return E_NOTIMPL;
 }
 
 HRESULT WINAPI D3DXCreateTextW(LPDIRECT3DDEVICE9 device,
                                HDC hdc, LPCWSTR text,
                                FLOAT deviation, FLOAT extrusion,
-                               LPD3DXMESH *mesh, LPD3DXBUFFER *adjacency,
+                               LPD3DXMESH *mesh_ptr, LPD3DXBUFFER *adjacency,
                                LPGLYPHMETRICSFLOAT glyphmetrics)
 {
-    FIXME("(%p, %p, %s, %f, %f, %p, %p, %p): stub\n", device, hdc,
-          debugstr_w(text), deviation, extrusion, mesh, adjacency, glyphmetrics);
+    HRESULT hr;
+    ID3DXMesh *mesh = NULL;
+    DWORD nb_vertices, nb_faces;
+    DWORD nb_front_faces, nb_corners, nb_outline_points;
+    struct vertex *vertices = NULL;
+    face *faces = NULL;
+    int textlen = 0;
+    float offset_x;
+    LOGFONTW lf;
+    OUTLINETEXTMETRICW otm;
+    HFONT font = NULL, oldfont = NULL;
+    const MAT2 identity = {{0, 1}, {0, 0}, {0, 0}, {0, 1}};
+    void *raw_outline = NULL;
+    int bufsize = 0;
+    struct glyphinfo *glyphs = NULL;
+    GLYPHMETRICS gm;
+    int i;
+    struct vertex *vertex_ptr;
+    face *face_ptr;
+    float max_deviation_sq;
+    const struct cos_table cos_table = {
+        cos(D3DXToRadian(0.5f)),
+        cos(D3DXToRadian(45.0f)),
+        cos(D3DXToRadian(90.0f)),
+    };
+    int f1, f2;
+
+    TRACE("(%p, %p, %s, %f, %f, %p, %p, %p)\n", device, hdc,
+          debugstr_w(text), deviation, extrusion, mesh_ptr, adjacency, glyphmetrics);
+
+    if (!device || !hdc || !text || !*text || deviation < 0.0f || extrusion < 0.0f || !mesh_ptr)
+        return D3DERR_INVALIDCALL;
 
-    return E_NOTIMPL;
+    if (adjacency)
+    {
+        FIXME("Case of adjacency != NULL not implemented.\n");
+        return E_NOTIMPL;
+    }
+
+    if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf) ||
+        !GetOutlineTextMetricsW(hdc, sizeof(otm), &otm))
+    {
+        return D3DERR_INVALIDCALL;
+    }
+
+    if (deviation == 0.0f)
+        deviation = 1.0f / otm.otmEMSquare;
+    max_deviation_sq = deviation * deviation;
+
+    lf.lfHeight = otm.otmEMSquare;
+    lf.lfWidth = 0;
+    font = CreateFontIndirectW(&lf);
+    if (!font) {
+        hr = E_OUTOFMEMORY;
+        goto error;
+    }
+    oldfont = SelectObject(hdc, font);
+
+    textlen = strlenW(text);
+    for (i = 0; i < textlen; i++)
+    {
+        int datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, 0, NULL, &identity);
+        if (datasize < 0)
+            return D3DERR_INVALIDCALL;
+        if (bufsize < datasize)
+            bufsize = datasize;
+    }
+    if (!bufsize) { /* e.g. text == " " */
+        hr = D3DERR_INVALIDCALL;
+        goto error;
+    }
+
+    glyphs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, textlen * sizeof(*glyphs));
+    raw_outline = HeapAlloc(GetProcessHeap(), 0, bufsize);
+    if (!glyphs || !raw_outline) {
+        hr = E_OUTOFMEMORY;
+        goto error;
+    }
+
+    offset_x = 0.0f;
+    for (i = 0; i < textlen; i++)
+    {
+        /* get outline points from data returned from GetGlyphOutline */
+        int datasize;
+
+        glyphs[i].offset_x = offset_x;
+
+        datasize = GetGlyphOutlineW(hdc, text[i], GGO_NATIVE, &gm, bufsize, raw_outline, &identity);
+        hr = create_outline(&glyphs[i], raw_outline, datasize,
+                            max_deviation_sq, otm.otmEMSquare, &cos_table);
+        if (hr != S_OK) goto error;
+
+        hr = triangulate(&glyphs[i]);
+        if (hr != S_OK) goto error;
+
+        if (glyphmetrics)
+        {
+            glyphmetrics[i].gmfBlackBoxX = gm.gmBlackBoxX / (float)otm.otmEMSquare;
+            glyphmetrics[i].gmfBlackBoxY = gm.gmBlackBoxY / (float)otm.otmEMSquare;
+            glyphmetrics[i].gmfptGlyphOrigin.x = gm.gmptGlyphOrigin.x / (float)otm.otmEMSquare;
+            glyphmetrics[i].gmfptGlyphOrigin.y = gm.gmptGlyphOrigin.y / (float)otm.otmEMSquare;
+            glyphmetrics[i].gmfCellIncX = gm.gmCellIncX / (float)otm.otmEMSquare;
+            glyphmetrics[i].gmfCellIncY = gm.gmCellIncY / (float)otm.otmEMSquare;
+        }
+        offset_x += gm.gmCellIncX / (float)otm.otmEMSquare;
+    }
+
+    /* corner points need an extra vertex for the different side faces normals */
+    nb_corners = 0;
+    nb_outline_points = 0;
+    nb_front_faces = 0;
+    for (i = 0; i < textlen; i++)
+    {
+        int j;
+        nb_outline_points += glyphs[i].ordered_vertices.count;
+        nb_front_faces += glyphs[i].faces.count;
+        for (j = 0; j < glyphs[i].outlines.count; j++)
+        {
+            int k;
+            struct outline *outline = &glyphs[i].outlines.items[j];
+            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 + nb_outline_points * 2;
+    nb_faces = nb_outline_points * 2 + nb_front_faces * 2;
+
+
+    hr = D3DXCreateMeshFVF(nb_faces, nb_vertices, D3DXMESH_MANAGED,
+                           D3DFVF_XYZ | D3DFVF_NORMAL, device, &mesh);
+    if (FAILED(hr))
+        goto error;
+
+    hr = mesh->lpVtbl->LockVertexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&vertices);
+    if (FAILED(hr))
+        goto error;
+
+    hr = mesh->lpVtbl->LockIndexBuffer(mesh, D3DLOCK_DISCARD, (LPVOID *)&faces);
+    if (FAILED(hr))
+        goto error;
+
+    /* convert 2D vertices and faces into 3D mesh */
+    vertex_ptr = vertices;
+    face_ptr = faces;
+    if (extrusion == 0.0f) {
+        f1 = 1;
+        f2 = 2;
+    } else {
+        f1 = 2;
+        f2 = 1;
+    }
+    for (i = 0; i < textlen; i++)
+    {
+        int j;
+        int count;
+        struct vertex *back_vertices;
+        face *back_faces;
+
+        /* 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 - 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) {
+                            D3DXVECTOR2 *nextpt2 = &outline->items[(k + 1) % outline->count].pos;
+                            D3DXVec2Subtract(&vec, nextpt2, &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++;
+            }
+        }
+
+        /* back vertices and faces */
+        back_faces = face_ptr;
+        back_vertices = vertex_ptr;
+        for (j = 0; j < glyphs[i].ordered_vertices.count; j++)
+        {
+            D3DXVECTOR2 *pt = get_ordered_vertex(&glyphs[i], j);
+            vertex_ptr->position.x = pt->x + glyphs[i].offset_x;
+            vertex_ptr->position.y = pt->y;
+            vertex_ptr->position.z = 0;
+            vertex_ptr->normal.x = 0;
+            vertex_ptr->normal.y = 0;
+            vertex_ptr->normal.z = 1;
+            vertex_ptr++;
+        }
+        count = back_vertices - vertices;
+        for (j = 0; j < glyphs[i].faces.count; j++)
+        {
+            face *f = &glyphs[i].faces.items[j];
+            (*face_ptr)[0] = (*f)[0] + count;
+            (*face_ptr)[1] = (*f)[1] + count;
+            (*face_ptr)[2] = (*f)[2] + count;
+            face_ptr++;
+        }
+
+        /* front vertices and faces */
+        j = count = vertex_ptr - back_vertices;
+        while (j--)
+        {
+            vertex_ptr->position.x = back_vertices->position.x;
+            vertex_ptr->position.y = back_vertices->position.y;
+            vertex_ptr->position.z = -extrusion;
+            vertex_ptr->normal.x = 0;
+            vertex_ptr->normal.y = 0;
+            vertex_ptr->normal.z = extrusion == 0.0f ? 1.0f : -1.0f;
+            vertex_ptr++;
+            back_vertices++;
+        }
+        j = face_ptr - back_faces;
+        while (j--)
+        {
+            (*face_ptr)[0] = (*back_faces)[0] + count;
+            (*face_ptr)[1] = (*back_faces)[f1] + count;
+            (*face_ptr)[2] = (*back_faces)[f2] + count;
+            face_ptr++;
+            back_faces++;
+        }
+    }
+
+    *mesh_ptr = mesh;
+    hr = D3D_OK;
+error:
+    if (mesh) {
+        if (faces) mesh->lpVtbl->UnlockIndexBuffer(mesh);
+        if (vertices) mesh->lpVtbl->UnlockVertexBuffer(mesh);
+        if (hr != D3D_OK) mesh->lpVtbl->Release(mesh);
+    }
+    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[i].faces.items);
+            HeapFree(GetProcessHeap(), 0, glyphs[i].ordered_vertices.items);
+        }
+        HeapFree(GetProcessHeap(), 0, glyphs);
+    }
+    HeapFree(GetProcessHeap(), 0, raw_outline);
+    if (oldfont) SelectObject(hdc, oldfont);
+    if (font) DeleteObject(font);
+
+    return hr;
 }
-- 
1.7.2.3




More information about the wine-patches mailing list