implement loading

This commit is contained in:
Ramy Kaddouri 2025-08-17 16:59:24 +01:00
parent 3b545552ec
commit ec51fd4267
3 changed files with 100 additions and 0 deletions

View file

@ -66,12 +66,25 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
return;
#endif
} else if (path.find(".avif") == len-5 || path.find(".AVIF") == len-5) {
#ifdef HEIF_FOUND
CAIROSURFACE = AVIF::createSurfaceFromAvif(path);
mime = "image/avif";
#else
lastError = "hyprgraphics compiled without HEIF support";
return;
#endif
} else {
// magic is slow, so only use it when no recognized extension is found
auto handle = magic_open(MAGIC_NONE | MAGIC_COMPRESS | MAGIC_SYMLINK);
magic_load(handle, nullptr);
const auto type_str = std::string(magic_file(handle, path.c_str()));
magic_close(handle);
const auto first_word = type_str.substr(0, type_str.find(' '));
if (first_word == "PNG") {
@ -88,6 +101,14 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
#else
lastError = "hyprgraphics compiled without JXL support";
return;
#endif
} else if (type_str.contains("AVIF")) { // libmagic can identify AVIF images as "ISO Media, AVIF Image"
#ifdef HEIF_FOUND
CAIROSURFACE = AVIF::createSurfaceFromAvif(path);
mime = "image/avif";
#else
lastError = "hyprgraphics compiled without AVIF support";
return;
#endif
} else if (first_word == "BMP") {
CAIROSURFACE = BMP::createSurfaceFromBMP(path);

View file

@ -0,0 +1,75 @@
#include "Avif.hpp"
#include <cairo.h>
#include <cstdint>
#include <cstring>
#include <expected>
#include <filesystem>
#include <hyprutils/utils/ScopeGuard.hpp>
#include <libheif/heif.h>
using namespace Hyprutils::Utils;
static std::expected<cairo_surface_t*, std::string> loadFromContext(heif_context* ctx) {
heif_image_handle* handle;
heif_context_get_primary_image_handle(ctx, &handle);
heif_image* img;
heif_decode_image(handle, &img, heif_colorspace_RGB, heif_chroma_interleaved_RGBA, nullptr);
size_t width = heif_image_get_width(img, heif_channel_interleaved);
size_t height = heif_image_get_height(img, heif_channel_interleaved);
int stride;
const uint8_t* data = heif_image_get_plane_readonly(img, heif_channel_interleaved, &stride);
std::vector<uint8_t> rawData;
rawData.resize(width * height * 4);
for (size_t y = 0; y < height; y++) {
const uint8_t* src = data + (y * stride);
uint32_t* dst = (uint32_t*)(rawData.data() + (y * width * 4));
for (size_t x = 0; x < width; x++) {
uint8_t r = src[(4 * x) + 0];
uint8_t g = src[(4 * x) + 1];
uint8_t b = src[(4 * x) + 2];
uint8_t a = src[(4 * x) + 3];
r = (r * a) / 255.F;
g = (g * a) / 255.F;
b = (b * a) / 255.F;
dst[x] = (a << 24) | (r << 16) | (g << 8) | b;
}
}
auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
if (!CAIROSURFACE)
return std::unexpected("loading avif: cairo failed");
memcpy(cairo_image_surface_get_data(CAIROSURFACE), rawData.data(), rawData.size());
cairo_surface_mark_dirty(CAIROSURFACE);
heif_image_release(img);
heif_image_handle_release(handle);
return CAIROSURFACE;
}
std::expected<cairo_surface_t*, std::string> AVIF::createSurfaceFromAvif(const std::string& path) {
if (!std::filesystem::exists(path))
return std::unexpected("loading avif: file doesn't exist");
heif_context* ctx = heif_context_alloc();
heif_context_read_from_file(ctx, path.c_str(), nullptr);
auto result = loadFromContext(ctx);
heif_context_free(ctx);
return result;
}
std::expected<cairo_surface_t*, std::string> AVIF::createSurfaceFromAvif(const std::span<uint8_t>& buf) {
heif_context* ctx = heif_context_alloc();
heif_context_read_from_memory(ctx, buf.data(), buf.size(), nullptr);
auto result = loadFromContext(ctx);
heif_context_free(ctx);
return result;
}

View file

@ -79,6 +79,10 @@ int main(int argc, char** argv, char** envp) {
#ifndef JXL_FOUND
if (file.path().filename() == "hyprland.jxl")
expectation = false;
#endif
#ifndef HEIF_FOUND
if (file.path().filename() == "hyprland.avif")
expectation = false;
#endif
EXPECT(tryLoadImageFromFile(file.path()), expectation);
}