Browse Source

Initial skia support code import from https://github.com/kekekeks/perspex-skia-temp

pull/310/head
Nikita Tsukanov 11 years ago
parent
commit
cb49e500e3
  1. 15
      Perspex.sln
  2. 92
      src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj
  3. 36
      src/Skia/Perspex.Skia.Desktop/Properties/AssemblyInfo.cs
  4. 62
      src/Skia/Perspex.Skia/BitmapImpl.cs
  5. 210
      src/Skia/Perspex.Skia/DrawingContextImpl.cs
  6. 135
      src/Skia/Perspex.Skia/FormattedTextImpl.cs
  7. 216
      src/Skia/Perspex.Skia/MethodTable.cs
  8. 113
      src/Skia/Perspex.Skia/NativeBrush.cs
  9. 10
      src/Skia/Perspex.Skia/NativeDrawingContextSettings.cs
  10. 23
      src/Skia/Perspex.Skia/NativeFormattedText.cs
  11. 28
      src/Skia/Perspex.Skia/Perspex.Skia.projitems
  12. 13
      src/Skia/Perspex.Skia/Perspex.Skia.shproj
  13. 47
      src/Skia/Perspex.Skia/PerspexHandleHolder.cs
  14. 69
      src/Skia/Perspex.Skia/PlatformRenderInterface.cs
  15. 30
      src/Skia/Perspex.Skia/RenderTarget.cs
  16. 26
      src/Skia/Perspex.Skia/SkRect.cs
  17. 13
      src/Skia/Perspex.Skia/SkiaPlatform.cs
  18. 29
      src/Skia/Perspex.Skia/SkiaPoint.cs
  19. 135
      src/Skia/Perspex.Skia/StreamGeometryImpl.cs
  20. 52
      src/Skia/Perspex.Skia/TypefaceCache.cs

15
Perspex.sln

@ -106,11 +106,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlTestApplicationPcl", "s
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "RenderHelpers", "src\Shared\RenderHelpers\RenderHelpers.shproj", "{3C4C0CB4-0C0F-4450-A37B-148C84FF905F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Skia", "Skia", "{3743B0F2-CC41-4F14-A8C8-267F579BF91E}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Perspex.Skia", "src\Skia\Perspex.Skia\Perspex.Skia.shproj", "{2F59F3D0-748D-4652-B01E-E0D954756308}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Skia.Desktop", "src\Skia\Perspex.Skia.Desktop\Perspex.Skia.Desktop.csproj", "{925DD807-B651-475F-9F7C-CBEB974CE43D}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{fb05ac90-89ba-4f2f-a924-f37875fb547c}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{e4d9629c-f168-4224-3f51-a5e482ffbc42}*SharedItemsImports = 13
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{db070a10-bf39-4752-8456-86e9d5928478}*SharedItemsImports = 4
src\Skia\Perspex.Skia\Perspex.Skia.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{925dd807-b651-475f-9f7c-cbeb974ce43d}*SharedItemsImports = 4
src\Shared\PlatformSupport\PlatformSupport.projitems*{54f237d5-a70a-4752-9656-0c70b1a7b047}*SharedItemsImports = 4
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
src\Shared\PlatformSupport\PlatformSupport.projitems*{811a76cf-1cf6-440f-963b-bbe31bd72a82}*SharedItemsImports = 4
@ -265,6 +274,10 @@ Global
{EA113F1A-D8D7-4142-9948-353270E7EBAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA113F1A-D8D7-4142-9948-353270E7EBAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA113F1A-D8D7-4142-9948-353270E7EBAE}.Release|Any CPU.Build.0 = Release|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{925DD807-B651-475F-9F7C-CBEB974CE43D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -295,5 +308,7 @@ Global
{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{EA113F1A-D8D7-4142-9948-353270E7EBAE} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{3C4C0CB4-0C0F-4450-A37B-148C84FF905F} = {A689DEF5-D50F-4975-8B72-124C9EB54066}
{2F59F3D0-748D-4652-B01E-E0D954756308} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
{925DD807-B651-475F-9F7C-CBEB974CE43D} = {3743B0F2-CC41-4F14-A8C8-267F579BF91E}
EndGlobalSection
EndGlobal

92
src/Skia/Perspex.Skia.Desktop/Perspex.Skia.Desktop.csproj

@ -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>

36
src/Skia/Perspex.Skia.Desktop/Properties/AssemblyInfo.cs

@ -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")]

62
src/Skia/Perspex.Skia/BitmapImpl.cs

@ -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)
{
}
}
}

210
src/Skia/Perspex.Skia/DrawingContextImpl.cs

@ -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);
}
}
}
}

135
src/Skia/Perspex.Skia/FormattedTextImpl.cs

@ -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;
}
}
}

216
src/Skia/Perspex.Skia/MethodTable.cs

@ -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()) { }
}
}

113
src/Skia/Perspex.Skia/NativeBrush.cs

@ -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();
}
}
}

10
src/Skia/Perspex.Skia/NativeDrawingContextSettings.cs

@ -0,0 +1,10 @@
using System.Runtime.InteropServices;
namespace Perspex.Skia
{
[StructLayout(LayoutKind.Sequential)]
struct NativeDrawingContextSettings
{
public double Opacity;
}
}

23
src/Skia/Perspex.Skia/NativeFormattedText.cs

@ -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;
};
}

28
src/Skia/Perspex.Skia/Perspex.Skia.projitems

@ -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>

13
src/Skia/Perspex.Skia/Perspex.Skia.shproj

@ -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>

47
src/Skia/Perspex.Skia/PerspexHandleHolder.cs

@ -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();
}
}
}

69
src/Skia/Perspex.Skia/PlatformRenderInterface.cs

@ -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));
}
}
}

30
src/Skia/Perspex.Skia/RenderTarget.cs

@ -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));
}
}
}

26
src/Skia/Perspex.Skia/SkRect.cs

@ -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);
}
}

13
src/Skia/Perspex.Skia/SkiaPlatform.cs

@ -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());
}
}

29
src/Skia/Perspex.Skia/SkiaPoint.cs

@ -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);
}
}

135
src/Skia/Perspex.Skia/StreamGeometryImpl.cs

@ -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
});
}
}
}
}

52
src/Skia/Perspex.Skia/TypefaceCache.cs

@ -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…
Cancel
Save