diff --git a/image-grid.c b/image-grid.c index aadd64812..72a9e3272 100644 --- a/image-grid.c +++ b/image-grid.c @@ -206,41 +206,47 @@ image_grid_add(struct grid *gd, struct image *im, u_int x, u_int y, } } -/* Remove the covered part of a span. */ +/* Remove the covered part of one span. */ +static void +image_span_remove(struct image_span *span, u_int x, u_int end) +{ + u_int span_end = span->x + span->sx; + + if (span_end <= x || span->x >= end) + return; + if (span->x < x && span_end > end) { + span->sx = x - span->x; + image_span_add(span->line, span->placement, end, span_end - end, + span->source_x + end - span->x, span->source_y); + return; + } + if (span->x < x) { + span->sx = x - span->x; + return; + } + if (span_end > end) { + span->source_x += end - span->x; + span->sx = span_end - end; + span->x = end; + return; + } + image_span_free(span); +} + +/* Remove a range of cells from every span on a line. */ static void image_line_remove(struct image_line *line, u_int x, u_int w) { struct image_span *span, *next; - u_int end, span_end, right; + u_int end; if (line == NULL || w == 0) return; end = x + w; if (end < x) end = UINT_MAX; - TAILQ_FOREACH_SAFE(span, &line->spans, line_entry, next) { - span_end = span->x + span->sx; - if (span_end <= x || span->x >= end) - continue; - if (span->x < x && span_end > end) { - right = span_end - end; - span->sx = x - span->x; - image_span_add(line, span->placement, end, right, - span->source_x + end - span->x, span->source_y); - continue; - } - if (span->x < x) { - span->sx = x - span->x; - continue; - } - if (span_end > end) { - span->source_x += end - span->x; - span->sx = span_end - end; - span->x = end; - continue; - } - image_span_free(span); - } + TAILQ_FOREACH_SAFE(span, &line->spans, line_entry, next) + image_span_remove(span, x, end); } /* Remove a rectangle of image spans from a grid. */ @@ -542,13 +548,69 @@ image_cell_has_visible_pixels(struct image *im, u_int x, u_int y) return (0); } +/* Return whether a new cell's pixels completely cover an old cell. */ +static int +image_cell_covers(struct image *new, u_int nx, u_int ny, struct image *old, + u_int ox, u_int oy) +{ + u_int npx, npy, nsx, nsy, opx, opy, osx, osy, xx, yy; + u_int ncw, nch, ocw, och, ncx, ncy, ocx, ocy; + const u_char *np, *op, *npixels, *opixels; + size_t nstride, ostride; + + image_get_canvas_size(new, &ncw, &nch); + image_get_canvas_size(old, &ocw, &och); + image_get_size_in_cells(new, &ncx, &ncy); + image_get_size_in_cells(old, &ocx, &ocy); + if (ncw / ncx != ocw / ocx || nch / ncy != och / ocy) + return (0); + image_get_pixel_area(new, nx, ny, 1, 1, &npx, &npy, &nsx, &nsy); + image_get_pixel_area(old, ox, oy, 1, 1, &opx, &opy, &osx, &osy); + + npixels = image_get_pixels(new, &nstride, NULL); + opixels = image_get_pixels(old, &ostride, NULL); + for (yy = 0; yy < osy; yy++) { + op = opixels + (opy + yy) * ostride + opx * 4; + if (yy < nsy) { + np = npixels + (npy + yy) * nstride + npx * 4; + } + for (xx = 0; xx < osx; xx++) { + if (op[xx * 4 + 3] == 0) + continue; + if (yy >= nsy || xx >= nsx || np[xx * 4 + 3] != 255) + return (0); + } + } + return (1); +} + +/* Discard cells of older placements fully covered by a new image. */ +static void +image_line_cover(struct image_line *line, struct image_placement *plmt, + u_int x, u_int src_x, u_int src_y) +{ + struct image_span *span, *next; + struct image_placement *old; + + TAILQ_FOREACH_SAFE(span, &line->spans, line_entry, next) { + old = span->placement; + if (old == plmt || x < span->x || x >= span->x + span->sx) + continue; + if (!image_cell_covers(plmt->image, src_x, src_y, + old->image, span->source_x + x - span->x, + span->source_y)) + continue; + image_span_remove(span, x, x + 1); + } +} + /* Attach the visible runs from one image row. */ static void image_add_row(struct image_line *line, struct image_placement *plmt, u_int cx, u_int src_y, u_int sx) { struct image *im = plmt->image; - u_int x, run; + u_int x, i, run; for (x = 0; x < sx; x += run) { if (!image_cell_has_visible_pixels(im, x, src_y)) { @@ -559,6 +621,8 @@ image_add_row(struct image_line *line, struct image_placement *plmt, if (!image_cell_has_visible_pixels(im, x + run, src_y)) break; } + for (i = 0; i < run; i++) + image_line_cover(line, plmt, cx + x + i, x + i, src_y); image_span_add(line, plmt, cx + x, run, x, src_y); } } diff --git a/image-sixel.c b/image-sixel.c index c7fc56b3d..c4c8fb23d 100644 --- a/image-sixel.c +++ b/image-sixel.c @@ -61,18 +61,25 @@ struct sixel_image { u_int dy; u_int dc; - /* Lines of palette indexes. */ + /* Lines of palette indexes and the bytes allocated for them. */ struct sixel_line *lines; + size_t allocated; }; /* Grow a SIXEL image to contain a line. */ static int sixel_parse_expand_lines(struct sixel_image *si, u_int y) { + u_int height = si->y; + + if (y > height) + height = y; + if (height > SIXEL_HEIGHT_LIMIT) + return (1); + if (si->x * height > IMAGE_SIZE_LIMIT / 4) + return (1); if (y <= si->y) return (0); - if (y > SIXEL_HEIGHT_LIMIT) - return (1); si->lines = xrecallocarray(si->lines, si->y, y, sizeof *si->lines); si->y = y; return (0); @@ -82,14 +89,24 @@ sixel_parse_expand_lines(struct sixel_image *si, u_int y) static int sixel_parse_expand_line(struct sixel_image *si, struct sixel_line *sl, u_int x) { + u_int width = si->x; + size_t added; + + if (x > width) + width = x; + if (width > SIXEL_WIDTH_LIMIT) + return (1); + if (width * si->y > IMAGE_SIZE_LIMIT / 4) + return (1); if (x <= sl->x) return (0); - if (x > SIXEL_WIDTH_LIMIT) + added = (width - sl->x) * sizeof *sl->data; + if (added > IMAGE_SIZE_LIMIT / 2 - si->allocated) return (1); - if (x > si->x) - si->x = x; + si->x = width; sl->data = xrecallocarray(sl->data, sl->x, si->x, sizeof *sl->data); sl->x = si->x; + si->allocated += added; return (0); } @@ -204,7 +221,10 @@ sixel_parse_attributes(struct sixel_image *si, const char *cp, const char *end) } si->x = x; - sixel_parse_expand_lines(si, y); + if (sixel_parse_expand_lines(si, y) != 0) { + log_debug("%s: size limit reached", __func__); + return (NULL); + } return (last); } @@ -320,7 +340,7 @@ sixel_parse_repeat(struct sixel_image *si, const char *cp, const char *end) ch = (*last++) - 0x3f; for (i = 0; i < n; i++) { if (sixel_parse_write(si, ch) != 0) { - log_debug("%s: width limit reached", __func__); + log_debug("%s: size limit reached", __func__); return (NULL); } si->dx++; @@ -379,7 +399,7 @@ sixel_parse(const char *buf, size_t len, u_int p1, u_int p2, u_int xpixel, if (ch < 0x3f || ch > 0x7e) goto bad; if (sixel_parse_write(si, ch - 0x3f) != 0) { - log_debug("%s: width limit reached", __func__); + log_debug("%s: size limit reached", __func__); goto bad; } si->dx++; diff --git a/screen-write.c b/screen-write.c index 08e76cedb..7df389867 100644 --- a/screen-write.c +++ b/screen-write.c @@ -730,6 +730,10 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, grid_get_cell(gd, xx, yy, &gc); if (xx + gc.data.width > px + nx) break; +#ifdef ENABLE_IMAGES + if (ctx->flags & SCREEN_WRITE_INPUT) + image_damage_screen(ctx, s->cx, s->cy, 1, 1); +#endif grid_view_set_cell(s->grid, s->cx, s->cy, &gc); if (!window_position_is_visible(r, xoff + s->cx)) diff --git a/window-copy.c b/window-copy.c index 39add5b28..18af79d93 100644 --- a/window-copy.c +++ b/window-copy.c @@ -5797,6 +5797,9 @@ window_copy_write_line(struct window_mode_entry *wme, char *expanded; struct format_tree *ft; int current, mode; +#ifdef ENABLE_IMAGES + int flags; +#endif width = window_copy_line_number_width(wme); if (width >= sx) @@ -5856,6 +5859,8 @@ window_copy_write_line(struct window_mode_entry *wme, data->backing->grid, 0, hsize - data->oy + py, content_sx, 1); if (data->image_refresh) image_redraw_area(ctx, width, py, content_sx, 1); + flags = ctx->flags; + ctx->flags |= SCREEN_WRITE_INPUT; #endif if (py == 0 && s->rupper < s->rlower && !data->hide_position) { @@ -5877,6 +5882,9 @@ window_copy_write_line(struct window_mode_entry *wme, screen_write_putc(ctx, &grid_default_cell, '$'); } +#ifdef ENABLE_IMAGES + ctx->flags = flags; +#endif format_free(ft); }