V1_commit_RGC

This commit is contained in:
2026-02-11 13:57:54 +01:00
commit ef397eedac
4901 changed files with 292881 additions and 0 deletions

21
SuiviREForamteur/node_modules/fast-png/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2015 Michaël Zasso
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

69
SuiviREForamteur/node_modules/fast-png/README.md generated vendored Normal file
View File

@@ -0,0 +1,69 @@
# fast-png
PNG image decoder and encoder written entirely in JavaScript.
<h3 align="center">
<a href="https://www.zakodium.com">
<img src="https://www.zakodium.com/brand/zakodium-logo-white.svg" width="50" alt="Zakodium logo" />
</a>
<p>
Maintained by <a href="https://www.zakodium.com">Zakodium</a>
</p>
[![NPM version][npm-image]][npm-url]
[![build status][ci-image]][ci-url]
[![npm download][download-image]][download-url]
</h3>
## Installation
`$ npm install --save fast-png`
## Usage
### `decode(png[, options])`
#### Arguments
- `png` - A TypedArray or Buffer that contains the PNG data.
- `options` - An object of options
#### Options
- `checkCrc` - If set to `true`, the CRC will be checked for each chunk and an error will be thrown in case it's wrong (default: false).
### `encode(image)`
#### Arguments
- `png` - An object representing the image. You can pass an ImageData from the Canvas API or an object with the following properties:
- `width` - The width of the image
- `height` - The height of the image
- `data` - An array or TypedArray with the image data
- `depth` - A number indicating the color depth (only 8 and 16 are supported now). Default: `8`.
- `channels` - Number of channels, including alpha (1, 2, 3 and 4 are supported). Default: `4`.
- `text` - An object with key-value pairs representing `tEXt` chunks. The keys must have less than 80 characters.
The keys and values must have only characters in the latin1 charset (maximum code point of 255).
Default: `undefined`.
### `hasPngSignature(array)`
Returns whether the array starts with the PNG signature (magic bytes).
## PNG standard
Spec can be found at: https://www.w3.org/TR/PNG/
## License
[MIT](./LICENSE)
[npm-image]: https://img.shields.io/npm/v/fast-png.svg?style=flat-square
[npm-url]: https://www.npmjs.com/package/fast-png
[ci-image]: https://github.com/image-js/fast-png/workflows/Node.js%20CI/badge.svg?branch=main
[ci-url]: https://github.com/image-js/fast-png/actions?query=workflow%3A%22Node.js+CI%22
[download-image]: https://img.shields.io/npm/dm/fast-png.svg?style=flat-square
[download-url]: https://www.npmjs.com/package/fast-png

View File

@@ -0,0 +1,505 @@
import { IOBuffer } from 'iobuffer';
import { inflate, Inflate as Inflator } from 'pako';
import { checkCrc } from './helpers/crc';
import { decodeInterlaceAdam7 } from './helpers/decodeInterlaceAdam7';
import { decodeInterlaceNull } from './helpers/decodeInterlaceNull';
import { checkSignature } from './helpers/signature';
import { decodetEXt, readKeyword, textChunkName } from './helpers/text';
import { ColorType, CompressionMethod, DisposeOpType, FilterMethod, InterlaceMethod, BlendOpType, } from './internalTypes';
export default class PngDecoder extends IOBuffer {
_checkCrc;
_inflator;
_png;
_apng;
_end;
_hasPalette;
_palette;
_hasTransparency;
_transparency;
_compressionMethod;
_filterMethod;
_interlaceMethod;
_colorType;
_isAnimated;
_numberOfFrames;
_numberOfPlays;
_frames;
_writingDataChunks;
constructor(data, options = {}) {
super(data);
const { checkCrc = false } = options;
this._checkCrc = checkCrc;
this._inflator = new Inflator();
this._png = {
width: -1,
height: -1,
channels: -1,
data: new Uint8Array(0),
depth: 1,
text: {},
};
this._apng = {
width: -1,
height: -1,
channels: -1,
depth: 1,
numberOfFrames: 1,
numberOfPlays: 0,
text: {},
frames: [],
};
this._end = false;
this._hasPalette = false;
this._palette = [];
this._hasTransparency = false;
this._transparency = new Uint16Array(0);
this._compressionMethod = CompressionMethod.UNKNOWN;
this._filterMethod = FilterMethod.UNKNOWN;
this._interlaceMethod = InterlaceMethod.UNKNOWN;
this._colorType = ColorType.UNKNOWN;
this._isAnimated = false;
this._numberOfFrames = 1;
this._numberOfPlays = 0;
this._frames = [];
this._writingDataChunks = false;
// PNG is always big endian
// https://www.w3.org/TR/PNG/#7Integers-and-byte-order
this.setBigEndian();
}
decode() {
checkSignature(this);
while (!this._end) {
const length = this.readUint32();
const type = this.readChars(4);
this.decodeChunk(length, type);
}
this.decodeImage();
return this._png;
}
decodeApng() {
checkSignature(this);
while (!this._end) {
const length = this.readUint32();
const type = this.readChars(4);
this.decodeApngChunk(length, type);
}
this.decodeApngImage();
return this._apng;
}
// https://www.w3.org/TR/PNG/#5Chunk-layout
decodeChunk(length, type) {
const offset = this.offset;
switch (type) {
// 11.2 Critical chunks
case 'IHDR': // 11.2.2 IHDR Image header
this.decodeIHDR();
break;
case 'PLTE': // 11.2.3 PLTE Palette
this.decodePLTE(length);
break;
case 'IDAT': // 11.2.4 IDAT Image data
this.decodeIDAT(length);
break;
case 'IEND': // 11.2.5 IEND Image trailer
this._end = true;
break;
// 11.3 Ancillary chunks
case 'tRNS': // 11.3.2.1 tRNS Transparency
this.decodetRNS(length);
break;
case 'iCCP': // 11.3.3.3 iCCP Embedded ICC profile
this.decodeiCCP(length);
break;
case textChunkName: // 11.3.4.3 tEXt Textual data
decodetEXt(this._png.text, this, length);
break;
case 'pHYs': // 11.3.5.3 pHYs Physical pixel dimensions
this.decodepHYs();
break;
default:
this.skip(length);
break;
}
if (this.offset - offset !== length) {
throw new Error(`Length mismatch while decoding chunk ${type}`);
}
if (this._checkCrc) {
checkCrc(this, length + 4, type);
}
else {
this.skip(4);
}
}
decodeApngChunk(length, type) {
const offset = this.offset;
if (type !== 'fdAT' && type !== 'IDAT' && this._writingDataChunks) {
this.pushDataToFrame();
}
switch (type) {
case 'acTL':
this.decodeACTL();
break;
case 'fcTL':
this.decodeFCTL();
break;
case 'fdAT':
this.decodeFDAT(length);
break;
default:
this.decodeChunk(length, type);
this.offset = offset + length;
break;
}
if (this.offset - offset !== length) {
throw new Error(`Length mismatch while decoding chunk ${type}`);
}
if (this._checkCrc) {
checkCrc(this, length + 4, type);
}
else {
this.skip(4);
}
}
// https://www.w3.org/TR/PNG/#11IHDR
decodeIHDR() {
const image = this._png;
image.width = this.readUint32();
image.height = this.readUint32();
image.depth = checkBitDepth(this.readUint8());
const colorType = this.readUint8();
this._colorType = colorType;
let channels;
switch (colorType) {
case ColorType.GREYSCALE:
channels = 1;
break;
case ColorType.TRUECOLOUR:
channels = 3;
break;
case ColorType.INDEXED_COLOUR:
channels = 1;
break;
case ColorType.GREYSCALE_ALPHA:
channels = 2;
break;
case ColorType.TRUECOLOUR_ALPHA:
channels = 4;
break;
// Kept for exhaustiveness.
// eslint-disable-next-line unicorn/no-useless-switch-case
case ColorType.UNKNOWN:
default:
throw new Error(`Unknown color type: ${colorType}`);
}
this._png.channels = channels;
this._compressionMethod = this.readUint8();
if (this._compressionMethod !== CompressionMethod.DEFLATE) {
throw new Error(`Unsupported compression method: ${this._compressionMethod}`);
}
this._filterMethod = this.readUint8();
this._interlaceMethod = this.readUint8();
}
decodeACTL() {
this._numberOfFrames = this.readUint32();
this._numberOfPlays = this.readUint32();
this._isAnimated = true;
}
decodeFCTL() {
const image = {
sequenceNumber: this.readUint32(),
width: this.readUint32(),
height: this.readUint32(),
xOffset: this.readUint32(),
yOffset: this.readUint32(),
delayNumber: this.readUint16(),
delayDenominator: this.readUint16(),
disposeOp: this.readUint8(),
blendOp: this.readUint8(),
data: new Uint8Array(0),
};
this._frames.push(image);
}
// https://www.w3.org/TR/PNG/#11PLTE
decodePLTE(length) {
if (length % 3 !== 0) {
throw new RangeError(`PLTE field length must be a multiple of 3. Got ${length}`);
}
const l = length / 3;
this._hasPalette = true;
const palette = [];
this._palette = palette;
for (let i = 0; i < l; i++) {
palette.push([this.readUint8(), this.readUint8(), this.readUint8()]);
}
}
// https://www.w3.org/TR/PNG/#11IDAT
decodeIDAT(length) {
this._writingDataChunks = true;
const dataLength = length;
const dataOffset = this.offset + this.byteOffset;
this._inflator.push(new Uint8Array(this.buffer, dataOffset, dataLength));
if (this._inflator.err) {
throw new Error(`Error while decompressing the data: ${this._inflator.err}`);
}
this.skip(length);
}
decodeFDAT(length) {
this._writingDataChunks = true;
let dataLength = length;
let dataOffset = this.offset + this.byteOffset;
dataOffset += 4;
dataLength -= 4;
this._inflator.push(new Uint8Array(this.buffer, dataOffset, dataLength));
if (this._inflator.err) {
throw new Error(`Error while decompressing the data: ${this._inflator.err}`);
}
this.skip(length);
}
// https://www.w3.org/TR/PNG/#11tRNS
decodetRNS(length) {
switch (this._colorType) {
case ColorType.GREYSCALE:
case ColorType.TRUECOLOUR: {
if (length % 2 !== 0) {
throw new RangeError(`tRNS chunk length must be a multiple of 2. Got ${length}`);
}
if (length / 2 > this._png.width * this._png.height) {
throw new Error(`tRNS chunk contains more alpha values than there are pixels (${length / 2} vs ${this._png.width * this._png.height})`);
}
this._hasTransparency = true;
this._transparency = new Uint16Array(length / 2);
for (let i = 0; i < length / 2; i++) {
this._transparency[i] = this.readUint16();
}
break;
}
case ColorType.INDEXED_COLOUR: {
if (length > this._palette.length) {
throw new Error(`tRNS chunk contains more alpha values than there are palette colors (${length} vs ${this._palette.length})`);
}
let i = 0;
for (; i < length; i++) {
const alpha = this.readByte();
this._palette[i].push(alpha);
}
for (; i < this._palette.length; i++) {
this._palette[i].push(255);
}
break;
}
// Kept for exhaustiveness.
/* eslint-disable unicorn/no-useless-switch-case */
case ColorType.UNKNOWN:
case ColorType.GREYSCALE_ALPHA:
case ColorType.TRUECOLOUR_ALPHA:
default: {
throw new Error(`tRNS chunk is not supported for color type ${this._colorType}`);
}
/* eslint-enable unicorn/no-useless-switch-case */
}
}
// https://www.w3.org/TR/PNG/#11iCCP
decodeiCCP(length) {
const name = readKeyword(this);
const compressionMethod = this.readUint8();
if (compressionMethod !== CompressionMethod.DEFLATE) {
throw new Error(`Unsupported iCCP compression method: ${compressionMethod}`);
}
const compressedProfile = this.readBytes(length - name.length - 2);
this._png.iccEmbeddedProfile = {
name,
profile: inflate(compressedProfile),
};
}
// https://www.w3.org/TR/PNG/#11pHYs
decodepHYs() {
const ppuX = this.readUint32();
const ppuY = this.readUint32();
const unitSpecifier = this.readByte();
this._png.resolution = { x: ppuX, y: ppuY, unit: unitSpecifier };
}
decodeApngImage() {
this._apng.width = this._png.width;
this._apng.height = this._png.height;
this._apng.channels = this._png.channels;
this._apng.depth = this._png.depth;
this._apng.numberOfFrames = this._numberOfFrames;
this._apng.numberOfPlays = this._numberOfPlays;
this._apng.text = this._png.text;
this._apng.resolution = this._png.resolution;
for (let i = 0; i < this._numberOfFrames; i++) {
const newFrame = {
sequenceNumber: this._frames[i].sequenceNumber,
delayNumber: this._frames[i].delayNumber,
delayDenominator: this._frames[i].delayDenominator,
data: this._apng.depth === 8
? new Uint8Array(this._apng.width * this._apng.height * this._apng.channels)
: new Uint16Array(this._apng.width * this._apng.height * this._apng.channels),
};
const frame = this._frames.at(i);
if (frame) {
frame.data = decodeInterlaceNull({
data: frame.data,
width: frame.width,
height: frame.height,
channels: this._apng.channels,
depth: this._apng.depth,
});
if (this._hasPalette) {
this._apng.palette = this._palette;
}
if (this._hasTransparency) {
this._apng.transparency = this._transparency;
}
if (i === 0 ||
(frame.xOffset === 0 &&
frame.yOffset === 0 &&
frame.width === this._png.width &&
frame.height === this._png.height)) {
newFrame.data = frame.data;
}
else {
const prevFrame = this._apng.frames.at(i - 1);
this.disposeFrame(frame, prevFrame, newFrame);
this.addFrameDataToCanvas(newFrame, frame);
}
this._apng.frames.push(newFrame);
}
}
return this._apng;
}
disposeFrame(frame, prevFrame, imageFrame) {
switch (frame.disposeOp) {
case DisposeOpType.NONE:
break;
case DisposeOpType.BACKGROUND:
for (let row = 0; row < this._png.height; row++) {
for (let col = 0; col < this._png.width; col++) {
const index = (row * frame.width + col) * this._png.channels;
for (let channel = 0; channel < this._png.channels; channel++) {
imageFrame.data[index + channel] = 0;
}
}
}
break;
case DisposeOpType.PREVIOUS:
imageFrame.data.set(prevFrame.data);
break;
default:
throw new Error('Unknown disposeOp');
}
}
addFrameDataToCanvas(imageFrame, frame) {
const maxValue = 1 << this._png.depth;
const calculatePixelIndices = (row, col) => {
const index = ((row + frame.yOffset) * this._png.width + frame.xOffset + col) *
this._png.channels;
const frameIndex = (row * frame.width + col) * this._png.channels;
return { index, frameIndex };
};
switch (frame.blendOp) {
case BlendOpType.SOURCE:
for (let row = 0; row < frame.height; row++) {
for (let col = 0; col < frame.width; col++) {
const { index, frameIndex } = calculatePixelIndices(row, col);
for (let channel = 0; channel < this._png.channels; channel++) {
imageFrame.data[index + channel] =
frame.data[frameIndex + channel];
}
}
}
break;
// https://www.w3.org/TR/png-3/#13Alpha-channel-processing
case BlendOpType.OVER:
for (let row = 0; row < frame.height; row++) {
for (let col = 0; col < frame.width; col++) {
const { index, frameIndex } = calculatePixelIndices(row, col);
for (let channel = 0; channel < this._png.channels; channel++) {
const sourceAlpha = frame.data[frameIndex + this._png.channels - 1] / maxValue;
const foregroundValue = channel % (this._png.channels - 1) === 0
? 1
: frame.data[frameIndex + channel];
const value = Math.floor(sourceAlpha * foregroundValue +
(1 - sourceAlpha) * imageFrame.data[index + channel]);
imageFrame.data[index + channel] += value;
}
}
}
break;
default:
throw new Error('Unknown blendOp');
}
}
decodeImage() {
if (this._inflator.err) {
throw new Error(`Error while decompressing the data: ${this._inflator.err}`);
}
const data = this._isAnimated
? (this._frames?.at(0)).data
: this._inflator.result;
if (this._filterMethod !== FilterMethod.ADAPTIVE) {
throw new Error(`Filter method ${this._filterMethod} not supported`);
}
if (this._interlaceMethod === InterlaceMethod.NO_INTERLACE) {
this._png.data = decodeInterlaceNull({
data: data,
width: this._png.width,
height: this._png.height,
channels: this._png.channels,
depth: this._png.depth,
});
}
else if (this._interlaceMethod === InterlaceMethod.ADAM7) {
this._png.data = decodeInterlaceAdam7({
data: data,
width: this._png.width,
height: this._png.height,
channels: this._png.channels,
depth: this._png.depth,
});
}
else {
throw new Error(`Interlace method ${this._interlaceMethod} not supported`);
}
if (this._hasPalette) {
this._png.palette = this._palette;
}
if (this._hasTransparency) {
this._png.transparency = this._transparency;
}
}
pushDataToFrame() {
const result = this._inflator.result;
const lastFrame = this._frames.at(-1);
if (lastFrame) {
lastFrame.data = result;
}
else {
this._frames.push({
sequenceNumber: 0,
width: this._png.width,
height: this._png.height,
xOffset: 0,
yOffset: 0,
delayNumber: 0,
delayDenominator: 0,
disposeOp: DisposeOpType.NONE,
blendOp: BlendOpType.SOURCE,
data: result,
});
}
this._inflator = new Inflator();
this._writingDataChunks = false;
}
}
function checkBitDepth(value) {
if (value !== 1 &&
value !== 2 &&
value !== 4 &&
value !== 8 &&
value !== 16) {
throw new Error(`invalid bit depth: ${value}`);
}
return value;
}
//# sourceMappingURL=PngDecoder.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,247 @@
import { IOBuffer } from 'iobuffer';
import { deflate } from 'pako';
import { writeCrc } from './helpers/crc';
import { writeSignature } from './helpers/signature';
import { encodetEXt } from './helpers/text';
import { InterlaceMethod, ColorType, CompressionMethod, FilterMethod, } from './internalTypes';
const defaultZlibOptions = {
level: 3,
};
export default class PngEncoder extends IOBuffer {
_png;
_zlibOptions;
_colorType;
_interlaceMethod;
constructor(data, options = {}) {
super();
this._colorType = ColorType.UNKNOWN;
this._zlibOptions = { ...defaultZlibOptions, ...options.zlib };
this._png = this._checkData(data);
this._interlaceMethod =
(options.interlace === 'Adam7'
? InterlaceMethod.ADAM7
: InterlaceMethod.NO_INTERLACE) ?? InterlaceMethod.NO_INTERLACE;
this.setBigEndian();
}
encode() {
writeSignature(this);
this.encodeIHDR();
if (this._png.palette) {
this.encodePLTE();
if (this._png.palette[0].length === 4) {
this.encodeTRNS();
}
}
this.encodeData();
if (this._png.text) {
for (const [keyword, text] of Object.entries(this._png.text)) {
encodetEXt(this, keyword, text);
}
}
this.encodeIEND();
return this.toArray();
}
// https://www.w3.org/TR/PNG/#11IHDR
encodeIHDR() {
this.writeUint32(13);
this.writeChars('IHDR');
this.writeUint32(this._png.width);
this.writeUint32(this._png.height);
this.writeByte(this._png.depth);
this.writeByte(this._colorType);
this.writeByte(CompressionMethod.DEFLATE);
this.writeByte(FilterMethod.ADAPTIVE);
this.writeByte(this._interlaceMethod);
writeCrc(this, 17);
}
// https://www.w3.org/TR/PNG/#11IEND
encodeIEND() {
this.writeUint32(0);
this.writeChars('IEND');
writeCrc(this, 4);
}
encodePLTE() {
const paletteLength = this._png.palette?.length * 3;
this.writeUint32(paletteLength);
this.writeChars('PLTE');
for (const color of this._png.palette) {
this.writeByte(color[0]);
this.writeByte(color[1]);
this.writeByte(color[2]);
}
writeCrc(this, 4 + paletteLength);
}
encodeTRNS() {
const alpha = this._png.palette.filter((color) => {
return color.at(-1) !== 255;
});
this.writeUint32(alpha.length);
this.writeChars('tRNS');
for (const el of alpha) {
this.writeByte(el.at(-1));
}
writeCrc(this, 4 + alpha.length);
}
// https://www.w3.org/TR/PNG/#11IDAT
encodeIDAT(data) {
this.writeUint32(data.length);
this.writeChars('IDAT');
this.writeBytes(data);
writeCrc(this, data.length + 4);
}
encodeData() {
const { width, height, channels, depth, data } = this._png;
const slotsPerLine = depth <= 8
? Math.ceil((width * depth) / 8) * channels
: Math.ceil((((width * depth) / 8) * channels) / 2);
const newData = new IOBuffer().setBigEndian();
let offset = 0;
if (this._interlaceMethod === InterlaceMethod.NO_INTERLACE) {
for (let i = 0; i < height; i++) {
newData.writeByte(0); // no filter
if (depth === 16) {
offset = writeDataUint16(data, newData, slotsPerLine, offset);
}
else {
offset = writeDataBytes(data, newData, slotsPerLine, offset);
}
}
}
else if (this._interlaceMethod === InterlaceMethod.ADAM7) {
// Adam7 interlacing
offset = writeDataInterlaced(this._png, data, newData, offset);
}
const buffer = newData.toArray();
const compressed = deflate(buffer, this._zlibOptions);
this.encodeIDAT(compressed);
}
_checkData(data) {
const { colorType, channels, depth } = getColorType(data, data.palette);
const png = {
width: checkInteger(data.width, 'width'),
height: checkInteger(data.height, 'height'),
channels,
data: data.data,
depth,
text: data.text,
palette: data.palette,
};
this._colorType = colorType;
const expectedSize = depth < 8
? Math.ceil((png.width * depth) / 8) * png.height * channels
: png.width * png.height * channels;
if (png.data.length !== expectedSize) {
throw new RangeError(`wrong data size. Found ${png.data.length}, expected ${expectedSize}`);
}
return png;
}
}
function checkInteger(value, name) {
if (Number.isInteger(value) && value > 0) {
return value;
}
throw new TypeError(`${name} must be a positive integer`);
}
function getColorType(data, palette) {
const { channels = 4, depth = 8 } = data;
if (channels !== 4 && channels !== 3 && channels !== 2 && channels !== 1) {
throw new RangeError(`unsupported number of channels: ${channels}`);
}
const returnValue = {
channels,
depth,
colorType: ColorType.UNKNOWN,
};
switch (channels) {
case 4:
returnValue.colorType = ColorType.TRUECOLOUR_ALPHA;
break;
case 3:
returnValue.colorType = ColorType.TRUECOLOUR;
break;
case 1:
if (palette) {
returnValue.colorType = ColorType.INDEXED_COLOUR;
}
else {
returnValue.colorType = ColorType.GREYSCALE;
}
break;
case 2:
returnValue.colorType = ColorType.GREYSCALE_ALPHA;
break;
default:
throw new Error('unsupported number of channels');
}
return returnValue;
}
function writeDataBytes(data, newData, slotsPerLine, offset) {
for (let j = 0; j < slotsPerLine; j++) {
newData.writeByte(data[offset++]);
}
return offset;
}
function writeDataInterlaced(imageData, data, newData, offset) {
const passes = [
{ x: 0, y: 0, xStep: 8, yStep: 8 },
{ x: 4, y: 0, xStep: 8, yStep: 8 },
{ x: 0, y: 4, xStep: 4, yStep: 8 },
{ x: 2, y: 0, xStep: 4, yStep: 4 },
{ x: 0, y: 2, xStep: 2, yStep: 4 },
{ x: 1, y: 0, xStep: 2, yStep: 2 },
{ x: 0, y: 1, xStep: 1, yStep: 2 },
];
const { width, height, channels, depth } = imageData;
let pixelSize = 0;
if (depth === 16) {
pixelSize = (channels * depth) / 8 / 2;
}
else {
pixelSize = (channels * depth) / 8;
}
// Process each pass
for (let passIndex = 0; passIndex < 7; passIndex++) {
const pass = passes[passIndex];
const passWidth = Math.floor((width - pass.x + pass.xStep - 1) / pass.xStep);
const passHeight = Math.floor((height - pass.y + pass.yStep - 1) / pass.yStep);
if (passWidth <= 0 || passHeight <= 0)
continue;
const passLineBytes = passWidth * pixelSize;
// For each scanline in this pass
for (let y = 0; y < passHeight; y++) {
const imageY = pass.y + y * pass.yStep;
// Extract raw scanline data
const rawScanline = depth <= 8
? new Uint8Array(passLineBytes)
: new Uint16Array(passLineBytes);
let rawOffset = 0;
for (let x = 0; x < passWidth; x++) {
const imageX = pass.x + x * pass.xStep;
if (imageX < width && imageY < height) {
const srcPos = (imageY * width + imageX) * pixelSize;
for (let i = 0; i < pixelSize; i++) {
rawScanline[rawOffset++] = data[srcPos + i];
}
}
}
newData.writeByte(0); // no filter
if (depth === 8) {
newData.writeBytes(rawScanline);
}
else if (depth === 16) {
for (const value of rawScanline) {
newData.writeByte((value >> 8) & 0xff); // High byte
newData.writeByte(value & 0xff);
}
}
}
}
return offset;
}
function writeDataUint16(data, newData, slotsPerLine, offset) {
for (let j = 0; j < slotsPerLine; j++) {
newData.writeUint16(data[offset++]);
}
return offset;
}
//# sourceMappingURL=PngEncoder.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,70 @@
/**
* Converts indexed data into RGB/RGBA format
* @param decodedImage - Image to decode data from.
* @returns Uint8Array with RGB data.
*/
export function convertIndexedToRgb(decodedImage) {
const palette = decodedImage.palette;
const depth = decodedImage.depth;
if (!palette) {
throw new Error('Color palette is undefined.');
}
checkDataSize(decodedImage);
const indexSize = decodedImage.width * decodedImage.height;
const resSize = indexSize * palette[0].length;
const res = new Uint8Array(resSize);
let indexPos = 0;
let offset = 0;
const indexes = new Uint8Array(indexSize);
let bit = 0xff;
switch (depth) {
case 1:
bit = 0x80;
break;
case 2:
bit = 0xc0;
break;
case 4:
bit = 0xf0;
break;
case 8:
bit = 0xff;
break;
default:
throw new Error('Incorrect depth value');
}
for (const byte of decodedImage.data) {
let bit2 = bit;
let shift = 8;
while (bit2) {
shift -= depth;
indexes[indexPos++] = (byte & bit2) >> shift;
bit2 = bit2 >> depth;
if (indexPos % decodedImage.width === 0) {
break;
}
}
}
if (decodedImage.palette) {
for (const index of indexes) {
const color = decodedImage.palette.at(index);
if (!color) {
throw new Error('Incorrect index of palette color');
}
res.set(color, offset);
offset += color.length;
}
}
return res;
}
function checkDataSize(image) {
const expectedSize = image.depth < 8
? Math.ceil((image.width * image.depth) / 8) *
image.height *
image.channels
: image.width * image.height * image.channels;
if (image.data.length !== expectedSize) {
throw new RangeError(`wrong data size. Found ${image.data.length}, expected ${expectedSize}`);
}
}
//# sourceMappingURL=convertIndexedToRgb.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"convertIndexedToRgb.js","sourceRoot":"","sources":["../src/convertIndexedToRgb.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,32 @@
import { unfilterAverage, unfilterNone, unfilterPaeth, unfilterSub, unfilterUp, } from './unfilter';
/**
* Apllies filter on scanline based on the filter type.
* @param filterType - The filter type to apply.
* @param currentLine - The current line of pixel data.
* @param newLine - The new line of pixel data.
* @param prevLine - The previous line of pixel data.
* @param passLineBytes - The number of bytes in the pass line.
* @param bytesPerPixel - The number of bytes per pixel.
*/
export function applyUnfilter(filterType, currentLine, newLine, prevLine, passLineBytes, bytesPerPixel) {
switch (filterType) {
case 0:
unfilterNone(currentLine, newLine, passLineBytes);
break;
case 1:
unfilterSub(currentLine, newLine, passLineBytes, bytesPerPixel);
break;
case 2:
unfilterUp(currentLine, newLine, prevLine, passLineBytes);
break;
case 3:
unfilterAverage(currentLine, newLine, prevLine, passLineBytes, bytesPerPixel);
break;
case 4:
unfilterPaeth(currentLine, newLine, prevLine, passLineBytes, bytesPerPixel);
break;
default:
throw new Error(`Unsupported filter: ${filterType}`);
}
}
//# sourceMappingURL=applyUnfilter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"applyUnfilter.js","sourceRoot":"","sources":["../../src/helpers/applyUnfilter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,aAAa,EACb,WAAW,EACX,UAAU,GACX,MAAM,YAAY,CAAC;AACpB;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAC3B,UAAkB,EAClB,WAAuB,EACvB,OAAmB,EACnB,QAAoB,EACpB,aAAqB,EACrB,aAAqB;IAErB,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,CAAC;YACJ,YAAY,CAAC,WAAW,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;YAClD,MAAM;QACR,KAAK,CAAC;YACJ,WAAW,CAAC,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;YAChE,MAAM;QACR,KAAK,CAAC;YACJ,UAAU,CAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;YAC1D,MAAM;QACR,KAAK,CAAC;YACJ,eAAe,CACb,WAAW,EACX,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,CACd,CAAC;YACF,MAAM;QACR,KAAK,CAAC;YACJ,aAAa,CACX,WAAW,EACX,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,CACd,CAAC;YACF,MAAM;QACR;YACE,MAAM,IAAI,KAAK,CAAC,uBAAuB,UAAU,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,35 @@
const crcTable = [];
for (let n = 0; n < 256; n++) {
let c = n;
for (let k = 0; k < 8; k++) {
if (c & 1) {
c = 0xedb88320 ^ (c >>> 1);
}
else {
c = c >>> 1;
}
}
crcTable[n] = c;
}
const initialCrc = 0xffffffff;
function updateCrc(currentCrc, data, length) {
let c = currentCrc;
for (let n = 0; n < length; n++) {
c = crcTable[(c ^ data[n]) & 0xff] ^ (c >>> 8);
}
return c;
}
function crc(data, length) {
return (updateCrc(initialCrc, data, length) ^ initialCrc) >>> 0;
}
export function checkCrc(buffer, crcLength, chunkName) {
const expectedCrc = buffer.readUint32();
const actualCrc = crc(new Uint8Array(buffer.buffer, buffer.byteOffset + buffer.offset - crcLength - 4, crcLength), crcLength); // "- 4" because we already advanced by reading the CRC
if (actualCrc !== expectedCrc) {
throw new Error(`CRC mismatch for chunk ${chunkName}. Expected ${expectedCrc}, found ${actualCrc}`);
}
}
export function writeCrc(buffer, length) {
buffer.writeUint32(crc(new Uint8Array(buffer.buffer, buffer.byteOffset + buffer.offset - length, length), length));
}
//# sourceMappingURL=crc.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"crc.js","sourceRoot":"","sources":["../../src/helpers/crc.ts"],"names":[],"mappings":"AAEA,MAAM,QAAQ,GAAa,EAAE,CAAC;AAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACV,CAAC,GAAG,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC;IACD,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,GAAG,UAAU,CAAC;AAC9B,SAAS,SAAS,CAChB,UAAkB,EAClB,IAAgB,EAChB,MAAc;IAEd,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,GAAG,CAAC,IAAgB,EAAE,MAAc;IAC3C,OAAO,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,QAAQ,CACtB,MAAgB,EAChB,SAAiB,EACjB,SAAiB;IAEjB,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IACxC,MAAM,SAAS,GAAG,GAAG,CACnB,IAAI,UAAU,CACZ,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,GAAG,SAAS,GAAG,CAAC,EACjD,SAAS,CACV,EACD,SAAS,CACV,CAAC,CAAC,uDAAuD;IAC1D,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,0BAA0B,SAAS,cAAc,WAAW,WAAW,SAAS,EAAE,CACnF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAgB,EAAE,MAAc;IACvD,MAAM,CAAC,WAAW,CAChB,GAAG,CACD,IAAI,UAAU,CACZ,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAC1C,MAAM,CACP,EACD,MAAM,CACP,CACF,CAAC;AACJ,CAAC"}

