improved the font system even futher. utf8 is now used everywhere. it uses less memory as well

This commit is contained in:
Magnus Auvinen 2009-06-13 08:22:37 +00:00
parent 9254ca61f1
commit 69511102de
11 changed files with 333 additions and 174 deletions

View file

@ -1188,6 +1188,139 @@ void gui_messagebox(const char *title, const char *message)
int str_isspace(char c) { return c == ' ' || c == '\n' || c == '\t'; }
static int str_utf8_isstart(char c)
{
if((c&0xC0) == 0x80) /* 10xxxxxx */
return 0;
return 1;
}
int str_utf8_rewind(const char *str, int cursor)
{
while(cursor)
{
cursor--;
if(str_utf8_isstart(*(str + cursor)))
break;
}
return cursor;
}
int str_utf8_forward(const char *str, int cursor)
{
const char *buf = str + cursor;
if(!buf[0])
return cursor;
if((*buf&0x80) == 0x0) /* 0xxxxxxx */
return cursor+1;
else if((*buf&0xE0) == 0xC0) /* 110xxxxx */
{
if(!buf[1]) return cursor+1;
return cursor+2;
}
else if((*buf & 0xF0) == 0xE0) /* 1110xxxx */
{
if(!buf[1]) return cursor+1;
if(!buf[2]) return cursor+2;
return cursor+2;
}
else if((*buf & 0xF8) == 0xF0) /* 11110xxx */
{
if(!buf[1]) return cursor+1;
if(!buf[2]) return cursor+2;
if(!buf[3]) return cursor+3;
return cursor+3;
}
/* invalid */
return cursor+1;
}
int str_utf8_encode(char *ptr, int chr)
{
/* encode */
if(chr <= 0x7F)
{
ptr[0] = (char)chr;
return 1;
}
else if(chr <= 0x7FF)
{
ptr[0] = 0xC0|((chr>>6)&0x1F);
ptr[1] = 0x80|(chr&0x3F);
return 2;
}
else if(chr <= 0xFFFF)
{
ptr[0] = 0xE0|((chr>>12)&0x0F);
ptr[1] = 0xC0|((chr>>6)&0x3F);
ptr[2] = 0xC0|(chr&0x3F);
return 3;
}
else if(chr <= 0x10FFFF)
{
ptr[0] = 0xF0|((chr>>18)&0x07);
ptr[1] = 0xC0|((chr>>12)&0x3F);
ptr[2] = 0xC0|((chr>>6)&0x3F);
ptr[3] = 0xC0|(chr&0x3F);
return 4;
}
return 0;
}
int str_utf8_decode(const char **ptr)
{
const char *buf = *ptr;
int ch = 0;
do
{
if((*buf&0x80) == 0x0) /* 0xxxxxxx */
{
ch = *buf;
buf++;
}
else if((*buf&0xE0) == 0xC0) /* 110xxxxx */
{
ch = (*buf++ & 0x3F) << 6; if(!(*buf)) break;
ch += (*buf++ & 0x3F);
if(ch == 0) ch = -1;
}
else if((*buf & 0xF0) == 0xE0) /* 1110xxxx */
{
ch = (*buf++ & 0x1F) << 12; if(!(*buf)) break;
ch += (*buf++ & 0x3F) << 6; if(!(*buf)) break;
ch += (*buf++ & 0x3F);
if(ch == 0) ch = -1;
}
else if((*buf & 0xF8) == 0xF0) /* 11110xxx */
{
ch = (*buf++ & 0x0F) << 18; if(!(*buf)) break;
ch += (*buf++ & 0x3F) << 12; if(!(*buf)) break;
ch += (*buf++ & 0x3F) << 6; if(!(*buf)) break;
ch += (*buf++ & 0x3F);
if(ch == 0) ch = -1;
}
else
{
/* invalid */
buf++;
break;
}
*ptr = buf;
return ch;
} while(0);
/* out of bounds */
*ptr = buf;
return -1;
}
#if defined(__cplusplus)
}
#endif

View file

