diff --git a/Pdf4QtLib/sources/pdf3d_u3d.cpp b/Pdf4QtLib/sources/pdf3d_u3d.cpp
new file mode 100644
index 0000000..fbe814c
--- /dev/null
+++ b/Pdf4QtLib/sources/pdf3d_u3d.cpp
@@ -0,0 +1,439 @@
+// Copyright (C) 2022 Jakub Melka
+//
+// This file is part of PDF4QT.
+//
+// PDF4QT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// with the written consent of the copyright owner, any later version.
+//
+// PDF4QT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with PDF4QT. If not, see .
+
+#include "pdf3d_u3d.h"
+
+#include
+
+namespace pdf
+{
+
+namespace u3d
+{
+
+PDF3D_U3D_DataReader::PDF3D_U3D_DataReader(QByteArray data) :
+ m_data(std::move(data)),
+ m_high(0x0000FFFF),
+ m_low(0),
+ m_underflow(0),
+ m_code(0)
+{
+
+}
+
+PDF3D_U3D_DataReader::~PDF3D_U3D_DataReader()
+{
+
+}
+
+uint8_t PDF3D_U3D_DataReader::readU8()
+{
+ uint32_t value = readSymbol(PDF3D_U3D_Constants::S_CONTEXT_8);
+ --value;
+ return swapBits8(value);
+}
+
+uint16_t PDF3D_U3D_DataReader::readU16()
+{
+ const uint16_t low = readU8();
+ const uint16_t high = readU8();
+ return low + (high << 8);
+}
+
+uint32_t PDF3D_U3D_DataReader::readU32()
+{
+ const uint32_t low = readU16();
+ const uint32_t high = readU16();
+ return low + (high << 16);
+}
+
+uint64_t PDF3D_U3D_DataReader::readU64()
+{
+ const uint64_t low = readU32();
+ const uint64_t high = readU32();
+ return low + (high << 32);
+}
+
+int32_t PDF3D_U3D_DataReader::readI32()
+{
+ return static_cast(readU32());
+}
+
+float PDF3D_U3D_DataReader::readF32()
+{
+ const uint32_t value = readU32();
+ return static_cast(value);
+}
+
+uint8_t PDF3D_U3D_DataReader::readCompressedU8(uint32_t context)
+{
+ if (m_contextManager.isContextCompressed(context))
+ {
+ const uint32_t symbol = readSymbol(context);
+ if (symbol != 0)
+ {
+ // Compressed symbol
+ return symbol - 1;
+ }
+ else
+ {
+ // New symbol
+ const uint32_t value = readU8();
+ m_contextManager.addSymbol(context, value + 1);
+ return value;
+ }
+ }
+
+ return readU8();
+}
+
+uint16_t PDF3D_U3D_DataReader::readCompressedU16(uint32_t context)
+{
+ if (m_contextManager.isContextCompressed(context))
+ {
+ const uint32_t symbol = readSymbol(context);
+ if (symbol != 0)
+ {
+ // Compressed symbol
+ return symbol - 1;
+ }
+ else
+ {
+ // New symbol
+ const uint32_t value = readU16();
+ m_contextManager.addSymbol(context, value + 1);
+ return value;
+ }
+ }
+
+ return readU16();
+}
+
+uint32_t PDF3D_U3D_DataReader::readCompressedU32(uint32_t context)
+{
+ if (m_contextManager.isContextCompressed(context))
+ {
+ const uint32_t symbol = readSymbol(context);
+ if (symbol != 0)
+ {
+ // Compressed symbol
+ return symbol - 1;
+ }
+ else
+ {
+ // New symbol
+ const uint32_t value = readU32();
+ m_contextManager.addSymbol(context, value + 1);
+ return value;
+ }
+ }
+
+ return readU32();
+}
+
+uint32_t PDF3D_U3D_DataReader::readSymbol(uint32_t context)
+{
+ uint32_t position = m_position;
+ m_code = readBit();
+ m_position += m_underflow;
+ m_code = (m_code << 15) | read15Bits();
+ m_position = position;
+
+ const uint32_t totalCumFreq = m_contextManager.getTotalSymbolFrequency(context);
+ const uint32_t range = (m_high + 1) - m_low;
+ const uint32_t codeCumFreq = ((totalCumFreq) * (1 + m_code - m_low) - 1) / range;
+ const uint32_t value = m_contextManager.getSymbolFromFrequency(context, codeCumFreq);
+ const uint32_t valueCumFreq = m_contextManager.getCumulativeSymbolFrequency(context, value);
+ const uint32_t valueFreq = m_contextManager.getSymbolFrequency(context, value);
+
+ uint32_t low = m_low;
+ uint32_t high = m_high;
+
+ high = low - 1 + range * (valueCumFreq + valueFreq) / totalCumFreq;
+ low = low + range * valueCumFreq / totalCumFreq;
+ m_contextManager.addSymbol(context, value);
+
+ constexpr std::array S_BIT_COUNTS = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
+ uint32_t bitCount = S_BIT_COUNTS[((low >> 12) ^ (high >> 12)) & 0x0000000F];
+
+ constexpr std::array S_FAST_NOT_MASK = { 0x0000FFFF, 0x00007FFF, 0x00003FFF, 0x00001FFF, 0x00000FFF };
+ low = low & S_FAST_NOT_MASK[bitCount];
+ high = high & S_FAST_NOT_MASK[bitCount];
+
+ if (bitCount > 0)
+ {
+ high <<= bitCount;
+ low <<= bitCount;
+ high = high | ((1 << bitCount) - 1);
+ }
+
+ uint32_t maskedLow = low & PDF3D_U3D_Constants::S_HALF_MASK;
+ uint32_t maskedHigh = high & PDF3D_U3D_Constants::S_HALF_MASK;
+
+ while (((maskedLow | maskedHigh) == 0) || ((maskedLow == PDF3D_U3D_Constants::S_HALF_MASK) && maskedHigh == PDF3D_U3D_Constants::S_HALF_MASK))
+ {
+ low = (low & PDF3D_U3D_Constants::S_NOT_HALF_MASK) << 1;
+ high = ((high & PDF3D_U3D_Constants::S_NOT_HALF_MASK) << 1) | 1;
+ maskedLow = low & PDF3D_U3D_Constants::S_HALF_MASK;
+ maskedHigh = high & PDF3D_U3D_Constants::S_HALF_MASK;
+ ++bitCount;
+ }
+
+ const uint32_t savedBitsLow = maskedLow;
+ const uint32_t savedBitsHigh = maskedHigh;
+
+ if (bitCount > 0)
+ {
+ bitCount += m_underflow;
+ m_underflow = 0;
+ }
+
+ maskedLow = low & PDF3D_U3D_Constants::S_QUARTER_MASK;
+ maskedHigh = high & PDF3D_U3D_Constants::S_QUARTER_MASK;
+
+ uint32_t underflow = 0;
+ while ((maskedLow == 0x4000) && (maskedHigh == 0))
+ {
+ low = low & PDF3D_U3D_Constants::S_NOT_THREE_QUARTER_MASK;
+ high = high & PDF3D_U3D_Constants::S_NOT_THREE_QUARTER_MASK;
+ low += low;
+ high += high;
+ high = high | 1;
+ maskedLow = low & PDF3D_U3D_Constants::S_QUARTER_MASK;
+ maskedHigh = high & PDF3D_U3D_Constants::S_QUARTER_MASK;
+ ++underflow;
+ }
+
+ m_underflow += underflow;
+ low = low | savedBitsLow;
+ high = high | savedBitsHigh;
+ m_low = low;
+ m_high = high;
+ m_position += bitCount;
+
+ return value;
+}
+
+uint8_t PDF3D_U3D_DataReader::swapBits8(uint8_t source)
+{
+ // Jakub Melka: Code is public domain from here:http:
+ // graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits
+ return ((source * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
+}
+
+uint32_t PDF3D_U3D_DataReader::readBit()
+{
+ uint32_t bytePosition = m_position / 8;
+ if (bytePosition < uint32_t(m_data.size()))
+ {
+ uint32_t data = m_data[bytePosition];
+ uint32_t bitPosition = m_position % 8;
+ return (data >> bitPosition) & 1;
+ }
+
+ return 0;
+}
+
+uint32_t PDF3D_U3D_DataReader::read15Bits()
+{
+ std::array data;
+
+ for (uint32_t& value : data)
+ {
+ value = readBit();
+ }
+
+ std::reverse(data.begin(), data.end());
+
+ uint32_t result = 0;
+ for (uint32_t value : data)
+ {
+ result = (result << 1) | value;
+ }
+
+ return result;
+}
+
+bool PDF3D_U3D_ContextManager::isContextCompressed(uint32_t context) const
+{
+ return (context != PDF3D_U3D_Constants::S_CONTEXT_8) && context < PDF3D_U3D_Constants::S_MAX_RANGE;
+}
+
+bool PDF3D_U3D_ContextManager::isContextStaticAndCompressed(uint32_t context) const
+{
+ return (context != PDF3D_U3D_Constants::S_CONTEXT_8) && context < PDF3D_U3D_Constants::S_STATIC_FULL;
+}
+
+uint32_t PDF3D_U3D_ContextManager::getTotalSymbolFrequency(uint32_t context) const
+{
+ if (isContextStaticAndCompressed(context))
+ {
+ if (const ContextData* contextData = getContextData(context))
+ {
+ return contextData->symbols.at(0).cumulativeSymbolCount;
+ }
+
+ return 1;
+ }
+ else if (context == PDF3D_U3D_Constants::S_CONTEXT_8)
+ {
+ return 256;
+ }
+ else
+ {
+ return context - PDF3D_U3D_Constants::S_STATIC_FULL;
+ }
+}
+
+uint32_t PDF3D_U3D_ContextManager::getSymbolFrequency(uint32_t context, uint32_t symbol) const
+{
+ if (isContextStaticAndCompressed(context))
+ {
+ if (const ContextData* contextData = getContextData(context))
+ {
+ auto it = contextData->symbols.find(symbol);
+ if (it != contextData->symbols.cend())
+ {
+ return it->second.symbolCount;
+ }
+ }
+ else if (symbol == 0)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+uint32_t PDF3D_U3D_ContextManager::getCumulativeSymbolFrequency(uint32_t context, uint32_t symbol) const
+{
+ if (isContextStaticAndCompressed(context))
+ {
+ uint32_t value = 0;
+
+ if (const ContextData* contextData = getContextData(context))
+ {
+ auto it = contextData->symbols.find(symbol);
+ if (it != contextData->symbols.cend())
+ {
+ return contextData->symbols.at(0).cumulativeSymbolCount - it->second.cumulativeSymbolCount;
+ }
+ else
+ {
+ return contextData->symbols.at(0).cumulativeSymbolCount;
+ }
+ }
+
+ return value;
+ }
+
+ return symbol - 1;
+}
+
+uint32_t PDF3D_U3D_ContextManager::getSymbolFromFrequency(uint32_t context, uint32_t symbolFrequency) const
+{
+ if (isContextStaticAndCompressed(context))
+ {
+ if (m_contexts.count(context) &&
+ symbolFrequency > 0 &&
+ m_contexts.at(context).symbols.at(0).cumulativeSymbolCount >= symbolFrequency)
+ {
+ uint32_t value = 0;
+
+ for (const auto& item : m_contexts.at(context).symbols)
+ {
+ if (getCumulativeSymbolFrequency(context, item.first) <= symbolFrequency)
+ {
+ value = item.first;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return value;
+ }
+
+ return 0;
+ }
+
+ return symbolFrequency + 1;
+}
+
+void PDF3D_U3D_ContextManager::addSymbol(uint32_t context, uint32_t symbol)
+{
+ if (isContextStaticAndCompressed(context) && symbol < PDF3D_U3D_Constants::S_MAXIMUM_SYMBOL_IN_HISTOGRAM)
+ {
+ ContextData& contextData = m_contexts[context];
+
+ if (contextData.symbols[0].cumulativeSymbolCount >= PDF3D_U3D_Constants::S_MAX_CUMULATIVE_SYMBOL_COUNT)
+ {
+ // Scale down
+ uint32_t tempAccum = 0;
+
+ for (auto it = contextData.symbols.rbegin(); it != contextData.symbols.rend(); ++it)
+ {
+ ContextData::Data& data = it->second;
+ data.symbolCount >>= 1;
+ tempAccum += data.symbolCount;
+ data.cumulativeSymbolCount = tempAccum;
+ }
+
+ // Preserve initial count of 1
+ ContextData::Data& initialData = contextData.symbols[0];
+ ++initialData.symbolCount;
+ ++initialData.cumulativeSymbolCount;
+ }
+
+ auto it = contextData.symbols.find(symbol);
+ if (it == contextData.symbols.cend())
+ {
+ it = contextData.symbols.insert(std::make_pair(symbol, ContextData::Data())).first;
+
+ if (it != contextData.symbols.cend())
+ {
+ it->second.cumulativeSymbolCount = std::next(it)->second.cumulativeSymbolCount;
+ }
+ }
+
+ ++it->second.symbolCount;
+ ++it->second.cumulativeSymbolCount;
+ for(auto cit = contextData.symbols.begin(); cit != it; ++cit)
+ {
+ ++cit->second.cumulativeSymbolCount;
+ }
+ }
+}
+
+const PDF3D_U3D_ContextManager::ContextData* PDF3D_U3D_ContextManager::getContextData(uint32_t context) const
+{
+ auto it = m_contexts.find(context);
+ if (it != m_contexts.cend())
+ {
+ return &it->second;
+ }
+
+ return nullptr;
+}
+
+} // namespace u3d
+
+} // namespace pdf
diff --git a/Pdf4QtLib/sources/pdf3d_u3d.h b/Pdf4QtLib/sources/pdf3d_u3d.h
new file mode 100644
index 0000000..4ec9eed
--- /dev/null
+++ b/Pdf4QtLib/sources/pdf3d_u3d.h
@@ -0,0 +1,123 @@
+// Copyright (C) 2022 Jakub Melka
+//
+// This file is part of PDF4QT.
+//
+// PDF4QT is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// with the written consent of the copyright owner, any later version.
+//
+// PDF4QT is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with PDF4QT. If not, see .
+
+#ifndef PDF3D_U3D_H
+#define PDF3D_U3D_H
+
+#include "pdfglobal.h"
+
+#include
+
+namespace pdf
+{
+
+namespace u3d
+{
+
+class PDF3D_U3D
+{
+public:
+};
+
+class PDF3D_U3D_ContextManager
+{
+public:
+ bool isContextCompressed(uint32_t context) const;
+ bool isContextStaticAndCompressed(uint32_t context) const;
+
+ uint32_t getTotalSymbolFrequency(uint32_t context) const;
+ uint32_t getSymbolFrequency(uint32_t context, uint32_t symbol) const;
+ uint32_t getCumulativeSymbolFrequency(uint32_t context, uint32_t symbol) const;
+ uint32_t getSymbolFromFrequency(uint32_t context, uint32_t symbolFrequency) const;
+
+ void addSymbol(uint32_t context, uint32_t symbol);
+
+private:
+ struct ContextData
+ {
+ ContextData()
+ {
+ symbols= { { 0, Data{ 1, 1} } };
+ }
+
+ struct Data
+ {
+ uint32_t symbolCount = 0;
+ uint32_t cumulativeSymbolCount = 0;
+ };
+
+ std::map symbols;
+ };
+
+ const ContextData* getContextData(uint32_t context) const;
+
+ std::map m_contexts;
+};
+
+class PDF3D_U3D_Constants
+{
+public:
+ static constexpr uint32_t S_NOT_THREE_QUARTER_MASK = 0x00003FFF;
+ static constexpr uint32_t S_QUARTER_MASK = 0x00004000;
+ static constexpr uint32_t S_NOT_HALF_MASK = 0x00007FFF;
+ static constexpr uint32_t S_HALF_MASK = 0x00008000;
+ static constexpr uint32_t S_STATIC_FULL = 0x00000400;
+ static constexpr uint32_t S_MAX_RANGE = S_STATIC_FULL + 0x00003FFF;
+ static constexpr uint32_t S_CONTEXT_8 = 0;
+
+ static constexpr uint32_t S_MAX_CUMULATIVE_SYMBOL_COUNT = 0x00001fff;
+ static constexpr uint32_t S_MAXIMUM_SYMBOL_IN_HISTOGRAM = 0x0000FFFF;
+};
+
+class PDF3D_U3D_DataReader
+{
+public:
+ PDF3D_U3D_DataReader(QByteArray data);
+ ~PDF3D_U3D_DataReader();
+
+ uint8_t readU8();
+ uint16_t readU16();
+ uint32_t readU32();
+ uint64_t readU64();
+ int32_t readI32();
+ float readF32();
+
+ uint8_t readCompressedU8(uint32_t context);
+ uint16_t readCompressedU16(uint32_t context);
+ uint32_t readCompressedU32(uint32_t context);
+
+private:
+ uint32_t readSymbol(uint32_t context);
+ uint8_t swapBits8(uint8_t source);
+ uint32_t readBit();
+ uint32_t read15Bits();
+
+ QByteArray m_data;
+ PDF3D_U3D_ContextManager m_contextManager;
+
+ uint32_t m_high;
+ uint32_t m_low;
+ uint32_t m_underflow;
+ uint32_t m_code;
+ uint32_t m_position;
+};
+
+} // namespace u3d
+
+} // namespace pdf
+
+#endif // PDF3D_U3D_H