Browse Source

[WIN32_CORE] Use flat GDI+ API for icon loading

pull/896/head
Nikita Tsukanov 9 years ago
parent
commit
dd25cffdcc
  1. 2
      src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj
  2. 197
      src/Windows/Avalonia.Win32.NetStandard/ComStreamWrapper.cs
  3. 128
      src/Windows/Avalonia.Win32.NetStandard/Gdip.cs
  4. 30
      src/Windows/Avalonia.Win32.NetStandard/IconImpl.cs
  5. 16
      src/Windows/Avalonia.Win32.NetStandard/NativeWin32Platform.cs

2
src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj

@ -75,6 +75,8 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="ComStreamWrapper.cs" />
<Compile Include="Gdip.cs" />
<Compile Include="IconImpl.cs" />
<Compile Include="NativeWin32Platform.cs" />
<Compile Include="Win32Exception.cs" />

197
src/Windows/Avalonia.Win32.NetStandard/ComStreamWrapper.cs

@ -0,0 +1,197 @@
//
// System.Drawing.ComIStreamWrapper.cs
//
// Author:
// Kornél Pál <http://www.kornelpal.hu/>
//
// Copyright (C) 2005-2008 Kornél Pál
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG;
namespace Avalonia.Win32
{
// Stream to IStream wrapper for COM interop
internal sealed class ComIStreamWrapper : IStream
{
private const int STG_E_INVALIDFUNCTION = unchecked((int)0x80030001);
private readonly Stream baseStream;
private long position = -1;
internal ComIStreamWrapper(Stream stream)
{
baseStream = stream;
}
private void SetSizeToPosition()
{
if (position != -1)
{
if (position > baseStream.Length)
baseStream.SetLength(position);
baseStream.Position = position;
position = -1;
}
}
public void Read(byte[] pv, int cb, IntPtr pcbRead)
{
int read = 0;
if (cb != 0)
{
SetSizeToPosition();
read = baseStream.Read(pv, 0, cb);
}
if (pcbRead != IntPtr.Zero)
Marshal.WriteInt32(pcbRead, read);
}
public void Write(byte[] pv, int cb, IntPtr pcbWritten)
{
if (cb != 0)
{
SetSizeToPosition();
baseStream.Write(pv, 0, cb);
}
if (pcbWritten != IntPtr.Zero)
Marshal.WriteInt32(pcbWritten, cb);
}
public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
{
long length = baseStream.Length;
long newPosition;
switch ((SeekOrigin)dwOrigin)
{
case SeekOrigin.Begin:
newPosition = dlibMove;
break;
case SeekOrigin.Current:
if (position == -1)
newPosition = baseStream.Position + dlibMove;
else
newPosition = position + dlibMove;
break;
case SeekOrigin.End:
newPosition = length + dlibMove;
break;
default:
throw new COMException(null, STG_E_INVALIDFUNCTION);
}
if (newPosition > length)
position = newPosition;
else
{
baseStream.Position = newPosition;
position = -1;
}
if (plibNewPosition != IntPtr.Zero)
Marshal.WriteInt64(plibNewPosition, newPosition);
}
public void SetSize(long libNewSize)
{
baseStream.SetLength(libNewSize);
}
public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
{
byte[] buffer;
long written = 0;
int read;
int count;
if (cb != 0)
{
if (cb < 4096)
count = (int)cb;
else
count = 4096;
buffer = new byte[count];
SetSizeToPosition();
while (true)
{
if ((read = baseStream.Read(buffer, 0, count)) == 0)
break;
pstm.Write(buffer, read, IntPtr.Zero);
written += read;
if (written >= cb)
break;
if (cb - written < 4096)
count = (int)(cb - written);
}
}
if (pcbRead != IntPtr.Zero)
Marshal.WriteInt64(pcbRead, written);
if (pcbWritten != IntPtr.Zero)
Marshal.WriteInt64(pcbWritten, written);
}
public void Commit(int grfCommitFlags)
{
baseStream.Flush();
SetSizeToPosition();
}
public void Revert()
{
throw new COMException(null, STG_E_INVALIDFUNCTION);
}
public void LockRegion(long libOffset, long cb, int dwLockType)
{
throw new COMException(null, STG_E_INVALIDFUNCTION);
}
public void UnlockRegion(long libOffset, long cb, int dwLockType)
{
throw new COMException(null, STG_E_INVALIDFUNCTION);
}
public void Stat(out STATSTG pstatstg, int grfStatFlag)
{
pstatstg = new STATSTG();
pstatstg.cbSize = baseStream.Length;
}
public void Clone(out IStream ppstm)
{
ppstm = null;
throw new COMException(null, STG_E_INVALIDFUNCTION);
}
}
}

128
src/Windows/Avalonia.Win32.NetStandard/Gdip.cs

