From bcd455be7a9b7258903e46f2c62d413e648971b5 Mon Sep 17 00:00:00 2001 From: Robin Mills Date: Thu, 11 Aug 2016 10:12:25 +0000 Subject: [PATCH] #1199 Patch from Ben. http://dev.exiv2.org/issues/1199#note-37 --- include/exiv2/webpimage.hpp | 3 +- src/webpimage.cpp | 150 ++++++++++++++++++++++++++++++------ 2 files changed, 128 insertions(+), 25 deletions(-) diff --git a/include/exiv2/webpimage.hpp b/include/exiv2/webpimage.hpp index a2f59cd0..f1306b3a 100644 --- a/include/exiv2/webpimage.hpp +++ b/include/exiv2/webpimage.hpp @@ -94,7 +94,8 @@ namespace Exiv2 { bool equalsWebPTag(Exiv2::DataBuf& buf ,const char* str); void decodeChunks(uint64_t filesize); void inject_VP8X(BasicIo& iIo, bool has_xmp, bool has_exif, - bool has_alpha, bool has_icc); + bool has_alpha, bool has_icc, int width, + int height); //! Copy constructor WebPImage(const WebPImage& rhs); diff --git a/src/webpimage.cpp b/src/webpimage.cpp index e66ef773..1795cd6e 100644 --- a/src/webpimage.cpp +++ b/src/webpimage.cpp @@ -120,19 +120,22 @@ namespace Exiv2 { io_->read(data, TAG_SIZE * 3); uint64_t filesize = Exiv2::getULong(data + 4, littleEndian); - // uint64_t endoffile = 12; /* Set up header */ if (outIo.write(data, TAG_SIZE * 3) != TAG_SIZE * 3) throw Error(21); /* Parse Chunks */ + bool has_size = false; bool has_xmp = false; bool has_exif = false; bool has_vp8x = false; bool has_alpha = false; bool has_icc = false; + int height; + int width; + byte size_buff[4]; std::string xmpData; Blob blob; @@ -176,37 +179,117 @@ namespace Exiv2 { uint64_t size = Exiv2::getULong(size_buff, littleEndian); DataBuf payload(size); io_->read(payload.pData_, payload.size_); + + /* Chunk with color profile. */ if (equalsWebPTag(chunkId, "ICCP") && !has_alpha) { has_icc = true; } - if (equalsWebPTag(chunkId, "VP8X")) { + + /* Chunk with information about features + used in the file. */ + if (equalsWebPTag(chunkId, "VP8X") && !has_vp8x) { has_vp8x = true; } -#if 0 // May need to verify for alpha for these chunks in the future - if (equalsWebPTag(chunkId, "VP8 ") && !has_alpha) { - has_alpha = true; + if (equalsWebPTag(chunkId, "VP8X") && !has_size) { + has_size = true; + byte size_buf[4]; + + // Fetch width + memcpy(&size_buf, &payload.pData_[4], 3); + size_buf[3] = 0; + width = Exiv2::getULong(size_buf, littleEndian) + 1; + + // Fetch height + memcpy(&size_buf, &payload.pData_[7], 3); + size_buf[3] = 0; + height = Exiv2::getULong(size_buf, littleEndian) + 1; } + + /* Chunk with with animation control data. */ +#ifdef __CHECK_FOR_ALPHA__ // Maybe in the future if (equalsWebPTag(chunkId, "ANIM") && !has_alpha) { has_alpha = true; } #endif + + /* Chunk with with lossy image data. */ +#ifdef __CHECK_FOR_ALPHA__ // Maybe in the future + if (equalsWebPTag(chunkId, "VP8 ") && !has_alpha) { + has_alpha = true; + } +#endif + if (equalsWebPTag(chunkId, "VP8 ") && !has_size) { + has_size = true; + byte size_buf[4]; + + // Fetch width + memcpy(&size_buf, &payload.pData_[6], 2); + size_buf[2] = 0; + size_buf[3] = 0; + width = Exiv2::getULong(size_buf, littleEndian) & 0x3fff; + + // Fetch height + memcpy(&size_buf, &payload.pData_[8], 2); + size_buf[2] = 0; + size_buf[3] = 0; + height = Exiv2::getULong(size_buf, littleEndian) & 0x3fff; + } + + /* Chunk with with lossless image data. */ if (equalsWebPTag(chunkId, "VP8L") && !has_alpha) { if ((payload.pData_[5] & 0x10) == 0x10) { has_alpha = true; } } + if (equalsWebPTag(chunkId, "VP8L") && !has_size) { + has_size = true; + byte size_buf_w[2]; + byte size_buf_h[3]; + + // Fetch width + memcpy(&size_buf_w, &payload.pData_[1], 2); + size_buf_w[1] &= 0x3F; + width = Exiv2::getUShort(size_buf_w, littleEndian) + 1; + + // Fetch height + memcpy(&size_buf_h, &payload.pData_[2], 3); + size_buf_h[0] = ((size_buf_h[0] >> 6) & 0x3) | ((size_buf_h[1] & 0x3F) << 0x2); + size_buf_h[1] = ((size_buf_h[1] >> 6) & 0x3) | ((size_buf_h[2] & 0xF) << 0x2); + height = Exiv2::getUShort(size_buf_h, littleEndian) + 1; + } + + /* Chunk with animation frame. */ if (equalsWebPTag(chunkId, "ANMF") && !has_alpha) { if ((payload.pData_[5] & 0x2) == 0x2) { has_alpha = true; } } + if (equalsWebPTag(chunkId, "ANMF") && !has_size) { + has_size = true; + byte size_buf[4]; + + // Fetch width + memcpy(&size_buf, &payload.pData_[6], 3); + size_buf[3] = 0; + width = Exiv2::getULong(size_buf, littleEndian) + 1; + + // Fetch height + memcpy(&size_buf, &payload.pData_[9], 3); + size_buf[3] = 0; + height = Exiv2::getULong(size_buf, littleEndian) + 1; + } + + /* Chunk with alpha data. */ if (equalsWebPTag(chunkId, "ALPH") && !has_alpha) { has_alpha = true; } } + std::cout << "VP8X res. size [" << width << "x" << height << "]\n"; + /* Inject a VP8X chunk if one isn't available. */ if (!has_vp8x) { - inject_VP8X(outIo, has_xmp, has_exif, has_alpha, has_icc); + inject_VP8X(outIo, has_xmp, has_exif, has_alpha, + has_icc, width, height); } } @@ -272,24 +355,24 @@ namespace Exiv2 { offset = io_->tell(); // Check for extra \0 padding - byte one_character[1]; bool has_zero = false; while (!io_->eof() && !has_zero && offset < filesize) { - io_->read(one_character, 1); - if (one_character[0] != 0) { + byte c = 0; + io_->read(&c, 1); + if ( c ) { io_->seek(-1, BasicIo::cur); break; } else { has_zero = true; - if (outIo.write(one_character, 1) != 1) + if (outIo.write(&c, 1) != 1) throw Error(21); } } - // Encoder seems to pad on odd sized data. - one_character[0] = 0x0; - if (size % 2 != 0 && !has_zero) { - if (outIo.write(one_character, 1) != 1) + // Encoder required to pad odd sized data with a null byte + if (size % 2 && !has_zero) { + byte c = 0; + if (outIo.write(&c, 1) != 1) throw Error(21); } @@ -448,11 +531,16 @@ namespace Exiv2 { if (equalsWebPTag(chunkId, "VP8X") && !has_canvas_data) { has_canvas_data = true; - io_->read(payload.pData_, payload.size_); byte size_buf[4]; + + io_->read(payload.pData_, payload.size_); + + // Fetch width memcpy(&size_buf, &payload.pData_[4], 3); size_buf[3] = 0; pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) + 1; + + // Fetch height memcpy(&size_buf, &payload.pData_[7], 3); size_buf[3] = 0; pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) + 1; @@ -460,33 +548,47 @@ namespace Exiv2 { has_canvas_data = true; io_->read(payload.pData_, payload.size_); byte size_buf[4]; + + // Fetch width"" memcpy(&size_buf, &payload.pData_[6], 2); size_buf[2] = 0; size_buf[3] = 0; pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) & 0x3fff; + + // Fetch height memcpy(&size_buf, &payload.pData_[8], 2); size_buf[2] = 0; size_buf[3] = 0; pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) & 0x3fff; } else if (equalsWebPTag(chunkId, "VP8L") && !has_canvas_data) { has_canvas_data = true; - io_->read(payload.pData_, payload.size_); byte size_buf_w[2]; + byte size_buf_h[3]; + + io_->read(payload.pData_, payload.size_); + + // Fetch width memcpy(&size_buf_w, &payload.pData_[1], 2); size_buf_w[1] &= 0x3F; pixelWidth_ = Exiv2::getUShort(size_buf_w, littleEndian) + 1; - byte size_buf_h[3]; + + // Fetch height memcpy(&size_buf_h, &payload.pData_[2], 3); - size_buf_h[0] = ((size_buf_h[0] >> 6) & 0x3) | ((size_buf_h[1] & 0x3F) << 0x2); - size_buf_h[1] = ((size_buf_h[1] >> 6) & 0x3) | ((size_buf_h[2] & 0x3F) << 0x2); + size_buf_h[0] = ((size_buf_h[0] >> 6) & 0x3) | ((size_buf_h[1] & 0x3F) << 0x2); + size_buf_h[1] = ((size_buf_h[1] >> 6) & 0x3) | ((size_buf_h[2] & 0xF) << 0x2); pixelHeight_ = Exiv2::getUShort(size_buf_h, littleEndian) + 1; } else if (equalsWebPTag(chunkId, "ANMF") && !has_canvas_data) { has_canvas_data = true; - io_->read(payload.pData_, payload.size_); byte size_buf[4]; + + io_->read(payload.pData_, payload.size_); + + // Fetch width memcpy(&size_buf, &payload.pData_[6], 3); size_buf[3] = 0; pixelWidth_ = Exiv2::getULong(size_buf, littleEndian) + 1; + + // Fetch height memcpy(&size_buf, &payload.pData_[9], 3); size_buf[3] = 0; pixelHeight_ = Exiv2::getULong(size_buf, littleEndian) + 1; @@ -574,7 +676,7 @@ namespace Exiv2 { return image; } - bool isWebPType(BasicIo& iIo, bool /*advance*/) + bool isWebPType(BasicIo& iIo, bool /*advance*/) { const int32_t len = 4; const unsigned char RiffImageId[4] = { 'R', 'I', 'F' ,'F'}; @@ -616,7 +718,7 @@ namespace Exiv2 { */ void WebPImage::inject_VP8X(BasicIo& iIo, bool has_xmp, bool has_exif, bool has_alpha, - bool has_icc) { + bool has_icc, int width, int height) { byte header[4]; byte size[4] = { 0x0A, 0x00, 0x00, 0x00 }; byte data[10] = { 0x00, 0x00, 0x00, 0x00, 0x00, @@ -642,13 +744,13 @@ namespace Exiv2 { } /* set width */ - int w = pixelWidth_- 1; + int w = width - 1; data[4] = w & 0xFF; data[5] = (w >> 8) & 0xFF; data[6] = (w >> 16) & 0xFF; /* set width */ - int h = pixelHeight_- 1; + int h = height - 1; data[7] = h & 0xFF; data[8] = (h >> 8) & 0xFF; data[9] = (h >> 16) & 0xFF;