@ -947,6 +947,69 @@ int str_isspace(char c);
*/
void gui_messagebox(const char *title, const char *message);
/*
Function: str_utf8_rewind
Moves a cursor backwards in an utf8 string
Parameters:
str - utf8 string
cursor - position in the string
Returns:
New cursor position.
Remarks:
- Won't move the cursor less then 0
*/
int str_utf8_rewind(const char *str, int cursor);
/*
Function: str_utf8_forward
Moves a cursor forwards in an utf8 string
Parameters:
str - utf8 string
cursor - position in the string
Returns:
New cursor position.
Remarks:
- Won't move the cursor beyond the zero termination marker
*/
int str_utf8_forward(const char *str, int cursor);
/*
Function: str_utf8_decode
Decodes an utf8 character
Parameters:
ptr - pointer to an utf8 string. this pointer will be moved forward
Returns:
Unicode value for the character. -1 for invalid characters and 0 for end of string.
Remarks:
- This function will also move the pointer forward.
*/
int str_utf8_decode(const char **ptr);
/*
Function: str_utf8_encode
Encode an utf8 character
Parameters:
ptr - Pointer to a buffer that should recive the data. Should be able to hold at least 4 bytes.
Returns:
Number of bytes put into the buffer.
Remarks:
- Does not do zero termination of the string.
*/
int str_utf8_encode(char *ptr, int chr);
#ifdef __cplusplus
}
#endif

View file

