Leaflet.js(或其他解决方案)缩放到无模糊的放大像素

Leaflet.js (or other solution) zoom to magnified pixels without blur

我最近一直在使用 Leaflet 显示光栅图像。

我想为特定项目做的是能够放大图像,使像素以清晰划定的方式在屏幕上放大,例如在放大图像时会看到Photoshop之类的。我还想在最大之前的某个缩放级别保留图像像素和屏幕像素之间的 1:1 对应关系。

我尝试按照 here and here 所述超越 maxNativeZoom,这有效,但插值导致像素模糊。

我想到了一个替代方案,即使用 'nearest neighbour' 插值将每个像素扩展为更大的正方形,使源图像更大:当缩放到 maxNativeZoom 时,正方形看起来像急剧放大的像素,即使它们不是。

问题是:

我考虑过使用两个图块集:第一个是从原始图像到它的 maxNativeZoom,然后是更大的 'nearest neighbour' 插值图像,之后是 this.

但是,这更复杂,并没有避免大量图块的问题,而且显得不够优雅。

所以:

非常感谢

一种方法是利用 image-rendering CSS property。这可以提示浏览器在 <img> 元素上使用最近邻插值,例如 Leaflet 地图图块。

例如:

img.leaflet-tile {
    image-rendering: pixelated;
}

看到一个working demo. Beware of incomplete browser support

一种更复杂的方法(但适用于更多浏览器的方法)是利用 WebGL; in particular Leaflet.TileLayer.GL.

这涉及一些internal changes to Leaflet.TileLayer.GL to support a per-tile uniform,最关键的是在每个瓦片渲染中将统一值设置为瓦片坐标...

gl.uniform3f(this._uTileCoordsPosition, coords.x, coords.y, coords.z);

...有一个 L.TileLayer "displays" 一个非过度缩放的图块用于过度缩放的图块坐标(而不是仅仅跳过不存在的图块)...

    var hackishTilelayer = new L.TileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        'attribution': 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors',
        maxNonPixelatedZoom: 3
    });

    hackishTilelayer.getTileUrl = function(coords) {
        if (coords.z > this.options.maxNonPixelatedZoom) {
            return this.getTileUrl({
                x: Math.floor(coords.x / 2),
                y: Math.floor(coords.y / 2),
                z: coords.z - 1
            });
        }

        // Skip L.TileLayer.prototype.getTileUrl.call(this, coords), instead
        // apply the URL template directly to avoid maxNativeZoom shenanigans
        var data = {
            r: L.Browser.retina ? '@2x' : '',
            s: this._getSubdomain(coords),
            x: coords.x,
            y: coords.y,
            z: coords.z // *not* this._getZoomForUrl() !
        };
        var url = L.Util.template(this._url, L.Util.extend(data, this.options));
        return url;
    }

...加上一个片段着色器,它在提取纹素之前向下舍入纹素坐标(加上一个平铺坐标模数相关偏移),以实际执行最近邻过采样...

var fragmentShader = `
highp float factor = max(1., pow(2., uTileCoords.z - uPixelatedZoomLevel));
vec2 subtileOffset = mod(uTileCoords.xy, factor);

void main(void) {
    vec2 texelCoord = floor(vTextureCoords.st * uTileSize / factor ) / uTileSize;

    texelCoord.xy += subtileOffset / factor;

    vec4 texelColour = texture2D(uTexture0, texelCoord);

    // This would output the image colours "as is"
    gl_FragColor = texelColour;
}
`;

...在 L.TileLayer.GL 的一个实例中全部捆绑在一起(同步周围制服的一些数字):

    var pixelated = L.tileLayer.gl({
        fragmentShader: fragmentShader,
        tileLayers: [hackishTilelayer],
        uniforms: {
            // The shader will need the zoom level as a uniform...
            uPixelatedZoomLevel: hackishTilelayer.options.maxNonPixelatedZoom,
            // ...as well as the tile size in pixels.
            uTileSize: [hackishTilelayer.getTileSize().x, hackishTilelayer.getTileSize().y]
        }
    }).addTo(map);

您可以看到一切都在协同工作 in this demo