Circle buffer and png cleaning.
This commit is contained in:
parent
59cc910d58
commit
5400a232dd
13 changed files with 353 additions and 122 deletions
89
src/image/png/PngHeader.cpp
Normal file
89
src/image/png/PngHeader.cpp
Normal file
|
@ -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<unsigned char> 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<unsigned char>& PngHeader::getData() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
void PngHeader::updateData()
|
||||
{
|
||||
mData.clear();
|
||||
unsigned num_bytes = sizeof(uint32_t);
|
||||
|
||||
for(unsigned idx=0; idx<num_bytes;idx++)
|
||||
{
|
||||
mData.push_back(ByteUtils::getByteN(mWidth, idx));
|
||||
}
|
||||
|
||||
for(unsigned idx=0; idx<num_bytes;idx++)
|
||||
{
|
||||
mData.push_back(ByteUtils::getByteN(mHeight, idx));
|
||||
}
|
||||
mData.push_back(mBitDepth);
|
||||
mData.push_back(static_cast<unsigned char>(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<unsigned char> 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;
|
||||
}
|
40
src/image/png/PngHeader.h
Normal file
40
src/image/png/PngHeader.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include "PngInfo.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class PngHeader
|
||||
{
|
||||
public:
|
||||
uint32_t getLength() const;
|
||||
|
||||
uint32_t getCrc() const;
|
||||
|
||||
const std::vector<unsigned char>& getData() const;
|
||||
|
||||
unsigned char getHighBitCheck() const;
|
||||
|
||||
std::vector<unsigned char> 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<unsigned char> mData;
|
||||
};
|
56
src/image/png/PngInfo.h
Normal file
56
src/image/png/PngInfo.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
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};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
157
src/image/png/PngReader.cpp
Normal file
157
src/image/png/PngReader.cpp
Normal file
|
@ -0,0 +1,157 @@
|
|||
#include "PngReader.h"
|
||||
|
||||
#include "BinaryStream.h"
|
||||
#include "BitStream.h"
|
||||
#include "BufferBitStream.h"
|
||||
|
||||
#include "ZlibEncoder.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
PngReader::~PngReader()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PngReader::setPath(const Path& path)
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
bool PngReader::checkSignature()
|
||||
{
|
||||
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::vector<char> sequence{13, 10, 26, 10};
|
||||
for (auto c : sequence)
|
||||
{
|
||||
if (mFile->GetInHandle()->get() != c)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentOffset += 8;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PngReader::readChunk()
|
||||
{
|
||||
unsigned length = *BinaryStream::getNextDWord(mFile->GetInHandle());
|
||||
|
||||
std::string chunkType;
|
||||
BinaryStream::getNextString(mFile->GetInHandle(), chunkType, 4);
|
||||
mCurrentOffset += 8;
|
||||
|
||||
std::cout << "Got chunk with type: " << chunkType << " and length: " << length << std::endl;
|
||||
bool lastChunk = false;
|
||||
if (chunkType == "IHDR")
|
||||
{
|
||||
readHeaderChunk();
|
||||
}
|
||||
else if(chunkType == "IEND")
|
||||
{
|
||||
lastChunk = true;
|
||||
}
|
||||
else if(chunkType == "IDAT")
|
||||
{
|
||||
readIDATChunk(length);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(unsigned idx=0;idx<length;idx++)
|
||||
{
|
||||
mFile->GetInHandle()->get();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned crcCheck = *BinaryStream::getNextDWord(mFile->GetInHandle());
|
||||
|
||||
mCurrentOffset += 4;
|
||||
return !lastChunk;
|
||||
}
|
||||
|
||||
void PngReader::readIDATChunk(unsigned length)
|
||||
{
|
||||
if (mAwaitingDataBlock)
|
||||
{
|
||||
mEncoder->setCompressionMethod(*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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PngReader::readHeaderChunk()
|
||||
{
|
||||
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<PngInfo::ColorType>(mFile->GetInHandle()->get());
|
||||
info.mCompressionMethod = mFile->GetInHandle()->get();
|
||||
info.mFilterMethod = mFile->GetInHandle()->get();
|
||||
info.mInterlaceMethod = mFile->GetInHandle()->get();
|
||||
mHeader.setPngInfo(info);
|
||||
|
||||
mCurrentOffset += 13;
|
||||
|
||||
logHeader();
|
||||
}
|
||||
|
||||
void PngReader::logHeader()
|
||||
{
|
||||
std::cout << "IHDR\n" << mHeader.toString() << "*************\n";
|
||||
}
|
||||
|
||||
std::unique_ptr<Image<unsigned char> > PngReader::read()
|
||||
{
|
||||
auto image = std::make_unique<Image<unsigned char> >(5, 5);
|
||||
|
||||
mFile = std::make_unique<File>(mPath);
|
||||
mFile->Open(true);
|
||||
|
||||
if (!checkSignature())
|
||||
{
|
||||
std::cout << "Signature check failed" << std::endl;
|
||||
return image;
|
||||
}
|
||||
|
||||
mInputStream = std::make_unique<BufferBitStream>();
|
||||
mOutputStream = std::make_unique<BufferBitStream>();
|
||||
mEncoder = std::make_unique<ZlibEncoder>(mInputStream.get(), mOutputStream.get());
|
||||
|
||||
while(readChunk())
|
||||
{
|
||||
|
||||
}
|
||||
mEncoder->decode();
|
||||
return std::move(image);
|
||||
}
|
47
src/image/png/PngReader.h
Normal file
47
src/image/png/PngReader.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
|
||||
#include "File.h"
|
||||
#include "Image.h"
|
||||
#include "PngHeader.h"
|
||||
#include "ZlibEncoder.h"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
|
||||
class BitStream;
|
||||
class ZlibEncoder;
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
class PngReader
|
||||
{
|
||||
public:
|
||||
~PngReader();
|
||||
void setPath(const Path& path);
|
||||
|
||||
std::unique_ptr<Image<unsigned char> > read();
|
||||
private:
|
||||
bool readChunk();
|
||||
|
||||
void readHeaderChunk();
|
||||
|
||||
void readIDATChunk(unsigned length);
|
||||
|
||||
void logHeader();
|
||||
|
||||
bool checkSignature();
|
||||
|
||||
unsigned mCurrentOffset{0};
|
||||
|
||||
PngHeader mHeader;
|
||||
|
||||
std::unique_ptr<Image<unsigned char> > mWorkingImage;
|
||||
std::unique_ptr<File> mFile;
|
||||
Path mPath;
|
||||
|
||||
std::unique_ptr<ZlibEncoder> mEncoder;
|
||||
std::unique_ptr<BitStream> mInputStream;
|
||||
std::unique_ptr<BitStream> mOutputStream;
|
||||
bool mAwaitingDataBlock{true};
|
||||
};
|
172
src/image/png/PngWriter.cpp
Normal file
172
src/image/png/PngWriter.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
#include "PngWriter.h"
|
||||
|
||||
#include "Image.h"
|
||||
#include "File.h"
|
||||
#include "BufferBitStream.h"
|
||||
#include "OutputBitStream.h"
|
||||
#include "ImageBitStream.h"
|
||||
|
||||
#include "Lz77Encoder.h"
|
||||
#include "ZlibEncoder.h"
|
||||
#include "CyclicRedundancyChecker.h"
|
||||
|
||||
#include "ByteUtils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
PngWriter::PngWriter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
PngWriter::~PngWriter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<PngWriter> PngWriter::Create()
|
||||
{
|
||||
return std::make_unique<PngWriter>();
|
||||
}
|
||||
|
||||
void PngWriter::setPath(const Path& path)
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
void PngWriter::setPngInfo(const PngInfo& info)
|
||||
{
|
||||
mPngInfoUserSet = true;
|
||||
mPngInfo = info;
|
||||
}
|
||||
|
||||
void PngWriter::writeSignature()
|
||||
{
|
||||
mOutStream->writeByte(mPngHeader.getHighBitCheck());
|
||||
mOutStream->writeBytes(StringUtils::toBytes(mPngHeader.getFileName()));
|
||||
mOutStream->writeBytes(mPngHeader.getSignature());
|
||||
}
|
||||
|
||||
void PngWriter::writeHeader()
|
||||
{
|
||||
writeSignature();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
mPngHeader.setPngInfo(mPngInfo);
|
||||
mPngHeader.setImageData(mWorkingImage->getWidth(), mWorkingImage->getHeight(), mWorkingImage->getBitDepth());
|
||||
|
||||
auto length = mPngHeader.getLength();
|
||||
mOutStream->write(length);
|
||||
|
||||
mOutStream->writeBytes(StringUtils::toBytes(mPngHeader.getChunkName()));
|
||||
|
||||
mPngHeader.updateData();
|
||||
mOutStream->writeBytes(mPngHeader.getData());
|
||||
|
||||
auto crc = mPngHeader.getCrc();
|
||||
mOutStream->write(crc);
|
||||
}
|
||||
|
||||
void PngWriter::writeEndChunk()
|
||||
{
|
||||
unsigned length{0};
|
||||
mOutStream->write(length);
|
||||
|
||||
mOutStream->writeBytes(StringUtils::toBytes("IEND"));
|
||||
|
||||
CyclicRedundancyChecker crc_check;
|
||||
auto crc = crc_check.doCrc(nullptr, 0);
|
||||
mOutStream->write(crc);
|
||||
}
|
||||
|
||||
void PngWriter::writeDataChunks(const BufferBitStream& buffer)
|
||||
{
|
||||
auto num_bytes = buffer.getBuffer().size();
|
||||
auto max_bytes{32000};
|
||||
std::vector<unsigned char> crc_buffer(max_bytes, 0);
|
||||
|
||||
unsigned num_dat_chunks = num_bytes/max_bytes + 1;
|
||||
unsigned offset = 0;
|
||||
for(unsigned idx=0;idx<num_dat_chunks;idx++)
|
||||
{
|
||||
auto length = max_bytes;
|
||||
if (idx == num_dat_chunks - 1)
|
||||
{
|
||||
length = num_bytes - num_dat_chunks*num_bytes;
|
||||
}
|
||||
|
||||
mOutStream->write(length);
|
||||
|
||||
mOutStream->writeBytes(StringUtils::toBytes("IDAT"));
|
||||
|
||||
for(unsigned jdx=0; jdx<length; jdx++)
|
||||
{
|
||||
auto val = buffer.getBuffer()[idx*max_bytes + jdx];
|
||||
crc_buffer[jdx] = val;
|
||||
mOutStream->writeByte(val);
|
||||
}
|
||||
|
||||
CyclicRedundancyChecker crc_check;
|
||||
auto crc = crc_check.doCrc(crc_buffer.data(), crc_buffer.size());
|
||||
mOutStream->write(crc);
|
||||
}
|
||||
}
|
||||
|
||||
void PngWriter::write(const std::unique_ptr<Image<unsigned char> >& image)
|
||||
{
|
||||
if (!mPath.empty())
|
||||
{
|
||||
mWorkingFile = std::make_unique<File>(mPath);
|
||||
mWorkingFile->SetAccessMode(File::AccessMode::Write);
|
||||
mWorkingFile->Open(true);
|
||||
mOutStream = std::make_unique<OutputBitStream>(mWorkingFile->GetOutHandle());
|
||||
}
|
||||
else
|
||||
{
|
||||
mOutStream = std::make_unique<BufferBitStream>();
|
||||
}
|
||||
|
||||
mWorkingImage = image.get();
|
||||
mInStream = std::make_unique<ImageBitStream>(image.get());
|
||||
|
||||
writeHeader();
|
||||
|
||||
BufferBitStream lz77_out_stream;
|
||||
Lz77Encoder lz77_encoder(mInStream.get(), &lz77_out_stream);
|
||||
|
||||
lz77_encoder.encode();
|
||||
lz77_out_stream.resetOffsets();
|
||||
|
||||
BufferBitStream zlib_out_stream;
|
||||
ZlibEncoder zlib_encoder(&lz77_out_stream, &zlib_out_stream);
|
||||
zlib_encoder.encode();
|
||||
zlib_out_stream.resetOffsets();
|
||||
|
||||
writeDataChunks(zlib_out_stream);
|
||||
|
||||
writeEndChunk();
|
||||
|
||||
if (mWorkingFile)
|
||||
{
|
||||
mWorkingFile->Close();
|
||||
}
|
||||
}
|
51
src/image/png/PngWriter.h
Normal file
51
src/image/png/PngWriter.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include "PngHeader.h"
|
||||
#include "Image.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
class BitStream;
|
||||
class BufferBitStream;
|
||||
class File;
|
||||
|
||||
class PngWriter
|
||||
{
|
||||
public:
|
||||
PngWriter();
|
||||
~PngWriter();
|
||||
|
||||
static std::unique_ptr<PngWriter> Create();
|
||||
|
||||
void setPath(const Path& path);
|
||||
|
||||
void setPngInfo(const PngInfo& info);
|
||||
|
||||
void write(const std::unique_ptr<Image<unsigned char> >& image);
|
||||
|
||||
private:
|
||||
void writeSignature();
|
||||
void writeHeader();
|
||||
|
||||
void writeDataChunks(const BufferBitStream& buffer);
|
||||
|
||||
void writeEndChunk();
|
||||
|
||||
//void writeIDatChunk();
|
||||
|
||||
Path mPath;
|
||||
Image<unsigned char>* mWorkingImage{nullptr};
|
||||
std::unique_ptr<BitStream> mInStream;
|
||||
std::unique_ptr<BitStream> mOutStream;
|
||||
std::unique_ptr<File> mWorkingFile;
|
||||
|
||||
unsigned mPngInfoUserSet{false};
|
||||
PngInfo mPngInfo;
|
||||
PngHeader mPngHeader;
|
||||
};
|
||||
|
||||
using PngWriterPtr = std::unique_ptr<PngWriter>;
|
Loading…
Add table
Add a link
Reference in a new issue