View File

@@ -0,0 +1,76 @@
import { applyUnfilter } from './applyUnfilter';
const uint16 = new Uint16Array([0x00ff]);
const uint8 = new Uint8Array(uint16.buffer);
const osIsLittleEndian = uint8[0] === 0xff;
/**
* Decodes the Adam7 interlaced PNG data.
*
* @param params - DecodeInterlaceNullParams
* @returns - array of pixel data.
*/
export function decodeInterlaceAdam7(params) {
const { data, width, height, channels, depth } = params;
// Adam7 interlacing pattern
const passes = [
{ x: 0, y: 0, xStep: 8, yStep: 8 }, // Pass 1
{ x: 4, y: 0, xStep: 8, yStep: 8 }, // Pass 2
{ x: 0, y: 4, xStep: 4, yStep: 8 }, // Pass 3
{ x: 2, y: 0, xStep: 4, yStep: 4 }, // Pass 4
{ x: 0, y: 2, xStep: 2, yStep: 4 }, // Pass 5
{ x: 1, y: 0, xStep: 2, yStep: 2 }, // Pass 6
{ x: 0, y: 1, xStep: 1, yStep: 2 }, // Pass 7
];
const bytesPerPixel = Math.ceil(depth / 8) * channels;
const resultData = new Uint8Array(height * width * bytesPerPixel);
let offset = 0;
// Process each pass
for (let passIndex = 0; passIndex < 7; passIndex++) {
const pass = passes[passIndex];
// Calculate pass dimensions
const passWidth = Math.ceil((width - pass.x) / pass.xStep);
const passHeight = Math.ceil((height - pass.y) / pass.yStep);
if (passWidth <= 0 || passHeight <= 0)
continue;
const passLineBytes = passWidth * bytesPerPixel;
const prevLine = new Uint8Array(passLineBytes);
// Process each scanline in this pass
for (let y = 0; y < passHeight; y++) {
// First byte is the filter type
const filterType = data[offset++];
const currentLine = data.subarray(offset, offset + passLineBytes);
offset += passLineBytes;
// Create a new line for the unfiltered data
const newLine = new Uint8Array(passLineBytes);
// Apply the appropriate unfilter
applyUnfilter(filterType, currentLine, newLine, prevLine, passLineBytes, bytesPerPixel);
prevLine.set(newLine);
for (let x = 0; x < passWidth; x++) {
const outputX = pass.x + x * pass.xStep;
const outputY = pass.y + y * pass.yStep;
if (outputX >= width || outputY >= height)
continue;
for (let i = 0; i < bytesPerPixel; i++) {
resultData[(outputY * width + outputX) * bytesPerPixel + i] =
newLine[x * bytesPerPixel + i];
}
}
}
}
if (depth === 16) {
const uint16Data = new Uint16Array(resultData.buffer);
if (osIsLittleEndian) {
for (let k = 0; k < uint16Data.length; k++) {
// PNG is always big endian. Swap the bytes.
uint16Data[k] = swap16(uint16Data[k]);
}
}
return uint16Data;
}
else {
return resultData;
}
}
function swap16(val) {
return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
}
//# sourceMappingURL=decodeInterlaceAdam7.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"decodeInterlaceAdam7.js","sourceRoot":"","sources":["../../src/helpers/decodeInterlaceAdam7.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,57 @@
import { unfilterAverage, unfilterNone, unfilterPaeth, unfilterSub, unfilterUp, } from './unfilter';
const uint16 = new Uint16Array([0x00ff]);
const uint8 = new Uint8Array(uint16.buffer);
const osIsLittleEndian = uint8[0] === 0xff;
const empty = new Uint8Array(0);
export function decodeInterlaceNull(params) {
const { data, width, height, channels, depth } = params;
const bytesPerPixel = Math.ceil(depth / 8) * channels;
const bytesPerLine = Math.ceil((depth / 8) * channels * width);
const newData = new Uint8Array(height * bytesPerLine);
let prevLine = empty;
let offset = 0;
let currentLine;
let newLine;
for (let i = 0; i < height; i++) {
currentLine = data.subarray(offset + 1, offset + 1 + bytesPerLine);
newLine = newData.subarray(i * bytesPerLine, (i + 1) * bytesPerLine);
switch (data[offset]) {
case 0:
unfilterNone(currentLine, newLine, bytesPerLine);
break;
case 1:
unfilterSub(currentLine, newLine, bytesPerLine, bytesPerPixel);
break;
case 2:
unfilterUp(currentLine, newLine, prevLine, bytesPerLine);
break;
case 3:
unfilterAverage(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel);
break;
case 4:
unfilterPaeth(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel);
break;
default:
throw new Error(`Unsupported filter: ${data[offset]}`);
}
prevLine = newLine;
offset += bytesPerLine + 1;
}
if (depth === 16) {
const uint16Data = new Uint16Array(newData.buffer);
if (osIsLittleEndian) {
for (let k = 0; k < uint16Data.length; k++) {
// PNG is always big endian. Swap the bytes.
uint16Data[k] = swap16(uint16Data[k]);
}
}
return uint16Data;
}
else {
return newData;
}
}
function swap16(val) {
return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
}
//# sourceMappingURL=decodeInterlaceNull.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"decodeInterlaceNull.js","sourceRoot":"","sources":["../../src/helpers/decodeInterlaceNull.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,22 @@
// https://www.w3.org/TR/PNG/#5PNG-file-signature
const pngSignature = Uint8Array.of(137, 80, 78, 71, 13, 10, 26, 10);
export function writeSignature(buffer) {
buffer.writeBytes(pngSignature);
}
export function checkSignature(buffer) {
if (!hasPngSignature(buffer.readBytes(pngSignature.length))) {
throw new Error('wrong PNG signature');
}
}
export function hasPngSignature(array) {
if (array.length < pngSignature.length) {
return false;
}
for (let i = 0; i < pngSignature.length; i++) {
if (array[i] !== pngSignature[i]) {
return false;
}
}
return true;
}
//# sourceMappingURL=signature.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"signature.js","sourceRoot":"","sources":["../../src/helpers/signature.ts"],"names":[],"mappings":"AAEA,iDAAiD;AAEjD,MAAM,YAAY,GAAG,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAEpE,MAAM,UAAU,cAAc,CAAC,MAAgB;IAC7C,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAgB;IAC7C,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAwB;IACtD,IAAI,KAAK,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;YACjC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}

View File

