95 lines
2.8 KiB
JavaScript
95 lines
2.8 KiB
JavaScript
// Split original image into multiple 1024x1024 chunks to reduce memory usage
|
|
// (images have to be unpacked into typed arrays for resizing) and allow
|
|
// parallel processing of multiple tiles at a time.
|
|
//
|
|
|
|
'use strict';
|
|
|
|
/*
|
|
* pixelFloor and pixelCeil are modified versions of Math.floor and Math.ceil
|
|
* functions which take into account floating point arithmetic errors.
|
|
* Those errors can cause undesired increments/decrements of sizes and offsets:
|
|
* Math.ceil(36 / (36 / 500)) = 501
|
|
* pixelCeil(36 / (36 / 500)) = 500
|
|
*/
|
|
|
|
var PIXEL_EPSILON = 1e-5;
|
|
|
|
function pixelFloor(x) {
|
|
var nearest = Math.round(x);
|
|
|
|
if (Math.abs(x - nearest) < PIXEL_EPSILON) { return nearest; }
|
|
return Math.floor(x);
|
|
}
|
|
|
|
function pixelCeil(x) {
|
|
var nearest = Math.round(x);
|
|
|
|
if (Math.abs(x - nearest) < PIXEL_EPSILON) { return nearest; }
|
|
return Math.ceil(x);
|
|
}
|
|
|
|
|
|
module.exports = function createRegions(options) {
|
|
var scaleX = options.toWidth / options.width;
|
|
var scaleY = options.toHeight / options.height;
|
|
|
|
var innerTileWidth = pixelFloor(options.srcTileSize * scaleX) - 2 * options.destTileBorder;
|
|
var innerTileHeight = pixelFloor(options.srcTileSize * scaleY) - 2 * options.destTileBorder;
|
|
|
|
// prevent infinite loop, this should never happen
|
|
if (innerTileWidth < 1 || innerTileHeight < 1) {
|
|
throw new Error('Internal error in pica: target tile width/height is too small.');
|
|
}
|
|
|
|
var x, y;
|
|
var innerX, innerY, toTileWidth, toTileHeight;
|
|
var tiles = [];
|
|
var tile;
|
|
|
|
// we go top-to-down instead of left-to-right to make image displayed from top to
|
|
// doesn in the browser
|
|
for (innerY = 0; innerY < options.toHeight; innerY += innerTileHeight) {
|
|
for (innerX = 0; innerX < options.toWidth; innerX += innerTileWidth) {
|
|
x = innerX - options.destTileBorder;
|
|
if (x < 0) { x = 0; }
|
|
toTileWidth = innerX + innerTileWidth + options.destTileBorder - x;
|
|
if (x + toTileWidth >= options.toWidth) {
|
|
toTileWidth = options.toWidth - x;
|
|
}
|
|
|
|
y = innerY - options.destTileBorder;
|
|
if (y < 0) { y = 0; }
|
|
toTileHeight = innerY + innerTileHeight + options.destTileBorder - y;
|
|
if (y + toTileHeight >= options.toHeight) {
|
|
toTileHeight = options.toHeight - y;
|
|
}
|
|
|
|
tile = {
|
|
toX: x,
|
|
toY: y,
|
|
toWidth: toTileWidth,
|
|
toHeight: toTileHeight,
|
|
|
|
toInnerX: innerX,
|
|
toInnerY: innerY,
|
|
toInnerWidth: innerTileWidth,
|
|
toInnerHeight: innerTileHeight,
|
|
|
|
offsetX: x / scaleX - pixelFloor(x / scaleX),
|
|
offsetY: y / scaleY - pixelFloor(y / scaleY),
|
|
scaleX: scaleX,
|
|
scaleY: scaleY,
|
|
|
|
x: pixelFloor(x / scaleX),
|
|
y: pixelFloor(y / scaleY),
|
|
width: pixelCeil(toTileWidth / scaleX),
|
|
height: pixelCeil(toTileHeight / scaleY)
|
|
};
|
|
|
|
tiles.push(tile);
|
|
}
|
|
}
|
|
|
|
return tiles;
|
|
};
|