mirror of
https://github.com/hyprwm/hyprgraphics.git
synced 2025-05-15 06:40:39 +01:00
Compare commits
17 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6075491094 | ||
![]() |
9d7f2687c8 | ||
![]() |
760d67a2a8 | ||
![]() |
175c6b29b6 | ||
![]() |
575ae47b78 | ||
![]() |
e19ee9031a | ||
![]() |
5ac80e3686 | ||
![]() |
12cd7034e4 | ||
![]() |
23783b9603 | ||
![]() |
6355b72d9c | ||
![]() |
0c11438de4 | ||
![]() |
0d77b4895a | ||
![]() |
52202272d8 | ||
![]() |
b09980755d | ||
![]() |
6dea3fba08 | ||
![]() |
0f9b8ca692 | ||
![]() |
fb2c026864 |
19 changed files with 503 additions and 63 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: |
|
||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||
pacman --noconfirm --noprogressbar -Syyu
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman cairo hyprutils libjpeg-turbo libjxl libwebp
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman cairo hyprutils libjpeg-turbo libjxl libwebp libspng
|
||||
|
||||
- name: Build hyprgraphics with gcc
|
||||
run: |
|
||||
|
@ -44,7 +44,7 @@ jobs:
|
|||
run: |
|
||||
sed -i 's/SigLevel = Required DatabaseOptional/SigLevel = Optional TrustAll/' /etc/pacman.conf
|
||||
pacman --noconfirm --noprogressbar -Syyu
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman cairo hyprutils libjpeg-turbo libjxl libwebp
|
||||
pacman --noconfirm --noprogressbar -Sy gcc base-devel cmake clang libc++ pixman cairo hyprutils libjpeg-turbo libjxl libwebp libspng
|
||||
|
||||
- name: Build hyprgraphics with clang
|
||||
run: |
|
||||
|
|
1
.github/workflows/nix.yml
vendored
1
.github/workflows/nix.yml
vendored
|
@ -14,7 +14,6 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: cachix/install-nix-action@v26
|
||||
- uses: DeterminateSystems/magic-nix-cache-action@main
|
||||
|
||||
# not needed (yet)
|
||||
# - uses: cachix/cachix-action@v12
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -44,3 +44,5 @@ Makefile
|
|||
cmake_install.cmake
|
||||
compile_commands.json
|
||||
hyprutils.pc
|
||||
|
||||
tests/test_output/
|
|
@ -21,6 +21,16 @@ set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR})
|
|||
configure_file(hyprgraphics.pc.in hyprgraphics.pc @ONLY)
|
||||
|
||||
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)
|
||||
message(STATUS "Configuring hyprgraphics in Debug")
|
||||
|
@ -47,10 +57,22 @@ pkg_check_modules(
|
|||
hyprutils
|
||||
libjpeg
|
||||
libwebp
|
||||
libmagic
|
||||
spng)
|
||||
|
||||
pkg_check_modules(
|
||||
JXL
|
||||
IMPORTED_TARGET
|
||||
libjxl
|
||||
libjxl_cms
|
||||
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()
|
||||
|
||||
add_library(hyprgraphics SHARED ${SRCFILES})
|
||||
target_include_directories(
|
||||
|
@ -60,6 +82,9 @@ target_include_directories(
|
|||
set_target_properties(hyprgraphics PROPERTIES VERSION ${HYPRGRAPHICS_VERSION}
|
||||
SOVERSION 0)
|
||||
target_link_libraries(hyprgraphics PkgConfig::deps)
|
||||
if(JXL_FOUND)
|
||||
target_link_libraries(hyprgraphics PkgConfig::JXL)
|
||||
endif()
|
||||
|
||||
# tests
|
||||
add_custom_target(tests)
|
||||
|
|
16
README.md
16
README.md
|
@ -6,6 +6,22 @@ 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.
|
||||
|
||||
## 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
|
||||
- libspng
|
||||
|
||||
## Building
|
||||
|
||||
```sh
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
0.1.1
|
||||
0.1.3
|
||||
|
|
12
flake.lock
12
flake.lock
|
@ -10,11 +10,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1732288281,
|
||||
"narHash": "sha256-XTU9B53IjGeJiJ7LstOhuxcRjCOFkQFl01H78sT9Lg4=",
|
||||
"lastModified": 1737632363,
|
||||
"narHash": "sha256-X9I8POSlHxBVjD0fiX1O2j7U9Zi1+4rIkrsyHP0uHXY=",
|
||||
"owner": "hyprwm",
|
||||
"repo": "hyprutils",
|
||||
"rev": "b26f33cc1c8a7fd5076e19e2cce3f062dca6351c",
|
||||
"rev": "006620eb29d54ea9086538891404c78563d1bae1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -25,11 +25,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1732014248,
|
||||
"narHash": "sha256-y/MEyuJ5oBWrWAic/14LaIr/u5E0wRVzyYsouYY3W6w=",
|
||||
"lastModified": 1737469691,
|
||||
"narHash": "sha256-nmKOgAU48S41dTPIXAq0AHZSehWUn6ZPrUKijHAMmIk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "23e89b7da85c3640bbc2173fe04f4bd114342367",
|
||||
"rev": "9e4d5190a9482a1fb9d18adf0bdb83c6e506eaab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
namespace Hyprgraphics {
|
||||
class CColor {
|
||||
public:
|
||||
|
@ -18,6 +19,25 @@ namespace Hyprgraphics {
|
|||
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);
|
||||
|
@ -35,4 +55,40 @@ namespace Hyprgraphics {
|
|||
// 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
|
||||
};
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
hyprutils,
|
||||
libjpeg,
|
||||
libjxl,
|
||||
libspng,
|
||||
libwebp,
|
||||
pixman,
|
||||
version ? "git",
|
||||
|
@ -50,6 +51,7 @@ in
|
|||
hyprutils
|
||||
libjpeg
|
||||
libjxl
|
||||
libspng
|
||||
libwebp
|
||||
pixman
|
||||
];
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <hyprgraphics/color/Color.hpp>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
using namespace Hyprgraphics;
|
||||
|
@ -8,7 +9,7 @@ static double gammaToLinear(const double in) {
|
|||
}
|
||||
|
||||
static double linearToGamma(const double in) {
|
||||
return in >= 0.0031308 ? 1.055 * std::pow(in, 0.41666666666) - 0.055 : 12.92 * 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) {
|
||||
|
@ -17,22 +18,128 @@ static double hueToRgb(double p, double q, double t) {
|
|||
if (t > 1)
|
||||
t -= 1;
|
||||
if (t < 1.0 / 6.0)
|
||||
return p + (q - p) * 6.0 * t;
|
||||
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 + ((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 SSRGB& rgb) : r(rgb.r), g(rgb.g), b(rgb.b) {
|
||||
;
|
||||
}
|
||||
|
||||
Hyprgraphics::CColor::CColor(const SHSL& hsl) {
|
||||
|
@ -41,22 +148,22 @@ Hyprgraphics::CColor::CColor(const SHSL& hsl) {
|
|||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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 {
|
||||
|
@ -68,7 +175,7 @@ Hyprgraphics::CColor::SSRGB Hyprgraphics::CColor::asRgb() const {
|
|||
}
|
||||
|
||||
Hyprgraphics::CColor::SHSL Hyprgraphics::CColor::asHSL() const {
|
||||
const double vmax = std::max(std::max(r, g), b), vmin = std::min(std::min(r, g), b);
|
||||
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) {
|
||||
|
@ -103,13 +210,13 @@ Hyprgraphics::CColor::SOkLab Hyprgraphics::CColor::asOkLab() const {
|
|||
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);
|
||||
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),
|
||||
.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)),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#include <hyprgraphics/image/Image.hpp>
|
||||
#include "formats/Bmp.hpp"
|
||||
#include "formats/Jpeg.hpp"
|
||||
#ifdef JXL_FOUND
|
||||
#include "formats/JpegXL.hpp"
|
||||
#endif
|
||||
#include "formats/Webp.hpp"
|
||||
#include "formats/Png.hpp"
|
||||
#include <magic.h>
|
||||
#include <format>
|
||||
|
||||
|
@ -13,7 +16,7 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
|
|||
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());
|
||||
CAIROSURFACE = PNG::createSurfaceFromPNG(path);
|
||||
mime = "image/png";
|
||||
} else if (path.find(".jpg") == len - 4 || path.find(".JPG") == len - 4 || path.find(".jpeg") == len - 5 || path.find(".JPEG") == len - 5) {
|
||||
CAIROSURFACE = JPEG::createSurfaceFromJPEG(path);
|
||||
|
@ -27,18 +30,25 @@ Hyprgraphics::CImage::CImage(const std::string& path) : filepath(path) {
|
|||
CAIROSURFACE = WEBP::createSurfaceFromWEBP(path);
|
||||
mime = "image/webp";
|
||||
} else if (path.find(".jxl") == len - 4 || path.find(".JXL") == len - 4) {
|
||||
|
||||
#ifdef JXL_FOUND
|
||||
CAIROSURFACE = JXL::createSurfaceFromJXL(path);
|
||||
mime = "image/jxl";
|
||||
#else
|
||||
lastError = "hyprgraphics compiled without JXL support";
|
||||
return;
|
||||
#endif
|
||||
|
||||
} else {
|
||||
// magic is slow, so only use it when no recognized extension is found
|
||||
auto handle = magic_open(MAGIC_NONE | MAGIC_COMPRESS);
|
||||
auto handle = magic_open(MAGIC_NONE | MAGIC_COMPRESS | MAGIC_SYMLINK);
|
||||
magic_load(handle, nullptr);
|
||||
|
||||
const auto type_str = std::string(magic_file(handle, path.c_str()));
|
||||
const auto first_word = type_str.substr(0, type_str.find(" "));
|
||||
const auto first_word = type_str.substr(0, type_str.find(' '));
|
||||
|
||||
if (first_word == "PNG") {
|
||||
CAIROSURFACE = cairo_image_surface_create_from_png(path.c_str());
|
||||
CAIROSURFACE = PNG::createSurfaceFromPNG(path);
|
||||
mime = "image/png";
|
||||
} else if (first_word == "JPEG") {
|
||||
CAIROSURFACE = JPEG::createSurfaceFromJPEG(path);
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
|
||||
class BmpHeader {
|
||||
public:
|
||||
|
@ -34,7 +35,7 @@ class BmpHeader {
|
|||
file.seekg(0, std::ios::beg);
|
||||
|
||||
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";
|
||||
|
||||
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;
|
||||
temp.resize(stride);
|
||||
while (rowStart < rowEnd) {
|
||||
memcpy(&temp[0], &image[rowStart * stride], stride);
|
||||
memcpy(&image[rowStart * stride], &image[rowEnd * stride], stride);
|
||||
memcpy(&image[rowEnd * stride], &temp[0], stride);
|
||||
memcpy(&temp[0], &image[static_cast<size_t>(rowStart * stride)], stride);
|
||||
memcpy(&image[static_cast<size_t>(rowStart * stride)], &image[static_cast<size_t>(rowEnd * stride)], stride);
|
||||
memcpy(&image[static_cast<size_t>(rowEnd * stride)], &temp[0], stride);
|
||||
rowStart++;
|
||||
rowEnd--;
|
||||
}
|
||||
|
@ -109,7 +110,7 @@ std::expected<cairo_surface_t*, std::string> BMP::createSurfaceFromBMP(const std
|
|||
|
||||
cairo_format_t format = CAIRO_FORMAT_ARGB32;
|
||||
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)
|
||||
convertRgbToArgb(bitmapImageStream, imageData, bitmapHeader.height * stride);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "Jpeg.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
@ -47,7 +48,7 @@ std::expected<cairo_surface_t*, std::string> JPEG::createSurfaceFromJPEG(const s
|
|||
JSAMPROW rowRead;
|
||||
|
||||
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;
|
||||
jpeg_read_scanlines(&decompressStruct, &rowRead, 1);
|
||||
}
|
||||
|
|
94
src/image/formats/Png.cpp
Normal file
94
src/image/formats/Png.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
#include "Png.hpp"
|
||||
#include <spng.h>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <cstdint>
|
||||
#include <hyprutils/utils/ScopeGuard.hpp>
|
||||
using namespace Hyprutils::Utils;
|
||||
|
||||
static std::vector<unsigned char> readBinaryFile(const std::string& filename) {
|
||||
std::ifstream f(filename, std::ios::binary);
|
||||
if (!f.good())
|
||||
return {};
|
||||
f.unsetf(std::ios::skipws);
|
||||
return {std::istreambuf_iterator<char>(f), std::istreambuf_iterator<char>()};
|
||||
}
|
||||
|
||||
std::expected<cairo_surface_t*, std::string> PNG::createSurfaceFromPNG(const std::string& path) {
|
||||
if (!std::filesystem::exists(path))
|
||||
return std::unexpected("loading png: file doesn't exist");
|
||||
|
||||
spng_ctx* ctx = spng_ctx_new(0);
|
||||
|
||||
CScopeGuard x([&] { spng_ctx_free(ctx); });
|
||||
|
||||
const auto PNGCONTENT = readBinaryFile(path);
|
||||
|
||||
if (PNGCONTENT.empty())
|
||||
return std::unexpected("loading png: file content was empty (bad file?)");
|
||||
|
||||
spng_set_png_buffer(ctx, PNGCONTENT.data(), PNGCONTENT.size());
|
||||
|
||||
spng_ihdr ihdr{.width = 0};
|
||||
if (int ret = spng_get_ihdr(ctx, &ihdr); ret)
|
||||
return std::unexpected(std::string{"loading png: spng_get_ihdr failed: "} + spng_strerror(ret));
|
||||
|
||||
int fmt = SPNG_FMT_PNG;
|
||||
if (ihdr.color_type == SPNG_COLOR_TYPE_INDEXED)
|
||||
fmt = SPNG_FMT_RGB8;
|
||||
|
||||
size_t imageLength = 0;
|
||||
if (int ret = spng_decoded_image_size(ctx, fmt, &imageLength); ret)
|
||||
return std::unexpected(std::string{"loading png: spng_decoded_image_size failed: "} + spng_strerror(ret));
|
||||
|
||||
uint8_t* imageData = (uint8_t*)malloc(imageLength);
|
||||
|
||||
if (!imageData)
|
||||
return std::unexpected("loading png: mallocing failed, out of memory?");
|
||||
|
||||
// TODO: allow proper decode of high bitrate images
|
||||
bool succeededDecode = false;
|
||||
int ret = spng_decode_image(ctx, imageData, imageLength, SPNG_FMT_RGBA8, 0);
|
||||
if (!ret)
|
||||
succeededDecode = true;
|
||||
|
||||
if (!succeededDecode && ret == SPNG_EBUFSIZ) {
|
||||
// hack, but I don't know why decoded_image_size is sometimes wrong
|
||||
imageLength = static_cast<size_t>(ihdr.height * ihdr.width * 4) /* FIXME: this is wrong if we doing >32bpp!!!! */;
|
||||
imageData = (uint8_t*)realloc(imageData, imageLength);
|
||||
|
||||
ret = spng_decode_image(ctx, imageData, imageLength, SPNG_FMT_RGBA8, 0);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
succeededDecode = true;
|
||||
|
||||
if (!succeededDecode) {
|
||||
free(imageData);
|
||||
return std::unexpected(std::string{"loading png: spng_decode_image failed: "} + spng_strerror(ret) + " (bad image?)");
|
||||
}
|
||||
|
||||
// convert RGBA8888 -> ARGB8888 premult for cairo
|
||||
for (size_t i = 0; i < imageLength; i += 4) {
|
||||
uint8_t r, g, b, a;
|
||||
a = ((*((uint32_t*)(imageData + i))) & 0xFF000000) >> 24;
|
||||
b = ((*((uint32_t*)(imageData + i))) & 0x00FF0000) >> 16;
|
||||
g = ((*((uint32_t*)(imageData + i))) & 0x0000FF00) >> 8;
|
||||
r = (*((uint32_t*)(imageData + i))) & 0x000000FF;
|
||||
|
||||
r *= ((float)a / 255.F);
|
||||
g *= ((float)a / 255.F);
|
||||
b *= ((float)a / 255.F);
|
||||
|
||||
*((uint32_t*)(imageData + i)) = (((uint32_t)a) << 24) | (((uint32_t)r) << 16) | (((uint32_t)g) << 8) | (uint32_t)b;
|
||||
}
|
||||
|
||||
auto CAIROSURFACE = cairo_image_surface_create_for_data(imageData, CAIRO_FORMAT_ARGB32, ihdr.width, ihdr.height, ihdr.width * 4);
|
||||
|
||||
if (!CAIROSURFACE)
|
||||
return std::unexpected("loading png: cairo failed");
|
||||
|
||||
return CAIROSURFACE;
|
||||
}
|
9
src/image/formats/Png.hpp
Normal file
9
src/image/formats/Png.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <cairo/cairo.h>
|
||||
#include <string>
|
||||
#include <expected>
|
||||
|
||||
namespace PNG {
|
||||
std::expected<cairo_surface_t*, std::string> createSurfaceFromPNG(const std::string&);
|
||||
};
|
|
@ -1,5 +1,6 @@
|
|||
#include "Webp.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <webp/decode.h>
|
||||
|
@ -46,7 +47,7 @@ std::expected<cairo_surface_t*, std::string> WEBP::createSurfaceFromWEBP(const s
|
|||
config.options.no_fancy_upsampling = 1;
|
||||
config.output.u.RGBA.rgba = CAIRODATA;
|
||||
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.width = WIDTH;
|
||||
config.output.height = HEIGHT;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <algorithm>
|
||||
#include <print>
|
||||
#include <format>
|
||||
#include <filesystem>
|
||||
|
@ -6,7 +7,7 @@
|
|||
|
||||
using namespace Hyprgraphics;
|
||||
|
||||
bool tryLoadImage(const std::string& path) {
|
||||
static bool tryLoadImage(const std::string& path) {
|
||||
auto image = CImage(path);
|
||||
|
||||
if (!image.success()) {
|
||||
|
@ -16,7 +17,17 @@ 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());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
|
@ -25,8 +36,12 @@ int main(int argc, char** argv, char** envp) {
|
|||
for (auto& file : std::filesystem::directory_iterator("./resource/images/")) {
|
||||
if (!file.is_regular_file())
|
||||
continue;
|
||||
|
||||
EXPECT(tryLoadImage(file.path()), true);
|
||||
auto expectation = true;
|
||||
#ifndef JXL_FOUND
|
||||
if (file.path().filename() == "hyprland.jxl")
|
||||
expectation = false;
|
||||
#endif
|
||||
EXPECT(tryLoadImage(file.path()), expectation);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
1
tests/resource/images/hyprland.symlink
Symbolic link
1
tests/resource/images/hyprland.symlink
Symbolic link
|
@ -0,0 +1 @@
|
|||
hyprland.png
|
Loading…
Reference in a new issue