212 lines
5.2 KiB
C++
212 lines
5.2 KiB
C++
#include "PngWriter.h"
|
|
|
|
#include "Image.h"
|
|
#include "File.h"
|
|
#include "BufferBitStream.h"
|
|
#include "OutputBitStream.h"
|
|
#include "ImageBitStream.h"
|
|
|
|
#include "PngFilter.h"
|
|
#include "Lz77Encoder.h"
|
|
#include "ZlibEncoder.h"
|
|
#include "CyclicRedundancyChecker.h"
|
|
|
|
#include "ByteUtils.h"
|
|
|
|
#include <iostream>
|
|
|
|
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();
|
|
|
|
std::cout << mPngHeader.toString() << "*********" << std::endl;
|
|
mOutStream->write(crc);
|
|
}
|
|
|
|
void PngWriter::writeEndChunk()
|
|
{
|
|
unsigned length{0};
|
|
mOutStream->write(length);
|
|
|
|
mOutStream->writeBytes(StringUtils::toBytes("IEND"));
|
|
std::vector<unsigned char> char_data = StringUtils::toBytes("IEND");
|
|
|
|
CyclicRedundancyChecker crc_check;
|
|
for (auto c : char_data)
|
|
{
|
|
crc_check.addValue(c);
|
|
}
|
|
auto crc = crc_check.getChecksum();
|
|
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};
|
|
|
|
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;
|
|
}
|
|
|
|
std::cout << "Writing idat length " << num_bytes << std::endl;
|
|
mOutStream->write(num_bytes);
|
|
|
|
std::vector<unsigned char> char_data = StringUtils::toBytes("IDAT");
|
|
mOutStream->writeBytes(char_data);
|
|
|
|
CyclicRedundancyChecker crc_check;
|
|
for (auto c : char_data)
|
|
{
|
|
crc_check.addValue(c);
|
|
}
|
|
|
|
for(unsigned jdx=0; jdx<num_bytes; jdx++)
|
|
{
|
|
auto val = buffer.getBuffer()[idx*max_bytes + jdx];
|
|
mOutStream->writeByte(val);
|
|
crc_check.addValue(val);
|
|
}
|
|
|
|
auto crc = crc_check.getChecksum();
|
|
std::cout << "Writing idat crc" << crc << std::endl;
|
|
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();
|
|
|
|
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();
|
|
|
|
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;
|
|
//}
|
|
|
|
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.get(), &zlib_out_stream);
|
|
zlib_encoder.encode();
|
|
zlib_out_stream.resetOffsets();
|
|
|
|
writeDataChunks(zlib_out_stream);
|
|
|
|
writeEndChunk();
|
|
|
|
if (mWorkingFile)
|
|
{
|
|
mWorkingFile->Close();
|
|
}
|
|
}
|