-
Notifications
You must be signed in to change notification settings - Fork 34
/
build-portfolio.js
114 lines (95 loc) · 4.09 KB
/
build-portfolio.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
* Selfie2Anime <https://selfie2anime.com>
* Copyright (c) 2019 by SilentByte <https://www.silentbyte.com/>
*/
// Builds portfolio images and generates composites for the portfolio lightbox.
const fs = require("fs").promises;
const path = require("path");
const crypto = require("crypto");
const glob = require("glob");
const del = require("del");
const fx = require("mkdir-recursive");
const Jimp = require("jimp");
const guetzli = require("imagemin-guetzli");
const PORTFOLIO_SRC_DIR = "./public_assets/portfolio/";
const PORTFOLIO_DST_DIR = "./public/gen/portfolio/";
const PORTFOLIO_TREE_DST_DIR = "./src/gen/portfolio-tree.gen.json";
const COMPOSITE_SIZE = 256;
const PORTFOLIO_SIZE = 160;
const QUALITY = 90;
function sha1(buffer) {
const hash = crypto.createHash("sha1");
hash.update(buffer);
return hash.digest("hex");
}
function rgbToCssHex(r, g, b) {
return "#" + r.toString(16).padStart(2, "0")
+ g.toString(16).padStart(2, "0")
+ b.toString(16).padStart(2, "0");
}
async function generate() {
fx.mkdirSync(PORTFOLIO_DST_DIR, undefined);
del.sync(path.join(PORTFOLIO_DST_DIR, "*.gen.jpg"));
const dirs = glob.sync(path.join(PORTFOLIO_SRC_DIR, "*"));
const font = await Jimp.loadFont(Jimp.FONT_SANS_16_WHITE);
const box = new Jimp(135, 40, "#000000aa");
let counter = 0;
const portfolioTree = [];
for(const filename of dirs) {
let progress = ++counter / dirs.length * 100;
console.log(`PROCESSING (${progress.toFixed(1).padStart(5)}%) ${filename}`);
const compositeImage = new Jimp(COMPOSITE_SIZE * 2, COMPOSITE_SIZE);
const [originalImage, ganImage] = await Promise.all([
Jimp.read(path.join(filename, "original.jpg")),
Jimp.read(path.join(filename, "gan.jpg")),
]);
let [compositeBuffer, originalBuffer, ganBuffer] = await Promise.all([
compositeImage
.blit(new Jimp(originalImage).resize(COMPOSITE_SIZE, COMPOSITE_SIZE), 0, 0)
.blit(new Jimp(ganImage).resize(COMPOSITE_SIZE, COMPOSITE_SIZE), COMPOSITE_SIZE, 0)
.blit(box, 2 * COMPOSITE_SIZE - 135, COMPOSITE_SIZE - 18)
.print(font, 2 * COMPOSITE_SIZE - 129, COMPOSITE_SIZE - 18, "selfie2anime.com")
.getBufferAsync(Jimp.MIME_JPEG),
originalImage
.resize(PORTFOLIO_SIZE, PORTFOLIO_SIZE)
.getBufferAsync(Jimp.MIME_JPEG),
ganImage
.resize(PORTFOLIO_SIZE, PORTFOLIO_SIZE)
.getBufferAsync(Jimp.MIME_JPEG),
]);
if(!process.env.DISABLE_IMAGE_OPTIMIZATION) {
[compositeBuffer, originalBuffer, ganBuffer] = await Promise.all([
guetzli({quality: QUALITY})(compositeBuffer),
guetzli({quality: QUALITY})(originalBuffer),
guetzli({quality: QUALITY})(ganBuffer),
]);
}
const originalSha1 = sha1(originalBuffer);
const ganSha1 = sha1(ganBuffer);
const compositeSha1 = sha1(compositeBuffer);
const originalDst = path.join(PORTFOLIO_DST_DIR, originalSha1 + ".gen.jpg");
const ganDst = path.join(PORTFOLIO_DST_DIR, ganSha1 + ".gen.jpg");
const compositeDst = path.join(PORTFOLIO_DST_DIR, compositeSha1 + ".gen.jpg");
await Promise.all([
fs.writeFile(originalDst, originalBuffer),
fs.writeFile(ganDst, ganBuffer),
fs.writeFile(compositeDst, compositeBuffer),
]);
const thumb = Jimp.intToRGBA(ganImage.resize(1, 1).getPixelColor(0, 0));
portfolioTree.push({
original: originalDst.replace("public/", ""),
gan: ganDst.replace("public/", ""),
composite: compositeDst.replace("public/", ""),
size: PORTFOLIO_SIZE,
thumb: rgbToCssHex(thumb.r, thumb.g, thumb.b),
});
}
await fs.writeFile(PORTFOLIO_TREE_DST_DIR, JSON.stringify(portfolioTree, null, 4));
}
if(process.env.DISABLE_PORTFOLIO_BUILD) {
console.log("SKIPPING PORTFOLIO BUILD");
} else {
generate().then(() => {
console.log("DONE");
});
}