Skip to content

Commit

Permalink
Implement interlace based on foliojs#8
Browse files Browse the repository at this point in the history
  • Loading branch information
blikblum committed Sep 26, 2019
1 parent cd085b7 commit 60f267e
Showing 1 changed file with 128 additions and 82 deletions.
210 changes: 128 additions & 82 deletions png-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,113 +185,159 @@ module.exports = class PNG {
throw err;
}

const { width, height } = this;
const pixelBytes = this.pixelBitlength / 8;
const scanlineLength = pixelBytes * this.width;

const pixels = new Buffer(scanlineLength * this.height);
const fullPixels = new Buffer(pixelBytes * width * height);
const { length } = data;
let row = 0;
let pos = 0;
let c = 0;

while (pos < length) {
var byte, col, i, left, upper;
switch (data[pos++]) {
case 0: // None
for (i = 0; i < scanlineLength; i++) {
pixels[c++] = data[pos++];
}
break;

case 1: // Sub
for (i = 0; i < scanlineLength; i++) {
byte = data[pos++];
left = i < pixelBytes ? 0 : pixels[c - pixelBytes];
pixels[c++] = (byte + left) % 256;
}
break;

case 2: // Up
for (i = 0; i < scanlineLength; i++) {
byte = data[pos++];
col = (i - (i % pixelBytes)) / pixelBytes;
upper =
row &&
pixels[
(row - 1) * scanlineLength +
col * pixelBytes +
(i % pixelBytes)
];
pixels[c++] = (upper + byte) % 256;
}
break;

case 3: // Average
for (i = 0; i < scanlineLength; i++) {
byte = data[pos++];
col = (i - (i % pixelBytes)) / pixelBytes;
left = i < pixelBytes ? 0 : pixels[c - pixelBytes];
upper =
row &&
pixels[
(row - 1) * scanlineLength +
col * pixelBytes +
(i % pixelBytes)
];
pixels[c++] = (byte + Math.floor((left + upper) / 2)) % 256;
}
break;

case 4: // Paeth
for (i = 0; i < scanlineLength; i++) {
var paeth, upperLeft;
byte = data[pos++];
col = (i - (i % pixelBytes)) / pixelBytes;
left = i < pixelBytes ? 0 : pixels[c - pixelBytes];

if (row === 0) {
upper = upperLeft = 0;
} else {
function pass(x0, y0, dx, dy) {
let w = Math.ceil((width - x0) / dx);
let h = Math.ceil((height - y0) / dy);
const isFull = width === w && height === h;
const scanlineLength = pixelBytes * w;
const pixels = isFull ? fullPixels : new Buffer(scanlineLength * h);
let row = 0;
let c = 0;
while (row < h && pos < length) {
var byte, col, i, left, upper;
switch (data[pos++]) {
case 0: // None
for (i = 0; i < scanlineLength; i++) {
pixels[c++] = data[pos++];
}
break;

case 1: // Sub
for (i = 0; i < scanlineLength; i++) {
byte = data[pos++];
left = i < pixelBytes ? 0 : pixels[c - pixelBytes];
pixels[c++] = (byte + left) % 256;
}
break;

case 2: // Up
for (i = 0; i < scanlineLength; i++) {
byte = data[pos++];
col = (i - (i % pixelBytes)) / pixelBytes;
upper =
row &&
pixels[
(row - 1) * scanlineLength +
col * pixelBytes +
(i % pixelBytes)
];
upperLeft =
col &&
pixels[c++] = (upper + byte) % 256;
}
break;

case 3: // Average
for (i = 0; i < scanlineLength; i++) {
byte = data[pos++];
col = (i - (i % pixelBytes)) / pixelBytes;
left = i < pixelBytes ? 0 : pixels[c - pixelBytes];
upper =
row &&
pixels[
(row - 1) * scanlineLength +
(col - 1) * pixelBytes +
col * pixelBytes +
(i % pixelBytes)
];
pixels[c++] = (byte + Math.floor((left + upper) / 2)) % 256;
}
break;

case 4: // Paeth
for (i = 0; i < scanlineLength; i++) {
var paeth, upperLeft;
byte = data[pos++];
col = (i - (i % pixelBytes)) / pixelBytes;
left = i < pixelBytes ? 0 : pixels[c - pixelBytes];

if (row === 0) {
upper = upperLeft = 0;
} else {
upper =
pixels[
(row - 1) * scanlineLength +
col * pixelBytes +
(i % pixelBytes)
];
upperLeft =
col &&
pixels[
(row - 1) * scanlineLength +
(col - 1) * pixelBytes +
(i % pixelBytes)
];
}

const p = left + upper - upperLeft;
const pa = Math.abs(p - left);
const pb = Math.abs(p - upper);
const pc = Math.abs(p - upperLeft);

if (pa <= pb && pa <= pc) {
paeth = left;
} else if (pb <= pc) {
paeth = upper;
} else {
paeth = upperLeft;
}

const p = left + upper - upperLeft;
const pa = Math.abs(p - left);
const pb = Math.abs(p - upper);
const pc = Math.abs(p - upperLeft);

if (pa <= pb && pa <= pc) {
paeth = left;
} else if (pb <= pc) {
paeth = upper;
} else {
paeth = upperLeft;
pixels[c++] = (byte + paeth) % 256;
}
break;

default:
throw new Error(`Invalid filter algorithm: ${data[pos - 1]}`);
}

pixels[c++] = (byte + paeth) % 256;
if (!isFull) {
var fullPos = ((y0 + row * dy) * width + x0) * pixelBytes;
var partPos = row * scanlineLength;
for (i = 0; i < w; i += 1) {
for (var j = 0; j < pixelBytes; j += 1)
fullPixels[fullPos++] = pixels[partPos++];
fullPos += (dx - 1) * pixelBytes;
}
break;
}

default:
throw new Error(`Invalid filter algorithm: ${data[pos - 1]}`);
row++;
}
}

row++;
if (this.interlaceMethod === 1) {
/*
1 6 4 6 2 6 4 6
7 7 7 7 7 7 7 7
5 6 5 6 5 6 5 6
7 7 7 7 7 7 7 7
3 6 4 6 3 6 4 6
7 7 7 7 7 7 7 7
5 6 5 6 5 6 5 6
7 7 7 7 7 7 7 7
*/
pass(0, 0, 8, 8); // 1
/* NOTE these seem to follow the pattern:
* pass(x, 0, 2*x, 2*x);
* pass(0, x, x, 2*x);
* with x being 4, 2, 1.
*/
pass(4, 0, 8, 8); // 2
pass(0, 4, 4, 8); // 3

pass(2, 0, 4, 4); // 4
pass(0, 2, 2, 4); // 5

pass(1, 0, 2, 2); // 6
pass(0, 1, 1, 2); // 7
} else {
pass(0, 0, 1, 1);
}

return fn(pixels);
return fn(fullPixels);
});
}

Expand Down

0 comments on commit 60f267e

Please sign in to comment.