[renderer] Add a cell-based character buffer

This is intended for the built-in 8x8 bitmap characters and quake's
"conchars", but could potentially be used for any simple (non-composed
characters) mono-spaced font. Currently, the buffers can be created,
destroyed, cleared, scrolled vertically in either direction, and
rendered to the screen in a single blast.

One of the reasons for creating the buffer is to make it so scaling can
be supported in the sw renderer.
This commit is contained in:
Bill Currie 2022-09-15 14:24:33 +09:00
parent 62ccafca8c
commit 25a14eb232
17 changed files with 311 additions and 1 deletions

View file

@ -30,9 +30,11 @@
struct qpic_s;
struct rfont_s;
struct draw_charbuffer_s;
void gl_Draw_Init (void);
void gl_Draw_Shutdown (void);
void gl_Draw_CharBuffer (int x, int y, struct draw_charbuffer_s *buffer);
void gl_Draw_Character (int x, int y, unsigned ch);
void gl_Draw_String (int x, int y, const char *str);
void gl_Draw_nString (int x, int y, const char *str, int count);

View file

@ -30,9 +30,11 @@
struct qpic_s;
struct rfont_s;
struct draw_charbuffer_s;
void glsl_Draw_Init (void);
void glsl_Draw_Shutdown (void);
void glsl_Draw_CharBuffer (int x, int y, struct draw_charbuffer_s *buffer);
void glsl_Draw_Character (int x, int y, unsigned ch);
void glsl_Draw_String (int x, int y, const char *str);
void glsl_Draw_nString (int x, int y, const char *str, int count);

View file

