diff --git a/kitty/fonts.c b/kitty/fonts.c index 190a0accf..50529dd8b 100644 --- a/kitty/fonts.c +++ b/kitty/fonts.c @@ -1076,7 +1076,7 @@ render_line(FONTS_DATA_HANDLE fg_, Line *line) { StringCanvas render_simple_text(FONTS_DATA_HANDLE fg_, const char *text) { FontGroup *fg = (FontGroup*)fg_; - if (fg->fonts_count) return render_simple_text_impl(fg->fonts[0].face, text); + if (fg->fonts_count && fg->medium_font_idx) return render_simple_text_impl(fg->fonts[fg->medium_font_idx].face, text); StringCanvas ans = {0}; return ans; } diff --git a/kitty/fonts.h b/kitty/fonts.h index 33f69fb04..1543ee10d 100644 --- a/kitty/fonts.h +++ b/kitty/fonts.h @@ -38,6 +38,7 @@ void render_line(FONTS_DATA_HANDLE, Line *line); void sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len); typedef void (*free_extra_data_func)(void*); StringCanvas render_simple_text_impl(PyObject *s, const char *text); +StringCanvas render_simple_text(FONTS_DATA_HANDLE fg_, const char *text); static inline void right_shift_canvas(pixel *canvas, size_t width, size_t height, size_t amt) { diff --git a/kitty/freetype.c b/kitty/freetype.c index 0678941cc..ed1270471 100644 --- a/kitty/freetype.c +++ b/kitty/freetype.c @@ -579,14 +579,14 @@ StringCanvas render_simple_text_impl(PyObject *s, const char *text) { Face *self = (Face*)s; StringCanvas ans = {0}; - size_t num_chars = strnlen(text, 20); + size_t num_chars = strnlen(text, 32); int max_char_width = font_units_to_pixels_x(self, self->face->max_advance_width); size_t canvas_width = max_char_width * (num_chars*2); size_t canvas_height = font_units_to_pixels_y(self, self->face->height) + 8; unsigned char *canvas = calloc(1, canvas_width * canvas_height); if (!canvas) return ans; size_t pen_x = 0; - for (size_t n = 0; n < num_chars; n++ ) { + for (size_t n = 0; n < num_chars; n++) { FT_UInt glyph_index = FT_Get_Char_Index(self->face, text[n]); int error = FT_Load_Glyph(self->face, glyph_index, FT_LOAD_DEFAULT); if (error) continue; diff --git a/kitty/glfw.c b/kitty/glfw.c index 759b00229..40f708bcc 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -58,6 +58,17 @@ update_os_window_viewport(OSWindow *window, bool notify_boss) { } } +static void +draw_resizing_window(OSWindow *w, unsigned int width, unsigned int height) { + char text[32] = {0}; + blank_os_window(w); + snprintf(text, sizeof(text), "%u x %u cells", width / w->fonts_data->cell_width, height / w->fonts_data->cell_height); + StringCanvas rendered = render_simple_text(w->fonts_data, text); + if (rendered.canvas) { + draw_centered_alpha_mask(w->gvao_idx, width, height, rendered.width, rendered.height, rendered.canvas); + free(rendered.canvas); + } +} // callbacks {{{ @@ -153,7 +164,7 @@ framebuffer_size_callback(GLFWwindow *w, int width, int height) { // you get partially rendered windows. make_os_window_context_current(window); update_surface_size(width, height, window->offscreen_texture_id); - blank_os_window(window); + draw_resizing_window(window, MAX(0, width), MAX(0, height)); swap_window_buffers(window); request_tick_callback(); } else log_error("Ignoring resize request for tiny size: %dx%d", width, height); diff --git a/kitty/graphics.c b/kitty/graphics.c index 4b900de31..0216e48a8 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -522,6 +522,24 @@ cmp_by_zindex_and_image(const void *a_, const void *b_) { return ans; } +static inline void +set_vertex_data(ImageRenderData *rd, const ImageRef *ref, const ImageRect *source_rect) { +#define R(n, a, b) rd->vertices[n*4] = ref->src_rect.a; rd->vertices[n*4 + 1] = ref->src_rect.b; rd->vertices[n*4 + 2] = source_rect->a; rd->vertices[n*4 + 3] = source_rect->b; + R(0, right, top); R(1, right, bottom); R(2, left, bottom); R(3, left, top); +#undef R +} + +void +gpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height) { + static const ImageRef source_rect = { .src_rect = { .left=0, .top=0, .bottom=1, .right=1 }}; + const ImageRef *ref = &source_rect; + float width_frac = width / (float)screen_width_px, height_frac = height / (float)screen_height_px; + float hmargin = MAX(0, 2 - width_frac) / 2; + float vmargin = MAX(0, 2 - height_frac) / 2; + const ImageRect r = { .left = -1 + hmargin, .right = -1 + hmargin + width_frac, .top = -1 + vmargin, .bottom = -1 + vmargin + height_frac }; + set_vertex_data(ans, ref, &r); +} + bool grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float screen_left, float screen_top, float dx, float dy, unsigned int num_cols, unsigned int num_rows, CellPixelSize cell) { if (self->last_scrolled_by != scrolled_by) self->layers_dirty = true; @@ -553,9 +571,7 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree if (ref->z_index < 0) self->num_of_negative_refs++; else self->num_of_positive_refs++; ensure_space_for(self, render_data, ImageRenderData, self->count + 1, capacity, 64, true); ImageRenderData *rd = self->render_data + self->count; -#define R(n, a, b) rd->vertices[n*4] = ref->src_rect.a; rd->vertices[n*4 + 1] = ref->src_rect.b; rd->vertices[n*4 + 2] = r.a; rd->vertices[n*4 + 3] = r.b; - R(0, right, top); R(1, right, bottom); R(2, left, bottom); R(3, left, top); -#undef R + set_vertex_data(rd, ref, &r); self->count++; rd->z_index = ref->z_index; rd->image_id = img->internal_id; rd->texture_id = img->texture_id; diff --git a/kitty/graphics.h b/kitty/graphics.h index 75db8db1c..d92c0644b 100644 --- a/kitty/graphics.h +++ b/kitty/graphics.h @@ -89,3 +89,4 @@ bool grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float void grman_scroll_images(GraphicsManager *self, const ScrollData*, CellPixelSize fg); void grman_resize(GraphicsManager*, index_type, index_type, index_type, index_type); 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); diff --git a/kitty/graphics_fragment.glsl b/kitty/graphics_fragment.glsl index d09a6785a..10b85ed77 100644 --- a/kitty/graphics_fragment.glsl +++ b/kitty/graphics_fragment.glsl @@ -2,15 +2,38 @@ #define ALPHA_TYPE uniform sampler2D image; +#ifdef ALPHA_MASK +uniform uint fg; +#else uniform float inactive_text_alpha; +#endif in vec2 texcoord; out vec4 color; +#ifdef ALPHA_MASK +const uint BYTE_MASK = uint(0xFF); + +vec3 color_to_vec(uint c) { + uint r, g, b; + r = (c >> 16) & BYTE_MASK; + g = (c >> 8) & BYTE_MASK; + b = c & BYTE_MASK; + return vec3(float(r) / 255.0, float(g) / 255.0, float(b) / 255.0); +} +#endif + + void main() { +#ifdef ALPHA_MASK + color = texture(image, texcoord); + vec3 fg_col = color_to_vec(fg); + color = vec4(color.r * fg_col.r, color.r * fg_col.g, color.r * fg_col.b, 1.0); +#else color = texture(image, texcoord); color.a *= inactive_text_alpha; #ifdef PREMULT color = vec4(color.rgb * color.a, color.a); #endif +#endif } diff --git a/kitty/shaders.c b/kitty/shaders.c index 92a296caa..0b98a2efa 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -8,7 +8,7 @@ #include "fonts.h" #include "gl.h" -enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_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, NUM_PROGRAMS }; enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BLIT_UNIT }; // Sprites {{{ @@ -210,7 +210,8 @@ create_graphics_vao() { struct CellUniformData { bool constants_set; - GLint gploc, gpploc, cploc, cfploc; + bool alpha_mask_fg_set; + GLint gploc, gpploc, cploc, cfploc, fg_loc; GLfloat prev_inactive_text_alpha; }; @@ -323,6 +324,27 @@ draw_graphics(int program, ssize_t vao_idx, ssize_t gvao_idx, ImageRenderData *d bind_vertex_array(vao_idx); } +void +draw_centered_alpha_mask(ssize_t gvao_idx, size_t screen_width, size_t screen_height, size_t width, size_t height, uint8_t *canvas) { + static ImageRenderData data = {.group_count=1}; + gpu_data_for_centered_image(&data, screen_width, screen_height, width, height); + if (!data.texture_id) { glGenTextures(1, &data.texture_id); } + glBindTexture(GL_TEXTURE_2D, data.texture_id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, canvas); + bind_program(GRAPHICS_ALPHA_MASK_PROGRAM); + if (!cell_uniform_data.alpha_mask_fg_set) { + cell_uniform_data.alpha_mask_fg_set = true; + glUniform1i(glGetUniformLocation(program_id(GRAPHICS_ALPHA_MASK_PROGRAM), "image"), GRAPHICS_UNIT); + glUniform1ui(glGetUniformLocation(program_id(GRAPHICS_ALPHA_MASK_PROGRAM), "fg"), OPT(foreground)); + } + draw_graphics(GRAPHICS_ALPHA_MASK_PROGRAM, 0, gvao_idx, &data, 0, 1); +} + #define BLEND_ONTO_OPAQUE glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // blending onto opaque colors #define BLEND_PREMULT glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // blending of pre-multiplied colors @@ -414,7 +436,8 @@ set_cell_uniforms(float current_inactive_text_alpha) { cell_uniform_data.cploc = glGetUniformLocation(program_id(CELL_PROGRAM), "inactive_text_alpha"); cell_uniform_data.cfploc = glGetUniformLocation(program_id(CELL_FG_PROGRAM), "inactive_text_alpha"); #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_PREMULT_PROGRAM, image, GRAPHICS_UNIT, 1i); + S(GRAPHICS_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, dim_opacity, OPT(dim_opacity), 1f); S(CELL_FG_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); #undef S @@ -632,7 +655,7 @@ static PyMethodDef module_methods[] = { bool init_shaders(PyObject *module) { #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(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(GLSL_VERSION); C(GL_VERSION); C(GL_VENDOR); diff --git a/kitty/state.c b/kitty/state.c index 68a5a821e..8918a419a 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -81,6 +81,7 @@ add_os_window() { memset(ans, 0, sizeof(OSWindow)); ans->id = ++global_state.os_window_id_counter; ans->tab_bar_render_data.vao_idx = create_cell_vao(); + ans->gvao_idx = create_graphics_vao(); ans->background_opacity = OPT(background_opacity); ans->font_sz_in_pts = global_state.font_sz_in_pts; END_WITH_OS_WINDOW_REFS @@ -180,6 +181,7 @@ destroy_os_window_item(OSWindow *w) { Py_CLEAR(w->window_title); Py_CLEAR(w->tab_bar_render_data.screen); if (w->offscreen_texture_id) free_texture(&w->offscreen_texture_id); remove_vao(w->tab_bar_render_data.vao_idx); + remove_vao(w->gvao_idx); free(w->tabs); w->tabs = NULL; } @@ -384,6 +386,7 @@ PYWRAP1(set_options) { S(click_interval, PyFloat_AsDouble); S(url_color, color_as_int); S(background, color_as_int); + S(foreground, color_as_int); S(active_border_color, color_as_int); S(inactive_border_color, color_as_int); S(bell_border_color, color_as_int); diff --git a/kitty/state.h b/kitty/state.h index 7a6b7395e..d0d51b2d7 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -21,7 +21,7 @@ typedef struct { unsigned int url_style; unsigned int scrollback_pager_history_size; char_type select_by_word_characters[256]; size_t select_by_word_characters_count; - color_type url_color, background, active_border_color, inactive_border_color, bell_border_color; + color_type url_color, background, foreground, active_border_color, inactive_border_color, bell_border_color; double repaint_delay, input_delay; bool focus_follows_mouse, hide_window_decorations; bool macos_hide_from_tasks, macos_quit_when_last_window_closed, macos_window_resizable, macos_traditional_fullscreen, macos_show_window_title_in_menubar; @@ -144,6 +144,7 @@ typedef struct { double pending_scroll_pixels; enum RENDER_STATE render_state; id_type last_focused_counter; + ssize_t gvao_idx; } OSWindow; @@ -205,6 +206,7 @@ ssize_t create_graphics_vao(); ssize_t create_border_vao(); bool send_cell_data_to_gpu(ssize_t, ssize_t, float, float, float, float, Screen *, OSWindow *); void draw_cells(ssize_t, ssize_t, float, float, float, float, Screen *, OSWindow *, bool, bool); +void draw_centered_alpha_mask(ssize_t gvao_idx, size_t screen_width, size_t screen_height, size_t width, size_t height, uint8_t *canvas); void update_surface_size(int, int, uint32_t); void free_texture(uint32_t*); void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool); diff --git a/kitty/window.py b/kitty/window.py index d46f1e702..a2f15eec4 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -16,14 +16,15 @@ ) from .fast_data_types import ( BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM, CELL_PROGRAM, - CELL_SPECIAL_PROGRAM, CSI, DCS, DECORATION, DIM, GRAPHICS_PREMULT_PROGRAM, - GRAPHICS_PROGRAM, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, - STRIKETHROUGH, Screen, add_window, cell_size_for_window, compile_program, - get_clipboard_string, init_cell_program, - set_clipboard_string, set_titlebar_color, set_window_render_data, - update_window_title, update_window_visibility, viewport_for_window + CELL_SPECIAL_PROGRAM, CSI, DCS, DECORATION, DIM, + GRAPHICS_ALPHA_MASK_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM, + OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, STRIKETHROUGH, Screen, + add_window, cell_size_for_window, compile_program, get_clipboard_string, + init_cell_program, set_clipboard_string, set_titlebar_color, + set_window_render_data, update_window_title, update_window_visibility, + viewport_for_window ) -from .keys import keyboard_mode_name, extended_key_event, defines +from .keys import defines, extended_key_event, keyboard_mode_name from .rgb import to_color from .terminfo import get_capabilities from .utils import ( @@ -87,6 +88,7 @@ def color_as_vec3(x): for which, p in { 'SIMPLE': GRAPHICS_PROGRAM, 'PREMULT': GRAPHICS_PREMULT_PROGRAM, + 'ALPHA_MASK': GRAPHICS_ALPHA_MASK_PROGRAM, }.items(): ff = f.replace('ALPHA_TYPE', which) compile_program(p, v, ff)