This commit is contained in:
Zack Buhman 2025-02-07 12:51:37 -06:00
commit f48edc2210
4 changed files with 294 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
*.out
*.o
*.gch
*.d
main
serial_forwarder
time_display

BIN
DejaVuSansMono.ttf Normal file

Binary file not shown.

43
Makefile Normal file
View File

@ -0,0 +1,43 @@
SDL ?= ../SDL
DEBUG = -g -gdwarf-5
CFLAGS += -Wall -Werror -Wfatal-errors
CFLAGS += -Wno-error=unused-function
CFLAGS += -std=c23
CFLAGS += -I$(SDL)/include -D_REENTRANT
CFLAGS += $(shell pkg-config --cflags freetype2)
LDFLAGS += -L$(SDL)/build -lSDL3 -Wl,-rpath=$(SDL)/build
LDFLAGS += $(shell pkg-config --libs freetype2)
DEPFLAGS = -MMD -MP
OPT = -Og -march=native
MAIN_OBJS = \
main.o
all: main
clean:
rm -f *.o *.d *.gch
rm -f main
%.o: %.c
$(CC) $(CARCH) $(CFLAGS) $(OPT) $(DEBUG) $(DEPFLAGS) -MF ${<}.d -c $< -o $@
main: $(MAIN_OBJS)
$(CC) $^ -o $@ $(LDFLAGS)
-include $(shell find -type f -name '*.d')
.SUFFIXES:
.INTERMEDIATE:
.SECONDARY:
.PHONY: all clean
%: RCS/%,v
%: RCS/%
%: %,v
%: s.%
%: SCCS/s.%

244
main.c Normal file
View File