@@ -0,0 +1,51 @@
import { writeCrc } from './crc';
// https://www.w3.org/TR/png/#11tEXt
export const textChunkName = 'tEXt';
const NULL = 0;
const latin1Decoder = new TextDecoder('latin1');
function validateKeyword(keyword) {
validateLatin1(keyword);
if (keyword.length === 0 || keyword.length > 79) {
throw new Error('keyword length must be between 1 and 79');
}
}
// eslint-disable-next-line no-control-regex
const latin1Regex = /^[\u0000-\u00FF]*$/;
function validateLatin1(text) {
if (!latin1Regex.test(text)) {
throw new Error('invalid latin1 text');
}
}
export function decodetEXt(text, buffer, length) {
const keyword = readKeyword(buffer);
text[keyword] = readLatin1(buffer, length - keyword.length - 1);
}
export function encodetEXt(buffer, keyword, text) {
validateKeyword(keyword);
validateLatin1(text);
const length = keyword.length + 1 /* NULL */ + text.length;
buffer.writeUint32(length);
buffer.writeChars(textChunkName);
buffer.writeChars(keyword);
buffer.writeByte(NULL);
buffer.writeChars(text);
writeCrc(buffer, length + 4);
}
// https://www.w3.org/TR/png/#11keywords
export function readKeyword(buffer) {
buffer.mark();
while (buffer.readByte() !== NULL) {
/* advance */
}
const end = buffer.offset;
buffer.reset();
const keyword = latin1Decoder.decode(buffer.readBytes(end - buffer.offset - 1));
// NULL
buffer.skip(1);
validateKeyword(keyword);
return keyword;
}
export function readLatin1(buffer, length) {
return latin1Decoder.decode(buffer.readBytes(length));
}
//# sourceMappingURL=text.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/helpers/text.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,oCAAoC;AAEpC,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AAEpC,MAAM,IAAI,GAAG,CAAC,CAAC;AAEf,MAAM,aAAa,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEhD,SAAS,eAAe,CAAC,OAAe;IACtC,cAAc,CAAC,OAAO,CAAC,CAAC;IACxB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,4CAA4C;AAC5C,MAAM,WAAW,GAAG,oBAAoB,CAAC;AACzC,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,IAA4B,EAC5B,MAAgB,EAChB,MAAc;IAEd,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAgB,EAAE,OAAe,EAAE,IAAY;IACxE,eAAe,CAAC,OAAO,CAAC,CAAC;IACzB,cAAc,CAAC,IAAI,CAAC,CAAC;IACrB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC;IAE3D,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACjC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC3B,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,wCAAwC;AACxC,MAAM,UAAU,WAAW,CAAC,MAAgB;IAC1C,MAAM,CAAC,IAAI,EAAE,CAAC;IACd,OAAO,MAAM,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC;QAClC,aAAa;IACf,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1B,MAAM,CAAC,KAAK,EAAE,CAAC;IACf,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAClC,MAAM,CAAC,SAAS,CAAC,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAC1C,CAAC;IACF,OAAO;IACP,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEf,eAAe,CAAC,OAAO,CAAC,CAAC;IAEzB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAgB,EAAE,MAAc;IACzD,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AACxD,CAAC"}

View File

@@ -0,0 +1,85 @@
export function unfilterNone(currentLine, newLine, bytesPerLine) {
for (let i = 0; i < bytesPerLine; i++) {
newLine[i] = currentLine[i];
}
}
export function unfilterSub(currentLine, newLine, bytesPerLine, bytesPerPixel) {
let i = 0;
for (; i < bytesPerPixel; i++) {
// just copy first bytes
newLine[i] = currentLine[i];
}
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + newLine[i - bytesPerPixel]) & 0xff;
}
}
export function unfilterUp(currentLine, newLine, prevLine, bytesPerLine) {
let i = 0;
if (prevLine.length === 0) {
// just copy bytes for first line
for (; i < bytesPerLine; i++) {
newLine[i] = currentLine[i];
}
}
else {
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + prevLine[i]) & 0xff;
}
}
}
export function unfilterAverage(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel) {
let i = 0;
if (prevLine.length === 0) {
for (; i < bytesPerPixel; i++) {
newLine[i] = currentLine[i];
}
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + (newLine[i - bytesPerPixel] >> 1)) & 0xff;
}
}
else {
for (; i < bytesPerPixel; i++) {
newLine[i] = (currentLine[i] + (prevLine[i] >> 1)) & 0xff;
}
for (; i < bytesPerLine; i++) {
newLine[i] =
(currentLine[i] + ((newLine[i - bytesPerPixel] + prevLine[i]) >> 1)) &
0xff;
}
}
}
export function unfilterPaeth(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel) {
let i = 0;
if (prevLine.length === 0) {
for (; i < bytesPerPixel; i++) {
newLine[i] = currentLine[i];
}
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + newLine[i - bytesPerPixel]) & 0xff;
}
}
else {
for (; i < bytesPerPixel; i++) {
newLine[i] = (currentLine[i] + prevLine[i]) & 0xff;
}
for (; i < bytesPerLine; i++) {
newLine[i] =
(currentLine[i] +
paethPredictor(newLine[i - bytesPerPixel], prevLine[i], prevLine[i - bytesPerPixel])) &
0xff;
}
}
}
function paethPredictor(a, b, c) {
const p = a + b - c;
const pa = Math.abs(p - a);
const pb = Math.abs(p - b);
const pc = Math.abs(p - c);
if (pa <= pb && pa <= pc)
return a;
else if (pb <= pc)
return b;
else
return c;
}
//# sourceMappingURL=unfilter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"unfilter.js","sourceRoot":"","sources":["../../src/helpers/unfilter.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,19 @@
import PngDecoder from './PngDecoder';
import PngEncoder from './PngEncoder';
export { hasPngSignature } from './helpers/signature';
export * from './types';
function decodePng(data, options) {
const decoder = new PngDecoder(data, options);
return decoder.decode();
}
function encodePng(png, options) {
const encoder = new PngEncoder(png, options);
return encoder.encode();
}
function decodeApng(data, options) {
const decoder = new PngDecoder(data, options);
return decoder.decodeApng();
}
export { decodePng as decode, encodePng as encode, decodeApng };
export { convertIndexedToRgb } from './convertIndexedToRgb';
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,cAAc,CAAC;AACtC,OAAO,UAAU,MAAM,cAAc,CAAC;AAUtC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,cAAc,SAAS,CAAC;AAExB,SAAS,SAAS,CAChB,IAAsB,EACtB,OAA2B;IAE3B,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,SAAS,CAAC,GAAc,EAAE,OAA2B;IAC5D,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC7C,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,UAAU,CACjB,IAAsB,EACtB,OAA2B;IAE3B,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,OAAO,CAAC,UAAU,EAAE,CAAC;AAC9B,CAAC;AAED,OAAO,EAAE,SAAS,IAAI,MAAM,EAAE,SAAS,IAAI,MAAM,EAAE,UAAU,EAAE,CAAC;AAEhE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC"}

View File

@@ -0,0 +1,31 @@
export const ColorType = {
UNKNOWN: -1,
GREYSCALE: 0,
TRUECOLOUR: 2,
INDEXED_COLOUR: 3,
GREYSCALE_ALPHA: 4,
TRUECOLOUR_ALPHA: 6,
};
export const CompressionMethod = {
UNKNOWN: -1,
DEFLATE: 0,
};
export const FilterMethod = {
UNKNOWN: -1,
ADAPTIVE: 0,
};
export const InterlaceMethod = {
UNKNOWN: -1,
NO_INTERLACE: 0,
ADAM7: 1,
};
export const DisposeOpType = {
NONE: 0,
BACKGROUND: 1,
PREVIOUS: 2,
};
export const BlendOpType = {
SOURCE: 0,
OVER: 1,
};
//# sourceMappingURL=internalTypes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"internalTypes.js","sourceRoot":"","sources":["../src/internalTypes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,CAAC;IACZ,UAAU,EAAE,CAAC;IACb,cAAc,EAAE,CAAC;IACjB,eAAe,EAAE,CAAC;IAClB,gBAAgB,EAAE,CAAC;CACX,CAAC;AAIX,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,CAAC;CACF,CAAC;AAKX,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,CAAC;CACH,CAAC;AAGX,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,YAAY,EAAE,CAAC;IACf,KAAK,EAAE,CAAC;CACA,CAAC;AAEX,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,CAAC;IACP,UAAU,EAAE,CAAC;IACb,QAAQ,EAAE,CAAC;CACH,CAAC;AAIX,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,CAAC;CACC,CAAC"}

View File

@@ -0,0 +1,12 @@
export var ResolutionUnitSpecifier;
(function (ResolutionUnitSpecifier) {
/**
* Unit is unknown
*/
ResolutionUnitSpecifier[ResolutionUnitSpecifier["UNKNOWN"] = 0] = "UNKNOWN";
/**
* Unit is the metre
*/
ResolutionUnitSpecifier[ResolutionUnitSpecifier["METRE"] = 1] = "METRE";
})(ResolutionUnitSpecifier || (ResolutionUnitSpecifier = {}));
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA4BA,MAAM,CAAN,IAAY,uBASX;AATD,WAAY,uBAAuB;IACjC;;OAEG;IACH,2EAAW,CAAA;IACX;;OAEG;IACH,uEAAS,CAAA;AACX,CAAC,EATW,uBAAuB,KAAvB,uBAAuB,QASlC"}

View File

@@ -0,0 +1,41 @@
import { IOBuffer } from 'iobuffer';
import type { DecodedPng, DecodedApng, DecoderInputType, PngDecoderOptions } from './types';
export default class PngDecoder extends IOBuffer {
private readonly _checkCrc;
private _inflator;
private readonly _png;
private readonly _apng;
private _end;
private _hasPalette;
private _palette;
private _hasTransparency;
private _transparency;
private _compressionMethod;
private _filterMethod;
private _interlaceMethod;
private _colorType;
private _isAnimated;
private _numberOfFrames;
private _numberOfPlays;
private _frames;
private _writingDataChunks;
constructor(data: DecoderInputType, options?: PngDecoderOptions);
decode(): DecodedPng;
decodeApng(): DecodedApng;
private decodeChunk;
private decodeApngChunk;
private decodeIHDR;
private decodeACTL;
private decodeFCTL;
private decodePLTE;
private decodeIDAT;
private decodeFDAT;
private decodetRNS;
private decodeiCCP;
private decodepHYs;
private decodeApngImage;
private disposeFrame;
private addFrameDataToCanvas;
private decodeImage;
private pushDataToFrame;
}

View File

@@ -0,0 +1,508 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const iobuffer_1 = require("iobuffer");
const pako_1 = require("pako");
const crc_1 = require("./helpers/crc");
const decodeInterlaceAdam7_1 = require("./helpers/decodeInterlaceAdam7");
const decodeInterlaceNull_1 = require("./helpers/decodeInterlaceNull");
const signature_1 = require("./helpers/signature");
const text_1 = require("./helpers/text");
const internalTypes_1 = require("./internalTypes");
class PngDecoder extends iobuffer_1.IOBuffer {
_checkCrc;
_inflator;
_png;
_apng;
_end;
_hasPalette;
_palette;
_hasTransparency;
_transparency;
_compressionMethod;
_filterMethod;
_interlaceMethod;
_colorType;
_isAnimated;
_numberOfFrames;
_numberOfPlays;
_frames;
_writingDataChunks;
constructor(data, options = {}) {
super(data);
const { checkCrc = false } = options;
this._checkCrc = checkCrc;
this._inflator = new pako_1.Inflate();
this._png = {
width: -1,
height: -1,
channels: -1,
data: new Uint8Array(0),
depth: 1,
text: {},
};
this._apng = {
width: -1,
height: -1,
channels: -1,
depth: 1,
numberOfFrames: 1,
numberOfPlays: 0,
text: {},
frames: [],
};
this._end = false;
this._hasPalette = false;
this._palette = [];
this._hasTransparency = false;
this._transparency = new Uint16Array(0);
this._compressionMethod = internalTypes_1.CompressionMethod.UNKNOWN;
this._filterMethod = internalTypes_1.FilterMethod.UNKNOWN;
this._interlaceMethod = internalTypes_1.InterlaceMethod.UNKNOWN;
this._colorType = internalTypes_1.ColorType.UNKNOWN;
this._isAnimated = false;
this._numberOfFrames = 1;
this._numberOfPlays = 0;
this._frames = [];
this._writingDataChunks = false;
// PNG is always big endian
// https://www.w3.org/TR/PNG/#7Integers-and-byte-order
this.setBigEndian();
}
decode() {
(0, signature_1.checkSignature)(this);
while (!this._end) {
const length = this.readUint32();
const type = this.readChars(4);
this.decodeChunk(length, type);
}
this.decodeImage();
return this._png;
}
decodeApng() {
(0, signature_1.checkSignature)(this);
while (!this._end) {
const length = this.readUint32();
const type = this.readChars(4);
this.decodeApngChunk(length, type);
}
this.decodeApngImage();
return this._apng;
}
// https://www.w3.org/TR/PNG/#5Chunk-layout
decodeChunk(length, type) {
const offset = this.offset;
switch (type) {
// 11.2 Critical chunks
case 'IHDR': // 11.2.2 IHDR Image header
this.decodeIHDR();
break;
case 'PLTE': // 11.2.3 PLTE Palette
this.decodePLTE(length);
break;
case 'IDAT': // 11.2.4 IDAT Image data
this.decodeIDAT(length);
break;
case 'IEND': // 11.2.5 IEND Image trailer
this._end = true;
break;
// 11.3 Ancillary chunks
case 'tRNS': // 11.3.2.1 tRNS Transparency
this.decodetRNS(length);
break;
case 'iCCP': // 11.3.3.3 iCCP Embedded ICC profile
this.decodeiCCP(length);
break;
case text_1.textChunkName: // 11.3.4.3 tEXt Textual data
(0, text_1.decodetEXt)(this._png.text, this, length);
break;
case 'pHYs': // 11.3.5.3 pHYs Physical pixel dimensions
this.decodepHYs();
break;
default:
this.skip(length);
break;
}
if (this.offset - offset !== length) {
throw new Error(`Length mismatch while decoding chunk ${type}`);
}
if (this._checkCrc) {
(0, crc_1.checkCrc)(this, length + 4, type);
}
else {
this.skip(4);
}
}
decodeApngChunk(length, type) {
const offset = this.offset;
if (type !== 'fdAT' && type !== 'IDAT' && this._writingDataChunks) {
this.pushDataToFrame();
}
switch (type) {
case 'acTL':
this.decodeACTL();
break;
case 'fcTL':
this.decodeFCTL();
break;
case 'fdAT':
this.decodeFDAT(length);
break;
default:
this.decodeChunk(length, type);
this.offset = offset + length;
break;
}
if (this.offset - offset !== length) {
throw new Error(`Length mismatch while decoding chunk ${type}`);
}
if (this._checkCrc) {
(0, crc_1.checkCrc)(this, length + 4, type);
}
else {
this.skip(4);
}
}
// https://www.w3.org/TR/PNG/#11IHDR
decodeIHDR() {
const image = this._png;
image.width = this.readUint32();
image.height = this.readUint32();
image.depth = checkBitDepth(this.readUint8());
const colorType = this.readUint8();
this._colorType = colorType;
let channels;
switch (colorType) {
case internalTypes_1.ColorType.GREYSCALE:
channels = 1;
break;
case internalTypes_1.ColorType.TRUECOLOUR:
channels = 3;
break;
case internalTypes_1.ColorType.INDEXED_COLOUR:
channels = 1;
break;
case internalTypes_1.ColorType.GREYSCALE_ALPHA:
channels = 2;
break;
case internalTypes_1.ColorType.TRUECOLOUR_ALPHA:
channels = 4;
break;
// Kept for exhaustiveness.
// eslint-disable-next-line unicorn/no-useless-switch-case
case internalTypes_1.ColorType.UNKNOWN:
default:
throw new Error(`Unknown color type: ${colorType}`);
}
this._png.channels = channels;
this._compressionMethod = this.readUint8();
if (this._compressionMethod !== internalTypes_1.CompressionMethod.DEFLATE) {
throw new Error(`Unsupported compression method: ${this._compressionMethod}`);
}
this._filterMethod = this.readUint8();
this._interlaceMethod = this.readUint8();
}
decodeACTL() {
this._numberOfFrames = this.readUint32();
this._numberOfPlays = this.readUint32();
this._isAnimated = true;
}
decodeFCTL() {
const image = {
sequenceNumber: this.readUint32(),
width: this.readUint32(),
height: this.readUint32(),
xOffset: this.readUint32(),
yOffset: this.readUint32(),
delayNumber: this.readUint16(),
delayDenominator: this.readUint16(),
disposeOp: this.readUint8(),
blendOp: this.readUint8(),
data: new Uint8Array(0),
};
this._frames.push(image);
}
// https://www.w3.org/TR/PNG/#11PLTE
decodePLTE(length) {
if (length % 3 !== 0) {
throw new RangeError(`PLTE field length must be a multiple of 3. Got ${length}`);
}
const l = length / 3;
this._hasPalette = true;
const palette = [];
this._palette = palette;
for (let i = 0; i < l; i++) {
palette.push([this.readUint8(), this.readUint8(), this.readUint8()]);
}
}
// https://www.w3.org/TR/PNG/#11IDAT
decodeIDAT(length) {
this._writingDataChunks = true;
const dataLength = length;
const dataOffset = this.offset + this.byteOffset;
this._inflator.push(new Uint8Array(this.buffer, dataOffset, dataLength));
if (this._inflator.err) {
throw new Error(`Error while decompressing the data: ${this._inflator.err}`);
}
this.skip(length);
}
decodeFDAT(length) {
this._writingDataChunks = true;
let dataLength = length;
let dataOffset = this.offset + this.byteOffset;
dataOffset += 4;
dataLength -= 4;
this._inflator.push(new Uint8Array(this.buffer, dataOffset, dataLength));
if (this._inflator.err) {
throw new Error(`Error while decompressing the data: ${this._inflator.err}`);
}
this.skip(length);
}
// https://www.w3.org/TR/PNG/#11tRNS
decodetRNS(length) {
switch (this._colorType) {
case internalTypes_1.ColorType.GREYSCALE:
case internalTypes_1.ColorType.TRUECOLOUR: {
if (length % 2 !== 0) {
throw new RangeError(`tRNS chunk length must be a multiple of 2. Got ${length}`);
}
if (length / 2 > this._png.width * this._png.height) {
throw new Error(`tRNS chunk contains more alpha values than there are pixels (${length / 2} vs ${this._png.width * this._png.height})`);
}
this._hasTransparency = true;
this._transparency = new Uint16Array(length / 2);
for (let i = 0; i < length / 2; i++) {
this._transparency[i] = this.readUint16();
}
break;
}
case internalTypes_1.ColorType.INDEXED_COLOUR: {
if (length > this._palette.length) {
throw new Error(`tRNS chunk contains more alpha values than there are palette colors (${length} vs ${this._palette.length})`);
}
let i = 0;
for (; i < length; i++) {
const alpha = this.readByte();
this._palette[i].push(alpha);
}
for (; i < this._palette.length; i++) {
this._palette[i].push(255);
}
break;
}
// Kept for exhaustiveness.
/* eslint-disable unicorn/no-useless-switch-case */
case internalTypes_1.ColorType.UNKNOWN:
case internalTypes_1.ColorType.GREYSCALE_ALPHA:
case internalTypes_1.ColorType.TRUECOLOUR_ALPHA:
default: {
throw new Error(`tRNS chunk is not supported for color type ${this._colorType}`);
}
/* eslint-enable unicorn/no-useless-switch-case */
}
}
// https://www.w3.org/TR/PNG/#11iCCP
decodeiCCP(length) {
const name = (0, text_1.readKeyword)(this);
const compressionMethod = this.readUint8();
if (compressionMethod !== internalTypes_1.CompressionMethod.DEFLATE) {
throw new Error(`Unsupported iCCP compression method: ${compressionMethod}`);
}
const compressedProfile = this.readBytes(length - name.length - 2);
this._png.iccEmbeddedProfile = {
name,
profile: (0, pako_1.inflate)(compressedProfile),
};
}
// https://www.w3.org/TR/PNG/#11pHYs
decodepHYs() {
const ppuX = this.readUint32();
const ppuY = this.readUint32();
const unitSpecifier = this.readByte();
this._png.resolution = { x: ppuX, y: ppuY, unit: unitSpecifier };
}
decodeApngImage() {
this._apng.width = this._png.width;
this._apng.height = this._png.height;
this._apng.channels = this._png.channels;
this._apng.depth = this._png.depth;
this._apng.numberOfFrames = this._numberOfFrames;
this._apng.numberOfPlays = this._numberOfPlays;
this._apng.text = this._png.text;
this._apng.resolution = this._png.resolution;
for (let i = 0; i < this._numberOfFrames; i++) {
const newFrame = {
sequenceNumber: this._frames[i].sequenceNumber,
delayNumber: this._frames[i].delayNumber,
delayDenominator: this._frames[i].delayDenominator,
data: this._apng.depth === 8
? new Uint8Array(this._apng.width * this._apng.height * this._apng.channels)
: new Uint16Array(this._apng.width * this._apng.height * this._apng.channels),
};
const frame = this._frames.at(i);
if (frame) {
frame.data = (0, decodeInterlaceNull_1.decodeInterlaceNull)({
data: frame.data,
width: frame.width,
height: frame.height,
channels: this._apng.channels,
depth: this._apng.depth,
});
if (this._hasPalette) {
this._apng.palette = this._palette;
}
if (this._hasTransparency) {
this._apng.transparency = this._transparency;
}
if (i === 0 ||
(frame.xOffset === 0 &&
frame.yOffset === 0 &&
frame.width === this._png.width &&
frame.height === this._png.height)) {
newFrame.data = frame.data;
}
else {
const prevFrame = this._apng.frames.at(i - 1);
this.disposeFrame(frame, prevFrame, newFrame);
this.addFrameDataToCanvas(newFrame, frame);
}
this._apng.frames.push(newFrame);
}
}
return this._apng;
}
disposeFrame(frame, prevFrame, imageFrame) {
switch (frame.disposeOp) {
case internalTypes_1.DisposeOpType.NONE:
break;
case internalTypes_1.DisposeOpType.BACKGROUND:
for (let row = 0; row < this._png.height; row++) {
for (let col = 0; col < this._png.width; col++) {
const index = (row * frame.width + col) * this._png.channels;
for (let channel = 0; channel < this._png.channels; channel++) {
imageFrame.data[index + channel] = 0;
}
}
}
break;
case internalTypes_1.DisposeOpType.PREVIOUS:
imageFrame.data.set(prevFrame.data);
break;
default:
throw new Error('Unknown disposeOp');
}
}
addFrameDataToCanvas(imageFrame, frame) {
const maxValue = 1 << this._png.depth;
const calculatePixelIndices = (row, col) => {
const index = ((row + frame.yOffset) * this._png.width + frame.xOffset + col) *
this._png.channels;
const frameIndex = (row * frame.width + col) * this._png.channels;
return { index, frameIndex };
};
switch (frame.blendOp) {
case internalTypes_1.BlendOpType.SOURCE:
for (let row = 0; row < frame.height; row++) {
for (let col = 0; col < frame.width; col++) {
const { index, frameIndex } = calculatePixelIndices(row, col);
for (let channel = 0; channel < this._png.channels; channel++) {
imageFrame.data[index + channel] =
frame.data[frameIndex + channel];
}
}
}
break;
// https://www.w3.org/TR/png-3/#13Alpha-channel-processing
case internalTypes_1.BlendOpType.OVER:
for (let row = 0; row < frame.height; row++) {
for (let col = 0; col < frame.width; col++) {
const { index, frameIndex } = calculatePixelIndices(row, col);
for (let channel = 0; channel < this._png.channels; channel++) {
const sourceAlpha = frame.data[frameIndex + this._png.channels - 1] / maxValue;
const foregroundValue = channel % (this._png.channels - 1) === 0
? 1
: frame.data[frameIndex + channel];
const value = Math.floor(sourceAlpha * foregroundValue +
(1 - sourceAlpha) * imageFrame.data[index + channel]);
imageFrame.data[index + channel] += value;
}
}
}
break;
default:
throw new Error('Unknown blendOp');
}
}
decodeImage() {
if (this._inflator.err) {
throw new Error(`Error while decompressing the data: ${this._inflator.err}`);
}
const data = this._isAnimated
? (this._frames?.at(0)).data
: this._inflator.result;
if (this._filterMethod !== internalTypes_1.FilterMethod.ADAPTIVE) {
throw new Error(`Filter method ${this._filterMethod} not supported`);
}
if (this._interlaceMethod === internalTypes_1.InterlaceMethod.NO_INTERLACE) {
this._png.data = (0, decodeInterlaceNull_1.decodeInterlaceNull)({
data: data,
width: this._png.width,
height: this._png.height,
channels: this._png.channels,
depth: this._png.depth,
});
}
else if (this._interlaceMethod === internalTypes_1.InterlaceMethod.ADAM7) {
this._png.data = (0, decodeInterlaceAdam7_1.decodeInterlaceAdam7)({
data: data,
width: this._png.width,
height: this._png.height,
channels: this._png.channels,
depth: this._png.depth,
});
}
else {
throw new Error(`Interlace method ${this._interlaceMethod} not supported`);
}
if (this._hasPalette) {
this._png.palette = this._palette;
}
if (this._hasTransparency) {
this._png.transparency = this._transparency;
}
}
pushDataToFrame() {
const result = this._inflator.result;
const lastFrame = this._frames.at(-1);
if (lastFrame) {
lastFrame.data = result;
}
else {
this._frames.push({
sequenceNumber: 0,
width: this._png.width,
height: this._png.height,
xOffset: 0,
yOffset: 0,
delayNumber: 0,
delayDenominator: 0,
disposeOp: internalTypes_1.DisposeOpType.NONE,
blendOp: internalTypes_1.BlendOpType.SOURCE,
data: result,
});
}
this._inflator = new pako_1.Inflate();
this._writingDataChunks = false;
}
}
exports.default = PngDecoder;
function checkBitDepth(value) {
if (value !== 1 &&
value !== 2 &&
value !== 4 &&
value !== 8 &&
value !== 16) {
throw new Error(`invalid bit depth: ${value}`);
}
return value;
}
//# sourceMappingURL=PngDecoder.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,17 @@
import { IOBuffer } from 'iobuffer';
import type { PngEncoderOptions, ImageData } from './types';
export default class PngEncoder extends IOBuffer {
private readonly _png;
private readonly _zlibOptions;
private _colorType;
private readonly _interlaceMethod;
constructor(data: ImageData, options?: PngEncoderOptions);
encode(): Uint8Array;
private encodeIHDR;
private encodeIEND;
private encodePLTE;
private encodeTRNS;
private encodeIDAT;
private encodeData;
private _checkData;
}

View File

@@ -0,0 +1,250 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const iobuffer_1 = require("iobuffer");
const pako_1 = require("pako");
const crc_1 = require("./helpers/crc");
const signature_1 = require("./helpers/signature");
const text_1 = require("./helpers/text");
const internalTypes_1 = require("./internalTypes");
const defaultZlibOptions = {
level: 3,
};
class PngEncoder extends iobuffer_1.IOBuffer {
_png;
_zlibOptions;
_colorType;
_interlaceMethod;
constructor(data, options = {}) {
super();
this._colorType = internalTypes_1.ColorType.UNKNOWN;
this._zlibOptions = { ...defaultZlibOptions, ...options.zlib };
this._png = this._checkData(data);
this._interlaceMethod =
(options.interlace === 'Adam7'
? internalTypes_1.InterlaceMethod.ADAM7
: internalTypes_1.InterlaceMethod.NO_INTERLACE) ?? internalTypes_1.InterlaceMethod.NO_INTERLACE;
this.setBigEndian();
}
encode() {
(0, signature_1.writeSignature)(this);
this.encodeIHDR();
if (this._png.palette) {
this.encodePLTE();
if (this._png.palette[0].length === 4) {
this.encodeTRNS();
}
}
this.encodeData();
if (this._png.text) {
for (const [keyword, text] of Object.entries(this._png.text)) {
(0, text_1.encodetEXt)(this, keyword, text);
}
}
this.encodeIEND();
return this.toArray();
}
// https://www.w3.org/TR/PNG/#11IHDR
encodeIHDR() {
this.writeUint32(13);
this.writeChars('IHDR');
this.writeUint32(this._png.width);
this.writeUint32(this._png.height);
this.writeByte(this._png.depth);
this.writeByte(this._colorType);
this.writeByte(internalTypes_1.CompressionMethod.DEFLATE);
this.writeByte(internalTypes_1.FilterMethod.ADAPTIVE);
this.writeByte(this._interlaceMethod);
(0, crc_1.writeCrc)(this, 17);
}
// https://www.w3.org/TR/PNG/#11IEND
encodeIEND() {
this.writeUint32(0);
this.writeChars('IEND');
(0, crc_1.writeCrc)(this, 4);
}
encodePLTE() {
const paletteLength = this._png.palette?.length * 3;
this.writeUint32(paletteLength);
this.writeChars('PLTE');
for (const color of this._png.palette) {
this.writeByte(color[0]);
this.writeByte(color[1]);
this.writeByte(color[2]);
}
(0, crc_1.writeCrc)(this, 4 + paletteLength);
}
encodeTRNS() {
const alpha = this._png.palette.filter((color) => {
return color.at(-1) !== 255;
});
this.writeUint32(alpha.length);
this.writeChars('tRNS');
for (const el of alpha) {
this.writeByte(el.at(-1));
}
(0, crc_1.writeCrc)(this, 4 + alpha.length);
}
// https://www.w3.org/TR/PNG/#11IDAT
encodeIDAT(data) {
this.writeUint32(data.length);
this.writeChars('IDAT');
this.writeBytes(data);
(0, crc_1.writeCrc)(this, data.length + 4);
}
encodeData() {
const { width, height, channels, depth, data } = this._png;
const slotsPerLine = depth <= 8
? Math.ceil((width * depth) / 8) * channels
: Math.ceil((((width * depth) / 8) * channels) / 2);
const newData = new iobuffer_1.IOBuffer().setBigEndian();
let offset = 0;
if (this._interlaceMethod === internalTypes_1.InterlaceMethod.NO_INTERLACE) {
for (let i = 0; i < height; i++) {
newData.writeByte(0); // no filter
if (depth === 16) {
offset = writeDataUint16(data, newData, slotsPerLine, offset);
}
else {
offset = writeDataBytes(data, newData, slotsPerLine, offset);
}
}
}
else if (this._interlaceMethod === internalTypes_1.InterlaceMethod.ADAM7) {
// Adam7 interlacing
offset = writeDataInterlaced(this._png, data, newData, offset);
}
const buffer = newData.toArray();
const compressed = (0, pako_1.deflate)(buffer, this._zlibOptions);
this.encodeIDAT(compressed);
}
_checkData(data) {
const { colorType, channels, depth } = getColorType(data, data.palette);
const png = {
width: checkInteger(data.width, 'width'),
height: checkInteger(data.height, 'height'),
channels,
data: data.data,
depth,
text: data.text,
palette: data.palette,
};
this._colorType = colorType;
const expectedSize = depth < 8
? Math.ceil((png.width * depth) / 8) * png.height * channels
: png.width * png.height * channels;
if (png.data.length !== expectedSize) {
throw new RangeError(`wrong data size. Found ${png.data.length}, expected ${expectedSize}`);
}
return png;
}
}
exports.default = PngEncoder;
function checkInteger(value, name) {
if (Number.isInteger(value) && value > 0) {
return value;
}
throw new TypeError(`${name} must be a positive integer`);
}
function getColorType(data, palette) {
const { channels = 4, depth = 8 } = data;
if (channels !== 4 && channels !== 3 && channels !== 2 && channels !== 1) {
throw new RangeError(`unsupported number of channels: ${channels}`);
}
const returnValue = {
channels,
depth,
colorType: internalTypes_1.ColorType.UNKNOWN,
};
switch (channels) {
case 4:
returnValue.colorType = internalTypes_1.ColorType.TRUECOLOUR_ALPHA;
break;
case 3:
returnValue.colorType = internalTypes_1.ColorType.TRUECOLOUR;
break;
case 1:
if (palette) {
returnValue.colorType = internalTypes_1.ColorType.INDEXED_COLOUR;
}
else {
returnValue.colorType = internalTypes_1.ColorType.GREYSCALE;
}
break;
case 2:
returnValue.colorType = internalTypes_1.ColorType.GREYSCALE_ALPHA;
break;
default:
throw new Error('unsupported number of channels');
}
return returnValue;
}
function writeDataBytes(data, newData, slotsPerLine, offset) {
for (let j = 0; j < slotsPerLine; j++) {
newData.writeByte(data[offset++]);
}
return offset;
}
function writeDataInterlaced(imageData, data, newData, offset) {
const passes = [
{ x: 0, y: 0, xStep: 8, yStep: 8 },
{ x: 4, y: 0, xStep: 8, yStep: 8 },
{ x: 0, y: 4, xStep: 4, yStep: 8 },
{ x: 2, y: 0, xStep: 4, yStep: 4 },
{ x: 0, y: 2, xStep: 2, yStep: 4 },
{ x: 1, y: 0, xStep: 2, yStep: 2 },
{ x: 0, y: 1, xStep: 1, yStep: 2 },
];
const { width, height, channels, depth } = imageData;
let pixelSize = 0;
if (depth === 16) {
pixelSize = (channels * depth) / 8 / 2;
}
else {
pixelSize = (channels * depth) / 8;
}
// Process each pass
for (let passIndex = 0; passIndex < 7; passIndex++) {
const pass = passes[passIndex];
const passWidth = Math.floor((width - pass.x + pass.xStep - 1) / pass.xStep);
const passHeight = Math.floor((height - pass.y + pass.yStep - 1) / pass.yStep);
if (passWidth <= 0 || passHeight <= 0)
continue;
const passLineBytes = passWidth * pixelSize;
// For each scanline in this pass
for (let y = 0; y < passHeight; y++) {
const imageY = pass.y + y * pass.yStep;
// Extract raw scanline data
const rawScanline = depth <= 8
? new Uint8Array(passLineBytes)
: new Uint16Array(passLineBytes);
let rawOffset = 0;
for (let x = 0; x < passWidth; x++) {
const imageX = pass.x + x * pass.xStep;
if (imageX < width && imageY < height) {
const srcPos = (imageY * width + imageX) * pixelSize;
for (let i = 0; i < pixelSize; i++) {
rawScanline[rawOffset++] = data[srcPos + i];
}
}
}
newData.writeByte(0); // no filter
if (depth === 8) {
newData.writeBytes(rawScanline);
}
else if (depth === 16) {
for (const value of rawScanline) {
newData.writeByte((value >> 8) & 0xff); // High byte
newData.writeByte(value & 0xff);
}
}
}
}
return offset;
}
function writeDataUint16(data, newData, slotsPerLine, offset) {
for (let j = 0; j < slotsPerLine; j++) {
newData.writeUint16(data[offset++]);
}
return offset;
}
//# sourceMappingURL=PngEncoder.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
import type { DecodedPng } from './types';
/**
* Converts indexed data into RGB/RGBA format
* @param decodedImage - Image to decode data from.
* @returns Uint8Array with RGB data.
*/
export declare function convertIndexedToRgb(decodedImage: DecodedPng): Uint8Array<ArrayBuffer>;

View File

@@ -0,0 +1,73 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertIndexedToRgb = convertIndexedToRgb;
/**
* Converts indexed data into RGB/RGBA format
* @param decodedImage - Image to decode data from.
* @returns Uint8Array with RGB data.
*/
function convertIndexedToRgb(decodedImage) {
const palette = decodedImage.palette;
const depth = decodedImage.depth;
if (!palette) {
throw new Error('Color palette is undefined.');
}
checkDataSize(decodedImage);
const indexSize = decodedImage.width * decodedImage.height;
const resSize = indexSize * palette[0].length;
const res = new Uint8Array(resSize);
let indexPos = 0;
let offset = 0;
const indexes = new Uint8Array(indexSize);
let bit = 0xff;
switch (depth) {
case 1:
bit = 0x80;
break;
case 2:
bit = 0xc0;
break;
case 4:
bit = 0xf0;
break;
case 8:
bit = 0xff;
break;
default:
throw new Error('Incorrect depth value');
}
for (const byte of decodedImage.data) {
let bit2 = bit;
let shift = 8;
while (bit2) {
shift -= depth;
indexes[indexPos++] = (byte & bit2) >> shift;
bit2 = bit2 >> depth;
if (indexPos % decodedImage.width === 0) {
break;
}
}
}
if (decodedImage.palette) {
for (const index of indexes) {
const color = decodedImage.palette.at(index);
if (!color) {
throw new Error('Incorrect index of palette color');
}
res.set(color, offset);
offset += color.length;
}
}
return res;
}
function checkDataSize(image) {
const expectedSize = image.depth < 8
? Math.ceil((image.width * image.depth) / 8) *
image.height *
image.channels
: image.width * image.height * image.channels;
if (image.data.length !== expectedSize) {
throw new RangeError(`wrong data size. Found ${image.data.length}, expected ${expectedSize}`);
}
}
//# sourceMappingURL=convertIndexedToRgb.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"convertIndexedToRgb.js","sourceRoot":"","sources":["../src/convertIndexedToRgb.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,10 @@
/**
* Apllies filter on scanline based on the filter type.
* @param filterType - The filter type to apply.
* @param currentLine - The current line of pixel data.
* @param newLine - The new line of pixel data.
* @param prevLine - The previous line of pixel data.
* @param passLineBytes - The number of bytes in the pass line.
* @param bytesPerPixel - The number of bytes per pixel.
*/
export declare function applyUnfilter(filterType: number, currentLine: Uint8Array, newLine: Uint8Array, prevLine: Uint8Array, passLineBytes: number, bytesPerPixel: number): void;

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyUnfilter = applyUnfilter;
const unfilter_1 = require("./unfilter");
/**
* Apllies filter on scanline based on the filter type.
* @param filterType - The filter type to apply.
* @param currentLine - The current line of pixel data.
* @param newLine - The new line of pixel data.
* @param prevLine - The previous line of pixel data.
* @param passLineBytes - The number of bytes in the pass line.
* @param bytesPerPixel - The number of bytes per pixel.
*/
function applyUnfilter(filterType, currentLine, newLine, prevLine, passLineBytes, bytesPerPixel) {
switch (filterType) {
case 0:
(0, unfilter_1.unfilterNone)(currentLine, newLine, passLineBytes);
break;
case 1:
(0, unfilter_1.unfilterSub)(currentLine, newLine, passLineBytes, bytesPerPixel);
break;
case 2:
(0, unfilter_1.unfilterUp)(currentLine, newLine, prevLine, passLineBytes);
break;
case 3:
(0, unfilter_1.unfilterAverage)(currentLine, newLine, prevLine, passLineBytes, bytesPerPixel);
break;
case 4:
(0, unfilter_1.unfilterPaeth)(currentLine, newLine, prevLine, passLineBytes, bytesPerPixel);
break;
default:
throw new Error(`Unsupported filter: ${filterType}`);
}
}
//# sourceMappingURL=applyUnfilter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"applyUnfilter.js","sourceRoot":"","sources":["../../src/helpers/applyUnfilter.ts"],"names":[],"mappings":";;AAgBA,sCAuCC;AAvDD,yCAMoB;AACpB;;;;;;;;GAQG;AACH,SAAgB,aAAa,CAC3B,UAAkB,EAClB,WAAuB,EACvB,OAAmB,EACnB,QAAoB,EACpB,aAAqB,EACrB,aAAqB;IAErB,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,CAAC;YACJ,IAAA,uBAAY,EAAC,WAAW,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;YAClD,MAAM;QACR,KAAK,CAAC;YACJ,IAAA,sBAAW,EAAC,WAAW,EAAE,OAAO,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;YAChE,MAAM;QACR,KAAK,CAAC;YACJ,IAAA,qBAAU,EAAC,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;YAC1D,MAAM;QACR,KAAK,CAAC;YACJ,IAAA,0BAAe,EACb,WAAW,EACX,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,CACd,CAAC;YACF,MAAM;QACR,KAAK,CAAC;YACJ,IAAA,wBAAa,EACX,WAAW,EACX,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,CACd,CAAC;YACF,MAAM;QACR;YACE,MAAM,IAAI,KAAK,CAAC,uBAAuB,UAAU,EAAE,CAAC,CAAC;IACzD,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,3 @@
import type { IOBuffer } from 'iobuffer';
export declare function checkCrc(buffer: IOBuffer, crcLength: number, chunkName: string): void;
export declare function writeCrc(buffer: IOBuffer, length: number): void;

View File

@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.checkCrc = checkCrc;
exports.writeCrc = writeCrc;
const crcTable = [];
for (let n = 0; n < 256; n++) {
let c = n;
for (let k = 0; k < 8; k++) {
if (c & 1) {
c = 0xedb88320 ^ (c >>> 1);
}
else {
c = c >>> 1;
}
}
crcTable[n] = c;
}
const initialCrc = 0xffffffff;
function updateCrc(currentCrc, data, length) {
let c = currentCrc;
for (let n = 0; n < length; n++) {
c = crcTable[(c ^ data[n]) & 0xff] ^ (c >>> 8);
}
return c;
}
function crc(data, length) {
return (updateCrc(initialCrc, data, length) ^ initialCrc) >>> 0;
}
function checkCrc(buffer, crcLength, chunkName) {
const expectedCrc = buffer.readUint32();
const actualCrc = crc(new Uint8Array(buffer.buffer, buffer.byteOffset + buffer.offset - crcLength - 4, crcLength), crcLength); // "- 4" because we already advanced by reading the CRC
if (actualCrc !== expectedCrc) {
throw new Error(`CRC mismatch for chunk ${chunkName}. Expected ${expectedCrc}, found ${actualCrc}`);
}
}
function writeCrc(buffer, length) {
buffer.writeUint32(crc(new Uint8Array(buffer.buffer, buffer.byteOffset + buffer.offset - length, length), length));
}
//# sourceMappingURL=crc.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"crc.js","sourceRoot":"","sources":["../../src/helpers/crc.ts"],"names":[],"mappings":";;AAgCA,4BAmBC;AAED,4BAWC;AA9DD,MAAM,QAAQ,GAAa,EAAE,CAAC;AAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACV,CAAC,GAAG,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC;IACD,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,GAAG,UAAU,CAAC;AAC9B,SAAS,SAAS,CAChB,UAAkB,EAClB,IAAgB,EAChB,MAAc;IAEd,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,GAAG,CAAC,IAAgB,EAAE,MAAc;IAC3C,OAAO,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;AAClE,CAAC;AAED,SAAgB,QAAQ,CACtB,MAAgB,EAChB,SAAiB,EACjB,SAAiB;IAEjB,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IACxC,MAAM,SAAS,GAAG,GAAG,CACnB,IAAI,UAAU,CACZ,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,GAAG,SAAS,GAAG,CAAC,EACjD,SAAS,CACV,EACD,SAAS,CACV,CAAC,CAAC,uDAAuD;IAC1D,IAAI,SAAS,KAAK,WAAW,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,0BAA0B,SAAS,cAAc,WAAW,WAAW,SAAS,EAAE,CACnF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAgB,QAAQ,CAAC,MAAgB,EAAE,MAAc;IACvD,MAAM,CAAC,WAAW,CAChB,GAAG,CACD,IAAI,UAAU,CACZ,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,EAC1C,MAAM,CACP,EACD,MAAM,CACP,CACF,CAAC;AACJ,CAAC"}

View File

@@ -0,0 +1,8 @@
import type { DecodeInterlaceNullParams } from './decodeInterlaceNull';
/**
* Decodes the Adam7 interlaced PNG data.
*
* @param params - DecodeInterlaceNullParams
* @returns - array of pixel data.
*/
export declare function decodeInterlaceAdam7(params: DecodeInterlaceNullParams): Uint8Array<ArrayBuffer> | Uint16Array<ArrayBuffer>;

View File

@@ -0,0 +1,79 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeInterlaceAdam7 = decodeInterlaceAdam7;
const applyUnfilter_1 = require("./applyUnfilter");
const uint16 = new Uint16Array([0x00ff]);
const uint8 = new Uint8Array(uint16.buffer);
const osIsLittleEndian = uint8[0] === 0xff;
/**
* Decodes the Adam7 interlaced PNG data.
*
* @param params - DecodeInterlaceNullParams
* @returns - array of pixel data.
*/
function decodeInterlaceAdam7(params) {
const { data, width, height, channels, depth } = params;
// Adam7 interlacing pattern
const passes = [
{ x: 0, y: 0, xStep: 8, yStep: 8 }, // Pass 1
{ x: 4, y: 0, xStep: 8, yStep: 8 }, // Pass 2
{ x: 0, y: 4, xStep: 4, yStep: 8 }, // Pass 3
{ x: 2, y: 0, xStep: 4, yStep: 4 }, // Pass 4
{ x: 0, y: 2, xStep: 2, yStep: 4 }, // Pass 5
{ x: 1, y: 0, xStep: 2, yStep: 2 }, // Pass 6
{ x: 0, y: 1, xStep: 1, yStep: 2 }, // Pass 7
];
const bytesPerPixel = Math.ceil(depth / 8) * channels;
const resultData = new Uint8Array(height * width * bytesPerPixel);
let offset = 0;
// Process each pass
for (let passIndex = 0; passIndex < 7; passIndex++) {
const pass = passes[passIndex];
// Calculate pass dimensions
const passWidth = Math.ceil((width - pass.x) / pass.xStep);
const passHeight = Math.ceil((height - pass.y) / pass.yStep);
if (passWidth <= 0 || passHeight <= 0)
continue;
const passLineBytes = passWidth * bytesPerPixel;
const prevLine = new Uint8Array(passLineBytes);
// Process each scanline in this pass
for (let y = 0; y < passHeight; y++) {
// First byte is the filter type
const filterType = data[offset++];
const currentLine = data.subarray(offset, offset + passLineBytes);
offset += passLineBytes;
// Create a new line for the unfiltered data
const newLine = new Uint8Array(passLineBytes);
// Apply the appropriate unfilter
(0, applyUnfilter_1.applyUnfilter)(filterType, currentLine, newLine, prevLine, passLineBytes, bytesPerPixel);
prevLine.set(newLine);
for (let x = 0; x < passWidth; x++) {
const outputX = pass.x + x * pass.xStep;
const outputY = pass.y + y * pass.yStep;
if (outputX >= width || outputY >= height)
continue;
for (let i = 0; i < bytesPerPixel; i++) {
resultData[(outputY * width + outputX) * bytesPerPixel + i] =
newLine[x * bytesPerPixel + i];
}
}
}
}
if (depth === 16) {
const uint16Data = new Uint16Array(resultData.buffer);
if (osIsLittleEndian) {
for (let k = 0; k < uint16Data.length; k++) {
// PNG is always big endian. Swap the bytes.
uint16Data[k] = swap16(uint16Data[k]);
}
}
return uint16Data;
}
else {
return resultData;
}
}
function swap16(val) {
return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
}
//# sourceMappingURL=decodeInterlaceAdam7.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"decodeInterlaceAdam7.js","sourceRoot":"","sources":["../../src/helpers/decodeInterlaceAdam7.ts"],"names":[],"mappings":";;AAYA,oDA4EC;AAxFD,mDAAgD;AAGhD,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AACzC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAC5C,MAAM,gBAAgB,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAC3C;;;;;GAKG;AACH,SAAgB,oBAAoB,CAAC,MAAiC;IACpE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAExD,4BAA4B;IAC5B,MAAM,MAAM,GAAG;QACb,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS;QAC7C,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS;QAC7C,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS;QAC7C,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS;QAC7C,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS;QAC7C,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS;QAC7C,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS;KAC9C,CAAC;IAEF,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;IACtD,MAAM,UAAU,GAAG,IAAI,UAAU,CAAC,MAAM,GAAG,KAAK,GAAG,aAAa,CAAC,CAAC;IAElE,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,oBAAoB;IACpB,KAAK,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,GAAG,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC;QACnD,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QAE/B,4BAA4B;QAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAE7D,IAAI,SAAS,IAAI,CAAC,IAAI,UAAU,IAAI,CAAC;YAAE,SAAS;QAEhD,MAAM,aAAa,GAAG,SAAS,GAAG,aAAa,CAAC;QAChD,MAAM,QAAQ,GAAG,IAAI,UAAU,CAAC,aAAa,CAAC,CAAC;QAE/C,qCAAqC;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,gCAAgC;YAChC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YAClC,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC;YAClE,MAAM,IAAI,aAAa,CAAC;YAExB,4CAA4C;YAC5C,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,aAAa,CAAC,CAAC;YAE9C,iCAAiC;YACjC,IAAA,6BAAa,EACX,UAAU,EACV,WAAW,EACX,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,CACd,CAAC;YACF,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAEtB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;gBACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;gBACxC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;gBACxC,IAAI,OAAO,IAAI,KAAK,IAAI,OAAO,IAAI,MAAM;oBAAE,SAAS;gBACpD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,aAAa,EAAE,CAAC,EAAE,EAAE,CAAC;oBACvC,UAAU,CAAC,CAAC,OAAO,GAAG,KAAK,GAAG,OAAO,CAAC,GAAG,aAAa,GAAG,CAAC,CAAC;wBACzD,OAAO,CAAC,CAAC,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACjB,MAAM,UAAU,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,gBAAgB,EAAE,CAAC;YACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC3C,4CAA4C;gBAC5C,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;SAAM,CAAC;QACN,OAAO,UAAU,CAAC;IACpB,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,GAAW;IACzB,OAAO,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACnD,CAAC"}

View File

@@ -0,0 +1,9 @@
import type { PngDataArray } from '../types';
export interface DecodeInterlaceNullParams {
data: Uint8Array;
width: number;
height: number;
channels: number;
depth: number;
}
export declare function decodeInterlaceNull(params: DecodeInterlaceNullParams): PngDataArray;

View File

@@ -0,0 +1,60 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeInterlaceNull = decodeInterlaceNull;
const unfilter_1 = require("./unfilter");
const uint16 = new Uint16Array([0x00ff]);
const uint8 = new Uint8Array(uint16.buffer);
const osIsLittleEndian = uint8[0] === 0xff;
const empty = new Uint8Array(0);
function decodeInterlaceNull(params) {
const { data, width, height, channels, depth } = params;
const bytesPerPixel = Math.ceil(depth / 8) * channels;
const bytesPerLine = Math.ceil((depth / 8) * channels * width);
const newData = new Uint8Array(height * bytesPerLine);
let prevLine = empty;
let offset = 0;
let currentLine;
let newLine;
for (let i = 0; i < height; i++) {
currentLine = data.subarray(offset + 1, offset + 1 + bytesPerLine);
newLine = newData.subarray(i * bytesPerLine, (i + 1) * bytesPerLine);
switch (data[offset]) {
case 0:
(0, unfilter_1.unfilterNone)(currentLine, newLine, bytesPerLine);
break;
case 1:
(0, unfilter_1.unfilterSub)(currentLine, newLine, bytesPerLine, bytesPerPixel);
break;
case 2:
(0, unfilter_1.unfilterUp)(currentLine, newLine, prevLine, bytesPerLine);
break;
case 3:
(0, unfilter_1.unfilterAverage)(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel);
break;
case 4:
(0, unfilter_1.unfilterPaeth)(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel);
break;
default:
throw new Error(`Unsupported filter: ${data[offset]}`);
}
prevLine = newLine;
offset += bytesPerLine + 1;
}
if (depth === 16) {
const uint16Data = new Uint16Array(newData.buffer);
if (osIsLittleEndian) {
for (let k = 0; k < uint16Data.length; k++) {
// PNG is always big endian. Swap the bytes.
uint16Data[k] = swap16(uint16Data[k]);
}
}
return uint16Data;
}
else {
return newData;
}
}
function swap16(val) {
return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
}
//# sourceMappingURL=decodeInterlaceNull.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"decodeInterlaceNull.js","sourceRoot":"","sources":["../../src/helpers/decodeInterlaceNull.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,4 @@
import type { IOBuffer } from 'iobuffer';
export declare function writeSignature(buffer: IOBuffer): void;
export declare function checkSignature(buffer: IOBuffer): void;
export declare function hasPngSignature(array: ArrayLike<number>): boolean;

View File

@@ -0,0 +1,27 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeSignature = writeSignature;
exports.checkSignature = checkSignature;
exports.hasPngSignature = hasPngSignature;
// https://www.w3.org/TR/PNG/#5PNG-file-signature
const pngSignature = Uint8Array.of(137, 80, 78, 71, 13, 10, 26, 10);
function writeSignature(buffer) {
buffer.writeBytes(pngSignature);
}
function checkSignature(buffer) {
if (!hasPngSignature(buffer.readBytes(pngSignature.length))) {
throw new Error('wrong PNG signature');
}
}
function hasPngSignature(array) {
if (array.length < pngSignature.length) {
return false;
}
for (let i = 0; i < pngSignature.length; i++) {
if (array[i] !== pngSignature[i]) {
return false;
}
}
return true;
}
//# sourceMappingURL=signature.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"signature.js","sourceRoot":"","sources":["../../src/helpers/signature.ts"],"names":[],"mappings":";;AAMA,wCAEC;AAED,wCAIC;AAED,0CAUC;AAxBD,iDAAiD;AAEjD,MAAM,YAAY,GAAG,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAEpE,SAAgB,cAAc,CAAC,MAAgB;IAC7C,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AAClC,CAAC;AAED,SAAgB,cAAc,CAAC,MAAgB;IAC7C,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,SAAgB,eAAe,CAAC,KAAwB;IACtD,IAAI,KAAK,CAAC,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;QACvC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;YACjC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}

View File

@@ -0,0 +1,6 @@
import type { IOBuffer } from 'iobuffer';
export declare const textChunkName = "tEXt";
export declare function decodetEXt(text: Record<string, string>, buffer: IOBuffer, length: number): void;
export declare function encodetEXt(buffer: IOBuffer, keyword: string, text: string): void;
export declare function readKeyword(buffer: IOBuffer): string;
export declare function readLatin1(buffer: IOBuffer, length: number): string;

View File

@@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.textChunkName = void 0;
exports.decodetEXt = decodetEXt;
exports.encodetEXt = encodetEXt;
exports.readKeyword = readKeyword;
exports.readLatin1 = readLatin1;
const crc_1 = require("./crc");
// https://www.w3.org/TR/png/#11tEXt
exports.textChunkName = 'tEXt';
const NULL = 0;
const latin1Decoder = new TextDecoder('latin1');
function validateKeyword(keyword) {
validateLatin1(keyword);
if (keyword.length === 0 || keyword.length > 79) {
throw new Error('keyword length must be between 1 and 79');
}
}
// eslint-disable-next-line no-control-regex
const latin1Regex = /^[\u0000-\u00FF]*$/;
function validateLatin1(text) {
if (!latin1Regex.test(text)) {
throw new Error('invalid latin1 text');
}
}
function decodetEXt(text, buffer, length) {
const keyword = readKeyword(buffer);
text[keyword] = readLatin1(buffer, length - keyword.length - 1);
}
function encodetEXt(buffer, keyword, text) {
validateKeyword(keyword);
validateLatin1(text);
const length = keyword.length + 1 /* NULL */ + text.length;
buffer.writeUint32(length);
buffer.writeChars(exports.textChunkName);
buffer.writeChars(keyword);
buffer.writeByte(NULL);
buffer.writeChars(text);
(0, crc_1.writeCrc)(buffer, length + 4);
}
// https://www.w3.org/TR/png/#11keywords
function readKeyword(buffer) {
buffer.mark();
while (buffer.readByte() !== NULL) {
/* advance */
}
const end = buffer.offset;
buffer.reset();
const keyword = latin1Decoder.decode(buffer.readBytes(end - buffer.offset - 1));
// NULL
buffer.skip(1);
validateKeyword(keyword);
return keyword;
}
function readLatin1(buffer, length) {
return latin1Decoder.decode(buffer.readBytes(length));
}
//# sourceMappingURL=text.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/helpers/text.ts"],"names":[],"mappings":";;;AA2BA,gCAOC;AAED,gCAWC;AAGD,kCAgBC;AAED,gCAEC;AApED,+BAAiC;AAEjC,oCAAoC;AAEvB,QAAA,aAAa,GAAG,MAAM,CAAC;AAEpC,MAAM,IAAI,GAAG,CAAC,CAAC;AAEf,MAAM,aAAa,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;AAEhD,SAAS,eAAe,CAAC,OAAe;IACtC,cAAc,CAAC,OAAO,CAAC,CAAC;IACxB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,4CAA4C;AAC5C,MAAM,WAAW,GAAG,oBAAoB,CAAC;AACzC,SAAS,cAAc,CAAC,IAAY;IAClC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED,SAAgB,UAAU,CACxB,IAA4B,EAC5B,MAAgB,EAChB,MAAc;IAEd,MAAM,OAAO,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,SAAgB,UAAU,CAAC,MAAgB,EAAE,OAAe,EAAE,IAAY;IACxE,eAAe,CAAC,OAAO,CAAC,CAAC;IACzB,cAAc,CAAC,IAAI,CAAC,CAAC;IACrB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC;IAE3D,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,MAAM,CAAC,UAAU,CAAC,qBAAa,CAAC,CAAC;IACjC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC3B,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACxB,IAAA,cAAQ,EAAC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,wCAAwC;AACxC,SAAgB,WAAW,CAAC,MAAgB;IAC1C,MAAM,CAAC,IAAI,EAAE,CAAC;IACd,OAAO,MAAM,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC;QAClC,aAAa;IACf,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1B,MAAM,CAAC,KAAK,EAAE,CAAC;IACf,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAClC,MAAM,CAAC,SAAS,CAAC,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAC1C,CAAC;IACF,OAAO;IACP,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAEf,eAAe,CAAC,OAAO,CAAC,CAAC;IAEzB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAgB,UAAU,CAAC,MAAgB,EAAE,MAAc;IACzD,OAAO,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AACxD,CAAC"}

View File

@@ -0,0 +1,6 @@
import type { PngDataArray } from '../types';
export declare function unfilterNone(currentLine: PngDataArray, newLine: PngDataArray, bytesPerLine: number): void;
export declare function unfilterSub(currentLine: PngDataArray, newLine: PngDataArray, bytesPerLine: number, bytesPerPixel: number): void;
export declare function unfilterUp(currentLine: PngDataArray, newLine: PngDataArray, prevLine: PngDataArray, bytesPerLine: number): void;
export declare function unfilterAverage(currentLine: PngDataArray, newLine: PngDataArray, prevLine: PngDataArray, bytesPerLine: number, bytesPerPixel: number): void;
export declare function unfilterPaeth(currentLine: PngDataArray, newLine: PngDataArray, prevLine: PngDataArray, bytesPerLine: number, bytesPerPixel: number): void;

View File

