Commit: cb637e808de46b397ef4d6b62872fecb0e068f0c
Parent: 64a87ca0208c054cda530c8fb020531c92fd4388
Author: Randy Palamar
Date: Wed, 29 Oct 2025 12:11:11 -0600
add bitmap export
Diffstat:
| M | common.c | | | 86 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
| M | util.c | | | 15 | +++++++++++++++ |
2 files changed, 96 insertions(+), 5 deletions(-)
diff --git a/common.c b/common.c
@@ -216,6 +216,74 @@ load_render_model(Arena arena, c8 *positions_file_name, c8 *indices_file_name, c
return result;
}
+function b32
+export_bitmap(ViewerContext *ctx)
+{
+ #pragma pack(push, 1)
+ struct bmp_header {
+ u8 file_type[2];
+ u32 file_size;
+ u16 reserved_1;
+ u16 reserved_2;
+ u32 bitmap_offset;
+
+ u32 size;
+ s32 width;
+ s32 height;
+ u16 planes;
+ u16 bits_per_pixel;
+
+ u32 compression;
+ u32 size_of_bitmap;
+
+ s32 horizontal_resolution;
+ s32 vertical_resolution;
+ u32 colours_used;
+ u32 colours_important;
+
+ u32 red_mask;
+ u32 green_mask;
+ u32 blue_mask;
+ u32 alpha_mask;
+
+ u32 colour_space_type;
+ u32 cie_xyz_triples[9];
+ u32 gamma_red;
+ u32 gamma_green;
+ u32 gamma_blue;
+ };
+ #pragma pack(pop)
+
+ s32 texture_size = ctx->output_target.size.w * ctx->output_target.size.h * sizeof(u32);
+ s32 out_size = texture_size + round_up_power_of_2(sizeof(struct bmp_header));
+
+ Arena arena = ctx->video_arena;
+ void *out_buf = arena_alloc(&arena, out_size, 64, 1);
+
+ struct bmp_header *header = (struct bmp_header *)out_buf;
+ header->bitmap_offset = out_size - texture_size;
+ header->file_type[0] = 'B';
+ header->file_type[1] = 'M';
+ header->file_size = out_size;
+ header->size = 108;
+ header->width = ctx->output_target.size.w;
+ header->height = -ctx->output_target.size.h;
+ header->planes = 1;
+ header->bits_per_pixel = 32;
+ header->compression = 3;
+ header->size_of_bitmap = texture_size;
+ header->red_mask = 0xFF000000;
+ header->green_mask = 0x00FF0000;
+ header->blue_mask = 0x0000FF00;
+ header->alpha_mask = 0x000000FF;
+
+ glGetTextureImage(ctx->output_target.textures[0], 0, GL_RGBA,
+ GL_UNSIGNED_INT_8_8_8_8, texture_size,
+ (u8 *)out_buf + header->bitmap_offset);
+
+ return os_write_new_file("view.bmp", (str8){.len = out_size, .data = out_buf});
+}
+
function void
scroll_callback(GLFWwindow *window, f64 x, f64 y)
{
@@ -234,16 +302,24 @@ key_callback(GLFWwindow *window, s32 key, s32 scancode, s32 action, s32 modifier
if (key == GLFW_KEY_SPACE && action == GLFW_PRESS)
ctx->demo_mode = !ctx->demo_mode;
- if (key == GLFW_KEY_F12 && action == GLFW_PRESS && ctx->output_frames_count == 0) {
+ if (key == GLFW_KEY_F11 || key == GLFW_KEY_F12) {
sz frames = TOTAL_OUTPUT_FRAMES;
sz needed_bytes = sizeof(u32) * RENDER_TARGET_HEIGHT * RENDER_TARGET_WIDTH * frames;
if (!ctx->video_arena.beg) {
ctx->video_arena = os_alloc_arena(needed_bytes);
- if (!ctx->video_arena.beg) {
- fputs("failed to allocate space for output video, video "
- "won't be saved\n", stderr);
- }
+ if (!ctx->video_arena.beg)
+ fputs("failed to allocate space for output data\n", stderr);
}
+ }
+
+ if (key == GLFW_KEY_F11) {
+ if (ctx->video_arena.beg) {
+ if (export_bitmap(ctx)) fputs("exported bitmap\n", stderr);
+ else fputs("failed to export bitmap\n", stderr);
+ }
+ }
+
+ if (key == GLFW_KEY_F12 && action == GLFW_PRESS && ctx->output_frames_count == 0) {
if (ctx->video_arena.beg) {
ctx->output_frames_count = TOTAL_OUTPUT_FRAMES;
ctx->cycle_t = 0;
diff --git a/util.c b/util.c
@@ -351,6 +351,21 @@ stream_append_f64(Stream *s, f64 f, s64 prec)
}
}
+function u32
+clz_u32(u32 a)
+{
+ u32 result = 32;
+ if (a) result = (u32)__builtin_clz(a);
+ return result;
+}
+
+function u32
+round_up_power_of_2(u32 a)
+{
+ u32 result = 0x80000000UL >> (clz_u32(a - 1) - 1);
+ return result;
+}
+
function Stream
arena_stream(Arena a)
{