Continue png writing.

This commit is contained in:
James Grogan 2022-11-24 16:15:41 +00:00
parent 5400a232dd
commit 8f97e9b7a1
29 changed files with 714 additions and 302 deletions

View file

@ -1,5 +1,9 @@
#pragma once #pragma once
#include "AbstractChecksumCalculator.h"
#include <vector>
class BitStream; class BitStream;
class AbstractEncoder class AbstractEncoder
@ -14,10 +18,17 @@ public:
virtual ~AbstractEncoder() = default; virtual ~AbstractEncoder() = default;
void addChecksumCalculator(AbstractChecksumCalculator* calculator)
{
mChecksumCalculators.push_back(calculator);
}
virtual bool encode() = 0; virtual bool encode() = 0;
virtual bool decode() = 0; virtual bool decode() = 0;
protected: protected:
std::vector<AbstractChecksumCalculator*> mChecksumCalculators;
BitStream* mInputStream{nullptr}; BitStream* mInputStream{nullptr};
BitStream* mOutputStream{nullptr}; BitStream* mOutputStream{nullptr};
}; };

View file

@ -0,0 +1,29 @@
#pragma once
#include "AbstractChecksumCalculator.h"
class Adler32Checksum : public AbstractChecksumCalculator
{
public:
void addValue(unsigned char val) override
{
mSum1 = (mSum1 + val) % MOD_ADLER32;
mSum2 = (mSum2 + mSum1) % MOD_ADLER32;
}
uint32_t getChecksum() const override
{
return (mSum2 << 16) | mSum1;
}
void reset() override
{
mSum1 = 1;
mSum2 = 0;
}
private:
static constexpr unsigned MOD_ADLER32{65536};
uint32_t mSum1{1};
uint32_t mSum2{0};
};

View file

@ -3,10 +3,9 @@ list(APPEND compression_LIB_INCLUDES
StreamCompressor.cpp StreamCompressor.cpp
HuffmanEncoder.cpp HuffmanEncoder.cpp
RunLengthEncoder.cpp RunLengthEncoder.cpp
ZlibData.cpp
ZlibEncoder.cpp ZlibEncoder.cpp
DeflateEncoder.cpp deflate/DeflateEncoder.cpp
DeflateBlock.cpp deflate/DeflateBlock.cpp
Lz77Encoder.cpp Lz77Encoder.cpp
CyclicRedundancyChecker.cpp CyclicRedundancyChecker.cpp
) )
@ -14,7 +13,8 @@ list(APPEND compression_LIB_INCLUDES
add_library(compression SHARED ${compression_LIB_INCLUDES}) add_library(compression SHARED ${compression_LIB_INCLUDES})
target_include_directories(compression PUBLIC target_include_directories(compression PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}" ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/deflate
) )
target_link_libraries(compression PUBLIC core) target_link_libraries(compression PUBLIC core)

View file

@ -1,38 +0,0 @@
#include "DeflateEncoder.h"
#include "BitStream.h"
#include "DeflateBlock.h"
#include <iostream>
DeflateEncoder::DeflateEncoder(BitStream* inputStream, BitStream* outputStream)
: AbstractEncoder(inputStream, outputStream)
{
}
DeflateEncoder::~DeflateEncoder()
{
}
bool DeflateEncoder::encode()
{
return false;
}
bool DeflateEncoder::decode()
{
auto working_block = std::make_unique<DeflateBlock>(mInputStream, mOutputStream);
working_block->readHeader();
DeflateBlock* raw_block = working_block.get();
while(!raw_block->isFinalBlock())
{
break;
}
return false;
}

View file

