From 5bfa3d5530fe91fa66718201249674fdee9bd73c Mon Sep 17 00:00:00 2001 From: Larpon Date: Wed, 13 Oct 2021 20:22:58 +0200 Subject: [PATCH] sokol: add screenshot function to OpenGL based backends (#12169) --- thirdparty/sokol/sokol_gfx.h | 3 ++ thirdparty/sokol/sokol_v.post.h | 9 ++++ thirdparty/sokol/{sokol_v.h => sokol_v.pre.h} | 1 - vlib/sokol/c/declaration.c.v | 3 +- vlib/sokol/sapp/sapp_v.c.v | 50 +++++++++++++++++++ 5 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 thirdparty/sokol/sokol_v.post.h rename thirdparty/sokol/{sokol_v.h => sokol_v.pre.h} (99%) create mode 100644 vlib/sokol/sapp/sapp_v.c.v diff --git a/thirdparty/sokol/sokol_gfx.h b/thirdparty/sokol/sokol_gfx.h index 6938a945d1..2375ad54f2 100644 --- a/thirdparty/sokol/sokol_gfx.h +++ b/thirdparty/sokol/sokol_gfx.h @@ -4657,7 +4657,9 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data #if defined(_SOKOL_USE_WIN32_GL_LOADER) // X Macro list of GL function names and signatures +// __v_ start #define _SG_GL_FUNCS \ + _SG_XMACRO(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * data)) \ _SG_XMACRO(glBindVertexArray, void, (GLuint array)) \ _SG_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \ _SG_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \ @@ -4754,6 +4756,7 @@ _SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data _SG_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ _SG_XMACRO(glFrontFace, void, (GLenum mode)) \ _SG_XMACRO(glCullFace, void, (GLenum mode)) +// __v_ end // generate GL function pointer typedefs #define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; diff --git a/thirdparty/sokol/sokol_v.post.h b/thirdparty/sokol/sokol_v.post.h new file mode 100644 index 0000000000..4e0110d468 --- /dev/null +++ b/thirdparty/sokol/sokol_v.post.h @@ -0,0 +1,9 @@ +#if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + void v_sapp_gl_read_rgba_pixels(int x, int y, int width, int height, unsigned char* pixels) { + glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + } +#else + void v_sapp_gl_read_rgba_pixels(int x, int y, int width, int height, unsigned char* pixels) { + // TODO + } +#endif diff --git a/thirdparty/sokol/sokol_v.h b/thirdparty/sokol/sokol_v.pre.h similarity index 99% rename from thirdparty/sokol/sokol_v.h rename to thirdparty/sokol/sokol_v.pre.h index cc08678928..4b35d582fb 100644 --- a/thirdparty/sokol/sokol_v.h +++ b/thirdparty/sokol/sokol_v.pre.h @@ -17,4 +17,3 @@ #define printf(...) __android_log_print(ANDROID_LOG_INFO, V_ANDROID_LOG_TAG_NAME, __VA_ARGS__) #define fprintf(a, ...) __android_log_print(ANDROID_LOG_ERROR, V_ANDROID_LOG_TAG_NAME, __VA_ARGS__) #endif - diff --git a/vlib/sokol/c/declaration.c.v b/vlib/sokol/c/declaration.c.v index c2e435abb5..7800269108 100644 --- a/vlib/sokol/c/declaration.c.v +++ b/vlib/sokol/c/declaration.c.v @@ -49,10 +49,11 @@ $if gcboehm ? { #define SOKOL_FREE GC_FREE } -#include "sokol_v.h" +#include "sokol_v.pre.h" #include "sokol_app.h" #define SOKOL_IMPL #define SOKOL_NO_DEPRECATED #include "sokol_gfx.h" #define SOKOL_GL_IMPL #include "util/sokol_gl.h" +#include "sokol_v.post.h" diff --git a/vlib/sokol/sapp/sapp_v.c.v b/vlib/sokol/sapp/sapp_v.c.v new file mode 100644 index 0000000000..95cb19f618 --- /dev/null +++ b/vlib/sokol/sapp/sapp_v.c.v @@ -0,0 +1,50 @@ +module sapp + +import os + +// v_sapp_gl_read_rgba_pixels reads pixles from the OpenGL buffer into `pixels`. +fn C.v_sapp_gl_read_rgba_pixels(x int, y int, width int, height int, pixels charptr) + +// screenshot takes a screenshot of the current window. +[inline] +pub fn screenshot(path string) ? { + if !path.ends_with('.ppm') { + return error(@MOD + '.' + @FN + ' currently only supports .ppm files.') + } + + w := width() + h := height() + + size := w * h * 4 // + mut pixels := []byte{len: size, init: 0} + + C.v_sapp_gl_read_rgba_pixels(0, 0, w, h, pixels.data) + + // TODO use separate thread for writing the data + // TODO use stbi to support more formats + // stbi.write_png(path, w, h, components, pixels.data, 3 * w) + // stbi.write_tga(path, w, h, components, pixels.data) + write_rgba_to_ppm(path, w, h, 4, pixels) ? + + unsafe { + pixels.free() + } +} + +// write_rgba_to_ppm writes `pixels` data in RGBA format to PPM3 format. +fn write_rgba_to_ppm(path string, w int, h int, components int, pixels []byte) ? { + mut f_out := os.create(path) ? + f_out.writeln('P3') ? + f_out.writeln('$w $h') ? + f_out.writeln('255') ? + for i := h - 1; i >= 0; i-- { + for j := 0; j < w; j++ { + idx := i * w * components + j * components + r := int(pixels[idx]) + g := int(pixels[idx + 1]) + b := int(pixels[idx + 2]) + f_out.write_string('$r $g $b ') ? + } + } + f_out.close() +}