Skip to content

Commit ef37fc9

Browse files
authored
Merge pull request #3 from thinknathan/v3
Add new final canvas size feature
2 parents 77b7576 + 72d57ae commit ef37fc9

File tree

8 files changed

+113
-31
lines changed

8 files changed

+113
-31
lines changed

@types/types.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ declare type Options = {
33
folderPath?: string;
44
width: number;
55
height?: number;
6+
canvasWidth?: number;
7+
canvasHeight?: number;
68
};

README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ Command-line utility that slices input images into segments according to specifi
1919
`node slice.cjs`
2020

2121
```
22-
-f, --filename Input image filename [string]
23-
-i, --folderPath Input folder [string]
24-
-w, --width Output image width [number] [required]
25-
-h, --height Output image height [number]
22+
-f, --filename Input image filename [string]
23+
-i, --folderPath Input folder [string]
24+
-w, --width Width of each slice [number] [required]
25+
-h, --height Height of each slice [number]
26+
-d, --canvasWidth Width of canvas for final output [number]
27+
-g, --canvasHeight Height of canvas for final output [number]
2628
```
2729

2830
- If `filename` does not include an extension, `.png`, `.gif`, `.jpg` and `.jpeg` will be guessed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "node-image-slice",
3-
"version": "2.0.0",
3+
"version": "2.1.0",
44
"description": "Slices an input image into segments according to specified width and height",
55
"repository": {
66
"type": "git",

slice.cjs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const options = yargs
1919
})
2020
.option('w', {
2121
alias: 'width',
22-
describe: 'Output image width',
22+
describe: 'Width of each slice',
2323
type: 'number',
2424
demandOption: true,
2525
coerce: (value) => {
@@ -31,19 +31,35 @@ const options = yargs
3131
})
3232
.option('h', {
3333
alias: 'height',
34-
describe: 'Output image height',
34+
describe: 'Height of each slice',
3535
type: 'number',
3636
coerce: (value) => {
3737
if (value !== undefined && value < 1) {
3838
throw new Error('height should not be lower than 1');
3939
}
4040
return Math.round(value);
4141
},
42+
})
43+
.option('d', {
44+
alias: 'canvasWidth',
45+
describe: 'Width of canvas for final output',
46+
type: 'number',
47+
})
48+
.option('g', {
49+
alias: 'canvasHeight',
50+
describe: 'Height of canvas for final output',
51+
type: 'number',
4252
}).argv;
4353
if (options.filename) {
4454
// Process a single image
45-
const { filename, width, height } = options;
46-
(0, processImage_1.sliceImage)(filename, width, height);
55+
const { filename, width, height, canvasWidth, canvasHeight } = options;
56+
(0, processImage_1.sliceImage)(
57+
filename,
58+
width,
59+
height,
60+
canvasWidth,
61+
canvasHeight,
62+
);
4763
} else if (options.folderPath) {
4864
// Process all images in a folder, splitting the task into threads
4965
let numCores = 2;

src/slice.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const options = yargs
2020
})
2121
.option('w', {
2222
alias: 'width',
23-
describe: 'Output image width',
23+
describe: 'Width of each slice',
2424
type: 'number',
2525
demandOption: true,
2626
coerce: (value) => {
@@ -32,20 +32,30 @@ const options = yargs
3232
})
3333
.option('h', {
3434
alias: 'height',
35-
describe: 'Output image height',
35+
describe: 'Height of each slice',
3636
type: 'number',
3737
coerce: (value) => {
3838
if (value !== undefined && value < 1) {
3939
throw new Error('height should not be lower than 1');
4040
}
4141
return Math.round(value);
4242
},
43+
})
44+
.option('d', {
45+
alias: 'canvasWidth',
46+
describe: 'Width of canvas for final output',
47+
type: 'number',
48+
})
49+
.option('g', {
50+
alias: 'canvasHeight',
51+
describe: 'Height of canvas for final output',
52+
type: 'number',
4353
}).argv as unknown as Options;
4454

4555
if (options.filename) {
4656
// Process a single image
47-
const { filename, width, height } = options;
48-
sliceImage(filename, width, height);
57+
const { filename, width, height, canvasWidth, canvasHeight } = options;
58+
sliceImage(filename, width, height, canvasWidth, canvasHeight);
4959
} else if (options.folderPath) {
5060
// Process all images in a folder, splitting the task into threads
5161
let numCores = 2;

src/utils/processImage.ts

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ export function sliceImage(
1212
filename: string,
1313
width: number,
1414
height?: number,
15+
canvasWidth?: number,
16+
canvasHeight?: number,
1517
skipExtCheck?: boolean,
1618
): void {
1719
Jimp.read(filename, (err, image) => {
@@ -20,7 +22,14 @@ export function sliceImage(
2022
} else {
2123
// Continue slicing if image is successfully read
2224
if (image) {
23-
continueSlicing(image, width, height, filename);
25+
continueSlicing(
26+
image,
27+
width,
28+
height,
29+
canvasWidth,
30+
canvasHeight,
31+
filename,
32+
);
2433
return;
2534
}
2635
}
@@ -41,7 +50,14 @@ export function sliceImage(
4150
Jimp.read(fullFilename, (err, image) => {
4251
if (!foundImage && !err) {
4352
foundImage = true;
44-
continueSlicing(image, width, height, fullFilename);
53+
continueSlicing(
54+
image,
55+
width,
56+
height,
57+
canvasWidth,
58+
canvasHeight,
59+
fullFilename,
60+
);
4561
}
4662
});
4763
}
@@ -55,6 +71,8 @@ function continueSlicing(
5571
image: Jimp,
5672
width: number,
5773
height: number | undefined,
74+
canvasWidth: number | undefined,
75+
canvasHeight: number | undefined,
5876
inputFilename: string,
5977
): void {
6078
// If height is not specified, use width as height
@@ -90,7 +108,27 @@ function continueSlicing(
90108
);
91109
const outputFilename = `${outputFolder}/${baseFilename}_${x}_${y}.png`;
92110

93-
slice.write(outputFilename);
111+
if (canvasWidth || canvasHeight) {
112+
// Calculate canvas dimensions
113+
const finalCanvasWidth = canvasWidth || width;
114+
const finalCanvasHeight = (canvasHeight || canvasWidth) ?? height;
115+
116+
// Create a new canvas with transparent background
117+
const canvas = new Jimp(
118+
finalCanvasWidth,
119+
finalCanvasHeight,
120+
0x00000000,
121+
);
122+
123+
// Composite the image in the middle of the canvas
124+
const startX2 = Math.floor((finalCanvasWidth - sliceWidth) / 2);
125+
const startY2 = Math.floor((finalCanvasHeight - sliceHeight) / 2);
126+
canvas.composite(slice, startX2, startY2);
127+
canvas.write(outputFilename);
128+
} else {
129+
slice.write(outputFilename);
130+
}
131+
94132
console.log(`Slice saved: ${outputFilename}`);
95133
}
96134
}
@@ -100,6 +138,6 @@ function continueSlicing(
100138
if (!isMainThread) {
101139
const { filePath, options } = workerData;
102140
options.filename = filePath;
103-
const { filename, width, height } = options;
104-
sliceImage(filename, width, height, true);
141+
const { filename, width, height, canvasWidth, canvasHeight } = options;
142+
sliceImage(filename, width, height, canvasWidth, canvasHeight, true);
105143
}

utils/processImage.js

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ const Jimp = require("jimp");
55
const fs = require("fs");
66
const path = require("path");
77
const worker_threads_1 = require("worker_threads");
8-
const outputFolder = "output";
8+
const outputFolder = 'output';
99
/**
1010
* Function to slice an image into smaller segments
1111
*/
12-
function sliceImage(filename, width, height, skipExtCheck) {
12+
function sliceImage(filename, width, height, canvasWidth, canvasHeight, skipExtCheck) {
1313
Jimp.read(filename, (err, image) => {
1414
if (err && skipExtCheck) {
1515
console.error(err);
1616
}
1717
else {
1818
// Continue slicing if image is successfully read
1919
if (image) {
20-
continueSlicing(image, width, height, filename);
20+
continueSlicing(image, width, height, canvasWidth, canvasHeight, filename);
2121
return;
2222
}
2323
}
@@ -26,7 +26,7 @@ function sliceImage(filename, width, height, skipExtCheck) {
2626
return;
2727
}
2828
// Check for supported image formats if skipExtCheck is false
29-
const supportedFormats = [".png", ".gif", ".jpg", ".jpeg"];
29+
const supportedFormats = ['.png', '.gif', '.jpg', '.jpeg'];
3030
let foundImage = false;
3131
// Attempt to read the image with different extensions
3232
supportedFormats.forEach((ext) => {
@@ -35,7 +35,7 @@ function sliceImage(filename, width, height, skipExtCheck) {
3535
Jimp.read(fullFilename, (err, image) => {
3636
if (!foundImage && !err) {
3737
foundImage = true;
38-
continueSlicing(image, width, height, fullFilename);
38+
continueSlicing(image, width, height, canvasWidth, canvasHeight, fullFilename);
3939
}
4040
});
4141
}
@@ -45,7 +45,7 @@ exports.sliceImage = sliceImage;
4545
/**
4646
* Continue slicing the image into smaller segments
4747
*/
48-
function continueSlicing(image, width, height, inputFilename) {
48+
function continueSlicing(image, width, height, canvasWidth, canvasHeight, inputFilename) {
4949
// If height is not specified, use width as height
5050
height = height || width;
5151
const imageWidth = image.getWidth();
@@ -68,7 +68,21 @@ function continueSlicing(image, width, height, inputFilename) {
6868
// Incorporate the input filename into the output filename
6969
const baseFilename = path.basename(inputFilename, path.extname(inputFilename));
7070
const outputFilename = `${outputFolder}/${baseFilename}_${x}_${y}.png`;
71-
slice.write(outputFilename);
71+
if (canvasWidth || canvasHeight) {
72+
// Calculate canvas dimensions
73+
const finalCanvasWidth = canvasWidth || width;
74+
const finalCanvasHeight = (canvasHeight || canvasWidth) ?? height;
75+
// Create a new canvas with transparent background
76+
const canvas = new Jimp(finalCanvasWidth, finalCanvasHeight, 0x00000000);
77+
// Composite the image in the middle of the canvas
78+
const startX2 = Math.floor((finalCanvasWidth - sliceWidth) / 2);
79+
const startY2 = Math.floor((finalCanvasHeight - sliceHeight) / 2);
80+
canvas.composite(slice, startX2, startY2);
81+
canvas.write(outputFilename);
82+
}
83+
else {
84+
slice.write(outputFilename);
85+
}
7286
console.log(`Slice saved: ${outputFilename}`);
7387
}
7488
}
@@ -77,6 +91,6 @@ function continueSlicing(image, width, height, inputFilename) {
7791
if (!worker_threads_1.isMainThread) {
7892
const { filePath, options } = worker_threads_1.workerData;
7993
options.filename = filePath;
80-
const { filename, width, height } = options;
81-
sliceImage(filename, width, height, true);
94+
const { filename, width, height, canvasWidth, canvasHeight } = options;
95+
sliceImage(filename, width, height, canvasWidth, canvasHeight, true);
8296
}

utils/workerPool.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ class WorkerPool {
2424
* @param options - Image processing options for the file.
2525
*/
2626
createWorker(filePath, options) {
27-
const worker = new worker_threads_1.Worker(path.join(__dirname, "processImage.js"), {
27+
const worker = new worker_threads_1.Worker(path.join(__dirname, 'processImage.js'), {
2828
workerData: { filePath, options },
2929
});
3030
// Listen for messages and errors from the worker
31-
worker.on("message", (message) => {
31+
worker.on('message', (message) => {
3232
console.log(message);
3333
this.processNextTask();
3434
});
35-
worker.on("error", (err) => {
35+
worker.on('error', (err) => {
3636
console.error(`Error in worker for file ${filePath}:`, err);
3737
this.processNextTask();
3838
});
@@ -66,7 +66,7 @@ class WorkerPool {
6666
*/
6767
waitForCompletion() {
6868
this.workers.forEach((worker) => {
69-
worker.on("exit", () => {
69+
worker.on('exit', () => {
7070
this.processNextTask();
7171
});
7272
});

0 commit comments

Comments
 (0)