More work on displaying images
This commit is contained in:
parent
710d00674a
commit
b722bc809c
136
kitty/graphics.c
136
kitty/graphics.c
@ -15,7 +15,7 @@
|
||||
#include <zlib.h>
|
||||
#include <png.h>
|
||||
|
||||
#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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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));
|
||||
}
|
||||
// }}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user