AnsweredAssumed Answered

Semi-transparent picture symbols in ArcObjects

Question asked by andek on Nov 28, 2014
Latest reply on Dec 2, 2014 by andek

When creating a new point layer with picture symbols in ArcMap 10.2.1 using ArcObjects, I have tried several ways to load semi-transparent symbol images from .png files, but to no avail, the transparency is always lost and appearing opaque.

 

Since IPictureMarkerSymbol.CreateMarkerSymbolFromFile doesn't handle .png, I think I will have to create the IPictureDisp myself. I know it should work somehow, since I can load the same semi-transparent .png file manually in the picture symbol in ArcMap, resulting in correct semi-transparency. And I know about the support for setting a transparency color, but it wouldn't handle semi-transparency.

 

My current solution, inspired from this post, goes something like this (except for the creation of the actual layer) but doesn't work either:

 

class IPictureDispConverter
{
  public static Guid iPictureDispGuid = typeof(stdole.IPictureDisp).GUID;
  /// Converts an Icon into a IPictureDisp
  public static IPictureDisp ConvertBitmapToIPictureDisp(Bitmap bitmap)
  {
    PICTDESC.Bitmap pictBit = new PICTDESC.Bitmap(bitmap);
    return OleCreatePictureIndirect(pictBit, ref iPictureDispGuid, true);
  }
  [DllImport("OleAut32.dll", EntryPoint = "OleCreatePictureIndirect", ExactSpelling = true, PreserveSig = false)]
  private static extern stdole.IPictureDisp OleCreatePictureIndirect([MarshalAs(UnmanagedType.AsAny)] object picdesc, ref Guid iid, bool fOwn);
  // WINFORMS COMMENT:
  // PICTDESC is a union in native, so we'll just
  // define different ones for the different types
  // the "unused" fields are there to make it the right
  // size, since the struct in native is as big as the biggest
  // union.
  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 = 0;
      internal int unused2 = 0;
      internal Icon(System.Drawing.Icon icon)
      {
        this.hicon = icon.ToBitmap().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 = 0;
      internal Bitmap(System.Drawing.Bitmap bitmap)
      {
        this.hbitmap = bitmap.GetHbitmap();
      }
    }
  }
}
// FROM ANOTHER PART OF THE CODE:
IPictureMarkerSymbol markerSymbol = new PictureMarkerSymbolClass();
Bitmap bitmap = new Bitmap(pngFilename);
markerSymbol.Picture = IPictureDispConverter.ConvertBitmapToIPictureDisp(bitmap);
ISimpleRenderer renderer = new SimpleRendererClass();
((IGeoFeatureLayer)layer).Renderer = renderer as IFeatureRenderer;
renderer.Symbol = markerSymbol.Picture as ISymbol;

 

Here is the result with my code:

Doesn't work

And here it is when I have pointed out the same .png file manually for the same layer in ArcMap afterwards:

Works

Does anyone know how to do this properly?

Outcomes