Skip to content

Conversation

@Gillibald
Copy link
Contributor

@Gillibald Gillibald commented Nov 20, 2025

What does the pull request do?

This PR introduces TextOptions similar to the current RenderOptions. This allows customization of certain font rasterization properties, such as font hinting, antialiasing, or baseline pixel alignment.

What is the current behavior?

Currently, it is only possible to customize the text antialiasing.

What is the updated/expected behavior with this PR?

How was the solution implemented (if it's not obvious)?

Default Skia mapping

Freetype

FT_Int loadFlags = FT_LOAD_DEFAULT;

switch (fontHinting) {
    case SkFontHinting::kNone:
        loadFlags |= FT_LOAD_NO_HINTING;
        break;
    case SkFontHinting::kSlight:
        loadFlags |= FT_LOAD_TARGET_LIGHT;
        break;
    case SkFontHinting::kNormal:
        loadFlags |= FT_LOAD_TARGET_NORMAL;
        break;
    case SkFontHinting::kFull:
        loadFlags |= FT_LOAD_TARGET_MONO;
        break;
}

DWrite

DWRITE_RENDERING_MODE renderingMode;

switch (fontHinting) {
    case SkFontHinting::kNone:
        renderingMode = DWRITE_RENDERING_MODE_OUTLINE;
        break;
    case SkFontHinting::kSlight:
        renderingMode = DWRITE_RENDERING_MODE_NATURAL;
        break;
    case SkFontHinting::kNormal:
        renderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
        break;
    case SkFontHinting::kFull:
        renderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
        break;
}

CoreText

switch (fontHinting) {
    case SkFontHinting::kNone:
        options.disableFontSubpixelQuantization = true;
        break;
    case SkFontHinting::kSlight:
        options.hinting = kCTFontDescriptorOptionLightHinting;
        break;
    case SkFontHinting::kNormal:
        options.hinting = kCTFontDescriptorOptionDefaultHinting;
        break;
    case SkFontHinting::kFull:
        options.hinting = kCTFontDescriptorOptionStrongHinting;
        break;
}

Checklist

Breaking changes

Obsoletions / Deprecations

Fixed issues

Fixes: #12510
Fixes: #19553

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This pull request introduces TextOptions as a new dedicated type for configuring text rendering, separating text-specific settings from the general RenderOptions. This change improves API design by providing a clearer separation of concerns between general rendering options and text-specific rendering configuration.

Key Changes

  • Introduces TextOptions struct with TextRenderingMode, TextHintingMode, and BaselinePixelAlign properties
  • Moves TextRenderingMode from RenderOptions to TextOptions
  • Implements a new TwoLevelCache for efficient text blob caching based on complete text options
  • Updates all drawing context implementations to support text options push/pop operations

Reviewed Changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/Avalonia.Base/Media/TextOptions.cs New struct defining text rendering options with methods for getting/setting options on visuals
src/Avalonia.Base/Media/TextHintingMode.cs New enum defining text hinting modes (None, Slight, Normal, Full)
src/Avalonia.Base/Media/TextRenderingMode.cs Enhanced with XML documentation for each enum value
src/Avalonia.Base/Media/RenderOptions.cs Removed TextRenderingMode property and related methods
src/Avalonia.Base/Visual.cs Added TextOptions property alongside existing RenderOptions
src/Avalonia.Base/Platform/IDrawingContextImpl.cs Added PushTextOptions and PopTextOptions methods to interface
src/Skia/Avalonia.Skia/TwoLevelCache.cs New cache implementation for storing text blobs with eviction support
src/Skia/Avalonia.Skia/GlyphRunImpl.cs Updated to use TextOptions for text blob caching instead of simple edging array
src/Skia/Avalonia.Skia/DrawingContextImpl.cs Implemented text options stack and applies hinting/baseline settings
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs Added text options support with separate text antialiasing control
src/Headless/Avalonia.Headless/HeadlessPlatformRenderInterface.cs Added no-op text options methods for headless rendering
src/Avalonia.Base/Rendering/ImmediateRenderer.cs Updated to push text options before render options
src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionVisual.cs Added text options push/pop in rendering pipeline
src/Avalonia.Base/Rendering/Composition/Server/DrawingContextProxy.cs Added text options proxy commands
tests/Avalonia.Skia.UnitTests/Media/GlyphRunTests.cs Updated to use new TextOptions API
tests/Avalonia.RenderTests/Media/GlyphRunTests.cs Migrated from RenderOptions.SetTextRenderingMode to TextOptions.SetTextRenderingMode

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Gillibald and others added 7 commits November 20, 2025 08:15
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
@Gillibald Gillibald marked this pull request as draft November 20, 2025 08:48
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0060178-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul
Copy link
Member

MrJul commented Dec 3, 2025

Notes from the API review meeting:

  • Keep RenderOptions.TextRenderingMode and make it obsolete (redirect to TextOptions.TextRenderingMode)
  • For consistency, BaselinePixelAlign might be better as an enum rather than a bool?. Proposed: BaselinePixelAlignment { Unspecified, Aligned, Unaligned }

@MrJul MrJul added api-change-requested The new public APIs need some changes. and removed needs-api-review The PR adds new public APIs that should be reviewed. labels Dec 3, 2025
@MrJul
Copy link
Member

MrJul commented Dec 3, 2025

Also, make sure documentation states that the various backends/platforms may behave a bit differently here.

@MrJul MrJul added api-approved The new public APIs have been approved. and removed api-change-requested The new public APIs need some changes. labels Dec 4, 2025
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0061229-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@Gillibald Gillibald marked this pull request as ready for review January 7, 2026 10:30
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 12.0.999-cibuild0061271-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file has likely been committed by mistake, please remove.

/// <remarks>Use this enumeration to control whether the baseline of rendered content is aligned to the
/// pixel grid, which can affect visual crispness and positioning. The value may influence rendering quality,
/// especially at small font sizes or when precise alignment is required.</remarks>
public enum BaselinePixelAlignment
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we've been pretty inconsistent with the usual "one type per file", but other used enums in this file are in their own files. Please move it to its own.

/// <remarks>Use this enumeration to control whether the baseline of rendered content is aligned to the
/// pixel grid, which can affect visual crispness and positioning. The value may influence rendering quality,
/// especially at small font sizes or when precise alignment is required.</remarks>
public enum BaselinePixelAlignment
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other enums used in RenderOptions and TextOptions specify a byte underlying type for tight packing of the render data. We should probably do the same here for consistency?

}