@ -1,107 +0,0 @@
#pragma once
#include "ByteUtils.h"
#include "BitStream.h"
#include <vector>
#include <iostream>
class ZlibData
{
public:
void setByte(unsigned idx, unsigned char data)
{
mBitStream.setByte(idx, data);
}
void setDataSize(std::size_t size)
{
mBitStream.setBufferSize(size);
}
void setCompressionMethod(unsigned char method)
{
std::cout << "Got compression input " << static_cast<int>(method) << std::endl;
mCmf = method;
mCompressionMethod = ByteUtils::getLowerNBits(method, 4);
mCompressionInfo = ByteUtils::getHigherNBits(method, 4);
std::cout << "Got compression method " << static_cast<int>(mCompressionMethod) << " and info " << static_cast<int>(mCompressionInfo) << std::endl;
}
void setExtraFlags(unsigned char extraFlags)
{
std::cout << "Got flags " << static_cast<int>(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<int>(mFlagCheck) << " and dict " << static_cast<int>(mFlagDict) << " and level " << static_cast<int>(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;
while(mBitStream.loadNextByte())
{
auto working_byte = mBitStream.getCurrentByte();
std::cout << "Into process data, byte is: " << static_cast<unsigned>(working_byte) << std::endl;
unsigned char final_block{0};
mBitStream.getNextNBits(1, final_block);
if (final_block)
{
std::cout << "Got final block" << std::endl;
in_final_block = true;
}
unsigned char compress_type{0};
mBitStream.getNextNBits(2, compress_type);
std::cout << "Compress type byte is: " << static_cast<unsigned>(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;
unsigned char h_list{0};
mBitStream.getNextNBits(5, h_list);
mHlist = h_list + 257;
std::cout << "Got HLIST " << mHlist << std::endl;
}
else if (compress_type == ERROR)
{
std::cout << "Got ERROR" << std::endl;
}
break;
}
}
private:
BitStream mBitStream;
unsigned mHlist{0};
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};
};

View file

@ -3,13 +3,18 @@
#include "ByteUtils.h" #include "ByteUtils.h"
#include "DeflateEncoder.h" #include "DeflateEncoder.h"
#include "FileLogger.h" #include "FileLogger.h"
#include "BitStream.h"
#include "Adler32Checksum.h"
#include <math.h>
#include <iostream> #include <iostream>
#include <sstream>
ZlibEncoder::ZlibEncoder(BitStream* inputStream, BitStream* outputStream) ZlibEncoder::ZlibEncoder(BitStream* inputStream, BitStream* outputStream)
: AbstractEncoder(inputStream, outputStream) : AbstractEncoder(inputStream, outputStream)
{ {
mChecksumCalculator = std::make_unique<Adler32Checksum>();
} }
ZlibEncoder::~ZlibEncoder() ZlibEncoder::~ZlibEncoder()
@ -17,58 +22,132 @@ ZlibEncoder::~ZlibEncoder()
} }
void ZlibEncoder::setCompressionMethod(unsigned char method) void ZlibEncoder::setWindowSize(unsigned size)
{ {
std::cout << "Got compression input " << static_cast<int>(method) << std::endl; mWindowSize = size;
mCmf = method;
mCompressionMethod = ByteUtils::getLowerNBits(method, 4);
mCompressionInfo = ByteUtils::getHigherNBits(method, 4);
std::cout << "Got compression method " << static_cast<int>(mCompressionMethod) << " and info " << static_cast<int>(mCompressionInfo) << std::endl;
} }
void ZlibEncoder::setExtraFlags(unsigned char extraFlags) std::string ZlibEncoder::toString(CompressionLevel level) const
{
switch(level)
{
case CompressionLevel::FASTEST:
return "FASTEST";
case CompressionLevel::FAST:
return "FAST";
case CompressionLevel::DEFAULT:
return "DEFAULT";
case CompressionLevel::MAX_COMPRESSION:
return "MAX_COMPRESSION";
default:
return "UNKNOWN";
}
}
std::string ZlibEncoder::toString(CompressionMethod method) const
{
return method == CompressionMethod::DEFLATE ? "DEFLATE" : "UNKNOWN";
}
void ZlibEncoder::parseCompressionMethod(unsigned char method)
{
std::cout << "Got compression input " << static_cast<int>(method) << std::endl;
mCompressionMethod = static_cast<CompressionMethod>(ByteUtils::getLowerNBits(method, 4));
auto compression_info = ByteUtils::getHigherNBits(method, 4);
if (mCompressionMethod == CompressionMethod::DEFLATE)
{
mWindowSize = pow(2, compression_info + 8);
}
}
void ZlibEncoder::parseExtraFlags(unsigned char extraFlags)
{ {
std::cout << "Got flags " << static_cast<int>(extraFlags) << std::endl; std::cout << "Got flags " << static_cast<int>(extraFlags) << std::endl;
mFlg = extraFlags;
mFlagCheck = ByteUtils::getLowerNBits(extraFlags, 5); mFlagCheck = ByteUtils::getLowerNBits(extraFlags, 5);
mFlagDict = ByteUtils::getBitN(extraFlags, 5); mUseDictionary = bool(ByteUtils::getBitN(extraFlags, 5));
mFlagLevel = ByteUtils::getHigherNBits(extraFlags, 2); mFlagLevel = static_cast<CompressionLevel>(ByteUtils::getHigherNBits(extraFlags, 2));
}
std::string ZlibEncoder::getData() const
{
std::stringstream sstream;
sstream << "ZlibEncoder data \n";
sstream << "Compression method: " << toString(mCompressionMethod) << '\n';
sstream << "Window size: " << mWindowSize << '\n';
sstream << "Flag check: " << mFlagCheck << '\n';
sstream << "Use dictionary: " << mUseDictionary << '\n';
sstream << "Flag level: " << toString(mFlagLevel) << '\n';
return sstream.str();
std::cout << "Got flag check " << static_cast<int>(mFlagCheck) << " and dict " << static_cast<int>(mFlagDict) << " and level " << static_cast<int>(mFlagLevel) << std::endl;
} }
bool ZlibEncoder::encode() bool ZlibEncoder::encode()
{ {
DeflateEncoder* deflate_encoder{nullptr};
if (!mWorkingEncoder) if (!mWorkingEncoder)
{ {
if (mCompressionMethod == 8) if (mCompressionMethod == CompressionMethod::DEFLATE)
{ {
mWorkingEncoder = std::make_unique<DeflateEncoder>(mInputStream, mOutputStream); auto uq_deflate_encoder = std::make_unique<DeflateEncoder>(mInputStream, mOutputStream);
deflate_encoder = uq_deflate_encoder.get();
mWorkingEncoder = std::move(uq_deflate_encoder);
mWorkingEncoder->addChecksumCalculator(mChecksumCalculator.get());
} }
else else
{ {
MLOG_ERROR("Zib requested decoder not recognized: " << mCompressionMethod << " aborting encode"); MLOG_ERROR("Zib requested decoder not recognized: " << static_cast<int>(mCompressionMethod) << " aborting encode");
return false; return false;
} }
} }
return mWorkingEncoder->encode(); deflate_encoder->setCompressionMethod(mDeflateCompressionMethod);
auto compression_info = static_cast<unsigned char>(log2(mWindowSize) - 8);
const unsigned char compression_byte = (compression_info << 4) | static_cast<unsigned char>(mCompressionMethod);
std::cout << "ZlibEncoder Writing compression byte " << static_cast<int>(compression_byte) << " with info " << static_cast<int>(compression_info) << std::endl;
mOutputStream->writeByte(compression_byte);
unsigned char flag_byte{0};
flag_byte |= (static_cast<unsigned char>(mUseDictionary) << 5);
flag_byte |= (static_cast<unsigned char>(mFlagLevel) << 6);
const auto mod = (unsigned(compression_byte)*256 + flag_byte) % 31;
flag_byte += (31 - mod);
std::cout << "ZlibEncoder Writing Flag byte " << static_cast<int>(flag_byte) << std::endl;
mOutputStream->writeByte(flag_byte);
if(!mWorkingEncoder->encode())
{
MLOG_ERROR("Sub-Encoder failed - aborting zlib encode");
//return false;
}
const auto checksum = mChecksumCalculator->getChecksum();
std::cout << "ZlibEncoder Writing Checksum " << checksum << std::endl;
mOutputStream->write(checksum);
return true;
} }
bool ZlibEncoder::decode() bool ZlibEncoder::decode()
{ {
parseCompressionMethod(*mInputStream->readNextByte());
parseExtraFlags(*mInputStream->readNextByte());
if (!mWorkingEncoder) if (!mWorkingEncoder)
{ {
if (mCompressionMethod == 8) if (mCompressionMethod == CompressionMethod::DEFLATE)
{ {
mWorkingEncoder = std::make_unique<DeflateEncoder>(mInputStream, mOutputStream); mWorkingEncoder = std::make_unique<DeflateEncoder>(mInputStream, mOutputStream);
} }
else else
{ {
MLOG_ERROR("Zib requested decoder not recognized: " << mCompressionMethod << " aborting decode"); MLOG_ERROR("Zib requested decoder not recognized: " << static_cast<int>(mCompressionMethod) << " aborting decode");
return false; return false;
} }
} }
return mWorkingEncoder->decode(); return mWorkingEncoder->decode();
} }

