[RELEASE] u22-v03
This version is submitted to U22 breau.
This commit is contained in:
33
util/glyphas/CMakeLists.txt
Normal file
33
util/glyphas/CMakeLists.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
add_library(glyphas
|
||||
aligner.c
|
||||
block.c
|
||||
cache.c
|
||||
context.c
|
||||
drawer.c
|
||||
face.c
|
||||
glyph.c
|
||||
)
|
||||
target_any_sources(glyphas
|
||||
drawer.vshader
|
||||
drawer.fshader
|
||||
)
|
||||
target_link_libraries(glyphas
|
||||
Freetype::Freetype
|
||||
GLEW::GLEW
|
||||
OpenGL::GL
|
||||
|
||||
chaos
|
||||
container
|
||||
conv
|
||||
gleasy
|
||||
math
|
||||
memory
|
||||
)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
add_executable(glyphas-test test.c)
|
||||
target_link_libraries(glyphas-test
|
||||
SDL2::SDL2
|
||||
|
||||
glyphas)
|
||||
endif()
|
4
util/glyphas/README.md
Normal file
4
util/glyphas/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
glyphas
|
||||
====
|
||||
|
||||
GLYPH texture atlAS library wrapping FreeType
|
78
util/glyphas/aligner.c
Normal file
78
util/glyphas/aligner.c
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "./aligner.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/gleasy/atlas.h"
|
||||
#include "util/math/algorithm.h"
|
||||
|
||||
#include "./cache.h"
|
||||
|
||||
void glyphas_aligner_initialize(
|
||||
glyphas_aligner_t* aligner,
|
||||
glyphas_aligner_direction_t dir,
|
||||
int32_t lineheight,
|
||||
int32_t maxpos) {
|
||||
assert(aligner != NULL);
|
||||
assert(maxpos > 0);
|
||||
|
||||
*aligner = (typeof(*aligner)) {
|
||||
.dir = dir,
|
||||
.lineheight = lineheight,
|
||||
.maxpos = maxpos,
|
||||
.line = lineheight,
|
||||
};
|
||||
}
|
||||
|
||||
void glyphas_aligner_deinitialize(glyphas_aligner_t* aligner) {
|
||||
assert(aligner != NULL);
|
||||
|
||||
}
|
||||
|
||||
void glyphas_aligner_reset(glyphas_aligner_t* aligner) {
|
||||
assert(aligner != NULL);
|
||||
|
||||
aligner->line = aligner->lineheight;
|
||||
aligner->pos = 0;
|
||||
}
|
||||
|
||||
void glyphas_aligner_push_character(
|
||||
glyphas_aligner_t* aligner,
|
||||
int32_t* x,
|
||||
int32_t* y,
|
||||
const glyphas_cache_glyph_t* g) {
|
||||
assert(aligner != NULL);
|
||||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
assert(g != NULL);
|
||||
|
||||
switch (aligner->dir) {
|
||||
case GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL:
|
||||
if (aligner->pos + g->hmetrics.advance > aligner->maxpos) {
|
||||
glyphas_aligner_break_line(aligner);
|
||||
}
|
||||
*x = aligner->pos + g->hmetrics.bear_x;
|
||||
*y = g->hmetrics.bear_y + aligner->line;
|
||||
aligner->pos += g->hmetrics.advance;
|
||||
break;
|
||||
case GLYPHAS_ALIGNER_DIRECTION_VERTICAL:
|
||||
if (aligner->pos - g->vmetrics.advance < -aligner->maxpos) {
|
||||
glyphas_aligner_break_line(aligner);
|
||||
}
|
||||
*x = aligner->pos - g->vmetrics.bear_x + aligner->line;
|
||||
*y = -g->vmetrics.bear_y;
|
||||
aligner->pos -= g->vmetrics.advance;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void glyphas_aligner_break_line(glyphas_aligner_t* aligner) {
|
||||
assert(aligner != NULL);
|
||||
|
||||
aligner->pos = 0;
|
||||
aligner->line += aligner->lineheight;
|
||||
}
|
52
util/glyphas/aligner.h
Normal file
52
util/glyphas/aligner.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "./cache.h"
|
||||
|
||||
typedef enum {
|
||||
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL,
|
||||
GLYPHAS_ALIGNER_DIRECTION_VERTICAL,
|
||||
} glyphas_aligner_direction_t;
|
||||
|
||||
typedef struct {
|
||||
glyphas_aligner_direction_t dir;
|
||||
int32_t lineheight;
|
||||
int32_t maxpos;
|
||||
|
||||
int32_t pos;
|
||||
int32_t line;
|
||||
} glyphas_aligner_t;
|
||||
|
||||
void
|
||||
glyphas_aligner_initialize(
|
||||
glyphas_aligner_t* aligner,
|
||||
glyphas_aligner_direction_t dir,
|
||||
int32_t lineheight,
|
||||
int32_t maxpos
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_aligner_deinitialize(
|
||||
glyphas_aligner_t* aligner
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_aligner_reset(
|
||||
glyphas_aligner_t* aligner
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_aligner_push_character(
|
||||
glyphas_aligner_t* aligner,
|
||||
int32_t* x,
|
||||
int32_t* y,
|
||||
const glyphas_cache_glyph_t* g
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_aligner_break_line(
|
||||
glyphas_aligner_t* aligner
|
||||
);
|
216
util/glyphas/block.c
Normal file
216
util/glyphas/block.c
Normal file
@@ -0,0 +1,216 @@
|
||||
#include "./block.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "util/chaos/xorshift.h"
|
||||
#include "util/conv/charcode.h"
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/gleasy/atlas.h"
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
#include "./cache.h"
|
||||
|
||||
struct glyphas_block_t {
|
||||
glyphas_aligner_t aligner;
|
||||
|
||||
size_t reserve;
|
||||
size_t length;
|
||||
glyphas_block_item_t items[1];
|
||||
};
|
||||
|
||||
glyphas_block_t* glyphas_block_new(
|
||||
glyphas_aligner_direction_t dir,
|
||||
int32_t lineheight,
|
||||
int32_t maxpos,
|
||||
size_t reserve) {
|
||||
assert(maxpos > 0);
|
||||
assert(reserve > 0);
|
||||
|
||||
glyphas_block_t* block =
|
||||
memory_new(sizeof(*block) + (reserve-1)*sizeof(block->items[0]));
|
||||
*block = (typeof(*block)) {
|
||||
.reserve = reserve,
|
||||
};
|
||||
|
||||
glyphas_aligner_initialize(&block->aligner, dir, lineheight, maxpos);
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
void glyphas_block_delete(glyphas_block_t* block) {
|
||||
if (block == NULL) return;
|
||||
|
||||
glyphas_aligner_deinitialize(&block->aligner);
|
||||
|
||||
memory_delete(block);
|
||||
}
|
||||
|
||||
void glyphas_block_clear(glyphas_block_t* block) {
|
||||
assert(block != NULL);
|
||||
|
||||
glyphas_aligner_reset(&block->aligner);
|
||||
block->length = 0;
|
||||
}
|
||||
|
||||
void glyphas_block_add_character(
|
||||
glyphas_block_t* block,
|
||||
const glyphas_cache_glyph_t* glyph,
|
||||
const vec4_t* color) {
|
||||
assert(block != NULL);
|
||||
assert(glyph != NULL);
|
||||
assert(vec4_valid(color));
|
||||
|
||||
if (block->length >= block->reserve) {
|
||||
fprintf(stderr, "glyphas block overflow\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
int32_t x, y;
|
||||
glyphas_aligner_push_character(&block->aligner, &x, &y, glyph);
|
||||
if (glyph->geometry.left >= glyph->geometry.right) return;
|
||||
|
||||
block->items[block->length++] = (glyphas_block_item_t) {
|
||||
.pos = vec2(x, y),
|
||||
.size = vec2(glyph->width, glyph->height),
|
||||
.color = *color,
|
||||
.uv = glyph->geometry,
|
||||
};
|
||||
}
|
||||
|
||||
void glyphas_block_add_characters(
|
||||
glyphas_block_t* block,
|
||||
glyphas_cache_t* cache,
|
||||
const vec4_t* color,
|
||||
const char* str,
|
||||
size_t len) {
|
||||
assert(block != NULL);
|
||||
assert(cache != NULL);
|
||||
assert(vec4_valid(color));
|
||||
|
||||
const char* end = str + len;
|
||||
while (str < end) {
|
||||
uint32_t c;
|
||||
const size_t consumed = conv_charcode_utf8_to_utf32(&c, str, end-str);
|
||||
str += MATH_MAX(consumed, 1);
|
||||
if (consumed == 0) continue;
|
||||
|
||||
if (c == '\n') {
|
||||
glyphas_block_break_line(block);
|
||||
continue;
|
||||
}
|
||||
|
||||
const glyphas_cache_glyph_t* g = glyphas_cache_add_glyph(cache, c);
|
||||
if (g == NULL) continue;
|
||||
|
||||
glyphas_block_add_character(block, g, color);
|
||||
}
|
||||
}
|
||||
|
||||
void glyphas_block_break_line(glyphas_block_t* block) {
|
||||
assert(block != NULL);
|
||||
|
||||
glyphas_aligner_break_line(&block->aligner);
|
||||
}
|
||||
|
||||
void glyphas_block_scale(glyphas_block_t* block, const vec2_t* s) {
|
||||
assert(block != NULL);
|
||||
assert(vec2_valid(s));
|
||||
|
||||
for (size_t i = 0; i < block->length; ++i) {
|
||||
vec2_t* pos = &block->items[i].pos;
|
||||
vec2_t* sz = &block->items[i].size;
|
||||
|
||||
pos->x *= s->x;
|
||||
pos->y *= s->y;
|
||||
sz->x *= s->x;
|
||||
sz->y *= s->y;
|
||||
}
|
||||
}
|
||||
|
||||
void glyphas_block_translate(glyphas_block_t* block, const vec2_t* v) {
|
||||
assert(block != NULL);
|
||||
assert(vec2_valid(v));
|
||||
|
||||
for (size_t i = 0; i < block->length; ++i) {
|
||||
vec2_addeq(&block->items[i].pos, v);
|
||||
}
|
||||
}
|
||||
|
||||
void glyphas_block_set_origin(glyphas_block_t* block, const vec2_t* r) {
|
||||
assert(block !=NULL);
|
||||
assert(vec2_valid(r));
|
||||
|
||||
vec2_t offset, size;
|
||||
glyphas_block_calculate_geometry(block, &offset, &size);
|
||||
|
||||
size.x *= r->x;
|
||||
size.y *= r->y;
|
||||
|
||||
vec2_addeq(&offset, &size);
|
||||
vec2_muleq(&offset, -1);
|
||||
glyphas_block_translate(block, &offset);
|
||||
}
|
||||
|
||||
void glyphas_block_set_alpha(glyphas_block_t* block, float a) {
|
||||
assert(block != NULL);
|
||||
|
||||
for (size_t i = 0; i < block->length; ++i) {
|
||||
block->items[i].color.w = a;
|
||||
}
|
||||
}
|
||||
|
||||
void glyphas_block_make_glitched(glyphas_block_t* block, uint64_t seed) {
|
||||
assert(block != NULL);
|
||||
|
||||
# define randf() (seed = chaos_xorshift(seed), seed%1000/1000.f)
|
||||
|
||||
for (size_t i = 0; i < block->length; ++i) {
|
||||
const vec2_t* sz = &block->items[i].size;
|
||||
|
||||
const vec2_t shift = vec2((randf()-.5f)*sz->x*.5f, (randf()-.5f)*sz->y*.5f);
|
||||
const float scale = randf()*.4f + .8f;
|
||||
|
||||
vec2_addeq(&block->items[i].pos, &shift);
|
||||
vec2_muleq(&block->items[i].size, scale);
|
||||
}
|
||||
|
||||
# undef randf
|
||||
}
|
||||
|
||||
void glyphas_block_calculate_geometry(
|
||||
const glyphas_block_t* block, vec2_t* offset, vec2_t* size) {
|
||||
assert(block != NULL);
|
||||
assert(offset != NULL);
|
||||
assert(size != NULL);
|
||||
|
||||
vec2_t lb = vec2(0, 0), rt = vec2(0, 0);
|
||||
for (size_t i = 0; i < block->length; ++i) {
|
||||
const glyphas_block_item_t* item = &block->items[i];
|
||||
|
||||
const float l = item->pos.x;
|
||||
const float r = l + item->size.x;
|
||||
const float t = item->pos.y;
|
||||
const float b = t - item->size.y;
|
||||
|
||||
lb.x = MATH_MIN(lb.x, l);
|
||||
lb.y = MATH_MIN(lb.y, b);
|
||||
rt.x = MATH_MAX(rt.x, r);
|
||||
rt.y = MATH_MAX(rt.y, t);
|
||||
}
|
||||
*offset = vec2(lb.x, rt.y);
|
||||
vec2_sub(size, &rt, &lb);
|
||||
}
|
||||
|
||||
const glyphas_block_item_t* glyphas_block_get_items(
|
||||
const glyphas_block_t* block, size_t* len) {
|
||||
assert(block != NULL);
|
||||
assert(len != NULL);
|
||||
|
||||
*len = block->length;
|
||||
return block->items;
|
||||
}
|
102
util/glyphas/block.h
Normal file
102
util/glyphas/block.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/math/vector.h"
|
||||
#include "util/gleasy/atlas.h"
|
||||
|
||||
#include "./aligner.h"
|
||||
#include "./cache.h"
|
||||
|
||||
struct glyphas_block_t;
|
||||
typedef struct glyphas_block_t glyphas_block_t;
|
||||
|
||||
typedef struct {
|
||||
vec2_t pos;
|
||||
vec2_t size;
|
||||
vec4_t color;
|
||||
gleasy_atlas_geometry_t uv;
|
||||
} glyphas_block_item_t;
|
||||
|
||||
glyphas_block_t*
|
||||
glyphas_block_new(
|
||||
glyphas_aligner_direction_t dir,
|
||||
int32_t lineheight,
|
||||
int32_t maxpos,
|
||||
size_t reserve
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_delete(
|
||||
glyphas_block_t* block
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_clear(
|
||||
glyphas_block_t* block
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_add_character(
|
||||
glyphas_block_t* block,
|
||||
const glyphas_cache_glyph_t* glyph,
|
||||
const vec4_t* color
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_add_characters(
|
||||
glyphas_block_t* block,
|
||||
glyphas_cache_t* cache,
|
||||
const vec4_t* color,
|
||||
const char* str,
|
||||
size_t len
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_break_line(
|
||||
glyphas_block_t* block
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_scale(
|
||||
glyphas_block_t* block,
|
||||
const vec2_t* translation
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_translate(
|
||||
glyphas_block_t* block,
|
||||
const vec2_t* translation
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_set_origin(
|
||||
glyphas_block_t* block,
|
||||
const vec2_t* r
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_set_alpha(
|
||||
glyphas_block_t* block,
|
||||
float a
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_make_glitched(
|
||||
glyphas_block_t* block,
|
||||
uint64_t seed
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_block_calculate_geometry(
|
||||
const glyphas_block_t* block,
|
||||
vec2_t* offset,
|
||||
vec2_t* size
|
||||
);
|
||||
|
||||
const glyphas_block_item_t*
|
||||
glyphas_block_get_items(
|
||||
const glyphas_block_t* block,
|
||||
size_t* len
|
||||
);
|
111
util/glyphas/cache.c
Normal file
111
util/glyphas/cache.c
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "./cache.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include "util/container/array.h"
|
||||
#include "util/gleasy/atlas.h"
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
#include "./face.h"
|
||||
|
||||
struct glyphas_cache_t {
|
||||
gleasy_atlas_t* atlas;
|
||||
glyphas_face_t* face;
|
||||
|
||||
int32_t char_width;
|
||||
int32_t char_height;
|
||||
|
||||
/* TODO(catfoot): linear search isn't efficient. :( */
|
||||
CONTAINER_ARRAY glyphas_cache_glyph_t* items;
|
||||
};
|
||||
|
||||
glyphas_cache_t* glyphas_cache_new(
|
||||
gleasy_atlas_t* atlas,
|
||||
glyphas_face_t* face,
|
||||
int32_t char_width,
|
||||
int32_t char_height) {
|
||||
assert(atlas != NULL);
|
||||
assert(face != NULL);
|
||||
assert(char_width > 0);
|
||||
assert(char_height > 0);
|
||||
|
||||
glyphas_cache_t* cache = memory_new(sizeof(*cache));
|
||||
*cache = (typeof(*cache)) {
|
||||
.atlas = atlas,
|
||||
.face = face,
|
||||
.char_width = char_width,
|
||||
.char_height = char_height,
|
||||
};
|
||||
return cache;
|
||||
}
|
||||
|
||||
void glyphas_cache_delete(glyphas_cache_t* cache) {
|
||||
if (cache == NULL) return;
|
||||
|
||||
container_array_delete(cache->items);
|
||||
|
||||
memory_delete(cache);
|
||||
}
|
||||
|
||||
const glyphas_cache_glyph_t* glyphas_cache_add_glyph(
|
||||
glyphas_cache_t* cache, uint32_t unicode) {
|
||||
assert(cache != NULL);
|
||||
|
||||
const glyphas_cache_glyph_t* found =
|
||||
glyphas_cache_lookup_glyph(cache, unicode);
|
||||
if (found != NULL) return found;
|
||||
|
||||
if (!glyphas_face_set_pixel_size(
|
||||
cache->face, cache->char_width, cache->char_height)) {
|
||||
return NULL;
|
||||
}
|
||||
if (!glyphas_face_render_glyph(cache->face, unicode)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gleasy_atlas_geometry_t geo = {0};
|
||||
|
||||
const glyphas_glyph_t* rendered = &cache->face->glyph;
|
||||
if (rendered->bitmap.width > 0) {
|
||||
const gleasy_atlas_bitmap_t bmp = {
|
||||
.width = rendered->bitmap.width,
|
||||
.height = rendered->bitmap.height,
|
||||
.buffer = rendered->bitmap.buffer,
|
||||
.format = GL_RED,
|
||||
.type = GL_UNSIGNED_BYTE,
|
||||
};
|
||||
if (!gleasy_atlas_add(cache->atlas, &geo, &bmp)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t index = container_array_get_length(cache->items);
|
||||
container_array_insert(cache->items, index);
|
||||
|
||||
glyphas_cache_glyph_t* g = &cache->items[index];
|
||||
*g = (typeof(*g)) {
|
||||
.unicode = unicode,
|
||||
.width = rendered->bitmap.width,
|
||||
.height = rendered->bitmap.height,
|
||||
.geometry = geo,
|
||||
.hmetrics = rendered->hmetrics,
|
||||
.vmetrics = rendered->vmetrics,
|
||||
};
|
||||
return g;
|
||||
}
|
||||
|
||||
const glyphas_cache_glyph_t* glyphas_cache_lookup_glyph(
|
||||
const glyphas_cache_t* cache, uint32_t unicode) {
|
||||
assert(cache != NULL);
|
||||
|
||||
const size_t len = container_array_get_length(cache->items);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
const glyphas_cache_glyph_t* g = &cache->items[i];
|
||||
if (g->unicode == unicode) return g;
|
||||
}
|
||||
return NULL;
|
||||
}
|
47
util/glyphas/cache.h
Normal file
47
util/glyphas/cache.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "util/gleasy/atlas.h"
|
||||
|
||||
#include "./face.h"
|
||||
#include "./glyph.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t unicode;
|
||||
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
|
||||
gleasy_atlas_geometry_t geometry;
|
||||
glyphas_glyph_metrics_t hmetrics;
|
||||
glyphas_glyph_metrics_t vmetrics;
|
||||
} glyphas_cache_glyph_t;
|
||||
|
||||
struct glyphas_cache_t;
|
||||
typedef struct glyphas_cache_t glyphas_cache_t;
|
||||
|
||||
glyphas_cache_t*
|
||||
glyphas_cache_new(
|
||||
gleasy_atlas_t* atlas,
|
||||
glyphas_face_t* face,
|
||||
int32_t char_width,
|
||||
int32_t char_height
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_cache_delete(
|
||||
glyphas_cache_t* cache
|
||||
);
|
||||
|
||||
const glyphas_cache_glyph_t* /* NULLABLE */
|
||||
glyphas_cache_add_glyph(
|
||||
glyphas_cache_t* cache,
|
||||
uint32_t unicode
|
||||
);
|
||||
|
||||
const glyphas_cache_glyph_t* /* NULLABLE */
|
||||
glyphas_cache_lookup_glyph(
|
||||
const glyphas_cache_t* cache,
|
||||
uint32_t unicode
|
||||
);
|
21
util/glyphas/context.c
Normal file
21
util/glyphas/context.c
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "./context.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
void glyphas_context_initialize(glyphas_context_t* ctx) {
|
||||
assert(ctx != NULL);
|
||||
|
||||
*ctx = (typeof(*ctx)) {0};
|
||||
|
||||
FT_Init_FreeType(&ctx->ft);
|
||||
}
|
||||
|
||||
void glyphas_context_deinitialize(glyphas_context_t* ctx) {
|
||||
assert(ctx != NULL);
|
||||
|
||||
FT_Done_FreeType(ctx->ft);
|
||||
}
|
18
util/glyphas/context.h
Normal file
18
util/glyphas/context.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
typedef struct {
|
||||
FT_Library ft;
|
||||
} glyphas_context_t;
|
||||
|
||||
void
|
||||
glyphas_context_initialize(
|
||||
glyphas_context_t* ctx
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_context_deinitialize(
|
||||
glyphas_context_t* ctx
|
||||
);
|
164
util/glyphas/drawer.c
Normal file
164
util/glyphas/drawer.c
Normal file
@@ -0,0 +1,164 @@
|
||||
#include "./drawer.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include "util/gleasy/buffer.h"
|
||||
#include "util/gleasy/texture.h"
|
||||
#include "util/gleasy/program.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
#include "./block.h"
|
||||
|
||||
/* resources */
|
||||
#include "anysrc/drawer.vshader.h"
|
||||
#include "anysrc/drawer.fshader.h"
|
||||
|
||||
#define GLYPHAS_DRAWER_UNIFORM_TEX 0
|
||||
|
||||
#define GLYPHAS_DRAWER_VSHADER_IN_POS 0
|
||||
#define GLYPHAS_DRAWER_VSHADER_IN_SIZE 1
|
||||
#define GLYPHAS_DRAWER_VSHADER_IN_UV_POS 2
|
||||
#define GLYPHAS_DRAWER_VSHADER_IN_UV_SIZE 3
|
||||
#define GLYPHAS_DRAWER_VSHADER_IN_COLOR 4
|
||||
|
||||
struct glyphas_drawer_t {
|
||||
gleasy_texture_2d_t tex;
|
||||
|
||||
GLuint vao;
|
||||
|
||||
gleasy_buffer_array_t instances;
|
||||
size_t instances_reserved;
|
||||
size_t instances_length;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
vec2_t pos;
|
||||
vec2_t size;
|
||||
vec2_t uv_pos;
|
||||
vec2_t uv_size;
|
||||
vec4_t color;
|
||||
} glyphas_drawer_instance_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
static void glyphas_drawer_setup_vao_(
|
||||
gleasy_buffer_array_t instances) {
|
||||
assert(instances != 0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, instances);
|
||||
|
||||
# define enable_attrib_(NAME, name, dim, type) do { \
|
||||
glEnableVertexAttribArray(GLYPHAS_DRAWER_VSHADER_IN_##NAME); \
|
||||
glVertexAttribPointer( \
|
||||
GLYPHAS_DRAWER_VSHADER_IN_##NAME, dim, type, GL_FALSE, \
|
||||
sizeof(glyphas_drawer_instance_t), \
|
||||
NULL + offsetof(glyphas_drawer_instance_t, name)); \
|
||||
glVertexAttribDivisor(GLYPHAS_DRAWER_VSHADER_IN_##NAME, 1); \
|
||||
} while (0)
|
||||
|
||||
enable_attrib_(POS, pos, 2, GL_FLOAT);
|
||||
enable_attrib_(SIZE, size, 2, GL_FLOAT);
|
||||
enable_attrib_(UV_POS, uv_pos, 2, GL_FLOAT);
|
||||
enable_attrib_(UV_SIZE, uv_size, 2, GL_FLOAT);
|
||||
enable_attrib_(COLOR, color, 4, GL_FLOAT);
|
||||
|
||||
# undef enable_attrib_
|
||||
}
|
||||
|
||||
gleasy_program_t glyphas_drawer_create_default_program(void) {
|
||||
return gleasy_program_new("", 0,
|
||||
glyphas_drawer_vshader_, sizeof(glyphas_drawer_vshader_),
|
||||
glyphas_drawer_fshader_, sizeof(glyphas_drawer_fshader_));
|
||||
}
|
||||
|
||||
glyphas_drawer_t* glyphas_drawer_new(void) {
|
||||
glyphas_drawer_t* drawer = memory_new(sizeof(*drawer));
|
||||
*drawer = (typeof(*drawer)) {0};
|
||||
|
||||
glCreateVertexArrays(1, &drawer->vao);
|
||||
glBindVertexArray(drawer->vao);
|
||||
|
||||
glGenBuffers(1, &drawer->instances);
|
||||
glyphas_drawer_setup_vao_(drawer->instances);
|
||||
|
||||
return drawer;
|
||||
}
|
||||
|
||||
void glyphas_drawer_delete(glyphas_drawer_t* drawer) {
|
||||
if (drawer == NULL) return;
|
||||
|
||||
glDeleteBuffers(1, &drawer->instances);
|
||||
|
||||
glDeleteVertexArrays(1, &drawer->vao);
|
||||
|
||||
memory_delete(drawer);
|
||||
}
|
||||
|
||||
void glyphas_drawer_clear(
|
||||
glyphas_drawer_t* drawer, gleasy_texture_2d_t tex, size_t reserve) {
|
||||
assert(drawer != NULL);
|
||||
assert(tex != 0);
|
||||
assert(reserve > 0);
|
||||
|
||||
drawer->tex = tex;
|
||||
|
||||
drawer->instances_length = 0;
|
||||
if (drawer->instances_reserved < reserve) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, drawer->instances);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
reserve * sizeof(glyphas_drawer_instance_t),
|
||||
NULL, GL_DYNAMIC_DRAW);
|
||||
drawer->instances_reserved = reserve;
|
||||
}
|
||||
}
|
||||
|
||||
void glyphas_drawer_add_block(
|
||||
glyphas_drawer_t* drawer, const glyphas_block_t* block) {
|
||||
assert(drawer != NULL);
|
||||
assert(block != NULL);
|
||||
|
||||
size_t len;
|
||||
const glyphas_block_item_t* items = glyphas_block_get_items(block, &len);
|
||||
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
glyphas_drawer_add_block_item(drawer, &items[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void glyphas_drawer_add_block_item(
|
||||
glyphas_drawer_t* drawer, const glyphas_block_item_t* item) {
|
||||
assert(drawer != NULL);
|
||||
assert(item != NULL);
|
||||
|
||||
const glyphas_drawer_instance_t insta = {
|
||||
.pos = item->pos,
|
||||
.size = item->size,
|
||||
.uv_pos = vec2(item->uv.left, item->uv.top),
|
||||
.uv_size = vec2(item->uv.right-item->uv.left, item->uv.top-item->uv.bottom),
|
||||
.color = item->color,
|
||||
};
|
||||
|
||||
const size_t offset = drawer->instances_length * sizeof(insta);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, drawer->instances);
|
||||
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(insta), &insta);
|
||||
|
||||
++drawer->instances_length;
|
||||
}
|
||||
|
||||
void glyphas_drawer_draw(const glyphas_drawer_t* drawer) {
|
||||
assert(drawer != NULL);
|
||||
|
||||
if (drawer->instances_length == 0) return;
|
||||
|
||||
glBindVertexArray(drawer->vao);
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, drawer->tex);
|
||||
glUniform1i(GLYPHAS_DRAWER_UNIFORM_TEX, 0);
|
||||
|
||||
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, drawer->instances_length);
|
||||
}
|
14
util/glyphas/drawer.fshader
Normal file
14
util/glyphas/drawer.fshader
Normal file
@@ -0,0 +1,14 @@
|
||||
#version 330
|
||||
#extension GL_ARB_explicit_uniform_location : enable
|
||||
|
||||
layout (location = 0) uniform sampler2D u_tex;
|
||||
|
||||
in vec2 v_uv;
|
||||
in vec4 v_color;
|
||||
|
||||
out vec4 o_color;
|
||||
|
||||
void main(void) {
|
||||
float a = texture(u_tex, v_uv).r;
|
||||
o_color = vec4(v_color.rgb, v_color.a*a);
|
||||
}
|
51
util/glyphas/drawer.h
Normal file
51
util/glyphas/drawer.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/gleasy/texture.h"
|
||||
#include "util/gleasy/program.h"
|
||||
|
||||
#include "./block.h"
|
||||
|
||||
struct glyphas_drawer_t;
|
||||
typedef struct glyphas_drawer_t glyphas_drawer_t;
|
||||
|
||||
gleasy_program_t
|
||||
glyphas_drawer_create_default_program(
|
||||
void
|
||||
);
|
||||
|
||||
glyphas_drawer_t*
|
||||
glyphas_drawer_new(
|
||||
void
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_drawer_delete(
|
||||
glyphas_drawer_t* drawer
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_drawer_clear(
|
||||
glyphas_drawer_t* drawer,
|
||||
gleasy_texture_2d_t tex,
|
||||
size_t reserve
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_drawer_add_block(
|
||||
glyphas_drawer_t* drawer,
|
||||
const glyphas_block_t* block
|
||||
/* The drawer doesn't hold the reference. */
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_drawer_add_block_item(
|
||||
glyphas_drawer_t* drawer,
|
||||
const glyphas_block_item_t* item
|
||||
/* The drawer doesn't hold the reference. */
|
||||
);
|
||||
|
||||
/* bind the program before drawing */
|
||||
void
|
||||
glyphas_drawer_draw(
|
||||
const glyphas_drawer_t* drawer
|
||||
);
|
25
util/glyphas/drawer.vshader
Normal file
25
util/glyphas/drawer.vshader
Normal file
@@ -0,0 +1,25 @@
|
||||
#version 330
|
||||
|
||||
layout (location = 0) in vec2 i_pos;
|
||||
layout (location = 1) in vec2 i_size;
|
||||
layout (location = 2) in vec2 i_uv_pos;
|
||||
layout (location = 3) in vec2 i_uv_size;
|
||||
layout (location = 4) in vec4 i_color;
|
||||
|
||||
out vec2 v_uv;
|
||||
out vec4 v_color;
|
||||
|
||||
void main(void) {
|
||||
vec2 p =
|
||||
gl_VertexID == 0? vec2(0., 0.):
|
||||
gl_VertexID == 1? vec2(0., -1.):
|
||||
gl_VertexID == 2? vec2(1., -1.):
|
||||
gl_VertexID == 3? vec2(0., 0.):
|
||||
gl_VertexID == 4? vec2(1., -1.):
|
||||
gl_VertexID == 5? vec2(1., 0.):
|
||||
vec2(0.);
|
||||
|
||||
gl_Position = vec4(p*i_size+i_pos, 0, 1);
|
||||
v_uv = p*i_uv_size + i_uv_pos;
|
||||
v_color = i_color;
|
||||
}
|
78
util/glyphas/face.c
Normal file
78
util/glyphas/face.c
Normal file
@@ -0,0 +1,78 @@
|
||||
#include "./face.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_ERRORS_H
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include "./context.h"
|
||||
|
||||
void glyphas_face_initialize_from_file(
|
||||
glyphas_face_t* face,
|
||||
const glyphas_context_t* ctx,
|
||||
const char* path,
|
||||
size_t index) {
|
||||
assert(face != NULL);
|
||||
assert(ctx != NULL);
|
||||
|
||||
*face = (typeof(*face)) {0};
|
||||
|
||||
const FT_Error err = FT_New_Face(ctx->ft, path, index, &face->ft);
|
||||
if (err != FT_Err_Ok) {
|
||||
fprintf(stderr,
|
||||
"failed to load font file '%s': %s\n", path, FT_Error_String(err));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void glyphas_face_initialize_from_buffer(
|
||||
glyphas_face_t* face,
|
||||
const glyphas_context_t* ctx,
|
||||
const void* data,
|
||||
size_t length,
|
||||
size_t index) {
|
||||
assert(face != NULL);
|
||||
assert(ctx != NULL);
|
||||
|
||||
*face = (typeof(*face)) {0};
|
||||
|
||||
const FT_Error err =
|
||||
FT_New_Memory_Face(ctx->ft, data, length, index, &face->ft);
|
||||
if (err != FT_Err_Ok) {
|
||||
fprintf(stderr,
|
||||
"failed to load font on memory: %s\n", FT_Error_String(err));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void glyphas_face_deinitialize(glyphas_face_t* face) {
|
||||
assert(face != NULL);
|
||||
|
||||
FT_Done_Face(face->ft);
|
||||
}
|
||||
|
||||
bool glyphas_face_set_pixel_size(
|
||||
glyphas_face_t* face, int32_t width, int32_t height) {
|
||||
assert(face != NULL);
|
||||
assert(width > 0);
|
||||
assert(height > 0);
|
||||
|
||||
const FT_Error err = FT_Set_Pixel_Sizes(face->ft, width, height);
|
||||
return err == FT_Err_Ok;
|
||||
}
|
||||
|
||||
bool glyphas_face_render_glyph(glyphas_face_t* face, uint64_t unicode) {
|
||||
assert(face != NULL);
|
||||
|
||||
const FT_UInt index = FT_Get_Char_Index(face->ft, unicode);
|
||||
if (index == 0) return false;
|
||||
|
||||
const FT_Error err = FT_Load_Glyph(face->ft, index, FT_LOAD_RENDER);
|
||||
if (err != FT_Err_Ok) return false;
|
||||
|
||||
return glyphas_glyph_reset_from_ft_glyph_slot(&face->glyph, face->ft->glyph);
|
||||
}
|
52
util/glyphas/face.h
Normal file
52
util/glyphas/face.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include "./context.h"
|
||||
#include "./glyph.h"
|
||||
|
||||
typedef struct {
|
||||
FT_Face ft;
|
||||
|
||||
glyphas_glyph_t glyph;
|
||||
} glyphas_face_t;
|
||||
|
||||
void
|
||||
glyphas_face_initialize_from_file(
|
||||
glyphas_face_t* face,
|
||||
const glyphas_context_t* ctx,
|
||||
const char* path,
|
||||
size_t index
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_face_initialize_from_buffer(
|
||||
glyphas_face_t* face,
|
||||
const glyphas_context_t* ctx,
|
||||
const void* data,
|
||||
size_t length,
|
||||
size_t index
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_face_deinitialize(
|
||||
glyphas_face_t* face
|
||||
);
|
||||
|
||||
bool
|
||||
glyphas_face_set_pixel_size(
|
||||
glyphas_face_t* face,
|
||||
int32_t width,
|
||||
int32_t height
|
||||
);
|
||||
|
||||
/* Set sizes before rendering glyphs. */
|
||||
bool
|
||||
glyphas_face_render_glyph(
|
||||
glyphas_face_t* face,
|
||||
uint64_t unicode
|
||||
);
|
36
util/glyphas/glyph.c
Normal file
36
util/glyphas/glyph.c
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "./glyph.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
bool glyphas_glyph_reset_from_ft_glyph_slot(
|
||||
glyphas_glyph_t* glyph, FT_GlyphSlot slot) {
|
||||
assert(glyph != NULL);
|
||||
assert(slot != NULL);
|
||||
|
||||
if (slot->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) return false;
|
||||
|
||||
*glyph = (typeof(*glyph)) {
|
||||
.bitmap = {
|
||||
.width = slot->bitmap.width,
|
||||
.height = slot->bitmap.rows,
|
||||
.buffer = slot->bitmap.buffer,
|
||||
},
|
||||
.hmetrics = {
|
||||
.bear_x = slot->metrics.horiBearingX/64,
|
||||
.bear_y = slot->metrics.horiBearingY/64,
|
||||
.advance = slot->metrics.horiAdvance /64,
|
||||
},
|
||||
.vmetrics = {
|
||||
.bear_x = slot->metrics.vertBearingX/64,
|
||||
.bear_y = slot->metrics.vertBearingY/64,
|
||||
.advance = slot->metrics.vertAdvance /64,
|
||||
},
|
||||
};
|
||||
return true;
|
||||
}
|
32
util/glyphas/glyph.h
Normal file
32
util/glyphas/glyph.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
typedef struct {
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
uint8_t* buffer; /* gray-scale bitmap */
|
||||
} glyphas_glyph_bitmap_t;
|
||||
|
||||
typedef struct {
|
||||
int32_t bear_x;
|
||||
int32_t bear_y;
|
||||
int32_t advance;
|
||||
} glyphas_glyph_metrics_t;
|
||||
|
||||
typedef struct {
|
||||
glyphas_glyph_bitmap_t bitmap;
|
||||
glyphas_glyph_metrics_t vmetrics;
|
||||
glyphas_glyph_metrics_t hmetrics;
|
||||
} glyphas_glyph_t;
|
||||
|
||||
/* The glyph's lifetime must be shorter than the slot. */
|
||||
bool
|
||||
glyphas_glyph_reset_from_ft_glyph_slot(
|
||||
glyphas_glyph_t* glyph,
|
||||
FT_GlyphSlot slot
|
||||
);
|
179
util/glyphas/test.c
Normal file
179
util/glyphas/test.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/* An expression in assert() can be expected to be executed absolutely. */
|
||||
#undef NDEBUG
|
||||
|
||||
#define SDL_MAIN_HANDLED
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "util/conv/charcode.h"
|
||||
#include "util/gleasy/buffer.h"
|
||||
#include "util/gleasy/program.h"
|
||||
#include "util/math/vector.h"
|
||||
|
||||
#include "./cache.h"
|
||||
#include "./context.h"
|
||||
#include "./drawer.h"
|
||||
#include "./face.h"
|
||||
#include "./glyph.h"
|
||||
|
||||
typedef struct {
|
||||
glyphas_context_t glyphas;
|
||||
glyphas_face_t face;
|
||||
|
||||
gleasy_atlas_t* atlas;
|
||||
glyphas_cache_t* cache;
|
||||
|
||||
gleasy_program_t prog;
|
||||
glyphas_drawer_t* drawer;
|
||||
} context_t;
|
||||
|
||||
#define SCALE 0.01f
|
||||
|
||||
static void align_text_(context_t* ctx, const char* str) {
|
||||
assert(ctx != NULL);
|
||||
|
||||
const size_t len = strlen(str);
|
||||
glyphas_block_t* block = glyphas_block_new(
|
||||
GLYPHAS_ALIGNER_DIRECTION_HORIZONTAL, -20, 400, len);
|
||||
|
||||
static vec4_t color = vec4(0, 0, 0, 1);
|
||||
glyphas_block_add_characters(block, ctx->cache, &color, str, len);
|
||||
|
||||
vec2_t offset, size;
|
||||
glyphas_block_calculate_geometry(block, &offset, &size);
|
||||
|
||||
offset = vec2(-offset.x, 0);
|
||||
glyphas_block_translate(block, &offset);
|
||||
|
||||
size = vec2(1.f/200, 1.f/200);
|
||||
glyphas_block_scale(block, &size);
|
||||
|
||||
static const vec2_t origin = vec2(.5f, -.5f);
|
||||
glyphas_block_set_origin(block, &origin);
|
||||
|
||||
glyphas_drawer_clear(ctx->drawer, gleasy_atlas_get_texture(ctx->atlas), len);
|
||||
glyphas_drawer_add_block(ctx->drawer, block);
|
||||
glyphas_block_delete(block);
|
||||
}
|
||||
|
||||
static void initialize_(context_t* ctx, const char* path, const char* str) {
|
||||
assert(ctx != NULL);
|
||||
|
||||
*ctx = (typeof(*ctx)) {0};
|
||||
glyphas_context_initialize(&ctx->glyphas);
|
||||
|
||||
glyphas_face_initialize_from_file(&ctx->face, &ctx->glyphas, path, 0);
|
||||
|
||||
ctx->atlas = gleasy_atlas_new(GL_RED, 256, 256, false /* anti-alias */);
|
||||
ctx->cache = glyphas_cache_new(ctx->atlas, &ctx->face, 16, 16);
|
||||
|
||||
ctx->prog = glyphas_drawer_create_default_program();
|
||||
ctx->drawer = glyphas_drawer_new();
|
||||
assert(glGetError() == GL_NO_ERROR);
|
||||
|
||||
align_text_(ctx, str);
|
||||
assert(glGetError() == GL_NO_ERROR);
|
||||
}
|
||||
|
||||
static void draw_(const context_t* ctx) {
|
||||
assert(ctx != NULL);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glUseProgram(ctx->prog);
|
||||
glyphas_drawer_draw(ctx->drawer);
|
||||
}
|
||||
|
||||
static void deinitialize_(context_t* ctx) {
|
||||
assert(ctx != NULL);
|
||||
|
||||
glyphas_drawer_delete(ctx->drawer);
|
||||
glDeleteProgram(ctx->prog);
|
||||
|
||||
glyphas_cache_delete(ctx->cache);
|
||||
gleasy_atlas_delete(ctx->atlas);
|
||||
|
||||
glyphas_face_deinitialize(&ctx->face);
|
||||
glyphas_context_deinitialize(&ctx->glyphas);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
SDL_Window* win;
|
||||
SDL_GLContext gl;
|
||||
} libs_t;
|
||||
|
||||
static void initialize_libraries_(libs_t* libs) {
|
||||
assert(libs != NULL);
|
||||
|
||||
SDL_SetHint(SDL_HINT_VIDEO_HIGHDPI_DISABLED, "0");
|
||||
assert(SDL_Init(SDL_INIT_VIDEO) == 0);
|
||||
|
||||
SDL_GL_SetAttribute(
|
||||
SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
|
||||
SDL_GL_SetAttribute(
|
||||
SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
|
||||
static const uint32_t win_flags =
|
||||
SDL_WINDOW_ALLOW_HIGHDPI |
|
||||
SDL_WINDOW_OPENGL |
|
||||
SDL_WINDOW_SHOWN;
|
||||
libs->win = SDL_CreateWindow(
|
||||
"glyphas-test", /* title */
|
||||
SDL_WINDOWPOS_CENTERED, /* X position */
|
||||
SDL_WINDOWPOS_CENTERED, /* Y position */
|
||||
400,
|
||||
400,
|
||||
win_flags);
|
||||
assert(libs->win != NULL);
|
||||
|
||||
libs->gl = SDL_GL_CreateContext(libs->win);
|
||||
|
||||
glewExperimental = 1;
|
||||
assert(glewInit() == GLEW_OK);
|
||||
}
|
||||
static void deinitialize_libraries_(libs_t* libs) {
|
||||
assert(libs != NULL);
|
||||
|
||||
SDL_GL_DeleteContext(libs->gl);
|
||||
SDL_DestroyWindow(libs->win);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
assert(argc == 3);
|
||||
|
||||
libs_t libs;
|
||||
initialize_libraries_(&libs);
|
||||
|
||||
context_t ctx;
|
||||
initialize_(&ctx, argv[1], argv[2]);
|
||||
|
||||
bool alive = true;
|
||||
while (alive) {
|
||||
SDL_Delay(30);
|
||||
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (e.type == SDL_QUIT) alive = false;
|
||||
}
|
||||
glClearColor(1, 1, 1, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
draw_(&ctx);
|
||||
SDL_GL_SwapWindow(libs.win);
|
||||
}
|
||||
|
||||
deinitialize_(&ctx);
|
||||
deinitialize_libraries_(&libs);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Reference in New Issue
Block a user