const fs = require('fs-extra'); const sharp = require('sharp'); const path = require('path'); const readChunk = require('read-chunk'); const imageType = require('image-type'); const bmp = require('bmp-js'); module.exports = Self => { require('../methods/image/download')(Self); require('../methods/image/upload')(Self); // Function extracted from jimp package (utils) function scan(image, x, y, w, h, f) { // round input x = Math.round(x); y = Math.round(y); w = Math.round(w); h = Math.round(h); for (let _y = y; _y < y + h; _y++) { for (let _x = x; _x < x + w; _x++) { const idx = (image.bitmap.width * _y + _x) << 2; f.call(image, _x, _y, idx); } } return image; } // Function extracted from jimp package (type-bmp) function fromAGBR(bitmap) { return scan({bitmap}, 0, 0, bitmap.width, bitmap.height, function( x, y, index ) { const alpha = this.bitmap.data[index + 0]; const blue = this.bitmap.data[index + 1]; const green = this.bitmap.data[index + 2]; const red = this.bitmap.data[index + 3]; this.bitmap.data[index + 0] = red; this.bitmap.data[index + 1] = green; this.bitmap.data[index + 2] = blue; this.bitmap.data[index + 3] = bitmap.is_with_alpha ? alpha : 0xff; }).bitmap; } Self.registerImage = async(collectionName, srcFilePath, fileName, entityId) => { const models = Self.app.models; const tx = await Self.beginTransaction({}); const myOptions = {transaction: tx}; try { const collection = await models.ImageCollection.findOne({ fields: [ 'id', 'name', 'maxWidth', 'maxHeight', 'model', 'property' ], where: {name: collectionName}, include: { relation: 'sizes', scope: { fields: ['width', 'height', 'crop'] } } }, myOptions); const data = { name: fileName, collectionFk: collectionName }; const newImage = await Self.upsertWithWhere(data, { name: fileName, collectionFk: collectionName, updated: Date.vnNow() }, myOptions); // Resizes and saves the image const container = await models.ImageContainer.container(collectionName); const rootPath = container.client.root; const collectionDir = path.join(rootPath, collectionName); const file = `${fileName}.png`; const dstDir = path.join(collectionDir, 'full'); const dstFile = path.join(dstDir, file); const buffer = readChunk.sync(srcFilePath, 0, 12); const type = imageType(buffer); let sharpOptions; let imgSrc = srcFilePath; if (type.mime == 'image/bmp') { const bmpBuffer = fs.readFileSync(srcFilePath); const bmpData = fromAGBR(bmp.decode(bmpBuffer)); imgSrc = bmpData.data; sharpOptions = { raw: { width: bmpData.width, height: bmpData.height, channels: 4 } }; } const resizeOpts = { withoutEnlargement: true, fit: 'inside' }; await fs.mkdir(dstDir, {recursive: true}); await sharp(imgSrc, sharpOptions) .resize(collection.maxWidth, collection.maxHeight, resizeOpts) .png() .toFile(dstFile); const sizes = collection.sizes(); for (let size of sizes) { const dstDir = path.join(collectionDir, `${size.width}x${size.height}`); const dstFile = path.join(dstDir, file); const resizeOpts = { withoutEnlargement: true, fit: size.crop ? 'cover' : 'inside' }; await fs.mkdir(dstDir, {recursive: true}); await sharp(imgSrc, sharpOptions) .resize(size.width, size.height, resizeOpts) .png() .toFile(dstFile); } const model = models[collection.model]; if (!model) throw new Error('Matching model not found'); const item = await model.findById(entityId, null, myOptions); if (item) { await item.updateAttribute( collection.property, fileName, myOptions ); } if (fs.existsSync(srcFilePath)) await fs.unlink(srcFilePath); await tx.commit(); return newImage; } catch (e) { await tx.rollback(); throw e; } }; };