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