gg: support `VGG_STOP_AT_FRAME=120 VGG_SCREENSHOT_FOLDER=. VGG_SCREENSHOT_FRAMES=10,20,30 ./v -d gg_record run examples/gg/bezier_anim.v` (#12767)

pull/12769/head
Delyan Angelov 2021-12-08 22:38:33 +02:00 committed by GitHub
parent 85f3372a32
commit 0021fbbaa9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 123 additions and 26 deletions

View File

@ -181,7 +181,7 @@ fn get_all_commands() []Command {
}
$if macos || linux {
res << Command{
line: '$vexe -o v.c cmd/v && cc -Werror -I "$vroot/thirdparty/stdatomic/nix" v.c -lpthread && rm -rf a.out'
line: '$vexe -o v.c cmd/v && cc -Werror -I "$vroot/thirdparty/stdatomic/nix" v.c -lpthread -lm && rm -rf a.out'
label: 'v.c should be buildable with no warnings...'
okmsg: 'v.c can be compiled without warnings. This is good :)'
rmfile: 'v.c'

View File

@ -106,6 +106,8 @@ fn gg_frame_fn(user_data voidptr) {
// return
}
ctx.record_frame()
if ctx.ui_mode && !ctx.needs_refresh {
// Draw 3 more frames after the "stop refresh" command
ctx.ticks++
@ -250,8 +252,8 @@ fn gg_fail_fn(msg &char, user_data voidptr) {
}
}
pub fn (gg &Context) run() {
sapp.run(&gg.window)
pub fn (ctx &Context) run() {
sapp.run(&ctx.window)
}
// quit closes the context window and exits the event loop for it

49
vlib/gg/recorder.v 100644
View File

@ -0,0 +1,49 @@
module gg
import os
import sokol.sapp
[heap]
pub struct SSRecorderSettings {
pub mut:
stop_at_frame i64 = -1
screenshot_frames []u64
screenshot_folder string
screenshot_prefix string
}
const recorder_settings = new_gg_recorder_settings()
fn new_gg_recorder_settings() &SSRecorderSettings {
$if gg_record ? {
stop_frame := os.getenv_opt('VGG_STOP_AT_FRAME') or { '-1' }.i64()
frames := os.getenv('VGG_SCREENSHOT_FRAMES').split_any(',').map(it.u64())
folder := os.getenv('VGG_SCREENSHOT_FOLDER')
prefix := os.join_path_single(folder, os.file_name(os.executable()).all_before('.') + '_')
return &SSRecorderSettings{
stop_at_frame: stop_frame
screenshot_frames: frames
screenshot_folder: folder
screenshot_prefix: prefix
}
} $else {
return &SSRecorderSettings{}
}
}
[if gg_record ?]
pub fn (mut ctx Context) record_frame() {
if ctx.frame in gg.recorder_settings.screenshot_frames {
screenshot_file_path := '$gg.recorder_settings.screenshot_prefix${ctx.frame}.png'
$if gg_record_trace ? {
eprintln('>>> screenshoting $screenshot_file_path')
}
sapp.screenshot_png(screenshot_file_path) or { panic(err) }
}
if ctx.frame == gg.recorder_settings.stop_at_frame {
$if gg_record_trace ? {
eprintln('>>> exiting at frame $ctx.frame')
}
exit(0)
}
}

View File

@ -1,50 +1,54 @@
module sapp
import os
import stbi
// 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.')
}
return screenshot_ppm(path)
}
w := width()
h := height()
// screenshot_ppm takes a screenshot of the current window as a .ppm file
[manualfree]
pub fn screenshot_ppm(path string) ? {
ss := screenshot_window()
write_rgba_to_ppm(path, ss.width, ss.height, 4, ss.pixels) ?
unsafe { ss.destroy() }
}
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()
}
// screenshot_png takes a screenshot of the current window as a .png file
[manualfree]
pub fn screenshot_png(path string) ? {
ss := screenshot_window()
stbi.set_flip_vertically_on_write(true)
stbi.stbi_write_png(path, ss.width, ss.height, 4, ss.pixels, ss.width * 4) ?
unsafe { ss.destroy() }
}
// 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) ? {
fn write_rgba_to_ppm(path string, w int, h int, components int, pixels &byte) ? {
mut f_out := os.create(path) ?
defer {
f_out.close()
}
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 ') ?
unsafe {
r := int(pixels[idx])
g := int(pixels[idx + 1])
b := int(pixels[idx + 2])
f_out.write_string('$r $g $b ') ?
}
}
}
f_out.close()
}

View File

@ -0,0 +1,42 @@
module sapp
[heap]
pub struct Screenshot {
width int
height int
size int
mut:
pixels &byte
}
[manualfree]
pub fn screenshot_window() &Screenshot {
img_width := width()
img_height := height()
img_size := img_width * img_height * 4
img_pixels := unsafe { &byte(malloc(img_size)) }
C.v_sapp_gl_read_rgba_pixels(0, 0, img_width, img_height, img_pixels)
return &Screenshot{
width: img_width
height: img_height
size: img_size
pixels: img_pixels
}
}
// free - free *only* the Screenshot pixels.
[unsafe]
pub fn (mut ss Screenshot) free() {
unsafe {
free(ss.pixels)
ss.pixels = &byte(0)
}
}
// destroy - free the Screenshot pixels,
// then free the screenshot data structure itself.
[unsafe]
pub fn (mut ss Screenshot) destroy() {
unsafe { ss.free() }
unsafe { free(ss) }
}