[FFmpeg-devel] [PATCH 1/1] avcodec/exr: add support data windows larger or outside display window

mindmark at gmail.com mindmark at gmail.com
Tue Sep 8 00:54:35 EEST 2020


From: Mark Reid <mindmark at gmail.com>

Hi,
The following patch adds exr support for data windows that are larger or outside the display window. 
This adds support for both scanline and tiled formats.

Here are the added exr files for the fate tests. Could somebody upload them to fate for me?
https://www.dropbox.com/s/m0941dmjoejskp0/exr_datawindow_fate_files.zip

---
 libavcodec/exr.c                              | 110 ++++++++++--------
 tests/fate/image.mak                          |  31 +++++
 .../fate/exr-rgb-scanline-float-zip-dw-large  |   6 +
 .../fate/exr-rgb-scanline-half-piz-dw-large   |   6 +
 .../fate/exr-rgb-scanline-half-zip-dw-large   |   6 +
 .../fate/exr-rgb-scanline-half-zip-dw-outside |   6 +
 .../fate/exr-rgb-scanline-uint32-piz-dw-large |   6 +
 tests/ref/fate/exr-rgb-tile-half-piz-dw-large |   6 +
 tests/ref/fate/exr-rgb-tile-half-zip          |   6 +
 .../ref/fate/exr-rgb-tile-half-zip-dw-outside |   6 +
 .../ref/fate/exr-rgb-tile-uint32-piz-dw-large |   6 +
 tests/ref/fate/exr-ya-scanline-zip-half-12x8  |   6 +
 12 files changed, 155 insertions(+), 46 deletions(-)
 create mode 100644 tests/ref/fate/exr-rgb-scanline-float-zip-dw-large
 create mode 100644 tests/ref/fate/exr-rgb-scanline-half-piz-dw-large
 create mode 100644 tests/ref/fate/exr-rgb-scanline-half-zip-dw-large
 create mode 100644 tests/ref/fate/exr-rgb-scanline-half-zip-dw-outside
 create mode 100644 tests/ref/fate/exr-rgb-scanline-uint32-piz-dw-large
 create mode 100644 tests/ref/fate/exr-rgb-tile-half-piz-dw-large
 create mode 100644 tests/ref/fate/exr-rgb-tile-half-zip
 create mode 100644 tests/ref/fate/exr-rgb-tile-half-zip-dw-outside
 create mode 100644 tests/ref/fate/exr-rgb-tile-uint32-piz-dw-large
 create mode 100644 tests/ref/fate/exr-ya-scanline-zip-half-12x8

diff --git a/libavcodec/exr.c b/libavcodec/exr.c
index 68d5befa40..d5f12cb22a 100644
--- a/libavcodec/exr.c
+++ b/libavcodec/exr.c
@@ -134,8 +134,8 @@ typedef struct EXRContext {
     const AVPixFmtDescriptor *desc;
 
     int w, h;
-    uint32_t xmax, xmin;
-    uint32_t ymax, ymin;
+    int32_t xmax, xmin;
+    int32_t ymax, ymin;
     uint32_t xdelta, ydelta;
 
     int scan_lines_per_block;
@@ -995,12 +995,13 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
     uint64_t line_offset, uncompressed_size;
     uint8_t *ptr;
     uint32_t data_size;
-    uint64_t line, col = 0;
+    int line, col = 0;
     uint64_t tile_x, tile_y, tile_level_x, tile_level_y;
     const uint8_t *src;
     int step = s->desc->flags & AV_PIX_FMT_FLAG_FLOAT ? 4 : 2 * s->desc->nb_components;
-    int axmax = (avctx->width - (s->xmax + 1)) * step; /* nb pixel to add at the right of the datawindow */
-    int bxmin = s->xmin * step; /* nb pixel to add at the left of the datawindow */
+    int bxmin, axmax, window_xoffset = 0;
+    int window_xmin, window_xmax, window_ymin, window_ymax;
+    int data_xoffset, data_yoffset, data_window_offset, xsize, ysize;
     int i, x, buf_size = s->buf_size;
     int c, rgb_channel_count;
     float one_gamma = 1.0f / s->gamma;
@@ -1029,28 +1030,16 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
             return AVERROR_PATCHWELCOME;
         }
 
-        if (s->xmin || s->ymin) {
-            avpriv_report_missing_feature(s->avctx, "Tiles with xmin/ymin");
-            return AVERROR_PATCHWELCOME;
-        }
-
-        line = s->tile_attr.ySize * tile_y;
+        line = s->ymin + s->tile_attr.ySize * tile_y;
         col = s->tile_attr.xSize * tile_x;
 
         if (line < s->ymin || line > s->ymax ||
-            col  < s->xmin || col  > s->xmax)
+            s->xmin + col  < s->xmin ||  s->xmin + col  > s->xmax)
             return AVERROR_INVALIDDATA;
 
         td->ysize = FFMIN(s->tile_attr.ySize, s->ydelta - tile_y * s->tile_attr.ySize);
         td->xsize = FFMIN(s->tile_attr.xSize, s->xdelta - tile_x * s->tile_attr.xSize);
 
-        if (col) { /* not the first tile of the line */
-            bxmin = 0; /* doesn't add pixel at the left of the datawindow */
-        }
-
-        if ((col + td->xsize) != s->xdelta)/* not the last tile of the line */
-            axmax = 0; /* doesn't add pixel at the right of the datawindow */
-
         td->channel_line_size = td->xsize * s->current_channel_offset;/* uncompress size of one line */
         uncompressed_size = td->channel_line_size * (uint64_t)td->ysize;/* uncompress size of the block */
     } else {
@@ -1081,6 +1070,33 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
         }
     }
 
+    window_xmin = FFMIN(avctx->width, FFMAX(0, s->xmin + col));
+    window_xmax = FFMIN(avctx->width, FFMAX(0, s->xmin + col + td->xsize));
+    window_ymin = FFMIN(avctx->height, FFMAX(0, line ));
+    window_ymax = FFMIN(avctx->height, FFMAX(0, line + td->ysize));
+    xsize = window_xmax - window_xmin;
+    ysize = window_ymax - window_ymin;
+
+    /* tile or scanline not visible skip decoding */
+    if (xsize <= 0 || ysize <= 0)
+        return 0;
+
+    /* is the first tile or is a scanline */
+    if(col == 0) {
+        window_xmin = 0;
+        /* pixels to add at the left of the display window */
+        window_xoffset = FFMAX(0, s->xmin);
+        /* bytes to add at the left of the display window */
+        bxmin = window_xoffset * step;
+    }
+
+    /* is the last tile or is a scanline */
+    if(col + td->xsize == s->xdelta) {
+        window_xmax = avctx->width;
+         /* bytes to add at the right of the display window */
+        axmax = FFMAX(0, (avctx->width - (s->xmax + 1))) * step;
+    }
+
     if (data_size < uncompressed_size || s->is_tile) { /* td->tmp is use for tile reorganization */
         av_fast_padded_malloc(&td->tmp, &td->tmp_size, uncompressed_size);
         if (!td->tmp)
@@ -1121,17 +1137,22 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
         src = td->uncompressed_data;
     }
 
+    /* offsets to crop data outside display window */
+    data_xoffset = FFABS(FFMIN(0, s->xmin + col)) * (s->pixel_type == EXR_HALF ? 2 : 4);
+    data_yoffset = FFABS(FFMIN(0, line));
+    data_window_offset = (data_yoffset * td->channel_line_size) + data_xoffset;
+
     if (!s->is_luma) {
-        channel_buffer[0] = src + td->xsize * s->channel_offsets[0];
-        channel_buffer[1] = src + td->xsize * s->channel_offsets[1];
-        channel_buffer[2] = src + td->xsize * s->channel_offsets[2];
+        channel_buffer[0] = src + (td->xsize * s->channel_offsets[0]) + data_window_offset;
+        channel_buffer[1] = src + (td->xsize * s->channel_offsets[1]) + data_window_offset;
+        channel_buffer[2] = src + (td->xsize * s->channel_offsets[2]) + data_window_offset;
         rgb_channel_count = 3;
     } else { /* put y data in the first channel_buffer */
-        channel_buffer[0] = src + td->xsize * s->channel_offsets[1];
+        channel_buffer[0] = src + (td->xsize * s->channel_offsets[1]) + data_window_offset;
         rgb_channel_count = 1;
     }
