Skip to content

Instantly share code, notes, and snippets.

@FrostBird347
Last active March 17, 2024 09:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FrostBird347/66c77eb97ecf7fa799e25f318308af5e to your computer and use it in GitHub Desktop.
Save FrostBird347/66c77eb97ecf7fa799e25f318308af5e to your computer and use it in GitHub Desktop.
An extremely simple and poorly written script that glitches images.
#!/usr/local/bin/node
//qoi180.js
//A terribly made script that:
// - encodes an input image into a "reversed" (horizontal+vertical flip) qoi
// - reverses the order of every chunk within the qoi file
// - saves the messed up file
//Note that it requires qoijs, imagemagick and arg
//It also has the same issues as copyimgdiff.js: It has not been tested on other systems, assigns a massive amount of ram, can't open images close to or above 1GB in size and doesn't support images with an alpha channel.
const qoi = require('qoijs');
const fs = require('fs');
const execFileSync = require('child_process').execFileSync;
const arg = require('arg');
const versionString = "1.2.1";
const args = arg({
// Types
'--help': Boolean,
'--invert': Boolean,
'--alpha': Boolean,
'--vertical': Boolean,
'--colourspace': String,
// Aliases
'-h': '--help',
'-i': '--invert',
'--flip': '--invert',
'-f': '--invert',
'--reverse': '--invert',
'-r': '--invert',
'-a': '--alpha',
'-v': '--vertical',
'-c': '--colourspace',
// Parity
'--parityNull': Boolean,
'-t': "--invert",
'-l': "--parityNull",
}, {permissive: true});
//Display help page else verify that args are correct
if (args['--help'] || JSON.stringify(args) == '{"_":[]}') {
console.log(`qoi180.js (version ${versionString})
Usage:
qoi180.js input_image output_image [-hia]
-h, --help Display this help page
-i, --invert Reverse the pixels at the end instead of the start
-f, --flip Alias for --invert
-r, --reverse Alias for --invert
-t Alias for --invert, for parity with previous versions
-l Does nothing, for parity with previous versions
-a, --alpha Keep the alpha channel if it exists
-v, --vertical Rotate the image by 90° while corrupting
-c, --colourspace Use a different colourspace (run "magick -list colorspace" to get a list of valid colourspaces)`);
process.exit(0);
} else if (args['_'].length != 2 || args['_'][0].startsWith("-") || args['_'][1].startsWith("-")) {
console.error("Invalid arguments: Either too many/little files were specified or an unknown option was used!");
console.log("Usage:\n qoi180.js input_image output_image [-hia]");
process.exit(1);
}
//Gen args for loading the input image
let inputArgs = [args['_'][0], "-layers", "flatten"];
if (!args['--invert']) {
inputArgs.push("-flip", "-flop");
}
if (args['--vertical']) {
inputArgs.push("-rotate", "90");
}
if (!args['--alpha']) {
inputArgs.push("-alpha", "off");
}
if (args['--colourspace'] != undefined) {
inputArgs.push("-colorspace", args['--colourspace'], "-set", "colorspace", "sRGB");
}
inputArgs.push("QOI:-");
//Load the image
let input;
try {
input = execFileSync("/usr/local/bin/convert", inputArgs, {maxBuffer: 10000000000});
} catch(err) {
console.error("Failed to load the image!");
process.exit(1);
}
//Use allocUnsafe because we are just reordering data in the input and thus the filesize should not change
let output = Buffer.allocUnsafe(input.length);
let actions = [];
let jumpAmount = 1;
//Add an action to the actions list, using the current jump amount and the inputted index
function addAction(iB) {
let newAction = [];
for (let i = 0; i < jumpAmount; i++) {
newAction.push(input[iB + i]);
}
actions.push(newAction);
}
//Copy the header over to the output file
let iOut = 14;
input.copy(output, 0, 0, iOut);
//Then extract each action
for (let i = 14; i < input.length; i+= jumpAmount) {
if (input[i] == 0 && input[i+1] == 0 && input[i+2] == 0 && input[i+3] == 0 && input[i+4] == 0 && input[i+5] == 0 && input[i+6] == 0 && input[i+7] == 1) {
jumpAmount = 8;
addAction(i);
} else if (input[i] == 254) {
jumpAmount = 4;
addAction(i);
} else if (input[i] == 255) {
jumpAmount = 5;
addAction(i);
} else if ((input[i] & 0b11000000) === 0b00000000) {
jumpAmount = 1;
addAction(i);
} else if ((input[i] & 0b11000000) === 0b01000000) {
jumpAmount = 1;
addAction(i);
} else if ((input[i] & 0b11000000) === 0b10000000) {
jumpAmount = 2;
addAction(i);
} else if ((input[i] & 0b11000000) === 0b11000000) {
jumpAmount = 1;
addAction(i);
} else {
jumpAmount = 1;
actions.push(input[i]);
console.error("Unknown chunk!");
}
}
//Append all the actions in the reverse order, skipping the last one
for (let i = actions.length - 2; i >= 0; i--) {
Buffer.from(actions[i]).copy(output, iOut, 0);
iOut += actions[i].length;
}
//Append the last action to the end of the file instead of the start: this is the end marker
Buffer.from(actions[actions.length - 1]).copy(output, iOut, 0);
//Gen args for saving the output image
let outputArgs = ["QOI:-"]
if (args['--invert']) {
outputArgs.push("-flip", "-flop");
}
if (args['--vertical']) {
outputArgs.push("-rotate", "270");
}
if (args['--colourspace'] != undefined) {
outputArgs.push("-set", "colorspace", args['--colourspace'], "-colorspace", 'sRGB');
}
outputArgs.push(args['_'][1]);
//Save image
try {
execFileSync("/usr/local/bin/convert", outputArgs, {input: output});
} catch(err) {
console.error("Failed to save the image!");
process.exit(1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment