mirror of
https://github.com/hyprwm/hyprgraphics.git
synced 2025-12-20 07:30:03 +01:00
formats: move to libpng for png support
This commit is contained in:
parent
6075491094
commit
7ea6a5f997
2 changed files with 52 additions and 62 deletions
|
|
@ -58,7 +58,7 @@ pkg_check_modules(
|
||||||
libjpeg
|
libjpeg
|
||||||
libwebp
|
libwebp
|
||||||
libmagic
|
libmagic
|
||||||
spng)
|
libpng)
|
||||||
|
|
||||||
pkg_check_modules(
|
pkg_check_modules(
|
||||||
JXL
|
JXL
|
||||||
|
|
|
||||||
|
|
@ -1,94 +1,84 @@
|
||||||
#include "Png.hpp"
|
#include "Png.hpp"
|
||||||
#include <spng.h>
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <png.h>
|
||||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||||
using namespace Hyprutils::Utils;
|
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) {
|
std::expected<cairo_surface_t*, std::string> PNG::createSurfaceFromPNG(const std::string& path) {
|
||||||
if (!std::filesystem::exists(path))
|
if (!std::filesystem::exists(path))
|
||||||
return std::unexpected("loading png: file doesn't exist");
|
return std::unexpected("loading png: file doesn't exist");
|
||||||
|
|
||||||
spng_ctx* ctx = spng_ctx_new(0);
|
FILE* fp = fopen(path.c_str(), "rb");
|
||||||
|
if (!fp)
|
||||||
|
return std::unexpected("loading png: couldn't open file");
|
||||||
|
|
||||||
CScopeGuard x([&] { spng_ctx_free(ctx); });
|
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||||
|
png_infop info = png_create_info_struct(png);
|
||||||
|
if (!png || !info)
|
||||||
|
return std::unexpected("loading png: couldn't init libpng");
|
||||||
|
|
||||||
const auto PNGCONTENT = readBinaryFile(path);
|
CScopeGuard x([&png, &info, fp] {
|
||||||
|
png_destroy_read_struct(&png, &info, nullptr);
|
||||||
|
fclose(fp);
|
||||||
|
});
|
||||||
|
|
||||||
if (PNGCONTENT.empty())
|
if (setjmp(png_jmpbuf(png)))
|
||||||
return std::unexpected("loading png: file content was empty (bad file?)");
|
return std::unexpected("loading png: couldn't setjmp");
|
||||||
|
|
||||||
spng_set_png_buffer(ctx, PNGCONTENT.data(), PNGCONTENT.size());
|
png_init_io(png, fp);
|
||||||
|
png_read_info(png, info);
|
||||||
|
|
||||||
spng_ihdr ihdr{.width = 0};
|
const size_t W = png_get_image_width(png, info);
|
||||||
if (int ret = spng_get_ihdr(ctx, &ihdr); ret)
|
const size_t H = png_get_image_height(png, info);
|
||||||
return std::unexpected(std::string{"loading png: spng_get_ihdr failed: "} + spng_strerror(ret));
|
const auto COLOR_TYPE = png_get_color_type(png, info);
|
||||||
|
const auto BPP = png_get_bit_depth(png, info);
|
||||||
|
|
||||||
int fmt = SPNG_FMT_PNG;
|
if (BPP == 16)
|
||||||
if (ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)
|
png_set_strip_16(png);
|
||||||
fmt = SPNG_FMT_RGB8;
|
if (COLOR_TYPE == PNG_COLOR_TYPE_PALETTE)
|
||||||
|
png_set_palette_to_rgb(png);
|
||||||
|
if (COLOR_TYPE == PNG_COLOR_TYPE_GRAY && BPP < 8)
|
||||||
|
png_set_expand_gray_1_2_4_to_8(png);
|
||||||
|
if (png_get_valid(png, info, PNG_INFO_tRNS))
|
||||||
|
png_set_tRNS_to_alpha(png);
|
||||||
|
|
||||||
size_t imageLength = 0;
|
if (COLOR_TYPE == PNG_COLOR_TYPE_RGB || COLOR_TYPE == PNG_COLOR_TYPE_GRAY || COLOR_TYPE == PNG_COLOR_TYPE_PALETTE)
|
||||||
if (int ret = spng_decoded_image_size(ctx, fmt, &imageLength); ret)
|
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
|
||||||
return std::unexpected(std::string{"loading png: spng_decoded_image_size failed: "} + spng_strerror(ret));
|
else if (COLOR_TYPE == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||||
|
png_set_gray_to_rgb(png);
|
||||||
|
|
||||||
uint8_t* imageData = (uint8_t*)malloc(imageLength);
|
png_read_update_info(png, info);
|
||||||
|
|
||||||
if (!imageData)
|
std::vector<uint8_t*> rowPointers;
|
||||||
return std::unexpected("loading png: mallocing failed, out of memory?");
|
rowPointers.resize(H);
|
||||||
|
std::vector<uint8_t> rawData;
|
||||||
// TODO: allow proper decode of high bitrate images
|
rawData.resize(W * H * 4);
|
||||||
bool succeededDecode = false;
|
for (size_t y = 0; y < H; y++) {
|
||||||
int ret = spng_decode_image(ctx, imageData, imageLength, SPNG_FMT_RGBA8, 0);
|
rowPointers[y] = rawData.data() + (y * W * 4);
|
||||||
if (!ret)
|
|
||||||
succeededDecode = true;
|
|
||||||
|
|
||||||
if (!succeededDecode && ret == SPNG_EBUFSIZ) {
|
|
||||||
// hack, but I don't know why decoded_image_size is sometimes wrong
|
|
||||||
imageLength = static_cast<size_t>(ihdr.height * ihdr.width * 4) /* FIXME: this is wrong if we doing >32bpp!!!! */;
|
|
||||||
imageData = (uint8_t*)realloc(imageData, imageLength);
|
|
||||||
|
|
||||||
ret = spng_decode_image(ctx, imageData, imageLength, SPNG_FMT_RGBA8, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ret)
|
png_read_image(png, rowPointers.data());
|
||||||
succeededDecode = true;
|
|
||||||
|
|
||||||
if (!succeededDecode) {
|
for (size_t i = 0; i < W * H * 4; i += 4) {
|
||||||
free(imageData);
|
uint8_t r = rawData[i + 0];
|
||||||
return std::unexpected(std::string{"loading png: spng_decode_image failed: "} + spng_strerror(ret) + " (bad image?)");
|
uint8_t g = rawData[i + 1];
|
||||||
|
uint8_t b = rawData[i + 2];
|
||||||
|
uint8_t a = rawData[i + 3];
|
||||||
|
*(uint32_t*)&rawData[i] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert RGBA8888 -> ARGB8888 premult for cairo
|
auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, W, H);
|
||||||
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)
|
if (!CAIROSURFACE)
|
||||||
return std::unexpected("loading png: cairo failed");
|
return std::unexpected("loading png: cairo failed");
|
||||||
|
|
||||||
|
memcpy(cairo_image_surface_get_data(CAIROSURFACE), rawData.data(), rawData.size());
|
||||||
|
|
||||||
return CAIROSURFACE;
|
return CAIROSURFACE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue