Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7a86aa7
feat: add MSDF glyph types and font store skeleton
isrcalebe Sep 18, 2026
dd39923
feat: implement MSDF text rendering with glyph support and shaders
isrcalebe Sep 18, 2026
3acd1ca
feat: enhance MSDF glyph handling with improved style parsing and fal…
isrcalebe Sep 21, 2026
5c7181b
feat: update WorkSans font files and add RegularItalic typeface asset…
isrcalebe Sep 21, 2026
e919747
fix: implement Dispose method to properly dispose of glyph sources
isrcalebe Sep 21, 2026
bd6597d
refactor: simplify Bindable initialization and remove unused TestScen…
isrcalebe Sep 21, 2026
fc8f7bb
Merge pull request #1 from KAJARLABORATORIES/feature/msdf
isrcalebe Sep 21, 2026
635d490
refactor: update banned-symbols.txt to replace osu!-related methods a…
isrcalebe Sep 21, 2026
18c875a
chore: add CI and release workflows for automated testing and deployment
isrcalebe Sep 21, 2026
a617241
chore: add CI and release workflows for automated testing and deploym…
isrcalebe Sep 21, 2026
ed76e5a
test(msdf): add tests for MsdfSpriteText presence behavior
isrcalebe Sep 21, 2026
a22c935
Merge pull request #3 from KAJARLABORATORIES/msdf-tests
isrcalebe Sep 21, 2026
330bbe6
fix(ci): use the actual solution name in dotnet test
isrcalebe Sep 21, 2026
ea1a429
Merge pull request #4 from KAJARLABORATORIES/fix/ci-solution-name
isrcalebe Sep 21, 2026
a201fa8
chore: add README and LICENSE files for project documentation
isrcalebe Sep 21, 2026
413835b
Merge pull request #5 from KAJARLABORATORIES/readme
isrcalebe Sep 21, 2026
27761d8
chore: add README and LICENSE files for project documentation
isrcalebe Sep 21, 2026
9e8239b
chore: update README files to improve clarity and remove outdated inf…
isrcalebe Sep 21, 2026
115429c
Merge branch 'develop' into new-readme
isrcalebe Sep 21, 2026
052cf43
Merge pull request #6 from KAJARLABORATORIES/new-readme
isrcalebe Sep 21, 2026
350650f
feat: implement MSDF text rendering components and tests
isrcalebe Sep 21, 2026
79e23a3
Merge pull request #7 from KAJARLABORATORIES/feature/textbuilder
isrcalebe Sep 21, 2026
be07e5c
Merge pull request #8 from KAJARLABORATORIES/release/0.2.0
isrcalebe Sep 21, 2026
91c0b77
fix: update README file paths in project configuration
isrcalebe Sep 21, 2026
c58cf2d
Merge pull request #10 from KAJARLABORATORIES/fix/update-readme
isrcalebe Sep 21, 2026
10919be
fix(build): rename packaged readme to avoid NU5118 collision
isrcalebe Sep 22, 2026
d4a4dd8
Merge pull request #11 from KAJARLABORATORIES/fix/package-readme
isrcalebe Sep 22, 2026
d572dbc
fix(build): reference README.md from Directory.Build.props
isrcalebe Sep 22, 2026
0f8553c
Merge pull request #12 from KAJARLABORATORIES/fix/nuget-readme
isrcalebe Sep 22, 2026
c4a5738
fix(build): fix README.md path
isrcalebe Sep 22, 2026
8c29e54
Merge pull request #13 from KAJARLABORATORIES/fix/nuget-readme
isrcalebe Sep 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# libmsdf

[![Build status](https://github.com/KAJARLABORATORIES/libmsdf/actions/workflows/release.yml/badge.svg?branch=main&event=push)](https://github.com/KAJARLABORATORIES/libmsdf/actions/workflows/release.yml)
[![NuGet](https://img.shields.io/nuget/vpre/kajarlabs.osu.Framework.MsdfTextRendering.svg)](https://www.nuget.org/packages/kajarlabs.osu.Framework.MsdfTextRendering)
[![GitHub release](https://img.shields.io/github/v/release/KAJARLABORATORIES/libmsdf?include_prereleases)](https://github.com/KAJARLABORATORIES/libmsdf/releases/latest)

MSDF text rendering for [osu!framework](https://github.com/ppy/osu-framework).

## Using libmsdf in your game

If you are interested in **using** the library, install the package and start from the [setting up MSDF fonts](https://github.com/KAJARLABORATORIES/libmsdf/wiki/Setting-Up-MSDF-Fonts) wiki page, which walks through generating an atlas, registering it and drawing text with it.

```
dotnet add package kajarlabs.osu.Framework.MsdfTextRendering --prerelease
```

The library is pre-`1.0.0`. The public API may change between `0.x` releases, so pin an exact version if you depend on it.

The rest of the information on this page is related to working *on* the library, not *using* it!

## Objectives

osu!framework renders text from pre-rasterised bitmap atlases. That is fast and predictable, but a glyph baked at one size degrades once it is drawn far beyond it, which shows up in any UI that zooms, animates scale, or shares a typeface across a wide range of sizes.

`libmsdf` provides a second rendering path built on [multi-channel signed distance fields](https://github.com/Chlumsky/msdfgen), where glyph coverage is reconstructed in the fragment shader instead of sampled from a fixed-resolution texture.

- Stay a **drop-in alternative**, not a replacement. `MsdfSpriteText` mirrors `SpriteText`, and MSDF glyphs implement the framework's own glyph abstractions so the existing text builder, layout and localisation keep working.
- Keep the atlas pipeline **external and standard**. Atlases are produced by [`msdf-atlas-gen`](https://github.com/Chlumsky/msdf-atlas-gen), so nothing here is a bespoke font format.
- Cover the same text features users already expect: kerning, multiline, truncation, fixed width, fallback glyphs and shadows.
MSDF is not a free upgrade for every case. At small, static sizes a hinted bitmap font is cheaper and often looks better; this library is aimed at the cases where one atlas has to serve many sizes.

## Requirements

- A desktop platform with the [.NET 10.0 SDK](https://dotnet.microsoft.com/download).
- [`msdf-atlas-gen`](https://github.com/Chlumsky/msdf-atlas-gen) for generating font atlases.
- When working with the codebase, we recommend an IDE with intellisense and syntax highlighting, such as [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/), or [Visual Studio Code](https://code.visualstudio.com/) with the [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) and [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) extensions installed.

### Building

```
dotnet build
dotnet test
```

Visual test scenes live in `tests`, and are the recommended place to develop and debug rendering changes: a distance-field bug is something you see, not something an assertion catches.

## Contributing

Contributions can be made via pull requests to this repository. If you are unsure where to start, check the [list of open issues](https://github.com/KAJARLABORATORIES/libmsdf/issues).

When reporting a rendering bug, include a minimal reproduction and the atlas and JSON metadata that trigger it. "The glyph looks wrong" is impossible to act on without the distance range it was generated with.

## License

This library is licensed under the [MIT license](https://opensource.org/licenses/MIT). Please see [the license file](../COPYING) for more information. [tl;dr](https://tldrlegal.com/license/mit-license) you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source.

The MSDF technique itself comes from [msdfgen](https://github.com/Chlumsky/msdfgen) by Viktor Chlumský, also MIT licensed. Atlases generated with `msdf-atlas-gen` carry the license of the font they were generated from; check it before shipping one.
57 changes: 57 additions & 0 deletions .github/README.nuget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# libmsdf

[![Build status](https://github.com/KAJARLABORATORIES/libmsdf/actions/workflows/release.yml/badge.svg?branch=main&event=push)](https://github.com/KAJARLABORATORIES/libmsdf/actions/workflows/release.yml)
[![NuGet](https://img.shields.io/nuget/vpre/kajarlabs.osu.Framework.MsdfTextRendering.svg)](https://www.nuget.org/packages/kajarlabs.osu.Framework.MsdfTextRendering)
[![GitHub release](https://img.shields.io/github/v/release/KAJARLABORATORIES/libmsdf?include_prereleases)](https://github.com/KAJARLABORATORIES/libmsdf/releases/latest)

MSDF text rendering for [osu!framework](https://github.com/ppy/osu-framework).

## Using libmsdf in your game

If you are interested in **using** the library, install the package and start from the [setting up MSDF fonts](https://github.com/KAJARLABORATORIES/libmsdf/wiki/Setting-Up-MSDF-Fonts) wiki page, which walks through generating an atlas, registering it and drawing text with it.

```
dotnet add package kajarlabs.osu.Framework.MsdfTextRendering --prerelease
```

The library is pre-`1.0.0`. The public API may change between `0.x` releases, so pin an exact version if you depend on it.

The rest of the information on this page is related to working *on* the library, not *using* it!

## Objectives

osu!framework renders text from pre-rasterised bitmap atlases. That is fast and predictable, but a glyph baked at one size degrades once it is drawn far beyond it, which shows up in any UI that zooms, animates scale, or shares a typeface across a wide range of sizes.

`libmsdf` provides a second rendering path built on [multi-channel signed distance fields](https://github.com/Chlumsky/msdfgen), where glyph coverage is reconstructed in the fragment shader instead of sampled from a fixed-resolution texture.

- Stay a **drop-in alternative**, not a replacement. `MsdfSpriteText` mirrors `SpriteText`, and MSDF glyphs implement the framework's own glyph abstractions so the existing text builder, layout and localisation keep working.
- Keep the atlas pipeline **external and standard**. Atlases are produced by [`msdf-atlas-gen`](https://github.com/Chlumsky/msdf-atlas-gen), so nothing here is a bespoke font format.
- Cover the same text features users already expect: kerning, multiline, truncation, fixed width, fallback glyphs and shadows.
MSDF is not a free upgrade for every case. At small, static sizes a hinted bitmap font is cheaper and often looks better; this library is aimed at the cases where one atlas has to serve many sizes.

## Requirements

- A desktop platform with the [.NET 10.0 SDK](https://dotnet.microsoft.com/download).
- [`msdf-atlas-gen`](https://github.com/Chlumsky/msdf-atlas-gen) for generating font atlases.
- When working with the codebase, we recommend an IDE with intellisense and syntax highlighting, such as [Visual Studio](https://visualstudio.microsoft.com/vs/), [JetBrains Rider](https://www.jetbrains.com/rider/), or [Visual Studio Code](https://code.visualstudio.com/) with the [EditorConfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig) and [C#](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp) extensions installed.

### Building

```
dotnet build
dotnet test
```

Visual test scenes live in `tests`, and are the recommended place to develop and debug rendering changes: a distance-field bug is something you see, not something an assertion catches.

## Contributing

Contributions can be made via pull requests to this repository. If you are unsure where to start, check the [list of open issues](https://github.com/KAJARLABORATORIES/libmsdf/issues).

When reporting a rendering bug, include a minimal reproduction and the atlas and JSON metadata that trigger it. "The glyph looks wrong" is impossible to act on without the distance range it was generated with.

## License

This library is licensed under the [MIT license](https://opensource.org/licenses/MIT). Please see [the license file](../COPYING) for more information. [tl;dr](https://tldrlegal.com/license/mit-license) you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source.

The MSDF technique itself comes from [msdfgen](https://github.com/Chlumsky/msdfgen) by Viktor Chlumský, also MIT licensed. Atlases generated with `msdf-atlas-gen` carry the license of the font they were generated from; check it before shipping one.
58 changes: 58 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Continuous Integration

on:
push:
branches: [develop]
pull_request:

permissions:
contents: read

jobs:
test:
name: Test
runs-on: ${{ matrix.os.fullname }}
timeout-minutes: 60
env:
OSU_EXECUTION_MODE: ${{ matrix.threadingMode }}
strategy:
fail-fast: false
matrix:
os:
- { prettyname: Windows, fullname: windows-latest, configuration: Debug }
- { prettyname: macOS, fullname: macos-latest, configuration: Debug } # ARM: veja o item 3 abaixo
- { prettyname: Linux, fullname: ubuntu-latest, configuration: Debug }
- { prettyname: Linux, fullname: ubuntu-latest, configuration: Release }
threadingMode: ["SingleThread", "MultiThreaded"]

steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Install .NET 10.0.x
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"

- name: Compile
run: dotnet build -c ${{ matrix.os.configuration }} osu-framework-msdf.slnx

- name: Test
run: >
dotnet test osu-framework-msdf.slnx
-c ${{ matrix.os.configuration }}
--no-build
--results-directory ${{ github.workspace }}/TestResults
--logger "trx;LogFileName=TestResults-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }}-${{ matrix.os.configuration }}.trx"
-- NUnit.ConsoleOut=0

# Sobe os resultados mesmo se o teste falhar.
- name: Upload Test Results
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: libmsdf-test-results-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }}-${{ matrix.os.configuration }}
path: ${{ github.workspace }}/TestResults/TestResults-${{ matrix.os.prettyname }}-${{ matrix.threadingMode }}-${{ matrix.os.configuration }}.trx
if-no-files-found: error
144 changes: 144 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
name: Package and NuGet

on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+*"

permissions:
contents: read

jobs:
notify_process_deploy:
name: Notify start
runs-on: ubuntu-latest
continue-on-error: true
env:
WEBHOOK_URL: ${{ secrets.DISCORD_INFRA_WEBHOOK_URL }}
steps:
- name: Notify Deployment
run: |
export TITLE="🚀 Deployment in Progress: libmsdf ($GITHUB_REF_NAME)"
export URL="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
export DESCRIPTION="A new deployment for **libmsdf** has started."$'\n'"Stay tuned for updates!"$'\n'"➜ [View Workflow Run]($URL)"
export ACTOR_ICON="https://avatars.githubusercontent.com/u/$GITHUB_ACTOR_ID"

BODY="$(jq --null-input '{
embeds: [{
title: env.TITLE,
color: 15098112,
description: env.DESCRIPTION,
url: env.URL,
author: { name: env.GITHUB_ACTOR, icon_url: env.ACTOR_ICON }
}]
}')"

curl --fail -H "Content-Type: application/json" -d "$BODY" "$WEBHOOK_URL"

test:
name: Test (${{ matrix.threadingMode }})
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: true
matrix:
threadingMode: ["SingleThread", "MultiThreaded"]
env:
OSU_EXECUTION_MODE: ${{ matrix.threadingMode }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Install .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"

- name: Test
run: dotnet test -c Release

pack:
name: Pack (libmsdf)
needs: [test]
runs-on: ubuntu-latest
environment: production
timeout-minutes: 15
permissions:
contents: read
id-token: write
env:
ARTIFACTS: ${{ github.workspace }}/artifacts
VERSION: ${{ github.ref_name }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
persist-credentials: false

- name: Install .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: "10.0.x"

- name: Pack (libmsdf)
run: dotnet pack lib -c Release /p:Version="${VERSION#v}" -o "$ARTIFACTS"

- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: libmsdf
path: ${{ github.workspace }}/artifacts/*.nupkg
if-no-files-found: error

- name: NuGet login (OIDC)
id: login
uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841
with:
user: ${{ vars.NUGET_USER }}

- name: Publish packages to NuGet
env:
NUGET_API_KEY: ${{ steps.login.outputs.NUGET_API_KEY }}
run: >
dotnet nuget push "$ARTIFACTS"/*.nupkg
--api-key "$NUGET_API_KEY"
--source https://api.nuget.org/v3/index.json
--skip-duplicate

notify_status_deploy:
name: Notify result
needs: [test, pack]
if: always()
runs-on: ubuntu-latest
continue-on-error: true
env:
PACK_RESULT: ${{ needs.pack.result }}
WEBHOOK_URL: ${{ secrets.DISCORD_INFRA_WEBHOOK_URL }}
steps:
- name: Notify Deployment Status
run: |
export TITLE="🚀 Deployment Completed: libmsdf ($GITHUB_REF_NAME)"
export URL="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"

if [ "$PACK_RESULT" = "success" ]; then
STATUS="✅ Deployment successful!"; export COLOR=3066993
else
STATUS="❌ Deployment failed. Check logs for details."; export COLOR=15158332
fi

export DESCRIPTION="Deployment for **libmsdf** has finished."$'\n'"$STATUS"$'\n'"➜ [View Workflow Run]($URL)"
export ACTOR_ICON="https://avatars.githubusercontent.com/u/$GITHUB_ACTOR_ID"

BODY="$(jq --null-input '{
embeds: [{
title: env.TITLE,
color: (env.COLOR | tonumber),
description: env.DESCRIPTION,
url: env.URL,
author: { name: env.GITHUB_ACTOR, icon_url: env.ACTOR_ICON }
}]
}')"

curl --fail -H "Content-Type: application/json" -d "$BODY" "$WEBHOOK_URL"
42 changes: 42 additions & 0 deletions .github/workflows/report-nunit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Annotate CI run with Test Results

on:
workflow_run:
workflows: ["Continuous Integration"]
types: [completed]

permissions:
contents: read
actions: read
checks: write

jobs:
annotate:
name: Annotate CI run with Test Results
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion != 'cancelled' }}
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v4
with:
repository: ${{ github.event.workflow_run.repository.full_name }}
ref: ${{ github.event.workflow_run.head_sha }}
persist-credentials: false

- name: Download results
uses: actions/download-artifact@v4
with:
pattern: libmsdf-test-results-*
merge-multiple: true
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ github.token }}

- name: Annotate CI run with Test Results
uses: dorny/test-reporter@a43b3a5f7366b97d083190328d2c652e1a8b6aa2
with:
name: Results
path: "*.trx"
reporter: dotnet-trx
list-suites: "failed"
list-tests: "failed"
21 changes: 21 additions & 0 deletions COPYING
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 KAJAR LABORATORIES

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading