UNPKG

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)

553 lines 20.2 kB
import { InputBuffer } from '../common/input-buffer.js'; import { MemoryImage } from '../image/image.js'; import { ImageFormat } from './image-format.js'; import { PvrAppleInfo } from './pvr/pvr-apple-info.js'; import { PvrPacket } from './pvr/pvr-packet.js'; import { Pvr2Info } from './pvr/pvr2-info.js'; import { Pvr3Info } from './pvr/pvr3-info.js'; export class PvrDecoder { get format() { return ImageFormat.pvr; } get numFrames() { return 1; } decodePvr3Header(bytes) { const input = new InputBuffer({ buffer: bytes, }); const size = input.readUint32(); if (size !== PvrDecoder.pvrHeaderSize) { return undefined; } const version = input.readUint32(); const pvr3Signature = 0x03525650; if (version !== pvr3Signature) { return undefined; } const flags = input.readUint32(); const format = input.readUint32(); const order = [input.read(), input.read(), input.read(), input.read()]; const colorSpace = input.readUint32(); const channelType = input.readUint32(); const height = input.readUint32(); const width = input.readUint32(); const depth = input.readUint32(); const numSurfaces = input.readUint32(); const numFaces = input.readUint32(); const mipCount = input.readUint32(); const metadataSize = input.readUint32(); const info = new Pvr3Info({ flags: flags, format: format, order: order, colorSpace: colorSpace, channelType: channelType, height: height, width: width, depth: depth, numSurfaces: numSurfaces, numFaces: numFaces, mipCount: mipCount, metadataSize: metadataSize, }); return info; } decodePvr2Header(bytes) { const input = new InputBuffer({ buffer: bytes, }); const size = input.readUint32(); if (size !== PvrDecoder.pvrHeaderSize) { return undefined; } const height = input.readUint32(); const width = input.readUint32(); const mipCount = input.readUint32(); const flags = input.readUint32(); const texDataSize = input.readUint32(); const bitsPerPixel = input.readUint32(); const redMask = input.readUint32(); const greenMask = input.readUint32(); const blueMask = input.readUint32(); const alphaMask = input.readUint32(); const magic = input.readUint32(); const numTex = input.readUint32(); const info = new Pvr2Info({ height: height, width: width, mipCount: mipCount, flags: flags, texDataSize: texDataSize, bitsPerPixel: bitsPerPixel, redMask: redMask, greenMask: greenMask, blueMask: blueMask, alphaMask: alphaMask, magic: magic, numTex: numTex, }); const pvr2Signature = 0x21525650; if (info.magic !== pvr2Signature) { return undefined; } return info; } decodeApplePvrtcHeader(bytes) { const fileSize = bytes.length; const input = new InputBuffer({ buffer: bytes, }); const sz = input.readUint32(); if (sz !== 0) { return undefined; } let height = input.readUint32(); let width = input.readUint32(); const mipCount = input.readUint32(); const flags = input.readUint32(); const texDataSize = input.readUint32(); const bitsPerPixel = input.readUint32(); const redMask = input.readUint32(); const greenMask = input.readUint32(); const blueMask = input.readUint32(); const magic = input.readUint32(); const info = new PvrAppleInfo({ height: height, width: width, mipCount: mipCount, flags: flags, texDataSize: texDataSize, bitsPerPixel: bitsPerPixel, redMask: redMask, greenMask: greenMask, blueMask: blueMask, magic: magic, }); const appleSignature = 0x21525650; if (info.magic === appleSignature) { return undefined; } let mode = 1; let res = 8; if (fileSize === 32) { mode = 0; res = 8; } else { let shift = 0; const test2bpp = 0x40; const test4bpp = 0x80; while (shift < 10) { const s2 = shift << 1; if (((test2bpp << s2) & fileSize) !== 0) { res = 16 << shift; mode = 1; break; } if (((test4bpp << s2) & fileSize) !== 0) { res = 16 << shift; mode = 0; break; } ++shift; } if (shift === 10) { return undefined; } } width = res; height = res; const bpp = (mode + 1) * 2; if (bpp === 4) { return undefined; } info.width = width; info.height = height; info.bitsPerPixel = bpp; return info; } decodePvr2(data) { const length = data.length; const pvrTexCubemap = 1 << 12; const pvrPixelTypeMask = 0xff; const pvrTypeRgba4444 = 0x10; const pvrTypeRgba5551 = 0x11; const pvrTypeRgba8888 = 0x12; const pvrTypeRgb565 = 0x13; const pvrTypeRgb555 = 0x14; const pvrTypeRgb888 = 0x15; const pvrTypeI8 = 0x16; const pvrTypeAI8 = 0x17; const pvrTypePvrtc2 = 0x18; const pvrTypePvrtc4 = 0x19; if (length < PvrDecoder.pvrHeaderSize || this._info === undefined) { return undefined; } const info = this._info; const input = new InputBuffer({ buffer: data, }); input.skip(PvrDecoder.pvrHeaderSize); let numTex = info.numTex; if (numTex < 1) { numTex = (info.flags & pvrTexCubemap) !== 0 ? 6 : 1; } if (numTex !== 1) { return undefined; } if ((info.width * info.height * info.bitsPerPixel) / 8 > length - PvrDecoder.pvrHeaderSize) { return undefined; } const pType = info.flags & pvrPixelTypeMask; switch (pType) { case pvrTypeRgba4444: { const image = new MemoryImage({ width: info.width, height: info.height, numChannels: 4, }); for (const p of image) { const v1 = input.read(); const v2 = input.read(); const a = (v1 & 0x0f) << 4; const b = v1 & 0xf0; const g = (v2 & 0x0f) << 4; const r = v2 & 0xf0; p.r = r; p.g = g; p.b = b; p.a = a; } return image; } case pvrTypeRgba5551: { const image = new MemoryImage({ width: info.width, height: info.height, numChannels: 4, }); for (const p of image) { const v = input.readUint16(); const r = (v & 0xf800) >> 8; const g = (v & 0x07c0) >> 3; const b = (v & 0x003e) << 2; const a = (v & 0x0001) !== 0 ? 255 : 0; p.r = r; p.g = g; p.b = b; p.a = a; } return image; } case pvrTypeRgba8888: { const image = new MemoryImage({ width: info.width, height: info.height, numChannels: 4, }); for (const p of image) { p.r = input.read(); p.g = input.read(); p.b = input.read(); p.a = input.read(); } return image; } case pvrTypeRgb565: { const image = new MemoryImage({ width: info.width, height: info.height, }); for (const p of image) { const v = input.readUint16(); const b = (v & 0x001f) << 3; const g = (v & 0x07e0) >> 3; const r = (v & 0xf800) >> 8; p.r = r; p.g = g; p.b = b; } return image; } case pvrTypeRgb555: { const image = new MemoryImage({ width: info.width, height: info.height, }); for (const p of image) { const v = input.readUint16(); const r = (v & 0x001f) << 3; const g = (v & 0x03e0) >> 2; const b = (v & 0x7c00) >> 7; p.r = r; p.g = g; p.b = b; } return image; } case pvrTypeRgb888: { const image = new MemoryImage({ width: info.width, height: info.height, }); for (const p of image) { p.r = input.read(); p.g = input.read(); p.b = input.read(); } return image; } case pvrTypeI8: { const image = new MemoryImage({ width: info.width, height: info.height, numChannels: 1, }); for (const p of image) { const i = input.read(); p.r = i; } return image; } case pvrTypeAI8: { const image = new MemoryImage({ width: info.width, height: info.height, numChannels: 4, }); for (const p of image) { const a = input.read(); const i = input.read(); p.r = i; p.g = i; p.b = i; p.a = a; } return image; } case pvrTypePvrtc2: return undefined; case pvrTypePvrtc4: return info.alphaMask === 0 ? this.decodeRgb4bpp(info.width, info.height, input.toUint8Array()) : this.decodeRgba4bpp(info.width, info.height, input.toUint8Array()); } return undefined; } decodePvr3(data) { if (this._info instanceof Pvr3Info) { return undefined; } const pvr3Pvrtc4bppRgb = 2; const pvr3Pvrtc4bppRgba = 3; const input = new InputBuffer({ buffer: data, }); input.skip(PvrDecoder.pvrHeaderSize); const info = this._info; input.skip(info.metadataSize); if (info.order[0] === 0) { switch (info.format) { case pvr3Pvrtc4bppRgb: return this.decodeRgb4bpp(info.width, info.height, input.toUint8Array()); case pvr3Pvrtc4bppRgba: return this.decodeRgba4bpp(info.width, info.height, input.toUint8Array()); } } return undefined; } countBits(x) { let _x = x; _x = (_x - ((_x >> 1) & 0x55555555)) & 0xffffffff; _x = ((_x & 0x33333333) + ((_x >> 2) & 0x33333333)) & 0xffffffff; _x = (_x + (_x >> 4)) & 0xffffffff; _x &= 0xf0f0f0f; _x = ((_x * 0x01010101) & 0xffffffff) >> 24; return _x; } decodeRgb4bpp(width, height, data) { const result = new MemoryImage({ width: width, height: height, }); const blocks = Math.trunc(width / 4); const blockMask = blocks - 1; const packet = new PvrPacket(data); const p0 = new PvrPacket(data); const p1 = new PvrPacket(data); const p2 = new PvrPacket(data); const p3 = new PvrPacket(data); const factors = PvrPacket.bilinearFactors; const weights = PvrPacket.weights; for (let y = 0, y4 = 0; y < blocks; ++y, y4 += 4) { for (let x = 0, x4 = 0; x < blocks; ++x, x4 += 4) { packet.setBlock(x, y); let mod = packet.modulationData; const weightIndex = packet.usePunchthroughAlpha ? 4 : 0; let factorIndex = 0; for (let py = 0; py < 4; ++py) { const yOffset = py < 2 ? -1 : 0; const y0 = (y + yOffset) & blockMask; const y1 = (y0 + 1) & blockMask; for (let px = 0; px < 4; ++px) { const xOffset = px < 2 ? -1 : 0; const x0 = (x + xOffset) & blockMask; const x1 = (x0 + 1) & blockMask; p0.setBlock(x0, y0); p1.setBlock(x1, y0); p2.setBlock(x0, y1); p3.setBlock(x1, y1); const ca = p0 .getColorRgbA() .mul(factors[factorIndex][0]) .add(p1 .getColorRgbA() .mul(factors[factorIndex][1]) .add(p2 .getColorRgbA() .mul(factors[factorIndex][2]) .add(p3.getColorRgbA().mul(factors[factorIndex][3])))); const cb = p0 .getColorRgbB() .mul(factors[factorIndex][0]) .add(p1 .getColorRgbB() .mul(factors[factorIndex][1]) .add(p2 .getColorRgbB() .mul(factors[factorIndex][2]) .add(p3.getColorRgbB().mul(factors[factorIndex][3])))); const w = weights[(weightIndex + mod) & 3]; const r = (ca.r * w[0] + cb.r * w[1]) >> 7; const g = (ca.g * w[0] + cb.g * w[1]) >> 7; const b = (ca.b * w[0] + cb.b * w[1]) >> 7; result.setPixelRgb(px + x4, py + y4, r, g, b); mod >>= 2; factorIndex++; } } } } return result; } decodeRgba4bpp(width, height, data) { const result = new MemoryImage({ width: width, height: height, numChannels: 4, }); const blocks = Math.trunc(width / 4); const blockMask = blocks - 1; const packet = new PvrPacket(data); const p0 = new PvrPacket(data); const p1 = new PvrPacket(data); const p2 = new PvrPacket(data); const p3 = new PvrPacket(data); const factors = PvrPacket.bilinearFactors; const weights = PvrPacket.weights; for (let y = 0, y4 = 0; y < blocks; ++y, y4 += 4) { for (let x = 0, x4 = 0; x < blocks; ++x, x4 += 4) { packet.setBlock(x, y); let mod = packet.modulationData; const weightIndex = packet.usePunchthroughAlpha ? 4 : 0; let factorIndex = 0; for (let py = 0; py < 4; ++py) { const yOffset = py < 2 ? -1 : 0; const y0 = (y + yOffset) & blockMask; const y1 = (y0 + 1) & blockMask; for (let px = 0; px < 4; ++px) { const xOffset = px < 2 ? -1 : 0; const x0 = (x + xOffset) & blockMask; const x1 = (x0 + 1) & blockMask; p0.setBlock(x0, y0); p1.setBlock(x1, y0); p2.setBlock(x0, y1); p3.setBlock(x1, y1); const ca = p0 .getColorRgbaA() .mul(factors[factorIndex][0]) .add(p1 .getColorRgbaA() .mul(factors[factorIndex][1]) .add(p2 .getColorRgbaA() .mul(factors[factorIndex][2]) .add(p3.getColorRgbaA().mul(factors[factorIndex][3])))); const cb = p0 .getColorRgbaB() .mul(factors[factorIndex][0]) .add(p1 .getColorRgbaB() .mul(factors[factorIndex][1]) .add(p2 .getColorRgbaB() .mul(factors[factorIndex][2]) .add(p3.getColorRgbaB().mul(factors[factorIndex][3])))); const w = weights[(weightIndex + mod) & 3]; const r = (ca.r * w[0] + cb.r * w[1]) >> 7; const g = (ca.g * w[0] + cb.g * w[1]) >> 7; const b = (ca.b * w[0] + cb.b * w[1]) >> 7; const a = (ca.a * w[2] + cb.a * w[3]) >> 7; result.setPixelRgba(px + x4, py + y4, r, g, b, a); mod >>= 2; factorIndex++; } } } } return result; } isValidFile(bytes) { return this.startDecode(bytes) !== undefined; } startDecode(bytes) { if (this.countBits(bytes.length) === 1) { const info = this.decodeApplePvrtcHeader(bytes); if (info !== undefined) { this._data = bytes; return (this._info = info); } } { const info = this.decodePvr3Header(bytes); if (info !== undefined) { this._data = bytes; return (this._info = info); } } { const info = this.decodePvr2Header(bytes); if (info !== undefined) { this._data = bytes; return (this._info = info); } } return undefined; } decode(opt) { var _a; if (this.startDecode(opt.bytes) === undefined) { return undefined; } return this.decodeFrame((_a = opt.frameIndex) !== null && _a !== void 0 ? _a : 0); } decodeFrame(_frameIndex) { if (this._info === undefined || this._data === undefined) { return undefined; } if (this._info instanceof PvrAppleInfo) { return this.decodeRgba4bpp(this._info.width, this._info.height, this._data); } else if (this._info instanceof Pvr2Info) { return this.decodePvr2(this._data); } else if (this._info instanceof Pvr3Info) { return this.decodePvr3(this._data); } return undefined; } } PvrDecoder.pvrHeaderSize = 52; //# sourceMappingURL=pvr-decoder.js.map