summaryrefslogtreecommitdiff
path: root/XMPFiles/source/FormatSupport/WEBP_Support.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'XMPFiles/source/FormatSupport/WEBP_Support.cpp')
-rw-r--r--XMPFiles/source/FormatSupport/WEBP_Support.cpp273
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();
+ }
+ }
+}
+}