Skip to content

Commit

Permalink
text rendering OK
Browse files Browse the repository at this point in the history
  • Loading branch information
Yatao Li committed Oct 27, 2020
1 parent 38c9995 commit 0fe6853
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 112 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ desktop.ini
*.zip
native/**/build
.ccls-cache

.fake
12 changes: 7 additions & 5 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Release/netcoreapp3.0/fvim.dll",
"program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/FVim.dll",
"args": [],
"cwd": "${workspaceFolder}/bin/Release/netcoreapp3.0/",
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"console": "internalConsole",
"justMyCode": false
},
"console": "integratedTerminal",
"justMyCode": false,
"enableStepFiltering": false
}

]
}
12 changes: 11 additions & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,22 @@
"tasks": [
{
"label": "build",
"command": "dotnet build -c Release",
"type": "shell",
"command": "dotnet",
"args": [
"msbuild",
// Ask msbuild to generate full paths for file names.
"/property:GenerateFullPaths=true",
"/t:build",
// Do not generate summary otherwise it leads to duplicate errors in Problems panel
"/consoleloggerparameters:NoSummary"
],
"group": "build",
"presentation": {
// Reveal the output only if unrecognized errors occur.
"reveal": "silent"
},
// Use the standard MS compiler pattern to detect errors, warnings and infos
"problemMatcher": "$msCompile"
}
]
Expand Down
File renamed without changes.
28 changes: 11 additions & 17 deletions Views/Cursor.xaml.fs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ type Cursor() as this =
static let IsActiveProperty = AvaloniaProperty.Register<Cursor, bool>("IsActive")

let mutable cursor_timer: IDisposable = null
let mutable bgbrush: SolidColorBrush = SolidColorBrush(Colors.Black)
let mutable fgbrush: SolidColorBrush = SolidColorBrush(Colors.White)
let mutable spbrush: SolidColorBrush = SolidColorBrush(Colors.Red)
let mutable fg = Colors.White
let mutable bg = Colors.Black
let mutable sp = Colors.Red

let mutable cursor_fb = AllocateFramebuffer (20.0) (20.0) 1.0
let mutable cursor_fb_vm = CursorViewModel(Some -1)
let mutable cursor_fb_s = 1.0
Expand All @@ -49,10 +50,6 @@ type Cursor() as this =
true
else false

let fgpaint = SolidColorBrush()
let bgpaint = SolidColorBrush()
let sppaint = SolidColorBrush()

let _buffer_glyph = [| 0u |]

let cursorTimerRun action time =
Expand Down Expand Up @@ -83,12 +80,9 @@ type Cursor() as this =
then ()
else
(* update the settings *)
if this.ViewModel.fg <> fgbrush.Color then
fgbrush <- SolidColorBrush(this.ViewModel.fg)
if this.ViewModel.bg <> bgbrush.Color then
bgbrush <- SolidColorBrush(this.ViewModel.bg)
if this.ViewModel.sp <> spbrush.Color then
spbrush <- SolidColorBrush(this.ViewModel.sp)
fg <- this.ViewModel.fg
bg <- this.ViewModel.bg
sp <- this.ViewModel.sp
(* reconfigure the cursor *)
showCursor true
cursorTimerRun blinkon this.ViewModel.blinkwait
Expand Down Expand Up @@ -150,7 +144,7 @@ type Cursor() as this =
if this.IsActive then
_buffer_glyph.[0] <- this.ViewModel.text.Codepoint
let _buffer_glyph_span = ReadOnlySpan(_buffer_glyph)
RenderText(ctx, bounds, scale, fgpaint, bgpaint, sppaint, this.ViewModel.underline, this.ViewModel.undercurl, _buffer_glyph_span, typeface, this.ViewModel.fontSize)
RenderText(ctx, bounds, scale, fg, bg, sp, this.ViewModel.underline, this.ViewModel.undercurl, _buffer_glyph_span, typeface, this.ViewModel.fontSize)
else
let brush = SolidColorBrush(this.ViewModel.bg)
ctx.DrawRectangle(brush, Pen(brush), RoundedRect(bounds))
Expand All @@ -159,9 +153,9 @@ type Cursor() as this =
match ctx.PlatformImpl with
| :? ISkiaDrawingContextImpl ->
// immediate
SetOpacity fgpaint this.Opacity
SetOpacity bgpaint this.Opacity
SetOpacity sppaint this.Opacity
fg <- UpdateOpacity fg this.Opacity
bg <- UpdateOpacity bg this.Opacity
sp <- UpdateOpacity sp this.Opacity
render_block ctx.PlatformImpl
| _ ->
// deferred
Expand Down
6 changes: 1 addition & 5 deletions Views/Editor.xaml.fs
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ type Editor() as this =
let px = pt * grid_scale
Point(Math.Ceiling px.X, Math.Ceiling px.Y) / grid_scale

