/* 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 ) { 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; jvstart+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; kvstart+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; }