Resolving Ribbon Icon(.bmp) Rendering Issues in Autodesk Inventor Dark Theme

A recurring issue was observed in Autodesk Inventor where ribbon icons appeared distorted when using the Dark Theme. Developers reported icons looking fuzzy, losing transparency, or displaying unwanted white edges. This affected .bmp image formats across Inventor 2024, 2025, and 2026.

After detailed analysis, it was found that the problem originated from how icon images were being converted to IPictureDisp objects before being assigned to ribbon buttons.

This article explains the investigation, the cause, and the final working solution — including the enhanced PictureDispConverter.cs class that restores proper alpha transparency and consistent icon rendering.


Issue Summary

Observed Behavior

  • Icons appear fuzzy or lose sharpness in dark theme.
  • White pixels become transparent; semi-transparent edges turn white.
  • Transparency artifacts persist across .ico and .png icons.
  • Conversion through stdole.IPictureDisp causes transparency loss.

Root Cause

The standard conversion from .NET Image to stdole.IPictureDisp was creating a bitmap-based image (PICTYPE_BITMAP), which doesn’t preserve transparency when internally converted to an icon (HICON) by Inventor.


Technical Solution

The fix involves ensuring that the conversion explicitly creates a PICTYPE_ICON type IPictureDisp, preserving the alpha transparency of the icon.

This is achieved by customizing the PictureDispConverter.cs file from the Inventor SDK SimpleAddIn project.

Below is the complete working version.


Complete Code: PictureDispConverter.cs

using Inventor;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace InvAddIn
{
    public static class PictureDispConverter
    {
        // IPictureDisp GUID reference
        static Guid iPictureDispGuid = typeof(stdole.IPictureDisp).GUID;

        /// Converts an Icon into an IPictureDisp (PICTYPE_ICON)
        public static stdole.IPictureDisp ToIPictureDisp(Icon icon)
        {
            PICTDESC.Icon pictIcon = new PICTDESC.Icon(icon);
            return NativeMethods.OleCreatePictureIndirect(pictIcon, ref iPictureDispGuid, true);
        }

        /// Converts a Bitmap into an IPictureDisp (PICTYPE_ICON)
        public static stdole.IPictureDisp ToIPictureDisp(Bitmap bitmap)
        {
            PICTDESC.Icon pictIcon = new PICTDESC.Icon(bitmap);
            return NativeMethods.OleCreatePictureIndirect(pictIcon, ref iPictureDispGuid, true);
        }

        /// Converts a general Image into an IPictureDisp (PICTYPE_BITMAP)
        public static stdole.IPictureDisp ToIPictureDisp(Image image)
        {
            Bitmap bitmap = image as Bitmap;
            if (bitmap == null)
                bitmap = new Bitmap(image);

            PICTDESC.Bitmap pictBit = new PICTDESC.Bitmap(bitmap);
            return NativeMethods.OleCreatePictureIndirect(pictBit, ref iPictureDispGuid, true);
        }

        static class NativeMethods
        {
#pragma warning disable CS0618
            [DllImport("OleAut32.dll", EntryPoint = "OleCreatePictureIndirect", ExactSpelling = true, PreserveSig = false)]
            public static extern stdole.IPictureDisp OleCreatePictureIndirect(
                [MarshalAs(UnmanagedType.AsAny)] object picdesc,
                ref Guid iid,
                [MarshalAs(UnmanagedType.Bool)] bool fOwn);
#pragma warning restore CS0618
        }

        private static class PICTDESC
        {
            // Picture types
            public const short PICTYPE_UNINITIALIZED = -1;
            public const short PICTYPE_NONE = 0;
            public const short PICTYPE_BITMAP = 1;
            public const short PICTYPE_METAFILE = 2;
            public const short PICTYPE_ICON = 3;
            public const short PICTYPE_ENHMETAFILE = 4;

            [StructLayout(LayoutKind.Sequential)]
            public class Icon
            {
                internal int cbSizeOfStruct = Marshal.SizeOf(typeof(PICTDESC.Icon));
                internal int picType = PICTDESC.PICTYPE_ICON;
                internal IntPtr hicon = IntPtr.Zero;
                internal int unused1;
                internal int unused2;

                internal Icon(System.Drawing.Icon icon)
                {
                    this.hicon = icon.ToBitmap().GetHicon();
                }

                internal Icon(System.Drawing.Bitmap bitmap)
                {
                    this.hicon = bitmap.GetHicon();
                }
            }

            [StructLayout(LayoutKind.Sequential)]
            public class Bitmap
            {
                internal int cbSizeOfStruct = Marshal.SizeOf(typeof(PICTDESC.Bitmap));
                internal int picType = PICTDESC.PICTYPE_BITMAP;
                internal IntPtr hbitmap = IntPtr.Zero;
                internal IntPtr hpal = IntPtr.Zero;
                internal int unused;

                internal Bitmap(System.Drawing.Bitmap bitmap)
                {
                    this.hbitmap = bitmap.GetHbitmap();
                }
            }
        }
    }
}

Key Code Highlights and Explanation

1. Switching from PICTYPE_BITMAP to PICTYPE_ICON

This is the core fix:

PICTDESC.Icon pictIcon = new PICTDESC.Icon(bitmap);
return NativeMethods.OleCreatePictureIndirect(pictIcon, ref iPictureDispGuid, true);

Inventor treats the result as a native HICON, preserving transparency during rendering.


2. Using OleCreatePictureIndirect for Native Conversion

OleCreatePictureIndirect (from OleAut32.dll) generates a COM-compatible IPictureDisp.

This avoids the internal bitmap conversions that cause transparency loss.


3. Internal Icon Conversion

The PICTDESC.Icon struct creates an HICON directly from a .NET bitmap:

this.hicon = bitmap.GetHicon();

This works consistently for:

  • .ico
  • .png
  • .bmp (where the issue was originally observed)

4. Maintaining Compatibility

ToIPictureDisp(Image image) still uses PICTYPE_BITMAP for older add-ins that rely on the previous behavior.


Results

After implementing this updated converter:

✔ Icons render cleanly in both Light and Dark themes
✔ Transparency and anti-aliasing are preserved
✔ No fuzzy or white borders
✔ Works consistently for .png, .ico, and .bmp sources
✔ Fix verified across Inventor 2024–2026


Takeaways

AspectBefore FixAfter Fix
Image TypePICTYPE_BITMAPPICTYPE_ICON
TransparencyLostPreserved
Dark Theme RenderingFuzzy, white edgesCrisp and clear
Conversion MethodAxHost.GetIPictureDispFromPictureOleCreatePictureIndirect (Icon)

Conclusion

By explicitly converting ribbon icons to PICTYPE_ICON using OleCreatePictureIndirect, developers can ensure:

  • Full transparency preservation
  • Clean rendering in both Light and Dark themes
  • Reliable behavior across all supported Inventor versions

This small but impactful enhancement significantly improves the user experience and aligns Inventor add-in development with modern UI rendering standards.


Comments

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading