parent
805f633af0
commit
dcce0ed11b
@ -1,7 +1,7 @@ |
||||
LDFLAGS = -lm -lgrapheme
|
||||
LDFLAGS = -lm -lgrapheme -lSDL2 -lGLEW -lGL
|
||||
CFLAGS = -g -Wall -Wextra -pedantic
|
||||
SOURCE = main.c font.c cache.c hash.c util.c
|
||||
HEADER = font.h hash.h cache.h util.h
|
||||
SOURCE = main.c font.c cache.c hash.c util.c ren.c
|
||||
HEADER = font.h hash.h cache.h util.h ren.h
|
||||
|
||||
test: ${HEADER} ${SOURCE} |
||||
gcc ${CFLAGS} ${LDFLAGS} ${SOURCE} -o test
|
||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
@ -1,33 +1,30 @@ |
||||
#include <stdlib.h> |
||||
#include <stdio.h> |
||||
#include <grapheme.h> |
||||
|
||||
#include "cache.h" |
||||
#include "font.h" |
||||
#include "ren.h" |
||||
#include "util.h" |
||||
|
||||
|
||||
int main(void) |
||||
{ |
||||
int err; |
||||
struct font_atlas *at = font_init(); |
||||
err = font_load(at, "./monospace.ttf"); |
||||
ERROR(err, -1, printf("failed to load font\n")); |
||||
|
||||
const char *s = "ciao mamma"; |
||||
const struct font_glyph *g; |
||||
size_t ret, off; |
||||
uint_least32_t cp; |
||||
for (off = 0; (ret = grapheme_decode_utf8(s+off, SIZE_MAX, &cp)) > 0 && cp != 0; off += ret) { |
||||
printf("%.*s (%d) -> %d\n", (int)ret, s+off, (int)ret, cp); |
||||
g = font_get_glyph_texture(at, cp); |
||||
if (!g) |
||||
printf("g is NULL\n"); |
||||
SDL_Window *win = SDL_CreateWindow( |
||||
"test render", |
||||
SDL_WINDOWPOS_UNDEFINED, |
||||
SDL_WINDOWPOS_UNDEFINED, |
||||
500, |
||||
500, |
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); |
||||
if (ren_init(win)) { |
||||
printf("renderer init error: %s\n", ren_strerror()); |
||||
return 1; |
||||
} |
||||
|
||||
font_dump(at, "./atlas.png"); |
||||
ren_render_text("ciao mamma", 100, 100, 100, 100, 12); |
||||
SDL_GL_SwapWindow(win); |
||||
|
||||
font_free(at); |
||||
while(1); |
||||
|
||||
ren_free(); |
||||
SDL_DestroyWindow(win); |
||||
return 0; |
||||
} |
||||
|
@ -1,53 +1,423 @@ |
||||
#include <SDL2/SDL.h> |
||||
#include <GL/glew.h> |
||||
#include <SDL2/SDL_opengl.h> |
||||
#include <SDL2/SDL_video.h> |
||||
#include <grapheme.h> |
||||
#include <stdio.h> |
||||
|
||||
#include "util.h" |
||||
#include "font.h" |
||||
#include "ren.h" |
||||
|
||||
#define GLSL_VERT_SHADER "vertex.glsl" |
||||
#define GLSL_FRAG_SHADER "fragment.glsl" |
||||
#define PACKED __attribute__((packed)) |
||||
|
||||
enum REN_ERR { |
||||
REN_SUCCESS = 0, |
||||
REN_ERRNO, |
||||
REN_INVAL, |
||||
REN_VERTEX, |
||||
REN_FRAGMENT, |
||||
REN_PROGRAM, |
||||
REN_COMPILE, |
||||
REN_LINK, |
||||
REN_TEXTURE, |
||||
REN_CONTEXT, |
||||
REN_GLEW, |
||||
REN_FONT, |
||||
REN_BUFFER, |
||||
}; |
||||
|
||||
|
||||
// TODO: make a macro for enum-associated string arrays
|
||||
const char * ren_err_msg[] = { |
||||
[REN_SUCCESS] = "Success", |
||||
[REN_ERRNO] = "Look at errno", |
||||
[REN_INVAL] = "Invalid or NULL arguments", |
||||
[REN_VERTEX] = "Failed to create opengl vertex shader", |
||||
[REN_FRAGMENT] = "Failed to create opengl fragment shader", |
||||
[REN_PROGRAM] = "Failed to create opengl program", |
||||
[REN_COMPILE] = "Failed to compile shaders", |
||||
[REN_LINK] = "Failed to link shaders", |
||||
[REN_TEXTURE] = "Failed to create texture", |
||||
[REN_CONTEXT] = "Failed to create SDL OpenGL context", |
||||
[REN_GLEW] = "GLEW Error", |
||||
[REN_FONT] = "Font Error", |
||||
[REN_BUFFER] = "Failed to create opengl buffer", |
||||
}; |
||||
|
||||
// different stacks
|
||||
#include "stack.h" |
||||
STACK_DECL(vtstack, struct v_text) |
||||
STACK_DECL(vcstack, struct v_col) |
||||
|
||||
|
||||
struct { |
||||
SDL_Window *w; |
||||
SDL_GLContext *gl; |
||||
GLuint gl_vertbuffer; |
||||
GLuint gl_program; |
||||
struct font_atlas *font; |
||||
GLuint font_texture; |
||||
GLuint font_prog; |
||||
GLuint box_prog; |
||||
GLuint font_buffer; |
||||
struct vtstack font_stack; |
||||
struct vcstack box_stack; |
||||
} ren = {0}; |
||||
|
||||
|
||||
typedef struct PACKED { |
||||
union { GLint x, u; }; |
||||
union { GLint y, v; }; |
||||
} vec2_i; |
||||
static int ren_errno; |
||||
|
||||
typedef struct PACKED { |
||||
union { GLint x, r; }; |
||||
union { GLint y, g; }; |
||||
union { GLint z, b; }; |
||||
union { GLint w, a; }; |
||||
} vec4_i; |
||||
|
||||
// textured vertex
|
||||
struct PACKED v_text { |
||||
vec2_i pos; |
||||
vec2_i uv; |
||||
}; |
||||
// print shader compilation errors
|
||||
static int shader_compile_error(GLuint shader) |
||||
{ |
||||
GLint status; |
||||
glGetShaderiv(shader, GL_COMPILE_STATUS, &status); |
||||
if (status != GL_FALSE) |
||||
return 0; |
||||
|
||||
// colored vertex
|
||||
struct PACKED v_col { |
||||
vec2_i pos; |
||||
vec2_i col; |
||||
}; |
||||
GLint log_length; |
||||
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); |
||||
|
||||
GLchar *log_str = emalloc((log_length + 1)*sizeof(GLchar)); |
||||
glGetShaderInfoLog(shader, log_length, NULL, log_str); |
||||
|
||||
// different stacks
|
||||
#define _STACK_NAME vtstack |
||||
#define _STACK_TYPE struct v_text |
||||
#include "stack.h" |
||||
#undef _STACK_NAME |
||||
#undef _STACK_TYPE |
||||
#define _STACK_NAME vcstack |
||||
#define _STACK_TYPE struct v_col |
||||
#include "stack.h" |
||||
const char *shader_type_str = NULL; |
||||
GLint shader_type; |
||||
glGetShaderiv(shader, GL_SHADER_TYPE, &shader_type); |
||||
switch(shader_type) { |
||||
case GL_VERTEX_SHADER: shader_type_str = "vertex"; break; |
||||
case GL_GEOMETRY_SHADER: shader_type_str = "geometry"; break; |
||||
case GL_FRAGMENT_SHADER: shader_type_str = "fragment"; break; |
||||
} |
||||
|
||||
fprintf(stderr, "Compile failure in %s shader:\n%s\n", shader_type_str, log_str); |
||||
efree(log_str); |
||||
return -1; |
||||
} |
||||
|
||||
|
||||
// print shader link errors
|
||||
static int shader_link_error(GLuint prog) |
||||
{ |
||||
GLint status; |
||||
glGetProgramiv (prog, GL_LINK_STATUS, &status); |
||||
if (status != GL_FALSE) |
||||
return 0; |
||||
|
||||
GLint log_length; |
||||
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_length); |
||||
|
||||
GLchar *log_str = emalloc((log_length + 1)*sizeof(GLchar)); |
||||
glGetProgramInfoLog(prog, log_length, NULL, log_str); |
||||
fprintf(stderr, "Linker failure: %s\n", log_str); |
||||
efree(log_str); |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
|
||||
#define REN_RET(a,b) {ren_errno = b; return a;} |
||||
static GLuint shader_compile(const char *shader, GLuint type) |
||||
{ |
||||
GLuint s; |
||||
// initialize the vertex shader and get the corresponding id
|
||||
s = glCreateShader(type); |
||||
if (!s) REN_RET(0, REN_VERTEX) |
||||
// get the shader into opengl
|
||||
glShaderSource(s, 1, &shader, NULL); |
||||
glCompileShader(s); |
||||
if (shader_compile_error(s)) |
||||
REN_RET(0, REN_COMPILE) |
||||
return s; |
||||
} |
||||
|
||||
|
||||
const char * ren_strerror(void) |
||||
{ |
||||
return ren_err_msg[ren_errno % (sizeof(ren_err_msg)/sizeof(char *))]; |
||||
} |
||||
|
||||
|
||||
// compile a vertex shader (vs_path) and a fragment shader (fs_path) into an opengl
|
||||
// program and return it's index
|
||||
static GLuint ren_compile_program(const char *vs_path, const char *fs_path) |
||||
{ |
||||
GLuint gl_vertshader, gl_fragshader, prog; |
||||
|
||||
if (!vs_path || !fs_path) |
||||
REN_RET(0, REN_INVAL) |
||||
|
||||
char *str = NULL; |
||||
|
||||
dump_file(vs_path, &str, NULL); |
||||
if (!str) REN_RET(0, REN_ERRNO) |
||||
gl_vertshader = shader_compile(str, GL_VERTEX_SHADER); |
||||
efree(str); |
||||
if (!gl_vertshader) |
||||
return 0; |
||||
|
||||
dump_file(fs_path, &str, NULL); |
||||
if (!str) REN_RET(0, REN_ERRNO) |
||||
gl_fragshader = shader_compile(str, GL_FRAGMENT_SHADER); |
||||
efree(str); |
||||
if (!gl_fragshader) |
||||
return 0; |
||||
|
||||
|
||||
// create the main program object, it is an amalgamation of all shaders
|
||||
prog = glCreateProgram(); |
||||
if (!prog) REN_RET(0, REN_PROGRAM) |
||||
|
||||
// attach the shaders to the program (set which shaders are present)
|
||||
glAttachShader(prog, gl_vertshader); |
||||
glAttachShader(prog, gl_fragshader); |
||||
// then link the program (basically the linking stage of the program)
|
||||
glLinkProgram(prog); |
||||
if (shader_link_error(prog)) |
||||
REN_RET(0, REN_LINK) |
||||
|
||||
// after linking the shaders can be detached and the source freed from
|
||||
// memory since the program is ready to use
|
||||
glDetachShader(prog, gl_vertshader); |
||||
glDetachShader(prog, gl_fragshader); |
||||
|
||||
return prog; |
||||
} |
||||
|
||||
|
||||
static GLuint ren_texturergb_2d(const char *buf, int w, int h, int upscale, int downscale) |
||||
{ |
||||
GLuint t; |
||||
|
||||
if (!buf || w <= 0 || h <= 0) |
||||
REN_RET(0, REN_INVAL) |
||||
|
||||
glGenTextures(1, &t); |
||||
if (!t) REN_RET(0, REN_TEXTURE) |
||||
|
||||
glBindTexture(GL_TEXTURE_2D, t); |
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, buf); |
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, downscale); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, upscale); |
||||
|
||||
return t; |
||||
} |
||||
|
||||
|
||||
static GLuint ren_texturergba_2d(const char *buf, int w, int h, int upscale, int downscale) |
||||
{ |
||||
GLuint t; |
||||
|
||||
if (!buf || w <= 0 || h <= 0) |
||||
REN_RET(0, REN_INVAL) |
||||
|
||||
glGenTextures(1, &t); |
||||
if (!t) REN_RET(0, REN_TEXTURE) |
||||
|
||||
glBindTexture(GL_TEXTURE_2D, t); |
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); |
||||
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, downscale); |
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, upscale); |
||||
|
||||
return t; |
||||
} |
||||
|
||||
|
||||
// FIXME: update only the newly generated character instead of the whole texture
|
||||
static int update_font_texture(void) |
||||
{ |
||||
glTexSubImage2D( |
||||
ren.font_texture, |
||||
0, 0, 0, |
||||
ren.font->width, |
||||
ren.font->height, |
||||
GL_RGB, |
||||
GL_UNSIGNED_BYTE, |
||||
ren.font->atlas); |
||||
font_dump(ren.font, "./atlas.png"); |
||||
return 0; |
||||
} |
||||
|
||||
|
||||
// TODO: push window size uniforms
|
||||
int ren_init(SDL_Window *w) |
||||
{ |
||||
if (!w) |
||||
REN_RET(-1, REN_INVAL) |
||||
ren.gl = SDL_GL_CreateContext(w); |
||||
if (!ren.gl) |
||||
REN_RET(-1, REN_CONTEXT) |
||||
|
||||
// select some features
|
||||
glEnable(GL_BLEND); |
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); |
||||
glDisable(GL_CULL_FACE); |
||||
glDisable(GL_DEPTH_TEST); |
||||
glEnable(GL_SCISSOR_TEST); |
||||
glEnable(GL_TEXTURE_2D); |
||||
|
||||
GLenum glew_err = glewInit(); |
||||
if (glew_err != GLEW_OK) |
||||
REN_RET(glew_err, REN_GLEW); |
||||
|
||||
ren.font = font_init(); |
||||
if (!ren.font) REN_RET(-1, REN_FONT)
|
||||
if (font_load(ren.font, FONT_PATH)) REN_RET(-1, REN_FONT) |
||||
|
||||
ren.font_texture = ren_texturergb_2d( |
||||
(const char *)ren.font->atlas, |
||||
ren.font->width, |
||||
ren.font->height, |
||||
GL_LINEAR, GL_LINEAR); |
||||
if (!ren.font_texture) return -1; |
||||
|
||||
ren.font_prog = ren_compile_program(FONT_VERSHADER, FONT_FRAGSHADER); |
||||
if (!ren.font_prog) return -1; |
||||
|
||||
ren.font_stack = vtstack_init(); |
||||
ren.box_stack = vcstack_init();
|
||||
|
||||
glGenBuffers(1, &ren.font_buffer); |
||||
if (!ren.font_buffer) REN_RET(-1, REN_BUFFER) |
||||
glBindBuffer(GL_ARRAY_BUFFER, ren.font_buffer); |
||||
glEnableVertexAttribArray(REN_VERTEX_IDX); |
||||
glVertexAttribPointer( |
||||
REN_VERTEX_IDX, |
||||
2, |
||||
GL_INT, |
||||
GL_FALSE, |
||||
sizeof(struct v_text), |
||||
0); |
||||
glEnableVertexAttribArray(REN_UV_IDX); |
||||
glVertexAttribPointer( |
||||
REN_UV_IDX, |
||||
2, |
||||
GL_INT, |
||||
GL_FALSE, |
||||
sizeof(struct v_text), |
||||
(void*)sizeof(vec2_i)); |
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); |
||||
|
||||
int width, height; |
||||
SDL_GetWindowSize(w, &width, &height); |
||||
ren_update_viewport(width, height); |
||||
glClearColor(0.3f, 0.3f, 0.3f, 0.f); |
||||
glClear(GL_COLOR_BUFFER_BIT); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
|
||||
static int ren_draw_font_stack(void) |
||||
{ |
||||
glUseProgram(ren.font_prog); |
||||
glBindBuffer(GL_ARRAY_BUFFER, ren.font_buffer); |
||||
|
||||
// TODO: implement size and damage tracking on stacks
|
||||
glBufferData( |
||||
GL_ARRAY_BUFFER, |
||||
ren.font_stack.idx*sizeof(struct v_text), |
||||
ren.font_stack.items, |
||||
GL_DYNAMIC_DRAW); |
||||
glDrawArrays(GL_TRIANGLES, 0, ren.font_stack.idx); |
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); |
||||
glUseProgram(0); |
||||
return 0; |
||||
} |
||||
|
||||
|
||||
int ren_update_viewport(int w, int h) |
||||
{ |
||||
glViewport(0, 0, w, h); |
||||
glUniform2i(REN_SCREENSIZE_LOC, w, h); |
||||
return 0; |
||||
} |
||||
|
||||
|
||||
// TODO: implement font size
|
||||
int ren_render_text(const char *str, int x, int y, int w, int h, int size) |
||||
{ |
||||
// x4,y4 x3,y3
|
||||
// +-------------+
|
||||
// |(x,y) /|
|
||||
// | / |
|
||||
// | 2 / |
|
||||
// | / |
|
||||
// | / |
|
||||
// | / 1 |
|
||||
// |/ |
|
||||
// +-------------+
|
||||
// x1,y1 x2,y2
|
||||
|
||||
const struct font_glyph *g; |
||||
size_t ret, off; |
||||
uint_least32_t cp; |
||||
int updated, gx = x, gy = y; |
||||
struct v_text v; |
||||
for (off = 0; (ret = grapheme_decode_utf8(str+off, SIZE_MAX, &cp)) > 0 && cp != 0; off += ret) { |
||||
g = font_get_glyph_texture(ren.font, cp, &updated); |
||||
if (!g) REN_RET(-1, REN_FONT); |
||||
if (updated) { |
||||
if (update_font_texture()) |
||||
REN_RET(-1, REN_TEXTURE); |
||||
} |
||||
|
||||
// x1,y1
|
||||
v = (struct v_text){ |
||||
.pos = { .x = gx, .y = gy+g->h }, |
||||
.uv = { .u = g->u, .v = g->v+g->h }, |
||||
}; |
||||
vtstack_push(&ren.font_stack, &v); |
||||
// x2,y2
|
||||
v = (struct v_text){ |
||||
.pos = { .x = gx+g->w, .y = gy+g->h }, |
||||
.uv = { .u = g->u+g->w, .v = g->v+g->h }, |
||||
}; |
||||
vtstack_push(&ren.font_stack, &v); |
||||
// x3,y3
|
||||
v = (struct v_text){ |
||||
.pos = { .x = gx+g->w, .y = gy }, |
||||
.uv = { .u = g->u+g->w, .v = g->v }, |
||||
}; |
||||
vtstack_push(&ren.font_stack, &v); |
||||
// x4,y4
|
||||
v = (struct v_text){ |
||||
.pos = { .x = gx, .y = gy }, |
||||
.uv = { .u = g->u, .v = g->v }, |
||||
}; |
||||
vtstack_push(&ren.font_stack, &v); |
||||
|
||||
// TODO: possible kerning needs to be applied here
|
||||
gx += g->w; |
||||
if (cp == '\n') |
||||
gy += 20; // FIXME: encode and/or store line height
|
||||
} |
||||
|
||||
// FIXME: here we are doing one draw call for string of text which is
|
||||
// inefficient but is simpler and allows for individual scissors
|
||||
glScissor(x, y, w, h); |
||||
ren_draw_font_stack(); |
||||
vtstack_clear(&ren.font_stack); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
|
||||
int ren_free(void) |
||||
{ |
||||
glUseProgram(0); |
||||
glBindBuffer(GL_ARRAY_BUFFER, 0); |
||||
glDeleteTextures(1, &ren.font_texture); |
||||
glDeleteBuffers(1, &ren.font_buffer); |
||||
SDL_GL_DeleteContext(ren.gl); |
||||
vtstack_free(&ren.font_stack); |
||||
vcstack_free(&ren.box_stack); |
||||
font_free(ren.font); |
||||
return 0; |
||||
} |
||||
|
@ -0,0 +1,48 @@ |
||||
#ifndef _RENDERER_H |
||||
#define _RENDERER_H |
||||
|
||||
#include <SDL2/SDL.h> |
||||
|
||||
|
||||
#define FONT_PATH "./monospace.ttf" |
||||
#define FONT_VERSHADER "./font_vertshader.glsl" |
||||
#define FONT_FRAGSHADER "./font_fragshader.glsl" |
||||
#define REN_VERTEX_IDX 0 |
||||
#define REN_UV_IDX 1 |
||||
#define REN_COLOR_IDX 2 |
||||
#define REN_SCREENSIZE_LOC 0 |
||||
|
||||
|
||||
typedef struct { |
||||
union { int x, u; }; |
||||
union { int y, v; }; |
||||
} vec2_i; |
||||
|
||||
typedef struct { |
||||
union { int x, r; }; |
||||
union { int y, g; }; |
||||
union { int z, b; }; |
||||
union { int w, a; }; |
||||
} vec4_i; |
||||
|
||||
// textured vertex
|
||||
struct v_text { |
||||
vec2_i pos; |
||||
vec2_i uv; |
||||
}; |
||||
|
||||
// colored vertex
|
||||
struct v_col { |
||||
vec2_i pos; |
||||
vec2_i col; |
||||
}; |
||||
|
||||
|
||||
int ren_init(SDL_Window *sdl_window); |
||||
int ren_free(void); |
||||
const char * ren_strerror(void); |
||||
int ren_update_viewport(int w, int h); |
||||
int ren_render_text(const char *str, int x, int y, int w, int h, int size); |
||||
|
||||
|
||||
#endif |
@ -1,61 +1,72 @@ |
||||
#ifdef _STACK_TYPE |
||||
#ifdef _STACK_NAME |
||||
#ifndef _STACK_GENERIC_H |
||||
#define _STACK_GENERIC_H |
||||
|
||||
|
||||
#ifndef _STACK_REALLOC |
||||
#define _STACK_REALLOC(p, s) realloc(p,s) |
||||
#endif |
||||
#ifndef _STACK_MEMSET |
||||
#define _STACK_MEMSET(p, c, s) memset(p,c,s) |
||||
#endif |
||||
#ifndef _STACK_STEP |
||||
#define _STACK_STEP 8 |
||||
#endif |
||||
#ifndef _STACK_CAT |
||||
#define _STACK_CAT_I(a,b) a ## b |
||||
#define _STACK_CAT(a,b) _STACK_CAT_I(a,b) |
||||
#endif |
||||
#define STACK_STEP 8 |
||||
|
||||
|
||||
// TODO: add a rolling hash
|
||||
struct _STACK_NAME { |
||||
_STACK_TYPE *items; |
||||
int size, idx; |
||||
}; |
||||
|
||||
|
||||
int _STACK_CAT(stack_grow_,_STACK_NAME)(struct _STACK_NAME *stack, int step) |
||||
{ |
||||
if (!stack) |
||||
return -1; |
||||
stack->items = _STACK_REALLOC(stack->items, (stack->size+step)*sizeof(_STACK_TYPE)); |
||||
if(!stack->items) |
||||
return -1; |
||||
_STACK_MEMSET(&(stack->items[stack->size]), 0, step*sizeof(*(stack->items))); |
||||
stack->size += step; |
||||
return 0; |
||||
} |
||||
#define STACK_DECL(stackname, type) \ |
||||
struct stackname { \
|
||||
type *items; \
|
||||
int size, idx; \
|
||||
}; \
|
||||
\
|
||||
\
|
||||
struct stackname stackname##_init(void) { return (struct stackname){0}; } \
|
||||
\
|
||||
\
|
||||
int stackname##_grow(struct stackname *stack, int step) \
|
||||
{ \
|
||||
if (!stack) \
|
||||
return -1; \
|
||||
stack->items = realloc(stack->items, (stack->size+step)*sizeof(type)); \
|
||||
if(!stack->items) \
|
||||
return -1; \
|
||||
memset(&(stack->items[stack->size]), 0, step*sizeof(*(stack->items))); \
|
||||
stack->size += step; \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
\
|
||||
int stackname##_push(struct stackname *stack, type *e) \
|
||||
{ \
|
||||
if (!stack || !e) \
|
||||
return -1; \
|
||||
if (stack->idx >= stack->size) \
|
||||
if (stackname##_grow(stack, STACK_STEP)) \
|
||||
return -1; \
|
||||
stack->items[stack->idx++] = *e; \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
\
|
||||
type stackname##_pop(struct stackname *stack) \
|
||||
{ \
|
||||
if (!stack || stack->idx == 0 || stack->size == 0) \
|
||||
return (type){0}; \
|
||||
return stack->items[stack->idx--]; \
|
||||
} \
|
||||
\
|
||||
\
|
||||
int stackname##_clear(struct stackname *stack) \
|
||||
{ \
|
||||
if (!stack) \
|
||||
return -1; \
|
||||
stack->idx = 0; \
|
||||
return 0; \
|
||||
} \
|
||||
\
|
||||
\
|
||||
int stackname##_free(struct stackname *stack) \
|
||||
{ \
|
||||
if (stack) { \
|
||||
stackname##_clear(stack); \
|
||||
if (stack->items) \
|
||||
free(stack->items); \
|
||||
} \
|
||||
return 0; \
|
||||
} \
|
||||
|
||||
|
||||
int _STACK_CAT(stack_push_,_STACK_NAME)(struct _STACK_NAME *stack, _STACK_TYPE *e) |
||||
{ |
||||
if (!stack || !e) |
||||
return -1; |
||||
if (stack->idx >= stack->size) |
||||
if (_STACK_CAT(stack_grow_,_STACK_NAME)(stack, _STACK_STEP)) |
||||
return -1; |
||||
stack->items[stack->idx++] = *e; |
||||
return 0; |
||||
} |
||||
|
||||
|
||||
int _STACK_CAT(stack_clear_,_STACK_NAME)(struct _STACK_NAME *stack) |
||||
{ |
||||
if (!stack) |
||||
return -1; |
||||
stack->idx = 0; |
||||
} |
||||
|
||||
|
||||
#endif |
||||
#endif |
||||
|
Binary file not shown.
Loading…
Reference in new issue