From 33369b17753a051a6d89ba5135fb35ac46a89f44 Mon Sep 17 00:00:00 2001 From: James Grogan Date: Thu, 24 Nov 2022 17:43:31 +0000 Subject: [PATCH] Toward first png. --- src/compression/CyclicRedundancyChecker.cpp | 42 +++++++++ src/compression/CyclicRedundancyChecker.h | 50 ++--------- src/compression/ZlibEncoder.cpp | 31 +++++-- src/compression/ZlibEncoder.h | 2 +- src/compression/deflate/DeflateBlock.cpp | 4 +- src/compression/deflate/DeflateEncoder.cpp | 2 + src/image/png/PngHeader.cpp | 18 ++-- src/image/png/PngHeader.h | 4 +- src/image/png/PngReader.cpp | 95 +++++++++++++++------ src/image/png/PngReader.h | 10 +-- src/image/png/PngWriter.cpp | 30 ++++--- test/image/TestPngReader.cpp | 4 +- 12 files changed, 190 insertions(+), 102 deletions(-) diff --git a/src/compression/CyclicRedundancyChecker.cpp b/src/compression/CyclicRedundancyChecker.cpp index e69de29..977ed97 100644 --- a/src/compression/CyclicRedundancyChecker.cpp +++ b/src/compression/CyclicRedundancyChecker.cpp @@ -0,0 +1,42 @@ +#include "CyclicRedundancyChecker.h" + +void CyclicRedundancyChecker::createTable() +{ + unsigned long c{0}; + for (int n = 0; n < 256; n++) + { + c = (unsigned long) n; + for (int k = 0; k < 8; k++) + { + if (c & 1) + { + c = 0xedb88320L ^ (c >> 1); + } + else + { + c = c >> 1; + } + } + mTable[n] = c; + } + mTableComputed = true; +} + +void CyclicRedundancyChecker::addValue(unsigned char val) +{ + if (!mTableComputed) + { + createTable(); + } + mLastValue = mTable[(mLastValue ^ val) & 0xff] ^ (mLastValue >> 8); +} + +uint32_t CyclicRedundancyChecker::getChecksum() const +{ + return mLastValue ^ 0xffffffffL; +} + +void CyclicRedundancyChecker::reset() +{ + mLastValue = 0xffffffffL; +} diff --git a/src/compression/CyclicRedundancyChecker.h b/src/compression/CyclicRedundancyChecker.h index 9d31d1e..fa6cd0e 100644 --- a/src/compression/CyclicRedundancyChecker.h +++ b/src/compression/CyclicRedundancyChecker.h @@ -1,52 +1,20 @@ #pragma once -class CyclicRedundancyChecker +#include "AbstractChecksumCalculator.h" + +class CyclicRedundancyChecker : public AbstractChecksumCalculator { public: + void addValue(unsigned char val) override; - void createTable() - { - unsigned long c{0}; - for (int n = 0; n < 256; n++) - { - c = (unsigned long) n; - for (int k = 0; k < 8; k++) - { - if (c & 1) - { - c = 0xedb88320L ^ (c >> 1); - } - else - { - c = c >> 1; - } - } - mTable[n] = c; - } - mTableComputed = true; - mTableComputed = 1; - } + uint32_t getChecksum() const override; - unsigned long updateCrc(unsigned long crc, unsigned char *buf, int len) - { - unsigned long c = crc; - if (!mTableComputed) - { - createTable(); - } - for (int n = 0; n < len; n++) - { - c = mTable[(c ^ buf[n]) & 0xff] ^ (c >> 8); - } - return c; - } - - unsigned long doCrc(unsigned char *buf, int len) - { - return updateCrc(0xffffffffL, buf, len) ^ 0xffffffffL; - } + void reset() override; private: + void createTable(); bool mTableComputed{false}; + + uint32_t mLastValue{0xffffffffL}; unsigned long mTable[256]; }; diff --git a/src/compression/ZlibEncoder.cpp b/src/compression/ZlibEncoder.cpp index 9a456e3..78764c2 100644 --- a/src/compression/ZlibEncoder.cpp +++ b/src/compression/ZlibEncoder.cpp @@ -61,10 +61,16 @@ void ZlibEncoder::parseCompressionMethod(unsigned char method) } } -void ZlibEncoder::parseExtraFlags(unsigned char extraFlags) +void ZlibEncoder::parseExtraFlags(unsigned char extraFlags, unsigned char compression_byte) { std::cout << "Got flags " << static_cast(extraFlags) << std::endl; + auto mod = ((static_cast(compression_byte) << 8) | extraFlags) % 31; + if (mod != 0) + { + std::cout << "Invalid header. Mod is " << mod << std::endl; + } + mFlagCheck = ByteUtils::getLowerNBits(extraFlags, 5); mUseDictionary = bool(ByteUtils::getBitN(extraFlags, 5)); mFlagLevel = static_cast(ByteUtils::getHigherNBits(extraFlags, 2)); @@ -76,11 +82,10 @@ std::string ZlibEncoder::getData() const sstream << "ZlibEncoder data \n"; sstream << "Compression method: " << toString(mCompressionMethod) << '\n'; sstream << "Window size: " << mWindowSize << '\n'; - sstream << "Flag check: " << mFlagCheck << '\n'; + sstream << "Flag check: " << static_cast(mFlagCheck) << '\n'; sstream << "Use dictionary: " << mUseDictionary << '\n'; sstream << "Flag level: " << toString(mFlagLevel) << '\n'; return sstream.str(); - } bool ZlibEncoder::encode() @@ -126,15 +131,17 @@ bool ZlibEncoder::encode() } const auto checksum = mChecksumCalculator->getChecksum(); - std::cout << "ZlibEncoder Writing Checksum " << checksum << std::endl; + std::cout << "ZlibEncoder Writing Adler32 Checksum " << checksum << std::endl; mOutputStream->write(checksum); return true; } bool ZlibEncoder::decode() { - parseCompressionMethod(*mInputStream->readNextByte()); - parseExtraFlags(*mInputStream->readNextByte()); + auto compression_byte = *mInputStream->readNextByte(); + + parseCompressionMethod(compression_byte); + parseExtraFlags(*mInputStream->readNextByte(), compression_byte); if (!mWorkingEncoder) { @@ -149,5 +156,15 @@ bool ZlibEncoder::decode() } } - return mWorkingEncoder->decode(); + auto valid = mWorkingEncoder->decode(); + + unsigned char byte0 = *mInputStream->readNextByte(); + unsigned char byte1 = *mInputStream->readNextByte(); + unsigned char byte2 = *mInputStream->readNextByte(); + unsigned char byte3 = *mInputStream->readNextByte(); + + uint32_t adler32 = (byte0 << 24) | (byte1 << 16) | (byte2 << 8) | byte3; + std::cout << "Got adler 32 checksum " << adler32 << std::endl; + + return valid; } diff --git a/src/compression/ZlibEncoder.h b/src/compression/ZlibEncoder.h index ec6653e..df898e8 100644 --- a/src/compression/ZlibEncoder.h +++ b/src/compression/ZlibEncoder.h @@ -40,7 +40,7 @@ public: private: void parseCompressionMethod(unsigned char method); - void parseExtraFlags(unsigned char extraFlags); + void parseExtraFlags(unsigned char extraFlags, unsigned char compression_byte); CompressionMethod mCompressionMethod{CompressionMethod::DEFLATE}; Deflate::CompressionMethod mDeflateCompressionMethod{Deflate::CompressionMethod::NONE}; diff --git a/src/compression/deflate/DeflateBlock.cpp b/src/compression/deflate/DeflateBlock.cpp index d178b53..5b07615 100644 --- a/src/compression/deflate/DeflateBlock.cpp +++ b/src/compression/deflate/DeflateBlock.cpp @@ -261,7 +261,7 @@ void DeflateBlock::write(uint16_t datalength) { unsigned char working_block{0}; working_block |= static_cast(mInFinalBlock); - working_block |= static_cast(mCompressionMethod) << 1; + working_block |= (static_cast(mCompressionMethod) << 1); if (mCompressionMethod == Deflate::CompressionMethod::NONE) { @@ -277,7 +277,7 @@ void DeflateBlock::write(uint16_t datalength) for(unsigned idx=0; idxreadNextByte(); - std::cout << "Writing next byte " << static_cast(byte) << std::endl; + //std::cout << "Writing next byte " << static_cast(byte) << std::endl; mOutputStream->writeByte(byte); } } diff --git a/src/compression/deflate/DeflateEncoder.cpp b/src/compression/deflate/DeflateEncoder.cpp index c887b19..92d1dc8 100644 --- a/src/compression/deflate/DeflateEncoder.cpp +++ b/src/compression/deflate/DeflateEncoder.cpp @@ -66,6 +66,8 @@ bool DeflateEncoder::decode() auto working_block = std::make_unique(mInputStream, mOutputStream); working_block->readHeader(); + std::cout << working_block->getMetaData() << std::endl; + DeflateBlock* raw_block = working_block.get(); while(!raw_block->isFinalBlock()) diff --git a/src/image/png/PngHeader.cpp b/src/image/png/PngHeader.cpp index b162e8e..b9fa13b 100644 --- a/src/image/png/PngHeader.cpp +++ b/src/image/png/PngHeader.cpp @@ -11,6 +11,8 @@ std::string PngHeader::toString() const sstr << "width: " << mWidth << "\n"; sstr << "height: " << mHeight << "\n"; sstr << "bitDepth: " << (int)mBitDepth << "\n"; + sstr << "cached CRC: " << mCachedCrc << "\n"; + sstr << mPngInfo.toString(); return sstr.str(); } @@ -66,14 +68,20 @@ void PngHeader::updateData() mData.push_back(static_cast(mPngInfo.mInterlaceMethod)); } -uint32_t PngHeader::getCrc() const +uint32_t PngHeader::getCrc() { CyclicRedundancyChecker crc_check; std::vector char_data = StringUtils::toBytes(mName); - std::copy(mData.begin(), mData.end(), std::back_inserter(char_data)); - - auto result = crc_check.doCrc(char_data.data(), char_data.size()); - return result; + for (auto c : char_data) + { + crc_check.addValue(c); + } + for (auto entry : mData) + { + crc_check.addValue(entry); + } + mCachedCrc = crc_check.getChecksum(); + return mCachedCrc; } void PngHeader::setPngInfo(const PngInfo& info) diff --git a/src/image/png/PngHeader.h b/src/image/png/PngHeader.h index 05538a7..17ee0b4 100644 --- a/src/image/png/PngHeader.h +++ b/src/image/png/PngHeader.h @@ -9,7 +9,7 @@ class PngHeader public: uint32_t getLength() const; - uint32_t getCrc() const; + uint32_t getCrc(); const std::vector& getData() const; @@ -36,5 +36,7 @@ private: PngInfo mPngInfo; std::string mName{"IHDR"}; + uint32_t mCachedCrc{0}; + std::vector mData; }; diff --git a/src/image/png/PngReader.cpp b/src/image/png/PngReader.cpp index 522717b..0b8322d 100644 --- a/src/image/png/PngReader.cpp +++ b/src/image/png/PngReader.cpp @@ -5,6 +5,7 @@ #include "BufferBitStream.h" #include "ZlibEncoder.h" +#include "CyclicRedundancyChecker.h" #include #include @@ -43,8 +44,6 @@ bool PngReader::checkSignature() return false; } } - - mCurrentOffset += 8; return true; } @@ -54,45 +53,51 @@ bool PngReader::readChunk() std::string chunkType; BinaryStream::getNextString(mFile->GetInHandle(), chunkType, 4); - mCurrentOffset += 8; std::cout << "Got chunk with type: " << chunkType << " and length: " << length << std::endl; bool lastChunk = false; if (chunkType == "IHDR") { - readHeaderChunk(); + if (!readHeaderChunk()) + { + return false; + } } else if(chunkType == "IEND") { lastChunk = true; + if (mProcessingDatablocks) + { + decodeData(); + } + unsigned crcCheck = *BinaryStream::getNextDWord(mFile->GetInHandle()); } else if(chunkType == "IDAT") { - readIDATChunk(length); + mProcessingDatablocks = true; + if(!readIDATChunk(length)) + { + return false; + } } else { + if (mProcessingDatablocks) + { + decodeData(); + } + for(unsigned idx=0;idxGetInHandle()->get(); } + + unsigned crcCheck = *BinaryStream::getNextDWord(mFile->GetInHandle()); } - - unsigned crcCheck = *BinaryStream::getNextDWord(mFile->GetInHandle()); - - mCurrentOffset += 4; return !lastChunk; } -void PngReader::readIDATChunk(unsigned length) -{ - for(unsigned idx=0; idxwriteByte(*mFile->readNextByte()); - } -} - -void PngReader::readHeaderChunk() +bool PngReader::readHeaderChunk() { auto width = *BinaryStream::getNextDWord(mFile->GetInHandle()); auto height = *BinaryStream::getNextDWord(mFile->GetInHandle()); @@ -106,15 +111,52 @@ void PngReader::readHeaderChunk() info.mFilterMethod = static_cast(mFile->GetInHandle()->get()); info.mInterlaceMethod = static_cast(mFile->GetInHandle()->get()); mHeader.setPngInfo(info); + mHeader.updateData(); - mCurrentOffset += 13; + uint32_t file_crc = *BinaryStream::getNextDWord(mFile->GetInHandle()); + auto crc_calc = mHeader.getCrc(); - logHeader(); + std::cout << mHeader.toString() << "*************\n"; + if (file_crc != crc_calc) + { + std::cout << "Header crc calc does not match file value. File: " << file_crc << " Header: " << crc_calc << std::endl;; + return false; + } + else + { + return true; + } } -void PngReader::logHeader() +bool PngReader::readIDATChunk(unsigned length) { - std::cout << "IHDR\n" << mHeader.toString() << "*************\n"; + auto crc_check = std::make_unique(); + + std::vector char_data = StringUtils::toBytes("IDAT"); + for (auto c : char_data) + { + crc_check->addValue(c); + } + + mInputStream->setChecksumCalculator(crc_check.get()); + for(unsigned idx=0; idxwriteByte(*mFile->readNextByte()); + } + mInputStream->clearChecksumCalculator(); + + uint32_t file_crc = *BinaryStream::getNextDWord(mFile->GetInHandle()); + auto crc_calc = crc_check->getChecksum(); + + if (file_crc != crc_calc) + { + std::cout << "IDAT crc calc does not match file value. File: " << file_crc << " Header: " << crc_calc << std::endl;; + return false; + } + else + { + return true; + } } std::unique_ptr > PngReader::read() @@ -138,8 +180,11 @@ std::unique_ptr > PngReader::read() { } - - std::cout << mEncoder->getData() << std::endl; - mEncoder->decode(); return std::move(image); } + +void PngReader::decodeData() +{ + mEncoder->decode(); + std::cout << mEncoder->getData() << "***********" << std::endl; +} diff --git a/src/image/png/PngReader.h b/src/image/png/PngReader.h index e657859..0dad321 100644 --- a/src/image/png/PngReader.h +++ b/src/image/png/PngReader.h @@ -24,15 +24,13 @@ public: private: bool readChunk(); - void readHeaderChunk(); + bool readHeaderChunk(); - void readIDATChunk(unsigned length); - - void logHeader(); + bool readIDATChunk(unsigned length); bool checkSignature(); - unsigned mCurrentOffset{0}; + void decodeData(); PngHeader mHeader; @@ -43,5 +41,5 @@ private: std::unique_ptr mEncoder; std::unique_ptr mInputStream; std::unique_ptr mOutputStream; - bool mAwaitingDataBlock{true}; + bool mProcessingDatablocks{false}; }; diff --git a/src/image/png/PngWriter.cpp b/src/image/png/PngWriter.cpp index 5c17a01..8811edd 100644 --- a/src/image/png/PngWriter.cpp +++ b/src/image/png/PngWriter.cpp @@ -83,9 +83,9 @@ void PngWriter::writeHeader() mPngHeader.updateData(); mOutStream->writeBytes(mPngHeader.getData()); - std::cout << "Writing header " << mPngHeader.toString() << std::endl; - auto crc = mPngHeader.getCrc(); + + std::cout << mPngHeader.toString() << "*********" << std::endl; mOutStream->write(crc); } @@ -98,7 +98,11 @@ void PngWriter::writeEndChunk() std::vector char_data = StringUtils::toBytes("IEND"); CyclicRedundancyChecker crc_check; - auto crc = crc_check.doCrc(char_data.data(), char_data.size()); + for (auto c : char_data) + { + crc_check.addValue(c); + } + auto crc = crc_check.getChecksum(); mOutStream->write(crc); std::cout << "Writing end chunk" << std::endl; @@ -108,11 +112,6 @@ void PngWriter::writeDataChunks(const BufferBitStream& buffer) { auto num_bytes = buffer.getBuffer().size(); auto max_bytes{32000}; - std::vector crc_buffer(num_bytes + 4, 0); - crc_buffer[0] = 'I'; - crc_buffer[1] = 'D'; - crc_buffer[2] = 'A'; - crc_buffer[3] = 'T'; unsigned num_dat_chunks = num_bytes/max_bytes + 1; unsigned offset = 0; @@ -127,18 +126,23 @@ void PngWriter::writeDataChunks(const BufferBitStream& buffer) std::cout << "Writing idat length " << num_bytes << std::endl; mOutStream->write(num_bytes); - mOutStream->writeBytes(StringUtils::toBytes("IDAT")); + std::vector char_data = StringUtils::toBytes("IDAT"); + mOutStream->writeBytes(char_data); + + CyclicRedundancyChecker crc_check; + for (auto c : char_data) + { + crc_check.addValue(c); + } for(unsigned jdx=0; jdxwriteByte(val); + crc_check.addValue(val); } - CyclicRedundancyChecker crc_check; - auto crc = crc_check.doCrc(crc_buffer.data(), crc_buffer.size()); - + auto crc = crc_check.getChecksum(); std::cout << "Writing idat crc" << crc << std::endl; mOutStream->write(crc); } diff --git a/test/image/TestPngReader.cpp b/test/image/TestPngReader.cpp index d1f2cdb..b0458fd 100644 --- a/test/image/TestPngReader.cpp +++ b/test/image/TestPngReader.cpp @@ -9,7 +9,9 @@ int main() { //const auto path = "/home/jmsgrogan/Downloads/test.png"; - const auto path = "/home/jmsgrogan/Downloads/index.png"; + //const auto path = "/home/jmsgrogan/Downloads/index.png"; + + const auto path = "/home/jmsgrogan/code/MediaTool-build/bin/test.png"; PngReader reader; reader.setPath(path);