public static u16 U16(byte[] buffer, ref Int64 index) { u16 result = BigEndianRead.U16(buffer, index); index += 2; return(result); }
public void GetControlPoints(string unicodeCharacter, List <Glyph> result) { #region Convert Character to glyph ID Profiler.BeginSample("Convert Character to Glyph ID"); int codePoint = char.ConvertToUtf32(unicodeCharacter, 0); int glyphID = 0; // Missing Glyph if (codePoint > u16.MaxValue) { Debug.LogWarning("Character is not supported in Open Font format 4."); } else { for (int segmentIndex = 0; segmentIndex < _selectedEncoding.endCodes.Length; ++segmentIndex) { if (_selectedEncoding.endCodes[segmentIndex] >= codePoint) { if (_selectedEncoding.startCodes[segmentIndex] <= codePoint) { u16 idRangeOffset = _selectedEncoding.idRangeOffsets[segmentIndex]; if (idRangeOffset != 0) { // Indexes are in an array right after the array of offsets, we'll need to convert the address to be indexed with bytes: s64 relativeCodePoint = (codePoint - _selectedEncoding.startCodes[segmentIndex]) * sizeof(u16); s64 addressOfcurrentSegment = _selectedEncodingIDRangeOffsetLocation + segmentIndex * sizeof(u16); s64 glyphIdArrayIndex = addressOfcurrentSegment + relativeCodePoint + idRangeOffset; glyphID = BigEndianRead.U16(_fontFileBuffer, glyphIdArrayIndex); if (glyphID != 0) { glyphID = (glyphID + _selectedEncoding.idDeltas[segmentIndex]) % 65536; // Do modulo manually, rather than relying on 16-bit integer wrapping. break; } else { break; // Missing glyph } } else { glyphID = (_selectedEncoding.idDeltas[segmentIndex] + codePoint) % 65536; break; } } else { break; // Missing Glyph } } } } Profiler.EndSample(); #endregion GetControlPoints(glyphID, result); }
public void GetControlPoints(int glyphID, List <Glyph> result) { if (glyphID >= Maximums.numberOfGlyphs) { return; } #region Parse location indexing table s64 offsetIntoGlyphArray = 0; if (_indexToLocationIndex >= 0) { var locaTableRecord = TableDirectory[_indexToLocationIndex]; s64 arrayOffset = locaTableRecord.offset; if (FontHeader.indexToLocFormat == OpenFontFormat.IndexToLocFormat.ShortOffsets) { s64 locationIndex = arrayOffset + glyphID * sizeof(u16); offsetIntoGlyphArray = BigEndianRead.U16(_fontFileBuffer, locationIndex) * sizeof(u16); if (offsetIntoGlyphArray == BigEndianRead.U16(_fontFileBuffer, locationIndex + sizeof(u16))) { return; // No contours. } } else if (FontHeader.indexToLocFormat == OpenFontFormat.IndexToLocFormat.LongOffsets) { s64 locationIndex = arrayOffset + glyphID * sizeof(u32); offsetIntoGlyphArray = BigEndianRead.U32(_fontFileBuffer, locationIndex); if (offsetIntoGlyphArray == BigEndianRead.U32(_fontFileBuffer, locationIndex + sizeof(u32))) { return; // No Contours. } } } #endregion #region Parse Glyph Profiler.BeginSample("Parse Glyph"); if (_glyphDataIndex >= 0) { var glyfHeaderRecord = TableDirectory[_glyphDataIndex]; GlyphHeader = new OpenFontFormat.GlyphHeader(); s64 arrayOffsetOfGlyphHeader = glyfHeaderRecord.offset; s64 arrayOffset = arrayOffsetOfGlyphHeader + offsetIntoGlyphArray; GlyphHeader.numberOfContours = BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); GlyphHeader.xMin = BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); GlyphHeader.yMin = BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); GlyphHeader.xMax = BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); GlyphHeader.yMax = BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); if (GlyphHeader.numberOfContours == 0) { Debug.LogError("Encountered a glyph with 0 contours, which is undefined at this stage."); } if (GlyphHeader.numberOfContours < 0) { u16 flags = 0; do { // Composite glyph flags = BigEndianReadAndAdvance.U16(_fontFileBuffer, ref arrayOffset); u16 glyphIndex = BigEndianReadAndAdvance.U16(_fontFileBuffer, ref arrayOffset); s32 xOffset = 0; s32 yOffset = 0; s32 firstGlyphMatchPointNumber = 0; s32 thisGlyphMatchPointNumber = 0; float scale = 1.0f; float yScale = 1.0f; float scale01 = 1.0f; float scale10 = 1.0f; bool argsAreXYOffsets = (flags & (u16)OpenFontFormat.CompositeGlyphFlags.ArgsAreXyValues) > 0; bool argsAreWords = (flags & (u16)OpenFontFormat.CompositeGlyphFlags.Args1And2AreWords) > 0; if (argsAreXYOffsets) { if (argsAreWords) { xOffset += BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); yOffset += BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); } else { xOffset += BigEndianReadAndAdvance.U8(_fontFileBuffer, ref arrayOffset); yOffset += BigEndianReadAndAdvance.U8(_fontFileBuffer, ref arrayOffset); } } if ((flags & (u16)OpenFontFormat.CompositeGlyphFlags.WeHaveAScale) > 0) { scale = BigEndianReadAndAdvance.F2Dot14(_fontFileBuffer, ref arrayOffset); } else if ((flags & (u16)OpenFontFormat.CompositeGlyphFlags.SeparateScale) > 0) { scale = BigEndianReadAndAdvance.F2Dot14(_fontFileBuffer, ref arrayOffset); yScale = BigEndianReadAndAdvance.F2Dot14(_fontFileBuffer, ref arrayOffset); } else if ((flags & (u16)OpenFontFormat.CompositeGlyphFlags.HasMatrix2x2) > 0) { scale = BigEndianReadAndAdvance.F2Dot14(_fontFileBuffer, ref arrayOffset); scale01 = BigEndianReadAndAdvance.F2Dot14(_fontFileBuffer, ref arrayOffset); scale10 = BigEndianReadAndAdvance.F2Dot14(_fontFileBuffer, ref arrayOffset); yScale = BigEndianReadAndAdvance.F2Dot14(_fontFileBuffer, ref arrayOffset); } GetControlPoints(glyphIndex, result); } while ((flags & (u16)OpenFontFormat.CompositeGlyphFlags.MoreComponents) > 0); u16 instructionCount = BigEndianReadAndAdvance.U16(_fontFileBuffer, ref arrayOffset); u8[] instructions = new u8[instructionCount]; for (int instructionIndex = 0; instructionIndex < instructionCount; ++instructionIndex) { instructions[instructionIndex] = BigEndianReadAndAdvance.U8(_fontFileBuffer, ref arrayOffset); } } else if (GlyphHeader.numberOfContours > 0) { Glyph simpleGlyph = new Glyph(); result.Add(simpleGlyph); simpleGlyph.ControlPoints = new List <Vector2>(); simpleGlyph.OnCurve = new List <bool>(); simpleGlyph.EndIndices = new List <int>(GlyphHeader.numberOfContours); for (int contourIndex = 0; contourIndex < GlyphHeader.numberOfContours; ++contourIndex) { simpleGlyph.EndIndices.Add(BigEndianReadAndAdvance.U16(_fontFileBuffer, ref arrayOffset)); } u16 instructionCount = BigEndianReadAndAdvance.U16(_fontFileBuffer, ref arrayOffset); arrayOffset += instructionCount; // skip instructions for now, instructions are one byte. // Unroll the flags array: int totalPointCount = simpleGlyph.EndIndices[simpleGlyph.EndIndices.Count - 1] + 1; List <u8> unrolledFlags = new List <u8>(); while (unrolledFlags.Count < totalPointCount) { u8 flag = _fontFileBuffer[arrayOffset++]; u16 flagCount = 1; if ((flag & (u8)OpenFontFormat.GlyphFlags.Repeat) > 0) { flagCount += _fontFileBuffer[arrayOffset++]; } for (int identicalFlagIndex = 0; identicalFlagIndex < flagCount; ++identicalFlagIndex) { unrolledFlags.Add(flag); } } #region Parse X Coordinates var xCoordinates = new List <s16>(); s16 lastX = 0; for (int flagIndex = 0; flagIndex < unrolledFlags.Count; ++flagIndex) { var flag = unrolledFlags[flagIndex]; s16 xCoord = 0; bool isOnCurve = (flag & (u8)OpenFontFormat.GlyphFlags.OnCurvePoint) > 0; bool isShort = (flag & (u8)OpenFontFormat.GlyphFlags.X_ShortVector) > 0; bool sameOrPositive = (flag & (u8)OpenFontFormat.GlyphFlags.X_IsSameOrPositive) > 0; bool isRepeated = !isShort && sameOrPositive; bool isPositive = isShort && sameOrPositive; simpleGlyph.OnCurve.Add(isOnCurve); if (isOnCurve) { if (isRepeated) { xCoord = lastX; } else if (isShort) { xCoord = _fontFileBuffer[arrayOffset++]; if (!isPositive) { xCoord = (short)-xCoord; } xCoord += lastX; } else { xCoord = BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); xCoord += lastX; } xCoordinates.Add(xCoord); lastX = xCoord; } else { if (isRepeated) { xCoord = lastX; } else if (isShort) { xCoord = _fontFileBuffer[arrayOffset++]; if (!isPositive) { xCoord = (short)-xCoord; } xCoord += lastX; } else { xCoord = BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); xCoord += lastX; } xCoordinates.Add(xCoord); lastX = xCoord; } } #endregion #region Parse Y Coordinates var yCoordinates = new List <s16>(); s16 lastY = 0; for (int flagIndex = 0; flagIndex < unrolledFlags.Count; ++flagIndex) { var flag = unrolledFlags[flagIndex]; s16 yCoord = 0; bool isOnCurve = (flag & (u8)OpenFontFormat.GlyphFlags.OnCurvePoint) > 0; bool isShort = (flag & (u8)OpenFontFormat.GlyphFlags.Y_ShortVector) > 0; bool sameOrPositive = (flag & (u8)OpenFontFormat.GlyphFlags.Y_IsSameOrPositive) > 0; bool isRepeated = !isShort && sameOrPositive; bool isPositive = isShort && sameOrPositive; if (isOnCurve) { if (isRepeated) { yCoord = lastY; } else if (isShort) { yCoord = _fontFileBuffer[arrayOffset++]; if (!isPositive) { yCoord = (short)-yCoord; } yCoord += lastY; } else { yCoord = BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); yCoord += lastY; } yCoordinates.Add(yCoord); lastY = yCoord; } else { if (isRepeated) { yCoord = lastY; } else if (isShort) { yCoord = _fontFileBuffer[arrayOffset++]; if (!isPositive) { yCoord = (short)-yCoord; } yCoord += lastY; } else { yCoord = BigEndianReadAndAdvance.S16(_fontFileBuffer, ref arrayOffset); yCoord += lastY; } yCoordinates.Add(yCoord); lastY = yCoord; } } #endregion for (int coordinateIndex = 0; coordinateIndex < xCoordinates.Count; ++coordinateIndex) { var x = xCoordinates[coordinateIndex] / 256f; var y = yCoordinates[coordinateIndex] / 256f; simpleGlyph.ControlPoints.Add(new Vector2(x, y)); } } } else { //Debug.LogError("No glyf table found", this); } Profiler.EndSample(); #endregion return; }