mirror of
https://github.com/hyprwm/hyprgraphics.git
synced 2025-02-13 21:32:08 +01:00
core: move to libspng for png
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: |
|
run: |
|
||||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||||
pacman --noconfirm --noprogressbar -Syyu
|
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
|
- name: Build hyprgraphics with gcc
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -47,7 +47,8 @@ pkg_check_modules(
|
||||||
hyprutils
|
hyprutils
|
||||||
libjpeg
|
libjpeg
|
||||||
libwebp
|
libwebp
|
||||||
libmagic)
|
libmagic
|
||||||
|
spng)
|
||||||
|
|
||||||
pkg_check_modules(
|
pkg_check_modules(
|
||||||
JXL
|
JXL
|
||||||
|
|
|
@ -20,6 +20,7 @@ Dep list:
|
||||||
- libjxl_cms [optional]
|
- libjxl_cms [optional]
|
||||||
- libjxl_threads [optional]
|
- libjxl_threads [optional]
|
||||||
- libmagic
|
- libmagic
|
||||||
|
- libspng
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "formats/JpegXL.hpp"
|
#include "formats/JpegXL.hpp"
|
||||||
#endif
|
#endif
|
||||||
#include "formats/Webp.hpp"
|
#include "formats/Webp.hpp"
|
||||||
|
#include "formats/Png.hpp"
|
||||||
#include <magic.h>
|
#include <magic.h>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
|
||||||
|
@ -15,7 +16,7 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
|
||||||
std::expected<cairo_surface_t*, std::string> CAIROSURFACE;
|
std::expected<cairo_surface_t*, std::string> CAIROSURFACE;
|
||||||
const auto len = path.length();
|
const auto len = path.length();
|
||||||
if (path.find(".png") == len - 4 || path.find(".PNG") == len - 4) {
|
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";
|
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) {
|
} 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);
|
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(" "));
|
const auto first_word = type_str.substr(0, type_str.find(" "));
|
||||||
|
|
||||||
if (first_word == "PNG") {
|
if (first_word == "PNG") {
|
||||||
CAIROSURFACE = cairo_image_surface_create_from_png(path.c_str());
|
CAIROSURFACE = PNG::createSurfaceFromPNG(path);
|
||||||
mime = "image/png";
|
mime = "image/png";
|
||||||
} else if (first_word == "JPEG") {
|
} else if (first_word == "JPEG") {
|
||||||
CAIROSURFACE = JPEG::createSurfaceFromJPEG(path);
|
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());
|
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) {
|
int main(int argc, char** argv, char** envp) {
|
||||||
|
@ -27,7 +36,8 @@ int main(int argc, char** argv, char** envp) {
|
||||||
continue;
|
continue;
|
||||||
auto expectation = true;
|
auto expectation = true;
|
||||||
#ifndef JXL_FOUND
|
#ifndef JXL_FOUND
|
||||||
if (file.path().filename() == "hyprland.jxl") expectation = false;
|
if (file.path().filename() == "hyprland.jxl")
|
||||||
|
expectation = false;
|
||||||
#endif
|
#endif
|
||||||
EXPECT(tryLoadImage(file.path()), expectation);
|
EXPECT(tryLoadImage(file.path()), expectation);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue