Skip to content

Commit

Permalink
Merge pull request #921 from b-editor/render-quality
Browse files Browse the repository at this point in the history
描画品質を改善
  • Loading branch information
yuto-trd authored Feb 9, 2024
2 parents 73b0a59 + ffb77ad commit 442ebf2
Show file tree
Hide file tree
Showing 12 changed files with 455 additions and 273 deletions.
66 changes: 33 additions & 33 deletions src/Beutl.Core/Reactive/DisposableExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,67 +9,67 @@ public static T DisposeWith<T>(this T disposable, ICollection<IDisposable> list)
return disposable;
}

public static void DisposeAll<T1, T2>(in this ValueTuple<T1, T2> tuple)
public static void DisposeAll<T1, T2>(in this ValueTuple<T1?, T2?> tuple)
where T1 : IDisposable
where T2 : IDisposable
{
tuple.Item1.Dispose();
tuple.Item2.Dispose();
tuple.Item1?.Dispose();
tuple.Item2?.Dispose();
}

public static void DisposeAll<T1, T2, T3>(in this ValueTuple<T1, T2, T3> tuple)
public static void DisposeAll<T1, T2, T3>(in this ValueTuple<T1?, T2?, T3?> tuple)
where T1 : IDisposable
where T2 : IDisposable
where T3 : IDisposable
{
tuple.Item1.Dispose();
tuple.Item2.Dispose();
tuple.Item3.Dispose();
tuple.Item1?.Dispose();
tuple.Item2?.Dispose();
tuple.Item3?.Dispose();
}

public static void DisposeAll<T1, T2, T3, T4>(in this ValueTuple<T1, T2, T3, T4> tuple)
public static void DisposeAll<T1, T2, T3, T4>(in this ValueTuple<T1?, T2?, T3?, T4?> tuple)
where T1 : IDisposable
where T2 : IDisposable
where T3 : IDisposable
where T4 : IDisposable
{
tuple.Item1.Dispose();
tuple.Item2.Dispose();
tuple.Item3.Dispose();
tuple.Item4.Dispose();
tuple.Item1?.Dispose();
tuple.Item2?.Dispose();
tuple.Item3?.Dispose();
tuple.Item4?.Dispose();
}

public static void DisposeAll<T1, T2, T3, T4, T5>(in this ValueTuple<T1, T2, T3, T4, T5> tuple)
public static void DisposeAll<T1, T2, T3, T4, T5>(in this ValueTuple<T1?, T2?, T3?, T4?, T5?> tuple)
where T1 : IDisposable
where T2 : IDisposable
where T3 : IDisposable
where T4 : IDisposable
where T5 : IDisposable
{
tuple.Item1.Dispose();
tuple.Item2.Dispose();
tuple.Item3.Dispose();
tuple.Item4.Dispose();
tuple.Item5.Dispose();
tuple.Item1?.Dispose();
tuple.Item2?.Dispose();
tuple.Item3?.Dispose();
tuple.Item4?.Dispose();
tuple.Item5?.Dispose();
}

public static void DisposeAll<T1, T2, T3, T4, T5, T6>(in this ValueTuple<T1, T2, T3, T4, T5, T6> tuple)
public static void DisposeAll<T1, T2, T3, T4, T5, T6>(in this ValueTuple<T1?, T2?, T3?, T4?, T5?, T6?> tuple)
where T1 : IDisposable
where T2 : IDisposable
where T3 : IDisposable
where T4 : IDisposable
where T5 : IDisposable
where T6 : IDisposable
{
tuple.Item1.Dispose();
tuple.Item2.Dispose();
tuple.Item3.Dispose();
tuple.Item4.Dispose();
tuple.Item5.Dispose();
tuple.Item6.Dispose();
tuple.Item1?.Dispose();
tuple.Item2?.Dispose();
tuple.Item3?.Dispose();
tuple.Item4?.Dispose();
tuple.Item5?.Dispose();
tuple.Item6?.Dispose();
}

