mirror of https://github.com/mpv-player/mpv
sub, vf_ass: allow rendering RGBA subs, replace old vf_ass rendering
Do this by replacing all the old vf_ass drawing code by draw_bmp.c. Change sub.c to always use osd_draw() for the other OSD drawing routines, and simplify the code a bit. spudec.c subtitles (i.e. DVD subs) are now considered subtitles, and are rendered by vf_ass, if that filter is inserted.
This commit is contained in:
parent
98f74335d5
commit
97c6425140
|
@ -38,25 +38,16 @@
|
|||
#include "sub/dec_sub.h"
|
||||
|
||||
#include "libvo/fastmemcpy.h"
|
||||
#include "libvo/csputils.h"
|
||||
|
||||
#include "m_option.h"
|
||||
#include "m_struct.h"
|
||||
|
||||
#include "sub/ass_mp.h"
|
||||
|
||||
#define _r(c) ((c)>>24)
|
||||
#define _g(c) (((c)>>16)&0xFF)
|
||||
#define _b(c) (((c)>>8)&0xFF)
|
||||
#define _a(c) ((c)&0xFF)
|
||||
#define rgba2y(c) ( (( 263*_r(c) + 516*_g(c) + 100*_b(c)) >> 10) + 16 )
|
||||
#define rgba2u(c) ( ((-152*_r(c) - 298*_g(c) + 450*_b(c)) >> 10) + 128 )
|
||||
#define rgba2v(c) ( (( 450*_r(c) - 376*_g(c) - 73*_b(c)) >> 10) + 128 )
|
||||
|
||||
|
||||
static const struct vf_priv_s {
|
||||
int outh, outw;
|
||||
|
||||
unsigned int outfmt;
|
||||
struct mp_csp_details csp;
|
||||
|
||||
// 1 = auto-added filter: insert only if chain does not support EOSD already
|
||||
// 0 = insert always
|
||||
|
@ -64,13 +55,9 @@ static const struct vf_priv_s {
|
|||
|
||||
struct osd_state *osd;
|
||||
struct mp_eosd_res dim;
|
||||
|
||||
unsigned char *planes[3];
|
||||
struct line_limits {
|
||||
uint16_t start;
|
||||
uint16_t end;
|
||||
} *line_limits;
|
||||
} vf_priv_dflt;
|
||||
} vf_priv_dflt = {
|
||||
.csp = MP_CSP_DETAILS_DEFAULTS,
|
||||
};
|
||||
|
||||
static int config(struct vf_instance *vf,
|
||||
int width, int height, int d_width, int d_height,
|
||||
|
@ -88,10 +75,6 @@ static int config(struct vf_instance *vf,
|
|||
d_height = d_height * vf->priv->outh / height;
|
||||
}
|
||||
|
||||
vf->priv->planes[1] = malloc(vf->priv->outw * vf->priv->outh);
|
||||
vf->priv->planes[2] = malloc(vf->priv->outw * vf->priv->outh);
|
||||
vf->priv->line_limits = malloc((vf->priv->outh + 1) / 2 * sizeof(*vf->priv->line_limits));
|
||||
|
||||
double dar = (double)d_width / d_height;
|
||||
double sar = (double)width / height;
|
||||
|
||||
|
@ -236,148 +219,22 @@ static int prepare_image(struct vf_instance *vf, mp_image_t *mpi)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void update_limits(struct vf_instance *vf, int starty, int endy,
|
||||
int startx, int endx)
|
||||
{
|
||||
starty >>= 1;
|
||||
endy = (endy + 1) >> 1;
|
||||
startx >>= 1;
|
||||
endx = (endx + 1) >> 1;
|
||||
for (int i = starty; i < endy; i++) {
|
||||
struct line_limits *ll = vf->priv->line_limits + i;
|
||||
if (startx < ll->start)
|
||||
ll->start = startx;
|
||||
if (endx > ll->end)
|
||||
ll->end = endx;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Copy specified rows from render_context.dmpi to render_context.planes, upsampling to 4:4:4
|
||||
*/
|
||||
static void copy_from_image(struct vf_instance *vf)
|
||||
{
|
||||
int pl;
|
||||
|
||||
for (pl = 1; pl < 3; ++pl) {
|
||||
int dst_stride = vf->priv->outw;
|
||||
int src_stride = vf->dmpi->stride[pl];
|
||||
|
||||
unsigned char *src = vf->dmpi->planes[pl];
|
||||
unsigned char *dst = vf->priv->planes[pl];
|
||||
for (int i = 0; i < (vf->priv->outh + 1) / 2; i++) {
|
||||
struct line_limits *ll = vf->priv->line_limits + i;
|
||||
unsigned char *dst_next = dst + dst_stride;
|
||||
for (int j = ll->start; j < ll->end; j++) {
|
||||
unsigned char val = src[j];
|
||||
dst[j << 1] = val;
|
||||
dst[(j << 1) + 1] = val;
|
||||
dst_next[j << 1] = val;
|
||||
dst_next[(j << 1) + 1] = val;
|
||||
}
|
||||
src += src_stride;
|
||||
dst = dst_next + dst_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Copy all previously copied rows back to render_context.dmpi
|
||||
*/
|
||||
static void copy_to_image(struct vf_instance *vf)
|
||||
{
|
||||
int pl;
|
||||
int i, j;
|
||||
for (pl = 1; pl < 3; ++pl) {
|
||||
int dst_stride = vf->dmpi->stride[pl];
|
||||
int src_stride = vf->priv->outw;
|
||||
|
||||
unsigned char *dst = vf->dmpi->planes[pl];
|
||||
unsigned char *src = vf->priv->planes[pl];
|
||||
unsigned char *src_next = vf->priv->planes[pl] + src_stride;
|
||||
for (i = 0; i < vf->priv->outh / 2; ++i) {
|
||||
for (j = vf->priv->line_limits[i].start; j < vf->priv->line_limits[i].end; j++) {
|
||||
unsigned val = 0;
|
||||
val += src[j << 1];
|
||||
val += src[(j << 1) + 1];
|
||||
val += src_next[j << 1];
|
||||
val += src_next[(j << 1) + 1];
|
||||
dst[j] = val >> 2;
|
||||
}
|
||||
dst += dst_stride;
|
||||
src = src_next + src_stride;
|
||||
src_next = src + src_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void my_draw_bitmap(struct vf_instance *vf, unsigned char *bitmap,
|
||||
int bitmap_w, int bitmap_h, int stride,
|
||||
int dst_x, int dst_y, unsigned color)
|
||||
{
|
||||
unsigned char y = rgba2y(color);
|
||||
unsigned char u = rgba2u(color);
|
||||
unsigned char v = rgba2v(color);
|
||||
unsigned char opacity = 255 - _a(color);
|
||||
unsigned char *src, *dsty, *dstu, *dstv;
|
||||
int i, j;
|
||||
mp_image_t *dmpi = vf->dmpi;
|
||||
|
||||
src = bitmap;
|
||||
dsty = dmpi->planes[0] + dst_x + dst_y * dmpi->stride[0];
|
||||
dstu = vf->priv->planes[1] + dst_x + dst_y * vf->priv->outw;
|
||||
dstv = vf->priv->planes[2] + dst_x + dst_y * vf->priv->outw;
|
||||
for (i = 0; i < bitmap_h; ++i) {
|
||||
for (j = 0; j < bitmap_w; ++j) {
|
||||
unsigned k = (src[j] * opacity + 255) >> 8;
|
||||
dsty[j] = (k * y + (255 - k) * dsty[j] + 255) >> 8;
|
||||
dstu[j] = (k * u + (255 - k) * dstu[j] + 255) >> 8;
|
||||
dstv[j] = (k * v + (255 - k) * dstv[j] + 255) >> 8;
|
||||
}
|
||||
src += stride;
|
||||
dsty += dmpi->stride[0];
|
||||
dstu += vf->priv->outw;
|
||||
dstv += vf->priv->outw;
|
||||
}
|
||||
}
|
||||
|
||||
static int render_frame(struct vf_instance *vf, mp_image_t *mpi,
|
||||
const ASS_Image *img)
|
||||
{
|
||||
if (img) {
|
||||
for (int i = 0; i < (vf->priv->outh + 1) / 2; i++)
|
||||
vf->priv->line_limits[i] = (struct line_limits){65535, 0};
|
||||
for (const ASS_Image *im = img; im; im = im->next)
|
||||
update_limits(vf, im->dst_y, im->dst_y + im->h,
|
||||
im->dst_x, im->dst_x + im->w);
|
||||
copy_from_image(vf);
|
||||
while (img) {
|
||||
my_draw_bitmap(vf, img->bitmap, img->w, img->h, img->stride,
|
||||
img->dst_x, img->dst_y, img->color);
|
||||
img = img->next;
|
||||
}
|
||||
copy_to_image(vf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int put_image(struct vf_instance *vf, mp_image_t *mpi, double pts)
|
||||
{
|
||||
struct vf_priv_s *priv = vf->priv;
|
||||
struct osd_state *osd = priv->osd;
|
||||
struct sub_bitmaps imgs = (struct sub_bitmaps) {0};
|
||||
|
||||
prepare_image(vf, mpi);
|
||||
|
||||
if (pts != MP_NOPTS_VALUE) {
|
||||
struct sub_render_params subparams = {
|
||||
.pts = pts,
|
||||
.dim = priv->dim,
|
||||
};
|
||||
bool formats[SUBBITMAP_COUNT] = {[SUBBITMAP_LIBASS] = true};
|
||||
osd_draw_sub(osd, &imgs, &subparams, formats);
|
||||
osd_draw_on_image(osd, &subparams, OSD_DRAW_SUB_FILTER, vf->dmpi,
|
||||
&priv->csp);
|
||||
}
|
||||
|
||||
prepare_image(vf, mpi);
|
||||
render_frame(vf, mpi, imgs.imgs);
|
||||
|
||||
return vf_next_put_image(vf, vf->dmpi, pts);
|
||||
}
|
||||
|
||||
|
@ -400,15 +257,17 @@ static int control(vf_instance_t *vf, int request, void *data)
|
|||
break;
|
||||
case VFCTRL_INIT_OSD:
|
||||
return CONTROL_TRUE;
|
||||
case VFCTRL_SET_YUV_COLORSPACE: {
|
||||
struct mp_csp_details colorspace = *(struct mp_csp_details *)data;
|
||||
vf->priv->csp = colorspace;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return vf_next_control(vf, request, data);
|
||||
}
|
||||
|
||||
static void uninit(struct vf_instance *vf)
|
||||
{
|
||||
free(vf->priv->planes[1]);
|
||||
free(vf->priv->planes[2]);
|
||||
free(vf->priv->line_limits);
|
||||
free(vf->priv);
|
||||
}
|
||||
|
||||
|
|
|
@ -504,7 +504,7 @@ static void draw_osd(struct vo *vo, struct osd_state *osd)
|
|||
},
|
||||
};
|
||||
|
||||
osd_draw_on_image(osd, vc->lastimg, &vc->colorspace, &subparams);
|
||||
osd_draw_on_image(osd, &subparams, 0, vc->lastimg, &vc->colorspace);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -453,7 +453,7 @@ static void draw_osd(struct vo *vo, struct osd_state *osd)
|
|||
},
|
||||
};
|
||||
|
||||
osd_draw_on_image(osd, &img, &csp, &subparams);
|
||||
osd_draw_on_image(osd, &subparams, 0, &img, &csp);
|
||||
}
|
||||
|
||||
static void flip_page(struct vo *vo)
|
||||
|
|
|
@ -358,7 +358,7 @@ static void draw_osd(struct vo *vo, struct osd_state *osd)
|
|||
},
|
||||
};
|
||||
|
||||
if (osd_draw_on_image(osd, &img, &csp, &subparams))
|
||||
if (osd_draw_on_image(osd, &subparams, 0, &img, &csp))
|
||||
ctx->unchanged_image = false;
|
||||
}
|
||||
|
||||
|
|
122
sub/sub.c
122
sub/sub.c
|
@ -127,7 +127,7 @@ struct osd_state *osd_create(struct MPOpts *opts, struct ass_library *asslib)
|
|||
osd->objs[n] = obj;
|
||||
}
|
||||
|
||||
// OSDTYPE_SPU is an odd case, because vf_ass.c can't render it.
|
||||
osd->objs[OSDTYPE_SPU]->is_sub = true; // spudec.c
|
||||
osd->objs[OSDTYPE_SUB]->is_sub = true; // dec_sub.c
|
||||
osd->objs[OSDTYPE_SUBTITLE]->is_sub = true; // osd_libass.c
|
||||
|
||||
|
@ -162,17 +162,14 @@ static bool spu_visible(struct osd_state *osd, struct osd_object *obj)
|
|||
return opts->sub_visibility && vo_spudec && spudec_visible(vo_spudec);
|
||||
}
|
||||
|
||||
// Return true if *out_imgs has been filled with valid values.
|
||||
// Return false on format mismatch, or if nothing to be renderer.
|
||||
static bool render_object(struct osd_state *osd, struct osd_object *obj,
|
||||
static void render_object(struct osd_state *osd, struct osd_object *obj,
|
||||
struct sub_bitmaps *out_imgs,
|
||||
struct sub_render_params *sub_params,
|
||||
const bool formats[SUBBITMAP_COUNT])
|
||||
{
|
||||
memset(out_imgs, 0x55, sizeof(*out_imgs));
|
||||
*out_imgs = (struct sub_bitmaps) {0};
|
||||
|
||||
if (obj->type == OSDTYPE_SPU) {
|
||||
*out_imgs = (struct sub_bitmaps) {0};
|
||||
if (spu_visible(osd, obj)) {
|
||||
//spudec_get_bitmap(vo_spudec, osd->res.w, osd->res.h, out_imgs);
|
||||
spudec_get_indexed(vo_spudec, &osd->res, out_imgs);
|
||||
|
@ -196,14 +193,14 @@ static bool render_object(struct osd_state *osd, struct osd_object *obj,
|
|||
obj->vo_bitmap_pos_id += out_imgs->bitmap_pos_id;
|
||||
|
||||
if (out_imgs->num_parts == 0)
|
||||
return false;
|
||||
return;
|
||||
|
||||
if (obj->cached.bitmap_id == obj->vo_bitmap_id
|
||||
&& obj->cached.bitmap_pos_id == obj->vo_bitmap_pos_id
|
||||
&& formats[obj->cached.format])
|
||||
{
|
||||
*out_imgs = obj->cached;
|
||||
return out_imgs->num_parts > 0;
|
||||
return;
|
||||
}
|
||||
|
||||
out_imgs->render_index = obj->type;
|
||||
|
@ -211,7 +208,7 @@ static bool render_object(struct osd_state *osd, struct osd_object *obj,
|
|||
out_imgs->bitmap_pos_id = obj->vo_bitmap_pos_id;
|
||||
|
||||
if (formats[out_imgs->format])
|
||||
return out_imgs->num_parts > 0;
|
||||
return;
|
||||
|
||||
bool cached = false; // do we have a copy of all the image data?
|
||||
|
||||
|
@ -221,41 +218,46 @@ static bool render_object(struct osd_state *osd, struct osd_object *obj,
|
|||
|
||||
if (cached)
|
||||
obj->cached = *out_imgs;
|
||||
|
||||
if (!formats[out_imgs->format]) {
|
||||
mp_msg(MSGT_OSD, MSGL_ERR, "Can't render OSD part %d (format %d).\n",
|
||||
obj->type, out_imgs->format);
|
||||
return false;
|
||||
}
|
||||
return out_imgs->num_parts > 0;
|
||||
}
|
||||
|
||||
// This is a hack to render the first subtitle OSD object, which is not empty.
|
||||
// It's a hack because it's really only useful for subtitles: normal OSD can
|
||||
// have multiple objects, and rendering one object may invalidate data of a
|
||||
// previously rendered, different object (that's how osd_libass.c works).
|
||||
// Also, it assumes this is called from a filter: it disables VO rendering of
|
||||
// subtitles, because we don't want to render both.
|
||||
bool osd_draw_sub(struct osd_state *osd, struct sub_bitmaps *out_imgs,
|
||||
struct sub_render_params *sub_params,
|
||||
const bool formats[SUBBITMAP_COUNT])
|
||||
// draw_flags is a bit field of OSD_DRAW_* constants
|
||||
void osd_draw(struct osd_state *osd, struct sub_render_params *params,
|
||||
int draw_flags, const bool formats[SUBBITMAP_COUNT],
|
||||
void (*cb)(void *ctx, struct sub_bitmaps *imgs), void *cb_ctx)
|
||||
{
|
||||
*out_imgs = (struct sub_bitmaps) {0};
|
||||
if (draw_flags & OSD_DRAW_SUB_FILTER)
|
||||
draw_flags |= OSD_DRAW_SUB_ONLY;
|
||||
|
||||
osd_update_ext(osd, params->dim);
|
||||
|
||||
for (int n = 0; n < MAX_OSD_PARTS; n++) {
|
||||
struct osd_object *obj = osd->objs[n];
|
||||
if (obj->is_sub) {
|
||||
// hack to allow rendering non-ASS subs with vf_ass inserted
|
||||
// (vf_ass is auto-inserted if VOs don't support EOSD)
|
||||
#ifdef CONFIG_ASS
|
||||
osd->render_subs_in_filter = !!sub_get_ass_track(osd);
|
||||
if (!osd->render_subs_in_filter)
|
||||
return false;
|
||||
#endif
|
||||
if (render_object(osd, obj, out_imgs, sub_params, formats))
|
||||
return true;
|
||||
|
||||
// Object is drawn into the video frame itself; don't draw twice
|
||||
if (osd->render_subs_in_filter && obj->is_sub &&
|
||||
!(draw_flags & OSD_DRAW_SUB_FILTER))
|
||||
continue;
|
||||
if ((draw_flags & OSD_DRAW_SUB_ONLY) && !obj->is_sub)
|
||||
continue;
|
||||
|
||||
struct sub_bitmaps imgs;
|
||||
render_object(osd, obj, &imgs, params, formats);
|
||||
if (imgs.num_parts > 0) {
|
||||
if (formats[imgs.format]) {
|
||||
cb(cb_ctx, &imgs);
|
||||
} else {
|
||||
mp_msg(MSGT_OSD, MSGL_ERR,
|
||||
"Can't render OSD part %d (format %d).\n",
|
||||
obj->type, imgs.format);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void vo_draw_eosd(void *ctx, struct sub_bitmaps *imgs)
|
||||
{
|
||||
struct vo *vo = ctx;
|
||||
vo_control(vo, VOCTRL_DRAW_EOSD, imgs);
|
||||
}
|
||||
|
||||
void draw_osd_with_eosd(struct vo *vo, struct osd_state *osd)
|
||||
|
@ -273,45 +275,39 @@ void draw_osd_with_eosd(struct vo *vo, struct osd_state *osd)
|
|||
dim.display_par = vo->monitor_par;
|
||||
dim.video_par = vo->aspdat.par;
|
||||
|
||||
osd_update_ext(osd, dim);
|
||||
|
||||
struct sub_render_params subparams = {
|
||||
.pts = osd->vo_sub_pts,
|
||||
.dim = dim,
|
||||
};
|
||||
|
||||
for (int n = 0; n < MAX_OSD_PARTS; n++) {
|
||||
struct osd_object *obj = osd->objs[n];
|
||||
if (obj->is_sub && osd->render_subs_in_filter)
|
||||
continue;
|
||||
struct sub_bitmaps imgs;
|
||||
if (render_object(osd, obj, &imgs, &subparams, formats))
|
||||
vo_control(vo, VOCTRL_DRAW_EOSD, &imgs);
|
||||
}
|
||||
osd_draw(osd, &subparams, 0, formats, &vo_draw_eosd, vo);
|
||||
}
|
||||
|
||||
struct draw_on_image_closure {
|
||||
struct mp_image *dest;
|
||||
struct mp_csp_details *dest_csp;
|
||||
bool changed;
|
||||
};
|
||||
|
||||
static void draw_on_image(void *ctx, struct sub_bitmaps *imgs)
|
||||
{
|
||||
struct draw_on_image_closure *closure = ctx;
|
||||
mp_draw_sub_bitmaps(closure->dest, imgs, closure->dest_csp);
|
||||
closure->changed = true;
|
||||
}
|
||||
|
||||
// Returns whether anything was drawn.
|
||||
bool osd_draw_on_image(struct osd_state *osd, struct mp_image *dest,
|
||||
struct mp_csp_details *dest_csp,
|
||||
struct sub_render_params *sub_params)
|
||||
bool osd_draw_on_image(struct osd_state *osd, struct sub_render_params *params,
|
||||
int draw_flags, struct mp_image *dest,
|
||||
struct mp_csp_details *dest_csp)
|
||||
{
|
||||
static const bool formats[SUBBITMAP_COUNT] = {
|
||||
[SUBBITMAP_LIBASS] = true,
|
||||
[SUBBITMAP_RGBA] = true,
|
||||
};
|
||||
bool changed = false;
|
||||
osd_update_ext(osd, sub_params->dim);
|
||||
for (int n = 0; n < MAX_OSD_PARTS; n++) {
|
||||
struct osd_object *obj = osd->objs[n];
|
||||
if (obj->is_sub && osd->render_subs_in_filter)
|
||||
continue;
|
||||
struct sub_bitmaps imgs;
|
||||
if (render_object(osd, obj, &imgs, sub_params, formats)) {
|
||||
mp_draw_sub_bitmaps(dest, &imgs, dest_csp);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
struct draw_on_image_closure closure = {dest, dest_csp};
|
||||
osd_draw(osd, params, draw_flags, formats, &draw_on_image, &closure);
|
||||
return closure.changed;
|
||||
}
|
||||
|
||||
void vo_osd_changed(int new_value)
|
||||
|
|
17
sub/sub.h
17
sub/sub.h
|
@ -212,15 +212,20 @@ void vo_osd_reset_changed(void);
|
|||
bool vo_osd_has_changed(struct osd_state *osd);
|
||||
void osd_free(struct osd_state *osd);
|
||||
|
||||
bool osd_draw_sub(struct osd_state *osd, struct sub_bitmaps *out_imgs,
|
||||
struct sub_render_params *sub_params,
|
||||
const bool formats[SUBBITMAP_COUNT]);
|
||||
enum mp_osd_draw_flags {
|
||||
OSD_DRAW_SUB_FILTER = (1 << 0),
|
||||
OSD_DRAW_SUB_ONLY = (1 << 1),
|
||||
};
|
||||
|
||||
void osd_draw(struct osd_state *osd, struct sub_render_params *params,
|
||||
int draw_flags, const bool formats[SUBBITMAP_COUNT],
|
||||
void (*cb)(void *ctx, struct sub_bitmaps *imgs), void *cb_ctx);
|
||||
|
||||
struct mp_image;
|
||||
struct mp_csp_details;
|
||||
bool osd_draw_on_image(struct osd_state *osd, struct mp_image *dest,
|
||||
struct mp_csp_details *dest_csp,
|
||||
struct sub_render_params *sub_params);
|
||||
bool osd_draw_on_image(struct osd_state *osd, struct sub_render_params *params,
|
||||
int draw_flags, struct mp_image *dest,
|
||||
struct mp_csp_details *dest_csp);
|
||||
|
||||
bool sub_bitmaps_bb(struct sub_bitmaps *imgs, int *x1, int *y1,
|
||||
int *x2, int *y2);
|
||||
|
|
Loading…
Reference in New Issue