st/vdpau: add format conversions for GetBitsYCbCr
authorGrigori Goronzy <greg@chown.ath.cx>
Sun, 13 Oct 2013 16:08:45 +0000 (18:08 +0200)
committerChristian König <christian.koenig@amd.com>
Sun, 13 Oct 2013 18:09:38 +0000 (20:09 +0200)
Add simple plain C routines for NV12<->YV12 and YUYV<->UYVY
conversions. The NV12->YV12 conversion is commonly used, for instance
by VLC.

Reviewed-by: Christian König <christian.koenig@amd.com>
src/gallium/state_trackers/vdpau/surface.c

index e371bcaa6153a0966b86c251af15df732f2a56e1..074363bdafb1cdac51be7fa2daeb4793ffcb0b3b 100644 (file)
 
 #include "vdpau_private.h"
 
+enum getbits_conversion {
+   CONVERSION_NONE,
+   CONVERSION_NV12_TO_YV12,
+   CONVERSION_YV12_TO_NV12,
+   CONVERSION_SWAP_YUYV_UYVY,
+};
+
 /**
  * Create a VdpVideoSurface.
  */
@@ -185,6 +192,80 @@ vlVdpVideoSurfaceSize(vlVdpSurface *p_surf, int component,
       *height /= 2;
 }
 
+static void
+vlVdpCopyNV12ToYV12(void *const *destination_data,
+                    uint32_t const *destination_pitches,
+                    int src_plane, int src_field,
+                    int src_stride, int num_fields,
+                    uint8_t const *src,
+                    int width, int height)
+{
+   int x, y;
+   unsigned u_stride = destination_pitches[2] * num_fields;
+   unsigned v_stride = destination_pitches[1] * num_fields;
+   uint8_t *u_dst = (uint8_t *)destination_data[2] + destination_pitches[2] * src_field;
+   uint8_t *v_dst = (uint8_t *)destination_data[1] + destination_pitches[1] * src_field;
+
+   /* TODO: SIMD */
+   for (y = 0; y < height; y++) {
+      for (x = 0; x < width; x++) {
+         u_dst[x] = src[2*x];
+         v_dst[x] = src[2*x+1];
+      }
+      u_dst += u_stride;
+      v_dst += v_stride;
+      src += src_stride;
+   }
+}
+
+static void
+vlVdpCopyYV12ToNV12(void *const *destination_data,
+                    uint32_t const *destination_pitches,
+                    int src_plane, int src_field,
+                    int src_stride, int num_fields,
+                    uint8_t const *src,
+                    int width, int height)
+{
+   int x, y;
+   unsigned offset = 2 - src_plane;
+   unsigned stride = destination_pitches[1] * num_fields;
+   uint8_t *dst = (uint8_t *)destination_data[1] + destination_pitches[1] * src_field;
+
+   /* TODO: SIMD */
+   for (y = 0; y < height; y++) {
+      for (x = 0; x < 2 * width; x += 2) {
+         dst[x+offset] = src[x>>1];
+      }
+      dst += stride;
+      src += src_stride;
+   }
+}
+
+static void
+vlVdpCopySwap422Packed(void *const *destination_data,
+                       uint32_t const *destination_pitches,
+                       int src_plane, int src_field,
+                       int src_stride, int num_fields,
+                       uint8_t const *src,
+                       int width, int height)
+{
+   int x, y;
+   unsigned stride = destination_pitches[0] * num_fields;
+   uint8_t *dst = (uint8_t *)destination_data[0] + destination_pitches[0] * src_field;
+
+   /* TODO: SIMD */
+   for (y = 0; y < height; y++) {
+      for (x = 0; x < 4 * width; x += 4) {
+         dst[x+0] = src[x+1];
+         dst[x+1] = src[x+0];
+         dst[x+2] = src[x+3];
+         dst[x+3] = src[x+2];
+      }
+      dst += stride;
+      src += src_stride;
+   }
+}
+
 /**
  * Copy image data from a VdpVideoSurface to application memory in a specified
  * YCbCr format.
@@ -197,8 +278,9 @@ vlVdpVideoSurfaceGetBitsYCbCr(VdpVideoSurface surface,
 {
    vlVdpSurface *vlsurface;
    struct pipe_context *pipe;
-   enum pipe_format format;
+   enum pipe_format format, buffer_format;
    struct pipe_sampler_view **sampler_views;
+   enum getbits_conversion conversion = CONVERSION_NONE;
    unsigned i, j;
 
    vlsurface = vlGetDataHTAB(surface);
@@ -211,10 +293,23 @@ vlVdpVideoSurfaceGetBitsYCbCr(VdpVideoSurface surface,
 
    format = FormatYCBCRToPipe(destination_ycbcr_format);
    if (format == PIPE_FORMAT_NONE)
-       return VDP_STATUS_INVALID_Y_CB_CR_FORMAT;
-
-   if (vlsurface->video_buffer == NULL || format != vlsurface->video_buffer->buffer_format)
-      return VDP_STATUS_NO_IMPLEMENTATION; /* TODO We don't support conversion (yet) */
+      return VDP_STATUS_INVALID_Y_CB_CR_FORMAT;
+
+   if (vlsurface->video_buffer == NULL)
+      return VDP_STATUS_INVALID_VALUE;
+
+   buffer_format = vlsurface->video_buffer->buffer_format;
+   if (format != buffer_format) {
+      if (format == PIPE_FORMAT_YV12 && buffer_format == PIPE_FORMAT_NV12)
+         conversion = CONVERSION_NV12_TO_YV12;
+      else if (format == PIPE_FORMAT_NV12 && buffer_format == PIPE_FORMAT_YV12)
+         conversion = CONVERSION_YV12_TO_NV12;
+      else if ((format == PIPE_FORMAT_YUYV && buffer_format == PIPE_FORMAT_UYVY) ||
+               (format == PIPE_FORMAT_UYVY && buffer_format == PIPE_FORMAT_YUYV))
+         conversion = CONVERSION_SWAP_YUYV_UYVY;
+      else
+         return VDP_STATUS_NO_IMPLEMENTATION;
+   }
 
    pipe_mutex_lock(vlsurface->device->mutex);
    sampler_views = vlsurface->video_buffer->get_sampler_view_planes(vlsurface->video_buffer);
