From 1bf180f354bb513ac1d6954f8ec2e7f3bcedf19b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 1 Mar 2023 16:11:38 +0530 Subject: [PATCH] Allow loading window background images from memory --- kitty/fast_data_types.pyi | 1 + kitty/graphics.c | 29 +++++++++++++++++------------ kitty/graphics.h | 1 + kitty/state.c | 17 +++++++++++++++-- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 6d756a03b..fddc8c18a 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -609,6 +609,7 @@ def set_background_image( os_window_ids: Tuple[int, ...], configured: bool = True, layout_name: Optional[str] = None, + png_data: bytes = b'' ) -> None: pass diff --git a/kitty/graphics.c b/kitty/graphics.c index 26fd84d51..e076845d8 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -297,41 +297,34 @@ add_trim_predicate(Image *img) { } bool -png_path_to_bitmap(const char* path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) { - FILE* fp = fopen(path, "r"); - if (fp == NULL) { - log_error("The PNG image: %s could not be opened with error: %s", path, strerror(errno)); - return false; - } +png_from_file_pointer(FILE *fp, const char *path_for_error_messages, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) { size_t capacity = 16*1024, pos = 0; unsigned char *buf = malloc(capacity); - if (!buf) { log_error("Out of memory reading PNG file at: %s", path); fclose(fp); return false; } + if (!buf) { log_error("Out of memory reading PNG file at: %s", path_for_error_messages); fclose(fp); return false; } while (!feof(fp)) { if (capacity - pos < 1024) { capacity *= 2; unsigned char *new_buf = realloc(buf, capacity); if (!new_buf) { free(buf); - log_error("Out of memory reading PNG file at: %s", path); fclose(fp); return false; + log_error("Out of memory reading PNG file at: %s", path_for_error_messages); fclose(fp); return false; } buf = new_buf; } pos += fread(buf + pos, sizeof(char), capacity - pos, fp); int saved_errno = errno; if (ferror(fp) && saved_errno != EINTR) { - log_error("Failed while reading from file: %s with error: %s", path, strerror(saved_errno)); - fclose(fp); + log_error("Failed while reading from file: %s with error: %s", path_for_error_messages, strerror(saved_errno)); free(buf); return false; } } - fclose(fp); fp = NULL; png_read_data d = {0}; inflate_png_inner(&d, buf, pos); free(buf); if (!d.ok) { free(d.decompressed); free(d.row_pointers); - log_error("Failed to decode PNG image at: %s", path); + log_error("Failed to decode PNG image at: %s", path_for_error_messages); return false; } *data = d.decompressed; @@ -341,6 +334,18 @@ png_path_to_bitmap(const char* path, uint8_t** data, unsigned int* width, unsign return true; } +bool +png_path_to_bitmap(const char* path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz) { + FILE* fp = fopen(path, "r"); + if (fp == NULL) { + log_error("The PNG image: %s could not be opened with error: %s", path, strerror(errno)); + return false; + } + bool ret = png_from_file_pointer(fp, path, data, width, height, sz); + fclose(fp); fp = NULL; + return ret; +} + static Image* find_or_create_image(GraphicsManager *self, uint32_t id, bool *existing) { diff --git a/kitty/graphics.h b/kitty/graphics.h index 760f8f272..fd0418785 100644 --- a/kitty/graphics.h +++ b/kitty/graphics.h @@ -160,5 +160,6 @@ void grman_resize(GraphicsManager*, index_type, index_type, index_type, index_ty void grman_rescale(GraphicsManager *self, CellPixelSize fg); void gpu_data_for_image(ImageRenderData *ans, float left, float top, float right, float bottom); 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_from_file_pointer(FILE* fp, const char *path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz); bool png_path_to_bitmap(const char *path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz); bool scan_active_animations(GraphicsManager *self, const monotonic_t now, monotonic_t *minimum_gap, bool os_window_context_set); diff --git a/kitty/state.c b/kitty/state.c index 53cf43be6..bb5f18fc3 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -1152,14 +1152,27 @@ pyset_background_image(PyObject *self UNUSED, PyObject *args) { PyObject *layout_name = NULL; PyObject *os_window_ids; int configured = 0; - PA("zO!|pO", &path, &PyTuple_Type, &os_window_ids, &configured, &layout_name); + char *png_data = NULL; Py_ssize_t png_data_size = 0; + PA("zO!|pOy#", &path, &PyTuple_Type, &os_window_ids, &configured, &layout_name, &png_data, png_data_size); size_t size; BackgroundImageLayout layout = PyUnicode_Check(layout_name) ? bglayout(layout_name) : OPT(background_image_layout); BackgroundImage *bgimage = NULL; if (path) { bgimage = calloc(1, sizeof(BackgroundImage)); if (!bgimage) return PyErr_NoMemory(); - if (!png_path_to_bitmap(path, &bgimage->bitmap, &bgimage->width, &bgimage->height, &size)) { + bool ok; + if (png_data) { + FILE *fp = fmemopen(png_data, png_data_size, "r"); + if (fp == NULL) { + PyErr_SetFromErrnoWithFilename(PyExc_OSError, path); + free(bgimage); + return NULL; + } + ok = png_from_file_pointer(fp, path, &bgimage->bitmap, &bgimage->width, &bgimage->height, &size); + } else { + ok = png_path_to_bitmap(path, &bgimage->bitmap, &bgimage->width, &bgimage->height, &size); + } + if (!ok) { PyErr_Format(PyExc_ValueError, "Failed to load image from: %s", path); free(bgimage); return NULL;