kopia lustrzana https://github.com/Tldraw/Tldraw
120 wiersze
2.7 KiB
TypeScript
120 wiersze
2.7 KiB
TypeScript
import crc32 from 'crc/crc32'
|
|
|
|
export function isPng(view: DataView, offset: number) {
|
|
if (
|
|
view.getUint8(offset + 0) === 0x89 &&
|
|
view.getUint8(offset + 1) === 0x50 &&
|
|
view.getUint8(offset + 2) === 0x4e &&
|
|
view.getUint8(offset + 3) === 0x47 &&
|
|
view.getUint8(offset + 4) === 0x0d &&
|
|
view.getUint8(offset + 5) === 0x0a &&
|
|
view.getUint8(offset + 6) === 0x1a &&
|
|
view.getUint8(offset + 7) === 0x0a
|
|
) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
function getChunkType(view: DataView, offset: number) {
|
|
return [
|
|
String.fromCharCode(view.getUint8(offset)),
|
|
String.fromCharCode(view.getUint8(offset + 1)),
|
|
String.fromCharCode(view.getUint8(offset + 2)),
|
|
String.fromCharCode(view.getUint8(offset + 3)),
|
|
].join('')
|
|
}
|
|
|
|
export function crc(arrayBuffer: ArrayBuffer) {
|
|
return crc32(arrayBuffer)
|
|
}
|
|
|
|
const LEN_SIZE = 4
|
|
const CRC_SIZE = 4
|
|
|
|
export function readChunks(view: DataView, offset = 0) {
|
|
const chunks: Record<string, { dataOffset: number; size: number; start: number }> = {}
|
|
if (!isPng(view, offset)) {
|
|
throw new Error('Not a PNG')
|
|
}
|
|
offset += 8
|
|
|
|
while (offset <= view.buffer.byteLength) {
|
|
const start = offset
|
|
const len = view.getInt32(offset)
|
|
offset += 4
|
|
const chunkType = getChunkType(view, offset)
|
|
|
|
if (chunkType === 'IDAT' && chunks[chunkType]) {
|
|
offset += len + LEN_SIZE + CRC_SIZE
|
|
continue
|
|
}
|
|
|
|
if (chunkType === 'IEND') {
|
|
break
|
|
}
|
|
|
|
chunks[chunkType] = {
|
|
start,
|
|
dataOffset: offset + 4,
|
|
size: len,
|
|
}
|
|
offset += len + LEN_SIZE + CRC_SIZE
|
|
}
|
|
|
|
return chunks
|
|
}
|
|
|
|
export function parsePhys(view: DataView, offset: number) {
|
|
return {
|
|
ppux: view.getUint32(offset),
|
|
ppuy: view.getUint32(offset + 4),
|
|
unit: view.getUint8(offset + 4),
|
|
}
|
|
}
|
|
|
|
export function findChunk(view: DataView, type: string) {
|
|
const chunks = readChunks(view)
|
|
return chunks[type]
|
|
}
|
|
|
|
export function setPhysChunk(view: DataView, dpr = 1, options?: BlobPropertyBag) {
|
|
let offset = 46
|
|
let size = 0
|
|
const res1 = findChunk(view, 'pHYs')
|
|
if (res1) {
|
|
offset = res1.start
|
|
size = res1.size
|
|
}
|
|
|
|
const res2 = findChunk(view, 'IDAT')
|
|
if (res2) {
|
|
offset = res2.start
|
|
size = 0
|
|
}
|
|
|
|
const pHYsData = new ArrayBuffer(21)
|
|
const pHYsDataView = new DataView(pHYsData)
|
|
|
|
pHYsDataView.setUint32(0, 9)
|
|
|
|
pHYsDataView.setUint8(4, 'p'.charCodeAt(0))
|
|
pHYsDataView.setUint8(5, 'H'.charCodeAt(0))
|
|
pHYsDataView.setUint8(6, 'Y'.charCodeAt(0))
|
|
pHYsDataView.setUint8(7, 's'.charCodeAt(0))
|
|
|
|
const DPI_96 = 2835.5
|
|
|
|
pHYsDataView.setInt32(8, DPI_96 * dpr)
|
|
pHYsDataView.setInt32(12, DPI_96 * dpr)
|
|
pHYsDataView.setInt8(16, 1)
|
|
|
|
const crcBit = new Uint8Array(pHYsData.slice(4, 17))
|
|
pHYsDataView.setInt32(17, crc(crcBit))
|
|
|
|
const startBuf = view.buffer.slice(0, offset)
|
|
const endBuf = view.buffer.slice(offset + size)
|
|
|
|
return new Blob([startBuf, pHYsData, endBuf], options)
|
|
}
|