feat: xorg text overlays, font atlas generator, v4l2_view_cli
- tools/gen_font_atlas: Python/Pillow build tool — skyline packs DejaVu Sans glyphs 32-255 into a grayscale atlas, emits build/gen/font_atlas.h with pixel data and Font_Glyph[256] metrics table - xorg: bitmap font atlas text overlay rendering (GL_R8 atlas texture, alpha-blended glyph quads, dark background rect per overlay) - xorg: add xorg_viewer_set_overlay_text / clear_overlays API - xorg: add xorg_viewer_handle_events for streaming use (events only, no redundant render) - xorg_cli: show today's date as white text overlay - v4l2_view_cli: new tool — V4L2 capture with format auto-selection (highest FPS then largest resolution), MJPEG/YUYV, measured FPS overlay - docs: update README, planning, architecture to reflect current status Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,12 +2,20 @@ ROOT := $(abspath ../../..)
|
||||
include $(ROOT)/common.mk
|
||||
|
||||
MODULE_BUILD = $(BUILD)/xorg
|
||||
GEN_DIR = $(BUILD)/gen
|
||||
ATLAS_H = $(GEN_DIR)/font_atlas.h
|
||||
ATLAS_PNG = $(GEN_DIR)/font_atlas.png
|
||||
GEN_SCRIPT = $(ROOT)/tools/gen_font_atlas/gen_font_atlas.py
|
||||
|
||||
# Select real implementation when glfw feature is enabled, stub otherwise.
|
||||
ifeq ($(filter glfw,$(FEATURES)),glfw)
|
||||
SRC = xorg.c
|
||||
SRC = xorg.c
|
||||
DEPS = $(ATLAS_H)
|
||||
EXTRA_CFLAGS = -I$(GEN_DIR)
|
||||
else
|
||||
SRC = xorg_stub.c
|
||||
SRC = xorg_stub.c
|
||||
DEPS =
|
||||
EXTRA_CFLAGS =
|
||||
endif
|
||||
|
||||
OBJ = $(MODULE_BUILD)/xorg.o
|
||||
@@ -16,12 +24,18 @@ OBJ = $(MODULE_BUILD)/xorg.o
|
||||
|
||||
all: $(OBJ)
|
||||
|
||||
$(OBJ): $(SRC) | $(MODULE_BUILD)
|
||||
$(CC) $(CFLAGS) $(DEPFLAGS) -c -o $@ $<
|
||||
$(OBJ): $(SRC) $(DEPS) | $(MODULE_BUILD)
|
||||
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(DEPFLAGS) -c -o $@ $<
|
||||
|
||||
$(ATLAS_H) $(ATLAS_PNG): $(GEN_SCRIPT) | $(GEN_DIR)
|
||||
python3 $< --out-header $(ATLAS_H) --out-png $(ATLAS_PNG)
|
||||
|
||||
$(MODULE_BUILD):
|
||||
mkdir -p $@
|
||||
|
||||
$(GEN_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) $(MODULE_BUILD)/xorg.d
|
||||
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
#endif
|
||||
|
||||
#include "xorg.h"
|
||||
#include "font_atlas.h" /* generated: font_glyphs[], font_atlas_pixels[], FONT_ATLAS_W/H */
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Shader sources */
|
||||
/* Shader sources — video */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
@@ -62,14 +63,99 @@ static const char *FRAG_RGB_SRC =
|
||||
"}\n";
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Viewer state */
|
||||
/* Shader sources — solid rect */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* Draws a screen-space axis-aligned rect using gl_VertexID (no VBO).
|
||||
* u_rect = (x0, y0, x1, y1) in window pixels.
|
||||
*/
|
||||
static const char *VERT_RECT_SRC =
|
||||
"#version 330 core\n"
|
||||
"uniform vec4 u_rect;\n"
|
||||
"uniform vec2 u_fb_size;\n"
|
||||
"void main() {\n"
|
||||
" vec2 corners[4] = vec2[4](\n"
|
||||
" vec2(u_rect.x, u_rect.y),\n"
|
||||
" vec2(u_rect.x, u_rect.w),\n"
|
||||
" vec2(u_rect.z, u_rect.y),\n"
|
||||
" vec2(u_rect.z, u_rect.w)\n"
|
||||
" );\n"
|
||||
" vec2 pos = corners[gl_VertexID];\n"
|
||||
" vec2 ndc = (pos / u_fb_size) * 2.0 - 1.0;\n"
|
||||
" ndc.y = -ndc.y;\n"
|
||||
" gl_Position = vec4(ndc, 0.0, 1.0);\n"
|
||||
"}\n";
|
||||
|
||||
static const char *FRAG_RECT_SRC =
|
||||
"#version 330 core\n"
|
||||
"out vec4 out_color;\n"
|
||||
"uniform vec4 u_color;\n"
|
||||
"void main() {\n"
|
||||
" out_color = u_color;\n"
|
||||
"}\n";
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Shader sources — text overlay */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/*
|
||||
* Screen-space vertex shader: a_pos in window pixels (top-left = 0,0),
|
||||
* converted to NDC. a_uv is the atlas UV coordinate.
|
||||
*/
|
||||
static const char *VERT_TEXT_SRC =
|
||||
"#version 330 core\n"
|
||||
"layout(location = 0) in vec2 a_pos;\n"
|
||||
"layout(location = 1) in vec2 a_uv;\n"
|
||||
"out vec2 v_uv;\n"
|
||||
"uniform vec2 u_fb_size;\n"
|
||||
"void main() {\n"
|
||||
" vec2 ndc = (a_pos / u_fb_size) * 2.0 - 1.0;\n"
|
||||
" ndc.y = -ndc.y;\n"
|
||||
" gl_Position = vec4(ndc, 0.0, 1.0);\n"
|
||||
" v_uv = a_uv;\n"
|
||||
"}\n";
|
||||
|
||||
/*
|
||||
* Fragment shader: samples the atlas (GL_R8, grayscale) and uses the
|
||||
* texel value as alpha, blended with the per-overlay colour.
|
||||
*/
|
||||
static const char *FRAG_TEXT_SRC =
|
||||
"#version 330 core\n"
|
||||
"in vec2 v_uv;\n"
|
||||
"out vec4 out_color;\n"
|
||||
"uniform sampler2D u_text_atlas;\n"
|
||||
"uniform vec3 u_text_color;\n"
|
||||
"void main() {\n"
|
||||
" float a = texture(u_text_atlas, v_uv).r;\n"
|
||||
" out_color = vec4(u_text_color, a);\n"
|
||||
"}\n";
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Constants and types */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
#define MAX_OVERLAYS 8
|
||||
#define MAX_OVERLAY_CHARS 256
|
||||
|
||||
/*
|
||||
* Each glyph quad = 6 vertices × 4 floats (x, y, u, v).
|
||||
* Maximum vertex buffer covers all overlays at full text length.
|
||||
*/
|
||||
#define MAX_TEXT_VERTS (MAX_OVERLAYS * MAX_OVERLAY_CHARS * 6 * 4)
|
||||
|
||||
typedef struct {
|
||||
char text[MAX_OVERLAY_CHARS];
|
||||
int x, y; /* top-left of text block in window pixels */
|
||||
float r, g, b;
|
||||
} Overlay;
|
||||
|
||||
typedef enum { MODE_NONE, MODE_YUV, MODE_RGB } Render_Mode;
|
||||
|
||||
struct Xorg_Viewer {
|
||||
GLFWwindow *window;
|
||||
|
||||
/* Video programs */
|
||||
GLuint prog_yuv;
|
||||
GLint u_tex_y, u_tex_cb, u_tex_cr;
|
||||
GLint u_uv_scale_yuv, u_uv_offset_yuv;
|
||||
@@ -86,6 +172,24 @@ struct Xorg_Viewer {
|
||||
Xorg_Anchor anchor;
|
||||
int frame_w, frame_h;
|
||||
|
||||
/* Solid rect (overlay background) */
|
||||
GLuint prog_rect;
|
||||
GLint u_rect_loc;
|
||||
GLint u_rect_fb_size_loc;
|
||||
GLint u_rect_color_loc;
|
||||
|
||||
/* Text overlay */
|
||||
GLuint prog_text;
|
||||
GLint u_fb_size_loc;
|
||||
GLint u_text_atlas_loc;
|
||||
GLint u_text_color_loc;
|
||||
GLuint tex_atlas;
|
||||
GLuint vao_text;
|
||||
GLuint vbo_text;
|
||||
|
||||
Overlay overlays[MAX_OVERLAYS];
|
||||
int n_overlays;
|
||||
|
||||
#ifdef HAVE_TURBOJPEG
|
||||
tjhandle tj;
|
||||
uint8_t *yuv_buf;
|
||||
@@ -114,9 +218,9 @@ static GLuint compile_shader(GLenum type, const char *src)
|
||||
return s;
|
||||
}
|
||||
|
||||
static GLuint link_program(const char *frag_src)
|
||||
static GLuint link_program(const char *vert_src, const char *frag_src)
|
||||
{
|
||||
GLuint vs = compile_shader(GL_VERTEX_SHADER, VERT_SRC);
|
||||
GLuint vs = compile_shader(GL_VERTEX_SHADER, vert_src);
|
||||
GLuint fs = compile_shader(GL_FRAGMENT_SHADER, frag_src);
|
||||
if (!vs || !fs) {
|
||||
glDeleteShader(vs);
|
||||
@@ -145,7 +249,8 @@ static GLuint link_program(const char *frag_src)
|
||||
/* Public API */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void render(Xorg_Viewer *v); /* forward declaration */
|
||||
static void render(Xorg_Viewer *v); /* forward declarations */
|
||||
static void draw_text_overlays(Xorg_Viewer *v, int fb_w, int fb_h);
|
||||
|
||||
bool xorg_available(void) { return true; }
|
||||
|
||||
@@ -166,6 +271,58 @@ static void framebuffer_size_callback(GLFWwindow *window, int width, int height)
|
||||
if (v) { render(v); }
|
||||
}
|
||||
|
||||
/* Initialise text rendering resources — called from xorg_viewer_open. */
|
||||
static bool init_text_rendering(Xorg_Viewer *v)
|
||||
{
|
||||
v->prog_rect = link_program(VERT_RECT_SRC, FRAG_RECT_SRC);
|
||||
if (!v->prog_rect) { return false; }
|
||||
v->u_rect_loc = glGetUniformLocation(v->prog_rect, "u_rect");
|
||||
v->u_rect_fb_size_loc = glGetUniformLocation(v->prog_rect, "u_fb_size");
|
||||
v->u_rect_color_loc = glGetUniformLocation(v->prog_rect, "u_color");
|
||||
|
||||
v->prog_text = link_program(VERT_TEXT_SRC, FRAG_TEXT_SRC);
|
||||
if (!v->prog_text) { return false; }
|
||||
|
||||
v->u_fb_size_loc = glGetUniformLocation(v->prog_text, "u_fb_size");
|
||||
v->u_text_atlas_loc = glGetUniformLocation(v->prog_text, "u_text_atlas");
|
||||
v->u_text_color_loc = glGetUniformLocation(v->prog_text, "u_text_color");
|
||||
|
||||
/* Upload atlas texture (grayscale GL_R8). */
|
||||
glGenTextures(1, &v->tex_atlas);
|
||||
glBindTexture(GL_TEXTURE_2D, v->tex_atlas);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8,
|
||||
FONT_ATLAS_W, FONT_ATLAS_H, 0,
|
||||
GL_RED, GL_UNSIGNED_BYTE, font_atlas_pixels);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
/* VAO + dynamic VBO for glyph quads. */
|
||||
glGenVertexArrays(1, &v->vao_text);
|
||||
glGenBuffers(1, &v->vbo_text);
|
||||
|
||||
glBindVertexArray(v->vao_text);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, v->vbo_text);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
MAX_TEXT_VERTS * sizeof(float), NULL, GL_DYNAMIC_DRAW);
|
||||
|
||||
/* layout(location=0): vec2 a_pos, layout(location=1): vec2 a_uv */
|
||||
glEnableVertexAttribArray(0);
|
||||
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,
|
||||
4 * sizeof(float), (void *)0);
|
||||
glEnableVertexAttribArray(1);
|
||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,
|
||||
4 * sizeof(float), (void *)(2 * sizeof(float)));
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Xorg_Viewer *xorg_viewer_open(int x, int y, int width, int height,
|
||||
const char *title)
|
||||
{
|
||||
@@ -209,8 +366,8 @@ Xorg_Viewer *xorg_viewer_open(int x, int y, int width, int height,
|
||||
glfwSetWindowUserPointer(win, v);
|
||||
glfwSetFramebufferSizeCallback(win, framebuffer_size_callback);
|
||||
|
||||
v->prog_yuv = link_program(FRAG_YUV_SRC);
|
||||
v->prog_rgb = link_program(FRAG_RGB_SRC);
|
||||
v->prog_yuv = link_program(VERT_SRC, FRAG_YUV_SRC);
|
||||
v->prog_rgb = link_program(VERT_SRC, FRAG_RGB_SRC);
|
||||
if (!v->prog_yuv || !v->prog_rgb) {
|
||||
xorg_viewer_close(v);
|
||||
return NULL;
|
||||
@@ -238,6 +395,11 @@ Xorg_Viewer *xorg_viewer_open(int x, int y, int width, int height,
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (!init_text_rendering(v)) {
|
||||
xorg_viewer_close(v);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_TURBOJPEG
|
||||
v->tj = tjInitDecompress();
|
||||
if (!v->tj) {
|
||||
@@ -260,6 +422,144 @@ void xorg_viewer_set_anchor(Xorg_Viewer *v, Xorg_Anchor anchor)
|
||||
if (v) { v->anchor = anchor; }
|
||||
}
|
||||
|
||||
void xorg_viewer_set_overlay_text(Xorg_Viewer *v, int idx, int x, int y,
|
||||
const char *text, float r, float g, float b)
|
||||
{
|
||||
if (!v || idx < 0 || idx >= MAX_OVERLAYS) { return; }
|
||||
Overlay *o = &v->overlays[idx];
|
||||
strncpy(o->text, text, MAX_OVERLAY_CHARS - 1);
|
||||
o->text[MAX_OVERLAY_CHARS - 1] = '\0';
|
||||
o->x = x;
|
||||
o->y = y;
|
||||
o->r = r;
|
||||
o->g = g;
|
||||
o->b = b;
|
||||
if (idx >= v->n_overlays) { v->n_overlays = idx + 1; }
|
||||
}
|
||||
|
||||
void xorg_viewer_clear_overlays(Xorg_Viewer *v)
|
||||
{
|
||||
if (v) { v->n_overlays = 0; }
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Internal: build and draw text quads for all overlays */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void draw_text_overlays(Xorg_Viewer *v, int fb_w, int fb_h)
|
||||
{
|
||||
if (v->n_overlays == 0 || !v->prog_text) { return; }
|
||||
|
||||
/*
|
||||
* Vertex layout: (x, y, u, uv) — 4 floats per vertex, 6 verts per glyph.
|
||||
* Declared static to keep it off the stack (2MB+ otherwise).
|
||||
*/
|
||||
static float verts[MAX_TEXT_VERTS];
|
||||
|
||||
/* Reset to full-window viewport for overlay drawing. */
|
||||
glViewport(0, 0, fb_w, fb_h);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, v->tex_atlas);
|
||||
|
||||
#define OVERLAY_MARGIN 4
|
||||
|
||||
for (int oi = 0; oi < v->n_overlays; oi++) {
|
||||
const Overlay *o = &v->overlays[oi];
|
||||
if (o->text[0] == '\0') { continue; }
|
||||
|
||||
/* Measure text to compute bounding box for the background rect. */
|
||||
int text_w = 0, max_ascent = 0, max_descent = 0;
|
||||
for (const char *p = o->text; *p; p++) {
|
||||
unsigned char cp = (unsigned char)*p;
|
||||
if (cp < 32) { continue; }
|
||||
const Font_Glyph *g = &font_glyphs[cp];
|
||||
text_w += g->advance;
|
||||
if (g->bearing_y > max_ascent) { max_ascent = g->bearing_y; }
|
||||
if (g->h > 0) {
|
||||
int desc = g->h - g->bearing_y;
|
||||
if (desc > max_descent) { max_descent = desc; }
|
||||
}
|
||||
}
|
||||
int text_h = max_ascent + max_descent;
|
||||
int max_bearing_y = max_ascent;
|
||||
|
||||
/* Draw semi-transparent dark background rect. */
|
||||
float rx0 = (float)(o->x - OVERLAY_MARGIN);
|
||||
float ry0 = (float)(o->y - OVERLAY_MARGIN);
|
||||
float rx1 = (float)(o->x + text_w + OVERLAY_MARGIN);
|
||||
float ry1 = (float)(o->y + text_h + OVERLAY_MARGIN);
|
||||
glUseProgram(v->prog_rect);
|
||||
glUniform2f(v->u_rect_fb_size_loc, (float)fb_w, (float)fb_h);
|
||||
glUniform4f(v->u_rect_loc, rx0, ry0, rx1, ry1);
|
||||
glUniform4f(v->u_rect_color_loc, 0.0f, 0.0f, 0.0f, 0.55f);
|
||||
glBindVertexArray(v->vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
int baseline_y = o->y + max_bearing_y;
|
||||
|
||||
/* Switch back to text program + VBO for glyph quads. */
|
||||
glUseProgram(v->prog_text);
|
||||
glUniform2f(v->u_fb_size_loc, (float)fb_w, (float)fb_h);
|
||||
glUniform1i(v->u_text_atlas_loc, 0);
|
||||
glBindVertexArray(v->vao_text);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, v->vbo_text);
|
||||
|
||||
/* Build quads for this overlay. */
|
||||
int n_verts = 0;
|
||||
int cursor_x = o->x;
|
||||
|
||||
for (const char *p = o->text; *p; p++) {
|
||||
unsigned char cp = (unsigned char)*p;
|
||||
if (cp < 32) { continue; }
|
||||
|
||||
const Font_Glyph *g = &font_glyphs[cp];
|
||||
|
||||
/* Advance even for non-printing glyphs (e.g. space). */
|
||||
if (g->w > 0 && g->h > 0) {
|
||||
/* Screen-space quad corners (top-left origin). */
|
||||
float sx0 = (float)(cursor_x + g->bearing_x);
|
||||
float sy0 = (float)(baseline_y - g->bearing_y);
|
||||
float sx1 = sx0 + (float)g->w;
|
||||
float sy1 = sy0 + (float)g->h;
|
||||
|
||||
/* Atlas UV corners. */
|
||||
float u0 = (float)g->x / FONT_ATLAS_W;
|
||||
float u1 = (float)(g->x + g->w) / FONT_ATLAS_W;
|
||||
float v0 = (float)g->y / FONT_ATLAS_H;
|
||||
float v1 = (float)(g->y + g->h) / FONT_ATLAS_H;
|
||||
|
||||
if (n_verts + 6 * 4 > MAX_TEXT_VERTS) { break; }
|
||||
|
||||
float *d = verts + n_verts;
|
||||
/* tri 0 */
|
||||
d[ 0]=sx0; d[ 1]=sy0; d[ 2]=u0; d[ 3]=v0;
|
||||
d[ 4]=sx0; d[ 5]=sy1; d[ 6]=u0; d[ 7]=v1;
|
||||
d[ 8]=sx1; d[ 9]=sy0; d[10]=u1; d[11]=v0;
|
||||
/* tri 1 */
|
||||
d[12]=sx1; d[13]=sy0; d[14]=u1; d[15]=v0;
|
||||
d[16]=sx0; d[17]=sy1; d[18]=u0; d[19]=v1;
|
||||
d[20]=sx1; d[21]=sy1; d[22]=u1; d[23]=v1;
|
||||
n_verts += 24;
|
||||
}
|
||||
cursor_x += g->advance;
|
||||
}
|
||||
|
||||
if (n_verts == 0) { continue; }
|
||||
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, n_verts * sizeof(float), verts);
|
||||
glUniform3f(v->u_text_color_loc, o->r, o->g, o->b);
|
||||
glDrawArrays(GL_TRIANGLES, 0, n_verts / 4);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glBindVertexArray(0);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Internal: compute layout and render from existing textures */
|
||||
/* ------------------------------------------------------------------ */
|
||||
@@ -383,6 +683,9 @@ static void render(Xorg_Viewer *v)
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
|
||||
/* Draw text overlays on top, then present. */
|
||||
draw_text_overlays(v, fb_w, fb_h);
|
||||
|
||||
glfwSwapBuffers(v->window);
|
||||
}
|
||||
|
||||
@@ -510,6 +813,13 @@ bool xorg_viewer_poll(Xorg_Viewer *v)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool xorg_viewer_handle_events(Xorg_Viewer *v)
|
||||
{
|
||||
if (!v || glfwWindowShouldClose(v->window)) { return false; }
|
||||
glfwPollEvents();
|
||||
return !glfwWindowShouldClose(v->window);
|
||||
}
|
||||
|
||||
void xorg_viewer_close(Xorg_Viewer *v)
|
||||
{
|
||||
if (!v) { return; }
|
||||
@@ -517,10 +827,15 @@ void xorg_viewer_close(Xorg_Viewer *v)
|
||||
if (v->tj) { tjDestroy(v->tj); }
|
||||
free(v->yuv_buf);
|
||||
#endif
|
||||
if (v->vao) { glDeleteVertexArrays(1, &v->vao); }
|
||||
if (v->tex[0]) { glDeleteTextures(4, v->tex); }
|
||||
if (v->prog_yuv) { glDeleteProgram(v->prog_yuv); }
|
||||
if (v->prog_rgb) { glDeleteProgram(v->prog_rgb); }
|
||||
if (v->vao_text) { glDeleteVertexArrays(1, &v->vao_text); }
|
||||
if (v->vbo_text) { glDeleteBuffers(1, &v->vbo_text); }
|
||||
if (v->tex_atlas) { glDeleteTextures(1, &v->tex_atlas); }
|
||||
if (v->prog_text) { glDeleteProgram(v->prog_text); }
|
||||
if (v->prog_rect) { glDeleteProgram(v->prog_rect); }
|
||||
if (v->vao) { glDeleteVertexArrays(1, &v->vao); }
|
||||
if (v->tex[0]) { glDeleteTextures(4, v->tex); }
|
||||
if (v->prog_yuv) { glDeleteProgram(v->prog_yuv); }
|
||||
if (v->prog_rgb) { glDeleteProgram(v->prog_rgb); }
|
||||
if (v->window) {
|
||||
glfwDestroyWindow(v->window);
|
||||
glfwTerminate();
|
||||
|
||||
@@ -34,5 +34,12 @@ bool xorg_viewer_push_mjpeg(Xorg_Viewer *v,
|
||||
|
||||
void xorg_viewer_set_scale(Xorg_Viewer *v, Xorg_Scale scale) { (void)v; (void)scale; }
|
||||
void xorg_viewer_set_anchor(Xorg_Viewer *v, Xorg_Anchor anchor) { (void)v; (void)anchor; }
|
||||
bool xorg_viewer_poll(Xorg_Viewer *v) { (void)v; return false; }
|
||||
void xorg_viewer_set_overlay_text(Xorg_Viewer *v, int idx, int x, int y,
|
||||
const char *text, float r, float g, float b)
|
||||
{
|
||||
(void)v; (void)idx; (void)x; (void)y; (void)text; (void)r; (void)g; (void)b;
|
||||
}
|
||||
void xorg_viewer_clear_overlays(Xorg_Viewer *v) { (void)v; }
|
||||
bool xorg_viewer_poll(Xorg_Viewer *v) { (void)v; return false; }
|
||||
bool xorg_viewer_handle_events(Xorg_Viewer *v) { (void)v; return false; }
|
||||
void xorg_viewer_close(Xorg_Viewer *v) { (void)v; }
|
||||
|
||||
Reference in New Issue
Block a user