// If primary matches after factory (unlikely) return primary and dispose created
if (_comparer.Equals(_primaryKey!, key))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I'm mistaken, this isn't unlikely (as noted by the comment above), but completely impossible? _primaryKey and key didn't change and the primary key comparison was already handled by TryGet at the beginning of the method.

}

// Rotate into secondary (evict last)
var newSec = new KeyValuePair<TKey, TValue>[sec.Length];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be rotated in place, there is no need to allocate a new array each time.

/// thread-safe.</remarks>
/// <typeparam name="TKey">The type of keys used to identify cached values. Must be non-nullable.</typeparam>
/// <typeparam name="TValue">The type of values to be stored in the cache. Must be a reference type.</typeparam>
internal class TwoLevelCache<TKey, TValue>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm aware this is an internal helper, but a couple of unit tests testing basic features (primary key, secondary cache, eviction) might be nice. This is typically the type of helper that has just enough complexity to contain hidden bugs ;)

(For example, we had several bugs in the past with InlineDictionary that manifested only in some specific scenarios.)

[InlineData(TextRenderingMode.Alias, TextHintingMode.None)]
[InlineData(TextRenderingMode.Antialias, TextHintingMode.Light)]
[InlineData(TextRenderingMode.Alias, TextHintingMode.Light)]
[Theory(Skip = "Backend dependent")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we at least execute the tests on Windows?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose font hinting option in RenderOptions Text snaps to pixels when animating a control's render translation

3 participants