Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1,302 changes: 321 additions & 981 deletions api/Avalonia.nupkg.xml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion samples/TextTestApp/InteractiveLineControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ public InteractiveLineControl()
_textSource = new TextSource(this);

RenderOptions.SetEdgeMode(this, EdgeMode.Aliased);
RenderOptions.SetTextRenderingMode(this, TextRenderingMode.SubpixelAntialias);
TextOptions.SetTextRenderingMode(this, TextRenderingMode.SubpixelAntialias);
}

private void InvalidateTextRunProperties()
Expand Down
26 changes: 26 additions & 0 deletions src/Avalonia.Base/Media/BaselinePixelAlignment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Avalonia.Media
{
/// <summary>
/// Specifies the baseline pixel alignment options for rendering text or graphics.
/// </summary>
/// <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 : byte
{
/// <summary>
/// The baseline pixel alignment is unspecified.
/// </summary>
Unspecified,

/// <summary>
/// The baseline is aligned to the pixel grid.
/// </summary>
Aligned,

/// <summary>
/// The baseline is not aligned to the pixel grid.
/// </summary>
Unaligned
}
}
20 changes: 19 additions & 1 deletion src/Avalonia.Base/Media/DrawingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ public enum PushedStateType
Clip,
GeometryClip,
OpacityMask,
RenderOptions
RenderOptions,
TextOptions
}

public RestoreState(DrawingContext context, PushedStateType type)
Expand All @@ -311,6 +312,8 @@ public void Dispose()
_context.PopOpacityMaskCore();
else if (_type == PushedStateType.RenderOptions)
_context.PopRenderOptionsCore();
else if (_type == PushedStateType.TextOptions)
_context.PopTextOptionsCore();
}
}

Expand Down Expand Up @@ -417,6 +420,20 @@ public PushedState PushRenderOptions(RenderOptions renderOptions)
}
protected abstract void PushRenderOptionsCore(RenderOptions renderOptions);

/// <summary>
/// Pushes text options for the drawing context.
/// </summary>
/// <param name="textOptions">The text options.</param>
/// <returns>A disposable to undo the text options.</returns>
public PushedState PushTextOptions(TextOptions textOptions)
{
PushTextOptionsCore(textOptions);
_states ??= StateStackPool.Get();
_states.Push(new RestoreState(this, RestoreState.PushedStateType.TextOptions));
return new PushedState(this);
}
protected abstract void PushTextOptionsCore(TextOptions textOptions);

[Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
public PushedState PushPreTransform(Matrix matrix) => PushTransform(matrix);
[Obsolete("Use PushTransform"), EditorBrowsable(EditorBrowsableState.Never)]
Expand All @@ -433,6 +450,7 @@ public PushedState PushRenderOptions(RenderOptions renderOptions)
protected abstract void PopOpacityMaskCore();
protected abstract void PopTransformCore();
protected abstract void PopRenderOptionsCore();
protected abstract void PopTextOptionsCore();

private static bool PenIsVisible(IPen? pen)
{
Expand Down
13 changes: 13 additions & 0 deletions src/Avalonia.Base/Media/DrawingGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public IBrush? OpacityMask
}

internal RenderOptions? RenderOptions { get; set; }
internal TextOptions? TextOptions { get; set; }

/// <summary>
/// Gets or sets the collection that contains the child geometries.
Expand All @@ -78,6 +79,7 @@ internal override void DrawCore(DrawingContext context)
using (ClipGeometry != null ? context.PushGeometryClip(ClipGeometry) : default)
using (OpacityMask != null ? context.PushOpacityMask(OpacityMask, bounds) : default)
using (RenderOptions != null ? context.PushRenderOptions(RenderOptions.Value) : default)
using (TextOptions != null ? context.PushTextOptions(TextOptions.Value) : default)
{
foreach (var drawing in Children)
{
Expand Down Expand Up @@ -325,6 +327,15 @@ protected override void PushRenderOptionsCore(RenderOptions renderOptions)
drawingGroup.RenderOptions = renderOptions;
}

protected override void PushTextOptionsCore(TextOptions textOptions)
{
// Instantiate a new drawing group and set it as the _currentDrawingGroup
var drawingGroup = PushNewDrawingGroup();

// Set the text options on the new DrawingGroup
drawingGroup.TextOptions = textOptions;
}

protected override void PopClipCore() => Pop();

protected override void PopGeometryClipCore() => Pop();
Expand All @@ -337,6 +348,8 @@ protected override void PushRenderOptionsCore(RenderOptions renderOptions)

protected override void PopRenderOptionsCore() => Pop();

protected override void PopTextOptionsCore() => Pop();

/// <summary>
/// Creates a new DrawingGroup for a Push* call by setting the
/// _currentDrawingGroup to a newly instantiated DrawingGroup,
Expand Down
4 changes: 4 additions & 0 deletions src/Avalonia.Base/Media/PlatformDrawingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ protected override void PushTransformCore(Matrix matrix)
}

protected override void PushRenderOptionsCore(RenderOptions renderOptions) => _impl.PushRenderOptions(renderOptions);

protected override void PushTextOptionsCore(TextOptions textOptions) => _impl.PushTextOptions(textOptions);

protected override void PopClipCore() => _impl.PopClip();

Expand All @@ -99,6 +101,8 @@ protected override void PopTransformCore() =>
(_transforms ?? throw new ObjectDisposedException(nameof(PlatformDrawingContext))).Pop();

protected override void PopRenderOptionsCore() => _impl.PopRenderOptions();

protected override void PopTextOptionsCore() => _impl.PopTextOptions();

protected override void DisposeCore()
{
Expand Down
45 changes: 43 additions & 2 deletions src/Avalonia.Base/Media/RenderOptions.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,48 @@
using Avalonia.Media.Imaging;
using System;
using Avalonia.Media.Imaging;

namespace Avalonia.Media
{
/// <summary>
/// Provides a set of options that control rendering behavior for visuals, including text rendering, bitmap
/// interpolation, edge rendering, blending, and opacity handling.
/// </summary>
/// <remarks>Use this structure to specify rendering preferences for visual elements. Each property
/// corresponds to a specific aspect of rendering, allowing fine-grained control over how content is displayed.
/// These options can be applied to visuals to influence quality, performance, and visual effects. When merging two
/// instances, unspecified values are inherited from the other instance, enabling layered configuration.</remarks>
public readonly record struct RenderOptions
{
/// <summary>
/// Gets the text rendering mode used to control how text glyphs are rendered.
/// </summary>
[Obsolete("TextRenderingMode is obsolete. Use TextOptions.TextRenderingMode instead.")]
public TextRenderingMode TextRenderingMode { get; init; }

/// <summary>
/// Gets the interpolation mode used when rendering bitmap images.
/// </summary>
/// <remarks>The interpolation mode determines how bitmap images are scaled or transformed during
/// rendering. Selecting an appropriate mode can affect image quality and performance.
/// </remarks>
public BitmapInterpolationMode BitmapInterpolationMode { get; init; }

/// <summary>
/// Gets the edge rendering mode used for drawing operations.
/// </summary>
public EdgeMode EdgeMode { get; init; }
public TextRenderingMode TextRenderingMode { get; init; }

/// <summary>
/// Gets the blending mode used when rendering bitmap images.
/// </summary>
/// <remarks>The blending mode determines how bitmap pixels are composited with the background or
/// other images. Select an appropriate mode based on the desired visual effect, such as transparency or
/// additive blending.</remarks>
public BitmapBlendingMode BitmapBlendingMode { get; init; }

/// <summary>
/// Gets a value indicating whether full opacity handling is required for the associated content.
/// </summary>
public bool? RequiresFullOpacityHandling { get; init; }

/// <summary>
Expand Down Expand Up @@ -75,6 +110,7 @@ public static void SetEdgeMode(Visual visual, EdgeMode value)
/// </summary>
/// <param name="visual">The control.</param>
/// <returns>The value.</returns>
[Obsolete("TextRenderingMode is obsolete. Use TextOptions.TextRenderingMode instead.")]
public static TextRenderingMode GetTextRenderingMode(Visual visual)
{
return visual.RenderOptions.TextRenderingMode;
Expand All @@ -85,6 +121,7 @@ public static TextRenderingMode GetTextRenderingMode(Visual visual)
/// </summary>
/// <param name="visual">The control.</param>
/// <param name="value">The value.</param>
[Obsolete("TextRenderingMode is obsolete. Use TextOptions.TextRenderingMode instead.")]
public static void SetTextRenderingMode(Visual visual, TextRenderingMode value)
{
visual.RenderOptions = visual.RenderOptions with { TextRenderingMode = value };
Expand Down Expand Up @@ -126,11 +163,15 @@ public RenderOptions MergeWith(RenderOptions other)
edgeMode = other.EdgeMode;
}

#pragma warning disable CS0618
var textRenderingMode = TextRenderingMode;
#pragma warning restore CS0618

if (textRenderingMode == TextRenderingMode.Unspecified)
{
#pragma warning disable CS0618
textRenderingMode = other.TextRenderingMode;
#pragma warning disable CS0618
}

var bitmapBlendingMode = BitmapBlendingMode;
Expand Down
31 changes: 31 additions & 0 deletions src/Avalonia.Base/Media/TextHintingMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace Avalonia.Media
{
/// <summary>
/// Specifies the level of hinting applied to text glyphs during rendering.
/// Text hinting adjusts glyph outlines to improve readability and crispness,
/// especially at small font sizes or low DPI. This enum controls the amount
/// of grid-fitting and outline adjustment performed.
/// </summary>
public enum TextHintingMode : byte
{
/// <summary>
/// Hinting mode is not explicitly specified. The default will be used.
/// </summary>
Unspecified,

/// <summary>
/// No hinting, outlines are scaled only.
/// </summary>
None,

/// <summary>
/// Minimal hinting, preserves glyph shape.
/// </summary>
Light,

/// <summary>
/// Aggressive grid-fitting, maximum crispness at low DPI.
/// </summary>
Strong
}
}
136 changes: 136 additions & 0 deletions src/Avalonia.Base/Media/TextOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
namespace Avalonia.Media
{
/// <summary>
/// Provides options for controlling text rendering behavior, including rendering mode, hinting mode, and baseline
/// pixel alignment. Used to configure how text appears within visual elements.
/// </summary>
/// <remarks>TextOptions encapsulates settings that influence the clarity, sharpness, and positioning of
/// rendered text. These options can be applied to visual elements to customize text appearance for different
/// display scenarios, such as optimizing for readability at small font sizes or ensuring pixel-perfect alignment.
/// The struct supports merging with other instances to inherit unspecified values, and exposes attached properties
/// for use with visuals.</remarks>
public readonly record struct TextOptions
{
/// <summary>
/// Gets the text rendering mode used to control how text glyphs are rendered.
/// </summary>
public TextRenderingMode TextRenderingMode { get; init; }

/// <summary>
/// Gets the text rendering hinting mode used to optimize the display of text.
/// </summary>
/// <remarks>The hinting mode determines how text is rendered to improve clarity and readability,
/// especially at small font sizes. Changing this value may affect the appearance of text depending on the
/// rendering engine and display device.</remarks>
public TextHintingMode TextHintingMode { get; init; }

/// <summary>
/// Gets a value indicating whether the text baseline should be aligned to the pixel grid.
/// </summary>
/// <remarks>
/// When enabled, the vertical position of the text baseline is snapped to whole pixel boundaries.
/// This ensures consistent sharpness and reduces blurriness caused by fractional positioning,
/// particularly at small font sizes or low DPI settings.
/// </remarks>
public BaselinePixelAlignment BaselinePixelAlignment { get; init; }

/// <summary>
/// Merges this instance with <paramref name="other"/> using inheritance semantics: unspecified values on this
/// instance are taken from <paramref name="other"/>.
/// </summary>
public TextOptions MergeWith(TextOptions other)
{
var textRenderingMode = TextRenderingMode;

if (textRenderingMode == TextRenderingMode.Unspecified)
{
textRenderingMode = other.TextRenderingMode;
}

var textHintingMode = TextHintingMode;

if (textHintingMode == TextHintingMode.Unspecified)
{
textHintingMode = other.TextHintingMode;
}

var baselinePixelAlignment = BaselinePixelAlignment;

if (baselinePixelAlignment == BaselinePixelAlignment.Unspecified)
{
baselinePixelAlignment = other.BaselinePixelAlignment;
}

return new TextOptions
{
TextRenderingMode = textRenderingMode,
TextHintingMode = textHintingMode,
BaselinePixelAlignment = baselinePixelAlignment
};
}

/// <summary>
/// Gets the TextOptions attached value for a visual.
/// </summary>
public static TextOptions GetTextOptions(Visual visual)
{
return visual.TextOptions;
}

/// <summary>
/// Sets the TextOptions attached value for a visual.
/// </summary>
public static void SetTextOptions(Visual visual, TextOptions value)
{
visual.TextOptions = value;
}

/// <summary>
/// Gets the TextRenderingMode attached property for a visual.
/// </summary>
public static TextRenderingMode GetTextRenderingMode(Visual visual)
{
return visual.TextOptions.TextRenderingMode;
}

/// <summary>
/// Sets the TextRenderingMode attached property for a visual.
/// </summary>
public static void SetTextRenderingMode(Visual visual, TextRenderingMode value)
{
visual.TextOptions = visual.TextOptions with { TextRenderingMode = value };
}

/// <summary>
/// Gets the TextHintingMode attached property for a visual.
/// </summary>
public static TextHintingMode GetTextHintingMode(Visual visual)
{
return visual.TextOptions.TextHintingMode;
}

/// <summary>
/// Sets the TextHintingMode attached property for a visual.
/// </summary>
public static void SetTextHintingMode(Visual visual, TextHintingMode value)
{
visual.TextOptions = visual.TextOptions with { TextHintingMode = value };
}

/// <summary>
/// Gets the BaselinePixelAlignment attached property for a visual.
/// </summary>
public static BaselinePixelAlignment GetBaselinePixelAlignment(Visual visual)
{
return visual.TextOptions.BaselinePixelAlignment;
}

/// <summary>
/// Sets the BaselinePixelAlignment attached property for a visual.
/// </summary>
public static void SetBaselinePixelAlignment(Visual visual, BaselinePixelAlignment value)
{
visual.TextOptions = visual.TextOptions with { BaselinePixelAlignment = value };
}
}
}
Loading