0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 12:02:23 +02:00
mpv/sub/img_convert.c
wm4 37cf92c07a sub: change how libass output is converted to RGBA in some cases
This affects VOs (or other code which render OSD) which does not support
the LIBASS format, but only RGBA. Instead of having a converter stage in
osd.c, make mp_ass_packer_pack() output directly in RGBA.

In general, this is work towards refcounted subtitle images.

Although we could keep the "converter" design, doing it this way seems
simpler, at least considering the current situation with only 2 OSD
formats. It also prevents copying & packing the data twice, which will
lead to better performance. (Although I guess this case is not important
at all.)

It also fixes --force-rgba-osd-rendering when used with vo_opengl,
vo_vdpau, and vo_direct3d.
2016-07-03 19:32:31 +02:00

193 lines
6.1 KiB
C

/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <assert.h>
#include <libavutil/mem.h>
#include <libavutil/common.h>
#include "mpv_talloc.h"
#include "common/common.h"
#include "img_convert.h"
#include "osd.h"
#include "video/img_format.h"
#include "video/mp_image.h"
#include "video/sws_utils.h"
struct osd_conv_cache {
struct sub_bitmap part[MP_SUB_BB_LIST_MAX];
struct sub_bitmap *parts;
void *scratch;
};
struct osd_conv_cache *osd_conv_cache_new(void)
{
return talloc_zero(NULL, struct osd_conv_cache);
}
void mp_blur_rgba_sub_bitmap(struct sub_bitmap *d, double gblur)
{
struct mp_image *tmp1 = mp_image_alloc(IMGFMT_BGRA, d->w, d->h);
if (tmp1) { // on OOM, skip region
struct mp_image s = {0};
mp_image_setfmt(&s, IMGFMT_BGRA);
mp_image_set_size(&s, d->w, d->h);
s.stride[0] = d->stride;
s.planes[0] = d->bitmap;
mp_image_copy(tmp1, &s);
mp_image_sw_blur_scale(&s, tmp1, gblur);
}
talloc_free(tmp1);
}
// If RGBA parts need scaling, scale them.
bool osd_scale_rgba(struct osd_conv_cache *c, struct sub_bitmaps *imgs)
{
struct sub_bitmaps src = *imgs;
if (src.format != SUBBITMAP_RGBA)
return false;
bool need_scale = false;
for (int n = 0; n < src.num_parts; n++) {
struct sub_bitmap *sb = &src.parts[n];
if (sb->w != sb->dw || sb->h != sb->dh)
need_scale = true;
}
if (!need_scale)
return false;
talloc_free(c->parts);
imgs->parts = c->parts = talloc_array(c, struct sub_bitmap, src.num_parts);
imgs->packed = NULL;
// Note: we scale all parts, since most likely all need scaling anyway, and
// to get a proper copy of all data in the imgs list.
for (int n = 0; n < src.num_parts; n++) {
struct sub_bitmap *d = &imgs->parts[n];
struct sub_bitmap *s = &src.parts[n];
struct mp_image src_image = {0};
mp_image_setfmt(&src_image, IMGFMT_BGRA);
mp_image_set_size(&src_image, s->w, s->h);
src_image.planes[0] = s->bitmap;
src_image.stride[0] = s->stride;
d->x = s->x;
d->y = s->y;
d->w = d->dw = s->dw;
d->h = d->dh = s->dh;
struct mp_image *image = mp_image_alloc(IMGFMT_BGRA, d->w, d->h);
talloc_steal(c->parts, image);
if (image) {
d->stride = image->stride[0];
d->bitmap = image->planes[0];
mp_image_swscale(image, &src_image, mp_sws_fast_flags);
} else {
// on OOM, skip the region; just don't scale it
*d = *s;
}
}
return true;
}
bool mp_sub_bitmaps_bb(struct sub_bitmaps *imgs, struct mp_rect *out_bb)
{
struct mp_rect bb = {INT_MAX, INT_MAX, INT_MIN, INT_MIN};
for (int n = 0; n < imgs->num_parts; n++) {
struct sub_bitmap *p = &imgs->parts[n];
bb.x0 = FFMIN(bb.x0, p->x);
bb.y0 = FFMIN(bb.y0, p->y);
bb.x1 = FFMAX(bb.x1, p->x + p->dw);
bb.y1 = FFMAX(bb.y1, p->y + p->dh);
}
// avoid degenerate bounding box if empty
bb.x0 = FFMIN(bb.x0, bb.x1);
bb.y0 = FFMIN(bb.y0, bb.y1);
*out_bb = bb;
return bb.x0 < bb.x1 && bb.y0 < bb.y1;
}
// Merge bounding rectangles if they're closer than the given amount of pixels.
// Avoids having too many rectangles due to spacing between letter.
#define MERGE_RC_PIXELS 50
static void remove_intersecting_rcs(struct mp_rect *list, int *count)
{
int M = MERGE_RC_PIXELS;
bool changed = true;
while (changed) {
changed = false;
for (int a = 0; a < *count; a++) {
struct mp_rect *rc_a = &list[a];
for (int b = *count - 1; b > a; b--) {
struct mp_rect *rc_b = &list[b];
if (rc_a->x0 - M <= rc_b->x1 && rc_a->x1 + M >= rc_b->x0 &&
rc_a->y0 - M <= rc_b->y1 && rc_a->y1 + M >= rc_b->y0)
{
mp_rect_union(rc_a, rc_b);
MP_TARRAY_REMOVE_AT(list, *count, b);
changed = true;
}
}
}
}
}
// Cluster the given subrectangles into a small numbers of bounding rectangles,
// and store them into list. E.g. when subtitles and toptitles are visible at
// the same time, there should be two bounding boxes, so that the video between
// the text is left untouched (need to resample less pixels -> faster).
// Returns number of rectangles added to out_rc_list (<= rc_list_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 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};
bool intersects = false;
for (int r = 0; r < num_rc; r++) {
struct mp_rect *rc = &out_rc_list[r];
if ((bb.x0 - M <= rc->x1 && bb.x1 + M >= rc->x0 &&
bb.y0 - M <= rc->y1 && bb.y1 + M >= rc->y0) ||
num_rc == rc_list_count)
{
mp_rect_union(rc, &bb);
intersects = true;
break;
}
}
if (!intersects) {
out_rc_list[num_rc++] = bb;
remove_intersecting_rcs(out_rc_list, &num_rc);
}
}
remove_intersecting_rcs(out_rc_list, &num_rc);
return num_rc;
}