BLF: Support All Render and Bitmap Formats

Add support for all of FreeType's various render formats and output
bitmap formats.

Pull Request: https://projects.blender.org/blender/blender/pulls/115452
This commit is contained in:
Harley Acheson
2023-11-30 22:17:30 +01:00
committed by Harley Acheson
parent abf59eb23a
commit 5d330ddb1f
6 changed files with 182 additions and 79 deletions

View File

@@ -193,6 +193,10 @@ static void blf_batch_draw_init()
g_batch.offset_loc = GPU_vertformat_attr_add(&format, "offset", GPU_COMP_I32, 1, GPU_FETCH_INT);
g_batch.glyph_size_loc = GPU_vertformat_attr_add(
&format, "glyph_size", GPU_COMP_I32, 2, GPU_FETCH_INT);
g_batch.glyph_depth_loc = GPU_vertformat_attr_add(
&format, "depth", GPU_COMP_I32, 1, GPU_FETCH_INT);
g_batch.glyph_mode_loc = GPU_vertformat_attr_add(
&format, "mode", GPU_COMP_I32, 1, GPU_FETCH_INT);
g_batch.verts = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_STREAM);
GPU_vertbuf_data_alloc(g_batch.verts, BLF_BATCH_DRAW_LEN_MAX);
@@ -201,6 +205,8 @@ static void blf_batch_draw_init()
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.col_loc, &g_batch.col_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.offset_loc, &g_batch.offset_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.glyph_size_loc, &g_batch.glyph_size_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.glyph_depth_loc, &g_batch.glyph_depth_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.glyph_mode_loc, &g_batch.glyph_mode_step);
g_batch.glyph_len = 0;
/* A dummy VBO containing 4 points, attributes are not used. */
@@ -347,6 +353,8 @@ void blf_batch_draw()
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.col_loc, &g_batch.col_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.offset_loc, &g_batch.offset_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.glyph_size_loc, &g_batch.glyph_size_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.glyph_depth_loc, &g_batch.glyph_depth_step);
GPU_vertbuf_attr_get_raw_data(g_batch.verts, g_batch.glyph_mode_loc, &g_batch.glyph_mode_step);
g_batch.glyph_len = 0;
}

View File

@@ -243,11 +243,6 @@ static GlyphBLF *blf_glyph_cache_add_glyph(FontBLF *font,
g->c = charcode;
g->idx = glyph_index;
g->advance_x = (ft_pix)glyph->advance.x;
g->pos[0] = glyph->bitmap_left;
g->pos[1] = glyph->bitmap_top;
g->dims[0] = int(glyph->bitmap.width);
g->dims[1] = int(glyph->bitmap.rows);
g->pitch = glyph->bitmap.pitch;
g->subpixel = subpixel;
FT_BBox bbox;
@@ -261,24 +256,99 @@ static GlyphBLF *blf_glyph_cache_add_glyph(FontBLF *font,
g->lsb_delta = (ft_pix)glyph->lsb_delta;
g->rsb_delta = (ft_pix)glyph->rsb_delta;
const int buffer_size = int(glyph->bitmap.width * glyph->bitmap.rows);
if (buffer_size != 0) {
if (font->flags & BLF_MONOCHROME) {
/* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range. */
if (font->flags & BLF_MONOCHROME) {
g->render_mode = FT_RENDER_MODE_MONO;
}
else if (font->flags & BLF_HINTING_SLIGHT) {
g->render_mode = FT_RENDER_MODE_LIGHT;
}
else {
g->render_mode = FT_RENDER_MODE_NORMAL;
}
if (glyph->format == FT_GLYPH_FORMAT_BITMAP) {
/* This has been rendered and we have a bitmap. */
g->pos[0] = glyph->bitmap_left;
g->pos[1] = glyph->bitmap_top;
g->dims[0] = int(glyph->bitmap.width);
g->dims[1] = int(glyph->bitmap.rows);
g->pitch = glyph->bitmap.pitch;
g->depth = 1;
switch (glyph->bitmap.pixel_mode) {
case FT_PIXEL_MODE_LCD:
g->depth = 3;
g->dims[0] /= 3;
break;
case FT_PIXEL_MODE_LCD_V:
g->depth = 3;
g->dims[1] /= 3;
g->pitch *= 3;
break;
case FT_PIXEL_MODE_BGRA:
g->depth = 4;
break;
}
const int buffer_size = g->dims[0] * g->dims[1] * g->depth;
g->bitmap = static_cast<uchar *>(MEM_mallocN(size_t(buffer_size), "glyph bitmap"));
if (ELEM(glyph->bitmap.pixel_mode,
FT_PIXEL_MODE_GRAY,
FT_PIXEL_MODE_GRAY2,
FT_PIXEL_MODE_GRAY4)) {
/* Scale 1, 2, 4-bit gray to 8-bit. */
const char scale = char(255 / (glyph->bitmap.num_grays - 1));
for (int i = 0; i < buffer_size; i++) {
glyph->bitmap.buffer[i] = glyph->bitmap.buffer[i] ? 255 : 0;
#ifdef BLF_GAMMA_CORRECT_GLYPHS
/* Convert coverage amounts to perceptually-improved lightness values. */
g->bitmap[i] = blf_glyph_gamma(glyph->bitmap.buffer[i] * scale);
#else
g->bitmap[i] = glyph->bitmap.buffer[i] * scale;
#endif /* BLF_GAMMA_CORRECT_GLYPHS */
}
}
else if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD) {
/* RGB (BGR) in successive columns. */
for (size_t y = 0; y < size_t(g->dims[1]); y++) {
for (size_t x = 0; x < size_t(g->dims[0]); x++) {
size_t offs_in = (y * size_t(glyph->bitmap.pitch)) + (x * size_t(g->depth));
size_t offs_out = (y * size_t(g->dims[0]) * size_t(g->depth)) + (x * size_t(g->depth));
g->bitmap[offs_out + 0] = glyph->bitmap.buffer[offs_in + 2];
g->bitmap[offs_out + 1] = glyph->bitmap.buffer[offs_in + 1];
g->bitmap[offs_out + 2] = glyph->bitmap.buffer[offs_in + 0];
}
}
}
else if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_LCD_V) {
/* RGB (BGR) in successive ROWS. */
for (size_t y = 0; y < size_t(g->dims[1]); y++) {
for (size_t x = 0; x < size_t(g->dims[0]); x++) {
size_t offs_in = (y * size_t(glyph->bitmap.pitch) * size_t(g->depth)) + x;
size_t offs_out = (y * size_t(g->dims[0]) * size_t(g->depth)) + (x * size_t(g->depth));
g->bitmap[offs_out + 2] = glyph->bitmap.buffer[offs_in];
g->bitmap[offs_out + 1] = glyph->bitmap.buffer[offs_in + size_t(glyph->bitmap.pitch)];
g->bitmap[offs_out + 0] = glyph->bitmap.buffer[offs_in + size_t(glyph->bitmap.pitch) +
size_t(glyph->bitmap.pitch)];
}
}
}
else if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
/* Convert from BGRA to RGBA. */
for (size_t y = 0; y < size_t(g->dims[1]); y++) {
for (size_t x = 0; x < size_t(g->dims[0]); x++) {
size_t offs_in = (y * size_t(g->pitch)) + (x * size_t(g->depth));
size_t offs_out = (y * size_t(g->dims[0]) * size_t(g->depth)) + (x * size_t(g->depth));
g->bitmap[offs_out + 0] = glyph->bitmap.buffer[offs_in + 2];
g->bitmap[offs_out + 1] = glyph->bitmap.buffer[offs_in + 1];
g->bitmap[offs_out + 2] = glyph->bitmap.buffer[offs_in + 0];
g->bitmap[offs_out + 3] = glyph->bitmap.buffer[offs_in + 3];
}
}
}
else {
#ifdef BLF_GAMMA_CORRECT_GLYPHS
/* Convert coverage amounts to perceptually-improved lightness values. */
for (int i = 0; i < buffer_size; i++) {
glyph->bitmap.buffer[i] = blf_glyph_gamma(glyph->bitmap.buffer[i]);
}
#endif /* BLF_GAMMA_CORRECT_GLYPHS */
memcpy(g->bitmap, glyph->bitmap.buffer, (size_t)buffer_size);
}
g->bitmap = static_cast<uchar *>(MEM_mallocN(size_t(buffer_size), "glyph bitmap"));
memcpy(g->bitmap, glyph->bitmap.buffer, size_t(buffer_size));
}
BLI_addhead(&(gc->bucket[blf_hash(g->c << 6 | subpixel)]), g);
@@ -747,6 +817,10 @@ static FT_GlyphSlot blf_glyph_load(FontBLF *font, FT_UInt glyph_index, bool outl
}
}
if (!outline_only && FT_HAS_COLOR(font->face)) {
load_flags |= FT_LOAD_COLOR;
}
if (FT_Load_Glyph(font->face, glyph_index, load_flags) == FT_Err_Ok) {
return font->face->glyph;
}
@@ -764,8 +838,17 @@ static FT_GlyphSlot blf_glyph_load(FontBLF *font, FT_UInt glyph_index, bool outl
*/
static bool blf_glyph_render_bitmap(FontBLF *font, FT_GlyphSlot glyph)
{
const int render_mode = (font->flags & BLF_MONOCHROME) ? FT_RENDER_MODE_MONO :
FT_RENDER_MODE_NORMAL;
int render_mode;
if (font->flags & BLF_MONOCHROME) {
render_mode = FT_RENDER_MODE_MONO;
}
else if (font->flags & BLF_HINTING_SLIGHT) {
render_mode = FT_RENDER_MODE_LIGHT;
}
else {
render_mode = FT_RENDER_MODE_NORMAL;
}
/* Render the glyph curves to a bitmap. */
FT_Error err = FT_Render_Glyph(glyph, FT_Render_Mode(render_mode));
@@ -773,11 +856,10 @@ static bool blf_glyph_render_bitmap(FontBLF *font, FT_GlyphSlot glyph)
return false;
}
FT_Bitmap tempbitmap;
if (font->flags & BLF_MONOCHROME) {
/* Convert result from 1 bit per pixel to 8 bit per pixel */
/* Accumulate errors for later, fine if not interested beyond "ok vs any error" */
if (ELEM(glyph->bitmap.pixel_mode, FT_PIXEL_MODE_MONO, FT_PIXEL_MODE_GRAY2, FT_PIXEL_MODE_GRAY4))
{
/* Convert to 8 bit per pixel */
FT_Bitmap tempbitmap;
FT_Bitmap_New(&tempbitmap);
/* Does Blender use Pitch 1 always? It works so far */
@@ -786,11 +868,7 @@ static bool blf_glyph_render_bitmap(FontBLF *font, FT_GlyphSlot glyph)
err += FT_Bitmap_Done(font->ft_lib, &tempbitmap);
}
if (err || glyph->format != FT_GLYPH_FORMAT_BITMAP) {
return false;
}
return true;
return (err == FT_Err_Ok);
}
/** \} */
@@ -1308,9 +1386,9 @@ static void blf_glyph_calc_rect_shadow(
/** \name Glyph Drawing
* \{ */
static void blf_texture_draw(const uchar color[4],
static void blf_texture_draw(GlyphBLF *g,
const uchar color[4],
const int glyph_size[2],
const int offset,
const int x1,
const int y1,
const int x2,
@@ -1325,7 +1403,9 @@ static void blf_texture_draw(const uchar color[4],
float(y2 + g_batch.ofs[1]));
copy_v4_v4_uchar(static_cast<uchar *>(GPU_vertbuf_raw_step(&g_batch.col_step)), color);
copy_v2_v2_int(static_cast<int *>(GPU_vertbuf_raw_step(&g_batch.glyph_size_step)), glyph_size);
*((int *)GPU_vertbuf_raw_step(&g_batch.offset_step)) = offset;
*((int *)GPU_vertbuf_raw_step(&g_batch.offset_step)) = g->offset;
*((int *)GPU_vertbuf_raw_step(&g_batch.glyph_depth_step)) = g->depth;
*((int *)GPU_vertbuf_raw_step(&g_batch.glyph_mode_step)) = g->render_mode;
g_batch.glyph_len++;
/* Flush cache if it's full. */
@@ -1334,36 +1414,26 @@ static void blf_texture_draw(const uchar color[4],
}
}
static void blf_texture5_draw(const uchar color_in[4],
const int glyph_size[2],
const int offset,
const int x1,
const int y1,
const int x2,
const int y2)
static void blf_texture5_draw(
GlyphBLF *g, const uchar color_in[4], const int x1, const int y1, const int x2, const int y2)
{
int glyph_size_flag[2];
/* flag the x and y component signs for 5x5 blurring */
glyph_size_flag[0] = -glyph_size[0];
glyph_size_flag[1] = -glyph_size[1];
glyph_size_flag[0] = -g->dims[0];
glyph_size_flag[1] = -g->dims[1];
blf_texture_draw(color_in, glyph_size_flag, offset, x1, y1, x2, y2);
blf_texture_draw(g, color_in, glyph_size_flag, x1, y1, x2, y2);
}
static void blf_texture3_draw(const uchar color_in[4],
const int glyph_size[2],
const int offset,
const int x1,
const int y1,
const int x2,
const int y2)
static void blf_texture3_draw(
GlyphBLF *g, const uchar color_in[4], const int x1, const int y1, const int x2, const int y2)
{
int glyph_size_flag[2];
/* flag the x component sign for 3x3 blurring */
glyph_size_flag[0] = -glyph_size[0];
glyph_size_flag[1] = glyph_size[1];
glyph_size_flag[0] = -g->dims[0];
glyph_size_flag[1] = g->dims[1];
blf_texture_draw(color_in, glyph_size_flag, offset, x1, y1, x2, y2);
blf_texture_draw(g, color_in, glyph_size_flag, x1, y1, x2, y2);
}
void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, const int x, const int y)
@@ -1379,7 +1449,7 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, const int x,
g->offset = gc->bitmap_len;
int buff_size = g->dims[0] * g->dims[1];
int buff_size = g->dims[0] * g->dims[1] * g->depth;
int bitmap_len = gc->bitmap_len + buff_size;
if (bitmap_len > gc->bitmap_len_alloc) {
@@ -1436,31 +1506,21 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, const int x,
blf_glyph_calc_rect_shadow(&rect_ofs, g, x, y, font);
if (font->shadow == 0) {
blf_texture_draw(font->shadow_color,
blf_texture_draw(g,
font->shadow_color,
g->dims,
g->offset,
rect_ofs.xmin,
rect_ofs.ymin,
rect_ofs.xmax,
rect_ofs.ymax);
}
else if (font->shadow <= 4) {
blf_texture3_draw(font->shadow_color,
g->dims,
g->offset,
rect_ofs.xmin,
rect_ofs.ymin,
rect_ofs.xmax,
rect_ofs.ymax);
blf_texture3_draw(
g, font->shadow_color, rect_ofs.xmin, rect_ofs.ymin, rect_ofs.xmax, rect_ofs.ymax);
}
else {
blf_texture5_draw(font->shadow_color,
g->dims,
g->offset,
rect_ofs.xmin,
rect_ofs.ymin,
rect_ofs.xmax,
rect_ofs.ymax);
blf_texture5_draw(
g, font->shadow_color, rect_ofs.xmin, rect_ofs.ymin, rect_ofs.xmax, rect_ofs.ymax);
}
}
@@ -1470,19 +1530,16 @@ void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, const int x,
#if BLF_BLUR_ENABLE
switch (font->blur) {
case 3:
blf_texture3_draw(
font->color, g->dims, g->offset, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
blf_texture3_draw(g, font->color, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
break;
case 5:
blf_texture5_draw(
font->color, g->dims, g->offset, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
blf_texture5_draw(g, font->color, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
break;
default:
blf_texture_draw(
font->color, g->dims, g->offset, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
blf_texture_draw(g, font->color, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
}
#else
blf_texture_draw(font->color, g->dims, g->offset, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
blf_texture_draw(g, font->color, g->dims, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
#endif
}

View File

@@ -88,8 +88,9 @@ typedef struct BatchBLF {
struct FontBLF *font;
struct GPUBatch *batch;
struct GPUVertBuf *verts;
struct GPUVertBufRaw pos_step, col_step, offset_step, glyph_size_step;
unsigned int pos_loc, col_loc, offset_loc, glyph_size_loc;
struct GPUVertBufRaw pos_step, col_step, offset_step, glyph_size_step, glyph_depth_step,
glyph_mode_step;
unsigned int pos_loc, col_loc, offset_loc, glyph_size_loc, glyph_depth_loc, glyph_mode_loc;
unsigned int glyph_len;
/** Copy of `font->pos`. */
int ofs[2];
@@ -174,6 +175,10 @@ typedef struct GlyphBLF {
/** Glyph width and height. */
int dims[2];
int pitch;
int depth;
/** Render mode (FT_Render_Mode). */
int render_mode;
/**
* X and Y bearing of the glyph.

View File

@@ -62,8 +62,35 @@ float texture_1D_custom_bilinear_filter(vec2 uv)
#endif
}
vec4 texture_1D_custom_bilinear_filter_color(vec2 uv)
{
vec2 texel_2d = uv * glyph_dim + 0.5;
ivec2 texel_2d_near = ivec2(texel_2d) - 1;
int frag_offset = glyph_offset + ((texel_2d_near.y * glyph_dim.x * glyph_depth) +
(texel_2d_near.x * glyph_depth));
float tr = 0.0;
float tg = 0.0;
float tb = 0.0;
float ta = 0.0;
if (is_inside_box(texel_2d_near)) {
tr = texel_fetch(frag_offset);
tg = texel_fetch(frag_offset + 1);
tb = texel_fetch(frag_offset + 2);
ta = texel_fetch(frag_offset + 3);
}
return vec4(tr, tg, tb, ta);
}
void main()
{
if (glyph_depth == 4) {
fragColor.rgba = texture_1D_custom_bilinear_filter_color(texCoord_interp).rgba;
return;
}
// input color replaces texture color
fragColor.rgb = color_flat.rgb;

View File

@@ -7,6 +7,8 @@ void main()
color_flat = col;
glyph_offset = offset;
glyph_dim = abs(glyph_size);
glyph_mode = mode;
glyph_depth = depth;
interp_size = int(glyph_size.x < 0) + int(glyph_size.y < 0);
/* Quad expansion using instanced rendering. */

View File

@@ -12,6 +12,8 @@ GPU_SHADER_INTERFACE_INFO(text_iface, "")
.flat(Type::VEC4, "color_flat")
.no_perspective(Type::VEC2, "texCoord_interp")
.flat(Type::INT, "glyph_offset")
.flat(Type::INT, "glyph_depth")
.flat(Type::INT, "glyph_mode")
.flat(Type::IVEC2, "glyph_dim")
.flat(Type::INT, "interp_size");
@@ -20,6 +22,8 @@ GPU_SHADER_CREATE_INFO(gpu_shader_text)
.vertex_in(1, Type::VEC4, "col")
.vertex_in(2, Type ::IVEC2, "glyph_size")
.vertex_in(3, Type ::INT, "offset")
.vertex_in(4, Type ::INT, "depth")
.vertex_in(5, Type ::INT, "mode")
.vertex_out(text_iface)
.fragment_out(0, Type::VEC4, "fragColor")
.push_constant(Type::MAT4, "ModelViewProjectionMatrix")