View file

@ -2,33 +2,54 @@
#include "AbstractEncoder.h" #include "AbstractEncoder.h"
#include "DeflateElements.h"
#include <memory> #include <memory>
#include <vector> #include <vector>
class AbstractChecksumCalculator;
class ZlibEncoder : public AbstractEncoder class ZlibEncoder : public AbstractEncoder
{ {
public: public:
enum class CompressionMethod : unsigned char
{
DEFLATE = 8,
};
enum class CompressionLevel : unsigned char
{
FASTEST,
FAST,
DEFAULT,
MAX_COMPRESSION
};
ZlibEncoder(BitStream* inputStream, BitStream* outputStream); ZlibEncoder(BitStream* inputStream, BitStream* outputStream);
~ZlibEncoder(); ~ZlibEncoder();
void setCompressionMethod(unsigned char method); void setWindowSize(unsigned size);
void setExtraFlags(unsigned char extraFlags);
bool encode() override; bool encode() override;
bool decode() override; bool decode() override;
private: std::string getData() const;
unsigned char mCmf{0}; std::string toString(CompressionLevel level) const;
unsigned char mFlg{0}; std::string toString(CompressionMethod method) const;
unsigned char mCompressionMethod{8};
unsigned char mCompressionInfo{0};
unsigned char mFlagCheck{0};
unsigned char mFlagDict{0};
unsigned char mFlagLevel{0};
unsigned char mCheckValue{0};
private:
void parseCompressionMethod(unsigned char method);
void parseExtraFlags(unsigned char extraFlags);
CompressionMethod mCompressionMethod{CompressionMethod::DEFLATE};
Deflate::CompressionMethod mDeflateCompressionMethod{Deflate::CompressionMethod::NONE};
unsigned mWindowSize{32768}; // Window size, n in 2^(n+8) bytes
unsigned char mFlagCheck{0};
bool mUseDictionary{false};
CompressionLevel mFlagLevel{CompressionLevel::DEFAULT};
std::unique_ptr<AbstractChecksumCalculator> mChecksumCalculator;
std::unique_ptr<AbstractEncoder> mWorkingEncoder; std::unique_ptr<AbstractEncoder> mWorkingEncoder;
}; };

View file

