This commit is contained in:
Vincent Wei
2019-03-26 18:09:49 +08:00
parent 9d9bf95c21
commit a95ee5b181
8 changed files with 196 additions and 67 deletions
-2
View File
@@ -41,8 +41,6 @@
#include <stdlib.h>
#include <string.h>
#define DEBUG
#include "common.h"
#include "minigui.h"
#include "gdi.h"
-2
View File
@@ -568,8 +568,6 @@ static void fixup_ellipsis_grun (EllipsizeState *state)
level = MIN (level, state->run_info[i].run->lrun->el);
layout_run->el = level;
layout_run->flags |= LAYOUTRUN_FLAG_ORPHAN;
}
/* Computes the new list of runs for the line
+52 -17
View File
@@ -101,9 +101,6 @@ LOGFONT* __mg_create_logfont_for_layout(const LAYOUTINFO* layout,
return NULL;
}
_DBG_PRINTF("%s: calling LoadResource for LOGFONT: %s\n",
__FUNCTION__, my_fontname);
lf = (LOGFONT*)LoadResource(my_fontname, RES_TYPE_FONT, 0);
if (lf == NULL) {
_ERR_PRINTF("%s: failed to create LOGFONT for layout: %p\n",
@@ -160,7 +157,7 @@ LayoutRun* __mg_layout_run_new_orphan(const LAYOUTINFO* layout,
lrun->el = trun->el;
lrun->dir = trun->dir;
lrun->ort = trun->ort;
lrun->flags |= LAYOUTRUN_FLAG_ORPHAN;
lrun->flags = trun->flags;
return lrun;
}
@@ -196,8 +193,11 @@ LayoutRun* __mg_layout_run_new_from_offset(const LAYOUTINFO* layout,
LOGFONT* lf;
LayoutRun* lrun;
if (offset >= trun->len)
if (offset >= trun->len) {
_DBG_PRINTF("%s: offset(%d) >= trun->len(%p, %d)\n",
__FUNCTION__, offset, trun, trun->len);
return NULL;
}
lf = __mg_create_logfont_for_layout(layout, trun->fontname, trun->ort);
if (lf == NULL)
@@ -221,12 +221,8 @@ LayoutRun* __mg_layout_run_new_from_offset(const LAYOUTINFO* layout,
void __mg_layout_run_free(LayoutRun* lrun)
{
_DBG_PRINTF("%s: called for %p\n",
__FUNCTION__, lrun);
if (lrun->lf) {
FONT_RES* font_res = (FONT_RES*)lrun->lf;
// FIXME
if (font_res->key)
ReleaseRes(font_res->key);
}
@@ -263,18 +259,12 @@ LayoutRun* __mg_layout_run_split(LayoutRun *orig, int split_index)
if (split_index >= orig->len)
return NULL;
_DBG_PRINTF("%s: orig: %d, %d\n",
__FUNCTION__, orig->si, orig->len);
new_run = __mg_layout_run_copy(orig);
new_run->len = split_index;
orig->si += split_index;
orig->len -= split_index;
_DBG_PRINTF("%s: new layout run: %d, %d; orig: %d, %d\n",
__FUNCTION__, new_run->si, new_run->len, orig->si, orig->len);
return new_run;
}
@@ -686,11 +676,56 @@ void __mg_glyph_run_get_logical_widths (const GlyphRun *glyph_run,
}
}
void __mg_glyph_run_letter_space (const GlyphRun* glyph_run,
void __mg_glyph_run_letter_space(const GlyphRun* glyph_run,
const Uchar32* ucs, const BreakOppo* bos, int letter_spacing)
{
}
GlyphRunIter iter;
ShapedGlyph *glyphs = glyph_run->gstr->glyphs;
BOOL have_cluster;
int space_left, space_right;
space_left = letter_spacing / 2;
/* hinting */
if ((letter_spacing & 1) == 0) {
space_left += 1;
}
space_right = letter_spacing - space_left;
for (have_cluster = __mg_glyph_run_iter_init_start(&iter, glyph_run, ucs);
have_cluster;
have_cluster = __mg_glyph_run_iter_next_cluster(&iter)) {
if (!(bos[iter.start_char] & BOV_GB_CURSOR_POS))
continue;
if (iter.start_glyph < iter.end_glyph) {
/* LTR */
if (iter.start_char > 0) {
glyphs[iter.start_glyph].width += space_left ;
glyphs[iter.start_glyph].x_off += space_left ;
}
if (iter.end_char < glyph_run->lrun->len) {
glyphs[iter.end_glyph-1].width += space_right;
}
}
else {
/* RTL */
if (iter.start_char > 0) {
glyphs[iter.start_glyph].width += space_right;
}
if (iter.end_char < glyph_run->lrun->len) {
glyphs[iter.end_glyph+1].x_off += space_left ;
glyphs[iter.end_glyph+1].width += space_left ;
}
}
}
}
#endif /* _MGCHARSET_UNICODE */
+132 -39
View File
@@ -36,6 +36,29 @@
** layoutlayout.c: The implementation of APIs related LAYOUTINFO
**
** Create by WEI Yongming at 2019/03/20
**
** This implementation is derived from LGPL'd Pango. However, we optimize
** and simplify the original implementation in the following respects:
**
** - We split the layout process into two stages. We get the text runs
** (Pango items) in the first stage, and the text runs will keep as
** constants for subsequent different layouts. In the seconde stage,
** we create a layout context for a set of specific layout parameters,
** and generates the lines one by one for the caller. This is useful
** for an app like browser, it can reuse the text runs if the output
** width or height changed, and it is no need to re-generate the text
** runs because of the size change of the output rectangle.
**
** - We use MiniGUI's fontname for the font attributes of text, and leave
** the font selection and the glyph generating to MiniGUI's LOGFONT.
** In this way, we simplify the layout process greatly.
**
** - We always use Uchar32 string for the whole layout process. So the
** code and the structures are clearer than original implementation.
**
** - We provide two shaping engines for rendering the text. One is a
** basic shaping engine and other is the complex shaping engine based
** on HarzBuff. The former can be used for standard scripts.
*/
#include <stdio.h>
@@ -54,6 +77,7 @@
#include "devfont.h"
#include "unicode-ops.h"
#include "layoutinfo.h"
#include "glyph.h"
LAYOUTINFO* GUIAPI CreateLayoutInfo(
const TEXTRUNSINFO* truninfo, Uint32 render_flags,
@@ -313,6 +337,39 @@ static void shape_tab(LAYOUTLINE *line, GlyphString *glyphs)
}
}
static void shape_space(const LAYOUTINFO* layout, const LayoutRun* lrun,
GlyphString* glyphs)
{
unsigned int i;
__mg_glyph_string_set_size (glyphs, lrun->len);
for (i = 0; i < lrun->len; i++) {
UCharGeneralCategory gc;
gc = UCharGetCategory(lrun->ucs[i]);
glyphs->glyphs[i].gv = INV_GLYPH_VALUE;
glyphs->glyphs[i].x_off = 0;
glyphs->glyphs[i].y_off = 0;
if (gc == UCHAR_CATEGORY_SPACE_SEPARATOR) {
Glyph32 space_gv = GetGlyphValue(lrun->lf, UCHAR_SPACE);
glyphs->glyphs[i].width
= _font_get_glyph_log_width(lrun->lf, space_gv);
if (IsUCharWide(lrun->ucs[i])) {
glyphs->glyphs[i].width *= 2;
}
}
else {
glyphs->glyphs[i].width = 0;
}
glyphs->glyphs[i].is_cluster_start = 1;
glyphs->log_clusters[i] = i;
}
}
static void shape_shape(const Uchar32* ucs, int nr_ucs,
RECT* ink_rc, RECT* log_rc, GlyphString *glyphs)
{
@@ -333,7 +390,7 @@ static void shape_shape(const Uchar32* ucs, int nr_ucs,
static void shape_full(const Uchar32* lrun_ucs, int nr_lrun_ucs,
const Uchar32* para_ucs, int nr_para_ucs,
const TEXTRUNSINFO* info, LayoutRun* lrun,
const TEXTRUNSINFO* info, const LayoutRun* lrun,
GlyphString* glyphs)
{
// TODO
@@ -358,8 +415,13 @@ static GlyphString* shape_run(LAYOUTLINE *line, LayoutState *state,
LAYOUTINFO *layout = line->layout;
GlyphString *glyphs = __mg_glyph_string_new ();
if (layout->truninfo->ucs[lrun->si] == UCHAR_TAB)
if (layout->truninfo->ucs[lrun->si] == UCHAR_TAB) {
shape_tab(line, glyphs);
}
else if (lrun->lf == NULL) {
// no need shaping
shape_space(layout, lrun, glyphs);
}
else {
if (state->shape_set)
shape_shape(layout->truninfo->ucs + lrun->si, lrun->len,
@@ -405,12 +467,31 @@ static void free_glyph_run (GlyphRun *grun)
}
#ifdef DEBUG
static inline void print_text_runs(const TEXTRUNSINFO* info, const char* func)
{
int j = 0;
struct list_head* i;
_DBG_PRINTF("Text runs in content when calling %s (nr_runs: %d):\n",
func, info->nr_runs);
list_for_each(i, &info->truns) {
TextRun* run = (TextRun*)i;
_DBG_PRINTF("RUN NO.: %d\n", j);
_DBG_PRINTF(" ADDRESS: %p\n", run);
_DBG_PRINTF(" INDEX: %d\n", run->si);
_DBG_PRINTF(" LENGHT: %d\n", run->len);
_DBG_PRINTF(" EMBEDDING LEVEL:%d\n", run->el);
j++;
}
}
static inline void print_line_runs(const LAYOUTLINE* line, const char* func)
{
int j = 0;
struct list_head* i;
_DBG_PRINTF("Runs in line after calling %s (line length: %d):\n",
_DBG_PRINTF("Runs in line when calling %s (line length: %d):\n",
func, line->len);
list_for_each(i, &line->gruns) {
@@ -419,7 +500,7 @@ static inline void print_line_runs(const LAYOUTLINE* line, const char* func)
_DBG_PRINTF(" ADDRESS: %p\n", run);
_DBG_PRINTF(" INDEX: %d\n", run->lrun->si);
_DBG_PRINTF(" LENGHT: %d\n", run->lrun->len);
_DBG_PRINTF(" EMBEDDING LEVEL:%d\n", run->lrun->len);
_DBG_PRINTF(" EMBEDDING LEVEL:%d\n", run->lrun->el);
_DBG_PRINTF(" NR GLYPHS: %d\n", run->gstr->nr_glyphs);
j++;
}
@@ -431,7 +512,7 @@ static inline void print_run(const GlyphRun* run, const char* func)
_DBG_PRINTF(" ADDRESS: %p\n", run);
_DBG_PRINTF(" INDEX: %d\n", run->lrun->si);
_DBG_PRINTF(" LENGHT: %d\n", run->lrun->len);
_DBG_PRINTF(" EMBEDDING LEVEL:%d\n", run->lrun->len);
_DBG_PRINTF(" EMBEDDING LEVEL:%d\n", run->lrun->el);
_DBG_PRINTF(" NR GLYPHS: %d\n", run->gstr->nr_glyphs);
}
@@ -450,6 +531,11 @@ static inline void list_print(struct list_head* head, const char* desc)
#else
static inline void print_text_runs(const TEXTRUNSINFO* info, const char* func)
{
// do nothing.
}
static inline void print_line_runs(const LAYOUTLINE* line, const char* func)
{
// do nothing.
@@ -471,11 +557,10 @@ static void uninsert_run(LAYOUTLINE *line)
{
GlyphRun *grun;
grun = (GlyphRun*)line->gruns.next;
grun = (GlyphRun*)line->gruns.prev;
list_del(line->gruns.next);
list_del(line->gruns.prev);
line->len -= grun->lrun->len;
free_glyph_run(grun);
print_line_runs(line, __FUNCTION__);
@@ -628,6 +713,7 @@ BreakResult process_layout_run(LAYOUTINFO *layout,
glyph_run.lrun = lrun;
glyph_run.gstr = state->glyphs;
assert(state->log_widths == NULL);
state->log_widths = malloc (sizeof (int) * lrun->len);
__mg_glyph_run_get_logical_widths(&glyph_run, layout->truninfo->ucs,
state->log_widths);
@@ -660,8 +746,7 @@ retry_break:
* the cluster here. But should be fine in practice. */
if (break_num_chars > 0 && break_num_chars < lrun->len &&
layout->bos[state->start_offset + break_num_chars - 1] &
BOV_WHITESPACE)
{
BOV_WHITESPACE) {
break_width -= state->log_widths[state->log_widths_offset +
break_num_chars - 1];
}
@@ -697,8 +782,8 @@ retry_break:
GlyphRun *grun;
new_lrun = __mg_layout_run_split(lrun, break_num_chars);
state->lrun = new_lrun;
/* we must free the original layout run */
__mg_layout_run_free(lrun);
/* Add the width back, to the line, reshape,
@@ -1343,12 +1428,10 @@ static void justify_words (LAYOUTLINE *line,
}
}
if (mode == MEASURE)
{
if (mode == MEASURE) {
total_space_width = spaces_so_far;
if (total_space_width == 0)
{
if (total_space_width == 0) {
justify_clusters (line, state);
return;
}
@@ -1433,7 +1516,6 @@ static LAYOUTLINE* check_next_line(LAYOUTINFO* layout, LayoutState* state)
state->remaining_width = state->line_width;
while (state->trun) {
LayoutRun *lrun;
BreakResult result;
int old_num_chars;
int old_remaining_width;
@@ -1446,17 +1528,12 @@ static LAYOUTLINE* check_next_line(LAYOUTINFO* layout, LayoutState* state)
else {
state->lrun = __mg_layout_run_new_from(layout, state->trun);
}
lrun = state->lrun;
old_num_chars = lrun->len;
old_num_chars = state->lrun->len;
old_remaining_width = state->remaining_width;
first_lrun_in_line = list_empty(&line->gruns);
result = process_layout_run(layout, line, state, !have_break, FALSE);
lrun = state->lrun;
_DBG_PRINTF("%s: result of process_layout_run: %d\n",
__FUNCTION__, result);
switch (result) {
case BREAK_ALL_FIT:
@@ -1469,25 +1546,26 @@ static LAYOUTLINE* check_next_line(LAYOUTINFO* layout, LayoutState* state)
}
state->trun = (const TextRun*)state->trun->list.next;
state->start_index_in_trun = 0;
state->start_offset += old_num_chars;
if (&state->trun->list == &layout->truninfo->truns)
state->trun = NULL;
state->start_offset += old_num_chars;
break;
case BREAK_EMPTY_FIT:
__mg_layout_run_free(state->lrun);
state->lrun = NULL;
wrapped = TRUE;
goto done;
case BREAK_SOME_FIT:
_DBG_PRINTF("%s: handle BREAK_SOME_FIT: %d\n",
__FUNCTION__, lrun->len);
state->start_offset += old_num_chars - lrun->len;
state->start_offset += old_num_chars - state->lrun->len;
wrapped = TRUE;
goto done;
case BREAK_NONE_FIT:
_DBG_PRINTF("%s: handle BREAK_NONE_FIT: %d\n",
__FUNCTION__, lrun->len);
__mg_layout_run_free(state->lrun);
/* Back up over unused runs to run where there is a break */
while (!list_empty(&line->gruns) &&
line->gruns.next != break_link) {
@@ -1509,8 +1587,7 @@ static LAYOUTLINE* check_next_line(LAYOUTINFO* layout, LayoutState* state)
}
/* Reshape run to break */
lrun = state->lrun;
old_num_chars = lrun->len;
old_num_chars = state->lrun->len;
result = process_layout_run(layout, line, state, TRUE, TRUE);
assert(result == BREAK_SOME_FIT || result == BREAK_EMPTY_FIT);
@@ -1521,9 +1598,10 @@ static LAYOUTLINE* check_next_line(LAYOUTINFO* layout, LayoutState* state)
case BREAK_LINE_SEPARATOR:
state->trun = (const TextRun*)state->trun->list.next;
state->start_index_in_trun = 0;
state->start_offset += old_num_chars;
if (&state->trun->list == &layout->truninfo->truns)
state->trun = NULL;
state->start_offset += old_num_chars;
/* A line-separate is just a forced break. Set wrapped, so we do
* justification */
wrapped = TRUE;
@@ -1532,8 +1610,6 @@ static LAYOUTLINE* check_next_line(LAYOUTINFO* layout, LayoutState* state)
}
done:
_DBG_PRINTF("%s: calling layout_line_postprocess: %d\n",
__FUNCTION__, line->len);
layout_line_postprocess(line, state, wrapped);
state->line_of_par++;
state->line_start_index += line->len;
@@ -1611,29 +1687,39 @@ LAYOUTLINE* GUIAPI LayoutNextLine(
}
state.line_start_index = 0;
state.start_offset = 0;
state.start_index_in_trun = 0;
state.trun = (TextRun*)layout->truninfo->truns.next;
}
else {
state.line_start_index = layout->truninfo->nr_ucs - layout->nr_left_ucs;
state.start_offset = state.line_start_index;
state.start_index_in_trun = 0;
state.trun = __mg_text_run_get_by_offset(layout->truninfo,
state.start_offset, &state.start_index_in_trun);
state.line_start_index, &state.start_index_in_trun);
_DBG_PRINTF("%s: line_start_index: %d, start_index_in_trun: %d(%p)\n",
__FUNCTION__, state.line_start_index, state.start_index_in_trun,
state.trun);
if (state.trun == NULL) {
next_line = NULL;
goto out;
}
}
if (prev_line) {
release_line(prev_line);
prev_line = NULL;
}
state.line_width = max_extent;
state.remaining_width = max_extent;
next_line = check_next_line(layout, &state);
if (state.glyphs) {
__mg_glyph_string_free(state.glyphs);
}
if (state.log_widths) {
free(state.log_widths);
}
if (next_line) {
if (layout->persist) {
list_add_tail(&next_line->list, &layout->lines);
@@ -1643,6 +1729,13 @@ LAYOUTLINE* GUIAPI LayoutNextLine(
layout->nr_left_ucs -= next_line->len;
}
// Release previous line after got next line.
// This will avoid releasing the LOGFONT objects earlier.
if (prev_line) {
release_line(prev_line);
prev_line = NULL;
}
out:
if (prev_line) {
release_line(prev_line);
+2 -1
View File
@@ -64,7 +64,8 @@ struct _GlyphString {
unsigned int space;
};
#define LAYOUTRUN_FLAG_ORPHAN 0x01
#define LAYOUTRUN_FLAG_CENTERED_BASELINE TEXTRUN_FLAG_CENTERED_BASELINE
#define LAYOUTRUN_FLAG_NO_SHAPING TEXTRUN_FLAG_NO_SHAPING
struct _LayoutRun {
LOGFONT* lf; // the logfont for this run
+5 -1
View File
@@ -1079,8 +1079,12 @@ int GUIAPI GetGlyphsExtentFromUChars(LOGFONT* logfont_upright,
}
}
else if (is_whitespace_glyph(&args, gis, n)) {
Glyph32 space_gv = GetGlyphValue(logfont_upright, UCHAR_SPACE);
gis[n].whitespace = 1;
ges[n].line_adv = logfont_upright->size / 6;
ges[n].line_adv = _font_get_glyph_log_width(logfont_upright,
space_gv);
switch (render_flags & GRF_WRITING_MODE_MASK) {
case GRF_WRITING_MODE_VERTICAL_RL:
case GRF_WRITING_MODE_VERTICAL_LR:
+4 -4
View File
@@ -168,7 +168,7 @@ static void state_add_character(TextRunState *state,
BOOL no_shaping, BOOL force_break, const Uchar32* pos)
{
if (state->run) {
BOOL without_shaping = (state->run->flags & TEXTRUN_FLAG_NOT_SHAPING);
BOOL without_shaping = (state->run->flags & TEXTRUN_FLAG_NO_SHAPING);
if (!force_break &&
state->run->lc == state->derived_lang &&
without_shaping == no_shaping) {
@@ -189,7 +189,7 @@ static void state_add_character(TextRunState *state,
state->run->flags = 0;
if (no_shaping) {
state->run->flags |= TEXTRUN_FLAG_NOT_SHAPING;
state->run->flags |= TEXTRUN_FLAG_NO_SHAPING;
}
/* The level vs. gravity dance:
@@ -567,10 +567,10 @@ const TextRun* __mg_text_run_get_by_offset(const TEXTRUNSINFO* runinfo,
int index, int *start_offset)
{
struct list_head *i;
TextRun* found = NULL;
const TextRun* found = NULL;
list_for_each(i, &runinfo->truns) {
TextRun* trun = (TextRun*)i;
const TextRun* trun = (const TextRun*)i;
if (index >= trun->si &&
(index < trun->si + trun->len)) {
found = trun;
+1 -1
View File
@@ -69,7 +69,7 @@ struct _ShapingEngineInfo {
};
#define TEXTRUN_FLAG_CENTERED_BASELINE 0x01
#define TEXTRUN_FLAG_NOT_SHAPING 0x02
#define TEXTRUN_FLAG_NO_SHAPING 0x02
struct _TextRun {
struct list_head list;