🐛 BUG - 8.14 - Rhino.UI.DrawingUtilities.BitmapFromSvg - No Longer Works

Hello,

Prior to 8.14, the Rhino.UI.DrawingUtilities.BitmapFromSvg would ensure that a bitmap derived from SVG code with adjustForDarkMode enabled would modify the SVG fill/stroke color to properly lighten or darken based on the Rhino Theme.

This no longer is working, the adjustForDarkMode value seems to have no effect whatsoever now. Please see the simplified code below to repeat this.

Unfortunately, this bugs out our entire UI as a result since all our icons/buttons rely on SVG to Bitmap and dynamic theme switching support.

Thanks for your help! @CallumSykes @wim @Gijs

System Info

Rhino 8 SR14 2024-11-12 (Rhino 8, 8.14.24317.14001, Git hash:master @ 69ab12ed53cf1e6d17f23ab5fec3c3f7216df72d)
License type: Commercial, build 2024-11-12
License details: Cloud Zoo

Windows 11 (10.0.22631 SR0.0) or greater (Physical RAM: 64GB)
.NET 7.0.20

Computer platform: LAPTOP - Plugged in [98% battery remaining]

Standard graphics configuration.
Primary display: DisplayLink USB Device (DisplayLink) Memory: 0MB, Driver date: 2-9-2024 (M-D-Y).
> External USB display device with 4 adapter port(s)
- Windows Main Display attached to adapter port 0
- Secondary monitor attached to adapter port 1
Primary OpenGL: NVIDIA GeForce RTX 3080 Ti Laptop GPU (NVidia) Memory: 16GB, Driver date: 1-15-2023 (M-D-Y). OpenGL Ver: 4.6.0 NVIDIA 528.24
> Integrated accelerated graphics device with 4 adapter port(s)
- Video pass-through to primary display device

Secondary graphics devices.
Intel(R) Iris(R) Xe Graphics (Intel) Memory: 1GB, Driver date: 10-26-2022 (M-D-Y).
> Integrated graphics device with 4 adapter port(s)
- There are no monitors attached to this device. Laptop lid is probably closed
DisplayLink USB Device (DisplayLink) Memory: 0MB, Driver date: 2-9-2024 (M-D-Y).
> External USB display device with 0 adapter port(s)
- There are no monitors attached to this device. Laptop lid is probably closed

OpenGL Settings
Safe mode: Off
Use accelerated hardware modes: On
GPU Tessellation is: On
Redraw scene when viewports are exposed: On
Graphics level being used: OpenGL 4.6 (primary GPU’s maximum)

Anti-alias mode: 4x
Mip Map Filtering: Linear
Anisotropic Filtering Mode: High

Vendor Name: NVIDIA Corporation
Render version: 4.6
Shading Language: 4.60 NVIDIA
Driver Date: 1-15-2023
Driver Version: 31.0.15.2824
Maximum Texture size: 32768 x 32768
Z-Buffer depth: 24 bits
Maximum Viewport size: 32768 x 32768
Total Video Memory: 16 GB

Rhino plugins that do not ship with Rhino
C:\ProgramData\McNeel\Rhinoceros\7.0\Plug-ins\Datasmith Rhino Exporter (d1fdc795-b334-4933-b680-088119cdc6bb)\DatasmithRhino7.rhp “Datasmith Exporter” 5.1.0.0
C:\Program Files\Enscape\Enscape.Rhino.Plugin-net48\Enscape.Rhino8.Plugin.dll “Enscape.Rhino8.Plugin” 4.1.1.35
C:\Users\micha\AppData\Roaming\McNeel\Rhinoceros\packages\8.0\doodle\0.1.1-beta+9085\doodle.rhp “doodle” 0.0.0.0
C:\Users\micha\AppData\Roaming\McNeel\Rhinoceros\packages\8.0\Crash\1.4.2-beta\Crash.rhp “Crash” 1.4.0.0
C:\Users\micha\AppData\Roaming\McNeel\Rhinoceros\8.0\Plug-ins\D5LiveSync (e0d5e210-02f6-4ee9-a2b0-1675e225d958)\D5Conv.rhp “D5 Live Sync for Rhino”

Rhino plugins that ship with Rhino
C:\Program Files\Rhino 8\Plug-ins\Commands.rhp “Commands” 8.14.24317.14001
C:\Program Files\Rhino 8\Plug-ins\rdk.rhp “Renderer Development Kit”
C:\Program Files\Rhino 8\Plug-ins\RhinoRenderCycles.rhp “Rhino Render” 8.14.24317.14001
C:\Program Files\Rhino 8\Plug-ins\rdk_etoui.rhp “RDK_EtoUI” 8.14.24317.14001
C:\Program Files\Rhino 8\Plug-ins\NamedSnapshots.rhp “Snapshots”
C:\Program Files\Rhino 8\Plug-ins\MeshCommands.rhp “MeshCommands” 8.14.24317.14001
C:\Program Files\Rhino 8\Plug-ins\RhinoCycles.rhp “RhinoCycles” 8.14.24317.14001
C:\Program Files\Rhino 8\Plug-ins\Grasshopper\GrasshopperPlugin.rhp “Grasshopper” 8.14.24317.14001
C:\Program Files\Rhino 8\Plug-ins\RhinoCode\RhinoCodePlugin.rhp “RhinoCodePlugin” 8.14.24317.14001
C:\Program Files\Rhino 8\Plug-ins\Toolbars\Toolbars.rhp “Toolbars” 8.14.24317.14001
C:\Program Files\Rhino 8\Plug-ins\3dxrhino.rhp “3Dconnexion 3D Mouse”
C:\Program Files\Rhino 8\Plug-ins\Displacement.rhp “Displacement”
C:\Program Files\Rhino 8\Plug-ins\SectionTools.rhp “SectionTools”

#! python3
import Rhino
import Eto


class UI():
    # Set the color values for all buttons
    h_colors = {
        "bh_color": "#F0F0F0",
        "info": "#D3D3D3"
    }

    misc_icons = {
        # Info Icons
        "info": {
            # Default Icon
            "default": """<?xml version="1.0" encoding="utf-8"?>
                <svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
                    x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" overflow="visible" xml:space="preserve">
                <path d="M257,199.29L257,199.29c-27.78,0-50.3,22.52-50.3,50.3v158.3c0,27.78,22.52,50.3,50.3,50.3h0
                    c27.78,0,50.3-22.52,50.3-50.3v-158.3C307.3,221.81,284.78,199.29,257,199.29z"/>
                <path d="M256,55.41L256,55.41c-27.78,0-50.3,22.52-50.3,50.3v0c0,27.78,22.52,50.3,50.3,50.3h0
                    c27.78,0,50.3-22.52,50.3-50.3v0C306.3,77.94,283.78,55.41,256,55.41z"/></svg>""",
            # Hover Icon
            "hover": f"""<?xml version="1.0" encoding="utf-8"?>
                <svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
                    x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" overflow="visible" xml:space="preserve">
                <path fill="{h_colors.get("info")}" d="M257,199.29L257,199.29c-27.78,0-50.3,22.52-50.3,50.3v158.3c0,27.78,22.52,50.3,50.3,50.3h0
                    c27.78,0,50.3-22.52,50.3-50.3v-158.3C307.3,221.81,284.78,199.29,257,199.29z"/>
                <path fill="{h_colors.get("info")}" d="M256,55.41L256,55.41c-27.78,0-50.3,22.52-50.3,50.3v0c0,27.78,22.52,50.3,50.3,50.3h0
                    c27.78,0,50.3-22.52,50.3-50.3v0C306.3,77.94,283.78,55.41,256,55.41z"/></svg>"""
        }
    }


# Draw the image
info_icon = UI.misc_icons.get("info")  # Get the svg code for the icon

# Rhino.UI.EtoExtensions.ToEto(Rhino.UI.DrawingUtilities.BitmapFromSvg(info_icon['hover'], 512, 512, False)),  # Hover Icon
bitmap = Rhino.UI.EtoExtensions.ToEto(Rhino.UI.DrawingUtilities.BitmapFromSvg(info_icon['default'], 512, 512, True))  # Default Icon (Dark Mode)
# Rhino.UI.EtoExtensions.ToEto(Rhino.UI.DrawingUtilities.BitmapFromSvg(info_icon['default'], 512, 512)),  # Default Icon (Light Mode)

image_control = Eto.Forms.Button()
image_control.Image = bitmap

form = Eto.Forms.Form()
form.Size = Eto.Drawing.Size(400, 400)

layout = Eto.Forms.DynamicLayout()
layout.AddRow(image_control)

form.Content = layout

form.Show()

EDIT: This seems to also effect this method:

Rhino.UI.ImageResources.CreateEtoIcon(info_icon['default'], 512, 512, False)

Is there something different as of 8.14 that I need to add into my SVG code for this to work? This seems like a bug/regression to me.

Thanks!

Was this utility switching black/white pixels for you correctly for your icons @michaelvollrath, or doing something else?

In the backend there (was) a black/white pixel switcher which I removed. Internally we don’t use it, and I didn’t imagine anyone else was relying on it, so I removed it in favour of the svg syntax system where you can have dark mode attributes inside your svg which is much more customisable, as this offers a lot more flexibility and the two were in conflict.

An example of code that will work for you now.

<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
                    x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" overflow="visible" xml:space="preserve">
                <path fill="{h_colors.get("info")}" fill-dark="probably-white" d="M257,199.29L257,199.29c-27.78,0-50.3,22.52-50.3,50.3v158.3c0,27.78,22.52,50.3,50.3,50.3h0
                    c27.78,0,50.3-22.52,50.3-50.3v-158.3C307.3,221.81,284.78,199.29,257,199.29z"/>
                <path fill="{h_colors.get("info")}" fill-dark="probably-white" d="M256,55.41L256,55.41c-27.78,0-50.3,22.52-50.3,50.3v0c0,27.78,22.52,50.3,50.3,50.3h0
                    c27.78,0,50.3-22.52,50.3-50.3v0C306.3,77.94,283.78,55.41,256,55.41z"/></svg>

1 Like

I think you need to add this:

Rhino.UI.EtoExtensions.UseRhinoStyle(form);
1 Like

Thanks @CallumSykes,

I’m in favor of that change if the svg syntax it supports it so thats great and thanks for sharing the updated code.

When i copy what you provided and replace my previous code (changing “probably-white”) for a “#FFFFFF” hex value it does not do anything differently.

I don’t see dark-fill as a svg syntax when I search online.

Do you meant that Rhino as of 8.14 will check the svg for “dark-fill” variable and somehow update the svg accordingly?

I tried using the code you provided within the code I opened this post with but it does not work for me even when replacing “probably-white” with a hex value.

Thanks for your help!

@Gijs Thank you I am using custom drawable graphics so I don’t have the form title bars exposed. I have a toggle button that switches the Rhino Theme through RhinoCommon and then my Eto UI updates the UI colors accordingly and refreshes the controls.

Maybe I don’t understand the issue very well, but if you are updating the UI colors via a button on the form, could you then not simply force the button colors as well in the same button press?

1 Like

You’re right, I can update the fill color when the theme is switched and that would suffice.

Prior I just passed an argument for the pixel switching Callum mentioned to do this automatically I guess but good point in that I can just update my svg fill code with the color i need on theme switch.

Thanks @Gijs, sometimes I need to get my head out of the weeds

Apologies, it is "fill-dark", and "stroke-dark" I have updated my answer above so others don’t get confused.

Thanks @CallumSykes,

The “fill-dark” is working now but “stroke-dark” does not seem to work.

I’ve tried this way:

        "collapse": {
            # Default Icon
            "default": f"""<?xml version="1.0" encoding="utf-8"?>
                <svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
                    x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" overflow="visible" xml:space="preserve" stroke-dark="{h_colors.get("bh_color")}"> 
                <line fill="none" stroke-width="120" stroke-linecap="round" stroke-miterlimit="10" x1="256" y1="340.83" x2="86.34" y2="171.17"/>
                <line fill="none" stroke-width="120" stroke-linecap="round" stroke-miterlimit="10" x1="256" y1="340.83" x2="425.66" y2="171.17"/></svg>""",
            # Hover Icon
            "hover": """<?xml version="1.0" encoding="utf-8"?>
                <svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
                    x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" overflow="visible" xml:space="preserve">
                <line fill="none" stroke="#000000" stroke-width="120" stroke-linecap="round" stroke-miterlimit="10" x1="256" y1="340.83" x2="86.34" y2="171.17"/>
                <line fill="none" stroke="#000000" stroke-width="120" stroke-linecap="round" stroke-miterlimit="10" x1="256" y1="340.83" x2="425.66" y2="171.17"/></svg>"""

And this way:

        "collapse": {
            # Default Icon
            "default": f"""<?xml version="1.0" encoding="utf-8"?>
                <svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
                    x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" overflow="visible" xml:space="preserve"> 
                <line fill="none" stroke-dark="{h_colors.get("bh_color")}" stroke-width="120" stroke-linecap="round" stroke-miterlimit="10" x1="256" y1="340.83" x2="86.34" y2="171.17"/>
                <line fill="none" stroke-dark="{h_colors.get("bh_color")}" stroke-width="120" stroke-linecap="round" stroke-miterlimit="10" x1="256" y1="340.83" x2="425.66" y2="171.17"/></svg>""",
            # Hover Icon
            "hover": """<?xml version="1.0" encoding="utf-8"?>
                <svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
                    x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" overflow="visible" xml:space="preserve">
                <line fill="none" stroke="#000000" stroke-width="120" stroke-linecap="round" stroke-miterlimit="10" x1="256" y1="340.83" x2="86.34" y2="171.17"/>
                <line fill="none" stroke="#000000" stroke-width="120" stroke-linecap="round" stroke-miterlimit="10" x1="256" y1="340.83" x2="425.66" y2="171.17"/></svg>"""
        },

Furthermore the dark-fill only works with this method:

Rhino.UI.ImageResources.CreateEtoIcon(string svgContents, int width, int height, bool adjustForDarkMode)

This method will not adjust the theme colors:

Rhino.UI.EtoExtensions.ToEto(Rhino.UI.DrawingUtilities.BitmapFromSvg(info_icon['default'], 512, 512, True)),  # Default Icon (Dark Mode)

One other thing is switching to the CreateEtoIcon method is significantly slower to create the UI over the BitmapFromSvg method and also does not seem to have the anti-aliasing applied like the BitmapFromSvg method does. Time wise it’s the difference of instant vs 2 seconds when more than 10 icons are present on screen

The svgs produced via CreateEtoIcon are jagged regardless of the size I set for them, is there an additional argument or setting I need to apply somewhere to prevent this?

Thanks for your help!

I will investigate this, do some tests, and get back to you. I didn’t plan to advertise this quite yet :sweat_smile:, but looks like I’ll need to as the pixel flipping was removed. Any fixes for this would be in 8.15 at the earliest, apologies for the regression :slight_smile: .

1 Like

All good @CallumSykes I appreciate you working with me on it and can appreciate that there is likely a lot of svg stuff going on behind the scenes more than just the way I’m trying to use it.

Thanks for looking into it!

1 Like