SNI: Allow svgs as icons

To facilitate that, Texture now accepts GdkPixbuf instead of raw
uint8_t[], because we can't convert svgs into uint8_t[] easily without
the use of GdkPixbuf.
This commit is contained in:
scorpion-26 2023-11-05 22:03:50 +01:00
parent dc7c7b7902
commit 2420574c70
4 changed files with 91 additions and 45 deletions

View file

@ -133,7 +133,7 @@ namespace Utils
return result;
}
inline std::string FindFileWithName(const std::string& directory, const std::string& name, const std::string& extension)
inline std::string FindFileWithName(const std::string& directory, const std::string& name)
{
if (!std::filesystem::exists(directory))
{
@ -143,7 +143,7 @@ namespace Utils
{
if (path.is_directory())
continue;
if (path.path().filename().string().find(name) != std::string::npos && path.path().extension().string() == extension)
if (path.path().filename().string().find(name) != std::string::npos)
{
return path.path().string();
}

View file

@ -32,7 +32,7 @@ namespace SNI
std::string object;
size_t w = 0;
size_t h = 0;
uint8_t* iconData = nullptr;
GdkPixbuf* pixbuf = nullptr;
std::string tooltip = "";
@ -54,6 +54,77 @@ namespace SNI
Widget* parentBox;
Widget* iconBox;
// Swap channels for the render format and create a pixbuf out of it
static GdkPixbuf* ToPixbuf(uint8_t* sniData, int32_t width, int32_t height)
{
for (int i = 0; i < width * height; i++)
{
struct Px
{
// This should be bgra...
// Since source is ARGB32 in network order(=big-endian)
// and x86 Linux is little-endian, we *should* swap b and r...
uint8_t a, r, g, b;
};
Px& pixel = ((Px*)sniData)[i];
// Swap to create rgba
pixel = {pixel.r, pixel.g, pixel.b, pixel.a};
}
return gdk_pixbuf_new_from_data(
sniData, GDK_COLORSPACE_RGB, true, 8, width, height, width * 4,
+[](uint8_t* data, void*)
{
delete[] data;
},
nullptr);
}
// Allocates a pixbuf that contains a bitmap of the icon
static void ToPixbuf(const std::string& location, GdkPixbuf*& outPixbuf, size_t& outWidth, size_t& outHeight)
{
std::string ext = location.substr(location.find_last_of('.') + 1);
if (ext == "png" || ext == "jpg")
{
// png, load via svg
int width, height, channels;
stbi_uc* pixels = stbi_load(location.c_str(), &width, &height, &channels, STBI_rgb_alpha);
if (!pixels)
{
LOG("SNI: Cannot open " << location);
return;
}
outWidth = width;
outHeight = height;
uint8_t* iconData = new uint8_t[width * height * 4];
// Already rgba32
memcpy(iconData, pixels, width * height * 4);
stbi_image_free(pixels);
outPixbuf = gdk_pixbuf_new_from_data(
iconData, GDK_COLORSPACE_RGB, true, 8, width, height, width * 4,
+[](uint8_t* data, void*)
{
delete[] data;
},
nullptr);
}
else if (ext == "svg")
{
// Just a random size, this should be plenty enough wiggle room
outWidth = 64;
outHeight = 64;
// Use glib functions
GError* err = nullptr;
outPixbuf = gdk_pixbuf_new_from_file_at_scale(location.c_str(), outWidth, outHeight, true, &err);
if (err)
{
LOG("SNI: Error loading svg " << location << ": " << err->message);
return;
}
}
}
static Item CreateItem(std::string&& name, std::string&& object)
{
Item item{};
@ -102,28 +173,16 @@ namespace SNI
LOG("SNI: Height: " << height);
item.w = width;
item.h = height;
item.iconData = new uint8_t[width * height * 4];
uint8_t* iconData = new uint8_t[width * height * 4];
uint8_t px = 0;
int i = 0;
while (g_variant_iter_next(data, "y", &px))
{
item.iconData[i] = px;
iconData[i] = px;
i++;
}
for (int i = 0; i < width * height; i++)
{
struct Px
{
// This should be bgra...
// Since source is ARGB32 in network order(=big-endian)
// and x86 Linux is little-endian, we *should* swap b and r...
uint8_t a, r, g, b;
};
Px& pixel = ((Px*)item.iconData)[i];
// Swap to create rgba
pixel = {pixel.r, pixel.g, pixel.b, pixel.a};
}
item.pixbuf = ToPixbuf(iconData, width, height);
g_variant_iter_free(data);
@ -148,7 +207,7 @@ namespace SNI
for (auto& dataDir : Utils::Split(dataDirs, ':'))
{
LOG("SNI: Searching icon " << iconName << " in " << dataDir << "/icons");
std::string path = Utils::FindFileWithName(dataDir + "/icons", iconName, ".png");
std::string path = Utils::FindFileWithName(dataDir + "/icons", iconName);
if (path != "")
{
iconPath = path;
@ -161,7 +220,7 @@ namespace SNI
// Fallback to /usr/share/icons
LOG("SNI: Searching icon " << iconName << " in "
<< "/usr/share/icons");
iconPath = Utils::FindFileWithName("/usr/share/icons", iconName, ".png");
iconPath = Utils::FindFileWithName("/usr/share/icons", iconName);
}
return iconPath;
};
@ -187,7 +246,8 @@ namespace SNI
}
else
{
iconPath = std::string(themePath) + "/" + iconName + ".png"; // TODO: Find out if this is always png
LOG("SNI: Searching icon " << iconName << " in " << themePath);
iconPath = Utils::FindFileWithName(themePath, iconName);
}
g_variant_unref(themePathVariant);
@ -222,20 +282,8 @@ namespace SNI
LOG("SNI: Cannot find icon path for " << name);
return item;
}
int width, height, channels;
stbi_uc* pixels = stbi_load(iconPath.c_str(), &width, &height, &channels, STBI_rgb_alpha);
if (!pixels)
{
LOG("SNI: Cannot open " << iconPath);
return item;
}
item.w = width;
item.h = height;
item.iconData = new uint8_t[width * height * 4];
// Already rgba32
memcpy(item.iconData, pixels, width * height * 4);
stbi_image_free(pixels);
LOG("SNI: Creating icon from \"" << iconPath << "\"");
ToPixbuf(iconPath, item.pixbuf, item.w, item.h);
}
// Query tooltip(Steam e.g. doesn't have one)
@ -303,6 +351,7 @@ namespace SNI
LOG("SNI: " << name << " vanished!");
g_bus_unwatch_name(it->watcherID);
g_dbus_connection_signal_unsubscribe(dbusConnection, it->propertyChangeWatcherID);
g_free(it->pixbuf);
items.erase(it);
InvalidateWidget();
return;
@ -360,6 +409,7 @@ namespace SNI
{
g_bus_unwatch_name(it->watcherID);
g_dbus_connection_signal_unsubscribe(dbusConnection, it->propertyChangeWatcherID);
g_free(it->pixbuf);
items.erase(it);
}
else
@ -441,7 +491,7 @@ namespace SNI
bool rotatedIcons = (Config::Get().location == 'L' || Config::Get().location == 'R') && Config::Get().iconsAlwaysUp;
for (auto& item : items)
{
if (item.iconData)
if (item.pixbuf)
{
auto eventBox = Widget::Create<EventBox>();
item.gtkEvent = eventBox.get();
@ -496,7 +546,7 @@ namespace SNI
}
}
Utils::SetTransform(*texture, {size, true, Alignment::Fill}, {size, true, Alignment::Fill, 0, rotatedIcons ? 6 : 0});
texture->SetBuf(item.w, item.h, item.iconData);
texture->SetBuf(item.pixbuf, item.w, item.h);
texture->SetTooltip(item.tooltip);
texture->SetAngle(Utils::GetAngle() == 270 ? 90 : 0);

View file

@ -525,16 +525,13 @@ Texture::~Texture()
{
if (m_Pixbuf)
g_free(m_Pixbuf);
if (m_Bytes)
g_free(m_Bytes);
}
void Texture::SetBuf(size_t width, size_t height, uint8_t* buf)
void Texture::SetBuf(GdkPixbuf* pixbuf, size_t width, size_t height)
{
m_Width = width;
m_Height = height;
m_Bytes = g_bytes_new(buf, m_Width * m_Height * 4);
m_Pixbuf = gdk_pixbuf_new_from_bytes((GBytes*)m_Bytes, GDK_COLORSPACE_RGB, true, 8, m_Width, m_Height, m_Width * 4);
m_Pixbuf = gdk_pixbuf_copy(pixbuf);
}
void Texture::Draw(cairo_t* cr)

View file

@ -285,8 +285,8 @@ public:
Texture() = default;
virtual ~Texture();
// Non-Owning, ARGB32
void SetBuf(size_t width, size_t height, uint8_t* buf);
// Non-Owning (Copies the pixbuf), ARGB32
void SetBuf(GdkPixbuf* pixbuf, size_t width, size_t height);
void ForceHeight(size_t height) { m_ForcedHeight = height; };
void AddPaddingTop(int32_t topPadding) { m_Padding = topPadding; };
@ -300,7 +300,6 @@ private:
size_t m_ForcedHeight = 0;
double m_Angle;
int32_t m_Padding = 0;
GBytes* m_Bytes;
GdkPixbuf* m_Pixbuf;
};