optimize backup tile creation
This commit is contained in:
parent
5db3083044
commit
b436937f86
|
@ -109,12 +109,13 @@ class Palette {
|
||||||
buffer2ABGR(chunkBuffer) {
|
buffer2ABGR(chunkBuffer) {
|
||||||
const { length } = chunkBuffer;
|
const { length } = chunkBuffer;
|
||||||
const colors = new Uint32Array(length);
|
const colors = new Uint32Array(length);
|
||||||
|
const { abgr } = this;
|
||||||
let value;
|
let value;
|
||||||
|
|
||||||
let pos = 0;
|
let pos = 0;
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
value = (chunkBuffer[i] & 0x3F);
|
value = (chunkBuffer[i] & 0x3F);
|
||||||
colors[pos++] = this.abgr[value];
|
colors[pos++] = abgr[value];
|
||||||
}
|
}
|
||||||
return colors;
|
return colors;
|
||||||
}
|
}
|
||||||
|
@ -129,16 +130,16 @@ class Palette {
|
||||||
const colors = new Uint8Array(length * 3);
|
const colors = new Uint8Array(length * 3);
|
||||||
let color;
|
let color;
|
||||||
let value;
|
let value;
|
||||||
const buffer = chunkBuffer;
|
const { rgb } = this;
|
||||||
|
|
||||||
let c = 0;
|
let c = 0;
|
||||||
for (let i = 0; i < length; i++) {
|
for (let i = 0; i < length; i++) {
|
||||||
value = buffer[i];
|
value = chunkBuffer[i];
|
||||||
|
|
||||||
color = (value & 0x3F) * 3;
|
color = (value & 0x3F) * 3;
|
||||||
colors[c++] = this.rgb[color++];
|
colors[c++] = rgb[color++];
|
||||||
colors[c++] = this.rgb[color++];
|
colors[c++] = rgb[color++];
|
||||||
colors[c++] = this.rgb[color];
|
colors[c++] = rgb[color];
|
||||||
}
|
}
|
||||||
return colors;
|
return colors;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,22 +13,6 @@ import Palette from './Palette';
|
||||||
import { TILE_SIZE } from './constants';
|
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
|
* Copy canvases from one redis instance to another
|
||||||
* @param canvasRedis redis from where to get the data
|
* @param canvasRedis redis from where to get the data
|
||||||
|
@ -98,8 +82,8 @@ export async function incrementialBackupRedis(
|
||||||
backupDir,
|
backupDir,
|
||||||
) {
|
) {
|
||||||
const ids = Object.keys(canvases);
|
const ids = Object.keys(canvases);
|
||||||
for (let i = 0; i < ids.length; i += 1) {
|
for (let ind = 0; ind < ids.length; ind += 1) {
|
||||||
const id = ids[i];
|
const id = ids[ind];
|
||||||
|
|
||||||
const canvas = canvases[id];
|
const canvas = canvases[id];
|
||||||
if (canvas.v || canvas.hid) {
|
if (canvas.v || canvas.hid) {
|
||||||
|
@ -162,43 +146,48 @@ export async function incrementialBackupRedis(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// is gonna be an Uint32Array
|
|
||||||
let tileBuffer = null;
|
let tileBuffer = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!oldChunk && !curChunk) {
|
if (!oldChunk && !curChunk) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (oldChunk && !curChunk) {
|
if (!oldChunk) {
|
||||||
// one does not exist
|
oldChunk = Buffer.allocUnsafe(0);
|
||||||
curChunk = Buffer.alloc(TILE_SIZE * TILE_SIZE);
|
}
|
||||||
tileBuffer = palette.buffer2ABGR(curChunk);
|
if (!curChunk) {
|
||||||
} else if (!oldChunk && curChunk) {
|
curChunk = Buffer.allocUnsafe(0);
|
||||||
tileBuffer = new Uint32Array(TILE_SIZE * TILE_SIZE);
|
}
|
||||||
const pxl = 0;
|
|
||||||
while (pxl < curChunk.length) {
|
const { abgr } = palette;
|
||||||
const clrIndex = curChunk[pxl] & 0x3F;
|
const compLength = Math.min(oldChunk.length, curChunk.length);
|
||||||
if (clrIndex > 0) {
|
tileBuffer = Buffer.allocUnsafe(TILE_SIZE ** 2 * 4);
|
||||||
const color = palette.abgr[clrIndex];
|
for (let i = 0; i < compLength; i += 1) {
|
||||||
tileBuffer[pxl] = color;
|
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);
|
const curLength = Math.max(oldChunkLength, curChunkLength) * 4;
|
||||||
} else if (curChunk.length > oldChunk.length) {
|
if (curLength < tileBuffer.length) {
|
||||||
oldChunk = padChunk(oldChunk, curChunk.length);
|
tileBuffer.fill(0, curLength);
|
||||||
}
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
|
@ -208,31 +197,29 @@ export async function incrementialBackupRedis(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tileBuffer) {
|
try {
|
||||||
try {
|
if (!createdDir && !fs.existsSync(xBackupDir)) {
|
||||||
if (!createdDir && !fs.existsSync(xBackupDir)) {
|
createdDir = true;
|
||||||
createdDir = true;
|
fs.mkdirSync(xBackupDir);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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,
|
backupDir,
|
||||||
) {
|
) {
|
||||||
const ids = Object.keys(canvases);
|
const ids = Object.keys(canvases);
|
||||||
for (let i = 0; i < ids.length; i += 1) {
|
for (let ind = 0; ind < ids.length; ind += 1) {
|
||||||
const id = ids[i];
|
const id = ids[ind];
|
||||||
|
|
||||||
const canvasBackupDir = `${backupDir}/${id}`;
|
const canvasBackupDir = `${backupDir}/${id}`;
|
||||||
if (!fs.existsSync(canvasBackupDir)) {
|
if (!fs.existsSync(canvasBackupDir)) {
|
||||||
|
@ -296,13 +283,27 @@ export async function createPngBackup(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (chunk && chunk.length) {
|
if (chunk && chunk.length) {
|
||||||
chunk = padChunk(chunk, TILE_SIZE * TILE_SIZE);
|
|
||||||
const textureBuffer = palette.buffer2RGB(chunk);
|
|
||||||
const filename = `${xBackupDir}/${y}.png`;
|
|
||||||
try {
|
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
|
// eslint-disable-next-line no-await-in-loop
|
||||||
await sharp(
|
await sharp(
|
||||||
Buffer.from(textureBuffer.buffer), {
|
tileBuffer, {
|
||||||
raw: {
|
raw: {
|
||||||
width: TILE_SIZE,
|
width: TILE_SIZE,
|
||||||
height: TILE_SIZE,
|
height: TILE_SIZE,
|
||||||
|
|
|
@ -43,6 +43,10 @@ class RedisCanvas {
|
||||||
key,
|
key,
|
||||||
);
|
);
|
||||||
if (padding > 0 && chunk && chunk.length < padding) {
|
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);
|
const pad = Buffer.alloc(padding - chunk.length);
|
||||||
chunk = Buffer.concat([chunk, pad]);
|
chunk = Buffer.concat([chunk, pad]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user