movies/ffmpeg: support grayscale and grayscale-alpha videos

Fixes #352
This commit is contained in:
rdb 2018-06-14 14:19:54 +02:00
parent 9231694f8f
commit d284cedbea
3 changed files with 93 additions and 30 deletions

View File

@ -112,13 +112,25 @@ init_from(FfmpegVideo *source) {
// Check if we got an alpha format. Please note that some video codecs
// (eg. libvpx) change the pix_fmt after decoding the first frame, which is
// why we didn't do this earlier.
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(_video_ctx->pix_fmt);
if (desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA) != 0) {
_num_components = 4;
_pixel_format = (int)AV_PIX_FMT_BGRA;
} else {
_num_components = 3;
_pixel_format = (int)AV_PIX_FMT_BGR24;
switch (_video_ctx->pix_fmt) {
case AV_PIX_FMT_GRAY8:
_num_components = 1;
_pixel_format = (int)AV_PIX_FMT_GRAY8;
break;
case AV_PIX_FMT_YA8:
_num_components = 2;
_pixel_format = (int)AV_PIX_FMT_YA8;
break;
default:
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(_video_ctx->pix_fmt);
if (desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA) != 0) {
_num_components = 4;
_pixel_format = (int)AV_PIX_FMT_BGRA;
} else {
_num_components = 3;
_pixel_format = (int)AV_PIX_FMT_BGR24;
}
break;
}
#ifdef HAVE_SWSCALE

View File

@ -141,6 +141,7 @@ void MovieTexture::
do_recalculate_image_properties(CData *cdata, Texture::CData *cdata_tex, const LoaderOptions &options) {
int x_max = 1;
int y_max = 1;
bool rgb = false;
bool alpha = false;
double len = 0.0;
@ -150,7 +151,8 @@ do_recalculate_image_properties(CData *cdata, Texture::CData *cdata_tex, const L
if (t->size_x() > x_max) x_max = t->size_x();
if (t->size_y() > y_max) y_max = t->size_y();
if (t->length() > len) len = t->length();
if (t->get_num_components() == 4) alpha=true;
if (t->get_num_components() >= 3) rgb=true;
if (t->get_num_components() == 4 || t->get_num_components() == 2) alpha=true;
}
t = cdata->_pages[i]._alpha;
if (t) {
@ -167,7 +169,8 @@ do_recalculate_image_properties(CData *cdata, Texture::CData *cdata_tex, const L
do_adjust_this_size(cdata_tex, x_max, y_max, get_name(), true);
do_reconsider_image_properties(cdata_tex, x_max, y_max, alpha?4:3,
int num_components = (rgb ? 3 : 1) + alpha;
do_reconsider_image_properties(cdata_tex, x_max, y_max, num_components,
T_unsigned_byte, cdata->_pages.size(),
options);
cdata_tex->_orig_file_x_size = cdata->_video_width;

View File

@ -60,7 +60,21 @@ setup_texture(Texture *tex) const {
int fullx = size_x();
int fully = size_y();
tex->adjust_this_size(fullx, fully, tex->get_name(), true);
Texture::Format fmt = (get_num_components() == 4) ? Texture::F_rgba : Texture::F_rgb;
Texture::Format fmt;
switch (get_num_components()) {
case 1:
fmt = Texture::F_luminance;
break;
case 2:
fmt = Texture::F_luminance_alpha;
break;
default:
fmt = Texture::F_rgb;
break;
case 4:
fmt = Texture::F_rgba;
break;
}
tex->setup_texture(Texture::TT_2d_texture, fullx, fully, 1, Texture::T_unsigned_byte, fmt);
tex->set_pad_size(fullx - size_x(), fully - size_y());
}
@ -113,7 +127,9 @@ apply_to_texture(const Buffer *buffer, Texture *t, int page) {
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4));
nassertv((t->get_num_components() == 3) || (t->get_num_components() == 4) ||
(t->get_num_components() == 1 && get_num_components() == 1) ||
(t->get_num_components() == 2 && get_num_components() == 2));
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_num_pages());
@ -132,17 +148,19 @@ apply_to_texture(const Buffer *buffer, Texture *t, int page) {
} else {
unsigned char *p = buffer->_block;
if (t->get_num_components() == get_num_components()) {
int src_stride = size_x() * get_num_components();
int dst_stride = t->get_x_size() * t->get_num_components();
int src_width = get_num_components();
int dst_width = t->get_num_components();
if (src_width == dst_width) {
int src_stride = src_width * size_x();
int dst_stride = dst_width * t->get_x_size();
for (int y=0; y<size_y(); y++) {
memcpy(data, p, src_stride);
data += dst_stride;
p += src_stride;
}
} else {
int src_width = get_num_components();
int dst_width = t->get_num_components();
nassertv(src_width >= 3);
nassertv(dst_width >= 3);
for (int y = 0; y < size_y(); ++y) {
for (int x = 0; x < size_x(); ++x) {
data[0] = p[0];
@ -168,9 +186,20 @@ apply_to_texture_alpha(const Buffer *buffer, Texture *t, int page, int alpha_src
PStatTimer timer(_copy_pcollector);
// Is this a grayscale texture?
if (get_num_components() < 3) {
if (get_num_components() == 1 || alpha_src < 2 || alpha_src == 3) {
// There's only one "RGB" channel to take from.
alpha_src = 1;
} else {
// Alpha is actually in the second channel for grayscale-alpha.
alpha_src = 2;
}
}
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv(t->get_num_components() == 4);
nassertv(t->get_num_components() == 4 || t->get_num_components() == 2);
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_z_size());
nassertv((alpha_src >= 0) && (alpha_src <= get_num_components()));
@ -186,14 +215,16 @@ apply_to_texture_alpha(const Buffer *buffer, Texture *t, int page, int alpha_src
PStatTimer timer2(_copy_pcollector_copy);
int src_width = get_num_components();
int dst_width = t->get_num_components();
int src_stride = size_x() * src_width;
int dst_stride = t->get_x_size() * 4;
int dst_stride = t->get_x_size() * dst_width;
unsigned char *p = buffer->_block;
if (alpha_src == 0) {
nassertv(src_width >= 3);
for (int y=0; y<size_y(); y++) {
for (int x=0; x<size_x(); x++) {
unsigned char *pp = &p[x * src_width];
data[x*4+3] = (pp[0] + pp[1] + pp[2]) / 3;
data[(x + 1) * dst_width - 1] = (pp[0] + pp[1] + pp[2]) / 3;
}
data += dst_stride;
p += src_stride;
@ -202,7 +233,7 @@ apply_to_texture_alpha(const Buffer *buffer, Texture *t, int page, int alpha_src
alpha_src -= 1;
for (int y=0; y<size_y(); y++) {
for (int x=0; x<size_x(); x++) {
data[x*4+3] = p[x * src_width + alpha_src];
data[(x + 1) * dst_width - 1] = p[x * src_width + alpha_src];
}
data += dst_stride;
p += src_stride;
@ -224,7 +255,7 @@ apply_to_texture_rgb(const Buffer *buffer, Texture *t, int page) {
nassertv(t->get_x_size() >= size_x());
nassertv(t->get_y_size() >= size_y());
nassertv(t->get_num_components() == 4);
nassertv(t->get_num_components() == 4 || t->get_num_components() == 2);
nassertv(t->get_component_width() == 1);
nassertv(page < t->get_z_size());
@ -238,18 +269,35 @@ apply_to_texture_rgb(const Buffer *buffer, Texture *t, int page) {
unsigned char *data = img.p() + page * t->get_expected_ram_page_size();
PStatTimer timer2(_copy_pcollector_copy);
int src_stride = size_x() * get_num_components();
int src_width = get_num_components();
int dst_stride = t->get_x_size() * 4;
int dst_width = t->get_num_components();
int src_stride = size_x() * src_width;
int dst_stride = t->get_x_size() * dst_width;
unsigned char *p = buffer->_block;
for (int y=0; y<size_y(); y++) {
for (int x=0; x<size_x(); x++) {
data[x * 4 + 0] = p[x * src_width + 0];
data[x * 4 + 1] = p[x * src_width + 1];
data[x * 4 + 2] = p[x * src_width + 2];
if (src_width >= 3) {
// It has RGB values.
nassertv(dst_width >= 3);
for (int y = 0; y < size_y(); ++y) {
for (int x = 0; x < size_x(); ++x) {
data[x * dst_width + 0] = p[x * src_width + 0];
data[x * dst_width + 1] = p[x * src_width + 1];
data[x * dst_width + 2] = p[x * src_width + 2];
}
data += dst_stride;
p += src_stride;
}
} else if (dst_width == 4) {
// It has only grayscale.
for (int y = 0; y < size_y(); ++y) {
for (int x = 0; x < size_x(); ++x) {
unsigned char gray = p[x * src_width];
data[x * dst_width + 0] = gray;
data[x * dst_width + 1] = gray;
data[x * dst_width + 2] = gray;
}
data += dst_stride;
p += src_stride;
}
data += dst_stride;
p += src_stride;
}
}