From b722bc809cb67add449745f983cd4f8cd6811b47 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Sep 2017 09:29:15 +0530 Subject: [PATCH] More work on displaying images --- kitty/graphics.c | 136 +++++++++++++++++++++++++++++++++-------------- kitty/graphics.h | 14 ++++- kitty/screen.c | 2 +- 3 files changed, 110 insertions(+), 42 deletions(-) diff --git a/kitty/graphics.c b/kitty/graphics.c index 3f2021796..c175ad224 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -15,7 +15,7 @@ #include #include -#define REPORT_ERROR(fmt, ...) { fprintf(stderr, fmt, __VA_ARGS__); fprintf(stderr, "\n"); } +#define REPORT_ERROR(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); } GraphicsManager* grman_realloc(GraphicsManager *old, index_type lines, index_type columns) { @@ -35,6 +35,12 @@ grman_realloc(GraphicsManager *old, index_type lines, index_type columns) { return self; } +static inline void +free_refs_data(Image *img) { + free(img->refs); img->refs = NULL; + img->refcnt = 0; img->refcap = 0; +} + static inline void free_load_data(LoadData *ld) { free(ld->buf); ld->buf_used = 0; ld->buf_capacity = 0; @@ -49,6 +55,7 @@ free_texture_func free_texture = NULL; static inline void free_image(Image *img) { if (img->texture_id) free_texture(&img->texture_id); + free_refs_data(img); free_load_data(&(img->load_data)); } @@ -75,21 +82,6 @@ ensure_space(void *array, size_t *capacity, size_t count, size_t item_size, bool return ans; } -static inline Image* -find_or_create_image(GraphicsManager *self, uint32_t id, bool *existing) { - if (id) { - for (size_t i = 0; i < self->image_count; i++) { - if (self->images[i].client_id == id) { - *existing = true; - return self->images + i; - } - } - } - *existing = false; - self->images = ensure_space(self->images, &self->images_capacity, self->image_count, sizeof(Image), true); - return self->images + self->image_count++; -} - static inline void remove_from_array(void *array, size_t item_size, size_t idx, size_t array_count) { size_t num_to_right = array_count - 1 - idx; @@ -98,16 +90,6 @@ remove_from_array(void *array, size_t item_size, size_t idx, size_t array_count) memset(p + (item_size * (array_count - 1)), 0, item_size); } -static inline void -remove_images(GraphicsManager *self, bool(*predicate)(Image*)) { - for (size_t i = self->image_count; i-- > 0;) { - if (predicate(self->images + i)) { - free_image(self->images + i); - remove_from_array(self->images, sizeof(Image), i, self->image_count--); - } - } -} - static inline Image* img_by_internal_id(GraphicsManager *self, size_t id) { for (size_t i = 0; i < self->image_count; i++) { @@ -116,6 +98,15 @@ img_by_internal_id(GraphicsManager *self, size_t id) { return NULL; } +static inline Image* +img_by_client_id(GraphicsManager *self, uint32_t id) { + for (size_t i = 0; i < self->image_count; i++) { + if (self->images[i].client_id == id) return self->images + i; + } + return NULL; +} + + // Loading image data {{{ static char add_response[512] = {0}; @@ -282,9 +273,35 @@ add_trim_predicate(Image *img) { return !img->data_loaded || (!img->client_id && !img->refcnt); } -static bool -handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload) { -#define ABRT(code, ...) { set_add_response(#code, __VA_ARGS__); self->loading_image = 0; return false; } +static inline Image* +find_or_create_image(GraphicsManager *self, uint32_t id, bool *existing) { + if (id) { + for (size_t i = 0; i < self->image_count; i++) { + if (self->images[i].client_id == id) { + *existing = true; + return self->images + i; + } + } + } + *existing = false; + self->images = ensure_space(self->images, &self->images_capacity, self->image_count, sizeof(Image), true); + return self->images + self->image_count++; +} + +static inline void +remove_images(GraphicsManager *self, bool(*predicate)(Image*)) { + for (size_t i = self->image_count; i-- > 0;) { + if (predicate(self->images + i)) { + free_image(self->images + i); + remove_from_array(self->images, sizeof(Image), i, self->image_count--); + } + } +} + + +static Image* +handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, bool *is_dirty) { +#define ABRT(code, ...) { set_add_response(#code, __VA_ARGS__); self->loading_image = 0; return NULL; } #define MAX_DATA_SZ (4 * 100000000) has_add_respose = false; bool existing, init_img = true; @@ -301,6 +318,8 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ if (existing) { free_load_data(&img->load_data); img->data_loaded = false; + free_refs_data(img); + *is_dirty = true; } else { img->internal_id = internal_id_counter++; img->client_id = g->id; @@ -371,7 +390,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ default: ABRT(EINVAL, "Unknown transmission type: %c", g->transmission_type); } - if (!img->data_loaded) return false; + if (!img->data_loaded) return NULL; self->loading_image = 0; bool needs_processing = g->compressed || fmt == PNG; if (needs_processing) { @@ -381,20 +400,20 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ case 'z': IB; if (!inflate_zlib(self, img, buf, bufsz)) { - img->data_loaded = false; return false; + img->data_loaded = false; return NULL; } break; case 0: break; default: ABRT(EINVAL, "Unknown image compression: %c", g->compressed); - img->data_loaded = false; return false; + img->data_loaded = false; return NULL; } switch(fmt) { case PNG: IB; if (!inflate_png(self, img, buf, bufsz)) { - img->data_loaded = false; return false; + img->data_loaded = false; return NULL; } break; default: break; @@ -422,7 +441,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ } else img->load_data.data = img->load_data.mapped_file; } } - return img->data_loaded; + return img; #undef MAX_DATA_SZ #undef ABRT } @@ -443,20 +462,59 @@ create_add_response(GraphicsManager UNUSED *self, const GraphicsCommand *g, bool // }}} +// Displaying images {{{ + +static void +handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, bool *is_dirty, Image *img) { + if (img == NULL) img = img_by_client_id(self, g->id); + if (img == NULL) { REPORT_ERROR("Put command refers to non-existent image with id: %u", g->id); return; } + if (!img->data_loaded) { REPORT_ERROR("Put command refers to image with id: %u that could not load its data", g->id); return; } + if (img->refcnt >= img->refcap) { + img->refcap = MAX(10, img->refcap * 2); + img->refs = realloc(img->refs, img->refcap * sizeof(ImageRef)); + if (img->refs == NULL) { REPORT_ERROR("Out of memory growing image refs array"); img->refcap = 0; return; } + } + *is_dirty = true; + ImageRef *ref = NULL; + for (size_t i=0; i < img->refcnt; i++) { + if ((unsigned)img->refs[i].start_row == c->x && (unsigned)img->refs[i].start_column == c->y) { + ref = img->refs + i; + break; + } + } + if (ref == NULL) ref = img->refs + img->refcnt++; + ref->src_x = g->x_offset; ref->src_y = g->y_offset; ref->src_width = g->width ? g->width : img->width; ref->src_height = g->height ? g->height : img->height; + ref->src_width = MIN(ref->src_width, img->width - (img->width > ref->src_x ? ref->src_x : img->width)); + ref->src_height = MIN(ref->src_height, img->height - (img->height > ref->src_y ? ref->src_y : img->height)); +} + +// }}} + const char* -grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload) { - bool data_loaded; +grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, Cursor *c, bool *is_dirty) { + Image *image; + const char *ret = NULL; switch(g->action) { case 0: case 't': - data_loaded = handle_add_command(self, g, payload); - return create_add_response(self, g, data_loaded); + case 'T': + image = handle_add_command(self, g, payload, is_dirty); + ret = create_add_response(self, g, image != NULL); + if (g->action == 'T') handle_put_command(self, g, c, is_dirty, image); + break; + case 'p': + if (!g->id) { + REPORT_ERROR("%s", "Put graphics command without image id"); + break; + } + handle_put_command(self, g, c, is_dirty, NULL); + break; default: REPORT_ERROR("Unknown graphics command action: %c", g->action); break; } - return NULL; + return ret; } void diff --git a/kitty/graphics.h b/kitty/graphics.h index 86bb680a7..fbe1470da 100644 --- a/kitty/graphics.h +++ b/kitty/graphics.h @@ -27,12 +27,22 @@ typedef struct { bool is_4byte_aligned; } LoadData; +typedef struct { + uint32_t src_width, src_height, src_x, src_y; + uint32_t dest_x_offset, dest_y_offset; + int start_row, start_column, end_row, end_column; +} ImageRef; + + typedef struct { uint32_t texture_id, client_id, width, height; - size_t internal_id, refcnt; + size_t internal_id; bool data_loaded; LoadData load_data; + + ImageRef *refs; + size_t refcnt, refcap; } Image; @@ -48,4 +58,4 @@ PyTypeObject GraphicsManager_Type; GraphicsManager* grman_realloc(GraphicsManager *, index_type lines, index_type columns); void grman_clear(GraphicsManager*); -const char* grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload); +const char* grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, Cursor *c, bool *is_dirty); diff --git a/kitty/screen.c b/kitty/screen.c index 8b8d7d5d2..0071375bc 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -418,7 +418,7 @@ write_to_child(Screen *self, const char *data, size_t sz) { void screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload) { - const char *response = grman_handle_command(self->grman, cmd, payload); + const char *response = grman_handle_command(self->grman, cmd, payload, self->cursor, &self->is_dirty); if (response != NULL) write_to_child(self, response, strlen(response)); } // }}}