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 extraConfig = drawInfo.extraConfig;

    if (!extraConfig) {
        extraConfig = {};
    }

    var imgPath = extraConfig.imgPath ? extraConfig.imgPath : "mtr:textures/block/sign/logo_grayscale.png";
    var imgIcon = loadResource("img", imgPath);

    g.setColor(Color.BLACK);
    var font = loadResource("font", "mtrsteamloco:fonts/source-han-sans.otf");
    var fontBold = loadResource("font", "mtrsteamloco:fonts/source-han-sans-bold.otf");
    // g.drawString(JSON.stringify(drawInfo), 10, 10);

    g.drawImage(imgIcon, widthPercent(0.85) - h * 0.4, heightPercent(0.05), h * 0.4, h * 0.4, null);
    g.drawImage(imgIcon, widthPercent(0.15), heightPercent(0.55), h * 0.4, h * 0.4, null);

    if (drawInfo.station) {
        // var dispStationName = addSuffix(drawInfo.station.name, "站|Station", false);
        var dispStationName = drawInfo.station.name;
        g.setColor(Color.WHITE);
        drawStrDL(g, fontBold, fontBold, dispStationName, widthPercent(0.85) - h * 0.45, heightPercent(0.125), h * 0.175, 2, 2);
        drawStrDL(g, fontBold, fontBold, dispStationName, widthPercent(0.15) + h * 0.45, heightPercent(0.625), h * 0.175, 0, 0);
    }

    if (drawInfo.routes) {
        var routeObj = {};
        var routeArr = [];
        for (var i = 0; i < drawInfo.routes.length; i++) {
            var thisRoute = routeToObj(drawInfo.routes[i]);
            routeObj[thisRoute.routeName] = thisRoute.routeColor;
        }
        for (var key in routeObj) {
            if (routeObj.hasOwnProperty(key)) {
                routeArr.push({ name: key, color: routeObj[key] });
            }
        }
        routeArr.sort(function (a, b) {
            return a.name.localeCompare(b.name);
        });
        var currentX1 = widthPercent(0.05);
        var currentX2 = widthPercent(0.95);
        for (var i = 0; i < routeArr.length; i += 2) {
            if (routeArr[i + 1]) {
                var route1 = routeArr[i];
                var route2 = routeArr[i + 1];
                var width1 =
                    isNumLine(route1.name) && hasCjkPart(route1.name) && hasNonCjkPart(route1.name)
                        ? getDLStringWidth(g, fontBold, fontBold, `号线|${getMatching(route1.name, false)}`, h * 0.075) +
                          getUnifiedStringWidth(g, fontBold, String(getCJKLineName(getMatching(route1.name, true))), h * 0.08)
                        : getDLStringWidth(g, fontBold, fontBold, route1.name, h * 0.075);
                var width2 =
                    isNumLine(route2.name) && hasCjkPart(route2.name) && hasNonCjkPart(route2.name)
                        ? getDLStringWidth(g, fontBold, fontBold, `号线|${getMatching(route2.name, false)}`, h * 0.075) +
                          getUnifiedStringWidth(g, fontBold, String(getCJKLineName(getMatching(route2.name, true))), h * 0.08)
                        : getDLStringWidth(g, fontBold, fontBold, route2.name, h * 0.075);
                var thisWidth = Math.max(width1, width2);

                g.setColor(Color.WHITE);

                var thisBaseX = currentX1 + thisWidth * 0.5 + h * 0.0125;
                drawRouteNameBox(route1.name, thisBaseX, heightPercent(0.1), h * 0.075);
                drawRouteNameBox(route2.name, thisBaseX, heightPercent(0.2), h * 0.075);
                thisBaseX = currentX2 - thisWidth * 0.5 - h * 0.0125;
                drawRouteNameBox(route1.name, thisBaseX, heightPercent(0.6), h * 0.075);
                drawRouteNameBox(route2.name, thisBaseX, heightPercent(0.7), h * 0.075);

                g.setColor(route1.color);
                g.fillRect(currentX1, heightPercent(0.18), thisWidth + h * 0.025, h * 0.02);
                g.fillRect(currentX2 - thisWidth - h * 0.0125, heightPercent(0.68), thisWidth + h * 0.025, h * 0.02);
                g.setColor(route2.color);
                g.fillRect(currentX1, heightPercent(0.28), thisWidth + h * 0.025, h * 0.02);
                g.fillRect(currentX2 - thisWidth - h * 0.0125, heightPercent(0.78), thisWidth + h * 0.025, h * 0.02);
                currentX1 += thisWidth + h * 0.025;
                currentX2 -= thisWidth + h * 0.025;
            } else {
                var route = routeArr[i];
                var width =
                    isNumLine(route.name) && hasCjkPart(route.name) && hasNonCjkPart(route.name)
                        ? getDLStringWidth(g, fontBold, fontBold, `号线|${getMatching(route.name, false)}`, h * 0.075) +
                          getUnifiedStringWidth(g, fontBold, String(getCJKLineName(getMatching(route.name, true))), h * 0.08)
                        : getDLStringWidth(g, fontBold, fontBold, route.name, h * 0.075);

                g.setColor(Color.WHITE);
                var thisBaseX = currentX1 + width * 0.5 + h * 0.0125;
                drawRouteNameBox(route.name, thisBaseX, heightPercent(0.1), h * 0.075);

                thisBaseX = currentX2 - width * 0.5 - h * 0.0125;
                drawRouteNameBox(route.name, thisBaseX, heightPercent(0.6), h * 0.075);

                g.setColor(route.color);
                g.fillRect(currentX1, heightPercent(0.18), width + h * 0.025, h * 0.02);
                g.fillRect(currentX2 - width - h * 0.0125, heightPercent(0.68), width + h * 0.025, h * 0.02);

                currentX1 -= width + h * 0.025;
                currentX2 += width + h * 0.025;
            }
        }
    }

    if (drawInfo.station) {
        var stationPos = new Vector3f(drawInfo.station.getCenter());
        var distance =
            parseInt((Math.sqrt((Math.abs(drawInfo.block.getWorldPosVector3f().x() - stationPos.x()) ^ 2) + (Math.abs(drawInfo.block.getWorldPosVector3f().z() - stationPos.z()) ^ 2)) * 8) / 50) * 50;
        g.setColor(Color.WHITE);
        var width = drawStrUnified(g, fontBold, distance + " m", widthPercent(0.05) + h * 0.025, heightPercent(0.4), h * 0.1, 0);
        drawStrUnified(g, fontBold, distance + " m", widthPercent(0.95) - h * 0.025, heightPercent(0.9), h * 0.1, 2);
        g.setStroke(new BasicStroke(h * 0.007));
        g.drawRoundRect(widthPercent(0.05), heightPercent(0.3), width + h * 0.05, h * 0.12, h * 0.005, h * 0.005);
        g.drawRoundRect(widthPercent(0.95) - width - h * 0.05, heightPercent(0.8), width + h * 0.05, h * 0.12, h * 0.005, h * 0.005);
    }

    function widthPercent(p) {
        return x + w * p;
    }
    function heightPercent(p) {
        return y + h * p;
    }
    function drawRouteNameBox(routeName, x, y, h) {
        if (isNumLine(routeName) && hasCjkPart(routeName) && hasNonCjkPart(routeName)) {
            var numWidth = getUnifiedStringWidth(g, fontBold, String(getCJKLineName(getMatching(routeName, true))), h * 1.125);
            drawStrUnified(
                g,
                fontBold,
                String(getCJKLineName(getMatching(routeName, true))),
                x - numWidth * 0.5 - drawStrDL(g, font, font, `号线|${getMatching(routeName, false)}`, x + numWidth * 0.5, y, h, 1, 0) * 0.5,
                y + h,
                h * 1.125,
                0
            );
        } else drawStrDL(g, font, font, routeName, x, y, h, 1, 1);
    }
}
