change how palette gets sent to worker thread

This commit is contained in:
HF 2022-04-04 15:50:51 +02:00
parent 7e38ecd6f7
commit 68ae99b4e0
3 changed files with 131 additions and 101 deletions

View File

@ -2,21 +2,14 @@
* Palette
*/
export type ColorIndex = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31;
export type Color = string;
class Palette {
length: number;
rgb: Uint8Array;
colors: Array<Color>;
abgr: Uint32Array;
fl: Array<number>;
length;
rgb;
colors;
abgr;
fl;
constructor(colors: Array) {
constructor(colors) {
this.length = colors.length;
this.rgb = new Uint8Array(this.length * 3);
this.colors = new Array(this.length);
@ -42,7 +35,7 @@ class Palette {
* @param color Index of color in palette
* @return dark True if color is dark
*/
isDark(color: number) {
isDark(color) {
color *= 3;
const r = this.rgb[color++];
const g = this.rgb[color++];
@ -59,10 +52,10 @@ class Palette {
* @return index of color
*/
getIndexOfColor(
r: number,
g: number,
b: number,
): ColorIndex {
r,
g,
b,
) {
const { rgb } = this;
let i = rgb.length / 3;
while (i > 0) {
@ -86,10 +79,10 @@ class Palette {
* @return index of color
*/
getClosestIndexOfColor(
r: number,
g: number,
b: number,
): ColorIndex {
r,
g,
b,
) {
const { rgb } = this;
let i = rgb.length / 3;
let closestIndex = 0;
@ -113,10 +106,10 @@ class Palette {
* @param chunkBuffer Buffer of indexed pixels
* @return ABRG Buffer
*/
buffer2ABGR(chunkBuffer: Buffer): Uint32Array {
buffer2ABGR(chunkBuffer) {
const { length } = chunkBuffer;
const colors = new Uint32Array(length);
let value: number;
let value;
const buffer = chunkBuffer;
let pos = 0;
@ -132,11 +125,11 @@ class Palette {
* @param chunkBuffer Buffer of indexed pixels
* @return RGB Buffer
*/
buffer2RGB(chunkBuffer: Buffer): Uint8Array {
buffer2RGB(chunkBuffer) {
const { length } = chunkBuffer;
const colors = new Uint8Array(length * 3);
let color: number;
let value: number;
let color;
let value;
const buffer = chunkBuffer;
let c = 0;
@ -157,7 +150,7 @@ class Palette {
* @param length Length of needed Buffer
* @return RGB Buffer of wanted size with just one color
*/
oneColorBuffer(color: ColorIndex, length: number) {
oneColorBuffer(color, length) {
const buffer = new Uint8Array(length * 3);
const r = this.rgb[color * 3];
const g = this.rgb[color * 3 + 1];
@ -173,7 +166,7 @@ class Palette {
}
}
export const COLORS_RGB: Uint8Array = new Uint8Array([
export const COLORS_RGB = new Uint8Array([
202, 227, 255, // first color is unset pixel in ocean
255, 255, 255, // second color is unset pixel on land
255, 255, 255, // white
@ -209,9 +202,9 @@ export const COLORS_RGB: Uint8Array = new Uint8Array([
]);
export const COLORS_AMOUNT = COLORS_RGB.length / 3;
export const COLORS: Array<Color> = new Array(COLORS_AMOUNT);
export const COLORS_ABGR: Uint32Array = new Uint32Array(COLORS_AMOUNT);
export const TRANSPARENT: ColorIndex = 0;
export const COLORS = new Array(COLORS_AMOUNT);
export const COLORS_ABGR = new Uint32Array(COLORS_AMOUNT);
export const TRANSPARENT = 0;
export default Palette;

View File

@ -12,6 +12,7 @@
import sharp from 'sharp';
import fs from 'fs';
import Palette from './Palette';
import { getMaxTiledZoom } from './utils';
import { TILE_SIZE, TILE_ZOOM_LEVEL } from './constants';
@ -113,22 +114,22 @@ function tileFileName(canvasTileFolder, cell) {
}
/*
* @param canvasSize dimension of the canvas (pixels width/height)
* @param redisClient redis instance
* @param canvasId id of the canvas
* @param canvas canvas data
* @param canvasTileFolder root folder where to save tiles
* @param palette Palette to use
* @param cell tile to create [x, y]
* @return true if successfully created tile, false if tile empty
*/
export async function createZoomTileFromChunk(
redisClient,
canvasSize,
canvasId,
canvas,
canvasTileFolder,
palette,
cell,
) {
const palette = new Palette(canvas.colors);
const canvasSize = canvas.size;
const [x, y] = cell;
const maxTiledZoom = getMaxTiledZoom(canvasSize);
const tileRGBBuffer = new Uint8Array(
@ -166,16 +167,23 @@ export async function createZoomTileFromChunk(
});
const filename = tileFileName(canvasTileFolder, [maxTiledZoom - 1, x, y]);
await sharp(Buffer.from(tileRGBBuffer.buffer), {
raw: {
width: TILE_SIZE * TILE_ZOOM_LEVEL,
height: TILE_SIZE * TILE_ZOOM_LEVEL,
channels: 3,
},
})
.resize(TILE_SIZE)
.png({ options: { compressionLevel: 6 } })
.toFile(filename);
try {
await sharp(Buffer.from(tileRGBBuffer.buffer), {
raw: {
width: TILE_SIZE * TILE_ZOOM_LEVEL,
height: TILE_SIZE * TILE_ZOOM_LEVEL,
channels: 3,
},
})
.resize(TILE_SIZE)
.png({ options: { compressionLevel: 6 } })
.toFile(filename);
} catch (error) {
console.error(
`Tiling: Error on createZoomTileFromChunk:\n${error.message}`,
);
return false;
}
console.log(
// eslint-disable-next-line max-len
`Tiling: Created Tile ${filename} with ${na.length} empty chunks in ${Date.now() - startTime}ms`,
@ -186,16 +194,17 @@ export async function createZoomTileFromChunk(
}
/*
* @param canvas canvas data
* @param canvasTileFolder root folder where to save tiles
* @param palette Palette to use
* @param cell tile to create [z, x, y]
* @return trie if successfully created tile, false if tile empty
*/
export async function createZoomedTile(
canvas,
canvasTileFolder,
palette,
cell,
) {
const palette = new Palette(canvas.colors);
const tileRGBBuffer = new Uint8Array(
TILE_SIZE * TILE_SIZE * TILE_ZOOM_LEVEL * TILE_ZOOM_LEVEL * 3,
);
@ -211,8 +220,15 @@ export async function createZoomedTile(
na.push([dx, dy]);
continue;
}
const chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer();
addRGBSubtiletoTile(TILE_ZOOM_LEVEL, [dx, dy], chunk, tileRGBBuffer);
try {
const chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer();
addRGBSubtiletoTile(TILE_ZOOM_LEVEL, [dx, dy], chunk, tileRGBBuffer);
} catch (error) {
console.error(
// eslint-disable-next-line max-len
`Tiling: Error on createZoomedTile on chunk ${chunkfile}:\n${error.message}`,
);
}
}
}
@ -222,17 +238,24 @@ export async function createZoomedTile(
});
const filename = tileFileName(canvasTileFolder, [z, x, y]);
await sharp(
Buffer.from(
tileRGBBuffer.buffer,
), {
raw: {
width: TILE_SIZE * TILE_ZOOM_LEVEL,
height: TILE_SIZE * TILE_ZOOM_LEVEL,
channels: 3,
try {
await sharp(
Buffer.from(
tileRGBBuffer.buffer,
), {
raw: {
width: TILE_SIZE * TILE_ZOOM_LEVEL,
height: TILE_SIZE * TILE_ZOOM_LEVEL,
channels: 3,
},
},
},
).resize(TILE_SIZE).toFile(filename);
).resize(TILE_SIZE).toFile(filename);
} catch (error) {
console.error(
`Tiling: Error on createZoomedTile:\n${error.message}`,
);
return false;
}
console.log(
// eslint-disable-next-line max-len
`Tiling: Created tile ${filename} with ${na.length} empty subtiles in ${Date.now() - startTime}ms.`,
@ -247,7 +270,7 @@ export async function createZoomedTile(
* @param canvasTileFolder root folder where to save texture
* @param palette Palette to use
*/
export async function createEmptyTile(
async function createEmptyTile(
canvasTileFolder,
palette,
) {
@ -265,15 +288,22 @@ export async function createEmptyTile(
tileRGBBuffer[i++] = palette.rgb[2];
}
const filename = `${canvasTileFolder}/emptytile.png`;
await sharp(Buffer.from(tileRGBBuffer.buffer), {
raw: {
width: TILE_SIZE,
height: TILE_SIZE,
channels: 3,
},
})
.png({ options: { compressionLevel: 6 } })
.toFile(filename);
try {
await sharp(Buffer.from(tileRGBBuffer.buffer), {
raw: {
width: TILE_SIZE,
height: TILE_SIZE,
channels: 3,
},
})
.png({ options: { compressionLevel: 6 } })
.toFile(filename);
} catch (error) {
console.error(
`Tiling: Error on createEmptyTile:\n${error.message}`,
);
return;
}
console.log(`Tiling: Created empty tile at ${filename}`);
}
@ -281,18 +311,18 @@ export async function createEmptyTile(
* created 4096x4096 texture of default canvas
* @param redisClient redis instance
* @param canvasId numberical Id of canvas
* @param canvasSize size of canvas
* @param canvas canvas data
* @param canvasTileFolder root folder where to save texture
* @param palette Palette to use
*
*/
export async function createTexture(
redisClient,
canvasId,
canvasSize,
canvas,
canvasTileFolder,
palette,
) {
const palette = new Palette(canvas.colors);
const canvasSize = canvas.size;
// dont create textures larger than 4096
const targetSize = Math.min(canvasSize, 4096);
const amount = targetSize / TILE_SIZE;
@ -310,8 +340,15 @@ export async function createTexture(
na.push([dx, dy]);
continue;
}
chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer();
addRGBSubtiletoTile(amount, [dx, dy], chunk, textureBuffer);
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}:\n${error.message}`,
);
}
}
}
} else {
@ -339,15 +376,22 @@ export async function createTexture(
});
const filename = `${canvasTileFolder}/texture.png`;
await sharp(
Buffer.from(textureBuffer.buffer), {
raw: {
width: targetSize,
height: targetSize,
channels: 3,
try {
await sharp(
Buffer.from(textureBuffer.buffer), {
raw: {
width: targetSize,
height: targetSize,
channels: 3,
},
},
},
).toFile(filename);
).toFile(filename);
} catch (error) {
console.error(
`Tiling: Error on createTexture:\n${error.message}`,
);
return;
}
console.log(
`Tiling: Created texture in ${(Date.now() - startTime) / 1000}s.`,
);
@ -356,24 +400,24 @@ export async function createTexture(
/*
* Create all tiles
* @param redisClient redis instance
* @param canvasSize dimension of the canvas (pixels width/height)
* @param canvasId id of the canvas
* @param canvasTileFolder root foler where to save tiles
* @param palette Palette of canvas
* @param canvas canvas data
* @param canvasTileFolder folder for tiles
* @param force overwrite existing tiles
*/
export async function initializeTiles(
redisClient,
canvasSize,
canvasId,
canvas,
canvasTileFolder,
palette,
force = false,
) {
console.log(
`Tiling: Initializing tiles in ${canvasTileFolder}, forceint = ${force}`,
);
const startTime = Date.now();
const palette = new Palette(canvas.colors);
const canvasSize = canvas.size;
const maxTiledZoom = getMaxTiledZoom(canvasSize);
// empty tile
await createEmptyTile(canvasTileFolder, palette);
@ -440,9 +484,8 @@ export async function initializeTiles(
await createTexture(
redisClient,
canvasId,
canvasSize,
canvas,
canvasTileFolder,
palette,
);
//--
console.log(

View File

@ -9,7 +9,6 @@ import { Worker } from 'worker_threads';
import logger from './logger';
// eslint-disable-next-line import/no-unresolved
import canvases from './canvases.json';
import Palette from './Palette';
import RedisCanvas from '../data/models/RedisCanvas';
import { TILE_FOLDER } from './config';
@ -29,7 +28,6 @@ const worker = new Worker('./workers/tilewriter.js');
class CanvasUpdater {
TileLoadingQueues;
palette;
id;
canvas;
firstZoomtileWidth;
@ -42,7 +40,6 @@ class CanvasUpdater {
this.id = id;
this.canvas = canvases[id];
this.canvasTileFolder = `${TILE_FOLDER}/${id}`;
this.palette = new Palette(this.canvas.colors);
this.firstZoomtileWidth = this.canvas.size / TILE_SIZE / TILE_ZOOM_LEVEL;
this.maxTiledZoom = getMaxTiledZoom(this.canvas.size);
this.startReloadingLoops();
@ -65,10 +62,9 @@ class CanvasUpdater {
worker.postMessage({
task: 'createZoomTileFromChunk',
args: [
this.canvas.size,
this.id,
this.canvas,
this.canvasTileFolder,
this.palette,
[cx, cy],
],
});
@ -76,8 +72,8 @@ class CanvasUpdater {
worker.postMessage({
task: 'createZoomedTile',
args: [
this.canvas,
this.canvasTileFolder,
this.palette,
[zoom, cx, cy],
],
});
@ -88,9 +84,8 @@ class CanvasUpdater {
task: 'createTexture',
args: [
this.id,
this.canvas.size,
this.canvas,
this.canvasTileFolder,
this.palette,
],
});
} else {
@ -126,10 +121,9 @@ class CanvasUpdater {
worker.postMessage({
task: 'initializeTiles',
args: [
this.canvas.size,
this.id,
this.canvas,
this.canvasTileFolder,
this.palette,
false,
],
});