Files
asciidisco.com/build/node_modules/to-ico/index.js
2023-08-01 13:49:46 +02:00

148 lines
3.3 KiB
JavaScript

'use strict';
const arrify = require('arrify');
const bufferAlloc = require('buffer-alloc');
const imageSize = require('image-size');
const parsePng = require('parse-png');
const resizeImg = require('resize-img');
const constants = {
bitmapSize: 40,
colorMode: 0,
directorySize: 16,
headerSize: 6
};
const createHeader = n => {
const buf = bufferAlloc(constants.headerSize);
buf.writeUInt16LE(0, 0);
buf.writeUInt16LE(1, 2);
buf.writeUInt16LE(n, 4);
return buf;
};
const createDirectory = (data, offset) => {
const buf = bufferAlloc(constants.directorySize);
const size = data.data.length + constants.bitmapSize;
const width = data.width === 256 ? 0 : data.width;
const height = data.height === 256 ? 0 : data.height;
const bpp = data.bpp * 8;
buf.writeUInt8(width, 0);
buf.writeUInt8(height, 1);
buf.writeUInt8(0, 2);
buf.writeUInt8(0, 3);
buf.writeUInt16LE(1, 4);
buf.writeUInt16LE(bpp, 6);
buf.writeUInt32LE(size, 8);
buf.writeUInt32LE(offset, 12);
return buf;
};
const createBitmap = (data, compression) => {
const buf = bufferAlloc(constants.bitmapSize);
buf.writeUInt32LE(constants.bitmapSize, 0);
buf.writeInt32LE(data.width, 4);
buf.writeInt32LE(data.height * 2, 8);
buf.writeUInt16LE(1, 12);
buf.writeUInt16LE(data.bpp * 8, 14);
buf.writeUInt32LE(compression, 16);
buf.writeUInt32LE(data.data.length, 20);
buf.writeInt32LE(0, 24);
buf.writeInt32LE(0, 28);
buf.writeUInt32LE(0, 32);
buf.writeUInt32LE(0, 36);
return buf;
};
const createDib = (data, width, height, bpp) => {
const cols = width * bpp;
const rows = height * cols;
const end = rows - cols;
const buf = bufferAlloc(data.length);
for (let row = 0; row < rows; row += cols) {
for (let col = 0; col < cols; col += bpp) {
let pos = row + col;
const r = data.readUInt8(pos);
const g = data.readUInt8(pos + 1);
const b = data.readUInt8(pos + 2);
const a = data.readUInt8(pos + 3);
pos = (end - row) + col;
buf.writeUInt8(b, pos);
buf.writeUInt8(g, pos + 1);
buf.writeUInt8(r, pos + 2);
buf.writeUInt8(a, pos + 3);
}
}
return buf;
};
const generateIco = data => {
return Promise.all(data.map(x => parsePng(x))).then(data => {
const header = createHeader(data.length);
const arr = [header];
let len = header.length;
let offset = constants.headerSize + (constants.directorySize * data.length);
for (const x of data) {
const dir = createDirectory(x, offset);
arr.push(dir);
len += dir.length;
offset += x.data.length + constants.bitmapSize;
}
for (const x of data) {
const header = createBitmap(x, constants.colorMode);
const dib = createDib(x.data, x.width, x.height, x.bpp);
arr.push(header, dib);
len += header.length + dib.length;
}
return Buffer.concat(arr, len);
});
};
const resizeImages = (data, opts) => {
data = data
.map(x => {
const size = imageSize(x);
return {
data: x,
width: size.width,
height: size.height
};
})
.reduce((a, b) => a.width > b.width ? a : b, {});
return Promise.all(opts.sizes.filter(x => x <= data.width).map(x => resizeImg(data.data, {
width: x,
height: x
})));
};
module.exports = (input, opts) => {
const data = arrify(input);
opts = Object.assign({
resize: false,
sizes: [16, 24, 32, 48, 64, 128, 256]
}, opts);
if (opts.resize) {
return resizeImages(data, opts).then(generateIco);
}
return generateIco(data);
};