File: MS\Internal\Shaping\ShaperBuffers.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
//
//
//
//  Contents:  base shaping engine buffers
//
//
 
using System;
using System.Security;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using MS.Internal.FontCache;
using MS.Internal.FontFace;
using System.Windows.Media;
using System.Windows.Media.TextFormatting;
using MS.Internal.PresentationCore;
using MS.Utility;
 
 
namespace MS.Internal.Shaping
{
    /// <summary>
    ///     ShaperBuffers encapsulates the shapers non-volatile, non-shareabld buffers 
    /// </summary>
    /// <remarks>
    ///     This class owns several buffers that need to live as long as there's a
    ///     shaping engine that uses them.  Each shaping engine "owns" one of these, but
    ///     the actual mapping/number of ShaperBuffers instances depends
    ///     on the THREAD_SAFE_SHAPERS constant in BaseShape.cs.  If THREAD_SAFE_SHAPERS
    ///     is defined, there's one ShaperBuffers for each thread created in the
    ///     WCP process space.  If not defined, each shaping engine has its own
    ///     ShaperBuffers object.  In this latter case, the owner of the ShapeManager
    ///     that owns the shaping engine is responsible for guaranteeing thread safety (everything
    ///     else in the shape engines is reentrant, but each ShaperBuffers is not.
    ///
    ///     A ShaperBuffers is created whenever a IShaper interface method of a
    ///     shaper is called and its member ShaperBuffers is null (ie, the first time
    ///     a shaper's IShaper interface is invoked).  It exists till the shaper is destroyed.
    /// </remarks>
    internal class ShaperBuffers
    {
        /// <summary>
        /// ShaperBuffers - constructor
        /// </summary>
        public ShaperBuffers(ushort charCount, ushort glyphCount)
        {
            // the charCount is used to provide an initial size for the
            // various buffers used by the shaper(s).  These may
            // grow over the shaper's life
            _glyphInfoList = new GlyphInfoList((glyphCount > charCount ? glyphCount : charCount), 16, false);
            _charMap = new UshortList(charCount, 16);
            _layoutWorkspace = new OpenTypeLayoutWorkspace();
 
            _charMap.SetRange(0,charCount);
            if (glyphCount > 0)
            {
                _glyphInfoList.SetRange(0,glyphCount);
            }
}
        
         ~ShaperBuffers()
         {
             _glyphInfoList = null;
             _charMap = null;
             _layoutWorkspace = null;
             _textFeatures = null;
         }
         
         // I have laid these out alphabetically.  If a particular variable has
         // an accessor with a different name than the variable, I've added a
         // comment in the position of the variable...
 
        public UshortList                       CharMap
        {
            get { return _charMap; }
        }
         
        
        public GlyphInfoList                    GlyphInfoList
        {
            get { return _glyphInfoList; }
            set { _glyphInfoList = value; }
        }
        
 
        /// <summary>
        /// ShaperBuffers.Initialize - initializer for GetGlyphs.
        /// </summary>
        /// <remarks>
        ///   Called by every shaper's GetGlyph method (indirectly -
        ///   this function is actually called by the ShapingWorkspace.Initialize
        ///   method (which is called from IShaper.GetGlyphs)).
        /// </remarks>
        public bool Initialize(ushort charCount, ushort glyphCount)
        {
            if (charCount <= 0)
            {
                return false;
            }
 
            // clear charmap and resize it        
            if (_charMap.Length > 0)
            {
                _charMap.Remove(0, _charMap.Length);
            }
            _charMap.Insert(0, charCount);
            Debug.Assert(_charMap.Length == charCount);
 
            // clear glyphinfolist
            if (_glyphInfoList.Length > 0)
            {
                _glyphInfoList.Remove(0, _glyphInfoList.Length);
            }
            if (glyphCount > 0)
            {
                _glyphInfoList.Insert(0, glyphCount);
            }
            Debug.Assert(_glyphInfoList.Length == glyphCount);
 
            return true;
        }
 
 
         /// <summary>
         /// ShaperBuffers.InitializeFeatureList - initializer for GetGlyphs.
         /// </summary>
         /// <param name="size">Requested new array size</param>
         /// <param name="keep">number of features to copy into new array</param>
         /// <remarks>
         ///   Called by pertinent shaper's GetGlyph method; ie, those shapers
         ///   that need to create a text dependent list of features
         ///   (e.g. Arabic, Mongolian).
         ///   The "keep" count takes priority over "size" if there's already an
         ///   array, so if size size is less than keep, the resized array has at
         ///    least keep elements.  It is possible to create a 0 sized array.
         /// </remarks>
         public bool InitializeFeatureList(ushort size, ushort keep)
         {
            if (_textFeatures == null)
            {
                _textFeatures = new ShaperFeaturesList();
                if (_textFeatures == null)
                {
                    return false;
                }
                
                _textFeatures.Initialize(size);
            }
            else
            {
                _textFeatures.Resize(size, keep);
            }
 
            return true;
         }
         
 
         public OpenTypeLayoutWorkspace            LayoutWorkspace
         {
             get { return _layoutWorkspace; }
         }
         