@ -1,9 +1,11 @@
#include "DeflateBlock.h" #include "DeflateBlock.h"
#include "ByteUtils.h" #include "ByteUtils.h"
#include "AbstractChecksumCalculator.h"
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include <sstream>
DeflateBlock::DeflateBlock(BitStream* inputStream, BitStream* outputStream) DeflateBlock::DeflateBlock(BitStream* inputStream, BitStream* outputStream)
: mInputStream(inputStream), : mInputStream(inputStream),
@ -88,7 +90,7 @@ void DeflateBlock::setDistanceTableLength(unsigned length)
void DeflateBlock::setIsFinalBlock(bool isFinal) void DeflateBlock::setIsFinalBlock(bool isFinal)
{ {
mInFinalBlock = isFinal;
} }
void DeflateBlock::flushToStream() void DeflateBlock::flushToStream()
@ -224,6 +226,22 @@ void DeflateBlock::readDynamicHuffmanTable()
readLiteralCodeLengths(); readLiteralCodeLengths();
} }
std::string DeflateBlock::getMetaData() const
{
std::stringstream sstr;
sstr << "DeflateBlock Metadata \n";
sstr << "Final block: " << mInFinalBlock << '\n';
sstr << "Compression method: " << Deflate::toString(mCompressionMethod) << '\n';
return sstr.str();
}
bool DeflateBlock::isFinalBlock() const
{
return mInFinalBlock;
}
void DeflateBlock::readHeader() void DeflateBlock::readHeader()
{ {
auto working_byte = mInputStream->getCurrentByte(); auto working_byte = mInputStream->getCurrentByte();
@ -234,29 +252,33 @@ void DeflateBlock::readHeader()
mInputStream->readNextNBits(1, final_block); mInputStream->readNextNBits(1, final_block);
mInFinalBlock = bool(final_block); mInFinalBlock = bool(final_block);
if (mInFinalBlock) unsigned char compression_type{0};
{ mInputStream->readNextNBits(2, compression_type);
std::cout << "Got final block" << std::endl; mCompressionMethod = static_cast<Deflate::CompressionMethod>(compression_type);
} }
mInputStream->readNextNBits(2, mCompressionType); void DeflateBlock::write(uint16_t datalength)
std::cout << "Compress type byte is: " << static_cast<unsigned>(mCompressionType) << std::endl; {
if (mCompressionType == NO_COMPRESSION) unsigned char working_block{0};
{ working_block |= static_cast<unsigned char>(mInFinalBlock);
std::cout << "Got NO_COMPRESSION" << std::endl; working_block |= static_cast<unsigned char>(mCompressionMethod) << 1;
}
else if (mCompressionType == FIXED_HUFFMAN) if (mCompressionMethod == Deflate::CompressionMethod::NONE)
{ {
std::cout << "Got FIXED_HUFFMAN" << std::endl; std::cout << "Writing compression block header " << static_cast<int>(working_block) << std::endl;
} mOutputStream->writeByte(working_block);
else if (mCompressionType == DYNAMIC_HUFFMAN)
{ std::cout << "Writing data length " << datalength << " " << ByteUtils::toString(datalength) << std::endl;
std::cout << "Got DYNAMIC_HUFFMAN" << std::endl; mOutputStream->writeWord(datalength);
readDynamicHuffmanTable();
} std::cout << "Writing iverse data length " << ~datalength << " " << ByteUtils::toString(~datalength) << std::endl;
else if (mCompressionType == ERROR) mOutputStream->writeWord(static_cast<uint16_t>(~datalength));
{
std::cout << "Got ERROR" << std::endl; for(unsigned idx=0; idx<datalength;idx++)
} {
auto byte = *mInputStream->readNextByte();
std::cout << "Writing next byte " << static_cast<int>(byte) << std::endl;
mOutputStream->writeByte(byte);
}
}
} }

View file

@ -1,27 +1,32 @@
#pragma once #pragma once
#include "DeflateElements.h"
#include "BitStream.h" #include "BitStream.h"
class AbstractChecksumCalculator;
class DeflateBlock class DeflateBlock
{ {
public: public:
DeflateBlock(BitStream* inputStream, BitStream* outputStream); DeflateBlock(BitStream* inputStream, BitStream* outputStream);
void buildCodeLengthMapping();
std::string getMetaData() const;
void flushToStream();
bool isFinalBlock() const;
void readHeader(); void readHeader();
void readDynamicHuffmanTable(); void readDynamicHuffmanTable();
void buildCodeLengthMapping();
void readLiteralCodeLengths(); void readLiteralCodeLengths();
bool readNextCodeLengthSymbol(unsigned char& buffer); bool readNextCodeLengthSymbol(unsigned char& buffer);
bool isFinalBlock() const
{
return mInFinalBlock;
}
void setCodeLengthAlphabetLengths(const std::vector<unsigned char>& lengths); void setCodeLengthAlphabetLengths(const std::vector<unsigned char>& lengths);
void setCodeLengthLength(unsigned length); void setCodeLengthLength(unsigned length);
@ -32,7 +37,7 @@ public:
void setIsFinalBlock(bool isFinal); void setIsFinalBlock(bool isFinal);
void flushToStream(); void write(uint16_t datalength);
private: private:
BitStream* mInputStream; BitStream* mInputStream;
@ -49,12 +54,6 @@ private:
std::vector<unsigned char> mCodeLengthAlphabetLengths; std::vector<unsigned char> mCodeLengthAlphabetLengths;
static constexpr unsigned CODE_LENGTH_ALPHABET_PERMUTATION[19]{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; static constexpr unsigned CODE_LENGTH_ALPHABET_PERMUTATION[19]{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
unsigned char mCompressionType{0};
bool mInFinalBlock{false}; bool mInFinalBlock{false};
Deflate::CompressionMethod mCompressionMethod{Deflate::CompressionMethod::NONE};
static constexpr unsigned char NO_COMPRESSION = 0x00;
static constexpr unsigned char FIXED_HUFFMAN = 0x01;
static constexpr unsigned char DYNAMIC_HUFFMAN = 0x02;
static constexpr unsigned char ERROR = 0x03;
}; };

View file

@ -0,0 +1,33 @@
#pragma once
#include <string>
namespace Deflate
{
enum class CompressionMethod
{
NONE,
FIXED_HUFFMAN,
DYNAMIC_HUFFMAN,
ERROR
};
inline std::string toString(CompressionMethod method)
{
switch (method)
{
case CompressionMethod::NONE:
return "NONE";
case CompressionMethod::FIXED_HUFFMAN:
return "FIXED_HUFFMAN";
case CompressionMethod::DYNAMIC_HUFFMAN:
return "DYNAMIC_HUFFMAN";
case CompressionMethod::ERROR:
return "ERROR";
default:
return "UNKNOWN";
}
}
}

View file

@ -0,0 +1,78 @@
#include "DeflateEncoder.h"
#include "BitStream.h"
#include "DeflateBlock.h"
#include "BufferBitStream.h"
#include <iostream>
DeflateEncoder::DeflateEncoder(BitStream* inputStream, BitStream* outputStream)
: AbstractEncoder(inputStream, outputStream)
{
}
DeflateEncoder::~DeflateEncoder()
{
}
bool DeflateEncoder::encode()
{
uint16_t count = 0;
BufferBitStream stream;
std::unique_ptr<DeflateBlock> working_block = std::make_unique<DeflateBlock>(&stream, mOutputStream);
AbstractChecksumCalculator* checksum_calc;
if (mChecksumCalculators.size() > 0)
{
std::cout << "Setting checksum calculator " << std::endl;
mOutputStream->setChecksumCalculator(mChecksumCalculators[0]);
}
while(true)
{
if (count == mMaxBlockSize)
{
std::cout << working_block->getMetaData();
working_block->write(count);
working_block = std::make_unique<DeflateBlock>(&stream, mOutputStream);
stream.reset();
}
if (auto byte = mInputStream->readNextByte())
{
stream.writeByte(*byte);
}
else
{
stream.resetOffsets();
working_block->setIsFinalBlock(true);
std::cout << working_block->getMetaData();
working_block->write(count);
break;
}
count++;
}
mOutputStream->clearChecksumCalculator();
return true;
}
bool DeflateEncoder::decode()
{
auto working_block = std::make_unique<DeflateBlock>(mInputStream, mOutputStream);
working_block->readHeader();
DeflateBlock* raw_block = working_block.get();
while(!raw_block->isFinalBlock())
{
break;
}
return false;
}

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "AbstractEncoder.h" #include "AbstractEncoder.h"
#include "DeflateElements.h"
#include <vector> #include <vector>
#include <memory> #include <memory>
@ -18,8 +19,15 @@ public:
bool decode() override; bool decode() override;
void setCompressionMethod(Deflate::CompressionMethod method)
{
mCompressionMethod = method;
}
private: private:
std::vector<std::unique_ptr<DeflateBlock > > mBlocks; uint16_t mMaxBlockSize{65535};
Deflate::CompressionMethod mCompressionMethod{Deflate::CompressionMethod::NONE};
std::unique_ptr<DeflateBlock > mLastBlock;
}; };

View file

@ -0,0 +1,16 @@
#pragma once
#include "stdint.h"
class AbstractChecksumCalculator
{
public:
virtual ~AbstractChecksumCalculator() = default;
virtual void addValue(unsigned char val) = 0;
virtual uint32_t getChecksum() const = 0;
virtual void reset()
{
}
};

View file

@ -28,6 +28,15 @@ void BitStream::write(uint32_t data)
} }
} }
void BitStream::writeWord(uint16_t data)
{
unsigned num_bytes = sizeof(uint16_t);
for(unsigned idx=0; idx<num_bytes;idx++)
{
writeByte(ByteUtils::getByteN(data, idx));
}
}
int BitStream::getCurrentByteOffset() const int BitStream::getCurrentByteOffset() const
{ {
return mByteOffset; return mByteOffset;

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "AbstractChecksumCalculator.h"
#include <vector> #include <vector>
#include <string> #include <string>
#include <optional> #include <optional>
@ -29,11 +31,39 @@ public:
void write(uint32_t data); void write(uint32_t data);
void writeWord(uint16_t data);
virtual void writeBytes(const std::vector<unsigned char> data) = 0; virtual void writeBytes(const std::vector<unsigned char> data) = 0;
void resetOffsets()
{
mByteOffset = -1;
mBitOffset = 0;
}
virtual void reset()
{
resetOffsets();
mCurrentByte = 0;
}
void setChecksumCalculator(AbstractChecksumCalculator* calc)
{
mChecksumCalculator = calc;
}
void clearChecksumCalculator()
{
mChecksumCalculator = nullptr;
}
//unsigned getSize() = 0;
protected: protected:
int mByteOffset{0}; int mByteOffset{-1};
unsigned mBitOffset{0}; unsigned mBitOffset{0};
unsigned char mCurrentByte{0}; unsigned char mCurrentByte{0};
AbstractChecksumCalculator* mChecksumCalculator{nullptr};
}; };

View file

@ -1,8 +1,10 @@
#include "BufferBitStream.h" #include "BufferBitStream.h"
#include <iostream>
bool BufferBitStream::isFinished() const bool BufferBitStream::isFinished() const
{ {
return mByteOffset == mBuffer.size(); return mByteOffset == mBuffer.size() - 1;
} }
std::vector<unsigned char> BufferBitStream::peekNextNBytes(unsigned n) const std::vector<unsigned char> BufferBitStream::peekNextNBytes(unsigned n) const
@ -43,6 +45,10 @@ void BufferBitStream::setBuffer(const std::vector<unsigned char>& data)
void BufferBitStream::writeByte(unsigned char data) void BufferBitStream::writeByte(unsigned char data)
{ {
if (mChecksumCalculator)
{
mChecksumCalculator->addValue(data);
}
mBuffer.push_back(data); mBuffer.push_back(data);
} }

View file

@ -27,12 +27,13 @@ public:
return mBuffer; return mBuffer;
} }
void resetOffsets() void reset() override
{ {
mByteOffset = 0; BitStream::reset();
mBitOffset = 0; mBuffer.clear();
} }
private: private:
unsigned mBufferSize{0};
std::vector<unsigned char> mBuffer; std::vector<unsigned char> mBuffer;
}; };

View file

@ -8,6 +8,7 @@ list(APPEND image_LIB_INCLUDES
png/PngWriter.cpp png/PngWriter.cpp
png/PngReader.cpp png/PngReader.cpp
png/PngHeader.cpp png/PngHeader.cpp
png/PngInfo.cpp
ImageBitStream.cpp ImageBitStream.cpp
) )

View file

@ -9,7 +9,7 @@ ImageBitStream::ImageBitStream(Image<unsigned char>* image)
bool ImageBitStream::isFinished() const bool ImageBitStream::isFinished() const
{ {
return true; return mByteOffset == mImage->getDataRef().size();
} }
std::vector<unsigned char> ImageBitStream::peekNextNBytes(unsigned n) const std::vector<unsigned char> ImageBitStream::peekNextNBytes(unsigned n) const
@ -19,7 +19,14 @@ std::vector<unsigned char> ImageBitStream::peekNextNBytes(unsigned n) const
std::optional<unsigned char> ImageBitStream::readNextByte() std::optional<unsigned char> ImageBitStream::readNextByte()
{ {
return std::nullopt; mByteOffset++;
if (isFinished() )
{
return std::nullopt;
}
const auto val = mImage->getDataRef()[mByteOffset];
return val;
} }
void ImageBitStream::writeByte(unsigned char data) void ImageBitStream::writeByte(unsigned char data)

View file

@ -22,6 +22,11 @@ public:
} }
unsigned getBytesPerScanline() const
{
return mImage->getBytesPerRow();
}
private: private:
Image<unsigned char>* mImage{nullptr}; Image<unsigned char>* mImage{nullptr};
}; };

73
src/image/png/PngFilter.h Normal file
View file

@ -0,0 +1,73 @@
#pragma once
#include "ImageBitStream.h"
#include "BitStream.h"
#include "FileLogger.h"
#include <iostream>
class PngFilter
{
public:
enum class FilterType : unsigned char
{
NONE,
SUB,
UP,
AVERAGE,
PAETH
};
PngFilter(BitStream* inputStream, BitStream* outputStream)
: mInputStream(inputStream),
mOutputStream(outputStream)
{
}
void encode()
{
auto image_stream = dynamic_cast<ImageBitStream*>(mInputStream);
if (!image_stream)
{
MLOG_ERROR("Expected ImageStream in PngFilter encode - aborting.");
return;
}
const auto bytes_per_scanline = image_stream->getBytesPerScanline();
unsigned count{0};
if (mFilterType == FilterType::NONE)
{
while(true)
{
if (const auto byte = image_stream->readNextByte())
{
if (count % bytes_per_scanline == 0)
{
mOutputStream->writeByte(0);
}
mOutputStream->writeByte(*byte);
}
else
{
break;
}
count++;
}
}
}
void decode()
{
}
private:
FilterType mFilterType{FilterType::NONE};
BitStream* mInputStream{nullptr};
BitStream* mOutputStream{nullptr};
};

View file

@ -37,7 +37,7 @@ std::string PngHeader::getFileName() const
const std::string& PngHeader::getChunkName() const const std::string& PngHeader::getChunkName() const
{ {
return "IHDR"; return mName;
} }
const std::vector<unsigned char>& PngHeader::getData() const const std::vector<unsigned char>& PngHeader::getData() const
@ -61,9 +61,9 @@ void PngHeader::updateData()
} }
mData.push_back(mBitDepth); mData.push_back(mBitDepth);
mData.push_back(static_cast<unsigned char>(mPngInfo.mColorType)); mData.push_back(static_cast<unsigned char>(mPngInfo.mColorType));
mData.push_back(mPngInfo.mCompressionMethod); mData.push_back(static_cast<unsigned char>(mPngInfo.mCompressionMethod));
mData.push_back(mPngInfo.mFilterMethod); mData.push_back(static_cast<unsigned char>(mPngInfo.mFilterMethod));
mData.push_back(mPngInfo.mInterlaceMethod); mData.push_back(static_cast<unsigned char>(mPngInfo.mInterlaceMethod));
} }
uint32_t PngHeader::getCrc() const uint32_t PngHeader::getCrc() const

95
src/image/png/PngInfo.cpp Normal file
View file

@ -0,0 +1,95 @@
#include "PngInfo.h"
std::string PngInfo::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 PngInfo::toString(CompressionMethod method) const
{
switch(method)
{
case CompressionMethod::DEFLATE:
return "DEFLATE";
default:
return "UNKNOWN";
}
}
std::string PngInfo::toString(FilterMethod method) const
{
switch(method)
{
case FilterMethod::ADAPTIVE:
return "ADAPTIVE";
default:
return "UNKNOWN";
}
}
std::string PngInfo::toString(InterlaceMethod method) const
{
switch(method)
{
case InterlaceMethod::NONE:
return "NONE";
case InterlaceMethod::ADAM7:
return "ADAM7";
default:
return "UNKNOWN";
}
}
bool PngInfo::bitDepthIsValid(ColorType colorType, unsigned char bitDepth) const
{
switch(colorType)
{
case ColorType::GREYSCALE:
return (bitDepth == 1) || (bitDepth == 2) || (bitDepth == 4) || (bitDepth == 8) || (bitDepth == 16) ;
case ColorType::RGB:
return (bitDepth == 8) || (bitDepth == 16);
case ColorType::PALETTE:
return (bitDepth == 1) || (bitDepth == 2) || (bitDepth == 4) || (bitDepth == 8);
case ColorType::GREYSCALE_ALPHA:
return (bitDepth == 8) || (bitDepth == 16);
case ColorType::RGB_ALPHA:
return (bitDepth == 8) || (bitDepth == 16);
default:
return false;
}
}
bool PngInfo::compressionMethodIsValid(unsigned char method)
{
return method == 0;
}
bool PngInfo::filterMethodIsValid(unsigned char method)
{
return method == 0;
}
std::string PngInfo::toString() const
{
std::stringstream sstr;
sstr << "PngInfo" << "\n";
sstr << "colorType: " << toString(mColorType) << "\n";
sstr << "compressionMethod: " << toString(mCompressionMethod) << "\n";
sstr << "filterMethod: " << toString(mFilterMethod) << "\n";
sstr << "interlaceMethod: " << toString(mInterlaceMethod) << "\n";
return sstr.str();
}

View file

