/*
* 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 .
*/
#include
#include
#include
#include
#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();
}