@ -121,6 +121,9 @@ typedef struct FONTSIZEDATA
int num_x_chars;
int num_y_chars;
int char_max_width;
int char_max_height;
FONTCHAR characters[MAX_CHARACTERS*MAX_CHARACTERS];
int current_character;
@ -201,35 +204,87 @@ static void grow(unsigned char *in, unsigned char *out, int w, int h)
}
}
static void font_init_texture(FONTSIZEDATA *font, int width, int height)
static void font_init_texture(FONTSIZEDATA *sizedata, int charwidth, int charheight, int xchars, int ychars)
{
static int font_memory_usage = 0;
int i;
int width = charwidth*xchars;
int height = charheight*ychars;
void *mem = mem_alloc(width*height, 1);
mem_zero(mem, width*height);
font->texture_width = width;
font->texture_height = height;
font->num_x_chars = 32;
font->num_y_chars = 32;
font->current_character = 0;
glGenTextures(2, font->textures);
if(sizedata->textures[0] == 0)
glGenTextures(2, sizedata->textures);
else
font_memory_usage -= sizedata->texture_width*sizedata->texture_height*2;
sizedata->num_x_chars = xchars;
sizedata->num_y_chars = ychars;
sizedata->texture_width = width;
sizedata->texture_height = height;
sizedata->current_character = 0;
for(i = 0; i < 2; i++)
{
glBindTexture(GL_TEXTURE_2D, font->textures[i]);
glBindTexture(GL_TEXTURE_2D, sizedata->textures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, font_texture_format, width, height, 0, font_texture_format, GL_UNSIGNED_BYTE, mem);
font_memory_usage += width*height;
}
dbg_msg("", "font memory usage: %d", font_memory_usage);
mem_free(mem);
}
static void font_increase_texture_size(FONTSIZEDATA *sizedata)
{
if(sizedata->texture_width < sizedata->texture_height)
sizedata->num_x_chars <<= 1;
else
sizedata->num_y_chars <<= 1;
font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, sizedata->num_x_chars, sizedata->num_y_chars);
}
static void font_init_index(FONT *font, int index)
{
font->sizes[index].font_size = font_sizes[index];
int outline_thickness = 1;
FONTSIZEDATA *sizedata = &font->sizes[index];
sizedata->font_size = font_sizes[index];
FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size);
if(sizedata->font_size >= 18)
outline_thickness = 2;
{
unsigned glyph_index;
int charcode;
int max_h = 0;
int max_w = 0;
charcode = FT_Get_First_Char(font->ft_face, &glyph_index);
while(glyph_index != 0)
{
/* do stuff */
FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT);
if(font->ft_face->glyph->metrics.width > max_w) max_w = font->ft_face->glyph->metrics.width;
if(font->ft_face->glyph->metrics.height > max_h) max_h = font->ft_face->glyph->metrics.height;
charcode = FT_Get_Next_Char(font->ft_face, charcode, &glyph_index);
}
max_w = (max_w>>6)+2+outline_thickness*2;
max_h = (max_h>>6)+2+outline_thickness*2;
for(sizedata->char_max_width = 1; sizedata->char_max_width < max_w; sizedata->char_max_width <<= 1);
for(sizedata->char_max_height = 1; sizedata->char_max_height < max_h; sizedata->char_max_height <<= 1);
}
//dbg_msg("font", "init size %d, texture size %d %d", font->sizes[index].font_size, w, h);
//FT_New_Face(ft_library, "data/fonts/vera.ttf", 0, &font->ft_face);
font_init_texture(&font->sizes[index], 1024*2, 1024*2);
font_init_texture(sizedata, sizedata->char_max_width, sizedata->char_max_height, 8, 8);
}
static FONTSIZEDATA *font_get_size(FONT *font, int pixelsize)
@ -279,6 +334,13 @@ static int font_get_slot(FONTSIZEDATA *sizedata)
if(sizedata->characters[i].touch_time < sizedata->characters[oldest].touch_time)
oldest = i;
}
if(time_get()-sizedata->characters[oldest].touch_time < time_freq())
{
font_increase_texture_size(sizedata);
return font_get_slot(sizedata);
}
return oldest;
}
}
@ -286,7 +348,7 @@ static int font_get_slot(FONTSIZEDATA *sizedata)
static int font_render_glyph(FONT *font, FONTSIZEDATA *sizedata, int chr)
{
FT_Bitmap *bitmap;
int slot_id = sizedata->current_character;
int slot_id = 0;
int slot_w = sizedata->texture_width / sizedata->num_x_chars;
int slot_h = sizedata->texture_height / sizedata->num_y_chars;
int slot_size = slot_w*slot_h;
@ -297,7 +359,7 @@ static int font_render_glyph(FONT *font, FONTSIZEDATA *sizedata, int chr)
FT_Set_Pixel_Sizes(font->ft_face, 0, sizedata->font_size);
if(FT_Load_Char(font->ft_face, chr, FT_LOAD_RENDER))
if(FT_Load_Char(font->ft_face, chr, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP))
{
dbg_msg("font", "error loading glyph %d", chr);
return -1;
@ -412,7 +474,7 @@ static FONTCHAR *font_get_char(FONT *font, FONTSIZEDATA *sizedata, int chr)
}
/* must only be called from the rendering function as the font must be set to the correct size */
static void font_setsize(FONT *font, int size)
static void font_render_setup(FONT *font, int size)
{
FT_Set_Pixel_Sizes(font->ft_face, 0, size);
}
@ -424,53 +486,6 @@ static float font_kerning(FONT *font, int left, int right)
return (kerning.x>>6);
}
static int utf8_decode(const unsigned char **ptr)
{
const unsigned char *buf = *ptr;
int ch = 0;
do
{
if((*buf&0x80) == 0x0) /* 0xxxxxxx */
{
ch = *buf;
buf++;
}
else if((*buf&0xE0) == 0xC0) /* 110xxxxx */
{
ch = (*buf++ & 0x3F) << 6; if(!(*buf)) break;
ch += (*buf++ & 0x3F);
if(ch == 0) ch = -1;
}
else if((*buf & 0xF0) == 0xE0) /* 1110xxxx */
{
ch = (*buf++ & 0x1F) << 12; if(!(*buf)) break;
ch += (*buf++ & 0x3F) << 6; if(!(*buf)) break;
ch += (*buf++ & 0x3F);
if(ch == 0) ch = -1;
}
else if((*buf & 0xF8) == 0xF0) /* 11110xxx */
{
ch = (*buf++ & 0x0F) << 18; if(!(*buf)) break;
ch += (*buf++ & 0x3F) << 12; if(!(*buf)) break;
ch += (*buf++ & 0x3F) << 6; if(!(*buf)) break;
ch += (*buf++ & 0x3F);
if(ch == 0) ch = -1;
}
else
break;
/* valid */
*ptr = buf;
return ch;
} while(0);
/* invalid */
*ptr = buf;
return -1;
}
void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
{
FONT *font = cursor->font;
@ -512,7 +527,7 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
return;
sizedata = font_get_size(font, actual_size);
font_setsize(font, actual_size);
font_render_setup(font, actual_size);
/* set length */
if(length < 0)
@ -527,8 +542,8 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
for(;i < 2; i++)
{
const unsigned char *current = (unsigned char *)text;
const unsigned char *end = current+length;
const char *current = (char *)text;
const char *end = current+length;
//int to_render = length;
draw_x = cursor_x;
draw_y = cursor_y;
@ -553,7 +568,7 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
{
int new_line = 0;
//int this_batch = (int)(end-current);
const unsigned char *batch_end = end;
const char *batch_end = end;
if(cursor->line_width > 0 && !(cursor->flags&TEXTFLAG_STOP_AT_END))
{
int wlen = word_length((char *)current);
@ -599,19 +614,23 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
while(current < batch_end)
{
const char *tmp;
float advance = 0;
int character = 0;
int nextcharacter = 0;
FONTCHAR *chr;
// TODO: UTF-8 decode
character = utf8_decode(&current);
character = str_utf8_decode(&current);
tmp = current;
nextcharacter = str_utf8_decode(&tmp);
if(character == '\n')
{
draw_x = cursor->start_x;
draw_y += size;
draw_x = (int)(draw_x * fake_to_screen_x) / fake_to_screen_x; /* realign */
draw_y = (int)(draw_y * fake_to_screen_y) / fake_to_screen_y;
/* current++; */
continue;
}
@ -625,7 +644,7 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length)
gfx_quads_drawTL(draw_x+chr->offset_x*size, draw_y+chr->offset_y*size, chr->width*size, chr->height*size);
}
advance = chr->advance_x; /* + font_kerning(font, character, *(current+1));*/
advance = chr->advance_x + font_kerning(font, character, nextcharacter)/size;
}
if(cursor->flags&TEXTFLAG_STOP_AT_END && draw_x+advance*size-cursor->start_x > cursor->line_width)

View file

@ -49,11 +49,11 @@ enum
static INPUT_EVENT input_events[INPUT_BUFFER_SIZE];
static int num_events = 0;
static void add_event(char c, int key, int flags)
static void add_event(int unicode, int key, int flags)
{
if(num_events != INPUT_BUFFER_SIZE)
{
input_events[num_events].ch = c;
input_events[num_events].unicode = unicode;
input_events[num_events].key = key;
input_events[num_events].flags = flags;
num_events++;
@ -178,7 +178,7 @@ void inp_update()
{
/* handle keys */
case SDL_KEYDOWN:
if(event.key.keysym.unicode < 255)
/*if(event.key.keysym.unicode < 255) */
add_event(event.key.keysym.unicode, 0, 0);
key = event.key.keysym.sym;
break;

View file

@ -671,5 +671,4 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length);
struct FONT *gfx_font_load(const char *filename);
void gfx_font_destroy(struct FONT *font);
#endif

View file

@ -20,7 +20,7 @@ enum
typedef struct
{
int flags;
unsigned char ch;
int unicode;
int key;
} INPUT_EVENT;

View file

@ -18,6 +18,7 @@
#include <game/generated/gc_data.hpp>
#include <game/client/gameclient.hpp>
#include <game/client/lineinput.hpp>
#include <mastersrv/mastersrv.h>
vec4 MENUS::gui_color;
@ -199,7 +200,7 @@ void MENUS::ui_draw_checkbox_number(const void *id, const char *text, int checke
ui_draw_checkbox_common(id, text, buf, r);
}
int MENUS::ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden)
int MENUS::ui_do_edit_box(void *id, const RECT *rect, char *str, unsigned str_size, float font_size, bool hidden)
{
int inside = ui_mouse_inside(rect);
int r = 0;
@ -229,48 +230,7 @@ int MENUS::ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, f
for(int i = 0; i < num_inputevents; i++)
{
len = strlen(str);
INPUT_EVENT e = inputevents[i];
char c = e.ch;
int k = e.key;
if (at_index > len)
at_index = len;
if (!(c >= 0 && c < 32) && c != 127)
{
if (len < str_size - 1 && at_index < str_size - 1)
{
memmove(str + at_index + 1, str + at_index, len - at_index + 1);
str[at_index] = c;
at_index++;
r = 1;
}
}
if(e.flags&INPFLAG_PRESS)
{
if (k == KEY_BACKSPACE && at_index > 0)
{
memmove(str + at_index - 1, str + at_index, len - at_index + 1);
at_index--;
r = 1;
}
else if (k == KEY_DELETE && at_index < len)
{
memmove(str + at_index, str + at_index + 1, len - at_index);
r = 1;
}
else if (k == KEY_RETURN)
ui_clear_last_active_item();
else if (k == KEY_LEFT && at_index > 0)
at_index--;
else if (k == KEY_RIGHT && at_index < len)
at_index++;
else if (k == KEY_HOME)
at_index = 0;
else if (k == KEY_END)
at_index = len;
}
LINEINPUT::manipulate(inputevents[i], str, str_size, &len, &at_index);
}
}

View file

@ -40,7 +40,7 @@ class MENUS : public COMPONENT
static void ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const RECT *r);
static void ui_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, const void *extra);
static void ui_draw_checkbox_number(const void *id, const char *text, int checked, const RECT *r, const void *extra);
static int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden=false);
static int ui_do_edit_box(void *id, const RECT *rect, char *str, unsigned str_size, float font_size, bool hidden=false);
static float ui_do_scrollbar_v(const void *id, const RECT *rect, float current);
static float ui_do_scrollbar_h(const void *id, const RECT *rect, float current);

