UI: Color Icon Theme Internal Parts #125146

Merged
Harley Acheson merged 15 commits from Harley/blender:IconThemeColor into main 2024-08-11 20:10:52 +02:00
7 changed files with 207 additions and 24 deletions

View File

@ -1 +1 @@
<svg id="svg2" height="1200" viewBox="0 0 1200 1200" width="1200" xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"><sodipodi:namedview pagecolor="#303030" showgrid="true"><inkscape:grid id="grid5" units="px" spacingx="100" spacingy="100" color="#4772b3" opacity="0.2" visible="true" /></sodipodi:namedview><path id="path1" d="m286 622c-2.7555 0-5 2.2445-5 5s2.2445 5 5 5 5-2.2445 5-5-2.2445-5-5-5z" fill="#fff" transform="matrix(100 0 0 100 -27999.994 -62100)"/></svg> <svg id="svg2" height="1200" viewBox="0 0 1200 1200" width="1200" xmlns="http://www.w3.org/2000/svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"><sodipodi:namedview pagecolor="#303030" showgrid="true"><inkscape:grid id="grid5" units="px" spacingx="100" spacingy="100" color="#4772b3" opacity="0.2" visible="true" /></sodipodi:namedview><g><g id="blender.red_alert" fill="#ffffff" transform="matrix(1 0 0 .99407813 0 5.93843)"><path id="path1" d="m599.97471 191.39126c-220.28631 0-403.19327 185.73026-403.19327 406.16752 0 229.42349 172.48512 404.04942 403.19327 404.04942 230.70813 0 401.14369-178.37144 401.89049-404.04942.7445-224.93037-181.60419-406.16752-401.89049-406.16752z" stroke-width="79.9717"/></g><g id="blender.text" fill="#ffffff" opacity=".8"><path id="path1-1" d="m599.88703 100.19229c-275.55 0-499.81389 224.2639-499.81389 499.8139s224.26389 499.81391 499.81389 499.81391 499.81387-224.45002 499.81387-500.00002-224.26387-499.62779-499.81387-499.62779zm0 99.62779c221.506 0 400.18607 178.68011 400.18607 400.18611s-178.68007 400.18611-400.18607 400.18611-400.55831-175.88855-400.55831-397.39455 179.05231-402.97767 400.55831-402.97767z" stroke-width="100"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 583 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -11,6 +11,7 @@
#include "BLI_array.hh" #include "BLI_array.hh"
#include "BLI_bounds_types.hh" #include "BLI_bounds_types.hh"
#include "BLI_compiler_attrs.h" #include "BLI_compiler_attrs.h"
#include "BLI_function_ref.hh"
#include "BLI_string_ref.hh" #include "BLI_string_ref.hh"
#include "BLI_sys_types.h" #include "BLI_sys_types.h"
#include "BLI_vector.hh" #include "BLI_vector.hh"
@ -152,7 +153,8 @@ void BLF_draw_svg_icon(uint icon_id,
float size, float size,
float color[4] = nullptr, float color[4] = nullptr,
float outline_alpha = 1.0f, float outline_alpha = 1.0f,
bool multicolor = false); bool multicolor = false,
blender::FunctionRef<void(std::string &)> edit_source_cb = nullptr);
blender::Array<uchar> BLF_svg_icon_bitmap( blender::Array<uchar> BLF_svg_icon_bitmap(
uint icon_id, float size, int *r_width, int *r_height, bool multicolor = false); uint icon_id, float size, int *r_width, int *r_height, bool multicolor = false);

View File

@ -615,7 +615,8 @@ void BLF_draw_svg_icon(uint icon_id,
float size, float size,
float color[4], float color[4],
float outline_alpha, float outline_alpha,
bool multicolor) bool multicolor,
blender::FunctionRef<void(std::string &)> edit_source_cb)
{ {
#ifndef WITH_HEADLESS #ifndef WITH_HEADLESS
FontBLF *font = global_font[0]; FontBLF *font = global_font[0];
@ -623,11 +624,11 @@ void BLF_draw_svg_icon(uint icon_id,
/* Avoid bgl usage to corrupt BLF drawing. */ /* Avoid bgl usage to corrupt BLF drawing. */
GPU_bgl_end(); GPU_bgl_end();
blf_draw_gpu__start(font); blf_draw_gpu__start(font);
blf_draw_svg_icon(font, icon_id, x, y, size, color, outline_alpha, multicolor); blf_draw_svg_icon(font, icon_id, x, y, size, color, outline_alpha, multicolor, edit_source_cb);
blf_draw_gpu__end(font); blf_draw_gpu__end(font);
} }
#else #else
UNUSED_VARS(icon_id, x, y, size, color, outline_alpha, multicolor); UNUSED_VARS(icon_id, x, y, size, color, outline_alpha, multicolor, edit_source_cb);
#endif /* WITH_HEADLESS */ #endif /* WITH_HEADLESS */
} }

View File

@ -532,7 +532,8 @@ void blf_draw_svg_icon(FontBLF *font,
float size, float size,
float color[4], float color[4],
float outline_alpha, float outline_alpha,
bool multicolor) bool multicolor,
blender::FunctionRef<void(std::string &)> edit_source_cb)
{ {
blf_font_size(font, size); blf_font_size(font, size);
font->pos[0] = int(x); font->pos[0] = int(x);
@ -557,7 +558,7 @@ void blf_draw_svg_icon(FontBLF *font,
GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); GlyphCacheBLF *gc = blf_glyph_cache_acquire(font);
blf_batch_draw_begin(font); blf_batch_draw_begin(font);
GlyphBLF *g = blf_glyph_ensure_icon(gc, icon_id, multicolor); GlyphBLF *g = blf_glyph_ensure_icon(gc, icon_id, multicolor, edit_source_cb);
if (g) { if (g) {
blf_glyph_draw(font, gc, g, 0, 0); blf_glyph_draw(font, gc, g, 0, 0);
} }

View File

@ -352,13 +352,18 @@ static GlyphBLF *blf_glyph_cache_add_blank(GlyphCacheBLF *gc, uint charcode)
return result; return result;
} }
static GlyphBLF *blf_glyph_cache_add_svg(GlyphCacheBLF *gc, uint charcode, bool color) static GlyphBLF *blf_glyph_cache_add_svg(
GlyphCacheBLF *gc,
uint charcode,
bool color,
blender::FunctionRef<void(std::string &)> edit_source_cb = nullptr)
{ {
const char *svg_source = blf_get_icon_svg(int(charcode) - BLF_ICON_OFFSET); std::string svg_source = blf_get_icon_svg(int(charcode) - BLF_ICON_OFFSET);
/* NanoSVG alters the source file while parsing. */ if (edit_source_cb) {
char *writeable = BLI_strdup(svg_source); edit_source_cb(svg_source);
NSVGimage *image = nsvgParse(writeable, "px", 96.0f); }
MEM_freeN(writeable);
NSVGimage *image = nsvgParse(svg_source.data(), "px", 96.0f);
if (image == nullptr) { if (image == nullptr) {
return blf_glyph_cache_add_blank(gc, charcode); return blf_glyph_cache_add_blank(gc, charcode);
@ -1393,13 +1398,16 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, const uint charcode
} }
#ifndef WITH_HEADLESS #ifndef WITH_HEADLESS
GlyphBLF *blf_glyph_ensure_icon(GlyphCacheBLF *gc, const uint icon_id, bool color) GlyphBLF *blf_glyph_ensure_icon(GlyphCacheBLF *gc,
const uint icon_id,
bool color,
blender::FunctionRef<void(std::string &)> edit_source_cb)
{ {
GlyphBLF *g = blf_glyph_cache_find_glyph(gc, icon_id + BLF_ICON_OFFSET, 0); GlyphBLF *g = blf_glyph_cache_find_glyph(gc, icon_id + BLF_ICON_OFFSET, 0);
if (g) { if (g) {
return g; return g;
} }
return blf_glyph_cache_add_svg(gc, icon_id + BLF_ICON_OFFSET, color); return blf_glyph_cache_add_svg(gc, icon_id + BLF_ICON_OFFSET, color, edit_source_cb);
} }
#endif /* WITH_HEADLESS */ #endif /* WITH_HEADLESS */

View File

@ -10,6 +10,7 @@
#include "BLI_array.hh" #include "BLI_array.hh"
#include "BLI_bounds_types.hh" #include "BLI_bounds_types.hh"
#include "BLI_function_ref.hh"
#include "BLI_string_ref.hh" #include "BLI_string_ref.hh"
#include "BLI_vector.hh" #include "BLI_vector.hh"
@ -107,7 +108,8 @@ void blf_draw_svg_icon(FontBLF *font,
float size, float size,
float color[4] = nullptr, float color[4] = nullptr,
float outline_alpha = 1.0f, float outline_alpha = 1.0f,
bool multicolor = false); bool multicolor = false,
blender::FunctionRef<void(std::string &)> edit_source_cb = nullptr);
Harley marked this conversation as resolved Outdated

FunctionRef would be a better fit here. std::function is only used when it needs to own data (i.e. the function is passed around and saved for later).

`FunctionRef` would be a better fit here. `std::function` is only used when it needs to own data (i.e. the function is passed around and saved for later).
blender::Array<uchar> blf_svg_icon_bitmap( blender::Array<uchar> blf_svg_icon_bitmap(
FontBLF *font, uint icon_id, float size, int *r_width, int *r_height, bool multicolor = false); FontBLF *font, uint icon_id, float size, int *r_width, int *r_height, bool multicolor = false);
@ -185,7 +187,11 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode, uint
GlyphBLF *blf_glyph_ensure_subpixel(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, int32_t pen_x); GlyphBLF *blf_glyph_ensure_subpixel(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, int32_t pen_x);
#endif #endif
GlyphBLF *blf_glyph_ensure_icon(GlyphCacheBLF *gc, uint icon_id, bool color = false); GlyphBLF *blf_glyph_ensure_icon(
GlyphCacheBLF *gc,
uint icon_id,
bool color = false,
blender::FunctionRef<void(std::string &)> edit_source_cb = nullptr);
/** /**
* Convert a character's outlines into curves. * Convert a character's outlines into curves.

View File

@ -49,6 +49,8 @@
#include "interface_intern.hh" #include "interface_intern.hh"
#include "fmt/format.h"
struct IconImage { struct IconImage {
int w; int w;
int h; int h;
@ -1295,6 +1297,155 @@ static int get_draw_size(enum eIconSizes size)
} }
} }
static void svg_replace_color_attributes(std::string &svg,
const std::string &name,
Harley marked this conversation as resolved Outdated

Pass std::string by const reference rather than by value.

Generally I'm a bit concerned by the performance of this function when it starts acting on thousands of icons. There are a lot of incremental updates to the text, and then it's called once for every theme color.

I don't want to get in the way or request premature optimizations, but I'd just suggest keeping an eye on this as more SVG icons are processed with this function. It shouldn't have an impact on startup time for example.

Pass `std::string` by const reference rather than by value. Generally I'm a bit concerned by the performance of this function when it starts acting on thousands of icons. There are a lot of incremental updates to the text, and then it's called once for every theme color. I don't want to get in the way or request premature optimizations, but I'd just suggest keeping an eye on this as more SVG icons are processed with this function. It shouldn't have an impact on startup time for example.

Agreed. I'll add a "Todo" comment to icon_source_edit_cb indicating that this text processing is suboptimal with all the inserts and has lots of room for optimization.

Agreed. I'll add a "Todo" comment to `icon_source_edit_cb` indicating that this text processing is suboptimal with all the inserts and has lots of room for optimization.

Actually re-wrote that section so the processing is done by scanning the string by group, only for those that start with "blender."

Actually re-wrote that section so the processing is done by scanning the string by group, only for those that start with "blender."
const size_t start,
const size_t end)
{
bTheme *btheme = UI_GetTheme();
uchar white[] = {255, 255, 255, 255};
uchar black[] = {0, 0, 0, 255};
uchar logo_orange[] = {232, 125, 13, 255};
uchar logo_blue[] = {38, 87, 135, 255};
/* Tool colors hardcoded for now. */
uchar tool_add[] = {117, 255, 175, 255};
uchar tool_remove[] = {245, 107, 91, 255};
uchar tool_select[] = {255, 176, 43, 255};
uchar tool_transform[] = {217, 175, 245, 255};
uchar tool_white[] = {255, 255, 255, 255};
uchar tool_red[] = {214, 45, 48, 255};
struct ColorItem {
const char *name;
uchar *col = nullptr;
int colorid = TH_UNDEFINED;
int spacetype = SPACE_TYPE_ANY;
} items[] = {
{"blender.white", white},
{"blender.black", black},
{"blender.logo_orange", logo_orange},
{"blender.logo_blue", logo_blue},
{"blender.selected", btheme->tui.wcol_regular.inner},
{"blender.mesh_selected", btheme->space_view3d.vertex_select},
{"blender.back", nullptr, TH_BACK},
{"blender.text", nullptr, TH_TEXT},
{"blender.text_hi", nullptr, TH_TEXT_HI},
{"blender.red_alert", nullptr, TH_REDALERT},
{"blender.error", nullptr, TH_INFO_ERROR, SPACE_INFO},
{"blender.warning", nullptr, TH_INFO_WARNING, SPACE_INFO},
{"blender.info", nullptr, TH_INFO_INFO, SPACE_INFO},
{"blender.scene", nullptr, TH_ICON_SCENE},
Harley marked this conversation as resolved Outdated

Why not just name != item.name?

Why not just `name != item.name`?
{"blender.collection", nullptr, TH_ICON_COLLECTION},
{"blender.object", nullptr, TH_ICON_OBJECT},
{"blender.object_data", nullptr, TH_ICON_OBJECT_DATA},
{"blender.modifier", nullptr, TH_ICON_MODIFIER},
{"blender.shading", nullptr, TH_ICON_SHADING},
{"blender.folder", nullptr, TH_ICON_FOLDER},
{"blender.fund", nullptr, TH_ICON_FUND},
{"blender.tool_add", tool_add},
{"blender.tool_remove", tool_remove},
{"blender.tool_select", tool_select},
{"blender.tool_transform", tool_transform},
{"blender.tool_white", tool_white},
{"blender.tool_red", tool_red},
};
for (const ColorItem &item : items) {
if (name != item.name) {
continue;
}
uchar color[4];
if (item.col) {
memcpy(color, item.col, sizeof(color));
}
else if (item.colorid != TH_UNDEFINED) {
if (item.spacetype != SPACE_TYPE_ANY) {
UI_GetThemeColorType4ubv(item.colorid, item.spacetype, color);
}
else {
Harley marked this conversation as resolved Outdated

These numbers 6, 20, 14, 16, should come from somewhere. I imagine they're the length of some string? Probably better to compute it at compile time then. For example with StringRef::size().

These numbers 6, 20, 14, 16, should come from somewhere. I imagine they're the length of some string? Probably better to compute it at compile time then. For example with `StringRef::size()`.
UI_GetThemeColor4ubv(item.colorid, color);
}
}
else {
continue;
}
std::string hexcolor = fmt::format(
"{:02x}{:02x}{:02x}{:02x}", color[0], color[1], color[2], color[3]);
size_t att_start = start;
while (1) {
constexpr static blender::StringRef key = "fill=\"";
att_start = svg.find(key, att_start);
if (att_start == std::string::npos || att_start > end) {
break;
}
const size_t att_end = svg.find("\"", att_start + key.size());
if (att_end != std::string::npos && att_end < end) {
svg.replace(att_start, att_end - att_start, key + "#" + hexcolor);
}
att_start += blender::StringRef(key + "#rrggbbaa\"").size();
}
att_start = start;
while (1) {
constexpr static blender::StringRef key = "fill:";
att_start = svg.find(key, att_start);
if (att_start == std::string::npos || att_start > end) {
break;
}
const size_t att_end = svg.find(";", att_start + key.size());
if (att_end != std::string::npos && att_end - att_start < end) {
svg.replace(att_start, att_end - att_start, key + "#" + hexcolor);
}
att_start += blender::StringRef(key + "#rrggbbaa").size();
}
}
}
static void icon_source_edit_cb(std::string &svg)
{
size_t g_start = 0;
/* Scan string, processing only groups with our keyword ids. */
while (1) {
/* Look for a blender id, quick exit if not found. */
constexpr static blender::StringRef key = "id=\"";
const size_t id_start = svg.find(key + "blender.", g_start);
if (id_start == std::string::npos) {
return;
}
/* Scan back to beginning of this group element. */
g_start = svg.rfind("<g", id_start);
if (g_start == std::string::npos) {
/* Malformed. */
return;
}
/* Scan forward to end of the group. */
const size_t g_end = svg.find("</g>", id_start);
if (g_end == std::string::npos) {
/* Malformed. */
return;
}
/* Get group id name. */
const size_t id_end = svg.find("\"", id_start + key.size());
if (id_end != std::string::npos) {
std::string id_name = svg.substr(id_start + key.size(), id_end - id_start - key.size());
/* Replace this group's colors. */
svg_replace_color_attributes(svg, id_name, g_start, g_end);
}
g_start = g_end;
}
}
static void icon_draw_size(float x, static void icon_draw_size(float x,
float y, float y,
int icon_id, int icon_id,
@ -1398,13 +1549,27 @@ static void icon_draw_size(float x,
} }
color[3] *= alpha; color[3] *= alpha;
BLF_draw_svg_icon(uint(icon_id),
x, if (di->type == ICON_TYPE_SVG_COLOR) {
y, BLF_draw_svg_icon(uint(icon_id),
float(draw_size) / aspect, x,
color, y,
outline_intensity, float(draw_size) / aspect,
di->type == ICON_TYPE_SVG_COLOR); color,
outline_intensity,
true,
icon_source_edit_cb);
}
else {
BLF_draw_svg_icon(uint(icon_id),
x,
y,
float(draw_size) / aspect,
color,
outline_intensity,
false,
nullptr);
}
if (text_overlay && text_overlay->text[0] != '\0') { if (text_overlay && text_overlay->text[0] != '\0') {
/* Handle the little numbers on top of the icon. */ /* Handle the little numbers on top of the icon. */