-
-
Save Danielku15/63a6a58cbe8cf0ac61d5b16e53715fd9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<OutputType>Exe</OutputType> | |
<TargetFramework>netcoreapp3.1</TargetFramework> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="HarfBuzzSharp" Version="2.6.1.4" /> | |
<PackageReference Include="SkiaSharp" Version="2.80.0-alpha.13" /> | |
</ItemGroup> | |
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Runtime.InteropServices; | |
using HarfBuzzSharp; | |
using SkiaSharp; | |
namespace SkiaCanvas | |
{ | |
public static class Program | |
{ | |
static Program() | |
{ | |
var libSkiaSharpPath = Path.GetDirectoryName(typeof(Program).Assembly.Location); | |
string runtime; | |
switch (Environment.OSVersion.Platform) | |
{ | |
case PlatformID.MacOSX: | |
libSkiaSharpPath = Path.Combine(libSkiaSharpPath, "runtimes", "osx", "native", | |
"libSkiaSharp.dylib"); | |
break; | |
case PlatformID.Unix: | |
runtime = IntPtr.Size == 4 ? "tizen-x86" : "tizen-x64"; | |
libSkiaSharpPath = Path.Combine(libSkiaSharpPath, "runtimes", runtime, "native", | |
"libSkiaSharp.so"); | |
break; | |
default: | |
runtime = IntPtr.Size == 4 ? "win-x86" : "win-x64"; | |
libSkiaSharpPath = Path.Combine(libSkiaSharpPath, "runtimes", runtime, "native", | |
"libSkiaSharp.dll"); | |
break; | |
} | |
Console.WriteLine($"Loading native lib from '{libSkiaSharpPath}'"); | |
var lib = NativeLibrary.Load(libSkiaSharpPath); | |
Console.WriteLine(lib == IntPtr.Zero | |
? $"Loading native lib from '{libSkiaSharpPath}' failed" | |
: $"Loading native lib from '{libSkiaSharpPath}' successful" | |
); | |
} | |
public static void Main() | |
{ | |
const float size = 12f; | |
const float height = size * 2; | |
const float width = 1300f; | |
const float x = 10; | |
using var colorSpace = SKColorSpace.CreateSrgb(); | |
using var surface = SKSurface.Create(new SKImageInfo((int)width, | |
(int)height, | |
SKColorType.Bgra8888, | |
SKAlphaType.Premul, | |
colorSpace), | |
new SKSurfaceProperties(SKSurfacePropsFlags.None, SKPixelGeometry.Unknown) | |
); | |
var paint = new SKPaint | |
{ | |
IsAntialias = true, | |
IsDither = false, | |
Style = SKPaintStyle.Fill, | |
Color = new SKColor(0, 0, 0, 255), | |
StrokeMiter = 4, | |
StrokeWidth = 0, | |
StrokeCap = SKStrokeCap.Butt, | |
BlendMode = SKBlendMode.SrcOver | |
}; | |
using var typeFace = SKTypeface.FromFamilyName("Arial", | |
SKFontStyleWeight.Bold, | |
SKFontStyleWidth.Normal, | |
SKFontStyleSlant.Upright | |
); | |
var msg = "rendered by myLibrary (https://myLibrary.domain)"; | |
DrawText(surface.Canvas, msg, x, 20.799999f, paint, typeFace, 12); | |
using var image = SKBitmap.FromImage(surface.Snapshot()); | |
using var fileStream = new SKFileWStream("output.png"); | |
image.Encode(fileStream, SKEncodedImageFormat.Png, 100); | |
} | |
private static void DrawText(SKCanvas surfaceCanvas, string msg, in float x, float y, SKPaint paint, SKTypeface typeface, int size) | |
{ | |
using var font = MakeHarfBuzzFont(typeface, size); | |
using var buffer = new HarfBuzzSharp.Buffer | |
{ | |
Direction = Direction.LeftToRight, | |
Language = Language.Default | |
}; | |
buffer.AddUtf8(msg); | |
font.Shape(buffer); | |
var infos = buffer.GlyphInfos; | |
var positions = buffer.GlyphPositions; | |
using var skFont = new SKFont | |
{ | |
Edging = SKFontEdging.Antialias, | |
Subpixel = true, | |
Hinting = SKFontHinting.Normal, | |
Typeface = typeface, | |
Size = size | |
}; | |
using var blobBuilder = new SKTextBlobBuilder(); | |
var runBuffer = blobBuilder.AllocatePositionedRun(skFont, infos.Length); | |
var bufferGlyphs = runBuffer.GetGlyphSpan(); | |
var bufferPositions = runBuffer.GetPositionSpan(); | |
var width = 0.0f; | |
for (var i = 0; i < infos.Length; i++) | |
{ | |
bufferGlyphs[i] = (ushort)infos[i].Codepoint; | |
var xOffset = width + HarfBuzzToSkiaFontSize * positions[i].XOffset; | |
var yOffset = HarfBuzzToSkiaFontSize * -positions[i].YOffset; | |
bufferPositions[i] = new SKPoint(xOffset, yOffset); | |
width += HarfBuzzToSkiaFontSize * positions[i].XAdvance; | |
} | |
var blob = blobBuilder.Build(); | |
surfaceCanvas.DrawText(blob, x, y, paint); | |
} | |
private const int SkiaToHarfBuzzFontSize = 1 << 16; | |
private const float HarfBuzzToSkiaFontSize = 1f / SkiaToHarfBuzzFontSize; | |
private static Font MakeHarfBuzzFont(SKTypeface typeface, int size) | |
{ | |
using var stream = typeface.OpenStream(out var ttcIndex); | |
var data = Marshal.AllocCoTaskMem(stream.Length); | |
stream.Read(data, stream.Length); | |
using var blob = new Blob(data, stream.Length, MemoryMode.ReadOnly, () => { Marshal.FreeCoTaskMem(data); }); | |
blob.MakeImmutable(); | |
using var face = new Face(blob, ttcIndex) | |
{ | |
Index = ttcIndex, | |
UnitsPerEm = typeface.UnitsPerEm | |
}; | |
var font = new Font(face); | |
var scale = size * SkiaToHarfBuzzFontSize; | |
font.SetScale(scale, scale); | |
font.SetFunctionsOpenType(); | |
return font; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment