Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Core / MS / Internal / Shaping / OpenTypeLayoutCache.cs / 1 / OpenTypeLayoutCache.cs
//---------------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // Description: The OpenTypeLayoutCache class is used by OpenType layout services // for performance optimizations. It is responsible for creating cache // structures as well as runtime support during time of actual layout calls. // // History: // 03/28/2005 : [....] - Created // //--------------------------------------------------------------------------- using System; using System.Security; using System.Security.Permissions; using System.IO; using System.Diagnostics; using System.Collections; using MS.Internal; using MS.Internal.FontCache; namespace MS.Internal.Shaping { // Cache is stroing for each glyph list of lookups where this glyph participate as // primary (first in rule sequence), and so can be substituted or positioned. // At runtime, main loop will attempt to apply lookups only to places where there // is a potential for substitution. // // Cache is of following structure. It contains a list of all glyphs that are affected // and each glyph has a list of lookups associated with it. Each list is terminated by 0xffff // value for glyph index. // // Glyph1 -> Lookup1.1, Lookup1.2, ..., 0xffff // ... // GlyphN -> LookupN.1, LookupN.2, ... 0xfffff // // Binary structure consists of a header, list of glyphs and offsets and area where actual // lookup lists reside. Offsets to lookup lists are relative to the start of the table: // // USHORT totalCacheSize // total size of table cache // USHORT 0xFFFF // glyphs not present assumed pointing to this empty list // USHORT lookupCount // number of lookups fit into cache // USHORT glyphCount // number of glyph records in the cache // ( // array of glyph records, each has // USHORT glyphId, // glyph id and // USHORT lookupListOffset // offset to lookup list, from the cache start // ) [glyphCount] // // USHORT[] lookupLists // Here is the area where lookup lists reside. Each list // // is in ascending order, terminataed by 0xffff. Several glyphs // // may point to the same list, for saving space. // // Cache buildig code in CreateTableCache does simple size optimiation, comparing two // consecutive glyphs if they have the same list of glyphs and point both to the same // physical list. If all lookups can not fit into cache, cache will remember how many // actually fit and simply assume that the rest of lookups applicable to all glyphs. // // During runtime, OTLS supports array of pointers to cache lookup lists, parallel to // array of glyphs. When processing lookup 'i', all pointers a being moved furhter through // the list to point to lookup that is >= i. Simple loop through pointers (FindNextLookup) // allows to find next lookup index to be processed. Then in FindNextGlyphInLookup, going // through pointers allows to find next glyph that should be tried for this lookup. // // Since every list is terminated by 0xffff, loop in FindNextLookup: // while(*cachePointers[i] < firstLookupIndex) cachePointers[i]++; // will never overrun the cache, and so does not require special check or additional data // to indicate end of the list. // ////// Implements OpenType layout services cache logic at both caching and using time /// internal static class OpenTypeLayoutCache { ////// Critical: Calls critical code and has unsafe code blocks /// TreatAsSafe: Pointers accessed are checked by probe. /// [SecurityCritical, SecurityTreatAsSafe] public static void InitCache( IOpenTypeFont font, OpenTypeTags tableTag, GlyphInfoList glyphInfo, OpenTypeLayoutWorkspace workspace ) { Debug.Assert(tableTag == OpenTypeTags.GSUB || tableTag == OpenTypeTags.GPOS); CheckedPointer cachePointer = font.GetTableCache(tableTag); unsafe { if (cachePointer.IsNull) { workspace.TableCacheData = null; } else { workspace.TableCacheData = (byte*)cachePointer.Probe(0, cachePointer.Size); workspace.AllocateCachePointers(glyphInfo.Length); RenewPointers(glyphInfo, workspace, 0, glyphInfo.Length); } } } ////// Critical: Calls critical code /// [SecurityCritical] public static void OnGlyphsChanged( OpenTypeLayoutWorkspace workspace, GlyphInfoList glyphInfo, int oldLength, int firstGlyphChanged, int afterLastGlyphChanged ) { unsafe { if (workspace.TableCacheData == null) { return; } } workspace.UpdateCachePointers(oldLength, glyphInfo.Length, firstGlyphChanged, afterLastGlyphChanged); RenewPointers(glyphInfo, workspace, firstGlyphChanged, afterLastGlyphChanged); } ////// Gets number of lookups that fit into table cache /// /// In: Storage for buffers we need ///Number of lookups in cache ////// Critical: Accesses font cache pointers /// [SecurityCritical] private static unsafe ushort GetCacheLookupCount(OpenTypeLayoutWorkspace workspace) { ushort* pCache = (ushort*)workspace.TableCacheData; // If there is no chache, just exit if (pCache == null) { return 0; } return pCache[2]; } ////// Find next glyph in lookup. Depending on search direction, /// it will update either firstGlyph or afterLastGlyph /// /// In: Storage for buffers we need /// In: Glyph run /// In: Minimal lookup index to search for. /// Out: Lookup index found /// Out: First applicable glyph for this lookup ///True if any lookup found, false otherwise ////// Critical: Accesses font cache pointers /// [SecurityCritical] public static unsafe void FindNextLookup( OpenTypeLayoutWorkspace workspace, GlyphInfoList glyphInfo, ushort firstLookupIndex, out ushort lookupIndex, out int firstGlyph ) { if (firstLookupIndex >= GetCacheLookupCount(workspace)) { // For lookups that did not fit into cache, just say we should always try it lookupIndex = firstLookupIndex; firstGlyph = 0; return; } ushort*[] cachePointers = workspace.CachePointers; int glyphCount = glyphInfo.Length; lookupIndex = 0xffff; firstGlyph = 0; for(int i = 0; i < glyphCount; i++) { // Sync up inside the list up to the minimal lookup requested // No additional boundary checks are necessary, because every list terminates with 0xffff while(*cachePointers[i] < firstLookupIndex) cachePointers[i]++; //Now we know that our index is higher or equal than firstLookup index if (*cachePointers[i] < lookupIndex) { // We now have new minimum lookupIndex = *(cachePointers[i]); firstGlyph = i; } } if (lookupIndex == 0xffff) { // We can't just say we are done, there may be lookups that did not fit into cache lookupIndex = GetCacheLookupCount(workspace); firstGlyph = 0; } } ////// Find next glyph in lookup. Depending on search direction, /// it will update either firstGlyph or afterLastGlyph /// /// Storage for buffers we need /// Current lookup in processing /// Do we go forward or backwards /// first glyph of search range /// position after last glyph ///True if any glyph found, false otherwise ////// Critical: unsafe pointer operations /// [SecurityCritical] public static unsafe bool FindNextGlyphInLookup( OpenTypeLayoutWorkspace workspace, ushort lookupIndex, bool isLookupReversal, ref int firstGlyph, ref int afterLastGlyph ) { if (lookupIndex >= GetCacheLookupCount(workspace)) { return true; } ushort*[] cachePointers = workspace.CachePointers; if (!isLookupReversal) { for (int i = firstGlyph; i < afterLastGlyph; i++) { if (*cachePointers[i] == lookupIndex) { firstGlyph = i; return true; } } return false; } else { for(int i = afterLastGlyph - 1; i >= firstGlyph; i--) { if (*cachePointers[i] == lookupIndex) { afterLastGlyph = i + 1; return true; } } return false; } } ////// Critical: unsafe pointer operations /// [SecurityCritical] private static unsafe void RenewPointers( GlyphInfoList glyphInfo, OpenTypeLayoutWorkspace workspace, int firstGlyph, int afterLastGlyph ) { byte* pCache = workspace.TableCacheData; // If there is no chache, just exit if (pCache == null) { return; } ushort*[] cachePointers = workspace.CachePointers; for(int i = firstGlyph; i < afterLastGlyph; i++) { ushort glyph = glyphInfo.Glyphs[i]; // If glyph is not there, we will point to the constant 0xFFFF in the cache int listOffset = 2; //Find glyph entry in the cache int glyphCount = *((ushort*)pCache + 3); ushort* pGlyphs = (ushort*)pCache + 4; int low = 0, high = glyphCount; while (low < high) { int mid = (low + high) >> 1; ushort midGlyph = pGlyphs[mid * 2]; if (glyph < midGlyph) { high = mid; continue; } if (glyph > midGlyph) { low = mid + 1; continue; } // Found it! listOffset = pGlyphs[mid * 2 + 1]; break; } // Whether we found glyph in the cache or not, // Pointer will be set to the list, but it may be empty. cachePointers[i] = (ushort*)(pCache + listOffset); } } #region Cache filling ////// Critical: Calls critical code /// [SecurityCritical] internal static void CreateCache(IOpenTypeFont font, int maxCacheSize) { if (maxCacheSize > ushort.MaxValue) { // Data structures do not support cache sizes more than 64K. maxCacheSize = ushort.MaxValue; } int tableCacheSize; int totalSize = 0; CreateTableCache(font, OpenTypeTags.GSUB, maxCacheSize - totalSize, out tableCacheSize); totalSize += tableCacheSize; Debug.Assert(totalSize <= maxCacheSize); CreateTableCache(font, OpenTypeTags.GPOS, maxCacheSize - totalSize, out tableCacheSize); totalSize += tableCacheSize; Debug.Assert(totalSize <= maxCacheSize); } ////// Critical: calling FillTableCache to change cache content in unmanaged memory /// [SecurityCritical] private static void CreateTableCache(IOpenTypeFont font, OpenTypeTags tableTag, int maxCacheSize, out int tableCacheSize) { // Initialize all computed values tableCacheSize = 0; int cacheSize = 0; int recordCount = 0; int glyphCount = 0; int lastLookupAdded = -1; GlyphLookupRecord[] records = null; try { ComputeTableCache( font, tableTag, maxCacheSize, ref cacheSize, ref records, ref recordCount, ref glyphCount, ref lastLookupAdded ); } catch (FileFormatException) { cacheSize = 0; } if (cacheSize > 0) { tableCacheSize = FillTableCache( font, tableTag, cacheSize, records, recordCount, glyphCount, lastLookupAdded ); } } ////// Critical: Accessing raw font table /// [SecurityCritical] private static void ComputeTableCache( IOpenTypeFont font, OpenTypeTags tableTag, int maxCacheSize, ref int cacheSize, ref GlyphLookupRecord[] records, ref int recordCount, ref int glyphCount, ref int lastLookupAdded ) { FontTable table = font.GetFontTable(tableTag); if (!table.IsPresent) { return; } FeatureList featureList; LookupList lookupList; Debug.Assert(tableTag == OpenTypeTags.GSUB || tableTag == OpenTypeTags.GPOS); switch (tableTag) { case OpenTypeTags.GSUB: { GSUBHeader header = new GSUBHeader(); featureList = header.GetFeatureList(table); lookupList = header.GetLookupList(table); break; } case OpenTypeTags.GPOS: { GPOSHeader header = new GPOSHeader(); featureList = header.GetFeatureList(table); lookupList = header.GetLookupList(table); break; } default: { Debug.Assert(false); featureList = new FeatureList(0); lookupList = new LookupList(0); break; } } // Estimate number of records that can fit into cache using ratio of approximately // 4 bytes of cache per actual record. Most of fonts will fit into this value, except // some tiny caches and big EA font that can have ratio of around 5 (theoretical maximum is 8). // // If actual ratio for particluar font will be larger than 4, we will remove records // from the end to fit into cache. // // If ratio is less than 4 we actually can fit more lookups, but for the speed and because most fonts // will fit into cache completely anyway we do not do anything about this here. int maxRecordCount = maxCacheSize / 4; // For now, we will just allocate array of maximum size. // Given heuristics above, it wont be greater than max cache size. // records = new GlyphLookupRecord[maxRecordCount]; // // Now iterate through lookups and subtables, filling in lookup-glyph pairs list // int lookupCount = lookupList.LookupCount(table); int recordCountAfterLastLookup = 0; // // Not all lookups can be invoked from feature directly, // they are actions from contextual lookups. // We are not interested in those, because they will // never work from high level, so do not bother adding them to the cache. // // Filling array of lookup usage bits, to skip those not mapped to any lookup // BitArray lookupUsage = new BitArray(lookupCount); for (ushort feature = 0; feature < featureList.FeatureCount(table); feature++) { FeatureTable featureTable = featureList.FeatureTable(table, feature); for (ushort lookup = 0; lookup < featureTable.LookupCount(table); lookup++) { ushort lookupIndex = featureTable.LookupIndex(table, lookup); if (lookupIndex >= lookupCount) { // This must be an invalid font. Just igonoring this lookup here. continue; } lookupUsage[lookupIndex] = true; } } // Done with lookup usage bits for(ushort lookupIndex = 0; lookupIndex < lookupCount; lookupIndex++) { if (!lookupUsage[lookupIndex]) { continue; } int firstLookupRecord = recordCount; int maxLookupGlyph = -1; bool cacheIsFull = false; LookupTable lookup = lookupList.Lookup(table, lookupIndex); ushort lookupType = lookup.LookupType(); ushort subtableCount = lookup.SubTableCount(); for(ushort subtableIndex = 0; subtableIndex < subtableCount; subtableIndex++) { int subtableOffset = lookup.SubtableOffset(table, subtableIndex); CoverageTable coverage = GetSubtablePrincipalCoverage(table, tableTag, lookupType, subtableOffset); if (coverage.IsInvalid) continue; cacheIsFull = !AppendCoverageGlyphRecords(table, lookupIndex, coverage, records, ref recordCount, ref maxLookupGlyph); if (cacheIsFull) break; } if (cacheIsFull) break; lastLookupAdded = lookupIndex; recordCountAfterLastLookup = recordCount; } // We may hit storage overflow in the middle of lookup. Throw this partial lookup away recordCount = recordCountAfterLastLookup; if (lastLookupAdded == -1) { // We did not succeed adding even single lookup. return; } // We now have glyph records for (may be not all) lookups in the table. // Cache structures should be sorted by glyph, then by lookup index. Array.Sort(records, 0, recordCount); cacheSize = -1; glyphCount = -1; // It may happen, that records do not fit into cache, even using our heuristics. // We will remove lookups one by one from the end until it fits. while (recordCount > 0) { CalculateCacheSize(records, recordCount, out cacheSize, out glyphCount); if (cacheSize <= maxCacheSize) { // Fine, we now fit into max cache size break; } else { // Find last lookup index int lastLookup = -1; for(int i = 0; i < recordCount; i++) { int lookup = records[i].Lookup; if (lastLookup < lookup) { lastLookup = lookup; } } Debug.Assert(lastLookup >= 0); // There are lookups, so there was an index // Remove it int currentRecord = 0; for(int i = 0; i < recordCount; i++) { if (records[i].Lookup == lastLookup) continue; if (currentRecord == i) continue; records[currentRecord] = records[i]; currentRecord++; } recordCount = currentRecord; // Do not forget update lastLookupAdded variable lastLookupAdded = lastLookup - 1; } } if (recordCount == 0) { // We can't fit even single lookup into the cache return; } Debug.Assert(cacheSize > 0); // We've calcucalted them at least ones, and Debug.Assert(glyphCount > 0); // if there is no records, we already should've exited } ////// Critical: unsafe pointer operations /// [SecurityCritical] private static int FillTableCache( IOpenTypeFont font, OpenTypeTags tableTag, int cacheSize, GlyphLookupRecord[] records, int recordCount, int glyphCount, int lastLookupAdded ) { // Fill the cache. // We are using basically the same code to fill the cache // that had been used to calculate the size. So pList pointer // moving through cache memory should not overrun allocated space. // Asserts are set to chek that at every place where we write to cache // and at the end where we check that we filled exactly the same amount. unsafe { CheckedPointer cache = font.AllocateTableCache(tableTag, cacheSize); if (cache.IsNull) { // We failed to allocate cache of requested size, // exit without created cache. return 0; } ushort* pCache = (ushort*)cache.Probe(0, cacheSize); pCache[0] = (ushort)cacheSize; // Cache size pCache[1] = 0xFFFF; // 0xFFFF constants pCache[2] = (ushort)(lastLookupAdded + 1); // Number of lookups that fit into the cache pCache[3] = (ushort)glyphCount; // Glyph count ushort* pGlyphs = pCache + 4; ushort* pList = pGlyphs + glyphCount * 2; ushort* pPrevList = null; int prevListIndex = -1, prevListLength = 0; int curListIndex = 0, curListLength = 1; ushort curGlyph = records[0].Glyph; for (int i = 1; i < recordCount; i++) { if (records[i].Glyph != curGlyph) { // We've found another list. Compare it with previous if (prevListLength != curListLength || // Fast check to avoid full comparison !CompareGlyphRecordLists(records, recordCount, prevListIndex, curListIndex) ) { // New list. Remember position in pPrevList and write list down pPrevList = pList; for(int j = curListIndex; j < i; j++) { Debug.Assert((pList - pCache) * sizeof(ushort) < cacheSize); *pList = records[j].Lookup; pList++; } Debug.Assert((pList - pCache) * sizeof(ushort) < cacheSize); *pList = 0xFFFF; pList++; } // Now pPrevList points at the first element of the correct list. *pGlyphs = curGlyph; // Write down glyph id pGlyphs++; *pGlyphs = (ushort)((pPrevList - pCache) * sizeof(ushort)); // Write down list offset pGlyphs++; prevListIndex = curListIndex; prevListLength = curListLength; curGlyph = records[i].Glyph; curListIndex = i; curListLength = 1; } } // And we need to check the last list we missed in the loop if (prevListLength != curListLength || // Fast check to avoid full comparison !CompareGlyphRecordLists(records, recordCount, prevListIndex, curListIndex) ) { // New list. Remember position in pPrevList and write list down pPrevList = pList; for (int j = curListIndex; j < recordCount; j++) { Debug.Assert((pList - pCache) * sizeof(ushort) < cacheSize); *pList = records[j].Lookup; pList++; } Debug.Assert((pList - pCache) * sizeof(ushort) < cacheSize); *pList = 0xFFFF; pList++; } // Now pPrevList points at the first element of the correct list. *pGlyphs = curGlyph; // Write down glyph id pGlyphs++; *pGlyphs = (ushort)((pPrevList - pCache) * sizeof(ushort)); // Write down list offset pGlyphs++; // We are done with the cache Debug.Assert((pList - pCache) * sizeof(ushort) == cacheSize); // We exactly filled up the cache Debug.Assert((pGlyphs - pCache) * sizeof(ushort) == (4 + glyphCount * 2) * sizeof(ushort)); // Glyphs ended where lists start. } return cacheSize; } private static void CalculateCacheSize(GlyphLookupRecord[] records, int recordCount, out int cacheSize, out int glyphCount ) { // Calc cache size glyphCount = 1; int listCount = 0; int entryCount = 0; int prevListIndex = -1, prevListLength = 0; int curListIndex = 0, curListLength = 1; ushort curGlyph = records[0].Glyph; for(int i = 1; i < recordCount; i++) { if (records[i].Glyph != curGlyph) { ++glyphCount; // We've found another list. Compare it with previous if (prevListLength != curListLength || // Fast check to avoid full comparison !CompareGlyphRecordLists(records, recordCount, prevListIndex, curListIndex) ) { listCount++; entryCount += curListLength; } prevListIndex = curListIndex; prevListLength = curListLength; curGlyph = records[i].Glyph; curListIndex = i; curListLength = 1; } else { ++curListLength; } } // And we need to check the last list we missed in the loop if (prevListLength != curListLength || // Fast check to avoid full comparison !CompareGlyphRecordLists(records, recordCount, prevListIndex, curListIndex) ) { listCount++; entryCount += curListLength; } cacheSize = sizeof(ushort) * ( 1 + // TotalCacheSize 1 + // Constant 0xFFFF, so we can point to it from glyphs that are not there 1 + // Number of lookups that fit into the cache 1 + // glyph count glyphCount * 2 + // {glyphId; listOffset} per glyph entryCount + // Each entry has lookup index listCount // Plus, terminator entry for each list ); } private static bool CompareGlyphRecordLists( GlyphLookupRecord[] records, int recordCount, int glyphListIndex1, int glyphListIndex2 ) { ushort listGlyph1 = records[glyphListIndex1].Glyph; ushort listGlyph2 = records[glyphListIndex2].Glyph; while (true) { ushort glyph1, glyph2; ushort lookup1, lookup2; if (glyphListIndex1 != recordCount) { glyph1 = records[glyphListIndex1].Glyph; lookup1 = records[glyphListIndex1].Lookup; } else { // Just emulate something that will be never in the real input glyph1 = 0xffff; lookup1 = 0xffff; } if (glyphListIndex2 != recordCount) { glyph2 = records[glyphListIndex2].Glyph; lookup2 = records[glyphListIndex2].Lookup; } else { // Just emulate something that will be never in the real input. glyph2 = 0xffff; lookup2 = 0xffff; } if (glyph1 != listGlyph1 && glyph2 != listGlyph2) { // Both lists are ended at the same time. return true; } if (glyph1 != listGlyph1 || glyph2 != listGlyph2) { // One list is ended, another does not. return false; } if (lookup1 != lookup2) { // We have different lookups on the lists. return false; } //Lists match so far, move further ++glyphListIndex1; ++glyphListIndex2; } } ////// Critical: Calls critical code /// [SecurityCritical] private static CoverageTable GetSubtablePrincipalCoverage( FontTable table, OpenTypeTags tableTag, ushort lookupType, int subtableOffset ) { Debug.Assert(tableTag == OpenTypeTags.GSUB || tableTag == OpenTypeTags.GPOS); CoverageTable coverage = CoverageTable.InvalidCoverage; switch (tableTag) { case OpenTypeTags.GSUB: if (lookupType == 7) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(table); subtableOffset = extension.LookupSubtableOffset(table); } switch (lookupType) { case 1: //SingleSubst SingleSubstitutionSubtable singleSubst = new SingleSubstitutionSubtable(subtableOffset); return singleSubst.GetPrimaryCoverage(table); case 2: //MultipleSubst MultipleSubstitutionSubtable multipleSub = new MultipleSubstitutionSubtable(subtableOffset); return multipleSub.GetPrimaryCoverage(table); case 3: //AlternateSubst AlternateSubstitutionSubtable alternateSub = new AlternateSubstitutionSubtable(subtableOffset); return alternateSub.GetPrimaryCoverage(table); case 4: //Ligature subst LigatureSubstitutionSubtable ligaSub = new LigatureSubstitutionSubtable(subtableOffset); return ligaSub.GetPrimaryCoverage(table); case 5: //ContextualSubst ContextSubtable contextSub = new ContextSubtable(subtableOffset); return contextSub.GetPrimaryCoverage(table); case 6: //ChainingSubst ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); return chainingSub.GetPrimaryCoverage(table); case 7: //Extension lookup // Ext.Lookup processed earlier. It can't contain another ext.lookups in it break; case 8: //ReverseCahiningSubst ReverseChainingSubtable reverseChainingSub = new ReverseChainingSubtable(subtableOffset); return reverseChainingSub.GetPrimaryCoverage(table); } break; case OpenTypeTags.GPOS: if (lookupType == 9) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(table); subtableOffset = extension.LookupSubtableOffset(table); } switch (lookupType) { case 1: //SinglePos SinglePositioningSubtable singlePos = new SinglePositioningSubtable(subtableOffset); return singlePos.GetPrimaryCoverage(table); case 2: //PairPos PairPositioningSubtable pairPos = new PairPositioningSubtable(subtableOffset); return pairPos.GetPrimaryCoverage(table); case 3: // CursivePos CursivePositioningSubtable cursivePos = new CursivePositioningSubtable(subtableOffset); return cursivePos.GetPrimaryCoverage(table); case 4: //MarkToBasePos MarkToBasePositioningSubtable markToBasePos = new MarkToBasePositioningSubtable(subtableOffset); return markToBasePos.GetPrimaryCoverage(table); case 5: //MarkToLigaturePos // Under construction MarkToLigaturePositioningSubtable markToLigaPos = new MarkToLigaturePositioningSubtable(subtableOffset); return markToLigaPos.GetPrimaryCoverage(table); case 6: //MarkToMarkPos MarkToMarkPositioningSubtable markToMarkPos = new MarkToMarkPositioningSubtable(subtableOffset); return markToMarkPos.GetPrimaryCoverage(table); case 7: // Contextual ContextSubtable contextPos = new ContextSubtable(subtableOffset); return contextPos.GetPrimaryCoverage(table); case 8: // Chaining ChainingSubtable chainingPos = new ChainingSubtable(subtableOffset); return chainingPos.GetPrimaryCoverage(table); case 9: //Extension lookup // Ext.Lookup processed earlier. It can't contain another ext.lookups in it break; } break; } return CoverageTable.InvalidCoverage; } ////// Append lookup coverage table to the list. /// /// Font table /// Lookup index /// Lookup principal coverage /// Record array /// Real number of records in record array /// Highest glyph index that we saw in this lookup ///Returns false if we are out of list space ////// Critical: Calls critical code /// [SecurityCritical] private static bool AppendCoverageGlyphRecords( FontTable table, ushort lookupIndex, CoverageTable coverage, GlyphLookupRecord[] records, ref int recordCount, ref int maxLookupGlyph ) { switch (coverage.Format(table)) { case 1: ushort glyphCount = coverage.Format1GlyphCount(table); for(ushort i = 0; i < glyphCount; i++) { ushort glyph = coverage.Format1Glyph(table, i); if (!AppendGlyphRecord(glyph, lookupIndex, records, ref recordCount, ref maxLookupGlyph)) { // We've failed to add another record. return false; } } break; case 2: ushort rangeCount = coverage.Format2RangeCount(table); for(ushort i = 0; i < rangeCount; i++) { ushort firstGlyph = coverage.Format2RangeStartGlyph(table, i); ushort lastGlyph = coverage.Format2RangeEndGlyph(table, i); for(int glyph = firstGlyph; glyph <= lastGlyph; glyph++) { if (!AppendGlyphRecord((ushort)glyph, lookupIndex, records, ref recordCount, ref maxLookupGlyph)) { // We've failed to add another record. return false; } } } break; } return true; } ////// Append record to the list, but first check if we have duplicate. /// /// Glyph /// Lookup index /// Record array /// Real number of records in record array /// Highest glyph index that we saw in this lookup ///Returns false if we are out of list space private static bool AppendGlyphRecord( ushort glyph, ushort lookupIndex, GlyphLookupRecord[] records, ref int recordCount, ref int maxLookupGlyph ) { if (glyph == maxLookupGlyph) { // It is exactly max, which means we already've seen it before. return true; } if (glyph > maxLookupGlyph) { // This should be very common - coverage tables are ordered by glyph index. maxLookupGlyph = glyph; } else { // We will go through records to check for duplicate. Debug.Assert(recordCount > 0); // Otherwise, we would go into (glyph > maxGlyphLookup); for(int i = recordCount - 1; i >= 0; i--) { if (records[i].Lookup != lookupIndex) { // We've iterated through all lookup records // (and haven't found duplicate) break; } if (records[i].Glyph == glyph) { // We found duplicate, no need to do anything. return true; } } } // Now, we need to add new record if (recordCount == records.Length) { // There is no space for new record. return false; } records[recordCount] = new GlyphLookupRecord(glyph, lookupIndex); recordCount++; return true; } private class GlyphLookupRecord : IComparable{ private ushort _glyph; private ushort _lookup; public GlyphLookupRecord(ushort glyph, ushort lookup) { _glyph = glyph; _lookup = lookup; } public ushort Glyph { get { return _glyph; } } public ushort Lookup { get { return _lookup; } } // Records will be sorted by glyph, then by lookup index public int CompareTo(GlyphLookupRecord value) { if (_glyph < value._glyph) return -1; if (_glyph > value._glyph) return 1; if (_lookup < value._lookup) return -1; if (_lookup > value._lookup) return 1; return 0; } public bool Equals(GlyphLookupRecord value) { return _glyph == value._glyph && _lookup == value._lookup; } public static bool operator ==(GlyphLookupRecord value1, GlyphLookupRecord value2) { return value1.Equals(value2); } public static bool operator !=(GlyphLookupRecord value1, GlyphLookupRecord value2) { return !value1.Equals(value2); } public override bool Equals(object value) { return Equals((GlyphLookupRecord)value); } public override int GetHashCode() { return _glyph << 16 + _lookup; } } #endregion Cache filling } } // 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
- BaseAddressElementCollection.cs
- ResourcesGenerator.cs
- Enlistment.cs
- BindingNavigatorDesigner.cs
- TargetParameterCountException.cs
- StructuralCache.cs
- SafeRegistryHandle.cs
- DataGridItemAutomationPeer.cs
- HtmlAnchor.cs
- FileDialogCustomPlace.cs
- Substitution.cs
- QueryParameter.cs
- MenuTracker.cs
- DataSourceProvider.cs
- SafeHandles.cs
- DesignerOptionService.cs
- PropertyEmitter.cs
- ScaleTransform.cs
- PathSegmentCollection.cs
- GCHandleCookieTable.cs
- OperationResponse.cs
- DispatcherExceptionEventArgs.cs
- ListViewInsertEventArgs.cs
- NotifyIcon.cs
- IntPtr.cs
- ActivityCollectionMarkupSerializer.cs
- FixedSOMTableCell.cs
- DragEvent.cs
- TriState.cs
- DesignerTextViewAdapter.cs
- HandlerFactoryCache.cs
- QilList.cs
- NgenServicingAttributes.cs
- CharEnumerator.cs
- AnyReturnReader.cs
- DataGridViewHitTestInfo.cs
- OutputCacheSection.cs
- Pen.cs
- GroupBoxRenderer.cs
- KeyTimeConverter.cs
- EncryptionUtility.cs
- RegexCapture.cs
- ChannelServices.cs
- LabelEditEvent.cs
- HttpConfigurationSystem.cs
- XamlTreeBuilderBamlRecordWriter.cs
- MdiWindowListItemConverter.cs
- MulticastDelegate.cs
- TextEditorDragDrop.cs
- EventManager.cs
- TemplatePropertyEntry.cs
- ViewService.cs
- ConfigurationManagerInternalFactory.cs
- ListBoxChrome.cs
- PolicyLevel.cs
- ResXResourceSet.cs
- StringWriter.cs
- XPathPatternBuilder.cs
- ByteStream.cs
- Timer.cs
- LinqExpressionNormalizer.cs
- cookie.cs
- ProtocolsConfiguration.cs
- XmlUTF8TextReader.cs
- SimpleTextLine.cs
- CompilerWrapper.cs
- WhereaboutsReader.cs
- IMembershipProvider.cs
- ComAdminWrapper.cs
- StringWriter.cs
- LinkedResourceCollection.cs
- ObjectContextServiceProvider.cs
- DataListGeneralPage.cs
- ExtentKey.cs
- Dictionary.cs
- DataFormats.cs
- DetailsViewPageEventArgs.cs
- Selection.cs
- IisTraceWebEventProvider.cs
- DeclaredTypeElementCollection.cs
- NewArrayExpression.cs
- RuleSettings.cs
- DataGridViewCellStyle.cs
- UserControl.cs
- RuntimeHelpers.cs
- ServiceDesigner.cs
- RemoteEndpointMessageProperty.cs
- AuthenticationSection.cs
- IPAddressCollection.cs
- TableItemStyle.cs
- InternalPolicyElement.cs
- BamlResourceDeserializer.cs
- WorkflowQueueInfo.cs
- AppDomainProtocolHandler.cs
- ErrorFormatter.cs
- SpecialTypeDataContract.cs
- UpdateEventArgs.cs
- TcpChannelFactory.cs
- DesignSurfaceCollection.cs
- ObjectDataSourceMethodEventArgs.cs