mirror of
https://github.com/hyprwm/hyprcursor.git
synced 2024-11-16 18:25:58 +01:00
lib: add resampling
This commit is contained in:
parent
fcfa979979
commit
a1df22903a
5 changed files with 149 additions and 19 deletions
|
@ -262,8 +262,8 @@ static std::optional<std::string> extractXTheme(const std::string& xpath, const
|
|||
std::cout << "Found xcursor " << xcursor.path().stem().string() << "\n";
|
||||
|
||||
// decompile xcursor
|
||||
const auto OUT = spawnSync(
|
||||
std::format("rm /tmp/hyprcursor-util/* && cd /tmp/hyprcursor-util && xcur2png {} -d /tmp/hyprcursor-util 2>&1", std::filesystem::canonical(xcursor.path()).string()));
|
||||
const auto OUT = spawnSync(std::format("rm -f /tmp/hyprcursor-util/* && cd /tmp/hyprcursor-util && xcur2png {} -d /tmp/hyprcursor-util 2>&1",
|
||||
std::filesystem::canonical(xcursor.path()).string()));
|
||||
|
||||
// read the config
|
||||
std::vector<XCursorConfigEntry> entries;
|
||||
|
|
|
@ -7,11 +7,13 @@ class CHyprcursorImplementation;
|
|||
namespace Hyprcursor {
|
||||
|
||||
/*!
|
||||
Simple struct for some info about shape requests
|
||||
Simple struct for styles
|
||||
*/
|
||||
struct SCursorSurfaceInfo {
|
||||
struct SCursorStyleInfo {
|
||||
/*
|
||||
Shape size
|
||||
Shape size.
|
||||
|
||||
0 means "any" or "unspecified".
|
||||
*/
|
||||
unsigned int size = 0;
|
||||
};
|
||||
|
@ -39,20 +41,23 @@ namespace Hyprcursor {
|
|||
*/
|
||||
bool valid();
|
||||
|
||||
/*!
|
||||
Loads this theme at a given style, synchronously.
|
||||
*/
|
||||
bool loadThemeStyle(const SCursorStyleInfo& info);
|
||||
|
||||
/*!
|
||||
Returns a cairo_surface_t for a given cursor
|
||||
shape and size.
|
||||
|
||||
Once done, call cursorSurfaceDone()
|
||||
Once done with a size, call cursorSurfaceDone()
|
||||
*/
|
||||
cairo_surface_t* getSurfaceFor(const char* shape, const SCursorSurfaceInfo& info);
|
||||
cairo_surface_t* getSurfaceFor(const char* shape, const SCursorStyleInfo& info);
|
||||
|
||||
/*!
|
||||
Marks a surface as done, meaning ready to be freed.
|
||||
|
||||
Always call after using a surface.
|
||||
Marks a certain style as done, allowing it to be potentially freed
|
||||
*/
|
||||
void cursorSurfaceDone(cairo_surface_t* surface);
|
||||
void cursorSurfaceStyleDone(const SCursorStyleInfo&);
|
||||
|
||||
private:
|
||||
CHyprcursorImplementation* impl = nullptr;
|
||||
|
|
|
@ -162,7 +162,7 @@ bool CHyprcursorManager::valid() {
|
|||
return finalizedAndValid;
|
||||
}
|
||||
|
||||
cairo_surface_t* CHyprcursorManager::getSurfaceFor(const char* shape_, const SCursorSurfaceInfo& info) {
|
||||
cairo_surface_t* CHyprcursorManager::getSurfaceFor(const char* shape_, const SCursorStyleInfo& info) {
|
||||
std::string REQUESTEDSHAPE = shape_;
|
||||
|
||||
for (auto& shape : impl->theme.shapes) {
|
||||
|
@ -174,18 +174,129 @@ cairo_surface_t* CHyprcursorManager::getSurfaceFor(const char* shape_, const SCu
|
|||
if (image->side != info.size)
|
||||
continue;
|
||||
|
||||
// found pixel-perfect size
|
||||
// found size
|
||||
return image->cairoSurface;
|
||||
}
|
||||
|
||||
// TODO: resampling
|
||||
// if we get here, means loadThemeStyle wasn't called most likely. If resize algo is specified, this is an error.
|
||||
if (shape.resizeAlgo != RESIZE_NONE) {
|
||||
Debug::log(ERR, "getSurfaceFor didn't match a size?");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// find nearest
|
||||
int leader = 1337;
|
||||
SLoadedCursorImage* leaderImg = nullptr;
|
||||
for (auto& image : impl->loadedShapes[&shape].images) {
|
||||
if (std::abs((int)(image->side - info.size)) > leader)
|
||||
continue;
|
||||
|
||||
leaderImg = image.get();
|
||||
leader = image->side;
|
||||
}
|
||||
|
||||
if (!leaderImg) { // ???
|
||||
Debug::log(ERR, "getSurfaceFor didn't match any nearest size?");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return leaderImg->cairoSurface;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CHyprcursorManager::cursorSurfaceDone(cairo_surface_t* surface) {
|
||||
;
|
||||
// TODO: when resampling.
|
||||
bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
|
||||
for (auto& shape : impl->theme.shapes) {
|
||||
if (shape.resizeAlgo == RESIZE_NONE)
|
||||
continue; // don't resample NONE style cursors
|
||||
|
||||
bool sizeFound = false;
|
||||
|
||||
for (auto& image : impl->loadedShapes[&shape].images) {
|
||||
if (image->side != info.size)
|
||||
continue;
|
||||
|
||||
sizeFound = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sizeFound)
|
||||
continue;
|
||||
|
||||
// size wasn't found, let's resample.
|
||||
SLoadedCursorImage* leader = nullptr;
|
||||
int leaderVal = 1000000;
|
||||
for (auto& image : impl->loadedShapes[&shape].images) {
|
||||
if (image->side < info.size)
|
||||
continue;
|
||||
|
||||
if (image->side > leaderVal)
|
||||
continue;
|
||||
|
||||
leaderVal = image->side;
|
||||
leader = image.get();
|
||||
}
|
||||
|
||||
if (!leader) {
|
||||
for (auto& image : impl->loadedShapes[&shape].images) {
|
||||
if (image->side < info.size)
|
||||
continue;
|
||||
|
||||
if (std::abs((int)(image->side - info.size)) > leaderVal)
|
||||
continue;
|
||||
|
||||
leaderVal = image->side;
|
||||
leader = image.get();
|
||||
}
|
||||
}
|
||||
|
||||
if (!leader) {
|
||||
Debug::log(ERR, "Resampling failed to find a candidate???");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& newImage = impl->loadedShapes[&shape].images.emplace_back(std::make_unique<SLoadedCursorImage>());
|
||||
newImage->artificial = true;
|
||||
newImage->side = info.size;
|
||||
newImage->artificialData = new char[info.size * info.size * 4];
|
||||
newImage->cairoSurface = cairo_image_surface_create_for_data((unsigned char*)newImage->artificialData, CAIRO_FORMAT_ARGB32, info.size, info.size, info.size * 4);
|
||||
|
||||
const auto PCAIRO = cairo_create(newImage->cairoSurface);
|
||||
|
||||
cairo_set_antialias(PCAIRO, shape.resizeAlgo == RESIZE_BILINEAR ? CAIRO_ANTIALIAS_GOOD : CAIRO_ANTIALIAS_NONE);
|
||||
|
||||
cairo_save(PCAIRO);
|
||||
cairo_set_operator(PCAIRO, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(PCAIRO);
|
||||
cairo_restore(PCAIRO);
|
||||
|
||||
const auto PTN = cairo_pattern_create_for_surface(leader->cairoSurface);
|
||||
cairo_pattern_set_extend(PTN, CAIRO_EXTEND_NONE);
|
||||
const float scale = info.size / (float)leader->side;
|
||||
cairo_scale(PCAIRO, scale, scale);
|
||||
cairo_pattern_set_filter(PTN, shape.resizeAlgo == RESIZE_BILINEAR ? CAIRO_FILTER_GOOD : CAIRO_FILTER_NEAREST);
|
||||
cairo_set_source(PCAIRO, PTN);
|
||||
|
||||
cairo_rectangle(PCAIRO, 0, 0, info.size, info.size);
|
||||
|
||||
cairo_fill(PCAIRO);
|
||||
cairo_surface_flush(newImage->cairoSurface);
|
||||
|
||||
cairo_pattern_destroy(PTN);
|
||||
cairo_destroy(PCAIRO);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHyprcursorManager::cursorSurfaceStyleDone(const SCursorStyleInfo& info) {
|
||||
for (auto& shape : impl->theme.shapes) {
|
||||
if (shape.resizeAlgo == RESIZE_NONE)
|
||||
continue;
|
||||
|
||||
std::erase_if(impl->loadedShapes[&shape].images, [info](const auto& e) { return !e->artificial && (info.size == 0 || e->side == info.size); });
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -10,6 +10,10 @@ struct SLoadedCursorImage {
|
|||
~SLoadedCursorImage() {
|
||||
if (data)
|
||||
delete[] (char*)data;
|
||||
if (artificialData)
|
||||
delete[] (char*)artificialData;
|
||||
if (cairoSurface)
|
||||
cairo_surface_destroy(cairoSurface);
|
||||
}
|
||||
|
||||
// read stuff
|
||||
|
@ -19,6 +23,10 @@ struct SLoadedCursorImage {
|
|||
|
||||
cairo_surface_t* cairoSurface = nullptr;
|
||||
int side = 0;
|
||||
|
||||
// means this was created by resampling
|
||||
void* artificialData = nullptr;
|
||||
bool artificial = false;
|
||||
};
|
||||
|
||||
struct SLoadedCursorShape {
|
||||
|
|
|
@ -5,8 +5,14 @@
|
|||
int main(int argc, char** argv) {
|
||||
Hyprcursor::CHyprcursorManager mgr(nullptr);
|
||||
|
||||
// get cursor for arrow
|
||||
const auto ARROW = mgr.getSurfaceFor("arrow", Hyprcursor::SCursorSurfaceInfo{.size = 64});
|
||||
// preload size 48 for testing
|
||||
if (!mgr.loadThemeStyle(Hyprcursor::SCursorStyleInfo{.size = 48})) {
|
||||
std::cout << "failed loading style\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get cursor for left_ptr
|
||||
const auto ARROW = mgr.getSurfaceFor("left_ptr", Hyprcursor::SCursorStyleInfo{.size = 48});
|
||||
|
||||
// save to disk
|
||||
const auto RET = cairo_surface_write_to_png(ARROW, "/tmp/arrow.png");
|
||||
|
|
Loading…
Reference in a new issue