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; 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)) if (!std::filesystem::exists(directory))
{ {
@ -143,7 +143,7 @@ namespace Utils
{ {
if (path.is_directory()) if (path.is_directory())
continue; 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(); return path.path().string();
} }

View file

@ -32,7 +32,7 @@ namespace SNI
std::string object; std::string object;
size_t w = 0; size_t w = 0;
size_t h = 0; size_t h = 0;
uint8_t* iconData = nullptr; GdkPixbuf* pixbuf = nullptr;
std::string tooltip = ""; std::string tooltip = "";
@ -54,6 +54,77 @@ namespace SNI
Widget* parentBox; Widget* parentBox;
Widget* iconBox; 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) static Item CreateItem(std::string&& name, std::string&& object)
{ {
Item item{}; Item item{};
@ -102,28 +173,16 @@ namespace SNI
LOG("SNI: Height: " << height); LOG("SNI: Height: " << height);
item.w = width; item.w = width;
item.h = height; item.h = height;
item.iconData = new uint8_t[width * height * 4]; uint8_t* iconData = new uint8_t[width * height * 4];
uint8_t px = 0; uint8_t px = 0;
int i = 0; int i = 0;
while (g_variant_iter_next(data, "y", &px)) while (g_variant_iter_next(data, "y", &px))
{ {
item.iconData[i] = px; iconData[i] = px;
i++; i++;
} }
for (int i = 0; i < width * height; i++) item.pixbuf = ToPixbuf(iconData, width, height);
{
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};
}
g_variant_iter_free(data); g_variant_iter_free(data);
@ -148,7 +207,7 @@ namespace SNI
for (auto& dataDir : Utils::Split(dataDirs, ':')) for (auto& dataDir : Utils::Split(dataDirs, ':'))
{ {
LOG("SNI: Searching icon " << iconName << " in " << dataDir << "/icons"); 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 != "") if (path != "")
{ {
iconPath = path; iconPath = path;
@ -161,7 +220,7 @@ namespace SNI
// Fallback to /usr/share/icons // Fallback to /usr/share/icons
LOG("SNI: Searching icon " << iconName << " in " LOG("SNI: Searching icon " << iconName << " in "
<< "/usr/share/icons"); << "/usr/share/icons");
iconPath = Utils::FindFileWithName("/usr/share/icons", iconName, ".png"); iconPath = Utils::FindFileWithName("/usr/share/icons", iconName);
} }
return iconPath; return iconPath;
}; };
@ -187,7 +246,8 @@ namespace SNI
} }
else 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); g_variant_unref(themePathVariant);
@ -222,20 +282,8 @@ namespace SNI
LOG("SNI: Cannot find icon path for " << name); LOG("SNI: Cannot find icon path for " << name);
return item; return item;
} }
LOG("SNI: Creating icon from \"" << iconPath << "\"");
int width, height, channels; ToPixbuf(iconPath, item.pixbuf, item.w, item.h);
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);
} }
// Query tooltip(Steam e.g. doesn't have one) // Query tooltip(Steam e.g. doesn't have one)
@ -303,6 +351,7 @@ namespace SNI
LOG("SNI: " << name << " vanished!"); LOG("SNI: " << name << " vanished!");
g_bus_unwatch_name(it->watcherID); g_bus_unwatch_name(it->watcherID);
g_dbus_connection_signal_unsubscribe(dbusConnection, it->propertyChangeWatcherID); g_dbus_connection_signal_unsubscribe(dbusConnection, it->propertyChangeWatcherID);
g_free(it->pixbuf);
items.erase(it); items.erase(it);
InvalidateWidget(); InvalidateWidget();
return; return;
@ -360,6 +409,7 @@ namespace SNI
{ {
g_bus_unwatch_name(it->watcherID); g_bus_unwatch_name(it->watcherID);
g_dbus_connection_signal_unsubscribe(dbusConnection, it->propertyChangeWatcherID); g_dbus_connection_signal_unsubscribe(dbusConnection, it->propertyChangeWatcherID);
g_free(it->pixbuf);
items.erase(it); items.erase(it);
} }
else else
@ -441,7 +491,7 @@ namespace SNI
bool rotatedIcons = (Config::Get().location == 'L' || Config::Get().location == 'R') && Config::Get().iconsAlwaysUp; bool rotatedIcons = (Config::Get().location == 'L' || Config::Get().location == 'R') && Config::Get().iconsAlwaysUp;
for (auto& item : items) for (auto& item : items)
{ {
if (item.iconData) if (item.pixbuf)
{ {
auto eventBox = Widget::Create<EventBox>(); auto eventBox = Widget::Create<EventBox>();
item.gtkEvent = eventBox.get(); 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}); 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->SetTooltip(item.tooltip);
texture->SetAngle(Utils::GetAngle() == 270 ? 90 : 0); texture->SetAngle(Utils::GetAngle() == 270 ? 90 : 0);

View file

@ -525,16 +525,13 @@ Texture::~Texture()
{ {
if (m_Pixbuf) if (m_Pixbuf)
g_free(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_Width = width;
m_Height = height; m_Height = height;
m_Bytes = g_bytes_new(buf, m_Width * m_Height * 4); m_Pixbuf = gdk_pixbuf_copy(pixbuf);
m_Pixbuf = gdk_pixbuf_new_from_bytes((GBytes*)m_Bytes, GDK_COLORSPACE_RGB, true, 8, m_Width, m_Height, m_Width * 4);
} }
void Texture::Draw(cairo_t* cr) void Texture::Draw(cairo_t* cr)

View file

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