public void AdjustPalette(IReadOnlyDictionary<string, MutablePalette> palettes)
        {
            if (remainingFrames == 0)
                return;

            foreach (var pal in palettes)
            {
                if (info.ExcludePalette.Contains(pal.Key))
                {
                    continue;
                }
                for (var x = 0; x < Palette.Size; x++)
                {
                    var orig = pal.Value.GetColor(x);
                    var c = Info.Color;
                    var color = Color.FromArgb(orig.A, ((int)c.R).Clamp(0, 255), ((int)c.G).Clamp(0, 255), ((int)c.B).Clamp(0, 255));
                    var final = GUtil.PremultipliedColorLerp(info.Ratio, orig, GUtil.PremultiplyAlpha(Color.FromArgb(orig.A, color)));
                    pal.Value.SetColor(x, final);
                }
            }
        }
        void DrawConnectedLine(float3[] points, float width, Color color, bool closed)
        {
            // Not a line
            if (points.Length < 2)
            {
                return;
            }

            // Single segment
            if (points.Length == 2)
            {
                DrawLine(points[0], points[1], width, color);
                return;
            }

            color = Util.PremultiplyAlpha(color);
            var r = color.R / 255.0f;
            var g = color.G / 255.0f;
            var b = color.B / 255.0f;
            var a = color.A / 255.0f;

            var start  = points[0];
            var end    = points[1];
            var dir    = (end - start) / (end - start).XY.Length;
            var corner = width / 2 * new float3(-dir.Y, dir.X, dir.Z);

            // Corners for start of line segment
            var ca = start - corner;
            var cb = start + corner;

            // Segment is part of closed loop
            if (closed)
            {
                var prev       = points[points.Length - 1];
                var prevDir    = (start - prev) / (start - prev).XY.Length;
                var prevCorner = width / 2 * new float3(-prevDir.Y, prevDir.X, prevDir.Z);
                ca = IntersectionOf(start - prevCorner, prevDir, start - corner, dir);
                cb = IntersectionOf(start + prevCorner, prevDir, start + corner, dir);
            }

            var limit = closed ? points.Length : points.Length - 1;

            for (var i = 0; i < limit; i++)
            {
                var next       = points[(i + 2) % points.Length];
                var nextDir    = (next - end) / (next - end).XY.Length;
                var nextCorner = width / 2 * new float3(-nextDir.Y, nextDir.X, nextDir.Z);

                // Vertices for the corners joining start-end to end-next
                var cc = closed || i < limit - 1 ? IntersectionOf(end + corner, dir, end + nextCorner, nextDir) : end + corner;
                var cd = closed || i < limit - 1 ? IntersectionOf(end - corner, dir, end - nextCorner, nextDir) : end - corner;

                // Fill segment
                vertices[0] = new Vertex(ca + Offset, r, g, b, a, 0, 0);
                vertices[1] = new Vertex(cb + Offset, r, g, b, a, 0, 0);
                vertices[2] = new Vertex(cc + Offset, r, g, b, a, 0, 0);
                vertices[3] = new Vertex(cc + Offset, r, g, b, a, 0, 0);
                vertices[4] = new Vertex(cd + Offset, r, g, b, a, 0, 0);
                vertices[5] = new Vertex(ca + Offset, r, g, b, a, 0, 0);
                parent.DrawRGBAVertices(vertices);

                // Advance line segment
                end    = next;
                dir    = nextDir;
                corner = nextCorner;

                ca = cd;
                cb = cc;
            }
        }