Continue work on png writer.

This commit is contained in:
James Grogan 2022-11-23 17:51:36 +00:00
parent 9c8faa534b
commit 86bc0d89f6
19 changed files with 225 additions and 19 deletions

View file

@ -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})

View 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];
};

View file

@ -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)

View file

@ -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);

View file

@ -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)

View file

@ -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:

View file

@ -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};

View file

@ -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;

View file

@ -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};
}; };

View file

@ -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);
}
}

View file

@ -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};
}; };

View file

@ -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};
}; };

View file

@ -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;
}
}; };
} }

View file

@ -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());
} }
} }
} }

View file

@ -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());
} }

View file

@ -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};

View file

@ -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;
} }

View file

@ -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;
} }