summaryrefslogtreecommitdiff
path: root/src/text.c
diff options
context:
space:
mode:
authorConnor Thomson <blumatrikz@gmail.com>2026-09-08 18:27:39 -0700
committerConnor Thomson <blumatrikz@gmail.com>2026-09-08 18:27:39 -0700
commitf1ddc12b68b4e4c8087962de25260470e6df2bea (patch)
tree7d0440a6402a9b0ef75ded23fa64b68534255bba /src/text.c
parent6025860a61386bf767b29c3ec5fd0d31285f1056 (diff)
Add tuxanci2 files
Diffstat (limited to 'src/text.c')
-rw-r--r--src/text.c205
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();
+}