Hello, in attach, a patch with some modifications in size/data window management, in order to manage negative datawindow/display window, and datawindow who can exceed display window
Size of the picture is now set from min and max value of the display window (before the patch, xmin, ymin of the display window are ignored (and assume to be 0)) With the previous patch (fix channel detection), official samples ./scanline/Blobbies.exr can now be correctly decode. DataWindow bigger than display window is a feature of the openExr. It can be use in some softwares, (for example to improve blur quality) Following the official documentation, for display, we need to use the display window only. In this patch, xmin, xmax, ymin, ymax, are now int32_t instead of uint32_t, because these values can be negative. (See officiel sample ./Displaywindow http://download.savannah.nongnu.org/releases/openexr/openexr-images-1.7.0.tar.gz ) Comments welcome Martin Jokyo Images
From 2c040349da52737de753c5c45c544445fdd358e4 Mon Sep 17 00:00:00 2001 From: Martin Vignali <martin.vign...@gmail.com> Date: Thu, 7 Apr 2016 23:01:45 +0200 Subject: [PATCH] libavcodec/exr : support datawindow who exceed display window. --- libavcodec/exr.c | 174 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 112 insertions(+), 62 deletions(-) diff --git a/libavcodec/exr.c b/libavcodec/exr.c index 2763126..9de6910 100644 --- a/libavcodec/exr.c +++ b/libavcodec/exr.c @@ -112,12 +112,15 @@ typedef struct EXRContext { enum ExrCompr compression; enum ExrPixelType pixel_type; + int pixel_size; int channel_offsets[4]; // 0 = red, 1 = green, 2 = blue and 3 = alpha const AVPixFmtDescriptor *desc; int w, h; - uint32_t xmax, xmin; - uint32_t ymax, ymin; + int32_t xmax, xmin; + int32_t ymax, ymin; + int32_t xmax_display, xmin_display; + int32_t ymax_display, ymin_display; uint32_t xdelta, ydelta; int ysize, xsize; @@ -985,17 +988,27 @@ 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, col = 0; + uint32_t data_size; + int32_t line, target_line, col = 0; + int32_t xmin_block, xmax_block, ymin_block, ymax_block;/* coord block */ + int32_t xmin_used = 0, xmax_used = 0;/* used coord of the data window in the final picture */ uint32_t tileX, tileY, tileLevelX, tileLevelY; int channelLineSize, indexSrc, tX, tY, tCh; const uint8_t *src; - 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 axmax = 0, bxmin = 0; 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; + /* calc nb pixels to add at the left of the data window */ + if (s->xmin_display < s->xmin) + bxmin = (s->xmin - s->xmin_display) * 2 * s->desc->nb_components; + + /* calc nb pixel to add at the right of the data window */ + if (s->xmax_display > s->xmax) + axmax = (s->xmax_display - s->xmax) * 2 * s->desc->nb_components; + line_offset = AV_RL64(s->gb.buffer + jobnr * 8); if (s->is_tile) { @@ -1019,14 +1032,19 @@ static int decode_block(AVCodecContext *avctx, void *tdata, } line = s->tile_attr.ySize * tileY; + target_line = line + s->ymin; 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); + + xmin_block = s->xmin + col; + ymin_block = s->ymin + line; + 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 */ + bxmin = 0; /* doesn't add pixel at the left of the datawindow */ } if ((col + s->xsize) != s->xdelta)/* not the last tile of the line */ @@ -1035,17 +1053,24 @@ static int decode_block(AVCodecContext *avctx, void *tdata, if (line_offset > buf_size - 8) return AVERROR_INVALIDDATA; + channelLineSize = s->scan_line_size; src = buf + line_offset + 8; - line = AV_RL32(src - 8); + line = (int32_t)AV_RL32(src - 8); if (line < s->ymin || line > s->ymax) return AVERROR_INVALIDDATA; + target_line = line; + 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; + + xmin_block = s->xmin; + ymin_block = line; + uncompressed_size = s->scan_line_size * s->ysize; if ((s->compression == EXR_RAW && (data_size != uncompressed_size || line_offset > buf_size - uncompressed_size)) || @@ -1055,6 +1080,27 @@ static int decode_block(AVCodecContext *avctx, void *tdata, } } + xmax_block = xmin_block + s->xsize - 1; + ymax_block = ymin_block + s->ysize - 1; + xmax_used = FFMIN(xmax_block, s->xmax_display); + xmin_used = FFMAX(xmin_block, s->xmin_display); + int32_t xmin_unused = 0, xmax_unused = 0; + int32_t line_to_skip = 0; + int32_t ymax_used = FFMIN(ymax_block, s->ymax_display); + + if ((ymax_block < s->ymin_display)||(ymin_block > s->ymax_display))/* scanline not use at start */ + return 0; + + int32_t ymin_used = FFMAX(ymin_block, s->ymin_display); + + line_to_skip = ymin_used - ymin_block; + + if (xmin_block < xmin_used) + xmin_unused = xmin_used - xmin_block;/* nb pixel to skip at the start of each line of the block */ + + if (xmax_block > xmax_used) + xmax_unused = xmax_block - xmax_used;/* nb pixel to skip at the end of each line of the block */ + 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) @@ -1097,9 +1143,7 @@ static int decode_block(AVCodecContext *avctx, void *tdata, if (s->is_tile) { indexSrc = 0; - channelLineSize = s->xsize * 2; - if (s->pixel_type == EXR_FLOAT) - channelLineSize *= 2; + channelLineSize = s->xsize * s->pixel_size; /* reorganise tile data to have each channel one after the other instead of line by line */ for (tY = 0; tY < s->ysize; tY ++) { @@ -1124,29 +1168,34 @@ static int decode_block(AVCodecContext *avctx, void *tdata, channel_buffer[3] = src + xdelta * s->channel_offsets[3]; } - ptr = p->data[0] + line * p->linesize[0] + (col * s->desc->nb_components * 2); + channel_buffer[0] += channelLineSize * line_to_skip; + channel_buffer[1] += channelLineSize * line_to_skip; + channel_buffer[2] += channelLineSize * line_to_skip; + if (channel_buffer[3]) + channel_buffer[3] += channelLineSize * line_to_skip; - for (i = 0; - i < s->ysize; i++, ptr += p->linesize[0]) { + ptr = p->data[0] + (target_line + line_to_skip) * p->linesize[0] + (col * s->desc->nb_components * 2); + for (i = ymin_used; + i <= ymax_used; i++, ptr += p->linesize[0]) { const uint8_t *r, *g, *b, *a; - r = channel_buffer[0]; - g = channel_buffer[1]; - b = channel_buffer[2]; + r = channel_buffer[0] + xmin_unused * s->pixel_size; + g = channel_buffer[1] + xmin_unused * s->pixel_size; + b = channel_buffer[2] + xmin_unused * s->pixel_size; if (channel_buffer[3]) - a = channel_buffer[3]; + a = channel_buffer[3] + xmin_unused * s->pixel_size; ptr_x = (uint16_t *) ptr; - // Zero out the start if xmin is not 0 + // Zero out the start if display window begin before datawindow memset(ptr_x, 0, bxmin); - ptr_x += s->xmin * s->desc->nb_components; + ptr_x += bxmin/2; if (s->pixel_type == EXR_FLOAT) { // 32-bit if (trc_func) { - for (x = 0; x < s->xsize; x++) { + for (x = xmin_used; x <= xmax_used; x++) { union av_intfloat32 t; t.i = bytestream_get_le32(&r); t.f = trc_func(t.f); @@ -1163,7 +1212,7 @@ static int decode_block(AVCodecContext *avctx, void *tdata, *ptr_x++ = exr_flt2uint(bytestream_get_le32(&a)); } } else { - for (x = 0; x < s->xsize; x++) { + for (x = xmin_used; x <= xmax_used; x++) { union av_intfloat32 t; t.i = bytestream_get_le32(&r); if (t.f > 0.0f) /* avoid negative values */ @@ -1185,7 +1234,7 @@ static int decode_block(AVCodecContext *avctx, void *tdata, } } else { // 16-bit - for (x = 0; x < s->xsize; x++) { + for (x = xmin_used; x <= xmax_used; 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)]; @@ -1194,22 +1243,14 @@ static int decode_block(AVCodecContext *avctx, void *tdata, } } - // Zero out the end if xmax+1 is not w + // Zero out the end if datawindow end before display window memset(ptr_x, 0, axmax); - 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; - } + channel_buffer[0] += channelLineSize; + channel_buffer[1] += channelLineSize; + channel_buffer[2] += channelLineSize; + if (channel_buffer[3]) + channel_buffer[3] += channelLineSize; } return 0; @@ -1258,13 +1299,17 @@ static int check_header_variable(EXRContext *s, static int decode_header(EXRContext *s) { - int magic_number, version, i, flags, sar = 0; + int magic_number, version, i, flags, sar = 0, ret; s->current_channel_offset = 0; s->xmin = ~0; s->xmax = ~0; s->ymin = ~0; s->ymax = ~0; + s->xmin_display = ~0; + s->xmax_display = ~0; + s->ymin_display = ~0; + s->ymax_display = ~0; s->xdelta = ~0; s->ydelta = ~0; s->channel_offsets[0] = -1; @@ -1434,15 +1479,23 @@ static int decode_header(EXRContext *s) s->xdelta = (s->xmax - s->xmin) + 1; s->ydelta = (s->ymax - s->ymin) + 1; + if (s->xmin > s->xmax || s->ymin > s->ymax) { + av_log(s->avctx, AV_LOG_ERROR, "Wrong datawindow.\n"); + return AVERROR_INVALIDDATA; + } + continue; } else if ((var_size = check_header_variable(s, "displayWindow", "box2i", 34)) >= 0) { if (!var_size) return AVERROR_INVALIDDATA; - bytestream2_skip(&s->gb, 8); - s->w = bytestream2_get_le32(&s->gb) + 1; - s->h = bytestream2_get_le32(&s->gb) + 1; + s->xmin_display = bytestream2_get_le32(&s->gb); + s->ymin_display = bytestream2_get_le32(&s->gb); + s->xmax_display = bytestream2_get_le32(&s->gb); + s->ymax_display = bytestream2_get_le32(&s->gb); + s->w = (s->xmax_display - s->xmin_display) + 1; + s->h = (s->ymax_display - s->ymin_display) + 1; continue; } else if ((var_size = check_header_variable(s, "lineOrder", @@ -1521,6 +1574,8 @@ static int decode_header(EXRContext *s) bytestream2_skip(&s->gb, bytestream2_get_le32(&s->gb)); } + if ((ret = ff_set_dimensions(s->avctx, s->w, s->h)) < 0) + return ret; ff_set_sar(s->avctx, av_d2q(av_int2float(sar), 255)); if (s->compression == EXR_UNKN) { @@ -1542,6 +1597,11 @@ static int decode_header(EXRContext *s) return AVERROR_INVALIDDATA; } + if (s->pixel_type == EXR_HALF) + s->pixel_size = 2; + else + s->pixel_size = 4; + // aaand we are done bytestream2_skip(&s->gb, 1); return 0; @@ -1603,20 +1663,6 @@ 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) { - av_log(avctx, AV_LOG_ERROR, "Wrong or missing size information.\n"); - return AVERROR_INVALIDDATA; - } - - if ((ret = ff_set_dimensions(avctx, s->w, s->h)) < 0) - return ret; - s->desc = av_pix_fmt_desc_get(avctx->pix_fmt); if (!s->desc) return AVERROR_INVALIDDATA; @@ -1641,20 +1687,24 @@ static int decode_frame(AVCodecContext *avctx, void *data, s->buf_size = avpkt->size; ptr = picture->data[0]; - // Zero out the start if ymin is not 0 - for (y = 0; y < s->ymin; y++) { - memset(ptr, 0, out_line_size); - ptr += picture->linesize[0]; + // Zero out the start lines if need + if (s->ymin_display < s->ymin) { + for (y = s->ymin_display; y < s->ymin; y++) { + memset(ptr, 0, out_line_size); + ptr += picture->linesize[0]; + } } s->picture = picture; 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++) { - memset(ptr, 0, out_line_size); - ptr += picture->linesize[0]; + // Zero out the end lines if need + if (s->ymax_display > s->ymax) { + for (y = s->ymax + 1; y < s->ymax_display; y++) { + memset(ptr, 0, out_line_size); + ptr += picture->linesize[0]; + } } picture->pict_type = AV_PICTURE_TYPE_I; -- 1.9.3 (Apple Git-50)
_______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel