X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavcodec%2Fexr.c;h=a95423f1dfdbda81329c91febfdda6af7441c799;hb=fec512a52cdd1bab84958474cc160fdb7be81dec;hp=579399e90f54f981c24747d199e06be4e582eb73;hpb=634c01bc189ad5775b438dca1abd546eed6c825d;p=ffmpeg diff --git a/libavcodec/exr.c b/libavcodec/exr.c index 579399e90f5..a95423f1dfd 100644 --- a/libavcodec/exr.c +++ b/libavcodec/exr.c @@ -20,7 +20,7 @@ */ /** - * @file libavcodec/exr.c + * @file * OpenEXR decoder * @author Jimmy Christensen * @@ -47,7 +47,7 @@ typedef struct EXRContext { AVFrame picture; int compr; int bits_per_color_id; - int8_t channel_offsets[3]; // 0 = red, 1 = green and 2 = blue + int8_t channel_offsets[4]; // 0 = red, 1 = green, 2 = blue and 3 = alpha } EXRContext; /** @@ -77,15 +77,13 @@ static inline uint16_t exr_flt2uint(uint32_t v) */ static inline uint16_t exr_halflt2uint(uint16_t v) { - int exp = v >> 10; - if (v & 0x8000) - return 0; - if (!exp) - return (v >> 9) & 1; - if (exp >= 15) - return 0xffff; + unsigned exp = 14 - (v >> 10); + if (exp >= 14) { + if (exp == 14) return (v >> 9) & 1; + else return (v & 0x8000) ? 0 : 0xffff; + } v <<= 6; - return (v + (1 << 16)) >> (15 - exp); + return (v + (1 << 16)) >> (exp + 1); } /** @@ -117,9 +115,9 @@ static unsigned int get_header_variable_length(const uint8_t **buf, * @param minimum_length minimum length of the variable data * @param variable_buffer_data_size variable length read from the header * after it's checked - * @return zero if variable is invalid and 1 if good + * @return negative if variable is invalid */ -static unsigned int check_header_variable(AVCodecContext *avctx, +static int check_header_variable(AVCodecContext *avctx, const uint8_t **buf, const uint8_t *buf_end, const char *value_name, @@ -133,13 +131,15 @@ static unsigned int check_header_variable(AVCodecContext *avctx, *buf += strlen(value_type)+1; *variable_buffer_data_size = get_header_variable_length(buf, buf_end); if (!*variable_buffer_data_size) - av_log(avctx, AVERROR_INVALIDDATA, "Incomplete header\n"); + av_log(avctx, AV_LOG_ERROR, "Incomplete header\n"); + if (*variable_buffer_data_size > buf_end - *buf) + return -1; return 1; } *buf -= strlen(value_name)+1; av_log(avctx, AV_LOG_WARNING, "Unknown data type for header variable %s\n", value_name); } - return 0; + return -1; } static int decode_frame(AVCodecContext *avctx, @@ -156,7 +156,7 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *const p = &s->picture; uint8_t *ptr; - int x, y, stride, magic_number, version_flag; + int i, x, y, stride, magic_number, version_flag; int w = 0; int h = 0; unsigned int xmin = ~0; @@ -170,8 +170,14 @@ static int decode_frame(AVCodecContext *avctx, s->channel_offsets[0] = -1; s->channel_offsets[1] = -1; s->channel_offsets[2] = -1; + s->channel_offsets[3] = -1; s->bits_per_color_id = -1; + if (buf_end - buf < 10) { + av_log(avctx, AV_LOG_ERROR, "Too short header to parse\n"); + return -1; + } + magic_number = bytestream_get_le32(&buf); if (magic_number != 20000630) { // As per documentation of OpenEXR it's supposed to be int 20000630 little-endian av_log(avctx, AV_LOG_ERROR, "Wrong magic number %d\n", magic_number); @@ -180,25 +186,20 @@ static int decode_frame(AVCodecContext *avctx, version_flag = bytestream_get_le32(&buf); if ((version_flag & 0x200) == 0x200) { - av_log(avctx, AVERROR_NOFMT, "Tile based images are not supported\n"); - return -1; - } - - if (buf_end - buf < 10) { - av_log(avctx, AVERROR_INVALIDDATA, "Too short header to parse\n"); + av_log(avctx, AV_LOG_ERROR, "Tile based images are not supported\n"); return -1; } // Parse the header - while (buf[0] != 0x0) { + while (buf < buf_end && buf[0]) { unsigned int variable_buffer_data_size; // Process the channel list - if (check_header_variable(avctx, &buf, buf_end, "channels", "chlist", 38, &variable_buffer_data_size)) { + if (check_header_variable(avctx, &buf, buf_end, "channels", "chlist", 38, &variable_buffer_data_size) >= 0) { const uint8_t *channel_list_end; if (!variable_buffer_data_size) return -1; - channel_list_end = buf + variable_buffer_data_size + 4; + channel_list_end = buf + variable_buffer_data_size; while (channel_list_end - buf >= 19) { int current_bits_per_color_id = -1; int channel_index = -1; @@ -209,6 +210,8 @@ static int decode_frame(AVCodecContext *avctx, channel_index = 1; if (!strcmp(buf, "B")) channel_index = 2; + if (!strcmp(buf, "A")) + channel_index = 3; while (bytestream_get_byte(&buf) && buf < channel_list_end) continue; /* skip */ @@ -257,7 +260,7 @@ static int decode_frame(AVCodecContext *avctx, } // Process the dataWindow variable - if (check_header_variable(avctx, &buf, buf_end, "dataWindow", "box2i", 31, &variable_buffer_data_size)) { + if (check_header_variable(avctx, &buf, buf_end, "dataWindow", "box2i", 31, &variable_buffer_data_size) >= 0) { if (!variable_buffer_data_size) return -1; @@ -272,7 +275,7 @@ static int decode_frame(AVCodecContext *avctx, } // Process the displayWindow variable - if (check_header_variable(avctx, &buf, buf_end, "displayWindow", "box2i", 34, &variable_buffer_data_size)) { + if (check_header_variable(avctx, &buf, buf_end, "displayWindow", "box2i", 34, &variable_buffer_data_size) >= 0) { if (!variable_buffer_data_size) return -1; @@ -284,7 +287,7 @@ static int decode_frame(AVCodecContext *avctx, } // Process the lineOrder variable - if (check_header_variable(avctx, &buf, buf_end, "lineOrder", "lineOrder", 25, &variable_buffer_data_size)) { + if (check_header_variable(avctx, &buf, buf_end, "lineOrder", "lineOrder", 25, &variable_buffer_data_size) >= 0) { if (!variable_buffer_data_size) return -1; @@ -298,13 +301,13 @@ static int decode_frame(AVCodecContext *avctx, } // Process the compression variable - if (check_header_variable(avctx, &buf, buf_end, "compression", "compression", 29, &variable_buffer_data_size)) { + if (check_header_variable(avctx, &buf, buf_end, "compression", "compression", 29, &variable_buffer_data_size) >= 0) { if (!variable_buffer_data_size) return -1; - switch (*buf) { + s->compr = *buf; + switch (s->compr) { case EXR_RAW: - s->compr = *buf; break; case EXR_RLE: case EXR_ZIP1: @@ -312,7 +315,7 @@ static int decode_frame(AVCodecContext *avctx, case EXR_PIZ: case EXR_B44: default: - av_log(avctx, AV_LOG_ERROR, "This type of compression is not supported\n"); + av_log(avctx, AV_LOG_ERROR, "Compression type %d is not supported\n", s->compr); return -1; } @@ -327,9 +330,9 @@ static int decode_frame(AVCodecContext *avctx, } // Process unknown variables - for (int i = 0; i < 2; i++) { + for (i = 0; i < 2; i++) { // Skip variable name/type - while (buf++ < buf_end) + while (++buf < buf_end) if (buf[0] == 0x0) break; } @@ -354,7 +357,10 @@ static int decode_frame(AVCodecContext *avctx, switch (s->bits_per_color_id) { case 2: // 32-bit case 1: // 16-bit - avctx->pix_fmt = PIX_FMT_RGB48; + if (s->channel_offsets[3] >= 0) + avctx->pix_fmt = PIX_FMT_RGBA64; + else + avctx->pix_fmt = PIX_FMT_RGB48; break; // 8-bit case 0: @@ -371,11 +377,7 @@ static int decode_frame(AVCodecContext *avctx, return -1; // Verify the xmin, xmax, ymin, ymax and xdelta before setting the actual image size - if (xmin > w || xmin == ~0 || - xmax > w || xmax == ~0 || - ymin > h || ymin == ~0 || - ymax > h || ymax == ~0 || - xdelta == ~0) { + if (xmin > xmax || ymin > ymax || xdelta != xmax - xmin + 1 || xmax >= w || ymax >= h) { av_log(avctx, AV_LOG_ERROR, "Wrong sizing or missing size information\n"); return -1; } @@ -394,7 +396,7 @@ static int decode_frame(AVCodecContext *avctx, // Zero out the start if ymin is not 0 for (y = 0; y < ymin; y++) { - memset(ptr, 0, avctx->width * 6); + memset(ptr, 0, avctx->width * 2 * av_pix_fmt_descriptors[avctx->pix_fmt].nb_components); ptr += stride; } @@ -409,21 +411,27 @@ static int decode_frame(AVCodecContext *avctx, if (line_offset > avpkt->size - xdelta * current_channel_offset) { // Line offset is probably wrong and not inside the buffer av_log(avctx, AV_LOG_WARNING, "Line offset for line %d is out of reach setting it to black\n", y); - memset(ptr_x, 0, avctx->width * 6); + memset(ptr_x, 0, avctx->width * 2 * av_pix_fmt_descriptors[avctx->pix_fmt].nb_components); } else { const uint8_t *red_channel_buffer = avpkt->data + line_offset + xdelta * s->channel_offsets[0]; const uint8_t *green_channel_buffer = avpkt->data + line_offset + xdelta * s->channel_offsets[1]; const uint8_t *blue_channel_buffer = avpkt->data + line_offset + xdelta * s->channel_offsets[2]; + const uint8_t *alpha_channel_buffer = 0; + + if (s->channel_offsets[3] >= 0) + alpha_channel_buffer = avpkt->data + line_offset + xdelta * s->channel_offsets[3]; // Zero out the start if xmin is not 0 - memset(ptr_x, 0, xmin * 6); - ptr_x += xmin * 3; + memset(ptr_x, 0, xmin * 2 * av_pix_fmt_descriptors[avctx->pix_fmt].nb_components); + ptr_x += xmin * av_pix_fmt_descriptors[avctx->pix_fmt].nb_components; if (s->bits_per_color_id == 2) { // 32-bit for (x = 0; x < xdelta; x++) { *ptr_x++ = exr_flt2uint(bytestream_get_le32(&red_channel_buffer)); *ptr_x++ = exr_flt2uint(bytestream_get_le32(&green_channel_buffer)); *ptr_x++ = exr_flt2uint(bytestream_get_le32(&blue_channel_buffer)); + if (alpha_channel_buffer) + *ptr_x++ = exr_flt2uint(bytestream_get_le32(&alpha_channel_buffer)); } } else { // 16-bit @@ -431,12 +439,14 @@ static int decode_frame(AVCodecContext *avctx, *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&red_channel_buffer)); *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&green_channel_buffer)); *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&blue_channel_buffer)); + if (alpha_channel_buffer) + *ptr_x++ = exr_halflt2uint(bytestream_get_le16(&alpha_channel_buffer)); } } // Zero out the end if xmax+1 is not w - memset(ptr_x, 0, (avctx->width - (xmax + 1)) * 6); - ptr_x += (avctx->width - (xmax + 1)) * 3; + memset(ptr_x, 0, (avctx->width - (xmax + 1)) * 2 * av_pix_fmt_descriptors[avctx->pix_fmt].nb_components); + ptr_x += (avctx->width - (xmax + 1)) * av_pix_fmt_descriptors[avctx->pix_fmt].nb_components; } // Move to next line @@ -445,8 +455,8 @@ static int decode_frame(AVCodecContext *avctx, } // Zero out the end if ymax+1 is not h - for (y = ymax; y < avctx->height; y++) { - memset(ptr, 0, avctx->width * 6); + for (y = ymax + 1; y < avctx->height; y++) { + memset(ptr, 0, avctx->width * 2 * av_pix_fmt_descriptors[avctx->pix_fmt].nb_components); ptr += stride; }