@ -12,43 +12,45 @@ public:
RGB = 2, RGB = 2,
PALETTE = 3, PALETTE = 3,
GREYSCALE_ALPHA = 4, GREYSCALE_ALPHA = 4,
RGB_ALPHA = 6, RGB_ALPHA = 6
}; };
std::string toString(ColorType colorType) const enum class CompressionMethod : unsigned char
{ {
switch(colorType) DEFLATE = 0
{ };
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 enum class FilterMethod : unsigned char
{ {
std::stringstream sstr; ADAPTIVE = 0
sstr << "PngInfo" << "\n"; };
sstr << "colorType: " << toString(mColorType) << "\n";
sstr << "compressionMethod: " << (int)mCompressionMethod << "\n"; enum class InterlaceMethod : unsigned char
sstr << "filterMethod: " << (int)mFilterMethod << "\n"; {
sstr << "interlaceMethod: " << (int)mInterlaceMethod << "\n"; NONE = 0,
return sstr.str(); ADAM7 = 1
} };
std::string toString(ColorType colorType) const;
std::string toString(CompressionMethod method) const;
std::string toString(FilterMethod method) const;
std::string toString(InterlaceMethod method) const;
bool bitDepthIsValid(ColorType colorType, unsigned char bitDepth) const;
bool compressionMethodIsValid(unsigned char method);
bool filterMethodIsValid(unsigned char method);
std::string toString() const;
ColorType mColorType{ColorType::RGB}; ColorType mColorType{ColorType::RGB};
unsigned char mCompressionMethod{0}; CompressionMethod mCompressionMethod{CompressionMethod::DEFLATE};
unsigned char mFilterMethod{0}; FilterMethod mFilterMethod{FilterMethod::ADAPTIVE};
unsigned char mInterlaceMethod{0}; InterlaceMethod mInterlaceMethod{InterlaceMethod::NONE};
}; };

View file

@ -86,22 +86,9 @@ bool PngReader::readChunk()
void PngReader::readIDATChunk(unsigned length) void PngReader::readIDATChunk(unsigned length)
{ {
if (mAwaitingDataBlock) for(unsigned idx=0; idx<length; idx++)
{ {
mEncoder->setCompressionMethod(*mFile->readNextByte()); mInputStream->writeByte(*mFile->readNextByte());
mEncoder->setExtraFlags(*mFile->readNextByte());
for(unsigned idx=0; idx<length-2; idx++)
{
mInputStream->writeByte(*mFile->readNextByte());
}
mAwaitingDataBlock = false;
}
else
{
for(unsigned idx=0; idx<length; idx++)
{
mInputStream->writeByte(*mFile->readNextByte());
}
} }
} }
@ -114,11 +101,10 @@ void PngReader::readHeaderChunk()
mHeader.setImageData(width, height, bitDepth); mHeader.setImageData(width, height, bitDepth);
PngInfo info; PngInfo info;
info.mColorType = static_cast<PngInfo::ColorType>(mFile->GetInHandle()->get()); info.mColorType = static_cast<PngInfo::ColorType>(mFile->GetInHandle()->get());
info.mCompressionMethod = mFile->GetInHandle()->get(); info.mCompressionMethod = static_cast<PngInfo::CompressionMethod>(mFile->GetInHandle()->get());
info.mFilterMethod = mFile->GetInHandle()->get(); info.mFilterMethod = static_cast<PngInfo::FilterMethod>(mFile->GetInHandle()->get());
info.mInterlaceMethod = mFile->GetInHandle()->get(); info.mInterlaceMethod = static_cast<PngInfo::InterlaceMethod>(mFile->GetInHandle()->get());
mHeader.setPngInfo(info); mHeader.setPngInfo(info);
mCurrentOffset += 13; mCurrentOffset += 13;
@ -152,6 +138,8 @@ std::unique_ptr<Image<unsigned char> > PngReader::read()
{ {
} }
std::cout << mEncoder->getData() << std::endl;
mEncoder->decode(); mEncoder->decode();
return std::move(image); return std::move(image);
} }

View file

