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(SecurityCriticalData fontFileName,
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,
IList glyphIndices,
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(SecurityCriticalData fontFileName,
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,
IList glyphIndices,
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
- HotSpotCollection.cs
- RemotingConfigParser.cs
- SessionStateSection.cs
- CryptoProvider.cs
- VirtualizingPanel.cs
- WebBrowserUriTypeConverter.cs
- FieldMetadata.cs
- RegexInterpreter.cs
- ComponentEditorForm.cs
- ExpressionLink.cs
- Model3DCollection.cs
- DeleteCardRequest.cs
- GetParentChain.cs
- LocalizedNameDescriptionPair.cs
- ServerValidateEventArgs.cs
- XsdBuilder.cs
- mediaeventshelper.cs
- GeneralTransform3DTo2D.cs
- COM2ColorConverter.cs
- AttributeData.cs
- pingexception.cs
- SiteMap.cs
- WebPartVerb.cs
- WindowsToolbarAsMenu.cs
- ErrorTableItemStyle.cs
- XamlFilter.cs
- DecimalSumAggregationOperator.cs
- ManagedCodeMarkers.cs
- Interlocked.cs
- TextContainerChangedEventArgs.cs
- List.cs
- TextRunCacheImp.cs
- ControlCollection.cs
- WindowsTreeView.cs
- DiscreteKeyFrames.cs
- GenericEnumerator.cs
- PermissionSetEnumerator.cs
- BindingManagerDataErrorEventArgs.cs
- LicenseProviderAttribute.cs
- SelectionListComponentEditor.cs
- TransactionProtocolConverter.cs
- QilCloneVisitor.cs
- ModelToObjectValueConverter.cs
- UITypeEditor.cs
- AdRotator.cs
- TableCell.cs
- Char.cs
- AsyncResult.cs
- CryptoApi.cs
- FixedDocument.cs
- ServiceNameElementCollection.cs
- DocumentPaginator.cs
- ExtensionWindowResizeGrip.cs
- X509CertificateValidator.cs
- Socket.cs
- XmlElementElement.cs
- LogRestartAreaEnumerator.cs
- TreeSet.cs
- ComboBoxAutomationPeer.cs
- DataGridViewComboBoxCell.cs
- PropertyInfo.cs
- CellLabel.cs
- DispatcherExceptionFilterEventArgs.cs
- ConnectionPoint.cs
- DecoderNLS.cs
- TickBar.cs
- ScrollChrome.cs
- TreeNodeStyleCollection.cs
- SqlDataSourceConfigureSelectPanel.cs
- DataError.cs
- BStrWrapper.cs
- CommandCollectionEditor.cs
- HotSpotCollection.cs
- NamedPipeTransportBindingElement.cs
- ChannelEndpointElementCollection.cs
- XPathNodeInfoAtom.cs
- PropertyReferenceSerializer.cs
- DescendantQuery.cs
- Error.cs
- ModuleElement.cs
- BindingNavigator.cs
- XomlSerializationHelpers.cs
- NonParentingControl.cs
- DbException.cs
- LabelInfo.cs
- MultipartIdentifier.cs
- Base64Encoder.cs
- Walker.cs
- StrokeNodeOperations.cs
- VectorCollectionConverter.cs
- ObjectComplexPropertyMapping.cs
- Span.cs
- Crypto.cs
- CodeLabeledStatement.cs
- GcHandle.cs
- DefaultBinder.cs
- odbcmetadatacollectionnames.cs
- DataServices.cs
- Shape.cs
- StaticContext.cs