From 374d6e2a9d3ec86ea3f8d8613f9c47c96f596f6a Mon Sep 17 00:00:00 2001 From: Barguzin <124505224+barrrguzin@users.noreply.github.com> Date: Sat, 8 Jun 2024 01:19:58 +1000 Subject: [PATCH] core: Bitmap image support (#175) * Add handler "reload" to do a change of wallpaper by one hyprctl execution * fixed contain parameter handling in "handleReload" * added bitmap (.bmp) image support * refactored * reserve -> resize --- src/helpers/Bmp.cpp | 126 +++++++++++++++++++++++++++++++++ src/helpers/Bmp.hpp | 7 ++ src/render/WallpaperTarget.cpp | 6 ++ src/render/WallpaperTarget.hpp | 1 + 4 files changed, 140 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..96afea4 --- /dev/null +++ b/src/helpers/Bmp.cpp @@ -0,0 +1,126 @@ +#include "Bmp.hpp" + +#include +#include +#include + +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::string& 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..c36e499 --- /dev/null +++ b/src/helpers/Bmp.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "../defines.hpp" + +namespace BMP { + cairo_surface_t* createSurfaceFromBMP(const std::string&); +}; \ No newline at end of file diff --git a/src/render/WallpaperTarget.cpp b/src/render/WallpaperTarget.cpp index 7881d57..b49d354 100644 --- a/src/render/WallpaperTarget.cpp +++ b/src/render/WallpaperTarget.cpp @@ -18,6 +18,9 @@ void CWallpaperTarget::create(const std::string& path) { } else if (path.find(".jpg") == len - 4 || path.find(".JPG") == len - 4 || path.find(".jpeg") == len - 5 || path.find(".JPEG") == len - 5) { CAIROSURFACE = JPEG::createSurfaceFromJPEG(path); m_bHasAlpha = false; + } else if (path.find(".bmp") == len - 4 || path.find(".BMP") == len - 4) { + CAIROSURFACE = BMP::createSurfaceFromBMP(path); + m_bHasAlpha = false; } else if (path.find(".webp") == len - 5 || path.find(".WEBP") == len - 5) { CAIROSURFACE = WEBP::createSurfaceFromWEBP(path); } else { @@ -33,6 +36,9 @@ void CWallpaperTarget::create(const std::string& path) { } else if (first_word == "JPEG") { CAIROSURFACE = JPEG::createSurfaceFromJPEG(path); m_bHasAlpha = false; + } else if (first_word == "BMP") { + CAIROSURFACE = BMP::createSurfaceFromBMP(path); + m_bHasAlpha = false; } else { Debug::log(CRIT, "unrecognized image %s", path.c_str()); exit(1); diff --git a/src/render/WallpaperTarget.hpp b/src/render/WallpaperTarget.hpp index 356c046..5a7eea6 100644 --- a/src/render/WallpaperTarget.hpp +++ b/src/render/WallpaperTarget.hpp @@ -2,6 +2,7 @@ #include "../defines.hpp" #include "../helpers/Jpeg.hpp" +#include "../helpers/Bmp.hpp" #include "../helpers/Webp.hpp" class CWallpaperTarget {