-    if (s->channel_offsets[3] >= 0)
-        channel_buffer[3] = src + td->xsize * s->channel_offsets[3];
+     if (s->channel_offsets[3] >= 0)
+        channel_buffer[3] = src + (td->xsize * s->channel_offsets[3]) + data_window_offset;
 
     if (s->desc->flags & AV_PIX_FMT_FLAG_FLOAT) {
 
@@ -1144,9 +1165,9 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
 
         for (c = 0; c < channel_count; c++) {
             int plane = s->desc->comp[c].plane;
-            ptr = p->data[plane] + line * p->linesize[plane] + (col * 4);
+            ptr = p->data[plane] + window_ymin * p->linesize[plane] + (window_xmin * 4);
 
-            for (i = 0; i < td->ysize; i++, ptr += p->linesize[plane]) {
+            for (i = 0; i < ysize; i++, ptr += p->linesize[plane]) {
                 const uint8_t *src;
                 union av_intfloat32 *ptr_x;
 
@@ -1155,19 +1176,19 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
 
                 // Zero out the start if xmin is not 0
                 memset(ptr_x, 0, bxmin);
-                ptr_x += s->xmin;
+                ptr_x += window_xoffset;
 
                 if (s->pixel_type == EXR_FLOAT) {
                     // 32-bit
                     union av_intfloat32 t;
                     if (trc_func && c < 3) {
-                        for (x = 0; x < td->xsize; x++) {
+                        for (x = 0; x < xsize; x++) {
                             t.i = bytestream_get_le32(&src);
                             t.f = trc_func(t.f);
                             *ptr_x++ = t;
                         }
                     } else {
-                        for (x = 0; x < td->xsize; x++) {
+                        for (x = 0; x < xsize; x++) {
                             t.i = bytestream_get_le32(&src);
                             if (t.f > 0.0f && c < 3)  /* avoid negative values */
                                 t.f = powf(t.f, one_gamma);
@@ -1177,11 +1198,11 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
                 } else if (s->pixel_type == EXR_HALF) {
                     // 16-bit
                     if (c < 3) {
-                        for (x = 0; x < td->xsize; x++) {
+                        for (x = 0; x < xsize; x++) {
                             *ptr_x++ = s->gamma_table[bytestream_get_le16(&src)];
                         }
                     } else {
-                        for (x = 0; x < td->xsize; x++) {
+                        for (x = 0; x < xsize; x++) {
                             *ptr_x++ = exr_half2float(bytestream_get_le16(&src));;
                         }
                     }
@@ -1195,9 +1216,9 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
     } else {
 
         av_assert1(s->pixel_type == EXR_UINT);
-        ptr = p->data[0] + line * p->linesize[0] + (col * s->desc->nb_components * 2);
+        ptr = p->data[0] + window_ymin * p->linesize[0] + (window_xmin * s->desc->nb_components * 2);
 
-        for (i = 0; i < td->ysize; i++, ptr += p->linesize[0]) {
+        for (i = 0; i < ysize; i++, ptr += p->linesize[0]) {
 
             const uint8_t * a;
             const uint8_t *rgb[3];
@@ -1214,9 +1235,9 @@ static int decode_block(AVCodecContext *avctx, void *tdata,
 
             // Zero out the start if xmin is not 0
             memset(ptr_x, 0, bxmin);
-            ptr_x += s->xmin * s->desc->nb_components;
+            ptr_x += window_xoffset * s->desc->nb_components;
 
-            for (x = 0; x < td->xsize; x++) {
+            for (x = 0; x < xsize; x++) {
                 for (c = 0; c < rgb_channel_count; c++) {
                     *ptr_x++ = bytestream_get_le32(&rgb[c]) >> 16;
                 }
@@ -1654,7 +1675,7 @@ static int decode_frame(AVCodecContext *avctx, void *data,
     AVFrame *picture = data;
     uint8_t *ptr;
 
-    int i, y, ret;
+    int i, y, ret, ymax;
     int planes;
     int out_line_size;
     int nb_blocks;   /* nb scanline or nb tile */
@@ -1728,13 +1749,9 @@ static int decode_frame(AVCodecContext *avctx, void *data,
         return AVERROR_PATCHWELCOME;
     }
 
-    /* Verify the xmin, xmax, ymin, ymax and xdelta before setting
-     * the actual image size. */
-    if (s->xmin > s->xmax                  ||
-        s->ymin > s->ymax                  ||
-        s->xdelta != s->xmax - s->xmin + 1 ||
-        s->xmax >= s->w                    ||
-        s->ymax >= s->h) {
+    /* Verify the xmin, xmax, ymin and ymax before setting the actual image size.
+     * It's possible for the data window can larger or outside the display window */
+    if (s->xmin > s->xmax  || s->ymin > s->ymax) {
         av_log(avctx, AV_LOG_ERROR, "Wrong or missing size information.\n");
         return AVERROR_INVALIDDATA;
     }
@@ -1804,10 +1821,11 @@ static int decode_frame(AVCodecContext *avctx, void *data,
 
     avctx->execute2(avctx, decode_block, s->thread_data, NULL, nb_blocks);
 
+    ymax = FFMAX(0, s->ymax + 1);
     // Zero out the end if ymax+1 is not h
     for (i = 0; i < planes; i++) {
-        ptr = picture->data[i] + ((s->ymax+1) * picture->linesize[i]);
-        for (y = s->ymax + 1; y < avctx->height; y++) {
+        ptr = picture->data[i] + (ymax * picture->linesize[i]);
+        for (y = ymax; y < avctx->height; y++) {
             memset(ptr, 0, out_line_size);
             ptr += picture->linesize[i];
         }
diff --git a/tests/fate/image.mak b/tests/fate/image.mak
index 44fd06c294..fb129c0133 100644
--- a/tests/fate/image.mak
+++ b/tests/fate/image.mak
@@ -287,6 +287,37 @@ fate-exr-rgb-scanline-half-piz-dw-t08: CMD = framecrc -i $(TARGET_SAMPLES)/exr/r
 FATE_EXR += fate-exr-rgba-zip16-16x32-flag4
 fate-exr-rgba-zip16-16x32-flag4: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgba_zip16_16x32_flag4.exr -pix_fmt gbrapf32le
 
+FATE_EXR += fate-exr-ya-scanline-zip-half-12x8
+fate-exr-ya-scanline-zip-half-12x8: CMD = framecrc -i $(TARGET_SAMPLES)/exr/ya_scanline_zip_half_12x8.exr -pix_fmt gbrapf32le
+
+FATE_EXR += fate-exr-rgb-tile-half-zip
+fate-exr-rgb-tile-half-zip: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_half_zip.exr -pix_fmt gbrpf32le
+
+FATE_EXR += fate-exr-rgb-scanline-float-zip-dw-large
+fate-exr-rgb-scanline-float-zip-dw-large: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_float_zip_dw_large.exr -pix_fmt gbrpf32le
+
+FATE_EXR += fate-exr-rgb-scanline-half-piz-dw-large
+fate-exr-rgb-scanline-half-piz-dw-large: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_piz_dw_large.exr -pix_fmt gbrpf32le
+
+FATE_EXR += fate-exr-rgb-scanline-half-zip-dw-large
+fate-exr-rgb-scanline-half-zip-dw-large: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_zip_dw_large.exr -pix_fmt gbrpf32le
+
+FATE_EXR += fate-exr-rgb-scanline-uint32-piz-dw-large
+fate-exr-rgb-scanline-uint32-piz-dw-large: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_uint32_piz_dw_large.exr -pix_fmt rgb48le
+
+FATE_EXR += fate-exr-rgb-tile-half-piz-dw-large
+fate-exr-rgb-tile-half-piz-dw-large: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_half_piz_dw_large.exr -pix_fmt gbrpf32le
+
+FATE_EXR += fate-exr-rgb-tile-uint32-piz-dw-large
+fate-exr-rgb-tile-uint32-piz-dw-large: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_uint32_piz_dw_large.exr -pix_fmt rgb48le
+
+FATE_EXR += fate-exr-rgb-scanline-half-zip-dw-outside
+fate-exr-rgb-scanline-half-zip-dw-outside: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_scanline_half_zip_dw_outside.exr -pix_fmt gbrpf32le
+
+FATE_EXR += fate-exr-rgb-tile-half-zip-dw-outside
+fate-exr-rgb-tile-half-zip-dw-outside: CMD = framecrc -i $(TARGET_SAMPLES)/exr/rgb_tile_half_zip_dw_outside.exr -pix_fmt gbrpf32le
+
+
 FATE_EXR-$(call DEMDEC, IMAGE2, EXR) += $(FATE_EXR)
 
 FATE_IMAGE += $(FATE_EXR-yes)
diff --git a/tests/ref/fate/exr-rgb-scanline-float-zip-dw-large b/tests/ref/fate/exr-rgb-scanline-float-zip-dw-large
new file mode 100644
index 0000000000..45fc6182c2
--- /dev/null
+++ b/tests/ref/fate/exr-rgb-scanline-float-zip-dw-large
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 50x50
+#sar 0: 1/1
+0,          0,          0,        1,    30000, 0x947ce379
diff --git a/tests/ref/fate/exr-rgb-scanline-half-piz-dw-large b/tests/ref/fate/exr-rgb-scanline-half-piz-dw-large
new file mode 100644
index 0000000000..811698074a
--- /dev/null
+++ b/tests/ref/fate/exr-rgb-scanline-half-piz-dw-large
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 50x50
+#sar 0: 1/1
+0,          0,          0,        1,    30000, 0xb329ee9c
diff --git a/tests/ref/fate/exr-rgb-scanline-half-zip-dw-large b/tests/ref/fate/exr-rgb-scanline-half-zip-dw-large
new file mode 100644
index 0000000000..811698074a
--- /dev/null
+++ b/tests/ref/fate/exr-rgb-scanline-half-zip-dw-large
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 50x50
+#sar 0: 1/1
+0,          0,          0,        1,    30000, 0xb329ee9c
diff --git a/tests/ref/fate/exr-rgb-scanline-half-zip-dw-outside b/tests/ref/fate/exr-rgb-scanline-half-zip-dw-outside
new file mode 100644
index 0000000000..8c2258838d
--- /dev/null
+++ b/tests/ref/fate/exr-rgb-scanline-half-zip-dw-outside
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 501x401
+#sar 0: 1/1
+0,          0,          0,        1,  2410812, 0x00000000
diff --git a/tests/ref/fate/exr-rgb-scanline-uint32-piz-dw-large b/tests/ref/fate/exr-rgb-scanline-uint32-piz-dw-large
new file mode 100644
index 0000000000..60e1254945
--- /dev/null
+++ b/tests/ref/fate/exr-rgb-scanline-uint32-piz-dw-large
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 50x50
+#sar 0: 1/1
+0,          0,          0,        1,    15000, 0xeeacd171
diff --git a/tests/ref/fate/exr-rgb-tile-half-piz-dw-large b/tests/ref/fate/exr-rgb-tile-half-piz-dw-large
new file mode 100644
index 0000000000..811698074a
--- /dev/null
+++ b/tests/ref/fate/exr-rgb-tile-half-piz-dw-large
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 50x50
+#sar 0: 1/1
+0,          0,          0,        1,    30000, 0xb329ee9c
diff --git a/tests/ref/fate/exr-rgb-tile-half-zip b/tests/ref/fate/exr-rgb-tile-half-zip
new file mode 100644
index 0000000000..7cf760334e
--- /dev/null
+++ b/tests/ref/fate/exr-rgb-tile-half-zip
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 501x401
+#sar 0: 1/1
+0,          0,          0,        1,  2410812, 0x2dd1b00b
diff --git a/tests/ref/fate/exr-rgb-tile-half-zip-dw-outside b/tests/ref/fate/exr-rgb-tile-half-zip-dw-outside
new file mode 100644
index 0000000000..8c2258838d
--- /dev/null
+++ b/tests/ref/fate/exr-rgb-tile-half-zip-dw-outside
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 501x401
+#sar 0: 1/1
+0,          0,          0,        1,  2410812, 0x00000000
diff --git a/tests/ref/fate/exr-rgb-tile-uint32-piz-dw-large b/tests/ref/fate/exr-rgb-tile-uint32-piz-dw-large
new file mode 100644
index 0000000000..60e1254945
--- /dev/null
+++ b/tests/ref/fate/exr-rgb-tile-uint32-piz-dw-large
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 50x50
+#sar 0: 1/1
+0,          0,          0,        1,    15000, 0xeeacd171
diff --git a/tests/ref/fate/exr-ya-scanline-zip-half-12x8 b/tests/ref/fate/exr-ya-scanline-zip-half-12x8
new file mode 100644
index 0000000000..23e1108ed9
--- /dev/null
+++ b/tests/ref/fate/exr-ya-scanline-zip-half-12x8
@@ -0,0 +1,6 @@
+#tb 0: 1/25
+#media_type 0: video
+#codec_id 0: rawvideo
+#dimensions 0: 12x8
+#sar 0: 1/1
+0,          0,          0,        1,     1536, 0x9473ee5c
-- 
2.27.0



More information about the ffmpeg-devel mailing list