Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Text cleanups #250

Merged
merged 13 commits into from
Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libnopegl/src/glsl/text_bg.vert
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@

void main()
{
ngl_out_pos = projection_matrix * modelview_matrix * vec4(position, 1.0);
ngl_out_pos = projection_matrix * modelview_matrix * vec4(position, 0.0, 1.0);
}
2 changes: 1 addition & 1 deletion libnopegl/src/glsl/text_chars.vert
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ void main()
*/
coords = atlas_coords;

vec4 position = user_transform * transform * vec4(ref_uv, 1.0, 1.0);
vec4 position = user_transform * vec4(transform.xy + ref_uv * transform.zw, 0.0, 1.0);
ngl_out_pos = projection_matrix * modelview_matrix * position;

color = frag_color;
Expand Down
198 changes: 55 additions & 143 deletions libnopegl/src/node_text.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,9 @@
#include <stddef.h>
#include <string.h>

#include "memory.h"
#include "internal.h"
#include "darray.h"
#include "gpu_ctx.h"
#include "log.h"
#include "math_utils.h"
#include "pgcache.h"
#include "pgcraft.h"
#include "pipeline_compat.h"
#include "text.h"
Expand Down Expand Up @@ -154,14 +150,11 @@ static const struct param_choices writing_mode_choices = {
}
};

#define SCALE_MODE_AUTO 0
#define SCALE_MODE_FIXED 1

