Skip to content
This repository was archived by the owner on Jan 3, 2025. It is now read-only.

Commit fdda365

Browse files
committed
Initial Commit
0 parents  commit fdda365

6 files changed

+800
-0
lines changed

.eslintrc.json

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"es6": true,
5+
"node": true
6+
},
7+
"extends": "eslint:recommended",
8+
"parserOptions": {
9+
"ecmaVersion": 2018
10+
},
11+
"rules": {
12+
"indent": [
13+
"error",
14+
4
15+
],
16+
"linebreak-style": [
17+
"error",
18+
"windows"
19+
],
20+
"quotes": [
21+
"error",
22+
"double"
23+
],
24+
"semi": [
25+
"error",
26+
"always"
27+
],
28+
"no-console": "off",
29+
"no-process-env": "off"
30+
}
31+
}

.gitignore

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
8+
# Runtime data
9+
pids
10+
*.pid
11+
*.seed
12+
*.pid.lock
13+
14+
# Directory for instrumented libs generated by jscoverage/JSCover
15+
lib-cov
16+
17+
# Coverage directory used by tools like istanbul
18+
coverage
19+
20+
# nyc test coverage
21+
.nyc_output
22+
23+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24+
.grunt
25+
26+
# Bower dependency directory (https://bower.io/)
27+
bower_components
28+
29+
# node-waf configuration
30+
.lock-wscript
31+
32+
# Compiled binary addons (https://nodejs.org/api/addons.html)
33+
build/Release
34+
35+
# Dependency directories
36+
node_modules/
37+
jspm_packages/
38+
39+
# Typescript v1 declaration files
40+
typings/
41+
42+
# Optional npm cache directory
43+
.npm
44+
45+
# Optional eslint cache
46+
.eslintcache
47+
48+
# Optional REPL history
49+
.node_repl_history
50+
51+
# Output of 'npm pack'
52+
*.tgz
53+
54+
# Yarn Integrity file
55+
.yarn-integrity
56+
57+
# dotenv environment variables file
58+
.env
59+
60+
# project-specific files
61+
pixelfix-macos-x64
62+
pixelfix-linux-x64
63+
pixelfix-win-x64.exe
64+
pixelfix-win-x86.exe

README.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Pixelfix
2+
3+
Changes the colors of completely transparent pixels in an image to match the color of the nearest non-transparent pixel.
4+
5+
Designed to be a quick drag-and-drop tool:
6+
7+
1. Make your images
8+
2. Select them all and drag them on to the pixelfix executable. The pixelfix executable will overwrite the original images with fixed copies.
9+
3. Make sure there were no errors and close the console window
10+
4. Use or upload your images. Your images should now look fine when resized.
11+
12+
## More info
13+
14+
When saving an image file, most image editors will save completely transparent pixels as black. On some platforms, the resizing algorithm blends transparent pixels with non-transparent pixels, resulting in black edges on resized images. [Here](http://www.adriancourreges.com/blog/2017/05/09/beware-of-transparent-pixels/) is an article showing the difference and discussing techniques to fix the issue.
15+
16+
This script keeps those pixels transparent, but changes their color to match the nearest non-transparent pixel. This means that when the non-transparent and transparent pixels are blended, there should be no color difference.
17+
18+
This script is made into an executable using [the pkg tool](https://www.npmjs.com/package/pkg).
19+
20+
`pkg -o pixelfix -t node10-win-x64,node10-win-x86,node10-macos-x64,node10-linux-x64 .\index.js`

index.js

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"use strict";
2+
3+
const jimp = require("jimp");
4+
const Delaunay = require("d3-delaunay").Delaunay;
5+
6+
const neighborLocations = [
7+
[-1, -1],
8+
[ 0, -1],
9+
[ 1, -1],
10+
[ 1, 0],
11+
[ 1, 1],
12+
[ 0, 1],
13+
[-1, 1],
14+
[-1, 0]
15+
];
16+
17+
let argsArray = process.argv.slice(2);
18+
19+
console.log(argsArray);
20+
21+
let dbgMode = false;
22+
23+
for (let i = argsArray.length + 1; i >= 0; i--) {
24+
let arg = argsArray[i];
25+
if (arg == "-d") {
26+
dbgMode = true;
27+
argsArray.splice(i, 1);
28+
}
29+
}
30+
31+
if (process.argv.length < 3) {
32+
console.log("pixelfix \"path to file\" to fix transparent pixels in file");
33+
console.log("pixelfix \"path to file\" \"path to file 2\" to fix transparent pixels in multiple files");
34+
console.log("pixelfix -d \"path to file\" to view debug output (will overwrite file)");
35+
return;
36+
}
37+
38+
let promises = [];
39+
for (let fileLocation of argsArray) {
40+
promises.push((async function() {
41+
let image = await jimp.read(fileLocation);
42+
43+
let voronoiPoints = [];
44+
let voronoiColors = [];
45+
image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) {
46+
let alpha = this.bitmap.data[ idx + 3 ];
47+
if (alpha != 0) {
48+
let red = this.bitmap.data[ idx + 0 ];
49+
let green = this.bitmap.data[ idx + 1 ];
50+
let blue = this.bitmap.data[ idx + 2 ];
51+
// Voronoi
52+
for (let offset of neighborLocations) {
53+
let neighborAlpha = this.bitmap.data[image.getPixelIndex(x + offset[0], y + offset[1]) + 3];
54+
if (neighborAlpha == 0) {
55+
voronoiPoints.push([x, y]);
56+
voronoiColors.push([red, green, blue]);
57+
break;
58+
}
59+
}
60+
}
61+
});
62+
if (voronoiPoints.length > 0) {
63+
let dela = Delaunay.from(voronoiPoints);
64+
image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) {
65+
let alpha = this.bitmap.data[ idx + 3 ];
66+
if (alpha == 0) {
67+
let closestIndex = dela.find(x, y);
68+
if (closestIndex != -1) {
69+
let color = voronoiColors[closestIndex];
70+
71+
this.bitmap.data[ idx + 0 ] = color[0];
72+
this.bitmap.data[ idx + 1 ] = color[1];
73+
this.bitmap.data[ idx + 2 ] = color[2];
74+
if (dbgMode) {
75+
this.bitmap.data[idx + 3] = 255;
76+
}
77+
}
78+
}
79+
});
80+
await image.writeAsync(fileLocation);
81+
console.log(`Written to ${fileLocation}`);
82+
} else {
83+
console.log(`No transparent pixels to fix in ${fileLocation}`);
84+
}
85+
})());
86+
}
87+
88+
Promise.all(promises).then(() => {
89+
console.log("Press any key to exit");
90+
process.stdin.setRawMode(true);
91+
process.stdin.resume();
92+
process.stdin.on("data", process.exit.bind(process, 0));
93+
});

0 commit comments

Comments
 (0)