Browse Source

movies/ffmpeg: support grayscale and grayscale-alpha videos

Fixes #352
rdb 7 years ago
parent
commit
d284cedbea

+ 19 - 7
panda/src/ffmpeg/ffmpegVideoCursor.cxx

@@ -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

+ 5 - 2
panda/src/grutil/movieTexture.cxx

@@ -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;

+ 69 - 21
panda/src/movies/movieVideoCursor.cxx

@@ -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;
   }
 }