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