Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / MS / Internal / TextFormatting / TextRunCacheImp.cs / 1 / TextRunCacheImp.cs
//------------------------------------------------------------------------ // // Microsoft Windows Client Platform // Copyright (C) Microsoft Corporation, 2001 // // File: TextRunCacheImp.cs // // Contents: Cache of text and text properties of run // // Created: 2-25-2003 Worachai Chaoweeraprasit (wchao) // //----------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Globalization; using System.Windows; using System.Diagnostics; using System.Windows.Media.TextFormatting; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace MS.Internal.TextFormatting { ////// TextFormatter caches runs it receives from GetTextRun callback. This cache /// object is managed by text layout client. /// /// This method is used to improve performance in application whose fetching the /// run has significant performance implication. Application using this caching /// mechanism is responsible for invalidating the content in the cache when /// its changed. /// internal sealed class TextRunCacheImp { private SpanVector _textRunVector; // text run vector indexed by cp private SpanPosition _latestPosition; ////// Default constructor used internally /// ////// Text layout client never create their own run cache as its implementation /// should be opaque to them. /// internal TextRunCacheImp() { _textRunVector = new SpanVector(null); _latestPosition = new SpanPosition(); } ////// Client to notify change in part of the cache when text or /// properties of the run is being added, removed or replaced. /// ////// The client's expectation of this notification is that TextFormatter /// retains valid state for run cache in response to this change. It's /// expected that at least TextFormatter will refetch the runs affected /// by the change. Subsequent runs may or may not be refected depending /// on the state of the cache after the change. /// public void Change( int textSourceCharacterIndex, int addition, int removal ) { if (textSourceCharacterIndex < 0) return; int cchActive = 0; for (int i = 0; i < _textRunVector.Count; i++) cchActive += _textRunVector[i].length; if (textSourceCharacterIndex >= cchActive) return; SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition, textSourceCharacterIndex); _latestPosition = textRunSpanRider.SpanPosition; // we remove runs from the cache starting from the one containing the change // to the end of the active range. We do not try to interpret the scope of // the change and try to minimize the range in which the cache is invalidated, // because that would require an in-depth understanding of how our client // implement their formatting change mechanism and how they respond to future // refetch after the change which could vary among different clients. That // kind of work is beyond the purpose of this notification. _latestPosition = _textRunVector.SetValue( textRunSpanRider.CurrentSpanStart, cchActive - textRunSpanRider.CurrentSpanStart, _textRunVector.Default, _latestPosition ); } ////// Fetch cached textrun /// internal TextRun FetchTextRun( FormatSettings settings, int cpFetch, int cpFirst, out int offsetToFirstCp, out int runLength ) { SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition, cpFetch); _latestPosition = textRunSpanRider.SpanPosition; TextRun textRun = (TextRun)textRunSpanRider.CurrentElement; if(textRun == null) { // run not already cached, fetch new run and cache it textRun = settings.TextSource.GetTextRun(cpFetch); if (textRun.Length < 1) { throw new ArgumentOutOfRangeException("textRun.Length", SR.Get(SRID.ParameterMustBeGreaterThanZero)); } Plsrun plsrun = TextRunInfo.GetRunType(textRun); if (plsrun == Plsrun.Text || plsrun == Plsrun.InlineObject) { TextRunProperties properties = textRun.Properties; if (properties == null) throw new ArgumentException(SR.Get(SRID.TextRunPropertiesCannotBeNull)); if (properties.FontRenderingEmSize <= 0) throw new ArgumentException(SR.Get(SRID.PropertyOfClassMustBeGreaterThanZero, "FontRenderingEmSize", "TextRunProperties")); double realMaxFontRenderingEmSize = settings.Formatter.IdealToReal(Constants.InfiniteWidth) / Constants.GreatestMutiplierOfEm; if (properties.FontRenderingEmSize > realMaxFontRenderingEmSize) throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeGreaterThan, "FontRenderingEmSize", "TextRunProperties", realMaxFontRenderingEmSize)); CultureInfo culture = CultureMapper.GetSpecificCulture(properties.CultureInfo); if (culture == null) throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeNull, "CultureInfo", "TextRunProperties")); if (properties.Typeface == null) throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeNull, "Typeface", "TextRunProperties")); } // // TextRun is specifial to SpanVector because TextRun also encodes position which needs to be // consistent with the positions encoded by SpanVector. In run cache, the begining of a span // should always correspond to the begining of a cached text run. If the end of the currently fetched // run overlaps with the begining of an already cached run, the begining of the cached run needs to be // adjusted as well as its span. Because we can't gurantee the correctness of the overlapped range // so we'll simply remove the overlapped runs here. // // Move the rider to the end of the current run textRunSpanRider.At(cpFetch + textRun.Length - 1); _latestPosition = textRunSpanRider.SpanPosition; if (textRunSpanRider.CurrentElement != _textRunVector.Default) { // The end overlaps with one or more cached runs, clear the range from the // begining of the current fetched run to the end of the last overlapped cached run. _latestPosition = _textRunVector.SetReference( cpFetch, textRunSpanRider.CurrentPosition + textRunSpanRider.Length - cpFetch, _textRunVector.Default, _latestPosition ); } _latestPosition = _textRunVector.SetReference(cpFetch, textRun.Length, textRun, _latestPosition); // Refresh the rider's SpanPosition following previous SpanVector.SetReference calls textRunSpanRider.At(_latestPosition, cpFetch); } offsetToFirstCp = textRunSpanRider.CurrentPosition - textRunSpanRider.CurrentSpanStart; runLength = textRunSpanRider.Length; Debug.Assert(textRun != null && runLength > 0, "Invalid run!"); bool isText = textRun is ITextSymbols; if (isText) { // Chop text run to optimal length so we dont spend forever analysing // them all at once. int looseCharLength = TextStore.TypicalCharactersPerLine - cpFetch + cpFirst; if(looseCharLength <= 0) { // this line already exceeds typical line length, incremental fetch goes // about a quarter of the typical length. looseCharLength = (int)Math.Round(TextStore.TypicalCharactersPerLine * 0.25); } if(runLength > looseCharLength) { if (TextRunInfo.GetRunType(textRun) == Plsrun.Text) { // // When chopping the run at the typical line length, // - don't chop in between of higher & lower surrogate // - don't chop combining mark away from its base character // - don't chop joiner from surrounding characters // // Starting from the initial chopping point, we look ahead to find a safe position. We stop at // a limit in case the run consists of many combining mark & joiner. That is rare and doesn't make // much sense in shaping already. // CharacterBufferReference charBufferRef = textRun.CharacterBufferReference; // We look ahead by one more line at most. It is not normal to have // so many combining mark or joiner characters in a row. It doesn't make sense to // look further if so. int lookAheadLimit = Math.Min(runLength, looseCharLength + TextStore.TypicalCharactersPerLine); int sizeOfChar = 0; int endOffset = 0; bool canBreakAfterPrecedingChar = false; for (endOffset = looseCharLength - 1; endOffset < lookAheadLimit; endOffset += sizeOfChar) { CharacterBufferRange charString = new CharacterBufferRange( charBufferRef.CharacterBuffer, charBufferRef.OffsetToFirstChar + offsetToFirstCp + endOffset, runLength - endOffset ); int ch = Classification.UnicodeScalar(charString, out sizeOfChar); // We can only safely break if the preceding char is not a joiner character (i.e. can-break-after), // and the current char is not combining or joiner (i.e. can-break-before). if (canBreakAfterPrecedingChar && !Classification.IsCombining(ch) && !Classification.IsJoiner(ch) ) { break; } canBreakAfterPrecedingChar = !Classification.IsJoiner(ch); } looseCharLength = Math.Min(runLength, endOffset); } runLength = looseCharLength; } } Debug.Assert( // valid run found runLength > 0 // non-text run always fetched at run start && ( isText || textRunSpanRider.CurrentSpanStart - textRunSpanRider.CurrentPosition == 0) // span rider of both text and format point to valid position && (textRunSpanRider.Length > 0 && textRunSpanRider.CurrentElement != null), "Text run fetching error!" ); return textRun; } ////// Get text immediately preceding cpLimit. /// internal TextSpanGetPrecedingText(TextSource textSource, int cpLimit) { if (cpLimit > 0) { SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition); if (textRunSpanRider.At(cpLimit - 1)) { CharacterBufferRange charString = CharacterBufferRange.Empty; CultureInfo culture = null; TextRun run = textRunSpanRider.CurrentElement as TextRun; if (run != null) { // Only TextRun containing text would have non-empty Character buffer range. if ( TextRunInfo.GetRunType(run) == Plsrun.Text && run.CharacterBufferReference.CharacterBuffer != null) { charString = new CharacterBufferRange( run.CharacterBufferReference, cpLimit - textRunSpanRider.CurrentSpanStart); culture = CultureMapper.GetSpecificCulture(run.Properties.CultureInfo); } return new TextSpan ( cpLimit - textRunSpanRider.CurrentSpanStart, // cp length new CultureSpecificCharacterBufferRange(culture, charString) ); } } } // not in cache so call back to client return textSource.GetPrecedingText(cpLimit); } /// /// Return all TextRuns cached. If for a particular range there is no TextRun cached, the TextSpan would /// contain null TextRun object. /// internal IList> GetTextRunSpans() { IList > textRunList = new List >(_textRunVector.Count); for (int i = 0; i < _textRunVector.Count; i++) { Span currentSpan = _textRunVector[i]; textRunList.Add(new TextSpan (currentSpan.length, currentSpan.element as TextRun)); } return textRunList; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------ // // Microsoft Windows Client Platform // Copyright (C) Microsoft Corporation, 2001 // // File: TextRunCacheImp.cs // // Contents: Cache of text and text properties of run // // Created: 2-25-2003 Worachai Chaoweeraprasit (wchao) // //----------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Globalization; using System.Windows; using System.Diagnostics; using System.Windows.Media.TextFormatting; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; namespace MS.Internal.TextFormatting { /// /// TextFormatter caches runs it receives from GetTextRun callback. This cache /// object is managed by text layout client. /// /// This method is used to improve performance in application whose fetching the /// run has significant performance implication. Application using this caching /// mechanism is responsible for invalidating the content in the cache when /// its changed. /// internal sealed class TextRunCacheImp { private SpanVector _textRunVector; // text run vector indexed by cp private SpanPosition _latestPosition; ////// Default constructor used internally /// ////// Text layout client never create their own run cache as its implementation /// should be opaque to them. /// internal TextRunCacheImp() { _textRunVector = new SpanVector(null); _latestPosition = new SpanPosition(); } ////// Client to notify change in part of the cache when text or /// properties of the run is being added, removed or replaced. /// ////// The client's expectation of this notification is that TextFormatter /// retains valid state for run cache in response to this change. It's /// expected that at least TextFormatter will refetch the runs affected /// by the change. Subsequent runs may or may not be refected depending /// on the state of the cache after the change. /// public void Change( int textSourceCharacterIndex, int addition, int removal ) { if (textSourceCharacterIndex < 0) return; int cchActive = 0; for (int i = 0; i < _textRunVector.Count; i++) cchActive += _textRunVector[i].length; if (textSourceCharacterIndex >= cchActive) return; SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition, textSourceCharacterIndex); _latestPosition = textRunSpanRider.SpanPosition; // we remove runs from the cache starting from the one containing the change // to the end of the active range. We do not try to interpret the scope of // the change and try to minimize the range in which the cache is invalidated, // because that would require an in-depth understanding of how our client // implement their formatting change mechanism and how they respond to future // refetch after the change which could vary among different clients. That // kind of work is beyond the purpose of this notification. _latestPosition = _textRunVector.SetValue( textRunSpanRider.CurrentSpanStart, cchActive - textRunSpanRider.CurrentSpanStart, _textRunVector.Default, _latestPosition ); } ////// Fetch cached textrun /// internal TextRun FetchTextRun( FormatSettings settings, int cpFetch, int cpFirst, out int offsetToFirstCp, out int runLength ) { SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition, cpFetch); _latestPosition = textRunSpanRider.SpanPosition; TextRun textRun = (TextRun)textRunSpanRider.CurrentElement; if(textRun == null) { // run not already cached, fetch new run and cache it textRun = settings.TextSource.GetTextRun(cpFetch); if (textRun.Length < 1) { throw new ArgumentOutOfRangeException("textRun.Length", SR.Get(SRID.ParameterMustBeGreaterThanZero)); } Plsrun plsrun = TextRunInfo.GetRunType(textRun); if (plsrun == Plsrun.Text || plsrun == Plsrun.InlineObject) { TextRunProperties properties = textRun.Properties; if (properties == null) throw new ArgumentException(SR.Get(SRID.TextRunPropertiesCannotBeNull)); if (properties.FontRenderingEmSize <= 0) throw new ArgumentException(SR.Get(SRID.PropertyOfClassMustBeGreaterThanZero, "FontRenderingEmSize", "TextRunProperties")); double realMaxFontRenderingEmSize = settings.Formatter.IdealToReal(Constants.InfiniteWidth) / Constants.GreatestMutiplierOfEm; if (properties.FontRenderingEmSize > realMaxFontRenderingEmSize) throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeGreaterThan, "FontRenderingEmSize", "TextRunProperties", realMaxFontRenderingEmSize)); CultureInfo culture = CultureMapper.GetSpecificCulture(properties.CultureInfo); if (culture == null) throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeNull, "CultureInfo", "TextRunProperties")); if (properties.Typeface == null) throw new ArgumentException(SR.Get(SRID.PropertyOfClassCannotBeNull, "Typeface", "TextRunProperties")); } // // TextRun is specifial to SpanVector because TextRun also encodes position which needs to be // consistent with the positions encoded by SpanVector. In run cache, the begining of a span // should always correspond to the begining of a cached text run. If the end of the currently fetched // run overlaps with the begining of an already cached run, the begining of the cached run needs to be // adjusted as well as its span. Because we can't gurantee the correctness of the overlapped range // so we'll simply remove the overlapped runs here. // // Move the rider to the end of the current run textRunSpanRider.At(cpFetch + textRun.Length - 1); _latestPosition = textRunSpanRider.SpanPosition; if (textRunSpanRider.CurrentElement != _textRunVector.Default) { // The end overlaps with one or more cached runs, clear the range from the // begining of the current fetched run to the end of the last overlapped cached run. _latestPosition = _textRunVector.SetReference( cpFetch, textRunSpanRider.CurrentPosition + textRunSpanRider.Length - cpFetch, _textRunVector.Default, _latestPosition ); } _latestPosition = _textRunVector.SetReference(cpFetch, textRun.Length, textRun, _latestPosition); // Refresh the rider's SpanPosition following previous SpanVector.SetReference calls textRunSpanRider.At(_latestPosition, cpFetch); } offsetToFirstCp = textRunSpanRider.CurrentPosition - textRunSpanRider.CurrentSpanStart; runLength = textRunSpanRider.Length; Debug.Assert(textRun != null && runLength > 0, "Invalid run!"); bool isText = textRun is ITextSymbols; if (isText) { // Chop text run to optimal length so we dont spend forever analysing // them all at once. int looseCharLength = TextStore.TypicalCharactersPerLine - cpFetch + cpFirst; if(looseCharLength <= 0) { // this line already exceeds typical line length, incremental fetch goes // about a quarter of the typical length. looseCharLength = (int)Math.Round(TextStore.TypicalCharactersPerLine * 0.25); } if(runLength > looseCharLength) { if (TextRunInfo.GetRunType(textRun) == Plsrun.Text) { // // When chopping the run at the typical line length, // - don't chop in between of higher & lower surrogate // - don't chop combining mark away from its base character // - don't chop joiner from surrounding characters // // Starting from the initial chopping point, we look ahead to find a safe position. We stop at // a limit in case the run consists of many combining mark & joiner. That is rare and doesn't make // much sense in shaping already. // CharacterBufferReference charBufferRef = textRun.CharacterBufferReference; // We look ahead by one more line at most. It is not normal to have // so many combining mark or joiner characters in a row. It doesn't make sense to // look further if so. int lookAheadLimit = Math.Min(runLength, looseCharLength + TextStore.TypicalCharactersPerLine); int sizeOfChar = 0; int endOffset = 0; bool canBreakAfterPrecedingChar = false; for (endOffset = looseCharLength - 1; endOffset < lookAheadLimit; endOffset += sizeOfChar) { CharacterBufferRange charString = new CharacterBufferRange( charBufferRef.CharacterBuffer, charBufferRef.OffsetToFirstChar + offsetToFirstCp + endOffset, runLength - endOffset ); int ch = Classification.UnicodeScalar(charString, out sizeOfChar); // We can only safely break if the preceding char is not a joiner character (i.e. can-break-after), // and the current char is not combining or joiner (i.e. can-break-before). if (canBreakAfterPrecedingChar && !Classification.IsCombining(ch) && !Classification.IsJoiner(ch) ) { break; } canBreakAfterPrecedingChar = !Classification.IsJoiner(ch); } looseCharLength = Math.Min(runLength, endOffset); } runLength = looseCharLength; } } Debug.Assert( // valid run found runLength > 0 // non-text run always fetched at run start && ( isText || textRunSpanRider.CurrentSpanStart - textRunSpanRider.CurrentPosition == 0) // span rider of both text and format point to valid position && (textRunSpanRider.Length > 0 && textRunSpanRider.CurrentElement != null), "Text run fetching error!" ); return textRun; } ////// Get text immediately preceding cpLimit. /// internal TextSpanGetPrecedingText(TextSource textSource, int cpLimit) { if (cpLimit > 0) { SpanRider textRunSpanRider = new SpanRider(_textRunVector, _latestPosition); if (textRunSpanRider.At(cpLimit - 1)) { CharacterBufferRange charString = CharacterBufferRange.Empty; CultureInfo culture = null; TextRun run = textRunSpanRider.CurrentElement as TextRun; if (run != null) { // Only TextRun containing text would have non-empty Character buffer range. if ( TextRunInfo.GetRunType(run) == Plsrun.Text && run.CharacterBufferReference.CharacterBuffer != null) { charString = new CharacterBufferRange( run.CharacterBufferReference, cpLimit - textRunSpanRider.CurrentSpanStart); culture = CultureMapper.GetSpecificCulture(run.Properties.CultureInfo); } return new TextSpan ( cpLimit - textRunSpanRider.CurrentSpanStart, // cp length new CultureSpecificCharacterBufferRange(culture, charString) ); } } } // not in cache so call back to client return textSource.GetPrecedingText(cpLimit); } /// /// Return all TextRuns cached. If for a particular range there is no TextRun cached, the TextSpan would /// contain null TextRun object. /// internal IList> GetTextRunSpans() { IList > textRunList = new List >(_textRunVector.Count); for (int i = 0; i < _textRunVector.Count; i++) { Span currentSpan = _textRunVector[i]; textRunList.Add(new TextSpan (currentSpan.length, currentSpan.element as TextRun)); } return textRunList; } } } // 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
- ZipArchive.cs
- WebHttpElement.cs
- DataGridTablesFactory.cs
- AnnotationAdorner.cs
- ContainerVisual.cs
- QueryAsyncResult.cs
- FrugalMap.cs
- ToolStripDropDownItem.cs
- TableFieldsEditor.cs
- DataIdProcessor.cs
- UserCancellationException.cs
- InputReferenceExpression.cs
- TreeViewBindingsEditor.cs
- ScrollBar.cs
- TranslateTransform.cs
- Decimal.cs
- OleDbEnumerator.cs
- XmlLinkedNode.cs
- ConstrainedDataObject.cs
- SmtpDateTime.cs
- ToolStripDropDownItem.cs
- DiffuseMaterial.cs
- FlowDocumentPaginator.cs
- BulletChrome.cs
- BlockCollection.cs
- UnitySerializationHolder.cs
- ConfigurationManagerHelperFactory.cs
- SqlVersion.cs
- RuntimeIdentifierPropertyAttribute.cs
- basevalidator.cs
- HttpClientCertificate.cs
- ModelVisual3D.cs
- ConnectionProviderAttribute.cs
- DispatchOperationRuntime.cs
- DbQueryCommandTree.cs
- XmlElementAttributes.cs
- SystemIPInterfaceProperties.cs
- CircleHotSpot.cs
- SiteMapPath.cs
- DataControlFieldCollection.cs
- rsa.cs
- EmptyReadOnlyDictionaryInternal.cs
- DynamicValidatorEventArgs.cs
- JsonFormatGeneratorStatics.cs
- CollectionEditVerbManager.cs
- ReferentialConstraintRoleElement.cs
- ApplyTemplatesAction.cs
- DataGridView.cs
- AddInToken.cs
- NumberSubstitution.cs
- LineInfo.cs
- XmlSchemaAny.cs
- ProfessionalColorTable.cs
- ObjectParameterCollection.cs
- objectquery_tresulttype.cs
- TextEditorContextMenu.cs
- VideoDrawing.cs
- TextEditorParagraphs.cs
- ExpressionBinding.cs
- mediaeventargs.cs
- UpdateEventArgs.cs
- ItemsChangedEventArgs.cs
- FontUnitConverter.cs
- StreamResourceInfo.cs
- EtwProvider.cs
- DaylightTime.cs
- SqlLiftWhereClauses.cs
- rsa.cs
- XmlAtomErrorReader.cs
- ToolStripDropDownMenu.cs
- EntityDataSourceUtil.cs
- ExpressionDumper.cs
- IntSecurity.cs
- OutOfMemoryException.cs
- DataGridSortCommandEventArgs.cs
- PopupRoot.cs
- GetResponse.cs
- DocumentApplication.cs
- DataSourceHelper.cs
- AsyncCompletedEventArgs.cs
- WindowsRichEditRange.cs
- SoapTypeAttribute.cs
- MappingMetadataHelper.cs
- MobileControlPersister.cs
- JsonClassDataContract.cs
- XmlSchemaGroup.cs
- WithStatement.cs
- ContentOperations.cs
- PageAdapter.cs
- GeometryModel3D.cs
- ScriptingWebServicesSectionGroup.cs
- BindingUtils.cs
- DefaultClaimSet.cs
- MessageBox.cs
- BookmarkList.cs
- ThreadPool.cs
- SimpleTextLine.cs
- MetadataUtil.cs
- DataSourceComponent.cs
- XmlDictionaryReaderQuotas.cs