Merge pull request #4375 from assimp/kimkulling/introduce_compression

Introduce compression class to encapsulate compression via zlib
pull/4380/head v5.2.0
Kim Kulling 2022-02-07 10:23:23 +01:00 committed by GitHub
commit dcd5c1dcfd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 272 additions and 78 deletions

View File

@ -182,6 +182,8 @@ inline size_t Write<aiVertexWeight>(IOStream *stream, const aiVertexWeight &v) {
return t + Write<float>(stream, v.mWeight); return t + Write<float>(stream, v.mWeight);
} }
constexpr size_t MatrixSize = 64;
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------
// Serialize a mat4x4 // Serialize a mat4x4
template <> template <>
@ -192,7 +194,7 @@ inline size_t Write<aiMatrix4x4>(IOStream *stream, const aiMatrix4x4 &m) {
} }
} }
return 64; return MatrixSize;
} }
// ----------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------

View File

@ -44,9 +44,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_XGL_IMPORTER #ifndef ASSIMP_BUILD_NO_XGL_IMPORTER
#include "XGLLoader.h" #include "XGLLoader.h"
#include "Common/Compression.h"
#include <assimp/ParsingUtils.h> #include <assimp/ParsingUtils.h>
#include <assimp/fast_atof.h> #include <assimp/fast_atof.h>
#include <assimp/MemoryIOWrapper.h> #include <assimp/MemoryIOWrapper.h>
#include <assimp/StreamReader.h> #include <assimp/StreamReader.h>
#include <assimp/importerdesc.h> #include <assimp/importerdesc.h>
@ -57,15 +58,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp; using namespace Assimp;
// zlib is needed for compressed XGL files
#ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL
# ifdef ASSIMP_BUILD_NO_OWN_ZLIB
# include <zlib.h>
# else
# include <contrib/zlib/zlib.h>
# endif
#endif
namespace Assimp { // this has to be in here because LogFunctions is in ::Assimp namespace Assimp { // this has to be in here because LogFunctions is in ::Assimp
template <> template <>
@ -73,6 +65,7 @@ const char *LogFunctions<XGLImporter>::Prefix() {
static auto prefix = "XGL: "; static auto prefix = "XGL: ";
return prefix; return prefix;
} }
} // namespace Assimp } // namespace Assimp
static const aiImporterDesc desc = { static const aiImporterDesc desc = {
@ -118,8 +111,8 @@ const aiImporterDesc *XGLImporter::GetInfo() const {
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure. // Imports the given file into the given scene structure.
void XGLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { void XGLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
#ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL #ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL
std::vector<Bytef> uncompressed; std::vector<unsigned char> uncompressed;
#endif #endif
m_scene = pScene; m_scene = pScene;
@ -137,46 +130,14 @@ void XGLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
#else #else
std::unique_ptr<StreamReaderLE> raw_reader(new StreamReaderLE(stream)); std::unique_ptr<StreamReaderLE> raw_reader(new StreamReaderLE(stream));
// build a zlib stream Compression c;
z_stream zstream; size_t total = 0l;
zstream.opaque = Z_NULL; if (c.open()) {
zstream.zalloc = Z_NULL; // skip two extra bytes, zgl files do carry a crc16 upfront (I think)
zstream.zfree = Z_NULL; raw_reader->IncPtr(2);
zstream.data_type = Z_BINARY; total = c.decompress((unsigned char *)raw_reader->GetPtr(), raw_reader->GetRemainingSize(), uncompressed);
c.close();
// raw decompression without a zlib or gzip header }
inflateInit2(&zstream, -MAX_WBITS);
// skip two extra bytes, zgl files do carry a crc16 upfront (I think)
raw_reader->IncPtr(2);
zstream.next_in = reinterpret_cast<Bytef *>(raw_reader->GetPtr());
zstream.avail_in = (uInt) raw_reader->GetRemainingSize();
size_t total = 0l;
// TODO: be smarter about this, decompress directly into heap buffer
// and decompress the data .... do 1k chunks in the hope that we won't kill the stack
#define MYBLOCK 1024
Bytef block[MYBLOCK];
int ret;
do {
zstream.avail_out = MYBLOCK;
zstream.next_out = block;
ret = inflate(&zstream, Z_NO_FLUSH);
if (ret != Z_STREAM_END && ret != Z_OK) {
ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .XGL file");
}
const size_t have = MYBLOCK - zstream.avail_out;
total += have;
uncompressed.resize(total);
memcpy(uncompressed.data() + total - have, block, have);
} while (ret != Z_STREAM_END);
// terminate zlib
inflateEnd(&zstream);
// replace the input stream with a memory stream // replace the input stream with a memory stream
stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t *>(uncompressed.data()), total)); stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t *>(uncompressed.data()), total));
#endif #endif
@ -291,7 +252,8 @@ aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope) {
const std::string &s = ai_stdStrToLower(child.name()); const std::string &s = ai_stdStrToLower(child.name());
if (s == "mesh") { if (s == "mesh") {
const size_t prev = scope.meshes_linear.size(); const size_t prev = scope.meshes_linear.size();
if (ReadMesh(child, scope)) { bool empty;
if (ReadMesh(child, scope, empty)) {
const size_t newc = scope.meshes_linear.size(); const size_t newc = scope.meshes_linear.size();
for (size_t i = 0; i < newc - prev; ++i) { for (size_t i = 0; i < newc - prev; ++i) {
meshes.push_back(static_cast<unsigned int>(i + prev)); meshes.push_back(static_cast<unsigned int>(i + prev));
@ -475,12 +437,12 @@ aiMesh *XGLImporter::ToOutputMesh(const TempMaterialMesh &m) {
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
bool XGLImporter::ReadMesh(XmlNode &node, TempScope &scope) { bool XGLImporter::ReadMesh(XmlNode &node, TempScope &scope, bool &empty) {
TempMesh t; TempMesh t;
std::map<unsigned int, TempMaterialMesh> bymat; std::map<unsigned int, TempMaterialMesh> bymat;
const unsigned int mesh_id = ReadIDAttr(node); const unsigned int mesh_id = ReadIDAttr(node);
bool empty_mesh = true;
for (XmlNode &child : node.children()) { for (XmlNode &child : node.children()) {
const std::string &s = ai_stdStrToLower(child.name()); const std::string &s = ai_stdStrToLower(child.name());
@ -539,6 +501,9 @@ bool XGLImporter::ReadMesh(XmlNode &node, TempScope &scope) {
mid = ResolveMaterialRef(sub_child, scope); mid = ResolveMaterialRef(sub_child, scope);
} }
} }
if (has[0] || has[1] || has[2]) {
empty_mesh = false;
}
if (mid == ~0u) { if (mid == ~0u) {
ThrowException("missing material index"); ThrowException("missing material index");
@ -590,6 +555,11 @@ bool XGLImporter::ReadMesh(XmlNode &node, TempScope &scope) {
scope.meshes.insert(std::pair<unsigned int, aiMesh *>(mesh_id, m)); scope.meshes.insert(std::pair<unsigned int, aiMesh *>(mesh_id, m));
} }
} }
if (empty_mesh) {
LogWarn("Mesh is empty, skipping.");
empty = empty_mesh;
return false;
}
// no id == not a reference, insert this mesh right *here* // no id == not a reference, insert this mesh right *here*
return mesh_id == ~0u; return mesh_id == ~0u;
@ -759,7 +729,7 @@ aiVector2D XGLImporter::ReadVec2(XmlNode &node) {
std::string val; std::string val;
XmlParser::getValueAsString(node, val); XmlParser::getValueAsString(node, val);
const char *s = val.c_str(); const char *s = val.c_str();
ai_real v[2]; ai_real v[2] = {};
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
if (!SkipSpaces(&s)) { if (!SkipSpaces(&s)) {
LogError("unexpected EOL, failed to parse vec2"); LogError("unexpected EOL, failed to parse vec2");

View File

@ -186,7 +186,7 @@ private:
void ReadLighting(XmlNode &node, TempScope &scope); void ReadLighting(XmlNode &node, TempScope &scope);
aiLight *ReadDirectionalLight(XmlNode &node); aiLight *ReadDirectionalLight(XmlNode &node);
aiNode *ReadObject(XmlNode &node, TempScope &scope); aiNode *ReadObject(XmlNode &node, TempScope &scope);
bool ReadMesh(XmlNode &node, TempScope &scope); bool ReadMesh(XmlNode &node, TempScope &scope, bool &empty);
void ReadMaterial(XmlNode &node, TempScope &scope); void ReadMaterial(XmlNode &node, TempScope &scope);
aiVector2D ReadVec2(XmlNode &node); aiVector2D ReadVec2(XmlNode &node);
aiVector3D ReadVec3(XmlNode &node); aiVector3D ReadVec3(XmlNode &node);

View File

@ -166,6 +166,8 @@ SET( Logging_SRCS
SOURCE_GROUP(Logging FILES ${Logging_SRCS}) SOURCE_GROUP(Logging FILES ${Logging_SRCS})
SET( Common_SRCS SET( Common_SRCS
Common/Compression.cpp
Common/Compression.h
Common/BaseImporter.cpp Common/BaseImporter.cpp
Common/BaseProcess.cpp Common/BaseProcess.cpp
Common/BaseProcess.h Common/BaseProcess.h

View File

@ -3,7 +3,7 @@
Open Asset Import Library (assimp) Open Asset Import Library (assimp)
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
Copyright (c) 2006-2021, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
@ -57,7 +57,7 @@ static const uint8_t tableDecodeBase64[128] = {
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0
}; };
static const char* tableEncodeBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; static const char *tableEncodeBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
static inline char EncodeChar(uint8_t b) { static inline char EncodeChar(uint8_t b) {
return tableEncodeBase64[size_t(b)]; return tableEncodeBase64[size_t(b)];
@ -104,17 +104,16 @@ void Encode(const uint8_t *in, size_t inLength, std::string &out) {
} }
} }
void Encode(const std::vector<uint8_t>& in, std::string &out) { void Encode(const std::vector<uint8_t> &in, std::string &out) {
Encode (in.data (), in.size (), out); Encode(in.data(), in.size(), out);
} }
std::string Encode (const std::vector<uint8_t>& in) { std::string Encode(const std::vector<uint8_t> &in) {
std::string encoded; std::string encoded;
Encode (in, encoded); Encode(in, encoded);
return encoded; return encoded;
} }
size_t Decode(const char *in, size_t inLength, uint8_t *&out) { size_t Decode(const char *in, size_t inLength, uint8_t *&out) {
if (inLength % 4 != 0) { if (inLength % 4 != 0) {
throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)), "\", length:", inLength); throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)), "\", length:", inLength);
@ -159,23 +158,22 @@ size_t Decode(const char *in, size_t inLength, uint8_t *&out) {
return outLength; return outLength;
} }
size_t Decode(const std::string& in, std::vector<uint8_t>& out) { size_t Decode(const std::string &in, std::vector<uint8_t> &out) {
uint8_t* outPtr = nullptr; uint8_t *outPtr = nullptr;
size_t decodedSize = Decode (in.data (), in.size (), outPtr); size_t decodedSize = Decode(in.data(), in.size(), outPtr);
if (outPtr == nullptr) { if (outPtr == nullptr) {
return 0; return 0;
} }
out.assign (outPtr, outPtr + decodedSize); out.assign(outPtr, outPtr + decodedSize);
delete[] outPtr; delete[] outPtr;
return decodedSize; return decodedSize;
} }
std::vector<uint8_t> Decode (const std::string& in) { std::vector<uint8_t> Decode(const std::string &in) {
std::vector<uint8_t> result; std::vector<uint8_t> result;
Decode (in, result); Decode(in, result);
return result; return result;
} }
} } // namespace Base64
} // namespace Assimp
}

View File

@ -0,0 +1,141 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#include "Compression.h"
#include <assimp/ai_assert.h>
#include <assimp/Exceptional.h>
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
#include <zlib.h>
#else
#include "../contrib/zlib/zlib.h"
#endif
namespace Assimp {
struct Compression::impl {
bool mOpen;
z_stream mZSstream;
impl() :
mOpen(false) {}
};
Compression::Compression() :
mImpl(new impl) {
// empty
}
Compression::~Compression() {
ai_assert(mImpl != nullptr);
delete mImpl;
}
bool Compression::open() {
ai_assert(mImpl != nullptr);
if (mImpl->mOpen) {
return false;
}
// build a zlib stream
mImpl->mZSstream.opaque = Z_NULL;
mImpl->mZSstream.zalloc = Z_NULL;
mImpl->mZSstream.zfree = Z_NULL;
mImpl->mZSstream.data_type = Z_BINARY;
// raw decompression without a zlib or gzip header
inflateInit2(&mImpl->mZSstream, -MAX_WBITS);
mImpl->mOpen = true;
return mImpl->mOpen;
}
constexpr size_t MYBLOCK = 1024;
size_t Compression::decompress(unsigned char *data, size_t in, std::vector<unsigned char> &uncompressed) {
ai_assert(mImpl != nullptr);
mImpl->mZSstream.next_in = reinterpret_cast<Bytef *>(data);
mImpl->mZSstream.avail_in = (uInt)in;
Bytef block[MYBLOCK] = {};
int ret = 0;
size_t total = 0l;
do {
mImpl->mZSstream.avail_out = MYBLOCK;
mImpl->mZSstream.next_out = block;
ret = inflate(&mImpl->mZSstream, Z_NO_FLUSH);
if (ret != Z_STREAM_END && ret != Z_OK) {
throw DeadlyImportError("Compression", "Failure decompressing this file using gzip.");
}
const size_t have = MYBLOCK - mImpl->mZSstream.avail_out;
total += have;
uncompressed.resize(total);
::memcpy(uncompressed.data() + total - have, block, have);
} while (ret != Z_STREAM_END);
return total;
}
bool Compression::isOpen() const {
ai_assert(mImpl != nullptr);
return mImpl->mOpen;
}
bool Compression::close() {
ai_assert(mImpl != nullptr);
if (!mImpl->mOpen) {
return false;
}
inflateEnd(&mImpl->mZSstream);
mImpl->mOpen = false;
return true;
}
} // namespace Assimp

View File

@ -0,0 +1,80 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2022, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the assimp team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the assimp team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#pragma once
#include <vector>
namespace Assimp {
/// @brief This class provides the decompression of zlib-compressed data.
class Compression {
public:
/// @brief The class constructor.
Compression();
/// @brief The class destructor.
~Compression();
/// @brief Will open the access to the compression.
/// @return true if close was successful, false if not.
bool open();
/// @brief Will return the open state.
/// @return true if the access is opened, false if not.
bool isOpen() const;
/// @brief Will close the decompress access.
/// @return true if close was successful, false if not.
bool close();
/// @brief Will decompress the data buffer.
/// @param[in] data The data to decompress
/// @param[in] in The size of the data.
/// @param[out uncompressed A std::vector containing the decompressed data.
size_t decompress(unsigned char *data, size_t in, std::vector<unsigned char> &uncompressed);
private:
struct impl;
impl *mImpl;
};
} // namespace Assimp

View File

@ -3,7 +3,7 @@
Open Asset Import Library (assimp) Open Asset Import Library (assimp)
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
Copyright (c) 2006-2019, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, assimp team
All rights reserved. All rights reserved.
Redistribution and use of this software in source and binary forms, Redistribution and use of this software in source and binary forms,

View File

@ -50,6 +50,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
namespace Assimp { namespace Assimp {
namespace Base64 { namespace Base64 {
/// @brief Will encode the given
/// @param in
/// @param inLength
/// @param out
void Encode(const uint8_t *in, size_t inLength, std::string &out); void Encode(const uint8_t *in, size_t inLength, std::string &out);
void Encode(const std::vector<uint8_t>& in, std::string &out); void Encode(const std::vector<uint8_t>& in, std::string &out);
std::string Encode(const std::vector<uint8_t>& in); std::string Encode(const std::vector<uint8_t>& in);