<html><head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>垃圾渲染器</title>
<style>
body {
    font-family: Arial, sans-serif;
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: #f0f0f0;
    margin: 0;
    padding: 20px;
}

#canvas {
    border: 1px solid #000;
}

.controls {
    margin-top: 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

input, button {
    padding: 5px;
    font-size: 16px;
}

button {
    cursor: pointer;
    background-color: #4CAF50;
    color: white;
    border: none;
    padding: 10px 20px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 16px;
    margin: 4px 2px;
    transition-duration: 0.4s;
}

button:hover {
    background-color: #45a049;
}
</style>
</head>
<body>
<h1>垃圾渲染器</h1>
<canvas id="canvas" width="500" height="500"></canvas>
<div class="controls">
    <label>
        方格宽度:
        <input type="number" id="squareWidth" value="50" min="1" max="500">
    </label>
    <label>
        方格高度:
        <input type="number" id="squareHeight" value="50" min="1" max="500">
    </label>
    <label>
        第二图层透明度:
        <input type="range" id="layerOpacity" min="0" max="1" step="0.01" value="0.5">
    </label>
    <button id="saveButton">保存为图片</button>
</div>

<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

const squareWidthInput = document.getElementById('squareWidth');
const squareHeightInput = document.getElementById('squareHeight');
const layerOpacityInput = document.getElementById('layerOpacity');
const saveButton = document.getElementById('saveButton');

function getColor(x, y) {
    // 第一图层：全局渐变
    const globalGradient = (x + (1 - y)) * 0.5;
    
    // 第二图层：方格渐变
    const squareWidth = parseInt(squareWidthInput.value);
    const squareHeight = parseInt(squareHeightInput.value);
    const squareX = (x * canvas.width) % squareWidth / squareWidth;
    const squareY = (y * canvas.height) % squareHeight / squareHeight;
    const squareGradient = (squareX + (1 - squareY)) * 0.5;
    
    // 叠加两个图层
    const layerOpacity = parseFloat(layerOpacityInput.value);
    const finalColor = globalGradient * (1 - layerOpacity * (1 - squareGradient));
    
    return Math.floor(finalColor * 255);
}

function render() {
    const imageData = ctx.createImageData(canvas.width, canvas.height);
    const data = imageData.data;

    for (let y = 0; y < canvas.height; y++) {
        for (let x = 0; x < canvas.width; x++) {
            const index = (y * canvas.width + x) * 4;
            const color = getColor(x / canvas.width, y / canvas.height);
            data[index] = color;     // R
            data[index + 1] = color; // G
            data[index + 2] = color; // B
            data[index + 3] = 255;   // A
        }
    }

    ctx.putImageData(imageData, 0, 0);
}

function debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
        const later = () => {
            clearTimeout(timeout);
            func(...args);
        };
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
}

const debouncedRender = debounce(render, 100);

squareWidthInput.addEventListener('input', debouncedRender);
squareHeightInput.addEventListener('input', debouncedRender);
layerOpacityInput.addEventListener('input', debouncedRender);

saveButton.addEventListener('click', () => {
    const link = document.createElement('a');
    link.download = 'laji.png';
    link.href = canvas.toDataURL();
    link.click();
});

render();
</script>
</body></html>

把它移植到Python+Numpy，要求尽可能高效运行，效率越高越好，但功能不变，并且允许指定图像尺寸