let fgpaint = SolidColorBrush()
let bgpaint = SolidColorBrush()
let sppaint = SolidColorBrush()

let mutable _buffer_glyphs = [||]

let drawBuffer (ctx: IDrawingContextImpl) row col colend hlid (issym: bool) =
Expand Down Expand Up @@ -112,7 +108,7 @@ type Editor() as this =
let txt = ReadOnlySpan(_buffer_glyphs, 0, colend - col)

try
RenderText(ctx, bg_region, grid_scale, fgpaint, bgpaint, sppaint, attrs.underline, attrs.undercurl, txt, typeface, fontsize)
RenderText(ctx, bg_region, grid_scale, fg, bg, sp, attrs.underline, attrs.undercurl, txt, typeface, fontsize)
with ex -> trace grid_vm "drawBuffer: %s" (ex.ToString())

// assembles text from grid and draw onto the context.
Expand Down
2 changes: 2 additions & 0 deletions Views/MainWindow.xaml.fs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ open Avalonia.Rendering
open Avalonia.Interactivity
open Avalonia.VisualTree

open Avalonia.Diagnostics

#nowarn "0025"

open System.Runtime.InteropServices
Expand Down
7 changes: 2 additions & 5 deletions fvim.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,17 @@
<AvaloniaResource Include="**\*.xaml">
<SubType>Designer</SubType>
</AvaloniaResource>
<EmbeddedResource Include="nerd.ttf" />
<EmbeddedResource Include="Fonts\nerd.ttf" />
<Content Include="Assets\*" />
<None Include="icons\*.ico" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.0-preview2" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.0-preview2" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.0-preview2" />
<PackageReference Include="Avalonia.ReactiveUI" Version="0.10.0-preview2" />
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2019013001" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.0-preview2" />

<PackageReference Include="FSharp.Control.Reactive" Version="4.4.2" />
<PackageReference Include="FSharp.Data" Version="3.3.3" />
Expand Down
155 changes: 76 additions & 79 deletions ui.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ open Avalonia.Input
open Avalonia.Media
open Avalonia.Media.Imaging
open Avalonia.Platform
open Avalonia.Skia
open SkiaSharp
open SkiaSharp.HarfBuzz
open System
open System.Reflection
open Avalonia.Controls.Shapes
open Avalonia.Media.TextFormatting

#nowarn "0009"

Expand Down Expand Up @@ -153,7 +152,7 @@ let DefaultFontEmoji =
elif RuntimeInformation.IsOSPlatform(OSPlatform.OSX) then "Apple Color Emoji"
else "Noto Color Emoji"

let private nerd_typeface = Typeface("fvim.nerd.ttf#Iosevka Nerd Font Complete")
let private nerd_typeface = Typeface("resm:FVim.Fonts.nerd.ttf?assembly=FVim")
let private emoji_typeface = Typeface(DefaultFontEmoji)
let private fontcache = System.Collections.Generic.Dictionary<string*bool*bool, Typeface>()

Expand All @@ -178,7 +177,9 @@ let GetTypeface(txt, italic, bold, font, wfont) =
trace "ui" "GetTypeface: allocating new typeface %s:%b:%b" fname italic bold
let weight = if bold then States.font_weight_bold else States.font_weight_normal
let slang = if italic then FontStyle.Italic else FontStyle.Normal
let typeface = Typeface(fname, slang, weight)
let typeface =
try Typeface(fname, slang, weight)
with | _ -> Typeface.Default
fontcache.[(fname, italic, bold)] <- typeface
typeface

Expand All @@ -192,25 +193,29 @@ let GetTypeface(txt, italic, bold, font, wfont) =
| _ -> _get font

let MeasureText (rune: Rune, font: string, wfont: string, fontSize: float, scaling: float) =
use typeface = GetTypeface(rune, false, false, font, wfont).GlyphTypeface
let typeface = GetTypeface(rune, false, false, font, wfont).GlyphTypeface