@@ -0,0 +1,92 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.unfilterNone = unfilterNone;
exports.unfilterSub = unfilterSub;
exports.unfilterUp = unfilterUp;
exports.unfilterAverage = unfilterAverage;
exports.unfilterPaeth = unfilterPaeth;
function unfilterNone(currentLine, newLine, bytesPerLine) {
for (let i = 0; i < bytesPerLine; i++) {
newLine[i] = currentLine[i];
}
}
function unfilterSub(currentLine, newLine, bytesPerLine, bytesPerPixel) {
let i = 0;
for (; i < bytesPerPixel; i++) {
// just copy first bytes
newLine[i] = currentLine[i];
}
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + newLine[i - bytesPerPixel]) & 0xff;
}
}
function unfilterUp(currentLine, newLine, prevLine, bytesPerLine) {
let i = 0;
if (prevLine.length === 0) {
// just copy bytes for first line
for (; i < bytesPerLine; i++) {
newLine[i] = currentLine[i];
}
}
else {
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + prevLine[i]) & 0xff;
}
}
}
function unfilterAverage(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel) {
let i = 0;
if (prevLine.length === 0) {
for (; i < bytesPerPixel; i++) {
newLine[i] = currentLine[i];
}
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + (newLine[i - bytesPerPixel] >> 1)) & 0xff;
}
}
else {
for (; i < bytesPerPixel; i++) {
newLine[i] = (currentLine[i] + (prevLine[i] >> 1)) & 0xff;
}
for (; i < bytesPerLine; i++) {
newLine[i] =
(currentLine[i] + ((newLine[i - bytesPerPixel] + prevLine[i]) >> 1)) &
0xff;
}
}
}
function unfilterPaeth(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel) {
let i = 0;
if (prevLine.length === 0) {
for (; i < bytesPerPixel; i++) {
newLine[i] = currentLine[i];
}
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + newLine[i - bytesPerPixel]) & 0xff;
}
}
else {
for (; i < bytesPerPixel; i++) {
newLine[i] = (currentLine[i] + prevLine[i]) & 0xff;
}
for (; i < bytesPerLine; i++) {
newLine[i] =
(currentLine[i] +
paethPredictor(newLine[i - bytesPerPixel], prevLine[i], prevLine[i - bytesPerPixel])) &
0xff;
}
}
}
function paethPredictor(a, b, c) {
const p = a + b - c;
const pa = Math.abs(p - a);
const pb = Math.abs(p - b);
const pc = Math.abs(p - c);
if (pa <= pb && pa <= pc)
return a;
else if (pb <= pc)
return b;
else
return c;
}
//# sourceMappingURL=unfilter.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"unfilter.js","sourceRoot":"","sources":["../../src/helpers/unfilter.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,8 @@
import type { DecoderInputType, PngDecoderOptions, DecodedPng, DecodedApng, ImageData, PngEncoderOptions } from './types';
export { hasPngSignature } from './helpers/signature';
export * from './types';
declare function decodePng(data: DecoderInputType, options?: PngDecoderOptions): DecodedPng;
declare function encodePng(png: ImageData, options?: PngEncoderOptions): Uint8Array;
declare function decodeApng(data: DecoderInputType, options?: PngDecoderOptions): DecodedApng;
export { decodePng as decode, encodePng as encode, decodeApng };
export { convertIndexedToRgb } from './convertIndexedToRgb';

43
SuiviREForamteur/node_modules/fast-png/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,43 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertIndexedToRgb = exports.hasPngSignature = void 0;
exports.decode = decodePng;
exports.encode = encodePng;
exports.decodeApng = decodeApng;
const PngDecoder_1 = __importDefault(require("./PngDecoder"));
const PngEncoder_1 = __importDefault(require("./PngEncoder"));
var signature_1 = require("./helpers/signature");
Object.defineProperty(exports, "hasPngSignature", { enumerable: true, get: function () { return signature_1.hasPngSignature; } });
__exportStar(require("./types"), exports);
function decodePng(data, options) {
const decoder = new PngDecoder_1.default(data, options);
return decoder.decode();
}
function encodePng(png, options) {
const encoder = new PngEncoder_1.default(png, options);
return encoder.encode();
}
function decodeApng(data, options) {
const decoder = new PngDecoder_1.default(data, options);
return decoder.decodeApng();
}
var convertIndexedToRgb_1 = require("./convertIndexedToRgb");
Object.defineProperty(exports, "convertIndexedToRgb", { enumerable: true, get: function () { return convertIndexedToRgb_1.convertIndexedToRgb; } });
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAmCsB,2BAAM;AAAe,2BAAM;AAAE,gCAAU;AAnC7D,8DAAsC;AACtC,8DAAsC;AAUtC,iDAAsD;AAA7C,4GAAA,eAAe,OAAA;AACxB,0CAAwB;AAExB,SAAS,SAAS,CAChB,IAAsB,EACtB,OAA2B;IAE3B,MAAM,OAAO,GAAG,IAAI,oBAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,SAAS,CAAC,GAAc,EAAE,OAA2B;IAC5D,MAAM,OAAO,GAAG,IAAI,oBAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC7C,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC;AAC1B,CAAC;AAED,SAAS,UAAU,CACjB,IAAsB,EACtB,OAA2B;IAE3B,MAAM,OAAO,GAAG,IAAI,oBAAU,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,OAAO,CAAC,UAAU,EAAE,CAAC;AAC9B,CAAC;AAID,6DAA4D;AAAnD,0HAAA,mBAAmB,OAAA"}

View File

@@ -0,0 +1,35 @@
export declare const ColorType: {
readonly UNKNOWN: -1;
readonly GREYSCALE: 0;
readonly TRUECOLOUR: 2;
readonly INDEXED_COLOUR: 3;
readonly GREYSCALE_ALPHA: 4;
readonly TRUECOLOUR_ALPHA: 6;
};
export type ColorType = (typeof ColorType)[keyof typeof ColorType];
export declare const CompressionMethod: {
readonly UNKNOWN: -1;
readonly DEFLATE: 0;
};
export type CompressionMethod = (typeof CompressionMethod)[keyof typeof CompressionMethod];
export declare const FilterMethod: {
readonly UNKNOWN: -1;
readonly ADAPTIVE: 0;
};
export type FilterMethod = (typeof FilterMethod)[keyof typeof FilterMethod];
export declare const InterlaceMethod: {
readonly UNKNOWN: -1;
readonly NO_INTERLACE: 0;
readonly ADAM7: 1;
};
export declare const DisposeOpType: {
readonly NONE: 0;
readonly BACKGROUND: 1;
readonly PREVIOUS: 2;
};
export type DisposeOpType = (typeof DisposeOpType)[keyof typeof DisposeOpType];
export declare const BlendOpType: {
readonly SOURCE: 0;
readonly OVER: 1;
};
export type InterlaceMethod = (typeof InterlaceMethod)[keyof typeof InterlaceMethod];

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BlendOpType = exports.DisposeOpType = exports.InterlaceMethod = exports.FilterMethod = exports.CompressionMethod = exports.ColorType = void 0;
exports.ColorType = {
UNKNOWN: -1,
GREYSCALE: 0,
TRUECOLOUR: 2,
INDEXED_COLOUR: 3,
GREYSCALE_ALPHA: 4,
TRUECOLOUR_ALPHA: 6,
};
exports.CompressionMethod = {
UNKNOWN: -1,
DEFLATE: 0,
};
exports.FilterMethod = {
UNKNOWN: -1,
ADAPTIVE: 0,
};
exports.InterlaceMethod = {
UNKNOWN: -1,
NO_INTERLACE: 0,
ADAM7: 1,
};
exports.DisposeOpType = {
NONE: 0,
BACKGROUND: 1,
PREVIOUS: 2,
};
exports.BlendOpType = {
SOURCE: 0,
OVER: 1,
};
//# sourceMappingURL=internalTypes.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"internalTypes.js","sourceRoot":"","sources":["../src/internalTypes.ts"],"names":[],"mappings":";;;AAAa,QAAA,SAAS,GAAG;IACvB,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,CAAC;IACZ,UAAU,EAAE,CAAC;IACb,cAAc,EAAE,CAAC;IACjB,eAAe,EAAE,CAAC;IAClB,gBAAgB,EAAE,CAAC;CACX,CAAC;AAIE,QAAA,iBAAiB,GAAG;IAC/B,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,CAAC;CACF,CAAC;AAKE,QAAA,YAAY,GAAG;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,CAAC;CACH,CAAC;AAGE,QAAA,eAAe,GAAG;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,YAAY,EAAE,CAAC;IACf,KAAK,EAAE,CAAC;CACA,CAAC;AAEE,QAAA,aAAa,GAAG;IAC3B,IAAI,EAAE,CAAC;IACP,UAAU,EAAE,CAAC;IACb,QAAQ,EAAE,CAAC;CACH,CAAC;AAIE,QAAA,WAAW,GAAG;IACzB,MAAM,EAAE,CAAC;IACT,IAAI,EAAE,CAAC;CACC,CAAC"}

97
SuiviREForamteur/node_modules/fast-png/lib/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,97 @@
import type { IOBuffer } from 'iobuffer';
import type { DeflateFunctionOptions } from 'pako';
export type { DeflateFunctionOptions } from 'pako';
export type PngDataArray = Uint8Array | Uint8ClampedArray | Uint16Array;
export type DecoderInputType = IOBuffer | ArrayBufferLike | ArrayBufferView;
export type BitDepth = 1 | 2 | 4 | 8 | 16;
export type IndexedColorBitDepth = 1 | 2 | 4 | 8;
export interface PngResolution {
/**
* Pixels per unit, X axis
*/
x: number;
/**
* Pixels per unit, Y axis
*/
y: number;
/**
* Unit specifier
*/
unit: ResolutionUnitSpecifier;
}
export declare enum ResolutionUnitSpecifier {
/**
* Unit is unknown
*/
UNKNOWN = 0,
/**
* Unit is the metre
*/
METRE = 1
}
export interface ImageData {
width: number;
height: number;
data: PngDataArray;
depth?: BitDepth;
channels?: number;
text?: Record<string, string>;
palette?: IndexedColors;
transparency?: Uint16Array;
}
export interface DecodedPng {
width: number;
height: number;
data: PngDataArray;
depth: BitDepth;
channels: number;
text: Record<string, string>;
resolution?: PngResolution;
palette?: IndexedColors;
transparency?: Uint16Array;
iccEmbeddedProfile?: IccEmbeddedProfile;
}
export interface DecodedApng {
width: number;
height: number;
depth: BitDepth;
channels: number;
numberOfFrames: number;
numberOfPlays: number;
text: Record<string, string>;
resolution?: PngResolution;
palette?: IndexedColors;
transparency?: Uint16Array;
iccEmbeddedProfile?: IccEmbeddedProfile;
frames: DecodedApngFrame[];
}
export interface ApngFrame {
sequenceNumber: number;
width: number;
height: number;
xOffset: number;
yOffset: number;
delayNumber: number;
delayDenominator: number;
disposeOp: number;
blendOp: number;
data: PngDataArray;
}
export interface DecodedApngFrame {
sequenceNumber: number;
delayNumber: number;
delayDenominator: number;
data: PngDataArray;
}
export interface PngDecoderOptions {
checkCrc?: boolean;
}
export interface PngEncoderOptions {
interlace?: 'null' | 'Adam7';
zlib?: DeflateFunctionOptions;
}
export type IndexedColors = number[][];
export interface IccEmbeddedProfile {
name: string;
profile: Uint8Array;
}

15
SuiviREForamteur/node_modules/fast-png/lib/types.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ResolutionUnitSpecifier = void 0;
var ResolutionUnitSpecifier;
(function (ResolutionUnitSpecifier) {
/**
* Unit is unknown
*/
ResolutionUnitSpecifier[ResolutionUnitSpecifier["UNKNOWN"] = 0] = "UNKNOWN";
/**
* Unit is the metre
*/
ResolutionUnitSpecifier[ResolutionUnitSpecifier["METRE"] = 1] = "METRE";
})(ResolutionUnitSpecifier || (exports.ResolutionUnitSpecifier = ResolutionUnitSpecifier = {}));
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AA4BA,IAAY,uBASX;AATD,WAAY,uBAAuB;IACjC;;OAEG;IACH,2EAAW,CAAA;IACX;;OAEG;IACH,uEAAS,CAAA;AACX,CAAC,EATW,uBAAuB,uCAAvB,uBAAuB,QASlC"}

60
SuiviREForamteur/node_modules/fast-png/package.json generated vendored Normal file
View File

@@ -0,0 +1,60 @@
{
"name": "fast-png",
"version": "6.4.0",
"description": "PNG image decoder and encoder written entirely in JavaScript",
"main": "./lib/index.js",
"module": "./lib-esm/index.js",
"types": "./lib/index.d.ts",
"keywords": [
"png",
"image",
"image-js",
"encoder",
"decoder"
],
"author": "Michaël Zasso",
"license": "MIT",
"files": [
"src",
"lib",
"lib-esm"
],
"scripts": {
"check-types": "tsc --noEmit",
"clean": "rimraf lib lib-esm",
"eslint": "eslint src",
"eslint-fix": "npm run eslint -- --fix",
"prepack": "npm run tsc",
"prettier": "prettier --check src",
"prettier-write": "prettier --write src",
"test": "npm run test-only && npm run eslint && npm run prettier && npm run check-types",
"test-only": "vitest run --coverage",
"tsc": "npm run clean && npm run tsc-cjs && npm run tsc-esm",
"tsc-cjs": "tsc --project tsconfig.cjs.json",
"tsc-esm": "tsc --project tsconfig.esm.json"
},
"repository": {
"type": "git",
"url": "git+https://github.com/image-js/fast-png.git"
},
"bugs": {
"url": "https://github.com/image-js/fast-png/issues"
},
"homepage": "https://github.com/image-js/fast-png#readme",
"devDependencies": {
"@types/node": "^22.13.0",
"@vitest/coverage-v8": "^3.0.4",
"eslint": "^9.19.0",
"eslint-config-cheminfo-typescript": "^17.0.0",
"pngjs": "^7.0.0",
"prettier": "^3.4.2",
"rimraf": "^6.0.1",
"typescript": "^5.7.3",
"vitest": "^3.0.4"
},
"dependencies": {
"@types/pako": "^2.0.3",
"iobuffer": "^5.3.2",
"pako": "^2.1.0"
}
}

View File

