diff --git a/src/core/Palette.js b/src/core/Palette.js index f3c5ada..75a24c6 100644 --- a/src/core/Palette.js +++ b/src/core/Palette.js @@ -109,12 +109,13 @@ class Palette { buffer2ABGR(chunkBuffer) { const { length } = chunkBuffer; const colors = new Uint32Array(length); + const { abgr } = this; let value; let pos = 0; for (let i = 0; i < length; i++) { value = (chunkBuffer[i] & 0x3F); - colors[pos++] = this.abgr[value]; + colors[pos++] = abgr[value]; } return colors; } @@ -129,16 +130,16 @@ class Palette { const colors = new Uint8Array(length * 3); let color; let value; - const buffer = chunkBuffer; + const { rgb } = this; let c = 0; for (let i = 0; i < length; i++) { - value = buffer[i]; + value = chunkBuffer[i]; color = (value & 0x3F) * 3; - colors[c++] = this.rgb[color++]; - colors[c++] = this.rgb[color++]; - colors[c++] = this.rgb[color]; + colors[c++] = rgb[color++]; + colors[c++] = rgb[color++]; + colors[c++] = rgb[color]; } return colors; } diff --git a/src/core/tilesBackup.js b/src/core/tilesBackup.js index 9912a3f..405a66b 100644 --- a/src/core/tilesBackup.js +++ b/src/core/tilesBackup.js @@ -13,22 +13,6 @@ import Palette from './Palette'; import { TILE_SIZE } from './constants'; -/* - * take chunk buffer and pad it to a specific length - * Fill missing pixels with zeros - * @param length target length - */ -function padChunk(chunk, length) { - let retChunk = chunk; - if (!chunk || !chunk.length) { - retChunk = Buffer.alloc(length); - } else if (chunk.length < length) { - const padding = Buffer.alloc(length - chunk.length); - retChunk = Buffer.concat([chunk, padding]); - } - return retChunk; -} - /* * Copy canvases from one redis instance to another * @param canvasRedis redis from where to get the data @@ -98,8 +82,8 @@ export async function incrementialBackupRedis( backupDir, ) { const ids = Object.keys(canvases); - for (let i = 0; i < ids.length; i += 1) { - const id = ids[i]; + for (let ind = 0; ind < ids.length; ind += 1) { + const id = ids[ind]; const canvas = canvases[id]; if (canvas.v || canvas.hid) { @@ -162,43 +146,48 @@ export async function incrementialBackupRedis( ); } - // is gonna be an Uint32Array let tileBuffer = null; try { if (!oldChunk && !curChunk) { continue; } - if (oldChunk && !curChunk) { - // one does not exist - curChunk = Buffer.alloc(TILE_SIZE * TILE_SIZE); - tileBuffer = palette.buffer2ABGR(curChunk); - } else if (!oldChunk && curChunk) { - tileBuffer = new Uint32Array(TILE_SIZE * TILE_SIZE); - const pxl = 0; - while (pxl < curChunk.length) { - const clrIndex = curChunk[pxl] & 0x3F; - if (clrIndex > 0) { - const color = palette.abgr[clrIndex]; - tileBuffer[pxl] = color; + if (!oldChunk) { + oldChunk = Buffer.allocUnsafe(0); + } + if (!curChunk) { + curChunk = Buffer.allocUnsafe(0); + } + + const { abgr } = palette; + const compLength = Math.min(oldChunk.length, curChunk.length); + tileBuffer = Buffer.allocUnsafe(TILE_SIZE ** 2 * 4); + for (let i = 0; i < compLength; i += 1) { + const curPxl = curChunk[i]; + if (oldChunk[i] !== curPxl) { + tileBuffer.writeUInt32BE(i * 4, abgr[curPxl & 0x3F]); + } + } + + const oldChunkLength = oldChunk.length; + const curChunkLength = curChunk.length; + + for (let i = oldChunkLength; i < curChunkLength; i += 1) { + tileBuffer.writeUInt32BE(i * 4, abgr[curChunk[i] & 0x3F]); + } + + if (oldChunkLength > curChunkLength) { + const blank = abgr[0]; + for (let i = curChunkLength; i < oldChunkLength; i += 1) { + if (oldChunk[i] !== 0) { + tileBuffer.writeUInt32BE(i * 4, blank); } } - } else { - if (curChunk.length < oldChunk.length) { - curChunk = padChunk(curChunk, oldChunk.length); - } else if (curChunk.length > oldChunk.length) { - oldChunk = padChunk(oldChunk, curChunk.length); - } - // both exist and are the same length - tileBuffer = new Uint32Array(TILE_SIZE * TILE_SIZE); - let pxl = 0; - while (pxl < curChunk.length) { - if (curChunk[pxl] !== oldChunk[pxl]) { - const color = palette.abgr[curChunk[pxl] & 0x3F]; - tileBuffer[pxl] = color; - } - pxl += 1; - } + } + + const curLength = Math.max(oldChunkLength, curChunkLength) * 4; + if (curLength < tileBuffer.length) { + tileBuffer.fill(0, curLength); } } catch (error) { console.error( @@ -208,31 +197,29 @@ export async function incrementialBackupRedis( continue; } - if (tileBuffer) { - try { - if (!createdDir && !fs.existsSync(xBackupDir)) { - createdDir = true; - fs.mkdirSync(xBackupDir); - } - const filename = `${xBackupDir}/${y}.png`; - // eslint-disable-next-line no-await-in-loop - await sharp( - Buffer.from(tileBuffer.buffer), { - raw: { - width: TILE_SIZE, - height: TILE_SIZE, - channels: 4, - }, - }, - ).toFile(filename); - amount += 1; - } catch (error) { - console.error( - // eslint-disable-next-line max-len - new Error(`Could not save incremential backup of chunk ${key}: ${error.message}`), - ); - continue; + try { + if (!createdDir && !fs.existsSync(xBackupDir)) { + createdDir = true; + fs.mkdirSync(xBackupDir); } + const filename = `${xBackupDir}/${y}.png`; + // eslint-disable-next-line no-await-in-loop + await sharp( + tileBuffer, { + raw: { + width: TILE_SIZE, + height: TILE_SIZE, + channels: 4, + }, + }, + ).toFile(filename); + amount += 1; + } catch (error) { + console.error( + // eslint-disable-next-line max-len + new Error(`Could not save incremential backup of chunk ${key}: ${error.message}`), + ); + continue; } } } @@ -256,8 +243,8 @@ export async function createPngBackup( backupDir, ) { const ids = Object.keys(canvases); - for (let i = 0; i < ids.length; i += 1) { - const id = ids[i]; + for (let ind = 0; ind < ids.length; ind += 1) { + const id = ids[ind]; const canvasBackupDir = `${backupDir}/${id}`; if (!fs.existsSync(canvasBackupDir)) { @@ -296,13 +283,27 @@ export async function createPngBackup( ); } if (chunk && chunk.length) { - chunk = padChunk(chunk, TILE_SIZE * TILE_SIZE); - const textureBuffer = palette.buffer2RGB(chunk); - const filename = `${xBackupDir}/${y}.png`; try { + const tileBuffer = Buffer.allocUnsafe(TILE_SIZE ** 2 * 4); + const chunkLength = chunk.length; + const { abgr } = palette; + for (let i = 0; i < chunkLength; i += 1) { + tileBuffer.writeUInt32BE( + i * 4, + abgr[chunk[i] & 0x3F], + ); + } + + const curLength = chunkLength * 4; + if (curLength < tileBuffer.length) { + tileBuffer.fill(0, curLength); + } + + const filename = `${xBackupDir}/${y}.png`; + // eslint-disable-next-line no-await-in-loop await sharp( - Buffer.from(textureBuffer.buffer), { + tileBuffer, { raw: { width: TILE_SIZE, height: TILE_SIZE, diff --git a/src/data/redis/RedisCanvas.js b/src/data/redis/RedisCanvas.js index 95c0b5c..d6d835f 100644 --- a/src/data/redis/RedisCanvas.js +++ b/src/data/redis/RedisCanvas.js @@ -43,6 +43,10 @@ class RedisCanvas { key, ); if (padding > 0 && chunk && chunk.length < padding) { + /* + * this padding is slow and should be avoided, + * better deal with non-full-size chunks yourself + */ const pad = Buffer.alloc(padding - chunk.length); chunk = Buffer.concat([chunk, pad]); }