Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added BMP image handling, the same as in hyprpaper #443

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions src/helpers/Bmp.cpp
Original file line number Diff line number Diff line change
@@ -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<char*>(&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<char*>(&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<char*>(&reserved1), sizeof(reserved1));
file.read(reinterpret_cast<char*>(&reserved2), sizeof(reserved2));
file.read(reinterpret_cast<char*>(&dataOffset), sizeof(dataOffset));
file.read(reinterpret_cast<char*>(&sizeOfBitmapHeader), sizeof(sizeOfBitmapHeader));
file.read(reinterpret_cast<char*>(&width), sizeof(width));
file.read(reinterpret_cast<char*>(&height), sizeof(height));
file.read(reinterpret_cast<char*>(&numberOfColors), sizeof(numberOfColors));
file.read(reinterpret_cast<char*>(&numberOfBitPerPixel), sizeof(numberOfBitPerPixel));
file.read(reinterpret_cast<char*>(&compressionMethod), sizeof(compressionMethod));
file.read(reinterpret_cast<char*>(&imageSize), sizeof(imageSize));
file.read(reinterpret_cast<char*>(&horizontalResolutionPPM), sizeof(horizontalResolutionPPM));
file.read(reinterpret_cast<char*>(&verticalResolutionPPM), sizeof(verticalResolutionPPM));
file.read(reinterpret_cast<char*>(&numberOfCollors), sizeof(numberOfCollors));
file.read(reinterpret_cast<char*>(&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<unsigned char> 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<char*>(&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<char*>(&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);
}
15 changes: 15 additions & 0 deletions src/helpers/Bmp.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma once

#include <cairo/cairo.h>
#include <filesystem>
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "string.h"
#include <vector>
#include "Log.hpp"

namespace BMP {
cairo_surface_t* createSurfaceFromBMP(const std::filesystem::path&);
};
5 changes: 5 additions & 0 deletions src/renderer/AsyncResourceGatherer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "../helpers/Webp.hpp"
#include "src/helpers/Color.hpp"
#include "src/helpers/Log.hpp"
#include <src/helpers/Bmp.hpp>

CAsyncResourceGatherer::CAsyncResourceGatherer() {
if (g_pHyprlock->getScreencopy())
Expand Down Expand Up @@ -85,6 +86,7 @@ SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
}

enum class FileType {
BMP,
PNG,
JPEG,
WEBP,
Expand All @@ -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);
Expand All @@ -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());
}

Expand Down