#include #include #include static int word_length(const char *text) { int s = 1; while(1) { if(*text == 0) return s-1; if(*text == '\n' || *text == '\t' || *text == ' ') return s; text++; s++; } } static float text_r=1; static float text_g=1; static float text_b=1; static float text_a=1; static FONT_SET *default_font_set = 0; void gfx_text_set_default_font(void *font) { default_font_set = (FONT_SET *)font; } void gfx_text_set_cursor(TEXT_CURSOR *cursor, float x, float y, float font_size, int flags) { mem_zero(cursor, sizeof(*cursor)); cursor->font_size = font_size; cursor->start_x = x; cursor->start_y = y; cursor->x = x; cursor->y = y; cursor->line_count = 1; cursor->line_width = -1; cursor->flags = flags; cursor->charcount = 0; } void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length) { FONT_SET *font_set = cursor->font_set; float screen_x0, screen_y0, screen_x1, screen_y1; float fake_to_screen_x, fake_to_screen_y; int actual_x, actual_y; FONT *font; int actual_size; int i; float draw_x, draw_y; const char *end; float size = cursor->font_size; /* to correct coords, convert to screen coords, round, and convert back */ gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); fake_to_screen_x = (gfx_screenwidth()/(screen_x1-screen_x0)); fake_to_screen_y = (gfx_screenheight()/(screen_y1-screen_y0)); actual_x = cursor->x * fake_to_screen_x; actual_y = cursor->y * fake_to_screen_y; cursor->x = actual_x / fake_to_screen_x; cursor->y = actual_y / fake_to_screen_y; /* same with size */ actual_size = size * fake_to_screen_y; size = actual_size / fake_to_screen_y; if(!font_set) font_set = default_font_set; font = font_set_pick(font_set, actual_size); if (length < 0) length = strlen(text); end = text + length; /* if we don't want to render, we can just skip the first outline pass */ i = 1; if(cursor->flags&TEXTFLAG_RENDER) i = 0; for(;i < 2; i++) { const unsigned char *current = (unsigned char *)text; int to_render = length; draw_x = cursor->x; draw_y = cursor->y; if(cursor->flags&TEXTFLAG_RENDER) { if (i == 0) gfx_texture_set(font->outline_texture); else gfx_texture_set(font->text_texture); gfx_quads_begin(); if (i == 0) gfx_setcolor(0.0f, 0.0f, 0.0f, 0.3f*text_a); else gfx_setcolor(text_r, text_g, text_b, text_a); } while(to_render > 0) { int new_line = 0; int this_batch = to_render; if(cursor->line_width > 0 && !(cursor->flags&TEXTFLAG_STOP_AT_END)) { int wlen = word_length((char *)current); TEXT_CURSOR compare = *cursor; compare.x = draw_x; compare.y = draw_y; compare.flags &= ~TEXTFLAG_RENDER; compare.line_width = -1; gfx_text_ex(&compare, text, wlen); if(compare.x-draw_x > cursor->line_width) { /* word can't be fitted in one line, cut it */ TEXT_CURSOR cutter = *cursor; cutter.charcount = 0; cutter.x = draw_x; cutter.y = draw_y; cutter.flags &= ~TEXTFLAG_RENDER; cutter.flags |= TEXTFLAG_STOP_AT_END; gfx_text_ex(&cutter, (const char *)current, wlen); wlen = cutter.charcount; new_line = 1; if(wlen <= 3) /* if we can't place 3 chars of the word on this line, take the next */ wlen = 0; } else if(compare.x-cursor->start_x > cursor->line_width) { new_line = 1; wlen = 0; } this_batch = wlen; } if((const char *)current+this_batch > end) this_batch = (const char *)end-(const char *)current; to_render -= this_batch; while(this_batch-- > 0) { float tex_x0, tex_y0, tex_x1, tex_y1; float width, height; float x_offset, y_offset, x_advance; float advance; if(*current == '\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; } font_character_info(font, *current, &tex_x0, &tex_y0, &tex_x1, &tex_y1, &width, &height, &x_offset, &y_offset, &x_advance); if(cursor->flags&TEXTFLAG_RENDER) { gfx_quads_setsubset(tex_x0, tex_y0, tex_x1, tex_y1); gfx_quads_drawTL(draw_x+x_offset*size, draw_y+y_offset*size, width*size, height*size); } advance = x_advance + font_kerning(font, *current, *(current+1)); if(cursor->flags&TEXTFLAG_STOP_AT_END && draw_x+advance*size-cursor->start_x > cursor->line_width) { /* we hit the end of the line, no more to render or count */ to_render = 0; break; } draw_x += advance*size; cursor->charcount++; current++; } if(new_line) { 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; } } if(cursor->flags&TEXTFLAG_RENDER) gfx_quads_end(); } cursor->x = draw_x; cursor->y = draw_y; } void gfx_text(void *font_set_v, float x, float y, float size, const char *text, int max_width) { TEXT_CURSOR cursor; gfx_text_set_cursor(&cursor, x, y, size, TEXTFLAG_RENDER); cursor.line_width = max_width; gfx_text_ex(&cursor, text, -1); } float gfx_text_width(void *font_set_v, float size, const char *text, int length) { TEXT_CURSOR cursor; gfx_text_set_cursor(&cursor, 0, 0, size, 0); gfx_text_ex(&cursor, text, length); return cursor.x; } void gfx_text_color(float r, float g, float b, float a) { text_r = r; text_g = g; text_b = b; text_a = a; }