diff --git a/CMakeLists.txt b/CMakeLists.txt index 4abc0ad..d7fb371 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,8 @@ include_directories( file(GLOB HQX_SOURCES "source/HQx.cc" "source/HQ2x.cc" - "source/HQ3x.cc") + "source/HQ3x.cc" + "source/bitmap.cc") add_library(hqx ${HQX_SOURCES}) set_target_properties(hqx PROPERTIES diff --git a/include/Bitmap.h b/include/Bitmap.h new file mode 100644 index 0000000..89212b3 --- /dev/null +++ b/include/Bitmap.h @@ -0,0 +1,97 @@ +#include +#include + +#pragma pack(push, 1) +struct BitmapHeader +{ + uint16_t bfType; + uint32_t bfSize; + uint32_t bfRes1; + uint32_t bfOffBits; +}; + +struct DibHeader +{ + uint32_t biSize; + uint32_t biWidth; + uint32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + uint32_t biXPelsPerMeter; + uint32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +}; + +typedef struct +{ + int32_t ciexyzX; + int32_t ciexyzY; + int32_t ciexyzZ; +} DibCIEXYZ; + +typedef struct +{ + DibCIEXYZ ciexyzRed; + DibCIEXYZ ciexyzGreen; + DibCIEXYZ ciexyzBlue; +} DibCIEXYZTRIPLE; + +typedef struct { + uint32_t bV5Size; + int32_t bV5Width; + int32_t bV5Height; + uint16_t bV5Planes; + uint16_t bV5BitCount; + uint32_t bV5Compression; + uint32_t bV5SizeImage; + int32_t bV5XPelsPerMeter; + int32_t bV5YPelsPerMeter; + uint32_t bV5ClrUsed; + uint32_t bV5ClrImportant; + uint32_t bV5RedMask; + uint32_t bV5GreenMask; + uint32_t bV5BlueMask; + uint32_t bV5AlphaMask; + uint32_t bV5CSType; + DibCIEXYZTRIPLE bV5Endpoints; + uint32_t bV5GammaRed; + uint32_t bV5GammaGreen; + uint32_t bV5GammaBlue; + uint32_t bV5Intent; + uint32_t bV5ProfileData; + uint32_t bV5ProfileSize; + uint32_t bV5Reserved; +} DibHeaderV5; +#pragma pack(pop) + + +class Bitmap { +public: + uint32_t* data; + uint32_t width; + uint32_t height; + uint16_t bitCount; + + Bitmap( + uint32_t* data, + uint32_t width, + uint32_t height, + uint16_t bitCount + ); + + Bitmap(std::istream& input); + + void save(std::ostream& output); + int load(std::istream& input); + +private: + void save24bit(std::ostream& output) const; + void save32bit(std::ostream& output) const; + int loadDibHeader(std::istream& input, const BitmapHeader& bh); + int loadV5Header(std::istream& input, const BitmapHeader& bh); + int read24bit(std::istream& input); + int read32bit(std::istream& input); +}; \ No newline at end of file diff --git a/source/HQ2x.cc b/source/HQ2x.cc index 5d0b4dc..e24ee2d 100644 --- a/source/HQ2x.cc +++ b/source/HQ2x.cc @@ -72,7 +72,7 @@ uint32_t *HQ2x::resize( // adjusts the previous and next line pointers if (row > 0) - previous = -width; + previous = -(int)width; else { if (wrapY) @@ -85,7 +85,7 @@ uint32_t *HQ2x::resize( else { if (wrapY) - next = -(width * (height - 1)); + next = -(int)(width * (height - 1)); else next = 0; } diff --git a/source/HQ3x.cc b/source/HQ3x.cc index c35a747..ea66d70 100644 --- a/source/HQ3x.cc +++ b/source/HQ3x.cc @@ -71,7 +71,7 @@ uint32_t *HQ3x::resize( // adjusts the previous and next line pointers if (row > 0) - previous = -width; + previous = -(int)width; else { if (wrapY) @@ -84,7 +84,7 @@ uint32_t *HQ3x::resize( else { if (wrapY) - next = -(width * (height - 1)); + next = -(int)(width * (height - 1)); else next = 0; } diff --git a/source/bitmap.cc b/source/bitmap.cc new file mode 100644 index 0000000..b87b891 --- /dev/null +++ b/source/bitmap.cc @@ -0,0 +1,204 @@ +#include "Bitmap.h" + +Bitmap::Bitmap( + uint32_t* data, + uint32_t width, + uint32_t height, + uint16_t bitCount +) + : data(data) + , width(width) + , height(height) + , bitCount(bitCount) +{ +} + +Bitmap::Bitmap(std::istream& input) { + load(input); +} + +void Bitmap::save(std::ostream& output) { + if (bitCount == 24) + save24bit(output); + if (bitCount == 32) + save32bit(output); +} + +void Bitmap::save24bit(std::ostream& output) const +{ + BitmapHeader bh; + DibHeader dh; + uint16_t suffix; + uint32_t zero = 0; + const uint32_t* ptr; + + suffix = (4 - (3 * width) % 4) % 4; + + dh.biSize = sizeof(DibHeader); + dh.biWidth = width; + dh.biHeight = height; + dh.biPlanes = 1; + dh.biBitCount = 24; + dh.biCompression = 0; + dh.biSizeImage = (uint32_t)((width * 3 + suffix) * height); + dh.biXPelsPerMeter = 0x2E23; + dh.biYPelsPerMeter = dh.biXPelsPerMeter; + dh.biClrUsed = 0; + dh.biClrImportant = 0; + + bh.bfType = 0x4D42; + bh.bfSize = dh.biSizeImage + 0x0036; + bh.bfRes1 = 0; + bh.bfOffBits = 0x0036; + output.write((char*)&bh, sizeof(BitmapHeader)); + output.write((char*)&dh, sizeof(DibHeader)); + + ptr = data + (width * height); + for (uint32_t i = 0; i < height; i++) + { + ptr -= width; + + for (uint32_t j = 0; j < width; ++j) + output.write((char*)(ptr + j), 3); + + if (suffix > 0) + output.write((char*)&zero, suffix); + } +} + +void Bitmap::save32bit(std::ostream& output) const +{ + BitmapHeader bh; + DibHeaderV5 dh = { 0 }; + uint32_t zero = 0; + const uint32_t* ptr; + + dh.bV5Size = sizeof(DibHeaderV5); + dh.bV5Width = width; + dh.bV5Height = height; + dh.bV5Planes = 1; + dh.bV5BitCount = 32; + dh.bV5Compression = 3; + dh.bV5SizeImage = 0; + dh.bV5XPelsPerMeter = 0x2E23; + dh.bV5YPelsPerMeter = dh.bV5XPelsPerMeter; + dh.bV5ClrUsed = 0; + dh.bV5ClrImportant = 0; + dh.bV5RedMask = 0x00ff0000; + dh.bV5GreenMask = 0x0000ff00; + dh.bV5BlueMask = 0x000000ff; + dh.bV5AlphaMask = 0xff000000; + dh.bV5CSType = 0x57696e20;// 'Win ' + + bh.bfType = 0x4D42; // 'BM' + bh.bfSize = width * height * 4 + sizeof(DibHeaderV5) + sizeof(BitmapHeader); + bh.bfRes1 = 0; + bh.bfOffBits = sizeof(DibHeaderV5) + sizeof(BitmapHeader); + output.write((char*)&bh, sizeof(BitmapHeader)); + output.write((char*)&dh, sizeof(DibHeaderV5)); + + ptr = data + (width * height); + for (uint32_t i = 0; i < height; i++) + { + ptr -= width; + + for (uint32_t j = 0; j < width; ++j) + output.write((char*)(ptr + j), 4); + } +} + +int Bitmap::load(std::istream& input) +{ + BitmapHeader bh; + input.read((char*)&bh, sizeof(BitmapHeader)); + if (bh.bfType != 0x4D42) return -1; + + uint32_t biSize; + input.read((char*)&biSize, sizeof(uint32_t)); + + if (biSize == sizeof(DibHeader)) + return loadDibHeader(input, bh); + + if (biSize == sizeof(DibHeaderV5)) + return loadV5Header(input, bh); + + return 0; +} + +int Bitmap::loadDibHeader(std::istream& input, const BitmapHeader& bh) +{ + DibHeader dh; + input.read((char*)&dh.biWidth, sizeof(DibHeader) - sizeof(uint32_t)); + width = dh.biWidth; + height = dh.biHeight; + bitCount = dh.biBitCount; + + input.seekg(bh.bfOffBits, std::ios_base::_Seekbeg); + if (dh.biBitCount == 24) + return read24bit(input); + + if (dh.biBitCount == 32) + return read32bit(input); + + return -1; +} + +int Bitmap::loadV5Header(std::istream& input, const BitmapHeader& bh) +{ + DibHeaderV5 dh; + input.read((char*)&dh.bV5Width, sizeof(DibHeaderV5) - sizeof(uint32_t)); + width = dh.bV5Width; + height = dh.bV5Height; + bitCount = dh.bV5BitCount; + + input.seekg(bh.bfOffBits, std::ios_base::_Seekbeg); + + if (dh.bV5BitCount == 24) + return read24bit(input); + + if (dh.bV5BitCount == 32) + return read32bit(input); + + return -1; +} + +int Bitmap::read24bit(std::istream& input) { + uint16_t suffix; + uint32_t* ptr; + uint32_t zero = 0; + + suffix = (4 - (3 * width) % 4) % 4; + ptr = data = new uint32_t[width * height](); + ptr += width * height; + for (uint32_t i = 0; i < height; i++) + { + ptr -= width; + + for (uint32_t j = 0; j < width; ++j) + { + input.read((char*)(ptr + j), 3); + *(ptr + j) |= 0xFF000000; + } + + if (suffix > 0) + input.read((char*)&zero, suffix); + } + return 0; +} + +int Bitmap::read32bit(std::istream& input) { + uint32_t* ptr; + ptr = data = new uint32_t[width * height](); + ptr += width * height; + for (uint32_t i = 0; i < height; i++) + { + ptr -= width; + + for (uint32_t j = 0; j < width; ++j) + { + input.read((char*)(ptr + j), 4); + } + } + + return 0; +} diff --git a/source/main.cc b/source/main.cc index 7b9322c..cf42dce 100644 --- a/source/main.cc +++ b/source/main.cc @@ -20,149 +20,14 @@ #include #include #include - +#include +#include "Bitmap.h" using std::ifstream; using std::ofstream; using std::string; -#pragma pack(push, 1) - -struct BitmapHeader -{ - uint16_t bfType; - uint32_t bfSize; - uint32_t bfRes1; - uint32_t bfOffBits; -}; - -struct DibHeader -{ - uint32_t biSize; - uint32_t biWidth; - uint32_t biHeight; - uint16_t biPlanes; - uint16_t biBitCount; - uint32_t biCompression; - uint32_t biSizeImage; - uint32_t biXPelsPerMeter; - uint32_t biYPelsPerMeter; - uint32_t biClrUsed; - uint32_t biClrImportant; -}; - -#pragma pack(pop) - - -/** - * @brief Saves an Windows Bitmap image (24 BPP). - */ -int main_saveBitmap( - const uint32_t *data, - uint32_t width, - uint32_t height, - const string &fileName ) -{ - BitmapHeader bh; - DibHeader dh; - uint16_t suffix; - uint32_t zero = 0; - const uint32_t *ptr; - - ofstream output(fileName.c_str(), std::ios_base::binary); - if (!output.good()) return -1; - - suffix = ((width + 3) & ~0x03) - width; - - dh.biSize = sizeof(DibHeader); - dh.biWidth = width; - dh.biHeight = height; - dh.biPlanes = 1; - dh.biBitCount = 24; - dh.biCompression = 0; - dh.biSizeImage = (uint16_t) ( (width*3+suffix)*height ); - dh.biXPelsPerMeter = 0x2E23; - dh.biYPelsPerMeter = dh.biXPelsPerMeter; - dh.biClrUsed = 0; - dh.biClrImportant = 0; - - bh.bfType = 0x4D42; - bh.bfSize = dh.biSizeImage + 0x0036; - bh.bfRes1 = 0; - bh.bfOffBits = 0x0036; - output.write( (char*) &bh, sizeof(BitmapHeader) ); - output.write( (char*) &dh, sizeof(DibHeader) ); - - ptr = data + (width * height); - for (uint32_t i = 0; i < height; i++) - { - ptr -= width; - - for (uint32_t j = 0; j < width; ++j) - output.write( (char*) (ptr + j), 3 ); - - if (suffix > 0) - output.write( (char*) &zero, suffix ); - } - - output.close(); - - return 0; -} - - -/** - * @brief Loads an Windows Bitmap image (24 BPP). - */ -int main_loadBitmap( - const string &fileName, - uint32_t *&data, - uint16_t &width, - uint16_t &height ) -{ - BitmapHeader bh; - DibHeader dh; - uint16_t suffix; - uint32_t zero = 0; - uint32_t *ptr; - uint16_t bits; - - ifstream input(fileName.c_str(), std::ios_base::binary); - if (!input.good()) return -1; - - input.read( (char*) &bh, sizeof(BitmapHeader) ); - if (bh.bfType != 0x4D42) return -1; - input.read( (char*) &dh.biSize, sizeof(uint32_t) ); - if (dh.biSize != 40) return -1; - - input.read( (char*) &dh.biWidth, sizeof(DibHeader) - sizeof(uint32_t) ); - width = dh.biWidth; - height = dh.biHeight; - if (dh.biBitCount != 24) return -1; - - suffix = ((width + 3) & ~0x03) - width; - ptr = data = new uint32_t[width * height](); - ptr += width * height; - for (uint32_t i = 0; i < height; i++) - { - ptr -= width; - - for (uint32_t j = 0; j < width; ++j) - { - input.read( (char*) (ptr + j), 3 ); - *(ptr + j) |= 0xFF000000; - } - - if (suffix > 0) - input.read( (char*) &zero, suffix ); - } - - input.close(); - return 0; -} - - int main(int argc, char **argv ) { uint32_t factor = 2; @@ -171,31 +36,42 @@ int main(int argc, char **argv ) if (argc == 3) factor = atoi(argv[2]); // loads the input image - uint16_t width, height; - uint32_t *image = NULL; - if ( main_loadBitmap(argv[1], image, width, height) != 0) return 1; - std::cout << "Resizing '" << argv[1] << "' [" << width << "x" << height << "] by " << + + ifstream input(argv[1], std::ios_base::binary); + if (!input.good()) { + std::cout << "File not found" << std::endl; + return -1; + } + Bitmap bitmap(input); + input.close(); + + std::cout << "Resizing '" << argv[1] << "' [" << bitmap.width << "x" << bitmap.height << "] by " << factor << 'x' << std::endl; clock_t t = clock(); // resize the input image using the given scale factor - uint32_t outputSize = (width * factor) * (height * factor); + uint32_t outputSize = (bitmap.width * factor) * (bitmap.height * factor); uint32_t *output = new uint32_t[outputSize](); HQx *scale; if (factor == 2) scale = new HQ2x(); else scale = new HQ3x(); - scale->resize(image, width, height, output); + scale->resize(bitmap.data, bitmap.width, bitmap.height, output); delete scale; t = clock() - t; std::cout << "Processing time: " << t / (CLOCKS_PER_SEC / 1000) << " ms" << std::endl; // saves the resized image - if ( main_saveBitmap(output, width * factor, height * factor, "output.bmp") != 0 ) return 1; + ofstream outputFile(argv[1] + std::string(".") + std::to_string(factor) + "x.bmp", std::ios_base::binary); + if (!outputFile.good()) return -1; + + Bitmap bitmapOut(output, bitmap.width * factor, bitmap.height * factor, bitmap.bitCount); + bitmapOut.save(outputFile); + outputFile.close(); - delete[] image; + delete[] bitmap.data; delete[] output; }