mirror of
https://github.com/mpv-player/mpv
synced 2024-10-26 07:22:17 +02:00
5fc5ae752b
The mplayer DVD sub decoder is the only remaining OSD image producer that still requires the old mplayer OSD format (SUBBITMAP_OLD_PLANAR). To make supporting this format optional in VOs, add a step that allows converting these images to RGBA in case the VO doesn't have direct support for it. Note: the mplayer DVD sub decoder uses the old mplayer OSD format (SUBBITMAP_OLD_PLANAR), which is assumed to use premultiplied alpha. However, it seems DVDs allow only binary transparency, so the rendered result will be the same.
257 lines
7.4 KiB
C
257 lines
7.4 KiB
C
/*
|
|
* This file is part of mplayer.
|
|
*
|
|
* mplayer is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* mplayer 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with mplayer. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include <libavutil/mem.h>
|
|
#include <libavutil/common.h>
|
|
|
|
#include "talloc.h"
|
|
|
|
#include "img_convert.h"
|
|
#include "sub.h"
|
|
|
|
struct osd_conv_cache {
|
|
struct sub_bitmap part;
|
|
// for osd_conv_cache_alloc_old_p() (SUBBITMAP_PLANAR)
|
|
int allocated, stride;
|
|
struct old_osd_planar bmp;
|
|
// for osd_conv_cache_alloc_bmp() (various other formats)
|
|
unsigned char *packed;
|
|
};
|
|
|
|
static int osd_conv_cache_destroy(void *p)
|
|
{
|
|
struct osd_conv_cache *c = p;
|
|
av_free(c->bmp.bitmap);
|
|
av_free(c->bmp.alpha);
|
|
return 0;
|
|
}
|
|
|
|
struct osd_conv_cache *osd_conv_cache_new(void)
|
|
{
|
|
struct osd_conv_cache *c = talloc_zero(NULL, struct osd_conv_cache);
|
|
talloc_set_destructor(c, &osd_conv_cache_destroy);
|
|
return c;
|
|
}
|
|
|
|
// allocates/enlarges the alpha/bitmap buffer
|
|
static void osd_conv_cache_alloc_old_p(struct osd_conv_cache *c, int w, int h)
|
|
{
|
|
assert(w > 0 && h > 0);
|
|
c->stride = (w + 7) & (~7);
|
|
int len = c->stride * h;
|
|
if (c->allocated < len) {
|
|
av_free(c->bmp.bitmap);
|
|
av_free(c->bmp.alpha);
|
|
c->allocated = len;
|
|
c->bmp.bitmap = av_malloc(len);
|
|
c->bmp.alpha = av_malloc(len);
|
|
}
|
|
memset(c->bmp.bitmap, sub_bg_color, len);
|
|
memset(c->bmp.alpha, sub_bg_alpha, len);
|
|
c->part = (struct sub_bitmap) {
|
|
.bitmap = &c->bmp,
|
|
.stride = c->stride,
|
|
.w = w, .h = h,
|
|
.dw = w, .dh = h,
|
|
};
|
|
}
|
|
|
|
static void osd_conv_cache_alloc_bmp(struct osd_conv_cache *c, int w, int h,
|
|
int bpp)
|
|
{
|
|
size_t size = talloc_get_size(c->packed);
|
|
size_t new_size = w * bpp * h;
|
|
if (new_size > size)
|
|
c->packed = talloc_realloc(c, c->packed, unsigned char, new_size);
|
|
c->part = (struct sub_bitmap) {
|
|
.bitmap = c->packed,
|
|
.stride = w * bpp,
|
|
.w = w, .h = h,
|
|
.dw = w, .dh = h,
|
|
};
|
|
}
|
|
|
|
static void draw_alpha_ass_to_old(unsigned char *src, int src_w, int src_h,
|
|
int src_stride, unsigned char *dst_a,
|
|
unsigned char *dst_i, size_t dst_stride,
|
|
int dst_x, int dst_y, uint32_t color)
|
|
{
|
|
const unsigned int r = (color >> 24) & 0xff;
|
|
const unsigned int g = (color >> 16) & 0xff;
|
|
const unsigned int b = (color >> 8) & 0xff;
|
|
const unsigned int a = 0xff - (color & 0xff);
|
|
|
|
int gray = (r + g + b) / 3; // not correct
|
|
|
|
dst_a += dst_y * dst_stride + dst_x;
|
|
dst_i += dst_y * dst_stride + dst_x;
|
|
|
|
int src_skip = src_stride - src_w;
|
|
int dst_skip = dst_stride - src_w;
|
|
|
|
for (int y = 0; y < src_h; y++) {
|
|
for (int x = 0; x < src_w; x++) {
|
|
unsigned char as = (*src * a) >> 8;
|
|
unsigned char bs = (gray * as) >> 8;
|
|
// to mplayer scale
|
|
as = -as;
|
|
|
|
unsigned char *a = dst_a;
|
|
unsigned char *b = dst_i;
|
|
|
|
// NOTE: many special cases, because alpha=0 means transparency,
|
|
// while alpha=1..255 is opaque..transparent
|
|
if (as) {
|
|
*b = ((*b * as) >> 8) + bs;
|
|
if (*a) {
|
|
*a = (*a * as) >> 8;
|
|
if (*a < 1)
|
|
*a = 1;
|
|
} else {
|
|
*a = as;
|
|
}
|
|
}
|
|
|
|
dst_a++;
|
|
dst_i++;
|
|
src++;
|
|
}
|
|
dst_a += dst_skip;
|
|
dst_i += dst_skip;
|
|
src += src_skip;
|
|
}
|
|
}
|
|
|
|
static void render_ass_to_old(unsigned char *a, unsigned char *i,
|
|
size_t stride, int x, int y,
|
|
struct sub_bitmaps *imgs)
|
|
{
|
|
for (int n = 0; n < imgs->num_parts; n++) {
|
|
struct sub_bitmap *p = &imgs->parts[n];
|
|
draw_alpha_ass_to_old(p->bitmap, p->w, p->h, p->stride, a, i, stride,
|
|
x + p->x, y + p->y, p->libass.color);
|
|
}
|
|
}
|
|
|
|
// SUBBITMAP_LIBASS -> SUBBITMAP_OLD_PLANAR
|
|
bool osd_conv_ass_to_old_p(struct osd_conv_cache *c, struct sub_bitmaps *imgs)
|
|
{
|
|
struct sub_bitmaps src = *imgs;
|
|
if (src.format != SUBBITMAP_LIBASS || src.scaled)
|
|
return false;
|
|
|
|
imgs->format = SUBBITMAP_OLD_PLANAR;
|
|
imgs->num_parts = 0;
|
|
imgs->parts = NULL;
|
|
|
|
int x1, y1, x2, y2;
|
|
if (!sub_bitmaps_bb(&src, &x1, &y1, &x2, &y2))
|
|
return true;
|
|
|
|
osd_conv_cache_alloc_old_p(c, x2 - x1, y2 - y1);
|
|
|
|
render_ass_to_old(c->bmp.alpha, c->bmp.bitmap, c->stride, -x1, -y1, &src);
|
|
|
|
c->part.x = x1;
|
|
c->part.y = y1;
|
|
|
|
imgs->parts = &c->part;
|
|
imgs->num_parts = 1;
|
|
return true;
|
|
}
|
|
|
|
// SUBBITMAP_OLD_PLANAR -> SUBBITMAP_RGBA
|
|
bool osd_conv_old_p_to_rgba(struct osd_conv_cache *c, struct sub_bitmaps *imgs)
|
|
{
|
|
struct sub_bitmaps src = *imgs;
|
|
if (src.format != SUBBITMAP_OLD_PLANAR || src.num_parts > 1)
|
|
return false;
|
|
|
|
imgs->format = SUBBITMAP_RGBA;
|
|
imgs->num_parts = 0;
|
|
imgs->parts = NULL;
|
|
|
|
if (src.num_parts == 0)
|
|
return true;
|
|
|
|
struct sub_bitmap *s = &src.parts[0];
|
|
struct old_osd_planar *p = s->bitmap;
|
|
|
|
osd_conv_cache_alloc_bmp(c, s->w, s->h, 4);
|
|
|
|
for (int y = 0; y < s->h; y++) {
|
|
unsigned char *y_src = p->bitmap + s->stride * y;
|
|
unsigned char *y_srca = p->alpha + s->stride * y;
|
|
unsigned char *cur = c->packed + y * s->w * 4;
|
|
for (int x = 0; x < s->w; x++) {
|
|
// This is incorrect, as input is premultiplied alpha, but output
|
|
// has to be non-premultiplied. However, this code is for
|
|
// compatibility with spudec.c only, and DVD subtitles have
|
|
// binary transparency only - the rendered result will be the same.
|
|
cur[x*4+0] = cur[x*4+1] = cur[x*4+2] = y_src[x];
|
|
cur[x*4+3] = -y_srca[x];
|
|
}
|
|
}
|
|
|
|
c->part.x = s->x;
|
|
c->part.y = s->y;
|
|
|
|
imgs->parts = &c->part;
|
|
imgs->num_parts = 1;
|
|
return true;
|
|
}
|
|
|
|
// SUBBITMAP_OLD_PLANAR -> SUBBITMAP_OLD
|
|
bool osd_conv_old_p_to_old(struct osd_conv_cache *c, struct sub_bitmaps *imgs)
|
|
{
|
|
struct sub_bitmaps src = *imgs;
|
|
if (src.format != SUBBITMAP_OLD_PLANAR || src.num_parts > 1)
|
|
return false;
|
|
|
|
imgs->format = SUBBITMAP_OLD;
|
|
imgs->num_parts = 0;
|
|
imgs->parts = NULL;
|
|
|
|
if (src.num_parts == 0)
|
|
return true;
|
|
|
|
struct sub_bitmap *s = &src.parts[0];
|
|
struct old_osd_planar *p = s->bitmap;
|
|
|
|
osd_conv_cache_alloc_bmp(c, s->w, s->h, 2);
|
|
|
|
for (int y = 0; y < s->h; y++) {
|
|
unsigned char *y_src = p->bitmap + s->stride * y;
|
|
unsigned char *y_srca = p->alpha + s->stride * y;
|
|
unsigned char *cur = c->packed + y * s->w * 2;
|
|
for (int x = 0; x < s->w; x++) {
|
|
cur[x*2+0] = y_src[x];
|
|
cur[x*2+1] = -y_srca[x];
|
|
}
|
|
}
|
|
|
|
c->part.x = s->x;
|
|
c->part.y = s->y;
|
|
|
|
imgs->parts = &c->part;
|
|
imgs->num_parts = 1;
|
|
return true;
|
|
}
|