mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-11 11:09:16 +02:00
committed by
Kovid Goyal
parent
dc328012f4
commit
b9c9986111
105
kitty/shaders.c
105
kitty/shaders.c
@@ -1436,39 +1436,90 @@ setup_os_window_for_rendering(OSWindow *os_window, Tab *tab, Window *active_wind
|
||||
// Take a screenshot of the OS Window, must be called immediately after
|
||||
// the OSWindow is rendered into the back buffer and before the buffers
|
||||
// are swapped. If thumb_w or thumb_h are zero the are set to the corresponding
|
||||
// dimension of the OS Window.
|
||||
// dimension of the source region (viewport or central region without tab bar).
|
||||
// The include_tab_bar parameter controls whether the tab bar is included in the screenshot.
|
||||
// When false, only the central window area is captured (excluding the tab bar).
|
||||
// Scaling is performed on the GPU using the BLIT_PROGRAM shader for better performance.
|
||||
// Setting the thumbnail dimensions to zero disables scaling.
|
||||
void
|
||||
take_screenshot_of_oswindow(OSWindow *os_window, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h) {
|
||||
take_screenshot_of_oswindow(OSWindow *os_window, unsigned char *dst_buf, unsigned *thumb_w, unsigned *thumb_h, bool include_tab_bar) {
|
||||
unsigned vw = os_window->viewport_width;
|
||||
unsigned vh = os_window->viewport_height;
|
||||
if (!*thumb_w) *thumb_w = vw; if (!*thumb_h) *thumb_h = vh;
|
||||
*thumb_w = MIN(vw, *thumb_w);
|
||||
*thumb_h = MIN(vw, *thumb_h);
|
||||
|
||||
size_t src_stride = (size_t)vw * 4, dst_stride = *thumb_w * 4;
|
||||
RAII_ALLOC(uint8_t, buf, malloc(src_stride * (size_t)vh));
|
||||
if (!buf) return;
|
||||
|
||||
// Read the current framebuffer (before swap)
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glReadPixels(0, 0, vw, vh, GL_RGBA, GL_UNSIGNED_BYTE, buf);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
|
||||
// Downsample with vertical flip (OpenGL origin is bottom-left)
|
||||
float x_ratio = (float)vw / (float)*thumb_w;
|
||||
float y_ratio = (float)vh / (float)*thumb_h;
|
||||
for (unsigned dy = 0; dy < *thumb_h; dy++) {
|
||||
// Vertical flip: dst row 0 = top of image = src row (vh-1)
|
||||
unsigned src_y = (unsigned)((float)(*thumb_h - 1 - dy) * y_ratio);
|
||||
if (src_y >= vh) src_y = vh - 1;
|
||||
const uint8_t *src_row = buf + (size_t)src_y * src_stride;
|
||||
uint8_t *dst_row = dst_buf + (size_t)dy * dst_stride;
|
||||
for (unsigned dx = 0; dx < *thumb_w; dx++) {
|
||||
unsigned src_x = (unsigned)((float)dx * x_ratio);
|
||||
if (src_x >= vw) src_x = vw - 1;
|
||||
memcpy(dst_row + dx * 4, src_row + src_x * 4, 4);
|
||||
// Calculate the source region to capture (excluding tab bar if requested)
|
||||
unsigned src_top = 0, src_height = vh;
|
||||
if (!include_tab_bar) {
|
||||
Region central = {0}, tab_bar = {0};
|
||||
os_window_regions(os_window, ¢ral, &tab_bar);
|
||||
if (tab_bar.bottom > tab_bar.top) {
|
||||
// Tab bar is present, exclude it from the screenshot
|
||||
src_top = central.top;
|
||||
src_height = central.bottom - central.top;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*thumb_w) *thumb_w = vw;
|
||||
if (!*thumb_h) *thumb_h = src_height;
|
||||
*thumb_w = MIN(vw, *thumb_w);
|
||||
*thumb_h = MIN(src_height, *thumb_h);
|
||||
|
||||
// Create a texture to hold the current framebuffer content
|
||||
GLuint src_texture = 0;
|
||||
glGenTextures(1, &src_texture);
|
||||
glBindTexture(GL_TEXTURE_2D, src_texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
// Copy the current framebuffer to the texture
|
||||
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, vw, vh, 0);
|
||||
|
||||
// Create a temporary framebuffer for GPU-based scaling
|
||||
GLuint temp_texture = 0, temp_framebuffer = 0;
|
||||
setup_texture_as_render_target(*thumb_w, *thumb_h, &temp_texture, &temp_framebuffer);
|
||||
|
||||
// Save current state
|
||||
GLint current_framebuffer;
|
||||
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_framebuffer);
|
||||
|
||||
// Bind our temporary framebuffer for rendering
|
||||
bind_framebuffer_for_output(temp_framebuffer);
|
||||
save_viewport_using_bottom_left_origin(0, 0, *thumb_w, *thumb_h);
|
||||
|
||||
// Use the blit program to render the scaled framebuffer
|
||||
bind_program(BLIT_PROGRAM);
|
||||
|
||||
// Set source rectangle (normalized coordinates: 0 to 1)
|
||||
// Note: OpenGL texture origin is bottom-left, but Region uses top-left origin
|
||||
// Convert from screen coordinates (top-left origin) to OpenGL texture coordinates (bottom-left origin)
|
||||
float src_bottom_norm = (float)(vh - (src_top + src_height)) / (float)vh;
|
||||
float src_top_norm = (float)(vh - src_top) / (float)vh;
|
||||
glUniform4f(blit_program_layout.uniforms.src_rect, 0.0f, src_top_norm, 1.0f, src_bottom_norm);
|
||||
|
||||
// Set destination rectangle (NDC coordinates: -1 to 1)
|
||||
glUniform4f(blit_program_layout.uniforms.dest_rect, -1.0f, -1.0f, 1.0f, 1.0f);
|
||||
|
||||
// Bind the source texture
|
||||
glActiveTexture(GL_TEXTURE0 + GRAPHICS_UNIT);
|
||||
glBindTexture(GL_TEXTURE_2D, src_texture);
|
||||
|
||||
// Draw the scaled quad
|
||||
draw_quad(false, 0);
|
||||
|
||||
// Read the scaled result
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glReadPixels(0, 0, *thumb_w, *thumb_h, GL_RGBA, GL_UNSIGNED_BYTE, dst_buf);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
|
||||
// Restore previous state
|
||||
bind_framebuffer_for_output(current_framebuffer);
|
||||
restore_viewport();
|
||||
|
||||
// Clean up temporary resources
|
||||
free_texture(&src_texture);
|
||||
free_texture(&temp_texture);
|
||||
free_framebuffer(&temp_framebuffer);
|
||||
}
|
||||
|
||||
// }}}
|
||||
|
||||
Reference in New Issue
Block a user