@@ -245,9 +340,23 @@ vlVdpVideoSurfaceGetBitsYCbCr(VdpVideoSurface surface,
             return VDP_STATUS_RESOURCES;
          }
 
-         util_copy_rect(destination_data[i] + destination_pitches[i] * j, sv->texture->format,
-                        destination_pitches[i] * sv->texture->array_size, 0, 0,
-                        box.width, box.height, map, transfer->stride, 0, 0);
+         if (conversion == CONVERSION_NV12_TO_YV12 && i == 1) {
+            vlVdpCopyNV12ToYV12(destination_data, destination_pitches,
+                                i, j, transfer->stride, sv->texture->array_size,
+                                map, box.width, box.height);
+         } else if (conversion == CONVERSION_YV12_TO_NV12 && i > 0) {
+            vlVdpCopyYV12ToNV12(destination_data, destination_pitches,
+                                i, j, transfer->stride, sv->texture->array_size,
+                                map, box.width, box.height);
+         } else if (conversion == CONVERSION_SWAP_YUYV_UYVY) {
+            vlVdpCopySwap422Packed(destination_data, destination_pitches,
+                                   i, j, transfer->stride, sv->texture->array_size,
+                                   map, box.width, box.height);
+         } else {
+            util_copy_rect(destination_data[i] + destination_pitches[i] * j, sv->texture->format,
+                           destination_pitches[i] * sv->texture->array_size, 0, 0,
+                           box.width, box.height, map, transfer->stride, 0, 0);
+         }
 
          pipe_transfer_unmap(pipe, transfer);
       }