public static void DisposeAll<T1, T2, T3, T4, T5, T6, T7>(in this ValueTuple<T1, T2, T3, T4, T5, T6, T7> tuple)
public static void DisposeAll<T1, T2, T3, T4, T5, T6, T7>(in this ValueTuple<T1?, T2?, T3?, T4?, T5?, T6?, T7?> tuple)
where T1 : IDisposable
where T2 : IDisposable
where T3 : IDisposable
Expand All @@ -78,12 +78,12 @@ public static void DisposeAll<T1, T2, T3, T4, T5, T6, T7>(in this ValueTuple<T1,
where T6 : IDisposable
where T7 : IDisposable
{
tuple.Item1.Dispose();
tuple.Item2.Dispose();
tuple.Item3.Dispose();
tuple.Item4.Dispose();
tuple.Item5.Dispose();
tuple.Item6.Dispose();
tuple.Item7.Dispose();
tuple.Item1?.Dispose();
tuple.Item2?.Dispose();
tuple.Item3?.Dispose();
tuple.Item4?.Dispose();
tuple.Item5?.Dispose();
tuple.Item6?.Dispose();
tuple.Item7?.Dispose();
}
}
3 changes: 3 additions & 0 deletions src/Beutl.Engine/Graphics/BrushConstructor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public void ConfigurePaint(SKPaint paint)
float opacity = (Brush?.Opacity ?? 0) / 100f;
paint.IsAntialias = true;
paint.BlendMode = (SKBlendMode)BlendMode;
paint.HintingLevel = SKPaintHinting.Full;
paint.LcdRenderText = true;
paint.SubpixelText = true;

paint.Color = new SKColor(255, 255, 255, (byte)(255 * opacity));

Expand Down
133 changes: 33 additions & 100 deletions src/Beutl.Engine/Graphics/ImmediateCanvas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,97 +328,30 @@ public void DrawRectangle(Rect rect, IBrush? fill, IPen? pen)
}
}

private static SKPath CreateSKPathFromText(ReadOnlySpan<ushort> glyphs, ReadOnlySpan<SKPoint> positions, SKFont font)
private void DrawTextStroke(FormattedText text, IPen pen)
{
var path = new SKPath();
ConfigureStrokePaint(new(text.Bounds.Size), pen!);
_sharedStrokePaint.IsStroke = false;
SKPath filled = text.GetFillPath()!;
SKPath stroke = text.GetStrokePath()!;

for (int i = 0; i < glyphs.Length; i++)
{
ushort glyph = glyphs[i];
SKPoint point = positions[i];

using SKPath glyphPath = font.GetGlyphPath(glyph);
path.AddPath(glyphPath, point.X, point.Y);
}

return path;
}

private void DrawTextStroke(FormattedText text, IPen pen, SKTextBlob textBlob,
ReadOnlySpan<ushort> glyphs, ReadOnlySpan<SKPoint> positions, SKFont font)
{
ConfigureStrokePaint(new(text.Bounds), pen!);
if (pen.StrokeAlignment == StrokeAlignment.Center)
{
Canvas.DrawText(textBlob, 0, 0, _sharedStrokePaint);
}
else
{
using SKPath path = CreateSKPathFromText(glyphs, positions, font);

switch (pen!.StrokeAlignment)
{
case StrokeAlignment.Inside:
Canvas.Save();
Canvas.ClipPath(path, SKClipOperation.Intersect, true);
Canvas.DrawText(textBlob, 0, 0, _sharedStrokePaint);
Canvas.Restore();
break;

case StrokeAlignment.Outside:
Canvas.Save();
Canvas.ClipPath(path, SKClipOperation.Difference, true);
Canvas.DrawText(textBlob, 0, 0, _sharedStrokePaint);
Canvas.Restore();
break;
}
}
Canvas.DrawPath(stroke, _sharedStrokePaint);
}

