Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Core / CSharp / MS / Internal / FontCache / GlyphElement.cs / 1 / GlyphElement.cs
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Security; using System.Security.Permissions; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Media; using MS.Internal; using MS.Internal.FontFace; using MS.Utility; using MS.Internal.FontRasterization; using Adobe.CffRasterizer; using System.Windows.Media.Composition; using MS.Internal.PresentationCore; using UnsafeNativeMethods = MS.Win32.PresentationCore.UnsafeNativeMethods; // Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 namespace MS.Internal.FontCache { ////// Glyph element class. /// Layout is: /// glyph block structure | file name string /// [FriendAccessAllowed] internal abstract class BaseGlyphElement : IFontCacheElement, IDisposable { [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct GlyphBlock { internal int faceIndex; internal int cbFileNameLength; internal ushort renderingFlags; internal ushort baseglyphIndex; ////// Critical - as this accesses unsafe code blocks /// TreatAsSafe - as this does not return any unsafe pointers and is used to compare two blocks /// [SecurityCritical, SecurityTreatAsSafe] internal unsafe bool Equal(GlyphBlock* rhs) { Invariant.Assert(rhs != null); return this.faceIndex == rhs->faceIndex && this.cbFileNameLength == rhs->cbFileNameLength && this.baseglyphIndex == rhs->baseglyphIndex && this.renderingFlags == rhs->renderingFlags; } } protected BaseGlyphElement(int glyphsPerBlock) { _glyphsPerBlock = glyphsPerBlock; Reset(); Debug.Assert(_key.baseglyphIndex != GetBaseGlyph(_key.baseglyphIndex)); } ////// Critical - as this calls GetUriString /// TreatAsSafe - as this does not uses the result only to compute the string length. /// [SecurityCritical, SecurityTreatAsSafe] protected BaseGlyphElement(int glyphsPerBlock, int faceIndex, FontSource fontSource, ushort renderingFlags) : this(glyphsPerBlock) { _fontSource = fontSource; _key.renderingFlags = renderingFlags; _key.faceIndex = faceIndex; unsafe { _key.cbFileNameLength = FontSource.GetUriString().Length * sizeof(char); } } ////// Critical as this calls _fontStream.Close(), which is a file operation. /// TreatAsSafe as it only releases the file handle. /// [SecurityCritical, SecurityTreatAsSafe] public void Dispose() { if (_rasterizer != null) { _rasterizer.Dispose(); _rasterizer = null; } if (_fontStream != null) { _fontStream.Close(); _fontStream = null; } } ////// Critical - as this accesses _glyphdata which is a pointer /// TreatAsSafe: This does not expose the data and simply resets the stream /// [SecurityCritical, SecurityTreatAsSafe] internal void Reset() { unsafe { _glyphData = null; } _cacher = null; _key.baseglyphIndex = 1; } internal FontSource FontSource { get { return _fontSource; } } internal int FaceIndex { get { return _key.faceIndex; } } internal RenderingFlags RenderingFlags { get { return (RenderingFlags)_key.renderingFlags; } } internal bool IsTrueType { get { return (_key.renderingFlags & (ushort)MilGlyphRun.IsTrueType) != 0; } } ////// Prevent JIT from inlining this method, so that PresentationCFFRasterizer.dll and PresentationCFFRasterizerNative.dll are loaded on demand. /// ////// Critical - This method calls critical code (OTFRasterizer()) /// Safe - This method doesn't expose any ciritical information. /// [MethodImpl(MethodImplOptions.NoInlining)] [SecurityCritical, SecurityTreatAsSafe] protected void CreateOtfRasterizer() { _rasterizer = new OTFRasterizer(); } ////// Critical - as this accesses unsafe code blocks and allocates an object that can hold unmanaged code and returns it /// [SecurityCritical] protected unsafe void* Allocate(int size) { int newOffset = _cacher.Alloc(size); return _cacher[newOffset]; } ////// Critical: This code acceses unsafe code and is used to allocate memory. It returns a pointer /// [SecurityCritical] protected unsafe void* AllocateNoThrow(int size) { try { return Allocate(size); } catch (FontCacheFullException) { return null; } } ////// Obtains a base glyph index for this cache element /// ///The base glyph index internal ushort GetBaseGlyph() { return _key.baseglyphIndex; } internal void SetBaseGlyph(ushort glyphIndex) { Debug.Assert(glyphIndex == GetBaseGlyph(glyphIndex)); _key.baseglyphIndex = glyphIndex; } internal ushort GetBaseGlyph(ushort glyphIndex) { return (ushort)(glyphIndex - glyphIndex % _glyphsPerBlock); } ////// Critical - as this gives out glyphstream /// [SecurityCritical] protected unsafe int* GlyphEntry(ushort glyphIndex) { Invariant.Assert(GetBaseGlyph(glyphIndex) == _key.baseglyphIndex); return _glyphData + (glyphIndex - _key.baseglyphIndex); } ////// Returns whether a given glyph is cached /// /// Glyph index ///true if the glyph is cached, false otherwise ////// Critical - as this accesses unsafe code blocks /// TreatAsSafe - as this does not return the stream /// [SecurityCritical, SecurityTreatAsSafe] internal bool IsGlyphCached(ushort glyphIndex) { unsafe { return *GlyphEntry(glyphIndex) != Util.nullOffset; } } ////// Critical - as this accesses unsafe code blocks and returns a glyph pointer /// [SecurityCritical] internal unsafe void* GetGlyph(ushort glyphIndex) { Invariant.Assert(IsGlyphCached(glyphIndex)); int offset = *GlyphEntry(glyphIndex); return _cacher[offset]; } ////// Critical - as this accesses unsafe code blocks and sets data from a pointer /// [SecurityCritical] protected unsafe void SetGlyph(ushort glyphIndex, void* data) { *GlyphEntry(glyphIndex) = _cacher[(byte*)data]; Debug.Assert(IsGlyphCached(glyphIndex)); } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: The calls to probe is bounds checked /// and the usage of elementcacher and checkedpointer are tracked. /// [SecurityCritical, SecurityTreatAsSafe] protected void InitGlyphData(CheckedPointer p, ElementCacher cacher) { unsafe { _glyphData = (int*)p.Probe(0, _glyphsPerBlock * sizeof(int)); Util.FillMemory(_glyphData, _glyphsPerBlock * sizeof(int), Util.nullOffset); } _cacher = cacher; } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: The calls to probe is bounds checked /// and the usage of elementcacher and checkedpointer are tracked. /// [SecurityCritical, SecurityTreatAsSafe] protected void SetGlyphData(CheckedPointer p, ElementCacher cacher) { unsafe { _glyphData = (int*)p.Probe(0, _glyphsPerBlock * sizeof(int)); } _cacher = cacher; } ////// Critical - as this gives out glyph data in the form of a pointer /// internal unsafe void* GlyphData { [SecurityCritical] get { return _glyphData; } } ////// Adds glyph to the cache /// /// Index of the glyph ///Whether the glyph index was valid for this font. ////// Critical - as this results in the rasterizer accessing font stream. /// TreatAsSafe - as this results in obtaining glyph data that the client is supposed to see anyway. /// [SecurityCritical, SecurityTreatAsSafe] internal abstract bool AddGlyph(ushort glyphIndex); ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: Probe is bounds checked and validates pointer. /// [SecurityCritical, SecurityTreatAsSafe] public virtual bool Match(CheckedPointer p) { unsafe { GlyphBlock* rhs = (GlyphBlock*)p.Probe(0, sizeof(GlyphBlock)); if (!_key.Equal(rhs)) return false; return Util.StringEqualIgnoreCase(p + sizeof(GlyphBlock), FontSource.GetUriString()); } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// [SecurityCritical] public virtual void RetrieveKey(CheckedPointer m) { unsafe { _key = *(GlyphBlock*)m.Probe(0, sizeof(GlyphBlock)); // Validate the base glyph index now so that malformed miss reports // don't trip Invarient.Assert() later when we try to construct the element. if (GetBaseGlyph(_key.baseglyphIndex) != _key.baseglyphIndex) throw new ArgumentOutOfRangeException(); // As the name size could come from arbitrary code // sending miss reports to the font cache server, // we must validate it so that Util.StringCopy() doesn't // call Invariant::Assert() if nameSize is invalid. if ((_key.cbFileNameLength < 0) || ((_key.cbFileNameLength % 2) != 0)) throw new ArgumentOutOfRangeException(); string fileName = Util.StringCopyFromCheckedPointer(m + sizeof(GlyphBlock), _key.cbFileNameLength); _fontSource = new FontSource(new Uri(fileName, UriKind.Absolute), false); } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: Probe is type and bounds checked and this functionality is safe to expose /// [SecurityCritical, SecurityTreatAsSafe] internal void StoreKeyInternal(CheckedPointer d, out int realSize) { realSize = InternalGetSize(); unsafe { GlyphBlock* glyphBlock = (GlyphBlock*)d.Probe(0, sizeof(GlyphBlock)); *glyphBlock = _key; Util.StringCopyToCheckedPointer(d + sizeof(GlyphBlock), FontSource.GetUriString()); } } public virtual int Size { get { return InternalGetSize(); } } public virtual bool IsAppSpecific { get { return _fontSource.IsAppSpecific; } } private int InternalGetSize() { unsafe { return sizeof(GlyphBlock) + _key.cbFileNameLength; } } ////// Critical: This code has unsafe code blocks which call into HasMemory. /// TreatAsSafe: This code is safe to call , since the _key variable is always /// pointing to a valid object and passing it to HashMemory which accepts a void pointer /// is safe. /// [SecurityCritical, SecurityTreatAsSafe] public override int GetHashCode() { int hash = 0; unsafe { fixed (GlyphBlock* k = &_key) { hash = HashFn.HashMemory(k, sizeof(GlyphBlock), hash); } } hash = HashFn.HashMultiply(hash) + FontSource.GetHashCode(); return hash; } public abstract void GetData(CheckedPointer p, ElementCacher cacher); public abstract void AddToCache(CheckedPointer p, ElementCacher cacher); public abstract int Type { get;} public abstract void StoreKey(CheckedPointer d, out int realSize); ////// Cached TrueType rasterizer instance /// protected IFontRasterizer _rasterizer; ////// Critical - as this gives out UnmanagedMemoryStream content which is from a file. /// [SecurityCritical] protected UnmanagedMemoryStream _fontStream; ////// The total number of glyphs in the font /// protected ushort _numberOfGlyphs; private int _glyphsPerBlock; ////// Critical - as this gives out glyph data in the form of a pointer /// [SecurityCritical] private unsafe int* _glyphData; // points to bitmap array in the cache private ElementCacher _cacher; private FontSource _fontSource; private GlyphBlock _key; } ////// This structure represents a black and white glyph bitmap. /// In font cache the structure is immediately followed by height*stride bytes of pixel data. /// Empty bitmaps have zero height. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] internal unsafe struct GlyphBitmap { internal int horOriginX; // horizontal X origin of glyph bitmap internal int horOriginY; // horizontal Y origin of glyph bitmap internal int horAdvance; // hinted advance width internal int verOriginX; // vertical X origin of glyph bitmap internal int verOriginY; // vertical Y origin of glyph bitmap internal int verAdvance; // hinted advance height internal int width; // bitmap width in pixels internal int height; // bitmap height in pixels internal int stride; // number of bytes to store one pixel row } ////// Glyph bitmap element class. /// Represents a block of glyph bitmaps /// Layout is: /// base glyph block | bitmap block | table of blockSize pointers to glyph data /// [FriendAccessAllowed] internal sealed class GlyphBitmapElement : BaseGlyphElement { [StructLayout(LayoutKind.Sequential, Pack = 1)] internal unsafe struct BitmapBlock { internal int a00; internal int a01; internal int a10; internal int a11; internal ushort pointSize; internal ushort renderingMode; ////// Critical - as this accesses unsafe code blocks /// TreatAsSafe - as this does not return unsafe information but is used to compare blocks /// [SecurityCritical, SecurityTreatAsSafe] internal bool Equal(BitmapBlock* rhs) { Invariant.Assert(rhs != null); return this.a00 == rhs->a00 && this.a01 == rhs->a01 && this.a10 == rhs->a10 && this.a11 == rhs->a11 && this.pointSize == rhs->pointSize && this.renderingMode == rhs->renderingMode; } } ////// Critical - Calls into the critical RetrieveKey method. /// [SecurityCritical] internal GlyphBitmapElement(CheckedPointer key) : base(BitmapsPerBlock) { RetrieveKey(key); } internal GlyphBitmapElement( int faceIndex, int a00, int a01, int a10, int a11, ushort pointSize, ushort renderingMode, ushort renderingFlags, FontSource fontSource ) : base(BitmapsPerBlock, faceIndex, fontSource, renderingFlags) { _bitmapKey.a00 = a00; _bitmapKey.a01 = a01; _bitmapKey.a10 = a10; _bitmapKey.a11 = a11; _bitmapKey.pointSize = pointSize; _bitmapKey.renderingMode = renderingMode; } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: The call to probe is checked for boundaries and pointer validity. /// This code is safe to call. /// [SecurityCritical, SecurityTreatAsSafe] public override void StoreKey(CheckedPointer d, out int realSize) { Debug.Assert(!FontSource.IsAppSpecific); int baseSize; StoreKeyInternal(d, out baseSize); baseSize = Util.Align4(baseSize); unsafe { realSize = baseSize + sizeof(BitmapBlock); void* dst = d.Probe(baseSize, sizeof(BitmapBlock)); *(BitmapBlock*)dst = _bitmapKey; } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// [SecurityCritical] public override void RetrieveKey(CheckedPointer m) { base.RetrieveKey(m); int baseSize = Util.Align4(base.Size); unsafe { BitmapBlock* s = (BitmapBlock*)m.Probe(baseSize, sizeof(BitmapBlock)); _bitmapKey = *s; } } ////// Critical: This code has unsafe code blocks It calls into HasMemory /// TreatAsSafe: This function is safe to call /// [SecurityCritical, SecurityTreatAsSafe] public override int GetHashCode() { int hash = base.GetHashCode(); unsafe { fixed (BitmapBlock* k = &_bitmapKey) { hash = HashFn.HashMemory(k, sizeof(BitmapBlock), hash); } } return HashFn.HashScramble(hash); } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: CheckedPointer construction is tracked and probe checks for invalid conditions /// [SecurityCritical, SecurityTreatAsSafe] public override bool Match(CheckedPointer p) { if (!base.Match(p)) return false; unsafe { BitmapBlock* b = (BitmapBlock*)p.Probe(Util.Align4(base.Size), sizeof(BitmapBlock)); return _bitmapKey.Equal(b); } } public override void GetData(CheckedPointer p, ElementCacher cacher) { unsafe { SetGlyphData(p + (Util.Align4(base.Size) + sizeof(BitmapBlock)), cacher); } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: Checked Pointer and ElementCacher are safe to work with. Construction is /// tracked for checkedpointer /// [SecurityCritical, SecurityTreatAsSafe] public override void AddToCache(CheckedPointer p, ElementCacher cacher) { int baseSize; StoreKeyInternal(p, out baseSize); baseSize = Util.Align4(baseSize); unsafe { BitmapBlock* b = (BitmapBlock*)p.Probe(baseSize, sizeof(BitmapBlock) + BitmapsPerBlock * sizeof(int)); *b = _bitmapKey; InitGlyphData(p + (baseSize + sizeof(BitmapBlock)), cacher); } } public override int Size { get { unsafe { return Util.Align4(base.Size) + sizeof(BitmapBlock) + BitmapsPerBlock * sizeof(int); } } } public override int Type { get { return 1; } } ////// Critical: This code calls into unsafe code blocks /// TreatAsSafe: This code allocates and returns an IntPtr that points to a bitmap. /// The IntPtr will fail to support any unsafe operations. /// [SecurityCritical, SecurityTreatAsSafe] private IntPtr AllocateBitmap(int size) { unsafe { // Reserve space to copy the bitmap header. byte* buffer = (byte*)AllocateNoThrow(sizeof(GlyphBitmap) + size); if (buffer == null) return IntPtr.Zero; // Pointer the rasterizer to the bitmap storage. return (IntPtr)(buffer + sizeof(GlyphBitmap)); } } //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647 ////// Critical - as this results in the rasterizer accessing font stream. /// TreatAsSafe - as this results in obtaining glyph bitmaps that the client is supposed to see anyway. /// [SecurityCritical, SecurityTreatAsSafe] internal override bool AddGlyph(ushort glyphIndex) { try { if (_rasterizer == null) { if (IsTrueType) _rasterizer = new TrueTypeRasterizer(); else CreateOtfRasterizer(); _fontStream = FontSource.GetUnmanagedStream(); _numberOfGlyphs = _rasterizer.NewFont(_fontStream, FontSource.Uri, FaceIndex); MS.Internal.FontRasterization.Transform tform = new MS.Internal.FontRasterization.Transform(); tform.a00 = _bitmapKey.a00; tform.a01 = _bitmapKey.a01; tform.a10 = _bitmapKey.a10; tform.a11 = _bitmapKey.a11; _rasterizer.NewTransform( _bitmapKey.pointSize, tform, (OverscaleMode)_bitmapKey.renderingMode, RenderingFlags); } if (glyphIndex >= _numberOfGlyphs) return false; MS.Internal.FontRasterization.GlyphBitmap glyphBitmap; GlyphMetrics glyphMetrics; try { _rasterizer.NewGlyph(glyphIndex); _rasterizer.GetBitmap( AllocateBitmap, // allocation delegate IntPtr.Zero, // current buffer -1, // sentinel value that forces the rasterizer to allocate memory even for empty glyphs // we use it to make sure glyph header is allocated out glyphBitmap, out glyphMetrics ); // Check whether glyph bitmap coordinates overflow the 16 bit range that GlyphCache.cs relies on. // Windows Client Task 50200 tracks a more complete solution to this issue that will ensure rasterizers // can display extremely large bitmaps without truncation. if ((short)glyphMetrics.horizontalOrigin.x != glyphMetrics.horizontalOrigin.x || (short)glyphMetrics.horizontalOrigin.y != glyphMetrics.horizontalOrigin.y || (short)glyphMetrics.horizontalAdvance != glyphMetrics.horizontalAdvance || (short)glyphMetrics.verticalOrigin.x != glyphMetrics.verticalOrigin.x || (short)glyphMetrics.verticalOrigin.y != glyphMetrics.verticalOrigin.y || (short)glyphMetrics.verticalAdvance != glyphMetrics.verticalAdvance || (ushort)glyphMetrics.width != glyphMetrics.width || (ushort)glyphMetrics.height != glyphMetrics.height || (ushort)glyphBitmap.stride != glyphBitmap.stride ) { throw new FileFormatException(FontSource.Uri); } if (glyphMetrics.width > (glyphBitmap.stride * 8)) { throw new System.ArgumentException("Rasterizer produced an invalid width", "glyphMetrics.width"); } } catch (FileFormatException) { unsafe { // Create empty bitmap with zero metrics for malformed glyph programs. GlyphBitmap* gb = (GlyphBitmap*)Allocate(sizeof(GlyphBitmap)); // Cache memory is zero initialized by default. Debug.Assert(gb->horOriginX == 0); Debug.Assert(gb->height == 0); SetGlyph(glyphIndex, gb); return true; } } catch (OutOfMemoryException e) { object delegateError = e.Data["GetMemoryDelegateError"]; // In case the exception was raised due to the delegate failure, // throw FontCacheFullException so that we have a chance to renew the cache and retry the request. if (delegateError != null && (bool)delegateError) throw new FontCacheFullException(); throw; } // Glyph rendering code requires stride to be a multiple of 4, // make sure glyph rasterizers do the same. Debug.Assert((glyphBitmap.stride & 3) == 0); unsafe { GlyphBitmap* gd = (GlyphBitmap*)((byte*)glyphBitmap.pixels - sizeof(GlyphBitmap)); gd->horOriginX = glyphMetrics.horizontalOrigin.x; gd->horOriginY = glyphMetrics.horizontalOrigin.y; gd->horAdvance = glyphMetrics.horizontalAdvance; gd->verOriginX = glyphMetrics.verticalOrigin.x; gd->verOriginY = glyphMetrics.verticalOrigin.y; gd->verAdvance = glyphMetrics.verticalAdvance; gd->stride = glyphBitmap.stride; gd->width = glyphMetrics.width; gd->height = glyphMetrics.height; SetGlyph(glyphIndex, gd); } return true; } catch (SEHException e) { throw Util.ConvertInPageException(FontSource, e); } } private BitmapBlock _bitmapKey; private const int BitmapsPerBlock = 128; }; ////// Glyph path element class. /// Represents a block of glyph outlines /// Layout is: /// glyph block structure | file name string | usEmResolution | table of blockSize pointers to glyph data /// [FriendAccessAllowed] internal sealed class GlyphPathElement : BaseGlyphElement { [StructLayout(LayoutKind.Sequential, Pack = 1)] private unsafe struct OutlineBlock { internal ushort designEmHeight; } ////// Outline data is prefixed by a header containing three integers. /// private static readonly int GlyphOutlineHeaderSize = 3 * sizeof(int); /// /// Create a GlyphPathElement for a specific font face /// internal GlyphPathElement(int faceIndex, FontSource fontSource, ushort renderingFlags, ushort designEmHeight) : base(OutlinesPerBlock, faceIndex, fontSource, renderingFlags) { _outlineKey.designEmHeight = designEmHeight; } ////// Critical - Calls into the critical RetrieveKey method. /// [SecurityCritical] internal GlyphPathElement(CheckedPointer key) : base(OutlinesPerBlock) { RetrieveKey(key); } public override int GetHashCode() { int hash = base.GetHashCode(); return HashFn.HashScramble(hash); } public override void GetData(CheckedPointer p, ElementCacher cacher) { unsafe { SetGlyphData( p + (Util.Align4(base.Size) + Util.Align4(sizeof(OutlineBlock))), cacher); } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: ElementCacher and Checkedpointer are safe to work with /// Also adding this to cache is a safe operation /// [SecurityCritical, SecurityTreatAsSafe] public override void AddToCache(CheckedPointer p, ElementCacher cacher) { int baseSize; StoreKeyInternal(p, out baseSize); baseSize = Util.Align4(baseSize); unsafe { OutlineBlock* b = (OutlineBlock*)p.Probe(baseSize, Util.Align4(sizeof(OutlineBlock)) + OutlinesPerBlock * sizeof(int)); *b = _outlineKey; InitGlyphData(p + (baseSize + Util.Align4(sizeof(OutlineBlock))), cacher); } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: CheckedPointer is safe to work with. Probe does bounds checking and /// pointer validation. /// [SecurityCritical, SecurityTreatAsSafe] public override void StoreKey(CheckedPointer d, out int realSize) { Debug.Assert(!FontSource.IsAppSpecific); int baseSize; StoreKeyInternal(d, out baseSize); baseSize = Util.Align4(baseSize); unsafe { realSize = baseSize + sizeof(OutlineBlock); void* dst = d.Probe(baseSize, sizeof(OutlineBlock)); *(OutlineBlock*)dst = _outlineKey; } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// [SecurityCritical] public override void RetrieveKey(CheckedPointer m) { base.RetrieveKey(m); int baseSize = Util.Align4(base.Size); unsafe { OutlineBlock* s = (OutlineBlock*)m.Probe(baseSize, sizeof(OutlineBlock)); _outlineKey = *s; } } public override int Size { get { unsafe { return Util.Align4(base.Size) + Util.Align4(sizeof(OutlineBlock)) + OutlinesPerBlock * sizeof(int); } } } public override int Type { get { return 4; } } ////// Critical: Allocates memory and calls into unsafe code. This code is also unsafe /// TreatAsSafe: This call returns an IntPtr which is safe to expose because if /// you try to do anything with it that is unsafe it will throw an exception /// [SecurityCritical, SecurityTreatAsSafe] private IntPtr AllocateOutline(int size) { unsafe { // Allocate room for vertical origin, length and the outline itself. byte* data = (byte*)AllocateNoThrow(GlyphOutlineHeaderSize + size); if (data == null) return IntPtr.Zero; return (IntPtr)(data + GlyphOutlineHeaderSize); } } ////// Critical - as this results in the rasterizer accessing font stream. /// TreatAsSafe - as this results in obtaining glyph outline that the client is supposed to see anyway. /// [SecurityCritical, SecurityTreatAsSafe] internal override bool AddGlyph(ushort glyphIndex) { try { if (_rasterizer == null) { if (IsTrueType) _rasterizer = new TrueTypeRasterizer(); else CreateOtfRasterizer(); _fontStream = FontSource.GetUnmanagedStream(); _numberOfGlyphs = _rasterizer.NewFont(_fontStream, FontSource.Uri, FaceIndex); // if (!IsTrueType) { MS.Internal.FontRasterization.Transform tform = new MS.Internal.FontRasterization.Transform(); tform.a01 = tform.a10 = 0; tform.a00 = tform.a11 = _outlineKey.designEmHeight * 0x10000; _rasterizer.NewTransform( 12, tform, OverscaleMode.None, RenderingFlags); } else { unsafe { _pathAllocator = new TrueTypeRasterizer.Allocator(Allocate); } } } if (glyphIndex >= _numberOfGlyphs) return false; if (IsGlyphCached(glyphIndex)) { // another thread already rasterized the glyph // so we can skip adding it return true; } if (!IsTrueType) { _rasterizer.NewGlyph(glyphIndex); GlyphMetrics glyphMetrics; GlyphOutline glyphOutline; try { _rasterizer.GetOutline( AllocateOutline, // allocation delegate IntPtr.Zero, // current buffer -1, // sentinel value that forces the rasterizer to allocate memory even for empty glyphs // we use it to make sure glyph header is allocated out glyphOutline, out glyphMetrics ); } catch (OutOfMemoryException e) { object delegateError = e.Data["GetMemoryDelegateError"]; // In case the exception was raised due to the delegate failure, // throw FontCacheFullException so that we have a chance to renew the cache and retry the request. if (delegateError != null && (bool)delegateError) throw new FontCacheFullException(); throw; } unsafe { int* p = (int*)((byte*)glyphOutline.outline - GlyphOutlineHeaderSize); p[0] = glyphMetrics.verticalOrigin.x; p[1] = glyphMetrics.verticalOrigin.y; p[2] = glyphOutline.length; SetGlyph(glyphIndex, p); } } else { unsafe { GlyphPathData* gpd = ((TrueTypeRasterizer)_rasterizer).GetPath(_pathAllocator, RenderingFlags, glyphIndex); SetGlyph(glyphIndex, gpd); } } return true; } catch (SEHException e) { throw Util.ConvertInPageException(FontSource, e); } } private TrueTypeRasterizer.Allocator _pathAllocator; private OutlineBlock _outlineKey; private const int OutlinesPerBlock = 128; }; ////// Wrapper class for glyph cache access. Getting glyph bitmaps and outlines is more complex than /// using other font cache elements because data for different glyphs can come from different caches. /// The caller should GC.KeepAlive an instance of FontCacheAccessor class while glyph data is being used, /// otherwise there is risk of cache being garbage collected while bitmap data is used. /// internal class FontCacheAccessor { private List_nativeCaches; private class FontFaceID { // // The font file name should not be accessible outside of SecurityCritical // functions. // internal SecurityCriticalData _fontFileName; internal int _faceIndex; internal int _scaleX; internal int _scaleY; internal ushort _flags; internal float _renderingEmSize; /// /// Helper function: wraps FontFace information into a FontFaceID struct /// internal FontFaceID(SecurityCriticalDatafontFileName, int fontFaceIndex, uint scaleX, uint scaleY, ushort glyphRunFlags, float emSize) { _fontFileName = fontFileName; _faceIndex = fontFaceIndex; _scaleX = (int)scaleX; _scaleY = (int)scaleY; _flags = glyphRunFlags; _renderingEmSize = emSize; if (_scaleY > _verticalAntialiasingThreashold) { // we are using VAA (vertical anti aliasing, that means 6*5 overscaled // bitmaps instead of regular 6*1) if font size is big, or if VAA // is forced by user _flags |= (ushort)MilGlyphRun.ForceVAA; } } } private const ushort _fontCacheFlagMask = (ushort)MilGlyphRun.Hinting | (ushort)MilGlyphRun.BoldSimulation | (ushort)MilGlyphRun.ItalicSimulation | (ushort)MilGlyphRun.Sideways | (ushort)MilGlyphRun.IsTrueType; private const ushort _faceFlagMask = _fontCacheFlagMask | (ushort)MilGlyphRun.ForceVAA | (ushort)MilGlyphRun.VerticalDropOut; private const uint _verticalAntialiasingThreashold = 20 * 0x10000; // Matches GLYPH_BASE_CREATE_REQUEST in native code unsafe private struct GLYPH_BASE_CREATE_REQUEST { #pragma warning disable 649 // These fields might not be initialized from managed code, but that's ok public uint uFontFaceIndex; // IN font typeface index public ushort usGlyphRunFlags; // IN GlyphRun flags public uint dScaleX; public uint dScaleY; public float muSize; // // // Validation has already been done on the font filename, so we protect it // here. // // [SecurityCritical] public char * pszFontFileName; // IN font filename string public ushort usGlyphCount; // IN number of glyphs in a glyph run public ushort * pusGlyphIndices; // IN glyph indices in a glyph run // (caller responsible for alloc/free) #pragma warning restore 649 } unsafe private struct GLYPH_GEOMETRY_CREATE_REQUEST { #pragma warning disable 649 // These fields might not be initialized from managed code, but that's ok #pragma warning disable 169 // baseRequest is not used directly, but is required for casting GLYPH_BASE_CREATE_REQUEST baseRequest; public float fBaseLineX; public float fBaseLineY; public float * pfGlyphPositions; #pragma warning restore 169 #pragma warning restore 649 } // Matches GLYPH_BITMAP_CREATE_REQUEST in native code unsafe private struct GLYPH_BITMAP_CREATE_REQUEST { #pragma warning disable 649 // These fields might not be initialized from managed code, but that's ok #pragma warning disable 169 // baseRequest is not used directly, but is required for casting GLYPH_BASE_CREATE_REQUEST baseRequest; public uint faceHandle; // IN internal font face identifier #pragma warning restore 169 #pragma warning restore 649 } ////// Callback entry point for unmanaged code to ask for glyph realizations. /// ////// Critical - calls critical code /// [SecurityCritical] internal static int CreateGlyphsCallback(IntPtr /*CMilSlaveGlyphCache* */ nativeObject, IntPtr /*GLYPH_BITMAP_CREATE_REQUEST | GLYPH_GEOMETRY_CREATE_REQUEST* */ request, ushort isGeometryRequest) { try { FontCacheAccessor fontCacheAccessor = new FontCacheAccessor(); fontCacheAccessor.CreateGlyphsAtRenderTime(nativeObject, request, isGeometryRequest); } catch (Exception e) { // // Need to catch all exceptions at this entry point and convert them into a // HRESULT that managed code can understand and handle. // e.ToString(); return HRESULT.E_FAIL; } return HRESULT.S_OK; } ////// Marshals glyph requests /// ////// Critical - calls critical code /// [SecurityCritical] private unsafe void CreateGlyphsAtRenderTime(IntPtr /*CMilSlaveGlyphCache* */ nativeObject, IntPtr /*GLYPH_BITMAP_CREATE_REQUEST | GLYPH_GEOMETRY_CREATE_REQUEST* */ request, ushort isGeometryRequest) { GLYPH_BASE_CREATE_REQUEST* pRequest = (GLYPH_BASE_CREATE_REQUEST*)request; string fontFileName = new String(pRequest->pszFontFileName); FontFaceID faceId = new FontFaceID(new SecurityCriticalData(fontFileName), (int)pRequest->uFontFaceIndex, pRequest->dScaleX, pRequest->dScaleY, pRequest->usGlyphRunFlags, pRequest->muSize); ushort[] glyphIndices = new ushort[pRequest->usGlyphCount]; int glyphCount = checked((int)pRequest->usGlyphCount); // Put Marshal.CopyArray or something in here for (uint n = 0; n < pRequest->usGlyphCount; n++) { glyphIndices[n] = pRequest->pusGlyphIndices[n]; } if (isGeometryRequest == 0) { GLYPH_BITMAP_CREATE_REQUEST* pRequestAsBitmap = (GLYPH_BITMAP_CREATE_REQUEST*)request; // Realize and send bitmaps CreateGlyphBitmapsHelper( nativeObject, faceId, glyphIndices, pRequestAsBitmap->faceHandle ); } else { GLYPH_GEOMETRY_CREATE_REQUEST* pRequestAsGlyphs = (GLYPH_GEOMETRY_CREATE_REQUEST*)request; // Realize and send geometry CreateGlyphGeometryHelper( nativeObject, faceId, glyphIndices, pRequestAsGlyphs->pfGlyphPositions, pRequestAsGlyphs->fBaseLineX, pRequestAsGlyphs->fBaseLineY ); } } /// /// Critical - as this accesses critical data and calls critical code /// [SecurityCritical] private unsafe void CreateGlyphGeometryHelper( IntPtr nativeGlyphRun, FontFaceID faceId, ushort[] glyphIndices, float *pGlyphPositions, float fBaseLineX, float fBaseLineY ) { Debug.Assert(glyphIndices.Length > 0); Geometry glyphRunGeometry = BuildGeometry(faceId, glyphIndices, pGlyphPositions, fBaseLineX, fBaseLineY); if (glyphRunGeometry.IsEmpty()) { return; } // If glyphRunGeometry is non empty, it is of type GeometryGroup, and all the // geometries inside are of type PathGeometry GeometryGroup glyphRunGroup = (GeometryGroup)glyphRunGeometry; Geometry.PathGeometryData glyphRunPathData = glyphRunGroup.GetAsPathGeometry().GetPathGeometryData(); // Packet layout is: // MILCMD_GLYPHRUN_SETRENDERGEOMETRY // MILCMD_PATHGEOMETRY // PathFigure data block (glyphRunPathData.SerializedData) // Allocate sufficient memory uint pathGeometrySize = (uint)sizeof(DUCE.MILCMD_PATHGEOMETRY); uint dataLength = (uint)glyphRunPathData.SerializedData.Length; uint packetSize = checked(pathGeometrySize + dataLength); byte[] packetBlock = new byte[packetSize]; fixed (byte* packet = packetBlock) { DUCE.MILCMD_PATHGEOMETRY *pathGeometryPacket = (DUCE.MILCMD_PATHGEOMETRY *)(packet); pathGeometryPacket->Type = MILCMD.MilCmdPathGeometry; //pathGeometryPacket->Transform = 0; pathGeometryPacket->FillRule = glyphRunGroup.FillRule; pathGeometryPacket->FiguresSize = glyphRunPathData.Size; byte* pathData = (byte *)(packet + pathGeometrySize); Marshal.Copy(glyphRunPathData.SerializedData, 0, (IntPtr)pathData, glyphRunPathData.SerializedData.Length); // Send packet HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphRun_SetGeometryAtRenderTime( nativeGlyphRun, (byte *)packet, packetSize )); } } ////// Obtains geometry for the glyph run. /// ///The geometry returned contains the combined geometry of all glyphs in the glyph run. /// Overlapping contours are merged by performing a Boolean union operation. ////// Critical - performs unsafe operations /// [SecurityCritical] private unsafe Geometry BuildGeometry( FontFaceID faceId, ushort[] glyphIndices, float *pPositions, float fBaseLineX, float fBaseLineY ) { GeometryGroup accumulatedGeometry = null; Uri fontURI = new Uri(faceId._fontFileName.Value, UriKind.Absolute); bool IsSideways = ((faceId._flags & (UInt16)MilGlyphRun.Sideways) != 0); bool IsLeftToRight = ((faceId._flags & (UInt16)MilGlyphRun.IsLeftToRight) != 0); // If there are Y positions the array is packed as positions XYXYXY etc, if there // are no Y positions, it's just XXX and Y is assumed to be 0. bool HasYPositions = ((faceId._flags & (UInt16)MilGlyphRun.HasYPositions) != 0); StyleSimulations style = StyleSimulations.None; if ((faceId._flags & (UInt16)MilGlyphRun.BoldSimulation) != 0) { style |= StyleSimulations.BoldSimulation; } if ((faceId._flags & (UInt16)MilGlyphRun.ItalicSimulation) != 0) { style |= StyleSimulations.ItalicSimulation; } GlyphTypeface glyphTypeface = new GlyphTypeface(fontURI, style, false); for (int i = 0; i < glyphIndices.Length; i++) { ushort glyphIndex = glyphIndices[i]; Geometry glyphGeometry = glyphTypeface.ComputeGlyphOutline(glyphIndex, IsSideways, faceId._renderingEmSize); if (glyphGeometry.IsEmpty()) continue; // transform glyphGeometry to the glyph origin unsafe { float xPosition; float yPosition; if (HasYPositions) { xPosition = ((float*)pPositions)[2*i]; yPosition = ((float*)pPositions)[2 * i + 1]; } else { // // If there are no Y positions, the first X position is always assumed to be 0 and is // ommitted, making the array one element smaller. If there are Y positions, all X // and Y positions are fully specified // xPosition = (i == 0) ? 0 : ((float*)pPositions)[i-1]; yPosition = 0; } // // If this is an RTL glyph run, xPosition will already be negative. We still need to subtract // the AdvanceWidth for the glyph though. For regular bitmap rendering, this happens in // CGlyphRunMaker::CalculateGlyphPositions() // float xOffset = (xPosition - (float)(IsLeftToRight ? 0 : glyphTypeface.GetAdvanceWidth(glyphIndex))) * faceId._renderingEmSize + fBaseLineX; float yOffset = yPosition * faceId._renderingEmSize + fBaseLineY; glyphGeometry.Transform = new TranslateTransform(xOffset, yOffset); } if (accumulatedGeometry == null) { accumulatedGeometry = new GeometryGroup(); accumulatedGeometry.FillRule = FillRule.Nonzero; } accumulatedGeometry.Children.Add(glyphGeometry.GetOutlinedPathGeometry(GlyphRun.RelativeFlatteningTolerance, ToleranceType.Relative)); } // Make sure to always return Geometry.Empty from public methods for empty geometries. if (accumulatedGeometry == null || accumulatedGeometry.IsEmpty()) return Geometry.Empty; return accumulatedGeometry; } ////// Critical - as this accesses critical data and calls GetBitmaps which is Critical. /// [SecurityCritical] private unsafe void CreateGlyphBitmapsHelper( IntPtr nativeGlyphCache, FontFaceID faceId, ushort[] glyphIndices, uint faceHandle ) { Debug.Assert(glyphIndices.Length > 0); // Should not pInvoke here when no glyphs are missing void*[] glyphBitmaps = new void*[glyphIndices.Length]; GetBitmaps( faceId._fontFileName.Value, faceId._faceIndex, faceId._scaleX, faceId._scaleY, (ushort)faceId._renderingEmSize, (faceId._flags & (uint)MilGlyphRun.ForceVAA) != 0 ? (ushort)MS.Internal.FontRasterization.OverscaleMode.OverscaleXandY : (ushort)MS.Internal.FontRasterization.OverscaleMode.OverscaleX, //renderingMode (ushort)(faceId._flags & _fontCacheFlagMask), //renderingFlags, glyphIndices.Length, glyphIndices, /*OUT*/ glyphBitmaps ); bool needDropOut = (faceId._flags & (uint)MilGlyphRun.VerticalDropOut) != 0; AddBitmapsToNativeCacheAtRenderTime(nativeGlyphCache, faceHandle, glyphBitmaps, glyphIndices, faceId._flags, needDropOut); } ////// Critical - as this accesses unsafe code blocks. /// [SecurityCritical] private unsafe void AddBitmapsToNativeCacheAtRenderTime(IntPtr nativeGlyphCache, uint faceHandle, void*[] glyphBitmaps, ushort[] glyphIndices, ushort faceFlags, bool needDropOut) { Debug.Assert(glyphBitmaps != null); Debug.Assert(glyphBitmaps.Length != 0); DUCE.MILCMD_GLYPHCACHE_ADDBITMAPS command; try { command.Type = MILCMD.MilCmdGlyphCacheAddBitmaps; command.FontFaceHandle = faceHandle; command.FaceFlags = faceFlags; command.GlyphCount = checked((UInt16)glyphIndices.Length); command.Handle = DUCE.ResourceHandle.Null; // Unused - MIL resource handle - bypassing DUCE channels // Precalculate size of command data buffer // The uint is used up by the MemWriter int dataSize = checked(sizeof(uint) + glyphIndices.Length * sizeof(ushort)); // Count the total size of the bitmaps. if (!needDropOut) { for (int i = 0; i < glyphIndices.Length; i++) { int blockSize = GetBitmapTransferSize((GlyphBitmap*)glyphBitmaps[i]); dataSize = checked(dataSize + blockSize); } } else { for (int i = 0; i < glyphIndices.Length; i++) { int blockSize = GetTransformedBitmapTransferSize((GlyphBitmap*)glyphBitmaps[i]); dataSize = checked(dataSize + blockSize); } } // Talk directly to native GlyphCache object HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_BeginCommandAtRenderTime( nativeGlyphCache, (byte*)&command, (UInt32)sizeof(DUCE.MILCMD_GLYPHCACHE_ADDBITMAPS), (UInt32)dataSize )); } catch (System.OverflowException e) { // re-throw more readable exception throw new OutOfMemoryException(SR.Get(SRID.TooManyGlyphRuns), e); } // Write the bitmaps if needed. // Bitmaps should go first because of aligning. if (!needDropOut) { for (int i = 0; i < glyphIndices.Length; i++) { TransferBitmapAtRenderTime(nativeGlyphCache, (GlyphBitmap*)glyphBitmaps[i]); } } else { for (int i = 0; i < glyphIndices.Length; i++) { TransferTransformedBitmapAtRenderTime(nativeGlyphCache, (GlyphBitmap*)glyphBitmaps[i]); } } // Write the glyph indices. fixed (ushort* pData = glyphIndices) { HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_AppendCommandDataAtRenderTime( nativeGlyphCache, (byte*)pData, (uint)(glyphIndices.Length * sizeof(ushort)))); } HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_EndCommandAtRenderTime( nativeGlyphCache )); } ////// Compose and transfer a piece of MILCMD_GLYPHCACHE_ADDBITMAPS command /// that represents given glyph bitmap. /// ////// Critical - as this accesses unsafe code blocks. /// [SecurityCritical] private unsafe void TransferBitmapAtRenderTime(IntPtr nativeGlyphCache, GlyphBitmap* pBitmap) { if (pBitmap == null) { TransferStubAtRenderTime(nativeGlyphCache); return; } DUCE.MILCMD_GLYPHBITMAP bitmap = new DUCE.MILCMD_GLYPHBITMAP(); bitmap.horOriginX = checked((Int16)(pBitmap->horOriginX)); bitmap.horOriginY = checked((Int16)(pBitmap->horOriginY)); bitmap.horAdvance = checked((Int16)(pBitmap->horAdvance)); bitmap.verOriginX = checked((Int16)(pBitmap->verOriginX)); bitmap.verOriginY = checked((Int16)(pBitmap->verOriginY)); bitmap.width = checked((UInt16)(pBitmap->width)); bitmap.height = checked((UInt16)(pBitmap->height)); bitmap.stride = checked((UInt16)(pBitmap->stride)); int cbDataSize = (int)bitmap.height * (int)bitmap.stride; HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_AppendCommandDataAtRenderTime( nativeGlyphCache, (byte*)&bitmap, (uint)sizeof(DUCE.MILCMD_GLYPHBITMAP))); if (cbDataSize != 0) { byte* pData = (byte*)pBitmap + sizeof(GlyphBitmap); // Write directly to slave GlyphCache HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_AppendCommandDataAtRenderTime( nativeGlyphCache, pData, (uint)cbDataSize)); } } ////// This routine is for seldom circumstances, when /// font cache unabled supply particular glyph bitmap. /// When happened so we'll replace glyph with special shape /// (currently empty) /// ////// Critical - as this accesses unsafe code blocks. /// [SecurityCritical] private unsafe void TransferStubAtRenderTime(IntPtr nativeGlyphCache) { // create empty struct GlyphBitmap in stack frame DUCE.MILCMD_GLYPHBITMAP bitmap = new DUCE.MILCMD_GLYPHBITMAP(); HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_AppendCommandDataAtRenderTime( nativeGlyphCache, (byte*)&bitmap, (uint)sizeof(DUCE.MILCMD_GLYPHBITMAP))); } ////// Pass the GlyphBitmap to command buffer, along the way /// making horizontal strokes *a little* thicker than given /// GlyphBitmap has. /// /// This thickening algorythm (also referred to as "virtual /// drop out control") is completely heuristic. It was /// initially proposed by Beat Stamm (beats). This version /// differs much from the initial. It thickens only /// horizontal strokes, increasing stroke width up to 2 virtual /// pixels. Heuristic prohibits thickening if it cause /// the stroke to bleed through pixel boundary, and also if /// next stroke is too close to given. /// ////// Critical - as this accesses unsafe code blocks. /// [SecurityCritical] private unsafe void TransferTransformedBitmapAtRenderTime(IntPtr slaveGlyphCache, GlyphBitmap* pBitmap) { if (pBitmap == null) { TransferStubAtRenderTime(slaveGlyphCache); return; } // Vertical drop out needs glyph bitmap (that's ovescaled 5 times by Y axis) // to be vertically aligned to physycal pixel grid. // We may need to allocate extra rows above and below // original image. int bitmapTop = -pBitmap->horOriginY; int bitmapBottom = bitmapTop + pBitmap->height; int pixelTop = bitmapTop >= 0 ? bitmapTop / 5 : -((4 - bitmapTop) / 5); int pixelBottom = bitmapBottom >= 0 ? (bitmapBottom + 4) / 5 : -((-bitmapBottom) / 5); int rowsAbove = bitmapTop - pixelTop * 5; int rowsBelow = pixelBottom * 5 - bitmapBottom; Debug.Assert(rowsAbove >= 0 && rowsAbove < 5); Debug.Assert(rowsBelow >= 0 && rowsBelow < 5); int newSize = checked( sizeof(DUCE.MILCMD_GLYPHBITMAP) + (pBitmap->height + rowsBelow + rowsAbove) * pBitmap->stride ); byte* pData = stackalloc byte[newSize]; DUCE.MILCMD_GLYPHBITMAP* pNewBitmap = (DUCE.MILCMD_GLYPHBITMAP*)pData; // fill bitmap header pNewBitmap->horOriginX = checked((Int16)(pBitmap->horOriginX)); pNewBitmap->horOriginY = checked((Int16)(pBitmap->horOriginY + rowsAbove)); pNewBitmap->horAdvance = checked((Int16)(pBitmap->horAdvance)); pNewBitmap->verOriginX = checked((Int16)(pBitmap->verOriginX - rowsAbove)); pNewBitmap->verOriginY = checked((Int16)(pBitmap->verOriginY)); pNewBitmap->width = checked((UInt16)(pBitmap->width)); pNewBitmap->height = checked((UInt16)(pBitmap->height + rowsAbove + rowsBelow)); pNewBitmap->stride = checked((UInt16)(pBitmap->stride)); UInt32* pSrc = (UInt32*)((byte*)pBitmap + sizeof(GlyphBitmap)); UInt32* pDst = (UInt32*)(pData + sizeof(DUCE.MILCMD_GLYPHBITMAP)); int stride32 = pBitmap->stride >> 2; // fill added rows above with zeros UInt32* pDstAbove = pDst; int sizeAbove = rowsAbove * stride32; for (int i = sizeAbove; --i >= 0; ) pDst[i] = 0; // copy bitmap bits UInt32* pDstOrigin = pDstAbove + sizeAbove; int sizeOrigin = stride32 * pBitmap->height; for (int i = sizeOrigin; --i >= 0; ) pDstOrigin[i] = pSrc[i]; // fill added rows above with zeros UInt32* pDstBelow = pDstOrigin + sizeOrigin; int sizeBelow = rowsBelow * stride32; for (int i = sizeBelow; --i >= 0; ) pDstBelow[i] = 0; UInt32* pColon = pDst; int bandsCount = pixelBottom - pixelTop; int colonsCount = (int)((pNewBitmap->width + 31) >> 5); Debug.Assert(colonsCount <= stride32); if (bandsCount == 1) { for (int i = 0; i < colonsCount; i++, pColon++) { UInt32* p0 = pColon; UInt32* p1 = p0 + stride32; UInt32* p2 = p1 + stride32; UInt32* p3 = p2 + stride32; UInt32* p4 = p3 + stride32; // bleeding up: set the bit if there is "one" // on next row but not two previous and second next. // The magic code below actually repeats many times // the same formula: // bit |= nextBit & ~next2Bit & ~previousBit & ~previous2Bit // where some members are removed if we are close to the // edge so some "next" and "previous" are missed. *p0 |= *p1 & ~*p2; *p1 |= ~*p0 & *p2 & ~*p3; *p2 |= ~*p0 & ~*p1 & *p3 & ~*p4; *p3 |= ~*p1 & ~*p2 & *p4; // bleed down: same as bleeding up but Y direction is reversed *p4 |= ~*p2 & *p3; *p3 |= ~*p1 & *p2 & ~*p4; *p2 |= ~*p0 & *p1 & ~*p3 & ~*p4; *p1 |= *p0 & ~*p2 & ~*p3; } } else { // there are two or more bands for (int i = 0; i < colonsCount; i++, pColon++) { UInt32* p0 = pColon; UInt32* p1 = p0 + stride32; UInt32* p2 = p1 + stride32; UInt32* p3 = p2 + stride32; UInt32* p4 = p3 + stride32; UInt32* p5 = p4 + stride32; UInt32* p6 = p5 + stride32; // Handle first band // bleed up *p0 |= *p1 & ~*p2; *p1 |= ~*p0 & *p2 & ~*p3; *p2 |= ~*p0 & ~*p1 & *p3 & ~*p4; *p3 |= ~*p1 & ~*p2 & *p4 & ~*p5; // bleed down *p4 |= ~*p2 & *p3 & ~*p5 & ~*p6; *p3 |= ~*p1 & *p2 & ~*p4 & ~*p5; *p2 |= ~*p0 & *p1 & ~*p3 & ~*p4; *p1 |= *p0 & ~*p2 & ~*p3; UInt32* q1 = p4; UInt32* q2 = p3; // Handle middle bands for (int b = bandsCount - 2; --b >= 0; ) { p0 = q1 + stride32; p1 = p0 + stride32; p2 = p1 + stride32; p3 = p2 + stride32; p4 = p3 + stride32; p5 = p4 + stride32; p6 = p5 + stride32; // bleed up *p0 |= ~*q2 & ~*q1 & *p1 & ~*p2; *p1 |= ~*q1 & ~*p0 & *p2 & ~*p3; *p2 |= ~*p0 & ~*p1 & *p3 & ~*p4; *p3 |= ~*p1 & ~*p2 & *p4 & ~*p5; // bleed down *p4 |= ~*p2 & *p3 & ~*p5 & ~*p6; *p3 |= ~*p1 & *p2 & ~*p4 & ~*p5; *p2 |= ~*p0 & *p1 & ~*p3 & ~*p4; *p1 |= ~*q1 & *p0 & ~*p2 & ~*p3; q1 = p4; q2 = p3; } // Handle last band p0 = q1 + stride32; p1 = p0 + stride32; p2 = p1 + stride32; p3 = p2 + stride32; p4 = p3 + stride32; // bleed up *p0 |= ~*q2 & ~*q1 & *p1 & ~*p2; *p1 |= ~*q1 & ~*p0 & *p2 & ~*p3; *p2 |= ~*p0 & ~*p1 & *p3 & ~*p4; *p3 |= ~*p1 & ~*p2 & *p4; // bleed down *p4 |= ~*p2 & *p3; *p3 |= ~*p1 & *p2 & ~*p4; *p2 |= ~*p0 & *p1 & ~*p3 & ~*p4; *p1 |= ~*q1 & *p0 & ~*p2 & ~*p3; } } HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_AppendCommandDataAtRenderTime( slaveGlyphCache, (byte*)pData, (uint)newSize)); } ////// Calculate the size in command buffer, required for transferring /// given bitmap to rendering machine. /// /// ///size, in bytes ////// Critical - as this accesses unsafe code blocks. /// [SecurityCritical] private static unsafe int GetBitmapTransferSize(GlyphBitmap* pBitmap) { int cbSize = sizeof(DUCE.MILCMD_GLYPHBITMAP); if (pBitmap != null) { cbSize += pBitmap->height * pBitmap->stride; } return cbSize; } ////// Precalculate the size of MILCMD_GLYPHCACHE_ADDBITMAPS command segment /// that will be used by TransferTransformedBitmap /// /// given glyph bitmap ///size, in bytes ////// Critical: accepts a pointer and processes it with no validation /// [SecurityCritical] private static unsafe int GetTransformedBitmapTransferSize(GlyphBitmap* pBitmap) { int cbSize = sizeof(DUCE.MILCMD_GLYPHBITMAP); if (pBitmap != null) { // Vertical drop out needs glyph bitmap (that's ovescaled 5 times by Y axis) // to be vertically aligned to physycal pixel grid. // We may need to allocate extra rows above and below // original image. int bitmapTop = -pBitmap->horOriginY; int bitmapBottom = bitmapTop + pBitmap->height; int pixelTop = bitmapTop >= 0 ? bitmapTop / 5 : -((4 - bitmapTop) / 5); int pixelBottom = bitmapBottom >= 0 ? (bitmapBottom + 4) / 5 : -((-bitmapBottom) / 5); int rowsAbove = bitmapTop - pixelTop * 5; int rowsBelow = pixelBottom * 5 - bitmapBottom; Debug.Assert(rowsAbove >= 0 && rowsAbove < 5); Debug.Assert(rowsBelow >= 0 && rowsBelow < 5); cbSize += (pBitmap->height + rowsBelow + rowsAbove) * pBitmap->stride; } return cbSize; } internal FontCacheAccessor() { _nativeCaches = new List(2); } /// /// Critical - as this calls GetGlyphs() which is Critical and return font information /// via glyphBitmaps parameter. /// [SecurityCritical] internal unsafe void GetBitmaps( string fontFileName, int faceIndex, int scaleX, int scaleY, ushort pointSize, ushort renderingMode, ushort renderingFlags, int glyphCount, ushort[] glyphIndices, /*OUT*/ void*[] glyphBitmaps ) { try { using (GlyphBitmapElement elem = new GlyphBitmapElement( faceIndex, scaleX, 0, 0, scaleY, pointSize, renderingMode, renderingFlags, // We set skipDemand to true here because we should have validated // whether the caller can access this font previously. // At this point we don't have context about the caller any more. new FontSource(new Uri(fontFileName, UriKind.Absolute), true) )) { GetGlyphs( elem, new PartialList(glyphIndices, 0, glyphCount), glyphBitmaps ); } } // Disable PreSharp warning about empty catch bodies, please see comments below. #pragma warning disable 6502 // Don't fail because of malformed fonts, use empty glyph bitmap stubs. catch (FileFormatException) { } // Don't fail because of inaccessible fonts, use empty glyph bitmap stubs. catch (IOException) { } // Don't fail because of inaccessible fonts, use empty glyph bitmap stubs. catch (UnauthorizedAccessException) { } // Don't fail because of inaccessible fonts, use empty glyph bitmap stubs. catch (System.Net.WebException) { } #pragma warning restore 6502 catch (ArgumentOutOfRangeException e) { if (e.ParamName != "transform") throw; // Don't fail because of invalid transformations, because they can frequently happen in the process of animating text. } } /// /// Critical - as this calls the critical function GetServerCache() and exposes /// font cache data via glyphsArray parameter. /// [SecurityCritical] internal unsafe void GetGlyphs( BaseGlyphElement element, IListglyphIndices, void*[] glyphsArray ) { ElementCacher c; if (!element.IsAppSpecific) { c = CacheManager.GetServerCache(); if (c != null) { bool allFound = true; element.Reset(); for (int i = 0; i < glyphIndices.Count; ++i) { Debug.Assert(glyphsArray[i] == null); ushort glyph = glyphIndices[i]; ushort baseGlyph = element.GetBaseGlyph(glyph); if (element.GetBaseGlyph() != baseGlyph) { element.Reset(); element.SetBaseGlyph(baseGlyph); if (!c.ReadOnlyLookup(element)) { allFound = false; continue; } } if (element.GlyphData == null || !element.IsGlyphCached(glyph)) { allFound = false; continue; } CacheManager.SaveNativeCache(c, _nativeCaches); glyphsArray[i] = element.GetGlyph(glyph); Debug.Assert(glyphsArray[i] != null); } if (allFound) return; } } bool retry = false; c = CacheManager.GetCurrentCache(); Debug.Assert(c != null); element.Reset(); for (int i = 0; i < glyphIndices.Count; ++i) { if (glyphsArray[i] != null) continue; if (retry) { c = CacheManager.RenewCache(c); element.Reset(); } ushort glyph = glyphIndices[i]; ushort baseGlyph = element.GetBaseGlyph(glyph); try { if (element.GetBaseGlyph() != baseGlyph) { element.SetBaseGlyph(baseGlyph); if (!c.LookupAndAdd(element) && !element.IsAppSpecific) CacheManager.SendMissReport(element); } if (!element.IsGlyphCached(glyph)) { if (!element.AddGlyph(glyph)) { // client passed an invalid glyph index throw new ArgumentOutOfRangeException("glyph", SR.Get(SRID.GlyphIndexOutOfRange, glyph)); } } } catch (FontCacheFullException) { retry = true; --i; continue; } retry = false; CacheManager.SaveNativeCache(c, _nativeCaches); glyphsArray[i] = element.GetGlyph(glyph); Debug.Assert(glyphsArray[i] != null); } } }; } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Security; using System.Security.Permissions; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Media; using MS.Internal; using MS.Internal.FontFace; using MS.Utility; using MS.Internal.FontRasterization; using Adobe.CffRasterizer; using System.Windows.Media.Composition; using MS.Internal.PresentationCore; using UnsafeNativeMethods = MS.Win32.PresentationCore.UnsafeNativeMethods; // Since we disable PreSharp warnings in this file, we first need to disable warnings about unknown message numbers and unknown pragmas. #pragma warning disable 1634, 1691 namespace MS.Internal.FontCache { /// /// Glyph element class. /// Layout is: /// glyph block structure | file name string /// [FriendAccessAllowed] internal abstract class BaseGlyphElement : IFontCacheElement, IDisposable { [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct GlyphBlock { internal int faceIndex; internal int cbFileNameLength; internal ushort renderingFlags; internal ushort baseglyphIndex; ////// Critical - as this accesses unsafe code blocks /// TreatAsSafe - as this does not return any unsafe pointers and is used to compare two blocks /// [SecurityCritical, SecurityTreatAsSafe] internal unsafe bool Equal(GlyphBlock* rhs) { Invariant.Assert(rhs != null); return this.faceIndex == rhs->faceIndex && this.cbFileNameLength == rhs->cbFileNameLength && this.baseglyphIndex == rhs->baseglyphIndex && this.renderingFlags == rhs->renderingFlags; } } protected BaseGlyphElement(int glyphsPerBlock) { _glyphsPerBlock = glyphsPerBlock; Reset(); Debug.Assert(_key.baseglyphIndex != GetBaseGlyph(_key.baseglyphIndex)); } ////// Critical - as this calls GetUriString /// TreatAsSafe - as this does not uses the result only to compute the string length. /// [SecurityCritical, SecurityTreatAsSafe] protected BaseGlyphElement(int glyphsPerBlock, int faceIndex, FontSource fontSource, ushort renderingFlags) : this(glyphsPerBlock) { _fontSource = fontSource; _key.renderingFlags = renderingFlags; _key.faceIndex = faceIndex; unsafe { _key.cbFileNameLength = FontSource.GetUriString().Length * sizeof(char); } } ////// Critical as this calls _fontStream.Close(), which is a file operation. /// TreatAsSafe as it only releases the file handle. /// [SecurityCritical, SecurityTreatAsSafe] public void Dispose() { if (_rasterizer != null) { _rasterizer.Dispose(); _rasterizer = null; } if (_fontStream != null) { _fontStream.Close(); _fontStream = null; } } ////// Critical - as this accesses _glyphdata which is a pointer /// TreatAsSafe: This does not expose the data and simply resets the stream /// [SecurityCritical, SecurityTreatAsSafe] internal void Reset() { unsafe { _glyphData = null; } _cacher = null; _key.baseglyphIndex = 1; } internal FontSource FontSource { get { return _fontSource; } } internal int FaceIndex { get { return _key.faceIndex; } } internal RenderingFlags RenderingFlags { get { return (RenderingFlags)_key.renderingFlags; } } internal bool IsTrueType { get { return (_key.renderingFlags & (ushort)MilGlyphRun.IsTrueType) != 0; } } ////// Prevent JIT from inlining this method, so that PresentationCFFRasterizer.dll and PresentationCFFRasterizerNative.dll are loaded on demand. /// ////// Critical - This method calls critical code (OTFRasterizer()) /// Safe - This method doesn't expose any ciritical information. /// [MethodImpl(MethodImplOptions.NoInlining)] [SecurityCritical, SecurityTreatAsSafe] protected void CreateOtfRasterizer() { _rasterizer = new OTFRasterizer(); } ////// Critical - as this accesses unsafe code blocks and allocates an object that can hold unmanaged code and returns it /// [SecurityCritical] protected unsafe void* Allocate(int size) { int newOffset = _cacher.Alloc(size); return _cacher[newOffset]; } ////// Critical: This code acceses unsafe code and is used to allocate memory. It returns a pointer /// [SecurityCritical] protected unsafe void* AllocateNoThrow(int size) { try { return Allocate(size); } catch (FontCacheFullException) { return null; } } ////// Obtains a base glyph index for this cache element /// ///The base glyph index internal ushort GetBaseGlyph() { return _key.baseglyphIndex; } internal void SetBaseGlyph(ushort glyphIndex) { Debug.Assert(glyphIndex == GetBaseGlyph(glyphIndex)); _key.baseglyphIndex = glyphIndex; } internal ushort GetBaseGlyph(ushort glyphIndex) { return (ushort)(glyphIndex - glyphIndex % _glyphsPerBlock); } ////// Critical - as this gives out glyphstream /// [SecurityCritical] protected unsafe int* GlyphEntry(ushort glyphIndex) { Invariant.Assert(GetBaseGlyph(glyphIndex) == _key.baseglyphIndex); return _glyphData + (glyphIndex - _key.baseglyphIndex); } ////// Returns whether a given glyph is cached /// /// Glyph index ///true if the glyph is cached, false otherwise ////// Critical - as this accesses unsafe code blocks /// TreatAsSafe - as this does not return the stream /// [SecurityCritical, SecurityTreatAsSafe] internal bool IsGlyphCached(ushort glyphIndex) { unsafe { return *GlyphEntry(glyphIndex) != Util.nullOffset; } } ////// Critical - as this accesses unsafe code blocks and returns a glyph pointer /// [SecurityCritical] internal unsafe void* GetGlyph(ushort glyphIndex) { Invariant.Assert(IsGlyphCached(glyphIndex)); int offset = *GlyphEntry(glyphIndex); return _cacher[offset]; } ////// Critical - as this accesses unsafe code blocks and sets data from a pointer /// [SecurityCritical] protected unsafe void SetGlyph(ushort glyphIndex, void* data) { *GlyphEntry(glyphIndex) = _cacher[(byte*)data]; Debug.Assert(IsGlyphCached(glyphIndex)); } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: The calls to probe is bounds checked /// and the usage of elementcacher and checkedpointer are tracked. /// [SecurityCritical, SecurityTreatAsSafe] protected void InitGlyphData(CheckedPointer p, ElementCacher cacher) { unsafe { _glyphData = (int*)p.Probe(0, _glyphsPerBlock * sizeof(int)); Util.FillMemory(_glyphData, _glyphsPerBlock * sizeof(int), Util.nullOffset); } _cacher = cacher; } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: The calls to probe is bounds checked /// and the usage of elementcacher and checkedpointer are tracked. /// [SecurityCritical, SecurityTreatAsSafe] protected void SetGlyphData(CheckedPointer p, ElementCacher cacher) { unsafe { _glyphData = (int*)p.Probe(0, _glyphsPerBlock * sizeof(int)); } _cacher = cacher; } ////// Critical - as this gives out glyph data in the form of a pointer /// internal unsafe void* GlyphData { [SecurityCritical] get { return _glyphData; } } ////// Adds glyph to the cache /// /// Index of the glyph ///Whether the glyph index was valid for this font. ////// Critical - as this results in the rasterizer accessing font stream. /// TreatAsSafe - as this results in obtaining glyph data that the client is supposed to see anyway. /// [SecurityCritical, SecurityTreatAsSafe] internal abstract bool AddGlyph(ushort glyphIndex); ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: Probe is bounds checked and validates pointer. /// [SecurityCritical, SecurityTreatAsSafe] public virtual bool Match(CheckedPointer p) { unsafe { GlyphBlock* rhs = (GlyphBlock*)p.Probe(0, sizeof(GlyphBlock)); if (!_key.Equal(rhs)) return false; return Util.StringEqualIgnoreCase(p + sizeof(GlyphBlock), FontSource.GetUriString()); } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// [SecurityCritical] public virtual void RetrieveKey(CheckedPointer m) { unsafe { _key = *(GlyphBlock*)m.Probe(0, sizeof(GlyphBlock)); // Validate the base glyph index now so that malformed miss reports // don't trip Invarient.Assert() later when we try to construct the element. if (GetBaseGlyph(_key.baseglyphIndex) != _key.baseglyphIndex) throw new ArgumentOutOfRangeException(); // As the name size could come from arbitrary code // sending miss reports to the font cache server, // we must validate it so that Util.StringCopy() doesn't // call Invariant::Assert() if nameSize is invalid. if ((_key.cbFileNameLength < 0) || ((_key.cbFileNameLength % 2) != 0)) throw new ArgumentOutOfRangeException(); string fileName = Util.StringCopyFromCheckedPointer(m + sizeof(GlyphBlock), _key.cbFileNameLength); _fontSource = new FontSource(new Uri(fileName, UriKind.Absolute), false); } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: Probe is type and bounds checked and this functionality is safe to expose /// [SecurityCritical, SecurityTreatAsSafe] internal void StoreKeyInternal(CheckedPointer d, out int realSize) { realSize = InternalGetSize(); unsafe { GlyphBlock* glyphBlock = (GlyphBlock*)d.Probe(0, sizeof(GlyphBlock)); *glyphBlock = _key; Util.StringCopyToCheckedPointer(d + sizeof(GlyphBlock), FontSource.GetUriString()); } } public virtual int Size { get { return InternalGetSize(); } } public virtual bool IsAppSpecific { get { return _fontSource.IsAppSpecific; } } private int InternalGetSize() { unsafe { return sizeof(GlyphBlock) + _key.cbFileNameLength; } } ////// Critical: This code has unsafe code blocks which call into HasMemory. /// TreatAsSafe: This code is safe to call , since the _key variable is always /// pointing to a valid object and passing it to HashMemory which accepts a void pointer /// is safe. /// [SecurityCritical, SecurityTreatAsSafe] public override int GetHashCode() { int hash = 0; unsafe { fixed (GlyphBlock* k = &_key) { hash = HashFn.HashMemory(k, sizeof(GlyphBlock), hash); } } hash = HashFn.HashMultiply(hash) + FontSource.GetHashCode(); return hash; } public abstract void GetData(CheckedPointer p, ElementCacher cacher); public abstract void AddToCache(CheckedPointer p, ElementCacher cacher); public abstract int Type { get;} public abstract void StoreKey(CheckedPointer d, out int realSize); ////// Cached TrueType rasterizer instance /// protected IFontRasterizer _rasterizer; ////// Critical - as this gives out UnmanagedMemoryStream content which is from a file. /// [SecurityCritical] protected UnmanagedMemoryStream _fontStream; ////// The total number of glyphs in the font /// protected ushort _numberOfGlyphs; private int _glyphsPerBlock; ////// Critical - as this gives out glyph data in the form of a pointer /// [SecurityCritical] private unsafe int* _glyphData; // points to bitmap array in the cache private ElementCacher _cacher; private FontSource _fontSource; private GlyphBlock _key; } ////// This structure represents a black and white glyph bitmap. /// In font cache the structure is immediately followed by height*stride bytes of pixel data. /// Empty bitmaps have zero height. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] internal unsafe struct GlyphBitmap { internal int horOriginX; // horizontal X origin of glyph bitmap internal int horOriginY; // horizontal Y origin of glyph bitmap internal int horAdvance; // hinted advance width internal int verOriginX; // vertical X origin of glyph bitmap internal int verOriginY; // vertical Y origin of glyph bitmap internal int verAdvance; // hinted advance height internal int width; // bitmap width in pixels internal int height; // bitmap height in pixels internal int stride; // number of bytes to store one pixel row } ////// Glyph bitmap element class. /// Represents a block of glyph bitmaps /// Layout is: /// base glyph block | bitmap block | table of blockSize pointers to glyph data /// [FriendAccessAllowed] internal sealed class GlyphBitmapElement : BaseGlyphElement { [StructLayout(LayoutKind.Sequential, Pack = 1)] internal unsafe struct BitmapBlock { internal int a00; internal int a01; internal int a10; internal int a11; internal ushort pointSize; internal ushort renderingMode; ////// Critical - as this accesses unsafe code blocks /// TreatAsSafe - as this does not return unsafe information but is used to compare blocks /// [SecurityCritical, SecurityTreatAsSafe] internal bool Equal(BitmapBlock* rhs) { Invariant.Assert(rhs != null); return this.a00 == rhs->a00 && this.a01 == rhs->a01 && this.a10 == rhs->a10 && this.a11 == rhs->a11 && this.pointSize == rhs->pointSize && this.renderingMode == rhs->renderingMode; } } ////// Critical - Calls into the critical RetrieveKey method. /// [SecurityCritical] internal GlyphBitmapElement(CheckedPointer key) : base(BitmapsPerBlock) { RetrieveKey(key); } internal GlyphBitmapElement( int faceIndex, int a00, int a01, int a10, int a11, ushort pointSize, ushort renderingMode, ushort renderingFlags, FontSource fontSource ) : base(BitmapsPerBlock, faceIndex, fontSource, renderingFlags) { _bitmapKey.a00 = a00; _bitmapKey.a01 = a01; _bitmapKey.a10 = a10; _bitmapKey.a11 = a11; _bitmapKey.pointSize = pointSize; _bitmapKey.renderingMode = renderingMode; } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: The call to probe is checked for boundaries and pointer validity. /// This code is safe to call. /// [SecurityCritical, SecurityTreatAsSafe] public override void StoreKey(CheckedPointer d, out int realSize) { Debug.Assert(!FontSource.IsAppSpecific); int baseSize; StoreKeyInternal(d, out baseSize); baseSize = Util.Align4(baseSize); unsafe { realSize = baseSize + sizeof(BitmapBlock); void* dst = d.Probe(baseSize, sizeof(BitmapBlock)); *(BitmapBlock*)dst = _bitmapKey; } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// [SecurityCritical] public override void RetrieveKey(CheckedPointer m) { base.RetrieveKey(m); int baseSize = Util.Align4(base.Size); unsafe { BitmapBlock* s = (BitmapBlock*)m.Probe(baseSize, sizeof(BitmapBlock)); _bitmapKey = *s; } } ////// Critical: This code has unsafe code blocks It calls into HasMemory /// TreatAsSafe: This function is safe to call /// [SecurityCritical, SecurityTreatAsSafe] public override int GetHashCode() { int hash = base.GetHashCode(); unsafe { fixed (BitmapBlock* k = &_bitmapKey) { hash = HashFn.HashMemory(k, sizeof(BitmapBlock), hash); } } return HashFn.HashScramble(hash); } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: CheckedPointer construction is tracked and probe checks for invalid conditions /// [SecurityCritical, SecurityTreatAsSafe] public override bool Match(CheckedPointer p) { if (!base.Match(p)) return false; unsafe { BitmapBlock* b = (BitmapBlock*)p.Probe(Util.Align4(base.Size), sizeof(BitmapBlock)); return _bitmapKey.Equal(b); } } public override void GetData(CheckedPointer p, ElementCacher cacher) { unsafe { SetGlyphData(p + (Util.Align4(base.Size) + sizeof(BitmapBlock)), cacher); } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: Checked Pointer and ElementCacher are safe to work with. Construction is /// tracked for checkedpointer /// [SecurityCritical, SecurityTreatAsSafe] public override void AddToCache(CheckedPointer p, ElementCacher cacher) { int baseSize; StoreKeyInternal(p, out baseSize); baseSize = Util.Align4(baseSize); unsafe { BitmapBlock* b = (BitmapBlock*)p.Probe(baseSize, sizeof(BitmapBlock) + BitmapsPerBlock * sizeof(int)); *b = _bitmapKey; InitGlyphData(p + (baseSize + sizeof(BitmapBlock)), cacher); } } public override int Size { get { unsafe { return Util.Align4(base.Size) + sizeof(BitmapBlock) + BitmapsPerBlock * sizeof(int); } } } public override int Type { get { return 1; } } ////// Critical: This code calls into unsafe code blocks /// TreatAsSafe: This code allocates and returns an IntPtr that points to a bitmap. /// The IntPtr will fail to support any unsafe operations. /// [SecurityCritical, SecurityTreatAsSafe] private IntPtr AllocateBitmap(int size) { unsafe { // Reserve space to copy the bitmap header. byte* buffer = (byte*)AllocateNoThrow(sizeof(GlyphBitmap) + size); if (buffer == null) return IntPtr.Zero; // Pointer the rasterizer to the bitmap storage. return (IntPtr)(buffer + sizeof(GlyphBitmap)); } } //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647 ////// Critical - as this results in the rasterizer accessing font stream. /// TreatAsSafe - as this results in obtaining glyph bitmaps that the client is supposed to see anyway. /// [SecurityCritical, SecurityTreatAsSafe] internal override bool AddGlyph(ushort glyphIndex) { try { if (_rasterizer == null) { if (IsTrueType) _rasterizer = new TrueTypeRasterizer(); else CreateOtfRasterizer(); _fontStream = FontSource.GetUnmanagedStream(); _numberOfGlyphs = _rasterizer.NewFont(_fontStream, FontSource.Uri, FaceIndex); MS.Internal.FontRasterization.Transform tform = new MS.Internal.FontRasterization.Transform(); tform.a00 = _bitmapKey.a00; tform.a01 = _bitmapKey.a01; tform.a10 = _bitmapKey.a10; tform.a11 = _bitmapKey.a11; _rasterizer.NewTransform( _bitmapKey.pointSize, tform, (OverscaleMode)_bitmapKey.renderingMode, RenderingFlags); } if (glyphIndex >= _numberOfGlyphs) return false; MS.Internal.FontRasterization.GlyphBitmap glyphBitmap; GlyphMetrics glyphMetrics; try { _rasterizer.NewGlyph(glyphIndex); _rasterizer.GetBitmap( AllocateBitmap, // allocation delegate IntPtr.Zero, // current buffer -1, // sentinel value that forces the rasterizer to allocate memory even for empty glyphs // we use it to make sure glyph header is allocated out glyphBitmap, out glyphMetrics ); // Check whether glyph bitmap coordinates overflow the 16 bit range that GlyphCache.cs relies on. // Windows Client Task 50200 tracks a more complete solution to this issue that will ensure rasterizers // can display extremely large bitmaps without truncation. if ((short)glyphMetrics.horizontalOrigin.x != glyphMetrics.horizontalOrigin.x || (short)glyphMetrics.horizontalOrigin.y != glyphMetrics.horizontalOrigin.y || (short)glyphMetrics.horizontalAdvance != glyphMetrics.horizontalAdvance || (short)glyphMetrics.verticalOrigin.x != glyphMetrics.verticalOrigin.x || (short)glyphMetrics.verticalOrigin.y != glyphMetrics.verticalOrigin.y || (short)glyphMetrics.verticalAdvance != glyphMetrics.verticalAdvance || (ushort)glyphMetrics.width != glyphMetrics.width || (ushort)glyphMetrics.height != glyphMetrics.height || (ushort)glyphBitmap.stride != glyphBitmap.stride ) { throw new FileFormatException(FontSource.Uri); } if (glyphMetrics.width > (glyphBitmap.stride * 8)) { throw new System.ArgumentException("Rasterizer produced an invalid width", "glyphMetrics.width"); } } catch (FileFormatException) { unsafe { // Create empty bitmap with zero metrics for malformed glyph programs. GlyphBitmap* gb = (GlyphBitmap*)Allocate(sizeof(GlyphBitmap)); // Cache memory is zero initialized by default. Debug.Assert(gb->horOriginX == 0); Debug.Assert(gb->height == 0); SetGlyph(glyphIndex, gb); return true; } } catch (OutOfMemoryException e) { object delegateError = e.Data["GetMemoryDelegateError"]; // In case the exception was raised due to the delegate failure, // throw FontCacheFullException so that we have a chance to renew the cache and retry the request. if (delegateError != null && (bool)delegateError) throw new FontCacheFullException(); throw; } // Glyph rendering code requires stride to be a multiple of 4, // make sure glyph rasterizers do the same. Debug.Assert((glyphBitmap.stride & 3) == 0); unsafe { GlyphBitmap* gd = (GlyphBitmap*)((byte*)glyphBitmap.pixels - sizeof(GlyphBitmap)); gd->horOriginX = glyphMetrics.horizontalOrigin.x; gd->horOriginY = glyphMetrics.horizontalOrigin.y; gd->horAdvance = glyphMetrics.horizontalAdvance; gd->verOriginX = glyphMetrics.verticalOrigin.x; gd->verOriginY = glyphMetrics.verticalOrigin.y; gd->verAdvance = glyphMetrics.verticalAdvance; gd->stride = glyphBitmap.stride; gd->width = glyphMetrics.width; gd->height = glyphMetrics.height; SetGlyph(glyphIndex, gd); } return true; } catch (SEHException e) { throw Util.ConvertInPageException(FontSource, e); } } private BitmapBlock _bitmapKey; private const int BitmapsPerBlock = 128; }; ////// Glyph path element class. /// Represents a block of glyph outlines /// Layout is: /// glyph block structure | file name string | usEmResolution | table of blockSize pointers to glyph data /// [FriendAccessAllowed] internal sealed class GlyphPathElement : BaseGlyphElement { [StructLayout(LayoutKind.Sequential, Pack = 1)] private unsafe struct OutlineBlock { internal ushort designEmHeight; } ////// Outline data is prefixed by a header containing three integers. /// private static readonly int GlyphOutlineHeaderSize = 3 * sizeof(int); /// /// Create a GlyphPathElement for a specific font face /// internal GlyphPathElement(int faceIndex, FontSource fontSource, ushort renderingFlags, ushort designEmHeight) : base(OutlinesPerBlock, faceIndex, fontSource, renderingFlags) { _outlineKey.designEmHeight = designEmHeight; } ////// Critical - Calls into the critical RetrieveKey method. /// [SecurityCritical] internal GlyphPathElement(CheckedPointer key) : base(OutlinesPerBlock) { RetrieveKey(key); } public override int GetHashCode() { int hash = base.GetHashCode(); return HashFn.HashScramble(hash); } public override void GetData(CheckedPointer p, ElementCacher cacher) { unsafe { SetGlyphData( p + (Util.Align4(base.Size) + Util.Align4(sizeof(OutlineBlock))), cacher); } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: ElementCacher and Checkedpointer are safe to work with /// Also adding this to cache is a safe operation /// [SecurityCritical, SecurityTreatAsSafe] public override void AddToCache(CheckedPointer p, ElementCacher cacher) { int baseSize; StoreKeyInternal(p, out baseSize); baseSize = Util.Align4(baseSize); unsafe { OutlineBlock* b = (OutlineBlock*)p.Probe(baseSize, Util.Align4(sizeof(OutlineBlock)) + OutlinesPerBlock * sizeof(int)); *b = _outlineKey; InitGlyphData(p + (baseSize + Util.Align4(sizeof(OutlineBlock))), cacher); } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// TreatAsSafe: CheckedPointer is safe to work with. Probe does bounds checking and /// pointer validation. /// [SecurityCritical, SecurityTreatAsSafe] public override void StoreKey(CheckedPointer d, out int realSize) { Debug.Assert(!FontSource.IsAppSpecific); int baseSize; StoreKeyInternal(d, out baseSize); baseSize = Util.Align4(baseSize); unsafe { realSize = baseSize + sizeof(OutlineBlock); void* dst = d.Probe(baseSize, sizeof(OutlineBlock)); *(OutlineBlock*)dst = _outlineKey; } } ////// Critical: Calls into probe which is critical and also has unsafe code blocks /// [SecurityCritical] public override void RetrieveKey(CheckedPointer m) { base.RetrieveKey(m); int baseSize = Util.Align4(base.Size); unsafe { OutlineBlock* s = (OutlineBlock*)m.Probe(baseSize, sizeof(OutlineBlock)); _outlineKey = *s; } } public override int Size { get { unsafe { return Util.Align4(base.Size) + Util.Align4(sizeof(OutlineBlock)) + OutlinesPerBlock * sizeof(int); } } } public override int Type { get { return 4; } } ////// Critical: Allocates memory and calls into unsafe code. This code is also unsafe /// TreatAsSafe: This call returns an IntPtr which is safe to expose because if /// you try to do anything with it that is unsafe it will throw an exception /// [SecurityCritical, SecurityTreatAsSafe] private IntPtr AllocateOutline(int size) { unsafe { // Allocate room for vertical origin, length and the outline itself. byte* data = (byte*)AllocateNoThrow(GlyphOutlineHeaderSize + size); if (data == null) return IntPtr.Zero; return (IntPtr)(data + GlyphOutlineHeaderSize); } } ////// Critical - as this results in the rasterizer accessing font stream. /// TreatAsSafe - as this results in obtaining glyph outline that the client is supposed to see anyway. /// [SecurityCritical, SecurityTreatAsSafe] internal override bool AddGlyph(ushort glyphIndex) { try { if (_rasterizer == null) { if (IsTrueType) _rasterizer = new TrueTypeRasterizer(); else CreateOtfRasterizer(); _fontStream = FontSource.GetUnmanagedStream(); _numberOfGlyphs = _rasterizer.NewFont(_fontStream, FontSource.Uri, FaceIndex); // if (!IsTrueType) { MS.Internal.FontRasterization.Transform tform = new MS.Internal.FontRasterization.Transform(); tform.a01 = tform.a10 = 0; tform.a00 = tform.a11 = _outlineKey.designEmHeight * 0x10000; _rasterizer.NewTransform( 12, tform, OverscaleMode.None, RenderingFlags); } else { unsafe { _pathAllocator = new TrueTypeRasterizer.Allocator(Allocate); } } } if (glyphIndex >= _numberOfGlyphs) return false; if (IsGlyphCached(glyphIndex)) { // another thread already rasterized the glyph // so we can skip adding it return true; } if (!IsTrueType) { _rasterizer.NewGlyph(glyphIndex); GlyphMetrics glyphMetrics; GlyphOutline glyphOutline; try { _rasterizer.GetOutline( AllocateOutline, // allocation delegate IntPtr.Zero, // current buffer -1, // sentinel value that forces the rasterizer to allocate memory even for empty glyphs // we use it to make sure glyph header is allocated out glyphOutline, out glyphMetrics ); } catch (OutOfMemoryException e) { object delegateError = e.Data["GetMemoryDelegateError"]; // In case the exception was raised due to the delegate failure, // throw FontCacheFullException so that we have a chance to renew the cache and retry the request. if (delegateError != null && (bool)delegateError) throw new FontCacheFullException(); throw; } unsafe { int* p = (int*)((byte*)glyphOutline.outline - GlyphOutlineHeaderSize); p[0] = glyphMetrics.verticalOrigin.x; p[1] = glyphMetrics.verticalOrigin.y; p[2] = glyphOutline.length; SetGlyph(glyphIndex, p); } } else { unsafe { GlyphPathData* gpd = ((TrueTypeRasterizer)_rasterizer).GetPath(_pathAllocator, RenderingFlags, glyphIndex); SetGlyph(glyphIndex, gpd); } } return true; } catch (SEHException e) { throw Util.ConvertInPageException(FontSource, e); } } private TrueTypeRasterizer.Allocator _pathAllocator; private OutlineBlock _outlineKey; private const int OutlinesPerBlock = 128; }; ////// Wrapper class for glyph cache access. Getting glyph bitmaps and outlines is more complex than /// using other font cache elements because data for different glyphs can come from different caches. /// The caller should GC.KeepAlive an instance of FontCacheAccessor class while glyph data is being used, /// otherwise there is risk of cache being garbage collected while bitmap data is used. /// internal class FontCacheAccessor { private List_nativeCaches; private class FontFaceID { // // The font file name should not be accessible outside of SecurityCritical // functions. // internal SecurityCriticalData _fontFileName; internal int _faceIndex; internal int _scaleX; internal int _scaleY; internal ushort _flags; internal float _renderingEmSize; /// /// Helper function: wraps FontFace information into a FontFaceID struct /// internal FontFaceID(SecurityCriticalDatafontFileName, int fontFaceIndex, uint scaleX, uint scaleY, ushort glyphRunFlags, float emSize) { _fontFileName = fontFileName; _faceIndex = fontFaceIndex; _scaleX = (int)scaleX; _scaleY = (int)scaleY; _flags = glyphRunFlags; _renderingEmSize = emSize; if (_scaleY > _verticalAntialiasingThreashold) { // we are using VAA (vertical anti aliasing, that means 6*5 overscaled // bitmaps instead of regular 6*1) if font size is big, or if VAA // is forced by user _flags |= (ushort)MilGlyphRun.ForceVAA; } } } private const ushort _fontCacheFlagMask = (ushort)MilGlyphRun.Hinting | (ushort)MilGlyphRun.BoldSimulation | (ushort)MilGlyphRun.ItalicSimulation | (ushort)MilGlyphRun.Sideways | (ushort)MilGlyphRun.IsTrueType; private const ushort _faceFlagMask = _fontCacheFlagMask | (ushort)MilGlyphRun.ForceVAA | (ushort)MilGlyphRun.VerticalDropOut; private const uint _verticalAntialiasingThreashold = 20 * 0x10000; // Matches GLYPH_BASE_CREATE_REQUEST in native code unsafe private struct GLYPH_BASE_CREATE_REQUEST { #pragma warning disable 649 // These fields might not be initialized from managed code, but that's ok public uint uFontFaceIndex; // IN font typeface index public ushort usGlyphRunFlags; // IN GlyphRun flags public uint dScaleX; public uint dScaleY; public float muSize; // // // Validation has already been done on the font filename, so we protect it // here. // // [SecurityCritical] public char * pszFontFileName; // IN font filename string public ushort usGlyphCount; // IN number of glyphs in a glyph run public ushort * pusGlyphIndices; // IN glyph indices in a glyph run // (caller responsible for alloc/free) #pragma warning restore 649 } unsafe private struct GLYPH_GEOMETRY_CREATE_REQUEST { #pragma warning disable 649 // These fields might not be initialized from managed code, but that's ok #pragma warning disable 169 // baseRequest is not used directly, but is required for casting GLYPH_BASE_CREATE_REQUEST baseRequest; public float fBaseLineX; public float fBaseLineY; public float * pfGlyphPositions; #pragma warning restore 169 #pragma warning restore 649 } // Matches GLYPH_BITMAP_CREATE_REQUEST in native code unsafe private struct GLYPH_BITMAP_CREATE_REQUEST { #pragma warning disable 649 // These fields might not be initialized from managed code, but that's ok #pragma warning disable 169 // baseRequest is not used directly, but is required for casting GLYPH_BASE_CREATE_REQUEST baseRequest; public uint faceHandle; // IN internal font face identifier #pragma warning restore 169 #pragma warning restore 649 } ////// Callback entry point for unmanaged code to ask for glyph realizations. /// ////// Critical - calls critical code /// [SecurityCritical] internal static int CreateGlyphsCallback(IntPtr /*CMilSlaveGlyphCache* */ nativeObject, IntPtr /*GLYPH_BITMAP_CREATE_REQUEST | GLYPH_GEOMETRY_CREATE_REQUEST* */ request, ushort isGeometryRequest) { try { FontCacheAccessor fontCacheAccessor = new FontCacheAccessor(); fontCacheAccessor.CreateGlyphsAtRenderTime(nativeObject, request, isGeometryRequest); } catch (Exception e) { // // Need to catch all exceptions at this entry point and convert them into a // HRESULT that managed code can understand and handle. // e.ToString(); return HRESULT.E_FAIL; } return HRESULT.S_OK; } ////// Marshals glyph requests /// ////// Critical - calls critical code /// [SecurityCritical] private unsafe void CreateGlyphsAtRenderTime(IntPtr /*CMilSlaveGlyphCache* */ nativeObject, IntPtr /*GLYPH_BITMAP_CREATE_REQUEST | GLYPH_GEOMETRY_CREATE_REQUEST* */ request, ushort isGeometryRequest) { GLYPH_BASE_CREATE_REQUEST* pRequest = (GLYPH_BASE_CREATE_REQUEST*)request; string fontFileName = new String(pRequest->pszFontFileName); FontFaceID faceId = new FontFaceID(new SecurityCriticalData(fontFileName), (int)pRequest->uFontFaceIndex, pRequest->dScaleX, pRequest->dScaleY, pRequest->usGlyphRunFlags, pRequest->muSize); ushort[] glyphIndices = new ushort[pRequest->usGlyphCount]; int glyphCount = checked((int)pRequest->usGlyphCount); // Put Marshal.CopyArray or something in here for (uint n = 0; n < pRequest->usGlyphCount; n++) { glyphIndices[n] = pRequest->pusGlyphIndices[n]; } if (isGeometryRequest == 0) { GLYPH_BITMAP_CREATE_REQUEST* pRequestAsBitmap = (GLYPH_BITMAP_CREATE_REQUEST*)request; // Realize and send bitmaps CreateGlyphBitmapsHelper( nativeObject, faceId, glyphIndices, pRequestAsBitmap->faceHandle ); } else { GLYPH_GEOMETRY_CREATE_REQUEST* pRequestAsGlyphs = (GLYPH_GEOMETRY_CREATE_REQUEST*)request; // Realize and send geometry CreateGlyphGeometryHelper( nativeObject, faceId, glyphIndices, pRequestAsGlyphs->pfGlyphPositions, pRequestAsGlyphs->fBaseLineX, pRequestAsGlyphs->fBaseLineY ); } } /// /// Critical - as this accesses critical data and calls critical code /// [SecurityCritical] private unsafe void CreateGlyphGeometryHelper( IntPtr nativeGlyphRun, FontFaceID faceId, ushort[] glyphIndices, float *pGlyphPositions, float fBaseLineX, float fBaseLineY ) { Debug.Assert(glyphIndices.Length > 0); Geometry glyphRunGeometry = BuildGeometry(faceId, glyphIndices, pGlyphPositions, fBaseLineX, fBaseLineY); if (glyphRunGeometry.IsEmpty()) { return; } // If glyphRunGeometry is non empty, it is of type GeometryGroup, and all the // geometries inside are of type PathGeometry GeometryGroup glyphRunGroup = (GeometryGroup)glyphRunGeometry; Geometry.PathGeometryData glyphRunPathData = glyphRunGroup.GetAsPathGeometry().GetPathGeometryData(); // Packet layout is: // MILCMD_GLYPHRUN_SETRENDERGEOMETRY // MILCMD_PATHGEOMETRY // PathFigure data block (glyphRunPathData.SerializedData) // Allocate sufficient memory uint pathGeometrySize = (uint)sizeof(DUCE.MILCMD_PATHGEOMETRY); uint dataLength = (uint)glyphRunPathData.SerializedData.Length; uint packetSize = checked(pathGeometrySize + dataLength); byte[] packetBlock = new byte[packetSize]; fixed (byte* packet = packetBlock) { DUCE.MILCMD_PATHGEOMETRY *pathGeometryPacket = (DUCE.MILCMD_PATHGEOMETRY *)(packet); pathGeometryPacket->Type = MILCMD.MilCmdPathGeometry; //pathGeometryPacket->Transform = 0; pathGeometryPacket->FillRule = glyphRunGroup.FillRule; pathGeometryPacket->FiguresSize = glyphRunPathData.Size; byte* pathData = (byte *)(packet + pathGeometrySize); Marshal.Copy(glyphRunPathData.SerializedData, 0, (IntPtr)pathData, glyphRunPathData.SerializedData.Length); // Send packet HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphRun_SetGeometryAtRenderTime( nativeGlyphRun, (byte *)packet, packetSize )); } } ////// Obtains geometry for the glyph run. /// ///The geometry returned contains the combined geometry of all glyphs in the glyph run. /// Overlapping contours are merged by performing a Boolean union operation. ////// Critical - performs unsafe operations /// [SecurityCritical] private unsafe Geometry BuildGeometry( FontFaceID faceId, ushort[] glyphIndices, float *pPositions, float fBaseLineX, float fBaseLineY ) { GeometryGroup accumulatedGeometry = null; Uri fontURI = new Uri(faceId._fontFileName.Value, UriKind.Absolute); bool IsSideways = ((faceId._flags & (UInt16)MilGlyphRun.Sideways) != 0); bool IsLeftToRight = ((faceId._flags & (UInt16)MilGlyphRun.IsLeftToRight) != 0); // If there are Y positions the array is packed as positions XYXYXY etc, if there // are no Y positions, it's just XXX and Y is assumed to be 0. bool HasYPositions = ((faceId._flags & (UInt16)MilGlyphRun.HasYPositions) != 0); StyleSimulations style = StyleSimulations.None; if ((faceId._flags & (UInt16)MilGlyphRun.BoldSimulation) != 0) { style |= StyleSimulations.BoldSimulation; } if ((faceId._flags & (UInt16)MilGlyphRun.ItalicSimulation) != 0) { style |= StyleSimulations.ItalicSimulation; } GlyphTypeface glyphTypeface = new GlyphTypeface(fontURI, style, false); for (int i = 0; i < glyphIndices.Length; i++) { ushort glyphIndex = glyphIndices[i]; Geometry glyphGeometry = glyphTypeface.ComputeGlyphOutline(glyphIndex, IsSideways, faceId._renderingEmSize); if (glyphGeometry.IsEmpty()) continue; // transform glyphGeometry to the glyph origin unsafe { float xPosition; float yPosition; if (HasYPositions) { xPosition = ((float*)pPositions)[2*i]; yPosition = ((float*)pPositions)[2 * i + 1]; } else { // // If there are no Y positions, the first X position is always assumed to be 0 and is // ommitted, making the array one element smaller. If there are Y positions, all X // and Y positions are fully specified // xPosition = (i == 0) ? 0 : ((float*)pPositions)[i-1]; yPosition = 0; } // // If this is an RTL glyph run, xPosition will already be negative. We still need to subtract // the AdvanceWidth for the glyph though. For regular bitmap rendering, this happens in // CGlyphRunMaker::CalculateGlyphPositions() // float xOffset = (xPosition - (float)(IsLeftToRight ? 0 : glyphTypeface.GetAdvanceWidth(glyphIndex))) * faceId._renderingEmSize + fBaseLineX; float yOffset = yPosition * faceId._renderingEmSize + fBaseLineY; glyphGeometry.Transform = new TranslateTransform(xOffset, yOffset); } if (accumulatedGeometry == null) { accumulatedGeometry = new GeometryGroup(); accumulatedGeometry.FillRule = FillRule.Nonzero; } accumulatedGeometry.Children.Add(glyphGeometry.GetOutlinedPathGeometry(GlyphRun.RelativeFlatteningTolerance, ToleranceType.Relative)); } // Make sure to always return Geometry.Empty from public methods for empty geometries. if (accumulatedGeometry == null || accumulatedGeometry.IsEmpty()) return Geometry.Empty; return accumulatedGeometry; } ////// Critical - as this accesses critical data and calls GetBitmaps which is Critical. /// [SecurityCritical] private unsafe void CreateGlyphBitmapsHelper( IntPtr nativeGlyphCache, FontFaceID faceId, ushort[] glyphIndices, uint faceHandle ) { Debug.Assert(glyphIndices.Length > 0); // Should not pInvoke here when no glyphs are missing void*[] glyphBitmaps = new void*[glyphIndices.Length]; GetBitmaps( faceId._fontFileName.Value, faceId._faceIndex, faceId._scaleX, faceId._scaleY, (ushort)faceId._renderingEmSize, (faceId._flags & (uint)MilGlyphRun.ForceVAA) != 0 ? (ushort)MS.Internal.FontRasterization.OverscaleMode.OverscaleXandY : (ushort)MS.Internal.FontRasterization.OverscaleMode.OverscaleX, //renderingMode (ushort)(faceId._flags & _fontCacheFlagMask), //renderingFlags, glyphIndices.Length, glyphIndices, /*OUT*/ glyphBitmaps ); bool needDropOut = (faceId._flags & (uint)MilGlyphRun.VerticalDropOut) != 0; AddBitmapsToNativeCacheAtRenderTime(nativeGlyphCache, faceHandle, glyphBitmaps, glyphIndices, faceId._flags, needDropOut); } ////// Critical - as this accesses unsafe code blocks. /// [SecurityCritical] private unsafe void AddBitmapsToNativeCacheAtRenderTime(IntPtr nativeGlyphCache, uint faceHandle, void*[] glyphBitmaps, ushort[] glyphIndices, ushort faceFlags, bool needDropOut) { Debug.Assert(glyphBitmaps != null); Debug.Assert(glyphBitmaps.Length != 0); DUCE.MILCMD_GLYPHCACHE_ADDBITMAPS command; try { command.Type = MILCMD.MilCmdGlyphCacheAddBitmaps; command.FontFaceHandle = faceHandle; command.FaceFlags = faceFlags; command.GlyphCount = checked((UInt16)glyphIndices.Length); command.Handle = DUCE.ResourceHandle.Null; // Unused - MIL resource handle - bypassing DUCE channels // Precalculate size of command data buffer // The uint is used up by the MemWriter int dataSize = checked(sizeof(uint) + glyphIndices.Length * sizeof(ushort)); // Count the total size of the bitmaps. if (!needDropOut) { for (int i = 0; i < glyphIndices.Length; i++) { int blockSize = GetBitmapTransferSize((GlyphBitmap*)glyphBitmaps[i]); dataSize = checked(dataSize + blockSize); } } else { for (int i = 0; i < glyphIndices.Length; i++) { int blockSize = GetTransformedBitmapTransferSize((GlyphBitmap*)glyphBitmaps[i]); dataSize = checked(dataSize + blockSize); } } // Talk directly to native GlyphCache object HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_BeginCommandAtRenderTime( nativeGlyphCache, (byte*)&command, (UInt32)sizeof(DUCE.MILCMD_GLYPHCACHE_ADDBITMAPS), (UInt32)dataSize )); } catch (System.OverflowException e) { // re-throw more readable exception throw new OutOfMemoryException(SR.Get(SRID.TooManyGlyphRuns), e); } // Write the bitmaps if needed. // Bitmaps should go first because of aligning. if (!needDropOut) { for (int i = 0; i < glyphIndices.Length; i++) { TransferBitmapAtRenderTime(nativeGlyphCache, (GlyphBitmap*)glyphBitmaps[i]); } } else { for (int i = 0; i < glyphIndices.Length; i++) { TransferTransformedBitmapAtRenderTime(nativeGlyphCache, (GlyphBitmap*)glyphBitmaps[i]); } } // Write the glyph indices. fixed (ushort* pData = glyphIndices) { HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_AppendCommandDataAtRenderTime( nativeGlyphCache, (byte*)pData, (uint)(glyphIndices.Length * sizeof(ushort)))); } HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_EndCommandAtRenderTime( nativeGlyphCache )); } ////// Compose and transfer a piece of MILCMD_GLYPHCACHE_ADDBITMAPS command /// that represents given glyph bitmap. /// ////// Critical - as this accesses unsafe code blocks. /// [SecurityCritical] private unsafe void TransferBitmapAtRenderTime(IntPtr nativeGlyphCache, GlyphBitmap* pBitmap) { if (pBitmap == null) { TransferStubAtRenderTime(nativeGlyphCache); return; } DUCE.MILCMD_GLYPHBITMAP bitmap = new DUCE.MILCMD_GLYPHBITMAP(); bitmap.horOriginX = checked((Int16)(pBitmap->horOriginX)); bitmap.horOriginY = checked((Int16)(pBitmap->horOriginY)); bitmap.horAdvance = checked((Int16)(pBitmap->horAdvance)); bitmap.verOriginX = checked((Int16)(pBitmap->verOriginX)); bitmap.verOriginY = checked((Int16)(pBitmap->verOriginY)); bitmap.width = checked((UInt16)(pBitmap->width)); bitmap.height = checked((UInt16)(pBitmap->height)); bitmap.stride = checked((UInt16)(pBitmap->stride)); int cbDataSize = (int)bitmap.height * (int)bitmap.stride; HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_AppendCommandDataAtRenderTime( nativeGlyphCache, (byte*)&bitmap, (uint)sizeof(DUCE.MILCMD_GLYPHBITMAP))); if (cbDataSize != 0) { byte* pData = (byte*)pBitmap + sizeof(GlyphBitmap); // Write directly to slave GlyphCache HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_AppendCommandDataAtRenderTime( nativeGlyphCache, pData, (uint)cbDataSize)); } } ////// This routine is for seldom circumstances, when /// font cache unabled supply particular glyph bitmap. /// When happened so we'll replace glyph with special shape /// (currently empty) /// ////// Critical - as this accesses unsafe code blocks. /// [SecurityCritical] private unsafe void TransferStubAtRenderTime(IntPtr nativeGlyphCache) { // create empty struct GlyphBitmap in stack frame DUCE.MILCMD_GLYPHBITMAP bitmap = new DUCE.MILCMD_GLYPHBITMAP(); HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_AppendCommandDataAtRenderTime( nativeGlyphCache, (byte*)&bitmap, (uint)sizeof(DUCE.MILCMD_GLYPHBITMAP))); } ////// Pass the GlyphBitmap to command buffer, along the way /// making horizontal strokes *a little* thicker than given /// GlyphBitmap has. /// /// This thickening algorythm (also referred to as "virtual /// drop out control") is completely heuristic. It was /// initially proposed by Beat Stamm (beats). This version /// differs much from the initial. It thickens only /// horizontal strokes, increasing stroke width up to 2 virtual /// pixels. Heuristic prohibits thickening if it cause /// the stroke to bleed through pixel boundary, and also if /// next stroke is too close to given. /// ////// Critical - as this accesses unsafe code blocks. /// [SecurityCritical] private unsafe void TransferTransformedBitmapAtRenderTime(IntPtr slaveGlyphCache, GlyphBitmap* pBitmap) { if (pBitmap == null) { TransferStubAtRenderTime(slaveGlyphCache); return; } // Vertical drop out needs glyph bitmap (that's ovescaled 5 times by Y axis) // to be vertically aligned to physycal pixel grid. // We may need to allocate extra rows above and below // original image. int bitmapTop = -pBitmap->horOriginY; int bitmapBottom = bitmapTop + pBitmap->height; int pixelTop = bitmapTop >= 0 ? bitmapTop / 5 : -((4 - bitmapTop) / 5); int pixelBottom = bitmapBottom >= 0 ? (bitmapBottom + 4) / 5 : -((-bitmapBottom) / 5); int rowsAbove = bitmapTop - pixelTop * 5; int rowsBelow = pixelBottom * 5 - bitmapBottom; Debug.Assert(rowsAbove >= 0 && rowsAbove < 5); Debug.Assert(rowsBelow >= 0 && rowsBelow < 5); int newSize = checked( sizeof(DUCE.MILCMD_GLYPHBITMAP) + (pBitmap->height + rowsBelow + rowsAbove) * pBitmap->stride ); byte* pData = stackalloc byte[newSize]; DUCE.MILCMD_GLYPHBITMAP* pNewBitmap = (DUCE.MILCMD_GLYPHBITMAP*)pData; // fill bitmap header pNewBitmap->horOriginX = checked((Int16)(pBitmap->horOriginX)); pNewBitmap->horOriginY = checked((Int16)(pBitmap->horOriginY + rowsAbove)); pNewBitmap->horAdvance = checked((Int16)(pBitmap->horAdvance)); pNewBitmap->verOriginX = checked((Int16)(pBitmap->verOriginX - rowsAbove)); pNewBitmap->verOriginY = checked((Int16)(pBitmap->verOriginY)); pNewBitmap->width = checked((UInt16)(pBitmap->width)); pNewBitmap->height = checked((UInt16)(pBitmap->height + rowsAbove + rowsBelow)); pNewBitmap->stride = checked((UInt16)(pBitmap->stride)); UInt32* pSrc = (UInt32*)((byte*)pBitmap + sizeof(GlyphBitmap)); UInt32* pDst = (UInt32*)(pData + sizeof(DUCE.MILCMD_GLYPHBITMAP)); int stride32 = pBitmap->stride >> 2; // fill added rows above with zeros UInt32* pDstAbove = pDst; int sizeAbove = rowsAbove * stride32; for (int i = sizeAbove; --i >= 0; ) pDst[i] = 0; // copy bitmap bits UInt32* pDstOrigin = pDstAbove + sizeAbove; int sizeOrigin = stride32 * pBitmap->height; for (int i = sizeOrigin; --i >= 0; ) pDstOrigin[i] = pSrc[i]; // fill added rows above with zeros UInt32* pDstBelow = pDstOrigin + sizeOrigin; int sizeBelow = rowsBelow * stride32; for (int i = sizeBelow; --i >= 0; ) pDstBelow[i] = 0; UInt32* pColon = pDst; int bandsCount = pixelBottom - pixelTop; int colonsCount = (int)((pNewBitmap->width + 31) >> 5); Debug.Assert(colonsCount <= stride32); if (bandsCount == 1) { for (int i = 0; i < colonsCount; i++, pColon++) { UInt32* p0 = pColon; UInt32* p1 = p0 + stride32; UInt32* p2 = p1 + stride32; UInt32* p3 = p2 + stride32; UInt32* p4 = p3 + stride32; // bleeding up: set the bit if there is "one" // on next row but not two previous and second next. // The magic code below actually repeats many times // the same formula: // bit |= nextBit & ~next2Bit & ~previousBit & ~previous2Bit // where some members are removed if we are close to the // edge so some "next" and "previous" are missed. *p0 |= *p1 & ~*p2; *p1 |= ~*p0 & *p2 & ~*p3; *p2 |= ~*p0 & ~*p1 & *p3 & ~*p4; *p3 |= ~*p1 & ~*p2 & *p4; // bleed down: same as bleeding up but Y direction is reversed *p4 |= ~*p2 & *p3; *p3 |= ~*p1 & *p2 & ~*p4; *p2 |= ~*p0 & *p1 & ~*p3 & ~*p4; *p1 |= *p0 & ~*p2 & ~*p3; } } else { // there are two or more bands for (int i = 0; i < colonsCount; i++, pColon++) { UInt32* p0 = pColon; UInt32* p1 = p0 + stride32; UInt32* p2 = p1 + stride32; UInt32* p3 = p2 + stride32; UInt32* p4 = p3 + stride32; UInt32* p5 = p4 + stride32; UInt32* p6 = p5 + stride32; // Handle first band // bleed up *p0 |= *p1 & ~*p2; *p1 |= ~*p0 & *p2 & ~*p3; *p2 |= ~*p0 & ~*p1 & *p3 & ~*p4; *p3 |= ~*p1 & ~*p2 & *p4 & ~*p5; // bleed down *p4 |= ~*p2 & *p3 & ~*p5 & ~*p6; *p3 |= ~*p1 & *p2 & ~*p4 & ~*p5; *p2 |= ~*p0 & *p1 & ~*p3 & ~*p4; *p1 |= *p0 & ~*p2 & ~*p3; UInt32* q1 = p4; UInt32* q2 = p3; // Handle middle bands for (int b = bandsCount - 2; --b >= 0; ) { p0 = q1 + stride32; p1 = p0 + stride32; p2 = p1 + stride32; p3 = p2 + stride32; p4 = p3 + stride32; p5 = p4 + stride32; p6 = p5 + stride32; // bleed up *p0 |= ~*q2 & ~*q1 & *p1 & ~*p2; *p1 |= ~*q1 & ~*p0 & *p2 & ~*p3; *p2 |= ~*p0 & ~*p1 & *p3 & ~*p4; *p3 |= ~*p1 & ~*p2 & *p4 & ~*p5; // bleed down *p4 |= ~*p2 & *p3 & ~*p5 & ~*p6; *p3 |= ~*p1 & *p2 & ~*p4 & ~*p5; *p2 |= ~*p0 & *p1 & ~*p3 & ~*p4; *p1 |= ~*q1 & *p0 & ~*p2 & ~*p3; q1 = p4; q2 = p3; } // Handle last band p0 = q1 + stride32; p1 = p0 + stride32; p2 = p1 + stride32; p3 = p2 + stride32; p4 = p3 + stride32; // bleed up *p0 |= ~*q2 & ~*q1 & *p1 & ~*p2; *p1 |= ~*q1 & ~*p0 & *p2 & ~*p3; *p2 |= ~*p0 & ~*p1 & *p3 & ~*p4; *p3 |= ~*p1 & ~*p2 & *p4; // bleed down *p4 |= ~*p2 & *p3; *p3 |= ~*p1 & *p2 & ~*p4; *p2 |= ~*p0 & *p1 & ~*p3 & ~*p4; *p1 |= ~*q1 & *p0 & ~*p2 & ~*p3; } } HRESULT.Check(UnsafeNativeMethods.MilCoreApi.MilGlyphCache_AppendCommandDataAtRenderTime( slaveGlyphCache, (byte*)pData, (uint)newSize)); } ////// Calculate the size in command buffer, required for transferring /// given bitmap to rendering machine. /// /// ///size, in bytes ////// Critical - as this accesses unsafe code blocks. /// [SecurityCritical] private static unsafe int GetBitmapTransferSize(GlyphBitmap* pBitmap) { int cbSize = sizeof(DUCE.MILCMD_GLYPHBITMAP); if (pBitmap != null) { cbSize += pBitmap->height * pBitmap->stride; } return cbSize; } ////// Precalculate the size of MILCMD_GLYPHCACHE_ADDBITMAPS command segment /// that will be used by TransferTransformedBitmap /// /// given glyph bitmap ///size, in bytes ////// Critical: accepts a pointer and processes it with no validation /// [SecurityCritical] private static unsafe int GetTransformedBitmapTransferSize(GlyphBitmap* pBitmap) { int cbSize = sizeof(DUCE.MILCMD_GLYPHBITMAP); if (pBitmap != null) { // Vertical drop out needs glyph bitmap (that's ovescaled 5 times by Y axis) // to be vertically aligned to physycal pixel grid. // We may need to allocate extra rows above and below // original image. int bitmapTop = -pBitmap->horOriginY; int bitmapBottom = bitmapTop + pBitmap->height; int pixelTop = bitmapTop >= 0 ? bitmapTop / 5 : -((4 - bitmapTop) / 5); int pixelBottom = bitmapBottom >= 0 ? (bitmapBottom + 4) / 5 : -((-bitmapBottom) / 5); int rowsAbove = bitmapTop - pixelTop * 5; int rowsBelow = pixelBottom * 5 - bitmapBottom; Debug.Assert(rowsAbove >= 0 && rowsAbove < 5); Debug.Assert(rowsBelow >= 0 && rowsBelow < 5); cbSize += (pBitmap->height + rowsBelow + rowsAbove) * pBitmap->stride; } return cbSize; } internal FontCacheAccessor() { _nativeCaches = new List(2); } /// /// Critical - as this calls GetGlyphs() which is Critical and return font information /// via glyphBitmaps parameter. /// [SecurityCritical] internal unsafe void GetBitmaps( string fontFileName, int faceIndex, int scaleX, int scaleY, ushort pointSize, ushort renderingMode, ushort renderingFlags, int glyphCount, ushort[] glyphIndices, /*OUT*/ void*[] glyphBitmaps ) { try { using (GlyphBitmapElement elem = new GlyphBitmapElement( faceIndex, scaleX, 0, 0, scaleY, pointSize, renderingMode, renderingFlags, // We set skipDemand to true here because we should have validated // whether the caller can access this font previously. // At this point we don't have context about the caller any more. new FontSource(new Uri(fontFileName, UriKind.Absolute), true) )) { GetGlyphs( elem, new PartialList(glyphIndices, 0, glyphCount), glyphBitmaps ); } } // Disable PreSharp warning about empty catch bodies, please see comments below. #pragma warning disable 6502 // Don't fail because of malformed fonts, use empty glyph bitmap stubs. catch (FileFormatException) { } // Don't fail because of inaccessible fonts, use empty glyph bitmap stubs. catch (IOException) { } // Don't fail because of inaccessible fonts, use empty glyph bitmap stubs. catch (UnauthorizedAccessException) { } // Don't fail because of inaccessible fonts, use empty glyph bitmap stubs. catch (System.Net.WebException) { } #pragma warning restore 6502 catch (ArgumentOutOfRangeException e) { if (e.ParamName != "transform") throw; // Don't fail because of invalid transformations, because they can frequently happen in the process of animating text. } } /// /// Critical - as this calls the critical function GetServerCache() and exposes /// font cache data via glyphsArray parameter. /// [SecurityCritical] internal unsafe void GetGlyphs( BaseGlyphElement element, IListglyphIndices, void*[] glyphsArray ) { ElementCacher c; if (!element.IsAppSpecific) { c = CacheManager.GetServerCache(); if (c != null) { bool allFound = true; element.Reset(); for (int i = 0; i < glyphIndices.Count; ++i) { Debug.Assert(glyphsArray[i] == null); ushort glyph = glyphIndices[i]; ushort baseGlyph = element.GetBaseGlyph(glyph); if (element.GetBaseGlyph() != baseGlyph) { element.Reset(); element.SetBaseGlyph(baseGlyph); if (!c.ReadOnlyLookup(element)) { allFound = false; continue; } } if (element.GlyphData == null || !element.IsGlyphCached(glyph)) { allFound = false; continue; } CacheManager.SaveNativeCache(c, _nativeCaches); glyphsArray[i] = element.GetGlyph(glyph); Debug.Assert(glyphsArray[i] != null); } if (allFound) return; } } bool retry = false; c = CacheManager.GetCurrentCache(); Debug.Assert(c != null); element.Reset(); for (int i = 0; i < glyphIndices.Count; ++i) { if (glyphsArray[i] != null) continue; if (retry) { c = CacheManager.RenewCache(c); element.Reset(); } ushort glyph = glyphIndices[i]; ushort baseGlyph = element.GetBaseGlyph(glyph); try { if (element.GetBaseGlyph() != baseGlyph) { element.SetBaseGlyph(baseGlyph); if (!c.LookupAndAdd(element) && !element.IsAppSpecific) CacheManager.SendMissReport(element); } if (!element.IsGlyphCached(glyph)) { if (!element.AddGlyph(glyph)) { // client passed an invalid glyph index throw new ArgumentOutOfRangeException("glyph", SR.Get(SRID.GlyphIndexOutOfRange, glyph)); } } } catch (FontCacheFullException) { retry = true; --i; continue; } retry = false; CacheManager.SaveNativeCache(c, _nativeCaches); glyphsArray[i] = element.GetGlyph(glyph); Debug.Assert(glyphsArray[i] != null); } } }; } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- Trigger.cs
- OpenTypeCommon.cs
- SmtpAuthenticationManager.cs
- MissingMemberException.cs
- _RequestCacheProtocol.cs
- GridViewDeleteEventArgs.cs
- WebPartEventArgs.cs
- XmlRawWriter.cs
- SignedPkcs7.cs
- OdbcParameter.cs
- EdmConstants.cs
- TextRenderer.cs
- SystemBrushes.cs
- Dispatcher.cs
- WindowsScrollBar.cs
- DbConnectionClosed.cs
- Cursor.cs
- InheritanceContextChangedEventManager.cs
- EllipseGeometry.cs
- ImageButton.cs
- GACMembershipCondition.cs
- DbMetaDataFactory.cs
- FileFormatException.cs
- InheritanceContextChangedEventManager.cs
- WindowsImpersonationContext.cs
- Throw.cs
- Variable.cs
- LabelDesigner.cs
- BufferModeSettings.cs
- SqlParameter.cs
- SqlRemoveConstantOrderBy.cs
- PermissionToken.cs
- SqlBuilder.cs
- TTSEvent.cs
- Base64Stream.cs
- XmlFormatReaderGenerator.cs
- TimeSpanSecondsOrInfiniteConverter.cs
- IIS7WorkerRequest.cs
- CLRBindingWorker.cs
- EventHandlerList.cs
- OletxCommittableTransaction.cs
- TextEndOfLine.cs
- HelpProvider.cs
- DbConnectionStringCommon.cs
- UidPropertyAttribute.cs
- SqlSelectStatement.cs
- TreeViewImageIndexConverter.cs
- Operators.cs
- AuthorizationRule.cs
- DocumentApplicationJournalEntryEventArgs.cs
- Matrix3DConverter.cs
- CodeChecksumPragma.cs
- ValidationManager.cs
- _TimerThread.cs
- AnnotationResourceCollection.cs
- PolicyDesigner.cs
- EntityContainerAssociationSetEnd.cs
- URI.cs
- DataControlButton.cs
- WebCategoryAttribute.cs
- CornerRadius.cs
- StringCollectionMarkupSerializer.cs
- Command.cs
- XsltQilFactory.cs
- EventLogPermissionEntry.cs
- SByte.cs
- WinFormsUtils.cs
- PeerDuplexChannelListener.cs
- HostedController.cs
- FixedHyperLink.cs
- JoinCqlBlock.cs
- DropTarget.cs
- StyleBamlRecordReader.cs
- OdbcDataAdapter.cs
- AuthenticationException.cs
- Switch.cs
- ComboBoxAutomationPeer.cs
- ListBoxAutomationPeer.cs
- TemplateColumn.cs
- EntityContainerEmitter.cs
- Switch.cs
- SafeNativeMethodsOther.cs
- SmtpException.cs
- InplaceBitmapMetadataWriter.cs
- SmtpNegotiateAuthenticationModule.cs
- PropertyState.cs
- SqlFunctionAttribute.cs
- QuaternionAnimationBase.cs
- WebPartRestoreVerb.cs
- DecimalFormatter.cs
- BrowserCapabilitiesCompiler.cs
- ExtendedProtectionPolicyTypeConverter.cs
- FontUnit.cs
- TextViewBase.cs
- HybridDictionary.cs
- Rectangle.cs
- AbstractExpressions.cs
- ISAPIApplicationHost.cs
- SqlUdtInfo.cs
- CngAlgorithmGroup.cs