@ -6,13 +6,14 @@
#include "OutputBitStream.h" #include "OutputBitStream.h"
#include "ImageBitStream.h" #include "ImageBitStream.h"
#include "PngFilter.h"
#include "Lz77Encoder.h" #include "Lz77Encoder.h"
#include "ZlibEncoder.h" #include "ZlibEncoder.h"
#include "CyclicRedundancyChecker.h" #include "CyclicRedundancyChecker.h"
#include "ByteUtils.h" #include "ByteUtils.h"
#include <stdio.h> #include <iostream>
PngWriter::PngWriter() PngWriter::PngWriter()
{ {
@ -82,6 +83,8 @@ void PngWriter::writeHeader()
mPngHeader.updateData(); mPngHeader.updateData();
mOutStream->writeBytes(mPngHeader.getData()); mOutStream->writeBytes(mPngHeader.getData());
std::cout << "Writing header " << mPngHeader.toString() << std::endl;
auto crc = mPngHeader.getCrc(); auto crc = mPngHeader.getCrc();
mOutStream->write(crc); mOutStream->write(crc);
} }
@ -92,17 +95,24 @@ void PngWriter::writeEndChunk()
mOutStream->write(length); mOutStream->write(length);
mOutStream->writeBytes(StringUtils::toBytes("IEND")); mOutStream->writeBytes(StringUtils::toBytes("IEND"));
std::vector<unsigned char> char_data = StringUtils::toBytes("IEND");
CyclicRedundancyChecker crc_check; CyclicRedundancyChecker crc_check;
auto crc = crc_check.doCrc(nullptr, 0); auto crc = crc_check.doCrc(char_data.data(), char_data.size());
mOutStream->write(crc); mOutStream->write(crc);
std::cout << "Writing end chunk" << std::endl;
} }
void PngWriter::writeDataChunks(const BufferBitStream& buffer) void PngWriter::writeDataChunks(const BufferBitStream& buffer)
{ {
auto num_bytes = buffer.getBuffer().size(); auto num_bytes = buffer.getBuffer().size();
auto max_bytes{32000}; auto max_bytes{32000};
std::vector<unsigned char> crc_buffer(max_bytes, 0); std::vector<unsigned char> 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 num_dat_chunks = num_bytes/max_bytes + 1;
unsigned offset = 0; unsigned offset = 0;
@ -114,19 +124,22 @@ void PngWriter::writeDataChunks(const BufferBitStream& buffer)
length = num_bytes - num_dat_chunks*num_bytes; length = num_bytes - num_dat_chunks*num_bytes;
} }
mOutStream->write(length); std::cout << "Writing idat length " << num_bytes << std::endl;
mOutStream->write(num_bytes);
mOutStream->writeBytes(StringUtils::toBytes("IDAT")); mOutStream->writeBytes(StringUtils::toBytes("IDAT"));
for(unsigned jdx=0; jdx<length; jdx++) for(unsigned jdx=0; jdx<num_bytes; jdx++)
{ {
auto val = buffer.getBuffer()[idx*max_bytes + jdx]; auto val = buffer.getBuffer()[idx*max_bytes + jdx];
crc_buffer[jdx] = val; crc_buffer[jdx + 4] = val;
mOutStream->writeByte(val); mOutStream->writeByte(val);
} }
CyclicRedundancyChecker crc_check; CyclicRedundancyChecker crc_check;
auto crc = crc_check.doCrc(crc_buffer.data(), crc_buffer.size()); auto crc = crc_check.doCrc(crc_buffer.data(), crc_buffer.size());
std::cout << "Writing idat crc" << crc << std::endl;
mOutStream->write(crc); mOutStream->write(crc);
} }
} }
@ -146,18 +159,41 @@ void PngWriter::write(const std::unique_ptr<Image<unsigned char> >& image)
} }
mWorkingImage = image.get(); mWorkingImage = image.get();
mInStream = std::make_unique<ImageBitStream>(image.get());
auto image_bit_stream = std::make_unique<ImageBitStream>(image.get());
auto raw_image_stream = image_bit_stream.get();
mInStream = std::move(image_bit_stream);
writeHeader(); writeHeader();
BufferBitStream lz77_out_stream; auto filter_out_stream = std::make_unique<BufferBitStream>();
Lz77Encoder lz77_encoder(mInStream.get(), &lz77_out_stream); PngFilter filter(raw_image_stream, filter_out_stream.get());
filter.encode();
//while(!filter_out_stream->isFinished())
//{
//std::cout << "Got pix " << static_cast<int>(*filter_out_stream->readNextByte()) << std::endl;
//}
lz77_encoder.encode(); filter_out_stream->resetOffsets();
lz77_out_stream.resetOffsets();
std::unique_ptr<BufferBitStream> lz77_out_stream;
if (mCompressionMethod == Deflate::CompressionMethod::NONE)
{
lz77_out_stream = std::move(filter_out_stream);
}
else
{
lz77_out_stream = std::make_unique<BufferBitStream>();
Lz77Encoder lz77_encoder(filter_out_stream.get(), lz77_out_stream.get());
lz77_encoder.encode();
lz77_out_stream->resetOffsets();
}
BufferBitStream zlib_out_stream; BufferBitStream zlib_out_stream;
ZlibEncoder zlib_encoder(&lz77_out_stream, &zlib_out_stream); ZlibEncoder zlib_encoder(lz77_out_stream.get(), &zlib_out_stream);
zlib_encoder.encode(); zlib_encoder.encode();
zlib_out_stream.resetOffsets(); zlib_out_stream.resetOffsets();

View file

@ -2,6 +2,7 @@
#include "PngHeader.h" #include "PngHeader.h"
#include "Image.h" #include "Image.h"
#include "DeflateElements.h"
#include <memory> #include <memory>
#include <string> #include <string>
@ -46,6 +47,8 @@ private:
unsigned mPngInfoUserSet{false}; unsigned mPngInfoUserSet{false};
PngInfo mPngInfo; PngInfo mPngInfo;
PngHeader mPngHeader; PngHeader mPngHeader;
Deflate::CompressionMethod mCompressionMethod{Deflate::CompressionMethod::NONE};
}; };
using PngWriterPtr = std::unique_ptr<PngWriter>; using PngWriterPtr = std::unique_ptr<PngWriter>;

View file

@ -9,21 +9,26 @@
int main() int main()
{ {
unsigned width = 20; unsigned width = 10;
unsigned height = 20; unsigned height = 10;
unsigned numChannels = 3; unsigned numChannels = 1;
auto image = Image<unsigned char>::Create(width, height); auto image = Image<unsigned char>::Create(width, height);
image->setNumChannels(numChannels); image->setNumChannels(numChannels);
image->setBitDepth(8);
std::vector<unsigned char> data(image->getBytesPerRow()*height, 0); std::vector<unsigned char> data(width*height, 0);
for (unsigned idx=0; idx<width*height; idx++)
{
data[idx] = 10;
}
ImagePrimitives::drawAlternatingStrips(data, width, height, numChannels, image->getBytesPerRow());
image->setData(data); image->setData(data);
PngWriter writer; PngWriter writer;
writer.setPath("test.png"); writer.setPath("test.png");
writer.write(image); writer.write(image);
return 0;
File test_file("test.png"); File test_file("test.png");
test_file.SetAccessMode(File::AccessMode::Read); test_file.SetAccessMode(File::AccessMode::Read);
test_file.Open(true); test_file.Open(true);