mirror of
https://github.com/hyprwm/hyprgraphics.git
synced 2025-01-29 02:49:49 +01:00
core: move to libspng for png
Some checks are pending
Some checks are pending
This commit is contained in:
parent
0d77b4895a
commit
0c11438de4
7 changed files with 105 additions and 6 deletions
2
.github/workflows/arch.yml
vendored
2
.github/workflows/arch.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
|||
run: |
|
||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||
pacman --noconfirm --noprogressbar -Syyu
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman cairo hyprutils libjpeg-turbo libjxl libwebp
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman cairo hyprutils libjpeg-turbo libjxl libwebp libspng
|
||||
|
||||
- name: Build hyprgraphics with gcc
|
||||
run: |
|
||||
|
|
|
@ -47,7 +47,8 @@ pkg_check_modules(
|
|||
hyprutils
|
||||
libjpeg
|
||||
libwebp
|
||||
libmagic)
|
||||
libmagic
|
||||
spng)
|
||||
|
||||
pkg_check_modules(
|
||||
JXL
|
||||
|
|
|
@ -20,6 +20,7 @@ Dep list:
|
|||
- libjxl_cms [optional]
|
||||
- libjxl_threads [optional]
|
||||
- libmagic
|
||||
- libspng
|
||||
|
||||
## Building
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "formats/JpegXL.hpp"
|
||||
#endif
|
||||
#include "formats/Webp.hpp"
|
||||
#include "formats/Png.hpp"
|
||||
#include <magic.h>
|
||||
#include <format>
|
||||
|
||||
|
@ -15,7 +16,7 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
|
|||
std::expected<cairo_surface_t*, std::string> CAIROSURFACE;
|
||||
const auto len = path.length();
|
||||
if (path.find(".png") == len - 4 || path.find(".PNG") == len - 4) {
|
||||
CAIROSURFACE = cairo_image_surface_create_from_png(path.c_str());
|
||||
CAIROSURFACE = PNG::createSurfaceFromPNG(path);
|
||||
mime = "image/png";
|
||||
} 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);
|
||||
|
@ -47,7 +48,7 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
|
|||
const auto first_word = type_str.substr(0, type_str.find(" "));
|
||||
|
||||
if (first_word == "PNG") {
|
||||
CAIROSURFACE = cairo_image_surface_create_from_png(path.c_str());
|
||||
CAIROSURFACE = PNG::createSurfaceFromPNG(path);
|
||||
mime = "image/png";
|
||||
} else if (first_word == "JPEG") {
|
||||
CAIROSURFACE = JPEG::createSurfaceFromJPEG(path);
|
||||
|
|
77
src/image/formats/Png.cpp
Normal file
77
src/image/formats/Png.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include "Png.hpp"
|
||||
#include <spng.h>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <cstdint>
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
using namespace Hyprutils::Utils;
|
||||
|
||||
static std::vector<unsigned char> readBinaryFile(const std::string& filename) {
|
||||
std::ifstream f(filename, std::ios::binary);
|
||||
if (!f.good())
|
||||
return {};
|
||||
f.unsetf(std::ios::skipws);
|
||||
return {std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()};
|
||||
}
|
||||
|
||||
std::expected<cairo_surface_t*, std::string> PNG::createSurfaceFromPNG(const std::string& path) {
|
||||
if (!std::filesystem::exists(path))
|
||||
return std::unexpected("loading png: file doesn't exist");
|
||||
|
||||
spng_ctx* ctx = spng_ctx_new(0);
|
||||
|
||||
CScopeGuard x([&] { spng_ctx_free(ctx); });
|
||||
|
||||
const auto PNGCONTENT = readBinaryFile(path);
|
||||
|
||||
if (PNGCONTENT.empty())
|
||||
return std::unexpected("loading png: file content was empty (bad file?)");
|
||||
|
||||
spng_set_png_buffer(ctx, PNGCONTENT.data(), PNGCONTENT.size());
|
||||
|
||||
spng_ihdr ihdr{0};
|
||||
if (spng_get_ihdr(ctx, &ihdr))
|
||||
return std::unexpected("loading png: file content was empty (bad file?)");
|
||||
|
||||
int fmt = SPNG_FMT_PNG;
|
||||
if (ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
|
||||
size_t imageLength = 0;
|
||||
if (spng_decoded_image_size(ctx, fmt, &imageLength))
|
||||
return std::unexpected("loading png: spng_decoded_image_size failed");
|
||||
|
||||
uint8_t* imageData = (uint8_t*)malloc(imageLength);
|
||||
|
||||
if (!imageData)
|
||||
return std::unexpected("loading png: mallocing failed, out of memory?");
|
||||
|
||||
// TODO: allow proper decode of high bitrate images
|
||||
if (spng_decode_image(ctx, imageData, imageLength, SPNG_FMT_RGBA8, 0)) {
|
||||
free(imageData);
|
||||
return std::unexpected("loading png: spng_decode_image failed (invalid image?)");
|
||||
}
|
||||
|
||||
// convert RGBA8888 -> ARGB8888 premult for cairo
|
||||
for (size_t i = 0; i < imageLength; i += 4) {
|
||||
uint8_t r, g, b, a;
|
||||
a = ((*((uint32_t*)(imageData + i))) & 0xFF000000) >> 24;
|
||||
b = ((*((uint32_t*)(imageData + i))) & 0x00FF0000) >> 16;
|
||||
g = ((*((uint32_t*)(imageData + i))) & 0x0000FF00) >> 8;
|
||||
r = (*((uint32_t*)(imageData + i))) & 0x000000FF;
|
||||
|
||||
r *= ((float)a / 255.F);
|
||||
g *= ((float)a / 255.F);
|
||||
b *= ((float)a / 255.F);
|
||||
|
||||
*((uint32_t*)(imageData + i)) = (((uint32_t)a) << 24) | (((uint32_t)r) << 16) | (((uint32_t)g) << 8) | (uint32_t)b;
|
||||
}
|
||||
|
||||
auto CAIROSURFACE = cairo_image_surface_create_for_data(imageData, CAIRO_FORMAT_ARGB32, ihdr.width, ihdr.height, ihdr.width * 4);
|
||||
|
||||
if (!CAIROSURFACE)
|
||||
return std::unexpected("loading png: cairo failed");
|
||||
|
||||
return CAIROSURFACE;
|
||||
}
|
9
src/image/formats/Png.hpp
Normal file
9
src/image/formats/Png.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <string>
|
||||
#include <expected>
|
||||
|
||||
namespace PNG {
|
||||
std::expected<cairo_surface_t*, std::string> createSurfaceFromPNG(const std::string&);
|
||||
};
|
|
@ -16,7 +16,16 @@ bool tryLoadImage(const std::string& path) {
|
|||
|
||||
std::println("Loaded {} successfully: Image is {}x{} of type {}", path, image.cairoSurface()->size().x, image.cairoSurface()->size().y, image.getMime());
|
||||
|
||||
return true;
|
||||
const auto TEST_DIR = std::filesystem::current_path().string() + "/test_output";
|
||||
|
||||
// try to write it for inspection
|
||||
if (!std::filesystem::exists(TEST_DIR))
|
||||
std::filesystem::create_directory(TEST_DIR);
|
||||
|
||||
std::string name = image.getMime();
|
||||
std::replace(name.begin(), name.end(), '/', '_');
|
||||
|
||||
return cairo_surface_write_to_png(image.cairoSurface()->cairo(), (TEST_DIR + "/" + name + ".png").c_str()) == CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
|
@ -27,7 +36,8 @@ int main(int argc, char** argv, char** envp) {
|
|||
continue;
|
||||
auto expectation = true;
|
||||
#ifndef JXL_FOUND
|
||||
if (file.path().filename() == "hyprland.jxl") expectation = false;
|
||||
if (file.path().filename() == "hyprland.jxl")
|
||||
expectation = false;
|
||||
#endif
|
||||
EXPECT(tryLoadImage(file.path()), expectation);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue