Skip to content

Commit 0021fbb

Browse files
authored
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)
1 parent 85f3372 commit 0021fbb

File tree

5 files changed

+123
-26
lines changed

5 files changed

+123
-26
lines changed

cmd/tools/vtest-all.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ fn get_all_commands() []Command {
181181
}
182182
$if macos || linux {
183183
res << Command{
184-
line: '$vexe -o v.c cmd/v && cc -Werror -I "$vroot/thirdparty/stdatomic/nix" v.c -lpthread && rm -rf a.out'
184+
line: '$vexe -o v.c cmd/v && cc -Werror -I "$vroot/thirdparty/stdatomic/nix" v.c -lpthread -lm && rm -rf a.out'
185185
label: 'v.c should be buildable with no warnings...'
186186
okmsg: 'v.c can be compiled without warnings. This is good :)'
187187
rmfile: 'v.c'

vlib/gg/gg.v

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ fn gg_frame_fn(user_data voidptr) {
106106
// return
107107
}
108108

109+
ctx.record_frame()
110+
109111
if ctx.ui_mode && !ctx.needs_refresh {
110112
// Draw 3 more frames after the "stop refresh" command
111113
ctx.ticks++
@@ -250,8 +252,8 @@ fn gg_fail_fn(msg &char, user_data voidptr) {
250252
}
251253
}
252254

253-
pub fn (gg &Context) run() {
254-
sapp.run(&gg.window)
255+
pub fn (ctx &Context) run() {
256+
sapp.run(&ctx.window)
255257
}
256258

257259
// quit closes the context window and exits the event loop for it

vlib/gg/recorder.v

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
module gg
2+
3+
import os
4+
import sokol.sapp
5+
6+
[heap]
7+
pub struct SSRecorderSettings {
8+
pub mut:
9+
stop_at_frame i64 = -1
10+
screenshot_frames []u64
11+
screenshot_folder string
12+
screenshot_prefix string
13+
}
14+
15+
const recorder_settings = new_gg_recorder_settings()
16+
17+
fn new_gg_recorder_settings() &SSRecorderSettings {
18+
$if gg_record ? {
19+
stop_frame := os.getenv_opt('VGG_STOP_AT_FRAME') or { '-1' }.i64()
20+
frames := os.getenv('VGG_SCREENSHOT_FRAMES').split_any(',').map(it.u64())
21+
folder := os.getenv('VGG_SCREENSHOT_FOLDER')
22+
prefix := os.join_path_single(folder, os.file_name(os.executable()).all_before('.') + '_')
23+
return &SSRecorderSettings{
24+
stop_at_frame: stop_frame
25+
screenshot_frames: frames
26+
screenshot_folder: folder
27+
screenshot_prefix: prefix
28+
}
29+
} $else {
30+
return &SSRecorderSettings{}
31+
}
32+
}
33+
34+
[if gg_record ?]
35+
pub fn (mut ctx Context) record_frame() {
36+
if ctx.frame in gg.recorder_settings.screenshot_frames {
37+
screenshot_file_path := '$gg.recorder_settings.screenshot_prefix${ctx.frame}.png'
38+
$if gg_record_trace ? {
39+
eprintln('>>> screenshoting $screenshot_file_path')
40+
}
41+
sapp.screenshot_png(screenshot_file_path) or { panic(err) }
42+
}
43+
if ctx.frame == gg.recorder_settings.stop_at_frame {
44+
$if gg_record_trace ? {
45+
eprintln('>>> exiting at frame $ctx.frame')
46+
}
47+
exit(0)
48+
}
49+
}

vlib/sokol/sapp/sapp_v.c.v

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
11
module sapp
22

33
import os
4+
import stbi
45

56
// v_sapp_gl_read_rgba_pixels reads pixles from the OpenGL buffer into `pixels`.
67
fn C.v_sapp_gl_read_rgba_pixels(x int, y int, width int, height int, pixels charptr)
78

89
// screenshot takes a screenshot of the current window.
9-
[inline]
1010
pub fn screenshot(path string) ? {
1111
if !path.ends_with('.ppm') {
1212
return error(@MOD + '.' + @FN + ' currently only supports .ppm files.')
1313
}
14+
return screenshot_ppm(path)
15+
}
1416

15-
w := width()
16-
h := height()
17-
18-
size := w * h * 4 //
19-
mut pixels := []byte{len: size, init: 0}
20-
21-
C.v_sapp_gl_read_rgba_pixels(0, 0, w, h, pixels.data)
22-
23-
// TODO use separate thread for writing the data
24-
// TODO use stbi to support more formats
25-
// stbi.write_png(path, w, h, components, pixels.data, 3 * w)
26-
// stbi.write_tga(path, w, h, components, pixels.data)
27-
write_rgba_to_ppm(path, w, h, 4, pixels) ?
17+
// screenshot_ppm takes a screenshot of the current window as a .ppm file
18+
[manualfree]
19+
pub fn screenshot_ppm(path string) ? {
20+
ss := screenshot_window()
21+
write_rgba_to_ppm(path, ss.width, ss.height, 4, ss.pixels) ?
22+
unsafe { ss.destroy() }
23+
}
2824

29-
unsafe {
30-
pixels.free()
31-
}
25+
// screenshot_png takes a screenshot of the current window as a .png file
26+
[manualfree]
27+
pub fn screenshot_png(path string) ? {
28+
ss := screenshot_window()
29+
stbi.set_flip_vertically_on_write(true)
30+
stbi.stbi_write_png(path, ss.width, ss.height, 4, ss.pixels, ss.width * 4) ?
31+
unsafe { ss.destroy() }
3232
}
3333

3434
// write_rgba_to_ppm writes `pixels` data in RGBA format to PPM3 format.
35-
fn write_rgba_to_ppm(path string, w int, h int, components int, pixels []byte) ? {
35+
fn write_rgba_to_ppm(path string, w int, h int, components int, pixels &byte) ? {
3636
mut f_out := os.create(path) ?
37+
defer {
38+
f_out.close()
39+
}
3740
f_out.writeln('P3') ?
3841
f_out.writeln('$w $h') ?
3942
f_out.writeln('255') ?
4043
for i := h - 1; i >= 0; i-- {
4144
for j := 0; j < w; j++ {
4245
idx := i * w * components + j * components
43-
r := int(pixels[idx])
44-
g := int(pixels[idx + 1])
45-
b := int(pixels[idx + 2])
46-
f_out.write_string('$r $g $b ') ?
46+
unsafe {
47+
r := int(pixels[idx])
48+
g := int(pixels[idx + 1])
49+
b := int(pixels[idx + 2])
50+
f_out.write_string('$r $g $b ') ?
51+
}
4752
}
4853
}
49-
f_out.close()
5054
}

vlib/sokol/sapp/screenshot.c.v

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
module sapp
2+
3+
[heap]
4+
pub struct Screenshot {
5+
width int
6+
height int
7+
size int
8+
mut:
9+
pixels &byte
10+
}
11+
12+
[manualfree]
13+
pub fn screenshot_window() &Screenshot {
14+
img_width := width()
15+
img_height := height()
16+
img_size := img_width * img_height * 4
17+
img_pixels := unsafe { &byte(malloc(img_size)) }
18+
C.v_sapp_gl_read_rgba_pixels(0, 0, img_width, img_height, img_pixels)
19+
return &Screenshot{
20+
width: img_width
21+
height: img_height
22+
size: img_size
23+
pixels: img_pixels
24+
}
25+
}
26+
27+
// free - free *only* the Screenshot pixels.
28+
[unsafe]
29+
pub fn (mut ss Screenshot) free() {
30+
unsafe {
31+
free(ss.pixels)
32+
ss.pixels = &byte(0)
33+
}
34+
}
35+
36+
// destroy - free the Screenshot pixels,
37+
// then free the screenshot data structure itself.
38+
[unsafe]
39+
pub fn (mut ss Screenshot) destroy() {
40+
unsafe { ss.free() }
41+
unsafe { free(ss) }
42+
}

0 commit comments

Comments
 (0)