diff options
| author | Dylan <boss@tehbox.org> | 2026-05-06 13:39:47 +1200 |
|---|---|---|
| committer | Dylan <boss@tehbox.org> | 2026-05-06 13:39:47 +1200 |
| commit | d60976a70e68e1cd312b0e56fe6fbe6c7428cbaa (patch) | |
| tree | 00bc987a6d935ca26e4d4c2d61ad70d7aa3c2f4c /src | |
| parent | a75bdd0e167140eeb4afb091c9dedd84474c8531 (diff) | |
| download | tehimage-d60976a70e68e1cd312b0e56fe6fbe6c7428cbaa.tar.gz tehimage-d60976a70e68e1cd312b0e56fe6fbe6c7428cbaa.zip | |
feat: Started on PNG writing implementation
Currently writes IHDR and IEND chunks correctly
CRC implementation is borrowed from the specification
Writer class now also has a buffer for the CRC calculation
Diffstat (limited to 'src')
| -rw-r--r-- | src/PNGImage.cpp | 86 | ||||
| -rw-r--r-- | src/PNGImage.h | 35 | ||||
| -rw-r--r-- | src/files.cpp | 23 | ||||
| -rw-r--r-- | src/files.h | 7 |
4 files changed, 139 insertions, 12 deletions
diff --git a/src/PNGImage.cpp b/src/PNGImage.cpp index b9f956f..1e46f22 100644 --- a/src/PNGImage.cpp +++ b/src/PNGImage.cpp @@ -70,7 +70,15 @@ namespace TehImage int PNGImage::writeToFile(std::string filename) { - cout << "Not implemented" << endl; + std::unique_ptr<Writer> writerMem(new Writer(filename, FileEndianness::BIG)); + writer = writerMem.get(); + + uint8_t signature[] = {137, 80, 78, 71, 13, 10, 26, 10}; + writer->writeBytes((char*)signature, 8); + + writeIHDR(); + writeIEND(); + return 2; }; @@ -403,4 +411,80 @@ namespace TehImage delete [] rawImage; } + static uint32_t CRCTable[256]; + static bool CRCComputed = false; + + static void generateCRC() + { + for(uint32_t i = 0; i < 256; i++) + { + uint32_t c = i; + for(int bit = 0; bit < 8; bit++) + { + if(c & 1) + c = 0xEDB88320 ^ (c >> 1); + else + c >>= 1; + } + CRCTable[i] = c; + } + CRCComputed = true; + } + + uint32_t PNGImage::calculateCRC(uint8_t* buffer, std::size_t bufflen) + { + uint32_t reg = 0xFFFFFFFF; + if (!CRCComputed) + generateCRC(); + for (int i = 0; i < bufflen; i++) { + reg = CRCTable[(reg ^ buffer[i]) & 0xff] ^ (reg >> 8); + } + return reg^0xFFFFFFFF; + } + + + DEFINE_CHUNK_WRITER(IHDR) + { + uint32_t chunkSize = 13; + writer->writeData(chunkSize); + writer->flushBuffer(); + char header[] = "IHDR"; + writer->writeBytes(header, 4); + + writer->writeData(width); + writer->writeData(height); + writer->writeData(bpp); + writer->writeData<uint8_t>(ColorTypes::TRUECOLOR | ColorTypes::ALPHA); + writer->writeData<uint8_t>(0); // Compression method + writer->writeData<uint8_t>(0); // Filter method + writer->writeData<uint8_t>(0); // Interlace method + + uint32_t CRC = calculateCRC((uint8_t*)writer->buffer, writer->pos); + // cout << CRC << endl; + writer->writeData(CRC); + } + + DEFINE_CHUNK_WRITER(IDAT) + { + uint32_t chunkSize = 0; + writer->writeData(chunkSize); + writer->flushBuffer(); + + char header[] = "IDAT"; + writer->writeBytes(header, 4); + } + + DEFINE_CHUNK_WRITER(IEND) + { + uint32_t chunkSize = 0; + writer->writeData(chunkSize); + writer->flushBuffer(); + + char header[] = "IEND"; + writer->writeBytes(header, 4); + + uint32_t CRC = calculateCRC((uint8_t*)writer->buffer, writer->pos); + // cout << CRC << endl; + writer->writeData(CRC); + } } diff --git a/src/PNGImage.h b/src/PNGImage.h index 1ed1be5..4bbb4ff 100644 --- a/src/PNGImage.h +++ b/src/PNGImage.h @@ -10,16 +10,28 @@ #include <string> #include <vector> -#define CHUNK_READER(X) void X(uint32_t chunkSize) -#define REGISTER_CHUNK_READER(X) chunkReaders.insert({#X, &PNGImage::X}) -#define DEFINE_CHUNK_READER(X) void PNGImage::X(uint32_t chunkSize) +#define CHUNK_READER(X) void read##X(uint32_t chunkSize) +#define REGISTER_CHUNK_READER(X) chunkReaders.insert({#X, &PNGImage::read##X}) +#define DEFINE_CHUNK_READER(X) void PNGImage::read##X(uint32_t chunkSize) + +#define CHUNK_WRITER(X) void write##X() +#define DEFINE_CHUNK_WRITER(X) void PNGImage::write##X() + + namespace TehImage { - class PNGImage : public Image { public: + typedef enum + { + GRAYSCALE = 0, + TRUECOLOR = 2, + INDEXED = 3, + ALPHA = 4 + } ColorTypes; + PNGImage(); ~PNGImage(); @@ -44,8 +56,6 @@ namespace TehImage uint8_t hour; uint8_t minute; uint8_t second; - - bool readNextChunk(); private: std::map<std::string, void(PNGImage::*)(uint32_t chunkSize)> chunkReaders; @@ -60,6 +70,18 @@ namespace TehImage CHUNK_READER(IDAT); CHUNK_READER(IEND); + CHUNK_WRITER(IHDR); + CHUNK_WRITER(IDAT); + CHUNK_WRITER(IEND); + + + + bool readNextChunk(); + + uint32_t calculateCRC(uint8_t *buffer, std::size_t bufflen); + + const uint64_t PNG_CRC = 0x104C11DB7; + ZLib zlib; uint8_t* idatData; unsigned long idatDataSize; @@ -67,6 +89,7 @@ namespace TehImage bool end = false; Reader *reader; + Writer *writer; }; } diff --git a/src/files.cpp b/src/files.cpp index b757811..20f2672 100644 --- a/src/files.cpp +++ b/src/files.cpp @@ -6,6 +6,7 @@ #include <cstdio> #include <cstring> #include <iostream> +#include <stdexcept> using std::cout, std::endl; @@ -124,14 +125,17 @@ namespace TehImage void Writer::writeByte(char toWrite) { - fwrite(&toWrite, sizeof(char), 1, file); + if(pos == WRITER_BUFFER_SIZE) + throw std::out_of_range("Writer buffer ran out single-byte!!!"); + buffer[pos++] = toWrite; } void Writer::writeBytes(char toWrite[], std::size_t len) { - // for(int i = 0; i < len; i++) - // cout << toWrite[i] << endl; - fwrite(toWrite, sizeof(char), len, file); + if(pos + len > WRITER_BUFFER_SIZE) + throw std::out_of_range("Writer buffer ran out multi-byte!!!"); + memcpy(&(buffer[pos]), toWrite, len); + pos += len; } void Writer::zeroBytes(std::size_t len) @@ -145,10 +149,21 @@ namespace TehImage void Writer::close() { if(ready) + { + flushBuffer(); fclose(file); + } ready = false; } + void Writer::flushBuffer() + { + if(!ready) + throw std::logic_error("Cannot flush buffer before opening file"); + fwrite(buffer, sizeof(char), pos, file); + pos = 0; + } + template <> void Writer::writeData(uint8_t toWrite) { writeByte(toWrite); diff --git a/src/files.h b/src/files.h index fbbe8b7..ad65ce8 100644 --- a/src/files.h +++ b/src/files.h @@ -4,6 +4,7 @@ #include <string> #define READER_BUFFER_SIZE 4096 +#define WRITER_BUFFER_SIZE 4096 #define DEFINE_INT_READER(TYPE) \ template<> TYPE Reader::readData<TYPE>() \ @@ -78,8 +79,12 @@ namespace TehImage void zeroBytes(std::size_t len); void close(); + + void flushBuffer(); + + char buffer[WRITER_BUFFER_SIZE]; + std::size_t pos; private: - /* std::size_t pos; */ FILE* file; bool ready = false; FileEndianness fileEndianness; |
