Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package hyprgraphics for openSUSE:Factory 
checked in at 2025-11-09 21:07:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/hyprgraphics (Old)
 and      /work/SRC/openSUSE:Factory/.hyprgraphics.new.1980 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "hyprgraphics"

Sun Nov  9 21:07:56 2025 rev:4 rq:1316499 version:0.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/hyprgraphics/hyprgraphics.changes        
2025-09-11 14:43:26.702211112 +0200
+++ /work/SRC/openSUSE:Factory/.hyprgraphics.new.1980/hyprgraphics.changes      
2025-11-09 21:08:20.432246274 +0100
@@ -1,0 +2,19 @@
+Fri Nov  7 21:21:42 UTC 2025 - Florian "spirit" <[email protected]>
+
+- Update to version 0.3.0:
+  + This update breaks ABI.
+  + resources/text: fix alignment layout with center/right
+  + add buffer option to image resource
+
+- Changes from version 0.2.0:
+  + This update breaks ABI.
+  + Image: added svg support
+  + text: various fixes and improvements
+
+- Changes from version 0.1.6:
+  + New module: AsyncResourceGatherer
+  + formats: add optional AVIF image support with libheif
+  + formats: include vector header
+  + png: fix gray pix formats
+
+-------------------------------------------------------------------

Old:
----
  hyprgraphics-0.1.5.tar.xz

New:
----
  hyprgraphics-0.3.0.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ hyprgraphics.spec ++++++
--- /var/tmp/diff_new_pack.G0XTrx/_old  2025-11-09 21:08:20.956268189 +0100
+++ /var/tmp/diff_new_pack.G0XTrx/_new  2025-11-09 21:08:20.956268189 +0100
@@ -1,8 +1,9 @@
 #
 # spec file for package hyprgraphics
 #
+# Copyright (c) 2025 SUSE LLC
 # Copyright (c) 2025 SUSE LLC and contributors
-# Copyright (c) 2024 Florian "sp1rit" <[email protected]>
+# Copyright (c) 2024/25 Florian "sp1rit" <[email protected]>
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,10 +18,10 @@
 #
 
 
-%define sover 0
+%define sover 2
 
 Name:           hyprgraphics
-Version:        0.1.5
+Version:        0.3.0
 Release:        0
 Summary:        Hyprland graphics / resource utilities
 License:        BSD-3-Clause
@@ -30,13 +31,14 @@
 BuildRequires:  gcc-c++
 BuildRequires:  pkg-config
 BuildRequires:  pkgconfig(cairo)
-BuildRequires:  pkgconfig(hyprutils)
+BuildRequires:  pkgconfig(hyprutils) >= 0.8.0
 BuildRequires:  pkgconfig(libjpeg)
 BuildRequires:  pkgconfig(libjxl)
 BuildRequires:  pkgconfig(libjxl_cms)
 BuildRequires:  pkgconfig(libjxl_threads)
 BuildRequires:  pkgconfig(libmagic)
 BuildRequires:  pkgconfig(libpng16)
+BuildRequires:  pkgconfig(librsvg-2.0)
 BuildRequires:  pkgconfig(libwebp)
 BuildRequires:  pkgconfig(pixman-1)
 

++++++ hyprgraphics-0.1.5.tar.xz -> hyprgraphics-0.3.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/.github/workflows/arch.yml 
new/hyprgraphics-0.3.0/.github/workflows/arch.yml
--- old/hyprgraphics-0.1.5/.github/workflows/arch.yml   2025-07-10 
14:05:40.000000000 +0200
+++ new/hyprgraphics-0.3.0/.github/workflows/arch.yml   2025-11-06 
21:47:32.000000000 +0100
@@ -17,7 +17,7 @@
         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 libspng
+          pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang 
libc++ pango pixman cairo hyprutils libjpeg-turbo libjxl libwebp libpng 
ttf-dejavu librsvg
 
       - name: Build hyprgraphics with gcc
         run: |
@@ -44,7 +44,7 @@
         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 libspng
+          pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang 
libc++ pango pixman cairo hyprutils libjpeg-turbo libjxl libwebp libpng 
ttf-dejavu librsvg
 
       - name: Build hyprgraphics with clang
         run: |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/CMakeLists.txt 
new/hyprgraphics-0.3.0/CMakeLists.txt
--- old/hyprgraphics-0.1.5/CMakeLists.txt       2025-07-10 14:05:40.000000000 
+0200
+++ new/hyprgraphics-0.3.0/CMakeLists.txt       2025-11-06 21:47:32.000000000 
+0100
@@ -54,11 +54,13 @@
   IMPORTED_TARGET
   pixman-1
   cairo
+  pangocairo
   hyprutils
   libjpeg
   libwebp
   libmagic
-  libpng)
+  libpng
+  librsvg-2.0)
 
 pkg_check_modules(
   JXL
@@ -74,18 +76,35 @@
   add_compile_definitions(JXL_FOUND)
 endif()
 
+pkg_check_modules(
+  HEIF
+  IMPORTED_TARGET
+  libheif
+)
+
+if(NOT HEIF_FOUND)
+  file(GLOB_RECURSE HEIFFILES CONFIGURE_DEPENDS "src/*Avif.cpp")
+  list(REMOVE_ITEM SRCFILES ${HEIFFILES})
+else()
+  add_compile_definitions(HEIF_FOUND)
+endif()
+
 add_library(hyprgraphics SHARED ${SRCFILES})
 target_include_directories(
   hyprgraphics
   PUBLIC "./include"
   PRIVATE "./src")
 set_target_properties(hyprgraphics PROPERTIES VERSION ${HYPRGRAPHICS_VERSION}
-                                              SOVERSION 0)
+                                              SOVERSION 2)
 target_link_libraries(hyprgraphics PkgConfig::deps)
 if(JXL_FOUND)
   target_link_libraries(hyprgraphics PkgConfig::JXL)
 endif()
 
+if(HEIF_FOUND)
+  target_link_libraries(hyprgraphics PkgConfig::HEIF)
+endif()
+
 # tests
 add_custom_target(tests)
 
@@ -96,6 +115,13 @@
   WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
   COMMAND hyprgraphics_image "image")
 add_dependencies(tests hyprgraphics_image)
+add_executable(hyprgraphics_arg "tests/arg.cpp")
+target_link_libraries(hyprgraphics_arg PRIVATE hyprgraphics PkgConfig::deps)
+add_test(
+  NAME "ARG"
+  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
+  COMMAND hyprgraphics_arg "image")
+add_dependencies(tests hyprgraphics_arg)
 
 # Installation
 install(TARGETS hyprgraphics)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/README.md 
new/hyprgraphics-0.3.0/README.md
--- old/hyprgraphics-0.1.5/README.md    2025-07-10 14:05:40.000000000 +0200
+++ new/hyprgraphics-0.3.0/README.md    2025-11-06 21:47:32.000000000 +0100
@@ -20,7 +20,8 @@
  - libjxl_cms [optional]
  - libjxl_threads [optional]
  - libmagic
- - libspng
+ - libpng
+ - librsvg2
 
 ## Building
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/VERSION 
new/hyprgraphics-0.3.0/VERSION
--- old/hyprgraphics-0.1.5/VERSION      2025-07-10 14:05:40.000000000 +0200
+++ new/hyprgraphics-0.3.0/VERSION      2025-11-06 21:47:32.000000000 +0100
@@ -1 +1 @@
-0.1.5
+0.3.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/flake.lock 
new/hyprgraphics-0.3.0/flake.lock
--- old/hyprgraphics-0.1.5/flake.lock   2025-07-10 14:05:40.000000000 +0200
+++ new/hyprgraphics-0.3.0/flake.lock   2025-11-06 21:47:32.000000000 +0100
@@ -10,11 +10,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1749135356,
-        "narHash": "sha256-Q8mAKMDsFbCEuq7zoSlcTuxgbIBVhfIYpX0RjE32PS0=",
+        "lastModified": 1756117388,
+        "narHash": "sha256-oRDel6pNl/T2tI+nc/USU9ZP9w08dxtl7hiZxa0C/Wc=",
         "owner": "hyprwm",
         "repo": "hyprutils",
-        "rev": "e36db00dfb3a3d3fdcc4069cb292ff60d2699ccb",
+        "rev": "b2ae3204845f5f2f79b4703b441252d8ad2ecfd0",
         "type": "github"
       },
       "original": {
@@ -25,11 +25,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1748929857,
-        "narHash": "sha256-lcZQ8RhsmhsK8u7LIFsJhsLh/pzR9yZ8yqpTzyGdj+Q=",
+        "lastModified": 1757745802,
+        "narHash": "sha256-hLEO2TPj55KcUFUU1vgtHE9UEIOjRcH/4QbmfHNF820=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4",
+        "rev": "c23193b943c6c689d70ee98ce3128239ed9e32d1",
         "type": "github"
       },
       "original": {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/include/hyprgraphics/image/Image.hpp 
new/hyprgraphics-0.3.0/include/hyprgraphics/image/Image.hpp
--- old/hyprgraphics-0.1.5/include/hyprgraphics/image/Image.hpp 2025-07-10 
14:05:40.000000000 +0200
+++ new/hyprgraphics-0.3.0/include/hyprgraphics/image/Image.hpp 2025-11-06 
21:47:32.000000000 +0100
@@ -7,14 +7,15 @@
 #include <hyprutils/memory/SharedPtr.hpp>
 
 namespace Hyprgraphics {
-    enum eImageFormat {
-        IMAGE_FORMAT_PNG
+    enum eImageFormat : uint8_t {
+        IMAGE_FORMAT_PNG,
+        IMAGE_FORMAT_AVIF,
     };
 
     class CImage {
       public:
-        CImage(const std::string& path);
-        CImage(const std::span<uint8_t>&, eImageFormat);
+        CImage(const std::string& path, const Hyprutils::Math::Vector2D& size 
= {} /* for SVG */);
+        CImage(const std::span<const uint8_t>, eImageFormat);
         ~CImage();
 
         CImage(const CImage&)            = delete;
@@ -30,6 +31,7 @@
 
       private:
         std::string                                      lastError, filepath, 
mime;
+        Hyprutils::Math::Vector2D                        m_svgSize;
         Hyprutils::Memory::CSharedPointer<CCairoSurface> pCairoSurface;
         bool                                             imageHasAlpha = true, 
loadSuccess = false;
     };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/include/hyprgraphics/resource/AsyncResourceGatherer.hpp 
new/hyprgraphics-0.3.0/include/hyprgraphics/resource/AsyncResourceGatherer.hpp
--- 
old/hyprgraphics-0.1.5/include/hyprgraphics/resource/AsyncResourceGatherer.hpp  
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/hyprgraphics-0.3.0/include/hyprgraphics/resource/AsyncResourceGatherer.hpp  
    2025-11-06 21:47:32.000000000 +0100
@@ -0,0 +1,38 @@
+#pragma once
+
+#include <thread>
+#include <atomic>
+#include <vector>
+#include <unordered_map>
+#include <condition_variable>
+#include "../cairo/CairoSurface.hpp"
+#include "./resources/AsyncResource.hpp"
+#include <hyprutils/memory/Atomic.hpp>
+
+namespace Hyprgraphics {
+    class CAsyncResourceGatherer {
+      public:
+        CAsyncResourceGatherer();
+        ~CAsyncResourceGatherer();
+
+        void enqueue(Hyprutils::Memory::CAtomicSharedPointer<IAsyncResource> 
resource);
+
+      private:
+        std::thread m_gatherThread;
+
+        struct {
+            std::mutex              requestMutex;
+            std::condition_variable requestsCV;
+
+            bool                    exit           = false;
+            bool                    needsToProcess = false;
+        } m_asyncLoopState;
+
+        std::vector<Hyprutils::Memory::CAtomicSharedPointer<IAsyncResource>> 
m_targetsToLoad;
+        std::mutex                                                           
m_targetsToLoadMutex;
+
+        //
+        void asyncAssetSpinLock();
+        void wakeUpMainThread();
+    };
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/include/hyprgraphics/resource/resources/AsyncResource.hpp
 
new/hyprgraphics-0.3.0/include/hyprgraphics/resource/resources/AsyncResource.hpp
--- 
old/hyprgraphics-0.1.5/include/hyprgraphics/resource/resources/AsyncResource.hpp
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/hyprgraphics-0.3.0/include/hyprgraphics/resource/resources/AsyncResource.hpp
    2025-11-06 21:47:32.000000000 +0100
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <hyprutils/memory/UniquePtr.hpp>
+#include <hyprutils/math/Vector2D.hpp>
+#include <hyprutils/signal/Signal.hpp>
+#include "../../cairo/CairoSurface.hpp"
+#include <atomic>
+
+namespace Hyprgraphics {
+    class IAsyncResource {
+      public:
+        IAsyncResource()          = default;
+        virtual ~IAsyncResource() = default;
+
+        virtual void render() = 0;
+
+        struct {
+            // this signal fires on the worker thread. **Really** consider 
making this signal handler call something to wake your
+            // main event loop up and do things there.
+            Hyprutils::Signal::CSignalT<> finished;
+        } m_events;
+
+        // you probably shouldn't use this but it's here just in case.
+        std::atomic<bool> m_ready = false;
+
+        struct {
+            // This pointer can be made not thread safe as after .finished the 
worker thread will not touch it anymore
+            // and before that you shouldnt touch it either
+            Hyprutils::Memory::CSharedPointer<CCairoSurface> cairoSurface;
+            Hyprutils::Math::Vector2D                        pixelSize;
+        } m_asset;
+    };
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/include/hyprgraphics/resource/resources/ImageResource.hpp
 
new/hyprgraphics-0.3.0/include/hyprgraphics/resource/resources/ImageResource.hpp
--- 
old/hyprgraphics-0.1.5/include/hyprgraphics/resource/resources/ImageResource.hpp
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/hyprgraphics-0.3.0/include/hyprgraphics/resource/resources/ImageResource.hpp
    2025-11-06 21:47:32.000000000 +0100
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <string>
+#include <hyprutils/math/Vector2D.hpp>
+#include "./AsyncResource.hpp"
+#include "../../color/Color.hpp"
+
+#include <optional>
+
+namespace Hyprgraphics {
+    class CImageResource : public IAsyncResource {
+      public:
+        enum eTextAlignmentMode : uint8_t {
+            TEXT_ALIGN_LEFT = 0,
+            TEXT_ALIGN_CENTER,
+            TEXT_ALIGN_RIGHT,
+        };
+
+        CImageResource(const std::string& path);
+        CImageResource(const std::string& svg, const 
Hyprutils::Math::Vector2D& size);
+        virtual ~CImageResource() = default;
+
+        virtual void render();
+
+      private:
+        std::string               m_path;
+        Hyprutils::Math::Vector2D m_svgSize;
+    };
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/include/hyprgraphics/resource/resources/StaticImageResource.hpp
 
new/hyprgraphics-0.3.0/include/hyprgraphics/resource/resources/StaticImageResource.hpp
--- 
old/hyprgraphics-0.1.5/include/hyprgraphics/resource/resources/StaticImageResource.hpp
      1970-01-01 01:00:00.000000000 +0100
+++ 
new/hyprgraphics-0.3.0/include/hyprgraphics/resource/resources/StaticImageResource.hpp
      2025-11-06 21:47:32.000000000 +0100
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "./AsyncResource.hpp"
+#include "../../color/Color.hpp"
+#include "hyprgraphics/image/Image.hpp"
+
+#include <optional>
+
+#include <hyprutils/math/Vector2D.hpp>
+
+namespace Hyprgraphics {
+    class CStaticImageResource : public IAsyncResource {
+      public:
+        enum eTextAlignmentMode : uint8_t {
+            TEXT_ALIGN_LEFT = 0,
+            TEXT_ALIGN_CENTER,
+            TEXT_ALIGN_RIGHT,
+        };
+
+        CStaticImageResource(const std::span<const uint8_t> data, eImageFormat 
format);
+        virtual ~CStaticImageResource() = default;
+
+        virtual void render();
+
+      private:
+        const std::span<const uint8_t> m_data;
+        const eImageFormat             m_format = 
eImageFormat::IMAGE_FORMAT_PNG;
+    };
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/include/hyprgraphics/resource/resources/TextResource.hpp 
new/hyprgraphics-0.3.0/include/hyprgraphics/resource/resources/TextResource.hpp
--- 
old/hyprgraphics-0.1.5/include/hyprgraphics/resource/resources/TextResource.hpp 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/hyprgraphics-0.3.0/include/hyprgraphics/resource/resources/TextResource.hpp 
    2025-11-06 21:47:32.000000000 +0100
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "AsyncResource.hpp"
+#include "../../color/Color.hpp"
+
+#include <cairo/cairo.h>
+
+#include <optional>
+
+#include <hyprutils/math/Vector2D.hpp>
+
+namespace Hyprgraphics {
+    class CTextResource : public IAsyncResource {
+      public:
+        enum eTextAlignmentMode : uint8_t {
+            TEXT_ALIGN_LEFT = 0,
+            TEXT_ALIGN_CENTER,
+            TEXT_ALIGN_RIGHT,
+        };
+
+        struct STextResourceData {
+            std::string                              text      = "Sample Text";
+            std::string                              font      = "Sans Serif";
+            size_t                                   fontSize  = 16;
+            CColor                                   color     = 
CColor{CColor::SSRGB{.r = 1.F, .g = 1.F, .b = 1.F}};
+            eTextAlignmentMode                       align     = 
TEXT_ALIGN_LEFT;
+            std::optional<Hyprutils::Math::Vector2D> maxSize   = std::nullopt;
+            cairo_antialias_t                        antialias = 
CAIRO_ANTIALIAS_GOOD;
+            cairo_hint_style_t                       hintStyle = 
CAIRO_HINT_STYLE_SLIGHT;
+            bool                                     ellipsize = false;
+            bool                                     wrap      = true;
+        };
+
+        CTextResource(STextResourceData&& data);
+        virtual ~CTextResource() = default;
+
+        virtual void render();
+
+      private:
+        STextResourceData m_data;
+    };
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/nix/default.nix 
new/hyprgraphics-0.3.0/nix/default.nix
--- old/hyprgraphics-0.1.5/nix/default.nix      2025-07-10 14:05:40.000000000 
+0200
+++ new/hyprgraphics-0.3.0/nix/default.nix      2025-11-06 21:47:32.000000000 
+0100
@@ -7,10 +7,13 @@
   cairo,
   file,
   hyprutils,
+  libheif,
   libjpeg,
   libjxl,
+  librsvg,
   libspng,
   libwebp,
+  pango,
   pixman,
   version ? "git",
   doCheck ? false,
@@ -49,10 +52,13 @@
       cairo
       file
       hyprutils
+      libheif
       libjpeg
       libjxl
+      librsvg
       libspng
       libwebp
+      pango
       pixman
     ];
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/src/color/Color.cpp 
new/hyprgraphics-0.3.0/src/color/Color.cpp
--- old/hyprgraphics-0.1.5/src/color/Color.cpp  2025-07-10 14:05:40.000000000 
+0200
+++ new/hyprgraphics-0.3.0/src/color/Color.cpp  2025-11-06 21:47:32.000000000 
+0100
@@ -37,9 +37,15 @@
         );
 
     return CMatrix3(std::array<std::array<double, 3>, 3>{
-        (m[1][1] * m[2][2] - m[2][1] * m[1][2]) * invDet, (m[0][2] * m[2][1] - 
m[0][1] * m[2][2]) * invDet, (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * invDet, 
//
-        (m[1][2] * m[2][0] - m[1][0] * m[2][2]) * invDet, (m[0][0] * m[2][2] - 
m[0][2] * m[2][0]) * invDet, (m[1][0] * m[0][2] - m[0][0] * m[1][2]) * invDet, 
//
-        (m[1][0] * m[2][1] - m[2][0] * m[1][1]) * invDet, (m[2][0] * m[0][1] - 
m[0][0] * m[2][1]) * invDet, (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * invDet, 
//
+        (m[1][1] * m[2][2] - m[2][1] * m[1][2]) * invDet,
+        (m[0][2] * m[2][1] - m[0][1] * m[2][2]) * invDet,
+        (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * invDet, //
+        (m[1][2] * m[2][0] - m[1][0] * m[2][2]) * invDet,
+        (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * invDet,
+        (m[1][0] * m[0][2] - m[0][0] * m[1][2]) * invDet, //
+        (m[1][0] * m[2][1] - m[2][0] * m[1][1]) * invDet,
+        (m[2][0] * m[0][1] - m[0][0] * m[2][1]) * invDet,
+        (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * invDet, //
     });
 }
 
@@ -69,9 +75,15 @@
 
 const CMatrix3& CMatrix3::identity() {
     static const CMatrix3 Identity3 = CMatrix3(std::array<std::array<double, 
3>, 3>{
-        1, 0, 0, //
-        0, 1, 0, //
-        0, 0, 1, //
+        1,
+        0,
+        0, //
+        0,
+        1,
+        0, //
+        0,
+        0,
+        1, //
     });
     return Identity3;
 }
@@ -84,14 +96,20 @@
 }
 
 static CMatrix3 Bradford = CMatrix3(std::array<std::array<double, 3>, 3>{
-    0.8951, 0.2664, -0.1614, //
-    -0.7502, 1.7135, 0.0367, //
-    0.0389, -0.0685, 1.0296, //
+    0.8951,
+    0.2664,
+    -0.1614, //
+    -0.7502,
+    1.7135,
+    0.0367, //
+    0.0389,
+    -0.0685,
+    1.0296, //
 });
 
 static CMatrix3 BradfordInv = Bradford.invert();
 
-CMatrix3 Hyprgraphics::adaptWhite(const CColor::xy& src, const CColor::xy& 
dst) {
+CMatrix3        Hyprgraphics::adaptWhite(const CColor::xy& src, const 
CColor::xy& dst) {
     if (src == dst)
         return CMatrix3::identity();
 
@@ -101,9 +119,15 @@
 
     return BradfordInv *
         CMatrix3(std::array<std::array<double, 3>, 3>{
-            factors.x, 0.0, 0.0, //
-            0.0, factors.y, 0.0, //
-            0.0, 0.0, factors.z, //
+            factors.x,
+            0.0,
+            0.0, //
+            0.0,
+            factors.y,
+            0.0, //
+            0.0,
+            0.0,
+            factors.z, //
         }) *
         Bradford;
 }
@@ -115,9 +139,15 @@
     const auto w = xy2xyz(white);
 
     const auto invMat = CMatrix3(std::array<std::array<double, 3>, 3>{
-                                     r.x, g.x, b.x, //
-                                     r.y, g.y, b.y, //
-                                     r.z, g.z, b.z, //
+                                     r.x,
+                                     g.x,
+                                     b.x, //
+                                     r.y,
+                                     g.y,
+                                     b.y, //
+                                     r.z,
+                                     g.z,
+                                     b.z, //
                                  })
                             .invert();
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/src/image/Image.cpp 
new/hyprgraphics-0.3.0/src/image/Image.cpp
--- old/hyprgraphics-0.1.5/src/image/Image.cpp  2025-07-10 14:05:40.000000000 
+0200
+++ new/hyprgraphics-0.3.0/src/image/Image.cpp  2025-11-06 21:47:32.000000000 
+0100
@@ -4,21 +4,34 @@
 #ifdef JXL_FOUND
 #include "formats/JpegXL.hpp"
 #endif
+#ifdef HEIF_FOUND
+#include "formats/Avif.hpp"
+#endif
 #include "formats/Webp.hpp"
 #include "formats/Png.hpp"
+#include "formats/Svg.hpp"
 #include <magic.h>
 #include <format>
 
 using namespace Hyprgraphics;
 using namespace Hyprutils::Memory;
+using namespace Hyprutils::Math;
 
-Hyprgraphics::CImage::CImage(const std::span<uint8_t>& data, eImageFormat 
format) {
+Hyprgraphics::CImage::CImage(const std::span<const uint8_t> data, eImageFormat 
format) {
     std::expected<cairo_surface_t*, std::string> CAIROSURFACE;
     if (format == eImageFormat::IMAGE_FORMAT_PNG) {
         CAIROSURFACE = PNG::createSurfaceFromPNG(data);
         mime         = "image/png";
+    } else if (format == eImageFormat::IMAGE_FORMAT_AVIF) {
+#ifndef HEIF_FOUND
+        lastError = "hyprgraphics compiled without HEIF support";
+        return;
+#else
+        CAIROSURFACE = AVIF::createSurfaceFromAvif(data);
+        mime         = "image/avif";
+#endif
     } else {
-        lastError = "Currently only PNG images are supported for embedding";
+        lastError = "Currently only PNG and AVIF images are supported for 
embedding";
         return;
     }
 
@@ -36,24 +49,26 @@
     pCairoSurface = makeShared<CCairoSurface>(CAIROSURFACE.value());
 }
 
-Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
+Hyprgraphics::CImage::CImage(const std::string& path, const Vector2D& size) : 
filepath(path), m_svgSize(size) {
     std::expected<cairo_surface_t*, std::string> CAIROSURFACE;
-    const auto                                   len = path.length();
-    if (path.find(".png") == len - 4 || path.find(".PNG") == len - 4) {
+    if (path.ends_with(".png") || path.ends_with(".PNG")) {
         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) {
+    } else if (path.ends_with(".jpg") || path.ends_with(".JPG") || 
path.ends_with(".jpeg") || path.ends_with(".JPEG")) {
         CAIROSURFACE  = JPEG::createSurfaceFromJPEG(path);
         imageHasAlpha = false;
         mime          = "image/jpeg";
-    } else if (path.find(".bmp") == len - 4 || path.find(".BMP") == len - 4) {
+    } else if (path.ends_with(".bmp") || path.ends_with(".BMP")) {
         CAIROSURFACE  = BMP::createSurfaceFromBMP(path);
         imageHasAlpha = false;
         mime          = "image/bmp";
-    } else if (path.find(".webp") == len - 5 || path.find(".WEBP") == len - 5) 
{
+    } else if (path.ends_with(".webp") || path.ends_with(".WEBP")) {
         CAIROSURFACE = WEBP::createSurfaceFromWEBP(path);
         mime         = "image/webp";
-    } else if (path.find(".jxl") == len - 4 || path.find(".JXL") == len - 4) {
+    } else if (path.ends_with(".svg") || path.ends_with(".SVG")) {
+        CAIROSURFACE = SVG::createSurfaceFromSVG(path, m_svgSize);
+        mime         = "image/svg";
+    } else if (path.ends_with(".jxl") || path.ends_with(".JXL")) {
 
 #ifdef JXL_FOUND
         CAIROSURFACE = JXL::createSurfaceFromJXL(path);
@@ -63,12 +78,25 @@
         return;
 #endif
 
+    } else if (path.ends_with(".avif") || path.ends_with(".AVIF")) {
+
+#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()));
+        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") {
@@ -86,10 +114,22 @@
             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);
             imageHasAlpha = false;
             mime          = "image/bmp";
+        } else if (first_word == "SVG") {
+            CAIROSURFACE  = SVG::createSurfaceFromSVG(path, m_svgSize);
+            imageHasAlpha = false;
+            mime          = "image/svg";
         } else {
             lastError = "unrecognized image";
             return;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/src/image/formats/Avif.cpp 
new/hyprgraphics-0.3.0/src/image/formats/Avif.cpp
--- old/hyprgraphics-0.1.5/src/image/formats/Avif.cpp   1970-01-01 
01:00:00.000000000 +0100
+++ new/hyprgraphics-0.3.0/src/image/formats/Avif.cpp   2025-11-06 
21:47:32.000000000 +0100
@@ -0,0 +1,92 @@
+#include "Avif.hpp"
+#include <cairo.h>
+#include <cstdint>
+#include <cstring>
+#include <expected>
+#include <filesystem>
+#include <hyprutils/utils/ScopeGuard.hpp>
+#include <libheif/heif.h>
+#include <vector>
+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;
+    struct heif_error err = heif_decode_image(handle, &img, 
heif_colorspace_RGB, heif_chroma_interleaved_RGBA, nullptr);
+
+    if (err.code != heif_error_Ok)
+        return std::unexpected("loading avif: failed to decode image");
+
+    size_t width  = heif_image_get_width(img, heif_channel_interleaved);
+    size_t height = heif_image_get_height(img, heif_channel_interleaved);
+
+    if (width == static_cast<size_t>(-1) || height == static_cast<size_t>(-1))
+        return std::unexpected("loading avif: failed to get width or height");
+
+    int            stride;
+    const uint8_t* data = heif_image_get_plane_readonly(img, 
heif_channel_interleaved, &stride);
+
+    if (!data)
+        return std::unexpected("loading avif: get_plane_readonly failed");
+
+    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();
+    struct heif_error err = heif_context_read_from_file(ctx, path.c_str(), 
nullptr);
+
+    if (err.code != heif_error_Ok)
+        return std::unexpected("loading avif: failed to load from file");
+
+    auto result = loadFromContext(ctx);
+    heif_context_free(ctx);
+
+    return result;
+}
+std::expected<cairo_surface_t*, std::string> AVIF::createSurfaceFromAvif(const 
std::span<const uint8_t> buf) {
+    heif_context*     ctx = heif_context_alloc();
+    struct heif_error err = heif_context_read_from_memory(ctx, buf.data(), 
buf.size(), nullptr);
+
+    if (err.code != heif_error_Ok)
+        return std::unexpected("loading avif: failed to load from memory");
+
+    auto result = loadFromContext(ctx);
+    heif_context_free(ctx);
+
+    return result;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/src/image/formats/Avif.hpp 
new/hyprgraphics-0.3.0/src/image/formats/Avif.hpp
--- old/hyprgraphics-0.1.5/src/image/formats/Avif.hpp   1970-01-01 
01:00:00.000000000 +0100
+++ new/hyprgraphics-0.3.0/src/image/formats/Avif.hpp   2025-11-06 
21:47:32.000000000 +0100
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <cairo/cairo.h>
+#include <cstdint>
+#include <span>
+#include <string>
+#include <expected>
+
+namespace AVIF {
+    std::expected<cairo_surface_t*, std::string> createSurfaceFromAvif(const 
std::string&);
+    std::expected<cairo_surface_t*, std::string> createSurfaceFromAvif(const 
std::span<const uint8_t>);
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/src/image/formats/Png.cpp 
new/hyprgraphics-0.3.0/src/image/formats/Png.cpp
--- old/hyprgraphics-0.1.5/src/image/formats/Png.cpp    2025-07-10 
14:05:40.000000000 +0200
+++ new/hyprgraphics-0.3.0/src/image/formats/Png.cpp    2025-11-06 
21:47:32.000000000 +0100
@@ -38,8 +38,8 @@
 }
 
 struct SReadState {
-    const std::span<uint8_t>& data;
-    size_t                    offset;
+    const std::span<const uint8_t> data;
+    size_t                         offset;
 };
 
 static void customReadFunction(png_structp png, png_bytep data, png_size_t 
length) {
@@ -53,7 +53,7 @@
     state->offset += length;
 }
 
-std::expected<cairo_surface_t*, std::string> PNG::createSurfaceFromPNG(const 
std::span<uint8_t>& data) {
+std::expected<cairo_surface_t*, std::string> PNG::createSurfaceFromPNG(const 
std::span<const uint8_t> data) {
     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)
@@ -86,13 +86,13 @@
         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 (COLOR_TYPE == PNG_COLOR_TYPE_GRAY || COLOR_TYPE == 
PNG_COLOR_TYPE_GRAY_ALPHA)
+        png_set_gray_to_rgb(png);
     if (png_get_valid(png, info, PNG_INFO_tRNS))
         png_set_tRNS_to_alpha(png);
 
     if (COLOR_TYPE == PNG_COLOR_TYPE_RGB || COLOR_TYPE == PNG_COLOR_TYPE_GRAY 
|| COLOR_TYPE == PNG_COLOR_TYPE_PALETTE)
         png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
-    else if (COLOR_TYPE == PNG_COLOR_TYPE_GRAY_ALPHA)
-        png_set_gray_to_rgb(png);
 
     png_read_update_info(png, info);
 
@@ -107,10 +107,10 @@
     png_read_image(png, rowPointers.data());
 
     for (size_t i = 0; i < W * H * 4; i += 4) {
-        uint8_t r               = rawData[i + 0];
-        uint8_t g               = rawData[i + 1];
-        uint8_t b               = rawData[i + 2];
-        uint8_t a               = rawData[i + 3];
+        uint8_t r = rawData[i + 0];
+        uint8_t g = rawData[i + 1];
+        uint8_t b = rawData[i + 2];
+        uint8_t a = rawData[i + 3];
 
         r *= ((float)a) / 255.F;
         g *= ((float)a) / 255.F;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/src/image/formats/Png.hpp 
new/hyprgraphics-0.3.0/src/image/formats/Png.hpp
--- old/hyprgraphics-0.1.5/src/image/formats/Png.hpp    2025-07-10 
14:05:40.000000000 +0200
+++ new/hyprgraphics-0.3.0/src/image/formats/Png.hpp    2025-11-06 
21:47:32.000000000 +0100
@@ -9,5 +9,5 @@
 
 namespace PNG {
     std::expected<cairo_surface_t*, std::string> createSurfaceFromPNG(const 
std::string&);
-    std::expected<cairo_surface_t*, std::string> createSurfaceFromPNG(const 
std::span<uint8_t>&);
+    std::expected<cairo_surface_t*, std::string> createSurfaceFromPNG(const 
std::span<const uint8_t>);
 };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/src/image/formats/Svg.cpp 
new/hyprgraphics-0.3.0/src/image/formats/Svg.cpp
--- old/hyprgraphics-0.1.5/src/image/formats/Svg.cpp    1970-01-01 
01:00:00.000000000 +0100
+++ new/hyprgraphics-0.3.0/src/image/formats/Svg.cpp    2025-11-06 
21:47:32.000000000 +0100
@@ -0,0 +1,63 @@
+#include "Svg.hpp"
+#include <filesystem>
+#include <fstream>
+#include <librsvg/rsvg.h>
+#include <hyprutils/utils/ScopeGuard.hpp>
+#include <hyprutils/string/String.hpp>
+
+using namespace Hyprutils::Utils;
+using namespace Hyprutils::Math;
+using namespace Hyprutils::String;
+
+static std::optional<std::string> readFileAsString(const std::string& path) {
+    std::error_code ec;
+
+    if (!std::filesystem::exists(path, ec) || ec)
+        return std::nullopt;
+
+    std::ifstream file(path);
+    if (!file.good())
+        return std::nullopt;
+
+    return trim(std::string((std::istreambuf_iterator<char>(file)), 
(std::istreambuf_iterator<char>())));
+}
+
+std::expected<cairo_surface_t*, std::string> SVG::createSurfaceFromSVG(const 
std::string& path, const Vector2D& size) {
+    if (!std::filesystem::exists(path))
+        return std::unexpected("loading svg: file doesn't exist");
+
+    if (size.x < 1 || size.y < 1)
+        return std::unexpected("loading svg: invalid size");
+
+    auto       cairoSurface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 
size.x, size.y);
+
+    const auto PCAIRO = cairo_create(cairoSurface);
+
+    cairo_save(PCAIRO);
+    cairo_set_operator(PCAIRO, CAIRO_OPERATOR_CLEAR);
+    cairo_paint(PCAIRO);
+    cairo_restore(PCAIRO);
+
+    GError* error = nullptr;
+    auto    file  = readFileAsString(path);
+
+    if (!file)
+        return std::unexpected("loading png: file doesn't exist / 
inaccessible");
+
+    RsvgHandle* handle = rsvg_handle_new_from_data((unsigned 
char*)file->data(), file->size(), &error);
+
+    if (!handle)
+        return std::unexpected("loading svg: rsvg failed to read data");
+
+    RsvgRectangle rect = {0, 0, (double)size.x, (double)size.y};
+
+    if (!rsvg_handle_render_document(handle, PCAIRO, &rect, &error))
+        return std::unexpected("loading svg: rsvg failed to render");
+
+    // done
+    cairo_surface_flush(cairoSurface);
+    cairo_destroy(PCAIRO);
+    g_object_unref(handle);
+
+    return cairoSurface;
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/src/image/formats/Svg.hpp 
new/hyprgraphics-0.3.0/src/image/formats/Svg.hpp
--- old/hyprgraphics-0.1.5/src/image/formats/Svg.hpp    1970-01-01 
01:00:00.000000000 +0100
+++ new/hyprgraphics-0.3.0/src/image/formats/Svg.hpp    2025-11-06 
21:47:32.000000000 +0100
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <cairo/cairo.h>
+#include <string>
+#include <expected>
+#include <png.h>
+#include <hyprutils/math/Vector2D.hpp>
+
+namespace SVG {
+    std::expected<cairo_surface_t*, std::string> createSurfaceFromSVG(const 
std::string&, const Hyprutils::Math::Vector2D& size);
+};
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/src/resource/AsyncResourceGatherer.cpp 
new/hyprgraphics-0.3.0/src/resource/AsyncResourceGatherer.cpp
--- old/hyprgraphics-0.1.5/src/resource/AsyncResourceGatherer.cpp       
1970-01-01 01:00:00.000000000 +0100
+++ new/hyprgraphics-0.3.0/src/resource/AsyncResourceGatherer.cpp       
2025-11-06 21:47:32.000000000 +0100
@@ -0,0 +1,63 @@
+#include <hyprgraphics/resource/AsyncResourceGatherer.hpp>
+
+using namespace Hyprgraphics;
+
+CAsyncResourceGatherer::CAsyncResourceGatherer() {
+    m_gatherThread = std::thread([this]() { asyncAssetSpinLock(); });
+}
+
+CAsyncResourceGatherer::~CAsyncResourceGatherer() {
+    m_asyncLoopState.exit = true;
+    wakeUpMainThread();
+
+    if (m_gatherThread.joinable())
+        m_gatherThread.join();
+}
+
+void CAsyncResourceGatherer::wakeUpMainThread() {
+    m_asyncLoopState.needsToProcess = true;
+    m_asyncLoopState.requestsCV.notify_all();
+}
+
+void 
CAsyncResourceGatherer::enqueue(Hyprutils::Memory::CAtomicSharedPointer<IAsyncResource>
 resource) {
+    {
+        std::lock_guard<std::mutex> lg(m_targetsToLoadMutex);
+        m_targetsToLoad.emplace_back(resource);
+    }
+
+    wakeUpMainThread();
+}
+
+void CAsyncResourceGatherer::asyncAssetSpinLock() {
+    while (!m_asyncLoopState.exit) {
+
+        std::unique_lock lk(m_asyncLoopState.requestMutex);
+        if (!m_asyncLoopState.needsToProcess) // avoid a lock if a thread 
managed to request something already since we .unlock()ed
+            m_asyncLoopState.requestsCV.wait_for(lk, std::chrono::seconds(5), 
[this] { return m_asyncLoopState.needsToProcess; }); // wait for events
+
+        if (m_asyncLoopState.exit)
+            break;
+
+        m_asyncLoopState.needsToProcess = false;
+        lk.unlock();
+        m_targetsToLoadMutex.lock();
+
+        if (m_targetsToLoad.empty()) {
+            m_targetsToLoadMutex.unlock();
+            continue;
+        }
+
+        auto requests = m_targetsToLoad;
+        m_targetsToLoad.clear();
+
+        m_targetsToLoadMutex.unlock();
+
+        // process requests
+        for (auto& r : requests) {
+            r->render();
+
+            r->m_ready = true;
+            r->m_events.finished.emit();
+        }
+    }
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/src/resource/resources/ImageResource.cpp 
new/hyprgraphics-0.3.0/src/resource/resources/ImageResource.cpp
--- old/hyprgraphics-0.1.5/src/resource/resources/ImageResource.cpp     
1970-01-01 01:00:00.000000000 +0100
+++ new/hyprgraphics-0.3.0/src/resource/resources/ImageResource.cpp     
2025-11-06 21:47:32.000000000 +0100
@@ -0,0 +1,25 @@
+#include <hyprgraphics/image/Image.hpp>
+#include <hyprgraphics/resource/resources/ImageResource.hpp>
+#include <hyprutils/memory/Atomic.hpp>
+#include <hyprutils/memory/Casts.hpp>
+
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+
+using namespace Hyprgraphics;
+using namespace Hyprutils::Memory;
+
+CImageResource::CImageResource(const std::string& path) : m_path(path) {
+    ;
+}
+
+CImageResource::CImageResource(const std::string& svg, const 
Hyprutils::Math::Vector2D& size) : m_path(svg), m_svgSize(size) {
+    ;
+}
+
+void CImageResource::render() {
+    auto image = CImage(m_path, m_svgSize);
+
+    m_asset.cairoSurface = image.cairoSurface();
+    m_asset.pixelSize    = m_asset.cairoSurface && 
m_asset.cairoSurface->cairo() ? m_asset.cairoSurface->size() : 
Hyprutils::Math::Vector2D{};
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/src/resource/resources/StaticImageResource.cpp 
new/hyprgraphics-0.3.0/src/resource/resources/StaticImageResource.cpp
--- old/hyprgraphics-0.1.5/src/resource/resources/StaticImageResource.cpp       
1970-01-01 01:00:00.000000000 +0100
+++ new/hyprgraphics-0.3.0/src/resource/resources/StaticImageResource.cpp       
2025-11-06 21:47:32.000000000 +0100
@@ -0,0 +1,20 @@
+#include <hyprgraphics/resource/resources/StaticImageResource.hpp>
+#include <hyprutils/memory/Atomic.hpp>
+#include <hyprutils/memory/Casts.hpp>
+
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+
+using namespace Hyprgraphics;
+using namespace Hyprutils::Memory;
+
+CStaticImageResource::CStaticImageResource(const std::span<const uint8_t> 
data, eImageFormat format) : m_data(data), m_format(format) {
+    ;
+}
+
+void CStaticImageResource::render() {
+    auto image = CImage(m_data, m_format);
+
+    m_asset.cairoSurface = image.cairoSurface();
+    m_asset.pixelSize    = m_asset.cairoSurface && 
m_asset.cairoSurface->cairo() ? m_asset.cairoSurface->size() : 
Hyprutils::Math::Vector2D{};
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/src/resource/resources/TextResource.cpp 
new/hyprgraphics-0.3.0/src/resource/resources/TextResource.cpp
--- old/hyprgraphics-0.1.5/src/resource/resources/TextResource.cpp      
1970-01-01 01:00:00.000000000 +0100
+++ new/hyprgraphics-0.3.0/src/resource/resources/TextResource.cpp      
2025-11-06 21:47:32.000000000 +0100
@@ -0,0 +1,109 @@
+#include <hyprgraphics/resource/resources/TextResource.hpp>
+#include <hyprutils/memory/Atomic.hpp>
+#include <hyprutils/memory/Casts.hpp>
+
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+
+using namespace Hyprgraphics;
+using namespace Hyprutils::Memory;
+
+CTextResource::CTextResource(CTextResource::STextResourceData&& data) : 
m_data(std::move(data)) {
+    ;
+}
+
+void CTextResource::render() {
+    auto                  CAIROSURFACE = 
makeUnique<CCairoSurface>(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1 
/* dummy value */));
+    auto                  CAIRO        = cairo_create(CAIROSURFACE->cairo());
+
+    PangoLayout*          layout = pango_cairo_create_layout(CAIRO);
+
+    PangoFontDescription* fontDesc = 
pango_font_description_from_string(m_data.font.c_str());
+    pango_font_description_set_size(fontDesc, m_data.fontSize * PANGO_SCALE);
+    pango_layout_set_font_description(layout, fontDesc);
+    pango_font_description_free(fontDesc);
+
+    cairo_font_options_t* options = cairo_font_options_create();
+    cairo_font_options_set_antialias(options, m_data.antialias);
+    cairo_font_options_set_hint_style(options, m_data.hintStyle);
+    pango_cairo_context_set_font_options(pango_layout_get_context(layout), 
options);
+    cairo_font_options_destroy(options);
+
+    PangoAlignment pangoAlign = PANGO_ALIGN_LEFT;
+
+    switch (m_data.align) {
+        case TEXT_ALIGN_LEFT: break;
+        case TEXT_ALIGN_CENTER: pangoAlign = PANGO_ALIGN_CENTER; break;
+        case TEXT_ALIGN_RIGHT: pangoAlign = PANGO_ALIGN_RIGHT; break;
+        default: break;
+    }
+
+    pango_layout_set_alignment(layout, pangoAlign);
+
+    PangoAttrList* attrList = nullptr;
+    GError*        gError   = nullptr;
+    char*          buf      = nullptr;
+    if (pango_parse_markup(m_data.text.c_str(), -1, 0, &attrList, &buf, 
nullptr, &gError))
+        pango_layout_set_text(layout, buf, -1);
+    else {
+        g_error_free(gError);
+        pango_layout_set_text(layout, m_data.text.c_str(), -1);
+    }
+
+    if (!attrList)
+        attrList = pango_attr_list_new();
+
+    if (buf)
+        free(buf);
+
+    pango_attr_list_insert(attrList, pango_attr_scale_new(1));
+    pango_layout_set_attributes(layout, attrList);
+    pango_attr_list_unref(attrList);
+
+    PangoRectangle ink, logical;
+    pango_layout_get_pixel_extents(layout, &ink, &logical);
+
+    if (m_data.maxSize) {
+        if (m_data.ellipsize)
+            pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
+        if (m_data.maxSize->x >= 0)
+            pango_layout_set_width(layout, std::min(logical.width * 
PANGO_SCALE, sc<int>(m_data.maxSize->x * PANGO_SCALE)));
+        if (m_data.maxSize->y >= 0)
+            pango_layout_set_height(layout, std::min(logical.height * 
PANGO_SCALE, sc<int>(m_data.maxSize->y * PANGO_SCALE)));
+        if (m_data.wrap)
+            pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
+
+        pango_layout_get_pixel_extents(layout, &ink, &logical);
+    }
+
+    pango_layout_get_pixel_extents(layout, &ink, &logical);
+
+    // TODO: avoid this?
+    cairo_destroy(CAIRO);
+
+    CAIROSURFACE.reset();
+
+    m_asset.cairoSurface = 
makeShared<CCairoSurface>(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 
logical.width, logical.height));
+    CAIRO                = cairo_create(m_asset.cairoSurface->cairo());
+
+    // clear the pixmap
+    cairo_save(CAIRO);
+    cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR);
+    cairo_paint(CAIRO);
+    cairo_restore(CAIRO);
+
+    // render the thing
+    const auto RGB = m_data.color.asRgb();
+    cairo_set_source_rgba(CAIRO, RGB.r, RGB.g, RGB.b, 1.F);
+
+    cairo_move_to(CAIRO, -logical.x, -logical.y);
+    pango_cairo_show_layout(CAIRO, layout);
+
+    g_object_unref(layout);
+
+    cairo_surface_flush(m_asset.cairoSurface->cairo());
+
+    m_asset.pixelSize = {logical.width, logical.height};
+
+    cairo_destroy(CAIRO);
+}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/tests/arg.cpp 
new/hyprgraphics-0.3.0/tests/arg.cpp
--- old/hyprgraphics-0.1.5/tests/arg.cpp        1970-01-01 01:00:00.000000000 
+0100
+++ new/hyprgraphics-0.3.0/tests/arg.cpp        2025-11-06 21:47:32.000000000 
+0100
@@ -0,0 +1,139 @@
+#include <algorithm>
+#include <print>
+#include <format>
+#include <filesystem>
+#include <fstream>
+#include <vector>
+#include <hyprgraphics/resource/AsyncResourceGatherer.hpp>
+#include <hyprgraphics/resource/resources/TextResource.hpp>
+#include <hyprgraphics/resource/resources/ImageResource.hpp>
+#include <hyprutils/memory/UniquePtr.hpp>
+#include <hyprutils/memory/Atomic.hpp>
+#include <hyprutils/math/Vector2D.hpp>
+#include "shared.hpp"
+
+using namespace Hyprutils::Memory;
+using namespace Hyprutils::Math;
+using namespace Hyprgraphics;
+
+#define UP CUniquePointer
+
+static UP<CAsyncResourceGatherer> g_asyncResourceGatherer;
+
+static struct {
+    std::mutex                                        wakeupMutex;
+    std::condition_variable                           wakeup;
+
+    bool                                              exit           = false;
+    bool                                              needsToProcess = false;
+
+    int                                               loadedAssets = 0;
+
+    std::mutex                                        resourcesMutex;
+    std::vector<CAtomicSharedPointer<IAsyncResource>> resources;
+} state;
+
+//
+
+static bool renderText(const std::string& text, Vector2D max = {}) {
+    // this stinks a bit but it's due to our ASP impl.
+    auto resource =
+        makeAtomicShared<CTextResource>(CTextResource::STextResourceData{.text 
= text, .fontSize = 72, .maxSize = max.x == 0 ? std::nullopt : 
std::optional<Vector2D>(max)});
+    CAtomicSharedPointer<IAsyncResource> resourceGeneric(resource);
+
+    g_asyncResourceGatherer->enqueue(resourceGeneric);
+
+    state.resourcesMutex.lock();
+    state.resources.emplace_back(std::move(resourceGeneric));
+    state.resourcesMutex.unlock();
+
+    resource->m_events.finished.listenStatic([]() {
+        state.needsToProcess = true;
+        state.wakeup.notify_all();
+    });
+
+    std::println("Enqueued \"{}\" successfully.", text);
+
+    return true;
+}
+
+static bool renderImage(const std::string& path) {
+    // this stinks a bit but it's due to our ASP impl.
+    auto                                 resource = 
makeAtomicShared<CImageResource>(path);
+    CAtomicSharedPointer<IAsyncResource> resourceGeneric(resource);
+
+    g_asyncResourceGatherer->enqueue(resourceGeneric);
+
+    state.resourcesMutex.lock();
+    state.resources.emplace_back(std::move(resourceGeneric));
+    state.resourcesMutex.unlock();
+
+    resource->m_events.finished.listenStatic([]() {
+        state.needsToProcess = true;
+        state.wakeup.notify_all();
+    });
+
+    std::println("Enqueued \"{}\" successfully.", path);
+
+    return true;
+}
+
+int main(int argc, char** argv, char** envp) {
+    int ret = 0;
+
+    g_asyncResourceGatherer = makeUnique<CAsyncResourceGatherer>();
+
+    EXPECT(renderText("Hello World"), true);
+    EXPECT(renderText("<b><i>Test markup</i></b>"), true);
+    EXPECT(renderText("Test ellipsis!!!!!", {512, 190}),
+           true);
+    EXPECT(renderImage("./resource/images/hyprland.png"), true);
+
+    while (!state.exit) {
+        std::unique_lock lk(state.wakeupMutex);
+        if (!state.needsToProcess) // avoid a lock if a thread managed to 
request something already since we .unlock()ed
+            state.wakeup.wait_for(lk, std::chrono::seconds(5), [] { return 
state.needsToProcess; }); // wait for events
+
+        if (state.exit)
+            break;
+
+        state.needsToProcess = false;
+
+        state.resourcesMutex.lock();
+
+        const bool SHOULD_EXIT = std::ranges::all_of(state.resources, [](const 
auto& e) { return !!e->m_ready; });
+
+        state.resourcesMutex.unlock();
+
+        if (SHOULD_EXIT)
+            break;
+
+        lk.unlock();
+    }
+
+    // all assets should be done, let's render them
+    size_t idx = 0;
+    for (const auto& r : state.resources) {
+        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 = std::format("render-arg-{}", idx);
+
+        EXPECT(!!r->m_asset.cairoSurface->cairo(), true);
+
+        //NOLINTNEXTLINE
+        if (!r->m_asset.cairoSurface->cairo())
+            continue;
+
+        EXPECT(cairo_surface_write_to_png(r->m_asset.cairoSurface->cairo(), 
(TEST_DIR + "/" + name + ".png").c_str()), CAIRO_STATUS_SUCCESS);
+
+        idx++;
+    }
+
+    g_asyncResourceGatherer.reset();
+
+    return ret;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/hyprgraphics-0.1.5/tests/image.cpp 
new/hyprgraphics-0.3.0/tests/image.cpp
--- old/hyprgraphics-0.1.5/tests/image.cpp      2025-07-10 14:05:40.000000000 
+0200
+++ new/hyprgraphics-0.3.0/tests/image.cpp      2025-11-06 21:47:32.000000000 
+0100
@@ -10,7 +10,7 @@
 using namespace Hyprgraphics;
 
 static bool tryLoadImageFromFile(const std::string& path) {
-    auto image = CImage(path);
+    auto image = path.ends_with("svg") ? CImage(path, {512, 512}) : 
CImage(path);
 
     if (!image.success()) {
         std::println("Failed to load {}: {}", path, image.getError());
@@ -32,8 +32,8 @@
     return cairo_surface_write_to_png(image.cairoSurface()->cairo(), (TEST_DIR 
+ "/" + name + ".png").c_str()) == CAIRO_STATUS_SUCCESS;
 }
 
-static bool tryLoadImageFromBuffer(const std::span<uint8_t>& data) {
-    auto image = CImage(data, IMAGE_FORMAT_PNG);
+static bool tryLoadImageFromBuffer(const std::span<uint8_t>& data, 
eImageFormat format) {
+    auto image = CImage(data, format);
 
     if (!image.success()) {
         std::println("Failed to load embedded image: {}", image.getError());
@@ -58,7 +58,7 @@
 static std::vector<uint8_t> getImageBuffer(const std::string& path) {
     std::vector<uint8_t> buffer;
 
-    std::ifstream        file("./resource/images/hyprland.png", 
std::ios::binary | std::ios::ate);
+    std::ifstream        file(path, std::ios::binary | std::ios::ate);
     std::streamsize      size = file.tellg();
     file.seekg(0, std::ios::beg);
 
@@ -80,11 +80,20 @@
         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);
     }
 
-    auto buffer = getImageBuffer("./resource/images/hyprland.png");
-    EXPECT(tryLoadImageFromBuffer(buffer), true);
+    auto pngBuffer = getImageBuffer("./resource/images/hyprland.png");
+    EXPECT(tryLoadImageFromBuffer(pngBuffer, Hyprgraphics::IMAGE_FORMAT_PNG), 
true);
+
+#ifdef HEIF_FOUND
+    auto avifBuffer = getImageBuffer("./resource/images/hyprland.avif");
+    EXPECT(tryLoadImageFromBuffer(avifBuffer, 
Hyprgraphics::IMAGE_FORMAT_AVIF), true);
+#endif
 
     return ret;
 }
Binary files old/hyprgraphics-0.1.5/tests/resource/images/hyprland.avif and 
new/hyprgraphics-0.3.0/tests/resource/images/hyprland.avif differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/hyprgraphics-0.1.5/tests/resource/images/hyprland.svg 
new/hyprgraphics-0.3.0/tests/resource/images/hyprland.svg
--- old/hyprgraphics-0.1.5/tests/resource/images/hyprland.svg   1970-01-01 
01:00:00.000000000 +0100
+++ new/hyprgraphics-0.3.0/tests/resource/images/hyprland.svg   2025-11-06 
21:47:32.000000000 +0100
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="Livello_1" xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; version="1.1" viewBox="0 0 1006.49 
1006.49">
+   <defs>
+    <style>
+      .st0 {
+        fill: url(#a);
+      }
+    </style>
+    <linearGradient id="a" x1="493.93" y1="901.88" x2="493.93" y2="104.6" 
gradientUnits="userSpaceOnUse">
+      <stop offset="0" stop-color="#00a8f4"/>
+      <stop offset="1" stop-color="#00e5d0"/>
+    </linearGradient>
+  </defs>
+  <path class="st0" 
d="M681.59,325.09c-6.06-8.7-11.78-16.91-17.43-25.14-8.03-11.73-18.9-26.74-31.49-44.09-28.76-39.72-73.68-100.94-104.06-151.25v125.92c31.29,44.88,61.67,85.34,77.88,108.99,35.52,51.8,76.66,107,101.06,162.21,73.34,165.94-29.82,328.29-209.07,330.23h-3.21c-.45,0-.89-.02-1.35-.02-.45,0-.89.02-1.35.02h-3.21c-179.25-1.94-282.41-164.29-209.07-330.23,24.41-55.21,65.54-110.41,101.06-162.21,16.21-23.65,46.59-64.11,77.88-108.99v-125.92c-30.38,50.32-75.3,111.54-104.06,151.25-12.59,17.36-23.45,32.37-31.49,44.09-5.65,8.23-11.37,16.44-17.43,25.14-32.82,47.18-66.78,95.97-89.93,148.35-22.49,50.89-32.35,102.89-29.28,154.54,2.96,50.07,18.54,98.28,45.04,139.46,26.2,40.69,63.03,74.42,106.51,97.55,44.94,23.88,95.4,36.31,150,36.89,1.33.02,2.64.02,3.96.02.45,0,.9-.02,1.35-.02.45,0,.89.02,1.35.02,1.33,0,2.64,0,3.96-.02,54.6-.57,105.06-13,150-36.89,43.48-23.13,80.32-56.86,106.51-97.55,26.5-41.17,42.09-89.39,45.04-139.46,3.07-51.64-6.8-103.65-29.28-154.54-23.15-52.38-57.11-101.17-89.93-148.35
 Z"/>
+</svg>

Reply via email to