Add background_image

This commit is contained in:
Fredrick Brennan 2020-01-30 17:23:42 +08:00
parent a67e4e550e
commit 9f364209af
14 changed files with 289 additions and 47 deletions

View File

@ -0,0 +1,26 @@
#version GLSL_VERSION
#define LAYOUT_TYPE
uniform sampler2D image;
uniform float bgimage_opacity;
#ifdef TILED
uniform float bgimage_scale;
// These are of the window, not the screen.
uniform float width;
uniform float height;
#endif
in vec2 texcoord;
out vec4 color;
void main() {
#ifdef TILED
vec2 txsz = vec2(width,height) / textureSize(image,0);
txsz /= bgimage_scale;
color = texture(image, texcoord * txsz);
#endif
#ifdef SIMPLE
color = texture(image, texcoord);
#endif
color = vec4(color.rgb, color.a * bgimage_opacity);
}

View File

@ -0,0 +1,9 @@
#version GLSL_VERSION
layout(location=0) in vec4 src;
out vec2 texcoord;
void main() {
texcoord = clamp(vec2(src[0], src[1]*-1), 0, 1);
gl_Position = src;
}

View File

@ -60,9 +60,11 @@ class Borders:
active_window, active_window,
current_layout, current_layout,
extra_blank_rects, extra_blank_rects,
draw_window_borders=True draw_window_borders=True,
draw_blank_rects=False
): ):
add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, BorderColor.default_bg) add_borders_rect(self.os_window_id, self.tab_id, 0, 0, 0, 0, BorderColor.default_bg)
if draw_blank_rects:
for br in chain(current_layout.blank_rects, extra_blank_rects): for br in chain(current_layout.blank_rects, extra_blank_rects):
add_borders_rect(self.os_window_id, self.tab_id, *br, BorderColor.default_bg) add_borders_rect(self.os_window_id, self.tab_id, *br, BorderColor.default_bg)
bw, pw = self.border_width, self.padding_width bw, pw = self.border_width, self.padding_width

View File

