text: gltex: add OpenGL text renderer
This adds an OpenGL text renderer. It uses textures to store glyph information and renders the characters by assembling a vertex-list first. To improve performance, we use texture-atlases. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
parent
ac3fa529dc
commit
df84e9f217
@ -88,7 +88,9 @@ SHADERS = \
|
||||
src/static_blend.vert \
|
||||
src/static_blend.frag \
|
||||
src/static_blit.vert \
|
||||
src/static_blit.frag
|
||||
src/static_blit.frag \
|
||||
src/static_gltex.vert \
|
||||
src/static_gltex.frag
|
||||
|
||||
EXTRA_DIST += \
|
||||
$(SHADERS)
|
||||
@ -142,6 +144,11 @@ libkmscon_core_la_SOURCES += \
|
||||
src/text_bblit.c
|
||||
endif
|
||||
|
||||
if KMSCON_HAVE_GLES2
|
||||
libkmscon_core_la_SOURCES += \
|
||||
src/text_gltex.c
|
||||
endif
|
||||
|
||||
libkmscon_core_la_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(GLES2_CFLAGS) \
|
||||
|
12
configure.ac
12
configure.ac
@ -459,6 +459,17 @@ fi
|
||||
|
||||
AM_CONDITIONAL([KMSCON_HAVE_BBLIT], [test x$bblit_enabled = xyes])
|
||||
|
||||
#
|
||||
# OpenGL Texture rendering backend
|
||||
# This is not really an option but automatically enabled if OpenGLES2 support
|
||||
# was selected.
|
||||
#
|
||||
|
||||
gltex_enabled=no
|
||||
if test x$gles2_enabled = xyes ; then
|
||||
gltex_enabled=yes
|
||||
fi
|
||||
|
||||
#
|
||||
# Makefile vars
|
||||
# After everything is configured, we correctly substitute the values for the
|
||||
@ -518,5 +529,6 @@ AC_MSG_NOTICE([Build configuration:
|
||||
|
||||
rendering backends:
|
||||
bblit: $bblit_enabled
|
||||
gltex: $gltex_enabled
|
||||
|
||||
Run "make" to start compilation process])
|
||||
|
@ -338,6 +338,7 @@ int main(int argc, char **argv)
|
||||
kmscon_font_pango_load();
|
||||
kmscon_font_freetype2_load();
|
||||
kmscon_text_bblit_load();
|
||||
kmscon_text_gltex_load();
|
||||
|
||||
memset(&app, 0, sizeof(app));
|
||||
ret = setup_app(&app);
|
||||
@ -367,6 +368,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
destroy_app(&app);
|
||||
kmscon_text_gltex_unload();
|
||||
kmscon_text_bblit_unload();
|
||||
kmscon_font_freetype2_unload();
|
||||
kmscon_font_pango_unload();
|
||||
@ -377,6 +379,7 @@ int main(int argc, char **argv)
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
err_unload:
|
||||
kmscon_text_gltex_unload();
|
||||
kmscon_text_bblit_unload();
|
||||
kmscon_font_freetype2_unload();
|
||||
kmscon_font_pango_unload();
|
||||
|
47
src/static_gltex.frag
Normal file
47
src/static_gltex.frag
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* kmscon - Fragment Shader
|
||||
*
|
||||
* Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com>
|
||||
* Copyright (c) 2011 University of Tuebingen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Fragement Shader
|
||||
* A basic fragment shader which applies a 2D texture and blends foreground and
|
||||
* background colors.
|
||||
*/
|
||||
|
||||
uniform sampler2D atlas;
|
||||
uniform float advance_htex;
|
||||
uniform float advance_vtex;
|
||||
|
||||
varying vec2 texpos;
|
||||
varying vec3 fgcol;
|
||||
varying vec3 bgcol;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 pos = vec2(texpos.x * advance_htex, texpos.y * advance_vtex);
|
||||
float alpha = texture2D(atlas, pos).a;
|
||||
vec3 val = alpha * fgcol + (1.0 - alpha) * bgcol;
|
||||
gl_FragColor = vec4(val, 1.0);
|
||||
}
|
50
src/static_gltex.vert
Normal file
50
src/static_gltex.vert
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* kmscon - Vertex Shader
|
||||
*
|
||||
* Copyright (c) 2011-2012 David Herrmann <dh.herrmann@googlemail.com>
|
||||
* Copyright (c) 2011 University of Tuebingen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Vertex Shader
|
||||
* This shader is a very basic vertex shader which forwards all data and
|
||||
* performs basic matrix multiplications.
|
||||
*/
|
||||
|
||||
uniform mat4 projection;
|
||||
|
||||
attribute vec2 position;
|
||||
attribute vec2 texture_position;
|
||||
attribute vec3 fgcolor;
|
||||
attribute vec3 bgcolor;
|
||||
|
||||
varying vec2 texpos;
|
||||
varying vec3 fgcol;
|
||||
varying vec3 bgcol;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * vec4(position, 0.0, 1.0);
|
||||
texpos = texture_position;
|
||||
fgcol = fgcolor;
|
||||
bgcol = bgcolor;
|
||||
}
|
18
src/text.h
18
src/text.h
@ -251,4 +251,22 @@ static inline void kmscon_text_bblit_unload(void)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef KMSCON_HAVE_GLES2
|
||||
|
||||
int kmscon_text_gltex_load(void);
|
||||
void kmscon_text_gltex_unload(void);
|
||||
|
||||
#else
|
||||
|
||||
static inline int kmscon_text_gltex_load(void)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void kmscon_text_gltex_unload(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* KMSCON_TEXT_H */
|
||||
|
627
src/text_gltex.c
Normal file
627
src/text_gltex.c
Normal file
@ -0,0 +1,627 @@
|
||||
/*
|
||||
* kmscon - OpenGL Textures Text Renderer Backend
|
||||
*
|
||||
* Copyright (c) 2012 David Herrmann <dh.herrmann@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:text_gltex.c
|
||||
* @short_description: OpenGL Textures Text Renderer Backend
|
||||
* @include: text.h
|
||||
*
|
||||
* Uses OpenGL textures to store glyph information and draws these textures with
|
||||
* a custom fragment shader.
|
||||
* Glyphs are stored in texture-atlases. OpenGL has heavy restrictions on
|
||||
* texture sizes so we need to use multiple atlases. As there is no way to pass
|
||||
* a varying amount of textures to a shader, we need to render the screen for
|
||||
* each atlas we have.
|
||||
*/
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
|
||||
#include <errno.h>
|
||||
#include <GLES2/gl2.h>
|
||||
#include <GLES2/gl2ext.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "log.h"
|
||||
#include "static_gl.h"
|
||||
#include "static_misc.h"
|
||||
#include "text.h"
|
||||
#include "unicode.h"
|
||||
#include "uterm.h"
|
||||
|
||||
#define LOG_SUBSYSTEM "text_gltex"
|
||||
|
||||
struct atlas {
|
||||
struct kmscon_dlist list;
|
||||
|
||||
GLuint tex;
|
||||
unsigned int height;
|
||||
unsigned int width;
|
||||
unsigned int count;
|
||||
unsigned int fill;
|
||||
|
||||
unsigned int cache_size;
|
||||
unsigned int cache_num;
|
||||
GLfloat *cache_pos;
|
||||
GLfloat *cache_texpos;
|
||||
GLfloat *cache_fgcol;
|
||||
GLfloat *cache_bgcol;
|
||||
|
||||
GLfloat advance_htex;
|
||||
GLfloat advance_vtex;
|
||||
};
|
||||
|
||||
struct glyph {
|
||||
const struct kmscon_glyph *glyph;
|
||||
struct atlas *atlas;
|
||||
unsigned int texoff;
|
||||
};
|
||||
|
||||
#define GLYPH_WIDTH(gly) ((gly)->glyph->buf.width)
|
||||
#define GLYPH_HEIGHT(gly) ((gly)->glyph->buf.height)
|
||||
#define GLYPH_STRIDE(gly) ((gly)->glyph->buf.stride)
|
||||
#define GLYPH_DATA(gly) ((gly)->glyph->buf.data)
|
||||
|
||||
struct gltex {
|
||||
struct kmscon_hashtable *glyphs;
|
||||
unsigned int max_tex_size;
|
||||
|
||||
struct kmscon_dlist atlases;
|
||||
|
||||
GLfloat advance_x;
|
||||
GLfloat advance_y;
|
||||
|
||||
struct gl_shader *shader;
|
||||
GLuint uni_proj;
|
||||
GLuint uni_atlas;
|
||||
GLuint uni_advance_htex;
|
||||
GLuint uni_advance_vtex;
|
||||
};
|
||||
|
||||
#define FONT_WIDTH(txt) ((txt)->font->attr.width)
|
||||
#define FONT_HEIGHT(txt) ((txt)->font->attr.height)
|
||||
|
||||
#define SCREEN_WIDTH(txt) uterm_screen_width((txt)->screen)
|
||||
#define SCREEN_HEIGHT(txt) uterm_screen_height((txt)->screen)
|
||||
|
||||
static int gltex_init(struct kmscon_text *txt)
|
||||
{
|
||||
struct gltex *gt;
|
||||
|
||||
gt = malloc(sizeof(*gt));
|
||||
if (!gt)
|
||||
return -ENOMEM;
|
||||
|
||||
txt->data = gt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gltex_destroy(struct kmscon_text *txt)
|
||||
{
|
||||
struct gltex *gt = txt->data;
|
||||
|
||||
free(gt);
|
||||
}
|
||||
|
||||
static void free_glyph(void *data)
|
||||
{
|
||||
struct glyph *glyph = data;
|
||||
|
||||
free(glyph);
|
||||
}
|
||||
|
||||
extern const char *gl_static_gltex_vert;
|
||||
extern const char *gl_static_gltex_frag;
|
||||
|
||||
static int gltex_set(struct kmscon_text *txt)
|
||||
{
|
||||
struct gltex *gt = txt->data;
|
||||
int ret;
|
||||
static char *attr[] = { "position", "texture_position",
|
||||
"fgcolor", "bgcolor" };
|
||||
unsigned int sw, sh;
|
||||
GLint s;
|
||||
|
||||
memset(gt, 0, sizeof(*gt));
|
||||
kmscon_dlist_init(>->atlases);
|
||||
|
||||
ret = kmscon_hashtable_new(>->glyphs, kmscon_direct_hash,
|
||||
kmscon_direct_equal, NULL,
|
||||
free_glyph);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = uterm_screen_use(txt->screen);
|
||||
if (ret)
|
||||
goto err_htable;
|
||||
|
||||
gl_clear_error();
|
||||
|
||||
ret = gl_shader_new(>->shader, gl_static_gltex_vert,
|
||||
gl_static_gltex_frag, attr, 4, log_llog);
|
||||
if (ret)
|
||||
goto err_htable;
|
||||
|
||||
gt->uni_proj = gl_shader_get_uniform(gt->shader, "projection");
|
||||
gt->uni_atlas = gl_shader_get_uniform(gt->shader, "atlas");
|
||||
gt->uni_advance_htex = gl_shader_get_uniform(gt->shader,
|
||||
"advance_htex");
|
||||
gt->uni_advance_vtex = gl_shader_get_uniform(gt->shader,
|
||||
"advance_vtex");
|
||||
|
||||
if (gl_has_error(gt->shader)) {
|
||||
log_warning("cannot create shader");
|
||||
goto err_shader;
|
||||
}
|
||||
|
||||
sw = SCREEN_WIDTH(txt);
|
||||
sh = SCREEN_HEIGHT(txt);
|
||||
|
||||
txt->cols = sw / FONT_WIDTH(txt);
|
||||
txt->rows = sh / FONT_HEIGHT(txt);
|
||||
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &s);
|
||||
if (s <= 0)
|
||||
s = 64;
|
||||
else if (s > 2048)
|
||||
s = 2048;
|
||||
gt->max_tex_size = s;
|
||||
|
||||
gl_clear_error();
|
||||
|
||||
return 0;
|
||||
|
||||
err_shader:
|
||||
gl_shader_unref(gt->shader);
|
||||
err_htable:
|
||||
kmscon_hashtable_free(gt->glyphs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gltex_unset(struct kmscon_text *txt)
|
||||
{
|
||||
struct gltex *gt = txt->data;
|
||||
int ret;
|
||||
struct kmscon_dlist *iter;
|
||||
struct atlas *atlas;
|
||||
bool gl = true;
|
||||
|
||||
ret = uterm_screen_use(txt->screen);
|
||||
if (ret) {
|
||||
gl = false;
|
||||
log_warning("cannot activate OpenGL-CTX during destruction");
|
||||
}
|
||||
|
||||
kmscon_hashtable_free(gt->glyphs);
|
||||
|
||||
while (!kmscon_dlist_empty(>->atlases)) {
|
||||
iter = gt->atlases.next;
|
||||
kmscon_dlist_unlink(iter);
|
||||
atlas = kmscon_dlist_entry(iter, struct atlas, list);
|
||||
|
||||
free(atlas->cache_pos);
|
||||
free(atlas->cache_texpos);
|
||||
free(atlas->cache_fgcol);
|
||||
free(atlas->cache_bgcol);
|
||||
|
||||
if (gl)
|
||||
gl_tex_free(&atlas->tex, 1);
|
||||
}
|
||||
|
||||
if (gl) {
|
||||
gl_shader_unref(gt->shader);
|
||||
|
||||
gl_clear_error();
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int next_pow2(unsigned int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!num)
|
||||
return num;
|
||||
|
||||
--num;
|
||||
for (i = 1; i < sizeof(unsigned int) * CHAR_BIT; i <<= 1)
|
||||
num = num | num >> i;
|
||||
|
||||
return num + 1;
|
||||
}
|
||||
|
||||
/* returns an atlas with at least 1 free glyph position; NULL on error */
|
||||
static struct atlas *get_atlas(struct kmscon_text *txt)
|
||||
{
|
||||
struct gltex *gt = txt->data;
|
||||
struct atlas *atlas;
|
||||
size_t newsize;
|
||||
unsigned int width, height, nsize;
|
||||
GLenum err;
|
||||
|
||||
/* check whether the last added atlas has still room for one glyph */
|
||||
if (!kmscon_dlist_empty(>->atlases)) {
|
||||
atlas = kmscon_dlist_entry(gt->atlases.next, struct atlas,
|
||||
list);
|
||||
if (atlas->fill < atlas->count)
|
||||
return atlas;
|
||||
}
|
||||
|
||||
/* all atlases are full so we have to create a new atlas */
|
||||
atlas = malloc(sizeof(*atlas));
|
||||
if (!atlas)
|
||||
return NULL;
|
||||
memset(atlas, 0, sizeof(*atlas));
|
||||
|
||||
gl_clear_error();
|
||||
|
||||
gl_tex_new(&atlas->tex, 1);
|
||||
err = glGetError();
|
||||
if (err != GL_NO_ERROR || !atlas->tex) {
|
||||
gl_clear_error();
|
||||
log_warning("cannot create new OpenGL texture: %d", err);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
newsize = gt->max_tex_size / FONT_WIDTH(txt);
|
||||
if (newsize < 1)
|
||||
newsize = 1;
|
||||
|
||||
/* OpenGL texture sizes are heavily restricted so we need to find a
|
||||
* valid texture size that is big enough to hold as many glyphs as
|
||||
* possible but at least 1 */
|
||||
try_next:
|
||||
width = next_pow2(FONT_WIDTH(txt) * newsize);
|
||||
height = next_pow2(FONT_HEIGHT(txt));
|
||||
|
||||
gl_clear_error();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, atlas->tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height,
|
||||
0, GL_ALPHA, GL_UNSIGNED_BYTE, NULL);
|
||||
|
||||
err = glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
if (newsize > 1) {
|
||||
--newsize;
|
||||
goto try_next;
|
||||
}
|
||||
gl_clear_error();
|
||||
log_warning("OpenGL textures too small for a single glyph (%d)",
|
||||
err);
|
||||
goto err_tex;
|
||||
}
|
||||
|
||||
log_debug("new atlas of size %ux%u for %u", width, height, newsize);
|
||||
|
||||
nsize = txt->cols * txt->rows;
|
||||
|
||||
atlas->cache_pos = malloc(sizeof(GLfloat) * nsize * 2 * 6);
|
||||
if (!atlas->cache_pos)
|
||||
goto err_mem;
|
||||
|
||||
atlas->cache_texpos = malloc(sizeof(GLfloat) * nsize * 2 * 6);
|
||||
if (!atlas->cache_texpos)
|
||||
goto err_mem;
|
||||
|
||||
atlas->cache_fgcol = malloc(sizeof(GLfloat) * nsize * 3 * 6);
|
||||
if (!atlas->cache_fgcol)
|
||||
goto err_mem;
|
||||
|
||||
atlas->cache_bgcol = malloc(sizeof(GLfloat) * nsize * 3 * 6);
|
||||
if (!atlas->cache_bgcol)
|
||||
goto err_mem;
|
||||
|
||||
atlas->cache_size = nsize;
|
||||
atlas->count = newsize;
|
||||
atlas->width = width;
|
||||
atlas->height = height;
|
||||
atlas->advance_htex = 1.0 / atlas->width * FONT_WIDTH(txt);
|
||||
atlas->advance_vtex = 1.0 / atlas->height * FONT_HEIGHT(txt);
|
||||
|
||||
kmscon_dlist_link(>->atlases, &atlas->list);
|
||||
return atlas;
|
||||
|
||||
err_mem:
|
||||
free(atlas->cache_pos);
|
||||
free(atlas->cache_texpos);
|
||||
free(atlas->cache_fgcol);
|
||||
free(atlas->cache_bgcol);
|
||||
err_tex:
|
||||
gl_tex_free(&atlas->tex, 1);
|
||||
err_free:
|
||||
free(atlas);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_glyph(struct kmscon_text *txt, struct glyph **out,
|
||||
kmscon_symbol_t ch)
|
||||
{
|
||||
struct gltex *gt = txt->data;
|
||||
struct atlas *atlas;
|
||||
struct glyph *glyph;
|
||||
bool res;
|
||||
int ret;
|
||||
GLenum err;
|
||||
|
||||
res = kmscon_hashtable_find(gt->glyphs, (void**)&glyph,
|
||||
(void*)(unsigned long)ch);
|
||||
if (res) {
|
||||
*out = glyph;
|
||||
return 0;
|
||||
}
|
||||
|
||||
atlas = get_atlas(txt);
|
||||
if (!atlas)
|
||||
return -EFAULT;
|
||||
|
||||
glyph = malloc(sizeof(*glyph));
|
||||
if (!glyph)
|
||||
return -ENOMEM;
|
||||
memset(glyph, 0, sizeof(*glyph));
|
||||
|
||||
if (ch == 0 || ch == ' ') {
|
||||
ret = kmscon_font_render_empty(txt->font, &glyph->glyph);
|
||||
} else {
|
||||
ret = kmscon_font_render(txt->font, ch, &glyph->glyph);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
ret = kmscon_font_render_inval(txt->font, &glyph->glyph);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
gl_clear_error();
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, atlas->tex);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, GLYPH_STRIDE(glyph));
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
||||
FONT_WIDTH(txt) * atlas->fill, 0,
|
||||
GLYPH_WIDTH(glyph),
|
||||
GLYPH_HEIGHT(glyph),
|
||||
GL_ALPHA, GL_UNSIGNED_BYTE,
|
||||
GLYPH_DATA(glyph));
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
|
||||
err = glGetError();
|
||||
if (err != GL_NO_ERROR) {
|
||||
gl_clear_error();
|
||||
log_warning("cannot load glyph data into OpenGL texture (%d)",
|
||||
err);
|
||||
ret = -EFAULT;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
glyph->atlas = atlas;
|
||||
glyph->texoff = atlas->fill;
|
||||
|
||||
ret = kmscon_hashtable_insert(gt->glyphs, (void*)(long)ch, glyph);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
++atlas->fill;
|
||||
|
||||
*out = glyph;
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
free(glyph);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gltex_prepare(struct kmscon_text *txt)
|
||||
{
|
||||
struct gltex *gt = txt->data;
|
||||
struct atlas *atlas;
|
||||
struct kmscon_dlist *iter;
|
||||
unsigned int sw, sh;
|
||||
int ret;
|
||||
|
||||
ret = uterm_screen_use(txt->screen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kmscon_dlist_for_each(iter, >->atlases) {
|
||||
atlas = kmscon_dlist_entry(iter, struct atlas, list);
|
||||
|
||||
atlas->cache_num = 0;
|
||||
}
|
||||
|
||||
sw = SCREEN_WIDTH(txt);
|
||||
sh = SCREEN_HEIGHT(txt);
|
||||
|
||||
gt->advance_x = 2.0 / sw * FONT_WIDTH(txt);
|
||||
gt->advance_y = 2.0 / sh * FONT_HEIGHT(txt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gltex_draw(struct kmscon_text *txt, kmscon_symbol_t ch,
|
||||
unsigned int posx, unsigned int posy,
|
||||
const struct font_char_attr *attr)
|
||||
{
|
||||
struct gltex *gt = txt->data;
|
||||
struct atlas *atlas;
|
||||
struct glyph *glyph;
|
||||
int ret, i, idx;
|
||||
|
||||
ret = find_glyph(txt, &glyph, ch);
|
||||
if (ret)
|
||||
return ret;
|
||||
atlas = glyph->atlas;
|
||||
|
||||
if (atlas->cache_num >= atlas->cache_size)
|
||||
return -ERANGE;
|
||||
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 0] =
|
||||
gt->advance_x * posx - 1;
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 1] =
|
||||
gt->advance_y * posy - 1;
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 2] =
|
||||
gt->advance_x * posx - 1;
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 3] =
|
||||
gt->advance_y * posy + gt->advance_y - 1;
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 4] =
|
||||
gt->advance_x * posx + gt->advance_x - 1;
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 5] =
|
||||
gt->advance_y * posy + gt->advance_y - 1;
|
||||
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 6] =
|
||||
gt->advance_x * posx - 1;
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 7] =
|
||||
gt->advance_y * posy - 1;
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 8] =
|
||||
gt->advance_x * posx + gt->advance_x - 1;
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 9] =
|
||||
gt->advance_y * posy + gt->advance_y - 1;
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 10] =
|
||||
gt->advance_x * posx + gt->advance_x - 1;
|
||||
atlas->cache_pos[atlas->cache_num * 2 * 6 + 11] =
|
||||
gt->advance_y * posy - 1;
|
||||
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 0] = glyph->texoff;
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 1] = 0.0;
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 2] = glyph->texoff;
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 3] = 1.0;
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 4] = glyph->texoff + 1;
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 5] = 1.0;
|
||||
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 6] = glyph->texoff;
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 7] = 0.0;
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 8] = glyph->texoff + 1;
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 9] = 1.0;
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 10] = glyph->texoff + 1;
|
||||
atlas->cache_texpos[atlas->cache_num * 2 * 6 + 11] = 0.0;
|
||||
|
||||
for (i = 0; i < 6; ++i) {
|
||||
idx = atlas->cache_num * 3 * 6 + i * 3;
|
||||
if (attr->inverse) {
|
||||
atlas->cache_fgcol[idx + 0] = attr->br / 255.0;
|
||||
atlas->cache_fgcol[idx + 1] = attr->bg / 255.0;
|
||||
atlas->cache_fgcol[idx + 2] = attr->bb / 255.0;
|
||||
atlas->cache_bgcol[idx + 0] = attr->fr / 255.0;
|
||||
atlas->cache_bgcol[idx + 1] = attr->fg / 255.0;
|
||||
atlas->cache_bgcol[idx + 2] = attr->fb / 255.0;
|
||||
} else {
|
||||
atlas->cache_fgcol[idx + 0] = attr->fr / 255.0;
|
||||
atlas->cache_fgcol[idx + 1] = attr->fg / 255.0;
|
||||
atlas->cache_fgcol[idx + 2] = attr->fb / 255.0;
|
||||
atlas->cache_bgcol[idx + 0] = attr->br / 255.0;
|
||||
atlas->cache_bgcol[idx + 1] = attr->bg / 255.0;
|
||||
atlas->cache_bgcol[idx + 2] = attr->bb / 255.0;
|
||||
}
|
||||
}
|
||||
|
||||
++atlas->cache_num;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gltex_render(struct kmscon_text *txt)
|
||||
{
|
||||
struct gltex *gt = txt->data;
|
||||
struct atlas *atlas;
|
||||
struct kmscon_dlist *iter;
|
||||
float mat[16];
|
||||
|
||||
gl_clear_error();
|
||||
|
||||
gl_shader_use(gt->shader);
|
||||
|
||||
glViewport(0, 0, SCREEN_WIDTH(txt), SCREEN_HEIGHT(txt));
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
gl_m4_identity(mat);
|
||||
glUniformMatrix4fv(gt->uni_proj, 1, GL_FALSE, mat);
|
||||
|
||||
glEnableVertexAttribArray(0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glEnableVertexAttribArray(2);
|
||||
glEnableVertexAttribArray(3);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glUniform1i(gt->uni_atlas, 0);
|
||||
|
||||
kmscon_dlist_for_each(iter, >->atlases) {
|
||||
atlas = kmscon_dlist_entry(iter, struct atlas, list);
|
||||
if (!atlas->cache_num)
|
||||
continue;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, atlas->tex);
|
||||
glUniform1f(gt->uni_advance_htex, atlas->advance_htex);
|
||||
glUniform1f(gt->uni_advance_vtex, atlas->advance_vtex);
|
||||
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, atlas->cache_pos);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, atlas->cache_texpos);
|
||||
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, atlas->cache_fgcol);
|
||||
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, atlas->cache_bgcol);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 6 * atlas->cache_num);
|
||||
}
|
||||
|
||||
glDisableVertexAttribArray(0);
|
||||
glDisableVertexAttribArray(1);
|
||||
glDisableVertexAttribArray(2);
|
||||
glDisableVertexAttribArray(3);
|
||||
|
||||
if (gl_has_error(gt->shader)) {
|
||||
log_warning("rendering console caused OpenGL errors");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kmscon_text_ops kmscon_text_gltex_ops = {
|
||||
.name = "gltex",
|
||||
.init = gltex_init,
|
||||
.destroy = gltex_destroy,
|
||||
.set = gltex_set,
|
||||
.unset = gltex_unset,
|
||||
.prepare = gltex_prepare,
|
||||
.draw = gltex_draw,
|
||||
.render = gltex_render,
|
||||
.abort = NULL,
|
||||
};
|
||||
|
||||
int kmscon_text_gltex_load(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = kmscon_text_register(&kmscon_text_gltex_ops);
|
||||
if (ret) {
|
||||
log_error("cannot register gltex renderer");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kmscon_text_gltex_unload(void)
|
||||
{
|
||||
kmscon_text_unregister(kmscon_text_gltex_ops.name);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user