[PATCH v3 3/4] d3dx9: Handle tabs in ID3DXFont_DrawText.
Sven Baars
sbaars at codeweavers.com
Fri Mar 27 11:31:31 CDT 2020
Signed-off-by: Sven Baars <sbaars at codeweavers.com>
---
dlls/d3dx9_36/font.c | 213 ++++++++++++++++++++++++++-----------
dlls/d3dx9_36/tests/core.c | 8 +-
2 files changed, 155 insertions(+), 66 deletions(-)
diff --git a/dlls/d3dx9_36/font.c b/dlls/d3dx9_36/font.c
index 54aef71913..4835732931 100644
--- a/dlls/d3dx9_36/font.c
+++ b/dlls/d3dx9_36/font.c
@@ -512,7 +512,8 @@ static INT WINAPI ID3DXFontImpl_DrawTextA(ID3DXFont *iface, ID3DXSprite *sprite,
}
static void word_break(HDC hdc, const WCHAR *str, unsigned int *str_len,
- unsigned int chars_fit, unsigned int *chars_used, DWORD format, SIZE *size)
+ unsigned int chars_fit, unsigned int *chars_used, BOOL line_start,
+ DWORD format, SIZE *size)
{
SCRIPT_LOGATTR *sla;
SCRIPT_ANALYSIS sa;
@@ -535,7 +536,7 @@ static void word_break(HDC hdc, const WCHAR *str, unsigned int *str_len,
--i;
/* If the there is no word that fits put in all characters that do fit */
- if (!sla[i].fSoftBreak || (format & DT_SINGLELINE))
+ if ((!sla[i].fSoftBreak && line_start) || (format & DT_SINGLELINE))
i = chars_fit;
*chars_used = i;
@@ -553,53 +554,104 @@ static void word_break(HDC hdc, const WCHAR *str, unsigned int *str_len,
}
static const WCHAR *read_line(HDC hdc, const WCHAR *str, int *count,
- WCHAR *dest, unsigned int *dest_len, int width, DWORD format, SIZE *size)
+ WCHAR *dest, unsigned int *dest_len, int width, unsigned int tab_width,
+ DWORD format, SIZE *size)
{
+ int num_fit, current_width = 0;
unsigned int i = 0;
- int orig_count = *count;
- int num_fit;
+ size->cy = 0;
*dest_len = 0;
- while (*count && (str[i] != '\n' || (format & DT_SINGLELINE)))
+ while (*count)
{
- --(*count);
- if (str[i] != '\r' && str[i] != '\n')
- dest[(*dest_len)++] = str[i];
- ++i;
- }
+ unsigned int seg_i, seg_len, prev_dest_len;
+ int seg_count, max_seg_width;
+ BOOL word_broken = FALSE;
+ SIZE seg_size = {0};
- num_fit = 0;
- GetTextExtentExPointW(hdc, dest, *dest_len, width, &num_fit, NULL, size);
+ /* Create line segments until the next tab. If we don't have to expand */
+ /* tabs, just skip the tab character. */
- if (num_fit < *dest_len)
- {
- if (format & DT_WORDBREAK)
+ if (str[i] == '\t')
{
- unsigned int chars_used;
+ if (tab_width)
+ current_width = ((current_width / tab_width) + 1) * tab_width;
+
+ --(*count);
+ if (format & DT_EXPANDTABS)
+ dest[(*dest_len)++] = str[i];
+ ++i;
- word_break(hdc, dest, dest_len, num_fit, &chars_used, format, size);
- *count = orig_count - chars_used;
- i = chars_used;
+ max_seg_width = width - current_width;
+ if (max_seg_width < 0 && (format & DT_WORDBREAK))
+ break;
+
+ continue;
}
- else if (format & DT_SINGLELINE)
+
+ seg_i = i;
+ seg_count = *count;
+ prev_dest_len = *dest_len;
+
+ while (*count && (str[i] != '\n' || (format & DT_SINGLELINE)) && str[i] != '\t')
{
- *dest_len = num_fit;
- *count = 0;
+ --(*count);
+ if (str[i] != '\r' && str[i] != '\n')
+ dest[(*dest_len)++] = str[i];
+ ++i;
}
- }
- if (*count && str[i] == '\n')
- {
- --(*count);
- ++i;
+ num_fit = 0;
+ seg_len = *(dest_len) - prev_dest_len;
+ max_seg_width = width - current_width;
+ GetTextExtentExPointW(hdc, dest + prev_dest_len, seg_len, max_seg_width,
+ &num_fit, NULL, &seg_size);
+
+ if (num_fit < seg_len)
+ {
+ if (format & DT_WORDBREAK)
+ {
+ unsigned int chars_used;
+
+ word_break(hdc, dest + prev_dest_len, &seg_len, num_fit, &chars_used,
+ !current_width, format, &seg_size);
+ *count = seg_count - chars_used;
+ i = seg_i + chars_used;
+ word_broken = TRUE;
+ }
+ else if (format & DT_SINGLELINE)
+ {
+ seg_len = num_fit;
+ *count = 0;
+ word_broken = TRUE;
+ }
+ *dest_len = prev_dest_len + seg_len;
+ }
+
+ current_width += seg_size.cx;
+ if (seg_size.cy > size->cy)
+ size->cy = seg_size.cy;
+
+ if (word_broken)
+ {
+ break;
+ }
+ else if (*count && str[i] == '\n')
+ {
+ --(*count);
+ ++i;
+ break;
+ }
}
+ size->cx = current_width;
if (*count)
return str + i;
return NULL;
}
-static int compute_rect(struct d3dx_font *font, const WCHAR *string, INT count, WCHAR *line, RECT *rect, DWORD format)
+static int compute_rect(struct d3dx_font *font, const WCHAR *string, INT count,
+ WCHAR *line, RECT *rect, DWORD format, unsigned int tab_width)
{
int y, lh, width, top = rect->top;
int max_width = 0;
@@ -613,7 +665,8 @@ static int compute_rect(struct d3dx_font *font, const WCHAR *string, INT count,
{
unsigned int line_len;
- string = read_line(font->hdc, string, &count, line, &line_len, width, format, &size);
+ string = read_line(font->hdc, string, &count, line, &line_len,
+ width, tab_width, format, &size);
if (size.cx > max_width)
max_width = size.cx;
@@ -660,6 +713,7 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
struct d3dx_font *font = impl_from_ID3DXFont(iface);
int lh, x, y, width, top, ret = 0;
ID3DXSprite *target = sprite;
+ unsigned int tab_width = 0;
RECT r = {0};
WCHAR *line;
SIZE size;
@@ -682,6 +736,9 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
if (format & DT_SINGLELINE)
format &= ~DT_WORDBREAK;
+ if (format & DT_EXPANDTABS)
+ tab_width = font->metrics.tmAveCharWidth * 8;
+
line = heap_alloc(count * sizeof(*line));
if (!line)
return 0;
@@ -701,7 +758,7 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
top = rect->top;
- ret = compute_rect(font, string, count, line, rect, format);
+ ret = compute_rect(font, string, count, line, rect, format, tab_width);
if (format & DT_CALCRECT)
goto cleanup;
@@ -721,13 +778,14 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
ID3DXSprite_Begin(target, 0);
}
+ /* Iterate over all lines */
while (string)
{
- unsigned int line_len, i;
- GCP_RESULTSW results;
- D3DXVECTOR3 pos;
+ const WCHAR *line_seg = line;
+ unsigned int line_len;
- string = read_line(font->hdc, string, &count, line, &line_len, width, format, &size);
+ string = read_line(font->hdc, string, &count, line, &line_len,
+ width, tab_width, format, &size);
if (format & DT_CENTER)
x = (rect->left + rect->right - size.cx) / 2;
@@ -736,42 +794,73 @@ static INT WINAPI ID3DXFontImpl_DrawTextW(ID3DXFont *iface, ID3DXSprite *sprite,
else
x = rect->left;
- memset(&results, 0, sizeof(results));
- results.nGlyphs = line_len;
+ /* Iterate over all line segments separated by tabs */
+ while (line_len)
+ {
+ unsigned int i, seg_len = line_len;
+ GCP_RESULTSW results;
+ D3DXVECTOR3 pos;
- results.lpCaretPos = heap_alloc(line_len * sizeof(*results.lpCaretPos));
- if (!results.lpCaretPos)
- goto cleanup;
+ if (format & DT_EXPANDTABS)
+ {
+ /* Iterate until the next tab to create a line segment */
+ i = 0;
+ while (i < line_len && line_seg[i] != '\t')
+ ++i;
+ seg_len = i;
+ if (seg_len != line_len && !GetTextExtentPointW(
+ font->hdc, line_seg, seg_len, &size))
+ goto cleanup;
+ }
- results.lpGlyphs = heap_alloc(line_len * sizeof(*results.lpGlyphs));
- if (!results.lpGlyphs)
- {
- heap_free(results.lpCaretPos);
- goto cleanup;
- }
+ memset(&results, 0, sizeof(results));
+ results.nGlyphs = seg_len;
- GetCharacterPlacementW(font->hdc, line, line_len, 0, &results, 0);
+ results.lpCaretPos = heap_alloc(seg_len * sizeof(*results.lpCaretPos));
+ if (!results.lpCaretPos)
+ goto cleanup;
- for (i = 0; i < results.nGlyphs; ++i)
- {
- IDirect3DTexture9 *texture;
- POINT cell_inc;
- RECT black_box;
+ results.lpGlyphs = heap_alloc(seg_len * sizeof(*results.lpGlyphs));
+ if (!results.lpGlyphs)
+ {
+ heap_free(results.lpCaretPos);
+ goto cleanup;
+ }
+
+ GetCharacterPlacementW(font->hdc, line_seg, seg_len, 0, &results, 0);
- ID3DXFont_GetGlyphData(iface, results.lpGlyphs[i], &texture, &black_box, &cell_inc);
+ for (i = 0; i < results.nGlyphs; ++i)
+ {
+ IDirect3DTexture9 *texture;
+ POINT cell_inc;
+ RECT black_box;
- if (!texture)
- continue;
+ ID3DXFont_GetGlyphData(iface, results.lpGlyphs[i], &texture,
+ &black_box, &cell_inc);
- pos.x = cell_inc.x + x + results.lpCaretPos[i];
- pos.y = cell_inc.y + y;
+ if (!texture)
+ continue;
- ID3DXSprite_Draw(target, texture, &black_box, NULL, &pos, color);
- IDirect3DTexture9_Release(texture);
- }
+ pos.x = cell_inc.x + x + results.lpCaretPos[i];
+ pos.y = cell_inc.y + y;
+
+ ID3DXSprite_Draw(target, texture, &black_box, NULL, &pos, color);
+ IDirect3DTexture9_Release(texture);
+ }
+
+ line_len -= seg_len;
+ line_seg += seg_len;
+ if (line_len)
+ {
+ /* If there is anything left, this character is a tab */
+ --line_len;
+ ++line_seg;
+ x += ((size.cx / tab_width) + 1) * tab_width;
+ }
- heap_free(results.lpCaretPos);
- heap_free(results.lpGlyphs);
+ heap_free(results.lpCaretPos);
+ heap_free(results.lpGlyphs);
+ }
y += lh;
if (!(DT_NOCLIP & format) && (y > rect->bottom))
diff --git a/dlls/d3dx9_36/tests/core.c b/dlls/d3dx9_36/tests/core.c
index 020f18e622..9b942e1535 100644
--- a/dlls/d3dx9_36/tests/core.c
+++ b/dlls/d3dx9_36/tests/core.c
@@ -807,10 +807,10 @@ static void test_ID3DXFont(IDirect3DDevice9 *device)
todo_wine ok(height == 0, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"\t\t\t\t\t\t\t\t\t\ta", -1, &rect, DT_WORDBREAK, 0xff00ff);
- todo_wine ok(height == 12, "Got unexpected height %d.\n", height);
+ ok(height == 12, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"\taaaaaaaaaa", -1, &rect, DT_WORDBREAK, 0xff00ff);
- todo_wine ok(height == 24, "Got unexpected height %d.\n", height);
+ ok(height == 24, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"\taaaaaaaaaa", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
ok(height == 36, "Got unexpected height %d.\n", height);
@@ -819,7 +819,7 @@ static void test_ID3DXFont(IDirect3DDevice9 *device)
ok(height == 24, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"\taaa\taaa\taaa", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
- todo_wine ok(height == 48, "Got unexpected height %d.\n", height);
+ ok(height == 48, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"\t\t\t\t\t\t\t\t\t\t", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
todo_wine ok(height == 60, "Got unexpected height %d.\n", height);
@@ -828,7 +828,7 @@ static void test_ID3DXFont(IDirect3DDevice9 *device)
ok(height == 12, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"a\ta\ta", -1, &rect, DT_EXPANDTABS | DT_WORDBREAK, 0xff00ff);
- todo_wine ok(height == 24, "Got unexpected height %d.\n", height);
+ ok(height == 24, "Got unexpected height %d.\n", height);
height = ID3DXFont_DrawTextW(font, NULL, L"aaaaaaaaaaaaaaaaaaaa", -1, &rect, DT_WORDBREAK, 0xff00ff);
ok(height == 36, "Got unexpected height %d.\n", height);
--
2.24.0
More information about the wine-devel
mailing list