commit f48edc221044c6655d781e31fa5f8f2e5e1e8780 Author: Zack Buhman Date: Fri Feb 7 12:51:37 2025 -0600 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..df18bcb --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.out +*.o +*.gch +*.d +main +serial_forwarder +time_display \ No newline at end of file diff --git a/DejaVuSansMono.ttf b/DejaVuSansMono.ttf new file mode 100644 index 0000000..f578602 Binary files /dev/null and b/DejaVuSansMono.ttf differ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..14d4831 --- /dev/null +++ b/Makefile @@ -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.% diff --git a/main.c b/main.c new file mode 100644 index 0000000..d3f5ceb --- /dev/null +++ b/main.c @@ -0,0 +1,244 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H + +#include + +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(); +}