aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan <boss@tehbox.org>2026-05-06 15:55:59 +1200
committerDylan <boss@tehbox.org>2026-05-06 15:55:59 +1200
commit85cd1ecf29c3a295536ccc3782c2415d7518aef0 (patch)
tree2725c2b022a081e57e9dca90ef1fb309ff379337
parent4d5d07beeef8ae4b7daf602bf66216ed8ff81442 (diff)
downloadtehimage-85cd1ecf29c3a295536ccc3782c2415d7518aef0.tar.gz
tehimage-85cd1ecf29c3a295536ccc3782c2415d7518aef0.zip
feat: Implemented PNG image writingPNG-writing
It finally works now Definitely some clean up to be done
-rw-r--r--src/PNGImage.cpp94
-rw-r--r--src/debug.h2
-rw-r--r--src/zlib.cpp40
-rw-r--r--test/main.cpp17
4 files changed, 107 insertions, 46 deletions
diff --git a/src/PNGImage.cpp b/src/PNGImage.cpp
index 1e46f22..22a6218 100644
--- a/src/PNGImage.cpp
+++ b/src/PNGImage.cpp
@@ -77,7 +77,10 @@ namespace TehImage
writer->writeBytes((char*)signature, 8);
writeIHDR();
+ writeIDAT();
writeIEND();
+
+ writer->close();
return 2;
};
@@ -88,18 +91,21 @@ namespace TehImage
return false;
uint32_t chunkSize = reader->readData<uint32_t>();
- char chunkType[4];
+ char chunkType[5];
reader->readBytes(chunkType, 4);
+ chunkType[4] = 0;
std::string chunkName(chunkType, 4);
- debug(
- cout << "-------------" << endl;
- cout << "|Chunk: " << chunkName << "|" << endl;
- cout << "-------------" << endl;
- );
+ cout << "-------------" << endl;
+ cout << "|Chunk: " << chunkName << "|" << endl;
+ cout << "-------------" << endl;
if(chunkReaders.count(chunkName) == 0)
{
- cout << "Chunk reader not found!!!" << endl;
+ cout << "Chunk reader not found!!! " << chunkName << endl;
+ // for(int i = 0; i < 4; i++)
+ // cout << 0+chunkType[i] << ' ';
+ // cout << endl;
+
reader->skipBytes(chunkSize + 4);
if(islower(chunkType[0]))
{
@@ -150,15 +156,6 @@ namespace TehImage
// cout << "Assigning " << imageDataSize << " bytes for image" << endl;
pixels = std::make_unique<Pixel[]>(width * height);
-
-/*
- Scanline<RGBPixel<uint8_t>>* lines = new Scanline<RGBPixel<uint8_t>> [height];
- for(int i = 0; i < height; i++)
- {
- lines[i].pixels = new RGBPixel<uint8_t>[width];
- }
- imageData = (uint8_t*)lines;
-*/
}
DEFINE_CHUNK_READER(iCCP)
@@ -172,7 +169,7 @@ namespace TehImage
c = reader->readByte();
chunkSize--;
}
- cout << profileName << endl;
+ // cout << profileName << endl;
uint8_t compresssionMethod = reader->readByte();
chunkSize--;
cout << 0+compresssionMethod << endl;
@@ -187,7 +184,7 @@ namespace TehImage
chunkSize--;
if(CM != 8)
cout << "Invalid CM: " << 0+CM << endl;
- cout << 0+CM << ", " << 0+CINFO << ", " << (check?"Valid":"Failed checksum") << ", " << (FDICT?"Dict is present":"No dict present") << ", " << 0+FLEVEL << endl;
+ // cout << 0+CM << ", " << 0+CINFO << ", " << (check?"Valid":"Failed checksum") << ", " << (FDICT?"Dict is present":"No dict present") << ", " << 0+FLEVEL << endl;
char compressedData[chunkSize - 4];
reader->readBytes(compressedData, chunkSize - 4);
@@ -215,21 +212,21 @@ namespace TehImage
reader->readBytes(endian, 4);
for(int i = 0; i < 2; i++)
{
- cout << endian[i];
+ // cout << endian[i];
}
for(int i = 2; i < 4; i++)
{
- cout << " " << 0+endian[i];
+ // cout << " " << 0+endian[i];
}
- cout << endl;
+ // cout << endl;
char rest[chunkSize - 4];
reader->readBytes(rest, chunkSize - 4);
- cout << std::hex;
+ // cout << std::hex;
for(int i = 0; i < chunkSize - 4; i++)
{
- cout << 0+rest[i] << " ";
+ // cout << 0+rest[i] << " ";
}
- cout << std::dec << endl;
+ // cout << std::dec << endl;
}
DEFINE_CHUNK_READER(iDOT)
@@ -299,6 +296,7 @@ namespace TehImage
if(CM != 8)
cout << "Invalid CM: " << 0+CM << endl;
cout << 0+CM << ", " << 0+CINFO << ", " << (check?"Valid":"Failed checksum") << ", " << (FDICT?"Dict is present":"No dict present") << ", " << 0+FLEVEL << endl;
+ // cout << 0 + CMF << " " << 0 + FLG << endl;
idatData = (uint8_t*)malloc(0);
}
@@ -453,25 +451,66 @@ namespace TehImage
writer->writeData(width);
writer->writeData(height);
- writer->writeData(bpp);
+ writer->writeData<uint8_t>(8); // 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;
+ unsigned long imageDataSize = height * (width * 4 + 1);
+ uint8_t* pngImageData = new uint8_t[imageDataSize];
+
+ for(int y = 0; y < height; y++)
+ {
+ pngImageData[y*(width*4 + 1)] = 0;
+ for(int x = 0; x < width; x++)
+ {
+ pngImageData[y*(width*4 + 1) + 1 + x*4 + 0] = (*this)[x, y].r;
+ pngImageData[y*(width*4 + 1) + 1 + x*4 + 1] = (*this)[x, y].g;
+ pngImageData[y*(width*4 + 1) + 1 + x*4 + 2] = (*this)[x, y].b;
+ pngImageData[y*(width*4 + 1) + 1 + x*4 + 3] = (*this)[x, y].a;
+ }
+ }
+
+ ZLib encoder;
+ uint8_t compressed[WRITER_BUFFER_SIZE];
+
+ size_t encodedSize = encoder.encodeData(pngImageData, imageDataSize, compressed, WRITER_BUFFER_SIZE);
+
+ // uint8_t *test = new uint8_t[imageDataSize];
+ // ZLib decoder;
+ // size_t decodeSize = decoder.decodeData(compressed, encodedSize, test, imageDataSize);
+ // if(decodeSize != imageDataSize)
+ // cout << "Wrong size" << endl;
+ // if(memcmp(pngImageData, test, imageDataSize) != 0)
+ // cout << "Wrong data" << endl;
+
+ // encoder.decodeData(compressed, encodedSize, pngImageData, imageDataSize);
+
+ uint32_t chunkSize = encodedSize + 2;
+
+ delete[] pngImageData;
writer->writeData(chunkSize);
writer->flushBuffer();
char header[] = "IDAT";
writer->writeBytes(header, 4);
+
+ writer->writeByte(120);
+ writer->writeByte(1);
+
+ writer->writeBytes((char*)compressed, encodedSize);
+
+ // writer->zeroBytes(4);
+
+ uint32_t CRC = calculateCRC((uint8_t*)writer->buffer, writer->pos);
+ writer->writeData(CRC);
}
DEFINE_CHUNK_WRITER(IEND)
@@ -484,7 +523,6 @@ namespace TehImage
writer->writeBytes(header, 4);
uint32_t CRC = calculateCRC((uint8_t*)writer->buffer, writer->pos);
- // cout << CRC << endl;
writer->writeData(CRC);
}
}
diff --git a/src/debug.h b/src/debug.h
index 907498b..75726a0 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -1,7 +1,7 @@
#pragma once
// Toggles global debug
-// #define ENABLE_DEBUG
+/* #define ENABLE_DEBUG */
#ifdef ENABLE_DEBUG
#define debug(x) (x)
diff --git a/src/zlib.cpp b/src/zlib.cpp
index 022b2fc..221137c 100644
--- a/src/zlib.cpp
+++ b/src/zlib.cpp
@@ -5,6 +5,7 @@
#include <bitset>
#include <cstdint>
#include <cstring>
+#include <exception>
#include <iomanip>
#include <iostream>
#include <stdexcept>
@@ -274,7 +275,6 @@ namespace TehImage
long byte = stream->pos / 8;
if(byte >= stream->length)
{
- cout << byte << " " << stream->length << endl;
throw std::out_of_range("Ran out of compressed data");
}
stream->pos++;
@@ -334,7 +334,6 @@ namespace TehImage
bool final;
do
{
-
final = nextBit(&stream);
CompressionType method = (CompressionType)nextBits(&stream, 2);
@@ -368,6 +367,8 @@ namespace TehImage
do
{
code = getNextCode(&stream);
+
+ // cout << 'a' << endl;
if(outPos > outLength && code != 256)
{
throw std::out_of_range("No more space left in image (normal)");
@@ -376,10 +377,14 @@ namespace TehImage
out[outPos++] = (uint8_t)code;
else if(code > 256)
{
+ // cout << 'b' << endl;
unsigned int len = lenStart[code-257] + (int)nextBits(&stream, lenExtra[code-257]);
+ // cout << 'c' << endl;
unsigned int distCode = getNextCode(&stream, &distTree);
+ // cout << 'd' << endl;
unsigned int dist = distStart[distCode] + (int)nextBits(&stream, distExtra[distCode]);
+ // cout << 'e' << endl;
if(outPos + len > outLength)
{
throw std::out_of_range("No more space left in image (RLE error)");
@@ -389,7 +394,9 @@ namespace TehImage
out[outPos] = out[outPos - dist];
outPos++;
}
+ // cout << 'f' << endl;
}
+ // cout << 'z' << endl;
}
while(code != 256);
}
@@ -440,7 +447,7 @@ namespace TehImage
getStaticHuffmanCodes(codes, codeLens, distCodes, distCodeLens);
// cout << inPos << " " << length << endl;
- HashTable hashTable(255);
+ HashTable hashTable(256);
while(inPos < length)
{
if(inPos + 2 >= length)
@@ -451,22 +458,26 @@ namespace TehImage
}
char curr[3];
- memcpy(curr, &data[inPos], 3);
- if(hashTable.contains(curr))
+ memcpy(curr, &(data[inPos]), 3);
+ if(hashTable.contains(curr) && inPos - hashTable.get(curr) < 32768)
{
unsigned long pos = hashTable.get(curr);
int length = 3;
- while(data[pos + length] == data[inPos + length])
+ while(data[pos + length] == data[inPos + length]
+ && pos + length < inPos && length < 258)
length++;
+ // length--;
-
+ bool lFound = false, dFound = false;
uint16_t lenCode, lenExtraBits;
- for(uint16_t i = 0; i < 30; i++)
+ for(uint16_t i = 0; i < 29; i++)
{
if(lenStart[i] <= length && lenStart[i] + (0b1 << lenExtra[i]) - 1 >= length)
{
lenCode = i;
lenExtraBits = length - lenStart[i];
+ lFound = true;
+ break;
}
}
unsigned long dist = inPos - pos;
@@ -476,10 +487,19 @@ namespace TehImage
if(distStart[i] <= dist && distStart[i] + (0b1 << distExtra[i]) - 1 >= dist)
{
distCode = i;
- distExtraBits = dist - distCodes[i];
+ distExtraBits = dist - distStart[i];
+ dFound = true;
+ break;
}
}
+ if(!(lFound && dFound))
+ {
+ cout << ((lFound)?'y':'n') << " " << ((dFound)?'y':'n') << endl;
+ cout << length << " " << dist << endl;
+ throw std::runtime_error("UH OH");
+ }
+
writeCode(&outStream, codes[lenCode + 257], codeLens[lenCode + 257]);
writeBits(&outStream, lenExtra[lenCode], lenExtraBits);
writeCode(&outStream, distCodes[distCode], distCodeLens[distCode]);
@@ -494,6 +514,8 @@ namespace TehImage
inPos++;
}
}
+
+ writeCode(&outStream, codes[256], codeLens[256]);
return (outStream.pos / 8) + ((outStream.pos % 8 == 0)?0:1);
}
diff --git a/test/main.cpp b/test/main.cpp
index 5a2525b..b7ae2ea 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -42,9 +42,9 @@ int main(int argc, char* argv[])
// return 0;
- if(argc < 3)
+ if(argc < 4)
{
- cout << "usage: " << argv[0] << " <in file> <out file>" << endl;
+ cout << "usage: " << argv[0] << " <in file> <tmpfile> <outfile>" << endl;
return 1;
}
@@ -83,18 +83,19 @@ int main(int argc, char* argv[])
// return 0;
std::string infile = argv[1];
- std::string outfile = argv[2];
+ std::string tmpfile = argv[2];
+ std::string outfile = argv[3];
TehImage::BMPImage bmp;
bmp.readFromFile(infile);
TehImage::PNGImage png(bmp);
- png.writeToFile(outfile);
+ png.writeToFile(tmpfile);
- // TehImage::PNGImage png;
- // png.readFromFile(infile);
+ TehImage::PNGImage png2;
+ png2.readFromFile(tmpfile);
- // TehImage::BMPImage bmp(png);
- // bmp.writeToFile(outfile);
+ TehImage::BMPImage bmp2(png2);
+ bmp.writeToFile(outfile);
}