From 61ec34d7b43882a1f98613aa1867cf45bd2a4e0b Mon Sep 17 00:00:00 2001 From: HF Date: Mon, 27 Jun 2022 14:03:52 +0200 Subject: [PATCH] save in webp adjust Tile creation --- src/core/Tile.js | 157 +++++++++++++++++++++------------------- src/core/tileserver.js | 4 +- src/globe.js | 2 +- src/routes/tiles.js | 6 +- src/ui/ChunkLoader2D.js | 2 +- 5 files changed, 90 insertions(+), 81 deletions(-) diff --git a/src/core/Tile.js b/src/core/Tile.js index 0c8a888..6186784 100644 --- a/src/core/Tile.js +++ b/src/core/Tile.js @@ -241,7 +241,7 @@ function addIndexedSubtiletoTile( */ function tileFileName(canvasTileFolder, cell) { const [z, x, y] = cell; - const filename = `${canvasTileFolder}/${z}/${x}/${y}.png`; + const filename = `${canvasTileFolder}/${z}/${x}/${y}.webp`; return filename; } @@ -271,24 +271,17 @@ export async function createZoomTileFromChunk( const xabs = x * TILE_ZOOM_LEVEL; const yabs = y * TILE_ZOOM_LEVEL; const na = []; - for (let dy = 0; dy < TILE_ZOOM_LEVEL; dy += 1) { - for (let dx = 0; dx < TILE_ZOOM_LEVEL; dx += 1) { - let chunk = null; - try { - chunk = await RedisCanvas.getChunk( - canvasId, - xabs + dx, - yabs + dy, - ); - } catch (error) { - console.error( - // eslint-disable-next-line max-len - `Tiling: Failed to get Chunk ch:${canvasId}:${xabs + dx}${yabs + dy} with error ${error.message}`, - ); - } + + const prom = async (dx, dy) => { + try { + const chunk = await RedisCanvas.getChunk( + canvasId, + xabs + dx, + yabs + dy, + ); if (!chunk || !chunk.length) { na.push([dx, dy]); - continue; + return; } addIndexedSubtiletoTile( palette, @@ -297,8 +290,22 @@ export async function createZoomTileFromChunk( chunk, tileRGBBuffer, ); + } catch (error) { + na.push([dx, dy]); + console.error( + // eslint-disable-next-line max-len + `Tiling: Failed to get Chunk ch:${canvasId}:${xabs + dx}${yabs + dy} with error ${error.message}`, + ); + } + }; + + const promises = []; + for (let dy = 0; dy < TILE_ZOOM_LEVEL; dy += 1) { + for (let dx = 0; dx < TILE_ZOOM_LEVEL; dx += 1) { + promises.push(prom(dx, dy)); } } + await Promise.all(promises); if (na.length !== TILE_ZOOM_LEVEL * TILE_ZOOM_LEVEL) { na.forEach((element) => { @@ -313,7 +320,7 @@ export async function createZoomTileFromChunk( const filename = tileFileName(canvasTileFolder, [maxTiledZoom - 1, x, y]); try { - await sharp(Buffer.from(tileRGBBuffer.buffer), { + await sharp(tileRGBBuffer, { raw: { width: TILE_SIZE * TILE_ZOOM_LEVEL, height: TILE_SIZE * TILE_ZOOM_LEVEL, @@ -321,7 +328,7 @@ export async function createZoomTileFromChunk( }, }) .resize(TILE_SIZE) - .png({ options: { compressionLevel: 6 } }) + .webp({ quality: 100, smartSubsample: true }) .toFile(filename); } catch (error) { console.error( @@ -361,7 +368,7 @@ export async function createZoomedTile( const prom = async (dx, dy) => { // eslint-disable-next-line max-len - const chunkfile = `${canvasTileFolder}/${z + 1}/${x * TILE_ZOOM_LEVEL + dx}/${y * TILE_ZOOM_LEVEL + dy}.png`; + const chunkfile = `${canvasTileFolder}/${z + 1}/${x * TILE_ZOOM_LEVEL + dx}/${y * TILE_ZOOM_LEVEL + dy}.webp`; try { if (!fs.existsSync(chunkfile)) { na.push([dx, dy]); @@ -404,17 +411,15 @@ export async function createZoomedTile( const filename = tileFileName(canvasTileFolder, [z, x, y]); try { - await sharp( - Buffer.from( - tileRGBBuffer.buffer, - ), { - raw: { - width: TILE_SIZE, - height: TILE_SIZE, - channels: 3, - }, + await sharp(tileRGBBuffer, { + raw: { + width: TILE_SIZE, + height: TILE_SIZE, + channels: 3, }, - ).toFile(filename); + }) + .webp({ quality: 100, smartSubsample: true }) + .toFile(filename); } catch (error) { console.error( `Tiling: Error on createZoomedTile: ${error.message}`, @@ -452,16 +457,16 @@ async function createEmptyTile( // eslint-disable-next-line prefer-destructuring tileRGBBuffer[i++] = palette.rgb[2]; } - const filename = `${canvasTileFolder}/emptytile.png`; + const filename = `${canvasTileFolder}/emptytile.webp`; try { - await sharp(Buffer.from(tileRGBBuffer.buffer), { + await sharp(tileRGBBuffer, { raw: { width: TILE_SIZE, height: TILE_SIZE, channels: 3, }, }) - .png({ options: { compressionLevel: 6 } }) + .webp({ quality: 100, smartSubsample: true }) .toFile(filename); } catch (error) { console.error( @@ -494,45 +499,36 @@ export async function createTexture( const startTime = Date.now(); const na = []; - if (targetSize !== canvasSize) { - for (let dy = 0; dy < amount; dy += 1) { - for (let dx = 0; dx < amount; dx += 1) { - let chunk = null; - const chunkfile = `${canvasTileFolder}/${zoom}/${dx}/${dy}.png`; + + const prom = (targetSize !== canvasSize) + ? async (dx, dy) => { + const chunkfile = `${canvasTileFolder}/${zoom}/${dx}/${dy}.webp`; + try { if (!fs.existsSync(chunkfile)) { na.push([dx, dy]); - continue; - } - try { - chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer(); - addRGBSubtiletoTile(amount, [dx, dy], chunk, textureBuffer); - } catch (error) { - console.error( - // eslint-disable-next-line max-len - `Tiling: Error on createTexture in chunk ${chunkfile}: ${error.message}`, - ); + return; } + const chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer(); + addRGBSubtiletoTile(amount, [dx, dy], chunk, textureBuffer); + } catch (error) { + na.push([dx, dy]); + console.error( + // eslint-disable-next-line max-len + `Tiling: Error on createTexture in chunk ${chunkfile}: ${error.message}`, + ); } } - } else { - for (let dy = 0; dy < amount; dy += 1) { - for (let dx = 0; dx < amount; dx += 1) { + : async (dx, dy) => { + try { let chunk = null; - try { - chunk = await RedisCanvas.getChunk( - canvasId, - dx, - dy, - ); - } catch (error) { - console.error( - // eslint-disable-next-line max-len - `Tiling: Failed to get Chunk ch:${canvasId}:${dx}${dy} with error ${error.message}`, - ); - } + chunk = await RedisCanvas.getChunk( + canvasId, + dx, + dy, + ); if (!chunk || !chunk.length) { na.push([dx, dy]); - continue; + return; } addIndexedSubtiletoTile( palette, @@ -541,25 +537,36 @@ export async function createTexture( chunk, textureBuffer, ); + } catch (error) { + na.push([dx, dy]); + console.error( + // eslint-disable-next-line max-len + `Tiling: Failed to get Chunk ch:${canvasId}:${dx}${dy} with error ${error.message}`, + ); } + }; + + const promises = []; + for (let dy = 0; dy < amount; dy += 1) { + for (let dx = 0; dx < amount; dx += 1) { + promises.push(prom(dx, dy)); } } + await Promise.all(promises); na.forEach((element) => { deleteSubtilefromTile(TILE_SIZE, palette, amount, element, textureBuffer); }); - const filename = `${canvasTileFolder}/texture.png`; + const filename = `${canvasTileFolder}/texture.webp`; try { - await sharp( - Buffer.from(textureBuffer.buffer), { - raw: { - width: targetSize, - height: targetSize, - channels: 3, - }, + await sharp(textureBuffer, { + raw: { + width: targetSize, + height: targetSize, + channels: 3, }, - ).toFile(filename); + }).toFile(filename); } catch (error) { console.error( `Tiling: Error on createTexture: ${error.message}`, @@ -605,7 +612,7 @@ export async function initializeTiles( const tileDir = `${canvasTileFolder}/${zoom}/${cx}`; if (!fs.existsSync(tileDir)) fs.mkdirSync(tileDir); for (let cy = 0; cy < maxBase; cy += 1) { - const filename = `${canvasTileFolder}/${zoom}/${cx}/${cy}.png`; + const filename = `${canvasTileFolder}/${zoom}/${cx}/${cy}.webp`; if (force || !fs.existsSync(filename)) { const ret = await createZoomTileFromChunk( canvasId, @@ -634,7 +641,7 @@ export async function initializeTiles( const tileDir = `${canvasTileFolder}/${zoom}/${cx}`; if (!fs.existsSync(tileDir)) fs.mkdirSync(tileDir); for (let cy = 0; cy < maxZ; cy += 1) { - const filename = `${canvasTileFolder}/${zoom}/${cx}/${cy}.png`; + const filename = `${canvasTileFolder}/${zoom}/${cx}/${cy}.webp`; if (force || !fs.existsSync(filename)) { const ret = await createZoomedTile( canvas, diff --git a/src/core/tileserver.js b/src/core/tileserver.js index 2ce7f4e..f8eeed5 100644 --- a/src/core/tileserver.js +++ b/src/core/tileserver.js @@ -161,7 +161,9 @@ class CanvasUpdater { } for (let c = 0; c < this.maxTiledZoom; c += 1) { this.TileLoadingQueues.push([]); - const timeout = (7 ** (this.maxTiledZoom - c - 1)) * 5 * 1000; + const invZoom = this.maxTiledZoom - c - 1; + // eslint-disable-next-line max-len + const timeout = TILE_ZOOM_LEVEL ** (2 * invZoom + 2) * (6 / TILE_ZOOM_LEVEL ** 2) * 1000; logger.info( `Tiling: Set interval for zoomlevel ${c} update to ${timeout / 1000}`, ); diff --git a/src/globe.js b/src/globe.js index a7c0613..67ef62c 100644 --- a/src/globe.js +++ b/src/globe.js @@ -51,7 +51,7 @@ document.addEventListener('DOMContentLoaded', () => { const [canvasIdent, canvasId, canvasSize, x, y] = parseHashCoords(); const canvasTexture = new THREE.MeshPhongMaterial({ - map: new THREE.TextureLoader().load(`./tiles/${canvasId}/texture.png`), + map: new THREE.TextureLoader().load(`./tiles/${canvasId}/texture.webp`), bumpMap: new THREE.TextureLoader().load(`./assets3d/normal${canvasId}.jpg`), bumpScale: 0.02, specularMap: new THREE.TextureLoader() diff --git a/src/routes/tiles.js b/src/routes/tiles.js index bb6dda9..8e1cf53 100644 --- a/src/routes/tiles.js +++ b/src/routes/tiles.js @@ -23,12 +23,12 @@ router.use('/', express.static(TILE_FOLDER, { /* * catch File Not Found: Send empty tile */ -router.use('/:c([0-9]+)/:z([0-9]+)/:x([0-9]+)/:y([0-9]+).png', +router.use('/:c([0-9]+)/:z([0-9]+)/:x([0-9]+)/:y([0-9]+).webp', async (req, res) => { const { c: paramC } = req.params; const c = parseInt(paramC, 10); - const filename = `${TILE_FOLDER}/${c}/emptytile.png`; + const filename = `${TILE_FOLDER}/${c}/emptytile.webp`; if (!fs.existsSync(filename)) { res.set({ 'Cache-Control': `public, s-maxage=${24 * 3600}, max-age=${24 * 3600}`, @@ -39,7 +39,7 @@ router.use('/:c([0-9]+)/:z([0-9]+)/:x([0-9]+)/:y([0-9]+).png', res.set({ 'Cache-Control': `public, s-maxage=${2 * 3600}, max-age=${1 * 3600}`, - 'Content-Type': 'image/png', + 'Content-Type': 'image/webp', }); res.status(200); res.sendFile(filename); diff --git a/src/ui/ChunkLoader2D.js b/src/ui/ChunkLoader2D.js index a7204d3..40d8869 100644 --- a/src/ui/ChunkLoader2D.js +++ b/src/ui/ChunkLoader2D.js @@ -304,7 +304,7 @@ class ChunkLoader { const center = [zoom, cx, cy]; this.store.dispatch(requestBigChunk(center)); try { - const url = `tiles/${this.canvasId}/${zoom}/${cx}/${cy}.png`; + const url = `tiles/${this.canvasId}/${zoom}/${cx}/${cy}.webp`; const img = await loadImage(url); chunkRGB.fromImage(img); this.store.dispatch(receiveBigChunk(center, chunkRGB));