public SKPath GetTextPathOnPath()

in binding/SkiaSharp/SKFont.cs [862:967]


		public SKPath GetTextPathOnPath (ReadOnlySpan<ushort> glyphs, ReadOnlySpan<float> glyphWidths, ReadOnlySpan<SKPoint> glyphPositions, SKPath path, SKTextAlign textAlign = SKTextAlign.Left)
		{
			if (glyphs.Length != glyphWidths.Length)
				throw new ArgumentException ("The number of glyphs and glyph widths must be the same.");
			if (glyphs.Length != glyphPositions.Length)
				throw new ArgumentException ("The number of glyphs and glyph offsets must be the same.");
			if (path == null)
				throw new ArgumentNullException (nameof (path));

			if (glyphs.Length == 0)
				return new SKPath ();

			using var glyphPathCache = new GlyphPathCache (this);
			using var pathMeasure = new SKPathMeasure (path);

			var contourLength = pathMeasure.Length;

			var textLength = glyphPositions[glyphs.Length - 1].X + glyphWidths[glyphs.Length - 1];
			var alignment = (int)textAlign * 0.5f;
			var startOffset = glyphPositions[0].X + (contourLength - textLength) * alignment;

			var textPath = new SKPath ();

			// TODO: deal with multiple contours?
			for (var index = 0; index < glyphPositions.Length; index++) {
				var glyphOffset = glyphPositions[index];
				var gw = glyphWidths[index];
				var x0 = startOffset + glyphOffset.X;
				var x1 = x0 + gw;

				if (x1 >= 0 && x0 <= contourLength) {
					var glyphId = glyphs[index];
					var glyphPath = glyphPathCache.GetPath (glyphId);
					if (glyphPath != null) {
						var transformation = SKMatrix.CreateTranslation (x0, glyphOffset.Y);
						MorphPath (textPath, glyphPath, pathMeasure, transformation);
					}
				}
			}

			return textPath;

			static void MorphPath (SKPath dst, SKPath src, SKPathMeasure meas, in SKMatrix matrix)
			{
				// TODO:
				// Need differentially more subdivisions when the follow-path is curvy. Not sure how to determine
				// that, but we need it. I guess a cheap answer is let the caller tell us, but that seems like a
				// cop-out. Another answer is to get Skia's Rob Johnson to figure it out.

				using var it = src.CreateIterator (false);

				SKPathVerb verb;

				Span<SKPoint> srcP = stackalloc SKPoint[4];
				Span<SKPoint> dstP = stackalloc SKPoint[4];

				while ((verb = it.Next (srcP)) != SKPathVerb.Done) {
					switch (verb) {
						case SKPathVerb.Move:
							MorphPoints (dstP, srcP, 1, meas, matrix);
							dst.MoveTo (dstP[0]);
							break;
						case SKPathVerb.Line:
							// turn lines into quads to look bendy
							srcP[0].X = (srcP[0].X + srcP[1].X) * 0.5f;
							srcP[0].Y = (srcP[0].Y + srcP[1].Y) * 0.5f;
							MorphPoints (dstP, srcP, 2, meas, matrix);
							dst.QuadTo (dstP[0], dstP[1]);
							break;
						case SKPathVerb.Quad:
							MorphPoints (dstP, srcP.Slice (1, 2), 2, meas, matrix);
							dst.QuadTo (dstP[0], dstP[1]);
							break;
						case SKPathVerb.Conic:
							MorphPoints (dstP, srcP.Slice (1, 2), 2, meas, matrix);
							dst.ConicTo (dstP[0], dstP[1], it.ConicWeight ());
							break;
						case SKPathVerb.Cubic:
							MorphPoints (dstP, srcP.Slice (1, 3), 3, meas, matrix);
							dst.CubicTo (dstP[0], dstP[1], dstP[2]);
							break;
						case SKPathVerb.Close:
							dst.Close ();
							break;
						default:
							Debug.Fail ("Unknown verb when iterating points in glyph path.");
							break;
					}
				}
			}

			static void MorphPoints (Span<SKPoint> dst, Span<SKPoint> src, int count, SKPathMeasure meas, in SKMatrix matrix)
			{
				for (int i = 0; i < count; i++) {
					SKPoint s = matrix.MapPoint (src[i].X, src[i].Y);

					if (!meas.GetPositionAndTangent (s.X, out var p, out var t)) {
						// set to 0 if the measure failed, so that we just set dst == pos
						t = SKPoint.Empty;
					}

					// y-offset the point in the direction of the normal vector on the path.
					dst[i] = new SKPoint (p.X - t.Y * s.Y, p.Y + t.X * s.Y);
				}
			}
		}