@ -657,6 +657,7 @@ render(monotonic_t now, bool input_read) {
for (size_t i = 0; i < global_state.num_os_windows; i++) { for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i; OSWindow *w = global_state.os_windows + i;
w->render_calls++;
if (!w->num_tabs) continue; if (!w->num_tabs) continue;
if (!should_os_window_be_rendered(w)) { if (!should_os_window_be_rendered(w)) {
update_os_window_title(w); update_os_window_title(w);
@ -688,6 +689,7 @@ render(monotonic_t now, bool input_read) {
if (!w->fonts_data) { log_error("No fonts data found for window id: %llu", w->id); continue; } if (!w->fonts_data) { log_error("No fonts data found for window id: %llu", w->id); continue; }
if (prepare_to_render_os_window(w, now, &active_window_id, &active_window_bg, &num_visible_windows, &all_windows_have_same_bg)) needs_render = true; if (prepare_to_render_os_window(w, now, &active_window_id, &active_window_bg, &num_visible_windows, &all_windows_have_same_bg)) needs_render = true;
if (w->last_active_window_id != active_window_id || w->last_active_tab != w->active_tab || w->focused_at_last_render != w->is_focused) needs_render = true; if (w->last_active_window_id != active_window_id || w->last_active_tab != w->active_tab || w->focused_at_last_render != w->is_focused) needs_render = true;
if (w->render_calls < 3 && w->bgimage != NULL) needs_render = true;
if (needs_render) render_os_window(w, now, active_window_id, active_window_bg, num_visible_windows, all_windows_have_same_bg); if (needs_render) render_os_window(w, now, active_window_id, active_window_bg, num_visible_windows, all_windows_have_same_bg);
} }
last_render_at = now; last_render_at = now;

View File

@ -846,6 +846,37 @@ of windows set :opt:`dynamic_background_opacity` to :code:`yes` (this is off by
default as it has a performance cost) default as it has a performance cost)
''')) '''))
def startup_session(x):
if x.lower() == 'none':
return
x = os.path.expanduser(x)
x = os.path.expandvars(x)
if not os.path.isabs(x):
x = os.path.join(config_dir, x)
return x
o('background_image', 'none', option_type=startup_session, long_text=_('''
Path to a background image. Must be PNG.'''))
o('background_image_layout', 'tiling', option_type=choices('tiling', 'scaled', 'mirror_tiled'), long_text=_('''
Whether to tile or scale the background image.'''))
o('background_image_linear', False, long_text=_('''
When background image is scaled, whether linear interpolation should be used.'''))
o('background_image_opacity', 0.5, option_type=positive_float, long_text=_('''
Background image opacity, between 0.0 and 1.0 inclusive. This
can only ever decrease a background's opacity, if the image is already
semi-transparent, "1" is interpreted as the image's current transparency.'''))
o('background_image_scale', 1.0, option_type=positive_float, long_text=_('''
Only has an effect if :opt:`background_image_layout` is tiling, should be positive.'''))
o('background_image_scale', 1.0, option_type=positive_float, long_text=_('''
Only has an effect if :opt:`background_image_layout` is tiling, should be positive.'''))
o('dynamic_background_opacity', False, long_text=_(''' o('dynamic_background_opacity', False, long_text=_('''
Allow changing of the :opt:`background_opacity` dynamically, using either keyboard Allow changing of the :opt:`background_opacity` dynamically, using either keyboard
shortcuts (:sc:`increase_background_opacity` and :sc:`decrease_background_opacity`) shortcuts (:sc:`increase_background_opacity` and :sc:`decrease_background_opacity`)
@ -966,16 +997,6 @@ The default is to check every 24 hrs, set to zero to disable.
''')) '''))
def startup_session(x):
if x.lower() == 'none':
return
x = os.path.expanduser(x)
x = os.path.expandvars(x)
if not os.path.isabs(x):
x = os.path.join(config_dir, x)
return x
o('startup_session', 'none', option_type=startup_session, long_text=_(''' o('startup_session', 'none', option_type=startup_session, long_text=_('''
Path to a session file to use for all kitty instances. Can be overridden Path to a session file to use for all kitty instances. Can be overridden
by using the :option:`kitty --session` command line option for individual by using the :option:`kitty --session` command line option for individual

View File

@ -55,6 +55,7 @@ typedef enum MouseTrackingModes { NO_TRACKING, BUTTON_MODE, MOTION_MODE, ANY_MOD
typedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL} MouseTrackingProtocol; typedef enum MouseTrackingProtocols { NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL} MouseTrackingProtocol;
typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
typedef enum { NONE, MENUBAR, WINDOW, ALL } WindowTitleIn; typedef enum { NONE, MENUBAR, WINDOW, ALL } WindowTitleIn;
typedef enum { TILING, SCALED, MIRRORED } BackgroundImageLayout;
#define MAX_CHILDREN 512 #define MAX_CHILDREN 512
#define BLANK_CHAR 0 #define BLANK_CHAR 0

View File

@ -377,7 +377,7 @@ make_os_window_context_current(OSWindow *w) {
} }
static inline void void
get_window_content_scale(GLFWwindow *w, float *xscale, float *yscale, double *xdpi, double *ydpi) { get_window_content_scale(GLFWwindow *w, float *xscale, float *yscale, double *xdpi, double *ydpi) {
if (w) glfwGetWindowContentScale(w, xscale, yscale); if (w) glfwGetWindowContentScale(w, xscale, yscale);
else { else {

View File

@ -5,6 +5,7 @@
* Distributed under terms of the GPL3 license. * Distributed under terms of the GPL3 license.
*/ */
#include "gl.h"
#include "graphics.h" #include "graphics.h"
#include "state.h" #include "state.h"
@ -29,9 +30,8 @@ static bool send_to_gpu = true;
GraphicsManager* GraphicsManager*
grman_alloc() { grman_alloc() {
GraphicsManager *self = (GraphicsManager *)GraphicsManager_Type.tp_alloc(&GraphicsManager_Type, 0); GraphicsManager *self = (GraphicsManager *)GraphicsManager_Type.tp_alloc(&GraphicsManager_Type, 0);
self->images_capacity = 64; self->images_capacity = self->capacity = 64;
self->images = calloc(self->images_capacity, sizeof(Image)); self->images = calloc(self->images_capacity, sizeof(Image));
self->capacity = 64;
self->render_data = calloc(self->capacity, sizeof(ImageRenderData)); self->render_data = calloc(self->capacity, sizeof(ImageRenderData));
if (self->images == NULL || self->render_data == NULL) { if (self->images == NULL || self->render_data == NULL) {
PyErr_NoMemory(); PyErr_NoMemory();
@ -248,6 +248,38 @@ add_trim_predicate(Image *img) {
return !img->data_loaded || (!img->client_id && !img->refcnt); return !img->data_loaded || (!img->client_id && !img->refcnt);
} }
bool png_path_to_bitmap(uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) {
const char* path = OPT(background_image);
if (access(path, R_OK) != 0) {
log_error("File %s, (requested background image,) does not exist (%d)", path, errno);
return false;
}
FILE* fp = fopen(path, "r");
if (fp == NULL) {
log_error("File %s, (requested background image,) could not be opened", path);
return false;
}
fseek(fp, 0L, SEEK_END);
size_t filesize = ftell(fp);
*data = calloc(filesize, sizeof(char));
fseek(fp, 0L, SEEK_SET); // rewind() deprecated on some platforms
fread(*data, sizeof(char), filesize, fp);
fclose(fp);
png_read_data d;
memset(&d, 0, sizeof(png_read_data));
inflate_png_inner(&d, *data, filesize);
if (!d.ok) {
log_error("File %s, (requested background image,) not readable by libpng", path);
return false;
}
free(*data);
*data = d.decompressed;
*sz = d.sz;
*height = d.height; *width = d.width;
return true;
}
static inline Image* static inline Image*
find_or_create_image(GraphicsManager *self, uint32_t id, bool *existing) { find_or_create_image(GraphicsManager *self, uint32_t id, bool *existing) {
if (id) { if (id) {
@ -421,7 +453,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
size_t required_sz = (img->load_data.is_opaque ? 3 : 4) * img->width * img->height; size_t required_sz = (img->load_data.is_opaque ? 3 : 4) * img->width * img->height;
if (img->load_data.data_sz != required_sz) ABRT(EINVAL, "Image dimensions: %ux%u do not match data size: %zu, expected size: %zu", img->width, img->height, img->load_data.data_sz, required_sz); if (img->load_data.data_sz != required_sz) ABRT(EINVAL, "Image dimensions: %ux%u do not match data size: %zu, expected size: %zu", img->width, img->height, img->load_data.data_sz, required_sz);
if (LIKELY(img->data_loaded && send_to_gpu)) { if (LIKELY(img->data_loaded && send_to_gpu)) {
send_image_to_gpu(&img->texture_id, img->load_data.data, img->width, img->height, img->load_data.is_opaque, img->load_data.is_4byte_aligned); send_image_to_gpu(&img->texture_id, img->load_data.data, img->width, img->height, img->load_data.is_opaque, img->load_data.is_4byte_aligned, false, REPEAT_CLAMP);
free_load_data(&img->load_data); free_load_data(&img->load_data);
self->used_storage += required_sz; self->used_storage += required_sz;
img->used_storage = required_sz; img->used_storage = required_sz;

View File

@ -55,6 +55,12 @@ typedef struct {
size_t used_storage; size_t used_storage;
} Image; } Image;
typedef struct {
uint32_t texture_id;
unsigned int height, width;
uint8_t* bitmap;
} BackgroundImage;
typedef struct { typedef struct {
float vertices[16]; float vertices[16];
uint32_t texture_id, group_count; uint32_t texture_id, group_count;
@ -71,6 +77,8 @@ typedef struct {
Image *images; Image *images;
size_t count, capacity; size_t count, capacity;
ImageRenderData *render_data; ImageRenderData *render_data;
Image bgimage;
ImageRenderData bgimage_rd;
bool layers_dirty; bool layers_dirty;
// The number of images below MIN_ZINDEX / 2, then the number of refs between MIN_ZINDEX / 2 and -1 inclusive, then the number of refs above 0 inclusive. // The number of images below MIN_ZINDEX / 2, then the number of refs between MIN_ZINDEX / 2 and -1 inclusive, then the number of refs above 0 inclusive.
size_t num_of_below_refs, num_of_negative_refs, num_of_positive_refs; size_t num_of_below_refs, num_of_negative_refs, num_of_positive_refs;
@ -93,3 +101,4 @@ void grman_scroll_images(GraphicsManager *self, const ScrollData*, CellPixelSize
void grman_resize(GraphicsManager*, index_type, index_type, index_type, index_type); void grman_resize(GraphicsManager*, index_type, index_type, index_type, index_type);
void grman_rescale(GraphicsManager *self, CellPixelSize fg); void grman_rescale(GraphicsManager *self, CellPixelSize fg);
void gpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height); void gpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height);
bool png_path_to_bitmap(uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz);

View File

@ -111,6 +111,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
self->historybuf = alloc_historybuf(MAX(scrollback, lines), columns, OPT(scrollback_pager_history_size)); self->historybuf = alloc_historybuf(MAX(scrollback, lines), columns, OPT(scrollback_pager_history_size));
self->main_grman = grman_alloc(); self->main_grman = grman_alloc();
self->alt_grman = grman_alloc(); self->alt_grman = grman_alloc();
self->grman = self->main_grman; self->grman = self->main_grman;
self->pending_mode.wait_time = 2.0; self->pending_mode.wait_time = 2.0;
self->disable_ligatures = OPT(disable_ligatures); self->disable_ligatures = OPT(disable_ligatures);

View File

@ -7,10 +7,12 @@
#include "fonts.h" #include "fonts.h"
#include "gl.h" #include "gl.h"
#include "png-reader.h"
#include <png.h>
#include <stddef.h> #include <stddef.h>
enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM, BLIT_PROGRAM, NUM_PROGRAMS }; enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM, BLIT_PROGRAM, BGIMAGE_PROGRAM, BGIMAGE_TILED_PROGRAM, NUM_PROGRAMS };
enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BLIT_UNIT }; enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BLIT_UNIT, BGIMAGE_UNIT };
// Sprites {{{ // Sprites {{{
typedef struct { typedef struct {
@ -22,6 +24,7 @@ typedef struct {
static const SpriteMap NEW_SPRITE_MAP = { .xnum = 1, .ynum = 1, .last_num_of_layers = 1, .last_ynum = -1 }; static const SpriteMap NEW_SPRITE_MAP = { .xnum = 1, .ynum = 1, .last_num_of_layers = 1, .last_ynum = -1 };
static GLint max_texture_size = 0, max_array_texture_layers = 0; static GLint max_texture_size = 0, max_array_texture_layers = 0;
//static uint8_t *bg_image = NULL;
SPRITE_MAP_HANDLE SPRITE_MAP_HANDLE
alloc_sprite_map(unsigned int cell_width, unsigned int cell_height) { alloc_sprite_map(unsigned int cell_width, unsigned int cell_height) {
@ -132,14 +135,23 @@ send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int x, unsigned int y, unsigne
} }
void void
send_image_to_gpu(GLuint *tex_id, const void* data, GLsizei width, GLsizei height, bool is_opaque, bool is_4byte_aligned) { send_image_to_gpu(GLuint *tex_id, const void* data, GLsizei width, GLsizei height, bool is_opaque, bool is_4byte_aligned, bool linear, RepeatStrategy repeat) {
if (!(*tex_id)) { glGenTextures(1, tex_id); } if (!(*tex_id)) { glGenTextures(1, tex_id); }
glBindTexture(GL_TEXTURE_2D, *tex_id); glBindTexture(GL_TEXTURE_2D, *tex_id);
glPixelStorei(GL_UNPACK_ALIGNMENT, is_4byte_aligned ? 4 : 1); glPixelStorei(GL_UNPACK_ALIGNMENT, is_4byte_aligned ? 4 : 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); RepeatStrategy r;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); switch (repeat) {
case REPEAT_MIRROR:
r = GL_MIRRORED_REPEAT; break;
case REPEAT_CLAMP:
r = GL_CLAMP_TO_EDGE; break;
default:
r = GL_REPEAT;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, r);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, r);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, is_opaque ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, data); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, is_opaque ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, data);
} }
@ -323,6 +335,47 @@ cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloa
return changed; return changed;
} }
void
draw_bg(int program, OSWindow *w) {
if (w->bvao_idx == 0) {
const GLfloat screenrect[4][2] = {
{ -1.0, 1.0 },
{ -1.0, -1.0 },
{ 1.0, -1.0 },
{ 1.0, 1.0 },
};
w->bvao_idx = create_vao();
bind_vertex_array(w->bvao_idx);
glGenBuffers(1, &w->vbo_idx);
glBindBuffer(GL_ARRAY_BUFFER, w->vbo_idx);
glBufferData(GL_ARRAY_BUFFER, 4*2*sizeof(float), screenrect, GL_STATIC_DRAW);
}
bind_vertex_array(w->bvao_idx);
glBindBuffer(GL_ARRAY_BUFFER, w->vbo_idx);
glClear(GL_COLOR_BUFFER_BIT);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
bind_program(program);
static bool bgimage_constants_set;
if (!bgimage_constants_set) {
glUniform1i(glGetUniformLocation(program_id(program), "image"), BGIMAGE_UNIT);
glUniform1f(glGetUniformLocation(program_id(program), "bgimage_scale"), OPT(background_image_scale));
glUniform1f(glGetUniformLocation(program_id(program), "bgimage_opacity"), OPT(background_image_opacity));
bgimage_constants_set = true;
}
glUniform1f(glGetUniformLocation(program_id(program), "height"), (float)(w->window_height));
glUniform1f(glGetUniformLocation(program_id(program), "width"), (float)(w->window_width));
glActiveTexture(GL_TEXTURE0 + BGIMAGE_UNIT);
glBindTexture(GL_TEXTURE_2D, w->bgimage->texture_id);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
unbind_vertex_array();
unbind_program();
}
static void static void
draw_graphics(int program, ssize_t vao_idx, ssize_t gvao_idx, ImageRenderData *data, GLuint start, GLuint count) { draw_graphics(int program, ssize_t vao_idx, ssize_t gvao_idx, ImageRenderData *data, GLuint start, GLuint count) {
bind_program(program); bind_program(program);
@ -389,16 +442,18 @@ draw_cells_simple(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) {
} }
static void static void
draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) { draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *w) {
glEnable(GL_BLEND); glEnable(GL_BLEND);
BLEND_ONTO_OPAQUE; BLEND_ONTO_OPAQUE;
bind_program(CELL_BG_PROGRAM); bind_program(CELL_BG_PROGRAM);
// draw background for all cells // draw background for all cells
glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3); glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3);
// This gives users a way to still use background images without a compositor.
if (OPT(background_opacity) != 0.0)
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);
if (screen->grman->num_of_below_refs) { if (screen->grman->num_of_below_refs || w->bgimage != NULL) {
draw_graphics(GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs); draw_graphics(GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs);
bind_program(CELL_BG_PROGRAM); bind_program(CELL_BG_PROGRAM);
// draw background for non-default bg cells // draw background for non-default bg cells
@ -435,12 +490,13 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, os_window->offscreen_framebuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, os_window->offscreen_framebuffer);
glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, os_window->offscreen_texture_id, 0); glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, os_window->offscreen_texture_id, 0);
/* if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) fatal("Offscreen framebuffer not complete"); */ /* if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) fatal("Offscreen framebuffer not complete"); */
glEnable(GL_BLEND);
BLEND_PREMULT;
glClear(GL_COLOR_BUFFER_BIT);
bind_program(CELL_BG_PROGRAM); bind_program(CELL_BG_PROGRAM);
// draw background for all cells // draw background for all cells
glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3); glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns);
glEnable(GL_BLEND);
BLEND_PREMULT;
if (screen->grman->num_of_below_refs) { if (screen->grman->num_of_below_refs) {
draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs); draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs);
@ -469,11 +525,10 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen
if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs); if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs);
glDisable(GL_BLEND);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glEnable(GL_SCISSOR_TEST);
// Now render the framebuffer to the screen // Now render the framebuffer to the screen
glEnable(GL_SCISSOR_TEST);
bind_program(BLIT_PROGRAM); bind_vertex_array(blit_vertex_array); bind_program(BLIT_PROGRAM); bind_vertex_array(blit_vertex_array);
static bool blit_constants_set = false; static bool blit_constants_set = false;
if (!blit_constants_set) { if (!blit_constants_set) {
@ -484,6 +539,7 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen
glBindTexture(GL_TEXTURE_2D, os_window->offscreen_texture_id); glBindTexture(GL_TEXTURE_2D, os_window->offscreen_texture_id);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
} }
static inline void static inline void
@ -496,6 +552,7 @@ set_cell_uniforms(float current_inactive_text_alpha, bool force) {
cell_uniform_data.amask_premult_loc = glGetUniformLocation(program_id(GRAPHICS_ALPHA_MASK_PROGRAM), "alpha_mask_premult"); cell_uniform_data.amask_premult_loc = glGetUniformLocation(program_id(GRAPHICS_ALPHA_MASK_PROGRAM), "alpha_mask_premult");
#define S(prog, name, val, type) { bind_program(prog); glUniform##type(glGetUniformLocation(program_id(prog), #name), val); } #define S(prog, name, val, type) { bind_program(prog); glUniform##type(glGetUniformLocation(program_id(prog), #name), val); }
S(GRAPHICS_PROGRAM, image, GRAPHICS_UNIT, 1i); S(GRAPHICS_PROGRAM, image, GRAPHICS_UNIT, 1i);
S(GRAPHICS_PROGRAM, image, BGIMAGE_UNIT, 1i);
S(GRAPHICS_PREMULT_PROGRAM, image, GRAPHICS_UNIT, 1i); S(GRAPHICS_PREMULT_PROGRAM, image, GRAPHICS_UNIT, 1i);
S(CELL_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i); S(CELL_FG_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i); S(CELL_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i); S(CELL_FG_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i);
S(CELL_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); S(CELL_FG_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); S(CELL_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); S(CELL_FG_PROGRAM, dim_opacity, OPT(dim_opacity), 1f);
@ -558,10 +615,10 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GL
); );
#undef SCALE #undef SCALE
if (os_window->is_semi_transparent) { if (os_window->is_semi_transparent) {
if (screen->grman->count) draw_cells_interleaved_premult(vao_idx, gvao_idx, screen, os_window); if (screen->grman->count || os_window->bgimage != NULL) draw_cells_interleaved_premult(vao_idx, gvao_idx, screen, os_window);
else draw_cells_simple(vao_idx, gvao_idx, screen); else draw_cells_simple(vao_idx, gvao_idx, screen);
} else { } else {
if (screen->grman->num_of_negative_refs || screen->grman->num_of_below_refs) draw_cells_interleaved(vao_idx, gvao_idx, screen); if (screen->grman->num_of_negative_refs || screen->grman->num_of_below_refs || os_window->bgimage != NULL) draw_cells_interleaved(vao_idx, gvao_idx, screen, os_window);
else draw_cells_simple(vao_idx, gvao_idx, screen); else draw_cells_simple(vao_idx, gvao_idx, screen);
} }
} }
@ -604,16 +661,28 @@ create_border_vao(void) {
void void
draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg, OSWindow *w) { draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, uint32_t viewport_width, uint32_t viewport_height, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg, OSWindow *w) {
glEnable(GL_BLEND);
BLEND_ONTO_OPAQUE;
if (w->bgimage != NULL) {
int program;
if (OPT(background_image_layout) == TILING || OPT(background_image_layout) == MIRRORED) program = BGIMAGE_TILED_PROGRAM;
else program = BGIMAGE_PROGRAM;
draw_bg(program, w);
}
if (num_border_rects) { if (num_border_rects) {
bind_vertex_array(vao_idx);
bind_program(BORDERS_PROGRAM);
if (rect_data_is_dirty) { if (rect_data_is_dirty) {
size_t sz = sizeof(GLuint) * 5 * num_border_rects; size_t sz = sizeof(GLuint) * 5 * num_border_rects;
void *borders_buf_address = alloc_and_map_vao_buffer(vao_idx, sz, 0, GL_STATIC_DRAW, GL_WRITE_ONLY); void *borders_buf_address = alloc_and_map_vao_buffer(vao_idx, sz, 0, GL_STATIC_DRAW, GL_WRITE_ONLY);
if (borders_buf_address) memcpy(borders_buf_address, rect_buf, sz); if (borders_buf_address) memcpy(borders_buf_address, rect_buf, sz);
unmap_vao_buffer(vao_idx, 0); unmap_vao_buffer(vao_idx, 0);
} }
bind_program(BORDERS_PROGRAM);
#define CV3(x) (((float)((x >> 16) & 0xff))/255.f), (((float)((x >> 8) & 0xff))/255.f), (((float)(x & 0xff))/255.f) #define CV3(x) (((float)((x >> 16) & 0xff))/255.f), (((float)((x >> 8) & 0xff))/255.f), (((float)(x & 0xff))/255.f)
glUniform1f(border_uniform_locations[BORDER_background_opacity], w->is_semi_transparent ? w->background_opacity : 1.0f); glUniform1f(border_uniform_locations[BORDER_background_opacity], w->is_semi_transparent ? MAX(w->background_opacity, OPT(background_image_opacity)) : 1.0f);
glUniform3f(border_uniform_locations[BORDER_active_border_color], CV3(OPT(active_border_color))); glUniform3f(border_uniform_locations[BORDER_active_border_color], CV3(OPT(active_border_color)));
glUniform3f(border_uniform_locations[BORDER_inactive_border_color], CV3(OPT(inactive_border_color))); glUniform3f(border_uniform_locations[BORDER_inactive_border_color], CV3(OPT(inactive_border_color)));
glUniform3f(border_uniform_locations[BORDER_bell_border_color], CV3(OPT(bell_border_color))); glUniform3f(border_uniform_locations[BORDER_bell_border_color], CV3(OPT(bell_border_color)));
@ -621,11 +690,11 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
color_type default_bg = (num_visible_windows > 1 && !all_windows_have_same_bg) ? OPT(background) : active_window_bg; color_type default_bg = (num_visible_windows > 1 && !all_windows_have_same_bg) ? OPT(background) : active_window_bg;
glUniform3f(border_uniform_locations[BORDER_default_bg], CV3(default_bg)); glUniform3f(border_uniform_locations[BORDER_default_bg], CV3(default_bg));
#undef CV3 #undef CV3
bind_vertex_array(vao_idx);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, num_border_rects); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, num_border_rects);
unbind_vertex_array(); unbind_vertex_array();
unbind_program(); unbind_program();
} }
glDisable(GL_BLEND);
} }
// }}} // }}}
@ -722,7 +791,7 @@ static PyMethodDef module_methods[] = {
bool bool
init_shaders(PyObject *module) { init_shaders(PyObject *module) {
#define C(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return false; } #define C(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return false; }
C(CELL_PROGRAM); C(CELL_BG_PROGRAM); C(CELL_SPECIAL_PROGRAM); C(CELL_FG_PROGRAM); C(BORDERS_PROGRAM); C(GRAPHICS_PROGRAM); C(GRAPHICS_PREMULT_PROGRAM); C(GRAPHICS_ALPHA_MASK_PROGRAM); C(BLIT_PROGRAM); C(CELL_PROGRAM); C(CELL_BG_PROGRAM); C(CELL_SPECIAL_PROGRAM); C(CELL_FG_PROGRAM); C(BORDERS_PROGRAM); C(GRAPHICS_PROGRAM); C(GRAPHICS_PREMULT_PROGRAM); C(GRAPHICS_ALPHA_MASK_PROGRAM); C(BLIT_PROGRAM); C(BGIMAGE_PROGRAM); C(BGIMAGE_TILED_PROGRAM);
C(GLSL_VERSION); C(GLSL_VERSION);
C(GL_VERSION); C(GL_VERSION);
C(GL_VENDOR); C(GL_VENDOR);

View File

@ -81,6 +81,39 @@ add_os_window() {
ans->tab_bar_render_data.vao_idx = create_cell_vao(); ans->tab_bar_render_data.vao_idx = create_cell_vao();
ans->gvao_idx = create_graphics_vao(); ans->gvao_idx = create_graphics_vao();
ans->background_opacity = OPT(background_opacity); ans->background_opacity = OPT(background_opacity);
bool wants_bg = OPT(background_image) != NULL;
if (wants_bg) {
bool has_bg;
has_bg = global_state.bgimage != NULL;
if (!has_bg) {
BackgroundImage* bgimage = calloc(1, sizeof(BackgroundImage));
size_t size;
has_bg = png_path_to_bitmap(&bgimage->bitmap, &bgimage->width, &bgimage->height, &size);
if (has_bg) {
bgimage->texture_id = 0;
RepeatStrategy r;
switch (OPT(background_image_layout)) {
case TILING:
r = REPEAT_DEFAULT; break;
case SCALED:
r = REPEAT_CLAMP; break;
case MIRRORED:
r = REPEAT_MIRROR;
}
send_image_to_gpu(&bgimage->texture_id, bgimage->bitmap, bgimage->width,
bgimage->height, false, true, false, r);
ans->bgimage = bgimage;
global_state.bgimage = bgimage;
}
} else {
// Reusing already loaded bgimage
ans->bgimage = global_state.bgimage;
}
}
ans->font_sz_in_pts = global_state.font_sz_in_pts; ans->font_sz_in_pts = global_state.font_sz_in_pts;
END_WITH_OS_WINDOW_REFS END_WITH_OS_WINDOW_REFS
return ans; return ans;
@ -416,6 +449,17 @@ window_title_in(PyObject *title_in) {
return ALL; return ALL;
} }
static BackgroundImageLayout bglayout(PyObject *layout_name) {
const char *name = PyUnicode_AsUTF8(layout_name);
switch(name[0]) {
case 't': return TILING;
case 'm': return MIRRORED;
case 's': return SCALED;
default: break;
}
return TILING;
}
static MouseShape static MouseShape
pointer_shape(PyObject *shape_name) { pointer_shape(PyObject *shape_name) {
const char *name = PyUnicode_AsUTF8(shape_name); const char *name = PyUnicode_AsUTF8(shape_name);
@ -484,6 +528,10 @@ PYWRAP1(set_options) {
S(cursor_blink_interval, parse_s_double_to_monotonic_t); S(cursor_blink_interval, parse_s_double_to_monotonic_t);
S(cursor_stop_blinking_after, parse_s_double_to_monotonic_t); S(cursor_stop_blinking_after, parse_s_double_to_monotonic_t);
S(background_opacity, PyFloat_AsFloat); S(background_opacity, PyFloat_AsFloat);
S(background_image_opacity, PyFloat_AsFloat);
S(background_image_scale, PyFloat_AsFloat);
S(background_image_layout, bglayout);
S(background_image, (char*)PyUnicode_AsUTF8);
S(dim_opacity, PyFloat_AsFloat); S(dim_opacity, PyFloat_AsFloat);
S(dynamic_background_opacity, PyObject_IsTrue); S(dynamic_background_opacity, PyObject_IsTrue);
S(inactive_text_alpha, PyFloat_AsFloat); S(inactive_text_alpha, PyFloat_AsFloat);

View File

@ -13,6 +13,7 @@
typedef enum { LEFT_EDGE, TOP_EDGE, RIGHT_EDGE, BOTTOM_EDGE } Edge; typedef enum { LEFT_EDGE, TOP_EDGE, RIGHT_EDGE, BOTTOM_EDGE } Edge;
typedef enum { RESIZE_DRAW_STATIC, RESIZE_DRAW_SCALED, RESIZE_DRAW_BLANK, RESIZE_DRAW_SIZE } ResizeDrawStrategy; typedef enum { RESIZE_DRAW_STATIC, RESIZE_DRAW_SCALED, RESIZE_DRAW_BLANK, RESIZE_DRAW_SIZE } ResizeDrawStrategy;
typedef enum { REPEAT_MIRROR, REPEAT_CLAMP, REPEAT_DEFAULT } RepeatStrategy;
typedef struct { typedef struct {
monotonic_t visual_bell_duration, cursor_blink_interval, cursor_stop_blinking_after, mouse_hide_wait, click_interval; monotonic_t visual_bell_duration, cursor_blink_interval, cursor_stop_blinking_after, mouse_hide_wait, click_interval;
@ -37,6 +38,12 @@ typedef struct {
int adjust_line_height_px, adjust_column_width_px; int adjust_line_height_px, adjust_column_width_px;
float adjust_line_height_frac, adjust_column_width_frac; float adjust_line_height_frac, adjust_column_width_frac;
float background_opacity, dim_opacity; float background_opacity, dim_opacity;
char* background_image;
BackgroundImageLayout background_image_layout;
float background_image_opacity;
float background_image_scale;
bool dynamic_background_opacity; bool dynamic_background_opacity;
float inactive_text_alpha; float inactive_text_alpha;
float window_padding_width; float window_padding_width;
@ -136,6 +143,7 @@ typedef struct {
int viewport_width, viewport_height, window_width, window_height; int viewport_width, viewport_height, window_width, window_height;
double viewport_x_ratio, viewport_y_ratio; double viewport_x_ratio, viewport_y_ratio;
Tab *tabs; Tab *tabs;
BackgroundImage *bgimage;
unsigned int active_tab, num_tabs, capacity, last_active_tab, last_num_tabs, last_active_window_id; unsigned int active_tab, num_tabs, capacity, last_active_tab, last_num_tabs, last_active_window_id;
bool focused_at_last_render, needs_render; bool focused_at_last_render, needs_render;
ScreenRenderData tab_bar_render_data; ScreenRenderData tab_bar_render_data;
@ -158,8 +166,10 @@ typedef struct {
id_type temp_font_group_id; id_type temp_font_group_id;
enum RENDER_STATE render_state; enum RENDER_STATE render_state;
monotonic_t last_render_frame_received_at; monotonic_t last_render_frame_received_at;
uint64_t render_calls;
id_type last_focused_counter; id_type last_focused_counter;
ssize_t gvao_idx; ssize_t gvao_idx, bvao_idx;
unsigned int vbo_idx;
} OSWindow; } OSWindow;
@ -168,6 +178,7 @@ typedef struct {
id_type os_window_id_counter, tab_id_counter, window_id_counter; id_type os_window_id_counter, tab_id_counter, window_id_counter;
PyObject *boss; PyObject *boss;
BackgroundImage *bgimage;
OSWindow *os_windows; OSWindow *os_windows;
size_t num_os_windows, capacity; size_t num_os_windows, capacity;
OSWindow *callback_os_window; OSWindow *callback_os_window;
@ -223,11 +234,12 @@ void draw_centered_alpha_mask(OSWindow *w, size_t screen_width, size_t screen_he
void update_surface_size(int, int, uint32_t); void update_surface_size(int, int, uint32_t);
void free_texture(uint32_t*); void free_texture(uint32_t*);
void free_framebuffer(uint32_t*); void free_framebuffer(uint32_t*);
void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool); void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool, bool, RepeatStrategy);
void send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int, unsigned int, unsigned int, pixel*); void send_sprite_to_gpu(FONTS_DATA_HANDLE fg, unsigned int, unsigned int, unsigned int, pixel*);
void blank_canvas(float, color_type); void blank_canvas(float, color_type);
void blank_os_window(OSWindow *); void blank_os_window(OSWindow *);
void set_titlebar_color(OSWindow *w, color_type color); void set_titlebar_color(OSWindow *w, color_type color);
void get_window_content_scale(GLFWwindow *w, float *xscale, float *yscale, double *xdpi, double *ydpi);
FONTS_DATA_HANDLE load_fonts_data(double, double, double); FONTS_DATA_HANDLE load_fonts_data(double, double, double);
void send_prerendered_sprites_for_window(OSWindow *w); void send_prerendered_sprites_for_window(OSWindow *w);
#ifdef __APPLE__ #ifdef __APPLE__

View File

@ -15,14 +15,14 @@ from .constants import (
ScreenGeometry, WindowGeometry, appname, get_boss, wakeup ScreenGeometry, WindowGeometry, appname, get_boss, wakeup
) )
from .fast_data_types import ( from .fast_data_types import (
BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM, CELL_PROGRAM, BLIT_PROGRAM, BGIMAGE_PROGRAM, BGIMAGE_TILED_PROGRAM, CELL_BG_PROGRAM,
CELL_SPECIAL_PROGRAM, CSI, DCS, DECORATION, DIM, CELL_FG_PROGRAM, CELL_PROGRAM, CELL_SPECIAL_PROGRAM, CSI, DCS, DECORATION,
GRAPHICS_ALPHA_MASK_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM, DIM, GRAPHICS_ALPHA_MASK_PROGRAM, GRAPHICS_PREMULT_PROGRAM,
MARK, MARK_MASK, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, GRAPHICS_PROGRAM, MARK, MARK_MASK, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE,
STRIKETHROUGH, Screen, add_window, cell_size_for_window, compile_program, SCROLL_PAGE, STRIKETHROUGH, Screen, add_window, cell_size_for_window,
get_clipboard_string, init_cell_program, set_clipboard_string, compile_program, get_clipboard_string, init_cell_program,
set_titlebar_color, set_window_render_data, update_window_title, set_clipboard_string, set_titlebar_color, set_window_render_data,
update_window_visibility, viewport_for_window update_window_title, update_window_visibility, viewport_for_window
) )
from .keys import defines, extended_key_event, keyboard_mode_name from .keys import defines, extended_key_event, keyboard_mode_name
from .rgb import to_color from .rgb import to_color
@ -83,6 +83,7 @@ def load_shader_programs(semi_transparent=False):
vv = vv.replace('#define USE_SELECTION_FG', '#define DONT_USE_SELECTION_FG') vv = vv.replace('#define USE_SELECTION_FG', '#define DONT_USE_SELECTION_FG')
ff = ff.replace('#define USE_SELECTION_FG', '#define DONT_USE_SELECTION_FG') ff = ff.replace('#define USE_SELECTION_FG', '#define DONT_USE_SELECTION_FG')
compile_program(p, vv, ff) compile_program(p, vv, ff)
v, f = load_shaders('graphics') v, f = load_shaders('graphics')
for which, p in { for which, p in {
'SIMPLE': GRAPHICS_PROGRAM, 'SIMPLE': GRAPHICS_PROGRAM,
@ -91,6 +92,15 @@ def load_shader_programs(semi_transparent=False):
}.items(): }.items():
ff = f.replace('ALPHA_TYPE', which) ff = f.replace('ALPHA_TYPE', which)
compile_program(p, v, ff) compile_program(p, v, ff)
v, f = load_shaders('bgimage')
for which, p in {
'SIMPLE': BGIMAGE_PROGRAM,
'TILED': BGIMAGE_TILED_PROGRAM,
}.items():
ff = f.replace('LAYOUT_TYPE', which)
compile_program(p, v, ff)
init_cell_program() init_cell_program()