avutil/hwcontext_cuda: add support for nvenc rgb formats
[ffmpeg.git] / libavutil / imgutils.c
index cc410ab..4938a7e 100644 (file)
@@ -24,6 +24,7 @@
 #include "avassert.h"
 #include "common.h"
 #include "imgutils.h"
+#include "imgutils_internal.h"
 #include "internal.h"
 #include "intreadwrite.h"
 #include "log.h"
@@ -124,7 +125,7 @@ int av_image_fill_pointers(uint8_t *data[4], enum AVPixelFormat pix_fmt, int hei
     size[0] = linesizes[0] * height;
 
     if (desc->flags & AV_PIX_FMT_FLAG_PAL ||
-        desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) {
+        desc->flags & FF_PSEUDOPAL) {
         data[1] = ptr + size[0]; /* palette is stored here as 256 32 bits words */
         return size[0] + 256 * 4;
     }
@@ -215,7 +216,7 @@ int av_image_alloc(uint8_t *pointers[4], int linesizes[4],
         av_free(buf);
         return ret;
     }
-    if (desc->flags & AV_PIX_FMT_FLAG_PAL || desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) {
+    if (desc->flags & AV_PIX_FMT_FLAG_PAL || (desc->flags & FF_PSEUDOPAL && pointers[1])) {
         avpriv_set_systematic_pal2((uint32_t*)pointers[1], pix_fmt);
         if (align < 4) {
             av_log(NULL, AV_LOG_ERROR, "Formats with a palette require a minimum alignment of 4\n");
@@ -224,7 +225,7 @@ int av_image_alloc(uint8_t *pointers[4], int linesizes[4],
     }
 
     if ((desc->flags & AV_PIX_FMT_FLAG_PAL ||
-         desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) &&
+         desc->flags & FF_PSEUDOPAL) && pointers[1] &&
         pointers[1] - pointers[0] > linesizes[0] * h) {
         /* zero-initialize the padding before the palette */
         memset(pointers[0] + linesizes[0] * h, 0,
@@ -241,9 +242,10 @@ typedef struct ImgUtils {
 } ImgUtils;
 
 static const AVClass imgutils_class = {
-    .class_name = "IMGUTILS",
-    .item_name  = av_default_item_name,
-    .version    = LIBAVUTIL_VERSION_INT,
+    .class_name                = "IMGUTILS",
+    .item_name                 = av_default_item_name,
+    .option                    = NULL,
+    .version                   = LIBAVUTIL_VERSION_INT,
     .log_level_offset_offset   = offsetof(ImgUtils, log_offset),
     .parent_log_context_offset = offsetof(ImgUtils, log_ctx),
 };
@@ -303,9 +305,9 @@ int av_image_check_sar(unsigned int w, unsigned int h, AVRational sar)
     return AVERROR(EINVAL);
 }
 
-void av_image_copy_plane(uint8_t       *dst, int dst_linesize,
-                         const uint8_t *src, int src_linesize,
-                         int bytewidth, int height)
+static void image_copy_plane(uint8_t       *dst, ptrdiff_t dst_linesize,
+                             const uint8_t *src, ptrdiff_t src_linesize,
+                             ptrdiff_t bytewidth, int height)
 {
     if (!dst || !src)
         return;
@@ -318,9 +320,33 @@ void av_image_copy_plane(uint8_t       *dst, int dst_linesize,
     }
 }
 
-void av_image_copy(uint8_t *dst_data[4], int dst_linesizes[4],
-                   const uint8_t *src_data[4], const int src_linesizes[4],
-                   enum AVPixelFormat pix_fmt, int width, int height)
+static void image_copy_plane_uc_from(uint8_t       *dst, ptrdiff_t dst_linesize,
+                                     const uint8_t *src, ptrdiff_t src_linesize,
+                                     ptrdiff_t bytewidth, int height)
+{
+    int ret = -1;
+
+#if ARCH_X86
+    ret = ff_image_copy_plane_uc_from_x86(dst, dst_linesize, src, src_linesize,
+                                          bytewidth, height);
+#endif
+
+    if (ret < 0)
+        image_copy_plane(dst, dst_linesize, src, src_linesize, bytewidth, height);
+}
+
+void av_image_copy_plane(uint8_t       *dst, int dst_linesize,
+                         const uint8_t *src, int src_linesize,
+                         int bytewidth, int height)
+{
+    image_copy_plane(dst, dst_linesize, src, src_linesize, bytewidth, height);
+}
+
+static void image_copy(uint8_t *dst_data[4], const ptrdiff_t dst_linesizes[4],
+                       const uint8_t *src_data[4], const ptrdiff_t src_linesizes[4],
+                       enum AVPixelFormat pix_fmt, int width, int height,
+                       void (*copy_plane)(uint8_t *, ptrdiff_t, const uint8_t *,
+                                          ptrdiff_t, ptrdiff_t, int))
 {
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
 
@@ -328,12 +354,13 @@ void av_image_copy(uint8_t *dst_data[4], int dst_linesizes[4],
         return;
 
     if (desc->flags & AV_PIX_FMT_FLAG_PAL ||
-        desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL) {
-        av_image_copy_plane(dst_data[0], dst_linesizes[0],
-                            src_data[0], src_linesizes[0],
-                            width, height);
+        desc->flags & FF_PSEUDOPAL) {
+        copy_plane(dst_data[0], dst_linesizes[0],
+                   src_data[0], src_linesizes[0],
+                   width, height);
         /* copy the palette */
-        memcpy(dst_data[1], src_data[1], 4*256);
+        if ((desc->flags & AV_PIX_FMT_FLAG_PAL) || (dst_data[1] && src_data[1]))
+            memcpy(dst_data[1], src_data[1], 4*256);
     } else {
         int i, planes_nb = 0;
 
@@ -342,7 +369,7 @@ void av_image_copy(uint8_t *dst_data[4], int dst_linesizes[4],
 
         for (i = 0; i < planes_nb; i++) {
             int h = height;
-            int bwidth = av_image_get_linesize(pix_fmt, width, i);
+            ptrdiff_t bwidth = av_image_get_linesize(pix_fmt, width, i);
             if (bwidth < 0) {
                 av_log(NULL, AV_LOG_ERROR, "av_image_get_linesize failed\n");
                 return;
@@ -350,13 +377,37 @@ void av_image_copy(uint8_t *dst_data[4], int dst_linesizes[4],
             if (i == 1 || i == 2) {
                 h = AV_CEIL_RSHIFT(height, desc->log2_chroma_h);
             }
-            av_image_copy_plane(dst_data[i], dst_linesizes[i],
-                                src_data[i], src_linesizes[i],
-                                bwidth, h);
+            copy_plane(dst_data[i], dst_linesizes[i],
+                       src_data[i], src_linesizes[i],
+                       bwidth, h);
         }
     }
 }
 
+void av_image_copy(uint8_t *dst_data[4], int dst_linesizes[4],
+                   const uint8_t *src_data[4], const int src_linesizes[4],
+                   enum AVPixelFormat pix_fmt, int width, int height)
+{
+    ptrdiff_t dst_linesizes1[4], src_linesizes1[4];
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        dst_linesizes1[i] = dst_linesizes[i];
+        src_linesizes1[i] = src_linesizes[i];
+    }
+
+    image_copy(dst_data, dst_linesizes1, src_data, src_linesizes1, pix_fmt,
+               width, height, image_copy_plane);
+}
+
+void av_image_copy_uc_from(uint8_t *dst_data[4], const ptrdiff_t dst_linesizes[4],
+                           const uint8_t *src_data[4], const ptrdiff_t src_linesizes[4],
+                           enum AVPixelFormat pix_fmt, int width, int height)
+{
+    image_copy(dst_data, dst_linesizes, src_data, src_linesizes, pix_fmt,
+               width, height, image_copy_plane_uc_from);
+}
+
 int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4],
                          const uint8_t *src, enum AVPixelFormat pix_fmt,
                          int width, int height, int align)
@@ -392,7 +443,7 @@ int av_image_get_buffer_size(enum AVPixelFormat pix_fmt,
         return ret;
 
     // do not include palette for these pseudo-paletted formats
-    if (desc->flags & AV_PIX_FMT_FLAG_PSEUDOPAL)
+    if (desc->flags & FF_PSEUDOPAL)
         return FFALIGN(width, align) * height;
 
     return av_image_fill_arrays(data, linesize, NULL, pix_fmt,
@@ -442,3 +493,170 @@ int av_image_copy_to_buffer(uint8_t *dst, int dst_size,
 
     return size;
 }
+
+// Fill dst[0..dst_size] with the bytes in clear[0..clear_size]. The clear
+// bytes are repeated until dst_size is reached. If dst_size is unaligned (i.e.
+// dst_size%clear_size!=0), the remaining data will be filled with the beginning
+// of the clear data only.
+static void memset_bytes(uint8_t *dst, size_t dst_size, uint8_t *clear,
+                         size_t clear_size)
+{
+    size_t pos = 0;
+    int same = 1;
+    int i;
+
+    if (!clear_size)
+        return;
+
+    // Reduce to memset() if possible.
+    for (i = 0; i < clear_size; i++) {
+        if (clear[i] != clear[0]) {
+            same = 0;
+            break;
+        }
+    }
+    if (same)
+        clear_size = 1;
+
+    if (clear_size == 1) {
+        memset(dst, clear[0], dst_size);
+        dst_size = 0;
+    } else if (clear_size == 2) {
+        uint16_t val = AV_RN16(clear);
+        for (; dst_size >= 2; dst_size -= 2) {
+            AV_WN16(dst, val);
+            dst += 2;
+        }
+    } else if (clear_size == 4) {
+        uint32_t val = AV_RN32(clear);
+        for (; dst_size >= 4; dst_size -= 4) {
+            AV_WN32(dst, val);
+            dst += 4;
+        }
+    } else if (clear_size == 8) {
+        uint32_t val = AV_RN64(clear);
+        for (; dst_size >= 8; dst_size -= 8) {
+            AV_WN64(dst, val);
+            dst += 8;
+        }
+    }
+
+    for (; dst_size; dst_size--)
+        *dst++ = clear[pos++ % clear_size];
+}
+
+// Maximum size in bytes of a plane element (usually a pixel, or multiple pixels
+// if it's a subsampled packed format).
+#define MAX_BLOCK_SIZE 32
+
+int av_image_fill_black(uint8_t *dst_data[4], const ptrdiff_t dst_linesize[4],
+                        enum AVPixelFormat pix_fmt, enum AVColorRange range,
+                        int width, int height)
+{
+    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
+    int nb_planes = av_pix_fmt_count_planes(pix_fmt);
+    // A pixel or a group of pixels on each plane, with a value that represents black.
+    // Consider e.g. AV_PIX_FMT_UYVY422 for non-trivial cases.
+    uint8_t clear_block[4][MAX_BLOCK_SIZE] = {{0}}; // clear padding with 0
+    int clear_block_size[4] = {0};
+    ptrdiff_t plane_line_bytes[4] = {0};
+    int rgb, limited;
+    int plane, c;
+
+    if (!desc || nb_planes < 1 || nb_planes > 4 || desc->flags & AV_PIX_FMT_FLAG_HWACCEL)
+        return AVERROR(EINVAL);
+
+    rgb = !!(desc->flags & AV_PIX_FMT_FLAG_RGB);
+    limited = !rgb && range != AVCOL_RANGE_JPEG;
+
+    if (desc->flags & AV_PIX_FMT_FLAG_BITSTREAM) {
+        ptrdiff_t bytewidth = av_image_get_linesize(pix_fmt, width, 0);
+        uint8_t *data;
+        int mono = pix_fmt == AV_PIX_FMT_MONOWHITE || pix_fmt == AV_PIX_FMT_MONOBLACK;
+        int fill = pix_fmt == AV_PIX_FMT_MONOWHITE ? 0xFF : 0;
+        if (nb_planes != 1 || !(rgb || mono) || bytewidth < 1)
+            return AVERROR(EINVAL);
+
+        if (!dst_data)
+            return 0;
+
+        data = dst_data[0];
+
+        // (Bitstream + alpha will be handled incorrectly - it'll remain transparent.)
+        for (;height > 0; height--) {
+            memset(data, fill, bytewidth);
+            data += dst_linesize[0];
+        }
+        return 0;
+    }
+
+    for (c = 0; c < desc->nb_components; c++) {
+        const AVComponentDescriptor comp = desc->comp[c];
+
+        // We try to operate on entire non-subsampled pixel groups (for
+        // AV_PIX_FMT_UYVY422 this would mean two consecutive pixels).
+        clear_block_size[comp.plane] = FFMAX(clear_block_size[comp.plane], comp.step);
+
+        if (clear_block_size[comp.plane] > MAX_BLOCK_SIZE)
+            return AVERROR(EINVAL);
+    }
+
+    // Create a byte array for clearing 1 pixel (sometimes several pixels).
+    for (c = 0; c < desc->nb_components; c++) {
+        const AVComponentDescriptor comp = desc->comp[c];
+        // (Multiple pixels happen e.g. with AV_PIX_FMT_UYVY422.)
+        int w = clear_block_size[comp.plane] / comp.step;
+        uint8_t *c_data[4];
+        const int c_linesize[4] = {0};
+        uint16_t src_array[MAX_BLOCK_SIZE];
+        uint16_t src = 0;
+        int x;
+
+        if (comp.depth > 16)
+            return AVERROR(EINVAL);
+        if (!rgb && comp.depth < 8)
+            return AVERROR(EINVAL);
+        if (w < 1)
+            return AVERROR(EINVAL);
+
+        if (c == 0 && limited) {
+            src = 16 << (comp.depth - 8);
+        } else if ((c == 1 || c == 2) && !rgb) {
+            src = 128 << (comp.depth - 8);
+        } else if (c == 3) {
+            // (Assume even limited YUV uses full range alpha.)
+            src = (1 << comp.depth) - 1;
+        }
+
+        for (x = 0; x < w; x++)
+            src_array[x] = src;
+
+        for (x = 0; x < 4; x++)
+            c_data[x] = &clear_block[x][0];
+
+        av_write_image_line(src_array, c_data, c_linesize, desc, 0, 0, c, w);
+    }
+
+    for (plane = 0; plane < nb_planes; plane++) {
+        plane_line_bytes[plane] = av_image_get_linesize(pix_fmt, width, plane);
+        if (plane_line_bytes[plane] < 0)
+            return AVERROR(EINVAL);
+    }
+
+    if (!dst_data)
+        return 0;
+
+    for (plane = 0; plane < nb_planes; plane++) {
+        size_t bytewidth = plane_line_bytes[plane];
+        uint8_t *data = dst_data[plane];
+        int chroma_div = plane == 1 || plane == 2 ? desc->log2_chroma_h : 0;
+        int plane_h = ((height + ( 1 << chroma_div) - 1)) >> chroma_div;
+
+        for (; plane_h > 0; plane_h--) {
+            memset_bytes(data, bytewidth, &clear_block[plane][0], clear_block_size[plane]);
+            data += dst_linesize[plane];
+        }
+    }
+
+    return 0;
+}