Continue work on png writer.
This commit is contained in:
parent
9c8faa534b
commit
86bc0d89f6
19 changed files with 225 additions and 19 deletions
|
@ -8,6 +8,7 @@ list(APPEND compression_LIB_INCLUDES
|
||||||
DeflateEncoder.cpp
|
DeflateEncoder.cpp
|
||||||
DeflateBlock.cpp
|
DeflateBlock.cpp
|
||||||
Lz77Encoder.cpp
|
Lz77Encoder.cpp
|
||||||
|
CyclicRedundancyChecker.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(compression SHARED ${compression_LIB_INCLUDES})
|
add_library(compression SHARED ${compression_LIB_INCLUDES})
|
||||||
|
|
0
src/compression/CyclicRedundancyChecker.cpp
Normal file
0
src/compression/CyclicRedundancyChecker.cpp
Normal file
52
src/compression/CyclicRedundancyChecker.h
Normal file
52
src/compression/CyclicRedundancyChecker.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class CyclicRedundancyChecker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
void createTable()
|
||||||
|
{
|
||||||
|
unsigned long c{0};
|
||||||
|
for (int n = 0; n < 256; n++)
|
||||||
|
{
|
||||||
|
c = (unsigned long) n;
|
||||||
|
for (int k = 0; k < 8; k++)
|
||||||
|
{
|
||||||
|
if (c & 1)
|
||||||
|
{
|
||||||
|
c = 0xedb88320L ^ (c >> 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c = c >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mTable[n] = c;
|
||||||
|
}
|
||||||
|
mTableComputed = true;
|
||||||
|
mTableComputed = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long updateCrc(unsigned long crc, unsigned char *buf, int len)
|
||||||
|
{
|
||||||
|
unsigned long c = crc;
|
||||||
|
if (!mTableComputed)
|
||||||
|
{
|
||||||
|
createTable();
|
||||||
|
}
|
||||||
|
for (int n = 0; n < len; n++)
|
||||||
|
{
|
||||||
|
c = mTable[(c ^ buf[n]) & 0xff] ^ (c >> 8);
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long doCrc(unsigned char *buf, int len)
|
||||||
|
{
|
||||||
|
return updateCrc(0xffffffffL, buf, len) ^ 0xffffffffL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mTableComputed{false};
|
||||||
|
unsigned long mTable[256];
|
||||||
|
};
|
|
@ -21,6 +21,11 @@ unsigned char ByteUtils::getHigherNBits(unsigned char input, unsigned num)
|
||||||
return input >> 8 - num;
|
return input >> 8 - num;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned char ByteUtils::getByteN(uint32_t input, unsigned n)
|
||||||
|
{
|
||||||
|
return (input << 8*n) >> 24;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char ByteUtils::getLowerNBits(unsigned char input, unsigned num)
|
unsigned char ByteUtils::getLowerNBits(unsigned char input, unsigned num)
|
||||||
{
|
{
|
||||||
switch (num)
|
switch (num)
|
||||||
|
|
|
@ -17,6 +17,8 @@ public:
|
||||||
|
|
||||||
static Word GetWordLastByte(const Word word);
|
static Word GetWordLastByte(const Word word);
|
||||||
|
|
||||||
|
static unsigned char getByteN(uint32_t input, unsigned n);
|
||||||
|
|
||||||
static unsigned char getHigherNBits(unsigned char input, unsigned num);
|
static unsigned char getHigherNBits(unsigned char input, unsigned num);
|
||||||
|
|
||||||
static unsigned char getLowerNBits(unsigned char input, unsigned num);
|
static unsigned char getLowerNBits(unsigned char input, unsigned num);
|
||||||
|
|
|
@ -32,9 +32,23 @@ std::ofstream* File::GetOutHandle() const
|
||||||
return mOutHandle.get();
|
return mOutHandle.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char File::readNextByte()
|
std::optional<unsigned char> File::readNextByte()
|
||||||
{
|
{
|
||||||
return mInHandle->get();
|
if (mInHandle->good())
|
||||||
|
{
|
||||||
|
if (auto val = mInHandle->get(); val == EOF)
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void File::Open(bool asBinary)
|
void File::Open(bool asBinary)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
using Path = std::filesystem::path;
|
using Path = std::filesystem::path;
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@ public:
|
||||||
|
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
unsigned char readNextByte();
|
std::optional<unsigned char> readNextByte();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -27,6 +27,8 @@ public:
|
||||||
|
|
||||||
virtual void writeByte(unsigned char data) = 0;
|
virtual void writeByte(unsigned char data) = 0;
|
||||||
|
|
||||||
|
virtual void writeBytes(const std::vector<unsigned char> data) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int mByteOffset{0};
|
int mByteOffset{0};
|
||||||
unsigned mBitOffset{0};
|
unsigned mBitOffset{0};
|
||||||
|
|
|
@ -17,6 +17,11 @@ public:
|
||||||
|
|
||||||
void writeByte(unsigned char data) override;
|
void writeByte(unsigned char data) override;
|
||||||
|
|
||||||
|
void writeBytes(const std::vector<unsigned char> data) override
|
||||||
|
{
|
||||||
|
std::copy(data.begin(), data.end(), std::back_inserter(mBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<unsigned char>& getBuffer() const
|
const std::vector<unsigned char>& getBuffer() const
|
||||||
{
|
{
|
||||||
return mBuffer;
|
return mBuffer;
|
||||||
|
|
|
@ -16,6 +16,11 @@ class InputBitStream : public BitStream
|
||||||
|
|
||||||
void writeByte(unsigned char data) override;
|
void writeByte(unsigned char data) override;
|
||||||
|
|
||||||
|
void writeBytes(const std::vector<unsigned char> data) override
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::basic_istream<unsigned char>* mStream{nullptr};
|
std::basic_istream<unsigned char>* mStream{nullptr};
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,3 +26,11 @@ void OutputBitStream::writeByte(unsigned char data)
|
||||||
{
|
{
|
||||||
(*mStream) << data;
|
(*mStream) << data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OutputBitStream::writeBytes(const std::vector<unsigned char> data)
|
||||||
|
{
|
||||||
|
for(auto byte : data)
|
||||||
|
{
|
||||||
|
writeByte(byte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ public:
|
||||||
|
|
||||||
void writeByte(unsigned char data) override;
|
void writeByte(unsigned char data) override;
|
||||||
|
|
||||||
|
void writeBytes(const std::vector<unsigned char> data) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::basic_ostream<char>* mStream{nullptr};
|
std::basic_ostream<char>* mStream{nullptr};
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,11 @@ public:
|
||||||
|
|
||||||
void writeByte(unsigned char data) override;
|
void writeByte(unsigned char data) override;
|
||||||
|
|
||||||
|
void writeBytes(const std::vector<unsigned char> data) override
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Image<unsigned char>* mImage{nullptr};
|
Image<unsigned char>* mImage{nullptr};
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "CyclicRedundancyChecker.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
@ -15,15 +17,23 @@ namespace Png
|
||||||
return {13, 10, 26, 10};
|
return {13, 10, 26, 10};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string getName()
|
||||||
|
{
|
||||||
|
return "PNG";
|
||||||
|
}
|
||||||
|
|
||||||
struct IHDRChunk
|
struct IHDRChunk
|
||||||
{
|
{
|
||||||
unsigned width{0};
|
uint32_t width{0};
|
||||||
unsigned height{0};
|
uint32_t height{0};
|
||||||
char bitDepth{0};
|
unsigned char bitDepth{0};
|
||||||
char colorType{0};
|
unsigned char colorType{0};
|
||||||
char compressionMethod{0};
|
unsigned char compressionMethod{0};
|
||||||
char filterMethod{0};
|
unsigned char filterMethod{0};
|
||||||
char interlaceMethod{0};
|
unsigned char interlaceMethod{0};
|
||||||
|
std::string name{"IHDR"};
|
||||||
|
|
||||||
|
std::vector<unsigned char> mData;
|
||||||
|
|
||||||
std::string toString() const
|
std::string toString() const
|
||||||
{
|
{
|
||||||
|
@ -37,6 +47,42 @@ namespace Png
|
||||||
sstr << "interlaceMethod: " << (int)interlaceMethod << "\n";
|
sstr << "interlaceMethod: " << (int)interlaceMethod << "\n";
|
||||||
return sstr.str();
|
return sstr.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t getLength() const
|
||||||
|
{
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateData()
|
||||||
|
{
|
||||||
|
mData.clear();
|
||||||
|
unsigned num_bytes = sizeof(uint32_t);
|
||||||
|
|
||||||
|
for(unsigned idx=0; idx<num_bytes;idx++)
|
||||||
|
{
|
||||||
|
mData.push_back(ByteUtils::getByteN(width, idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned idx=0; idx<num_bytes;idx++)
|
||||||
|
{
|
||||||
|
mData.push_back(ByteUtils::getByteN(height, idx));
|
||||||
|
}
|
||||||
|
mData.push_back(bitDepth);
|
||||||
|
mData.push_back(colorType);
|
||||||
|
mData.push_back(compressionMethod);
|
||||||
|
mData.push_back(filterMethod);
|
||||||
|
mData.push_back(interlaceMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getCrc() const
|
||||||
|
{
|
||||||
|
CyclicRedundancyChecker crc_check;
|
||||||
|
std::vector<unsigned char> 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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,11 +88,11 @@ void PngReader::readIDATChunk(unsigned length)
|
||||||
{
|
{
|
||||||
if (mAwaitingDataBlock)
|
if (mAwaitingDataBlock)
|
||||||
{
|
{
|
||||||
mEncoder->setCompressionMethod(mFile->readNextByte());
|
mEncoder->setCompressionMethod(*mFile->readNextByte());
|
||||||
mEncoder->setExtraFlags(mFile->readNextByte());
|
mEncoder->setExtraFlags(*mFile->readNextByte());
|
||||||
for(unsigned idx=0; idx<length-2; idx++)
|
for(unsigned idx=0; idx<length-2; idx++)
|
||||||
{
|
{
|
||||||
mInputStream->writeByte(mFile->readNextByte());
|
mInputStream->writeByte(*mFile->readNextByte());
|
||||||
}
|
}
|
||||||
mAwaitingDataBlock = false;
|
mAwaitingDataBlock = false;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ void PngReader::readIDATChunk(unsigned length)
|
||||||
{
|
{
|
||||||
for(unsigned idx=0; idx<length; idx++)
|
for(unsigned idx=0; idx<length; idx++)
|
||||||
{
|
{
|
||||||
mInputStream->writeByte(mFile->readNextByte());
|
mInputStream->writeByte(*mFile->readNextByte());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
#include "Lz77Encoder.h"
|
#include "Lz77Encoder.h"
|
||||||
|
|
||||||
|
#include "ByteUtils.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
PngWriter::PngWriter()
|
PngWriter::PngWriter()
|
||||||
|
@ -34,15 +36,49 @@ void PngWriter::setPath(const Path& path)
|
||||||
void PngWriter::writeSignature()
|
void PngWriter::writeSignature()
|
||||||
{
|
{
|
||||||
mOutStream->writeByte(Png::getHighBitCheck());
|
mOutStream->writeByte(Png::getHighBitCheck());
|
||||||
for (auto byte : Png::getSignature())
|
mOutStream->writeBytes(StringUtils::toBytes(Png::getName()));
|
||||||
{
|
mOutStream->writeBytes(Png::getSignature());
|
||||||
mOutStream->writeByte(byte);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PngWriter::writeHeader()
|
void PngWriter::writeHeader()
|
||||||
{
|
{
|
||||||
writeSignature();
|
writeSignature();
|
||||||
|
|
||||||
|
Png::IHDRChunk header_chunk;
|
||||||
|
header_chunk.width = mWorkingImage->getWidth();
|
||||||
|
header_chunk.height = mWorkingImage->getHeight();
|
||||||
|
header_chunk.bitDepth = mWorkingImage->getBitDepth();
|
||||||
|
header_chunk.colorType = 6;
|
||||||
|
|
||||||
|
auto length = header_chunk.getLength();
|
||||||
|
auto crc = header_chunk.getCrc();
|
||||||
|
|
||||||
|
unsigned num_bytes = sizeof(uint32_t);
|
||||||
|
for(unsigned idx=0; idx<num_bytes;idx++)
|
||||||
|
{
|
||||||
|
mOutStream->writeByte(ByteUtils::getByteN(length, idx));
|
||||||
|
}
|
||||||
|
mOutStream->writeBytes(StringUtils::toBytes(header_chunk.name));
|
||||||
|
|
||||||
|
for(unsigned idx=0; idx<num_bytes;idx++)
|
||||||
|
{
|
||||||
|
mOutStream->writeByte(ByteUtils::getByteN(header_chunk.width, idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
for(unsigned idx=0; idx<num_bytes;idx++)
|
||||||
|
{
|
||||||
|
mOutStream->writeByte(ByteUtils::getByteN(header_chunk.height, idx));
|
||||||
|
}
|
||||||
|
mOutStream->writeByte(header_chunk.bitDepth);
|
||||||
|
mOutStream->writeByte(header_chunk.colorType);
|
||||||
|
mOutStream->writeByte(header_chunk.compressionMethod);
|
||||||
|
mOutStream->writeByte(header_chunk.filterMethod);
|
||||||
|
mOutStream->writeByte(header_chunk.interlaceMethod);
|
||||||
|
|
||||||
|
for(unsigned idx=0; idx<num_bytes;idx++)
|
||||||
|
{
|
||||||
|
mOutStream->writeByte(ByteUtils::getByteN(crc, idx));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PngWriter::write(const std::unique_ptr<Image<unsigned char> >& image)
|
void PngWriter::write(const std::unique_ptr<Image<unsigned char> >& image)
|
||||||
|
@ -50,6 +86,7 @@ void PngWriter::write(const std::unique_ptr<Image<unsigned char> >& image)
|
||||||
if (!mPath.empty())
|
if (!mPath.empty())
|
||||||
{
|
{
|
||||||
mWorkingFile = std::make_unique<File>(mPath);
|
mWorkingFile = std::make_unique<File>(mPath);
|
||||||
|
mWorkingFile->SetAccessMode(File::AccessMode::Write);
|
||||||
mWorkingFile->Open(true);
|
mWorkingFile->Open(true);
|
||||||
mOutStream = std::make_unique<OutputBitStream>(mWorkingFile->GetOutHandle());
|
mOutStream = std::make_unique<OutputBitStream>(mWorkingFile->GetOutHandle());
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ private:
|
||||||
void writeSignature();
|
void writeSignature();
|
||||||
void writeHeader();
|
void writeHeader();
|
||||||
|
|
||||||
|
//void writeIDatChunk();
|
||||||
|
|
||||||
Path mPath;
|
Path mPath;
|
||||||
Image<unsigned char>* mWorkingImage{nullptr};
|
Image<unsigned char>* mWorkingImage{nullptr};
|
||||||
|
|
|
@ -13,6 +13,16 @@ int main()
|
||||||
auto slice = ByteUtils::getMBitsAtN(byte, 3, 3);
|
auto slice = ByteUtils::getMBitsAtN(byte, 3, 3);
|
||||||
std::cout << "Slice is " << ByteUtils::toString(slice) << std::endl;
|
std::cout << "Slice is " << ByteUtils::toString(slice) << std::endl;
|
||||||
|
|
||||||
|
uint32_t input {12345678};
|
||||||
|
auto byte0 = ByteUtils::getByteN(input, 0);
|
||||||
|
auto byte1 = ByteUtils::getByteN(input, 1);
|
||||||
|
auto byte2 = ByteUtils::getByteN(input, 2);
|
||||||
|
auto byte3 = ByteUtils::getByteN(input, 3);
|
||||||
|
|
||||||
|
std::cout << "Byte0 is " << ByteUtils::toString(byte0) << std::endl;
|
||||||
|
std::cout << "Byte1 is " << ByteUtils::toString(byte1) << std::endl;
|
||||||
|
std::cout << "Byte2 is " << ByteUtils::toString(byte2) << std::endl;
|
||||||
|
std::cout << "Byte3 is " << ByteUtils::toString(byte3) << std::endl;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "Image.h"
|
#include "Image.h"
|
||||||
#include "PngWriter.h"
|
#include "PngWriter.h"
|
||||||
|
|
||||||
|
#include "File.h"
|
||||||
#include "BitStream.h"
|
#include "BitStream.h"
|
||||||
#include "ImagePrimitives.h"
|
#include "ImagePrimitives.h"
|
||||||
|
|
||||||
|
@ -24,5 +24,15 @@ int main()
|
||||||
writer.setPath("test.png");
|
writer.setPath("test.png");
|
||||||
writer.write(image);
|
writer.write(image);
|
||||||
|
|
||||||
|
File test_file("test.png");
|
||||||
|
test_file.SetAccessMode(File::AccessMode::Read);
|
||||||
|
test_file.Open(true);
|
||||||
|
|
||||||
|
while(auto byte = test_file.readNextByte())
|
||||||
|
{
|
||||||
|
std::cout << static_cast<unsigned>(*byte) << std::endl;
|
||||||
|
}
|
||||||
|
test_file.Close();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue