From 078b4e08fe3bccb7424dac76e158bf8bf48a182d Mon Sep 17 00:00:00 2001 From: BossCode45 Date: Fri, 27 Jun 2025 17:58:36 +1200 Subject: feat: Made it so that you can now convert between image subclasses Also added the bitmap image subclass note: this is pretty hacky in how it works --- src/BMPImage.cpp | 49 ++++++++++++++++++ src/BMPImage.h | 15 ++++++ src/PNGImage.cpp | 148 +++++++++++++++++++++++++------------------------------ src/PNGImage.h | 12 +++-- src/image.cpp | 3 +- src/image.h | 43 ++++++++++++++-- 6 files changed, 180 insertions(+), 90 deletions(-) create mode 100644 src/BMPImage.cpp create mode 100644 src/BMPImage.h (limited to 'src') diff --git a/src/BMPImage.cpp b/src/BMPImage.cpp new file mode 100644 index 0000000..71e5317 --- /dev/null +++ b/src/BMPImage.cpp @@ -0,0 +1,49 @@ +#include "BMPImage.h" + +#include + +using std::cout, std::endl; + +int BMPImage::readFromFile(std::string filename) +{ + cout << "Not implemented" << endl; + return 2; +}; + +int BMPImage::writeToFile(std::string filename) +{ + FILE* fd = fopen(filename.c_str(), "w"); + char magic[] = "BM"; + fwrite(magic, sizeof(char), 2, fd); + uint32_t fileSize = 14 + 12 + width*height*/*(bitDepth/8)*/8*3; + fwrite(&fileSize, sizeof(uint32_t), 1, fd); + char zero[] = "\0\0\0\0"; + fwrite(zero, sizeof(char), 4, fd); + uint32_t offset = 26; + fwrite(&offset, sizeof(uint32_t), 1, fd); + uint32_t headerSize = 12; + fwrite(&headerSize, sizeof(uint32_t), 1, fd); + uint16_t width = this->width; + uint16_t height = this->height; + uint16_t colorPlanes = 1; + uint16_t bitsPerPixel = /*bitDepth*/8*3; + fwrite(&width, sizeof(uint16_t), 1, fd); + fwrite(&height, sizeof(uint16_t), 1, fd); + fwrite(&colorPlanes, sizeof(uint16_t), 1, fd); + fwrite(&bitsPerPixel, sizeof(uint16_t), 1, fd); + + for(int y = height-1; y >= 0; y--) + { + for(int x = 0; x < width; x++) + { + Pixel pixel = getPixel(x, y); + fwrite(&pixel.b, bitDepth/8, 1, fd); + fwrite(&pixel.g, bitDepth/8, 1, fd); + fwrite(&pixel.r, bitDepth/8, 1, fd); + } + } + + fclose(fd); + + return 0; +}; diff --git a/src/BMPImage.h b/src/BMPImage.h new file mode 100644 index 0000000..697c72d --- /dev/null +++ b/src/BMPImage.h @@ -0,0 +1,15 @@ +#pragma once + +#include "image.h" +#include +#include + +class BMPImage : public Image +{ +private: +public: + template T> BMPImage(const T& other) : Image(other) { } + + int readFromFile(std::string filename) override; + int writeToFile(std::string filename) override; +}; diff --git a/src/PNGImage.cpp b/src/PNGImage.cpp index b7f28ce..50df7c6 100644 --- a/src/PNGImage.cpp +++ b/src/PNGImage.cpp @@ -14,9 +14,7 @@ using std::cout, std::endl; -PNGImage::PNGImage(std::string filename) - :reader(filename) - ,idatData() +PNGImage::PNGImage() { //cout << "Reader good" << endl; REGISTER_CHUNK_READER(IHDR); @@ -32,11 +30,6 @@ PNGImage::PNGImage(std::string filename) //cout << "Chunk readers loaded" << endl; - char signature[8]; - uint8_t expected[] = {137, 80, 78, 71, 13, 10, 26, 10}; - reader.readBytes(signature, 8); - if(strncmp(signature, (char*)expected, 8) != 0) - cout << "UH OH" << endl; idatData = nullptr; idatDataSize = 0; @@ -50,14 +43,39 @@ PNGImage::~PNGImage() idatDataSize = 0; } +int PNGImage::readFromFile(std::string filename) +{ + std::unique_ptr readerMem(new Reader(filename)); + reader = readerMem.get(); + + char signature[8]; + uint8_t expected[] = {137, 80, 78, 71, 13, 10, 26, 10}; + reader->readBytes(signature, 8); + if(strncmp(signature, (char*)expected, 8) != 0) + { + cout << "Not a PNG" << endl; + return 1; + } + + while (readNextChunk()) {} + + return 0; +}; + +int PNGImage::writeToFile(std::string filename) +{ + cout << "Not implemented" << endl; + return 2; +}; + bool PNGImage::readNextChunk() { if(end) return false; - uint32_t chunkSize = reader.readData(); + uint32_t chunkSize = reader->readData(); char chunkType[4]; - reader.readBytes(chunkType, 4); + reader->readBytes(chunkType, 4); std::string chunkName(chunkType, 4); cout << "-------------" << endl; cout << "|Chunk: " << chunkName << "|" << endl; @@ -66,7 +84,7 @@ bool PNGImage::readNextChunk() if(chunkReaders.count(chunkName) == 0) { cout << "Chunk reader not found!!!" << endl; - reader.skipBytes(chunkSize + 4); + reader->skipBytes(chunkSize + 4); if(islower(chunkType[0])) { cout << "\tAble to skip chunk" << endl; @@ -79,20 +97,20 @@ bool PNGImage::readNextChunk() void(PNGImage::*chunkReader)(uint32_t chunkSize) = chunkReaders.find(chunkName)->second; (this->*chunkReader)(chunkSize); - reader.skipBytes(4); // CRC + reader->skipBytes(4); // CRC return true; } DEFINE_CHUNK_READER(IHDR) { - width = reader.readData(); - height = reader.readData(); - bitDepth = reader.readData(); - colorType = reader.readData(); - compressionMethod = reader.readData(); - filterMethod = reader.readData(); - interlaceMethod = reader.readData(); + width = reader->readData(); + height = reader->readData(); + bitDepth = reader->readData(); + colorType = reader->readData(); + compressionMethod = reader->readData(); + filterMethod = reader->readData(); + interlaceMethod = reader->readData(); cout << "Width: " << width << ", Height: " << height << ", Bit depth: " << 0+bitDepth << ", Color type: " << 0+colorType << ", Compression method: " << 0+compressionMethod << ", Filter method: " << 0+filterMethod << ", Interlace method: " << 0+interlaceMethod << endl; if(colorType != 2 && colorType != 6) @@ -108,6 +126,7 @@ DEFINE_CHUNK_READER(IHDR) bpp = colorValues * (bitDepth/8); unsigned long imageDataSize = width * height * bpp; + //imageDataSize = imageDataSize; cout << "Assigning " << imageDataSize << " bytes for image" << endl; @@ -127,48 +146,48 @@ DEFINE_CHUNK_READER(iCCP) { cout << "!!! iCCP chunk reader not finished !!!" << endl; std::string profileName; - char c = reader.readByte(); + char c = reader->readByte(); chunkSize--; while(c != 0) { profileName.push_back(c); - c = reader.readByte(); + c = reader->readByte(); chunkSize--; } cout << profileName << endl; - uint8_t compresssionMethod = reader.readByte(); + uint8_t compresssionMethod = reader->readByte(); chunkSize--; cout << 0+compresssionMethod << endl; - uint8_t CMF = reader.readByte(); + uint8_t CMF = reader->readByte(); uint8_t CM = CMF & 0b00001111; uint8_t CINFO = (CMF & 0b11110000) >> 4; chunkSize--; - uint8_t FLG = reader.readByte(); + uint8_t FLG = reader->readByte(); bool check = (CMF * 256 + FLG)%31 == 0; bool FDICT = FLG & 0b00100000; uint8_t FLEVEL = FLG & 0b11000000; chunkSize--; cout << std::bitset<4>(CM) << ", " << std::bitset<4>(CINFO) << ", " << (check?"Valid":"Failed checksum") << ", " << (FDICT?"Dict is present":"No dict present") << ", " << std::bitset<2>(FLEVEL) << endl; char compressedData[chunkSize - 4]; - reader.readBytes(compressedData, chunkSize - 4); + reader->readBytes(compressedData, chunkSize - 4); const int compressedSize = chunkSize - 4; - uint32_t checkValue = reader.readData(); + uint32_t checkValue = reader->readData(); //end = true; } DEFINE_CHUNK_READER(sRGB) { - renderingIntent = reader.readData(); + renderingIntent = reader->readData(); cout << "Rendering intent: " << 0+renderingIntent << endl; } DEFINE_CHUNK_READER(eXIf) { char endian[4]; - reader.readBytes(endian, 4); + reader->readBytes(endian, 4); for(int i = 0; i < 2; i++) { cout << endian[i]; @@ -179,7 +198,7 @@ DEFINE_CHUNK_READER(eXIf) } cout << endl; char rest[chunkSize - 4]; - reader.readBytes(rest, chunkSize - 4); + reader->readBytes(rest, chunkSize - 4); cout << std::hex; for(int i = 0; i < chunkSize - 4; i++) { @@ -191,25 +210,25 @@ DEFINE_CHUNK_READER(eXIf) DEFINE_CHUNK_READER(iDOT) { cout << "!!! Ignoring iDOT !!!" << endl; - reader.skipBytes(chunkSize); + reader->skipBytes(chunkSize); } DEFINE_CHUNK_READER(pHYs) { - pixelsPerX = reader.readData(); - pixelsPerY = reader.readData(); - unit = reader.readData(); + pixelsPerX = reader->readData(); + pixelsPerY = reader->readData(); + unit = reader->readData(); cout << "Pixels per unit (x): " << pixelsPerX << ", Pixels per unit (y): " << pixelsPerY << ", unit: " << 0+unit << endl; } DEFINE_CHUNK_READER(tIME) { - year = reader.readData(); - month = reader.readData(); - day = reader.readData(); - hour = reader.readData(); - minute = reader.readData(); - second = reader.readData(); + year = reader->readData(); + month = reader->readData(); + day = reader->readData(); + hour = reader->readData(); + minute = reader->readData(); + second = reader->readData(); cout << "Image last modified: " << 0+hour << ":" << 0+minute << ":" << 0+second << " " << 0+day << "-" << 0+month << "-" << 0+year << endl; } @@ -217,22 +236,22 @@ DEFINE_CHUNK_READER(tEXt) { std::string keyword; - char c = reader.readByte(); + char c = reader->readByte(); chunkSize--; while(c != 0) { keyword.push_back(c); - c = reader.readByte(); + c = reader->readByte(); chunkSize--; } cout << keyword << endl; std::string textString; - c = reader.readByte(); + c = reader->readByte(); chunkSize--; while(chunkSize > 0) { textString.push_back(c); - c = reader.readByte(); + c = reader->readByte(); chunkSize--; } textString.push_back(c); @@ -243,11 +262,11 @@ DEFINE_CHUNK_READER(IDAT) { if(idatDataSize == 0) { - uint8_t CMF = reader.readByte(); + uint8_t CMF = reader->readByte(); uint8_t CM = (CMF & 0b11110000) >> 4; uint8_t CINFO = CMF & 0b00001111; chunkSize--; - uint8_t FLG = reader.readByte(); + uint8_t FLG = reader->readByte(); bool check = (CMF * 256 + FLG)%31 == 0; bool FDICT = FLG & 0b00000100; uint8_t FLEVEL = FLG & 0b00000011; @@ -257,7 +276,7 @@ DEFINE_CHUNK_READER(IDAT) } idatData = (uint8_t *)realloc(idatData, idatDataSize + chunkSize); - reader.readBytes((char *)&idatData[idatDataSize], chunkSize); + reader->readBytes((char *)&idatData[idatDataSize], chunkSize); idatDataSize += chunkSize; /* @@ -268,7 +287,7 @@ DEFINE_CHUNK_READER(IDAT) //cout << (int)puff((unsigned char*)imageData, &imageDataSize, (const unsigned char*)compressedData, &compressedSize) << endl; */ - //uint32_t checkValue = reader.readData(); + //uint32_t checkValue = reader->readData(); //end = true; } @@ -293,28 +312,7 @@ DEFINE_CHUNK_READER(IEND) uint8_t* pngImageData = new uint8_t[imageDataSize]; cout << "My inflate " << zlib.decodeData(idatData, idatDataSize, pngImageData, imageDataSize) << endl; end = true; - reader.close(); - - - FILE* fd = fopen("tmp.bmp", "w"); - char magic[] = "BM"; - fwrite(magic, sizeof(char), 2, fd); - uint32_t fileSize = 14 + 12 + width*height*/*(bitDepth/8)*/8*3; - fwrite(&fileSize, sizeof(uint32_t), 1, fd); - char zero[] = "\0\0\0\0"; - fwrite(zero, sizeof(char), 4, fd); - uint32_t offset = 26; - fwrite(&offset, sizeof(uint32_t), 1, fd); - uint32_t headerSize = 12; - fwrite(&headerSize, sizeof(uint32_t), 1, fd); - uint16_t width = this->width; - uint16_t height = this->height; - uint16_t colorPlanes = 1; - uint16_t bitsPerPixel = /*bitDepth*/8*3; - fwrite(&width, sizeof(uint16_t), 1, fd); - fwrite(&height, sizeof(uint16_t), 1, fd); - fwrite(&colorPlanes, sizeof(uint16_t), 1, fd); - fwrite(&bitsPerPixel, sizeof(uint16_t), 1, fd); + reader->close(); #define imageDataIndex(x, y) imageData[y*width*bpp + x] #define pngImageDataIndex(x, y) pngImageData[y*(width*bpp + 1) + x + 1] @@ -361,17 +359,5 @@ DEFINE_CHUNK_READER(IEND) #undef pngImageDataIndex #undef filterByte - for(int y = height-1; y >= 0; y--) - { - for(int x = 0; x < width; x++) - { - Pixel pixel = getPixel(x, y); - fwrite(&pixel.b, bitDepth/8, 1, fd); - fwrite(&pixel.g, bitDepth/8, 1, fd); - fwrite(&pixel.r, bitDepth/8, 1, fd); - } - } - delete [] pngImageData; - fclose(fd); } diff --git a/src/PNGImage.h b/src/PNGImage.h index 23c32a0..e937212 100644 --- a/src/PNGImage.h +++ b/src/PNGImage.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -13,15 +14,20 @@ #define REGISTER_CHUNK_READER(X) chunkReaders.insert({#X, &PNGImage::X}) #define DEFINE_CHUNK_READER(X) void PNGImage::X(uint32_t chunkSize) -class PNGImage : Image +class PNGImage : public Image { private: ZLibInflator zlib; uint8_t* idatData; unsigned long idatDataSize; public: - PNGImage(std::string filename); + PNGImage(); ~PNGImage(); + + template T> PNGImage(const T& other) : Image(other) { } + + int readFromFile(std::string filename) override; + int writeToFile(std::string filename) override; // sRGB uint8_t renderingIntent; @@ -56,5 +62,5 @@ private: bool end = false; - Reader reader; + Reader *reader; }; diff --git a/src/image.cpp b/src/image.cpp index 35941e5..f819b94 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -2,9 +2,8 @@ Image::~Image() { - if(width != 0 && height != 0) + if(width != 0) { delete[] imageData; } } - diff --git a/src/image.h b/src/image.h index f4071ea..52f4279 100644 --- a/src/image.h +++ b/src/image.h @@ -1,7 +1,8 @@ #pragma once -#include #include +#include +#include template struct Pixel @@ -9,6 +10,14 @@ struct Pixel T r, g, b, a; }; +struct ImageData +{ + + + + + //unsigned long imageDataSize = 0; +}; class Image { @@ -19,7 +28,16 @@ protected: public: Image() = default; ~Image(); + + template T> + Image(const T& other); + + virtual int readFromFile(std::string filename) = 0; + virtual int writeToFile(std::string filename) = 0; + template + Pixel getPixel(unsigned int x, unsigned int y); + uint32_t width = 0; uint32_t height = 0; uint8_t bitDepth; @@ -27,12 +45,29 @@ public: uint8_t compressionMethod; uint8_t filterMethod; uint8_t interlaceMethod; - - template - Pixel getPixel(unsigned int x, unsigned int y); }; + +template T> Image::Image(const T& other) +{ + this->colorValues = other.colorValues; + this->bpp = other.bpp; + + this->width = other.width; + this->height = other.height; + this->bitDepth = other.bitDepth; + this->colorType = other.colorType; + this->compressionMethod = other.compressionMethod; + this->filterMethod = other.filterMethod; + this->interlaceMethod = other.interlaceMethod; + + unsigned long imageDataSize = width * height * bpp; + imageData = new uint8_t[imageDataSize]; + mempcpy(imageData, other.imageData, imageDataSize); +} + + template Pixel Image::getPixel(unsigned int x, unsigned int y) { -- cgit v1.2.3