@ -0,0 +1,244 @@
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <SDL3/SDL.h>
struct glyph {
int32_t width;
int32_t height;
int32_t horiBearingX;
int32_t horiBearingY;
int32_t horiAdvance;
SDL_Texture * texture;
};
static struct glyph glyphs[0x80 - 0x20] = {0};
int32_t face_height;
int
load_outline_char(SDL_Renderer * renderer,
const FT_Face face,
const FT_Int32 load_flags,
const FT_Render_Mode render_mode,
const FT_ULong char_code)
{
FT_Error error;
FT_UInt glyph_index = FT_Get_Char_Index(face, char_code);
error = FT_Load_Glyph(face, glyph_index, load_flags);
if (error) {
printf("FT_Load_Glyph %s\n", FT_Error_String(error));
return -1;
}
error = FT_Render_Glyph(face->glyph, render_mode);
if (error) {
printf("FT_Render_Glyph %s\n", FT_Error_String(error));
return -1;
}
struct glyph * glyph = &glyphs[char_code - 0x20];
glyph->width = face->glyph->bitmap.width;
glyph->height = face->glyph->bitmap.rows;
glyph->horiBearingX = face->glyph->metrics.horiBearingX;
glyph->horiBearingY = face->glyph->metrics.horiBearingY;
glyph->horiAdvance = face->glyph->metrics.horiAdvance;
if (face->glyph->bitmap.pitch != 0) {
SDL_Surface * surface = SDL_CreateSurface(face->glyph->bitmap.width,
face->glyph->bitmap.rows,
SDL_PIXELFORMAT_RGBA8888);
SDL_LockSurface(surface);
for (int y = 0; y < face->glyph->bitmap.rows; y++) {
for (int x = 0; x < face->glyph->bitmap.width; x++) {
int s_ix = y * face->glyph->bitmap.pitch + x;
int d_ix = y * surface->pitch + x * 4;
uint8_t gray = face->glyph->bitmap.buffer[s_ix];
((uint8_t *)surface->pixels)[d_ix + 0] = gray;
((uint8_t *)surface->pixels)[d_ix + 1] = gray;
((uint8_t *)surface->pixels)[d_ix + 2] = gray;
((uint8_t *)surface->pixels)[d_ix + 3] = 255;
}
}
SDL_UnlockSurface(surface);
if (glyph->texture != NULL)
SDL_DestroyTexture(glyph->texture);
glyph->texture = SDL_CreateTextureFromSurface(renderer, surface);
if (glyph->texture == NULL) {
printf("%s\n", SDL_GetError());
}
assert(glyph->texture != NULL);
SDL_DestroySurface(surface);
}
return 0;
}
int load_font(SDL_Renderer * renderer, int font_size)
{
FT_Library library;
FT_Face face;
FT_Error error;
error = FT_Init_FreeType(&library);
if (error) {
printf("FT_Init_FreeType\n");
return -1;
}
error = FT_New_Face(library, "/home/bilbo/timer/DejaVuSansMono.ttf", 0, &face);
if (error) {
printf("FT_New_Face\n");
return -1;
}
error = FT_Set_Pixel_Sizes(face, 0, font_size);
if (error) {
printf("FT_Set_Pixel_Sizes: %s %d\n", FT_Error_String(error), error);
return -1;
}
for (int char_code = 0x20; char_code <= 0x7f; char_code++) {
load_outline_char(renderer, face, FT_LOAD_DEFAULT, FT_RENDER_MODE_NORMAL, char_code);
}
printf("loaded size %ld\n", face->size->metrics.height >> 6);
face_height = face->size->metrics.height;
return 0;
}
void render(SDL_Renderer * renderer, int window_width, int window_height, const char * s, int length)
{
int32_t advance = (window_width - (window_width * 5 / 100)) << 6;
for (int i = (length - 1); i >= 0; i--) {
char c = s[i];
struct glyph * glyph = &glyphs[c - 0x20];
advance -= glyph->horiAdvance;
if (glyph->texture != NULL) {
double x = (double)(advance + glyph->horiBearingX) / 64.f;
double y = (window_height / 2) + (face_height >> 6) / 3 + ((double)glyph->horiBearingY / -64.f);
SDL_FRect srect = {
.x = 0.f,
.y = 0.f,
.w = glyph->width,
.h = glyph->height
};
SDL_FRect drect = {
.x = x,
.y = y,
.w = glyph->width,
.h = glyph->height
};
SDL_RenderTexture(renderer, glyph->texture, &srect, &drect);
}
}
}
char * format_base10(char * buf, int value, int digits)
{
static int table[10] = {
1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
};
if (digits <= 0)
return buf;
int power = table[digits - 1];
for (int i = 0; i < digits; i++) {
buf[i] = '0' + ((value / power) % 10);
power /= 10;
}
return buf + digits;
}
int main()
{
SDL_Window * window;
SDL_Renderer * renderer;
bool success = SDL_Init(SDL_INIT_VIDEO);
if (!success) printf("error: `%s`\n", SDL_GetError());
assert(success == true);
window = SDL_CreateWindow("sandbox",
512, // w
512, // h
SDL_WINDOW_RESIZABLE);// | SDL_WINDOW_MAXIMIZED);
int num_drivers = SDL_GetNumRenderDrivers();
printf("available drivers:\n");
for (int i = 0; i < num_drivers; i++) {
const char * s = SDL_GetRenderDriver(i);
printf(" %s\n", s);
}
renderer = SDL_CreateRenderer(window, "opengles2");
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_PropertiesID props = SDL_GetRendererProperties(renderer);
const char * name = SDL_GetRendererName(renderer);
assert(name != NULL);
printf("renderer: %s\n", name);
const SDL_PixelFormat * formats = SDL_GetPointerProperty(props, SDL_PROP_RENDERER_TEXTURE_FORMATS_POINTER, NULL);
assert(formats != NULL);
while (*formats != SDL_PIXELFORMAT_UNKNOWN) {
printf("%s\n", SDL_GetPixelFormatName(*formats++));
}
uint64_t ticks = SDL_GetTicks();
int font_size = 30;
load_font(renderer, font_size);
while (1) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
int window_width;
int window_height;
bool success = SDL_GetWindowSizeInPixels(window, &window_width, &window_height);
assert(success == true);
render(renderer, window_width, window_height, "test", 4);
while (SDL_GetTicks() - ticks < (1000 / 60)) { SDL_Delay(1); }
SDL_RenderPresent(renderer);
ticks = SDL_GetTicks();
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_EVENT_QUIT:
goto exit;
case SDL_EVENT_KEY_DOWN:
if (event.key.key == SDLK_ESCAPE)
goto exit;
break;
default:
break;
}
}
}
exit:
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}