Skip to content

Instantly share code, notes, and snippets.

@Danielku15
Last active June 3, 2020 17:50
Show Gist options
  • Save Danielku15/63a6a58cbe8cf0ac61d5b16e53715fd9 to your computer and use it in GitHub Desktop.
Save Danielku15/63a6a58cbe8cf0ac61d5b16e53715fd9 to your computer and use it in GitHub Desktop.
<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>
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