static const struct param_choices scale_mode_choices = {
.name = "scale_mode",
.consts = {
{"auto", SCALE_MODE_AUTO, .desc=NGLI_DOCSTRING("automatic size by fitting the specified bounding box")},
{"fixed", SCALE_MODE_FIXED, .desc=NGLI_DOCSTRING("fixed character size (bounding box ignored for scaling)")},
{"auto", NGLI_TEXT_SCALE_MODE_AUTO, .desc=NGLI_DOCSTRING("automatic size by fitting the specified bounding box")},
{"fixed", NGLI_TEXT_SCALE_MODE_FIXED, .desc=NGLI_DOCSTRING("fixed character size (bounding box ignored for scaling)")},
{NULL}
}
};
Expand All @@ -181,15 +174,15 @@ static const struct node_param text_params[] = {
.desc=NGLI_DOCSTRING("text string to rasterize")},
{"live_id", NGLI_PARAM_TYPE_STR, OFFSET(live.id),
.desc=NGLI_DOCSTRING("live control identifier")},
{"fg_color", NGLI_PARAM_TYPE_VEC3, OFFSET(fg_color), {.vec={1.0, 1.0, 1.0}},
{"fg_color", NGLI_PARAM_TYPE_VEC3, OFFSET(fg_color), {.vec={1.f, 1.f, 1.f}},
.flags=NGLI_PARAM_FLAG_ALLOW_LIVE_CHANGE,
.update_func=set_live_changed,
.desc=NGLI_DOCSTRING("foreground text color")},
{"fg_opacity", NGLI_PARAM_TYPE_F32, OFFSET(fg_opacity), {.f32=1.f},
.flags=NGLI_PARAM_FLAG_ALLOW_LIVE_CHANGE,
.update_func=set_live_changed,
.desc=NGLI_DOCSTRING("foreground text opacity")},
{"bg_color", NGLI_PARAM_TYPE_VEC3, OFFSET(bg_color), {.vec={0.0, 0.0, 0.0}},
{"bg_color", NGLI_PARAM_TYPE_VEC3, OFFSET(bg_color), {.vec={0.f, 0.f, 0.f}},
.flags=NGLI_PARAM_FLAG_ALLOW_LIVE_CHANGE,
.desc=NGLI_DOCSTRING("background text color")},
{"bg_opacity", NGLI_PARAM_TYPE_F32, OFFSET(bg_opacity), {.f32=.8f},
Expand All @@ -208,7 +201,7 @@ static const struct node_param text_params[] = {
.desc=NGLI_DOCSTRING("resolution (dot per inch)")},
{"font_scale", NGLI_PARAM_TYPE_F32, OFFSET(font_scale), {.f32=1.f},
.desc=NGLI_DOCSTRING("scaling of the font")},
{"scale_mode", NGLI_PARAM_TYPE_SELECT, OFFSET(scale_mode), {.i32=SCALE_MODE_AUTO},
{"scale_mode", NGLI_PARAM_TYPE_SELECT, OFFSET(scale_mode), {.i32=NGLI_TEXT_SCALE_MODE_AUTO},
.choices=&scale_mode_choices,
.desc=NGLI_DOCSTRING("scaling behaviour for the characters")},
{"effects", NGLI_PARAM_TYPE_NODELIST, OFFSET(effect_nodes),
Expand Down Expand Up @@ -238,14 +231,12 @@ static void destroy_characters_resources(struct text_priv *s)
s->nb_chars = 0;
}

static int refresh_geometry(struct ngl_node *node)
static int refresh_pipeline_data(struct ngl_node *node)
{
int ret = 0;
struct ngl_ctx *ctx = node->ctx;
struct gpu_ctx *gpu_ctx = ctx->gpu_ctx;
struct text_priv *s = node->priv_data;
struct text_opts *o = node->opts;

struct text *text = s->text_ctx;

const size_t text_nbchr = ngli_darray_count(&text->chars);
Expand All @@ -254,125 +245,45 @@ static int refresh_geometry(struct ngl_node *node)
return 0;
}

float *transforms = ngli_calloc(text_nbchr, 4 * 4 * sizeof(*transforms));
float *atlas_coords = ngli_calloc(text_nbchr, 4 * sizeof(*atlas_coords));
if (!transforms || !atlas_coords) {
ret = NGL_ERROR_MEMORY;
goto end;
}

/* Text/Box ratio */
const struct ngli_box box = {NGLI_ARG_VEC4(o->box)};
const struct viewport viewport = ngli_gpu_ctx_get_viewport(ctx->gpu_ctx);
const int32_t ar[] = {viewport.width, viewport.height};
const float box_ratio = (float)ar[0] * box.w / ((float)ar[1] * box.h);
const float text_ratio = (float)text->width / (float)text->height;

/* Apply aspect ratio and font scaling */
float width = box.w * o->font_scale;
float height = box.h * o->font_scale;
if (o->scale_mode == SCALE_MODE_FIXED) {
const float tw = (float)text->width / (float)s->viewport.width;
const float th = (float)text->height / (float)s->viewport.height;
const float rw = tw / box.w;
const float rh = th / box.h;

width *= rw;
height *= rh;
} else {
float ratio_w, ratio_h;
if (text_ratio < box_ratio) {
ratio_w = text_ratio / box_ratio;
ratio_h = 1.0;
} else {
ratio_w = 1.0;
ratio_h = box_ratio / text_ratio;
}

width *= ratio_w;
height *= ratio_h;
}

/* Adjust text position according to alignment settings */
const float align_padw = box.w - width;
const float align_padh = box.h - height;

const float spx = (o->halign == NGLI_TEXT_HALIGN_CENTER ? .5f :
o->halign == NGLI_TEXT_HALIGN_RIGHT ? 1.f :
0.f);
const float spy = (o->valign == NGLI_TEXT_VALIGN_CENTER ? .5f :
o->valign == NGLI_TEXT_VALIGN_TOP ? 1.f :
0.f);

const float corner_x = box.x + align_padw * spx;
const float corner_y = box.y + align_padh * spy;

const struct char_info *chars = ngli_darray_data(&text->chars);
for (size_t n = 0; n < text_nbchr; n++) {
const struct char_info *chr = &chars[n];

/* character dimension and position */
const float chr_width = width * chr->geom.w;
const float chr_height = height * chr->geom.h;
const float chr_corner_x = corner_x + width * chr->geom.x;
const float chr_corner_y = corner_y + height * chr->geom.y;

/* deduce character transform from chr_{width,height,corner} */
const NGLI_ALIGNED_MAT(transform) = {
chr_width, 0, 0, 0,
0, chr_height, 0, 0,
chr_corner_x, chr_corner_y, 0, 0,
0, 0, 0, 1,
};
memcpy(transforms + 4 * 4 * n, transform, sizeof(transform));

/* register atlas identifier */
memcpy(atlas_coords + 4 * n, chr->atlas_coords, sizeof(chr->atlas_coords));
}

if (text_nbchr > s->nb_chars) { // need re-alloc
destroy_characters_resources(s);

/* The content of these buffers will remain constant until the next text content update */
s->transforms = ngli_buffer_create(gpu_ctx);
s->transforms = ngli_buffer_create(gpu_ctx);
s->atlas_coords = ngli_buffer_create(gpu_ctx);
if (!s->transforms || !s->atlas_coords) {
ret = NGL_ERROR_MEMORY;
goto end;
}
if (!s->transforms || !s->atlas_coords)
return NGL_ERROR_MEMORY;

/* The content of these buffers will be updated later using the effects data (see apply_effects()) */
s->user_transforms = ngli_buffer_create(gpu_ctx);
s->colors = ngli_buffer_create(gpu_ctx);
s->outlines = ngli_buffer_create(gpu_ctx);
s->glows = ngli_buffer_create(gpu_ctx);
s->blurs = ngli_buffer_create(gpu_ctx);
if (!s->user_transforms || !s->colors || !s->outlines || !s->glows || !s->blurs) {
ret = NGL_ERROR_MEMORY;
goto end;
}

if ((ret = ngli_buffer_init(s->transforms, text_nbchr * 4 * 4 * sizeof(*transforms), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->atlas_coords, text_nbchr * 4 * sizeof(*atlas_coords), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->user_transforms, text_nbchr * 4 * 4 * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->colors, text_nbchr * 4 * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->outlines, text_nbchr * 4 * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->glows, text_nbchr * 4 * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->blurs, text_nbchr * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0)
goto end;
if (!s->user_transforms || !s->colors || !s->outlines || !s->glows || !s->blurs)
return NGL_ERROR_MEMORY;

if ((ret = ngli_buffer_init(s->transforms, text_nbchr * 4 * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->atlas_coords, text_nbchr * 4 * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->user_transforms, text_nbchr * 4 * 4 * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->colors, text_nbchr * 4 * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->outlines, text_nbchr * 4 * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->glows, text_nbchr * 4 * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0 ||
(ret = ngli_buffer_init(s->blurs, text_nbchr * sizeof(float), DYNAMIC_VERTEX_USAGE_FLAGS)) < 0)
return ret;

struct pipeline_desc *descs = ngli_darray_data(&s->pipeline_descs);
for (size_t i = 0; i < ngli_darray_count(&s->pipeline_descs); i++) {
struct pipeline_desc_fg *desc_fg = &descs[i].fg;
struct pipeline_desc_common *desc = &desc_fg->common;

ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->transform_index, s->transforms);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->atlas_coords_index, s->atlas_coords);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->transform_index, s->transforms);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->atlas_coords_index, s->atlas_coords);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->user_transform_index, s->user_transforms);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->color_index, s->colors);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->outline_index, s->outlines);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->glow_index, s->glows);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->blur_index, s->blurs);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->color_index, s->colors);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->outline_index, s->outlines);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->glow_index, s->glows);
ngli_pipeline_compat_update_vertex_buffer(desc->pipeline_compat, desc_fg->blur_index, s->blurs);
}
}

