image-in-browser
Version:
Package for encoding / decoding images, transforming images, applying filters, drawing primitives on images on the client side (no need for server Node.js)
141 lines • 5.05 kB
JavaScript
import { InputBuffer } from '../common/input-buffer.js';
import { OutputBuffer } from '../common/output-buffer.js';
import { BmpFileHeader } from './bmp/bmp-file-header.js';
import { DibDecoder } from './dib-decoder.js';
import { IcoBmpInfo } from './ico/ico-bmp-info.js';
import { IcoInfo } from './ico/ico-info.js';
import { PngDecoder } from './png-decoder.js';
import { FrameType } from '../image/frame-type.js';
import { ImageFormat } from './image-format.js';
export class IcoDecoder {
get format() {
return ImageFormat.ico;
}
get numFrames() {
return this._info !== undefined ? this._info.numFrames : 0;
}
isValidFile(bytes) {
this._input = new InputBuffer({
buffer: bytes,
});
this._info = IcoInfo.read(this._input);
return this._info !== undefined;
}
startDecode(bytes) {
this._input = new InputBuffer({
buffer: bytes,
});
this._info = IcoInfo.read(this._input);
return this._info;
}
decode(opt) {
var _a;
const bytes = opt.bytes;
const info = this.startDecode(bytes);
if (info === undefined) {
return undefined;
}
if (info.images.length === 1 || opt.frameIndex !== undefined) {
return this.decodeFrame((_a = opt.frameIndex) !== null && _a !== void 0 ? _a : 0);
}
let firstImage = undefined;
for (let i = 0; i < info.images.length; i++) {
const frame = this.decodeFrame(i);
if (frame === undefined) {
continue;
}
if (firstImage === undefined) {
frame.frameType = FrameType.sequence;
firstImage = frame;
}
else {
firstImage.addFrame(frame);
}
}
return firstImage;
}
decodeFrame(frameIndex) {
if (this._input === undefined ||
this._info === undefined ||
frameIndex >= this._info.numFrames) {
return undefined;
}
const imageInfo = this._info.images[frameIndex];
const imageBuffer = this._input.buffer.subarray(this._input.start + imageInfo.bytesOffset, this._input.start + imageInfo.bytesOffset + imageInfo.bytesSize);
const png = new PngDecoder();
if (png.isValidFile(imageBuffer)) {
return png.decode({
bytes: imageBuffer,
});
}
const dummyBmpHeader = new OutputBuffer({
size: 14,
});
dummyBmpHeader.writeUint16(BmpFileHeader.signature);
dummyBmpHeader.writeUint32(imageInfo.bytesSize);
dummyBmpHeader.writeUint32(0);
dummyBmpHeader.writeUint32(0);
const bmpInfo = new IcoBmpInfo(new InputBuffer({
buffer: imageBuffer,
}), new BmpFileHeader(new InputBuffer({
buffer: dummyBmpHeader.getBytes(),
})));
if (bmpInfo.headerSize !== 40 && bmpInfo.planes !== 1) {
return undefined;
}
let offset = 0;
if (bmpInfo.totalColors === 0 && bmpInfo.bitsPerPixel <= 8) {
offset = 40 + 4 * (1 << bmpInfo.bitsPerPixel);
}
else {
offset = 40 + 4 * bmpInfo.totalColors;
}
bmpInfo.header.imageOffset = offset;
dummyBmpHeader.length -= 4;
dummyBmpHeader.writeUint32(offset);
const inp = new InputBuffer({
buffer: imageBuffer,
});
const bmp = new DibDecoder(inp, bmpInfo, true);
const image = bmp.decodeFrame(0);
if (image === undefined || bmpInfo.bitsPerPixel >= 32) {
return image;
}
const padding = 32 - (bmpInfo.width % 32);
const rowLength = Math.trunc((padding === 32 ? bmpInfo.width : bmpInfo.width + padding) / 8);
for (let y = 0; y < bmpInfo.height; y++) {
const line = bmpInfo.readBottomUp ? y : image.height - 1 - y;
const row = inp.readRange(rowLength);
const p = image.getPixel(0, line);
for (let x = 0; x < bmpInfo.width;) {
const b = row.read();
for (let j = 7; j > -1 && x < bmpInfo.width; j--) {
if ((b & (1 << j)) !== 0) {
p.a = 0;
}
p.next();
x++;
}
}
}
return image;
}
decodeImageLargest(bytes) {
const info = this.startDecode(bytes);
if (info === undefined) {
return undefined;
}
let largestFrame = 0;
let largestSize = 0;
for (let i = 0; i < info.images.length; i++) {
const image = info.images[i];
const size = image.width * image.height;
if (size > largestSize) {
largestSize = size;
largestFrame = i;
}
}
return this.decodeFrame(largestFrame);
}
}
//# sourceMappingURL=ico-decoder.js.map