Hello, In attach a patch in order to add support for exr Tile (One Level)
Some samples can be found here : https://we.tl/IC4XTrEJf4 Theses samples can also be add to ./fate-suite/exr/ (i will make a fate test soon). Details of the sample files : rgb_tile_half_raw_12x8.exr : uncompress half float file, in tile (only one tile in the file) rgb_tile_float_raw_12x8.exr : uncompress float file, in tile (only one tile in the file) rgb_tile_float_raw_150x130.exr : uncompress float file, in tile (multiple tile in the file) In this patch, compression in tile is not enabled. (i will make new patchs for that after making more tests) I tried to keep the most code common with the scanline mode (reason why, scanline code part have had some modifications). Tile data are reorganized, to be more like scanline uncompress data. Pass fate-exr tests. More information about tile in exr can be found here : http://www.openexr.com/TechnicalIntroduction.pdf Comments welcome. Martin Jokyo Images
From 457893e633cc4c8162e69dcb40d2de3f40ae9de5 Mon Sep 17 00:00:00 2001 From: Martin Vignali <martin.vign...@gmail.com> Date: Sun, 3 Apr 2016 00:21:30 +0200 Subject: [PATCH] libavcodec/exr : add support for tile --- libavcodec/exr.c | 273 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 216 insertions(+), 57 deletions(-) diff --git a/libavcodec/exr.c b/libavcodec/exr.c index f6141fa..5ac70bc 100644 --- a/libavcodec/exr.c +++ b/libavcodec/exr.c @@ -3,6 +3,8 @@ * Copyright (c) 2006 Industrial Light & Magic, a division of Lucas Digital Ltd. LLC * Copyright (c) 2009 Jimmy Christensen * + * B44/B44A, Tile added by Jokyo Images support by CNC - French National Center for Cinema + * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -67,11 +69,29 @@ enum ExrPixelType { EXR_UNKNOWN, }; +enum ExrTileLevelMode { + EXR_TILE_LEVEL_ONE, + EXR_TILE_LEVEL_MIPMAP, + EXR_TILE_LEVEL_RIPMAP, +}; + +enum ExrTileLevelRound { + EXR_TILE_ROUND_UP, + EXR_TILE_ROUND_DOWN, +}; + typedef struct EXRChannel { int xsub, ysub; enum ExrPixelType pixel_type; } EXRChannel; +typedef struct EXRTileAttribute { + int32_t xSize; + int32_t ySize; + enum ExrTileLevelMode level_mode; + enum ExrTileLevelRound level_round; +} EXRTileAttribute; + typedef struct EXRThreadData { uint8_t *uncompressed_data; int uncompressed_size; @@ -97,10 +117,13 @@ typedef struct EXRContext { uint32_t xmax, xmin; uint32_t ymax, ymin; uint32_t xdelta, ydelta; - int ysize; + int ysize, xsize; uint64_t scan_line_size; int scan_lines_per_block; + + EXRTileAttribute tile_attr;/* header data attribute of tile */ + int is_tile;/* 0 if scanline, 1 if tile */ GetByteContext gb; const uint8_t *buf; @@ -108,6 +131,7 @@ typedef struct EXRContext { EXRChannel *channels; int nb_channels; + int current_channel_offset; EXRThreadData *thread_data; @@ -950,43 +974,88 @@ static int decode_block(AVCodecContext *avctx, void *tdata, uint32_t xdelta = s->xdelta; uint16_t *ptr_x; uint8_t *ptr; - uint32_t data_size, line; - const uint8_t *src; - int axmax = (avctx->width - (s->xmax + 1)) * 2 * s->desc->nb_components; - int bxmin = s->xmin * 2 * s->desc->nb_components; + uint32_t data_size, line, col = 0; + uint32_t tileX, tileY, tileLevelX, tileLevelY; + int channelLineSize, indexSrc, tX, tY, tCh; + const uint8_t *src;/* remove const for srcTile */ + int axmax = (avctx->width - (s->xmax + 1)) * 2 * s->desc->nb_components;/* nb pixel to add at the right of the datawindow */ + int bxmin = s->xmin * 2 * s->desc->nb_components;/* nb pixel to add at the left of the datawindow */ int i, x, buf_size = s->buf_size; float one_gamma = 1.0f / s->gamma; avpriv_trc_function trc_func = avpriv_get_trc_function_from_trc(s->apply_trc_type); int ret; - line_offset = AV_RL64(s->gb.buffer + jobnr * 8); - // Check if the buffer has the required bytes needed from the offset - if (line_offset > buf_size - 8) - return AVERROR_INVALIDDATA; - - src = buf + line_offset + 8; - line = AV_RL32(src - 8); - if (line < s->ymin || line > s->ymax) - return AVERROR_INVALIDDATA; + line_offset = AV_RL64(s->gb.buffer + jobnr * 8);/* read offset block in table */ - data_size = AV_RL32(src - 4); - if (data_size <= 0 || data_size > buf_size) - return AVERROR_INVALIDDATA; - - s->ysize = FFMIN(s->scan_lines_per_block, s->ymax - line + 1); - uncompressed_size = s->scan_line_size * s->ysize; - if ((s->compression == EXR_RAW && (data_size != uncompressed_size || - line_offset > buf_size - uncompressed_size)) || - (s->compression != EXR_RAW && (data_size > uncompressed_size || - line_offset > buf_size - data_size))) { - return AVERROR_INVALIDDATA; + // Check if the buffer has the required bytes needed from the offset + if (s->is_tile){ + if (line_offset > buf_size - 20) + return AVERROR_INVALIDDATA; + + src = buf + line_offset + 20; + + tileX = AV_RL32(src - 20); + tileY = AV_RL32(src - 16); + tileLevelX = AV_RL32(src - 12); + tileLevelY = AV_RL32(src - 8); + + data_size = AV_RL32(src - 4); + if (data_size <= 0 || data_size > buf_size) + return AVERROR_INVALIDDATA; + + if ((tileLevelX != 0)||(tileLevelY != 0)){/* tile of low resolution (Mipmap, rimmap) */ + av_log(s->avctx, AV_LOG_ERROR, "Wrong Tile level %i / %i.\n", tileLevelX, tileLevelY); + return AVERROR_INVALIDDATA; + } + + line = s->tile_attr.ySize * tileY; + col = s->tile_attr.xSize * tileX; + + s->ysize = FFMIN(s->tile_attr.ySize, s->ydelta - tileY * s->tile_attr.ySize); + s->xsize = FFMIN(s->tile_attr.xSize, s->xdelta - tileX * s->tile_attr.xSize); + uncompressed_size = s->current_channel_offset * s->ysize * s->xsize; + + if (col)/* not the first tile of the line */ + bxmin = 0;axmax = 0;/* doesn't add pixel at the left of the datawindow */ + + if ((col + s->xsize) != s->xdelta)/* not the last tile of the line */ + axmax = 0;/* doesn't add pixel at the right of the datawindow */ + } + else { /* scanline */ + if (line_offset > buf_size - 8) + return AVERROR_INVALIDDATA; + + src = buf + line_offset + 8; + line = AV_RL32(src - 8); + if (line < s->ymin || line > s->ymax) + return AVERROR_INVALIDDATA; + + data_size = AV_RL32(src - 4); + if (data_size <= 0 || data_size > buf_size) + return AVERROR_INVALIDDATA; + + s->ysize = FFMIN(s->scan_lines_per_block, s->ymax - line + 1);/* s->ydelta - line ?? */ + s->xsize = s->xdelta; + uncompressed_size = s->scan_line_size * s->ysize; + if ((s->compression == EXR_RAW && (data_size != uncompressed_size || + line_offset > buf_size - uncompressed_size)) || + (s->compression != EXR_RAW && (data_size > uncompressed_size || + line_offset > buf_size - data_size))) { + return AVERROR_INVALIDDATA; + } + } + + 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) + return AVERROR(ENOMEM); } if (data_size < uncompressed_size) { av_fast_padded_malloc(&td->uncompressed_data, &td->uncompressed_size, uncompressed_size); - av_fast_padded_malloc(&td->tmp, &td->tmp_size, uncompressed_size); - if (!td->uncompressed_data || !td->tmp) + + if (!td->uncompressed_data) return AVERROR(ENOMEM); ret = AVERROR_INVALIDDATA; @@ -1015,17 +1084,42 @@ static int decode_block(AVCodecContext *avctx, void *tdata, } src = td->uncompressed_data; } + + if (s->is_tile){ + indexSrc = 0; + channelLineSize = s->xsize * 2; + if (s->pixel_type == EXR_FLOAT) + channelLineSize *= 2; + + /* reorganise tile data to have each channel one after the other instead of line by line */ + for (tY = 0; tY < s->ysize; tY ++) { + for (tCh = 0; tCh < s->nb_channels; tCh++) { + for (tX = 0; tX < channelLineSize; tX++) { + td->tmp[tCh * channelLineSize * s->ysize + tY * channelLineSize + tX] = src[indexSrc]; + indexSrc++; + } + } + } + + channel_buffer[0] = td->tmp + s->xsize * s->channel_offsets[0] * s->ysize;//0 + channel_buffer[1] = td->tmp + s->xsize * s->channel_offsets[1] * s->ysize;//1 + channel_buffer[2] = td->tmp + s->xsize * s->channel_offsets[2] * s->ysize;//2 + if (s->channel_offsets[3] >= 0) + channel_buffer[3] = td->tmp + s->xsize * s->channel_offsets[3]; + } + else{ /* scanline */ + channel_buffer[0] = src + xdelta * s->channel_offsets[0]; + channel_buffer[1] = src + xdelta * s->channel_offsets[1]; + channel_buffer[2] = src + xdelta * s->channel_offsets[2]; + if (s->channel_offsets[3] >= 0) + channel_buffer[3] = src + xdelta * s->channel_offsets[3]; + } - channel_buffer[0] = src + xdelta * s->channel_offsets[0]; - channel_buffer[1] = src + xdelta * s->channel_offsets[1]; - channel_buffer[2] = src + xdelta * s->channel_offsets[2]; - if (s->channel_offsets[3] >= 0) - channel_buffer[3] = src + xdelta * s->channel_offsets[3]; + ptr = p->data[0] + line * p->linesize[0] + (col * s->desc->nb_components * 2); - ptr = p->data[0] + line * p->linesize[0]; for (i = 0; - i < s->scan_lines_per_block && line + i <= s->ymax; - i++, ptr += p->linesize[0]) { + i < s->ysize; i++, ptr += p->linesize[0]){ + const uint8_t *r, *g, *b, *a; r = channel_buffer[0]; @@ -1039,10 +1133,11 @@ 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; + if (s->pixel_type == EXR_FLOAT) { // 32-bit if (trc_func) { - for (x = 0; x < xdelta; x++) { + for (x = 0; x < s->xsize; x++) { union av_intfloat32 t; t.i = bytestream_get_le32(&r); t.f = trc_func(t.f); @@ -1059,7 +1154,7 @@ static int decode_block(AVCodecContext *avctx, void *tdata, *ptr_x++ = exr_flt2uint(bytestream_get_le32(&a)); } } else { - for (x = 0; x < xdelta; x++) { + for (x = 0; x < s->xsize; x++) { union av_intfloat32 t; t.i = bytestream_get_le32(&r); if (t.f > 0.0f) /* avoid negative values */ @@ -1081,7 +1176,7 @@ static int decode_block(AVCodecContext *avctx, void *tdata, } } else { // 16-bit - for (x = 0; x < xdelta; x++) { + for (x = 0; x < s->xsize; x++) { *ptr_x++ = s->gamma_table[bytestream_get_le16(&r)]; *ptr_x++ = s->gamma_table[bytestream_get_le16(&g)]; *ptr_x++ = s->gamma_table[bytestream_get_le16(&b)]; @@ -1093,11 +1188,20 @@ static int decode_block(AVCodecContext *avctx, void *tdata, // Zero out the end if xmax+1 is not w memset(ptr_x, 0, axmax); - channel_buffer[0] += s->scan_line_size; - channel_buffer[1] += s->scan_line_size; - channel_buffer[2] += s->scan_line_size; - if (channel_buffer[3]) - channel_buffer[3] += s->scan_line_size; + if (s->is_tile){ + channel_buffer[0] += channelLineSize; + channel_buffer[1] += channelLineSize; + channel_buffer[2] += channelLineSize; + if (channel_buffer[3]) + channel_buffer[3] += channelLineSize; + } + else{ + channel_buffer[0] += s->scan_line_size; + channel_buffer[1] += s->scan_line_size; + channel_buffer[2] += s->scan_line_size; + if (channel_buffer[3]) + channel_buffer[3] += s->scan_line_size; + } } return 0; @@ -1146,9 +1250,9 @@ static int check_header_variable(EXRContext *s, static int decode_header(EXRContext *s) { - int current_channel_offset = 0; - int magic_number, version, flags, i, sar = 0; - + int magic_number, version, i, flags, sar = 0; + + s->current_channel_offset = 0; s->xmin = ~0; s->xmax = ~0; s->ymin = ~0; @@ -1164,6 +1268,9 @@ static int decode_header(EXRContext *s) s->nb_channels = 0; s->w = 0; s->h = 0; + s->tile_attr.xSize = -1; + s->tile_attr.ySize = -1; + s->is_tile = 0; if (bytestream2_get_bytes_left(&s->gb) < 10) { av_log(s->avctx, AV_LOG_ERROR, "Header too short to parse.\n"); @@ -1185,8 +1292,13 @@ static int decode_header(EXRContext *s) } flags = bytestream2_get_le24(&s->gb); - if (flags & 0x02) { - avpriv_report_missing_feature(s->avctx, "Tile support"); + + if (flags == 0x00) + s->is_tile = 0; + else if (flags & 0x02) + s->is_tile = 1; + else{ + avpriv_report_missing_feature(s->avctx, "flags %d", flags); return AVERROR_PATCHWELCOME; } @@ -1270,7 +1382,7 @@ static int decode_header(EXRContext *s) return AVERROR_INVALIDDATA; } s->pixel_type = current_pixel_type; - s->channel_offsets[channel_index] = current_channel_offset; + s->channel_offsets[channel_index] = s->current_channel_offset; } s->channels = av_realloc(s->channels, @@ -1282,7 +1394,7 @@ static int decode_header(EXRContext *s) channel->xsub = xsub; channel->ysub = ysub; - current_channel_offset += 1 << current_pixel_type; + s->current_channel_offset += 1 << current_pixel_type; } /* Check if all channels are set with an offset or if the channels @@ -1359,6 +1471,32 @@ static int decode_header(EXRContext *s) "Found more than one compression attribute.\n"); continue; + } else if ((var_size = check_header_variable(s, "tiles", + "tiledesc", 22)) >= 0) { + if (!s->is_tile) + av_log(s->avctx, AV_LOG_WARNING, + "Found tile attribute and scanline flags. Exr will be interpreted as scanline.\n"); + + s->tile_attr.xSize = bytestream2_get_le32(&s->gb); + s->tile_attr.ySize = bytestream2_get_le32(&s->gb); + + char tileLevel = bytestream2_get_byte(&s->gb); + s->tile_attr.level_mode = tileLevel & 0x0f; + s->tile_attr.level_round = (tileLevel >> 4) & 0x0f; + + if (s->tile_attr.level_mode != EXR_TILE_LEVEL_ONE){ + avpriv_report_missing_feature(s->avctx, "Tile level mode %d", + s->tile_attr.level_mode); + return AVERROR_PATCHWELCOME; + } + + if (s->tile_attr.level_round != EXR_TILE_ROUND_UP){ + avpriv_report_missing_feature(s->avctx, "Tile level round %d", + s->tile_attr.level_round); + return AVERROR_PATCHWELCOME; + } + + continue; } // Check if there are enough bytes for a header @@ -1381,7 +1519,20 @@ static int decode_header(EXRContext *s) av_log(s->avctx, AV_LOG_ERROR, "Missing compression attribute.\n"); return AVERROR_INVALIDDATA; } - s->scan_line_size = s->xdelta * current_channel_offset; + + if (s->is_tile) { + if ((s->tile_attr.xSize < 1)||(s->tile_attr.ySize < 1)){ + av_log(s->avctx, AV_LOG_ERROR, "Invalid tile attribute.\n"); + return AVERROR_INVALIDDATA; + } + + if (s->compression != EXR_RAW) { + avpriv_report_missing_feature(s->avctx, "Compression in tile %d", s->compression); + return AVERROR_PATCHWELCOME; + } + } + + s->scan_line_size = s->xdelta * s->current_channel_offset; if (bytestream2_get_bytes_left(&s->gb) <= 0) { av_log(s->avctx, AV_LOG_ERROR, "Incomplete frame.\n"); @@ -1403,7 +1554,7 @@ static int decode_frame(AVCodecContext *avctx, void *data, int y, ret; int out_line_size; - int scan_line_blocks; + int nb_blocks;/* nb scanline or nb tile */ bytestream2_init(&s->gb, avpkt->data, avpkt->size); @@ -1467,13 +1618,20 @@ static int decode_frame(AVCodecContext *avctx, void *data, if (!s->desc) return AVERROR_INVALIDDATA; out_line_size = avctx->width * 2 * s->desc->nb_components; - scan_line_blocks = (s->ydelta + s->scan_lines_per_block - 1) / - s->scan_lines_per_block; + + if (s->is_tile) { + nb_blocks = ((s->xdelta + s->tile_attr.xSize - 1) / s->tile_attr.xSize) * + ((s->ydelta + s->tile_attr.ySize - 1) / s->tile_attr.ySize); + } + else {/* scanline */ + nb_blocks = (s->ydelta + s->scan_lines_per_block - 1) / + s->scan_lines_per_block; + } if ((ret = ff_thread_get_buffer(avctx, &frame, 0)) < 0) return ret; - - if (bytestream2_get_bytes_left(&s->gb) < scan_line_blocks * 8) + + if (bytestream2_get_bytes_left(&s->gb) < nb_blocks * 8) return AVERROR_INVALIDDATA; // save pointer we are going to use in decode_block @@ -1488,7 +1646,8 @@ static int decode_frame(AVCodecContext *avctx, void *data, } s->picture = picture; - avctx->execute2(avctx, decode_block, s->thread_data, NULL, scan_line_blocks); + + avctx->execute2(avctx, decode_block, s->thread_data, NULL, nb_blocks); // Zero out the end if ymax+1 is not h for (y = s->ymax + 1; y < avctx->height; y++) { -- 1.9.3 (Apple Git-50)
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel