From e2fc75434edeba95dacf2035871a6eb3809203f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= Date: Sat, 12 Jul 2025 22:27:40 +0200 Subject: [PATCH] sub/ass_mp: add missing padding for ass packer This ensures it is safe to sample packed image with bilinear filtering. --- sub/ass_mp.c | 86 ++++++++++++++++++++++++++++++++++++++--------- sub/img_convert.c | 7 ++-- sub/img_convert.h | 2 +- 3 files changed, 76 insertions(+), 19 deletions(-) diff --git a/sub/ass_mp.c b/sub/ass_mp.c index c02eb2a545..a9dbdf374e 100644 --- a/sub/ass_mp.c +++ b/sub/ass_mp.c @@ -206,6 +206,7 @@ struct mp_ass_packer *mp_ass_packer_alloc(void *ta_parent) { struct mp_ass_packer *p = talloc_zero(ta_parent, struct mp_ass_packer); p->packer = talloc_zero(p, struct bitmap_packer); + p->packer->padding = 1; // assume bilinear sampling return p; } @@ -256,18 +257,68 @@ static bool pack(struct mp_ass_packer *p, struct sub_bitmaps *res, int imgfmt) return true; } +static void fill_padding_1(uint8_t *base, int w, int h, int stride, int padding) +{ + for (int row = 0; row < h; ++row) { + uint8_t *row_ptr = base + row * stride; + uint8_t left_pixel = row_ptr[0]; + uint8_t right_pixel = row_ptr[w - 1]; + + for (int i = 1; i <= padding; ++i) + row_ptr[-i] = left_pixel; + + for (int i = 0; i < padding; ++i) + row_ptr[w + i] = right_pixel; + } + + int row_bytes = (w + 2 * padding); + uint8_t *top_row = base - padding; + for (int i = 1; i <= padding; ++i) + memcpy(base - i * stride - padding, top_row, row_bytes); + + uint8_t *last_row = base + (h - 1) * stride - padding; + for (int i = 0; i < padding; ++i) + memcpy(base + (h + i) * stride - padding, last_row, row_bytes); +} + +static void fill_padding_4(uint8_t *base, int w, int h, int stride, int padding) +{ + for (int row = 0; row < h; ++row) { + uint32_t *row_ptr = (uint32_t *)(base + row * stride); + uint32_t left_pixel = row_ptr[0]; + uint32_t right_pixel = row_ptr[w - 1]; + + for (int i = 1; i <= padding; ++i) + row_ptr[-i] = left_pixel; + + for (int i = 0; i < padding; ++i) + row_ptr[w + i] = right_pixel; + } + + int row_bytes = (w + 2 * padding) * 4; + uint8_t *top_row = base - padding * 4; + for (int i = 1; i <= padding; ++i) + memcpy(base - i * stride - padding * 4, top_row, row_bytes); + + uint8_t *last_row = base + (h - 1) * stride - padding * 4; + for (int i = 0; i < padding; ++i) + memcpy(base + (h + i) * stride - padding * 4, last_row, row_bytes); +} + static bool pack_libass(struct mp_ass_packer *p, struct sub_bitmaps *res) { if (!pack(p, res, IMGFMT_Y8)) return false; + int padding = p->packer->padding; + uint8_t *base = res->packed->planes[0]; + int stride = res->packed->stride[0]; + for (int n = 0; n < res->num_parts; n++) { struct sub_bitmap *b = &res->parts[n]; - - int stride = res->packed->stride[0]; - void *pdata = - (uint8_t *)res->packed->planes[0] + b->src_y * stride + b->src_x; + void *pdata = base + b->src_y * stride + b->src_x; memcpy_pic(pdata, b->bitmap, b->w, b->h, stride, b->stride); + fill_padding_1(pdata, b->w, b->h, stride, padding); b->bitmap = pdata; b->stride = stride; @@ -278,8 +329,9 @@ static bool pack_libass(struct mp_ass_packer *p, struct sub_bitmaps *res) static bool pack_rgba(struct mp_ass_packer *p, struct sub_bitmaps *res) { + int padding = p->packer->padding; struct mp_rect bb_list[MP_SUB_BB_LIST_MAX]; - int num_bb = mp_get_sub_bb_list(res, bb_list, MP_SUB_BB_LIST_MAX); + int num_bb = mp_get_sub_bb_list(res, bb_list, MP_SUB_BB_LIST_MAX, padding); struct sub_bitmaps imgs = { .change_id = res->change_id, @@ -296,18 +348,19 @@ static bool pack_rgba(struct mp_ass_packer *p, struct sub_bitmaps *res) if (!pack(p, &imgs, IMGFMT_BGRA)) return false; + uint8_t *base = imgs.packed->planes[0]; + int stride = imgs.packed->stride[0]; + for (int n = 0; n < num_bb; n++) { struct mp_rect bb = bb_list[n]; struct sub_bitmap *b = &imgs.parts[n]; - b->x = bb.x0; - b->y = bb.y0; - b->w = b->dw = bb.x1 - bb.x0; - b->h = b->dh = bb.y1 - bb.y0; - b->stride = imgs.packed->stride[0]; - b->bitmap = (uint8_t *)imgs.packed->planes[0] + - b->stride * b->src_y + b->src_x * 4; - + b->x = bb.x0 + padding; + b->y = bb.y0 + padding; + b->w = b->dw = mp_rect_w(bb) - padding * 2; + b->h = b->dh = mp_rect_h(bb) - padding * 2; + b->stride = stride; + b->bitmap = base + b->stride * b->src_y + b->src_x * 4; memset_pic(b->bitmap, 0, b->w * 4, b->h, b->stride); for (int i = 0; i < res->num_parts; i++) { @@ -315,14 +368,15 @@ static bool pack_rgba(struct mp_ass_packer *p, struct sub_bitmaps *res) // Assume mp_get_sub_bb_list() never splits sub bitmaps // So we don't clip/adjust the size of the sub bitmap - if (s->x > bb.x1 || s->x + s->w < bb.x0 || - s->y > bb.y1 || s->y + s->h < bb.y0) + if (s->x >= b->x + b->w || s->x + s->w <= b->x || + s->y >= b->y + b->h || s->y + s->h <= b->y) continue; draw_ass_rgba(s->bitmap, s->w, s->h, s->stride, b->bitmap, b->stride, - s->x - bb.x0, s->y - bb.y0, + s->x - b->x, s->y - b->y, s->libass.color); + fill_padding_4(b->bitmap, b->w, b->h, b->stride, padding); } } diff --git a/sub/img_convert.c b/sub/img_convert.c index 3c18e1713a..9fe4bd24b4 100644 --- a/sub/img_convert.c +++ b/sub/img_convert.c @@ -100,13 +100,16 @@ static void remove_intersecting_rcs(struct mp_rect *list, int *count) // NOTE: some callers assume that sub bitmaps are never split or partially // covered by returned rectangles. int mp_get_sub_bb_list(struct sub_bitmaps *sbs, struct mp_rect *out_rc_list, - int rc_list_count) + int rc_list_count, int padding) { int M = MERGE_RC_PIXELS; int num_rc = 0; for (int n = 0; n < sbs->num_parts; n++) { struct sub_bitmap *sb = &sbs->parts[n]; - struct mp_rect bb = {sb->x, sb->y, sb->x + sb->dw, sb->y + sb->dh}; + struct mp_rect bb = {sb->x - padding, + sb->y - padding, + sb->x + sb->dw + padding, + sb->y + sb->dh + padding}; bool intersects = false; for (int r = 0; r < num_rc; r++) { struct mp_rect *rc = &out_rc_list[r]; diff --git a/sub/img_convert.h b/sub/img_convert.h index e03c155f46..c299908797 100644 --- a/sub/img_convert.h +++ b/sub/img_convert.h @@ -18,6 +18,6 @@ bool mp_sub_bitmaps_bb(struct sub_bitmaps *imgs, struct mp_rect *out_bb); #define MP_SUB_BB_LIST_MAX 15 int mp_get_sub_bb_list(struct sub_bitmaps *sbs, struct mp_rect *out_rc_list, - int rc_list_count); + int rc_list_count, int padding); #endif