Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Corecii committed Nov 17, 2018
0 parents commit fdda365
Show file tree
Hide file tree
Showing 6 changed files with 800 additions and 0 deletions.
31 changes: 31 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"indent": [
"error",
4
],
"linebreak-style": [
"error",
"windows"
],
"quotes": [
"error",
"double"
],
"semi": [
"error",
"always"
],
"no-console": "off",
"no-process-env": "off"
}
}
64 changes: 64 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# project-specific files
pixelfix-macos-x64
pixelfix-linux-x64
pixelfix-win-x64.exe
pixelfix-win-x86.exe
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Pixelfix

Changes the colors of completely transparent pixels in an image to match the color of the nearest non-transparent pixel.

Designed to be a quick drag-and-drop tool:

1. Make your images
2. Select them all and drag them on to the pixelfix executable. The pixelfix executable will overwrite the original images with fixed copies.
3. Make sure there were no errors and close the console window
4. Use or upload your images. Your images should now look fine when resized.

## More info

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.

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.

This script is made into an executable using [the pkg tool](https://www.npmjs.com/package/pkg).

`pkg -o pixelfix -t node10-win-x64,node10-win-x86,node10-macos-x64,node10-linux-x64 .\index.js`
93 changes: 93 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"use strict";

const jimp = require("jimp");
const Delaunay = require("d3-delaunay").Delaunay;

const neighborLocations = [
[-1, -1],
[ 0, -1],
[ 1, -1],
[ 1, 0],
[ 1, 1],
[ 0, 1],
[-1, 1],
[-1, 0]
];

let argsArray = process.argv.slice(2);

console.log(argsArray);

let dbgMode = false;

for (let i = argsArray.length + 1; i >= 0; i--) {
let arg = argsArray[i];
if (arg == "-d") {
dbgMode = true;
argsArray.splice(i, 1);
}
}

if (process.argv.length < 3) {
console.log("pixelfix \"path to file\" to fix transparent pixels in file");
console.log("pixelfix \"path to file\" \"path to file 2\" to fix transparent pixels in multiple files");
console.log("pixelfix -d \"path to file\" to view debug output (will overwrite file)");
return;
}

let promises = [];
for (let fileLocation of argsArray) {
promises.push((async function() {
let image = await jimp.read(fileLocation);

let voronoiPoints = [];
let voronoiColors = [];
image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) {
let alpha = this.bitmap.data[ idx + 3 ];
if (alpha != 0) {
let red = this.bitmap.data[ idx + 0 ];
let green = this.bitmap.data[ idx + 1 ];
let blue = this.bitmap.data[ idx + 2 ];
// Voronoi
for (let offset of neighborLocations) {
let neighborAlpha = this.bitmap.data[image.getPixelIndex(x + offset[0], y + offset[1]) + 3];
if (neighborAlpha == 0) {
voronoiPoints.push([x, y]);
voronoiColors.push([red, green, blue]);
break;
}
}
}
});
if (voronoiPoints.length > 0) {
let dela = Delaunay.from(voronoiPoints);
image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) {
let alpha = this.bitmap.data[ idx + 3 ];
if (alpha == 0) {
let closestIndex = dela.find(x, y);
if (closestIndex != -1) {
let color = voronoiColors[closestIndex];

this.bitmap.data[ idx + 0 ] = color[0];
this.bitmap.data[ idx + 1 ] = color[1];
this.bitmap.data[ idx + 2 ] = color[2];
if (dbgMode) {
this.bitmap.data[idx + 3] = 255;
}
}
}
});
await image.writeAsync(fileLocation);
console.log(`Written to ${fileLocation}`);
} else {
console.log(`No transparent pixels to fix in ${fileLocation}`);
}
})());
}

Promise.all(promises).then(() => {
console.log("Press any key to exit");
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.on("data", process.exit.bind(process, 0));
});
Loading

0 comments on commit fdda365

Please sign in to comment.