Skip to content

Commit dc6eb7d

Browse files
committed
feat: refactor to use Jimp instead of canvas, refactor gif
1 parent 126d192 commit dc6eb7d

4 files changed

Lines changed: 2187 additions & 76 deletions

File tree

cli.js

Lines changed: 91 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
#!/usr/bin/env node
22
"use strict";
33

4-
const Clipper = require("image-clipper");
5-
const canvas = require("canvas");
6-
const { GifFrame, GifUtil } = require("gifwrap");
4+
const { GifFrame, GifUtil, BitmapImage } = require("gifwrap");
75
const Jimp = require("jimp");
86
const updateNotifier = require("update-notifier");
97
const meow = require("meow");
108

9+
const CONTAINER_WIDTH = 727;
10+
const CUT_WIDTH = 325;
11+
const CUT_HEIGHT = 100;
12+
const CARD_PADDING_TOP = 37;
13+
const CARD_PADDING_HORIZONTAL = 16;
14+
const CARD_PADDING_BOTTOM = 16;
15+
const CARD_MARGIN_BOTTOM = 16;
16+
const CARD_HEIGHT = CARD_PADDING_TOP + CUT_HEIGHT + CARD_PADDING_BOTTOM;
17+
const Y_OFFSET = CARD_HEIGHT + CARD_MARGIN_BOTTOM;
18+
const MINIMUM_HEIGHT = 3 * CARD_HEIGHT + 2 * CARD_MARGIN_BOTTOM;
19+
1120
const cli = meow(
1221
`
1322
Usage
@@ -23,66 +32,94 @@ const cli = meow(
2332
updateNotifier({ pkg: cli.pkg }).notify();
2433

2534
if (cli.input.length === 0) {
26-
console.error("Specify at least one path");
35+
console.error("Specify exactly one path to the image/gif");
2736
process.exit(1);
2837
}
2938

30-
const cropGithubGifs = path => {
31-
const GIF_WIDTH = 325;
32-
const GIF_HEIGHT = 100;
33-
const VERTICAL_MARGIN = 53;
34-
const HORIZONTAL_MARGIN = 32;
35-
36-
GifUtil.read(path)
37-
.then(source => {
38-
let croppedGifs = [];
39-
40-
for (let i = 0; i < 6; i++) {
41-
const isLeft = i % 2 === 0;
42-
const x = isLeft ? 0 : GIF_WIDTH + HORIZONTAL_MARGIN;
43-
const y = Math.floor(i / 2) * (GIF_HEIGHT + VERTICAL_MARGIN);
44-
45-
let gif = [];
46-
for (let frame = 0; frame < source.frames.length; frame++) {
47-
let j = new Jimp(
48-
source.frames[frame].bitmap.width,
49-
source.frames[frame].bitmap.height,
50-
0
51-
);
52-
j.bitmap.data = source.frames[frame].bitmap.data;
53-
j.resize(GIF_WIDTH * 2 + HORIZONTAL_MARGIN, GIF_HEIGHT * 3 + VERTICAL_MARGIN * 2)
54-
.crop(x, y, GIF_WIDTH, GIF_HEIGHT);
55-
gif[frame] = new GifFrame(j.bitmap);
56-
}
57-
croppedGifs.push(gif);
58-
}
59-
60-
croppedGifs.forEach((gif, i) => {
61-
GifUtil.quantizeDekker(gif);
62-
GifUtil.write(`${i}.gif`, gif).then(result => {
63-
console.log(`saved ${i}.gif`);
64-
});
65-
});
66-
})
67-
.catch(console.error);
39+
const getXY = index => {
40+
const isLeft = index % 2 === 0;
41+
// There is no margin between cards, instead, they are
42+
// separated by flex's space-between, which is directly
43+
// affected by container width. When someday container
44+
// width changes, we can just change its value and this
45+
// method will be fixed.
46+
const x = isLeft
47+
? CARD_PADDING_HORIZONTAL
48+
: CONTAINER_WIDTH - (CARD_PADDING_HORIZONTAL + CUT_WIDTH);
49+
const indexFromTop = Math.floor(index / 2);
50+
const y = CARD_PADDING_TOP + indexFromTop * Y_OFFSET;
51+
return { x, y };
52+
};
53+
54+
const cropFrame = image => {
55+
const cropped = [];
56+
for (let i = 0; i < 6; i++) {
57+
const clone = image.clone();
58+
const { x, y } = getXY(i);
59+
clone.crop(x, y, CUT_WIDTH, CUT_HEIGHT);
60+
cropped.push(clone);
61+
}
62+
return cropped;
6863
};
6964

70-
const WIDTH = 727;
71-
const INTERVAL = 171;
65+
const cropGithubGifs = async path => {
66+
try {
67+
const source = await GifUtil.read(path);
68+
const { frames } = source;
69+
let croppedGifs = [];
70+
let frameIndex = 1;
71+
for (const frame of frames) {
72+
console.log(`Processing frame ${frameIndex} of ${frames.length}`);
73+
const buf = frame.bitmap.data;
74+
frame.scanAllCoords((x, y, bi) => {
75+
buf[bi + 3] = 0xff;
76+
});
7277

73-
const cropGithubImages = path => {
74-
const clipper = Clipper({ canvas });
78+
let jimpToCrop = new Jimp(frame.bitmap.width, frame.bitmap.height, 0);
79+
jimpToCrop.bitmap.data = frame.bitmap.data;
80+
jimpToCrop.resize(CONTAINER_WIDTH, Jimp.AUTO);
7581

76-
for (let i = 0; i < 6; i++) {
77-
const isLeft = i % 2 === 0;
78-
const x = isLeft ? 16 : WIDTH - 16 - 325;
79-
const y = 53 + INTERVAL * Math.floor(i / 2 + 0.1);
80-
clipper.image(path, function() {
81-
const name = `${i}.jpg`;
82-
this.crop(x, y, 325, 100).toFile(`./${name}`, function() {
83-
console.log(`saved ${name}`);
82+
const frameToCrop = await Jimp.read(jimpToCrop);
83+
84+
const cropped = cropFrame(frameToCrop).map(img => {
85+
return new GifFrame(img.bitmap);
8486
});
87+
88+
cropped.forEach((croppedFrame, i) => {
89+
croppedFrame.scanAllCoords((x, y, bi) => {
90+
buf[bi + 3] = 0xff;
91+
});
92+
croppedGifs[i] = croppedGifs[i]
93+
? [...croppedGifs[i], croppedFrame]
94+
: [croppedFrame];
95+
});
96+
97+
frameIndex++;
98+
}
99+
100+
croppedGifs.forEach(async (croppedFrames, i) => {
101+
console.log(
102+
`Quantizing Dekker value for gif ${i} (this might take a while)`
103+
);
104+
GifUtil.quantizeDekker(croppedFrames);
105+
await GifUtil.write(`${i}.gif`, croppedFrames);
106+
console.log(`saved ${i}.gif`);
85107
});
108+
} catch (error) {
109+
console.error(error);
110+
}
111+
};
112+
113+
const cropGithubImages = async path => {
114+
const image = await Jimp.read(path);
115+
116+
image.resize(CONTAINER_WIDTH, Jimp.AUTO);
117+
const cropped = cropFrame(image);
118+
119+
for (let i = 0; i < cropped.length; i++) {
120+
const clone = cropped[i];
121+
await clone.writeAsync(`${i}.jpg`);
122+
console.log(i, "has been written.");
86123
}
87124
};
88125

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"bin": {
88
"crop-github-images": "cli.js"
99
},
10+
"scripts": {
11+
"test": "ava"
12+
},
1013
"engines": {
1114
"node": ">=8"
1215
},
@@ -31,5 +34,8 @@
3134
"jimp": "^0.6.1",
3235
"meow": "^5.0.0",
3336
"update-notifier": "^2.5.0"
37+
},
38+
"devDependencies": {
39+
"ava": "^1.4.1"
3440
}
3541
}

rick.gif

3.04 MB
Loading

0 commit comments

Comments
 (0)