View file

@ -21,23 +21,30 @@ void LINEINPUT::set(const char *string)
cursor_pos = len;
}
void LINEINPUT::process_input(INPUT_EVENT e)
void LINEINPUT::manipulate(INPUT_EVENT e, char *str, int str_max_size, int *str_len_ptr, int *cursor_pos_ptr)
{
int cursor_pos = *cursor_pos_ptr;
int len = *str_len_ptr;
if(cursor_pos > len)
cursor_pos = len;
char c = e.ch;
int code = e.unicode;
int k = e.key;
// 127 is produced on Mac OS X and corresponds to the delete key
if (!(c >= 0 && c < 32) && c != 127)
if (!(code >= 0 && code < 32) && code != 127)
{
if (len < sizeof(str) - 1 && cursor_pos < sizeof(str) - 1)
char tmp[8];
int charsize = str_utf8_encode(tmp, code);
if (len < str_max_size - charsize && cursor_pos < str_max_size - charsize)
{
memmove(str + cursor_pos + 1, str + cursor_pos, len - cursor_pos + 1);
str[cursor_pos] = c;
cursor_pos++;
len++;
memmove(str + cursor_pos + charsize, str + cursor_pos, len - cursor_pos + charsize);
for(int i = 0; i < charsize; i++)
str[cursor_pos+i] = tmp[i];
cursor_pos += charsize;
len += charsize;
}
}
@ -45,22 +52,34 @@ void LINEINPUT::process_input(INPUT_EVENT e)
{
if (k == KEY_BACKSPACE && cursor_pos > 0)
{
memmove(str + cursor_pos - 1, str + cursor_pos, len - cursor_pos + 1);
cursor_pos--;
len--;
int new_cursor_pos = str_utf8_rewind(str, cursor_pos);
int charsize = cursor_pos-new_cursor_pos;
memmove(str+new_cursor_pos, str+cursor_pos, len - charsize + 1); // +1 == null term
cursor_pos = new_cursor_pos;
len -= charsize;
}
else if (k == KEY_DELETE && cursor_pos < len)
{
memmove(str + cursor_pos, str + cursor_pos + 1, len - cursor_pos);
len--;
int p = str_utf8_forward(str, cursor_pos);
int charsize = p-cursor_pos;
memmove(str + cursor_pos, str + cursor_pos + charsize, len - cursor_pos - charsize + 1); // +1 == null term
len -= charsize;
}
else if (k == KEY_LEFT && cursor_pos > 0)
cursor_pos--;
cursor_pos = str_utf8_rewind(str, cursor_pos);
else if (k == KEY_RIGHT && cursor_pos < len)
cursor_pos++;
cursor_pos = str_utf8_forward(str, cursor_pos);
else if (k == KEY_HOME)
cursor_pos = 0;
else if (k == KEY_END)
cursor_pos = len;
}
*cursor_pos_ptr = cursor_pos;
*str_len_ptr = len;
}
void LINEINPUT::process_input(INPUT_EVENT e)
{
manipulate(e, str, sizeof(str), &len, &cursor_pos);
}

