Continue png writing.
This commit is contained in:
parent
5400a232dd
commit
8f97e9b7a1
29 changed files with 714 additions and 302 deletions
|
@ -8,6 +8,7 @@ list(APPEND image_LIB_INCLUDES
|
|||
png/PngWriter.cpp
|
||||
png/PngReader.cpp
|
||||
png/PngHeader.cpp
|
||||
png/PngInfo.cpp
|
||||
ImageBitStream.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
73
src/image/png/PngFilter.h
Normal 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};
|
||||
};
|
|
@ -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
95
src/image/png/PngInfo.cpp
Normal 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();
|
||||
}
|
|
@ -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};
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue