Simplified SVG Icon Implementation #121434

Open
opened 2024-05-05 01:39:43 +02:00 by Harley Acheson · 4 comments
Member

In Improved Icons #115536 I described the limitations of our current icons and proposed a way to replace them with scalable icons fonts. However, that idea has a number of complications and there might be a way to do it much simpler.

Our current process involves an SVG source document that is exported to multiple (two versions for different user scales) PNG sprite sheets that are broken into separate bitmaps at build time that are made into a bitmap sprite sheet we use at run time.

My earlier proposal was to turn these separate SVG icons into glyphs in a icon font. That way they could be used at any size, handled by our font drawing system. However the complications include waiting for FreeType to better support OpenType SVG, a lack of open source editing for this type of font, and the need for a separate and complex font build system that would create the fonts from a directory of SVGs.

However, much of this complication can be removed.

We break up the current SVG source file into separate SVG files, one per icon, and we include them with our installation as files on disk, similarly to how we ship multiple fonts in a folder.

We then internally map our icons to a range in a Unicode private use area, for example at U+F0000..U+FFFFD. When we get a request for a character in that range we first treat it just like we do for other text characters, we check if we already have this in our glyph caches. If not found we then load the SVG file, rasterize it ourselves (nanosvg), and then add it to our glyph cache in the same way we do when we get a bitmap from FreeType. Our GlyphBLF is just a bitmap, bounds, and an advance. Once added to our glyph cache the icon can be drawn with our current text shader exactly as if it came from an icon font.

There would be no more overhead for each icon as we currently have for any single font character. There is just one rasterization and then it is cached for later. We could then use our icons at ANY size and in multiple sizes simultaneously (like when we have blender windows on multiple monitors that differ in scale). In fact each size used would be rasterized to the exact size requested and would therefore look better when outside of our current exported sizes.

This idea would also work for tool icons. These icons are meant to be used at larger sizes, and in color, but we currently rasterize a single size of them on the CPU. We literally rasterize them every draw when we have use them at multiple sizes simultaneously.

This would also work for alert icons and the ones we use for File Browser. It would also work for the Blender logo, so no need to include exported PNGs of all these files, just rasterize when needed. This is work not done if never needed. If you only see 50 icons in a blender session that is all that is rasterized.

Not only would this eliminate the need for our current icon sheets, it also eliminates the need for our icon shadow sheets. We have an option for "Icon Border" which gives an outline around each icon, needed for some themes. To support this we make a blurred version of each icon sheet and keep it in memory for this purpose. By treating these icons as text characters we get the benefit of using our text shader to do shadows and outlines on the GPU.

This idea also works for our “event” icons, which are used for keymaps shown on the status bar. Currently these are constructed as needed using text and immediate mode calls, therefore not cached. But if we construct these as custom text glyphs then they are cached and drawn with our shader in batches. Doing it this way, versus making an icon font, means that the glyphs can contain translated text parts. And since they would have bounds and advance they would no longer have to be square. Edit: We wouldn't be able to construct an icon that contains text because of where this would need to occur in the process.

By delaying the rasterization of the SVGs in this way we also have a chance to (possibly) override their colors. Tool icons could have named colors that are replaced by theme colors at run time, fixing a problem with them currently in that their colors are hard-coded. It would certainly be easier for us to create and maintain tool icons as separate color SVG files rather than how they are now defined in blend files, exported as vectors that are then rasterized to bitmaps.

With the large number of available private use area slots we could also experiment with layered icons and put them together as separate pieces. With icon layers could treat layers as separate depths, and therefore support special effects. A layer could shadow over another layer. They could be shown as differing sizes at times for highlighting or bounce effects, for example. Or the layers could be offset horizontally to make left/right 3D pairs.