@@ -0,0 +1,594 @@
import { IOBuffer } from 'iobuffer';
import { inflate, Inflate as Inflator } from 'pako';
import { checkCrc } from './helpers/crc';
import { decodeInterlaceAdam7 } from './helpers/decodeInterlaceAdam7';
import { decodeInterlaceNull } from './helpers/decodeInterlaceNull';
import { checkSignature } from './helpers/signature';
import { decodetEXt, readKeyword, textChunkName } from './helpers/text';
import {
ColorType,
CompressionMethod,
DisposeOpType,
FilterMethod,
InterlaceMethod,
BlendOpType,
} from './internalTypes';
import type {
BitDepth,
DecodedPng,
DecodedApng,
DecodedApngFrame,
ApngFrame,
DecoderInputType,
IndexedColors,
PngDecoderOptions,
} from './types';
export default class PngDecoder extends IOBuffer {
private readonly _checkCrc: boolean;
private _inflator: Inflator;
private readonly _png: DecodedPng;
private readonly _apng: DecodedApng;
private _end: boolean;
private _hasPalette: boolean;
private _palette: IndexedColors;
private _hasTransparency: boolean;
private _transparency: Uint16Array;
private _compressionMethod: CompressionMethod;
private _filterMethod: FilterMethod;
private _interlaceMethod: InterlaceMethod;
private _colorType: ColorType;
private _isAnimated: boolean;
private _numberOfFrames: number;
private _numberOfPlays: number;
private _frames: ApngFrame[];
private _writingDataChunks: boolean;
public constructor(data: DecoderInputType, options: PngDecoderOptions = {}) {
super(data);
const { checkCrc = false } = options;
this._checkCrc = checkCrc;
this._inflator = new Inflator();
this._png = {
width: -1,
height: -1,
channels: -1,
data: new Uint8Array(0),
depth: 1,
text: {},
};
this._apng = {
width: -1,
height: -1,
channels: -1,
depth: 1,
numberOfFrames: 1,
numberOfPlays: 0,
text: {},
frames: [],
};
this._end = false;
this._hasPalette = false;
this._palette = [];
this._hasTransparency = false;
this._transparency = new Uint16Array(0);
this._compressionMethod = CompressionMethod.UNKNOWN;
this._filterMethod = FilterMethod.UNKNOWN;
this._interlaceMethod = InterlaceMethod.UNKNOWN;
this._colorType = ColorType.UNKNOWN;
this._isAnimated = false;
this._numberOfFrames = 1;
this._numberOfPlays = 0;
this._frames = [];
this._writingDataChunks = false;
// PNG is always big endian
// https://www.w3.org/TR/PNG/#7Integers-and-byte-order
this.setBigEndian();
}
public decode(): DecodedPng {
checkSignature(this);
while (!this._end) {
const length = this.readUint32();
const type = this.readChars(4);
this.decodeChunk(length, type);
}
this.decodeImage();
return this._png;
}
public decodeApng(): DecodedApng {
checkSignature(this);
while (!this._end) {
const length = this.readUint32();
const type = this.readChars(4);
this.decodeApngChunk(length, type);
}
this.decodeApngImage();
return this._apng;
}
// https://www.w3.org/TR/PNG/#5Chunk-layout
private decodeChunk(length: number, type: string): void {
const offset = this.offset;
switch (type) {
// 11.2 Critical chunks
case 'IHDR': // 11.2.2 IHDR Image header
this.decodeIHDR();
break;
case 'PLTE': // 11.2.3 PLTE Palette
this.decodePLTE(length);
break;
case 'IDAT': // 11.2.4 IDAT Image data
this.decodeIDAT(length);
break;
case 'IEND': // 11.2.5 IEND Image trailer
this._end = true;
break;
// 11.3 Ancillary chunks
case 'tRNS': // 11.3.2.1 tRNS Transparency
this.decodetRNS(length);
break;
case 'iCCP': // 11.3.3.3 iCCP Embedded ICC profile
this.decodeiCCP(length);
break;
case textChunkName: // 11.3.4.3 tEXt Textual data
decodetEXt(this._png.text, this, length);
break;
case 'pHYs': // 11.3.5.3 pHYs Physical pixel dimensions
this.decodepHYs();
break;
default:
this.skip(length);
break;
}
if (this.offset - offset !== length) {
throw new Error(`Length mismatch while decoding chunk ${type}`);
}
if (this._checkCrc) {
checkCrc(this, length + 4, type);
} else {
this.skip(4);
}
}
private decodeApngChunk(length: number, type: string): void {
const offset = this.offset;
if (type !== 'fdAT' && type !== 'IDAT' && this._writingDataChunks) {
this.pushDataToFrame();
}
switch (type) {
case 'acTL':
this.decodeACTL();
break;
case 'fcTL':
this.decodeFCTL();
break;
case 'fdAT':
this.decodeFDAT(length);
break;
default:
this.decodeChunk(length, type);
this.offset = offset + length;
break;
}
if (this.offset - offset !== length) {
throw new Error(`Length mismatch while decoding chunk ${type}`);
}
if (this._checkCrc) {
checkCrc(this, length + 4, type);
} else {
this.skip(4);
}
}
// https://www.w3.org/TR/PNG/#11IHDR
private decodeIHDR(): void {
const image = this._png;
image.width = this.readUint32();
image.height = this.readUint32();
image.depth = checkBitDepth(this.readUint8());
const colorType = this.readUint8() as ColorType;
this._colorType = colorType;
let channels: number;
switch (colorType) {
case ColorType.GREYSCALE:
channels = 1;
break;
case ColorType.TRUECOLOUR:
channels = 3;
break;
case ColorType.INDEXED_COLOUR:
channels = 1;
break;
case ColorType.GREYSCALE_ALPHA:
channels = 2;
break;
case ColorType.TRUECOLOUR_ALPHA:
channels = 4;
break;
// Kept for exhaustiveness.
// eslint-disable-next-line unicorn/no-useless-switch-case
case ColorType.UNKNOWN:
default:
throw new Error(`Unknown color type: ${colorType}`);
}
this._png.channels = channels;
this._compressionMethod = this.readUint8() as CompressionMethod;
if (this._compressionMethod !== CompressionMethod.DEFLATE) {
throw new Error(
`Unsupported compression method: ${this._compressionMethod}`,
);
}
this._filterMethod = this.readUint8() as FilterMethod;
this._interlaceMethod = this.readUint8() as InterlaceMethod;
}
private decodeACTL(): void {
this._numberOfFrames = this.readUint32();
this._numberOfPlays = this.readUint32();
this._isAnimated = true;
}
private decodeFCTL(): void {
const image: ApngFrame = {
sequenceNumber: this.readUint32(),
width: this.readUint32(),
height: this.readUint32(),
xOffset: this.readUint32(),
yOffset: this.readUint32(),
delayNumber: this.readUint16(),
delayDenominator: this.readUint16(),
disposeOp: this.readUint8(),
blendOp: this.readUint8(),
data: new Uint8Array(0),
};
this._frames.push(image);
}
// https://www.w3.org/TR/PNG/#11PLTE
private decodePLTE(length: number): void {
if (length % 3 !== 0) {
throw new RangeError(
`PLTE field length must be a multiple of 3. Got ${length}`,
);
}
const l = length / 3;
this._hasPalette = true;
const palette: IndexedColors = [];
this._palette = palette;
for (let i = 0; i < l; i++) {
palette.push([this.readUint8(), this.readUint8(), this.readUint8()]);
}
}
// https://www.w3.org/TR/PNG/#11IDAT
private decodeIDAT(length: number): void {
this._writingDataChunks = true;
const dataLength = length;
const dataOffset = this.offset + this.byteOffset;
this._inflator.push(new Uint8Array(this.buffer, dataOffset, dataLength));
if (this._inflator.err) {
throw new Error(
`Error while decompressing the data: ${this._inflator.err}`,
);
}
this.skip(length);
}
private decodeFDAT(length: number): void {
this._writingDataChunks = true;
let dataLength = length;
let dataOffset = this.offset + this.byteOffset;
dataOffset += 4;
dataLength -= 4;
this._inflator.push(new Uint8Array(this.buffer, dataOffset, dataLength));
if (this._inflator.err) {
throw new Error(
`Error while decompressing the data: ${this._inflator.err}`,
);
}
this.skip(length);
}
// https://www.w3.org/TR/PNG/#11tRNS
private decodetRNS(length: number): void {
switch (this._colorType) {
case ColorType.GREYSCALE:
case ColorType.TRUECOLOUR: {
if (length % 2 !== 0) {
throw new RangeError(
`tRNS chunk length must be a multiple of 2. Got ${length}`,
);
}
if (length / 2 > this._png.width * this._png.height) {
throw new Error(
`tRNS chunk contains more alpha values than there are pixels (${
length / 2
} vs ${this._png.width * this._png.height})`,
);
}
this._hasTransparency = true;
this._transparency = new Uint16Array(length / 2);
for (let i = 0; i < length / 2; i++) {
this._transparency[i] = this.readUint16();
}
break;
}
case ColorType.INDEXED_COLOUR: {
if (length > this._palette.length) {
throw new Error(
`tRNS chunk contains more alpha values than there are palette colors (${length} vs ${this._palette.length})`,
);
}
let i = 0;
for (; i < length; i++) {
const alpha = this.readByte();
this._palette[i].push(alpha);
}
for (; i < this._palette.length; i++) {
this._palette[i].push(255);
}
break;
}
// Kept for exhaustiveness.
/* eslint-disable unicorn/no-useless-switch-case */
case ColorType.UNKNOWN:
case ColorType.GREYSCALE_ALPHA:
case ColorType.TRUECOLOUR_ALPHA:
default: {
throw new Error(
`tRNS chunk is not supported for color type ${this._colorType}`,
);
}
/* eslint-enable unicorn/no-useless-switch-case */
}
}
// https://www.w3.org/TR/PNG/#11iCCP
private decodeiCCP(length: number): void {
const name = readKeyword(this);
const compressionMethod = this.readUint8();
if (compressionMethod !== CompressionMethod.DEFLATE) {
throw new Error(
`Unsupported iCCP compression method: ${compressionMethod}`,
);
}
const compressedProfile = this.readBytes(length - name.length - 2);
this._png.iccEmbeddedProfile = {
name,
profile: inflate(compressedProfile),
};
}
// https://www.w3.org/TR/PNG/#11pHYs
private decodepHYs(): void {
const ppuX = this.readUint32();
const ppuY = this.readUint32();
const unitSpecifier = this.readByte();
this._png.resolution = { x: ppuX, y: ppuY, unit: unitSpecifier };
}
private decodeApngImage() {
this._apng.width = this._png.width;
this._apng.height = this._png.height;
this._apng.channels = this._png.channels;
this._apng.depth = this._png.depth;
this._apng.numberOfFrames = this._numberOfFrames;
this._apng.numberOfPlays = this._numberOfPlays;
this._apng.text = this._png.text;
this._apng.resolution = this._png.resolution;
for (let i = 0; i < this._numberOfFrames; i++) {
const newFrame: DecodedApngFrame = {
sequenceNumber: this._frames[i].sequenceNumber,
delayNumber: this._frames[i].delayNumber,
delayDenominator: this._frames[i].delayDenominator,
data:
this._apng.depth === 8
? new Uint8Array(
this._apng.width * this._apng.height * this._apng.channels,
)
: new Uint16Array(
this._apng.width * this._apng.height * this._apng.channels,
),
};
const frame = this._frames.at(i);
if (frame) {
frame.data = decodeInterlaceNull({
data: frame.data as Uint8Array,
width: frame.width,
height: frame.height,
channels: this._apng.channels,
depth: this._apng.depth,
});
if (this._hasPalette) {
this._apng.palette = this._palette;
}
if (this._hasTransparency) {
this._apng.transparency = this._transparency;
}
if (
i === 0 ||
(frame.xOffset === 0 &&
frame.yOffset === 0 &&
frame.width === this._png.width &&
frame.height === this._png.height)
) {
newFrame.data = frame.data;
} else {
const prevFrame = this._apng.frames.at(i - 1);
this.disposeFrame(frame, prevFrame as DecodedApngFrame, newFrame);
this.addFrameDataToCanvas(newFrame, frame);
}
this._apng.frames.push(newFrame);
}
}
return this._apng;
}
private disposeFrame(
frame: ApngFrame,
prevFrame: DecodedApngFrame,
imageFrame: DecodedApngFrame,
): void {
switch (frame.disposeOp) {
case DisposeOpType.NONE:
break;
case DisposeOpType.BACKGROUND:
for (let row = 0; row < this._png.height; row++) {
for (let col = 0; col < this._png.width; col++) {
const index = (row * frame.width + col) * this._png.channels;
for (let channel = 0; channel < this._png.channels; channel++) {
imageFrame.data[index + channel] = 0;
}
}
}
break;
case DisposeOpType.PREVIOUS:
imageFrame.data.set(prevFrame.data);
break;
default:
throw new Error('Unknown disposeOp');
}
}
private addFrameDataToCanvas(
imageFrame: DecodedApngFrame,
frame: ApngFrame,
): void {
const maxValue = 1 << this._png.depth;
const calculatePixelIndices = (row: number, col: number) => {
const index =
((row + frame.yOffset) * this._png.width + frame.xOffset + col) *
this._png.channels;
const frameIndex = (row * frame.width + col) * this._png.channels;
return { index, frameIndex };
};
switch (frame.blendOp) {
case BlendOpType.SOURCE:
for (let row = 0; row < frame.height; row++) {
for (let col = 0; col < frame.width; col++) {
const { index, frameIndex } = calculatePixelIndices(row, col);
for (let channel = 0; channel < this._png.channels; channel++) {
imageFrame.data[index + channel] =
frame.data[frameIndex + channel];
}
}
}
break;
// https://www.w3.org/TR/png-3/#13Alpha-channel-processing
case BlendOpType.OVER:
for (let row = 0; row < frame.height; row++) {
for (let col = 0; col < frame.width; col++) {
const { index, frameIndex } = calculatePixelIndices(row, col);
for (let channel = 0; channel < this._png.channels; channel++) {
const sourceAlpha =
frame.data[frameIndex + this._png.channels - 1] / maxValue;
const foregroundValue =
channel % (this._png.channels - 1) === 0
? 1
: frame.data[frameIndex + channel];
const value = Math.floor(
sourceAlpha * foregroundValue +
(1 - sourceAlpha) * imageFrame.data[index + channel],
);
imageFrame.data[index + channel] += value;
}
}
}
break;
default:
throw new Error('Unknown blendOp');
}
}
private decodeImage(): void {
if (this._inflator.err) {
throw new Error(
`Error while decompressing the data: ${this._inflator.err}`,
);
}
const data = this._isAnimated
? (this._frames?.at(0) as ApngFrame).data
: this._inflator.result;
if (this._filterMethod !== FilterMethod.ADAPTIVE) {
throw new Error(`Filter method ${this._filterMethod} not supported`);
}
if (this._interlaceMethod === InterlaceMethod.NO_INTERLACE) {
this._png.data = decodeInterlaceNull({
data: data as Uint8Array,
width: this._png.width,
height: this._png.height,
channels: this._png.channels,
depth: this._png.depth,
});
} else if (this._interlaceMethod === InterlaceMethod.ADAM7) {
this._png.data = decodeInterlaceAdam7({
data: data as Uint8Array,
width: this._png.width,
height: this._png.height,
channels: this._png.channels,
depth: this._png.depth,
});
} else {
throw new Error(
`Interlace method ${this._interlaceMethod} not supported`,
);
}
if (this._hasPalette) {
this._png.palette = this._palette;
}
if (this._hasTransparency) {
this._png.transparency = this._transparency;
}
}
private pushDataToFrame() {
const result = this._inflator.result;
const lastFrame = this._frames.at(-1);
if (lastFrame) {
lastFrame.data = result as Uint8Array;
} else {
this._frames.push({
sequenceNumber: 0,
width: this._png.width,
height: this._png.height,
xOffset: 0,
yOffset: 0,
delayNumber: 0,
delayDenominator: 0,
disposeOp: DisposeOpType.NONE,
blendOp: BlendOpType.SOURCE,
data: result as Uint8Array,
});
}
this._inflator = new Inflator();
this._writingDataChunks = false;
}
}
function checkBitDepth(value: number): BitDepth {
if (
value !== 1 &&
value !== 2 &&
value !== 4 &&
value !== 8 &&
value !== 16
) {
throw new Error(`invalid bit depth: ${value}`);
}
return value;
}

View File

@@ -0,0 +1,327 @@
import { IOBuffer } from 'iobuffer';
import { deflate } from 'pako';
import { writeCrc } from './helpers/crc';
import { writeSignature } from './helpers/signature';
import { encodetEXt } from './helpers/text';
import {
InterlaceMethod,
ColorType,
CompressionMethod,
FilterMethod,
} from './internalTypes';
import type {
DeflateFunctionOptions,
PngEncoderOptions,
ImageData,
PngDataArray,
BitDepth,
IndexedColors,
} from './types';
const defaultZlibOptions: DeflateFunctionOptions = {
level: 3,
};
interface PngToEncode {
width: number;
height: number;
data: PngDataArray;
depth: BitDepth;
channels: number;
text?: ImageData['text'];
palette?: IndexedColors;
}
export default class PngEncoder extends IOBuffer {
private readonly _png: PngToEncode;
private readonly _zlibOptions: DeflateFunctionOptions;
private _colorType: ColorType;
private readonly _interlaceMethod: InterlaceMethod;
public constructor(data: ImageData, options: PngEncoderOptions = {}) {
super();
this._colorType = ColorType.UNKNOWN;
this._zlibOptions = { ...defaultZlibOptions, ...options.zlib };
this._png = this._checkData(data);
this._interlaceMethod =
(options.interlace === 'Adam7'
? InterlaceMethod.ADAM7
: InterlaceMethod.NO_INTERLACE) ?? InterlaceMethod.NO_INTERLACE;
this.setBigEndian();
}
public encode(): Uint8Array {
writeSignature(this);
this.encodeIHDR();
if (this._png.palette) {
this.encodePLTE();
if (this._png.palette[0].length === 4) {
this.encodeTRNS();
}
}
this.encodeData();
if (this._png.text) {
for (const [keyword, text] of Object.entries(this._png.text)) {
encodetEXt(this, keyword, text);
}
}
this.encodeIEND();
return this.toArray();
}
// https://www.w3.org/TR/PNG/#11IHDR
private encodeIHDR(): void {
this.writeUint32(13);
this.writeChars('IHDR');
this.writeUint32(this._png.width);
this.writeUint32(this._png.height);
this.writeByte(this._png.depth);
this.writeByte(this._colorType);
this.writeByte(CompressionMethod.DEFLATE);
this.writeByte(FilterMethod.ADAPTIVE);
this.writeByte(this._interlaceMethod);
writeCrc(this, 17);
}
// https://www.w3.org/TR/PNG/#11IEND
private encodeIEND(): void {
this.writeUint32(0);
this.writeChars('IEND');
writeCrc(this, 4);
}
private encodePLTE() {
const paletteLength = (this._png.palette?.length as number) * 3;
this.writeUint32(paletteLength);
this.writeChars('PLTE');
for (const color of this._png.palette as IndexedColors) {
this.writeByte(color[0]);
this.writeByte(color[1]);
this.writeByte(color[2]);
}
writeCrc(this, 4 + paletteLength);
}
private encodeTRNS() {
const alpha = (this._png.palette as IndexedColors).filter((color) => {
return color.at(-1) !== 255;
});
this.writeUint32(alpha.length);
this.writeChars('tRNS');
for (const el of alpha) {
this.writeByte(el.at(-1) as number);
}
writeCrc(this, 4 + alpha.length);
}
// https://www.w3.org/TR/PNG/#11IDAT
private encodeIDAT(data: PngDataArray): void {
this.writeUint32(data.length);
this.writeChars('IDAT');
this.writeBytes(data);
writeCrc(this, data.length + 4);
}
private encodeData(): void {
const { width, height, channels, depth, data } = this._png;
const slotsPerLine =
depth <= 8
? Math.ceil((width * depth) / 8) * channels
: Math.ceil((((width * depth) / 8) * channels) / 2);
const newData = new IOBuffer().setBigEndian();
let offset = 0;
if (this._interlaceMethod === InterlaceMethod.NO_INTERLACE) {
for (let i = 0; i < height; i++) {
newData.writeByte(0); // no filter
if (depth === 16) {
offset = writeDataUint16(data, newData, slotsPerLine, offset);
} else {
offset = writeDataBytes(data, newData, slotsPerLine, offset);
}
}
} else if (this._interlaceMethod === InterlaceMethod.ADAM7) {
// Adam7 interlacing
offset = writeDataInterlaced(this._png, data, newData, offset);
}
const buffer = newData.toArray();
const compressed = deflate(buffer, this._zlibOptions);
this.encodeIDAT(compressed);
}
private _checkData(data: ImageData): PngToEncode {
const { colorType, channels, depth } = getColorType(data, data.palette);
const png: PngToEncode = {
width: checkInteger(data.width, 'width'),
height: checkInteger(data.height, 'height'),
channels,
data: data.data,
depth,
text: data.text,
palette: data.palette,
};
this._colorType = colorType;
const expectedSize =
depth < 8
? Math.ceil((png.width * depth) / 8) * png.height * channels
: png.width * png.height * channels;
if (png.data.length !== expectedSize) {
throw new RangeError(
`wrong data size. Found ${png.data.length}, expected ${expectedSize}`,
);
}
return png;
}
}
function checkInteger(value: number, name: string): number {
if (Number.isInteger(value) && value > 0) {
return value;
}
throw new TypeError(`${name} must be a positive integer`);
}
interface GetColorTypeReturn {
channels: number;
depth: BitDepth;
colorType: ColorType;
}
function getColorType(
data: ImageData,
palette?: IndexedColors,
): GetColorTypeReturn {
const { channels = 4, depth = 8 } = data;
if (channels !== 4 && channels !== 3 && channels !== 2 && channels !== 1) {
throw new RangeError(`unsupported number of channels: ${channels}`);
}
const returnValue: GetColorTypeReturn = {
channels,
depth,
colorType: ColorType.UNKNOWN,
};
switch (channels) {
case 4:
returnValue.colorType = ColorType.TRUECOLOUR_ALPHA;
break;
case 3:
returnValue.colorType = ColorType.TRUECOLOUR;
break;
case 1:
if (palette) {
returnValue.colorType = ColorType.INDEXED_COLOUR;
} else {
returnValue.colorType = ColorType.GREYSCALE;
}
break;
case 2:
returnValue.colorType = ColorType.GREYSCALE_ALPHA;
break;
default:
throw new Error('unsupported number of channels');
}
return returnValue;
}
function writeDataBytes(
data: PngDataArray,
newData: IOBuffer,
slotsPerLine: number,
offset: number,
): number {
for (let j = 0; j < slotsPerLine; j++) {
newData.writeByte(data[offset++]);
}
return offset;
}
function writeDataInterlaced(
imageData: PngToEncode,
data: PngDataArray,
newData: IOBuffer,
offset: number,
) {
const passes = [
{ x: 0, y: 0, xStep: 8, yStep: 8 },
{ x: 4, y: 0, xStep: 8, yStep: 8 },
{ x: 0, y: 4, xStep: 4, yStep: 8 },
{ x: 2, y: 0, xStep: 4, yStep: 4 },
{ x: 0, y: 2, xStep: 2, yStep: 4 },
{ x: 1, y: 0, xStep: 2, yStep: 2 },
{ x: 0, y: 1, xStep: 1, yStep: 2 },
];
const { width, height, channels, depth } = imageData;
let pixelSize = 0;
if (depth === 16) {
pixelSize = (channels * depth) / 8 / 2;
} else {
pixelSize = (channels * depth) / 8;
}
// Process each pass
for (let passIndex = 0; passIndex < 7; passIndex++) {
const pass = passes[passIndex];
const passWidth = Math.floor(
(width - pass.x + pass.xStep - 1) / pass.xStep,
);
const passHeight = Math.floor(
(height - pass.y + pass.yStep - 1) / pass.yStep,
);
if (passWidth <= 0 || passHeight <= 0) continue;
const passLineBytes = passWidth * pixelSize;
// For each scanline in this pass
for (let y = 0; y < passHeight; y++) {
const imageY = pass.y + y * pass.yStep;
// Extract raw scanline data
const rawScanline =
depth <= 8
? new Uint8Array(passLineBytes)
: new Uint16Array(passLineBytes);
let rawOffset = 0;
for (let x = 0; x < passWidth; x++) {
const imageX = pass.x + x * pass.xStep;
if (imageX < width && imageY < height) {
const srcPos = (imageY * width + imageX) * pixelSize;
for (let i = 0; i < pixelSize; i++) {
rawScanline[rawOffset++] = data[srcPos + i];
}
}
}
newData.writeByte(0); // no filter
if (depth === 8) {
newData.writeBytes(rawScanline);
} else if (depth === 16) {
for (const value of rawScanline) {
newData.writeByte((value >> 8) & 0xff); // High byte
newData.writeByte(value & 0xff);
}
}
}
}
return offset;
}
function writeDataUint16(
data: PngDataArray,
newData: IOBuffer,
slotsPerLine: number,
offset: number,
): number {
for (let j = 0; j < slotsPerLine; j++) {
newData.writeUint16(data[offset++]);
}
return offset;
}

View File

@@ -0,0 +1,78 @@
import type { DecodedPng, IndexedColorBitDepth } from './types';
/**
* Converts indexed data into RGB/RGBA format
* @param decodedImage - Image to decode data from.
* @returns Uint8Array with RGB data.
*/
export function convertIndexedToRgb(decodedImage: DecodedPng) {
const palette = decodedImage.palette;
const depth = decodedImage.depth as IndexedColorBitDepth;
if (!palette) {
throw new Error('Color palette is undefined.');
}
checkDataSize(decodedImage);
const indexSize = decodedImage.width * decodedImage.height;
const resSize = indexSize * palette[0].length;
const res = new Uint8Array(resSize);
let indexPos = 0;
let offset = 0;
const indexes = new Uint8Array(indexSize);
let bit = 0xff;
switch (depth) {
case 1:
bit = 0x80;
break;
case 2:
bit = 0xc0;
break;
case 4:
bit = 0xf0;
break;
case 8:
bit = 0xff;
break;
default:
throw new Error('Incorrect depth value');
}
for (const byte of decodedImage.data) {
let bit2 = bit;
let shift = 8;
while (bit2) {
shift -= depth;
indexes[indexPos++] = (byte & bit2) >> shift;
bit2 = bit2 >> depth;
if (indexPos % decodedImage.width === 0) {
break;
}
}
}
if (decodedImage.palette) {
for (const index of indexes) {
const color = decodedImage.palette.at(index);
if (!color) {
throw new Error('Incorrect index of palette color');
}
res.set(color, offset);
offset += color.length;
}
}
return res;
}
function checkDataSize(image: DecodedPng): void {
const expectedSize =
image.depth < 8
? Math.ceil((image.width * image.depth) / 8) *
image.height *
image.channels
: image.width * image.height * image.channels;
if (image.data.length !== expectedSize) {
throw new RangeError(
`wrong data size. Found ${image.data.length}, expected ${expectedSize}`,
);
}
}

