pixelplanet/utils/ocean-tiles/drawOcean.js

159 lines
4.5 KiB
JavaScript

/*
* this script takes black/whtie tiles and sets their colors on the canvas
* its used to set the land area on the planet.
*/
import { createClient, commandOptions } from 'redis';
import sharp from 'sharp';
import fs from 'fs';
import canvases from '../../src/canvases.json';
import { getChunkOfPixel } from '../../src/core/utils';
// -----------------------------------------------------------------------------
const CANVAS_SIZE = 256 * 256;
const TILE_SIZE = 256;
const TILEFOLDER = './utils/ocean-tiles/ocean';
const IMG_TILE_SIZE = 2048;
// change redis URL here if you need a different one
const redis = createClient({ url: "redis://localhost:6379" });
// -----------------------------------------------------------------------------
const CANVAS_MIN_XY = -(CANVAS_SIZE / 2);
const CANVAS_MAX_XY = (CANVAS_SIZE / 2) - 1;
/**
* get Chunk from redis, pad if neccessary
*/
async function getChunk(
canvasId,
i,
j,
padding = null,
) {
const key = `ch:${canvasId}:${i}:${j}`;
let chunk = await redis.get(
commandOptions({ returnBuffers: true }),
key,
);
if (padding > 0 && chunk && chunk.length < padding) {
const pad = Buffer.alloc(padding - chunk.length);
chunk = Buffer.concat([chunk, pad]);
}
return chunk;
}
/**
* Load imagemask from ABGR buffer and execute filter function
* for each black pixel
*/
async function imagemask2Canvas(
canvasId,
x,
y,
data,
width,
height,
filter,
) {
console.info(
`Loading mask with size ${width} / ${height} to ${x} / ${y} to the canvas`,
);
const expectedLength = TILE_SIZE ** 2;
const canvas = canvases[canvasId];
const canvasMinXY = -(canvas.size / 2);
const imageData = new Uint8Array(data.buffer);
const [ucx, ucy] = getChunkOfPixel(canvas.size, x, y);
const [lcx, lcy] = getChunkOfPixel(canvas.size, x + width, y + height);
console.info(`Loading to chunks from ${ucx} / ${ucy} to ${lcx} / ${lcy} ...`);
for (let cx = ucx; cx <= lcx; cx += 1) {
for (let cy = ucy; cy <= lcy; cy += 1) {
let chunk = null;
try {
chunk = await getChunk(canvasId, cx, cy, expectedLength);
} catch (error) {
console.error(
// eslint-disable-next-line max-len
`Could not load chunk ch:${canvasId}:${cx}:${cy} for imagemask-load: ${error.message}`,
);
}
chunk = (chunk && chunk.length)
? new Uint8Array(chunk)
: new Uint8Array(TILE_SIZE * TILE_SIZE);
// offset of chunk in image
const cOffX = cx * TILE_SIZE + canvasMinXY - x;
const cOffY = cy * TILE_SIZE + canvasMinXY - y;
let cOff = 0;
let pxlCnt = 0;
for (let py = 0; py < TILE_SIZE; py += 1) {
for (let px = 0; px < TILE_SIZE; px += 1) {
const clrX = cOffX + px;
const clrY = cOffY + py;
if (clrX >= 0 && clrY >= 0 && clrX < width && clrY < height) {
let offset = (clrX + clrY * width) * 3;
if (!imageData[offset++]
&& !imageData[offset++]
&& !imageData[offset]
) {
chunk[cOff] = filter();
pxlCnt += 1;
}
}
cOff += 1;
}
}
if (pxlCnt) {
const key = `ch:${canvasId}:${cx}:${cy}`;
const ret = await redis.set(key, Buffer.from(chunk.buffer));
if (ret) {
console.info(`Loaded ${pxlCnt} pixels into chunk ${cx}, ${cy}.`);
}
}
chunk = null;
}
}
console.info('Imagemask loading done.');
}
/**
* Load black/white tiles from TILEFOLDER onto canvas
*/
async function applyMasks() {
await redis.connect();
try {
for (let ty = 0; ty < CANVAS_SIZE / IMG_TILE_SIZE; ty += 1) {
for (let tx = 0; tx < CANVAS_SIZE / IMG_TILE_SIZE; tx += 1) {
const [ x, y ] = [tx, ty].map(z => z * IMG_TILE_SIZE + CANVAS_MIN_XY);
const filename = `${TILEFOLDER}/${tx}/${ty}.png`;
console.log(`Checking tile ${filename}`);
if (!fs.existsSync(filename)) {
console.log(`Not Found ${filename}`);
continue;
}
let tile = await sharp(filename).removeAlpha().raw().toBuffer();
// eslint-disable-next-line max-len
await imagemask2Canvas(0, x, y, tile, IMG_TILE_SIZE, IMG_TILE_SIZE, () => {
return 1; //set color to index 1 -> land
});
tile = null;
}
if (global?.gc) {
global.gc();
}
}
await redis.quit();
process.exit(0);
} catch (err) {
console.error(err);
await redis.quit();
process.exit(1);
}
}
applyMasks();