Atlas - testgpurender_msdf.c

Home / ext / SDL / test Lines: 1 | Size: 9879 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely. 11*/ 12 13#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ 14#include <SDL3/SDL.h> 15#include <SDL3/SDL_main.h> 16#include <SDL3/SDL_test.h> 17 18#include "testutils.h" 19 20/* This font was created with: 21 * ./msdf-atlas-gen.exe -font OpenSans-VariableFont_wdth,wght.ttf -chars '[32,126]' -type msdf -potr -yorigin top -imageout msdf_font.png -csv msdf_font.csv 22 */ 23 24/* This is the distance field range in pixels used when generating the font atlas, defaults to 2 */ 25#define DISTANCE_FIELD_RANGE 2.0f 26 27/* MSDF shaders */ 28#include "testgpurender_msdf.frag.dxil.h" 29#include "testgpurender_msdf.frag.msl.h" 30#include "testgpurender_msdf.frag.spv.h" 31 32typedef struct 33{ 34 float distance_field_range; 35 float texture_width; 36 float texture_height; 37 float padding; 38} MSDFShaderUniforms; 39 40static SDLTest_CommonState *state = NULL; 41static SDL_Window *window = NULL; 42static SDL_Renderer *renderer = NULL; 43static SDL_Texture *font_texture = NULL; 44static SDL_GPUDevice *device = NULL; 45static SDL_GPUShader *shader = NULL; 46static SDL_GPURenderState *render_state = NULL; 47 48typedef struct 49{ 50 bool loaded; 51 SDL_FRect src; 52 SDL_FRect dst; 53 float advance; 54} GlyphInfo; 55 56static GlyphInfo glyphs[128]; 57 58static bool LoadFontTexture(void) 59{ 60 font_texture = LoadTexture(renderer, "msdf_font.png", false); 61 if (!font_texture) { 62 SDL_Log("Failed to create font texture: %s", SDL_GetError()); 63 return false; 64 } 65 SDL_SetTextureBlendMode(font_texture, SDL_BLENDMODE_BLEND); 66 67 /* Set the font color, doesn't need to be done every frame */ 68 SDL_SetTextureColorMod(font_texture, 0, 0, 0); 69 70 return true; 71} 72 73static bool LoadFontLayout(void) 74{ 75 const char *file = "msdf_font.csv"; 76 char *path; 77 int offset = 0, len, codepoint; 78 float src_left, src_top, src_right, src_bottom; 79 float dst_left, dst_top, dst_right, dst_bottom; 80 float advance; 81 char *font_layout; 82 83 path = GetNearbyFilename(file); 84 if (path) { 85 font_layout = (char *)SDL_LoadFile(path, NULL); 86 SDL_free(path); 87 } else { 88 font_layout = (char *)SDL_LoadFile(file, NULL); 89 } 90 if (!font_layout) { 91 SDL_Log("Failed to load font layout: %s", SDL_GetError()); 92 return false; 93 } 94 95 while (SDL_sscanf(&font_layout[offset], "%d,%f,%f,%f,%f,%f,%f,%f,%f,%f%n", 96 &codepoint, &advance, 97 &dst_left, &dst_top, &dst_right, &dst_bottom, 98 &src_left, &src_top, &src_right, &src_bottom, &len) == 10) { 99 if (codepoint >= 0 && codepoint < SDL_arraysize(glyphs)) { 100 GlyphInfo *glyph = &glyphs[codepoint]; 101 glyph->loaded = true; 102 glyph->src.x = src_left; 103 glyph->src.y = src_top; 104 glyph->src.w = src_right - src_left; 105 glyph->src.h = src_bottom - src_top; 106 glyph->dst.x = dst_left; 107 glyph->dst.y = dst_top; 108 glyph->dst.w = dst_right - dst_left; 109 glyph->dst.h = dst_bottom - dst_top; 110 glyph->advance = advance; 111 } 112 offset += len; 113 } 114 SDL_free(font_layout); 115 return true; 116} 117 118static float MeasureText(const char *text, float font_size) 119{ 120 float width = 0.0f; 121 122 while (*text) { 123 GlyphInfo *glyph; 124 Uint32 codepoint = SDL_StepUTF8(&text, NULL); 125 if (codepoint >= SDL_arraysize(glyphs)) { 126 continue; 127 } 128 129 glyph = &glyphs[codepoint]; 130 if (!glyph->loaded) { 131 continue; 132 } 133 width += (glyph->advance * font_size); 134 } 135 return width; 136} 137 138static void RenderText(const char *text, float font_size, float x, float y) 139{ 140 SDL_FRect dst; 141 142 /* The y coordinate is actually the baseline for the text */ 143 144 while (*text) { 145 GlyphInfo *glyph; 146 Uint32 codepoint = SDL_StepUTF8(&text, NULL); 147 if (codepoint >= SDL_arraysize(glyphs)) { 148 continue; 149 } 150 151 glyph = &glyphs[codepoint]; 152 if (!glyph->loaded) { 153 continue; 154 } 155 156 dst.x = x + glyph->dst.x * font_size; 157 dst.y = y + glyph->dst.y * font_size; 158 dst.w = glyph->dst.w * font_size; 159 dst.h = glyph->dst.h * font_size; 160 SDL_RenderTexture(renderer, font_texture, &glyph->src, &dst); 161 x += (glyph->advance * font_size); 162 } 163} 164 165static bool InitGPURenderState(void) 166{ 167 SDL_GPUShaderFormat formats; 168 SDL_GPUShaderCreateInfo info; 169 SDL_GPURenderStateCreateInfo createinfo; 170 MSDFShaderUniforms uniforms; 171 172 device = SDL_GetGPURendererDevice(renderer); 173 if (!device) { 174 SDL_Log("Couldn't get GPU device"); 175 return false; 176 } 177 178 formats = SDL_GetGPUShaderFormats(device); 179 if (formats == SDL_GPU_SHADERFORMAT_INVALID) { 180 SDL_Log("Couldn't get supported shader formats: %s", SDL_GetError()); 181 return false; 182 } 183 184 SDL_zero(info); 185 if (formats & SDL_GPU_SHADERFORMAT_SPIRV) { 186 info.format = SDL_GPU_SHADERFORMAT_SPIRV; 187 info.code = testgpurender_msdf_frag_spv; 188 info.code_size = testgpurender_msdf_frag_spv_len; 189 } else if (formats & SDL_GPU_SHADERFORMAT_DXIL) { 190 info.format = SDL_GPU_SHADERFORMAT_DXIL; 191 info.code = testgpurender_msdf_frag_dxil; 192 info.code_size = testgpurender_msdf_frag_dxil_len; 193 } else if (formats & SDL_GPU_SHADERFORMAT_MSL) { 194 info.format = SDL_GPU_SHADERFORMAT_MSL; 195 info.code = testgpurender_msdf_frag_msl; 196 info.code_size = testgpurender_msdf_frag_msl_len; 197 } else { 198 SDL_Log("No supported shader format found"); 199 return false; 200 } 201 info.num_samplers = 1; 202 info.num_uniform_buffers = 1; 203 info.stage = SDL_GPU_SHADERSTAGE_FRAGMENT; 204 shader = SDL_CreateGPUShader(device, &info); 205 if (!shader) { 206 SDL_Log("Couldn't create shader: %s", SDL_GetError()); 207 return false; 208 } 209 210 SDL_zero(createinfo); 211 createinfo.fragment_shader = shader; 212 render_state = SDL_CreateGPURenderState(renderer, &createinfo); 213 if (!render_state) { 214 SDL_Log("Couldn't create render state: %s", SDL_GetError()); 215 return false; 216 } 217 218 SDL_zero(uniforms); 219 uniforms.distance_field_range = DISTANCE_FIELD_RANGE; 220 uniforms.texture_width = (float)font_texture->w; 221 uniforms.texture_height = (float)font_texture->h; 222 if (!SDL_SetGPURenderStateFragmentUniforms(render_state, 0, &uniforms, sizeof(uniforms))) { 223 SDL_Log("Couldn't set uniform data: %s", SDL_GetError()); 224 return false; 225 } 226 227 return true; 228} 229 230static void QuitGPURenderState(void) 231{ 232 if (render_state) { 233 SDL_DestroyGPURenderState(render_state); 234 render_state = NULL; 235 } 236 if (shader) { 237 SDL_ReleaseGPUShader(device, shader); 238 shader = NULL; 239 } 240} 241 242/* This function runs once at startup. */ 243SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) 244{ 245 const char *description = "GPU render MSDF example"; 246 247 SDL_SetAppMetadata(description, "1.0", "com.example.testgpurender_msdf"); 248 249 state = SDLTest_CommonCreateState(argv, 0); 250 if (!state) { 251 return SDL_APP_FAILURE; 252 } 253 if (!SDLTest_CommonDefaultArgs(state, argc, argv)) { 254 return SDL_APP_FAILURE; 255 } 256 257 if (!SDL_Init(SDL_INIT_VIDEO)) { 258 SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); 259 return SDL_APP_FAILURE; 260 } 261 262 window = SDL_CreateWindow(description, 640, 480, 0); 263 if (!window) { 264 SDL_Log("Couldn't create window: %s", SDL_GetError()); 265 return SDL_APP_FAILURE; 266 } 267 268 renderer = SDL_CreateRenderer(window, SDL_GPU_RENDERER); 269 if (!renderer) { 270 SDL_Log("Couldn't create renderer: %s", SDL_GetError()); 271 return SDL_APP_FAILURE; 272 } 273 SDL_SetRenderVSync(renderer, 1); 274 275 if (!LoadFontTexture() || !LoadFontLayout()) { 276 return SDL_APP_FAILURE; 277 } 278 279 if (!InitGPURenderState()) { 280 return SDL_APP_FAILURE; 281 } 282 283 return SDL_APP_CONTINUE; /* carry on with the program! */ 284} 285 286/* This function runs when a new event (mouse input, keypresses, etc) occurs. */ 287SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) 288{ 289 if (event->type == SDL_EVENT_QUIT || 290 (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_ESCAPE)) { 291 return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ 292 } 293 return SDL_APP_CONTINUE; /* carry on with the program! */ 294} 295 296/* This function runs once per frame, and is the heart of the program. */ 297SDL_AppResult SDL_AppIterate(void *appstate) 298{ 299 const char *text = "Hello world!"; 300 float text_width; 301 float text_height; 302 float x, y; 303 int output_width, output_height; 304 305 SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); 306 SDL_RenderClear(renderer); 307 308 text_height = 72.0f; 309 text_width = MeasureText(text, text_height); 310 SDL_GetCurrentRenderOutputSize(renderer, &output_width, &output_height); 311 x = (output_width - text_width) / 2; 312 y = (output_height - text_height) / 2; 313 SDL_SetGPURenderState(renderer, render_state); 314 RenderText("Hello World!", text_height, x, y); 315 SDL_SetGPURenderState(renderer, NULL); 316 317 SDL_RenderPresent(renderer); 318 319 return SDL_APP_CONTINUE; /* carry on with the program! */ 320} 321 322/* This function runs once at shutdown. */ 323void SDL_AppQuit(void *appstate, SDL_AppResult result) 324{ 325 /* SDL will clean up the window/renderer for us. */ 326 QuitGPURenderState(); 327 SDL_Quit(); 328 SDLTest_CommonDestroyState(state); 329} 330 331
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.