@ -32,7 +32,10 @@ struct vulkan_ctx_s;
struct qfv_renderframe_s;
struct qpic_s;
struct rfont_s;
struct draw_charbuffer_s;
void Vulkan_Draw_CharBuffer (int x, int y, struct draw_charbuffer_s *buffer,
struct vulkan_ctx_s *ctx);
void Vulkan_Draw_Init (struct vulkan_ctx_s *ctx);
void Vulkan_Draw_Shutdown (struct vulkan_ctx_s *ctx);
void Vulkan_Draw_Character (int x, int y, unsigned ch,

View file

@ -41,6 +41,23 @@
#include "QF/wad.h"
/** Buffer for drawing text using quake conchars or the default 8x8 font.
Characters are stored with the first character in the upper left, scanning
horizontally to the right.
*/
typedef struct draw_charbuffer_s {
int width; ///< width in character cells
int height; ///< height in character cells
char *chars; ///< width * height characters
} draw_charbuffer_t;
draw_charbuffer_t *Draw_CreateBuffer (int width, int height);
void Draw_DestroyBuffer (draw_charbuffer_t *buffer);
void Draw_ClearBuffer (draw_charbuffer_t *buffer);
void Draw_ScrollBuffer (draw_charbuffer_t *buffer, int lines);
void Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer);
extern byte *draw_chars;
/** Initialize the draw stuff.

View file

@ -79,10 +79,13 @@ typedef struct vid_model_funcs_s {
struct tex_s;
struct rfont_s;
struct draw_charbuffer_s;
typedef void (*capfunc_t) (struct tex_s *screencap, void *data);
typedef struct vid_render_funcs_s {
void (*init) (void);
void (*Draw_CharBuffer) (int x, int y, struct draw_charbuffer_s *buffer);
void (*Draw_Character) (int x, int y, unsigned ch);
void (*Draw_String) (int x, int y, const char *str);
void (*Draw_nString) (int x, int y, const char *str, int count);

View file

@ -226,4 +226,7 @@ extern float r_skytime;
extern int c_surf;
struct draw_charbuffer_s;
void sw_Draw_CharBuffer (int x, int y, struct draw_charbuffer_s *buffer);
#endif // _D_IFACE_H

View file

@ -50,6 +50,7 @@ libs_video_renderer_libQFrenderer_la_DEPENDENCIES= $(renderer_libs)
libs_video_renderer_libQFrenderer_la_SOURCES=\
libs/video/renderer/r_bsp.c \
libs/video/renderer/r_cvar.c \
libs/video/renderer/r_draw.c \
libs/video/renderer/r_efrag.c \
libs/video/renderer/r_fog.c \
libs/video/renderer/r_font.c \

View file

@ -486,6 +486,21 @@ tVA_increment (void)
flush_text ();
}
void
gl_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer)
{
const byte *line = (byte *) buffer->chars;
int width = buffer->width;
int height = buffer->height;
while (height-- > 0) {
for (int i = 0; i < width; i++) {
gl_Draw_Character (x + i * 8, y, line[i]);
}
line += width;
y += 8;
}
}
/*
Draw_Character

View file

@ -485,6 +485,21 @@ flush_2d (void)
line_queue->size = 0;
}
void
glsl_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer)
{
const byte *line = (byte *) buffer->chars;
int width = buffer->width;
int height = buffer->height;
while (height-- > 0) {
for (int i = 0; i < width; i++) {
glsl_Draw_Character (x + i * 8, y, line[i]);
}
line += width;
y += 8;
}
}
void
glsl_Draw_Character (int x, int y, unsigned int chr)
{

View file

@ -0,0 +1,88 @@
/*
r_draw.c
Renderer general draw support
Copyright (C) 2022 Bill Currie <bill@taniwha.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "QF/draw.h"
#include "QF/render.h"
#include "QF/plugin/vid_render.h"
VISIBLE draw_charbuffer_t *
Draw_CreateBuffer (int width, int height)
{
size_t size = width * height;
draw_charbuffer_t *buffer = malloc (sizeof (draw_charbuffer_t) + size);
buffer->width = width;
buffer->height = height;
buffer->chars = (char *) (buffer + 1);
return buffer;
}
VISIBLE void
Draw_DestroyBuffer (draw_charbuffer_t *buffer)
{
free (buffer);
}
VISIBLE void
Draw_ClearBuffer (draw_charbuffer_t *buffer)
{
memset (buffer->chars, ' ', buffer->height * buffer->width);
}
VISIBLE void
Draw_ScrollBuffer (draw_charbuffer_t *buffer, int lines)
{
int down = 0;
if (lines < 0) {
lines = -lines;
down = 1;
}
if (lines > buffer->height) {
lines = buffer->height;
}
if (down) {
memmove (buffer->chars + lines * buffer->width, buffer->chars,
(buffer->height - lines) * buffer->width);
memset (buffer->chars, ' ', lines * buffer->width);
} else {
memmove (buffer->chars, buffer->chars + lines * buffer->width,
(buffer->height - lines) * buffer->width);
memset (buffer->chars + (buffer->height - lines) * buffer->width, ' ',
lines * buffer->width);
}
}
VISIBLE void
Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer)
{
r_funcs->Draw_CharBuffer (x, y, buffer);
}

View file

@ -63,10 +63,17 @@ typedef struct qpic_res_s {
char *name;
} qpic_res_t;
typedef struct bi_charbuff_s {
struct bi_charbuff_s *next;
draw_charbuffer_t *buffer;
} bi_charbuff_t;
typedef struct {
PR_RESMAP (qpic_res_t) qpic_map;
PR_RESMAP (bi_charbuff_t) charbuff_map;
qpic_res_t *qpics;
hashtab_t *pic_hash;
bi_charbuff_t *charbuffs;
} draw_resources_t;
static qpic_res_t *
@ -121,6 +128,47 @@ get_qpic (progs_t *pr, draw_resources_t *res, const char *name, int qpic_handle)
return qp;
}
static bi_charbuff_t *
charbuff_new (draw_resources_t *res)
{
return PR_RESNEW (res->charbuff_map);
}
static void
charbuff_free (draw_resources_t *res, bi_charbuff_t *cbuff)
{
Draw_DestroyBuffer (cbuff->buffer);
PR_RESFREE (res->charbuff_map, cbuff);
}
static void
charbuff_reset (draw_resources_t *res)
{
PR_RESRESET (res->charbuff_map);
}
static inline bi_charbuff_t *
charbuff_get (draw_resources_t *res, int index)
{
return PR_RESGET (res->charbuff_map, index);
}
static inline int __attribute__((pure))
charbuff_index (draw_resources_t *res, bi_charbuff_t *qp)
{
return PR_RESINDEX (res->charbuff_map, qp);
}
static bi_charbuff_t * __attribute__((pure))
get_charbuff (progs_t *pr, draw_resources_t *res, const char *name, int charbuff_handle)
{
bi_charbuff_t *cbuff = charbuff_get (res, charbuff_handle);
if (!cbuff)
PR_RunError (pr, "invalid charbuff handle passed to %s", name + 3);
return cbuff;
}
static void
bi_Draw_FreePic (progs_t *pr, void *_res)
{
@ -354,6 +402,66 @@ bi_Font_String (progs_t *pr, void *_res)
r_funcs->Draw_FontString (x, y, str);
}
static void
bi_Draw_CreateBuffer (progs_t *pr, void *_res)
{
draw_resources_t *res = _res;
int width = P_INT (pr, 0);
int height = P_INT (pr, 1);
draw_charbuffer_t *buffer;
bi_charbuff_t *cbuff;
buffer = Draw_CreateBuffer (width, height);
cbuff = charbuff_new (res);
cbuff->next = res->charbuffs;
res->charbuffs = cbuff;
cbuff->buffer = buffer;
R_INT (pr) = charbuff_index (res, cbuff);
}
static void
bi_Draw_DestroyBuffer (progs_t *pr, void *_res)
{
draw_resources_t *res = _res;
pr_ptr_t cbuff_handle = P_POINTER (pr, 0);
bi_charbuff_t *cbuff = get_charbuff (pr, res, __FUNCTION__, cbuff_handle);
charbuff_free (res, cbuff);
}
static void
bi_Draw_ClearBuffer (progs_t *pr, void *_res)
{
draw_resources_t *res = _res;
pr_ptr_t cbuff_handle = P_POINTER (pr, 0);
bi_charbuff_t *cbuff = get_charbuff (pr, res, __FUNCTION__, cbuff_handle);
Draw_ClearBuffer (cbuff->buffer);
}
static void
bi_Draw_ScrollBuffer (progs_t *pr, void *_res)
{
draw_resources_t *res = _res;
pr_ptr_t cbuff_handle = P_POINTER (pr, 0);
bi_charbuff_t *cbuff = get_charbuff (pr, res, __FUNCTION__, cbuff_handle);
int lines = P_INT (pr, 1);
Draw_ScrollBuffer (cbuff->buffer, lines);
}
static void
bi_Draw_CharBuffer (progs_t *pr, void *_res)
{
draw_resources_t *res = _res;
int x = P_INT (pr, 0);
int y = P_INT (pr, 1);
pr_ptr_t cbuff_handle = P_POINTER (pr, 2);
bi_charbuff_t *cbuff = get_charbuff (pr, res, __FUNCTION__, cbuff_handle);
Draw_CharBuffer (x, y, cbuff->buffer);
}
static const char *
bi_draw_get_key (const void *p, void *unused)
{
@ -365,13 +473,20 @@ bi_draw_clear (progs_t *pr, void *_res)
{
draw_resources_t *res = (draw_resources_t *) _res;
qpic_res_t *qp;
bi_charbuff_t *cbuff;
for (qp = res->qpics; qp; qp = qp->next) {
bi_draw_free_qpic (qp);
}
res->qpics = 0;
for (cbuff = res->charbuffs; cbuff; cbuff = cbuff->next) {
Draw_DestroyBuffer (cbuff->buffer);
}
res->charbuffs = 0;
qpic_reset (res);
charbuff_reset (res);
Hash_FlushTable (res->pic_hash);
}
@ -406,6 +521,12 @@ static builtin_t builtins[] = {
bi(Font_Load, 2, p(string), p(int)),
bi(Font_String, 3, p(int), p(int), p(string)),
bi(Draw_CreateBuffer, 2, p(int), p(int)),
bi(Draw_DestroyBuffer, 1, p(ptr)),
bi(Draw_ClearBuffer, 1, p(ptr)),
bi(Draw_ScrollBuffer, 2, p(ptr), p(int)),
bi(Draw_CharBuffer, 3, p(int), p(int), p(ptr)),
{0}
};

View file

@ -245,7 +245,6 @@ Draw_Init (void)
}
}
/*
Draw_Character
@ -306,6 +305,21 @@ Draw_Character (int x, int y, unsigned int chr)
}
}
void
sw_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer)
{
const byte *line = (byte *) buffer->chars;
int width = buffer->width;
int height = buffer->height;
while (height-- > 0) {
for (int i = 0; i < width; i++) {
Draw_Character (x + i * 8, y, line[i]);
}
line += width;
y += 8;
}
}
void
Draw_String (int x, int y, const char *str)

View file

@ -486,6 +486,7 @@ gl_capture_screen (capfunc_t callback, void *data)
vid_render_funcs_t gl_vid_render_funcs = {
gl_vid_render_init,
gl_Draw_CharBuffer,
gl_Draw_Character,
gl_Draw_String,
gl_Draw_nString,

View file

@ -430,6 +430,7 @@ glsl_capture_screen (capfunc_t callback, void *data)
vid_render_funcs_t glsl_vid_render_funcs = {
glsl_vid_render_init,
glsl_Draw_CharBuffer,
glsl_Draw_Character,
glsl_Draw_String,
glsl_Draw_nString,

View file

@ -450,6 +450,7 @@ sw_capture_screen (capfunc_t callback, void *data)
vid_render_funcs_t sw_vid_render_funcs = {
sw_vid_render_init,
sw_Draw_CharBuffer,
Draw_Character,
Draw_String,
Draw_nString,

View file

@ -135,6 +135,12 @@ vulkan_R_LineGraph (int x, int y, int *h_vals, int count, int height)
{
}
static void
vulkan_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer)
{
Vulkan_Draw_CharBuffer (x, y, buffer, vulkan_ctx);
}
static void
vulkan_Draw_Character (int x, int y, unsigned ch)
{
@ -740,6 +746,7 @@ vulkan_vid_render_shutdown (void)
vid_render_funcs_t vulkan_vid_render_funcs = {
vulkan_vid_render_init,
vulkan_Draw_CharBuffer,
vulkan_Draw_Character,
vulkan_Draw_String,
vulkan_Draw_nString,

View file

@ -571,6 +571,22 @@ queue_character (int x, int y, byte chr, vulkan_ctx_t *ctx)
&frame->quad_verts);
}
void
Vulkan_Draw_CharBuffer (int x, int y, draw_charbuffer_t *buffer,
vulkan_ctx_t *ctx)
{
const byte *line = (byte *) buffer->chars;
int width = buffer->width;
int height = buffer->height;
while (height-- > 0) {
for (int i = 0; i < width; i++) {
Vulkan_Draw_Character (x + i * 8, y, line[i], ctx);
}
line += width;
y += 8;
}
}
void
Vulkan_Draw_Character (int x, int y, unsigned int chr, vulkan_ctx_t *ctx)
{