function draw(g, state, drawInfo) {
    var x = drawInfo.texArea[0];
    var y = drawInfo.texArea[1];
    var w = drawInfo.texArea[2] - drawInfo.texArea[0];
    var h = drawInfo.texArea[3] - drawInfo.texArea[1];

    var routeInfo = drawInfo.routeInfo;

    var CircularState = {
        NONE: "NONE",
        CLOCKWISE: "CLOCKWISE",
        ANTICLOCKWISE: "ANTICLOCKWISE"
    };

    g.setColor(Color.WHITE);
    g.fillRect(x, y, w, h);

    if (routeInfo != null && routeInfo != undefined) {
        var circularState = routeInfo.circularState;

        var font = loadResource("font", "mtrsteamloco:fonts/source-han-sans.otf");
        var fontBold = loadResource("font", "mtrsteamloco:fonts/source-han-sans-bold.otf");
        var headAlign = drawInfo.arrowDirection ? Number(drawInfo.arrowDirection) : 0;
        var headString = addPrefix(routeInfo.drawStations[routeInfo.drawStations.length - 1].stationName, "往|To", false);
        var headText = circularState == CircularState.NONE ? routeInfo.drawStations[routeInfo.drawStations.length - 1].stationName : routeInfo.drawStations[drawInfo.index + 1].stationName;
        var strWidth;
        var cjkWidth;
        var nonCjkWidth;
        if (circularState == CircularState.NONE) {
            g.setColor(Color.BLACK);
            if (drawInfo.index == routeInfo.drawStations.length - 1) {
                if (hasCjkPart(headText))
                    if (hasNonCjkPart(headText)) drawStrDL(g, fontBold, font, "终点站|Terminus", widthPercent(0.5), heightPercent(0.3), h * 0.4, 1, 1);
                    else drawStrUnified(g, fontBold, "终点站", widthPercent(0.5), heightPercent(0.65), h * 0.3, 1);
                else drawStrUnified(g, fontBold, "Terminus", widthPercent(0.5), heightPercent(0.65), h * 0.3, 1);
            } else {
                cjkWidth = hasCjkPart(headText)
                    ? hasNonCjkPart(headText)
                        ? getUnifiedStringWidth(g, font, "开往 ", h * 0.2) + getUnifiedStringWidth(g, fontBold, getMatching(headText, true), h * 0.275)
                        : getUnifiedStringWidth(g, font, "开往 ", h * 0.25) + getUnifiedStringWidth(g, fontBold, getMatching(headText, true), h * 0.3)
                    : 0;
                nonCjkWidth = hasNonCjkPart(headText)
                    ? hasCjkPart(headText)
                        ? getUnifiedStringWidth(g, font, `To ${getMatching(headText, false)}`, h * 0.1)
                        : getUnifiedStringWidth(g, font, `To ${getMatching(headText, false)}`, h * 0.3)
                    : 0;
                strWidth = Math.max(cjkWidth, nonCjkWidth);

                var currentX;
                switch (headAlign) {
                    case 0:
                        currentX = widthPercent(0.5) - cjkWidth * 0.5;
                        if (hasCjkPart(headText))
                            if (hasNonCjkPart(headText)) {
                                currentX += drawStrUnified(g, font, "开往 ", currentX, heightPercent(0.575), h * 0.275, 0);
                                currentX += drawStrUnified(g, fontBold, getMatching(headText, true), currentX, heightPercent(0.575), h * 0.275, 0);
                                drawStrUnified(g, font, `To ${getMatching(headText, false)}`, widthPercent(0.5), heightPercent(0.7), h * 0.1, 1);
                            } else {
                                currentX += drawStrUnified(g, font, "开往 ", currentX, heightPercent(0.65), h * 0.3, 0);
                                currentX += drawStrUnified(g, fontBold, getMatching(headText, true), currentX, heightPercent(0.65), h * 0.3, 0);
                            }
                        else drawStrUnified(g, font, `To ${getMatching(headText, false)}`, widthPercent(0.5), heightPercent(0.65), h * 0.3, 1);
                        break;
                    case 1:
                        g.drawImage(loadResource("img", "fangsu:sign/alb.png"), widthPercent(0.5) - strWidth * 0.5 - h * 0.25, heightPercent(0.3), h * 0.4, h * 0.4, null);
                        currentX = widthPercent(0.5) - strWidth * 0.5 + h * 0.225;
                        if (hasCjkPart(headText))
                            if (hasNonCjkPart(headText)) {
                                drawStrUnified(g, font, `To ${getMatching(headText, false)}`, currentX, heightPercent(0.7), h * 0.1, 0);
                                currentX += drawStrUnified(g, font, "开往 ", currentX, heightPercent(0.575), h * 0.275, 0);
                                currentX += drawStrUnified(g, fontBold, getMatching(headText, true), currentX, heightPercent(0.575), h * 0.275, 0);
                            } else {
                                currentX += drawStrUnified(g, font, "开往 ", currentX, heightPercent(0.65), h * 0.3, 0);
                                currentX += drawStrUnified(g, fontBold, getMatching(headText, true), currentX, heightPercent(0.65), h * 0.3, 0);
                            }
                        else drawStrUnified(g, font, `To ${getMatching(headText, false)}`, currentX, heightPercent(0.65), h * 0.3, 0);
                        break;
                    case 2:
                        g.drawImage(loadResource("img", "fangsu:sign/arb.png"), widthPercent(0.5) + strWidth * 0.5 - h * 0.2, heightPercent(0.3), h * 0.4, h * 0.4, null);
                        currentX = widthPercent(0.5) + strWidth * 0.5 - h * 0.225;
                        if (hasCjkPart(headText))
                            if (hasNonCjkPart(headText)) {
                                drawStrUnified(g, font, `To ${getMatching(headText, false)}`, currentX, heightPercent(0.7), h * 0.1, 2);
                                currentX -= drawStrUnified(g, fontBold, getMatching(headText, true), currentX, heightPercent(0.575), h * 0.275, 2);
                                currentX -= drawStrUnified(g, font, "开往 ", currentX, heightPercent(0.575), h * 0.275, 2);
                            } else {
                                currentX -= drawStrUnified(g, font, "开往 ", currentX, heightPercent(0.65), h * 0.3, 0);
                                currentX -= drawStrUnified(g, fontBold, getMatching(headText, true), currentX, heightPercent(0.65), h * 0.3, 0);
                            }
                        else drawStrUnified(g, font, `To ${getMatching(headText, false)}`, currentX, heightPercent(0.65), h * 0.3, 2);
                        break;
                }
            }
        } else {
            g.setColor(Color.BLACK);
            strWidth = getDLStringWidth(g, fontBold, font, headText, h * 0.4);
            var viaStations = [];
            var viaStationIndex = [2, 3, 4];
            if (routeInfo.drawStations.length > 6) viaStationIndex = [2, 4, 6];
            else if (routeInfo.drawStations.length > 5) viaStationIndex = [2, 4, 5];
            else if (routeInfo.drawStations.length > 4) viaStationIndex = [2, 3, 4];
            else if (routeInfo.drawStations.length > 3) viaStationIndex = [2, 3];
            else if (routeInfo.drawStations.length > 2) viaStationIndex = [2];
            else if (routeInfo.drawStations.length > 1) viaStationIndex = [1];
            var viaWidth = 0;
            for (var i = 0; i < viaStationIndex.length; i++) {
                var thisIndex = drawInfo.index + viaStationIndex[i];
                if (thisIndex >= routeInfo.drawStations.length - 1) thisIndex -= routeInfo.drawStations.length - 1;
                if (thisIndex == drawInfo.index) break;
                viaStations.push(routeInfo.drawStations[thisIndex]);
                viaWidth = Math.max(viaWidth, getDLStringWidth(g, font, font, routeInfo.drawStations[thisIndex].stationName, h * 0.15));
            }
            viaWidth += Math.max(w * 0.05, h * 0.1);
            var nextStationWidth =
                headAlign != 0
                    ? h * 0.3 + w * 0.1
                    : 0 +
                      (hasCjkPart(headText)
                          ? hasNonCjkPart(headText)
                              ? getDLStringWidth(g, font, font, "下一站|Next Station", h * 0.3)
                              : getUnifiedStringWidth(g, font, "下一站 ", h * 0.25)
                          : getUnifiedStringWidth(g, font, "Next Station ", h * 0.25));

            switch (headAlign) {
                case 0:
                case 1:
                    if (headAlign == 1) {
                        g.drawImage(loadResource("img", "fangsu:sign/alb.png"), widthPercent(0.1), heightPercent(0.4), h * 0.3, h * 0.3, null);
                        if (hasCjkPart(headText))
                            if (hasNonCjkPart(headText)) drawStrDL(g, font, font, "下一站|Next Station", widthPercent(0.11) + h * 0.3, heightPercent(0.4125), h * 0.3, 0, 0);
                            else drawStrUnified(g, font, "下一站", widthPercent(0.11) + h * 0.3, heightPercent(0.425), h * 0.2, 0);
                        else drawStrUnified(g, font, "Next Station", widthPercent(0.11) + h * 0.3, heightPercent(0.425), h * 0.2, 0);
                    } else {
                        if (hasCjkPart(headText))
                            if (hasNonCjkPart(headText)) drawStrDL(g, font, font, "下一站|Next Station", widthPercent(0.1), heightPercent(0.4125), h * 0.25, 0, 0);
                            else drawStrUnified(g, font, "下一站", widthPercent(0.1), heightPercent(0.425), h * 0.2, 0);
                        else drawStrUnified(g, font, "Next Station", widthPercent(0.1), heightPercent(0.425), h * 0.2, 0);
                    }
                    g.fillRect(widthPercent(0.95) - viaWidth, heightPercent(0.15), h * 0.01, h * 0.7);
                    drawStrUnified(g, font, "途径 Via", widthPercent(0.95) - viaWidth + h * 0.05, heightPercent(0.275), h * 0.1, 0);
                    for (var i = 0; i < viaStations.length; i++) {
                        var thisY = heightPercent(0.4) + ((h * 0.45) / viaStations.length) * i;
                        drawStrDL(g, font, font, viaStations[i].stationName, widthPercent(0.95) - viaWidth + h * 0.1, thisY, h * 0.125, 0, 0);
                    }
                    drawStrDL(g, fontBold, font, headText, widthPercent(0.1) + (w * 0.85 - viaWidth - nextStationWidth) * 0.5 + nextStationWidth, heightPercent(0.3), h * 0.4, 1, 1);
                    break;
                case 2:
                    g.drawImage(loadResource("img", "fangsu:sign/arb.png"), widthPercent(0.9) - h * 0.3, heightPercent(0.4), h * 0.3, h * 0.3, null);
                    var stnNameWidth = drawStrDL(g, fontBold, font, headText, widthPercent(0.87) - h * 0.3, heightPercent(0.3), h * 0.4, 2, 1);

                    if (hasCjkPart(headText))
                        if (hasNonCjkPart(headText)) drawStrDL(g, font, font, "下一站|Next Station", widthPercent(0.85) - h * 0.3 - stnNameWidth, heightPercent(0.4125), h * 0.3, 2, 2);
                        else drawStrUnified(g, font, "下一站", widthPercent(0.85) - h * 0.3 - stnNameWidth, heightPercent(0.425), h * 0.2, 2);
                    else drawStrUnified(g, font, "Next Station", widthPercent(0.85) - h * 0.3 - stnNameWidth, heightPercent(0.425), h * 0.2, 2);

                    g.fillRect(widthPercent(0.05) + viaWidth, heightPercent(0.15), h * 0.01, h * 0.7);
                    drawStrUnified(g, font, "途径 Via", widthPercent(0.05) + viaWidth - h * 0.05, heightPercent(0.275), h * 0.1, 2);
                    for (var i = 0; i < viaStations.length; i++) {
                        var thisY = heightPercent(0.4) + ((h * 0.45) / viaStations.length) * i;
                        drawStrDL(g, font, font, viaStations[i].stationName, widthPercent(0.05) + viaWidth - h * 0.1, thisY, h * 0.125, 2, 2);
                    }

                    break;
            }
        }
    }

    function widthPercent(p) {
        return x + w * p;
    }
    function heightPercent(p) {
        return y + h * p;
    }
}