         //                                          NextIx; see CurrentCharIx
 
 
        public ShaperFeaturesList                  TextFeatures
        {
            get { return _textFeatures; }
        }
 
        
        // these are one per shaping engine (or per thread) and are kept around
        // between calls (ie, we allocate these once for the lifetime of this
        // ShapingWorkspace instance)
        private UshortList                  _charMap;
        private GlyphInfoList               _glyphInfoList;
        private OpenTypeLayoutWorkspace     _layoutWorkspace;
        private ShaperFeaturesList          _textFeatures;
}
 
    internal class ShaperFeaturesList
    {
        public int                                FeaturesCount
        {
            get { return _featuresCount; }
        }
         
        public Feature[]                          Features
        {
            get { return _features; }
        }
        
        public int                                NextIx
        {
            get { return _featuresCount; }
        }
 
        public uint                               CurrentTag
        {
            get { return _featuresCount == 0 ? 0 : _features[_featuresCount - 1].Tag;}
        }
 
        public int                                Length
        {
            get { return _featuresCount; }
        }
 
        public void SetFeatureParameter (ushort featureIx, uint paramValue)
        {
            Invariant.Assert ( _featuresCount > featureIx );
            _features[featureIx].Parameter = paramValue;
        }
        
        /// <summary>
        /// ShaperFeateruList.Initialize - initializer for GetGlyphs.
        /// </summary>
        /// <remarks>
        ///   Called by pertinent shaper's GetGlyph method (indirectly -
        ///   this function is actually called by the
        ///   ShaperBuffers.InitializeFeatureList method which is
        ///   called by those shapers that need to create a text dependent
        ///   list of features (e.g. Arabic, Mongolian).
        /// </remarks>
        internal bool Initialize (ushort newSize)
        {
            if (_features == null || 
                newSize > _features.Length ||
                newSize == 0)
            {
                Feature[] newArray = new Feature[newSize];
                if (newArray != null)
                {
                    _features = newArray;
                }
            }
 
            _featuresCount = 0;
            _minimumAddCount = 3;       // add space for init,med,final whenever we need to actually resize array
            return _features != null;
        }
 
        /// <summary>
        /// ShaperFeateruList.Resize - used to change the size of the features array
        /// </summary>
        /// <param name="newSize">Requested new array size</param>
        /// <param name="keepCount">number of features to copy into new array</param>
        /// <remarks>
        ///   Used locally for each AddFeature, and by ShaperBuffers.InitializeFeatureList.
        ///   May be called from a shaping engine.
        ///   The "keepCount" count takes priority over "newSize" if there's already an
        ///   array, so if size is less than keep, the resized array has at least keep elements.
        /// </remarks>
        internal bool Resize (ushort newSize, ushort keepCount)
        {
            _featuresCount = keepCount;     // 
   
            if (_features != null && 
                _features.Length != 0 && 
                keepCount > 0 &&
                _features.Length >= keepCount)
            {
                // make sure keep count is no bigger than current
                // array size
                ushort currentLength = (ushort)_features.Length;
 
                // make sure new size is at least as big as keep count
                if (newSize < keepCount)
                {
                    newSize = keepCount;
                }
                
                // if new size is bigger than the current array, create
                // a new array
                if (newSize > currentLength)
                {
                    // always use minimum leap for adding to array
                    if (newSize < (currentLength + _minimumAddCount))
                    {
                       newSize = (ushort)(currentLength + _minimumAddCount);
                    }
                    
                    Feature[] newArray = new Feature[newSize];
                    if (newArray == null)
                    {
                        // can't create new array, leave (at least we still
                        // have our current array)
                        return false;
                    }
                    
                    // Our client wants us to keep the first "keepCount" entries.
                    // so copy them now.
                    for (int i = 0; i < keepCount; ++i)
                    {
                        newArray[i] = _features[i];
                    }
 
                    _features = newArray;
                }
            }
            else
            {
                // nothing to keep, or currently no array so initialize
                return Initialize(newSize);
            }
 
            return true;
}
        
