20 changed files with 1354 additions and 0 deletions
@ -0,0 +1,92 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|||
<PropertyGroup> |
|||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
|||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
|||
<ProjectGuid>{925DD807-B651-475F-9F7C-CBEB974CE43D}</ProjectGuid> |
|||
<OutputType>Library</OutputType> |
|||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|||
<RootNamespace>Perspex.Skia.Desktop</RootNamespace> |
|||
<AssemblyName>Perspex.Skia.Desktop</AssemblyName> |
|||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> |
|||
<FileAlignment>512</FileAlignment> |
|||
<TargetFrameworkProfile /> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|||
<DebugSymbols>true</DebugSymbols> |
|||
<DebugType>full</DebugType> |
|||
<Optimize>false</Optimize> |
|||
<OutputPath>bin\Debug\</OutputPath> |
|||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|||
<DebugType>pdbonly</DebugType> |
|||
<Optimize>true</Optimize> |
|||
<OutputPath>bin\Release\</OutputPath> |
|||
<DefineConstants>TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<Reference Include="System" /> |
|||
<Reference Include="System.Core" /> |
|||
<Reference Include="System.Xml.Linq" /> |
|||
<Reference Include="System.Data.DataSetExtensions" /> |
|||
<Reference Include="Microsoft.CSharp" /> |
|||
<Reference Include="System.Data" /> |
|||
<Reference Include="System.Net.Http" /> |
|||
<Reference Include="System.Xml" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\Perspex.Animation\Perspex.Animation.csproj"> |
|||
<Project>{D211E587-D8BC-45B9-95A4-F297C8FA5200}</Project> |
|||
<Name>Perspex.Animation</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\Perspex.Base\Perspex.Base.csproj"> |
|||
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project> |
|||
<Name>Perspex.Base</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\Perspex.Controls\Perspex.Controls.csproj"> |
|||
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project> |
|||
<Name>Perspex.Controls</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\Perspex.Input\Perspex.Input.csproj"> |
|||
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project> |
|||
<Name>Perspex.Input</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\Perspex.Interactivity\Perspex.Interactivity.csproj"> |
|||
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project> |
|||
<Name>Perspex.Interactivity</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\Perspex.Layout\Perspex.Layout.csproj"> |
|||
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project> |
|||
<Name>Perspex.Layout</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\Perspex.SceneGraph\Perspex.SceneGraph.csproj"> |
|||
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project> |
|||
<Name>Perspex.SceneGraph</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\Perspex.Styling\Perspex.Styling.csproj"> |
|||
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project> |
|||
<Name>Perspex.Styling</Name> |
|||
</ProjectReference> |
|||
</ItemGroup> |
|||
<Import Project="..\Perspex.Skia\Perspex.Skia.projitems" Label="Shared" /> |
|||
<Import Project="..\..\Shared\RenderHelpers\RenderHelpers.projitems" Label="Shared" /> |
|||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
|||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
|||
Other similar extension points exist, see Microsoft.Common.targets. |
|||
<Target Name="BeforeBuild"> |
|||
</Target> |
|||
<Target Name="AfterBuild"> |
|||
</Target> |
|||
--> |
|||
</Project> |
|||
@ -0,0 +1,36 @@ |
|||
using System.Reflection; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
// General Information about an assembly is controlled through the following
|
|||
// set of attributes. Change these attribute values to modify the information
|
|||
// associated with an assembly.
|
|||
[assembly: AssemblyTitle("Perspex.Skia.Desktop")] |
|||
[assembly: AssemblyDescription("")] |
|||
[assembly: AssemblyConfiguration("")] |
|||
[assembly: AssemblyCompany("")] |
|||
[assembly: AssemblyProduct("Perspex.Skia.Desktop")] |
|||
[assembly: AssemblyCopyright("Copyright © 2015")] |
|||
[assembly: AssemblyTrademark("")] |
|||
[assembly: AssemblyCulture("")] |
|||
|
|||
// Setting ComVisible to false makes the types in this assembly not visible
|
|||
// to COM components. If you need to access a type in this assembly from
|
|||
// COM, set the ComVisible attribute to true on that type.
|
|||
[assembly: ComVisible(false)] |
|||
|
|||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|||
[assembly: Guid("925dd807-b651-475f-9f7c-cbeb974ce43d")] |
|||
|
|||
// Version information for an assembly consists of the following four values:
|
|||
//
|
|||
// Major Version
|
|||
// Minor Version
|
|||
// Build Number
|
|||
// Revision
|
|||
//
|
|||
// You can specify all the values or you can default the Build and Revision Numbers
|
|||
// by using the '*' as shown below:
|
|||
// [assembly: AssemblyVersion("1.0.*")]
|
|||
[assembly: AssemblyVersion("1.0.0.0")] |
|||
[assembly: AssemblyFileVersion("1.0.0.0")] |
|||
@ -0,0 +1,62 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
using Perspex.Media; |
|||
using Perspex.Platform; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
class BitmapImpl : PerspexHandleHolder, IRenderTargetBitmapImpl |
|||
{ |
|||
private int width; |
|||
private int height; |
|||
|
|||
public void Save(string fileName) |
|||
{ |
|||
var ext = Path.GetExtension(fileName)?.ToLower(); |
|||
var type = MethodTable.SkiaImageType.Png; |
|||
if(ext=="gif") |
|||
type = MethodTable.SkiaImageType.Gif; |
|||
if(ext=="jpeg" || ext =="jpg") |
|||
type = MethodTable.SkiaImageType.Jpeg; |
|||
var skdata = MethodTable.Instance.SaveImage(Handle, type, 100); |
|||
var size = MethodTable.Instance.GetSkDataSize(skdata); |
|||
var buffer = new byte[size]; |
|||
MethodTable.Instance.ReadSkData(skdata, buffer, size); |
|||
File.WriteAllBytes(fileName, buffer); |
|||
} |
|||
|
|||
public int PixelWidth { get; private set; } |
|||
public int PixelHeight { get; private set; } |
|||
|
|||
protected override void Delete(IntPtr handle) |
|||
{ |
|||
MethodTable.Instance.DisposeImage(handle); |
|||
} |
|||
|
|||
public DrawingContext CreateDrawingContext() |
|||
{ |
|||
return |
|||
new DrawingContext( |
|||
new DrawingContextImpl(MethodTable.Instance.RenderTargetCreateRenderingContext(Handle))); |
|||
} |
|||
|
|||
public void Resize(int width, int height) |
|||
{ |
|||
MethodTable.Instance.ResizeBitmap(Handle, PixelWidth = Math.Max(1, width), PixelHeight = Math.Max(1, height)); |
|||
} |
|||
|
|||
public BitmapImpl(IntPtr handle, int width, int height) : base(handle) |
|||
{ |
|||
PixelHeight = height; |
|||
PixelWidth = width; |
|||
} |
|||
|
|||
public BitmapImpl(int width, int height) |
|||
: this(MethodTable.Instance.CreateRenderTargetBitmap(width, height), width, height) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,210 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using Perspex.Media; |
|||
using Perspex.Media.Imaging; |
|||
using Perspex.RenderHelpers; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
unsafe class DrawingContextImpl : PerspexHandleHolder, IDrawingContextImpl |
|||
{ |
|||
private readonly NativeDrawingContextSettings* _settings; |
|||
public DrawingContextImpl(IntPtr handle) : base(handle) |
|||
{ |
|||
_settings = MethodTable.Instance.GetDrawingContextSettingsPtr(handle); |
|||
_settings->Opacity = 1; |
|||
} |
|||
|
|||
protected override void Delete(IntPtr handle) => MethodTable.Instance.DisposeRenderingContext(handle); |
|||
|
|||
public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect) |
|||
{ |
|||
var impl = (BitmapImpl) source.PlatformImpl; |
|||
var s = SkRect.FromRect(sourceRect); |
|||
var d = SkRect.FromRect(destRect); |
|||
MethodTable.Instance.DrawImage(Handle, impl.Handle, (float) opacity, ref s, ref d); |
|||
} |
|||
|
|||
public void DrawLine(Pen pen, Point p1, Point p2) |
|||
{ |
|||
|
|||
using (var brush = CreateBrush(pen, new Size(Math.Abs(p2.X - p1.X), Math.Abs(p2.Y - p1.Y)))) |
|||
MethodTable.Instance.DrawLine(Handle, brush.Brush, |
|||
(float) p1.X, (float) p1.Y, (float) p2.X, (float) p2.Y); |
|||
} |
|||
|
|||
static readonly NativeBrushContainer _dummy = new NativeBrushContainer(null); |
|||
public void DrawGeometry(Brush brush, Pen pen, Geometry geometry) |
|||
{ |
|||
var impl = ((StreamGeometryImpl) geometry.PlatformImpl); |
|||
var oldTransform = Transform; |
|||
if (!impl.Transform.IsIdentity) |
|||
Transform = impl.Transform*Transform; |
|||
|
|||
var size = geometry.Bounds.Size; |
|||
using(var fill = brush!=null?CreateBrush(brush, size):null) |
|||
using (var stroke = pen?.Brush != null ? CreateBrush(pen, size) : null) |
|||
{ |
|||
MethodTable.Instance.DrawGeometry(Handle, impl.Path.Handle, fill != null ? fill.Brush : null, |
|||
stroke != null ? stroke.Brush : null); |
|||
} |
|||
Transform = oldTransform; |
|||
} |
|||
|
|||
unsafe NativeBrushContainer CreateBrush(Brush brush, Size targetSize) |
|||
{ |
|||
var rv = NativeBrushPool.Instance.Get(); |
|||
rv.Brush->Opacity = brush.Opacity; |
|||
|
|||
|
|||
var solid = brush as SolidColorBrush; |
|||
if (solid != null) |
|||
{ |
|||
rv.Brush->Type = NativeBrushType.Solid; |
|||
rv.Brush->Color = solid.Color.ToUint32(); |
|||
return rv; |
|||
} |
|||
var gradient = brush as GradientBrush; |
|||
if (gradient != null) |
|||
{ |
|||
if (gradient.GradientStops.Count > NativeBrush.MaxGradientStops) |
|||
throw new NotSupportedException("Maximum supported gradient stop count is " + |
|||
NativeBrush.MaxGradientStops); |
|||
rv.Brush->GradientSpreadMethod = gradient.SpreadMethod; |
|||
rv.Brush->GradientStopCount = gradient.GradientStops.Count; |
|||
|
|||
for (var c = 0; c < gradient.GradientStops.Count; c++) |
|||
{ |
|||
var st = gradient.GradientStops[c]; |
|||
rv.Brush->GradientStops[c] = (float) st.Offset; |
|||
rv.Brush->GradientStopColors[c] = st.Color.ToUint32(); |
|||
} |
|||
|
|||
} |
|||
|
|||
var linearGradient = brush as LinearGradientBrush; |
|||
if (linearGradient != null) |
|||
{ |
|||
rv.Brush->Type = NativeBrushType.LinearGradient; |
|||
rv.Brush->GradientStartPoint = linearGradient.StartPoint.ToPixels(targetSize); |
|||
rv.Brush->GradientEndPoint = linearGradient.EndPoint.ToPixels(targetSize); |
|||
} |
|||
var radialGradient = brush as RadialGradientBrush; |
|||
if (radialGradient != null) |
|||
{ |
|||
rv.Brush->Type = NativeBrushType.RadialGradient; |
|||
rv.Brush->GradientStartPoint = radialGradient.Center.ToPixels(targetSize); |
|||
rv.Brush->GradientRadius = (float)radialGradient.Radius; |
|||
} |
|||
var tileBrush = brush as TileBrush; |
|||
if (tileBrush != null) |
|||
{ |
|||
rv.Brush->Type = NativeBrushType.Image; |
|||
var helper = new TileBrushImplHelper(tileBrush, targetSize); |
|||
var bitmap = new BitmapImpl((int) helper.IntermediateSize.Width, (int) helper.IntermediateSize.Height); |
|||
rv.AddDisposable(bitmap); |
|||
using (var ctx = bitmap.CreateDrawingContext()) |
|||
helper.DrawIntermediate(ctx); |
|||
rv.Brush->Bitmap = bitmap.Handle; |
|||
rv.Brush->BitmapTileMode = tileBrush.TileMode; |
|||
rv.Brush->BitmapTranslation = new SkiaPoint(-helper.DestinationRect.X, -helper.DestinationRect.Y); |
|||
|
|||
|
|||
} |
|||
|
|||
return rv; |
|||
} |
|||
|
|||
NativeBrushContainer CreateBrush(Pen pen, Size targetSize) |
|||
{ |
|||
var brush = CreateBrush(pen.Brush, targetSize); |
|||
brush.Brush->Stroke = true; |
|||
brush.Brush->StrokeThickness = (float)pen.Thickness; |
|||
brush.Brush->StrokeLineCap = pen.StartLineCap; |
|||
brush.Brush->StrokeMiterLimit = (float)pen.MiterLimit; |
|||
|
|||
if (pen.DashStyle != null) |
|||
{ |
|||
var dashes = pen.DashStyle.Dashes; |
|||
if (dashes.Count > NativeBrush.MaxDashCount) |
|||
throw new NotSupportedException("Maximum supported dash count is " + NativeBrush.MaxDashCount); |
|||
brush.Brush->StrokeDashCount = dashes.Count; |
|||
for (int c = 0; c < dashes.Count; c++) |
|||
brush.Brush->StrokeDashes[c] = (float) dashes[c]; |
|||
brush.Brush->StrokeDashOffset = (float)pen.DashStyle.Offset; |
|||
|
|||
} |
|||
|
|||
|
|||
return brush; |
|||
} |
|||
|
|||
public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0) |
|||
{ |
|||
using (var brush = CreateBrush(pen, rect.Size)) |
|||
{ |
|||
var rc = SkRect.FromRect(rect); |
|||
MethodTable.Instance.DrawRectangle(Handle, brush.Brush, ref rc, cornerRadius); |
|||
} |
|||
} |
|||
|
|||
public void FillRectangle(Brush pbrush, Rect rect, float cornerRadius = 0) |
|||
{ |
|||
using (var brush = CreateBrush(pbrush, rect.Size)) |
|||
{ |
|||
var rc = SkRect.FromRect(rect); |
|||
MethodTable.Instance.DrawRectangle(Handle, brush.Brush, ref rc, cornerRadius); |
|||
} |
|||
} |
|||
|
|||
public void DrawText(Brush foreground, Point origin, FormattedText text) |
|||
{ |
|||
using (var br = CreateBrush(foreground, text.Measure())) |
|||
MethodTable.Instance.DrawFormattedText(Handle, br.Brush, ((FormattedTextImpl) text.PlatformImpl).Handle, |
|||
(float) origin.X, (float) origin.Y); |
|||
} |
|||
|
|||
|
|||
public void PushClip(Rect clip) |
|||
{ |
|||
var rc = SkRect.FromRect(clip); |
|||
//MethodTable.Instance.PushClip(Handle, ref rc);
|
|||
} |
|||
|
|||
public void PopClip() |
|||
{ |
|||
//MethodTable.Instance.PopClip(Handle);
|
|||
} |
|||
|
|||
private readonly Stack<double> _opacityStack = new Stack<double>(); |
|||
|
|||
public void PushOpacity(double opacity) |
|||
{ |
|||
_opacityStack.Push(_settings->Opacity); |
|||
_settings->Opacity *= opacity; |
|||
} |
|||
|
|||
public void PopOpacity() => _settings->Opacity = _opacityStack.Pop(); |
|||
|
|||
private Matrix _currentTransform = Matrix.Identity; |
|||
private float[] _fmatrix = new float[6]; |
|||
public Matrix Transform |
|||
{ |
|||
get { return _currentTransform; } |
|||
set |
|||
{ |
|||
if(_currentTransform == value) |
|||
return; |
|||
_currentTransform = value; |
|||
_fmatrix[0] = (float)value.M11; |
|||
_fmatrix[1] = (float)value.M21; |
|||
_fmatrix[2] = (float)value.M31; |
|||
|
|||
_fmatrix[3] = (float)value.M12; |
|||
_fmatrix[4] = (float)value.M22; |
|||
_fmatrix[5] = (float)value.M32; |
|||
MethodTable.Instance.SetTransform(Handle, _fmatrix); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,135 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
using Perspex.Media; |
|||
using Perspex.Platform; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
unsafe class FormattedTextImpl : PerspexHandleHolder, IFormattedTextImpl |
|||
{ |
|||
private readonly NativeFormattedText* _shared; |
|||
private readonly string _text; |
|||
|
|||
public static FormattedTextImpl Create(string text, string fontFamilyName, double fontSize, FontStyle fontStyle, |
|||
TextAlignment textAlignment, FontWeight fontWeight) |
|||
{ |
|||
NativeFormattedText* pShared; |
|||
fixed (void* ptext = text) |
|||
{ |
|||
IntPtr handle = MethodTable.Instance.CreateFormattedText(ptext, text.Length, |
|||
TypefaceCache.GetTypeface(fontFamilyName, fontStyle, fontWeight), |
|||
(float) fontSize, textAlignment, &pShared); |
|||
return new FormattedTextImpl(handle, pShared, text); |
|||
} |
|||
} |
|||
|
|||
List<FormattedTextLine> _lines = new List<FormattedTextLine>(); |
|||
List<Rect> _rects = new List<Rect>(); |
|||
Size _size; |
|||
|
|||
public IEnumerable<FormattedTextLine> GetLines() |
|||
{ |
|||
return _lines; |
|||
} |
|||
|
|||
public TextHitTestResult HitTestPoint(Point point) |
|||
{ |
|||
for (int c = 0; c < _rects.Count; c++) |
|||
{ |
|||
//TODO: Detect line first
|
|||
var rc = _rects[c]; |
|||
if (rc.Contains(point)) |
|||
{ |
|||
return new TextHitTestResult |
|||
{ |
|||
IsInside = true, |
|||
TextPosition = c, |
|||
IsTrailing = (point.X - rc.X) > rc.Width/2 |
|||
}; |
|||
|
|||
} |
|||
} |
|||
return new TextHitTestResult(); |
|||
} |
|||
|
|||
public Rect HitTestTextPosition(int index) |
|||
{ |
|||
return _rects[index]; |
|||
} |
|||
|
|||
public IEnumerable<Rect> HitTestTextRange(int index, int length) |
|||
{ |
|||
for (var c = 0; c < length; c++) |
|||
yield return _rects[c + index]; |
|||
} |
|||
|
|||
public Size Measure() |
|||
{ |
|||
return _size; |
|||
} |
|||
|
|||
public void SetForegroundBrush(Brush brush, int startIndex, int length) |
|||
{ |
|||
} |
|||
|
|||
|
|||
void Reload() |
|||
{ |
|||
_lines.Clear(); |
|||
_rects.Clear(); |
|||
float maxX = 0; |
|||
for (var c = 0; c < _shared->LineCount; c++) |
|||
{ |
|||
var w = _shared->Lines[c].Width; |
|||
if (maxX < w) |
|||
maxX = w; |
|||
_lines.Add(new FormattedTextLine(_shared->Lines[c].Length, _shared->Lines[c].Height)); |
|||
} |
|||
for (var c = 0; c < _text.Length; c++) |
|||
_rects.Add(_shared->Bounds[c].ToRect()); |
|||
var lastLine = _shared->Lines[_shared->LineCount - 1]; |
|||
_size = new Size(maxX, lastLine.Top + lastLine.Height); |
|||
} |
|||
|
|||
void Rebuild() |
|||
{ |
|||
MethodTable.Instance.RebuildFormattedText(Handle); |
|||
Reload(); |
|||
} |
|||
|
|||
Size _constraint = new Size(double.PositiveInfinity, double.PositiveInfinity); |
|||
|
|||
public Size Constraint |
|||
{ |
|||
get { return _constraint; } |
|||
set |
|||
{ |
|||
_constraint = value; |
|||
|
|||
_shared->WidthConstraint = (_constraint.Width != double.PositiveInfinity) |
|||
? (float) _constraint.Width |
|||
: -1; |
|||
Rebuild(); |
|||
} |
|||
} |
|||
|
|||
protected override void Delete(IntPtr handle) |
|||
{ |
|||
MethodTable.Instance.DestroyFormattedText(handle); |
|||
} |
|||
|
|||
public FormattedTextImpl(IntPtr handle, NativeFormattedText* shared, string text) : base(handle) |
|||
{ |
|||
_shared = shared; |
|||
_text = text; |
|||
Reload(); |
|||
} |
|||
|
|||
public override string ToString() |
|||
{ |
|||
return _text; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,216 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using Perspex.Media; |
|||
|
|||
// ReSharper disable InconsistentNaming
|
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
internal unsafe abstract class MethodTable |
|||
{ |
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _CreateRenderTarget(IntPtr nativeHandle, int width, int height); |
|||
|
|||
public _CreateRenderTarget CreateRenderTarget; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _RenderTargetCreateRenderingContext(IntPtr target); |
|||
|
|||
public _RenderTargetCreateRenderingContext RenderTargetCreateRenderingContext; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _RenderTargetResize(IntPtr target, int width, int height); |
|||
|
|||
public _RenderTargetResize RenderTargetResize; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DisposeRenderTarget(IntPtr target); |
|||
|
|||
public _DisposeRenderTarget DisposeRenderTarget; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DisposeRenderingContext(IntPtr ctx); |
|||
|
|||
public _DisposeRenderingContext DisposeRenderingContext; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DrawRectangle(IntPtr ctx, void* brush, ref SkRect rect, float borderRadius); |
|||
|
|||
public _DrawRectangle DrawRectangle; |
|||
|
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _PushClip(IntPtr ctx, ref SkRect rect); |
|||
|
|||
public _PushClip PushClip; |
|||
|
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _PopClip(IntPtr ctx); |
|||
|
|||
public _PopClip PopClip; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _SetTransform(IntPtr ctx, float[] matrix6); |
|||
|
|||
public _SetTransform SetTransform; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DrawLine(IntPtr ctx, void* brush, float x1, float y1, float x2, float y2); |
|||
|
|||
public _DrawLine DrawLine; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _CreatePath(SkiaGeometryElement[] elements, int count, out SkRect bounds); |
|||
|
|||
public _CreatePath CreatePath; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DisposePath(IntPtr handle); |
|||
|
|||
public _DisposePath DisposePath; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DrawGeometry(IntPtr ctx, IntPtr path, void* fill, void* stroke); |
|||
|
|||
public _DrawGeometry DrawGeometry; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DestroySkData(IntPtr handle); |
|||
|
|||
public _DestroySkData DestroySkData; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate bool _LoadImage(byte[] data, int len, out IntPtr image, out int width, out int height); |
|||
|
|||
public _LoadImage LoadImage; |
|||
|
|||
public enum SkiaImageType |
|||
{ |
|||
Png,Gif,Jpeg |
|||
} |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _SaveImage(IntPtr image, SkiaImageType type, int quality); |
|||
|
|||
public _SaveImage SaveImage; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DrawImage(IntPtr ctx, IntPtr image, float opacity, ref SkRect srcRect, ref SkRect destRect); |
|||
|
|||
public _DrawImage DrawImage; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _CreateRenderTargetBitmap(int width, int height); |
|||
|
|||
public _CreateRenderTargetBitmap CreateRenderTargetBitmap; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _ResizeBitmap(IntPtr image, int width, int height); |
|||
|
|||
public _ResizeBitmap ResizeBitmap; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DisposeImage(IntPtr image); |
|||
|
|||
public _DisposeImage DisposeImage; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate int _GetSkDataSize(IntPtr data); |
|||
|
|||
public _GetSkDataSize GetSkDataSize; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _ReadSkData(IntPtr data, byte[] buffer, int count); |
|||
|
|||
public _ReadSkData ReadSkData; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate NativeDrawingContextSettings* _GetDrawingContextSettingsPtr(IntPtr ctx); |
|||
|
|||
public _GetDrawingContextSettingsPtr GetDrawingContextSettingsPtr; |
|||
|
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _CreateTypeface(void* name, int style); |
|||
|
|||
public _CreateTypeface CreateTypeface; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate IntPtr _CreateFormattedText(void*utf16, int len, IntPtr typeface, float fontSize, TextAlignment align, NativeFormattedText** shared); |
|||
|
|||
public _CreateFormattedText CreateFormattedText; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _RebuildFormattedText(IntPtr handle); |
|||
|
|||
public _RebuildFormattedText RebuildFormattedText; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DestroyFormattedText(IntPtr handle); |
|||
|
|||
public _DestroyFormattedText DestroyFormattedText; |
|||
|
|||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] |
|||
public delegate void _DrawFormattedText(IntPtr ctx, void* brush, IntPtr text, float x, float y); |
|||
|
|||
public _DrawFormattedText DrawFormattedText; |
|||
|
|||
private static readonly Type[] TableOrder = new Type[] |
|||
{ |
|||
typeof (_CreateRenderTarget), |
|||
typeof (_DisposeRenderTarget), |
|||
typeof (_RenderTargetResize), |
|||
typeof (_RenderTargetCreateRenderingContext), |
|||
typeof (_DisposeRenderingContext), |
|||
typeof (_DrawRectangle), |
|||
typeof (_PushClip), |
|||
typeof (_PopClip), |
|||
typeof (_SetTransform), |
|||
typeof (_DrawLine), |
|||
typeof (_CreatePath), |
|||
typeof (_DisposePath), |
|||
typeof (_DrawGeometry), |
|||
typeof (_GetSkDataSize), |
|||
typeof (_ReadSkData), |
|||
typeof (_DestroySkData), |
|||
typeof (_LoadImage), |
|||
typeof (_SaveImage), |
|||
typeof (_DrawImage), |
|||
typeof (_CreateRenderTargetBitmap), |
|||
typeof (_ResizeBitmap), |
|||
typeof (_DisposeImage), |
|||
typeof (_GetDrawingContextSettingsPtr), |
|||
typeof (_CreateTypeface), |
|||
typeof (_CreateFormattedText), |
|||
typeof (_RebuildFormattedText), |
|||
typeof (_DestroyFormattedText), |
|||
typeof (_DrawFormattedText) |
|||
}; |
|||
|
|||
|
|||
|
|||
protected MethodTable(IntPtr methodTable) |
|||
{ |
|||
var dic = typeof (MethodTable).GetFields().ToDictionary(f => f.FieldType, f => f); |
|||
|
|||
for (var c = 0; c < TableOrder.Length; c++) |
|||
{ |
|||
IntPtr pMethod = Marshal.ReadIntPtr(methodTable, IntPtr.Size*c); |
|||
var t = TableOrder[c]; |
|||
dic[t].SetValue(this, Marshal.GetDelegateForFunctionPointer(pMethod, t)); |
|||
} |
|||
} |
|||
|
|||
public static readonly MethodTable Instance = new Win32MethodTable(); |
|||
} |
|||
|
|||
class Win32MethodTable : MethodTable |
|||
{ |
|||
[DllImport(@"libperspesk.dll")] |
|||
private static extern IntPtr GetPerspexMethodTable(); |
|||
|
|||
public Win32MethodTable() : base(GetPerspexMethodTable()) { } |
|||
} |
|||
} |
|||
@ -0,0 +1,113 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
using Perspex.Media; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
internal enum NativeBrushType |
|||
{ |
|||
Solid, |
|||
LinearGradient, |
|||
RadialGradient, |
|||
Image |
|||
} |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
internal unsafe struct NativeBrush |
|||
{ |
|||
public const int MaxGradientStops = 1024; |
|||
public const int MaxDashCount = 1024; |
|||
public NativeBrushType Type; |
|||
public double Opacity; |
|||
public uint Color; |
|||
|
|||
//Strokes
|
|||
public bool Stroke; |
|||
public float StrokeThickness; |
|||
public PenLineJoin StrokeLineJoin; |
|||
public float StrokeMiterLimit; |
|||
public int StrokeDashCount; |
|||
public float StrokeDashOffset; |
|||
public PenLineCap StrokeLineCap; |
|||
|
|||
|
|||
//Gradients
|
|||
public int GradientStopCount; |
|||
public GradientSpreadMethod GradientSpreadMethod; |
|||
public SkiaPoint GradientStartPoint, GradientEndPoint; |
|||
public float GradientRadius; |
|||
|
|||
//Image Brush
|
|||
public IntPtr Bitmap; |
|||
public TileMode BitmapTileMode; |
|||
public SkiaPoint BitmapTranslation; |
|||
|
|||
//Blobs
|
|||
public fixed uint GradientStopColors [MaxGradientStops]; |
|||
public fixed float GradientStops [MaxGradientStops]; |
|||
public fixed float StrokeDashes [MaxDashCount]; |
|||
|
|||
public void Reset() |
|||
{ |
|||
Type = NativeBrushType.Solid; |
|||
Opacity = 1f; |
|||
Color = 0; |
|||
Stroke = false; |
|||
StrokeThickness = 1; |
|||
GradientStopCount = 0; |
|||
StrokeDashCount = 0; |
|||
StrokeLineCap = PenLineCap.Flat; |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
unsafe class NativeBrushContainer : IDisposable |
|||
{ |
|||
private readonly NativeBrushPool _pool; |
|||
public NativeBrush* Brush; |
|||
|
|||
List<IDisposable> _disposables = new List<IDisposable>(); |
|||
|
|||
public NativeBrushContainer(NativeBrushPool pool) |
|||
{ |
|||
_pool = pool; |
|||
Brush = (NativeBrush*) Marshal.AllocHGlobal(Marshal.SizeOf(typeof (NativeBrush))).ToPointer(); |
|||
Brush->Reset(); |
|||
} |
|||
|
|||
public void AddDisposable(IDisposable disp) |
|||
{ |
|||
_disposables.Add(disp); |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
foreach (var disp in _disposables) |
|||
disp.Dispose(); |
|||
_disposables.Clear(); |
|||
Brush->Reset(); |
|||
_pool?.Return(this); |
|||
} |
|||
} |
|||
|
|||
class NativeBrushPool |
|||
{ |
|||
public static NativeBrushPool Instance { get; } = new NativeBrushPool(); |
|||
Stack<NativeBrushContainer> _pool = new Stack<NativeBrushContainer>(); |
|||
|
|||
public void Return(NativeBrushContainer c) |
|||
{ |
|||
_pool.Push(c); |
|||
} |
|||
|
|||
public NativeBrushContainer Get() |
|||
{ |
|||
if (_pool.Count == 0) |
|||
return new NativeBrushContainer(this); |
|||
return _pool.Pop(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
[StructLayout(LayoutKind.Sequential)] |
|||
struct NativeDrawingContextSettings |
|||
{ |
|||
public double Opacity; |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
[StructLayout(LayoutKind.Sequential)] |
|||
unsafe struct NativeFormattedText |
|||
{ |
|||
public float WidthConstraint; |
|||
public int LineCount; |
|||
public NativeFormattedTextLine* Lines; |
|||
public SkRect* Bounds; |
|||
}; |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
unsafe struct NativeFormattedTextLine |
|||
{ |
|||
public float Top; |
|||
public int Start; |
|||
public int Length; |
|||
public float Height; |
|||
public float Width; |
|||
}; |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup> |
|||
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects> |
|||
<HasSharedItems>true</HasSharedItems> |
|||
<SharedGUID>2f59f3d0-748d-4652-b01e-e0d954756308</SharedGUID> |
|||
</PropertyGroup> |
|||
<PropertyGroup Label="Configuration"> |
|||
<Import_RootNamespace>Perspex.Skia</Import_RootNamespace> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<Compile Include="$(MSBuildThisFileDirectory)BitmapImpl.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)DrawingContextImpl.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)FormattedTextImpl.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)MethodTable.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)NativeBrush.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)NativeDrawingContextSettings.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)NativeFormattedText.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)PerspexHandleHolder.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)PlatformRenderInterface.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)RenderTarget.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)SkiaPlatform.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)SkiaPoint.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)SkRect.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)StreamGeometryImpl.cs" /> |
|||
<Compile Include="$(MSBuildThisFileDirectory)TypefaceCache.cs" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,13 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<PropertyGroup Label="Globals"> |
|||
<ProjectGuid>2f59f3d0-748d-4652-b01e-e0d954756308</ProjectGuid> |
|||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion> |
|||
</PropertyGroup> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" /> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" /> |
|||
<PropertyGroup /> |
|||
<Import Project="Perspex.Skia.projitems" Label="Shared" /> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" /> |
|||
</Project> |
|||
@ -0,0 +1,47 @@ |
|||
using System; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
abstract class PerspexHandleHolder : IDisposable |
|||
{ |
|||
private IntPtr _handle; |
|||
|
|||
public IntPtr Handle |
|||
{ |
|||
get |
|||
{ |
|||
CheckDisposed(); |
|||
return _handle; |
|||
} |
|||
} |
|||
|
|||
public bool IsDisposed { get; private set; } |
|||
|
|||
public void CheckDisposed() |
|||
{ |
|||
if (IsDisposed) |
|||
throw new ObjectDisposedException(GetType().FullName); |
|||
} |
|||
|
|||
protected PerspexHandleHolder(IntPtr handle) |
|||
{ |
|||
_handle = handle; |
|||
} |
|||
|
|||
protected abstract void Delete(IntPtr handle); |
|||
|
|||
public void Dispose() |
|||
{ |
|||
if(IsDisposed) |
|||
return; |
|||
IsDisposed = true; |
|||
Delete(_handle); |
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
~PerspexHandleHolder() |
|||
{ |
|||
Dispose(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Text; |
|||
using System.Threading.Tasks; |
|||
using Perspex.Media; |
|||
using Perspex.Platform; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
public class PlatformRenderInterface : IPlatformRenderInterface |
|||
{ |
|||
public IBitmapImpl CreateBitmap(int width, int height) |
|||
{ |
|||
return CreateRenderTargetBitmap(width, height); |
|||
} |
|||
|
|||
public IFormattedTextImpl CreateFormattedText(string text, string fontFamilyName, double fontSize, FontStyle fontStyle, |
|||
TextAlignment textAlignment, FontWeight fontWeight) |
|||
{ |
|||
return FormattedTextImpl.Create(text, fontFamilyName, fontSize, fontStyle, textAlignment, fontWeight); |
|||
} |
|||
|
|||
public IStreamGeometryImpl CreateStreamGeometry() |
|||
{ |
|||
return new StreamGeometryImpl(); |
|||
} |
|||
|
|||
IBitmapImpl LoadBitmap(byte[] data) |
|||
{ |
|||
IntPtr ptr; |
|||
int width; |
|||
int height; |
|||
if (!MethodTable.Instance.LoadImage(data, data.Length, out ptr, out width, out height)) |
|||
throw new ArgumentException("Unable to load bitmap from provided data"); |
|||
return new BitmapImpl(ptr, width, height); |
|||
} |
|||
|
|||
public IBitmapImpl LoadBitmap(System.IO.Stream stream) |
|||
{ |
|||
using (var sr = new BinaryReader(stream)) |
|||
{ |
|||
return LoadBitmap(sr.ReadBytes((int)stream.Length)); |
|||
} |
|||
} |
|||
|
|||
public IBitmapImpl LoadBitmap(string fileName) |
|||
{ |
|||
return LoadBitmap(File.ReadAllBytes(fileName)); |
|||
} |
|||
|
|||
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height) |
|||
{ |
|||
if (width < 1) |
|||
throw new ArgumentException("Width can't be less than 1", nameof(width)); |
|||
if (height < 1) |
|||
throw new ArgumentException("Height can't be less than 1", nameof(height)); |
|||
return new BitmapImpl(width, height); |
|||
} |
|||
|
|||
public IRenderTarget CreateRenderer(IPlatformHandle handle, double width, double height) |
|||
{ |
|||
width = Math.Min(1, width); |
|||
height = Math.Min(1, height); |
|||
return new RenderTarget(MethodTable.Instance.CreateRenderTarget(handle.Handle, (int) width, (int) height)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
using Perspex.Media; |
|||
using Perspex.Platform; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
class RenderTarget : PerspexHandleHolder, IRenderTarget |
|||
{ |
|||
public RenderTarget(IntPtr handle) : base(handle) |
|||
{ |
|||
} |
|||
|
|||
protected override void Delete(IntPtr handle) => MethodTable.Instance.DisposeRenderTarget(handle); |
|||
|
|||
public DrawingContext CreateDrawingContext() |
|||
{ |
|||
return |
|||
new DrawingContext( |
|||
new DrawingContextImpl(MethodTable.Instance.RenderTargetCreateRenderingContext(Handle))); |
|||
} |
|||
|
|||
public void Resize(int width, int height) |
|||
{ |
|||
MethodTable.Instance.RenderTargetResize(Handle,Math.Max(width, 1), Math.Max(height, 1)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
[StructLayout(LayoutKind.Sequential)] |
|||
struct SkRect |
|||
{ |
|||
public float Left, Top, Right, Bottom; |
|||
|
|||
public static SkRect FromRect(Rect rc) |
|||
{ |
|||
return new SkRect() |
|||
{ |
|||
Left = (float) rc.X, |
|||
Top = (float) rc.Y, |
|||
Right = (float) rc.Right, |
|||
Bottom = (float) rc.Bottom |
|||
}; |
|||
} |
|||
|
|||
public Rect ToRect() => new Rect(Left, Top, Right - Left, Bottom - Top); |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using Perspex.Platform; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
public static class SkiaPlatform |
|||
{ |
|||
public static void Initialize() |
|||
=> PerspexLocator.CurrentMutable.Bind<IPlatformRenderInterface>().ToConstant(new PlatformRenderInterface()); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
[StructLayout(LayoutKind.Sequential)] |
|||
struct SkiaPoint |
|||
{ |
|||
public float X, Y; |
|||
|
|||
public SkiaPoint(float x, float y) |
|||
{ |
|||
X = x; |
|||
Y = y; |
|||
} |
|||
|
|||
public SkiaPoint(double x, double y) |
|||
{ |
|||
X = (float)x; |
|||
Y = (float)y; |
|||
} |
|||
|
|||
public SkiaPoint(Point p) : this(p.X, p.Y) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public static implicit operator SkiaPoint(Point pt) => new SkiaPoint(pt); |
|||
} |
|||
} |
|||
@ -0,0 +1,135 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using System.Text; |
|||
using Perspex.Media; |
|||
using Perspex.Platform; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
enum SkiaGeometryElementType |
|||
{ |
|||
LineTo, |
|||
ArcTo, |
|||
BezierTo, |
|||
BeginFigure, |
|||
EndFigure |
|||
}; |
|||
|
|||
[StructLayout(LayoutKind.Sequential)] |
|||
struct SkiaGeometryElement |
|||
{ |
|||
public SkiaGeometryElementType Type; |
|||
public SkiaPoint Point1, Point2, Point3; |
|||
public bool Flag; |
|||
} |
|||
|
|||
class SkPath : PerspexHandleHolder |
|||
{ |
|||
public SkPath(IntPtr handle) : base(handle) |
|||
{ |
|||
} |
|||
|
|||
protected override void Delete(IntPtr handle) |
|||
{ |
|||
MethodTable.Instance.DisposePath(handle); |
|||
} |
|||
} |
|||
|
|||
|
|||
class StreamGeometryImpl : IStreamGeometryImpl |
|||
{ |
|||
public SkPath Path; |
|||
|
|||
public Rect GetRenderBounds(double strokeThickness) |
|||
{ |
|||
// TODO: Calculate properly.
|
|||
return Bounds.Inflate(strokeThickness); |
|||
} |
|||
|
|||
public Rect Bounds { get; private set; } |
|||
|
|||
public Matrix Transform { get; set; } = Matrix.Identity; |
|||
|
|||
public IStreamGeometryImpl Clone() |
|||
{ |
|||
return new StreamGeometryImpl() {Path = Path, Transform = Transform, Bounds = Bounds}; |
|||
} |
|||
|
|||
public IStreamGeometryContextImpl Open() |
|||
{ |
|||
return new StreamContext(this); |
|||
} |
|||
|
|||
class StreamContext : IStreamGeometryContextImpl |
|||
{ |
|||
private readonly StreamGeometryImpl _geometryImpl; |
|||
readonly List<SkiaGeometryElement> _elements = new List<SkiaGeometryElement>(); |
|||
|
|||
public StreamContext(StreamGeometryImpl geometryImpl) |
|||
{ |
|||
_geometryImpl = geometryImpl; |
|||
} |
|||
|
|||
public void Dispose() |
|||
{ |
|||
var arr = _elements.ToArray(); |
|||
SkRect rc; |
|||
_geometryImpl.Path = new SkPath(MethodTable.Instance.CreatePath(arr, arr.Length, out rc)); |
|||
_geometryImpl.Bounds = rc.ToRect(); |
|||
|
|||
} |
|||
|
|||
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection) |
|||
{ |
|||
//TODO: Implement me
|
|||
|
|||
} |
|||
|
|||
public void BeginFigure(Point startPoint, bool isFilled) |
|||
{ |
|||
_elements.Add(new SkiaGeometryElement |
|||
{ |
|||
Type = SkiaGeometryElementType.BeginFigure, |
|||
Point1 = new SkiaPoint(startPoint), |
|||
Flag = isFilled |
|||
}); |
|||
} |
|||
|
|||
public void BezierTo(Point point1, Point point2, Point point3) |
|||
{ |
|||
_elements.Add(new SkiaGeometryElement |
|||
{ |
|||
Type = SkiaGeometryElementType.BezierTo, |
|||
Point1 = new SkiaPoint(point1), |
|||
Point2 = new SkiaPoint(point2), |
|||
Point3 = new SkiaPoint(point3) |
|||
}); |
|||
} |
|||
|
|||
public void QuadTo(Point control, Point endPoint) |
|||
{ |
|||
//throw new NotImplementedException();
|
|||
} |
|||
|
|||
public void LineTo(Point point) |
|||
{ |
|||
_elements.Add(new SkiaGeometryElement |
|||
{ |
|||
Type = SkiaGeometryElementType.LineTo, |
|||
Point1 = new SkiaPoint(point) |
|||
}); |
|||
} |
|||
|
|||
public void EndFigure(bool isClosed) |
|||
{ |
|||
_elements.Add(new SkiaGeometryElement |
|||
{ |
|||
Type = SkiaGeometryElementType.EndFigure, |
|||
Flag = isClosed |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Text; |
|||
using Perspex.Media; |
|||
|
|||
namespace Perspex.Skia |
|||
{ |
|||
static class TypefaceCache |
|||
{ |
|||
static readonly Dictionary<string, Dictionary<Style, IntPtr>>Cache = new Dictionary<string, Dictionary<Style, IntPtr>>(); |
|||
unsafe static IntPtr GetTypeface(string name, Style style) |
|||
{ |
|||
if (name == null) |
|||
name = "Arial"; |
|||
Dictionary<Style, IntPtr> entry; |
|||
if (!Cache.TryGetValue(name, out entry)) |
|||
Cache[name] = entry = new Dictionary<Style, IntPtr>(); |
|||
IntPtr rv; |
|||
if (!entry.TryGetValue(style, out rv)) |
|||
{ |
|||
var bytes = Encoding.ASCII.GetBytes(name); |
|||
var buffer = new byte[bytes.Length + 1]; |
|||
bytes.CopyTo(buffer, 0); |
|||
fixed (void* pname = buffer) |
|||
{ |
|||
entry[style] = rv = MethodTable.Instance.CreateTypeface(pname, (int)style); |
|||
} |
|||
} |
|||
return rv; |
|||
} |
|||
|
|||
[Flags] |
|||
enum Style |
|||
{ |
|||
Normal = 0, |
|||
Bold = 0x01, |
|||
Italic = 0x02, |
|||
BoldItalic = 0x03 |
|||
}; |
|||
|
|||
public static IntPtr GetTypeface(string name, FontStyle style, FontWeight weight) |
|||
{ |
|||
Style sstyle = Style.Normal; |
|||
if (style != FontStyle.Normal) |
|||
sstyle |= Style.Italic; |
|||
if(weight>FontWeight.Normal) |
|||
sstyle |= Style.Bold; |
|||
return GetTypeface(name, sstyle); |
|||
} |
|||
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue