Skip to content

Commit

Permalink
initially populate tile borders with existing data to avoid border fl…
Browse files Browse the repository at this point in the history
…ashes as data backfills
  • Loading branch information
Molly Lloyd committed Oct 3, 2017
1 parent a44aaf0 commit d5798f2
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 66 deletions.
20 changes: 19 additions & 1 deletion src/data/dem_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,24 @@ class DEMData {
}
}

// in order to avoid flashing seams between tiles, here we are initially populating a 1px border of pixels around the image
// with the data of the nearest pixel from the image. this data is eventually replaced when the tile's neighboring
// tiles are loaded and the accurate data can be backfilled using DEMData#backfillBorder
for (let x = 0; x < data.width; x++) {
// left vertical border
level.set(-1, x, level.get(0, x));
// right vertical border
level.set(data.width, x, level.get(data.width - 1, x));
// left horizontal border
level.set(x, -1, level.get(x, 0));
// right horizontal border
level.set(x, data.width, level.get(x, data.width - 1));
}
// corners
level.set(-1, -1, level.get(0, 0));
level.set(data.width, -1, level.get(data.width - 1, 0));
level.set(-1, data.width, level.get(0, data.width - 1));
level.set(data.width, data.width, level.get(data.width - 1, data.width - 1));
this.loaded = true;
}

Expand All @@ -89,7 +107,7 @@ class DEMData {
return references;
}