        /// <summary>
        /// ShaperFeateruList.AddFeature - add a feature to the array
        /// </summary>
        /// <param name="feature">new feature to add</param>
        /// <remarks>
        ///     This is aimed at allowing shaping engines to add features
        ///     to the array (generally used for required, all character
        ///     features added at the start of shaping)
        /// </remarks>
        internal void AddFeature (Feature feature)
        {
            if ( _featuresCount == _features.Length )
            {
                // need more space, so resize the array
               if (!Resize((ushort)(_featuresCount + 1),_featuresCount))
               {
                    return; // can't resize array, fail quietly (not going
                            // to apply this feature!)
               }
            }
            
            _features[_featuresCount] = feature;
            ++_featuresCount;
        }
 
        /// <summary>
        /// ShaperFeateruList.AddFeature - add a feature to the array
        /// </summary>
        /// <remarks>
        ///     An alternative to adding an already created feature.
        /// </remarks>
        internal void AddFeature (ushort startIndex, ushort length, uint featureTag, uint parameter)
        {
            if ( _featuresCount == _features.Length )
            {
                // need more space
               if (!Resize((ushort)(_featuresCount + 1),_featuresCount))
               {
                    return;
               }
            }
            
            if (_features[_featuresCount] != null)
            {
                _features[_featuresCount].Tag = featureTag;
                _features[_featuresCount].StartIndex = startIndex;
                _features[_featuresCount].Length = length;
                _features[_featuresCount].Parameter = parameter;
            }
            else
            {
                _features[_featuresCount] = new Feature(startIndex,length,featureTag,parameter);
            }
            
            ++_featuresCount;
        }
 
        /// <summary>
        /// ShaperFeateruList.AddFeature - add a feature to the array
        /// </summary>
        /// <remarks>
        ///     This variation of "AddFeature" is used by the shaper state
        ///     machines for adding each new feature.
        /// </remarks>
        internal void AddFeature (ushort charIx, uint featureTag )
        {
            if (featureTag == 1)     // "NotShaped"
            {
                return;
            }
            
            if (_featuresCount > 0)     // if previous feature exists
            {
                // see if this feature can just be subsumed in the latest feature
                ushort latestFeatureIx = (ushort)(_featuresCount - 1);
                if ((featureTag == 0 || featureTag == _features[latestFeatureIx].Tag) &&
                     (_features[latestFeatureIx].StartIndex +
                      _features[latestFeatureIx].Length) == charIx)
                {
                    _features[latestFeatureIx].Length += 1;
                }
                else
                {
                    // can't be added to previous feature, so add one.
                    AddFeature(charIx,
                               1,
                               (featureTag == 0 ? _features[latestFeatureIx].Tag : featureTag),
                               1);
                }
            }
            else if (featureTag != 0)        // cant' be "Same" (there's no feature yet)
            {
                AddFeature(charIx,1,featureTag,1);
            }
}
        
        /// <summary>
        /// ShaperFeateruList.UpdatePreviousShapedChar - adjust previous char's tag
        /// </summary>
        /// <remarks>
        ///     This is used by the shaper state machines for modifying the
        ///     feature tag of the previous character.
        /// </remarks>
        internal void UpdatePreviousShapedChar (uint featureTag)
        {
            if (featureTag <= 1)     // nothing to do if "NotShaped" or "Same"
            {
                return;
            }
            
            if (_featuresCount > 0)
            {
                // see if this feature can just be subsumed in the latest feature
                ushort latestFeatureIx = (ushort)(_featuresCount - 1);
 
                if (_features[latestFeatureIx].Tag != featureTag)
                {
                    // the previous char's feature just applied to
                    // it, so update its tag and we're done
                    _features[latestFeatureIx].Tag = featureTag;
                }
            }
        }
 
        private ushort                            _minimumAddCount;
        private ushort                            _featuresCount;
        private Feature[]                         _features;
    }
}