View file

@ -5,9 +5,11 @@
class LINEINPUT
{
char str[256];
unsigned len;
unsigned cursor_pos;
int len;
int cursor_pos;
public:
static void manipulate(INPUT_EVENT e, char *str, int str_max_size, int *str_len, int *cursor_pos);
class CALLBACK
{
public:

View file

@ -18,6 +18,7 @@ extern "C" {
#include <game/client/render.hpp>
#include "ed_editor.hpp"
#include <game/client/lineinput.hpp>
static int checker_texture = 0;
static int background_texture = 0;
@ -209,44 +210,7 @@ int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float fo
for(int i = 0; i < inp_num_events(); i++)
{
len = strlen(str);
INPUT_EVENT e = inp_get_event(i);
char c = e.ch;
int k = e.key;
if (at_index > len)
at_index = len;
if (!(c >= 0 && c < 32) && c != 127)
{
if (len < str_size - 1 && at_index < str_size - 1)
{
memmove(str + at_index + 1, str + at_index, len - at_index + 1);
str[at_index] = c;
at_index++;
}
}
if(e.flags&INPFLAG_PRESS)
{
if (k == KEY_BACKSPACE && at_index > 0)
{
memmove(str + at_index - 1, str + at_index, len - at_index + 1);
at_index--;
}
else if (k == KEY_DELETE && at_index < len)
memmove(str + at_index, str + at_index + 1, len - at_index);
else if (k == KEY_RETURN)
ui_clear_last_active_item();
else if (k == KEY_LEFT && at_index > 0)
at_index--;
else if (k == KEY_RIGHT && at_index < len)
at_index++;
else if (k == KEY_HOME)
at_index = 0;
else if (k == KEY_END)
at_index = len;
}
LINEINPUT::manipulate(inp_get_event(i), str, str_size, &len, &at_index);
}
r = 1;