Add transparent bitmap support for credential tiles

This commit is contained in:
Ryan Newington
2026-08-21 09:32:53 +10:00
parent 459470a5ce
commit be65cbaa93
7 changed files with 143 additions and 28 deletions
@@ -22,11 +22,16 @@ namespace Lithnet.CredentialProvider
this.backgroundColor = Color.FromArgb(70, 70, 70);
}
protected BitmapControl(BitmapControl source) : base(source) { }
protected BitmapControl(BitmapControl source) : base(source)
{
this.bitmap = source.bitmap;
this.backgroundColor = source.backgroundColor;
}
/// <summary>
/// Specifies the background color that should replace any transparent elements of the image. This defaults to #707070
/// Specifies the background color used to replace transparent pixels for <see cref="CredentialTile"/> and <see cref="CredentialTile2"/>. This defaults to #707070.
/// </summary>
/// <remarks>This property does not apply to <see cref="CredentialTile3"/>.</remarks>
public Color BackgroundColor
{
get { return this.backgroundColor; }
@@ -53,10 +58,7 @@ namespace Lithnet.CredentialProvider
{
this.bitmap = value;
if (this.Events is ICredentialProviderCredentialEvents2 e)
{
e.SetFieldBitmap(this.Credential, this.Id, this.GetHBitmap());
}
this.UpdateBitmap();
this.RaisePropertyChanged();
}
@@ -76,26 +78,48 @@ namespace Lithnet.CredentialProvider
internal IntPtr GetBitmapBuffer(out uint size)
{
size = 0;
var hbitmap = this.GetHBitmap();
if (hbitmap == IntPtr.Zero)
if (this.bitmap == null)
{
return IntPtr.Zero;
}
var image = Bitmap.FromHbitmap(hbitmap);
IntPtr buffer = IntPtr.Zero;
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, ImageFormat.Bmp);
this.bitmap.Save(ms, ImageFormat.Png);
var bitmapBytes = ms.ToArray();
size = (uint)bitmapBytes.Length;
buffer = Marshal.AllocCoTaskMem(bitmapBytes.Length);
size = checked((uint)bitmapBytes.Length);
IntPtr buffer = Marshal.AllocCoTaskMem(bitmapBytes.Length);
Marshal.Copy(bitmapBytes, 0, buffer, bitmapBytes.Length);
return buffer;
}
}
private void UpdateBitmap()
{
if (this.Credential is ICredentialProviderCredential3 && this.Events is ICredentialProviderCredentialEvents3 events3)
{
IntPtr buffer = this.GetBitmapBuffer(out uint size);
try
{
events3.SetFieldBitmapBuffer(this.Credential, this.Id, size, buffer);
}
finally
{
if (buffer != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(buffer);
}
}
return;
}
return buffer;
if (this.Events is ICredentialProviderCredentialEvents2 events2)
{
events2.SetFieldBitmap(this.Credential, this.Id, this.GetHBitmap());
}
}
}
}
}
@@ -35,10 +35,7 @@ namespace Lithnet.CredentialProvider
internal override ControlBase Clone()
{
var clone = new CredentialProviderLogoControl(this);
clone.Bitmap = this.Bitmap;
clone.BackgroundColor = this.BackgroundColor;
return clone;
return new CredentialProviderLogoControl(this);
}
}
}
@@ -32,11 +32,7 @@ namespace Lithnet.CredentialProvider
internal override ControlBase Clone()
{
var clone = new UserTileControl(this);
clone.Bitmap = this.Bitmap;
clone.BackgroundColor = this.BackgroundColor;
return clone;
return new UserTileControl(this);
}
}
}
@@ -3,7 +3,7 @@ using Lithnet.CredentialProvider.Interop;
namespace Lithnet.CredentialProvider
{
internal abstract partial class CredentialTile3 : ICredentialProviderCredential3
public abstract partial class CredentialTile3 : ICredentialProviderCredential3
{
int ICredentialProviderCredential3.GetBitmapBufferValue(uint dwFieldID, out uint pImageBufferSize, out IntPtr ppImageBuffer)
{
@@ -18,7 +18,7 @@ namespace Lithnet.CredentialProvider
if (this.Controls.TryGetControl<BitmapControl>(dwFieldID, FieldType.TileImage, out var instance))
{
var hbitmap = instance.GetBitmapBuffer(out pImageBufferSize);
ppImageBuffer = instance.GetBitmapBuffer(out pImageBufferSize);
return HRESULT.S_OK;
}
@@ -7,7 +7,7 @@ namespace Lithnet.CredentialProvider
/// Represents a user credential tile that implements the functionality of <see cref="CredentialTile"/> and <see cref="CredentialTile2"/>, but includes support for dynamically updating bitmap images.
/// </summary>
/// <remarks>This interface is public, but undocumented by Microsoft. It is recommended to use <see cref="CredentialTile2"/> tiles unless this specific functionality is needed</remarks>
internal abstract partial class CredentialTile3 : CredentialTile2
public abstract partial class CredentialTile3 : CredentialTile2
{
protected CredentialTile3(CredentialProviderBase credentialProvider) : this(credentialProvider, null) { }