148 lines
3.3 KiB
JavaScript
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);
|
|
};
|