mirror of
https://github.com/hyprwm/hyprcursor.git
synced 2024-11-17 02:35:57 +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";
|
std::cout << "Found xcursor " << xcursor.path().stem().string() << "\n";
|
||||||
|
|
||||||
// decompile xcursor
|
// decompile xcursor
|
||||||
const auto OUT = spawnSync(
|
const auto OUT = spawnSync(std::format("rm -f /tmp/hyprcursor-util/* && cd /tmp/hyprcursor-util && xcur2png {} -d /tmp/hyprcursor-util 2>&1",
|
||||||
std::format("rm /tmp/hyprcursor-util/* && cd /tmp/hyprcursor-util && xcur2png {} -d /tmp/hyprcursor-util 2>&1", std::filesystem::canonical(xcursor.path()).string()));
|
std::filesystem::canonical(xcursor.path()).string()));
|
||||||
|
|
||||||
// read the config
|
// read the config
|
||||||
std::vector<XCursorConfigEntry> entries;
|
std::vector<XCursorConfigEntry> entries;
|
||||||
|
|
|
@ -7,11 +7,13 @@ class CHyprcursorImplementation;
|
||||||
namespace Hyprcursor {
|
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;
|
unsigned int size = 0;
|
||||||
};
|
};
|
||||||
|
@ -39,20 +41,23 @@ namespace Hyprcursor {
|
||||||
*/
|
*/
|
||||||
bool valid();
|
bool valid();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Loads this theme at a given style, synchronously.
|
||||||
|
*/
|
||||||
|
bool loadThemeStyle(const SCursorStyleInfo& info);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Returns a cairo_surface_t for a given cursor
|
Returns a cairo_surface_t for a given cursor
|
||||||
shape and size.
|
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.
|
Marks a certain style as done, allowing it to be potentially freed
|
||||||
|
|
||||||
Always call after using a surface.
|
|
||||||
*/
|
*/
|
||||||
void cursorSurfaceDone(cairo_surface_t* surface);
|
void cursorSurfaceStyleDone(const SCursorStyleInfo&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CHyprcursorImplementation* impl = nullptr;
|
CHyprcursorImplementation* impl = nullptr;
|
||||||
|
|
|
@ -162,7 +162,7 @@ bool CHyprcursorManager::valid() {
|
||||||
return finalizedAndValid;
|
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_;
|
std::string REQUESTEDSHAPE = shape_;
|
||||||
|
|
||||||
for (auto& shape : impl->theme.shapes) {
|
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)
|
if (image->side != info.size)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// found pixel-perfect size
|
// found size
|
||||||
return image->cairoSurface;
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CHyprcursorManager::cursorSurfaceDone(cairo_surface_t* surface) {
|
bool CHyprcursorManager::loadThemeStyle(const SCursorStyleInfo& info) {
|
||||||
;
|
for (auto& shape : impl->theme.shapes) {
|
||||||
// TODO: when resampling.
|
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() {
|
~SLoadedCursorImage() {
|
||||||
if (data)
|
if (data)
|
||||||
delete[] (char*)data;
|
delete[] (char*)data;
|
||||||
|
if (artificialData)
|
||||||
|
delete[] (char*)artificialData;
|
||||||
|
if (cairoSurface)
|
||||||
|
cairo_surface_destroy(cairoSurface);
|
||||||
}
|
}
|
||||||
|
|
||||||
// read stuff
|
// read stuff
|
||||||
|
@ -19,6 +23,10 @@ struct SLoadedCursorImage {
|
||||||
|
|
||||||
cairo_surface_t* cairoSurface = nullptr;
|
cairo_surface_t* cairoSurface = nullptr;
|
||||||
int side = 0;
|
int side = 0;
|
||||||
|
|
||||||
|
// means this was created by resampling
|
||||||
|
void* artificialData = nullptr;
|
||||||
|
bool artificial = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SLoadedCursorShape {
|
struct SLoadedCursorShape {
|
||||||
|
|
|
@ -5,8 +5,14 @@
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
Hyprcursor::CHyprcursorManager mgr(nullptr);
|
Hyprcursor::CHyprcursorManager mgr(nullptr);
|
||||||
|
|
||||||
// get cursor for arrow
|
// preload size 48 for testing
|
||||||
const auto ARROW = mgr.getSurfaceFor("arrow", Hyprcursor::SCursorSurfaceInfo{.size = 64});
|
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
|
// save to disk
|
||||||
const auto RET = cairo_surface_write_to_png(ARROW, "/tmp/arrow.png");
|
const auto RET = cairo_surface_write_to_png(ARROW, "/tmp/arrow.png");
|
||||||
|
|
Loading…
Reference in a new issue