Expand All @@ -383,20 +294,17 @@ static int refresh_geometry(struct ngl_node *node)
struct pipeline_desc_common *desc = &desc_fg->common;
int ret = ngli_pipeline_compat_update_texture(desc->pipeline_compat, 0, text->atlas_texture);
if (ret < 0)
goto end;
return ret;
}
}

if ((ret = ngli_buffer_upload(s->transforms, transforms, 0, text_nbchr * 4 * 4 * sizeof(*transforms))) < 0 ||
(ret = ngli_buffer_upload(s->atlas_coords, atlas_coords, 0, text_nbchr * 4 * sizeof(*atlas_coords))) < 0)
goto end;
if ((ret = ngli_buffer_upload(s->transforms, text->data_ptrs.pos_size, 0, text_nbchr * 4 * sizeof(float))) < 0 ||
(ret = ngli_buffer_upload(s->atlas_coords, text->data_ptrs.atlas_coords, 0, text_nbchr * 4 * sizeof(float))) < 0)
return ret;

s->nb_chars = text_nbchr;

end:
ngli_free(transforms);
ngli_free(atlas_coords);
return ret;
return 0;
}

static int update_text_content(struct ngl_node *node)
Expand All @@ -408,7 +316,7 @@ static int update_text_content(struct ngl_node *node)
if (ret < 0)
return ret;

return refresh_geometry(node);
return refresh_pipeline_data(node);
}

/* Update the GPU buffers using the updated effects data */
Expand All @@ -421,7 +329,7 @@ static int apply_effects(struct text_priv *s)
if (!text_nbchr)
return 0;

const struct text_effects_pointers *ptrs = &text->data_ptrs;
const struct text_data_pointers *ptrs = &text->data_ptrs;
if ((ret = ngli_buffer_upload(s->user_transforms, ptrs->transform, 0, text_nbchr * 4 * 4 * sizeof(*ptrs->transform))) < 0 ||
(ret = ngli_buffer_upload(s->colors, ptrs->color, 0, text_nbchr * 4 * sizeof(*ptrs->color))) < 0 ||
(ret = ngli_buffer_upload(s->outlines, ptrs->outline, 0, text_nbchr * 4 * sizeof(*ptrs->outline))) < 0 ||
Expand All @@ -441,10 +349,10 @@ static int init_bounding_box_geometry(struct ngl_node *node)

const struct ngli_box box = {NGLI_ARG_VEC4(o->box)};
const float vertices[] = {
box.x, box.y, 0,
box.x + box.w, box.y, 0,
box.x, box.y + box.h, 0,
box.x + box.w, box.y + box.h, 0,
box.x, box.y,
box.x + box.w, box.y,
box.x, box.y + box.h,
box.x + box.w, box.y + box.h,
};

s->bg_vertices = ngli_buffer_create(gpu_ctx);
Expand Down Expand Up @@ -476,9 +384,12 @@ static int text_init(struct ngl_node *node)
.pt_size = o->pt_size,
.dpi = o->dpi,
.padding = o->padding,
.scale_mode = o->scale_mode,
.font_scale = o->font_scale,
.valign = o->valign,
.halign = o->halign,
.writing_mode = o->writing_mode,
.box = {NGLI_ARG_VEC4(o->box)},
.effect_nodes = o->effect_nodes,
.nb_effect_nodes = o->nb_effect_nodes,
.defaults = {
Expand Down Expand Up @@ -543,7 +454,7 @@ static int init_subdesc(struct ngl_node *node,
if (ret < 0)
return ret;

desc->modelview_matrix_index = ngli_pgcraft_get_uniform_index(desc->crafter, "modelview_matrix", NGLI_PROGRAM_SHADER_VERT);
desc->modelview_matrix_index = ngli_pgcraft_get_uniform_index(desc->crafter, "modelview_matrix", NGLI_PROGRAM_SHADER_VERT);
desc->projection_matrix_index = ngli_pgcraft_get_uniform_index(desc->crafter, "projection_matrix", NGLI_PROGRAM_SHADER_VERT);

return 0;
Expand All @@ -566,9 +477,9 @@ static int bg_prepare(struct ngl_node *node, struct pipeline_desc_bg *desc)
const struct pgcraft_attribute attributes[] = {
{
.name = "position",
.type = NGLI_TYPE_VEC3,
.format = NGLI_FORMAT_R32G32B32_SFLOAT,
.stride = 3 * sizeof(float),
.type = NGLI_TYPE_VEC2,
.format = NGLI_FORMAT_R32G32_SFLOAT,
.stride = 2 * sizeof(float),
.buffer = s->bg_vertices,
},
};
Expand All @@ -595,7 +506,7 @@ static int bg_prepare(struct ngl_node *node, struct pipeline_desc_bg *desc)
if (ret < 0)
return ret;

desc->color_index = ngli_pgcraft_get_uniform_index(desc->common.crafter, "color", NGLI_PROGRAM_SHADER_FRAG);
desc->color_index = ngli_pgcraft_get_uniform_index(desc->common.crafter, "color", NGLI_PROGRAM_SHADER_FRAG);
desc->opacity_index = ngli_pgcraft_get_uniform_index(desc->common.crafter, "opacity", NGLI_PROGRAM_SHADER_FRAG);

return 0;
Expand Down Expand Up @@ -624,9 +535,9 @@ static int fg_prepare(struct ngl_node *node, struct pipeline_desc_fg *desc)
const struct pgcraft_attribute attributes[] = {
{
.name = "transform",
.type = NGLI_TYPE_MAT4,
.type = NGLI_TYPE_VEC4,
.format = NGLI_FORMAT_R32G32B32A32_SFLOAT,
.stride = 4 * 4 * sizeof(float),
.stride = 4 * sizeof(float),
.buffer = s->transforms,
.rate = 1,
}, {
Expand Down Expand Up @@ -709,13 +620,13 @@ static int fg_prepare(struct ngl_node *node, struct pipeline_desc_fg *desc)
if (ret < 0)
return ret;

desc->transform_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "transform");
desc->atlas_coords_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "atlas_coords");
desc->transform_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "transform");
desc->atlas_coords_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "atlas_coords");
desc->user_transform_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "user_transform");
desc->color_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "frag_color");
desc->outline_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "frag_outline");
desc->glow_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "frag_glow");
desc->blur_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "frag_blur");
desc->color_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "frag_color");
desc->outline_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "frag_outline");
desc->glow_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "frag_glow");
desc->blur_index = ngli_pgcraft_get_vertex_buffer_index(desc->common.crafter, "frag_blur");

return 0;
}
Expand Down Expand Up @@ -764,7 +675,8 @@ static int text_update(struct ngl_node *node, double t)
const struct viewport viewport = ngli_gpu_ctx_get_viewport(node->ctx->gpu_ctx);
if (memcmp(&s->viewport, &viewport, sizeof(viewport))) {
memcpy(&s->viewport, &viewport, sizeof(viewport));
int ret = refresh_geometry(node);
ngli_text_refresh_geometry_data(s->text_ctx);
int ret = refresh_pipeline_data(node);
if (ret < 0)
return ret;
}
Expand Down
Loading
Loading