libglez/ftgl/text-buffer.c
2018-05-22 17:43:37 +02:00

537 lines
18 KiB
C

/* Freetype GL - A C OpenGL Freetype engine
*
* Distributed under the OSI-approved BSD 2-Clause License. See accompanying
* file `LICENSE` for more details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>
#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;
}