let mutable score = 999999999999.0
let mutable s = fontSize
let mutable w = 0.0
let mutable h = 0.0

let search (sizeStep: int) =
// s' is pixels per em
let s' = fontSize + float(sizeStep) * 0.01
// u' is pixels per font design unit
let u' = s' / float typeface.DesignEmHeight
let glyph = [| typeface.GetGlyph(rune.Codepoint) |]
use run = new GlyphRun(typeface, s', Utilities.ReadOnlySlice(ReadOnlyMemory(glyph)))
let bounds = run.Bounds


let w' = bounds.Width
let h'' =
match States.font_lineheight with
| States.Absolute h' -> h'
| States.Default -> float(typeface.LineHeight + typeface.LineGap)
| States.Add h' -> float(typeface.LineHeight + typeface.LineGap) + h'
| States.Absolute lh -> lh
| States.Default -> float typeface.LineHeight * u'
| States.Add lh -> float typeface.LineHeight * u' + lh
let h' = round(h'' * scaling) / scaling
let h' = max h' 1.0

Expand All @@ -234,94 +239,86 @@ let AllocateFramebuffer w h scale =
let pxsize = PixelSize(int <| (w * scale), int <| (h * scale))
new RenderTargetBitmap(pxsize, Vector(96.0 * scale, 96.0 * scale))

let SetOpacity (paint: SolidColorBrush) (opacity: float) =
let c = paint.Color
paint.Color <- Color(byte <| opacity * 255.0, c.R, c.G, c.B)

let SetForegroundBrush(fgpaint: SolidColorBrush, c: Color) =
fgpaint.Color <- c
()
let UpdateOpacity (color: Color) opacity =
Color(byte(255.0 * opacity), color.R, color.G, color.B)

let mutable _render_glyph_buf = [||]
let mutable _render_char_buf = [||]

let RenderText (ctx: IDrawingContextImpl, region: Rect, scale: float, fg: SolidColorBrush, bg: SolidColorBrush, sp: SolidColorBrush, underline: bool, undercurl: bool, text: ReadOnlySpan<uint>, font: Typeface, fontSize: float) =
let _render_brush = SolidColorBrush()
let _sp_brush = SolidColorBrush()
let _sp_pen = Pen(_sp_brush)
let _sp_points = ResizeArray()

// don't clip all along. see #60
let RenderText (ctx: IDrawingContextImpl, region: Rect, scale: float, fg: Color, bg: Color, sp: Color, underline: bool, undercurl: bool, text: ReadOnlySpan<uint>, font: Typeface, fontSize: float) =

let glyphTypeface = font.GlyphTypeface
let px_per_unit = fontSize / float glyphTypeface.DesignEmHeight

// DrawText accepts the coordinate of the baseline.
// h = [padding space 1] + above baseline | below baseline + [padding space 2]
// let h = region.Bottom - region.Top
let h = region.Bottom - region.Top

// fh = [above baseline + below baseline]
let fh = float (glyphTypeface.Descent - glyphTypeface.Ascent) * px_per_unit
// total_padding = padding space 1 + padding space 2
// let total_padding = h - ((fg.FontMetrics.Bottom - fg.FontMetrics.Top))
// let baseline = region.Top + ceil((total_padding / 2.0f) - fg.FontMetrics.Top)
let total_padding = h - fh
let ascent = float glyphTypeface.Ascent * px_per_unit
// Text drawing is done at the coordinate of the baseline.
let baseline = region.Top + ceil((total_padding / 2.0) - ascent)
(*printfn "scale=%A pad=%A base=%A region=%A" scale total_padding baseline region*)
// let fontPos = SKPoint(region.Left, baseline)
let fontPos = Point(region.Left, baseline)

// let skia = ctx :?> ISkiaDrawingContextImpl

//lol wat??
//fg.Shader <- SKShader.CreateCompose(SKShader.CreateColor(fg.Color), SKShader.CreatePerlinNoiseFractalNoise(0.1F, 0.1F, 1, 6.41613F))

// push clip and fill bg
// push clip and fill bg
ctx.PushClip region
ctx.Clear bg.Color
ctx.Clear bg
// don't clip all along. see #60
ctx.PopClip ()

let glyphTypeface = font.GlyphTypeface
if _render_glyph_buf.Length < text.Length then
_render_glyph_buf <- Array.zeroCreate text.Length
for i in 0..text.Length-1 do
_render_glyph_buf.[i] <- glyphTypeface.GetGlyph(text.[i])
let slice = ReadOnlyMemory(_render_glyph_buf, 0, text.Length)
use glyphrun = new GlyphRun(glyphTypeface, fontSize, Utilities.ReadOnlySlice(slice))
ctx.DrawGlyphRun(fg, glyphrun, region.TopLeft)

// Text bounding box drawing:
// --------------------------------------------------
// if States.font_drawBounds then
// let mutable bounds = SKRect()
// let text = if String.IsNullOrEmpty text then " " else text
// ignore <| fg.MeasureText(text, &bounds)
// bounds.Left <- bounds.Left + single (fontPos.X)
// bounds.Top <- bounds.Top + single (fontPos.Y)
// bounds.Right <- bounds.Right + single (fontPos.X)
// bounds.Bottom <- bounds.Bottom + single (fontPos.Y)
// fg.Style <- SKPaintStyle.Stroke
// skia.SkCanvas.DrawRect(bounds, fg)
// --------------------------------------------------

// let sp_thickness = fg.FontMetrics.UnderlineThickness.GetValueOrDefault(1.0F)
//
// if underline then
// let underline_pos = fg.FontMetrics.UnderlinePosition.GetValueOrDefault()
// let p1 = fontPos + SKPoint(0.0f, underline_pos)
// let p2 = p1 + SKPoint(region.Width, 0.0f)
// sp.Style <- SKPaintStyle.Stroke
// //sppaint.StrokeWidth <- sp_thickness
// skia.SkCanvas.DrawLine(p1, p2, sp)
//
// if undercurl then
// let underline_pos = fg.FontMetrics.UnderlinePosition.GetValueOrDefault()
// let mutable px, py = single fontPos.X, single fontPos.Y
// py <- py + underline_pos
// let qf = 1.5F
// let hf = qf * 2.0F
// let q3f = qf * 3.0F
// let ff = qf * 4.0F
// let r = single region.Right
// let py1 = py - 2.0f
// let py2 = py + 2.0f
// sp.Style <- SKPaintStyle.Stroke
// sp.StrokeWidth <- sp_thickness
// use path = new SKPath()
// path.MoveTo(px, py)
// while px < r do
// path.LineTo(px, py)
// path.LineTo(px + qf, py1)
// path.LineTo(px + hf, py)
// path.LineTo(px + q3f, py2)
// px <- px + ff
// skia.SkCanvas.DrawPath(path , sp)

_render_brush.Color <- fg

ctx.DrawGlyphRun(_render_brush, glyphrun, fontPos)

let shaper = TextShaper.Current

// Text bounding box drawing:
if States.font_drawBounds then
ctx.DrawRectangle(Brushes.Transparent, Pen(_render_brush), RoundedRect(Rect(glyphrun.Bounds.TopLeft + region.TopLeft, glyphrun.Bounds.BottomRight + region.TopLeft)))

let sp_thickness = float glyphTypeface.UnderlineThickness * px_per_unit
let underline_pos = float glyphTypeface.UnderlinePosition * px_per_unit
_sp_pen.Thickness <- sp_thickness
_sp_brush.Color <- sp

if underline then
let p1 = fontPos + Point(0.0, underline_pos)
let p2 = p1 + Point(region.Width, 0.0)
ctx.DrawLine(_sp_pen, p1, p2)

if undercurl then
let mutable p = fontPos + Point(0.0, underline_pos)
let qf = 1.5
let hf = qf * 2.0
let q3f = qf * 3.0
let v1 = Point(qf, -2.0)
let v2 = Point(hf, 0.0)
let v3 = Point(q3f, 2.0)
let ff = Point(qf * 4.0, 0.0)
let r = region.Right
_sp_points.Clear()
while p.X < r do
_sp_points.Add(p)
_sp_points.Add(p + v1)
_sp_points.Add(p + v2)
_sp_points.Add(p + v3)
p <- p + ff
ctx.DrawGeometry(Brushes.Transparent, _sp_pen, PolylineGeometry(_sp_points, false).PlatformImpl)

type WindowBackgroundComposition =
| SolidBackground of opacity: float * color: Color
Expand Down

0 comments on commit 0fe6853

Please sign in to comment.