mirror of https://github.com/mpv-player/mpv
Better collision detection algorithm. The idea is to keep a subtitle in place
when a lower placed one disappears, thus improving readability. As a side effect, layers are supported now. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@19644 b3059339-0415-0410-9bf9-f77b7e298cf2
This commit is contained in:
parent
549b7f03c8
commit
fc6751b73a
|
@ -109,6 +109,8 @@ void ass_free_event(ass_track_t* track, int eid) {
|
|||
free(event->Effect);
|
||||
if (event->Text)
|
||||
free(event->Text);
|
||||
if (event->render_priv)
|
||||
free(event->render_priv);
|
||||
}
|
||||
|
||||
void ass_free_style(ass_track_t* track, int sid) {
|
||||
|
|
25
libass/ass.h
25
libass/ass.h
|
@ -51,36 +51,11 @@ void ass_done(ass_instance_t* priv);
|
|||
*/
|
||||
void ass_configure(ass_instance_t* priv, const ass_settings_t* config);
|
||||
|
||||
/**
|
||||
* \brief start rendering a frame
|
||||
* \param priv library
|
||||
* \param track subtitle track
|
||||
* \param now video timestamp in milliseconds
|
||||
*/
|
||||
int ass_start_frame(ass_instance_t *priv, ass_track_t* track, long long now);
|
||||
|
||||
/**
|
||||
* \brief render a single event
|
||||
* uses library, track and timestamp from the previous call to ass_start_frame
|
||||
*/
|
||||
int ass_render_event(ass_event_t* event);
|
||||
|
||||
/**
|
||||
* \brief done rendering frame, give out the results
|
||||
* \return a list of images for blending
|
||||
*/
|
||||
ass_image_t* ass_end_frame(void); // returns linked list of images to render
|
||||
|
||||
/**
|
||||
* \brief render a frame, producing a list of ass_image_t
|
||||
* \param priv library
|
||||
* \param track subtitle track
|
||||
* \param now video timestamp in milliseconds
|
||||
* This function is equivalent to
|
||||
* ass_start_frame()
|
||||
* for events: start <= now < end:
|
||||
* ass_render_event()
|
||||
* ass_end_frame()
|
||||
*/
|
||||
ass_image_t* ass_render_frame(ass_instance_t *priv, ass_track_t* track, long long now);
|
||||
|
||||
|
|
|
@ -30,14 +30,15 @@ extern int font_fontconfig;
|
|||
static int font_fontconfig = 0;
|
||||
#endif
|
||||
|
||||
static int last_render_id = 0;
|
||||
|
||||
struct ass_instance_s {
|
||||
FT_Library library;
|
||||
fc_instance_t* fontconfig_priv;
|
||||
ass_settings_t settings;
|
||||
int render_id;
|
||||
|
||||
ass_image_t* images_root; // rendering result is stored here
|
||||
int n_images;
|
||||
int max_images;
|
||||
};
|
||||
|
||||
int no_more_font_messages = 0; // don't print font warnings
|
||||
|
@ -128,8 +129,6 @@ typedef struct frame_context_s {
|
|||
int orig_height; // frame height ( = screen height - margins )
|
||||
int orig_width; // frame width ( = screen width - margins )
|
||||
ass_track_t* track;
|
||||
int add_bottom_margin; // additional margin, used to shift subtitles in case of collision
|
||||
int add_top_margin;
|
||||
long long time; // frame's timestamp, ms
|
||||
double font_scale_x; // x scale applied to all glyphs to preserve text aspect ratio
|
||||
} frame_context_t;
|
||||
|
@ -140,6 +139,20 @@ static text_info_t text_info;
|
|||
static render_context_t render_context;
|
||||
static frame_context_t frame_context;
|
||||
|
||||
// a rendered event
|
||||
typedef struct event_images_s {
|
||||
ass_image_t* imgs;
|
||||
int top, height;
|
||||
int detect_collisions;
|
||||
int shift_direction;
|
||||
ass_event_t* event;
|
||||
} event_images_t;
|
||||
|
||||
struct render_priv_s {
|
||||
int top, height;
|
||||
int render_id;
|
||||
};
|
||||
|
||||
static void ass_lazy_track_init(void)
|
||||
{
|
||||
ass_track_t* track = frame_context.track;
|
||||
|
@ -231,22 +244,12 @@ void ass_done(ass_instance_t* priv)
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Create a new ass_image_t and append it to images_root
|
||||
* \brief Create a new ass_image_t
|
||||
* Parameters are the same as ass_image_t fields.
|
||||
*/
|
||||
static void my_draw_bitmap(unsigned char* bitmap, int bitmap_w, int bitmap_h, int stride, int dst_x, int dst_y, uint32_t color)
|
||||
static ass_image_t* my_draw_bitmap(unsigned char* bitmap, int bitmap_w, int bitmap_h, int stride, int dst_x, int dst_y, uint32_t color)
|
||||
{
|
||||
ass_instance_t* priv = ass_instance;
|
||||
ass_image_t* img;
|
||||
|
||||
assert(priv->n_images <= priv->max_images);
|
||||
if (priv->n_images == priv->max_images) {
|
||||
if (!priv->max_images) priv->max_images = 100;
|
||||
else priv->max_images *= 2;
|
||||
priv->images_root = (ass_image_t*)realloc(priv->images_root, priv->max_images * sizeof(ass_image_t));
|
||||
}
|
||||
assert(priv->images_root);
|
||||
img = priv->images_root + priv->n_images;
|
||||
ass_image_t* img = calloc(1, sizeof(ass_image_t));
|
||||
|
||||
img->w = bitmap_w;
|
||||
img->h = bitmap_h;
|
||||
|
@ -256,7 +259,7 @@ static void my_draw_bitmap(unsigned char* bitmap, int bitmap_w, int bitmap_h, in
|
|||
img->dst_x = dst_x;
|
||||
img->dst_y = dst_y;
|
||||
|
||||
priv->n_images++;
|
||||
return img;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,9 +270,11 @@ static void my_draw_bitmap(unsigned char* bitmap, int bitmap_w, int bitmap_h, in
|
|||
* \param color first color, RGBA
|
||||
* \param color2 second color, RGBA
|
||||
* \param brk x coordinate relative to glyph origin, color is used to the left of brk, color2 - to the right
|
||||
* \param tail pointer to the last image's next field, head of the generated list should be stored here
|
||||
* \return pointer to the new list tail
|
||||
* Performs clipping. Uses my_draw_bitmap for actual bitmap convertion.
|
||||
*/
|
||||
static int render_glyph(FT_BitmapGlyph bit, int dst_x, int dst_y, uint32_t color, uint32_t color2, int brk)
|
||||
static ass_image_t** render_glyph(FT_BitmapGlyph bit, int dst_x, int dst_y, uint32_t color, uint32_t color2, int brk, ass_image_t** tail)
|
||||
{
|
||||
// brk is relative to dst_x
|
||||
// color = color left of brk
|
||||
|
@ -278,6 +283,7 @@ static int render_glyph(FT_BitmapGlyph bit, int dst_x, int dst_y, uint32_t color
|
|||
int clip_x0, clip_y0, clip_x1, clip_y1;
|
||||
int tmp;
|
||||
FT_Bitmap* bitmap;
|
||||
ass_image_t* img;
|
||||
|
||||
bitmap = &(bit->bitmap);
|
||||
dst_x += bit->left;
|
||||
|
@ -286,7 +292,7 @@ static int render_glyph(FT_BitmapGlyph bit, int dst_x, int dst_y, uint32_t color
|
|||
|
||||
if (bitmap->pixel_mode != FT_PIXEL_MODE_GRAY) {
|
||||
mp_msg(MSGT_GLOBAL, MSGL_WARN, "Unsupported pixel mode: %d\n", (int)(bitmap->pixel_mode));
|
||||
return 1;
|
||||
return tail;
|
||||
}
|
||||
|
||||
// clipping
|
||||
|
@ -321,29 +327,32 @@ static int render_glyph(FT_BitmapGlyph bit, int dst_x, int dst_y, uint32_t color
|
|||
}
|
||||
|
||||
if ((b_y0 >= b_y1) || (b_x0 >= b_x1))
|
||||
return 0;
|
||||
return tail;
|
||||
|
||||
if (brk > b_x0) { // draw left part
|
||||
if (brk > b_x1) brk = b_x1;
|
||||
my_draw_bitmap(bitmap->buffer + bitmap->pitch * b_y0 + b_x0,
|
||||
img = my_draw_bitmap(bitmap->buffer + bitmap->pitch * b_y0 + b_x0,
|
||||
brk - b_x0, b_y1 - b_y0, bitmap->pitch,
|
||||
dst_x + b_x0, dst_y + b_y0, color);
|
||||
*tail = img;
|
||||
tail = &img->next;
|
||||
}
|
||||
if (brk < b_x1) { // draw right part
|
||||
if (brk < b_x0) brk = b_x0;
|
||||
my_draw_bitmap(bitmap->buffer + bitmap->pitch * b_y0 + brk,
|
||||
img = my_draw_bitmap(bitmap->buffer + bitmap->pitch * b_y0 + brk,
|
||||
b_x1 - brk, b_y1 - b_y0, bitmap->pitch,
|
||||
dst_x + brk, dst_y + b_y0, color2);
|
||||
|
||||
*tail = img;
|
||||
tail = &img->next;
|
||||
}
|
||||
return 0;
|
||||
return tail;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Render text_info_t struct into ass_images_t list
|
||||
* Rasterize glyphs and put them in glyph cache.
|
||||
*/
|
||||
static int render_text(text_info_t* text_info, int dst_x, int dst_y)
|
||||
static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y)
|
||||
{
|
||||
int pen_x, pen_y;
|
||||
int error, error2;
|
||||
|
@ -351,6 +360,8 @@ static int render_text(text_info_t* text_info, int dst_x, int dst_y)
|
|||
FT_Glyph image;
|
||||
FT_BitmapGlyph bit;
|
||||
glyph_hash_val_t hash_val;
|
||||
ass_image_t* head;
|
||||
ass_image_t** tail = &head;
|
||||
|
||||
for (i = 0; i < text_info->length; ++i) {
|
||||
if (text_info->glyphs[i].bitmap != 1) {
|
||||
|
@ -391,7 +402,7 @@ static int render_text(text_info_t* text_info, int dst_x, int dst_y)
|
|||
if ((info->effect_type == EF_KARAOKE_KO) && (info->effect_timing <= info->bbox.xMax)) {
|
||||
// do nothing
|
||||
} else
|
||||
render_glyph(bit, pen_x, pen_y, info->c3, 0, 1000000);
|
||||
tail = render_glyph(bit, pen_x, pen_y, info->c3, 0, 1000000, tail);
|
||||
}
|
||||
for (i = 0; i < text_info->length; ++i) {
|
||||
glyph_info_t* info = text_info->glyphs + i;
|
||||
|
@ -405,16 +416,17 @@ static int render_text(text_info_t* text_info, int dst_x, int dst_y)
|
|||
|
||||
if ((info->effect_type == EF_KARAOKE) || (info->effect_type == EF_KARAOKE_KO)) {
|
||||
if (info->effect_timing > info->bbox.xMax)
|
||||
render_glyph(bit, pen_x, pen_y, info->c1, 0, 1000000);
|
||||
tail = render_glyph(bit, pen_x, pen_y, info->c1, 0, 1000000, tail);
|
||||
else
|
||||
render_glyph(bit, pen_x, pen_y, info->c2, 0, 1000000);
|
||||
tail = render_glyph(bit, pen_x, pen_y, info->c2, 0, 1000000, tail);
|
||||
} else if (info->effect_type == EF_KARAOKE_KF) {
|
||||
render_glyph(bit, pen_x, pen_y, info->c1, info->c2, info->effect_timing);
|
||||
tail = render_glyph(bit, pen_x, pen_y, info->c1, info->c2, info->effect_timing, tail);
|
||||
} else
|
||||
render_glyph(bit, pen_x, pen_y, info->c1, 0, 1000000);
|
||||
tail = render_glyph(bit, pen_x, pen_y, info->c1, 0, 1000000, tail);
|
||||
}
|
||||
|
||||
return 0;
|
||||
*tail = 0;
|
||||
return head;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1092,6 +1104,7 @@ static void apply_transition_effects(ass_event_t* event)
|
|||
render_context.clip_y0 = y0;
|
||||
render_context.clip_y1 = y1;
|
||||
render_context.evt_type = EVENT_VSCROLL;
|
||||
render_context.detect_collisions = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1482,7 +1495,7 @@ static int get_face_descender(FT_Face face)
|
|||
* \param event event to render
|
||||
* Process event, appending resulting ass_image_t's to images_root.
|
||||
*/
|
||||
int ass_render_event(ass_event_t* event)
|
||||
static int ass_render_event(ass_event_t* event, event_images_t* event_images)
|
||||
{
|
||||
char* p;
|
||||
FT_UInt glyph_index;
|
||||
|
@ -1514,7 +1527,7 @@ int ass_render_event(ass_event_t* event)
|
|||
p = event->Text;
|
||||
if (!p) {
|
||||
mp_msg(MSGT_GLOBAL, MSGL_WARN, "Empty event!\n");
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Event parsing.
|
||||
|
@ -1528,7 +1541,7 @@ int ass_render_event(ass_event_t* event)
|
|||
// face could have been changed in get_next_char
|
||||
if (!render_context.face) {
|
||||
free_render_context();
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (code == 0)
|
||||
|
@ -1605,7 +1618,7 @@ int ass_render_event(ass_event_t* event)
|
|||
if (text_info.length == 0) {
|
||||
// no valid symbols in the event; this can be smth like {comment}
|
||||
free_render_context();
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// depends on glyph x coordinates being monotonous, so it should be done before line wrap
|
||||
|
@ -1677,10 +1690,6 @@ int ass_render_event(ass_event_t* event)
|
|||
render_context.evt_type == EVENT_HSCROLL) {
|
||||
if (valign == VALIGN_TOP) { // toptitle
|
||||
device_y = y2scr_top(MarginV) + (text_info.lines[0].asc >> 6);
|
||||
if (render_context.detect_collisions) {
|
||||
device_y += frame_context.add_top_margin;
|
||||
frame_context.add_top_margin += (text_info.height >> 6);
|
||||
}
|
||||
} else if (valign == VALIGN_CENTER) { // midtitle
|
||||
int scr_y = y2scr(frame_context.track->PlayResY / 2);
|
||||
device_y = scr_y - (bbox.yMax - bbox.yMin) / 2;
|
||||
|
@ -1692,10 +1701,6 @@ int ass_render_event(ass_event_t* event)
|
|||
device_y = scr_y;
|
||||
device_y -= (text_info.height >> 6);
|
||||
device_y += (text_info.lines[0].asc >> 6);
|
||||
if (render_context.detect_collisions) {
|
||||
device_y -= frame_context.add_bottom_margin;
|
||||
frame_context.add_bottom_margin += (text_info.height >> 6);
|
||||
}
|
||||
}
|
||||
} else if (render_context.evt_type == EVENT_VSCROLL) {
|
||||
if (render_context.scroll_direction == SCROLL_TB)
|
||||
|
@ -1813,8 +1818,12 @@ int ass_render_event(ass_event_t* event)
|
|||
}
|
||||
}
|
||||
|
||||
// render
|
||||
render_text(&text_info, device_x, device_y);
|
||||
event_images->top = device_y - (text_info.lines[0].asc >> 6);
|
||||
event_images->height = text_info.height >> 6;
|
||||
event_images->detect_collisions = render_context.detect_collisions;
|
||||
event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1;
|
||||
event_images->event = event;
|
||||
event_images->imgs = render_text(&text_info, device_x, device_y);
|
||||
|
||||
free_render_context();
|
||||
|
||||
|
@ -1827,6 +1836,8 @@ void ass_configure(ass_instance_t* priv, const ass_settings_t* config)
|
|||
mp_msg(MSGT_GLOBAL, MSGL_V, "ass_configure: %d x %d; margins: l: %d, r: %d, t: %d, b: %d \n",
|
||||
config->frame_width, config->frame_height,
|
||||
config->left_margin, config->right_margin, config->top_margin, config->bottom_margin);
|
||||
|
||||
priv->render_id = ++last_render_id;
|
||||
memcpy(&priv->settings, config, sizeof(ass_settings_t));
|
||||
ass_glyph_cache_reset();
|
||||
}
|
||||
|
@ -1835,8 +1846,10 @@ void ass_configure(ass_instance_t* priv, const ass_settings_t* config)
|
|||
/**
|
||||
* \brief Start a new frame
|
||||
*/
|
||||
int ass_start_frame(ass_instance_t *priv, ass_track_t* track, long long now)
|
||||
static int ass_start_frame(ass_instance_t *priv, ass_track_t* track, long long now)
|
||||
{
|
||||
ass_image_t* img;
|
||||
|
||||
ass_instance = priv;
|
||||
global_settings = &priv->settings;
|
||||
|
||||
|
@ -1849,8 +1862,6 @@ int ass_start_frame(ass_instance_t *priv, ass_track_t* track, long long now)
|
|||
frame_context.orig_width = global_settings->frame_width - global_settings->left_margin - global_settings->right_margin;
|
||||
frame_context.orig_height = global_settings->frame_height - global_settings->top_margin - global_settings->bottom_margin;
|
||||
frame_context.track = track;
|
||||
frame_context.add_bottom_margin = 0;
|
||||
frame_context.add_top_margin = 0;
|
||||
frame_context.time = now;
|
||||
|
||||
ass_lazy_track_init();
|
||||
|
@ -1860,28 +1871,175 @@ int ass_start_frame(ass_instance_t *priv, ass_track_t* track, long long now)
|
|||
else
|
||||
frame_context.font_scale_x = ((double)(frame_context.orig_width * track->PlayResY)) / (frame_context.orig_height * track->PlayResX);
|
||||
|
||||
priv->n_images = 0;
|
||||
img = priv->images_root;
|
||||
while (img) {
|
||||
ass_image_t* next = img->next;
|
||||
free(img);
|
||||
img = next;
|
||||
}
|
||||
priv->images_root = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief End a frame, give out rendering results
|
||||
* \return list of ass_image_t
|
||||
*/
|
||||
ass_image_t* ass_end_frame(void)
|
||||
static ass_image_t** find_list_tail(ass_image_t** phead)
|
||||
{
|
||||
ass_image_t* img = *phead;
|
||||
if (!img)
|
||||
return phead;
|
||||
while (img->next)
|
||||
img = img->next;
|
||||
return &img->next;
|
||||
}
|
||||
|
||||
static int cmp_event_layer(const void* p1, const void* p2)
|
||||
{
|
||||
ass_event_t* e1 = ((event_images_t*)p1)->event;
|
||||
ass_event_t* e2 = ((event_images_t*)p2)->event;
|
||||
if (e1->Layer < e2->Layer)
|
||||
return -1;
|
||||
if (e1->Layer > e2->Layer)
|
||||
return 1;
|
||||
if (e1->Start < e2->Start)
|
||||
return -1;
|
||||
if (e1->Start > e2->Start)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_EVENTS 100
|
||||
|
||||
static render_priv_t* get_render_priv(ass_event_t* event)
|
||||
{
|
||||
if (!event->render_priv)
|
||||
event->render_priv = calloc(1, sizeof(render_priv_t));
|
||||
// FIXME: check render_id
|
||||
if (ass_instance->render_id != event->render_priv->render_id) {
|
||||
memset(event->render_priv, 0, sizeof(render_priv_t));
|
||||
event->render_priv->render_id = ass_instance->render_id;
|
||||
}
|
||||
return event->render_priv;
|
||||
}
|
||||
|
||||
typedef struct segment_s {
|
||||
int a, b; // top and height
|
||||
} segment_t;
|
||||
|
||||
static int overlap(segment_t* s1, segment_t* s2)
|
||||
{
|
||||
if (s1->a >= s2->b || s2->a >= s1->b)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cmp_segment(const void* p1, const void* p2)
|
||||
{
|
||||
return ((segment_t*)p1)->a - ((segment_t*)p1)->b;
|
||||
}
|
||||
|
||||
static void shift_event(event_images_t* ei, int shift)
|
||||
{
|
||||
ass_image_t* cur = ei->imgs;
|
||||
while (cur) {
|
||||
cur->dst_y += shift;
|
||||
cur = cur->next;
|
||||
}
|
||||
ei->top += shift;
|
||||
}
|
||||
|
||||
// dir: 1 - move down
|
||||
// -1 - move up
|
||||
static int fit_segment(segment_t* s, segment_t* fixed, int* cnt, int dir)
|
||||
{
|
||||
ass_instance_t* priv = ass_instance;
|
||||
if (priv->n_images) {
|
||||
int i;
|
||||
for (i = 0; i < priv->n_images - 1; ++i)
|
||||
priv->images_root[i].next = priv->images_root + i + 1;
|
||||
priv->images_root[priv->n_images - 1].next = 0;
|
||||
return priv->images_root;
|
||||
} else {
|
||||
int shift;
|
||||
|
||||
if (*cnt == 0) {
|
||||
*cnt = 1;
|
||||
fixed[0].a = s->a;
|
||||
fixed[0].b = s->b;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dir == 1) { // move down
|
||||
if (s->b <= fixed[0].a) // all ok
|
||||
return 0;
|
||||
for (i = 0; i < *cnt; ++i) {
|
||||
shift = fixed[i].b - s->a;
|
||||
if (i == *cnt - 1 || fixed[i+1].a >= shift + s->b) { // here is a good place
|
||||
fixed[i].b += s->b - s->a;
|
||||
return shift;
|
||||
}
|
||||
}
|
||||
} else { // dir == -1, move up
|
||||
if (s->a >= fixed[*cnt-1].b) // all ok
|
||||
return 0;
|
||||
for (i = *cnt-1; i >= 0; --i) {
|
||||
shift = fixed[i].a - s->b;
|
||||
if (i == 0 || fixed[i-1].b <= shift + s->a) { // here is a good place
|
||||
fixed[i].a -= s->b - s->a;
|
||||
return shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(0); // unreachable
|
||||
}
|
||||
|
||||
static void fix_collisions(event_images_t* imgs, int cnt)
|
||||
{
|
||||
segment_t used[MAX_EVENTS];
|
||||
int cnt_used = 0;
|
||||
int i, j;
|
||||
|
||||
// fill used[] with fixed events
|
||||
for (i = 0; i < cnt; ++i) {
|
||||
render_priv_t* priv;
|
||||
if (!imgs[i].detect_collisions) break;
|
||||
priv = get_render_priv(imgs[i].event);
|
||||
if (priv->height > 0) { // it's a fixed event
|
||||
segment_t s;
|
||||
s.a = priv->top;
|
||||
s.b = priv->top + priv->height;
|
||||
if (priv->height != imgs[i].height) { // no, it's not
|
||||
mp_msg(MSGT_GLOBAL, MSGL_WARN, "Achtung! Event height has changed! \n");
|
||||
priv->top = 0;
|
||||
priv->height = 0;
|
||||
}
|
||||
for (j = 0; j < cnt_used; ++j)
|
||||
if (overlap(&s, used + j)) { // no, it's not
|
||||
priv->top = 0;
|
||||
priv->height = 0;
|
||||
}
|
||||
if (priv->height > 0) { // still a fixed event
|
||||
used[cnt_used].a = priv->top;
|
||||
used[cnt_used].b = priv->top + priv->height;
|
||||
cnt_used ++;
|
||||
shift_event(imgs + i, priv->top - imgs[i].top);
|
||||
}
|
||||
}
|
||||
}
|
||||
qsort(used, cnt_used, sizeof(segment_t), cmp_segment);
|
||||
|
||||
// try to fit other events in free spaces
|
||||
for (i = 0; i < cnt; ++i) {
|
||||
render_priv_t* priv;
|
||||
if (!imgs[i].detect_collisions) break;
|
||||
priv = get_render_priv(imgs[i].event);
|
||||
if (priv->height == 0) { // not a fixed event
|
||||
int shift;
|
||||
segment_t s;
|
||||
s.a = imgs[i].top;
|
||||
s.b = imgs[i].top + imgs[i].height;
|
||||
shift = fit_segment(&s, used, &cnt_used, imgs[i].shift_direction);
|
||||
if (shift) shift_event(imgs + i, shift);
|
||||
// make it fixed
|
||||
priv->top = imgs[i].top;
|
||||
priv->height = imgs[i].height;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief render a frame
|
||||
* \param priv library handle
|
||||
|
@ -1890,17 +2048,54 @@ ass_image_t* ass_end_frame(void)
|
|||
*/
|
||||
ass_image_t* ass_render_frame(ass_instance_t *priv, ass_track_t* track, long long now)
|
||||
{
|
||||
int i, rc;
|
||||
int i, cnt, rc;
|
||||
event_images_t eimg[MAX_EVENTS];
|
||||
event_images_t* last;
|
||||
ass_image_t* head = 0;
|
||||
ass_image_t** tail = &head;
|
||||
|
||||
// init frame
|
||||
rc = ass_start_frame(priv, track, now);
|
||||
if (rc != 0) // some error
|
||||
if (rc != 0)
|
||||
return 0;
|
||||
|
||||
// render events separately
|
||||
cnt = 0;
|
||||
for (i = 0; i < track->n_events; ++i) {
|
||||
ass_event_t* event = track->events + i;
|
||||
if ( (event->Start <= now) && (now < (event->Start + event->Duration)) ) {
|
||||
ass_render_event(event);
|
||||
if (cnt < MAX_EVENTS) {
|
||||
rc = ass_render_event(event, eimg + cnt);
|
||||
if (!rc) ++cnt;
|
||||
} else {
|
||||
mp_msg(MSGT_GLOBAL, MSGL_WARN, "Too many simultaneous events \n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ass_end_frame();
|
||||
}
|
||||
|
||||
// sort by layer
|
||||
qsort(eimg, cnt, sizeof(event_images_t), cmp_event_layer);
|
||||
|
||||
// call fix_collisions for each group of events with the same layer
|
||||
last = eimg;
|
||||
for (i = 1; i < cnt; ++i)
|
||||
if (last->event->Layer != eimg[i].event->Layer) {
|
||||
fix_collisions(last, eimg + i - last);
|
||||
last = eimg + i;
|
||||
}
|
||||
if (cnt > 0)
|
||||
fix_collisions(last, eimg + cnt - last);
|
||||
|
||||
// concat lists
|
||||
head = cnt ? eimg[0].imgs : 0;
|
||||
tail = find_list_tail(&head);
|
||||
for (i = 1; i < cnt; ++i) {
|
||||
*tail = eimg[i].imgs;
|
||||
tail = find_list_tail(&eimg[i].imgs);
|
||||
}
|
||||
|
||||
ass_instance->images_root = head;
|
||||
return ass_instance->images_root;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@ typedef struct ass_style_s {
|
|||
int Encoding;
|
||||
} ass_style_t;
|
||||
|
||||
typedef struct render_priv_s render_priv_t;
|
||||
|
||||
/// ass_event_t corresponds to a single Dialogue line
|
||||
/// Text is stored as-is, style overrides will be parsed later
|
||||
typedef struct ass_event_s {
|
||||
|
@ -51,6 +53,8 @@ typedef struct ass_event_s {
|
|||
int MarginV;
|
||||
char* Effect;
|
||||
char* Text;
|
||||
|
||||
render_priv_t* render_priv;
|
||||
} ass_event_t;
|
||||
|
||||
typedef struct parser_priv_s parser_priv_t;
|
||||
|
|
Loading…
Reference in New Issue