Commit 82d73733 authored by Nikolay Sivov's avatar Nikolay Sivov Committed by Alexandre Julliard

dwrite: Restructure line wrapping logic, fix the way breaking position is selected.

parent 3871981e
......@@ -1230,7 +1230,7 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const
return E_OUTOFMEMORY;
/* No need to iterate for that, use simple fact that:
<last cluster position> = first cluster position> + <sum of cluster lengths not including last one> */
<last cluster position> = <first cluster position> + <sum of cluster lengths not including last one> */
last_cluster = first_cluster + cluster_count - 1;
length = layout->clusters[last_cluster].position - layout->clusters[first_cluster].position +
layout->clustermetrics[last_cluster].length;
......@@ -1303,7 +1303,7 @@ static HRESULT layout_add_effective_run(struct dwrite_textlayout *layout, const
return S_OK;
}
static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_LINE_METRICS1 *metrics, UINT32 *line)
static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_LINE_METRICS1 *metrics)
{
if (!layout->line_alloc) {
layout->line_alloc = 5;
......@@ -1320,9 +1320,7 @@ static HRESULT layout_set_line_metrics(struct dwrite_textlayout *layout, DWRITE_
layout->line_alloc *= 2;
}
layout->lines[*line] = *metrics;
layout->metrics.lineCount += 1;
*line += 1;
layout->lines[layout->metrics.lineCount++] = *metrics;
return S_OK;
}
......@@ -1689,10 +1687,10 @@ static HRESULT layout_add_underline(struct dwrite_textlayout *layout, struct lay
}
/* Adds zero width line, metrics are derived from font at specified text position. */
static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout, UINT32 pos, UINT32 *line)
static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout, UINT32 pos)
{
DWRITE_LINE_METRICS1 metrics = { 0 };
DWRITE_FONT_METRICS fontmetrics;
DWRITE_LINE_METRICS1 metrics;
struct layout_range *range;
IDWriteFontFace *fontface;
IDWriteFont *font;
......@@ -1716,181 +1714,202 @@ static HRESULT layout_set_dummy_line_metrics(struct dwrite_textlayout *layout, U
layout_get_font_height(range->fontsize, &fontmetrics, &metrics.baseline, &metrics.height);
IDWriteFontFace_Release(fontface);
metrics.length = 0;
metrics.trailingWhitespaceLength = 0;
metrics.newlineLength = 0;
metrics.isTrimmed = FALSE;
metrics.leadingBefore = 0.0f;
metrics.leadingAfter = 0.0f;
return layout_set_line_metrics(layout, &metrics, line);
return layout_set_line_metrics(layout, &metrics);
}
static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout)
static void layout_add_line(struct dwrite_textlayout *layout, UINT32 first_cluster, UINT32 last_cluster,
UINT32 *textpos)
{
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
struct layout_final_splitting_params prev_params, params;
struct layout_effective_run *erun, *first_underlined;
struct layout_effective_inline *inrun;
struct layout_final_splitting_params params, prev_params;
UINT32 strlength, index, start, pos = *textpos;
UINT32 line = layout->metrics.lineCount, i;
DWRITE_LINE_METRICS1 metrics = { 0 };
FLOAT descent, trailingspacewidth;
const struct layout_run *run;
DWRITE_LINE_METRICS1 metrics;
FLOAT width, origin_x, origin_y;
UINT32 i, start, line, textpos;
FLOAT width, origin_x;
HRESULT hr;
if (!(layout->recompute & RECOMPUTE_LINES))
return S_OK;
/* Take a look at clusters we got for this line in reverse order to set trailing properties for current line */
for (index = last_cluster, trailingspacewidth = 0.0f; index >= first_cluster; index--) {
DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
struct layout_cluster *lc = &layout->clusters[index];
WCHAR ch;
free_layout_eruns(layout);
/* This also filters out clusters added from inline objects, those are never
treated as a white space. */
if (!cluster->isWhitespace)
break;
hr = layout_compute(layout);
if (FAILED(hr))
return hr;
/* Every isNewline cluster is also isWhitespace, but not every
newline character cluster has isNewline set, so go back to original string. */
ch = lc->run->u.regular.descr.string[lc->position];
if (cluster->length == 1 && lb_is_newline_char(ch))
metrics.newlineLength += cluster->length;
layout->metrics.lineCount = 0;
origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f;
line = 0;
memset(&metrics, 0, sizeof(metrics));
metrics.trailingWhitespaceLength += cluster->length;
trailingspacewidth += cluster->width;
}
layout_splitting_params_from_pos(layout, 0, &params);
prev_params = params;
/* Line metrics length includes trailing whitespace length too */
for (i = first_cluster; i <= last_cluster; i++)
metrics.length += layout->clustermetrics[i].length;
/* Ignore trailing whitespaces */
while (last_cluster > first_cluster) {
if (!layout->clustermetrics[last_cluster].isWhitespace)
break;
if (layout->cluster_count)
run = layout->clusters[0].run;
for (i = 0, start = 0, textpos = 0, width = 0.0f; i < layout->cluster_count; i++) {
BOOL overflow;
last_cluster--;
}
/* Does not include trailing space width */
width = get_cluster_range_width(layout, first_cluster, last_cluster + 1);
layout_splitting_params_from_pos(layout, textpos, &params);
layout_splitting_params_from_pos(layout, pos, &params);
prev_params = params;
run = layout->clusters[first_cluster].run;
/* Form runs from a range of clusters; this is what will be reported with DrawGlyphRun() */
origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f;
for (start = first_cluster, i = first_cluster; i <= last_cluster; i++) {
layout_splitting_params_from_pos(layout, pos, &params);
/* switched to next nominal run, at this point all previous pending clusters are already
checked for layout line overflow, so new effective run will fit in current line */
if (run != layout->clusters[i].run || !is_same_splitting_params(&prev_params, &params)) {
hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
if (FAILED(hr))
return hr;
return;
origin_x += is_rtl ? -get_cluster_range_width(layout, start, i) :
get_cluster_range_width(layout, start, i);
get_cluster_range_width(layout, start, i);
run = layout->clusters[i].run;
start = i;
}
overflow = layout->clustermetrics[i].canWrapLineAfter &&
(width + layout->clustermetrics[i].width > layout->metrics.layoutWidth) &&
(layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP);
/* check if we got new */
if (overflow ||
layout->clustermetrics[i].isNewline || /* always wrap on new line */
i == layout->cluster_count - 1) /* end of the text */ {
UINT32 strlength, last_cluster, index;
FLOAT descent, trailingspacewidth;
struct layout_final_splitting_params *p;
if (!overflow) {
width += layout->clustermetrics[i].width;
metrics.length += layout->clustermetrics[i].length;
last_cluster = i;
p = &params;
}
else {
last_cluster = i ? i - 1 : i;
p = &prev_params;
}
prev_params = params;
pos += layout->clustermetrics[i].length;
}
if (i >= start) {
hr = layout_add_effective_run(layout, run, start, last_cluster - start + 1, line, origin_x, p);
if (FAILED(hr))
return hr;
/* we don't need to update origin for next run as we're going to wrap */
}
/* Final run from what's left from cluster range */
hr = layout_add_effective_run(layout, run, start, i - start, line, origin_x, &prev_params);
if (FAILED(hr))
return;
/* take a look at clusters we got for this line in reverse order to set
trailing properties for current line */
strlength = metrics.length;
index = last_cluster;
trailingspacewidth = 0.0f;
while (strlength) {
DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
struct layout_cluster *lc = &layout->clusters[index];
WCHAR ch;
/* This also filters out clusters added from inline objects, those are never
treated as a white space. */
if (!cluster->isWhitespace)
break;
/* Every isNewline cluster is also isWhitespace, but not every
newline character cluster has isNewline set, so go back to original string. */
ch = lc->run->u.regular.descr.string[lc->position];
if (cluster->length == 1 && lb_is_newline_char(ch))
metrics.newlineLength += cluster->length;
metrics.trailingWhitespaceLength += cluster->length;
trailingspacewidth += cluster->width;
strlength -= cluster->length;
index--;
}
/* Look for max baseline and descent for this line */
strlength = metrics.length - metrics.trailingWhitespaceLength;
index = last_cluster;
metrics.baseline = 0.0f;
descent = 0.0f;
while (strlength) {
DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
const struct layout_run *cur = layout->clusters[index].run;
FLOAT cur_descent = cur->height - cur->baseline;
/* look for max baseline and descent for this line */
strlength = metrics.length;
index = last_cluster;
metrics.baseline = 0.0f;
descent = 0.0f;
while (strlength) {
DWRITE_CLUSTER_METRICS *cluster = &layout->clustermetrics[index];
const struct layout_run *cur = layout->clusters[index].run;
FLOAT cur_descent = cur->height - cur->baseline;
if (cur->baseline > metrics.baseline)
metrics.baseline = cur->baseline;
if (cur->baseline > metrics.baseline)
metrics.baseline = cur->baseline;
if (cur_descent > descent)
descent = cur_descent;
if (cur_descent > descent)
descent = cur_descent;
strlength -= cluster->length;
index--;
}
strlength -= cluster->length;
index--;
}
metrics.height = descent + metrics.baseline;
layout->metrics.width = max(width, layout->metrics.width);
layout->metrics.widthIncludingTrailingWhitespace = max(width + trailingspacewidth,
layout->metrics.widthIncludingTrailingWhitespace);
if (width > layout->metrics.widthIncludingTrailingWhitespace)
layout->metrics.widthIncludingTrailingWhitespace = width;
if (width - trailingspacewidth > layout->metrics.width)
layout->metrics.width = width - trailingspacewidth;
metrics.height = descent + metrics.baseline;
metrics.isTrimmed = width > layout->metrics.layoutWidth;
layout_set_line_metrics(layout, &metrics);
metrics.isTrimmed = width > layout->metrics.layoutWidth;
hr = layout_set_line_metrics(layout, &metrics, &line);
if (FAILED(hr))
return hr;
*textpos += metrics.length;
}
width = layout->clustermetrics[i].width;
memset(&metrics, 0, sizeof(metrics));
origin_x = is_rtl ? layout->metrics.layoutWidth : 0.0f;
start = i;
}
else {
metrics.length += layout->clustermetrics[i].length;
static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout)
{
BOOL is_rtl = layout->format.readingdir == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
struct layout_effective_run *erun, *first_underlined;
UINT32 i, start, line, textpos, last_breaking_point;
struct layout_effective_inline *inrun;
FLOAT width, origin_y;
DWRITE_LINE_METRICS1 metrics;
HRESULT hr;
if (!(layout->recompute & RECOMPUTE_LINES))
return S_OK;
free_layout_eruns(layout);
hr = layout_compute(layout);
if (FAILED(hr))
return hr;
layout->metrics.lineCount = 0;
line = 0;
memset(&metrics, 0, sizeof(metrics));
layout->metrics.height = 0.0f;
layout->metrics.width = 0.0f;
layout->metrics.widthIncludingTrailingWhitespace = 0.0f;
last_breaking_point = ~0u;
for (i = 0, start = 0, width = 0.0f, textpos = 0; i < layout->cluster_count; i++) {
BOOL overflow = FALSE;
while (i < layout->cluster_count && !layout->clustermetrics[i].isNewline) {
/* Check for overflow */
overflow = ((width + layout->clustermetrics[i].width > layout->metrics.layoutWidth) &&
(layout->format.wrapping != DWRITE_WORD_WRAPPING_NO_WRAP));
if (overflow)
break;
if (layout->clustermetrics[i].canWrapLineAfter)
last_breaking_point = i;
width += layout->clustermetrics[i].width;
i++;
}
i = min(i, layout->cluster_count - 1);
if (overflow) {
/* Overflown on whitespace, ignore it */
if (layout->clustermetrics[i].isWhitespace && layout->clustermetrics[i].canWrapLineAfter)
i = i;
/* Use most recently found breaking point */
else if (last_breaking_point != ~0u) {
i = last_breaking_point;
last_breaking_point = ~0u;
}
else {
/* Otherwise proceed forward to next newline or breaking point */
for (; i < layout->cluster_count; i++)
if (layout->clustermetrics[i].canWrapLineAfter || layout->clustermetrics[i].isNewline)
break;
}
}
i = min(i, layout->cluster_count - 1);
prev_params = params;
textpos += layout->clustermetrics[i].length;
layout_add_line(layout, start, i, &textpos);
start = i + 1;
width = 0.0f;
}
/* Add dummy line if:
- there's no text, metrics come from first range in this case;
- last ended with a mandatory break, metrics come from last text position.
*/
line = layout->metrics.lineCount - 1;
if (layout->len == 0)
hr = layout_set_dummy_line_metrics(layout, 0, &line);
else if (layout->clustermetrics[layout->cluster_count-1].isNewline)
hr = layout_set_dummy_line_metrics(layout, layout->len-1, &line);
hr = layout_set_dummy_line_metrics(layout, 0);
else if (layout->clustermetrics[layout->cluster_count - 1].isNewline)
hr = layout_set_dummy_line_metrics(layout, layout->len - 1);
if (FAILED(hr))
return hr;
layout->metrics.left = is_rtl ? layout->metrics.layoutWidth - layout->metrics.width : 0.0f;
layout->metrics.top = 0.0f;
layout->metrics.maxBidiReorderingDepth = 1; /* FIXME */
layout->metrics.height = 0.0f;
/* Now all line info is here, update effective runs positions in flow direction */
erun = layout_get_next_erun(layout, NULL);
......@@ -1921,15 +1940,16 @@ static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout)
inrun->origin_y = origin_y - inrun->run->baseline;
inrun = layout_get_next_inline_run(layout, inrun);
}
layout->metrics.height += layout->lines[line].height;
}
/* Use last line origin y + line descent as total content height */
line--;
layout->metrics.height = origin_y + layout->lines[line].height - layout->lines[line].baseline;
/* initial alignment is always leading */
/* Initial alignment is always leading */
if (layout->format.textalignment != DWRITE_TEXT_ALIGNMENT_LEADING)
layout_apply_text_alignment(layout);
/* initial paragraph alignment is always near */
/* Initial paragraph alignment is always near */
if (layout->format.paralign != DWRITE_PARAGRAPH_ALIGNMENT_NEAR)
layout_apply_par_alignment(layout);
......@@ -1939,7 +1959,8 @@ static HRESULT layout_compute_effective_runs(struct dwrite_textlayout *layout)
return hr;
}
static BOOL is_same_layout_attrvalue(struct layout_range_header const *h, enum layout_range_attr_kind attr, struct layout_range_attr_value *value)
static BOOL is_same_layout_attrvalue(struct layout_range_header const *h, enum layout_range_attr_kind attr,
struct layout_range_attr_value *value)
{
struct layout_range_spacing const *range_spacing = (struct layout_range_spacing*)h;
struct layout_range_iface const *range_iface = (struct layout_range_iface*)h;
......
......@@ -2161,7 +2161,6 @@ todo_wine
hr = IDWriteTextLayout_GetLineMetrics(layout, &line, 1, &count);
ok(hr == S_OK, "got 0x%08x\n", hr);
ok(count == 1, "got %u\n", count);
todo_wine
ok(line.length == 4, "got %u\n", line.length);
ok(line.isTrimmed, "got %d\n", line.isTrimmed);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment