csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
352 lines
13 KiB
352 lines
13 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Platform.Surfaces;
|
|
using Avalonia.Direct2D1.Media;
|
|
using Avalonia.Direct2D1.Media.Imaging;
|
|
using Avalonia.Media;
|
|
using Avalonia.Media.Imaging;
|
|
using Avalonia.Platform;
|
|
using SharpDX.DirectWrite;
|
|
using GlyphRun = Avalonia.Media.GlyphRun;
|
|
using SharpDX.Mathematics.Interop;
|
|
|
|
namespace Avalonia
|
|
{
|
|
public static class Direct2DApplicationExtensions
|
|
{
|
|
public static AppBuilder UseDirect2D1(this AppBuilder builder)
|
|
{
|
|
builder.UseRenderingSubsystem(Direct2D1.Direct2D1Platform.Initialize, "Direct2D1");
|
|
return builder;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace Avalonia.Direct2D1
|
|
{
|
|
public class Direct2D1Platform : IPlatformRenderInterface
|
|
{
|
|
private static readonly Direct2D1Platform s_instance = new Direct2D1Platform();
|
|
|
|
public static SharpDX.Direct3D11.Device Direct3D11Device { get; private set; }
|
|
|
|
public static SharpDX.Direct2D1.Factory1 Direct2D1Factory { get; private set; }
|
|
|
|
public static SharpDX.Direct2D1.Device Direct2D1Device { get; private set; }
|
|
|
|
public static SharpDX.DirectWrite.Factory1 DirectWriteFactory { get; private set; }
|
|
|
|
public static SharpDX.WIC.ImagingFactory ImagingFactory { get; private set; }
|
|
|
|
public static SharpDX.DXGI.Device1 DxgiDevice { get; private set; }
|
|
|
|
private static readonly object s_initLock = new object();
|
|
private static bool s_initialized = false;
|
|
|
|
internal static void InitializeDirect2D()
|
|
{
|
|
lock (s_initLock)
|
|
{
|
|
if (s_initialized)
|
|
{
|
|
return;
|
|
}
|
|
#if DEBUG
|
|
try
|
|
{
|
|
Direct2D1Factory = new SharpDX.Direct2D1.Factory1(
|
|
SharpDX.Direct2D1.FactoryType.MultiThreaded,
|
|
SharpDX.Direct2D1.DebugLevel.Error);
|
|
}
|
|
catch
|
|
{
|
|
//
|
|
}
|
|
#endif
|
|
if (Direct2D1Factory == null)
|
|
{
|
|
Direct2D1Factory = new SharpDX.Direct2D1.Factory1(
|
|
SharpDX.Direct2D1.FactoryType.MultiThreaded,
|
|
SharpDX.Direct2D1.DebugLevel.None);
|
|
}
|
|
|
|
using (var factory = new SharpDX.DirectWrite.Factory())
|
|
{
|
|
DirectWriteFactory = factory.QueryInterface<SharpDX.DirectWrite.Factory1>();
|
|
}
|
|
|
|
ImagingFactory = new SharpDX.WIC.ImagingFactory();
|
|
|
|
var featureLevels = new[]
|
|
{
|
|
SharpDX.Direct3D.FeatureLevel.Level_11_1,
|
|
SharpDX.Direct3D.FeatureLevel.Level_11_0,
|
|
SharpDX.Direct3D.FeatureLevel.Level_10_1,
|
|
SharpDX.Direct3D.FeatureLevel.Level_10_0,
|
|
SharpDX.Direct3D.FeatureLevel.Level_9_3,
|
|
SharpDX.Direct3D.FeatureLevel.Level_9_2,
|
|
SharpDX.Direct3D.FeatureLevel.Level_9_1,
|
|
};
|
|
|
|
Direct3D11Device = new SharpDX.Direct3D11.Device(
|
|
SharpDX.Direct3D.DriverType.Hardware,
|
|
SharpDX.Direct3D11.DeviceCreationFlags.BgraSupport | SharpDX.Direct3D11.DeviceCreationFlags.VideoSupport,
|
|
featureLevels);
|
|
|
|
DxgiDevice = Direct3D11Device.QueryInterface<SharpDX.DXGI.Device1>();
|
|
|
|
Direct2D1Device = new SharpDX.Direct2D1.Device(Direct2D1Factory, DxgiDevice);
|
|
|
|
s_initialized = true;
|
|
}
|
|
}
|
|
|
|
public static void Initialize()
|
|
{
|
|
InitializeDirect2D();
|
|
AvaloniaLocator.CurrentMutable
|
|
.Bind<IPlatformRenderInterface>().ToConstant(s_instance)
|
|
.Bind<IFontManagerImpl>().ToConstant(new FontManagerImpl())
|
|
.Bind<ITextShaperImpl>().ToConstant(new TextShaperImpl());
|
|
SharpDX.Configuration.EnableReleaseOnFinalizer = true;
|
|
}
|
|
|
|
private IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces)
|
|
{
|
|
foreach (var s in surfaces)
|
|
{
|
|
if (s is IPlatformHandle nativeWindow)
|
|
{
|
|
if (nativeWindow.HandleDescriptor != "HWND")
|
|
{
|
|
throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from " +
|
|
nativeWindow.HandleDescriptor);
|
|
}
|
|
|
|
return new HwndRenderTarget(nativeWindow);
|
|
}
|
|
if (s is IExternalDirect2DRenderTargetSurface external)
|
|
{
|
|
return new ExternalRenderTarget(external);
|
|
}
|
|
|
|
if (s is IFramebufferPlatformSurface fb)
|
|
{
|
|
return new FramebufferShimRenderTarget(fb);
|
|
}
|
|
}
|
|
throw new NotSupportedException("Don't know how to create a Direct2D1 renderer from any of provided surfaces");
|
|
}
|
|
|
|
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)
|
|
{
|
|
return new WicRenderTargetBitmapImpl(size, dpi);
|
|
}
|
|
|
|
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
|
|
{
|
|
return new WriteableWicBitmapImpl(size, dpi, format, alphaFormat);
|
|
}
|
|
|
|
public IGeometryImpl CreateEllipseGeometry(Rect rect) => new EllipseGeometryImpl(rect);
|
|
public IGeometryImpl CreateLineGeometry(Point p1, Point p2) => new LineGeometryImpl(p1, p2);
|
|
public IGeometryImpl CreateRectangleGeometry(Rect rect) => new RectangleGeometryImpl(rect);
|
|
public IStreamGeometryImpl CreateStreamGeometry() => new StreamGeometryImpl();
|
|
public IGeometryImpl CreateGeometryGroup(FillRule fillRule, IReadOnlyList<Geometry> children) => new GeometryGroupImpl(fillRule, children);
|
|
public IGeometryImpl CreateCombinedGeometry(GeometryCombineMode combineMode, Geometry g1, Geometry g2) => new CombinedGeometryImpl(combineMode, g1, g2);
|
|
|
|
public IGlyphRunImpl CreateGlyphRun(IGlyphTypeface glyphTypeface, double fontRenderingEmSize, IReadOnlyList<ushort> glyphIndices,
|
|
IReadOnlyList<double> glyphAdvances, IReadOnlyList<Vector> glyphOffsets)
|
|
{
|
|
var glyphTypefaceImpl = (GlyphTypefaceImpl)glyphTypeface;
|
|
|
|
var glyphCount = glyphIndices.Count;
|
|
|
|
var run = new SharpDX.DirectWrite.GlyphRun
|
|
{
|
|
FontFace = glyphTypefaceImpl.FontFace,
|
|
FontSize = (float)fontRenderingEmSize
|
|
};
|
|
|
|
var indices = new short[glyphCount];
|
|
|
|
for (var i = 0; i < glyphCount; i++)
|
|
{
|
|
indices[i] = (short)glyphIndices[i];
|
|
}
|
|
|
|
run.Indices = indices;
|
|
|
|
run.Advances = new float[glyphCount];
|
|
|
|
var scale = (float)(fontRenderingEmSize / glyphTypeface.Metrics.DesignEmHeight);
|
|
|
|
if (glyphAdvances == null)
|
|
{
|
|
for (var i = 0; i < glyphCount; i++)
|
|
{
|
|
var advance = glyphTypeface.GetGlyphAdvance(glyphIndices[i]) * scale;
|
|
|
|
run.Advances[i] = advance;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (var i = 0; i < glyphCount; i++)
|
|
{
|
|
var advance = (float)glyphAdvances[i];
|
|
|
|
run.Advances[i] = advance;
|
|
}
|
|
}
|
|
|
|
if (glyphOffsets == null)
|
|
{
|
|
return new GlyphRunImpl(run);
|
|
}
|
|
|
|
run.Offsets = new GlyphOffset[glyphCount];
|
|
|
|
for (var i = 0; i < glyphCount; i++)
|
|
{
|
|
var (x, y) = glyphOffsets[i];
|
|
|
|
run.Offsets[i] = new GlyphOffset
|
|
{
|
|
AdvanceOffset = (float)x,
|
|
AscenderOffset = (float)y
|
|
};
|
|
}
|
|
|
|
return new GlyphRunImpl(run);
|
|
}
|
|
|
|
class D2DApi : IPlatformRenderInterfaceContext
|
|
{
|
|
private readonly Direct2D1Platform _platform;
|
|
|
|
public D2DApi(Direct2D1Platform platform)
|
|
{
|
|
_platform = platform;
|
|
}
|
|
public object TryGetFeature(Type featureType) => null;
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
|
|
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) => _platform.CreateRenderTarget(surfaces);
|
|
}
|
|
|
|
public IPlatformRenderInterfaceContext CreateBackendContext(IPlatformGraphicsContext graphicsContext) =>
|
|
new D2DApi(this);
|
|
|
|
public IGeometryImpl BuildGlyphRunGeometry(GlyphRun glyphRun)
|
|
{
|
|
if (glyphRun.GlyphTypeface is not GlyphTypefaceImpl glyphTypeface)
|
|
{
|
|
throw new InvalidOperationException("PlatformImpl can't be null.");
|
|
}
|
|
|
|
var pathGeometry = new SharpDX.Direct2D1.PathGeometry(Direct2D1Factory);
|
|
|
|
using (var sink = pathGeometry.Open())
|
|
{
|
|
var glyphs = new short[glyphRun.GlyphIndices.Count];
|
|
|
|
for (int i = 0; i < glyphRun.GlyphIndices.Count; i++)
|
|
{
|
|
glyphs[i] = (short)glyphRun.GlyphIndices[i];
|
|
}
|
|
|
|
glyphTypeface.FontFace.GetGlyphRunOutline((float)glyphRun.FontRenderingEmSize, glyphs, null, null, false, !glyphRun.IsLeftToRight, sink);
|
|
|
|
sink.Close();
|
|
}
|
|
|
|
var (baselineOriginX, baselineOriginY) = glyphRun.BaselineOrigin;
|
|
|
|
var transformedGeometry = new SharpDX.Direct2D1.TransformedGeometry(
|
|
Direct2D1Factory,
|
|
pathGeometry,
|
|
new RawMatrix3x2(1.0f, 0.0f, 0.0f, 1.0f, (float)baselineOriginX, (float)baselineOriginY));
|
|
|
|
return new TransformedGeometryWrapper(transformedGeometry);
|
|
}
|
|
|
|
private class TransformedGeometryWrapper : GeometryImpl
|
|
{
|
|
public TransformedGeometryWrapper(SharpDX.Direct2D1.TransformedGeometry geometry) : base(geometry)
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IBitmapImpl LoadBitmap(string fileName)
|
|
{
|
|
return new WicBitmapImpl(fileName);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IBitmapImpl LoadBitmap(Stream stream)
|
|
{
|
|
return new WicBitmapImpl(stream);
|
|
}
|
|
|
|
public IWriteableBitmapImpl LoadWriteableBitmapToWidth(Stream stream, int width,
|
|
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
|
{
|
|
return new WriteableWicBitmapImpl(stream, width, true, interpolationMode);
|
|
}
|
|
|
|
public IWriteableBitmapImpl LoadWriteableBitmapToHeight(Stream stream, int height,
|
|
BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
|
{
|
|
return new WriteableWicBitmapImpl(stream, height, false, interpolationMode);
|
|
}
|
|
|
|
public IWriteableBitmapImpl LoadWriteableBitmap(string fileName)
|
|
{
|
|
return new WriteableWicBitmapImpl(fileName);
|
|
}
|
|
|
|
public IWriteableBitmapImpl LoadWriteableBitmap(Stream stream)
|
|
{
|
|
return new WriteableWicBitmapImpl(stream);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
|
{
|
|
return new WicBitmapImpl(stream, width, true, interpolationMode);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
|
{
|
|
return new WicBitmapImpl(stream, height, false, interpolationMode);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
|
|
{
|
|
// https://github.com/sharpdx/SharpDX/issues/959 blocks implementation.
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
|
|
{
|
|
return new WicBitmapImpl(format, alphaFormat, data, size, dpi, stride);
|
|
}
|
|
|
|
public bool SupportsIndividualRoundRects => false;
|
|
|
|
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
|
|
|
|
public PixelFormat DefaultPixelFormat => PixelFormat.Bgra8888;
|
|
}
|
|
}
|
|
|