#include #include #include #include #include #include #include #include #include #include #include FT_FREETYPE_H #include #include "math/vec2.hpp" #include "math/mat2x2.hpp" 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 (unsigned int y = 0; y < face->glyph->bitmap.rows; y++) { for (unsigned 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; } int32_t render_text(SDL_Renderer * renderer, int32_t x, int32_t y, const char * s, int length) { int32_t x_advance = x; int32_t y_advance = y; for (int i = 0; i < length; i++) { char c = s[i]; struct glyph * glyph = &glyphs[c - 0x20]; x_advance += glyph->horiAdvance; if (glyph->texture != NULL) { float x = (float)(x_advance + glyph->horiBearingX) / 64.f; float y = (float)(y_advance - glyph->horiBearingY) / 64.f; SDL_FRect srect = { .x = 0.f, .y = 0.f, .w = (float)glyph->width, .h = (float)glyph->height }; SDL_FRect drect = { .x = x, .y = y, .w = (float)glyph->width, .h = (float)glyph->height }; SDL_RenderTexture(renderer, glyph->texture, &srect, &drect); } } return x_advance; } using vec2 = vec<2, float>; using mat2x2 = mat<2, 2, float>; struct line { vec2 a; vec2 b; }; struct state { struct line line; vec2 normal; vec2 mouse_position; }; struct state state = { .line = { { 0, -0.5 }, { 0, 0.5 }, }, .normal = { -1, 0 }, }; static int window_width = 1; static int window_height = 1; static inline int min(int a, int b) { return (a > b) ? b : a; } vec2 transform_vertex(vec2 v, float scale) { float dim = ((float)min(window_height, window_width)) / 2.0f; float x = v.x; float y = v.y; x *= scale; y *= scale; return { x * dim + window_width / 2.0f, y * dim + window_height / 2.0f, }; } vec2 inverse_transform(float x, float y) { float dim = ((float)min(window_height, window_width)) / 2.0f; assert(dim != 0); return { -(window_width - 2 * x) / (2 * dim), -(window_height - 2 * y) / (2 * dim), }; } struct line transform_line(struct line line, float scale) { return { transform_vertex(line.a, scale), transform_vertex(line.b, scale), }; } static inline void render_line(SDL_Renderer * renderer, struct line line) { assert(SDL_RenderLine(renderer, line.a.x, line.a.y, line.b.x, line.b.y)); } void render_lines(SDL_Renderer * renderer) { // line assert(SDL_SetRenderDrawColorFloat(renderer, 1, 0.5, 0.5, 1)); struct line tl = transform_line(state.line, 1.0f); render_line(renderer, tl); // normal assert(SDL_SetRenderDrawColorFloat(renderer, 1, 0, 0, 1)); struct line normal_line = {{0, 0}, state.normal}; render_line(renderer, transform_line(normal_line, 0.5f)); // mouse assert(SDL_SetRenderDrawColorFloat(renderer, 0, 0, 1, 1)); struct line mouse_line = {{0, 0}, state.mouse_position}; render_line(renderer, transform_line(mouse_line, 0.5f)); // foo float s = cross(state.normal, -state.mouse_position); float c = dot(state.normal, -state.mouse_position); mat2x2 r = { c, -s, s, c, }; vec2 nr = r * state.normal; assert(SDL_SetRenderDrawColorFloat(renderer, 0, 1, 0, 1)); struct line nr_line = {{0, 0}, nr}; render_line(renderer, transform_line(nr_line, 0.5f)); { assert(SDL_SetRenderDrawColorFloat(renderer, 0.5, 1, 0.5, 1)); vec2 ar = r * state.line.a; vec2 br = r * state.line.b; struct line tl = transform_line({ar, br}, 1.0f); render_line(renderer, tl); } } void render_text_state(SDL_Renderer * renderer) { int32_t x_advance = 10 << 6; int32_t y_advance = face_height; x_advance = render_text(renderer, x_advance, y_advance, "dot: ", 5); //float d = dot(state.normal, state.mouse_position); float d = dot({0, 1}, state.mouse_position); char buf[64]; int len = snprintf(buf, 64, "%.03f", d); x_advance = render_text(renderer, x_advance, y_advance, buf, len); } 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, "software"); 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 = (const SDL_PixelFormat *)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 = 25; load_font(renderer, font_size); while (1) { SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); bool success = SDL_GetWindowSizeInPixels(window, &window_width, &window_height); assert(success == true); render_text_state(renderer); render_lines(renderer); 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; case SDL_EVENT_MOUSE_BUTTON_DOWN: if (event.button.button == 1) { vec2 mv = inverse_transform(event.button.x, event.button.y); float m = magnitude(mv); state.mouse_position = mv / m; } break; default: break; } } } exit: SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); }