From ff962a6b16fd5c95989ebd45530055e13f076e47 Mon Sep 17 00:00:00 2001 From: James Grogan Date: Mon, 21 Nov 2022 17:45:12 +0000 Subject: [PATCH] Some encode/decode practice. --- src/compression/CMakeLists.txt | 2 + src/compression/HuffmanEncoder.cpp | 155 +++++++++------- src/compression/HuffmanEncoder.h | 12 +- .../RunLengthEncoder.cpp} | 0 src/compression/RunLengthEncoder.h | 110 ++++++++++++ .../ZlibData.cpp} | 0 src/compression/ZlibData.h | 97 ++++++++++ src/core/ByteUtils.h | 40 +++++ src/core/CMakeLists.txt | 1 + src/core/StringUtils.cpp | 5 + src/core/StringUtils.h | 3 + src/core/data_structures/RawTree.cpp | 0 src/core/data_structures/RawTree.h | 97 ++++++++++ src/core/data_structures/Tree.h | 86 +++++---- src/core/file_utilities/File.cpp | 5 + src/core/file_utilities/File.h | 2 + src/image/CMakeLists.txt | 20 +-- src/image/ImagePrimitives.h | 15 ++ src/image/PngElements.h | 33 ++++ src/image/PngReader.cpp | 168 ++++++++++-------- src/image/PngReader.h | 40 ++--- src/image/PngWriter.cpp | 20 +-- src/image/PngWriter.h | 16 +- src/image/PngWriterBasic.cpp | 11 -- src/image/PngWriterBasic.h | 14 -- src/image/PngWriterImpl.h | 16 -- test/compression/TestStreamCompressor.cpp | 42 ++++- test/image/TestPngReader.cpp | 2 +- test/image/TestPngWriter.cpp | 20 +-- 29 files changed, 727 insertions(+), 305 deletions(-) rename src/{image/PngWriterLibPng.cpp => compression/RunLengthEncoder.cpp} (100%) create mode 100644 src/compression/RunLengthEncoder.h rename src/{image/PngWriterLibPng.h => compression/ZlibData.cpp} (100%) create mode 100644 src/compression/ZlibData.h create mode 100644 src/core/data_structures/RawTree.cpp create mode 100644 src/core/data_structures/RawTree.h create mode 100644 src/image/PngElements.h delete mode 100644 src/image/PngWriterBasic.cpp delete mode 100644 src/image/PngWriterBasic.h delete mode 100644 src/image/PngWriterImpl.h diff --git a/src/compression/CMakeLists.txt b/src/compression/CMakeLists.txt index a635f45..bb4f7a0 100644 --- a/src/compression/CMakeLists.txt +++ b/src/compression/CMakeLists.txt @@ -2,6 +2,8 @@ list(APPEND compression_LIB_INCLUDES StreamCompressor.cpp HuffmanEncoder.cpp + RunLengthEncoder.cpp + ZlibData.cpp ) add_library(compression SHARED ${compression_LIB_INCLUDES}) diff --git a/src/compression/HuffmanEncoder.cpp b/src/compression/HuffmanEncoder.cpp index 1b2901f..852f8d7 100644 --- a/src/compression/HuffmanEncoder.cpp +++ b/src/compression/HuffmanEncoder.cpp @@ -1,77 +1,100 @@ #include "HuffmanEncoder.h" -#include "Tree.h" +#include "RawTree.h" #include #include #include #include +void HuffmanEncoder::dumpNode(RawNode* node, unsigned depth) const +{ + if (!node) + { + return; + } + + auto data = node->getData(); + + std::string prefix(depth, '_'); + + if (node->isLeaf()) + { + std::cout << prefix << "Leaf with value: " << data.first << " and sum " << data.second << std::endl; + } + else + { + std::cout << prefix << "Intermediate with sum " << data.second << std::endl; + std::cout << prefix << "Doing Left.." << std::endl; + dumpNode(node->getLeftChild(), depth+1); + + std::cout << prefix << "Doing Right.." << std::endl; + dumpNode(node->getRightChild(), depth+1); + + std::cout << prefix << "*****" << std::endl; + } +} + +void HuffmanEncoder::dumpTree(const RawTree& tree) const +{ + dumpNode(tree.getRootNode(), 0); +} + void HuffmanEncoder::encode(const HuffmanEncoder::DataStream& stream) { - std::unordered_map counts; - for (auto c : stream) - { - counts[c]++; - } - - using CountPair = std::pair; - auto cmp = [](CountPair left, CountPair right) - { - return left.second > right.second; - }; - std::priority_queue, decltype(cmp)> q(cmp); - for (const auto& entry : counts) - { - q.push({entry.first, entry.second}); - } - - NodePtr lastNode; - while(!q.empty()) - { - const auto charData = q.top(); - auto characterNode = std::make_unique >(charData); - q.pop(); - - if (!lastNode) - { - const auto rightCharData = q.top(); - auto rightCharacterNode = std::make_unique >(rightCharData); - q.pop(); - - const auto sum = charData.second + rightCharData.second; - CountPair data{0, sum}; - auto midNode = std::make_unique >(data); - - midNode->addChild(std::move(characterNode)); - midNode->addChild(std::move(rightCharacterNode)); - lastNode = std::move(midNode); - } - else - { - const auto sum = lastNode->getData().second; - CountPair data{0, sum}; - auto midNode = std::make_unique >(data); - - if (charData.second < lastNode->getData().second) - { - midNode->addChild(std::move(lastNode)); - midNode->addChild(std::move(characterNode)); - } - else - { - midNode->addChild(std::move(characterNode)); - midNode->addChild(std::move(lastNode)); - } - lastNode = std::move(midNode); - } - } - - Tree tree; - tree.addRootNode(std::move(lastNode)); - - //using TableEntry = std::tuple<> - - - std::cout << "********" << std::endl; + std::unordered_map counts; + for (auto c : stream) + { + counts[c]++; + } + encode(counts); +} + +void HuffmanEncoder::encode(const std::unordered_map& counts) +{ + std::cout << "Counts" << std::endl; + for (const auto& data: counts) + { + std::cout << data.first << " | " << data.second << std::endl; + } + std::cout << "*******" << std::endl; + + auto cmp = [](RawNode* left, RawNode* right) + { + return left->getData().second > right->getData().second; + }; + + std::priority_queue*, std::vector* >, decltype(cmp)> q(cmp); + for (const auto& entry : counts) + { + q.push(new RawNode(entry)); + } + + while(q.size() > 1) + { + auto node0 = q.top(); + q.pop(); + + auto node1 = q.top(); + q.pop(); + + const auto sum = node0->getData().second + node1->getData().second; + auto new_node = new RawNode(CountPair{0, sum}); + + new_node->addChild(node0); + new_node->addChild(node1); + q.push(new_node); + } + + auto root = q.top(); + q.pop(); + + RawTree tree; + tree.addRootNode(root); + + //using TableEntry = std::tuple<> + + dumpTree(tree); + + std::cout << "********" << std::endl; } diff --git a/src/compression/HuffmanEncoder.h b/src/compression/HuffmanEncoder.h index 857c988..39c9c7f 100644 --- a/src/compression/HuffmanEncoder.h +++ b/src/compression/HuffmanEncoder.h @@ -1,11 +1,21 @@ #pragma once +#include "RawTree.h" + #include +#include class HuffmanEncoder { using DataStream = std::vector; +using CountPair = std::pair; public: - void encode(const DataStream& stream); + void encode(const DataStream& stream); + + void encode(const std::unordered_map& counts); + +private: + void dumpTree(const RawTree& tree) const; + void dumpNode(RawNode* node, unsigned depth) const; }; diff --git a/src/image/PngWriterLibPng.cpp b/src/compression/RunLengthEncoder.cpp similarity index 100% rename from src/image/PngWriterLibPng.cpp rename to src/compression/RunLengthEncoder.cpp diff --git a/src/compression/RunLengthEncoder.h b/src/compression/RunLengthEncoder.h new file mode 100644 index 0000000..c9fb898 --- /dev/null +++ b/src/compression/RunLengthEncoder.h @@ -0,0 +1,110 @@ +#pragma once + +#include "StringUtils.h" + +#include +#include + +class RunLengthEncoder +{ +public: + std::string encode(const std::string& string) + { + std::string ret; + if (string.empty()) + { + return ret; + } + + char working_char{0}; + unsigned count = 1; + for(unsigned idx=0; idx= 3) + { + output += mDelimiter + std::to_string(count) + c; + } + else + { + for (unsigned jdx=0;jdx +#include + +class ZlibData +{ +public: + void setByte(unsigned idx, unsigned char data) + { + mData[idx] = data; + } + + void setDataSize(std::size_t size) + { + mData = std::vector(size); + } + + void setCompressionMethod(unsigned char method) + { + std::cout << "Got compression input " << static_cast(method) << std::endl; + mCmf = method; + mCompressionMethod = ByteUtils::getLowerNBits(method, 4); + mCompressionInfo = ByteUtils::getHigherNBits(method, 4); + + std::cout << "Got compression method " << static_cast(mCompressionMethod) << " and info " << static_cast(mCompressionInfo) << std::endl; + } + + void setExtraFlags(unsigned char extraFlags) + { + std::cout << "Got flags " << static_cast(extraFlags) << std::endl; + + mFlg = extraFlags; + mFlagCheck = ByteUtils::getLowerNBits(extraFlags, 5); + mFlagDict = ByteUtils::getBitN(extraFlags, 5); + mFlagLevel = ByteUtils::getHigherNBits(extraFlags, 2); + + std::cout << "Got flag check " << static_cast(mFlagCheck) << " and dict " << static_cast(mFlagDict) << " and level " << static_cast(mFlagLevel) << std::endl; + } + + void processData() + { + unsigned char NO_COMPRESSION = 0x00; + unsigned char FIXED_HUFFMAN = 0x01; + unsigned char DYNAMIC_HUFFMAN = 0x02; + unsigned char ERROR = 0x03; + + bool in_final_block = false; + unsigned working_byte_id = 0; + for (unsigned idx=0; idx(working_byte) << std::endl; + + auto final_block = ByteUtils::getBitN(working_byte, 0); + if (final_block) + { + std::cout << "Got final block" << std::endl; + in_final_block = true; + } + + auto compress_type = ByteUtils::getTwoBitsAtN(working_byte, 1); + std::cout << "Compress type byte is: " << static_cast(compress_type) << std::endl; + + if (compress_type == NO_COMPRESSION) + { + std::cout << "Got NO_COMPRESSION" << std::endl; + } + else if (compress_type == FIXED_HUFFMAN) + { + std::cout << "Got FIXED_HUFFMAN" << std::endl; + } + else if (compress_type == DYNAMIC_HUFFMAN) + { + std::cout << "Got DYNAMIC_HUFFMAN" << std::endl; + } + else if (compress_type == ERROR) + { + std::cout << "Got ERROR" << std::endl; + } + break; + } + } + +private: + std::vector mData; + unsigned char mCmf{0}; + unsigned char mFlg{0}; + unsigned char mCompressionMethod{0}; + unsigned char mCompressionInfo{0}; + unsigned char mFlagCheck{0}; + unsigned char mFlagDict{0}; + unsigned char mFlagLevel{0}; + unsigned char mCheckValue{0}; +}; diff --git a/src/core/ByteUtils.h b/src/core/ByteUtils.h index 7780b7a..a29928a 100644 --- a/src/core/ByteUtils.h +++ b/src/core/ByteUtils.h @@ -25,6 +25,46 @@ public: return word & ByteUtils::WORD_LAST_BYTE; } + static unsigned char getHigherNBits(unsigned char input, unsigned num) + { + return input >> 8 - num; + } + + static unsigned char getLowerNBits(unsigned char input, unsigned num) + { + switch (num) + { + case 1: + return input & 0x01; + case 2: + return input & 0x03; + case 3: + return input & 0x07; + case 4: + return input & 0x0F; + case 5: + return input & 0x1F; + case 6: + return input & 0x3F; + case 7: + return input & 0x7F; + case 8: + return input; + default: + return 0; + } + } + + static unsigned char getTwoBitsAtN(unsigned char input, unsigned n) + { + return (input & (0x03 << n)) >> n; + } + + static unsigned char getBitN(unsigned char input, unsigned n) + { + return input & (1 << n); + } + static void ReverseBuffer(char* buffer, char* reverse, unsigned size, unsigned targetSize) { for(unsigned idx=0; idx StringUtils::toLines(const std::string& input) { auto result = std::vector{}; diff --git a/src/core/StringUtils.h b/src/core/StringUtils.h index 45db271..14c6784 100644 --- a/src/core/StringUtils.h +++ b/src/core/StringUtils.h @@ -17,6 +17,9 @@ public: static constexpr char COLON = ':'; static bool IsAlphaNumeric(char c); + + static bool IsAlphabetical(char c); + static bool IsSpace(char c); static std::string ToLower(const std::string& s); static std::string convert(const std::wstring& input); diff --git a/src/core/data_structures/RawTree.cpp b/src/core/data_structures/RawTree.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/core/data_structures/RawTree.h b/src/core/data_structures/RawTree.h new file mode 100644 index 0000000..633473e --- /dev/null +++ b/src/core/data_structures/RawTree.h @@ -0,0 +1,97 @@ +#pragma once + +#include + +template +class RawNode +{ +public: + RawNode(T data) + : mData(data) + { + + + } + + ~RawNode() + { + if(mLeftChild) + { + delete mLeftChild; + } + + if(mRightChild) + { + delete mRightChild; + } + } + + void addChild(RawNode* child) + { + if(!mLeftChild) + { + mLeftChild = child; + } + else + { + mRightChild = child; + } + } + + bool isLeaf() const + { + return !mLeftChild && !mRightChild; + } + + T getData() const + { + return mData; + } + + RawNode* getLeftChild() const + { + return mLeftChild; + } + + RawNode* getRightChild() const + { + return mRightChild; + } + +private: + T mData; + unsigned char mTag{0}; + RawNode* mLeftChild{nullptr}; + RawNode* mRightChild{nullptr}; +}; + +template +class RawTree +{ +public: + RawTree() + { + + } + + ~RawTree() + { + if (mRootNode) + { + delete mRootNode; + } + } + + void addRootNode(RawNode* root) + { + mRootNode = root; + } + + RawNode* getRootNode() const + { + return mRootNode; + } + +private: + RawNode* mRootNode{nullptr}; +}; diff --git a/src/core/data_structures/Tree.h b/src/core/data_structures/Tree.h index 57166ac..7b19016 100644 --- a/src/core/data_structures/Tree.h +++ b/src/core/data_structures/Tree.h @@ -6,37 +6,47 @@ template class Node { public: - Node(T data) - : mData(data) - {} + Node(T data) + : mData(data) + {} - void addChild(std::unique_ptr child) - { - if(!mLeftChild) - { - mLeftChild = std::move(child); - } - else - { - mRightChild = std::move(child); - } - } + void addChild(std::unique_ptr child) + { + if(!mLeftChild) + { + mLeftChild = std::move(child); + } + else + { + mRightChild = std::move(child); + } + } - bool isLeaf() const - { - return !mLeftChild && !mRightChild; - } + bool isLeaf() const + { + return !mLeftChild && !mRightChild; + } - T getData() const - { - return mData; - } + T getData() const + { + return mData; + } + + Node* getLeftChild() const + { + return mLeftChild.get(); + } + + Node* getRightChild() const + { + return mRightChild.get(); + } private: - T mData; - unsigned char mTag{0}; - std::unique_ptr mLeftChild; - std::unique_ptr mRightChild; + T mData; + unsigned char mTag{0}; + std::unique_ptr mLeftChild; + std::unique_ptr mRightChild; }; template @@ -46,21 +56,21 @@ template class Tree { public: - Tree() - { + Tree() + { - } + } - void addRootNode(NodePtr root) - { - mRootNode = std::move(root); - } + void addRootNode(NodePtr root) + { + mRootNode = std::move(root); + } - Node* getRootNode() const - { - return mRootNode.get(); - } + Node* getRootNode() const + { + return mRootNode.get(); + } private: - NodePtr mRootNode; + NodePtr mRootNode; }; diff --git a/src/core/file_utilities/File.cpp b/src/core/file_utilities/File.cpp index 28673de..9c1f29c 100644 --- a/src/core/file_utilities/File.cpp +++ b/src/core/file_utilities/File.cpp @@ -32,6 +32,11 @@ std::ofstream* File::GetOutHandle() const return mOutHandle.get(); } +unsigned char File::readNextByte() +{ + return mInHandle->get(); +} + void File::Open(bool asBinary) { if(mAccessMode == AccessMode::Read) diff --git a/src/core/file_utilities/File.h b/src/core/file_utilities/File.h index c594f0a..1fe93e3 100644 --- a/src/core/file_utilities/File.h +++ b/src/core/file_utilities/File.h @@ -48,6 +48,8 @@ public: void Close(); + unsigned char readNextByte(); + private: diff --git a/src/image/CMakeLists.txt b/src/image/CMakeLists.txt index bcfa8cf..b93813d 100644 --- a/src/image/CMakeLists.txt +++ b/src/image/CMakeLists.txt @@ -1,38 +1,22 @@ list(APPEND image_HEADERS Image.h PngWriter.h - PngWriterBasic.h - PngWriterImpl.h ) list(APPEND image_LIB_INCLUDES Image.cpp PngWriter.cpp - PngWriterBasic.cpp PngReader.cpp ) -list(APPEND image_LIBS core) +list(APPEND image_LIBS core compression) list(APPEND image_DEFINES "") -find_package(PNG QUIET) -if(PNG_FOUND) - list(APPEND image_LIBS PNG::PNG) - list(APPEND image_LIB_INCLUDES - PngWriterLibPng.cpp - ) - list(APPEND image_DEFINES HAS_LIBPNG) -else() - message(STATUS "LIBRARY CHECK: libPNG not found - disabling libPNG based image i/o.") -endif() - add_library(image SHARED ${image_LIB_INCLUDES} ${image_HEADERS}) #target_compile_definitions(image PRIVATE ${image_DEFINES}) -target_include_directories(image PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}" - ) +target_include_directories(image PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") set_target_properties( image PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) target_link_libraries( image PUBLIC ${image_LIBS}) diff --git a/src/image/ImagePrimitives.h b/src/image/ImagePrimitives.h index b7a6d2c..834c6cd 100644 --- a/src/image/ImagePrimitives.h +++ b/src/image/ImagePrimitives.h @@ -23,4 +23,19 @@ public: } } } + + static void drawAlternatingStrips(std::vector& data, unsigned width, unsigned height, unsigned channels, unsigned bytesPerRow) + { + for(unsigned jdx=0;jdx +#include + + +namespace Png +{ + struct IHDRChunk + { + unsigned width{0}; + unsigned height{0}; + char bitDepth{0}; + char colorType{0}; + char compressionMethod{0}; + char filterMethod{0}; + char interlaceMethod{0}; + + std::string toString() const + { + std::stringstream sstr; + sstr << "width: " << width << "\n"; + sstr << "height: " << height << "\n"; + sstr << "bitDepth: " << (int)bitDepth << "\n"; + sstr << "colorType: " << (int)colorType << "\n"; + sstr << "compressionMethod: " << (int)compressionMethod << "\n"; + sstr << "filterMethod: " << (int)filterMethod << "\n"; + sstr << "interlaceMethod: " << (int)interlaceMethod << "\n"; + return sstr.str(); + } + }; +} + diff --git a/src/image/PngReader.cpp b/src/image/PngReader.cpp index dad6b2e..47ea371 100644 --- a/src/image/PngReader.cpp +++ b/src/image/PngReader.cpp @@ -1,7 +1,6 @@ #include "PngReader.h" -#include "BinaryStream.h" -#include "Image.h" +#include "BinaryStream.h" #include #include @@ -11,120 +10,135 @@ PngReader::~PngReader() } -void PngReader::setPath(const std::string& path) +void PngReader::setPath(const Path& path) { - mPath = path; + mPath = path; } bool PngReader::checkSignature() { - const int highBitCheck = 0x89; - const auto firstPos = mFile->GetInHandle()->get(); - if (firstPos != highBitCheck) - { - return false; - } + const int highBitCheck = 0x89; + const auto firstPos = mFile->GetInHandle()->get(); + if (firstPos != highBitCheck) + { + return false; + } - std::string fileType; - BinaryStream::getNextString(mFile->GetInHandle(), fileType, 3); - if (fileType != "PNG") - { - return false; - } + std::string fileType; + BinaryStream::getNextString(mFile->GetInHandle(), fileType, 3); + if (fileType != "PNG") + { + return false; + } - std::vector sequence{13, 10, 26, 10}; - for (auto c : sequence) - { - if (mFile->GetInHandle()->get() != c) - { - return false; - } - } + std::vector sequence{13, 10, 26, 10}; + for (auto c : sequence) + { + if (mFile->GetInHandle()->get() != c) + { + return false; + } + } - mCurrentOffset += 8; - return true; + mCurrentOffset += 8; + return true; } bool PngReader::readChunk() { - unsigned length = *BinaryStream::getNextDWord(mFile->GetInHandle()); + unsigned length = *BinaryStream::getNextDWord(mFile->GetInHandle()); - std::string chunkType; - BinaryStream::getNextString(mFile->GetInHandle(), chunkType, 4); - mCurrentOffset += 8; + 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") - { - parseHeader(); - } - else if(chunkType == "IEND") - { - lastChunk = true; - } - else - { - for(unsigned idx=0;idxGetInHandle()->get(); - } - } + std::cout << "Got chunk with type: " << chunkType << " and length: " << length << std::endl; + bool lastChunk = false; + if (chunkType == "IHDR") + { + readHeaderChunk(); + } + else if(chunkType == "IEND") + { + lastChunk = true; + } + else if(chunkType == "IDAT") + { + readIDATChunk(length); + } + else + { + for(unsigned idx=0;idxGetInHandle()->get(); + } + } - unsigned crcCheck = *BinaryStream::getNextDWord(mFile->GetInHandle()); + unsigned crcCheck = *BinaryStream::getNextDWord(mFile->GetInHandle()); - mCurrentOffset += 4; - return !lastChunk; + mCurrentOffset += 4; + return !lastChunk; } -void PngReader::parseHeader() +void PngReader::readIDATChunk(unsigned length) { - mIHDRChunk.width = *BinaryStream::getNextDWord(mFile->GetInHandle()); - mIHDRChunk.height = *BinaryStream::getNextDWord(mFile->GetInHandle()); - mIHDRChunk.bitDepth = mFile->GetInHandle()->get(); - mIHDRChunk.colorType = mFile->GetInHandle()->get(); - mIHDRChunk.compressionMethod = mFile->GetInHandle()->get(); - mIHDRChunk.filterMethod = mFile->GetInHandle()->get(); - mIHDRChunk.interlaceMethod = mFile->GetInHandle()->get(); + if (mAwaitingDataBlock) + { + mImageData.setCompressionMethod(mFile->readNextByte()); + mImageData.setExtraFlags(mFile->readNextByte()); + mImageData.setDataSize(length-2); + for(unsigned idx=0; idxreadNextByte()); + } + mAwaitingDataBlock = false; + } + else + { + for(unsigned idx=0; idxreadNextByte()); + } + } +} - mCurrentOffset += 13; +void PngReader::readHeaderChunk() +{ + mIHDRChunk.width = *BinaryStream::getNextDWord(mFile->GetInHandle()); + mIHDRChunk.height = *BinaryStream::getNextDWord(mFile->GetInHandle()); + mIHDRChunk.bitDepth = mFile->GetInHandle()->get(); + mIHDRChunk.colorType = mFile->GetInHandle()->get(); + mIHDRChunk.compressionMethod = mFile->GetInHandle()->get(); + mIHDRChunk.filterMethod = mFile->GetInHandle()->get(); + mIHDRChunk.interlaceMethod = mFile->GetInHandle()->get(); - logHeader(); + mCurrentOffset += 13; + + logHeader(); } void PngReader::logHeader() { - std::stringstream sstr; - sstr << "IHDR\n"; - sstr << "width: " << mIHDRChunk.width << "\n"; - sstr << "height: " << mIHDRChunk.height << "\n"; - sstr << "bitDepth: " << (int)mIHDRChunk.bitDepth << "\n"; - sstr << "colorType: " << (int)mIHDRChunk.colorType << "\n"; - sstr << "compressionMethod: " << (int)mIHDRChunk.compressionMethod << "\n"; - sstr << "filterMethod: " << (int)mIHDRChunk.filterMethod << "\n"; - sstr << "interlaceMethod: " << (int)mIHDRChunk.interlaceMethod << "\n"; - - sstr << "************\n"; - std::cout << sstr.str() << std::endl; + std::cout << "IHDR\n" << mIHDRChunk.toString() << "*************\n"; } std::unique_ptr > PngReader::read() { - auto image = std::make_unique >(5, 5); + auto image = std::make_unique >(5, 5); mFile = std::make_unique(mPath); mFile->Open(true); if (!checkSignature()) { - std::cout << "Signature check failed" << std::endl; - return image; + std::cout << "Signature check failed" << std::endl; + return image; } while(readChunk()) { } - - return std::move(image); + mImageData.processData(); + return std::move(image); } diff --git a/src/image/PngReader.h b/src/image/PngReader.h index 23c7397..b2062e0 100644 --- a/src/image/PngReader.h +++ b/src/image/PngReader.h @@ -1,44 +1,42 @@ #pragma once -#include -#include -#include - #include "File.h" #include "Image.h" +#include "PngElements.h" +#include "ZlibData.h" + +#include +#include +#include + +using Path = std::filesystem::path; class PngReader { public: ~PngReader(); - void setPath(const std::string& path); + void setPath(const Path& path); std::unique_ptr > read(); - private: - - struct IHDRChunk - { - unsigned width{0}; - unsigned height{0}; - char bitDepth{0}; - char colorType{0}; - char compressionMethod{0}; - char filterMethod{0}; - char interlaceMethod{0}; - }; - bool readChunk(); - void parseHeader(); + + void readHeaderChunk(); + + void readIDATChunk(unsigned length); + void logHeader(); bool checkSignature(); unsigned mCurrentOffset{0}; - IHDRChunk mIHDRChunk; + Png::IHDRChunk mIHDRChunk; std::unique_ptr > mWorkingImage; std::unique_ptr mFile; - std::string mPath; + Path mPath; + + ZlibData mImageData; + bool mAwaitingDataBlock{true}; }; diff --git a/src/image/PngWriter.cpp b/src/image/PngWriter.cpp index 985c2de..e3b53c2 100644 --- a/src/image/PngWriter.cpp +++ b/src/image/PngWriter.cpp @@ -2,21 +2,11 @@ #include "Image.h" -#ifdef HAS_LIBPNG -#include -#include "PngWriterLibPng.h" -#else -#include "PngWriterBasic.h" -#endif #include PngWriter::PngWriter() { -#ifdef HAS_LIBPNG - mImpl = std::make_unique(); -#else - mImpl = std::make_unique(); -#endif + } std::unique_ptr PngWriter::Create() @@ -24,14 +14,14 @@ std::unique_ptr PngWriter::Create() return std::make_unique(); } -void PngWriter::SetPath(const std::string& path) +void PngWriter::setPath(const Path& path) { - mImpl->setPath(path); + mPath = path; } -void PngWriter::Write(const std::unique_ptr >& image) const +void PngWriter::write(const std::unique_ptr >& image) const { - mImpl->write(image); + //mImpl->write(image); //auto fp = fopen(mPath.c_str(), "wb"); //auto png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); diff --git a/src/image/PngWriter.h b/src/image/PngWriter.h index b90f227..3712d56 100644 --- a/src/image/PngWriter.h +++ b/src/image/PngWriter.h @@ -1,11 +1,12 @@ #pragma once -#include -#include - #include "Image.h" -class PngWriterImpl; +#include +#include +#include + +using Path = std::filesystem::path; class PngWriter { @@ -14,13 +15,12 @@ public: static std::unique_ptr Create(); - void SetPath(const std::string& path); + void setPath(const Path& path); - void Write(const std::unique_ptr >& image) const; + void write(const std::unique_ptr >& image) const; private: - std::unique_ptr mImpl; - + Path mPath; }; using PngWriterPtr = std::unique_ptr; diff --git a/src/image/PngWriterBasic.cpp b/src/image/PngWriterBasic.cpp deleted file mode 100644 index ef21abb..0000000 --- a/src/image/PngWriterBasic.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "PngWriterBasic.h" - -void PngWriterBasic::setPath(const std::string& path) -{ - -} - -void PngWriterBasic::write(const std::unique_ptr >& image) const -{ - -} diff --git a/src/image/PngWriterBasic.h b/src/image/PngWriterBasic.h deleted file mode 100644 index 4fbbfdc..0000000 --- a/src/image/PngWriterBasic.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "PngWriterImpl.h" - -class PngWriterBasic : public PngWriterImpl -{ -public: - void setPath(const std::string& path) override; - - void write(const std::unique_ptr>& image) const override; - -private: - std::string mPath; -}; diff --git a/src/image/PngWriterImpl.h b/src/image/PngWriterImpl.h deleted file mode 100644 index 084c4a7..0000000 --- a/src/image/PngWriterImpl.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include -#include - -#include "Image.h" - -class PngWriterImpl -{ -public: - virtual ~PngWriterImpl() = default; - - virtual void setPath(const std::string& path) = 0; - - virtual void write(const std::unique_ptr >& image) const = 0; -}; diff --git a/test/compression/TestStreamCompressor.cpp b/test/compression/TestStreamCompressor.cpp index 37cb691..cf62548 100644 --- a/test/compression/TestStreamCompressor.cpp +++ b/test/compression/TestStreamCompressor.cpp @@ -1,14 +1,46 @@ #include #include "HuffmanEncoder.h" +#include "RunLengthEncoder.h" + +void test_run_length_encoder() +{ + std::string test_data = "BCAAAADDDCCACACAC"; + + RunLengthEncoder encoder; + auto encoded = encoder.encode(test_data); + + std::cout << "Encoded: " << encoded << std::endl; + + auto decoded = encoder.decode(encoded); + + std::cout << "Decoded: " << decoded << std::endl; +} + +void test_huffman_encoder() +{ + //std::string testData = "BCAADDDCCACACAC"; + //std::vector stream(testData.begin(), testData.end()); + + std::unordered_map counts; + counts['A'] = 1; + counts['B'] = 1; + counts['C'] = 1; + counts['D'] = 2; + counts['E'] = 3; + counts['F'] = 5; + counts['G'] = 5; + counts['H'] = 12; + + HuffmanEncoder encoder; + encoder.encode(counts); + +} int main() { - std::string testData = "BCAADDDCCACACAC"; - std::vector stream(testData.begin(), testData.end()); - - HuffmanEncoder encoder; - encoder.encode(stream); + test_huffman_encoder(); + //test_run_length_encoder(); return 0; } diff --git a/test/image/TestPngReader.cpp b/test/image/TestPngReader.cpp index ce3d865..7062b02 100644 --- a/test/image/TestPngReader.cpp +++ b/test/image/TestPngReader.cpp @@ -5,7 +5,7 @@ int main() { - const auto path = "/home/jmsgrogan/code/MediaTool-build/bin/test.png"; + const auto path = "/home/jmsgrogan/Downloads/test.png"; PngReader reader; reader.setPath(path); diff --git a/test/image/TestPngWriter.cpp b/test/image/TestPngWriter.cpp index c01cf9d..2124afd 100644 --- a/test/image/TestPngWriter.cpp +++ b/test/image/TestPngWriter.cpp @@ -1,6 +1,7 @@ #include "Image.h" #include "PngWriter.h" -#include "PngWriterImpl.h" + +#include "ImagePrimitives.h" #include @@ -13,22 +14,13 @@ int main() image->setNumChannels(numChannels); std::vector data(image->getBytesPerRow()*height, 0); - for(unsigned jdx=0;jdxgetBytesPerRow(); - for(unsigned idx=0;idxgetBytesPerRow()); image->setData(data); PngWriter writer; - writer.SetPath("test.png"); - writer.Write(image); + writer.setPath("test.png"); + writer.write(image); return 0; }