diff options
Diffstat (limited to 'XMPFiles/source/FormatSupport/WEBP_Support.cpp')
-rw-r--r-- | XMPFiles/source/FormatSupport/WEBP_Support.cpp | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/XMPFiles/source/FormatSupport/WEBP_Support.cpp b/XMPFiles/source/FormatSupport/WEBP_Support.cpp new file mode 100644 index 0000000..2b007de --- /dev/null +++ b/XMPFiles/source/FormatSupport/WEBP_Support.cpp @@ -0,0 +1,273 @@ +#include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "public/include/XMP_Const.h" +#include "public/include/XMP_IO.hpp" + +#include "XMPFiles/source/XMPFiles_Impl.hpp" +#include "source/XIO.hpp" +#include "source/XMPFiles_IO.hpp" + +#include "XMPFiles/source/FileHandlers/WEBP_Handler.hpp" +#include "XMPFiles/source/FormatSupport/WEBP_Support.hpp" + +#include <stdexcept> // std::out_of_range +#include <vector> + +using namespace WEBP; + +namespace WEBP { + +// Constructor for reading an existing chunk +Chunk::Chunk(Container* parent, WEBP_MetaHandler* handler) +{ + this->needsRewrite = (parent) ? parent->needsRewrite : false; + this->parent = parent; + + XMP_IO* file = handler->parent->ioRef; + this->pos = file->Offset(); + + this->tag = XIO::ReadUns32_LE(file); + + this->size = XIO::ReadUns32_LE(file); + + // Make sure the size is within expected bounds. + if ((this->pos + this->size + 8) > handler->initialFileSize) { + XMP_Throw("Bad RIFF chunk size", kXMPErr_BadFileFormat); + } + + this->data.reserve((XMP_Int32) this->size); + this->data.assign((XMP_Int32) this->size, '\0'); + file->ReadAll((void*)this->data.data(), (XMP_Int32) this->size); + + // Account for padding + if (this->size & 1) { + file->Seek(1, kXMP_SeekFromCurrent); + } +} + +// Constructor for creating a new chunk +Chunk::Chunk(Container* parent, XMP_Uns32 tag) + : parent(parent), tag(tag) +{ + this->needsRewrite = true; +} + +void Chunk::write(WEBP_MetaHandler* handler) +{ + XMP_IO* file = handler->parent->ioRef; + if (this->needsRewrite) { + this->pos = file->Offset(); + XIO::WriteUns32_LE(file, this->tag); + XIO::WriteUns32_LE(file, (XMP_Uns32) this->size); + file->Write(this->data.data(), (XMP_Int32) this->size); + } + else { + file->Seek(this->pos + this->size + 8, kXMP_SeekFromStart); + } + if (this->size & 1) { + const XMP_Uns8 zero = 0; + file->Write(&zero, 1); + } +} + +Chunk::~Chunk() +{ + // Do nothing +} + +XMPChunk::XMPChunk(Container* parent) + : Chunk(parent, kChunk_XMP_) +{ + this->size = 0; +} + +XMPChunk::XMPChunk(Container* parent, WEBP_MetaHandler* handler) + : Chunk(parent, handler) +{ + handler->packetInfo.offset = this->pos + 8; + handler->packetInfo.length = (XMP_Int32) this->data.size(); + + handler->xmpPacket.reserve(handler->packetInfo.length); + handler->xmpPacket.assign(handler->packetInfo.length, '\0'); + handler->xmpPacket.insert(handler->xmpPacket.begin(), this->data.begin(), + this->data.end()); + + handler->containsXMP = true; // last, after all possible failure + + // pointer for later processing + handler->xmpChunk = this; +} + +void XMPChunk::write(WEBP_MetaHandler* handler) +{ + XMP_IO* file = handler->parent->ioRef; + this->size = handler->xmpPacket.size(); + XIO::WriteUns32_LE(file, this->tag); + XIO::WriteUns32_LE(file, (XMP_Uns32) this->size); + file->Write(handler->xmpPacket.data(), (XMP_Int32) this->size); + if (this->size & 1) { + const XMP_Uns8 zero = 0; + file->Write(&zero, 1); + } +} + +VP8XChunk::VP8XChunk(Container* parent) + : Chunk(parent, kChunk_VP8X) +{ + this->needsRewrite = true; + this->size = 10; + this->data.resize(this->size); + this->data.assign(this->size, 0); + XMP_Uns8* bitstream = + (XMP_Uns8*)parent->chunks[WEBP_CHUNK_IMAGE][0]->data.data(); + XMP_Uns32 width = ((bitstream[7] << 8) | bitstream[6]) & 0x3fff; + XMP_Uns32 height = ((bitstream[9] << 8) | bitstream[8]) & 0x3fff; + this->width(width); + this->height(height); + parent->vp8x = this; +} + +VP8XChunk::VP8XChunk(Container* parent, WEBP_MetaHandler* handler) + : Chunk(parent, handler) +{ + this->size = 10; + this->needsRewrite = true; + parent->vp8x = this; +} + +XMP_Uns32 VP8XChunk::width() +{ + return GetLE24(&this->data[4]) + 1; +} +void VP8XChunk::width(XMP_Uns32 val) +{ + PutLE24(&this->data[4], val - 1); +} +XMP_Uns32 VP8XChunk::height() +{ + return GetLE24(&this->data[7]) + 1; +} +void VP8XChunk::height(XMP_Uns32 val) +{ + PutLE24(&this->data[7], val - 1); +} +bool VP8XChunk::xmp() +{ + XMP_Uns32 flags = GetLE32(&this->data[0]); + return (bool)((flags >> XMP_FLAG_BIT) & 1); +} +void VP8XChunk::xmp(bool hasXMP) +{ + XMP_Uns32 flags = GetLE32(&this->data[0]); + flags ^= (-hasXMP ^ flags) & (1 << XMP_FLAG_BIT); + PutLE32(&this->data[0], flags); +} + +Container::Container(WEBP_MetaHandler* handler) : Chunk(NULL, handler) +{ + this->needsRewrite = false; + + XMP_IO* file = handler->parent->ioRef; + + file->Seek(12, kXMP_SeekFromStart); + + XMP_Int64 size = handler->initialFileSize; + + XMP_Uns32 peek = 0; + + while (file->Offset() < size) { + peek = XIO::PeekUns32_LE(file); + switch (peek) { + case kChunk_XMP_: + this->addChunk(new XMPChunk(this, handler)); + break; + case kChunk_VP8X: + this->addChunk(new VP8XChunk(this, handler)); + break; + default: + this->addChunk(new Chunk(this, handler)); + break; + } + } + + if (this->chunks[WEBP_CHUNK_IMAGE].size() == 0) { + XMP_Throw("File has no image bitstream", kXMPErr_BadFileFormat); + } + + if (this->chunks[WEBP_CHUNK_VP8X].size() == 0) { + // The file is either in Lossy or Lossless Simple File Format. + // For simplicity we will always convert it to the Extended File Format. + this->needsRewrite = true; + this->addChunk(new VP8XChunk(this)); + } + + if (this->chunks[WEBP_CHUNK_XMP].size() == 0) { + XMPChunk* xmpChunk = new XMPChunk(this); + this->addChunk(xmpChunk); + handler->xmpChunk = xmpChunk; + this->vp8x->xmp(true); + } +} + +Chunk* Container::getExifChunk() +{ + if (this->chunks[WEBP::WEBP_CHUNK_EXIF].size() == 0) { + return NULL; + } + return this->chunks[WEBP::WEBP_CHUNK_EXIF][0]; +} + +void Container::addChunk(Chunk* chunk) +{ + ChunkId idx; + + try { + idx = chunkMap.at(chunk->tag); + } + catch (const std::out_of_range& e) { + idx = WEBP_CHUNK_UNKNOWN; + } + this->chunks[idx].push_back(chunk); +} + +void Container::write(WEBP_MetaHandler* handler) +{ + XMP_IO* file = handler->parent->ioRef; + file->Rewind(); + XIO::WriteUns32_LE(file, this->tag); + XIO::WriteUns32_LE(file, (XMP_Uns32) this->size); + XIO::WriteUns32_LE(file, kChunk_WEBP); + + size_t i, j; + std::vector<Chunk*> chunkVect; + for (i = 0; i < WEBP_CHUNK_NIL; i++) { + chunkVect = this->chunks[i]; + for (j = 0; j < chunkVect.size(); j++) { + chunkVect.at(j)->write(handler); + } + } + XMP_Int64 lastOffset = file->Offset(); + this->size = lastOffset - 8; + file->Seek(this->pos + 4, kXMP_SeekFromStart); + XIO::WriteUns32_LE(file, (XMP_Uns32) this->size); + file->Seek(lastOffset, kXMP_SeekFromStart); + if (lastOffset < handler->initialFileSize) { + file->Truncate(lastOffset); + } +} + +Container::~Container() +{ + Chunk* chunk; + size_t i; + std::vector<Chunk*> chunkVect; + for (i = 0; i < WEBP_CHUNK_NIL; i++) { + chunkVect = this->chunks[i]; + while (!chunkVect.empty()) { + chunk = chunkVect.back(); + delete chunk; + chunkVect.pop_back(); + } + } +} +} |