diff --git a/src/compression/CMakeLists.txt b/src/compression/CMakeLists.txt index 27153ac..7eebe23 100644 --- a/src/compression/CMakeLists.txt +++ b/src/compression/CMakeLists.txt @@ -8,6 +8,7 @@ list(APPEND compression_LIB_INCLUDES DeflateEncoder.cpp DeflateBlock.cpp Lz77Encoder.cpp + CyclicRedundancyChecker.cpp ) add_library(compression SHARED ${compression_LIB_INCLUDES}) diff --git a/src/compression/CyclicRedundancyChecker.cpp b/src/compression/CyclicRedundancyChecker.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/compression/CyclicRedundancyChecker.h b/src/compression/CyclicRedundancyChecker.h new file mode 100644 index 0000000..9d31d1e --- /dev/null +++ b/src/compression/CyclicRedundancyChecker.h @@ -0,0 +1,52 @@ +#pragma once + +class CyclicRedundancyChecker +{ +public: + + 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; + } + + 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; + } + +private: + bool mTableComputed{false}; + unsigned long mTable[256]; +}; diff --git a/src/core/ByteUtils.cpp b/src/core/ByteUtils.cpp index 25829c9..7ad14bb 100644 --- a/src/core/ByteUtils.cpp +++ b/src/core/ByteUtils.cpp @@ -21,6 +21,11 @@ unsigned char ByteUtils::getHigherNBits(unsigned char input, unsigned num) return input >> 8 - num; } +unsigned char ByteUtils::getByteN(uint32_t input, unsigned n) +{ + return (input << 8*n) >> 24; +} + unsigned char ByteUtils::getLowerNBits(unsigned char input, unsigned num) { switch (num) diff --git a/src/core/ByteUtils.h b/src/core/ByteUtils.h index 096c8fc..a34c2ea 100644 --- a/src/core/ByteUtils.h +++ b/src/core/ByteUtils.h @@ -17,6 +17,8 @@ public: static Word GetWordLastByte(const Word word); + static unsigned char getByteN(uint32_t input, unsigned n); + static unsigned char getHigherNBits(unsigned char input, unsigned num); static unsigned char getLowerNBits(unsigned char input, unsigned num); diff --git a/src/core/file_utilities/File.cpp b/src/core/file_utilities/File.cpp index 9c1f29c..2dde794 100644 --- a/src/core/file_utilities/File.cpp +++ b/src/core/file_utilities/File.cpp @@ -32,9 +32,23 @@ std::ofstream* File::GetOutHandle() const return mOutHandle.get(); } -unsigned char File::readNextByte() +std::optional File::readNextByte() { - return mInHandle->get(); + if (mInHandle->good()) + { + if (auto val = mInHandle->get(); val == EOF) + { + return std::nullopt; + } + else + { + return val; + } + } + else + { + return std::nullopt; + } } void File::Open(bool asBinary) diff --git a/src/core/file_utilities/File.h b/src/core/file_utilities/File.h index 1fe93e3..cf842c6 100644 --- a/src/core/file_utilities/File.h +++ b/src/core/file_utilities/File.h @@ -7,6 +7,7 @@ #include #include #include +#include using Path = std::filesystem::path; @@ -48,7 +49,7 @@ public: void Close(); - unsigned char readNextByte(); + std::optional readNextByte(); private: diff --git a/src/core/streams/BitStream.h b/src/core/streams/BitStream.h index 9dd3fc6..f73714f 100644 --- a/src/core/streams/BitStream.h +++ b/src/core/streams/BitStream.h @@ -27,6 +27,8 @@ public: virtual void writeByte(unsigned char data) = 0; + virtual void writeBytes(const std::vector data) = 0; + protected: int mByteOffset{0}; unsigned mBitOffset{0}; diff --git a/src/core/streams/BufferBitStream.h b/src/core/streams/BufferBitStream.h index bc898c4..1a3639c 100644 --- a/src/core/streams/BufferBitStream.h +++ b/src/core/streams/BufferBitStream.h @@ -17,6 +17,11 @@ public: void writeByte(unsigned char data) override; + void writeBytes(const std::vector data) override + { + std::copy(data.begin(), data.end(), std::back_inserter(mBuffer)); + } + const std::vector& getBuffer() const { return mBuffer; diff --git a/src/core/streams/InputBitStream.h b/src/core/streams/InputBitStream.h index 5831fe6..6b762d7 100644 --- a/src/core/streams/InputBitStream.h +++ b/src/core/streams/InputBitStream.h @@ -16,6 +16,11 @@ class InputBitStream : public BitStream void writeByte(unsigned char data) override; + void writeBytes(const std::vector data) override + { + + } + private: std::basic_istream* mStream{nullptr}; }; diff --git a/src/core/streams/OutputBitStream.cpp b/src/core/streams/OutputBitStream.cpp index 5e05194..5adf599 100644 --- a/src/core/streams/OutputBitStream.cpp +++ b/src/core/streams/OutputBitStream.cpp @@ -26,3 +26,11 @@ void OutputBitStream::writeByte(unsigned char data) { (*mStream) << data; } + +void OutputBitStream::writeBytes(const std::vector data) +{ + for(auto byte : data) + { + writeByte(byte); + } +} diff --git a/src/core/streams/OutputBitStream.h b/src/core/streams/OutputBitStream.h index 5dc59a1..1a119d5 100644 --- a/src/core/streams/OutputBitStream.h +++ b/src/core/streams/OutputBitStream.h @@ -17,6 +17,8 @@ public: void writeByte(unsigned char data) override; + void writeBytes(const std::vector data) override; + private: std::basic_ostream* mStream{nullptr}; }; diff --git a/src/image/ImageBitStream.h b/src/image/ImageBitStream.h index 0663bd0..7a9324b 100644 --- a/src/image/ImageBitStream.h +++ b/src/image/ImageBitStream.h @@ -17,6 +17,11 @@ public: void writeByte(unsigned char data) override; + void writeBytes(const std::vector data) override + { + + } + private: Image* mImage{nullptr}; }; diff --git a/src/image/PngElements.h b/src/image/PngElements.h index 5125c80..d9612f1 100644 --- a/src/image/PngElements.h +++ b/src/image/PngElements.h @@ -1,5 +1,7 @@ #pragma once +#include "CyclicRedundancyChecker.h" + #include #include @@ -15,15 +17,23 @@ namespace Png return {13, 10, 26, 10}; } + inline std::string getName() + { + return "PNG"; + } + struct IHDRChunk { - unsigned width{0}; - unsigned height{0}; - char bitDepth{0}; - char colorType{0}; - char compressionMethod{0}; - char filterMethod{0}; - char interlaceMethod{0}; + uint32_t width{0}; + uint32_t height{0}; + unsigned char bitDepth{0}; + unsigned char colorType{0}; + unsigned char compressionMethod{0}; + unsigned char filterMethod{0}; + unsigned char interlaceMethod{0}; + std::string name{"IHDR"}; + + std::vector mData; std::string toString() const { @@ -37,6 +47,42 @@ namespace Png sstr << "interlaceMethod: " << (int)interlaceMethod << "\n"; return sstr.str(); } + + uint32_t getLength() const + { + return 13; + } + + void updateData() + { + mData.clear(); + unsigned num_bytes = sizeof(uint32_t); + + for(unsigned idx=0; idx char_data = StringUtils::toBytes(name); + std::copy(mData.begin(), mData.end(), std::back_inserter(char_data)); + + auto result = crc_check.doCrc(char_data.data(), char_data.size()); + return result; + } }; } diff --git a/src/image/PngReader.cpp b/src/image/PngReader.cpp index 77fefe0..5050c0d 100644 --- a/src/image/PngReader.cpp +++ b/src/image/PngReader.cpp @@ -88,11 +88,11 @@ void PngReader::readIDATChunk(unsigned length) { if (mAwaitingDataBlock) { - mEncoder->setCompressionMethod(mFile->readNextByte()); - mEncoder->setExtraFlags(mFile->readNextByte()); + mEncoder->setCompressionMethod(*mFile->readNextByte()); + mEncoder->setExtraFlags(*mFile->readNextByte()); for(unsigned idx=0; idxwriteByte(mFile->readNextByte()); + mInputStream->writeByte(*mFile->readNextByte()); } mAwaitingDataBlock = false; } @@ -100,7 +100,7 @@ void PngReader::readIDATChunk(unsigned length) { for(unsigned idx=0; idxwriteByte(mFile->readNextByte()); + mInputStream->writeByte(*mFile->readNextByte()); } } } diff --git a/src/image/PngWriter.cpp b/src/image/PngWriter.cpp index 3064021..73a3384 100644 --- a/src/image/PngWriter.cpp +++ b/src/image/PngWriter.cpp @@ -9,6 +9,8 @@ #include "Lz77Encoder.h" +#include "ByteUtils.h" + #include PngWriter::PngWriter() @@ -34,15 +36,49 @@ void PngWriter::setPath(const Path& path) void PngWriter::writeSignature() { mOutStream->writeByte(Png::getHighBitCheck()); - for (auto byte : Png::getSignature()) - { - mOutStream->writeByte(byte); - } + mOutStream->writeBytes(StringUtils::toBytes(Png::getName())); + mOutStream->writeBytes(Png::getSignature()); } void PngWriter::writeHeader() { writeSignature(); + + Png::IHDRChunk header_chunk; + header_chunk.width = mWorkingImage->getWidth(); + header_chunk.height = mWorkingImage->getHeight(); + header_chunk.bitDepth = mWorkingImage->getBitDepth(); + header_chunk.colorType = 6; + + auto length = header_chunk.getLength(); + auto crc = header_chunk.getCrc(); + + unsigned num_bytes = sizeof(uint32_t); + for(unsigned idx=0; idxwriteByte(ByteUtils::getByteN(length, idx)); + } + mOutStream->writeBytes(StringUtils::toBytes(header_chunk.name)); + + for(unsigned idx=0; idxwriteByte(ByteUtils::getByteN(header_chunk.width, idx)); + } + + for(unsigned idx=0; idxwriteByte(ByteUtils::getByteN(header_chunk.height, idx)); + } + mOutStream->writeByte(header_chunk.bitDepth); + mOutStream->writeByte(header_chunk.colorType); + mOutStream->writeByte(header_chunk.compressionMethod); + mOutStream->writeByte(header_chunk.filterMethod); + mOutStream->writeByte(header_chunk.interlaceMethod); + + for(unsigned idx=0; idxwriteByte(ByteUtils::getByteN(crc, idx)); + } } void PngWriter::write(const std::unique_ptr >& image) @@ -50,6 +86,7 @@ void PngWriter::write(const std::unique_ptr >& image) if (!mPath.empty()) { mWorkingFile = std::make_unique(mPath); + mWorkingFile->SetAccessMode(File::AccessMode::Write); mWorkingFile->Open(true); mOutStream = std::make_unique(mWorkingFile->GetOutHandle()); } diff --git a/src/image/PngWriter.h b/src/image/PngWriter.h index b8e8656..db96936 100644 --- a/src/image/PngWriter.h +++ b/src/image/PngWriter.h @@ -27,6 +27,7 @@ private: void writeSignature(); void writeHeader(); + //void writeIDatChunk(); Path mPath; Image* mWorkingImage{nullptr}; diff --git a/test/core/TestByteUtils.cpp b/test/core/TestByteUtils.cpp index ae88e84..6b545a7 100644 --- a/test/core/TestByteUtils.cpp +++ b/test/core/TestByteUtils.cpp @@ -13,6 +13,16 @@ int main() auto slice = ByteUtils::getMBitsAtN(byte, 3, 3); std::cout << "Slice is " << ByteUtils::toString(slice) << std::endl; + uint32_t input {12345678}; + auto byte0 = ByteUtils::getByteN(input, 0); + auto byte1 = ByteUtils::getByteN(input, 1); + auto byte2 = ByteUtils::getByteN(input, 2); + auto byte3 = ByteUtils::getByteN(input, 3); + + std::cout << "Byte0 is " << ByteUtils::toString(byte0) << std::endl; + std::cout << "Byte1 is " << ByteUtils::toString(byte1) << std::endl; + std::cout << "Byte2 is " << ByteUtils::toString(byte2) << std::endl; + std::cout << "Byte3 is " << ByteUtils::toString(byte3) << std::endl; return 0; } diff --git a/test/image/TestPngWriter.cpp b/test/image/TestPngWriter.cpp index 26c6549..b6df3c5 100644 --- a/test/image/TestPngWriter.cpp +++ b/test/image/TestPngWriter.cpp @@ -1,7 +1,7 @@ #include "Image.h" #include "PngWriter.h" - +#include "File.h" #include "BitStream.h" #include "ImagePrimitives.h" @@ -24,5 +24,15 @@ int main() writer.setPath("test.png"); writer.write(image); + File test_file("test.png"); + test_file.SetAccessMode(File::AccessMode::Read); + test_file.Open(true); + + while(auto byte = test_file.readNextByte()) + { + std::cout << static_cast(*byte) << std::endl; + } + test_file.Close(); + return 0; }