lib: add resampling

This commit is contained in:
Vaxry 2024-03-07 14:50:48 +00:00
parent fcfa979979
commit a1df22903a
5 changed files with 149 additions and 19 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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); });
}
}
/*

View file

@ -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 {

View file

@ -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");