backfillBorders(borderTile: DEMData, dx: number, dy: number) {
backfillBorder(borderTile: DEMData, dx: number, dy: number) {
for (let l = 0; l < this.data.length; l++) {
const t = this.data[l];
const o = borderTile.data[l];
Expand Down
21 changes: 2 additions & 19 deletions src/render/draw_hillshade.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,14 @@ function drawHillshade(painter: Painter, sourceCache: SourceCache, layer: StyleL
painter.setDepthSublayer(0);
gl.disable(gl.STENCIL_TEST);


const maxzoom = sourceCache.getSource().maxzoom;

for (const coord of coords) {
const tile = sourceCache.getTile(coord);
if (!tile.texture && painter.renderPass === 'hillshadeprepare') {
prepareHillshade(painter, tile);
continue;
}
if (painter.renderPass === 'translucent') {
let bordersLoaded = true;
if (painter.transform.tileZoom < maxzoom) {
for (const key in tile.neighboringTiles) {
if (!tile.neighboringTiles[key].backfilled && sourceCache._tiles[key]) {
bordersLoaded = false;
break;
}
}
}
renderHillshade(painter, tile, layer, bordersLoaded);
renderHillshade(painter, tile, layer);
}
}

Expand All @@ -74,7 +62,7 @@ function getTileLatRange(painter, coord) {
return [painter.transform.coordinateLocation(coordinate0).lat, painter.transform.coordinateLocation(coordinate1).lat];
}

function renderHillshade(painter, tile, layer, bordersLoaded) {
function renderHillshade(painter, tile, layer) {
const gl = painter.gl;
const program = painter.useProgram('hillshade');
const posMatrix = painter.transform.calculatePosMatrix(tile.coord);
Expand Down Expand Up @@ -103,11 +91,6 @@ function renderHillshade(painter, tile, layer, bordersLoaded) {
tile.maskedIndexBuffer,
tile.segments
);
} else if (!bordersLoaded) {
const buffer = painter.incompleteHillshadeBoundsBuffer;
const vao = painter.incompleteHillshadeBoundsVAO;
vao.bind(gl, program, buffer);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, buffer.length);
} else {
const buffer = painter.rasterBoundsBuffer;
const vao = painter.rasterBoundsVAO;
Expand Down
10 changes: 0 additions & 10 deletions src/render/painter.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,16 +178,6 @@ class Painter {
this.rasterBoundsBuffer = new VertexBuffer(gl, rasterBoundsArray);
this.rasterBoundsVAO = new VertexArrayObject();

// used if raster-terrain tile isn't fully backfilled in order to prevent seams with missing data from flashing
const incompleteHillshadeBoundsArray = new RasterBoundsArray();
const hideCoords = 2 * EXTENT / 512;
incompleteHillshadeBoundsArray.emplaceBack(hideCoords, hideCoords, hideCoords, hideCoords);
incompleteHillshadeBoundsArray.emplaceBack((EXTENT - hideCoords), hideCoords, (EXTENT - hideCoords), hideCoords);
incompleteHillshadeBoundsArray.emplaceBack(hideCoords, (EXTENT - hideCoords), hideCoords, (EXTENT - hideCoords));
incompleteHillshadeBoundsArray.emplaceBack((EXTENT - hideCoords), (EXTENT - hideCoords), (EXTENT - hideCoords), (EXTENT - hideCoords));
this.incompleteHillshadeBoundsBuffer = new VertexBuffer(gl, incompleteHillshadeBoundsArray);
this.incompleteHillshadeBoundsVAO = new VertexArrayObject();

this.extTextureFilterAnisotropic = (
gl.getExtension('EXT_texture_filter_anisotropic') ||
gl.getExtension('MOZ_EXT_texture_filter_anisotropic') ||
Expand Down
2 changes: 1 addition & 1 deletion src/source/source_cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ class SourceCache extends Evented {
}
}
if (!borderTile.dem || !tile.dem) return;
tile.dem.backfillBorders(borderTile.dem, dx, dy);
tile.dem.backfillBorder(borderTile.dem, dx, dy);
if (tile.neighboringTiles && tile.neighboringTiles[borderId])
tile.neighboringTiles[borderId].backfilled = true;
}
Expand Down
74 changes: 39 additions & 35 deletions test/unit/data/dem_data.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ test('Level', (t)=>{
t.end();
});

t.test('constructor throws on ');

t.test('setters and getters return correct values', (t) => {
const level = new Level(4, 4, 2);

Expand Down Expand Up @@ -84,7 +82,7 @@ test('DEMData constructor', (t) => {
});


test('DEMData#backfillBorders', (t) => {
test('DEMData#backfillBorder', (t) => {
const imageData0 = createMockImage(4, 4);
const dem0 = new DEMData(0);
const imageData1 = createMockImage(4, 4);
Expand All @@ -93,79 +91,85 @@ test('DEMData#backfillBorders', (t) => {
dem0.loadFromImage(imageData0);
dem1.loadFromImage(imageData1);

t.test('border region is initially empty', (t)=>{
t.test('border region is initially populated with neighboring data', (t)=>{
const level0 = dem0.data[0];
let nonempty = true;
for (let x = 0; x < 4; x++) {
for (let y = 0; y < 4; y++) {
for (let x = -1; x < 5; x++) {
for (let y = -1; y < 5; y++) {
if (level0.get(x, y) <= 0) {
nonempty = false;
break;
}
}
}
t.true(nonempty, 'pixel data is processed correctly into DEM');
// vertical borders empty
let empty = true;
t.true(nonempty, 'pixel data populates DEM data level');

let verticalBorderMatch = true;
for (let x = -1; x < 5; x += 5) {
for (let y = -1; y < 5; y++) {
if (level0.get(x, y) !== -65536) {
empty = false;
for (let y = 0; y < 4; y++) {
if (level0.get(x, y) !== level0.get(x < 0 ? x + 1 : x - 1, y)) {
verticalBorderMatch = false;
break;
}
}
}
t.true(empty, 'vertical border of DEM data is initially empty');
t.true(verticalBorderMatch, 'vertical border of DEM data is initially equal to next column of data');

// horizontal borders empty
empty = true;
let horizontalBorderMatch = true;
for (let y = -1; y < 5; y += 5) {
for (let x = -1; x < 5; x++) {
if (level0.get(x, y) !== -65536) {
empty = false;
for (let x = 0; x < 4; x++) {
if (level0.get(x, y) !== level0.get(x, y < 0 ? y + 1 : y - 1)) {
horizontalBorderMatch = false;
break;
}
}
}
t.true(empty, 'horizontal border of DEM data is initially empty');
t.true(horizontalBorderMatch, 'horizontal border of DEM data is initially equal to next row of data');

t.true(level0.get(-1, 4) === level0.get(0, 3), '-1, 1 corner initially equal to closest corner data');
t.true(level0.get(4, 4) === level0.get(3, 3), '1, 1 corner initially equal to closest corner data');
t.true(level0.get(-1, -1) === level0.get(0, 0), '-1, -1 corner initially equal to closest corner data');
t.true(level0.get(4, -1) === level0.get(3, 0), '-1, 1 corner initially equal to closest corner data');


t.end();
});

t.test('backfillBorders correctly populates neighboring border', (t)=>{
t.test('backfillBorder correctly populates borders with neighboring data', (t)=>{
const level0 = dem0.data[0];

dem0.backfillBorders(dem1, -1, 0);
dem0.backfillBorder(dem1, -1, 0);
for (let y = 0; y < 4; y++) {
t.true(level0.get(-1, y) > 0, 'backfills neighbor -1, 0');
t.true(level0.get(-1, y) !== level0.get(0, y), 'backfills neighbor -1, 0');
}

dem0.backfillBorders(dem1, 0, -1);
dem0.backfillBorder(dem1, 0, -1);
for (let x = 0; x < 4; x++) {
t.true(level0.get(x, -1) > 0, 'backfills neighbor 0, -1');
t.true(level0.get(x, -1) !== level0.get(x, 0), 'backfills neighbor 0, -1');
}

dem0.backfillBorders(dem1, 1, 0);
dem0.backfillBorder(dem1, 1, 0);
for (let y = 0; y < 4; y++) {
t.true(level0.get(1, y) > 0, 'backfills neighbor 1, 0');
t.true(level0.get(4, y) !== level0.get(3, y), 'backfills neighbor 1, 0');
}

dem0.backfillBorders(dem1, 0, 1);
dem0.backfillBorder(dem1, 0, 1);
for (let x = 0; x < 4; x++) {
t.true(level0.get(x, 1) > 0, 'backfills neighbor 0, 1');
t.true(level0.get(x, 4) !== level0.get(x, 3), 'backfills neighbor 0, 1');
}

dem0.backfillBorders(dem1, -1, 1);
t.true(level0.get(-1, 1) > 0, 'backfills neighbor -1, 1');
dem0.backfillBorder(dem1, -1, 1);
t.true(level0.get(-1, 4) !== level0.get(0, 3), 'backfills neighbor -1, 1');

dem0.backfillBorders(dem1, 1, 1);
t.true(level0.get(1, 1) > 0, 'backfills neighbor 1, 1');
dem0.backfillBorder(dem1, 1, 1);
t.true(level0.get(4, 4) !== level0.get(3, 3), 'backfills neighbor 1, 1');

dem0.backfillBorders(dem1, -1, -1);
t.true(level0.get(-1, -1) > 0, 'backfills neighbor -1, -1');
dem0.backfillBorder(dem1, -1, -1);
t.true(level0.get(-1, -1) !== level0.get(0, 0), 'backfills neighbor -1, -1');

dem0.backfillBorders(dem1, 1, -1);
t.true(level0.get(1, -1) > 0, 'backfills neighbor -1, 1');
dem0.backfillBorder(dem1, 1, -1);
t.true(level0.get(4, -1) !== level0.get(3, 0), 'backfills neighbor -1, 1');


t.end();
Expand Down

0 comments on commit d5798f2

Please sign in to comment.