]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/exrenc.c
avformat/avio: Add Metacube support
[ffmpeg] / libavcodec / exrenc.c
index 305d44ad2d4a1c30f77c67e5358a4347886eccd9..b2cb9c4205912a5d06030f48ed997ae1a769f301 100644 (file)
@@ -34,6 +34,7 @@
 #include "avcodec.h"
 #include "bytestream.h"
 #include "internal.h"
+#include "float2half.h"
 
 enum ExrCompr {
     EXR_RAW,
@@ -72,6 +73,7 @@ typedef struct EXRContext {
     const AVClass *class;
 
     int compression;
+    int pixel_type;
     int planes;
     int nb_scanlines;
     int scanline_height;
@@ -81,12 +83,17 @@ typedef struct EXRContext {
     PutByteContext pb;
 
     EXRScanlineData *scanline;
+
+    uint16_t basetable[512];
+    uint8_t shifttable[512];
 } EXRContext;
 
 static int encode_init(AVCodecContext *avctx)
 {
     EXRContext *s = avctx->priv_data;
 
+    float2half_tables(s->basetable, s->shifttable);
+
     switch (avctx->pix_fmt) {
     case AV_PIX_FMT_GBRPF32:
         s->planes = 3;
@@ -175,7 +182,8 @@ static int64_t rle_compress(uint8_t *out, int64_t out_size,
             run++;
 
         if (run >= 3) {
-            av_assert1(o + 2 <= out_size);
+            if (o + 2 >= out_size)
+                return -1;
             out[o++] = run - 1;
             out[o++] = in[i];
             i += run;
@@ -185,7 +193,8 @@ static int64_t rle_compress(uint8_t *out, int64_t out_size,
             while (i + copy < in_size && copy < 127 && in[i + copy] != in[i + copy - 1])
                 copy++;
 
-            av_assert1(o + 1 + copy <= out_size);
+            if (o + 1 + copy >= out_size)
+                return -1;
             out[o++] = -copy;
 
             for (int x = 0; x < copy; x++)
@@ -204,9 +213,11 @@ static int64_t rle_compress(uint8_t *out, int64_t out_size,
 
 static int encode_scanline_rle(EXRContext *s, const AVFrame *frame)
 {
+    const int64_t element_size = s->pixel_type == EXR_HALF ? 2LL : 4LL;
+
     for (int y = 0; y < frame->height; y++) {
         EXRScanlineData *scanline = &s->scanline[y];
-        int64_t tmp_size = 4LL * s->planes * frame->width;
+        int64_t tmp_size = element_size * s->planes * frame->width;
         int64_t max_compressed_size = tmp_size * 3 / 2;
 
         av_fast_padded_malloc(&scanline->uncompressed_data, &scanline->uncompressed_size, tmp_size);
@@ -221,11 +232,25 @@ static int encode_scanline_rle(EXRContext *s, const AVFrame *frame)
         if (!scanline->compressed_data)
             return AVERROR(ENOMEM);
 
-        for (int p = 0; p < s->planes; p++) {
-            int ch = s->ch_order[p];
+        switch (s->pixel_type) {
+        case EXR_FLOAT:
+            for (int p = 0; p < s->planes; p++) {
+                int ch = s->ch_order[p];
 
-            memcpy(scanline->uncompressed_data + frame->width * 4 * p,
-                   frame->data[ch] + y * frame->linesize[ch], frame->width * 4);
+                memcpy(scanline->uncompressed_data + frame->width * 4 * p,
+                       frame->data[ch] + y * frame->linesize[ch], frame->width * 4);
+            }
+            break;
+        case EXR_HALF:
+            for (int p = 0; p < s->planes; p++) {
+                int ch = s->ch_order[p];
+                uint16_t *dst = (uint16_t *)(scanline->uncompressed_data + frame->width * 2 * p);
+                uint32_t *src = (uint32_t *)(frame->data[ch] + y * frame->linesize[ch]);
+
+                for (int x = 0; x < frame->width; x++)
+                    dst[x] = float2half(src[x], s->basetable, s->shifttable);
+            }
+            break;
         }
 
         reorder_pixels(scanline->tmp, scanline->uncompressed_data, tmp_size);
@@ -234,7 +259,7 @@ static int encode_scanline_rle(EXRContext *s, const AVFrame *frame)
                                              max_compressed_size,
                                              scanline->tmp, tmp_size);
 
-        if (scanline->actual_size >= tmp_size) {
+        if (scanline->actual_size <= 0 || scanline->actual_size >= tmp_size) {
             FFSWAP(uint8_t *, scanline->uncompressed_data, scanline->compressed_data);
             FFSWAP(int, scanline->uncompressed_size, scanline->compressed_size);
             scanline->actual_size = tmp_size;
@@ -246,11 +271,14 @@ static int encode_scanline_rle(EXRContext *s, const AVFrame *frame)
 
 static int encode_scanline_zip(EXRContext *s, const AVFrame *frame)
 {
+    const int64_t element_size = s->pixel_type == EXR_HALF ? 2LL : 4LL;
+
     for (int y = 0; y < s->nb_scanlines; y++) {
         EXRScanlineData *scanline = &s->scanline[y];
         const int scanline_height = FFMIN(s->scanline_height, frame->height - y * s->scanline_height);
-        int64_t tmp_size = 4LL * s->planes * frame->width * scanline_height;
+        int64_t tmp_size = element_size * s->planes * frame->width * scanline_height;
         int64_t max_compressed_size = tmp_size * 3 / 2;
+        unsigned long actual_size, source_size;
 
         av_fast_padded_malloc(&scanline->uncompressed_data, &scanline->uncompressed_size, tmp_size);
         if (!scanline->uncompressed_data)
@@ -264,24 +292,44 @@ static int encode_scanline_zip(EXRContext *s, const AVFrame *frame)
         if (!scanline->compressed_data)
             return AVERROR(ENOMEM);
 
-        for (int l = 0; l < scanline_height; l++) {
-            const int scanline_size = frame->width * 4 * s->planes;
+        switch (s->pixel_type) {
+        case EXR_FLOAT:
+            for (int l = 0; l < scanline_height; l++) {
+                const int scanline_size = frame->width * 4 * s->planes;
 
-            for (int p = 0; p < s->planes; p++) {
-                int ch = s->ch_order[p];
+                for (int p = 0; p < s->planes; p++) {
+                    int ch = s->ch_order[p];
 
-                memcpy(scanline->uncompressed_data + scanline_size * l + p * frame->width * 4,
-                       frame->data[ch] + (y * s->scanline_height + l) * frame->linesize[ch],
-                       frame->width * 4);
+                    memcpy(scanline->uncompressed_data + scanline_size * l + p * frame->width * 4,
+                           frame->data[ch] + (y * s->scanline_height + l) * frame->linesize[ch],
+                           frame->width * 4);
+                }
+            }
+            break;
+        case EXR_HALF:
+            for (int l = 0; l < scanline_height; l++) {
+                const int scanline_size = frame->width * 2 * s->planes;
+
+                for (int p = 0; p < s->planes; p++) {
+                    int ch = s->ch_order[p];
+                    uint16_t *dst = (uint16_t *)(scanline->uncompressed_data + scanline_size * l + p * frame->width * 2);
+                    uint32_t *src = (uint32_t *)(frame->data[ch] + (y * s->scanline_height + l) * frame->linesize[ch]);
+
+                    for (int x = 0; x < frame->width; x++)
+                        dst[x] = float2half(src[x], s->basetable, s->shifttable);
+                }
             }
+            break;
         }
 
         reorder_pixels(scanline->tmp, scanline->uncompressed_data, tmp_size);
         predictor(scanline->tmp, tmp_size);
-        scanline->actual_size = max_compressed_size;
-        compress(scanline->compressed_data, &scanline->actual_size,
-                 scanline->tmp, tmp_size);
+        source_size = tmp_size;
+        actual_size = max_compressed_size;
+        compress(scanline->compressed_data, &actual_size,
+                 scanline->tmp, source_size);
 
+        scanline->actual_size = actual_size;
         if (scanline->actual_size >= tmp_size) {
             FFSWAP(uint8_t *, scanline->uncompressed_data, scanline->compressed_data);
             FFSWAP(int, scanline->uncompressed_size, scanline->compressed_size);
@@ -318,7 +366,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     for (int p = 0; p < s->planes; p++) {
         bytestream2_put_byte(pb, s->ch_names[p]);
         bytestream2_put_byte(pb, 0);
-        bytestream2_put_le32(pb, EXR_FLOAT);
+        bytestream2_put_le32(pb, s->pixel_type);
         bytestream2_put_le32(pb, 0);
         bytestream2_put_le32(pb, 1);
         bytestream2_put_le32(pb, 1);
@@ -396,18 +444,38 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     case EXR_RAW:
         offset = bytestream2_tell_p(pb) + avctx->height * 8LL;
 
-        for (int y = 0; y < avctx->height; y++) {
-            bytestream2_put_le64(pb, offset);
-            offset += avctx->width * s->planes * 4 + 8;
-        }
+        if (s->pixel_type == EXR_FLOAT) {
 
-        for (int y = 0; y < avctx->height; y++) {
-            bytestream2_put_le32(pb, y);
-            bytestream2_put_le32(pb, s->planes * avctx->width * 4);
-            for (int p = 0; p < s->planes; p++) {
-                int ch = s->ch_order[p];
-                bytestream2_put_buffer(pb, frame->data[ch] + y * frame->linesize[ch],
-                                       avctx->width * 4);
+            for (int y = 0; y < avctx->height; y++) {
+                bytestream2_put_le64(pb, offset);
+                offset += avctx->width * s->planes * 4 + 8;
+            }
+
+            for (int y = 0; y < avctx->height; y++) {
+                bytestream2_put_le32(pb, y);
+                bytestream2_put_le32(pb, s->planes * avctx->width * 4);
+                for (int p = 0; p < s->planes; p++) {
+                    int ch = s->ch_order[p];
+                    bytestream2_put_buffer(pb, frame->data[ch] + y * frame->linesize[ch],
+                                           avctx->width * 4);
+                }
+            }
+        } else {
+            for (int y = 0; y < avctx->height; y++) {
+                bytestream2_put_le64(pb, offset);
+                offset += avctx->width * s->planes * 2 + 8;
+            }
+
+            for (int y = 0; y < avctx->height; y++) {
+                bytestream2_put_le32(pb, y);
+                bytestream2_put_le32(pb, s->planes * avctx->width * 2);
+                for (int p = 0; p < s->planes; p++) {
+                    int ch = s->ch_order[p];
+                    uint32_t *src = (uint32_t *)(frame->data[ch] + y * frame->linesize[ch]);
+
+                    for (int x = 0; x < frame->width; x++)
+                        bytestream2_put_le16(pb, float2half(src[x], s->basetable, s->shifttable));
+                }
             }
         }
         break;
@@ -452,6 +520,9 @@ static const AVOption options[] = {
     { "rle" ,        "RLE",                  0,                   AV_OPT_TYPE_CONST, {.i64=EXR_RLE}, 0, 0, VE, "compr" },
     { "zip1",        "ZIP1",                 0,                   AV_OPT_TYPE_CONST, {.i64=EXR_ZIP1}, 0, 0, VE, "compr" },
     { "zip16",       "ZIP16",                0,                   AV_OPT_TYPE_CONST, {.i64=EXR_ZIP16}, 0, 0, VE, "compr" },
+    { "format", "set pixel type", OFFSET(pixel_type), AV_OPT_TYPE_INT,   {.i64=EXR_FLOAT}, EXR_HALF, EXR_UNKNOWN-1, VE, "pixel" },
+    { "half" ,       NULL,                   0,                   AV_OPT_TYPE_CONST, {.i64=EXR_HALF},  0, 0, VE, "pixel" },
+    { "float",       NULL,                   0,                   AV_OPT_TYPE_CONST, {.i64=EXR_FLOAT}, 0, 0, VE, "pixel" },
     { "gamma", "set gamma", OFFSET(gamma), AV_OPT_TYPE_FLOAT, {.dbl=1.f}, 0.001, FLT_MAX, VE },
     { NULL},
 };
@@ -463,7 +534,7 @@ static const AVClass exr_class = {
     .version    = LIBAVUTIL_VERSION_INT,
 };
 
-AVCodec ff_exr_encoder = {
+const AVCodec ff_exr_encoder = {
     .name           = "exr",
     .long_name      = NULL_IF_CONFIG_SMALL("OpenEXR image"),
     .priv_data_size = sizeof(EXRContext),