View File

@@ -0,0 +1,56 @@
import {
unfilterAverage,
unfilterNone,
unfilterPaeth,
unfilterSub,
unfilterUp,
} from './unfilter';
/**
* Apllies filter on scanline based on the filter type.
* @param filterType - The filter type to apply.
* @param currentLine - The current line of pixel data.
* @param newLine - The new line of pixel data.
* @param prevLine - The previous line of pixel data.
* @param passLineBytes - The number of bytes in the pass line.
* @param bytesPerPixel - The number of bytes per pixel.
*/
export function applyUnfilter(
filterType: number,
currentLine: Uint8Array,
newLine: Uint8Array,
prevLine: Uint8Array,
passLineBytes: number,
bytesPerPixel: number,
) {
switch (filterType) {
case 0:
unfilterNone(currentLine, newLine, passLineBytes);
break;
case 1:
unfilterSub(currentLine, newLine, passLineBytes, bytesPerPixel);
break;
case 2:
unfilterUp(currentLine, newLine, prevLine, passLineBytes);
break;
case 3:
unfilterAverage(
currentLine,
newLine,
prevLine,
passLineBytes,
bytesPerPixel,
);
break;
case 4:
unfilterPaeth(
currentLine,
newLine,
prevLine,
passLineBytes,
bytesPerPixel,
);
break;
default:
throw new Error(`Unsupported filter: ${filterType}`);
}
}

View File

@@ -0,0 +1,65 @@
import type { IOBuffer } from 'iobuffer';
const crcTable: number[] = [];
for (let n = 0; n < 256; n++) {
let c = n;
for (let k = 0; k < 8; k++) {
if (c & 1) {
c = 0xedb88320 ^ (c >>> 1);
} else {
c = c >>> 1;
}
}
crcTable[n] = c;
}
const initialCrc = 0xffffffff;
function updateCrc(
currentCrc: number,
data: Uint8Array,
length: number,
): number {
let c = currentCrc;
for (let n = 0; n < length; n++) {
c = crcTable[(c ^ data[n]) & 0xff] ^ (c >>> 8);
}
return c;
}
function crc(data: Uint8Array, length: number): number {
return (updateCrc(initialCrc, data, length) ^ initialCrc) >>> 0;
}
export function checkCrc(
buffer: IOBuffer,
crcLength: number,
chunkName: string,
) {
const expectedCrc = buffer.readUint32();
const actualCrc = crc(
new Uint8Array(
buffer.buffer,
buffer.byteOffset + buffer.offset - crcLength - 4,
crcLength,
),
crcLength,
); // "- 4" because we already advanced by reading the CRC
if (actualCrc !== expectedCrc) {
throw new Error(
`CRC mismatch for chunk ${chunkName}. Expected ${expectedCrc}, found ${actualCrc}`,
);
}
}
export function writeCrc(buffer: IOBuffer, length: number) {
buffer.writeUint32(
crc(
new Uint8Array(
buffer.buffer,
buffer.byteOffset + buffer.offset - length,
length,
),
length,
),
);
}

View File

@@ -0,0 +1,93 @@
import { applyUnfilter } from './applyUnfilter';
import type { DecodeInterlaceNullParams } from './decodeInterlaceNull';
const uint16 = new Uint16Array([0x00ff]);
const uint8 = new Uint8Array(uint16.buffer);
const osIsLittleEndian = uint8[0] === 0xff;
/**
* Decodes the Adam7 interlaced PNG data.
*
* @param params - DecodeInterlaceNullParams
* @returns - array of pixel data.
*/
export function decodeInterlaceAdam7(params: DecodeInterlaceNullParams) {
const { data, width, height, channels, depth } = params;
// Adam7 interlacing pattern
const passes = [
{ x: 0, y: 0, xStep: 8, yStep: 8 }, // Pass 1
{ x: 4, y: 0, xStep: 8, yStep: 8 }, // Pass 2
{ x: 0, y: 4, xStep: 4, yStep: 8 }, // Pass 3
{ x: 2, y: 0, xStep: 4, yStep: 4 }, // Pass 4
{ x: 0, y: 2, xStep: 2, yStep: 4 }, // Pass 5
{ x: 1, y: 0, xStep: 2, yStep: 2 }, // Pass 6
{ x: 0, y: 1, xStep: 1, yStep: 2 }, // Pass 7
];
const bytesPerPixel = Math.ceil(depth / 8) * channels;
const resultData = new Uint8Array(height * width * bytesPerPixel);
let offset = 0;
// Process each pass
for (let passIndex = 0; passIndex < 7; passIndex++) {
const pass = passes[passIndex];
// Calculate pass dimensions
const passWidth = Math.ceil((width - pass.x) / pass.xStep);
const passHeight = Math.ceil((height - pass.y) / pass.yStep);
if (passWidth <= 0 || passHeight <= 0) continue;
const passLineBytes = passWidth * bytesPerPixel;
const prevLine = new Uint8Array(passLineBytes);
// Process each scanline in this pass
for (let y = 0; y < passHeight; y++) {
// First byte is the filter type
const filterType = data[offset++];
const currentLine = data.subarray(offset, offset + passLineBytes);
offset += passLineBytes;
// Create a new line for the unfiltered data
const newLine = new Uint8Array(passLineBytes);
// Apply the appropriate unfilter
applyUnfilter(
filterType,
currentLine,
newLine,
prevLine,
passLineBytes,
bytesPerPixel,
);
prevLine.set(newLine);
for (let x = 0; x < passWidth; x++) {
const outputX = pass.x + x * pass.xStep;
const outputY = pass.y + y * pass.yStep;
if (outputX >= width || outputY >= height) continue;
for (let i = 0; i < bytesPerPixel; i++) {
resultData[(outputY * width + outputX) * bytesPerPixel + i] =
newLine[x * bytesPerPixel + i];
}
}
}
}
if (depth === 16) {
const uint16Data = new Uint16Array(resultData.buffer);
if (osIsLittleEndian) {
for (let k = 0; k < uint16Data.length; k++) {
// PNG is always big endian. Swap the bytes.
uint16Data[k] = swap16(uint16Data[k]);
}
}
return uint16Data;
} else {
return resultData;
}
}
function swap16(val: number): number {
return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
}

View File

@@ -0,0 +1,94 @@
import type { PngDataArray } from '../types';
import {
unfilterAverage,
unfilterNone,
unfilterPaeth,
unfilterSub,
unfilterUp,
} from './unfilter';
const uint16 = new Uint16Array([0x00ff]);
const uint8 = new Uint8Array(uint16.buffer);
const osIsLittleEndian = uint8[0] === 0xff;
const empty = new Uint8Array(0);
export interface DecodeInterlaceNullParams {
data: Uint8Array;
width: number;
height: number;
channels: number;
depth: number;
}
export function decodeInterlaceNull(
params: DecodeInterlaceNullParams,
): PngDataArray {
const { data, width, height, channels, depth } = params;
const bytesPerPixel = Math.ceil(depth / 8) * channels;
const bytesPerLine = Math.ceil((depth / 8) * channels * width);
const newData = new Uint8Array(height * bytesPerLine);
let prevLine = empty;
let offset = 0;
let currentLine;
let newLine;
for (let i = 0; i < height; i++) {
currentLine = data.subarray(offset + 1, offset + 1 + bytesPerLine);
newLine = newData.subarray(i * bytesPerLine, (i + 1) * bytesPerLine);
switch (data[offset]) {
case 0:
unfilterNone(currentLine, newLine, bytesPerLine);
break;
case 1:
unfilterSub(currentLine, newLine, bytesPerLine, bytesPerPixel);
break;
case 2:
unfilterUp(currentLine, newLine, prevLine, bytesPerLine);
break;
case 3:
unfilterAverage(
currentLine,
newLine,
prevLine,
bytesPerLine,
bytesPerPixel,
);
break;
case 4:
unfilterPaeth(
currentLine,
newLine,
prevLine,
bytesPerLine,
bytesPerPixel,
);
break;
default:
throw new Error(`Unsupported filter: ${data[offset]}`);
}
prevLine = newLine;
offset += bytesPerLine + 1;
}
if (depth === 16) {
const uint16Data = new Uint16Array(newData.buffer);
if (osIsLittleEndian) {
for (let k = 0; k < uint16Data.length; k++) {
// PNG is always big endian. Swap the bytes.
uint16Data[k] = swap16(uint16Data[k]);
}
}
return uint16Data;
} else {
return newData;
}
}
function swap16(val: number): number {
return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
}

View File

@@ -0,0 +1,27 @@
import type { IOBuffer } from 'iobuffer';
// https://www.w3.org/TR/PNG/#5PNG-file-signature
const pngSignature = Uint8Array.of(137, 80, 78, 71, 13, 10, 26, 10);
export function writeSignature(buffer: IOBuffer) {
buffer.writeBytes(pngSignature);
}
export function checkSignature(buffer: IOBuffer) {
if (!hasPngSignature(buffer.readBytes(pngSignature.length))) {
throw new Error('wrong PNG signature');
}
}
export function hasPngSignature(array: ArrayLike<number>) {
if (array.length < pngSignature.length) {
return false;
}
for (let i = 0; i < pngSignature.length; i++) {
if (array[i] !== pngSignature[i]) {
return false;
}
}
return true;
}

View File

@@ -0,0 +1,71 @@
import type { IOBuffer } from 'iobuffer';
import { writeCrc } from './crc';
// https://www.w3.org/TR/png/#11tEXt
export const textChunkName = 'tEXt';
const NULL = 0;
const latin1Decoder = new TextDecoder('latin1');
function validateKeyword(keyword: string) {
validateLatin1(keyword);
if (keyword.length === 0 || keyword.length > 79) {
throw new Error('keyword length must be between 1 and 79');
}
}
// eslint-disable-next-line no-control-regex
const latin1Regex = /^[\u0000-\u00FF]*$/;
function validateLatin1(text: string) {
if (!latin1Regex.test(text)) {
throw new Error('invalid latin1 text');
}
}
export function decodetEXt(
text: Record<string, string>,
buffer: IOBuffer,
length: number,
) {
const keyword = readKeyword(buffer);
text[keyword] = readLatin1(buffer, length - keyword.length - 1);
}
export function encodetEXt(buffer: IOBuffer, keyword: string, text: string) {
validateKeyword(keyword);
validateLatin1(text);
const length = keyword.length + 1 /* NULL */ + text.length;
buffer.writeUint32(length);
buffer.writeChars(textChunkName);
buffer.writeChars(keyword);
buffer.writeByte(NULL);
buffer.writeChars(text);
writeCrc(buffer, length + 4);
}
// https://www.w3.org/TR/png/#11keywords
export function readKeyword(buffer: IOBuffer): string {
buffer.mark();
while (buffer.readByte() !== NULL) {
/* advance */
}
const end = buffer.offset;
buffer.reset();
const keyword = latin1Decoder.decode(
buffer.readBytes(end - buffer.offset - 1),
);
// NULL
buffer.skip(1);
validateKeyword(keyword);
return keyword;
}
export function readLatin1(buffer: IOBuffer, length: number): string {
return latin1Decoder.decode(buffer.readBytes(length));
}

View File

@@ -0,0 +1,115 @@
import type { PngDataArray } from '../types';
export function unfilterNone(
currentLine: PngDataArray,
newLine: PngDataArray,
bytesPerLine: number,
): void {
for (let i = 0; i < bytesPerLine; i++) {
newLine[i] = currentLine[i];
}
}
export function unfilterSub(
currentLine: PngDataArray,
newLine: PngDataArray,
bytesPerLine: number,
bytesPerPixel: number,
): void {
let i = 0;
for (; i < bytesPerPixel; i++) {
// just copy first bytes
newLine[i] = currentLine[i];
}
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + newLine[i - bytesPerPixel]) & 0xff;
}
}
export function unfilterUp(
currentLine: PngDataArray,
newLine: PngDataArray,
prevLine: PngDataArray,
bytesPerLine: number,
): void {
let i = 0;
if (prevLine.length === 0) {
// just copy bytes for first line
for (; i < bytesPerLine; i++) {
newLine[i] = currentLine[i];
}
} else {
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + prevLine[i]) & 0xff;
}
}
}
export function unfilterAverage(
currentLine: PngDataArray,
newLine: PngDataArray,
prevLine: PngDataArray,
bytesPerLine: number,
bytesPerPixel: number,
): void {
let i = 0;
if (prevLine.length === 0) {
for (; i < bytesPerPixel; i++) {
newLine[i] = currentLine[i];
}
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + (newLine[i - bytesPerPixel] >> 1)) & 0xff;
}
} else {
for (; i < bytesPerPixel; i++) {
newLine[i] = (currentLine[i] + (prevLine[i] >> 1)) & 0xff;
}
for (; i < bytesPerLine; i++) {
newLine[i] =
(currentLine[i] + ((newLine[i - bytesPerPixel] + prevLine[i]) >> 1)) &
0xff;
}
}
}
export function unfilterPaeth(
currentLine: PngDataArray,
newLine: PngDataArray,
prevLine: PngDataArray,
bytesPerLine: number,
bytesPerPixel: number,
): void {
let i = 0;
if (prevLine.length === 0) {
for (; i < bytesPerPixel; i++) {
newLine[i] = currentLine[i];
}
for (; i < bytesPerLine; i++) {
newLine[i] = (currentLine[i] + newLine[i - bytesPerPixel]) & 0xff;
}
} else {
for (; i < bytesPerPixel; i++) {
newLine[i] = (currentLine[i] + prevLine[i]) & 0xff;
}
for (; i < bytesPerLine; i++) {
newLine[i] =
(currentLine[i] +
paethPredictor(
newLine[i - bytesPerPixel],
prevLine[i],
prevLine[i - bytesPerPixel],
)) &
0xff;
}
}
}
function paethPredictor(a: number, b: number, c: number): number {
const p = a + b - c;
const pa = Math.abs(p - a);
const pb = Math.abs(p - b);
const pc = Math.abs(p - c);
if (pa <= pb && pa <= pc) return a;
else if (pb <= pc) return b;
else return c;
}

38
SuiviREForamteur/node_modules/fast-png/src/index.ts generated vendored Normal file
View File

@@ -0,0 +1,38 @@
import PngDecoder from './PngDecoder';
import PngEncoder from './PngEncoder';
import type {
DecoderInputType,
PngDecoderOptions,
DecodedPng,
DecodedApng,
ImageData,
PngEncoderOptions,
} from './types';
export { hasPngSignature } from './helpers/signature';
export * from './types';
function decodePng(
data: DecoderInputType,
options?: PngDecoderOptions,
): DecodedPng {
const decoder = new PngDecoder(data, options);
return decoder.decode();
}
function encodePng(png: ImageData, options?: PngEncoderOptions): Uint8Array {
const encoder = new PngEncoder(png, options);
return encoder.encode();
}
function decodeApng(
data: DecoderInputType,
options?: PngDecoderOptions,
): DecodedApng {
const decoder = new PngDecoder(data, options);
return decoder.decodeApng();
}
export { decodePng as decode, encodePng as encode, decodeApng };
export { convertIndexedToRgb } from './convertIndexedToRgb';

View File

@@ -0,0 +1,46 @@
export const ColorType = {
UNKNOWN: -1,
GREYSCALE: 0,
TRUECOLOUR: 2,
INDEXED_COLOUR: 3,
GREYSCALE_ALPHA: 4,
TRUECOLOUR_ALPHA: 6,
} as const;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type ColorType = (typeof ColorType)[keyof typeof ColorType];
export const CompressionMethod = {
UNKNOWN: -1,
DEFLATE: 0,
} as const;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type CompressionMethod =
(typeof CompressionMethod)[keyof typeof CompressionMethod];
export const FilterMethod = {
UNKNOWN: -1,
ADAPTIVE: 0,
} as const;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type FilterMethod = (typeof FilterMethod)[keyof typeof FilterMethod];
export const InterlaceMethod = {
UNKNOWN: -1,
NO_INTERLACE: 0,
ADAM7: 1,
} as const;
export const DisposeOpType = {
NONE: 0,
BACKGROUND: 1,
PREVIOUS: 2,
} as const;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type DisposeOpType = (typeof DisposeOpType)[keyof typeof DisposeOpType];
export const BlendOpType = {
SOURCE: 0,
OVER: 1,
} as const;
// eslint-disable-next-line @typescript-eslint/no-redeclare
export type InterlaceMethod =
(typeof InterlaceMethod)[keyof typeof InterlaceMethod];

113
SuiviREForamteur/node_modules/fast-png/src/types.ts generated vendored Normal file
View File

@@ -0,0 +1,113 @@
import type { IOBuffer } from 'iobuffer';
import type { DeflateFunctionOptions } from 'pako';
export type { DeflateFunctionOptions } from 'pako';
export type PngDataArray = Uint8Array | Uint8ClampedArray | Uint16Array;
export type DecoderInputType = IOBuffer | ArrayBufferLike | ArrayBufferView;
export type BitDepth = 1 | 2 | 4 | 8 | 16;
export type IndexedColorBitDepth = 1 | 2 | 4 | 8;
export interface PngResolution {
/**
* Pixels per unit, X axis
*/
x: number;
/**
* Pixels per unit, Y axis
*/
y: number;
/**
* Unit specifier
*/
unit: ResolutionUnitSpecifier;
}
export enum ResolutionUnitSpecifier {
/**
* Unit is unknown
*/
UNKNOWN = 0,
/**
* Unit is the metre
*/
METRE = 1,
}
export interface ImageData {
width: number;
height: number;
data: PngDataArray;
depth?: BitDepth;
channels?: number;
text?: Record<string, string>;
palette?: IndexedColors;
transparency?: Uint16Array;
}
export interface DecodedPng {
width: number;
height: number;
data: PngDataArray;
depth: BitDepth;
channels: number;
text: Record<string, string>;
resolution?: PngResolution;
palette?: IndexedColors;
transparency?: Uint16Array;
iccEmbeddedProfile?: IccEmbeddedProfile;
}
export interface DecodedApng {
width: number;
height: number;
depth: BitDepth;
channels: number;
numberOfFrames: number;
numberOfPlays: number;
text: Record<string, string>;
resolution?: PngResolution;
palette?: IndexedColors;
transparency?: Uint16Array;
iccEmbeddedProfile?: IccEmbeddedProfile;
frames: DecodedApngFrame[];
}
export interface ApngFrame {
sequenceNumber: number;
width: number;
height: number;
xOffset: number;
yOffset: number;
delayNumber: number;
delayDenominator: number;
disposeOp: number;
blendOp: number;
data: PngDataArray;
}
export interface DecodedApngFrame {
sequenceNumber: number;
delayNumber: number;
delayDenominator: number;
data: PngDataArray;
}
export interface PngDecoderOptions {
checkCrc?: boolean;
}
export interface PngEncoderOptions {
interlace?: 'null' | 'Adam7';
zlib?: DeflateFunctionOptions;
}
export type IndexedColors = number[][];
export interface IccEmbeddedProfile {
name: string;
profile: Uint8Array;
}