mirror of
https://github.com/ZuneDev/MicrosoftIris.git
synced 2026-07-27 13:13:29 -07:00
Start text abstraction
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user