aboutsummaryrefslogtreecommitdiff
path: root/src/image_writer.c
blob: c4d66f40fdd4b7c51ce1077e4131719b36e281fd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include "../include/image_writer.h"
#include "../include/egl.h"
#include "../include/utils.h"

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "../external/stb_image_write.h"

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <assert.h>

/* TODO: Support hdr/10-bit */
bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int width, int height) {
    memset(self, 0, sizeof(*self));
    self->source = GSR_IMAGE_WRITER_SOURCE_OPENGL;
    self->egl = egl;
    self->width = width;
    self->height = height;
    self->texture = gl_create_texture(self->egl, self->width, self->height, GL_RGB8, GL_RGB, GL_NEAREST); /* TODO: use GL_RGB16 instead of GL_RGB8 for hdr/10-bit */
    if(self->texture == 0) {
        fprintf(stderr, "gsr error: gsr_image_writer_init: failed to create texture\n");
        return false;
    }
    return true;
}

bool gsr_image_writer_init_memory(gsr_image_writer *self, const void *memory, int width, int height) {
    memset(self, 0, sizeof(*self));
    self->source = GSR_IMAGE_WRITER_SOURCE_OPENGL;
    self->width = width;
    self->height = height;
    self->memory = memory;
    return true;
}

void gsr_image_writer_deinit(gsr_image_writer *self) {
    if(self->texture) {
        self->egl->glDeleteTextures(1, &self->texture);
        self->texture = 0;
    }
}

static bool gsr_image_writer_write_memory_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality, const void *data) {
    if(quality < 1)
        quality = 1;
    else if(quality > 100)
        quality = 100;

    bool success = false;
    switch(image_format) {
        case GSR_IMAGE_FORMAT_JPEG:
            success = stbi_write_jpg(filepath, self->width, self->height, 3, data, quality);
            break;
        case GSR_IMAGE_FORMAT_PNG:
            success = stbi_write_png(filepath, self->width, self->height, 3, data, 0);
            break;
    }

    if(!success)
        fprintf(stderr, "gsr error: gsr_image_writer_write_to_file: failed to write image data to output file %s\n", filepath);

    return success;
}

static bool gsr_image_writer_write_opengl_texture_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality) {
    assert(self->source == GSR_IMAGE_WRITER_SOURCE_OPENGL);
    uint8_t *frame_data = malloc(self->width * self->height * 3);
    if(!frame_data) {
        fprintf(stderr, "gsr error: gsr_image_writer_write_to_file: failed to allocate memory for image frame\n");
        return false;
    }

    // TODO: hdr support
    self->egl->glBindTexture(GL_TEXTURE_2D, self->texture);
    // We could use glGetTexSubImage, but it's only available starting from opengl 4.5
    self->egl->glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, frame_data);
    self->egl->glBindTexture(GL_TEXTURE_2D, 0);

    self->egl->glFlush();
    self->egl->glFinish();
    
    const bool success = gsr_image_writer_write_memory_to_file(self, filepath, image_format, quality, frame_data);
    free(frame_data);
    return success;
}

bool gsr_image_writer_write_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality) {
    switch(self->source) {
        case GSR_IMAGE_WRITER_SOURCE_OPENGL:
            return gsr_image_writer_write_opengl_texture_to_file(self, filepath, image_format, quality);
        case GSR_IMAGE_WRITER_SOURCE_MEMORY:
            return gsr_image_writer_write_memory_to_file(self, filepath, image_format, quality, self->memory);
    }
    return false;
}