In [Improved Icons #115536](https://projects.blender.org/blender/blender/issues/115536) I described the limitations of our current icons and proposed a way to replace them with scalable icons fonts. However, that idea has a number of complications and there might be a way to do it much simpler. Our current process involves an SVG source document that is exported to multiple (two versions for different user scales) PNG sprite sheets that are broken into separate bitmaps at build time that are made into a bitmap sprite sheet we use at run time. My earlier proposal was to turn these separate SVG icons into glyphs in a icon font. That way they could be used at any size, handled by our font drawing system. However the complications include waiting for FreeType to better support OpenType SVG, a lack of open source editing for this type of font, and the need for a separate and complex font build system that would create the fonts from a directory of SVGs. However, much of this complication can be removed. We break up the current SVG source file into separate SVG files, one per icon, and we include them with our installation as files on disk, similarly to how we ship multiple fonts in a folder. We then internally map our icons to a range in a Unicode private use area, for example at U+F0000..U+FFFFD. When we get a request for a character in that range we first treat it just like we do for other text characters, we check if we already have this in our glyph caches. If not found we then load the SVG file, rasterize it ourselves (nanosvg), and then add it to our glyph cache in the same way we do when we get a bitmap from FreeType. Our GlyphBLF is just a bitmap, bounds, and an advance. Once added to our glyph cache the icon can be drawn with our current text shader exactly as if it came from an icon font. There would be no more overhead for each icon as we currently have for any single font character. There is just one rasterization and then it is cached for later. We could then use our icons at ANY size and in multiple sizes simultaneously (like when we have blender windows on multiple monitors that differ in scale). In fact each size used would be rasterized to the exact size requested and would therefore look better when outside of our current exported sizes. This idea would also work for tool icons. These icons are meant to be used at larger sizes, and in color, but we currently rasterize a single size of them on the CPU. We literally rasterize them every draw when we have use them at multiple sizes simultaneously. This would also work for alert icons and the ones we use for File Browser. It would also work for the Blender logo, so no need to include exported PNGs of all these files, just rasterize when needed. This is work not done if never needed. If you only see 50 icons in a blender session that is all that is rasterized. Not only would this eliminate the need for our current icon sheets, it also eliminates the need for our icon shadow sheets. We have an option for "Icon Border" which gives an outline around each icon, needed for some themes. To support this we make a blurred version of each icon sheet and keep it in memory for this purpose. By treating these icons as text characters we get the benefit of using our text shader to do shadows and outlines on the GPU. This idea also works for our “event” icons, which are used for keymaps shown on the status bar. Currently these are constructed as needed using text and immediate mode calls, therefore not cached. But if we construct these as custom text glyphs then they are cached and drawn with our shader in batches. ~~Doing it this way, versus making an icon font, means that the glyphs can contain translated text parts. And since they would have bounds and advance they would no longer have to be square.~~ Edit: We wouldn't be able to construct an icon that contains text because of where this would need to occur in the process. By delaying the rasterization of the SVGs in this way we also have a chance to (possibly) override their colors. Tool icons could have named colors that are replaced by theme colors at run time, fixing a problem with them currently in that their colors are hard-coded. It would certainly be easier for us to create and maintain tool icons as separate color SVG files rather than how they are now defined in blend files, exported as vectors that are then rasterized to bitmaps. With the large number of available private use area slots we could also experiment with layered icons and put them together as separate pieces. With icon layers could treat layers as separate depths, and therefore support special effects. A layer could shadow over another layer. They could be shown as differing sizes at times for highlighting or bounce effects, for example. Or the layers could be offset horizontally to make left/right 3D pairs.
Harley Acheson added the
Type
Design
label 2024-05-05 01:39:43 +02:00
Harley Acheson added this to the User Interface project 2024-05-05 01:39:43 +02:00

If not found we then load the SVG file, rasterize it ourselves (nanosvg), and then add it to our glyph cache in the same way we do when we get a bitmap from FreeType

Any guesses what is runtime performance of rasterizing a svg icon with nanosvg, comapred to rasterizing a glyph with freetype? Wondering whether that could be a potential performance issue (i.e. how much time it would add to startup to rasterize all the svg icons used in the UI?).

> If not found we then load the SVG file, rasterize it ourselves (nanosvg), and then add it to our glyph cache in the same way we do when we get a bitmap from FreeType Any guesses what is runtime performance of rasterizing a svg icon with nanosvg, comapred to rasterizing a glyph with freetype? Wondering whether that could be a potential performance issue (i.e. how much time it would add to startup to rasterize all the svg icons used in the UI?).
Author
Member

My guess is that it would be similar as I've tested something very close..

FreeType is now (sort of) supporting a specific font type called OpenTypeSVG where the glyphs can be defined using SVG. FreeType doesn't do much with these, but adds callbacks where you can obtain that SVG from it and rasterize it yourself and then hand back the bitmap. I tested that with NanoSVG and the speed was great.

But I had difficulties with their implementation and realized that there are lot of steps here we can eliminate instead of putting SVG into a font then using these callbacks to get it back, render it, give it freetype, only to get that very same bitmap back and put it into our own glyph cache that we use to output it.

My guess is that it would be similar as I've tested something very close.. FreeType is now (sort of) supporting a specific font type called OpenTypeSVG where the glyphs can be defined using SVG. FreeType doesn't do much with these, but adds callbacks where you can obtain that SVG from it and rasterize it yourself and then hand back the bitmap. I tested that with NanoSVG and the speed was great. But I had difficulties with their implementation and realized that there are lot of steps here we can eliminate instead of putting SVG into a font then using these callbacks to get it back, render it, give it freetype, only to get that very same bitmap back and put it into our own glyph cache that we use to output it.
Author
Member

First (very early and rough) proof of concept.

Here ICON_SCENE_DATA is being treated specially, forced through a new BLF_draw_icon() function. That function just draws with our regular text drawing code, but using specific Unicode codepoint. When a character in that range is detected, and is not in our glyph cache, blf_glyph_ensure then calls a new function that rasterizes the ICON_SCENE_DATA source SVG and adds it to our cache. Otherwise treated like any other text and is taken from the glyph cache if available:

image

First (very early and rough) proof of concept. Here ICON_SCENE_DATA is being treated specially, forced through a new BLF_draw_icon() function. That function just draws with our regular text drawing code, but using specific Unicode codepoint. When a character in that range is detected, and is not in our glyph cache, `blf_glyph_ensure` then calls a new function that rasterizes the ICON_SCENE_DATA source SVG and adds it to our cache. Otherwise treated like any other text and is taken from the glyph cache if available: ![image](/attachments/487a6954-df96-4faf-bc10-9f4c4d3e7d85)

This looks quite stunning, a pleasure to the eyes.

If this means we start shipping the SVG icons as files on disk maybe this opens up the possibility of users editing the files themselves.

Hopefully it can eventually mean users could make icon themes for Blender, maybe as part of the current UI color themes, or as an entirely separate themeable element.
Could be as simple as placing each "icon pack" as a subfolder, and allow users to add more of them, or make them installable as a zip package like an addon.

This looks quite stunning, a pleasure to the eyes. If this means we start shipping the SVG icons as files on disk maybe this opens up the possibility of users editing the files themselves. Hopefully it can eventually mean users could make icon themes for Blender, maybe as part of the current UI color themes, or as an entirely separate themeable element. Could be as simple as placing each "icon pack" as a subfolder, and allow users to add more of them, or make them installable as a zip package like an addon.
Sign in to join this conversation.
No Label
Interest
Alembic
Interest
Animation & Rigging
Interest
Asset Browser
Interest
Asset Browser Project Overview
Interest
Audio
Interest
Automated Testing
Interest
Blender Asset Bundle
Interest
BlendFile
Interest
Collada
Interest
Compatibility
Interest
Compositing
Interest
Core
Interest
Cycles
Interest
Dependency Graph
Interest
Development Management
Interest
EEVEE
Interest
EEVEE & Viewport
Interest
Freestyle
Interest
Geometry Nodes
Interest
Grease Pencil
Interest
ID Management
Interest
Images & Movies
Interest
Import Export
Interest
Line Art
Interest
Masking
Interest
Metal
Interest
Modeling
Interest
Modifiers
Interest
Motion Tracking
Interest
Nodes & Physics
Interest
OpenGL
Interest
Overlay
Interest
Overrides
Interest
Performance
Interest
Physics
Interest
Pipeline, Assets & IO
Interest
Platforms, Builds & Tests
Interest
Python API
Interest
Render & Cycles
Interest
Render Pipeline
Interest
Sculpt, Paint & Texture
Interest
Text Editor
Interest
Translations
Interest
Triaging
Interest
Undo
Interest
USD
Interest
User Interface
Interest
UV Editing
Interest
VFX & Video
Interest
Video Sequencer
Interest
Virtual Reality
Interest
Vulkan
Interest
Wayland
Interest
Workbench
Interest: X11
Legacy
Blender 2.8 Project
Legacy
Milestone 1: Basic, Local Asset Browser
Legacy
OpenGL Error
Meta
Good First Issue
Meta
Papercut
Meta
Retrospective
Meta
Security
Module
Animation & Rigging
Module
Core
Module
Development Management
Module
EEVEE & Viewport
Module
Grease Pencil
Module
Modeling
Module
Nodes & Physics
Module
Pipeline, Assets & IO
Module
Platforms, Builds & Tests
Module
Python API
Module
Render & Cycles
Module
Sculpt, Paint & Texture
Module
Triaging
Module
User Interface
Module
VFX & Video
Platform
FreeBSD
Platform
Linux
Platform
macOS
Platform
Windows
Priority
High
Priority
Low
Priority
Normal
Priority
Unbreak Now!
Status
Archived
Status
Confirmed
Status
Duplicate
Status
Needs Info from Developers
Status
Needs Information from User
Status
Needs Triage
Status
Resolved
Type
Bug
Type
Design
Type
Known Issue
Type
Patch
Type
Report
Type
To Do
No Milestone
No project
No Assignees
3 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: blender/blender#121434
No description provided.