mirror of
https://github.com/hyprwm/hyprgraphics.git
synced 2026-05-07 20:08:18 +02:00
Compare commits
66 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 68d0644347 | |||
|
|
04d7d9f0e5 | ||
|
|
cf95d93d17 | ||
| 482d4b7ec3 | |||
| 7d63c04b4a | |||
| b803731e52 | |||
| d3d6bf63d8 | |||
|
|
7c75487edd | ||
|
|
13c536659d | ||
|
|
583331e065 | ||
|
|
f114ea3d97 | ||
| 4af02a3925 | |||
| 790adab7eb | |||
| 6cfc891808 | |||
| 28e17fbf49 | |||
| 8a860f2949 | |||
| 8f1bec691b | |||
| 1fb5bfbd62 | |||
| ffc999d980 | |||
| e381b2f1f0 | |||
|
|
50fb9f0692 | ||
| 9431db625c | |||
| 2503063d75 | |||
| 899ff15787 | |||
| f4995eaa46 | |||
| 32e6b8386f | |||
| 72e6801f08 | |||
| d839c3f808 | |||
| ecdbae40ae | |||
| 758678a614 | |||
| c44e749dd6 | |||
|
|
b86c4d9ed3 | ||
|
|
aa9d14963b | ||
|
|
621e2e00f1 | ||
|
|
157cc52065 | ||
|
|
4c1d63a0f2 | ||
| 340494a38b | |||
|
|
83885a6edf | ||
| b841473a0b | |||
|
|
a71c0529d1 | ||
|
|
5f9c68e3f8 | ||
|
|
13375fa03f | ||
| b3d628d016 | |||
| 15c6f8f3a5 | |||
| c7225d7375 | |||
|
|
80b754e38e | ||
|
|
df811098c1 | ||
|
|
6075491094 | ||
| 9d7f2687c8 | |||
|
|
760d67a2a8 | ||
| 175c6b29b6 | |||
|
|
575ae47b78 | ||
| e19ee9031a | |||
|
|
5ac80e3686 | ||
| 12cd7034e4 | |||
| 23783b9603 | |||
| 6355b72d9c | |||
| 0c11438de4 | |||
| 0d77b4895a | |||
|
|
52202272d8 | ||
|
|
b09980755d | ||
| 6dea3fba08 | |||
|
|
0f9b8ca692 | ||
|
|
fb2c026864 | ||
| cc95e5babc | |||
| 7ba28704d3 |
44 changed files with 2225 additions and 158 deletions
101
.clang-tidy
Normal file
101
.clang-tidy
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
WarningsAsErrors: '*'
|
||||||
|
HeaderFilterRegex: '.*\.hpp'
|
||||||
|
FormatStyle: 'file'
|
||||||
|
Checks: >
|
||||||
|
-*,
|
||||||
|
bugprone-*,
|
||||||
|
-bugprone-easily-swappable-parameters,
|
||||||
|
-bugprone-forward-declaration-namespace,
|
||||||
|
-bugprone-forward-declaration-namespace,
|
||||||
|
-bugprone-macro-parentheses,
|
||||||
|
-bugprone-narrowing-conversions,
|
||||||
|
-bugprone-branch-clone,
|
||||||
|
-bugprone-assignment-in-if-condition,
|
||||||
|
concurrency-*,
|
||||||
|
-concurrency-mt-unsafe,
|
||||||
|
cppcoreguidelines-*,
|
||||||
|
-cppcoreguidelines-owning-memory,
|
||||||
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
|
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||||
|
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
||||||
|
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||||
|
-cppcoreguidelines-avoid-goto,
|
||||||
|
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||||
|
-cppcoreguidelines-avoid-do-while,
|
||||||
|
-cppcoreguidelines-avoid-non-const-global-variables,
|
||||||
|
-cppcoreguidelines-special-member-functions,
|
||||||
|
-cppcoreguidelines-explicit-virtual-functions,
|
||||||
|
-cppcoreguidelines-avoid-c-arrays,
|
||||||
|
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||||
|
-cppcoreguidelines-narrowing-conversions,
|
||||||
|
-cppcoreguidelines-pro-type-union-access,
|
||||||
|
-cppcoreguidelines-pro-type-member-init,
|
||||||
|
-cppcoreguidelines-macro-usage,
|
||||||
|
-cppcoreguidelines-macro-to-enum,
|
||||||
|
-cppcoreguidelines-init-variables,
|
||||||
|
-cppcoreguidelines-pro-type-cstyle-cast,
|
||||||
|
-cppcoreguidelines-pro-type-vararg,
|
||||||
|
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||||
|
google-global-names-in-headers,
|
||||||
|
-google-readability-casting,
|
||||||
|
google-runtime-operator,
|
||||||
|
misc-*,
|
||||||
|
-misc-unused-parameters,
|
||||||
|
-misc-no-recursion,
|
||||||
|
-misc-non-private-member-variables-in-classes,
|
||||||
|
-misc-include-cleaner,
|
||||||
|
-misc-use-anonymous-namespace,
|
||||||
|
-misc-const-correctness,
|
||||||
|
modernize-*,
|
||||||
|
-modernize-return-braced-init-list,
|
||||||
|
-modernize-use-trailing-return-type,
|
||||||
|
-modernize-use-using,
|
||||||
|
-modernize-use-override,
|
||||||
|
-modernize-avoid-c-arrays,
|
||||||
|
-modernize-macro-to-enum,
|
||||||
|
-modernize-loop-convert,
|
||||||
|
-modernize-use-nodiscard,
|
||||||
|
-modernize-pass-by-value,
|
||||||
|
-modernize-use-auto,
|
||||||
|
performance-*,
|
||||||
|
-performance-avoid-endl,
|
||||||
|
-performance-unnecessary-value-param,
|
||||||
|
portability-std-allocator-const,
|
||||||
|
readability-*,
|
||||||
|
-readability-function-cognitive-complexity,
|
||||||
|
-readability-function-size,
|
||||||
|
-readability-identifier-length,
|
||||||
|
-readability-magic-numbers,
|
||||||
|
-readability-uppercase-literal-suffix,
|
||||||
|
-readability-braces-around-statements,
|
||||||
|
-readability-redundant-access-specifiers,
|
||||||
|
-readability-else-after-return,
|
||||||
|
-readability-container-data-pointer,
|
||||||
|
-readability-implicit-bool-conversion,
|
||||||
|
-readability-avoid-nested-conditional-operator,
|
||||||
|
-readability-redundant-member-init,
|
||||||
|
-readability-redundant-string-init,
|
||||||
|
-readability-avoid-const-params-in-decls,
|
||||||
|
-readability-named-parameter,
|
||||||
|
-readability-convert-member-functions-to-static,
|
||||||
|
-readability-qualified-auto,
|
||||||
|
-readability-make-member-function-const,
|
||||||
|
-readability-isolate-declaration,
|
||||||
|
-readability-inconsistent-declaration-parameter-name,
|
||||||
|
-clang-diagnostic-error,
|
||||||
|
|
||||||
|
CheckOptions:
|
||||||
|
performance-for-range-copy.WarnOnAllAutoCopies: true
|
||||||
|
performance-inefficient-string-concatenation.StrictMode: true
|
||||||
|
readability-braces-around-statements.ShortStatementLines: 0
|
||||||
|
readability-identifier-naming.ClassCase: CamelCase
|
||||||
|
readability-identifier-naming.ClassIgnoredRegexp: I.*
|
||||||
|
readability-identifier-naming.ClassPrefix: C # We can't use regex here?!?!?!?
|
||||||
|
readability-identifier-naming.EnumCase: CamelCase
|
||||||
|
readability-identifier-naming.EnumPrefix: e
|
||||||
|
readability-identifier-naming.EnumConstantCase: UPPER_CASE
|
||||||
|
readability-identifier-naming.FunctionCase: camelBack
|
||||||
|
readability-identifier-naming.NamespaceCase: CamelCase
|
||||||
|
readability-identifier-naming.NamespacePrefix: N
|
||||||
|
readability-identifier-naming.StructPrefix: S
|
||||||
|
readability-identifier-naming.StructCase: CamelCase
|
||||||
4
.github/workflows/arch.yml
vendored
4
.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++ pango pixman cairo hyprutils libdrm libglvnd libjpeg-turbo libjxl libwebp libpng ttf-dejavu librsvg
|
||||||
|
|
||||||
- name: Build hyprgraphics with gcc
|
- name: Build hyprgraphics with gcc
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -44,7 +44,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++ pango pixman cairo hyprutils libdrm libglvnd libjpeg-turbo libjxl libwebp libpng ttf-dejavu librsvg
|
||||||
|
|
||||||
- name: Build hyprgraphics with clang
|
- name: Build hyprgraphics with clang
|
||||||
run: |
|
run: |
|
||||||
|
|
|
||||||
22
.github/workflows/nix.yml
vendored
22
.github/workflows/nix.yml
vendored
|
|
@ -1,6 +1,7 @@
|
||||||
name: Build & Test
|
name: Build & Test
|
||||||
|
|
||||||
on: [push, pull_request, workflow_dispatch]
|
on: [push, pull_request, workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
nix:
|
nix:
|
||||||
strategy:
|
strategy:
|
||||||
|
|
@ -9,19 +10,8 @@ jobs:
|
||||||
- hyprgraphics
|
- hyprgraphics
|
||||||
- hyprgraphics-with-tests
|
- hyprgraphics-with-tests
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
if: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork)
|
||||||
steps:
|
uses: hyprwm/actions/.github/workflows/nix.yml@main
|
||||||
- uses: actions/checkout@v3
|
secrets: inherit
|
||||||
|
with:
|
||||||
- uses: cachix/install-nix-action@v26
|
command: nix build .#${{ matrix.package }} --print-build-logs
|
||||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
|
||||||
|
|
||||||
# not needed (yet)
|
|
||||||
# - uses: cachix/cachix-action@v12
|
|
||||||
# with:
|
|
||||||
# name: hyprland
|
|
||||||
# authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
|
||||||
|
|
||||||
- name: Build & Test
|
|
||||||
run: nix build .#${{ matrix.package }} --print-build-logs
|
|
||||||
|
|
||||||
|
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -44,3 +44,5 @@ Makefile
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
hyprutils.pc
|
hyprutils.pc
|
||||||
|
|
||||||
|
tests/test_output/
|
||||||
|
|
@ -21,6 +21,16 @@ set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR})
|
||||||
configure_file(hyprgraphics.pc.in hyprgraphics.pc @ONLY)
|
configure_file(hyprgraphics.pc.in hyprgraphics.pc @ONLY)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 26)
|
set(CMAKE_CXX_STANDARD 26)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
|
||||||
|
# -Wno-missing-braces for clang
|
||||||
|
add_compile_options(
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wpedantic
|
||||||
|
-Wno-unused-parameter
|
||||||
|
-Wno-unused-value
|
||||||
|
-Wno-missing-field-initializers
|
||||||
|
-Wno-missing-braces)
|
||||||
|
|
||||||
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||||
message(STATUS "Configuring hyprgraphics in Debug")
|
message(STATUS "Configuring hyprgraphics in Debug")
|
||||||
|
|
@ -37,20 +47,51 @@ endif()
|
||||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/*.hpp")
|
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp" "include/*.hpp")
|
||||||
file(GLOB_RECURSE PUBLIC_HEADERS CONFIGURE_DEPENDS "include/*.hpp")
|
file(GLOB_RECURSE PUBLIC_HEADERS CONFIGURE_DEPENDS "include/*.hpp")
|
||||||
|
|
||||||
|
set(GLES_VERSION "GLES3")
|
||||||
|
find_package(OpenGL REQUIRED COMPONENTS ${GLES_VERSION})
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(
|
pkg_check_modules(
|
||||||
deps
|
deps
|
||||||
REQUIRED
|
REQUIRED
|
||||||
IMPORTED_TARGET
|
IMPORTED_TARGET
|
||||||
|
libdrm
|
||||||
pixman-1
|
pixman-1
|
||||||
cairo
|
cairo
|
||||||
|
pangocairo
|
||||||
hyprutils
|
hyprutils
|
||||||
libjpeg
|
libjpeg
|
||||||
libwebp
|
libwebp
|
||||||
|
libmagic
|
||||||
|
libpng
|
||||||
|
librsvg-2.0)
|
||||||
|
|
||||||
|
pkg_check_modules(
|
||||||
|
JXL
|
||||||
|
IMPORTED_TARGET
|
||||||
libjxl
|
libjxl
|
||||||
libjxl_cms
|
libjxl_cms
|
||||||
libjxl_threads
|
libjxl_threads
|
||||||
libmagic)
|
)
|
||||||
|
if(NOT JXL_FOUND)
|
||||||
|
file(GLOB_RECURSE JPEGXLFILES CONFIGURE_DEPENDS "src/*JpegXL.cpp")
|
||||||
|
list(REMOVE_ITEM SRCFILES ${JPEGXLFILES})
|
||||||
|
else()
|
||||||
|
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})
|
add_library(hyprgraphics SHARED ${SRCFILES})
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
|
|
@ -58,8 +99,15 @@ target_include_directories(
|
||||||
PUBLIC "./include"
|
PUBLIC "./include"
|
||||||
PRIVATE "./src")
|
PRIVATE "./src")
|
||||||
set_target_properties(hyprgraphics PROPERTIES VERSION ${HYPRGRAPHICS_VERSION}
|
set_target_properties(hyprgraphics PROPERTIES VERSION ${HYPRGRAPHICS_VERSION}
|
||||||
SOVERSION 0)
|
SOVERSION 4)
|
||||||
target_link_libraries(hyprgraphics PkgConfig::deps)
|
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
|
# tests
|
||||||
add_custom_target(tests)
|
add_custom_target(tests)
|
||||||
|
|
@ -71,6 +119,13 @@ add_test(
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/tests
|
||||||
COMMAND hyprgraphics_image "image")
|
COMMAND hyprgraphics_image "image")
|
||||||
add_dependencies(tests hyprgraphics_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
|
# Installation
|
||||||
install(TARGETS hyprgraphics)
|
install(TARGETS hyprgraphics)
|
||||||
|
|
|
||||||
17
README.md
17
README.md
|
|
@ -6,6 +6,23 @@ Hyprgraphics is a small C++ library with graphics / resource related utilities u
|
||||||
|
|
||||||
Hyprgraphics depends on the ABI stability of the stdlib implementation of your compiler. Sover bumps will be done only for hyprgraphics ABI breaks, not stdlib.
|
Hyprgraphics depends on the ABI stability of the stdlib implementation of your compiler. Sover bumps will be done only for hyprgraphics ABI breaks, not stdlib.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
Requires a compiler with C++26 support.
|
||||||
|
|
||||||
|
Dep list:
|
||||||
|
- pixman-1
|
||||||
|
- cairo
|
||||||
|
- hyprutils
|
||||||
|
- libjpeg
|
||||||
|
- libwebp
|
||||||
|
- libjxl [optional]
|
||||||
|
- libjxl_cms [optional]
|
||||||
|
- libjxl_threads [optional]
|
||||||
|
- libmagic
|
||||||
|
- libpng
|
||||||
|
- librsvg2
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.1.0
|
0.5.1
|
||||||
|
|
|
||||||
12
flake.lock
generated
12
flake.lock
generated
|
|
@ -10,11 +10,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1732288281,
|
"lastModified": 1772459870,
|
||||||
"narHash": "sha256-XTU9B53IjGeJiJ7LstOhuxcRjCOFkQFl01H78sT9Lg4=",
|
"narHash": "sha256-xxkK2Cvqxpf/4UGcJ/TyCwrvmiNWsKsJfFzHMp2bxis=",
|
||||||
"owner": "hyprwm",
|
"owner": "hyprwm",
|
||||||
"repo": "hyprutils",
|
"repo": "hyprutils",
|
||||||
"rev": "b26f33cc1c8a7fd5076e19e2cce3f062dca6351c",
|
"rev": "e63f3a79334dec49f8eb1691f66f18115df04085",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
@ -25,11 +25,11 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1732014248,
|
"lastModified": 1772198003,
|
||||||
"narHash": "sha256-y/MEyuJ5oBWrWAic/14LaIr/u5E0wRVzyYsouYY3W6w=",
|
"narHash": "sha256-I45esRSssFtJ8p/gLHUZ1OUaaTaVLluNkABkk6arQwE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "23e89b7da85c3640bbc2173fe04f4bd114342367",
|
"rev": "dd9b079222d43e1943b6ebd802f04fd959dc8e61",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
|
||||||
64
flake.nix
64
flake.nix
|
|
@ -12,46 +12,32 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs =
|
||||||
self,
|
{
|
||||||
nixpkgs,
|
self,
|
||||||
systems,
|
nixpkgs,
|
||||||
...
|
systems,
|
||||||
} @ inputs: let
|
...
|
||||||
inherit (nixpkgs) lib;
|
}@inputs:
|
||||||
eachSystem = lib.genAttrs (import systems);
|
let
|
||||||
pkgsFor = eachSystem (system:
|
inherit (nixpkgs) lib;
|
||||||
import nixpkgs {
|
eachSystem = lib.genAttrs (import systems);
|
||||||
localSystem.system = system;
|
pkgsFor = eachSystem (
|
||||||
overlays = with self.overlays; [hyprgraphics];
|
system:
|
||||||
|
import nixpkgs {
|
||||||
|
localSystem.system = system;
|
||||||
|
overlays = with self.overlays; [ hyprgraphics-with-deps ];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
overlays = import ./nix/overlays.nix { inherit inputs lib self; };
|
||||||
|
|
||||||
|
packages = eachSystem (system: {
|
||||||
|
default = self.packages.${system}.hyprgraphics;
|
||||||
|
inherit (pkgsFor.${system}) hyprgraphics hyprgraphics-with-tests;
|
||||||
});
|
});
|
||||||
mkDate = longDate: (lib.concatStringsSep "-" [
|
|
||||||
(builtins.substring 0 4 longDate)
|
|
||||||
(builtins.substring 4 2 longDate)
|
|
||||||
(builtins.substring 6 2 longDate)
|
|
||||||
]);
|
|
||||||
|
|
||||||
version = lib.removeSuffix "\n" (builtins.readFile ./VERSION);
|
formatter = eachSystem (system: pkgsFor.${system}.nixfmt-tree);
|
||||||
in {
|
|
||||||
overlays = {
|
|
||||||
default = self.overlays.hyprgraphics;
|
|
||||||
hyprgraphics = lib.composeManyExtensions [
|
|
||||||
inputs.hyprutils.overlays.default
|
|
||||||
(final: prev: {
|
|
||||||
hyprgraphics = final.callPackage ./nix/default.nix {
|
|
||||||
stdenv = final.gcc14Stdenv;
|
|
||||||
version = version + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty");
|
|
||||||
};
|
|
||||||
hyprgraphics-with-tests = final.hyprgraphics.override {doCheck = true;};
|
|
||||||
})
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
packages = eachSystem (system: {
|
|
||||||
default = self.packages.${system}.hyprgraphics;
|
|
||||||
inherit (pkgsFor.${system}) hyprgraphics hyprgraphics-with-tests;
|
|
||||||
});
|
|
||||||
|
|
||||||
formatter = eachSystem (system: pkgsFor.${system}.alejandra);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
94
include/hyprgraphics/color/Color.hpp
Normal file
94
include/hyprgraphics/color/Color.hpp
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
namespace Hyprgraphics {
|
||||||
|
class CColor {
|
||||||
|
public:
|
||||||
|
// SRGB (NOT linear!!) 0.0 - 1.0
|
||||||
|
struct SSRGB {
|
||||||
|
double r = 0, g = 0, b = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// HSL 0.0 - 1.0
|
||||||
|
struct SHSL {
|
||||||
|
double h = 0, s = 0, l = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// OkLab 0.0 - 1.0
|
||||||
|
struct SOkLab {
|
||||||
|
double l = 0, a = 0, b = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// xy 0.0 - 1.0
|
||||||
|
struct xy {
|
||||||
|
double x = 0, y = 0;
|
||||||
|
|
||||||
|
bool operator==(const xy& p2) const {
|
||||||
|
return x == p2.x && y == p2.y;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// XYZ 0.0 - 1.0
|
||||||
|
struct XYZ {
|
||||||
|
double x = 0, y = 0, z = 0;
|
||||||
|
|
||||||
|
// per-component division
|
||||||
|
XYZ operator/(const XYZ& other) const {
|
||||||
|
return {.x = x / other.x, .y = y / other.y, .z = z / other.z};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CColor(); // black
|
||||||
|
CColor(const SSRGB& rgb);
|
||||||
|
CColor(const SHSL& hsl);
|
||||||
|
CColor(const SOkLab& lab);
|
||||||
|
|
||||||
|
SSRGB asRgb() const;
|
||||||
|
SHSL asHSL() const;
|
||||||
|
SOkLab asOkLab() const;
|
||||||
|
|
||||||
|
bool operator==(const CColor& other) const {
|
||||||
|
return other.r == r && other.g == g && other.b == b;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// SRGB space for internal color storage
|
||||||
|
double r = 0, g = 0, b = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3x3 matrix for CM transformations
|
||||||
|
class CMatrix3 {
|
||||||
|
public:
|
||||||
|
CMatrix3() = default;
|
||||||
|
CMatrix3(const std::array<std::array<double, 3>, 3>& values);
|
||||||
|
|
||||||
|
CMatrix3 invert() const;
|
||||||
|
CColor::XYZ operator*(const CColor::XYZ& xyz) const;
|
||||||
|
CMatrix3 operator*(const CMatrix3& other) const;
|
||||||
|
|
||||||
|
const std::array<std::array<double, 3>, 3>& mat();
|
||||||
|
|
||||||
|
static const CMatrix3& identity();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<std::array<double, 3>, 3> m = {
|
||||||
|
0, 0, 0, //
|
||||||
|
0, 0, 0, //
|
||||||
|
0, 0, 0, //
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
CColor::XYZ xy2xyz(const CColor::xy& xy);
|
||||||
|
CMatrix3 adaptWhite(const CColor::xy& src, const CColor::xy& dst);
|
||||||
|
|
||||||
|
struct SPCPRimaries {
|
||||||
|
CColor::xy red, green, blue, white;
|
||||||
|
|
||||||
|
bool operator==(const SPCPRimaries& p2) const {
|
||||||
|
return red == p2.red && green == p2.green && blue == p2.blue && white == p2.white;
|
||||||
|
}
|
||||||
|
|
||||||
|
CMatrix3 toXYZ() const; // toXYZ() * rgb -> xyz
|
||||||
|
CMatrix3 convertMatrix(const SPCPRimaries& dst) const; // convertMatrix(dst) * rgb with "this" primaries -> rgb with dst primaries
|
||||||
|
};
|
||||||
|
};
|
||||||
47
include/hyprgraphics/egl/Egl.hpp
Normal file
47
include/hyprgraphics/egl/Egl.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <GLES3/gl32.h>
|
||||||
|
#include <optional>
|
||||||
|
#include <hyprutils/math/Vector2D.hpp>
|
||||||
|
|
||||||
|
namespace Hyprgraphics::Egl {
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_A1GB{GL_ALPHA, GL_ONE, GL_GREEN, GL_BLUE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_ABG1{GL_ALPHA, GL_BLUE, GL_GREEN, GL_ONE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_ABGR{GL_ALPHA, GL_BLUE, GL_GREEN, GL_RED};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_ARGB{GL_ALPHA, GL_RED, GL_GREEN, GL_BLUE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_B1RG{GL_BLUE, GL_ONE, GL_RED, GL_GREEN};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_BARG{GL_BLUE, GL_ALPHA, GL_RED, GL_GREEN};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_BGR1{GL_BLUE, GL_GREEN, GL_RED, GL_ONE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_BGRA{GL_BLUE, GL_GREEN, GL_RED, GL_ALPHA};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_G1AB{GL_GREEN, GL_ONE, GL_ALPHA, GL_BLUE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_GBA1{GL_GREEN, GL_BLUE, GL_ALPHA, GL_ONE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_GBAR{GL_GREEN, GL_BLUE, GL_ALPHA, GL_RED};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_GRAB{GL_GREEN, GL_RED, GL_ALPHA, GL_BLUE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_R001{GL_RED, GL_ZERO, GL_ZERO, GL_ONE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_R1BG{GL_RED, GL_ONE, GL_BLUE, GL_GREEN};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_RABG{GL_RED, GL_ALPHA, GL_BLUE, GL_GREEN};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_RG01{GL_RED, GL_GREEN, GL_ZERO, GL_ONE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_GR01{GL_GREEN, GL_RED, GL_ZERO, GL_ONE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_RGB1{GL_RED, GL_GREEN, GL_BLUE, GL_ONE};
|
||||||
|
inline constexpr std::array<GLint, 4> SWIZZLE_RGBA{GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
|
||||||
|
|
||||||
|
struct SPixelFormat {
|
||||||
|
uint32_t drmFormat = 0; /* DRM_FORMAT_INVALID */
|
||||||
|
int glInternalFormat = 0;
|
||||||
|
int glFormat = 0;
|
||||||
|
int glType = 0;
|
||||||
|
bool withAlpha = true;
|
||||||
|
uint32_t alphaStripped = 0; /* DRM_FORMAT_INVALID */
|
||||||
|
uint32_t bytesPerBlock = 0;
|
||||||
|
Hyprutils::Math::Vector2D blockSize;
|
||||||
|
std::optional<std::array<GLint, 4>> swizzle = std::nullopt;
|
||||||
|
};
|
||||||
|
|
||||||
|
const SPixelFormat* getPixelFormatFromDRM(uint32_t drmFormat);
|
||||||
|
const SPixelFormat* getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha);
|
||||||
|
bool isDrmFormatOpaque(uint32_t drmFormat);
|
||||||
|
int pixelsPerBlock(const SPixelFormat* const fmt);
|
||||||
|
int minStride(const SPixelFormat* const fmt, int32_t width);
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,29 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <span>
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include "../cairo/CairoSurface.hpp"
|
#include "../cairo/CairoSurface.hpp"
|
||||||
#include <hyprutils/memory/SharedPtr.hpp>
|
#include <hyprutils/memory/SharedPtr.hpp>
|
||||||
|
|
||||||
namespace Hyprgraphics {
|
namespace Hyprgraphics {
|
||||||
|
enum eImageFormat : uint8_t {
|
||||||
|
IMAGE_FORMAT_PNG,
|
||||||
|
IMAGE_FORMAT_AVIF,
|
||||||
|
IMAGE_FORMAT_JPEG,
|
||||||
|
IMAGE_FORMAT_JXL,
|
||||||
|
IMAGE_FORMAT_BMP,
|
||||||
|
IMAGE_FORMAT_SVG,
|
||||||
|
IMAGE_FORMAT_WEBP,
|
||||||
|
|
||||||
|
IMAGE_FORMAT_ERROR,
|
||||||
|
IMAGE_FORMAT_AUTO, // take an educated guess
|
||||||
|
};
|
||||||
|
|
||||||
class CImage {
|
class CImage {
|
||||||
public:
|
public:
|
||||||
// create an image from a provided path.
|
CImage(const std::string& path, const Hyprutils::Math::Vector2D& size = {} /* Ignored if not svg */);
|
||||||
CImage(const std::string& path);
|
CImage(std::span<const uint8_t>, eImageFormat format = IMAGE_FORMAT_AUTO, const Hyprutils::Math::Vector2D& size = {} /* Ignored if not svg */);
|
||||||
~CImage();
|
~CImage();
|
||||||
|
|
||||||
CImage(const CImage&) = delete;
|
CImage(const CImage&) = delete;
|
||||||
|
|
@ -23,8 +37,11 @@ namespace Hyprgraphics {
|
||||||
|
|
||||||
Hyprutils::Memory::CSharedPointer<CCairoSurface> cairoSurface();
|
Hyprutils::Memory::CSharedPointer<CCairoSurface> cairoSurface();
|
||||||
|
|
||||||
|
static bool isImageFile(const std::string& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string lastError, filepath, mime;
|
std::string lastError, filepath, mime;
|
||||||
|
Hyprutils::Math::Vector2D m_svgSize;
|
||||||
Hyprutils::Memory::CSharedPointer<CCairoSurface> pCairoSurface;
|
Hyprutils::Memory::CSharedPointer<CCairoSurface> pCairoSurface;
|
||||||
bool imageHasAlpha = true, loadSuccess = false;
|
bool imageHasAlpha = true, loadSuccess = false;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
41
include/hyprgraphics/resource/AsyncResourceGatherer.hpp
Normal file
41
include/hyprgraphics/resource/AsyncResourceGatherer.hpp
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#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);
|
||||||
|
|
||||||
|
// Synchronously await the resource being available
|
||||||
|
void await(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();
|
||||||
|
};
|
||||||
|
}
|
||||||
37
include/hyprgraphics/resource/resources/AsyncResource.hpp
Normal file
37
include/hyprgraphics/resource/resources/AsyncResource.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#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 {
|
||||||
|
struct SAsyncResourceImpl;
|
||||||
|
|
||||||
|
class IAsyncResource {
|
||||||
|
public:
|
||||||
|
IAsyncResource();
|
||||||
|
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;
|
||||||
|
|
||||||
|
Hyprutils::Memory::CUniquePointer<SAsyncResourceImpl> m_impl;
|
||||||
|
};
|
||||||
|
}
|
||||||
33
include/hyprgraphics/resource/resources/ImageResource.hpp
Normal file
33
include/hyprgraphics/resource/resources/ImageResource.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <span>
|
||||||
|
#include <hyprutils/math/Vector2D.hpp>
|
||||||
|
#include "./AsyncResource.hpp"
|
||||||
|
#include "../../color/Color.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
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);
|
||||||
|
CImageResource(const std::span<const uint8_t>& data, const Hyprutils::Math::Vector2D& size = {} /* unused if not svg */);
|
||||||
|
virtual ~CImageResource() = default;
|
||||||
|
|
||||||
|
virtual void render();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string m_path;
|
||||||
|
Hyprutils::Math::Vector2D m_svgSize;
|
||||||
|
std::span<const uint8_t> m_data;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "./AsyncResource.hpp"
|
||||||
|
#include "../../color/Color.hpp"
|
||||||
|
#include "hyprgraphics/image/Image.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
};
|
||||||
|
};
|
||||||
42
include/hyprgraphics/resource/resources/TextResource.hpp
Normal file
42
include/hyprgraphics/resource/resources/TextResource.hpp
Normal file
|
|
@ -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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
@ -7,14 +7,21 @@
|
||||||
cairo,
|
cairo,
|
||||||
file,
|
file,
|
||||||
hyprutils,
|
hyprutils,
|
||||||
|
libGL,
|
||||||
|
libdrm,
|
||||||
|
libheif,
|
||||||
libjpeg,
|
libjpeg,
|
||||||
libjxl,
|
libjxl,
|
||||||
|
librsvg,
|
||||||
|
libspng,
|
||||||
libwebp,
|
libwebp,
|
||||||
|
pango,
|
||||||
pixman,
|
pixman,
|
||||||
version ? "git",
|
version ? "git",
|
||||||
doCheck ? false,
|
doCheck ? false,
|
||||||
debug ? false,
|
debug ? false,
|
||||||
}: let
|
}:
|
||||||
|
let
|
||||||
inherit (builtins) foldl';
|
inherit (builtins) foldl';
|
||||||
inherit (lib.lists) flatten;
|
inherit (lib.lists) flatten;
|
||||||
inherit (lib.sources) cleanSource cleanSourceWith;
|
inherit (lib.sources) cleanSource cleanSourceWith;
|
||||||
|
|
@ -27,44 +34,52 @@
|
||||||
|
|
||||||
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
|
customStdenv = foldl' (acc: adapter: adapter acc) stdenv adapters;
|
||||||
in
|
in
|
||||||
customStdenv.mkDerivation {
|
customStdenv.mkDerivation {
|
||||||
pname = "hyprgraphics";
|
pname = "hyprgraphics";
|
||||||
inherit version doCheck;
|
inherit version doCheck;
|
||||||
|
|
||||||
src = cleanSourceWith {
|
src = cleanSourceWith {
|
||||||
filter = name: _type: let
|
filter =
|
||||||
|
name: _type:
|
||||||
|
let
|
||||||
baseName = baseNameOf (toString name);
|
baseName = baseNameOf (toString name);
|
||||||
in
|
in
|
||||||
! (hasSuffix ".nix" baseName);
|
!(hasSuffix ".nix" baseName);
|
||||||
src = cleanSource ../.;
|
src = cleanSource ../.;
|
||||||
};
|
};
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
cmake
|
cmake
|
||||||
pkg-config
|
pkg-config
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
cairo
|
cairo
|
||||||
file
|
file
|
||||||
hyprutils
|
hyprutils
|
||||||
libjpeg
|
libGL
|
||||||
libjxl
|
libdrm
|
||||||
libwebp
|
libheif
|
||||||
pixman
|
libjpeg
|
||||||
];
|
libjxl
|
||||||
|
librsvg
|
||||||
|
libspng
|
||||||
|
libwebp
|
||||||
|
pango
|
||||||
|
pixman
|
||||||
|
];
|
||||||
|
|
||||||
outputs = ["out" "dev"];
|
outputs = [
|
||||||
|
"out"
|
||||||
|
"dev"
|
||||||
|
];
|
||||||
|
|
||||||
cmakeBuildType =
|
cmakeBuildType = if debug then "Debug" else "RelWithDebInfo";
|
||||||
if debug
|
|
||||||
then "Debug"
|
|
||||||
else "RelWithDebInfo";
|
|
||||||
|
|
||||||
meta = with lib; {
|
meta = with lib; {
|
||||||
homepage = "https://github.com/hyprwm/hyprgraphics";
|
homepage = "https://github.com/hyprwm/hyprgraphics";
|
||||||
description = "Small C++ library with graphics / resource related utilities used across the hypr* ecosystem";
|
description = "Small C++ library with graphics / resource related utilities used across the hypr* ecosystem";
|
||||||
license = licenses.bsd3;
|
license = licenses.bsd3;
|
||||||
platforms = platforms.linux;
|
platforms = platforms.linux;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
39
nix/overlays.nix
Normal file
39
nix/overlays.nix
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
self,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
|
||||||
|
mkDate =
|
||||||
|
longDate:
|
||||||
|
(lib.concatStringsSep "-" [
|
||||||
|
(builtins.substring 0 4 longDate)
|
||||||
|
(builtins.substring 4 2 longDate)
|
||||||
|
(builtins.substring 6 2 longDate)
|
||||||
|
]);
|
||||||
|
|
||||||
|
version = lib.removeSuffix "\n" (builtins.readFile ../VERSION);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
default = self.overlays.hyprgraphics;
|
||||||
|
|
||||||
|
hyprgraphics-with-deps = lib.composeManyExtensions [
|
||||||
|
inputs.hyprutils.overlays.default
|
||||||
|
self.overlays.hyprgraphics
|
||||||
|
];
|
||||||
|
|
||||||
|
hyprgraphics = final: prev: {
|
||||||
|
hyprgraphics = final.callPackage ./default.nix {
|
||||||
|
stdenv = final.gcc15Stdenv;
|
||||||
|
version =
|
||||||
|
version
|
||||||
|
+ "+date="
|
||||||
|
+ (mkDate (self.lastModifiedDate or "19700101"))
|
||||||
|
+ "_"
|
||||||
|
+ (self.shortRev or "dirty");
|
||||||
|
};
|
||||||
|
hyprgraphics-with-tests = final.hyprgraphics.override { doCheck = true; };
|
||||||
|
};
|
||||||
|
}
|
||||||
252
src/color/Color.cpp
Normal file
252
src/color/Color.cpp
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
#include <hyprgraphics/color/Color.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
using namespace Hyprgraphics;
|
||||||
|
|
||||||
|
static double gammaToLinear(const double in) {
|
||||||
|
return in >= 0.04045 ? std::pow((in + 0.055) / 1.055, 2.4) : in / 12.92;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double linearToGamma(const double in) {
|
||||||
|
return in >= 0.0031308 ? (1.055 * std::pow(in, 0.41666666666)) - 0.055 : 12.92 * in;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double hueToRgb(double p, double q, double t) {
|
||||||
|
if (t < 0)
|
||||||
|
t += 1;
|
||||||
|
if (t > 1)
|
||||||
|
t -= 1;
|
||||||
|
if (t < 1.0 / 6.0)
|
||||||
|
return p + ((q - p) * 6.0 * t);
|
||||||
|
if (t < 1.0 / 2.0)
|
||||||
|
return q;
|
||||||
|
if (t < 2.0 / 3.0)
|
||||||
|
return p + ((q - p) * (2.0 / 3.0 - t) * 6.0);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprgraphics::CMatrix3::CMatrix3(const std::array<std::array<double, 3>, 3>& values) : m(values) {}
|
||||||
|
|
||||||
|
CMatrix3 Hyprgraphics::CMatrix3::invert() const {
|
||||||
|
double invDet = 1 /
|
||||||
|
(0 //
|
||||||
|
+ m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]) //
|
||||||
|
- m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) //
|
||||||
|
+ m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]) //
|
||||||
|
);
|
||||||
|
|
||||||
|
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, //
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CColor::XYZ Hyprgraphics::CMatrix3::operator*(const CColor::XYZ& value) const {
|
||||||
|
return CColor::XYZ{
|
||||||
|
.x = (m[0][0] * value.x) + (m[0][1] * value.y) + (m[0][2] * value.z), //
|
||||||
|
.y = (m[1][0] * value.x) + (m[1][1] * value.y) + (m[1][2] * value.z), //
|
||||||
|
.z = (m[2][0] * value.x) + (m[2][1] * value.y) + (m[2][2] * value.z), //
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CMatrix3 Hyprgraphics::CMatrix3::operator*(const CMatrix3& other) const {
|
||||||
|
std::array<std::array<double, 3>, 3> res = {0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
for (int j = 0; j < 3; j++) {
|
||||||
|
for (int k = 0; k < 3; k++) {
|
||||||
|
res[i][j] += m[i][k] * other.m[k][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CMatrix3(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::array<std::array<double, 3>, 3>& Hyprgraphics::CMatrix3::mat() {
|
||||||
|
return m;
|
||||||
|
};
|
||||||
|
|
||||||
|
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, //
|
||||||
|
});
|
||||||
|
return Identity3;
|
||||||
|
}
|
||||||
|
|
||||||
|
CColor::XYZ Hyprgraphics::xy2xyz(const CColor::xy& xy) {
|
||||||
|
if (xy.y == 0.0)
|
||||||
|
return {.x = 0.0, .y = 0.0, .z = 0.0};
|
||||||
|
|
||||||
|
return {.x = xy.x / xy.y, .y = 1.0, .z = (1.0 - xy.x - xy.y) / xy.y};
|
||||||
|
}
|
||||||
|
|
||||||
|
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, //
|
||||||
|
});
|
||||||
|
|
||||||
|
static CMatrix3 BradfordInv = Bradford.invert();
|
||||||
|
|
||||||
|
CMatrix3 Hyprgraphics::adaptWhite(const CColor::xy& src, const CColor::xy& dst) {
|
||||||
|
if (src == dst)
|
||||||
|
return CMatrix3::identity();
|
||||||
|
|
||||||
|
const auto srcXYZ = xy2xyz(src);
|
||||||
|
const auto dstXYZ = xy2xyz(dst);
|
||||||
|
const auto factors = (Bradford * dstXYZ) / (Bradford * srcXYZ);
|
||||||
|
|
||||||
|
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, //
|
||||||
|
}) *
|
||||||
|
Bradford;
|
||||||
|
}
|
||||||
|
|
||||||
|
CMatrix3 Hyprgraphics::SPCPRimaries::toXYZ() const {
|
||||||
|
const auto r = xy2xyz(red);
|
||||||
|
const auto g = xy2xyz(green);
|
||||||
|
const auto b = xy2xyz(blue);
|
||||||
|
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, //
|
||||||
|
})
|
||||||
|
.invert();
|
||||||
|
|
||||||
|
const auto s = invMat * w;
|
||||||
|
|
||||||
|
return std::array<std::array<double, 3>, 3>{
|
||||||
|
s.x * r.x, s.y * g.x, s.z * b.x, //
|
||||||
|
s.x * r.y, s.y * g.y, s.z * b.y, //
|
||||||
|
s.x * r.z, s.y * g.z, s.z * b.z, //
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
CMatrix3 Hyprgraphics::SPCPRimaries::convertMatrix(const SPCPRimaries& dst) const {
|
||||||
|
return dst.toXYZ().invert() * adaptWhite(white, dst.white) * toXYZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprgraphics::CColor::CColor() {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprgraphics::CColor::CColor(const SSRGB& rgb) : r(rgb.r), g(rgb.g), b(rgb.b) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprgraphics::CColor::CColor(const SHSL& hsl) {
|
||||||
|
if (hsl.s <= 0) {
|
||||||
|
r = hsl.l;
|
||||||
|
g = hsl.l;
|
||||||
|
b = hsl.l;
|
||||||
|
} else {
|
||||||
|
const double q = hsl.l < 0.5 ? hsl.l * (1.0 + hsl.s) : hsl.l + hsl.s - (hsl.l * hsl.s);
|
||||||
|
const double p = (2.0 * hsl.l) - q;
|
||||||
|
r = hueToRgb(p, q, hsl.h + (1.0 / 3.0));
|
||||||
|
g = hueToRgb(p, q, hsl.h);
|
||||||
|
b = hueToRgb(p, q, hsl.h - (1.0 / 3.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprgraphics::CColor::CColor(const SOkLab& lab) {
|
||||||
|
const double l = std::pow(lab.l + (lab.a * 0.3963377774) + (lab.b * 0.2158037573), 3);
|
||||||
|
const double m = std::pow(lab.l + (lab.a * (-0.1055613458)) + (lab.b * (-0.0638541728)), 3);
|
||||||
|
const double s = std::pow(lab.l + (lab.a * (-0.0894841775)) + (lab.b * (-1.2914855480)), 3);
|
||||||
|
|
||||||
|
r = linearToGamma((l * 4.0767416621) + (m * -3.3077115913) + (s * 0.2309699292));
|
||||||
|
g = linearToGamma((l * (-1.2684380046)) + (m * 2.6097574011) + (s * (-0.3413193965)));
|
||||||
|
b = linearToGamma((l * (-0.0041960863)) + (m * (-0.7034186147)) + (s * 1.7076147010));
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprgraphics::CColor::SSRGB Hyprgraphics::CColor::asRgb() const {
|
||||||
|
return Hyprgraphics::CColor::SSRGB{
|
||||||
|
.r = r,
|
||||||
|
.g = g,
|
||||||
|
.b = b,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprgraphics::CColor::SHSL Hyprgraphics::CColor::asHSL() const {
|
||||||
|
const double vmax = std::max({r, g, b}), vmin = std::min({r, g, b});
|
||||||
|
double h = 0, s = 0, l = (vmax + vmin) / 2.0;
|
||||||
|
|
||||||
|
if (vmax == vmin) {
|
||||||
|
return Hyprgraphics::CColor::SHSL{
|
||||||
|
.h = 0,
|
||||||
|
.s = 0,
|
||||||
|
.l = l,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const double d = vmax - vmin;
|
||||||
|
s = l > 0.5 ? d / (2.0 - vmax - vmin) : d / (vmax + vmin);
|
||||||
|
|
||||||
|
if (vmax == r)
|
||||||
|
h = (g - b) / d + (g < b ? 6.0 : 0.0);
|
||||||
|
if (vmax == g)
|
||||||
|
h = (b - r) / d + 2;
|
||||||
|
if (vmax == b)
|
||||||
|
h = (r - g) / d + 4;
|
||||||
|
|
||||||
|
h /= 6.0;
|
||||||
|
|
||||||
|
return Hyprgraphics::CColor::SHSL{
|
||||||
|
.h = h,
|
||||||
|
.s = s,
|
||||||
|
.l = l,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprgraphics::CColor::SOkLab Hyprgraphics::CColor::asOkLab() const {
|
||||||
|
const double linR = gammaToLinear(r);
|
||||||
|
const double linG = gammaToLinear(g);
|
||||||
|
const double linB = gammaToLinear(b);
|
||||||
|
|
||||||
|
const double l = std::cbrtf((0.4122214708 * linR) + (0.5363325363 * linG) + (0.0514459929 * linB));
|
||||||
|
const double m = std::cbrtf((0.2119034982 * linR) + (0.6806995451 * linG) + (0.1073969566 * linB));
|
||||||
|
const double s = std::cbrtf((0.0883024619 * linR) + (0.2817188376 * linG) + (0.6299787005 * linB));
|
||||||
|
|
||||||
|
return Hyprgraphics::CColor::SOkLab{
|
||||||
|
.l = (l * 0.2104542553) + (m * 0.7936177850) + (s * (-0.0040720468)),
|
||||||
|
.a = (l * 1.9779984951) + (m * (-2.4285922050)) + (s * 0.4505937099),
|
||||||
|
.b = (l * 0.0259040371) + (m * 0.7827717662) + (s * (-0.8086757660)),
|
||||||
|
};
|
||||||
|
}
|
||||||
282
src/egl/Egl.cpp
Normal file
282
src/egl/Egl.cpp
Normal file
|
|
@ -0,0 +1,282 @@
|
||||||
|
#include <cmath>
|
||||||
|
#include <hyprgraphics/egl/Egl.hpp>
|
||||||
|
#include <hyprutils/memory/Casts.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <GLES3/gl32.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
|
namespace Hyprgraphics::Egl {
|
||||||
|
static inline const std::vector<SPixelFormat> GLES3_FORMATS = {
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_ARGB8888,
|
||||||
|
.glInternalFormat = GL_RGBA8,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_BYTE,
|
||||||
|
.withAlpha = true,
|
||||||
|
.alphaStripped = DRM_FORMAT_XRGB8888,
|
||||||
|
.bytesPerBlock = 4,
|
||||||
|
.swizzle = {SWIZZLE_BGRA},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_XRGB8888,
|
||||||
|
.glInternalFormat = GL_RGBA8,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_BYTE,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_XRGB8888,
|
||||||
|
.bytesPerBlock = 4,
|
||||||
|
.swizzle = {SWIZZLE_BGR1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_XBGR8888,
|
||||||
|
.glInternalFormat = GL_RGBA8,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_BYTE,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_XBGR8888,
|
||||||
|
.bytesPerBlock = 4,
|
||||||
|
.swizzle = {SWIZZLE_RGB1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_ABGR8888,
|
||||||
|
.glInternalFormat = GL_RGBA8,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_BYTE,
|
||||||
|
.withAlpha = true,
|
||||||
|
.alphaStripped = DRM_FORMAT_XBGR8888,
|
||||||
|
.bytesPerBlock = 4,
|
||||||
|
.swizzle = {SWIZZLE_RGBA},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_BGR888,
|
||||||
|
.glInternalFormat = GL_RGB8,
|
||||||
|
.glFormat = GL_RGB,
|
||||||
|
.glType = GL_UNSIGNED_BYTE,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_BGR888,
|
||||||
|
.bytesPerBlock = 3,
|
||||||
|
.swizzle = {SWIZZLE_RGB1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_RGBX4444,
|
||||||
|
.glInternalFormat = GL_RGBA4,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_SHORT_4_4_4_4,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_RGBX4444,
|
||||||
|
.bytesPerBlock = 2,
|
||||||
|
.swizzle = {SWIZZLE_RGB1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_RGBA4444,
|
||||||
|
.glInternalFormat = GL_RGBA4,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_SHORT_4_4_4_4,
|
||||||
|
.withAlpha = true,
|
||||||
|
.alphaStripped = DRM_FORMAT_RGBX4444,
|
||||||
|
.bytesPerBlock = 2,
|
||||||
|
.swizzle = {SWIZZLE_RGBA},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_RGBX5551,
|
||||||
|
.glInternalFormat = GL_RGB5_A1,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_SHORT_5_5_5_1,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_RGBX5551,
|
||||||
|
.bytesPerBlock = 2,
|
||||||
|
.swizzle = {SWIZZLE_RGB1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_RGBA5551,
|
||||||
|
.glInternalFormat = GL_RGB5_A1,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_SHORT_5_5_5_1,
|
||||||
|
.withAlpha = true,
|
||||||
|
.alphaStripped = DRM_FORMAT_RGBX5551,
|
||||||
|
.bytesPerBlock = 2,
|
||||||
|
.swizzle = {SWIZZLE_RGBA},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_RGB565,
|
||||||
|
.glInternalFormat = GL_RGB565,
|
||||||
|
.glFormat = GL_RGB,
|
||||||
|
.glType = GL_UNSIGNED_SHORT_5_6_5,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_RGB565,
|
||||||
|
.bytesPerBlock = 2,
|
||||||
|
.swizzle = {SWIZZLE_RGB1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_XBGR2101010,
|
||||||
|
.glInternalFormat = GL_RGB10_A2,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_XBGR2101010,
|
||||||
|
.bytesPerBlock = 4,
|
||||||
|
.swizzle = {SWIZZLE_RGB1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_ABGR2101010,
|
||||||
|
.glInternalFormat = GL_RGB10_A2,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
|
||||||
|
.withAlpha = true,
|
||||||
|
.alphaStripped = DRM_FORMAT_XBGR2101010,
|
||||||
|
.bytesPerBlock = 4,
|
||||||
|
.swizzle = {SWIZZLE_RGBA},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_XRGB2101010,
|
||||||
|
.glInternalFormat = GL_RGB10_A2,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_XRGB2101010,
|
||||||
|
.bytesPerBlock = 4,
|
||||||
|
.swizzle = {SWIZZLE_BGR1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_ARGB2101010,
|
||||||
|
.glInternalFormat = GL_RGB10_A2,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_UNSIGNED_INT_2_10_10_10_REV,
|
||||||
|
.withAlpha = true,
|
||||||
|
.alphaStripped = DRM_FORMAT_XRGB2101010,
|
||||||
|
.bytesPerBlock = 4,
|
||||||
|
.swizzle = {SWIZZLE_BGRA},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_XRGB16161616F,
|
||||||
|
.glInternalFormat = GL_RGBA16F,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_HALF_FLOAT,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_XRGB16161616F,
|
||||||
|
.bytesPerBlock = 8,
|
||||||
|
.swizzle = {SWIZZLE_BGR1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_ARGB16161616F,
|
||||||
|
.glInternalFormat = GL_RGBA16F,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_HALF_FLOAT,
|
||||||
|
.withAlpha = true,
|
||||||
|
.alphaStripped = DRM_FORMAT_XRGB16161616F,
|
||||||
|
.bytesPerBlock = 8,
|
||||||
|
.swizzle = {SWIZZLE_BGRA},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_XBGR16161616F,
|
||||||
|
.glInternalFormat = GL_RGBA16F,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_HALF_FLOAT,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_XBGR16161616F,
|
||||||
|
.bytesPerBlock = 8,
|
||||||
|
.swizzle = {SWIZZLE_RGB1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_ABGR16161616F,
|
||||||
|
.glInternalFormat = GL_RGBA16F,
|
||||||
|
.glFormat = GL_RGBA,
|
||||||
|
.glType = GL_HALF_FLOAT,
|
||||||
|
.withAlpha = true,
|
||||||
|
.alphaStripped = DRM_FORMAT_XBGR16161616F,
|
||||||
|
.bytesPerBlock = 8,
|
||||||
|
.swizzle = {SWIZZLE_RGBA},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_XBGR16161616,
|
||||||
|
.glInternalFormat = GL_RGBA16UI,
|
||||||
|
.glFormat = GL_RGBA_INTEGER,
|
||||||
|
.glType = GL_UNSIGNED_SHORT,
|
||||||
|
.withAlpha = false,
|
||||||
|
.alphaStripped = DRM_FORMAT_XBGR16161616,
|
||||||
|
.bytesPerBlock = 8,
|
||||||
|
.swizzle = {SWIZZLE_RGBA},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_ABGR16161616,
|
||||||
|
.glInternalFormat = GL_RGBA16UI,
|
||||||
|
.glFormat = GL_RGBA_INTEGER,
|
||||||
|
.glType = GL_UNSIGNED_SHORT,
|
||||||
|
.withAlpha = true,
|
||||||
|
.alphaStripped = DRM_FORMAT_XBGR16161616,
|
||||||
|
.bytesPerBlock = 8,
|
||||||
|
.swizzle = {SWIZZLE_RGBA},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_YVYU,
|
||||||
|
.bytesPerBlock = 4,
|
||||||
|
.blockSize = {2, 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_VYUY,
|
||||||
|
.bytesPerBlock = 4,
|
||||||
|
.blockSize = {2, 1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_R8,
|
||||||
|
.glInternalFormat = GL_R8,
|
||||||
|
.glFormat = GL_RED,
|
||||||
|
.glType = GL_UNSIGNED_BYTE,
|
||||||
|
.bytesPerBlock = 1,
|
||||||
|
.swizzle = {SWIZZLE_R001},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_GR88,
|
||||||
|
.glInternalFormat = GL_RG8,
|
||||||
|
.glFormat = GL_RG,
|
||||||
|
.glType = GL_UNSIGNED_BYTE,
|
||||||
|
.bytesPerBlock = 2,
|
||||||
|
.swizzle = {SWIZZLE_RG01},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.drmFormat = DRM_FORMAT_RGB888,
|
||||||
|
.glInternalFormat = GL_RGB8,
|
||||||
|
.glFormat = GL_RGB,
|
||||||
|
.glType = GL_UNSIGNED_BYTE,
|
||||||
|
.bytesPerBlock = 3,
|
||||||
|
.swizzle = {SWIZZLE_BGR1},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const SPixelFormat* getPixelFormatFromDRM(uint32_t drmFormat) {
|
||||||
|
for (auto const& fmt : GLES3_FORMATS) {
|
||||||
|
if (fmt.drmFormat == drmFormat)
|
||||||
|
return &fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SPixelFormat* getPixelFormatFromGL(uint32_t glFormat, uint32_t glType, bool alpha) {
|
||||||
|
for (auto const& fmt : GLES3_FORMATS) {
|
||||||
|
if (fmt.glFormat == sc<int>(glFormat) && fmt.glType == sc<int>(glType) && fmt.withAlpha == alpha)
|
||||||
|
return &fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDrmFormatOpaque(uint32_t drmFormat) {
|
||||||
|
const auto FMT = getPixelFormatFromDRM(drmFormat);
|
||||||
|
if (!FMT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !FMT->withAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pixelsPerBlock(const SPixelFormat* const fmt) {
|
||||||
|
return fmt->blockSize.x * fmt->blockSize.y > 0 ? fmt->blockSize.x * fmt->blockSize.y : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minStride(const SPixelFormat* const fmt, int32_t width) {
|
||||||
|
return std::ceil((width * fmt->bytesPerBlock) / pixelsPerBlock(fmt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,57 +1,89 @@
|
||||||
#include <hyprgraphics/image/Image.hpp>
|
#include <hyprgraphics/image/Image.hpp>
|
||||||
|
#include "utils/Format.hpp"
|
||||||
#include "formats/Bmp.hpp"
|
#include "formats/Bmp.hpp"
|
||||||
#include "formats/Jpeg.hpp"
|
#include "formats/Jpeg.hpp"
|
||||||
|
#ifdef JXL_FOUND
|
||||||
#include "formats/JpegXL.hpp"
|
#include "formats/JpegXL.hpp"
|
||||||
|
#endif
|
||||||
|
#ifdef HEIF_FOUND
|
||||||
|
#include "formats/Avif.hpp"
|
||||||
|
#endif
|
||||||
#include "formats/Webp.hpp"
|
#include "formats/Webp.hpp"
|
||||||
|
#include "formats/Png.hpp"
|
||||||
|
#include "formats/Svg.hpp"
|
||||||
#include <magic.h>
|
#include <magic.h>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
|
||||||
using namespace Hyprgraphics;
|
using namespace Hyprgraphics;
|
||||||
using namespace Hyprutils::Memory;
|
using namespace Hyprutils::Memory;
|
||||||
|
using namespace Hyprutils::Math;
|
||||||
|
|
||||||
|
Hyprgraphics::CImage::CImage(std::span<const uint8_t> data, eImageFormat format, const Vector2D& size) {
|
||||||
|
|
||||||
|
const auto FORMAT = format == IMAGE_FORMAT_AUTO ? formatFromStream(data) : format;
|
||||||
|
|
||||||
|
if (FORMAT == IMAGE_FORMAT_ERROR) {
|
||||||
|
lastError = "invalid file";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
if (path.find(".png") == len - 4 || path.find(".PNG") == len - 4) {
|
|
||||||
CAIROSURFACE = cairo_image_surface_create_from_png(path.c_str());
|
|
||||||
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);
|
|
||||||
imageHasAlpha = false;
|
|
||||||
mime = "image/jpeg";
|
|
||||||
} else if (path.find(".bmp") == len - 4 || path.find(".BMP") == len - 4) {
|
|
||||||
CAIROSURFACE = BMP::createSurfaceFromBMP(path);
|
|
||||||
imageHasAlpha = false;
|
|
||||||
mime = "image/bmp";
|
|
||||||
} else if (path.find(".webp") == len - 5 || path.find(".WEBP") == len - 5) {
|
|
||||||
CAIROSURFACE = WEBP::createSurfaceFromWEBP(path);
|
|
||||||
mime = "image/webp";
|
|
||||||
} else if (path.find(".jxl") == len - 4 || path.find(".JXL") == len - 4) {
|
|
||||||
CAIROSURFACE = JXL::createSurfaceFromJXL(path);
|
|
||||||
mime = "image/jxl";
|
|
||||||
} else {
|
|
||||||
// magic is slow, so only use it when no recognized extension is found
|
|
||||||
auto handle = magic_open(MAGIC_NONE | MAGIC_COMPRESS);
|
|
||||||
magic_load(handle, nullptr);
|
|
||||||
|
|
||||||
const auto type_str = std::string(magic_file(handle, path.c_str()));
|
switch (FORMAT) {
|
||||||
const auto first_word = type_str.substr(0, type_str.find(" "));
|
case IMAGE_FORMAT_PNG: CAIROSURFACE = PNG::createSurfaceFromPNG(data); break;
|
||||||
|
#ifdef HEIF_FOUND
|
||||||
|
case IMAGE_FORMAT_AVIF: CAIROSURFACE = AVIF::createSurfaceFromAvif(data); break;
|
||||||
|
#else
|
||||||
|
case IMAGE_FORMAT_AVIF: lastError = "hyprgraphics compiled without HEIF support"; return;
|
||||||
|
#endif
|
||||||
|
case IMAGE_FORMAT_SVG: CAIROSURFACE = SVG::createSurfaceFromData(data, size); break;
|
||||||
|
default: lastError = "Currently only PNG and AVIF images are supported for embedding"; return;
|
||||||
|
}
|
||||||
|
|
||||||
if (first_word == "PNG") {
|
if (!CAIROSURFACE) {
|
||||||
CAIROSURFACE = cairo_image_surface_create_from_png(path.c_str());
|
lastError = CAIROSURFACE.error();
|
||||||
mime = "image/png";
|
return;
|
||||||
} else if (first_word == "JPEG") {
|
}
|
||||||
CAIROSURFACE = JPEG::createSurfaceFromJPEG(path);
|
|
||||||
imageHasAlpha = false;
|
if (const auto STATUS = cairo_surface_status(*CAIROSURFACE); STATUS != CAIRO_STATUS_SUCCESS) {
|
||||||
mime = "image/jpeg";
|
lastError = std::format("Could not create surface: {}", cairo_status_to_string(STATUS));
|
||||||
} else if (first_word == "BMP") {
|
return;
|
||||||
CAIROSURFACE = BMP::createSurfaceFromBMP(path);
|
}
|
||||||
imageHasAlpha = false;
|
|
||||||
mime = "image/bmp";
|
loadSuccess = true;
|
||||||
} else {
|
pCairoSurface = makeShared<CCairoSurface>(CAIROSURFACE.value());
|
||||||
lastError = "unrecognized image";
|
}
|
||||||
return;
|
|
||||||
}
|
Hyprgraphics::CImage::CImage(const std::string& path, const Vector2D& size) : filepath(path), m_svgSize(size) {
|
||||||
|
|
||||||
|
const auto FORMAT = formatFromFile(path);
|
||||||
|
|
||||||
|
if (FORMAT == IMAGE_FORMAT_ERROR) {
|
||||||
|
lastError = "invalid file";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<cairo_surface_t*, std::string> CAIROSURFACE;
|
||||||
|
|
||||||
|
mime = mimeOf(FORMAT);
|
||||||
|
|
||||||
|
switch (FORMAT) {
|
||||||
|
case IMAGE_FORMAT_PNG: CAIROSURFACE = PNG::createSurfaceFromPNG(path); break;
|
||||||
|
case IMAGE_FORMAT_BMP: CAIROSURFACE = BMP::createSurfaceFromBMP(path); break;
|
||||||
|
#ifdef HEIF_FOUND
|
||||||
|
case IMAGE_FORMAT_AVIF: CAIROSURFACE = AVIF::createSurfaceFromAvif(path); break;
|
||||||
|
#else
|
||||||
|
case IMAGE_FORMAT_AVIF: lastError = "hyprgraphics compiled without HEIF support"; return;
|
||||||
|
#endif
|
||||||
|
#ifdef JXL_FOUND
|
||||||
|
case IMAGE_FORMAT_JXL: CAIROSURFACE = JXL::createSurfaceFromJXL(path); break;
|
||||||
|
#else
|
||||||
|
case IMAGE_FORMAT_JXL: lastError = "hyprgraphics compiled without JXL support"; return;
|
||||||
|
#endif
|
||||||
|
case IMAGE_FORMAT_JPEG: CAIROSURFACE = JPEG::createSurfaceFromJPEG(path); break;
|
||||||
|
case IMAGE_FORMAT_SVG: CAIROSURFACE = SVG::createSurfaceFromSVG(path, size); break;
|
||||||
|
case IMAGE_FORMAT_WEBP: CAIROSURFACE = WEBP::createSurfaceFromWEBP(path); break;
|
||||||
|
default: lastError = "internal error"; return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CAIROSURFACE) {
|
if (!CAIROSURFACE) {
|
||||||
|
|
@ -91,3 +123,7 @@ Hyprutils::Memory::CSharedPointer<CCairoSurface> Hyprgraphics::CImage::cairoSurf
|
||||||
std::string Hyprgraphics::CImage::getMime() {
|
std::string Hyprgraphics::CImage::getMime() {
|
||||||
return mime;
|
return mime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Hyprgraphics::CImage::isImageFile(const std::string& path) {
|
||||||
|
return formatFromFile(path) != IMAGE_FORMAT_ERROR;
|
||||||
|
}
|
||||||
|
|
|
||||||
92
src/image/formats/Avif.cpp
Normal file
92
src/image/formats/Avif.cpp
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
12
src/image/formats/Avif.hpp
Normal file
12
src/image/formats/Avif.hpp
Normal file
|
|
@ -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>);
|
||||||
|
};
|
||||||
|
|
@ -3,11 +3,12 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <cstddef>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string.h>
|
#include <cstring>
|
||||||
|
|
||||||
class BmpHeader {
|
class BmpHeader {
|
||||||
public:
|
public:
|
||||||
|
|
@ -34,7 +35,7 @@ class BmpHeader {
|
||||||
file.seekg(0, std::ios::beg);
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
file.read(reinterpret_cast<char*>(&format), sizeof(format));
|
file.read(reinterpret_cast<char*>(&format), sizeof(format));
|
||||||
if (!(format[0] == 66 && format[1] == 77))
|
if (format[0] != 66 || format[1] != 77)
|
||||||
return "Unable to parse bitmap header: wrong bmp file type";
|
return "Unable to parse bitmap header: wrong bmp file type";
|
||||||
|
|
||||||
file.read(reinterpret_cast<char*>(&sizeOfFile), sizeof(sizeOfFile));
|
file.read(reinterpret_cast<char*>(&sizeOfFile), sizeof(sizeOfFile));
|
||||||
|
|
@ -75,9 +76,9 @@ static void reflectImage(unsigned char* image, uint32_t numberOfRows, int stride
|
||||||
std::vector<unsigned char> temp;
|
std::vector<unsigned char> temp;
|
||||||
temp.resize(stride);
|
temp.resize(stride);
|
||||||
while (rowStart < rowEnd) {
|
while (rowStart < rowEnd) {
|
||||||
memcpy(&temp[0], &image[rowStart * stride], stride);
|
memcpy(&temp[0], &image[static_cast<size_t>(rowStart * stride)], stride);
|
||||||
memcpy(&image[rowStart * stride], &image[rowEnd * stride], stride);
|
memcpy(&image[static_cast<size_t>(rowStart * stride)], &image[static_cast<size_t>(rowEnd * stride)], stride);
|
||||||
memcpy(&image[rowEnd * stride], &temp[0], stride);
|
memcpy(&image[static_cast<size_t>(rowEnd * stride)], &temp[0], stride);
|
||||||
rowStart++;
|
rowStart++;
|
||||||
rowEnd--;
|
rowEnd--;
|
||||||
}
|
}
|
||||||
|
|
@ -102,14 +103,14 @@ std::expected<cairo_surface_t*, std::string> BMP::createSurfaceFromBMP(const std
|
||||||
if (!std::filesystem::exists(path))
|
if (!std::filesystem::exists(path))
|
||||||
return std::unexpected("loading bmp: file doesn't exist");
|
return std::unexpected("loading bmp: file doesn't exist");
|
||||||
|
|
||||||
std::ifstream bitmapImageStream(path);
|
std::ifstream bitmapImageStream(path);
|
||||||
BmpHeader bitmapHeader;
|
BmpHeader bitmapHeader;
|
||||||
if (const auto RET = bitmapHeader.load(bitmapImageStream); RET.has_value())
|
if (const auto RET = bitmapHeader.load(bitmapImageStream); RET.has_value())
|
||||||
return std::unexpected("loading bmp: " + *RET);
|
return std::unexpected("loading bmp: " + *RET);
|
||||||
|
|
||||||
cairo_format_t format = CAIRO_FORMAT_ARGB32;
|
cairo_format_t format = CAIRO_FORMAT_ARGB32;
|
||||||
int stride = cairo_format_stride_for_width(format, bitmapHeader.width);
|
int stride = cairo_format_stride_for_width(format, bitmapHeader.width);
|
||||||
unsigned char* imageData = (unsigned char*)malloc(bitmapHeader.height * stride);
|
unsigned char* imageData = (unsigned char*)malloc(static_cast<size_t>(bitmapHeader.height * stride));
|
||||||
|
|
||||||
if (bitmapHeader.numberOfBitPerPixel == 24)
|
if (bitmapHeader.numberOfBitPerPixel == 24)
|
||||||
convertRgbToArgb(bitmapImageStream, imageData, bitmapHeader.height * stride);
|
convertRgbToArgb(bitmapImageStream, imageData, bitmapHeader.height * stride);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,16 @@
|
||||||
#include "Jpeg.hpp"
|
#include "Jpeg.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <csetjmp>
|
||||||
|
|
||||||
|
// TODO: TurboJPEG C API and get rid of this
|
||||||
|
jmp_buf bailoutBuf = {};
|
||||||
|
static void bailout(j_common_ptr _) {
|
||||||
|
longjmp(bailoutBuf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
std::expected<cairo_surface_t*, std::string> JPEG::createSurfaceFromJPEG(const std::string& path) {
|
std::expected<cairo_surface_t*, std::string> JPEG::createSurfaceFromJPEG(const std::string& path) {
|
||||||
|
|
||||||
|
|
@ -18,12 +26,20 @@ std::expected<cairo_surface_t*, std::string> JPEG::createSurfaceFromJPEG(const s
|
||||||
file.seekg(0);
|
file.seekg(0);
|
||||||
file.read(reinterpret_cast<char*>(bytes.data()), bytes.size());
|
file.read(reinterpret_cast<char*>(bytes.data()), bytes.size());
|
||||||
|
|
||||||
|
if (bytes[0] != 0xFF || bytes[1] != 0xD8)
|
||||||
|
return std::unexpected("loading jpeg: invalid magic bytes");
|
||||||
|
|
||||||
// now the JPEG is in the memory
|
// now the JPEG is in the memory
|
||||||
|
|
||||||
jpeg_decompress_struct decompressStruct = {};
|
jpeg_decompress_struct decompressStruct = {};
|
||||||
jpeg_error_mgr errorManager = {};
|
jpeg_error_mgr errorManager = {};
|
||||||
|
|
||||||
decompressStruct.err = jpeg_std_error(&errorManager);
|
decompressStruct.err = jpeg_std_error(&errorManager);
|
||||||
|
|
||||||
|
errorManager.error_exit = bailout;
|
||||||
|
if (setjmp(bailoutBuf))
|
||||||
|
return std::unexpected("loading jpeg: libjpeg encountered a fatal error");
|
||||||
|
|
||||||
jpeg_create_decompress(&decompressStruct);
|
jpeg_create_decompress(&decompressStruct);
|
||||||
jpeg_mem_src(&decompressStruct, bytes.data(), bytes.size());
|
jpeg_mem_src(&decompressStruct, bytes.data(), bytes.size());
|
||||||
jpeg_read_header(&decompressStruct, true);
|
jpeg_read_header(&decompressStruct, true);
|
||||||
|
|
@ -47,7 +63,7 @@ std::expected<cairo_surface_t*, std::string> JPEG::createSurfaceFromJPEG(const s
|
||||||
JSAMPROW rowRead;
|
JSAMPROW rowRead;
|
||||||
|
|
||||||
while (decompressStruct.output_scanline < decompressStruct.output_height) {
|
while (decompressStruct.output_scanline < decompressStruct.output_height) {
|
||||||
const auto PROW = CAIRODATA + (decompressStruct.output_scanline * CAIROSTRIDE);
|
const auto PROW = CAIRODATA + (static_cast<size_t>(decompressStruct.output_scanline * CAIROSTRIDE));
|
||||||
rowRead = PROW;
|
rowRead = PROW;
|
||||||
jpeg_read_scanlines(&decompressStruct, &rowRead, 1);
|
jpeg_read_scanlines(&decompressStruct, &rowRead, 1);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
130
src/image/formats/Png.cpp
Normal file
130
src/image/formats/Png.cpp
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
#include "Png.hpp"
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <png.h>
|
||||||
|
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||||
|
using namespace Hyprutils::Utils;
|
||||||
|
|
||||||
|
static std::expected<cairo_surface_t*, std::string> loadPNG(png_structp, png_infop);
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
FILE* fp = fopen(path.c_str(), "rb");
|
||||||
|
if (!fp)
|
||||||
|
return std::unexpected("loading png: couldn't open file");
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
CScopeGuard x([&png, &info, fp] {
|
||||||
|
png_destroy_read_struct(&png, &info, nullptr);
|
||||||
|
fclose(fp);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png)))
|
||||||
|
return std::unexpected("loading png: couldn't setjmp");
|
||||||
|
|
||||||
|
png_init_io(png, fp);
|
||||||
|
|
||||||
|
return loadPNG(png, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SReadState {
|
||||||
|
const std::span<const uint8_t> data;
|
||||||
|
size_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void customReadFunction(png_structp png, png_bytep data, png_size_t length) {
|
||||||
|
SReadState* state = static_cast<SReadState*>(png_get_io_ptr(png));
|
||||||
|
if (state->offset + length > state->data.size()) {
|
||||||
|
png_error(png, "read error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(data, state->data.data() + state->offset, length);
|
||||||
|
state->offset += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
return std::unexpected("loading png: couldn't init libpng");
|
||||||
|
|
||||||
|
CScopeGuard x([&png, &info] { png_destroy_read_struct(&png, &info, nullptr); });
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png)))
|
||||||
|
return std::unexpected("loading png: couldn't setjmp");
|
||||||
|
|
||||||
|
SReadState readState = {.data = data, .offset = 0};
|
||||||
|
|
||||||
|
png_set_read_fn(png, &readState, customReadFunction);
|
||||||
|
png_set_sig_bytes(png, 0);
|
||||||
|
|
||||||
|
return loadPNG(png, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::expected<cairo_surface_t*, std::string> loadPNG(png_structp png, png_infop info) {
|
||||||
|
png_read_info(png, info);
|
||||||
|
|
||||||
|
const size_t W = png_get_image_width(png, info);
|
||||||
|
const size_t H = png_get_image_height(png, info);
|
||||||
|
const auto COLOR_TYPE = png_get_color_type(png, info);
|
||||||
|
const auto BPP = png_get_bit_depth(png, info);
|
||||||
|
|
||||||
|
if (BPP == 16)
|
||||||
|
png_set_strip_16(png);
|
||||||
|
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 (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);
|
||||||
|
|
||||||
|
png_read_update_info(png, info);
|
||||||
|
|
||||||
|
std::vector<uint8_t*> rowPointers;
|
||||||
|
rowPointers.resize(H);
|
||||||
|
std::vector<uint8_t> rawData;
|
||||||
|
rawData.resize(W * H * 4);
|
||||||
|
for (size_t y = 0; y < H; y++) {
|
||||||
|
rowPointers[y] = rawData.data() + (y * W * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
r *= ((float)a) / 255.F;
|
||||||
|
g *= ((float)a) / 255.F;
|
||||||
|
b *= ((float)a) / 255.F;
|
||||||
|
*(uint32_t*)&rawData[i] = (a << 24) | (r << 16) | (g << 8) | b;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, W, H);
|
||||||
|
|
||||||
|
if (!CAIROSURFACE)
|
||||||
|
return std::unexpected("loading png: cairo failed");
|
||||||
|
|
||||||
|
memcpy(cairo_image_surface_get_data(CAIROSURFACE), rawData.data(), rawData.size());
|
||||||
|
cairo_surface_mark_dirty(CAIROSURFACE);
|
||||||
|
|
||||||
|
return CAIROSURFACE;
|
||||||
|
}
|
||||||
13
src/image/formats/Png.hpp
Normal file
13
src/image/formats/Png.hpp
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
#include <string>
|
||||||
|
#include <expected>
|
||||||
|
#include <png.h>
|
||||||
|
#include <span>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace PNG {
|
||||||
|
std::expected<cairo_surface_t*, std::string> createSurfaceFromPNG(const std::string&);
|
||||||
|
std::expected<cairo_surface_t*, std::string> createSurfaceFromPNG(const std::span<const uint8_t>);
|
||||||
|
};
|
||||||
96
src/image/formats/Svg.cpp
Normal file
96
src/image/formats/Svg.cpp
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::expected<cairo_surface_t*, std::string> SVG::createSurfaceFromData(const std::span<const uint8_t>& data, const Vector2D& size) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
RsvgHandle* handle = rsvg_handle_new_from_data((unsigned char*)data.data(), data.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;
|
||||||
|
}
|
||||||
14
src/image/formats/Svg.hpp
Normal file
14
src/image/formats/Svg.hpp
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cairo/cairo.h>
|
||||||
|
#include <string>
|
||||||
|
#include <expected>
|
||||||
|
#include <span>
|
||||||
|
#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);
|
||||||
|
std::expected<cairo_surface_t*, std::string> createSurfaceFromData(const std::span<const uint8_t>&, const Hyprutils::Math::Vector2D& size);
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "Webp.hpp"
|
#include "Webp.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <webp/decode.h>
|
#include <webp/decode.h>
|
||||||
|
|
@ -16,6 +17,9 @@ std::expected<cairo_surface_t*, std::string> WEBP::createSurfaceFromWEBP(const s
|
||||||
file.seekg(0);
|
file.seekg(0);
|
||||||
file.read(reinterpret_cast<char*>(bytes.data()), bytes.size());
|
file.read(reinterpret_cast<char*>(bytes.data()), bytes.size());
|
||||||
|
|
||||||
|
if (bytes[0] != 'R' || bytes[1] != 'I' || bytes[2] != 'F' || bytes[3] != 'F')
|
||||||
|
return std::unexpected("loading webp: invalid magic bytes");
|
||||||
|
|
||||||
// now the WebP is in the memory
|
// now the WebP is in the memory
|
||||||
|
|
||||||
WebPDecoderConfig config;
|
WebPDecoderConfig config;
|
||||||
|
|
@ -46,7 +50,7 @@ std::expected<cairo_surface_t*, std::string> WEBP::createSurfaceFromWEBP(const s
|
||||||
config.options.no_fancy_upsampling = 1;
|
config.options.no_fancy_upsampling = 1;
|
||||||
config.output.u.RGBA.rgba = CAIRODATA;
|
config.output.u.RGBA.rgba = CAIRODATA;
|
||||||
config.output.u.RGBA.stride = CAIROSTRIDE;
|
config.output.u.RGBA.stride = CAIROSTRIDE;
|
||||||
config.output.u.RGBA.size = CAIROSTRIDE * HEIGHT;
|
config.output.u.RGBA.size = static_cast<size_t>(CAIROSTRIDE * HEIGHT);
|
||||||
config.output.is_external_memory = 1;
|
config.output.is_external_memory = 1;
|
||||||
config.output.width = WIDTH;
|
config.output.width = WIDTH;
|
||||||
config.output.height = HEIGHT;
|
config.output.height = HEIGHT;
|
||||||
|
|
|
||||||
88
src/image/utils/Format.cpp
Normal file
88
src/image/utils/Format.cpp
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
#include "Format.hpp"
|
||||||
|
|
||||||
|
#include <magic.h>
|
||||||
|
|
||||||
|
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||||
|
|
||||||
|
using namespace Hyprgraphics;
|
||||||
|
using namespace Hyprutils::Utils;
|
||||||
|
|
||||||
|
static eImageFormat formatFromStr(const std::string& r) {
|
||||||
|
if (r == "image/png")
|
||||||
|
return IMAGE_FORMAT_PNG;
|
||||||
|
if (r == "image/jpeg" || r == "image/jpg")
|
||||||
|
return IMAGE_FORMAT_JPEG;
|
||||||
|
if (r == "image/bmp")
|
||||||
|
return IMAGE_FORMAT_BMP;
|
||||||
|
if (r == "image/webp")
|
||||||
|
return IMAGE_FORMAT_WEBP;
|
||||||
|
if (r == "image/svg" || r.starts_with("image/svg") /* +xml */)
|
||||||
|
return IMAGE_FORMAT_SVG;
|
||||||
|
if (r == "image/jxl")
|
||||||
|
return IMAGE_FORMAT_JXL;
|
||||||
|
if (r == "image/avif")
|
||||||
|
return IMAGE_FORMAT_AVIF;
|
||||||
|
|
||||||
|
return IMAGE_FORMAT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static eImageFormat formatOf(const std::span<const uint8_t>& data) {
|
||||||
|
magic_t m = magic_open(MAGIC_MIME_TYPE);
|
||||||
|
if (!m)
|
||||||
|
return IMAGE_FORMAT_ERROR;
|
||||||
|
|
||||||
|
CScopeGuard x([&] {
|
||||||
|
magic_close(m); //
|
||||||
|
});
|
||||||
|
|
||||||
|
if (magic_load(m, nullptr) != 0)
|
||||||
|
return IMAGE_FORMAT_ERROR;
|
||||||
|
|
||||||
|
const char* result = magic_buffer(m, data.data(), data.size());
|
||||||
|
if (!result)
|
||||||
|
return IMAGE_FORMAT_ERROR;
|
||||||
|
|
||||||
|
auto r = std::string{result};
|
||||||
|
return formatFromStr(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
static eImageFormat formatOf(const std::string& path) {
|
||||||
|
magic_t m = magic_open(MAGIC_MIME_TYPE | MAGIC_SYMLINK);
|
||||||
|
if (!m)
|
||||||
|
return IMAGE_FORMAT_ERROR;
|
||||||
|
|
||||||
|
CScopeGuard x([&] {
|
||||||
|
magic_close(m); //
|
||||||
|
});
|
||||||
|
|
||||||
|
if (magic_load(m, nullptr) != 0)
|
||||||
|
return IMAGE_FORMAT_ERROR;
|
||||||
|
|
||||||
|
const char* result = magic_file(m, path.c_str());
|
||||||
|
if (!result)
|
||||||
|
return IMAGE_FORMAT_ERROR;
|
||||||
|
|
||||||
|
auto r = std::string{result};
|
||||||
|
return formatFromStr(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
eImageFormat Hyprgraphics::formatFromStream(const std::span<const uint8_t>& data) {
|
||||||
|
return formatOf(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
eImageFormat Hyprgraphics::formatFromFile(const std::string& path) {
|
||||||
|
return formatOf(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Hyprgraphics::mimeOf(eImageFormat f) {
|
||||||
|
switch (f) {
|
||||||
|
case IMAGE_FORMAT_PNG: return "image/png";
|
||||||
|
case IMAGE_FORMAT_AVIF: return "image/avif";
|
||||||
|
case IMAGE_FORMAT_BMP: return "image/bmp";
|
||||||
|
case IMAGE_FORMAT_JPEG: return "image/jpeg";
|
||||||
|
case IMAGE_FORMAT_JXL: return "image/jxl";
|
||||||
|
case IMAGE_FORMAT_SVG: return "image/svg";
|
||||||
|
case IMAGE_FORMAT_WEBP: return "image/webp";
|
||||||
|
default: return "error";
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/image/utils/Format.hpp
Normal file
9
src/image/utils/Format.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <hyprgraphics/image/Image.hpp>
|
||||||
|
|
||||||
|
namespace Hyprgraphics {
|
||||||
|
eImageFormat formatFromStream(const std::span<const uint8_t>& data);
|
||||||
|
eImageFormat formatFromFile(const std::string& path);
|
||||||
|
const char* mimeOf(eImageFormat);
|
||||||
|
};
|
||||||
75
src/resource/AsyncResourceGatherer.cpp
Normal file
75
src/resource/AsyncResourceGatherer.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#include <hyprgraphics/resource/AsyncResourceGatherer.hpp>
|
||||||
|
#include "resources/AsyncResource.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::await(Hyprutils::Memory::CAtomicSharedPointer<IAsyncResource> resource) {
|
||||||
|
resource->m_impl->awaitingCv = Hyprutils::Memory::makeUnique<std::condition_variable>();
|
||||||
|
std::unique_lock<std::mutex> lk(resource->m_impl->awaitingMtx);
|
||||||
|
resource->m_impl->awaitingCv->wait(lk, [&resource] { return resource->m_impl->awaitingEvent; });
|
||||||
|
resource->m_impl->awaitingCv.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
if (r->m_impl->awaitingCv) {
|
||||||
|
r->m_impl->awaitingEvent = true;
|
||||||
|
r->m_impl->awaitingCv->notify_all();
|
||||||
|
}
|
||||||
|
r->m_ready = true;
|
||||||
|
r->m_events.finished.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/resource/resources/AsyncResource.cpp
Normal file
8
src/resource/resources/AsyncResource.cpp
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
#include "AsyncResource.hpp"
|
||||||
|
|
||||||
|
using namespace Hyprgraphics;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
|
||||||
|
IAsyncResource::IAsyncResource() : m_impl(makeUnique<SAsyncResourceImpl>()) {
|
||||||
|
;
|
||||||
|
}
|
||||||
11
src/resource/resources/AsyncResource.hpp
Normal file
11
src/resource/resources/AsyncResource.hpp
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#include <hyprgraphics/resource/resources/AsyncResource.hpp>
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
namespace Hyprgraphics {
|
||||||
|
struct SAsyncResourceImpl {
|
||||||
|
Hyprutils::Memory::CUniquePointer<std::condition_variable> awaitingCv;
|
||||||
|
std::mutex awaitingMtx;
|
||||||
|
bool awaitingEvent = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
29
src/resource/resources/ImageResource.cpp
Normal file
29
src/resource/resources/ImageResource.cpp
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#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) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
CImageResource::CImageResource(const std::span<const uint8_t>& data, const Hyprutils::Math::Vector2D& size) : m_svgSize(size), m_data(data) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CImageResource::render() {
|
||||||
|
auto image = !m_data.empty() ? CImage(m_data, IMAGE_FORMAT_AUTO, m_svgSize) : 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{};
|
||||||
|
}
|
||||||
20
src/resource/resources/StaticImageResource.cpp
Normal file
20
src/resource/resources/StaticImageResource.cpp
Normal file
|
|
@ -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{};
|
||||||
|
}
|
||||||
109
src/resource/resources/TextResource.cpp
Normal file
109
src/resource/resources/TextResource.cpp
Normal file
|
|
@ -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);
|
||||||
|
}
|
||||||
139
tests/arg.cpp
Normal file
139
tests/arg.cpp
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
|
#include <algorithm>
|
||||||
#include <print>
|
#include <print>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
#include <hyprgraphics/image/Image.hpp>
|
#include <hyprgraphics/image/Image.hpp>
|
||||||
#include "shared.hpp"
|
#include "shared.hpp"
|
||||||
|
|
||||||
using namespace Hyprgraphics;
|
using namespace Hyprgraphics;
|
||||||
|
|
||||||
bool tryLoadImage(const std::string& path) {
|
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()) {
|
if (!image.success()) {
|
||||||
std::println("Failed to load {}: {}", path, image.getError());
|
std::println("Failed to load {}: {}", path, image.getError());
|
||||||
|
|
@ -16,7 +19,54 @@ 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::ranges::replace(name, '/', '_');
|
||||||
|
|
||||||
|
//NOLINTNEXTLINE
|
||||||
|
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, eImageFormat format) {
|
||||||
|
auto image = CImage(data, format);
|
||||||
|
|
||||||
|
if (!image.success()) {
|
||||||
|
std::println("Failed to load embedded image: {}", image.getError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::println("Loaded embedded Image successfully: Image is {}x{} of type {}", image.cairoSurface()->size().x, image.cairoSurface()->size().y, image.getMime());
|
||||||
|
|
||||||
|
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() + "_embedded";
|
||||||
|
std::ranges::replace(name, '/', '_');
|
||||||
|
|
||||||
|
//NOLINTNEXTLINE
|
||||||
|
return cairo_surface_write_to_png(image.cairoSurface()->cairo(), (TEST_DIR + "/" + name + ".png").c_str()) == CAIRO_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<uint8_t> getImageBuffer(const std::string& path) {
|
||||||
|
std::vector<uint8_t> buffer;
|
||||||
|
|
||||||
|
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||||
|
std::streamsize size = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
|
||||||
|
buffer.resize(size);
|
||||||
|
|
||||||
|
file.read(reinterpret_cast<char*>(buffer.data()), size);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
int main(int argc, char** argv, char** envp) {
|
||||||
|
|
@ -25,9 +75,28 @@ int main(int argc, char** argv, char** envp) {
|
||||||
for (auto& file : std::filesystem::directory_iterator("./resource/images/")) {
|
for (auto& file : std::filesystem::directory_iterator("./resource/images/")) {
|
||||||
if (!file.is_regular_file())
|
if (!file.is_regular_file())
|
||||||
continue;
|
continue;
|
||||||
|
auto expectation = true;
|
||||||
EXPECT(tryLoadImage(file.path()), true);
|
#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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto pngBuffer = getImageBuffer("./resource/images/hyprland.png");
|
||||||
|
EXPECT(tryLoadImageFromBuffer(pngBuffer, Hyprgraphics::IMAGE_FORMAT_AUTO), true);
|
||||||
|
|
||||||
|
#ifdef HEIF_FOUND
|
||||||
|
auto avifBuffer = getImageBuffer("./resource/images/hyprland.avif");
|
||||||
|
EXPECT(tryLoadImageFromBuffer(avifBuffer, Hyprgraphics::IMAGE_FORMAT_AVIF), true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
auto svgBuffer = getImageBuffer("./resource/images/hyprland.svg");
|
||||||
|
EXPECT(tryLoadImageFromBuffer(pngBuffer, Hyprgraphics::IMAGE_FORMAT_AUTO), true);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
tests/resource/images/hyprland.avif
Normal file
BIN
tests/resource/images/hyprland.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
15
tests/resource/images/hyprland.svg
Normal file
15
tests/resource/images/hyprland.svg
Normal file
|
|
@ -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.35Z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
1
tests/resource/images/hyprland.symlink
Symbolic link
1
tests/resource/images/hyprland.symlink
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
hyprland.png
|
||||||
Loading…
Add table
Reference in a new issue