Skip to content

Commit

Permalink
rendersub: subdivide non overlapping ssa rects to avoid blending larg…
Browse files Browse the repository at this point in the history
…e empty areas
  • Loading branch information
galad87 committed Oct 24, 2024
1 parent edaa40e commit 2f2ac66
Showing 1 changed file with 166 additions and 47 deletions.
213 changes: 166 additions & 47 deletions libhb/rendersub.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,118 @@

#define ABS(a) ((a) > 0 ? (a) : (-(a)))

typedef struct hb_rect_s
{
int x;
int y;
int width;
int height;
} hb_rect_t;

typedef struct hb_rect_list_s
{
hb_rect_t *rects;
int count;
int size;
} hb_rect_list_t;

static void hb_rect_list_resize(hb_rect_list_t *list, int size)
{
hb_rect_t *rects = realloc(list->rects, size * sizeof(hb_rect_t));
list->rects = rects;
list->size = size;
}

static void hb_rect_list_merge_overlapping_rects(hb_rect_list_t *list)
{
if (list->count < 2)
{
return;
}

// Merge nearby rects too
const int offset = 8;
hb_rect_t empty = {0};

for (int i = 0; i < list->count - 1; i++)
{
hb_rect_t a = list->rects[i];
for (int j = i + 1; j < list->count; j++)
{
hb_rect_t b = list->rects[j];
if (a.x - offset <= b.x + b.width && a.x + a.width + offset >= b.x &&
a.y - offset <= b.y + b.height && a.y + a.height + offset >= b.y)
{
int width = FFMAX(a.width + a.x, b.width + b.x);
int height = FFMAX(a.height + a.y, b.height + b.y);
a.x = FFMIN(a.x, b.x);
a.y = FFMIN(a.y, b.y);
a.width = width - a.x;
a.height = height - a.y;
list->rects[i] = a;
list->rects[j] = empty;
}
}
}
}

static void hb_rect_list_compact(hb_rect_list_t *list)
{
int j = 0, i = 0;
while (i < list->count)
{
hb_rect_t a = list->rects[i];
if (a.x == 0 && a.y == 0 && a.width == 0 && a.height == 0)
{
i++;
}
else
{
list->rects[j] = list->rects[i];
i++;
j++;
}
}
list->count = j;
}

static void hb_rect_list_append(hb_rect_list_t *list, int x, int y, int width, int height)
{
if (width == 0 || height == 0)
{
return;
}

if (list->size < list->count + 1)
{
hb_rect_list_resize(list, list->count + 10);
}

list->rects[list->count].x = x;
list->rects[list->count].y = y;
list->rects[list->count].width = width;
list->rects[list->count].height = height;
list->count += 1;

hb_rect_list_merge_overlapping_rects(list);
hb_rect_list_compact(list);
}

static void hb_rect_list_clear(hb_rect_list_t *list)
{
memset(list->rects, 0, sizeof(hb_rect_t) * list->size);
list->count = 0;
}

static void hb_rect_list_close(hb_rect_list_t *list)
{
free(list->rects);
list->rects = NULL;
list->count = 0;
list->size = 0;
}


struct hb_filter_private_s
{
// Common
Expand All @@ -36,7 +148,8 @@ struct hb_filter_private_s
ASS_Renderer *renderer;
ASS_Track *ssa_track;
uint8_t script_initialized;
hb_buffer_t *last_render;
hb_buffer_list_t last_renders;
hb_rect_list_t last_rect_list;

// SRT
int line;
Expand Down Expand Up @@ -751,7 +864,9 @@ static hb_buffer_t * compose_subsample_ass(hb_filter_private_t *pv, const ASS_Im

while (frame)
{
if (frame->w && frame->h)
if (frame->w && frame->h &&
x <= frame->dst_x && x + width >= frame->dst_x + frame->w &&
y <= frame->dst_y && y + height >= frame->dst_y + frame->h)
{
const int yuv = hb_rgb2yuv_bt709(frame->color >> 8);

Expand Down Expand Up @@ -860,7 +975,7 @@ static hb_buffer_t * compose_subsample_ass(hb_filter_private_t *pv, const ASS_Im
return sub;
}

static hb_buffer_t * render_ssa_subs(hb_filter_private_t *pv, int64_t start)
static hb_buffer_list_t * render_ssa_subs(hb_filter_private_t *pv, int64_t start)
{
ASS_Image *frame_list;
int changed;
Expand All @@ -875,47 +990,51 @@ static hb_buffer_t * render_ssa_subs(hb_filter_private_t *pv, int64_t start)
// Re-use cached overlay, whenever possible
if (changed)
{
if (pv->last_render)
if (hb_buffer_list_count(&pv->last_renders))
{
hb_buffer_close(&pv->last_render);
hb_buffer_list_close(&pv->last_renders);
hb_rect_list_clear(&pv->last_rect_list);
}

unsigned int x1, y1, x2, y2;
x1 = y1 = (unsigned)(-1);
x2 = y2 = 0;

// Find overlay size and pos (faster than composing at the video dimensions)
// Find overlay size and pos of non overlapped rects
// (faster than composing at the video dimensions)
for (ASS_Image *frame = frame_list; frame; frame = frame->next)
{
if (frame->w && frame->h)
{
x2 = FFMAX(x2, frame->dst_x + frame->w);
y2 = FFMAX(y2, frame->dst_y + frame->h);
x1 = FFMIN(x1, frame->dst_x);
y1 = FFMIN(y1, frame->dst_y);
}
hb_rect_list_append(&pv->last_rect_list,
frame->dst_x, frame->dst_y,
frame->w, frame->h);
}

// Don't process empty framelist
if (x2 > 0)
if (pv->last_rect_list.count)
{
// Overlay must be aligned to the chroma plane, pad as needed.
x1 -= (x1 + pv->crop[2]) & ((1 << pv->wshift) - 1);
y1 -= (y1 + pv->crop[0]) & ((1 << pv->hshift) - 1);
for (int i = 0; i < pv->last_rect_list.count; i++)
{
hb_rect_t rect = pv->last_rect_list.rects[i];

pv->last_render = compose_subsample_ass(pv, frame_list, x2 - x1, y2 - y1, x1, y1);
// Overlay must be aligned to the chroma plane, pad as needed.
int x2 = rect.x + rect.width;
int y2 = rect.y + rect.height;
rect.x -= (rect.x + pv->crop[2]) & ((1 << pv->wshift) - 1);
rect.y -= (rect.y + pv->crop[0]) & ((1 << pv->hshift) - 1);
rect.width = x2 - rect.x;
rect.height = y2 - rect.y;

if (pv->last_render)
{
pv->last_render->f.x += pv->crop[2];
pv->last_render->f.y += pv->crop[0];
hb_buffer_t *sub = compose_subsample_ass(pv, frame_list, rect.width, rect.height, rect.x, rect.y);

if (sub)
{
sub->f.x += pv->crop[2];
sub->f.y += pv->crop[0];
hb_buffer_list_append(&pv->last_renders, sub);
}
}
}
}

if (pv->last_render)
if (hb_buffer_list_count(&pv->last_renders))
{
return pv->last_render;
return &pv->last_renders;
}
else
{
Expand Down Expand Up @@ -1004,11 +1123,7 @@ static int ssa_post_init(hb_filter_object_t *filter, hb_job_t *job)
ass_set_frame_size(pv->renderer, width, height);
ass_set_storage_size(pv->renderer, width, height);

if (pv->last_render)
{
hb_buffer_close(&pv->last_render);
pv->last_render = NULL;
}
hb_buffer_list_close(&pv->last_renders);

return 0;
}
Expand All @@ -1034,10 +1149,8 @@ static void ssa_close(hb_filter_object_t *filter)
{
ass_library_done(pv->ssa);
}
if (pv->last_render)
{
hb_buffer_close(&pv->last_render);
}
hb_buffer_list_close(&pv->last_renders);
hb_rect_list_close(&pv->last_rect_list);

free(pv);
filter->private_data = NULL;
Expand All @@ -1051,7 +1164,7 @@ static int ssa_work(hb_filter_object_t *filter,
hb_buffer_t *in = *buf_in;
hb_buffer_t *out = in;
hb_buffer_t *sub;
hb_buffer_t *rendered_subs;
hb_buffer_list_t *bitmaps;

if (!pv->script_initialized)
{
Expand Down Expand Up @@ -1091,9 +1204,9 @@ static int ssa_work(hb_filter_object_t *filter,
hb_buffer_close(&sub);
}

rendered_subs = render_ssa_subs(pv, in->s.start);
bitmaps = render_ssa_subs(pv, in->s.start);

if (rendered_subs && hb_buffer_is_writable(in) == 0)
if (bitmaps && hb_buffer_is_writable(in) == 0)
{
out = hb_buffer_dup(in);
}
Expand All @@ -1102,9 +1215,12 @@ static int ssa_work(hb_filter_object_t *filter,
*buf_in = NULL;
}

if (rendered_subs)
if (bitmaps)
{
apply_sub(pv, out, rendered_subs);
for (hb_buffer_t *bitmap = hb_buffer_list_head(bitmaps); bitmap; bitmap = bitmap->next)
{
apply_sub(pv, out, bitmap);
}
}

*buf_out = out;
Expand Down Expand Up @@ -1156,7 +1272,7 @@ static int textsub_work(hb_filter_object_t *filter,
hb_buffer_t *in = *buf_in;
hb_buffer_t *out = in;
hb_buffer_t *sub;
hb_buffer_t *rendered_subs;
hb_buffer_list_t *bitmaps;

if (!pv->script_initialized)
{
Expand Down Expand Up @@ -1246,9 +1362,9 @@ static int textsub_work(hb_filter_object_t *filter,
process_sub(pv, pv->current_sub);
}

rendered_subs = render_ssa_subs(pv, in->s.start);
bitmaps = render_ssa_subs(pv, in->s.start);

if (rendered_subs && hb_buffer_is_writable(in) == 0)
if (bitmaps && hb_buffer_is_writable(in) == 0)
{
out = hb_buffer_dup(in);
}
Expand All @@ -1257,9 +1373,12 @@ static int textsub_work(hb_filter_object_t *filter,
*buf_in = NULL;
}

if (rendered_subs)
if (bitmaps)
{
apply_sub(pv, out, rendered_subs);
for (hb_buffer_t *bitmap = hb_buffer_list_head(bitmaps); bitmap; bitmap = bitmap->next)
{
apply_sub(pv, out, bitmap);
}
}

*buf_out = out;
Expand Down

0 comments on commit 2f2ac66

Please sign in to comment.