@ -0,0 +1,128 @@
//
// Code copy-pasted from from Mono / System.Drawing.*.cs
// Original license below:
//
// Authors:
// Alexandre Pigolkine (pigolkine@gmx.de)
// Jordi Mas (jordi@ximian.com)
// Sanjay Gupta (gsanjay@novell.com)
// Ravindra (rkumar@novell.com)
// Peter Dennis Bartok (pbartok@novell.com)
// Sebastien Pouliot <sebastien@ximian.com>
//
//
// Copyright (C) 2004, 2007 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Win32
{
static class Gdip
{
public enum Status
{
Ok = 0,
GenericError = 1,
InvalidParameter = 2,
OutOfMemory = 3,
ObjectBusy = 4,
InsufficientBuffer = 5,
NotImplemented = 6,
Win32Error = 7,
WrongState = 8,
Aborted = 9,
FileNotFound = 10,
ValueOverflow = 11,
AccessDenied = 12,
UnknownImageFormat = 13,
FontFamilyNotFound = 14,
FontStyleNotFound = 15,
NotTrueTypeFont = 16,
UnsupportedGdiplusVersion = 17,
GdiplusNotInitialized = 18,
PropertyNotFound = 19,
PropertyNotSupported = 20,
ProfileNotFound = 21
}
[StructLayout(LayoutKind.Sequential)]
internal struct GdiplusStartupInput
{
// internalted to silent compiler
internal uint GdiplusVersion;
internal IntPtr DebugEventCallback;
internal int SuppressBackgroundThread;
internal int SuppressExternalCodecs;
internal static GdiplusStartupInput MakeGdiplusStartupInput()
{
GdiplusStartupInput result = new GdiplusStartupInput();
result.GdiplusVersion = 1;
result.DebugEventCallback = IntPtr.Zero;
result.SuppressBackgroundThread = 0;
result.SuppressExternalCodecs = 0;
return result;
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct GdiplusStartupOutput
{
internal IntPtr NotificationHook;
internal IntPtr NotificationUnhook;
internal static GdiplusStartupOutput MakeGdiplusStartupOutput()
{
GdiplusStartupOutput result = new GdiplusStartupOutput();
result.NotificationHook = result.NotificationUnhook = IntPtr.Zero;
return result;
}
}
[DllImport("gdiplus.dll")]
public static extern Status GdiplusStartup(ref ulong token, ref GdiplusStartupInput input, ref GdiplusStartupOutput output);
[DllImport("gdiplus.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]
public static extern Status GdipLoadImageFromStream([MarshalAs(UnmanagedType.Interface, MarshalTypeRef = typeof(IStream))] IStream stream, out IntPtr image);
[DllImport("gdiplus.dll")]
public static extern Status GdipCreateHICONFromBitmap(IntPtr bmp, out IntPtr HandleIcon);
[DllImport("gdiplus.dll")]
internal static extern Status GdipDisposeImage(IntPtr image);
static Gdip()
{
ulong token = 0;
var input = GdiplusStartupInput.MakeGdiplusStartupInput();
var output = GdiplusStartupOutput.MakeGdiplusStartupOutput();
GdiplusStartup(ref token, ref input, ref output);
}
}
}

30
src/Windows/Avalonia.Win32.NetStandard/IconImpl.cs

@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Platform;
@ -10,10 +12,34 @@ namespace Avalonia.Win32
{
public class IconImpl : IWindowIconImpl
{
public IntPtr HIcon { get; set; }
private readonly MemoryStream _ms;
public IconImpl(Stream data)
{
_ms = new MemoryStream();
data.CopyTo(_ms);
_ms.Seek(0, SeekOrigin.Begin);
IntPtr bitmap;
var status = Gdip.GdipLoadImageFromStream(new ComIStreamWrapper(_ms), out bitmap);
if (status != Gdip.Status.Ok)
throw new Exception("Unable to load icon, gdip status: " + (int) status);
IntPtr icon;
status = Gdip.GdipCreateHICONFromBitmap(bitmap, out icon);
if (status != Gdip.Status.Ok)
throw new Exception("Unable to create HICON, gdip status: " + (int)status);
Gdip.GdipDisposeImage(bitmap);
HIcon = icon;
}
public IntPtr HIcon { get;}
public void Save(Stream outputStream)
{
throw new NotImplementedException();
lock (_ms)
{
_ms.Seek(0, SeekOrigin.Begin);
_ms.CopyTo(outputStream);
}
}
}
}

16
src/Windows/Avalonia.Win32.NetStandard/NativeWin32Platform.cs

@ -11,10 +11,20 @@ namespace Avalonia.Win32
partial class Win32Platform
{
//TODO: An actual implementation
public IWindowIconImpl LoadIcon(string fileName) => new IconImpl();
public IWindowIconImpl LoadIcon(string fileName)
{
//No file IO for netstandard, still waiting for proper net core tooling
throw new NotSupportedException();
}
public IWindowIconImpl LoadIcon(Stream stream) => new IconImpl();
public IWindowIconImpl LoadIcon(Stream stream) => new IconImpl(stream);
public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) => new IconImpl();
public IWindowIconImpl LoadIcon(IBitmapImpl bitmap)
{
var ms = new MemoryStream();
bitmap.Save(ms);
ms.Seek(0, SeekOrigin.Begin);
return new IconImpl(ms);
}
}
}

Loading…
Cancel
Save