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

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

View file

@ -9,7 +9,7 @@ ImageBitStream::ImageBitStream(Image<unsigned char>* image)
bool ImageBitStream::isFinished() const
{
return true;
return mByteOffset == mImage->getDataRef().size();
}
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()
{
return std::nullopt;
mByteOffset++;
if (isFinished() )
{
return std::nullopt;
}
const auto val = mImage->getDataRef()[mByteOffset];
return val;
}
void ImageBitStream::writeByte(unsigned char data)

View file

@ -22,6 +22,11 @@ public:
}
unsigned getBytesPerScanline() const
{
return mImage->getBytesPerRow();
}
private:
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
{
return "IHDR";
return mName;
}
const std::vector<unsigned char>& PngHeader::getData() const
@ -61,9 +61,9 @@ void PngHeader::updateData()
}
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);
mData.push_back(static_cast<unsigned char>(mPngInfo.mCompressionMethod));
mData.push_back(static_cast<unsigned char>(mPngInfo.mFilterMethod));
mData.push_back(static_cast<unsigned char>(mPngInfo.mInterlaceMethod));
}
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,
PALETTE = 3,
GREYSCALE_ALPHA = 4,
RGB_ALPHA = 6,
RGB_ALPHA = 6
};
std::string toString(ColorType colorType) const
enum class CompressionMethod : unsigned char
{
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";
}
}
DEFLATE = 0
};
std::string toString() const
enum class FilterMethod : unsigned char
{
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();
}
ADAPTIVE = 0
};
enum class InterlaceMethod : unsigned char
{
NONE = 0,
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};
unsigned char mCompressionMethod{0};
unsigned char mFilterMethod{0};
unsigned char mInterlaceMethod{0};
CompressionMethod mCompressionMethod{CompressionMethod::DEFLATE};
FilterMethod mFilterMethod{FilterMethod::ADAPTIVE};
InterlaceMethod mInterlaceMethod{InterlaceMethod::NONE};
};

View file

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

View file

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

View file

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