void SubtitleRenderer:: prepare_glyphs(const std::vector<InternalChar>& text) { for (auto c = text.begin(); c != text.end(); ++c) { if (glyphs_.find(*c) == glyphs_.end()) load_glyph(*c); } }
// ------------------------------------------------------------------- init --- void init( void ) { atlas = texture_atlas_new( 512, 512, 1 ); font = texture_font_new_from_file( atlas, 32, "../media/fonts/Vera.ttf" ); texture_glyph_t *glyph; // Generate the glyp at 512 points, compute distance field and scale it // back to 32 points // Just load another glyph if you want to see difference (draw render a '@') glyph = load_glyph( "../media/fonts/Vera.ttf", "@", 512, 64, 0.1); vector_push_back( font->glyphs, &glyph ); texture_atlas_upload( atlas ); glyph = texture_font_get_glyph( font, "@"); GLuint indices[6] = {0,1,2, 0,2,3}; vertex_t vertices[4] = { { -.5,-.5,0, glyph->s0,glyph->t1, 0,0,0,1 }, { -.5, .5,0, glyph->s0,glyph->t0, 0,0,0,1 }, { .5, .5,0, glyph->s1,glyph->t0, 0,0,0,1 }, { .5,-.5,0, glyph->s1,glyph->t1, 0,0,0,1 } }; buffer = vertex_buffer_new( "vertex:3f,tex_coord:2f,color:4f" ); vertex_buffer_push_back( buffer, vertices, 4, indices, 6 ); program = shader_load( "../media/shaders/distance-field.vert", "../media/shaders/distance-field-2.frag" ); mat4_set_identity( &projection ); mat4_set_identity( &model ); mat4_set_identity( &view ); }
static int dtext_prepare_text(AVFilterContext *ctx) { DrawTextContext *s = ctx->priv; uint32_t code = 0, prev_code = 0; int x = 0, y = 0, i = 0, ret; int text_height, baseline; char *text; uint8_t *p; int str_w = 0, len; int y_min = 32000, y_max = -32000; FT_Vector delta; Glyph *glyph = NULL, *prev_glyph = NULL; Glyph dummy = { 0 }; int width = ctx->inputs[0]->w; int height = ctx->inputs[0]->h; ret = expand_strftime(s); if (ret < 0) return ret; text = s->expanded_text ? s->expanded_text : s->text; if ((len = strlen(text)) > s->nb_positions) { FT_Vector *p = av_realloc(s->positions, len * sizeof(*s->positions)); if (!p) { av_freep(s->positions); s->nb_positions = 0; return AVERROR(ENOMEM); } else { s->positions = p; s->nb_positions = len; } } /* load and cache glyphs */ for (i = 0, p = text; *p; i++) { GET_UTF8(code, *p++, continue;); /* get glyph */ dummy.code = code; glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); if (!glyph) { ret = load_glyph(ctx, &glyph, code); if (ret) return ret; } y_min = FFMIN(glyph->bbox.yMin, y_min); y_max = FFMAX(glyph->bbox.yMax, y_max); }
// ------------------------------------------------------------------- main --- int main( int argc, char **argv ) { glutInit( &argc, argv ); glutInitWindowSize( 512, 512 ); glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); glutCreateWindow( "Distance fields demo" ); glutReshapeFunc( reshape ); glutDisplayFunc( display ); glutKeyboardFunc( keyboard ); glutTimerFunc( 1000.0/60, timer, 60 ); glewExperimental = GL_TRUE; GLenum err = glewInit(); if (GLEW_OK != err) { /* Problem: glewInit failed, something is seriously wrong. */ fprintf( stderr, "Error: %s\n", glewGetErrorString(err) ); exit( EXIT_FAILURE ); } fprintf( stderr, "Using GLEW %s\n", glewGetString(GLEW_VERSION) ); program = shader_load( "shaders/distance-field.vert", "shaders/distance-field-3.frag" ); glUseProgram( program ); atlas = texture_atlas_new( 512, 512, 1 ); font = texture_font_new_from_file( atlas, 32, "fonts/Vera.ttf" ); texture_glyph_t *glyph; // Generate the glyp at 512 points, compute distance field and scale it // back to 32 points // Just load another glyph if you want to see difference (draw render a '@') glyph = load_glyph( "fonts/Vera.ttf", L'@', 512, 64, 0.1); vector_push_back( font->glyphs, &glyph ); texture_atlas_upload( atlas ); glutMainLoop( ); return 0; }
static av_cold int init(AVFilterContext *ctx) { int err; DrawTextContext *s = ctx->priv; Glyph *glyph; if (!s->fontfile && !CONFIG_LIBFONTCONFIG) { av_log(ctx, AV_LOG_ERROR, "No font filename provided\n"); return AVERROR(EINVAL); } if (s->textfile) { if (s->text) { av_log(ctx, AV_LOG_ERROR, "Both text and text file provided. Please provide only one\n"); return AVERROR(EINVAL); } if ((err = load_textfile(ctx)) < 0) return err; } if (s->reload && !s->textfile) av_log(ctx, AV_LOG_WARNING, "No file to reload\n"); if (s->tc_opt_string) { int ret = av_timecode_init_from_string(&s->tc, s->tc_rate, s->tc_opt_string, ctx); if (ret < 0) return ret; if (s->tc24hmax) s->tc.flags |= AV_TIMECODE_FLAG_24HOURSMAX; if (!s->text) s->text = av_strdup(""); } if (!s->text) { av_log(ctx, AV_LOG_ERROR, "Either text, a valid file or a timecode must be provided\n"); return AVERROR(EINVAL); } #if CONFIG_LIBFRIBIDI if (s->text_shaping) if ((err = shape_text(ctx)) < 0) return err; #endif if ((err = FT_Init_FreeType(&(s->library)))) { av_log(ctx, AV_LOG_ERROR, "Could not load FreeType: %s\n", FT_ERRMSG(err)); return AVERROR(EINVAL); } err = load_font(ctx); if (err) return err; if (!s->fontsize) s->fontsize = 16; if ((err = FT_Set_Pixel_Sizes(s->face, 0, s->fontsize))) { av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n", s->fontsize, FT_ERRMSG(err)); return AVERROR(EINVAL); } if (s->borderw) { if (FT_Stroker_New(s->library, &s->stroker)) { av_log(ctx, AV_LOG_ERROR, "Coult not init FT stroker\n"); return AVERROR_EXTERNAL; } FT_Stroker_Set(s->stroker, s->borderw << 6, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); } s->use_kerning = FT_HAS_KERNING(s->face); /* load the fallback glyph with code 0 */ load_glyph(ctx, NULL, 0); /* set the tabsize in pixels */ if ((err = load_glyph(ctx, &glyph, ' ')) < 0) { av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n"); return err; } s->tabsize *= glyph->advance; if (s->exp_mode == EXP_STRFTIME && (strchr(s->text, '%') || strchr(s->text, '\\'))) av_log(ctx, AV_LOG_WARNING, "expansion=strftime is deprecated.\n"); av_bprint_init(&s->expanded_text, 0, AV_BPRINT_SIZE_UNLIMITED); av_bprint_init(&s->expanded_fontcolor, 0, AV_BPRINT_SIZE_UNLIMITED); return 0; }
static int dtext_prepare_text(AVFilterContext *ctx) { DrawTextContext *s = ctx->priv; uint32_t code = 0, prev_code = 0; int x = 0, y = 0, i = 0, ret; int text_height, baseline; char *text = s->text; uint8_t *p; int str_w = 0, len; int y_min = 32000, y_max = -32000; FT_Vector delta; Glyph *glyph = NULL, *prev_glyph = NULL; Glyph dummy = { 0 }; int width = ctx->inputs[0]->w; int height = ctx->inputs[0]->h; time_t now = time(0); struct tm ltime; uint8_t *buf = s->expanded_text; int buf_size = s->expanded_text_size; if (!buf) buf_size = 2*strlen(s->text)+1; localtime_r(&now, <ime); while ((buf = av_realloc(buf, buf_size))) { *buf = 1; if (strftime(buf, buf_size, s->text, <ime) != 0 || *buf == 0) break; buf_size *= 2; } if (!buf) return AVERROR(ENOMEM); text = s->expanded_text = buf; s->expanded_text_size = buf_size; if ((len = strlen(text)) > s->nb_positions) { FT_Vector *p = av_realloc(s->positions, len * sizeof(*s->positions)); if (!p) { av_freep(s->positions); s->nb_positions = 0; return AVERROR(ENOMEM); } else { s->positions = p; s->nb_positions = len; } } /* load and cache glyphs */ for (i = 0, p = text; *p; i++) { GET_UTF8(code, *p++, continue;); /* get glyph */ dummy.code = code; glyph = av_tree_find(s->glyphs, &dummy, glyph_cmp, NULL); if (!glyph) { ret = load_glyph(ctx, &glyph, code); if (ret) return ret; } y_min = FFMIN(glyph->bbox.yMin, y_min); y_max = FFMAX(glyph->bbox.yMax, y_max); }
static av_cold int init(AVFilterContext *ctx) { int err; DrawTextContext *s = ctx->priv; Glyph *glyph; if ((err = parse_font(ctx)) < 0) return err; if (s->textfile) { uint8_t *textbuf; size_t textbuf_size; if (s->text) { av_log(ctx, AV_LOG_ERROR, "Both text and text file provided. Please provide only one\n"); return AVERROR(EINVAL); } if ((err = av_file_map(s->textfile, &textbuf, &textbuf_size, 0, ctx)) < 0) { av_log(ctx, AV_LOG_ERROR, "The text file '%s' could not be read or is empty\n", s->textfile); return err; } if (textbuf_size > SIZE_MAX - 1 || !(s->text = av_malloc(textbuf_size + 1))) { av_file_unmap(textbuf, textbuf_size); return AVERROR(ENOMEM); } memcpy(s->text, textbuf, textbuf_size); s->text[textbuf_size] = 0; av_file_unmap(textbuf, textbuf_size); } if (!s->text) { av_log(ctx, AV_LOG_ERROR, "Either text or a valid file must be provided\n"); return AVERROR(EINVAL); } if ((err = av_parse_color(s->fontcolor_rgba, s->fontcolor_string, -1, ctx))) { av_log(ctx, AV_LOG_ERROR, "Invalid font color '%s'\n", s->fontcolor_string); return err; } if ((err = av_parse_color(s->boxcolor_rgba, s->boxcolor_string, -1, ctx))) { av_log(ctx, AV_LOG_ERROR, "Invalid box color '%s'\n", s->boxcolor_string); return err; } if ((err = av_parse_color(s->shadowcolor_rgba, s->shadowcolor_string, -1, ctx))) { av_log(ctx, AV_LOG_ERROR, "Invalid shadow color '%s'\n", s->shadowcolor_string); return err; } if ((err = FT_Init_FreeType(&(s->library)))) { av_log(ctx, AV_LOG_ERROR, "Could not load FreeType: %s\n", FT_ERRMSG(err)); return AVERROR(EINVAL); } /* load the face, and set up the encoding, which is by default UTF-8 */ if ((err = FT_New_Face(s->library, s->fontfile, 0, &s->face))) { av_log(ctx, AV_LOG_ERROR, "Could not load fontface from file '%s': %s\n", s->fontfile, FT_ERRMSG(err)); return AVERROR(EINVAL); } if ((err = FT_Set_Pixel_Sizes(s->face, 0, s->fontsize))) { av_log(ctx, AV_LOG_ERROR, "Could not set font size to %d pixels: %s\n", s->fontsize, FT_ERRMSG(err)); return AVERROR(EINVAL); } s->use_kerning = FT_HAS_KERNING(s->face); /* load the fallback glyph with code 0 */ load_glyph(ctx, NULL, 0); /* set the tabsize in pixels */ if ((err = load_glyph(ctx, &glyph, ' ') < 0)) { av_log(ctx, AV_LOG_ERROR, "Could not set tabsize.\n"); return err; } s->tabsize *= glyph->advance; #if !HAVE_LOCALTIME_R av_log(ctx, AV_LOG_WARNING, "strftime() expansion unavailable!\n"); #endif return 0; }
void xd::font::render(const std::string& text, const font_style& style, xd::shader_program::handle shader, const glm::mat4& mvp, glm::vec2 *pos) { // check if we're rendering using this font or a linked font if (style.m_type && style.m_type->length() != 0) { font_map_t::iterator i = m_linked_fonts.find(*style.m_type); if (i == m_linked_fonts.end()) throw invalid_font_type(*style.m_type); font_style linked_style = style; linked_style.m_type = boost::none; i->second->render(text, linked_style, shader, mvp, pos); return; } // check if the font size is already loaded auto it = m_face->sizes.find(style.m_size); if (it == m_face->sizes.end()) { // create a new size FT_Size size; if (FT_New_Size(m_face->handle, &size) != 0) throw font_load_failed(m_filename); // free the size in catch block if something goes wrong try { if (FT_Activate_Size(size) != 0) throw font_load_failed(m_filename); // set the pixel size if (FT_Set_Pixel_Sizes(m_face->handle, 0, style.m_size) != 0) throw font_load_failed(m_filename); // pre-load 7-bit ASCII glyphs for (int i = 0; i < 128; i++) { load_glyph(i, style.m_size); } } catch (...) { // free the size and re-throw FT_Done_Size(size); throw; } // insert the newly loaded size in the map m_face->sizes.insert(std::make_pair(style.m_size, size)); } else { // activate the size FT_Activate_Size(it->second); } // bind to first texture unit glActiveTexture(GL_TEXTURE0); // setup the shader shader->use(); shader->bind_uniform(m_mvp_uniform, mvp); shader->bind_uniform(m_color_uniform, style.m_color); shader->bind_uniform(m_texture_uniform, 0); // is kerning supported FT_Bool kerning = FT_HAS_KERNING(m_face->handle); // render each glyph in the string glm::vec2 text_pos; if (pos) text_pos = *pos; FT_UInt prev_glyph_index = 0; auto i = text.begin(); while (i != text.end()) { // get the unicode code point utf8::uint32_t char_index = utf8::next(i, text.end()); // get the cached glyph, or cache if it is not yet cached const detail::font::glyph& glyph = load_glyph(char_index, style.m_size); // bind the texture glBindTexture(GL_TEXTURE_2D, glyph.texture_id); // check for kerning if (kerning && glyph.glyph_index && prev_glyph_index) { FT_Vector kerning_delta; FT_Get_Kerning(m_face->handle, prev_glyph_index, glyph.glyph_index, FT_KERNING_DEFAULT, &kerning_delta); text_pos.x += kerning_delta.x >> 6; } // calculate exact offset glm::vec2 glyph_pos = text_pos; glyph_pos.x += glyph.offset.x; glyph_pos.y += glyph.offset.y; // add optional letter spacing glyph_pos.x += style.m_letter_spacing/2; // if shadow is enabled, draw the shadow first if (style.m_shadow) { // calculate shadow position glm::vec2 shadow_pos = glyph_pos; shadow_pos.x += style.m_shadow->x; shadow_pos.y += style.m_shadow->y; // calculate shadow color glm::vec4 shadow_color = style.m_shadow->color; shadow_color.a *= style.m_color.a; // bind uniforms shader->bind_uniform(m_color_uniform, shadow_color); shader->bind_uniform(m_position_uniform, shadow_pos); // draw shadow glyph.quad_ptr->render(); // restore the text color shader->bind_uniform(m_color_uniform, style.m_color); } // if outline is enabled, draw outline if (style.m_outline) { // calculate outline color glm::vec4 outline_color = style.m_outline->color; outline_color.a *= style.m_color.a; // bind color shader->bind_uniform(m_color_uniform, outline_color); // draw font multiple times times to draw outline (EXPENSIVE!) for (int x = -style.m_outline->width; x <= style.m_outline->width; x++) { for (int y = -style.m_outline->width; y <= style.m_outline->width; y++) { if (x == 0 && y == 0) continue; shader->bind_uniform(m_position_uniform, glyph_pos + glm::vec2(x, y)); glyph.quad_ptr->render(); } } // restore the text color shader->bind_uniform(m_color_uniform, style.m_color); } // bind uniforms shader->bind_uniform(m_position_uniform, glyph_pos); // draw the glyph glyph.quad_ptr->render(); // advance the position text_pos += glyph.advance; text_pos.x += style.m_letter_spacing; // keep track of previous glyph to do kerning prev_glyph_index = glyph.glyph_index; }