public void DrawText(FormattedText text, IBrush? fill, IPen? pen)
{
VerifyAccess();

var typeface = new Typeface(text.Font, text.Style, text.Weight);
SKTypeface sktypeface = typeface.ToSkia();
_sharedFillPaint.Reset();
_sharedFillPaint.TextSize = text.Size;
_sharedFillPaint.Typeface = sktypeface;

using var shaper = new SKShaper(sktypeface);
using var buffer = new HarfBuzzSharp.Buffer();
buffer.AddUtf16(text.Text.AsSpan());
buffer.GuessSegmentProperties();

SKShaper.Result result = shaper.Shape(buffer, _sharedFillPaint);

// create the text blob
using var builder = new SKTextBlobBuilder();
using SKFont font = _sharedFillPaint.ToFont();
SKPositionedRunBuffer run = builder.AllocatePositionedRun(font, result.Codepoints.Length);

// copy the glyphs
Span<ushort> glyphs = run.GetGlyphSpan();
Span<SKPoint> positions = run.GetPositionSpan();
for (int i = 0; i < result.Codepoints.Length; i++)
{
glyphs[i] = (ushort)result.Codepoints[i];
SKPoint point = result.Points[i];
point.X += i * text.Spacing;
positions[i] = point;
}

// build
using SKTextBlob textBlob = builder.Build();
SKPath filled = text.GetFillPath()!;

// draw filled
ConfigureFillPaint(text.Bounds, fill);
Canvas.DrawText(textBlob, 0, 0, _sharedFillPaint);
ConfigureFillPaint(text.Bounds.Size, fill);
Canvas.DrawPath(filled, _sharedFillPaint);

// draw stroke
if (pen != null && pen.Thickness != 0)
if (pen != null && pen.Thickness > 0)
{
DrawTextStroke(text, pen, textBlob, glyphs, positions, font);
DrawTextStroke(text, pen);
}
}

Expand All @@ -432,37 +365,33 @@ internal void DrawSKPath(SKPath skPath, bool strokeOnly, IBrush? fill, IPen? pen
Canvas.DrawPath(skPath, _sharedFillPaint);
}

if (pen != null && pen.Thickness != 0)
if (pen != null && pen.Thickness > 0)
{
ConfigureStrokePaint(rect, pen);
switch (pen.StrokeAlignment)
{
case StrokeAlignment.Center:
Canvas.DrawPath(skPath, _sharedStrokePaint);
break;

case StrokeAlignment.Inside:
Canvas.Save();
Canvas.ClipPath(skPath, SKClipOperation.Intersect, true);
Canvas.DrawPath(skPath, _sharedStrokePaint);
Canvas.Restore();
break;

case StrokeAlignment.Outside:
Canvas.Save();
Canvas.ClipPath(skPath, SKClipOperation.Difference, true);
Canvas.DrawPath(skPath, _sharedStrokePaint);
Canvas.Restore();
break;
}
using SKPath strokePath = PenHelper.CreateStrokePath(skPath, pen, rect);
_sharedStrokePaint.IsStroke = false;
Canvas.DrawPath(strokePath, _sharedStrokePaint);
}
}

public void DrawGeometry(Geometry geometry, IBrush? fill, IPen? pen)
{
VerifyAccess();
SKPath skPath = geometry.GetNativeObject();
DrawSKPath(skPath, false, fill, pen);

Rect rect = skPath.Bounds.ToGraphicsRect();

ConfigureFillPaint(rect.Size, fill);
Canvas.DrawPath(skPath, _sharedFillPaint);

if (pen != null && pen.Thickness > 0)
{
ConfigureStrokePaint(rect, pen);
_sharedStrokePaint.IsStroke = false;
SKPath? stroke = geometry.GetStrokePath(pen);
Canvas.DrawPath(stroke, _sharedStrokePaint);
}
}

public unsafe Bitmap<Bgra8888> GetBitmap()
Expand Down Expand Up @@ -621,6 +550,7 @@ private void ConfigureStrokePaint(Rect rect, IPen? pen)
if (pen != null && pen.Thickness != 0)
{
float thickness = pen.Thickness;

switch (pen.StrokeAlignment)
{
case StrokeAlignment.Center:
Expand All @@ -629,10 +559,13 @@ private void ConfigureStrokePaint(Rect rect, IPen? pen)

case StrokeAlignment.Outside:
rect = rect.Inflate(thickness);
goto case StrokeAlignment.Inside;
thickness *= 2;
break;

case StrokeAlignment.Inside:
thickness *= 2;
float maxAspect = Math.Max(rect.Width, rect.Height);
thickness = Math.Min(thickness, maxAspect);
break;

default:
Expand Down
Loading

0 comments on commit 442ebf2

Please sign in to comment.