/* Freetype GL - A C OpenGL Freetype engine * * Distributed under the OSI-approved BSD 2-Clause License. See accompanying * file `LICENSE` for more details. */ #include #include #include #include #include #include #include "opengl.h" #include "text-buffer.h" #include "utf8-utils.h" #define SET_GLYPH_VERTEX(value, x0, y0, z0, s0, t0, r, g, b, a, sh, gm) \ { \ glyph_vertex_t *gv = &value; \ gv->x = x0; \ gv->y = y0; \ gv->z = z0; \ gv->u = s0; \ gv->v = t0; \ gv->r = r; \ gv->g = g; \ gv->b = b; \ gv->a = a; \ gv->shift = sh; \ gv->gamma = gm; \ } // ---------------------------------------------------------------------------- text_buffer_t *text_buffer_new() { text_buffer_t *self = (text_buffer_t *) malloc(sizeof(text_buffer_t)); self->buffer = vertex_buffer_new( "vertex:3f,tex_coord:2f,color:4f,ashift:1f,agamma:1f"); self->line_start = 0; self->line_ascender = 0; self->base_color.r = 0.0; self->base_color.g = 0.0; self->base_color.b = 0.0; self->base_color.a = 1.0; self->line_descender = 0; self->lines = vector_new(sizeof(line_info_t)); self->bounds.left = 0.0; self->bounds.top = 0.0; self->bounds.width = 0.0; self->bounds.height = 0.0; return self; } // ---------------------------------------------------------------------------- void text_buffer_delete(text_buffer_t *self) { vector_delete(self->lines); vertex_buffer_delete(self->buffer); free(self); } // ---------------------------------------------------------------------------- void text_buffer_clear(text_buffer_t *self) { assert(self); vertex_buffer_clear(self->buffer); self->line_start = 0; self->line_ascender = 0; self->line_descender = 0; vector_clear(self->lines); self->bounds.left = 0.0; self->bounds.top = 0.0; self->bounds.width = 0.0; self->bounds.height = 0.0; } // ---------------------------------------------------------------------------- void text_buffer_printf(text_buffer_t *self, vec2 *pen, ...) { markup_t *markup; char *text; va_list args; if (vertex_buffer_size(self->buffer) == 0) { self->origin = *pen; } va_start(args, pen); do { markup = va_arg(args, markup_t *); if (markup == NULL) { va_end(args); return; } text = va_arg(args, char *); text_buffer_add_text(self, pen, markup, text, 0); } while (markup != 0); va_end(args); } // ---------------------------------------------------------------------------- void text_buffer_move_last_line(text_buffer_t *self, float dy) { size_t i; int j; for (i = self->line_start; i < vector_size(self->buffer->items); ++i) { ivec4 *item = (ivec4 *) vector_get(self->buffer->items, i); for (j = item->vstart; j < item->vstart + item->vcount; ++j) { glyph_vertex_t *vertex = (glyph_vertex_t *) vector_get(self->buffer->vertices, j); vertex->y -= dy; } } } // ---------------------------------------------------------------------------- // text_buffer_finish_line (internal use only) // // Performs calculations needed at the end of each line of text // and prepares for the next line if necessary // // advancePen: if true, advance the pen to the next line // static void text_buffer_finish_line(text_buffer_t *self, vec2 *pen, bool advancePen) { float line_left = self->line_left; float line_right = pen->x; float line_width = line_right - line_left; float line_top = pen->y + self->line_ascender; float line_height = self->line_ascender - self->line_descender; float line_bottom = line_top - line_height; line_info_t line_info; line_info.line_start = self->line_start; line_info.bounds.left = line_left; line_info.bounds.top = line_top; line_info.bounds.width = line_width; line_info.bounds.height = line_height; vector_push_back(self->lines, &line_info); if (line_left < self->bounds.left) { self->bounds.left = line_left; } if (line_top > self->bounds.top) { self->bounds.top = line_top; } float self_right = self->bounds.left + self->bounds.width; float self_bottom = self->bounds.top - self->bounds.height; if (line_right > self_right) { self->bounds.width = line_right - self->bounds.left; } if (line_bottom < self_bottom) { self->bounds.height = self->bounds.top - line_bottom; } if (advancePen) { pen->x = self->origin.x; pen->y += (int) (self->line_descender); } self->line_descender = 0; self->line_ascender = 0; self->line_start = vector_size(self->buffer->items); self->line_left = pen->x; } // ---------------------------------------------------------------------------- void text_buffer_add_text(text_buffer_t *self, vec2 *pen, markup_t *markup, const char *text, size_t length) { size_t i; const char *prev_character = NULL; if (markup == NULL) { return; } if (!markup->font) { fprintf(stderr, "Houston, we've got a problem !\n"); return; } if (length == 0) { length = utf8_strlen(text); } if (vertex_buffer_size(self->buffer) == 0) { self->origin = *pen; self->line_left = pen->x; self->bounds.left = pen->x; self->bounds.top = pen->y; } else { if (pen->x < self->origin.x) { self->origin.x = pen->x; } if (pen->y != self->last_pen_y) { text_buffer_finish_line(self, pen, false); } } for (i = 0; length; i += utf8_surrogate_len(text + i)) { text_buffer_add_char(self, pen, markup, text + i, prev_character); prev_character = text + i; length--; } self->last_pen_y = pen->y; } // ---------------------------------------------------------------------------- void text_buffer_add_char(text_buffer_t *self, vec2 *pen, markup_t *markup, const char *current, const char *previous) { size_t vcount = 0; size_t icount = 0; vertex_buffer_t *buffer = self->buffer; texture_font_t *font = markup->font; float gamma = markup->gamma; // Maximum number of vertices is 20 (= 5x2 triangles) per glyph: // - 2 triangles for background // - 2 triangles for overline // - 2 triangles for underline // - 2 triangles for strikethrough // - 2 triangles for glyph glyph_vertex_t vertices[4 * 5]; GLuint indices[6 * 5]; texture_glyph_t *glyph; texture_glyph_t *black; float kerning = 0.0f; if (markup->font->ascender > self->line_ascender) { float y = pen->y; pen->y -= (markup->font->ascender - self->line_ascender); text_buffer_move_last_line(self, (float) (int) (y - pen->y)); self->line_ascender = markup->font->ascender; } if (markup->font->descender < self->line_descender) { self->line_descender = markup->font->descender; } if (*current == '\n') { text_buffer_finish_line(self, pen, true); return; } glyph = texture_font_get_glyph(font, current); black = texture_font_get_glyph(font, NULL); if (glyph == NULL) { return; } if (previous && markup->font->kerning) { kerning = texture_glyph_get_kerning(glyph, previous); } pen->x += kerning; // Background if (markup->background_color.alpha > 0) { float r = markup->background_color.r; float g = markup->background_color.g; float b = markup->background_color.b; float a = markup->background_color.a; float x0 = (pen->x - kerning); float y0 = (float) (int) (pen->y + font->descender); float x1 = (x0 + glyph->advance_x); float y1 = (float) (int) (y0 + font->height + font->linegap); float s0 = black->s0; float t0 = black->t0; float s1 = black->s1; float t1 = black->t1; SET_GLYPH_VERTEX(vertices[vcount + 0], (float) (int) x0, y0, 0, s0, t0, r, g, b, a, x0 - ((int) x0), gamma); SET_GLYPH_VERTEX(vertices[vcount + 1], (float) (int) x0, y1, 0, s0, t1, r, g, b, a, x0 - ((int) x0), gamma); SET_GLYPH_VERTEX(vertices[vcount + 2], (float) (int) x1, y1, 0, s1, t1, r, g, b, a, x1 - ((int) x1), gamma); SET_GLYPH_VERTEX(vertices[vcount + 3], (float) (int) x1, y0, 0, s1, t0, r, g, b, a, x1 - ((int) x1), gamma); indices[icount + 0] = vcount + 0; indices[icount + 1] = vcount + 1; indices[icount + 2] = vcount + 2; indices[icount + 3] = vcount + 0; indices[icount + 4] = vcount + 2; indices[icount + 5] = vcount + 3; vcount += 4; icount += 6; } // Underline if (markup->underline) { float r = markup->underline_color.r; float g = markup->underline_color.g; float b = markup->underline_color.b; float a = markup->underline_color.a; float x0 = (pen->x - kerning); float y0 = (float) (int) (pen->y + font->underline_position); float x1 = (x0 + glyph->advance_x); float y1 = (float) (int) (y0 + font->underline_thickness); float s0 = black->s0; float t0 = black->t0; float s1 = black->s1; float t1 = black->t1; SET_GLYPH_VERTEX(vertices[vcount + 0], (float) (int) x0, y0, 0, s0, t0, r, g, b, a, x0 - ((int) x0), gamma); SET_GLYPH_VERTEX(vertices[vcount + 1], (float) (int) x0, y1, 0, s0, t1, r, g, b, a, x0 - ((int) x0), gamma); SET_GLYPH_VERTEX(vertices[vcount + 2], (float) (int) x1, y1, 0, s1, t1, r, g, b, a, x1 - ((int) x1), gamma); SET_GLYPH_VERTEX(vertices[vcount + 3], (float) (int) x1, y0, 0, s1, t0, r, g, b, a, x1 - ((int) x1), gamma); indices[icount + 0] = vcount + 0; indices[icount + 1] = vcount + 1; indices[icount + 2] = vcount + 2; indices[icount + 3] = vcount + 0; indices[icount + 4] = vcount + 2; indices[icount + 5] = vcount + 3; vcount += 4; icount += 6; } // Overline if (markup->overline) { float r = markup->overline_color.r; float g = markup->overline_color.g; float b = markup->overline_color.b; float a = markup->overline_color.a; float x0 = (pen->x - kerning); float y0 = (float) (int) (pen->y + (int) font->ascender); float x1 = (x0 + glyph->advance_x); float y1 = (float) (int) (y0 + (int) font->underline_thickness); float s0 = black->s0; float t0 = black->t0; float s1 = black->s1; float t1 = black->t1; SET_GLYPH_VERTEX(vertices[vcount + 0], (float) (int) x0, y0, 0, s0, t0, r, g, b, a, x0 - ((int) x0), gamma); SET_GLYPH_VERTEX(vertices[vcount + 1], (float) (int) x0, y1, 0, s0, t1, r, g, b, a, x0 - ((int) x0), gamma); SET_GLYPH_VERTEX(vertices[vcount + 2], (float) (int) x1, y1, 0, s1, t1, r, g, b, a, x1 - ((int) x1), gamma); SET_GLYPH_VERTEX(vertices[vcount + 3], (float) (int) x1, y0, 0, s1, t0, r, g, b, a, x1 - ((int) x1), gamma); indices[icount + 0] = vcount + 0; indices[icount + 1] = vcount + 1; indices[icount + 2] = vcount + 2; indices[icount + 3] = vcount + 0; indices[icount + 4] = vcount + 2; indices[icount + 5] = vcount + 3; vcount += 4; icount += 6; } /* Strikethrough */ if (markup->strikethrough) { float r = markup->strikethrough_color.r; float g = markup->strikethrough_color.g; float b = markup->strikethrough_color.b; float a = markup->strikethrough_color.a; float x0 = (pen->x - kerning); float y0 = (float) (int) (pen->y + (int) font->ascender * .33f); float x1 = (x0 + glyph->advance_x); float y1 = (float) (int) (y0 + (int) font->underline_thickness); float s0 = black->s0; float t0 = black->t0; float s1 = black->s1; float t1 = black->t1; SET_GLYPH_VERTEX(vertices[vcount + 0], (float) (int) x0, y0, 0, s0, t0, r, g, b, a, x0 - ((int) x0), gamma); SET_GLYPH_VERTEX(vertices[vcount + 1], (float) (int) x0, y1, 0, s0, t1, r, g, b, a, x0 - ((int) x0), gamma); SET_GLYPH_VERTEX(vertices[vcount + 2], (float) (int) x1, y1, 0, s1, t1, r, g, b, a, x1 - ((int) x1), gamma); SET_GLYPH_VERTEX(vertices[vcount + 3], (float) (int) x1, y0, 0, s1, t0, r, g, b, a, x1 - ((int) x1), gamma); indices[icount + 0] = vcount + 0; indices[icount + 1] = vcount + 1; indices[icount + 2] = vcount + 2; indices[icount + 3] = vcount + 0; indices[icount + 4] = vcount + 2; indices[icount + 5] = vcount + 3; vcount += 4; icount += 6; } { // Actual glyph float r = markup->foreground_color.red; float g = markup->foreground_color.green; float b = markup->foreground_color.blue; float a = markup->foreground_color.alpha; float x0 = (pen->x + glyph->offset_x); float y0 = (float) (int) (pen->y + glyph->offset_y); float x1 = (x0 + glyph->width); float y1 = (float) (int) (y0 - glyph->height); float s0 = glyph->s0; float t0 = glyph->t0; float s1 = glyph->s1; float t1 = glyph->t1; SET_GLYPH_VERTEX(vertices[vcount + 0], (float) (int) x0, y0, 0, s0, t0, r, g, b, a, x0 - ((int) x0), gamma); SET_GLYPH_VERTEX(vertices[vcount + 1], (float) (int) x0, y1, 0, s0, t1, r, g, b, a, x0 - ((int) x0), gamma); SET_GLYPH_VERTEX(vertices[vcount + 2], (float) (int) x1, y1, 0, s1, t1, r, g, b, a, x1 - ((int) x1), gamma); SET_GLYPH_VERTEX(vertices[vcount + 3], (float) (int) x1, y0, 0, s1, t0, r, g, b, a, x1 - ((int) x1), gamma); indices[icount + 0] = vcount + 0; indices[icount + 1] = vcount + 1; indices[icount + 2] = vcount + 2; indices[icount + 3] = vcount + 0; indices[icount + 4] = vcount + 2; indices[icount + 5] = vcount + 3; vcount += 4; icount += 6; vertex_buffer_push_back(buffer, vertices, vcount, indices, icount); pen->x += glyph->advance_x * (1.0f + markup->spacing); } } // ---------------------------------------------------------------------------- void text_buffer_align(text_buffer_t *self, vec2 *pen, enum Align alignment) { if (ALIGN_LEFT == alignment) { return; } size_t total_items = vector_size(self->buffer->items); if (self->line_start != total_items) { text_buffer_finish_line(self, pen, false); } size_t i, j; int k; float self_left, self_right, self_center; float line_left, line_right, line_center; float dx; self_left = self->bounds.left; self_right = self->bounds.left + self->bounds.width; self_center = (self_left + self_right) / 2; line_info_t *line_info; size_t lines_count, line_end; lines_count = vector_size(self->lines); for (i = 0; i < lines_count; ++i) { line_info = (line_info_t *) vector_get(self->lines, i); if (i + 1 < lines_count) { line_end = ((line_info_t *) vector_get(self->lines, i + 1))->line_start; } else { line_end = vector_size(self->buffer->items); } line_right = line_info->bounds.left + line_info->bounds.width; if (ALIGN_RIGHT == alignment) { dx = self_right - line_right; } else // ALIGN_CENTER { line_left = line_info->bounds.left; line_center = (line_left + line_right) / 2; dx = self_center - line_center; } dx = roundf(dx); for (j = line_info->line_start; j < line_end; ++j) { ivec4 *item = (ivec4 *) vector_get(self->buffer->items, j); for (k = item->vstart; k < item->vstart + item->vcount; ++k) { glyph_vertex_t *vertex = (glyph_vertex_t *) vector_get(self->buffer->vertices, k); vertex->x += dx; } } } } vec4 text_buffer_get_bounds(text_buffer_t *self, vec2 *pen) { size_t total_items = vector_size(self->buffer->items); if (self->line_start != total_items) { text_buffer_finish_line(self, pen, false); } return self->bounds; }