From 36159287d5be98992cf07985eaadd2fe642e4c09 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Fri, 8 Apr 2022 11:29:07 +0100 Subject: [PATCH 1/2] handle gutter in renderer --- src/ol/renderer/webgl/TileLayer.js | 30 +++++++++++----- src/ol/webgl/TileTexture.js | 57 +++++++----------------------- 2 files changed, 34 insertions(+), 53 deletions(-) diff --git a/src/ol/renderer/webgl/TileLayer.js b/src/ol/renderer/webgl/TileLayer.js index 9323554099..565496d06e 100644 --- a/src/ol/renderer/webgl/TileLayer.js +++ b/src/ol/renderer/webgl/TileLayer.js @@ -420,6 +420,7 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { const tileLayer = this.getLayer(); const tileSource = tileLayer.getRenderSource(); const tileGrid = tileSource.getTileGridForProjection(viewState.projection); + const gutter = tileSource.getGutterForProjection(viewState.projection); const extent = getRenderExtent(frameState, frameState.extent); const z = tileGrid.getZForResolution( viewState.resolution, @@ -543,11 +544,13 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { this.tileTransform_, 0, 0, - 2 / ((frameState.size[0] * tileScale) / tileSize[0]), - -2 / ((frameState.size[1] * tileScale) / tileSize[1]), + 2 / ((frameState.size[0] * tileScale) / (tileSize[0] + 2 * gutter)), + -2 / ((frameState.size[1] * tileScale) / (tileSize[1] + 2 * gutter)), viewState.rotation, - -(centerI - tileCenterI), - -(centerJ - tileCenterJ) + ((tileCenterI - centerI - gutter / tileSize[0]) * tileSize[0]) / + (tileSize[0] + 2 * gutter), + ((tileCenterJ - centerJ - gutter / tileSize[1]) * tileSize[1]) / + (tileSize[1] + 2 * gutter) ); this.helper.setUniformMatrixValue( @@ -599,11 +602,11 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { this.helper.setUniformFloatValue(Uniforms.DEPTH, depth); this.helper.setUniformFloatValue( Uniforms.TEXTURE_PIXEL_WIDTH, - tileSize[0] + tileSize[0] + 2 * gutter ); this.helper.setUniformFloatValue( Uniforms.TEXTURE_PIXEL_HEIGHT, - tileSize[1] + tileSize[1] + 2 * gutter ); this.helper.setUniformFloatValue( Uniforms.TEXTURE_RESOLUTION, @@ -611,13 +614,22 @@ class WebGLTileLayerRenderer extends WebGLLayerRenderer { ); this.helper.setUniformFloatValue( Uniforms.TEXTURE_ORIGIN_X, - tileOrigin[0] + tileCenterI * tileSize[0] * tileResolution + tileOrigin[0] + + tileCenterI * tileSize[0] * tileResolution - + gutter * tileResolution ); this.helper.setUniformFloatValue( Uniforms.TEXTURE_ORIGIN_Y, - tileOrigin[1] - tileCenterJ * tileSize[1] * tileResolution + tileOrigin[1] - + tileCenterJ * tileSize[1] * tileResolution + + gutter * tileResolution ); - this.helper.setUniformFloatVec4(Uniforms.RENDER_EXTENT, extent); + let gutterExtent = extent; + if (gutter > 0) { + gutterExtent = tileGrid.getTileCoordExtent(tileCoord); + getIntersection(gutterExtent, extent, gutterExtent); + } + this.helper.setUniformFloatVec4(Uniforms.RENDER_EXTENT, gutterExtent); this.helper.setUniformFloatValue( Uniforms.RESOLUTION, viewState.resolution diff --git a/src/ol/webgl/TileTexture.js b/src/ol/webgl/TileTexture.js index 8c457639bf..35d3080e58 100644 --- a/src/ol/webgl/TileTexture.js +++ b/src/ol/webgl/TileTexture.js @@ -10,9 +10,6 @@ import ReprojTile from '../reproj/Tile.js'; import TileState from '../TileState.js'; import WebGLArrayBuffer from './Buffer.js'; import {ARRAY_BUFFER, STATIC_DRAW} from '../webgl.js'; -import {IMAGE_SMOOTHING_DISABLED} from '../renderer/canvas/common.js'; -import {assign} from '../obj.js'; -import {createCanvasContext2D} from '../dom.js'; import {toSize} from '../size.js'; /** @@ -243,38 +240,16 @@ class TileTexture extends EventTarget { const tile = this.tile; if (tile instanceof ImageTile || tile instanceof ReprojTile) { - let image = tile.getImage(); - if (this.gutter_ !== 0) { - const gutter = this.tilePixelRatio_ * this.gutter_; - const width = Math.round(image.width - 2 * gutter); - const height = Math.round(image.height - 2 * gutter); - const context = createCanvasContext2D(width, height); - if (!tile.interpolate) { - assign(context, IMAGE_SMOOTHING_DISABLED); - } - context.drawImage( - image, - gutter, - gutter, - width, - height, - 0, - 0, - width, - height - ); - image = context.canvas; - } const texture = gl.createTexture(); this.textures.push(texture); this.bandCount = 4; - uploadImageTexture(gl, texture, image, tile.interpolate); + uploadImageTexture(gl, texture, tile.getImage(), tile.interpolate); return; } const pixelSize = [ - this.size[0] * this.tilePixelRatio_, - this.size[1] * this.tilePixelRatio_, + (this.size[0] + 2 * this.gutter_) * this.tilePixelRatio_, + (this.size[1] + 2 * this.gutter_) * this.tilePixelRatio_, ]; const data = tile.getData(); const isFloat = data instanceof Float32Array; @@ -373,14 +348,19 @@ class TileTexture extends EventTarget { return null; } - col = Math.floor(this.tilePixelRatio_ * col); - row = Math.floor(this.tilePixelRatio_ * row); + const gutter = Math.round(this.tilePixelRatio_ * this.gutter_); + col = Math.floor(this.tilePixelRatio_ * col) + gutter; + row = Math.floor(this.tilePixelRatio_ * row) + gutter; if (this.tile instanceof DataTile) { const data = this.tile.getData(); - const pixelsPerRow = Math.floor(this.tilePixelRatio_ * this.size[0]); + let size = this.size; + if (gutter > 0) { + size = [size[0] + 2 * this.gutter_, size[1] + 2 * this.gutter_]; + } + const pixelsPerRow = Math.floor(this.tilePixelRatio_ * size[0]); if (data instanceof DataView) { - const bytesPerPixel = data.byteLength / (this.size[0] * this.size[1]); + const bytesPerPixel = data.byteLength / (size[0] * size[1]); const offset = row * pixelsPerRow * bytesPerPixel + col * bytesPerPixel; const buffer = data.buffer.slice(offset, offset + bytesPerPixel); return new DataView(buffer); @@ -397,19 +377,8 @@ class TileTexture extends EventTarget { let data; const image = this.tile.getImage(); - const gutter = Math.round(this.tilePixelRatio_ * this.gutter_); try { - pixelContext.drawImage( - image, - col + gutter, - row + gutter, - 1, - 1, - 0, - 0, - 1, - 1 - ); + pixelContext.drawImage(image, col, row, 1, 1, 0, 0, 1, 1); data = pixelContext.getImageData(0, 0, 1, 1).data; } catch (err) { pixelContext = null; From 6ca44f6ffa9e0d59341a9fee19afaee39aa358d4 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Fri, 8 Apr 2022 13:06:18 +0100 Subject: [PATCH 2/2] add gutter option --- src/ol/source/DataTile.js | 17 +++++++++ src/ol/source/XYZ.js | 16 +++++++++ .../expected.png | Bin 0 -> 3475 bytes .../main.js | 33 ++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100644 test/rendering/cases/webgl-data-tile-interpolate-gutter/expected.png create mode 100644 test/rendering/cases/webgl-data-tile-interpolate-gutter/main.js diff --git a/src/ol/source/DataTile.js b/src/ol/source/DataTile.js index 3836457d7a..c840d37a46 100644 --- a/src/ol/source/DataTile.js +++ b/src/ol/source/DataTile.js @@ -27,6 +27,9 @@ import {toPromise} from '../functions.js'; * @property {number} [maxZoom=42] Optional max zoom level. Not used if `tileGrid` is provided. * @property {number} [minZoom=0] Optional min zoom level. Not used if `tileGrid` is provided. * @property {number|import("../size.js").Size} [tileSize=[256, 256]] The pixel width and height of the tiles. + * @property {number} [gutter=0] The size in pixels of the gutter around data tiles to ignore. + * This allows artifacts of rendering at tile edges to be ignored. + * Supported data should be wider and taller than the tile size by a value of `2 x gutter`. * @property {number} [maxResolution] Optional tile grid resolution at level zero. Not used if `tileGrid` is provided. * @property {import("../proj.js").ProjectionLike} [projection='EPSG:3857'] Tile projection. * @property {import("../tilegrid/TileGrid.js").default} [tileGrid] Tile grid. @@ -80,6 +83,12 @@ class DataTileSource extends TileSource { interpolate: options.interpolate, }); + /** + * @private + * @type {number} + */ + this.gutter_ = options.gutter !== undefined ? options.gutter : 0; + /** * @private * @type {!Object} @@ -99,6 +108,14 @@ class DataTileSource extends TileSource { this.bandCount = options.bandCount === undefined ? 4 : options.bandCount; // assume RGBA if undefined } + /** + * @param {import("../proj/Projection.js").default} projection Projection. + * @return {number} Gutter. + */ + getGutterForProjection(projection) { + return this.gutter_; + } + /** * @param {Loader} loader The data loader. * @protected diff --git a/src/ol/source/XYZ.js b/src/ol/source/XYZ.js index 15b11cb380..4bf7e73f6d 100644 --- a/src/ol/source/XYZ.js +++ b/src/ol/source/XYZ.js @@ -36,6 +36,9 @@ import {createXYZ, extentFromProjection} from '../tilegrid.js'; * should be set to `2`. * @property {number|import("../size.js").Size} [tileSize=[256, 256]] The tile size used by the tile service. * Not used if `tileGrid` is provided. + * @property {number} [gutter=0] The size in pixels of the gutter around image tiles to ignore. + * This allows artifacts of rendering at tile edges to be ignored. + * Supported images should be wider and taller than the tile size by a value of `2 x gutter`. * @property {import("../Tile.js").UrlFunction} [tileUrlFunction] Optional function to get * tile URL given a tile coordinate and the projection. * Required if `url` or `urls` are not provided. @@ -114,6 +117,19 @@ class XYZ extends TileImage { attributionsCollapsible: options.attributionsCollapsible, zDirection: options.zDirection, }); + + /** + * @private + * @type {number} + */ + this.gutter_ = options.gutter !== undefined ? options.gutter : 0; + } + + /** + * @return {number} Gutter. + */ + getGutter() { + return this.gutter_; } } diff --git a/test/rendering/cases/webgl-data-tile-interpolate-gutter/expected.png b/test/rendering/cases/webgl-data-tile-interpolate-gutter/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..b2bc53610990486b70201118e8e21058b268d538 GIT binary patch literal 3475 zcmeH~X;4#F7=}-R$W{mUg4KMVj8en|qR)A5h(2XDkq+$Dw4{Xoex`)+(uaG?LXEh)eDweMiG zRnI#qJ@FDsMd!h$`z70QuW44aiX3#}=I%u-)>w}w60}Jq5zK51!(PxXhM4I`N1tGI*r znnsY4mKMYeo2l1nRZ^1_HamFYMj_B!2>^bYLP;^|&}bs@%(^M()HG2$WnggdYF=L6 zusnzrncF{y%j4-4x_B_AR<3Vtktq}k&Il=8H+pV-%#o9lqQ2)46b>pEJ3`QD@g?Fc>zrX*!*(v<|wdu1! z=c(g`R`iowcI>l3CIMs1=r)ssj7VpOy`d=$#GMN_FoK1A@Ty`vr#gZ^cw|sWq}Ie zkyz2Jx~5+D1r;B<*DBnDC{Ci*-LoSJhFimQ$gLe?jv#QjD2epC^r$2;_sYuJ_deQQ|$QwSwt+Nn4 z96_aYK5CsC*j9BGMG}!OKOz|V-~tjQWx`-ZpoQwu#y|!0->f7w(QMwa=TokA)Zp+X zRqnE=K`A~3LeVNx)Fh-=tGRn1$^=K_EJDHcca7-XFfG+c2cZ>-C6|CM$G~*iDf&>r z<&AJ_DH+kb30%DF3sLI~LyzPT333p_B3wYb&on_+wm>U8sdYsK4|)DUXkr`5zENzm zYttq1kk}@66EbHwy_8~}uH73@@5M^UQ9id>3`cQ`pOiNMgNF%gTMY1NKx0%$% zv<>VLo7xg15)KeOgSGu5@;!a4P$XRcHi)4uaK=Q-p)UDkXgx&1oSkoo!Tpa;Ly=h7 zBZGN!92tZcSO`VR*Mhm3c8-m9M;8(%Wsc~Tk|*34jniyU+#Z1B5Jw7qPcY=;0?Km^ zfVw0c6csFLwk4cuuW_R1kxd!yscE7kn|^WE3@DYQvbYP};z>v!?U7A*@yK+j;Bjf| z(E`F?v}pr`Rut2HiNQ^xg5%%U7o<9c5+f2pLiEBxj-A!NM1H5$13>1W%BT++yC0+^(OV5=Pj}=VxHmuvOwU55dRXtm}7qf D(I|`o literal 0 HcmV?d00001 diff --git a/test/rendering/cases/webgl-data-tile-interpolate-gutter/main.js b/test/rendering/cases/webgl-data-tile-interpolate-gutter/main.js new file mode 100644 index 0000000000..d294e8f3f3 --- /dev/null +++ b/test/rendering/cases/webgl-data-tile-interpolate-gutter/main.js @@ -0,0 +1,33 @@ +import DataTile from '../../../../src/ol/source/DataTile.js'; +import Map from '../../../../src/ol/Map.js'; +import TileLayer from '../../../../src/ol/layer/WebGLTile.js'; +import View from '../../../../src/ol/View.js'; + +const size = 260; + +const data = new Uint8Array(size * size); +for (let row = 0; row < size; ++row) { + for (let col = 0; col < size; ++col) { + data[row * size + col] = (row + col) % 2 === 0 ? 255 : 0; + } +} + +new Map({ + target: 'map', + layers: [ + new TileLayer({ + source: new DataTile({ + maxZoom: 1, + interpolate: true, + loader: () => data, + gutter: 2, + }), + }), + ], + view: new View({ + center: [0, 0], + zoom: 5, + }), +}); + +render();