Start text abstraction

This commit is contained in:
Yoshi Askharoun
2026-07-27 11:00:37 -05:00
parent 272cc14649
commit aedbd7bf6b
24 changed files with 1058 additions and 80 deletions
+19 -15
View File
@@ -6,6 +6,7 @@
using Microsoft.Iris.OS;
using Microsoft.Iris.Render;
using Microsoft.Iris.Render.Text;
using Microsoft.Iris.RenderAPI;
using Microsoft.Iris.RenderAPI.Drawing;
using Microsoft.Iris.Session;
@@ -22,6 +23,15 @@ namespace Microsoft.Iris.Drawing
public const float MaxWidthConstraint = 4095f;
public const float MaxHeightConstraint = 8191f;
private Win32Api.HANDLE _rtoHandle;
// Bound to _rtoHandle: routes SetContent/GetSimpleContent/GetNaturalBounds
// through the cross-platform Microsoft.Iris.Render.Text.TextDocument
// abstraction. Measure/Rasterize stay on NativeApi.SpRichText* directly
// below - TextMeasureParams' multi-range formatting doesn't fit this
// simplified single-style abstraction without losing fidelity. See
// logs/ for the rationale. Only meaningful on Windows: RichText's own
// constructor is native-only already (SpRichTextBuildObject), so this
// instance is always the Sp-backed implementation in practice.
private readonly TextDocument _textDocument;
private NativeApi.ReportRunCallback _rrcb;
private string _currentlyMeasuringText;
private bool _oversampled;
@@ -48,6 +58,7 @@ namespace Microsoft.Iris.Drawing
_timerTickHandler = new EventHandler(OnTimerTick);
}
RendererApi.IFC(NativeApi.SpRichTextBuildObject(richTextMode, sizeMaximumSurface, callbacks, out _rtoHandle));
_textDocument = new SpTextDocument(_rtoHandle);
_oversampled = false;
_lock = new object();
_rrcb = new NativeApi.ReportRunCallback(OnReportRun);
@@ -62,6 +73,7 @@ namespace Microsoft.Iris.Drawing
GC.SuppressFinalize(this);
lock (_lock)
{
_textDocument.Dispose();
NativeApi.SpRichTextDestroyObject(_rtoHandle);
_rtoHandle.h = IntPtr.Zero;
}
@@ -79,7 +91,7 @@ namespace Microsoft.Iris.Drawing
set
{
lock (_lock)
RendererApi.IFC(NativeApi.SpRichTextSetContent(_rtoHandle, value));
RendererApi.IFC(new HRESULT(_textDocument.SetContent(value).Int));
}
}
@@ -87,19 +99,11 @@ namespace Microsoft.Iris.Drawing
{
get
{
string str = null;
int textLength = 0;
lock (_lock)
{
RendererApi.IFC(NativeApi.SpRichTextGetSimpleContentLength(_rtoHandle, out textLength));
if (textLength != 0)
{
StringBuilder textBuffer = new StringBuilder(textLength);
RendererApi.IFC(NativeApi.SpRichTextGetSimpleContent(_rtoHandle, textBuffer, textBuffer.Capacity));
str = textBuffer.ToString();
}
RendererApi.IFC(new HRESULT(_textDocument.GetSimpleContent(out var content).Int));
return content;
}
return str;
}
}
@@ -153,11 +157,11 @@ namespace Microsoft.Iris.Drawing
public Size GetNaturalBounds()
{
int cWidth;
int cHeight;
lock (_lock)
RendererApi.IFC(NativeApi.SpRichTextGetNaturalBounds(_rtoHandle, out cWidth, out cHeight));
return new Size(cWidth, cHeight);
{
RendererApi.IFC(new HRESULT(_textDocument.GetNaturalBounds(out var bounds).Int));
return bounds;
}
}
public void SetSelectionRange(int selectionStart, int selectionEnd)
+41 -47
View File
@@ -1,53 +1,43 @@
// Decompiled with JetBrains decompiler
// Based on decompilation with JetBrains decompiler
// Type: Microsoft.Iris.Drawing.SimpleText
// Assembly: UIX, Version=4.8.0.0, Culture=neutral, PublicKeyToken=ddd0da4d3e678217
// MVID: A56C6C9D-B7F6-46A9-8BDE-B3D9B8D60B11
// Assembly location: C:\Program Files\Zune\UIX.dll
using Microsoft.Iris.OS;
using Microsoft.Iris.Render;
using Microsoft.Iris.RenderAPI;
using Microsoft.Iris.Session;
using Microsoft.Iris.Render.Text;
using Microsoft.Iris.ViewItems;
using System;
namespace Microsoft.Iris.Drawing
{
// Uses the cross-platform TextDocument abstraction (Microsoft.Iris.Render.Text)
// instead of calling NativeApi.SpSimpleText* directly, so this class works on
// any platform: TextDocumentFactory.CreateStandalone() resolves to the native
// SpTextDocument on Windows and to a SixLabors.Fonts-backed TextDocument
// elsewhere.
internal class SimpleText : IDisposable
{
private Win32Api.HANDLE _stoHandle;
private readonly TextDocument _document;
public SimpleText()
{
Size sizeMaximumSurface = Size.Zero;
if (UISession.Default != null)
sizeMaximumSurface = UIImage.MaximumSurfaceSize(UISession.Default);
RendererApi.IFC(NativeApi.SpSimpleTextBuildObject(sizeMaximumSurface, out _stoHandle));
_document = TextDocumentFactory.CreateStandalone();
}
public void Dispose()
{
GC.SuppressFinalize(this);
NativeApi.SpSimpleTextDestroyObject(_stoHandle);
_stoHandle = Win32Api.HANDLE.NULL;
_document.Dispose();
}
public unsafe bool CanMeasure(string content, TextStyle textStyle)
public bool CanMeasure(string content, TextStyle textStyle)
{
bool fPossible;
fixed (char* chPtr = textStyle.TruncatedFontFace)
{
var style = new TextStyle.MarshalledData(textStyle)
{
_fontFace = chPtr
};
RendererApi.IFC(NativeApi.SpSimpleTextMeasurePossible(_stoHandle, content, &style, out fPossible));
}
return fPossible;
_document.MeasurePossible(content, ToStyleInfo(textStyle), out var possible);
return possible;
}
public unsafe TextFlow Measure(
public TextFlow Measure(
string content,
LineAlignment alignment,
TextStyle textStyle,
@@ -56,33 +46,37 @@ namespace Microsoft.Iris.Drawing
TextFlow textFlow = new TextFlow();
if (content == null)
content = string.Empty;
short wAlignment = 0;
switch (alignment)
var textAlignment = alignment switch
{
case LineAlignment.Near:
wAlignment = 1;
break;
case LineAlignment.Center:
wAlignment = 3;
break;
case LineAlignment.Far:
wAlignment = 2;
break;
}
IntPtr hGlyphRunInfo;
NativeApi.RasterizeRunPacket rasterizeRunPacket;
fixed (char* chPtr = textStyle.TruncatedFontFace)
LineAlignment.Near => TextAlignment.Near,
LineAlignment.Center => TextAlignment.Center,
LineAlignment.Far => TextAlignment.Far,
_ => TextAlignment.Near,
};
var hresult = _document.Measure(content, textAlignment, ToStyleInfo(textStyle), constraint, out var glyphRunInfo);
if (hresult.IsSuccess() && glyphRunInfo != null)
{
var style = new TextStyle.MarshalledData(textStyle)
{
_fontFace = chPtr
};
RendererApi.IFC(NativeApi.SpSimpleTextMeasure(_stoHandle, content, wAlignment,
&style, constraint, out hGlyphRunInfo, &rasterizeRunPacket));
var run = TextRun.FromGlyphRunInfo(glyphRunInfo, _document);
textFlow.Add(run);
}
TextRun run = TextRun.FromRunPacket(hGlyphRunInfo, &rasterizeRunPacket, content);
textFlow.Add(run);
return textFlow;
}
private static TextStyleInfo ToStyleInfo(TextStyle textStyle) => new()
{
FontFace = textStyle.FontFace,
FontSize = textStyle.FontSize,
AltFontSize = textStyle.AltFontSize,
Bold = textStyle.Bold,
Italic = textStyle.Italic,
Underline = textStyle.Underline,
Color = textStyle.Color.RenderConvert(),
HasColor = textStyle.HasColor,
LineSpacing = textStyle.LineSpacing,
CharacterSpacing = textStyle.CharacterSpacing,
EnableKerning = textStyle.EnableKerning,
};
}
}
@@ -0,0 +1,40 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.Iris.OS;
using Microsoft.Iris.Render.Internal;
using Microsoft.Iris.Render.Text;
namespace Microsoft.Iris.Drawing;
// Windows backend for Microsoft.Iris.Render.Text.FontResource, living here
// (rather than alongside the abstract class in UIX.RenderApi) so it can reuse
// Microsoft.Iris.OS.NativeApi's existing P/Invoke declarations instead of
// duplicating them in an assembly NativeApi isn't visible from. Registered
// with FontResourceLoader by TextBackendRegistration.
internal sealed class SpFontResource : FontResource
{
[DllImport("gdi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int AddFontResourceExW(string lpszFilename, uint fl, IntPtr pdv);
private const uint FR_PRIVATE = 0x10;
public override HRESULT LoadFile(string filename)
{
var addedCount = AddFontResourceExW(filename, FR_PRIVATE, IntPtr.Zero);
return addedCount > 0 ? HRESULT.S_OK : HRESULT.E_FAIL;
}
public override HRESULT LoadResource(string moduleName, string resourceId) =>
NativeApi.SpLoadFontResource(moduleName, resourceId) ? HRESULT.S_OK : HRESULT.E_FAIL;
public override HRESULT LoadBuffer(IntPtr pvSrc, int cbSize)
{
uint cFonts = 0;
var hFont = Win32Api.AddFontMemResourceEx(pvSrc, (uint)cbSize, IntPtr.Zero, ref cFonts);
return hFont != IntPtr.Zero ? HRESULT.S_OK : HRESULT.E_FAIL;
}
public override void Dispose()
{
}
}
@@ -0,0 +1,247 @@
using System;
using System.Text;
using Microsoft.Iris.OS;
using Microsoft.Iris.Render;
using Microsoft.Iris.RenderAPI;
using Microsoft.Iris.Render.Text;
using Microsoft.Iris.Session;
using Microsoft.Iris.ViewItems;
using Size = Microsoft.Iris.Render.Size;
using Color = Microsoft.Iris.Drawing.Color;
using HRESULT = Microsoft.Iris.Render.Internal.HRESULT;
namespace Microsoft.Iris.Drawing;
// Windows backend for Microsoft.Iris.Render.Text.TextDocument - see the
// remark on SpFontResource for why this lives in UIX rather than
// UIX.RenderApi. Registered with TextDocumentFactory by TextBackendRegistration.
//
// Wraps one of the two native text object families NativeApi exposes:
// - "standalone" wraps its own SpSimpleTextBuildObject handle (STO), exactly
// what Microsoft.Iris.Drawing.SimpleText used before this refactor. Only
// Measure/MeasurePossible/Rasterize are meaningful for an STO.
// - "bound" is constructed with an existing RichText RTO handle so
// SetContent/GetSimpleContent/GetNaturalBounds run against RichText's own
// session. Measure/MeasurePossible are NOT implemented in bound mode:
// RichText.Measure needs TextMeasureParams' multi-range formatting, which
// this simplified single-style abstraction can't safely replicate without
// regressing existing Windows behavior - RichText keeps calling
// NativeApi.SpRichTextMeasure directly for that. See logs/ for the
// rationale.
internal sealed class SpTextDocument : TextDocument
{
private Win32Api.HANDLE _handle;
private readonly bool _bound;
public SpTextDocument()
{
var sizeMaximumSurface = Size.Zero;
if (UISession.Default != null)
sizeMaximumSurface = UIImage.MaximumSurfaceSize(UISession.Default);
RendererApi.IFC(NativeApi.SpSimpleTextBuildObject(sizeMaximumSurface, out _handle));
_bound = false;
}
public SpTextDocument(Win32Api.HANDLE boundRtoHandle)
{
_handle = boundRtoHandle;
_bound = true;
}
public override HRESULT SetContent(string content)
{
RequireBound();
return NativeApi.SpRichTextSetContent(_handle, content).Int;
}
public override HRESULT GetSimpleContent(out string content)
{
RequireBound();
var hresult = NativeApi.SpRichTextGetSimpleContentLength(_handle, out var textLength);
if (!hresult.IsSuccess() || textLength == 0)
{
content = null;
return hresult.Int;
}
var textBuffer = new StringBuilder(textLength);
hresult = NativeApi.SpRichTextGetSimpleContent(_handle, textBuffer, textBuffer.Capacity);
content = hresult.IsSuccess() ? textBuffer.ToString() : null;
return hresult.Int;
}
public override HRESULT GetNaturalBounds(out Size bounds)
{
RequireBound();
var hresult = NativeApi.SpRichTextGetNaturalBounds(_handle, out var cWidth, out var cHeight);
bounds = hresult.IsSuccess() ? new Size(cWidth, cHeight) : Size.Zero;
return hresult.Int;
}
public override unsafe HRESULT MeasurePossible(string content, TextStyleInfo style, out bool possible)
{
RequireStandalone();
fixed (char* facePtr = TruncateFontFace(style?.FontFace))
{
var marshalled = ToMarshalledData(style, facePtr);
return NativeApi.SpSimpleTextMeasurePossible(_handle, content, &marshalled, out possible).Int;
}
}
public override unsafe HRESULT Measure(string content, TextAlignment alignment, TextStyleInfo style, Size constraint, out GlyphRunInfo glyphRun)
{
RequireStandalone();
content ??= string.Empty;
short wAlignment = alignment switch
{
TextAlignment.Near => 1,
TextAlignment.Center => 3,
TextAlignment.Far => 2,
_ => 0,
};
IntPtr hGlyphRunInfo;
NativeApi.RasterizeRunPacket rasterizeRunPacket;
Microsoft.Iris.RenderAPI.HRESULT hresult;
fixed (char* facePtr = TruncateFontFace(style?.FontFace))
{
var marshalled = ToMarshalledData(style, facePtr);
hresult = NativeApi.SpSimpleTextMeasure(_handle, content, wAlignment, &marshalled, constraint, out hGlyphRunInfo, &rasterizeRunPacket);
}
if (!hresult.IsSuccess())
{
glyphRun = null;
return hresult.Int;
}
glyphRun = FromRasterizeRunPacket(hGlyphRunInfo, ref rasterizeRunPacket, content);
return HRESULT.S_OK;
}
public override unsafe HRESULT Rasterize(GlyphRunInfo glyphRun, ColorF textColor, bool outline, bool shadow, out RasterizedGlyphBitmap bitmap)
{
if (glyphRun?.BackendHandle is not IntPtr hGlyphRunInfo)
{
bitmap = null;
return HRESULT.E_FAIL;
}
var textColorArgb = new Color(textColor.A, textColor.R, textColor.G, textColor.B);
var hresult = NativeApi.SpRichTextRasterize(hGlyphRunInfo, outline ? 1 : 0, textColorArgb, shadow ? 1 : 0,
out var phTextBitmap, out var ppvBits, out var psizeBitmap);
if (!hresult.IsSuccess())
{
bitmap = null;
return hresult.Int;
}
bitmap = new RasterizedGlyphBitmap(() => NativeApi.SpFreeDib(phTextBitmap))
{
Size = psizeBitmap,
Bits = ppvBits,
NativeHandle = phTextBitmap,
};
return HRESULT.S_OK;
}
public override void Dispose()
{
if (_bound)
return;
if (_handle == Win32Api.HANDLE.NULL)
return;
NativeApi.SpSimpleTextDestroyObject(_handle);
_handle = Win32Api.HANDLE.NULL;
}
private void RequireBound()
{
if (!_bound)
throw new NotSupportedException("Standalone (SimpleText) text documents have no persistent content buffer.");
}
private void RequireStandalone()
{
if (_bound)
throw new NotSupportedException("Bound (RichText) text documents don't support single-style Measure - RichText.Measure uses NativeApi.SpRichTextMeasure directly to keep multi-range formatting.");
}
private static GlyphRunInfo FromRasterizeRunPacket(IntPtr hGlyphRunInfo, ref NativeApi.RasterizeRunPacket run, string content)
{
return new GlyphRunInfo(handle => NativeApi.SpRichTextDestroyGlyphRunInfo((IntPtr)handle))
{
Content = content,
LayoutBounds = run.rcLayoutBounds,
RenderBoundsX = run.rcfRenderBounds.X,
RenderBoundsY = run.rcfRenderBounds.Y,
RenderBoundsWidth = run.rcfRenderBounds.Width,
RenderBoundsHeight = run.rcfRenderBounds.Height,
NaturalExtent = run.sizeNatural,
NaturalX = run.naturalX,
NaturalY = run.naturalY,
RasterizeX = run.rasterizeX,
RasterizeY = run.rasterizeY,
RasterizerConfig = run.AAConfig,
RunColor = run.clrText.RenderConvert(),
HighlightColor = run.clrBackground.RenderConvert(),
FontFaceUniqueId = run.fontFaceUniqueId,
FontHeight = run.lf.lfHeight,
FontWeight = run.lf.lfWeight,
Italic = run.lf.lfItalic != 0,
Underline = run.lf.lfUnderline != 0,
Link = (run.dwEffects & 32) != 0,
UnderlineStyle = (Microsoft.Iris.Render.Text.UnderlineStyle)run.usUnderlineStyle,
UnderlineBounds = run.rcUnderlineBounds,
Line = run.nLineNumber,
AscenderInset = run.ascenderInset,
BaselineInset = run.baselineInset,
BackendHandle = hGlyphRunInfo,
};
}
private static string TruncateFontFace(string fontFace)
{
fontFace ??= string.Empty;
return fontFace.Length < 32 ? fontFace : fontFace.Substring(0, 31);
}
private static unsafe TextStyle.MarshalledData ToMarshalledData(TextStyleInfo style, char* fontFacePtr)
{
style ??= new TextStyleInfo();
var flags = TextStyle.SetFlags.FontFace | TextStyle.SetFlags.FontHeight | TextStyle.SetFlags.Bold |
TextStyle.SetFlags.Italic | TextStyle.SetFlags.Underline | TextStyle.SetFlags.LineSpacing |
TextStyle.SetFlags.EnableKerning | TextStyle.SetFlags.CharacterSpacing;
if (style.HasColor)
flags |= TextStyle.SetFlags.TextColor;
if (style.AltFontSize != 0)
flags |= TextStyle.SetFlags.AltFontHeight;
if (style.Bold)
flags |= TextStyle.SetFlags.BoldValue;
if (style.Italic)
flags |= TextStyle.SetFlags.ItalicValue;
if (style.Underline)
flags |= TextStyle.SetFlags.UnderlineValue;
if (style.EnableKerning)
flags |= TextStyle.SetFlags.EnableKerningValue;
return new TextStyle.MarshalledData
{
_flags = (int)flags,
_fontFace = fontFacePtr,
_fontHeightPts = style.FontSize,
_altFontHeightPts = style.AltFontSize,
_lineSpacing = style.LineSpacing,
_characterSpacing = style.CharacterSpacing,
_textColor = new Color(style.Color.A, style.Color.R, style.Color.G, style.Color.B),
};
}
}
+65 -3
View File
@@ -7,6 +7,7 @@
using Microsoft.Iris.Library;
using Microsoft.Iris.OS;
using Microsoft.Iris.Render;
using Microsoft.Iris.Render.Text;
using Microsoft.Iris.RenderAPI.Drawing;
using System;
@@ -17,6 +18,9 @@ namespace Microsoft.Iris.Drawing
private const int CFE_LINK = 32;
private IntPtr _hGlyphRunInfo;
private IntPtr _hRasterizeRunPacket;
private readonly GlyphRunInfo _glyphRunInfo;
private readonly TextDocument _owner;
private NativeApi.UnderlineStyle _underlineStyleManaged;
private Rectangle _layoutBounds;
private RectangleF _renderBounds;
private Point _offsetPoint;
@@ -63,7 +67,7 @@ namespace Microsoft.Iris.Drawing
_lfWeight = runPacketPtr->lf.lfWeight;
SetBit(Bits.Italic, runPacketPtr->lf.lfItalic != 0);
SetBit(Bits.Underline, runPacketPtr->lf.lfUnderline != 0);
SetBit(Bits.Link, (runPacketPtr->dwEffects & 32) != 0);
SetBit(Bits.Link, (runPacketPtr->dwEffects & CFE_LINK) != 0);
_underlineBounds = runPacketPtr->rcUnderlineBounds;
_lineNumber = runPacketPtr->nLineNumber;
_naturalRunExtent = runPacketPtr->sizeNatural;
@@ -72,9 +76,46 @@ namespace Microsoft.Iris.Drawing
_content = content;
}
// Used for runs measured through the cross-platform TextDocument
// abstraction (currently only Microsoft.Iris.Drawing.SimpleText) rather
// than a raw native RasterizeRunPacket.
private TextRun(GlyphRunInfo info, TextDocument owner)
{
_glyphRunInfo = info;
_owner = owner;
_layoutBounds = info.LayoutBounds;
_renderBounds = new RectangleF(info.RenderBoundsX, info.RenderBoundsY, info.RenderBoundsWidth, info.RenderBoundsHeight);
_naturalX = info.NaturalX;
_naturalY = info.NaturalY;
_rasterizeX = info.RasterizeX;
_rasterizeY = info.RasterizeY;
_rasterizerConfig = info.RasterizerConfig;
_runColor = FromColorF(info.RunColor);
_overrideColor = Color.Transparent;
_highlightColor = FromColorF(info.HighlightColor);
_fontFaceUniqueId = info.FontFaceUniqueId;
_lfHeight = info.FontHeight;
_lfWeight = info.FontWeight;
SetBit(Bits.Italic, info.Italic);
SetBit(Bits.Underline, info.Underline);
SetBit(Bits.Link, info.Link);
_underlineBounds = info.UnderlineBounds;
_underlineStyleManaged = (NativeApi.UnderlineStyle)info.UnderlineStyle;
_lineNumber = info.Line;
_naturalRunExtent = info.NaturalExtent;
_ascenderInset = info.AscenderInset;
_baselineInset = info.BaselineInset;
_content = info.Content;
}
private static Color FromColorF(ColorF color) => new(color.A, color.R, color.G, color.B);
protected override void OnDispose()
{
NativeApi.SpRichTextDestroyGlyphRunInfo(_hGlyphRunInfo);
if (_glyphRunInfo != null)
_glyphRunInfo.Dispose();
else
NativeApi.SpRichTextDestroyGlyphRunInfo(_hGlyphRunInfo);
base.OnDispose();
}
@@ -140,9 +181,19 @@ namespace Microsoft.Iris.Drawing
public unsafe NativeApi.UnderlineStyle UnderlineStyle
{
get => _hRasterizeRunPacket == IntPtr.Zero ? NativeApi.UnderlineStyle.None : ((NativeApi.RasterizeRunPacket*)(void*)_hRasterizeRunPacket)->usUnderlineStyle;
get
{
if (_glyphRunInfo != null)
return _underlineStyleManaged;
return _hRasterizeRunPacket == IntPtr.Zero ? NativeApi.UnderlineStyle.None : ((NativeApi.RasterizeRunPacket*)(void*)_hRasterizeRunPacket)->usUnderlineStyle;
}
set
{
if (_glyphRunInfo != null)
{
_underlineStyleManaged = value;
return;
}
if (!(_hRasterizeRunPacket != IntPtr.Zero))
return;
((NativeApi.RasterizeRunPacket*)(void*)_hRasterizeRunPacket)->usUnderlineStyle = value;
@@ -171,6 +222,15 @@ namespace Microsoft.Iris.Drawing
bool shadowMode = false;
if (samplingMode == "sdw")
shadowMode = true;
if (_glyphRunInfo != null)
{
var hresult = _owner.Rasterize(_glyphRunInfo, textColor.RenderConvert(), outlineFlag, shadowMode, out var bitmap);
return hresult.IsSuccess() && bitmap != null
? new Dib(bitmap.NativeHandle, bitmap.Bits, bitmap.Size, bitmap.Dispose)
: null;
}
return RichText.Rasterize(_hGlyphRunInfo, outlineFlag, textColor, shadowMode);
}
@@ -182,6 +242,8 @@ namespace Microsoft.Iris.Drawing
return new TextRun(hGlyphRunInfo, runPacketPtr, content);
}
internal static TextRun FromGlyphRunInfo(GlyphRunInfo info, TextDocument owner) => new(info, owner);
public void RemoveSprites(IVisualContainer container)
{
if (TextSprite != null)
+6 -9
View File
@@ -8,6 +8,7 @@ using Microsoft.Iris.Data;
using Microsoft.Iris.Drawing;
using Microsoft.Iris.Library;
using Microsoft.Iris.OS;
using Microsoft.Iris.Render.Text;
using Microsoft.Iris.Session;
namespace Microsoft.Iris.Markup.UIX
@@ -217,27 +218,23 @@ namespace Microsoft.Iris.Markup.UIX
if (string.IsNullOrEmpty(resourceName))
ErrorManager.ReportError("Script runtime failure: Invalid 'null' value for '{0}'", "resourceName");
bool error;
bool loaded;
// UIXRender doesn't know how to load resources from .NET assemblies, so we'll
// call the relevant GDI API manually for CLR DLLs.
// read the bytes ourselves for CLR DLLs and hand them to the platform's font loader.
var assemblyName = System.IO.Path.GetFileNameWithoutExtension(moduleName);
if (ClrDllResources.Instance.TryGetResource($"{assemblyName}!{resourceName}", $"clr-res://{assemblyName}", true, out var resource))
{
resource.Acquire();
uint cFonts = 0;
var hFont = Win32Api.AddFontMemResourceEx(resource.Buffer, resource.Length, System.IntPtr.Zero, ref cFonts);
error = hFont == System.IntPtr.Zero;
loaded = FontResourceLoader.LoadFromBuffer(resource.Buffer, (int)resource.Length);
}
else
{
error = !NativeApi.SpLoadFontResource(moduleName, resourceName);
loaded = FontResourceLoader.LoadFromModuleResource(moduleName, resourceName);
}
if (error)
if (!loaded)
ErrorManager.ReportError("Font Resource {1} not found in module {0}", moduleName, resourceName);
return null;
@@ -0,0 +1,22 @@
using System.Runtime.CompilerServices;
using Microsoft.Iris.Drawing;
using Microsoft.Iris.Render.Text;
namespace Microsoft.Iris.OS;
// Registers the native Sp*/NativeApi-backed text/font implementations with
// UIX.RenderApi's factories. Gated on WINDOWS so non-Windows builds of this
// assembly (UIX also targets plain net8.0, see Directory.Build.props) leave
// nothing registered and FontResourceLoader/TextDocumentFactory fall back to
// their SixLabors.Fonts-based defaults.
internal static class TextBackendRegistration
{
[ModuleInitializer]
internal static void Register()
{
#if WINDOWS
FontResourceLoader.RegisterWindowsBackend(() => new SpFontResource());
TextDocumentFactory.RegisterWindowsBackend(() => new SpTextDocument());
#endif
}
}
+17 -4
View File
@@ -15,12 +15,22 @@ namespace Microsoft.Iris.RenderAPI.Drawing
private IntPtr m_hdib;
private IntPtr m_prgbData;
private Size m_sizePxl;
private Action m_onDispose;
public Dib(IntPtr hdib, IntPtr prgbData, Size sizePxl)
: this(hdib, prgbData, sizePxl, null)
{
}
// onDispose lets non-native-backed bitmaps (e.g. from a cross-platform
// TextDocument backend, which has no hdib to free via SpFreeDib) supply
// their own cleanup for the memory prgbData points to.
public Dib(IntPtr hdib, IntPtr prgbData, Size sizePxl, Action onDispose)
{
m_hdib = hdib;
m_prgbData = prgbData;
m_sizePxl = sizePxl;
m_onDispose = onDispose;
}
~Dib() => Dispose(false);
@@ -33,11 +43,14 @@ namespace Microsoft.Iris.RenderAPI.Drawing
private void Dispose(bool fInDispose)
{
if (!(m_hdib != IntPtr.Zero))
return;
NativeApi.SpFreeDib(m_hdib);
m_hdib = IntPtr.Zero;
if (m_hdib != IntPtr.Zero)
{
NativeApi.SpFreeDib(m_hdib);
m_hdib = IntPtr.Zero;
}
m_prgbData = IntPtr.Zero;
m_onDispose?.Invoke();
m_onDispose = null;
}
public Size ContentSize => m_sizePxl;
+6 -1
View File
@@ -107,7 +107,12 @@ namespace Microsoft.Iris.UI
public virtual void WriteSymbol(SymbolReference symbolRef, object value) => SetProperty(symbolRef.Symbol, value);
public virtual object GetProperty(string name) => _storage[name];
public virtual object GetProperty(string name)
{
if (!_storage.ContainsKey(name))
return null;
return _storage[name];
}
public virtual void SetProperty(string name, object value)
{