Skip to content

Instantly share code, notes, and snippets.

@disgusting-dev
Created April 4, 2021 09:12
Show Gist options
  • Save disgusting-dev/899774a89f9cefa55e0add0c79930dac to your computer and use it in GitHub Desktop.
Save disgusting-dev/899774a89f9cefa55e0add0c79930dac to your computer and use it in GitHub Desktop.
module.exports = {
//First 8 bites of any original PNG Image
PNG_SIGNATURE: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
RGBA_DIMENSIONS_AMOUNT: 4, //1 for each letter in RGBA;
TYPE_IHDR: 0x49484452,
TYPE_IEND: 0x49454e44,
TYPE_IDAT: 0x49444154,
TYPE_PLTE: 0x504c5445,
TYPE_tRNS: 0x74524e53, // eslint-disable-line camelcase
TYPE_gAMA: 0x67414d41, // eslint-disable-line camelcase
// color-type bits
COLORTYPE_GRAYSCALE: 0,
COLORTYPE_PALETTE: 1,
COLORTYPE_COLOR: 2,
COLORTYPE_ALPHA: 4, // e.g. grayscale and alpha
// color-type combinations
COLORTYPE_PALETTE_COLOR: 3,
COLORTYPE_COLOR_ALPHA: 6,
COLORTYPE_TO_BPP_MAP: {
0: 1,
2: 3,
3: 1,
4: 2,
6: 4,
},
GAMMA_DIVISION: 100000,
}
// var fs = require("fs"),
// PNG = require("pngjs").PNG;
// fs.createReadStream("art.png")
// .pipe(
// new PNG({
// filterType: 4,
// })
// )
// .on("parsed", function () {
// for (var y = 0; y < this.height; y++) {
// for (var x = 0; x < this.width; x++) {
// var idx = (this.width * y + x) << 2;
// // invert color
// this.data[idx] = 255 - this.data[idx];
// this.data[idx + 1] = 255 - this.data[idx + 1];
// this.data[idx + 2] = 255 - this.data[idx + 2];
// }
// }
// this.pack().pipe(fs.createWriteStream("out.png"));
// });
const Stream = require('stream');
const { RGBA_DIMENSIONS_AMOUNT } = require('./constants');
class PNG extends Stream {
constructor(
options = {},
) {
super();
// if create picture from scratch, user may define width/heigth in options
// we will use automatical data from parsed picture
const { width = 0, height = 0 } = options;
this.gamma = 0;
this.options = options;
this.data = null;
//Stream should be both readable and writable
this.readable = this.writable = true;
// Cutting of floating point parts in case of such existance;
[this.width, this.height] = [width, height].map(Math.trunc);
this._initializeDataBuffer();
// this._parser = new Parser(this.options);
}
_initializeDataBuffer() {
//check typeof to be sure we work with number-like structures
if (
this.width
&& this.height
&& !isNaN(this.width)
&& !isNaN(this.height)
) {
//Buffer is array of integers, 1 dimensional by default.
// So, for our needs we have to take width + height and multiplied by dimensions amount,
// Cause each pixel is representation of 4 rgba values;
//So we already allocate needed amount of memory for this;
this.data = Buffer.alloc(RGBA_DIMENSIONS_AMOUNT * this.width * this.height);
} else {
throw new Error('Width and Height should be numbers');
}
}
}
const {
PNG_SIGNATURE,
RGBA_DIMENSIONS_AMOUNT
} = require('./constants');
class Parser {
constructor(
options,
{
read,
} = {}
) {
this._options = options;
this._hasIHDR = false;
this._emittedHeadersFinished = false;
this._colorType = 0;
this._palette = [];
this._chunks = {};
this.read = read;
}
start = () => {
this.read(PNG_SIGNATURE.length, this._parseSignature());
}
_parseSignature = (data) => {
// To be sure we work with correct format of file by signature
if (JSON.stringify(
this.data.slice(0, PNG_SIGNATURE.length)
) === JSON.stringify(PNG_SIGNATURE)) {
return this.read(8, this._parseChunkBegin);
}
throw new Error('Invalid file signature');
}
_parseChunkBegin = () => {
const data = Buffer.from([0x21, 0x09, 0x19, 0x98,
0x34, 0xae, 0xe8, 0xff]);
console.log(data);
const length = data.readUInt32BE();
console.log(length);
const type = data.readUInt32BE(4);
console.log(type);
let name = '';
for (let i = RGBA_DIMENSIONS_AMOUNT; i < PNG_SIGNATURE.length; i++) {
name += String.fromCharCode(data[i]);
}
console.log(name);
let ancillary = Boolean(data[4] & 0x20);
}
}
const parser = new Parser();
parser._parseChunkBegin();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment