diff options
| author | Connor Thomson <blumatrikz@gmail.com> | 2026-09-08 18:27:39 -0700 |
|---|---|---|
| committer | Connor Thomson <blumatrikz@gmail.com> | 2026-09-08 18:27:39 -0700 |
| commit | f1ddc12b68b4e4c8087962de25260470e6df2bea (patch) | |
| tree | 7d0440a6402a9b0ef75ded23fa64b68534255bba /src/text.c | |
| parent | 6025860a61386bf767b29c3ec5fd0d31285f1056 (diff) | |
Add tuxanci2 files
Diffstat (limited to 'src/text.c')
| -rw-r--r-- | src/text.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/text.c b/src/text.c new file mode 100644 index 0000000..6cff2b4 --- /dev/null +++ b/src/text.c @@ -0,0 +1,205 @@ +/* + * Tuxánci 2 - A first person shooter + * Copyright (C) 2007-2011 Tuxánci Development Team + * Copyright (C) 2025-2026 Connor Thomson + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "files.h" +#include "logging.h" +#include "main.h" +#include "ta_sdl_ttf.h" +#include "ta_sdl.h" +#include "gpu.h" +#include "window.h" +#include "color.h" + +static TTF_Font *ta_text_font; +SDL_Surface *ta_text_surface; +int ta_text_current_x; +int ta_text_current_y; + +static void ta_text_upload_surface(SDL_GPUCommandBuffer *command_buffer, SDL_Surface *text_surface, SDL_GPUTexture **texture, Uint32 *texture_width, Uint32 *texture_height) { + if (!text_surface) { + return; + } + + SDL_Surface *surface = ta_sdl_convert_surface(text_surface, SDL_PIXELFORMAT_RGBA32); + + Uint32 width = (Uint32)surface->w; + Uint32 height = (Uint32)surface->h; + if (!*texture || width != *texture_width || height != *texture_height) { + SDL_ReleaseGPUTexture(ta_gpu_device, *texture); + SDL_GPUTextureCreateInfo texture_info = { + .type = SDL_GPU_TEXTURETYPE_2D, + .format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM, + .usage = SDL_GPU_TEXTUREUSAGE_SAMPLER, + .width = width, + .height = height, + .layer_count_or_depth = 1, + .num_levels = 1, + }; + *texture = ta_sdl_create_gpu_texture(ta_gpu_device, &texture_info); + *texture_width = width; + *texture_height = height; + } + + SDL_GPUTransferBufferCreateInfo transfer_info = { + .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, + .size = width * height * 4, + }; + SDL_GPUTransferBuffer *transfer_buffer = ta_sdl_create_gpu_transfer_buffer(ta_gpu_device, &transfer_info); + void *mapped = ta_sdl_map_gpu_transfer_buffer(ta_gpu_device, transfer_buffer, false); + for (Uint32 row = 0; row < height; row++) { + memcpy( + (Uint8 *)mapped + row * width * 4, + (Uint8 *)surface->pixels + row * (Uint32)surface->pitch, + width * 4 + ); + } + SDL_UnmapGPUTransferBuffer(ta_gpu_device, transfer_buffer); + + SDL_GPUTextureTransferInfo source = { + .transfer_buffer = transfer_buffer, + .pixels_per_row = width, + .rows_per_layer = height, + }; + SDL_GPUTextureRegion destination = { + .texture = *texture, + .w = width, + .h = height, + .d = 1, + }; + SDL_GPUCopyPass *copy_pass = ta_sdl_begin_gpu_copy_pass(command_buffer); + SDL_UploadToGPUTexture(copy_pass, &source, &destination, true); + SDL_EndGPUCopyPass(copy_pass); + SDL_ReleaseGPUTransferBuffer(ta_gpu_device, transfer_buffer); + + SDL_DestroySurface(surface); +} + +void ta_gpu_upload_text(SDL_GPUCommandBuffer *command_buffer) { + ta_text_upload_surface(command_buffer, ta_text_surface, &ta_gpu_text_texture, &ta_gpu_text_texture_width, &ta_gpu_text_texture_height); +} + +static void ta_text_render_surface(SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *render_pass, SDL_Surface *text_surface, SDL_GPUTexture *texture, Uint32 texture_width, Uint32 texture_height, int x, int y) { + if (!text_surface) { + return; + } + + float clip_rect[4] = { + 2.0f * (float) x / (float)ta_window_width - 1.0f, + 1.0f - 2.0f * (float)(y + (int)texture_height) / (float)ta_window_height, + 2.0f * (float)(x + (int)texture_width) / (float)ta_window_width - 1.0f, + 1.0f - 2.0f * (float) y / (float)ta_window_height, + }; + SDL_PushGPUVertexUniformData(command_buffer, 0, clip_rect, sizeof(clip_rect)); + SDL_BindGPUGraphicsPipeline(render_pass, ta_gpu_text_pipeline); + SDL_GPUTextureSamplerBinding binding = { + .texture = texture, + .sampler = ta_gpu_text_sampler, + }; + SDL_BindGPUFragmentSamplers(render_pass, 0, &binding, 1); + SDL_DrawGPUPrimitives(render_pass, 6, 1, 0, 0); +} + +void ta_text_render(SDL_GPUCommandBuffer *command_buffer, SDL_GPURenderPass *render_pass) { + ta_text_render_surface(command_buffer, render_pass, ta_text_surface, ta_gpu_text_texture, ta_gpu_text_texture_width, ta_gpu_text_texture_height, ta_text_current_x, ta_text_current_y); +} + +void ta_text_init(void) { + ta_sdl_ttf_init(); + + SDL_IOStream *font_io = ta_sdl_io_from_mem( + file_data_fonts_DejaVuSans_ttf_start, + file_data_fonts_DejaVuSans_ttf_size + ); + + ta_text_font = ta_sdl_ttf_open_font(font_io); +} + +static void ta_text_draw_surface(SDL_Surface **target, float size, int red, int green, int blue, int alpha, const char *format, va_list arguments) { + if (!ta_text_font || !format || size <= 0.0f) { + return; + } + + va_list length_arguments; + va_copy(length_arguments, arguments); + int length = vsnprintf(NULL, 0, format, length_arguments); + va_end(length_arguments); + if (length < 0) { + return; + } + + char *text = malloc((size_t)length + 1); + if (!text) { + return; + } + + va_list text_arguments; + va_copy(text_arguments, arguments); + vsnprintf(text, (size_t)length + 1, format, text_arguments); + va_end(text_arguments); + + ta_sdl_ttf_set_font_size(ta_text_font, size); + + SDL_Color color = { + red, + green, + blue, + alpha + }; + SDL_Surface *surface = ta_sdl_ttf_render_text_blended(ta_text_font, text, 0, color); + free(text); + + SDL_DestroySurface(*target); + *target = surface; +} + +void ta_text_draw(int x, int y, float size, ta_color color, const char *format, ...) { + int red = color.red; + int green = color.green; + int blue = color.blue; + int alpha = color.alpha; + + va_list arguments; + va_start(arguments, format); + + ta_text_draw_surface(&ta_text_surface, size, red, green, blue, alpha, format, arguments); + + va_end(arguments); + ta_text_current_x = x; + ta_text_current_y = y; +} + +void ta_text_clear(void) { + SDL_DestroySurface(ta_text_surface); + ta_text_surface = NULL; +} + +void ta_text_shutdown(void) { + ta_text_clear(); + + if (ta_text_font) { + TTF_CloseFont(ta_text_font); + ta_text_font = NULL; + } + + TTF_Quit(); +} |