From 5400a232dd489dcfc9d838879a55f026e4d694fe Mon Sep 17 00:00:00 2001 From: James Grogan Date: Thu, 24 Nov 2022 09:05:39 +0000 Subject: [PATCH] Circle buffer and png cleaning. --- src/core/data_structures/CircleBuffer.h | 64 +++++++++++++++++ src/image/CMakeLists.txt | 11 +-- src/image/PngElements.h | 91 ------------------------- src/image/png/PngHeader.cpp | 89 ++++++++++++++++++++++++ src/image/png/PngHeader.h | 40 +++++++++++ src/image/png/PngInfo.h | 56 +++++++++++++++ src/image/{ => png}/PngReader.cpp | 22 +++--- src/image/{ => png}/PngReader.h | 4 +- src/image/{ => png}/PngWriter.cpp | 50 ++++++++++---- src/image/{ => png}/PngWriter.h | 7 ++ test/CMakeLists.txt | 1 + test/core/TestDataStructures.cpp | 34 +++++++++ test/image/TestPngWriter.cpp | 6 +- 13 files changed, 353 insertions(+), 122 deletions(-) create mode 100644 src/core/data_structures/CircleBuffer.h delete mode 100644 src/image/PngElements.h create mode 100644 src/image/png/PngHeader.cpp create mode 100644 src/image/png/PngHeader.h create mode 100644 src/image/png/PngInfo.h rename src/image/{ => png}/PngReader.cpp (82%) rename src/image/{ => png}/PngReader.h (93%) rename src/image/{ => png}/PngWriter.cpp (69%) rename src/image/{ => png}/PngWriter.h (84%) create mode 100644 test/core/TestDataStructures.cpp diff --git a/src/core/data_structures/CircleBuffer.h b/src/core/data_structures/CircleBuffer.h new file mode 100644 index 0000000..8b83f8b --- /dev/null +++ b/src/core/data_structures/CircleBuffer.h @@ -0,0 +1,64 @@ +#pragma once + +#include + +#include + +template +class CircleBuffer +{ +public: + CircleBuffer(std::size_t size) + : mData(size) + { + + } + + void addItem(const T& item) + { + if (mEndPointer < mData.size()) + { + mData[mEndPointer] = item; + mEndPointer++; + } + else + { + mData[mStartPointer] = item; + if (mStartPointer < mData.size() - 1) + { + mStartPointer++; + } + else + { + mStartPointer = 0; + } + } + } + + std::size_t getNumItems() const + { + return mEndPointer; + } + + const T& getItem(std::size_t index) const + { + if (mEndPointer < mData.size()) + { + return mData[index]; + } + else + { + auto offset = mStartPointer + index; + if (offset >= mData.size()) + { + offset -= mData.size(); + } + return mData[offset]; + } + } + +private: + std::size_t mStartPointer{0}; + std::size_t mEndPointer{0}; + std::vector mData; +}; diff --git a/src/image/CMakeLists.txt b/src/image/CMakeLists.txt index 2880f4f..f61ec8d 100644 --- a/src/image/CMakeLists.txt +++ b/src/image/CMakeLists.txt @@ -1,12 +1,13 @@ list(APPEND image_HEADERS Image.h - PngWriter.h + png/PngWriter.h ) list(APPEND image_LIB_INCLUDES Image.cpp - PngWriter.cpp - PngReader.cpp + png/PngWriter.cpp + png/PngReader.cpp + png/PngHeader.cpp ImageBitStream.cpp ) @@ -17,7 +18,9 @@ list(APPEND image_DEFINES "") 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} + ${CMAKE_CURRENT_SOURCE_DIR}/png) set_target_properties( image PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON ) target_link_libraries( image PUBLIC ${image_LIBS}) diff --git a/src/image/PngElements.h b/src/image/PngElements.h deleted file mode 100644 index a1867db..0000000 --- a/src/image/PngElements.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include "CyclicRedundancyChecker.h" - -#include "ByteUtils.h" -#include "StringUtils.h" - -#include -#include - -namespace Png -{ - inline unsigned char getHighBitCheck() - { - return 0x89; - } - - inline std::vector getSignature() - { - return {13, 10, 26, 10}; - } - - inline std::string getName() - { - return "PNG"; - } - - struct IHDRChunk - { - 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 - { - 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(); - } - - 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/png/PngHeader.cpp b/src/image/png/PngHeader.cpp new file mode 100644 index 0000000..536e9bb --- /dev/null +++ b/src/image/png/PngHeader.cpp @@ -0,0 +1,89 @@ +#include "PngHeader.h" + +#include "ByteUtils.h" +#include "StringUtils.h" +#include "CyclicRedundancyChecker.h" + +std::string PngHeader::toString() const +{ + std::stringstream sstr; + sstr << "PngHeader" << "\n"; + sstr << "width: " << mWidth << "\n"; + sstr << "height: " << mHeight << "\n"; + sstr << "bitDepth: " << (int)mBitDepth << "\n"; + sstr << mPngInfo.toString(); + return sstr.str(); +} + +uint32_t PngHeader::getLength() const +{ + return 13; +} + +unsigned char PngHeader::getHighBitCheck() const +{ + return 0x89; +} + +std::vector PngHeader::getSignature() const +{ + return {13, 10, 26, 10}; +} + +std::string PngHeader::getFileName() const +{ + return "PNG"; +} + +const std::string& PngHeader::getChunkName() const +{ + return "IHDR"; +} + +const std::vector& PngHeader::getData() const +{ + return mData; +} + +void PngHeader::updateData() +{ + mData.clear(); + unsigned num_bytes = sizeof(uint32_t); + + for(unsigned idx=0; idx(mPngInfo.mColorType)); + mData.push_back(mPngInfo.mCompressionMethod); + mData.push_back(mPngInfo.mFilterMethod); + mData.push_back(mPngInfo.mInterlaceMethod); +} + +uint32_t PngHeader::getCrc() const +{ + 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; +} + +void PngHeader::setPngInfo(const PngInfo& info) +{ + mPngInfo = info; +} + +void PngHeader::setImageData(uint32_t width, uint32_t height, unsigned char bitDepth) +{ + mWidth = width; + mHeight = height; + mBitDepth = bitDepth; +} diff --git a/src/image/png/PngHeader.h b/src/image/png/PngHeader.h new file mode 100644 index 0000000..05538a7 --- /dev/null +++ b/src/image/png/PngHeader.h @@ -0,0 +1,40 @@ +#pragma once + +#include "PngInfo.h" + +#include + +class PngHeader +{ +public: + uint32_t getLength() const; + + uint32_t getCrc() const; + + const std::vector& getData() const; + + unsigned char getHighBitCheck() const; + + std::vector getSignature() const; + + std::string getFileName() const; + + const std::string& getChunkName() const; + + void setPngInfo(const PngInfo& info); + + void setImageData(uint32_t width, uint32_t height, unsigned char bitDepth); + + std::string toString() const; + + void updateData(); + +private: + uint32_t mWidth{0}; + uint32_t mHeight{0}; + unsigned char mBitDepth{0}; + PngInfo mPngInfo; + std::string mName{"IHDR"}; + + std::vector mData; +}; diff --git a/src/image/png/PngInfo.h b/src/image/png/PngInfo.h new file mode 100644 index 0000000..c5347ae --- /dev/null +++ b/src/image/png/PngInfo.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +class PngInfo +{ +public: + enum class ColorType : unsigned char + { + GREYSCALE = 0, + RGB = 2, + PALETTE = 3, + GREYSCALE_ALPHA = 4, + RGB_ALPHA = 6, + }; + + std::string toString(ColorType colorType) const + { + switch(colorType) + { + case ColorType::GREYSCALE: + return "GREYSCALE"; + case ColorType::RGB: + return "RGB"; + case ColorType::PALETTE: + return "PALETTE"; + case ColorType::GREYSCALE_ALPHA: + return "GREYSCALE_ALPHA"; + case ColorType::RGB_ALPHA: + return "RGB_ALPHA"; + default: + return "UNKNOWN"; + } + } + + std::string toString() const + { + std::stringstream sstr; + sstr << "PngInfo" << "\n"; + sstr << "colorType: " << toString(mColorType) << "\n"; + sstr << "compressionMethod: " << (int)mCompressionMethod << "\n"; + sstr << "filterMethod: " << (int)mFilterMethod << "\n"; + sstr << "interlaceMethod: " << (int)mInterlaceMethod << "\n"; + return sstr.str(); + } + + ColorType mColorType{ColorType::RGB}; + unsigned char mCompressionMethod{0}; + unsigned char mFilterMethod{0}; + unsigned char mInterlaceMethod{0}; +}; + + + + diff --git a/src/image/PngReader.cpp b/src/image/png/PngReader.cpp similarity index 82% rename from src/image/PngReader.cpp rename to src/image/png/PngReader.cpp index 5050c0d..038abf3 100644 --- a/src/image/PngReader.cpp +++ b/src/image/png/PngReader.cpp @@ -107,13 +107,19 @@ void PngReader::readIDATChunk(unsigned length) 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(); + auto width = *BinaryStream::getNextDWord(mFile->GetInHandle()); + auto height = *BinaryStream::getNextDWord(mFile->GetInHandle()); + auto bitDepth = mFile->GetInHandle()->get(); + + mHeader.setImageData(width, height, bitDepth); + + PngInfo info; + + info.mColorType = static_cast(mFile->GetInHandle()->get()); + info.mCompressionMethod = mFile->GetInHandle()->get(); + info.mFilterMethod = mFile->GetInHandle()->get(); + info.mInterlaceMethod = mFile->GetInHandle()->get(); + mHeader.setPngInfo(info); mCurrentOffset += 13; @@ -122,7 +128,7 @@ void PngReader::readHeaderChunk() void PngReader::logHeader() { - std::cout << "IHDR\n" << mIHDRChunk.toString() << "*************\n"; + std::cout << "IHDR\n" << mHeader.toString() << "*************\n"; } std::unique_ptr > PngReader::read() diff --git a/src/image/PngReader.h b/src/image/png/PngReader.h similarity index 93% rename from src/image/PngReader.h rename to src/image/png/PngReader.h index a921c00..e657859 100644 --- a/src/image/PngReader.h +++ b/src/image/png/PngReader.h @@ -2,7 +2,7 @@ #include "File.h" #include "Image.h" -#include "PngElements.h" +#include "PngHeader.h" #include "ZlibEncoder.h" #include @@ -34,7 +34,7 @@ private: unsigned mCurrentOffset{0}; - Png::IHDRChunk mIHDRChunk; + PngHeader mHeader; std::unique_ptr > mWorkingImage; std::unique_ptr mFile; diff --git a/src/image/PngWriter.cpp b/src/image/png/PngWriter.cpp similarity index 69% rename from src/image/PngWriter.cpp rename to src/image/png/PngWriter.cpp index 2159700..df8650a 100644 --- a/src/image/PngWriter.cpp +++ b/src/image/png/PngWriter.cpp @@ -1,6 +1,5 @@ #include "PngWriter.h" -#include "PngElements.h" #include "Image.h" #include "File.h" #include "BufferBitStream.h" @@ -35,32 +34,55 @@ void PngWriter::setPath(const Path& path) mPath = path; } +void PngWriter::setPngInfo(const PngInfo& info) +{ + mPngInfoUserSet = true; + mPngInfo = info; +} + void PngWriter::writeSignature() { - mOutStream->writeByte(Png::getHighBitCheck()); - mOutStream->writeBytes(StringUtils::toBytes(Png::getName())); - mOutStream->writeBytes(Png::getSignature()); + mOutStream->writeByte(mPngHeader.getHighBitCheck()); + mOutStream->writeBytes(StringUtils::toBytes(mPngHeader.getFileName())); + mOutStream->writeBytes(mPngHeader.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; + if (!mPngInfoUserSet) + { + if (mWorkingImage->getNumChannels() == 1) + { + mPngInfo.mColorType = PngInfo::ColorType::GREYSCALE; + } + else if (mWorkingImage->getNumChannels() == 2) + { + mPngInfo.mColorType = PngInfo::ColorType::GREYSCALE_ALPHA; + } + else if (mWorkingImage->getNumChannels() == 3) + { + mPngInfo.mColorType = PngInfo::ColorType::RGB; + } + else if (mWorkingImage->getNumChannels() == 4) + { + mPngInfo.mColorType = PngInfo::ColorType::RGB_ALPHA; + } + } - auto length = header_chunk.getLength(); + mPngHeader.setPngInfo(mPngInfo); + mPngHeader.setImageData(mWorkingImage->getWidth(), mWorkingImage->getHeight(), mWorkingImage->getBitDepth()); + + auto length = mPngHeader.getLength(); mOutStream->write(length); - mOutStream->writeBytes(StringUtils::toBytes(header_chunk.name)); + mOutStream->writeBytes(StringUtils::toBytes(mPngHeader.getChunkName())); - header_chunk.updateData(); - mOutStream->writeBytes(header_chunk.mData); + mPngHeader.updateData(); + mOutStream->writeBytes(mPngHeader.getData()); - auto crc = header_chunk.getCrc(); + auto crc = mPngHeader.getCrc(); mOutStream->write(crc); } diff --git a/src/image/PngWriter.h b/src/image/png/PngWriter.h similarity index 84% rename from src/image/PngWriter.h rename to src/image/png/PngWriter.h index d4d1d4d..35f5856 100644 --- a/src/image/PngWriter.h +++ b/src/image/png/PngWriter.h @@ -1,5 +1,6 @@ #pragma once +#include "PngHeader.h" #include "Image.h" #include @@ -22,6 +23,8 @@ public: void setPath(const Path& path); + void setPngInfo(const PngInfo& info); + void write(const std::unique_ptr >& image); private: @@ -39,6 +42,10 @@ private: std::unique_ptr mInStream; std::unique_ptr mOutStream; std::unique_ptr mWorkingFile; + + unsigned mPngInfoUserSet{false}; + PngInfo mPngInfo; + PngHeader mPngHeader; }; using PngWriterPtr = std::unique_ptr; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9b44e1f..f0843d7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,6 +14,7 @@ list(APPEND TestFiles core/TestBinaryStream.cpp core/TestBitStream.cpp core/TestTomlReader.cpp + core/TestDataStructures.cpp compiler/TestLexer.cpp compiler/TestTemplatingEngine.cpp compression/TestStreamCompressor.cpp diff --git a/test/core/TestDataStructures.cpp b/test/core/TestDataStructures.cpp new file mode 100644 index 0000000..c297475 --- /dev/null +++ b/test/core/TestDataStructures.cpp @@ -0,0 +1,34 @@ +#include "CircleBuffer.h" + +#include + +int main() +{ + CircleBuffer buffer(3); + + for (auto item : {1, 2, 3}) + { + std::cout << "Add item: " << item << std::endl; + buffer.addItem(item); + } + + for (std::size_t idx=0; idx<3; idx++) + { + auto item = buffer.getItem(idx); + std::cout << "Got item: " << idx << " " << item << std::endl; + } + + for (auto item : {4, 5}) + { + std::cout << "Add item: " << item << std::endl; + buffer.addItem(item); + } + + for (std::size_t idx=0; idx<3; idx++) + { + auto item = buffer.getItem(idx); + std::cout << "Got item: " << idx << " " << item << std::endl; + } + + return 0; +} diff --git a/test/image/TestPngWriter.cpp b/test/image/TestPngWriter.cpp index b6df3c5..e3db61a 100644 --- a/test/image/TestPngWriter.cpp +++ b/test/image/TestPngWriter.cpp @@ -9,15 +9,15 @@ int main() { - unsigned width = 200; - unsigned height = 200; + unsigned width = 20; + unsigned height = 20; unsigned numChannels = 3; auto image = Image::Create(width, height); image->setNumChannels(numChannels); std::vector data(image->getBytesPerRow()*height, 0); - ImagePrimitives::drawAlternatingStrips(data, width,height, numChannels, image->getBytesPerRow()); + ImagePrimitives::drawAlternatingStrips(data, width, height, numChannels, image->getBytesPerRow()); image->setData(data); PngWriter writer;