From 80718cf0480f64684b7a7ab26baccfd1f21584e9 Mon Sep 17 00:00:00 2001 From: barrrguzin Date: Sun, 28 Jul 2024 15:05:48 +0300 Subject: [PATCH] Added BMP image handling, the same as in hyprpaper --- src/helpers/Bmp.cpp | 122 +++++++++++++++++++++++++ src/helpers/Bmp.hpp | 15 +++ src/renderer/AsyncResourceGatherer.cpp | 5 + 3 files changed, 142 insertions(+) create mode 100644 src/helpers/Bmp.cpp create mode 100644 src/helpers/Bmp.hpp diff --git a/src/helpers/Bmp.cpp b/src/helpers/Bmp.cpp new file mode 100644 index 0000000..df69a65 --- /dev/null +++ b/src/helpers/Bmp.cpp @@ -0,0 +1,122 @@ +#include "Bmp.hpp" + +class BmpHeader { +public: + unsigned char format[2]; + uint32_t sizeOfFile; + uint16_t reserved1; + uint16_t reserved2; + uint32_t dataOffset; + uint32_t sizeOfBitmapHeader; + uint32_t width; + uint32_t height; + uint16_t numberOfColors; + uint16_t numberOfBitPerPixel; + uint32_t compressionMethod; + uint32_t imageSize; + uint32_t horizontalResolutionPPM; + uint32_t verticalResolutionPPM; + uint32_t numberOfCollors; + uint32_t numberOfImportantCollors; + + BmpHeader(std::ifstream& file) { + file.seekg(0, std::ios::end); + uint32_t streamLength = file.tellg(); + file.seekg(0, std::ios::beg); + + file.read(reinterpret_cast(&format), sizeof(format)); + if (!(format[0] == 66 && format[1] == 77)) { + Debug::log(ERR, "Unable to parse bitmap header: wrong bmp file type"); + exit(1); + } + + file.read(reinterpret_cast(&sizeOfFile), sizeof(sizeOfFile)); + + if (sizeOfFile != streamLength) { + Debug::log(ERR, "Unable to parse bitmap header: wrong value of file size header"); + exit(1); + } + + file.read(reinterpret_cast(&reserved1), sizeof(reserved1)); + file.read(reinterpret_cast(&reserved2), sizeof(reserved2)); + file.read(reinterpret_cast(&dataOffset), sizeof(dataOffset)); + file.read(reinterpret_cast(&sizeOfBitmapHeader), sizeof(sizeOfBitmapHeader)); + file.read(reinterpret_cast(&width), sizeof(width)); + file.read(reinterpret_cast(&height), sizeof(height)); + file.read(reinterpret_cast(&numberOfColors), sizeof(numberOfColors)); + file.read(reinterpret_cast(&numberOfBitPerPixel), sizeof(numberOfBitPerPixel)); + file.read(reinterpret_cast(&compressionMethod), sizeof(compressionMethod)); + file.read(reinterpret_cast(&imageSize), sizeof(imageSize)); + file.read(reinterpret_cast(&horizontalResolutionPPM), sizeof(horizontalResolutionPPM)); + file.read(reinterpret_cast(&verticalResolutionPPM), sizeof(verticalResolutionPPM)); + file.read(reinterpret_cast(&numberOfCollors), sizeof(numberOfCollors)); + file.read(reinterpret_cast(&numberOfImportantCollors), sizeof(numberOfImportantCollors)); + + if (!imageSize) + imageSize = sizeOfFile - dataOffset; + + if (imageSize != (width * height * numberOfBitPerPixel/8)) { + Debug::log(ERR, "Unable to parse bitmap header: wrong image size"); + exit(1); + } + + file.seekg(dataOffset); + }; +}; + +void reflectImage(unsigned char* image, uint32_t numberOfRows, int stride) { + int rowStart = 0; + int rowEnd = numberOfRows - 1; + std::vector temp; + temp.resize(stride); + while (rowStart < rowEnd) { + memcpy(&temp[0], &image[rowStart * stride], stride); + memcpy(&image[rowStart * stride], &image[rowEnd * stride], stride); + memcpy(&image[rowEnd * stride], &temp[0], stride); + rowStart++; + rowEnd--; + } +}; + +void convertRgbToArgb(std::ifstream& imageStream, unsigned char* outputImage, uint32_t newImageSize) { + uint8_t forthBitCounter = 0; + unsigned long imgCursor = 0; + while (imgCursor < newImageSize) { + imageStream.read(reinterpret_cast(&outputImage[imgCursor]), 1); + imgCursor++; + forthBitCounter++; + if (forthBitCounter == 3) { + outputImage[imgCursor] = 0; + imgCursor++; + forthBitCounter = 0; + } + } +}; + +cairo_surface_t* BMP::createSurfaceFromBMP(const std::filesystem::path& path) { + + if (!std::filesystem::exists(path)) { + Debug::log(ERR, "createSurfaceFromBMP: file doesn't exist??"); + exit(1); + } + + std::ifstream bitmapImageStream(path); + BmpHeader bitmapHeader(bitmapImageStream); + + cairo_format_t format = CAIRO_FORMAT_ARGB32; + int stride = cairo_format_stride_for_width (format, bitmapHeader.width); + unsigned char* imageData = (unsigned char*) malloc(bitmapHeader.height * stride); + + if (bitmapHeader.numberOfBitPerPixel == 24) + convertRgbToArgb(bitmapImageStream, imageData, bitmapHeader.height * stride); + else if (bitmapHeader.numberOfBitPerPixel == 32) + bitmapImageStream.read(reinterpret_cast(&imageData), bitmapHeader.imageSize); + else { + Debug::log(ERR, "createSurfaceFromBMP: unsupported bmp format"); + bitmapImageStream.close(); + exit(1); + } + bitmapImageStream.close(); + reflectImage(imageData, bitmapHeader.height, stride); + return cairo_image_surface_create_for_data(imageData, format, bitmapHeader.width, bitmapHeader.height, stride); +} \ No newline at end of file diff --git a/src/helpers/Bmp.hpp b/src/helpers/Bmp.hpp new file mode 100644 index 0000000..70cabfb --- /dev/null +++ b/src/helpers/Bmp.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "string.h" +#include +#include "Log.hpp" + +namespace BMP { + cairo_surface_t* createSurfaceFromBMP(const std::filesystem::path&); +}; \ No newline at end of file diff --git a/src/renderer/AsyncResourceGatherer.cpp b/src/renderer/AsyncResourceGatherer.cpp index 17627ed..7857608 100644 --- a/src/renderer/AsyncResourceGatherer.cpp +++ b/src/renderer/AsyncResourceGatherer.cpp @@ -12,6 +12,7 @@ #include "../helpers/Webp.hpp" #include "src/helpers/Color.hpp" #include "src/helpers/Log.hpp" +#include CAsyncResourceGatherer::CAsyncResourceGatherer() { if (g_pHyprlock->getScreencopy()) @@ -85,6 +86,7 @@ SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) { } enum class FileType { + BMP, PNG, JPEG, WEBP, @@ -104,6 +106,8 @@ FileType getFileType(const std::filesystem::path& path) { ft = FileType::JPEG; else if (ext == ".webp") ft = FileType::WEBP; + else if (ext == ".bmp") + ft = FileType::BMP; else { // magic is slow, so only use it when no recognized extension is found auto handle = magic_open(MAGIC_NONE | MAGIC_COMPRESS); @@ -130,6 +134,7 @@ cairo_surface_t* getCairoSurfaceFromImageFile(const std::filesystem::path& path) case FileType::PNG: cairoSurface = cairo_image_surface_create_from_png(path.c_str()); break; case FileType::JPEG: cairoSurface = JPEG::createSurfaceFromJPEG(path); break; case FileType::WEBP: cairoSurface = WEBP::createSurfaceFromWEBP(path); break; + case FileType::BMP: cairoSurface = BMP::createSurfaceFromBMP(path); break; default: Debug::log(ERR, "unrecognized image format of {}", path.c_str()); }