{"version":3,"file":"png.mjs","names":[],"sources":["../../src/renderers/png.ts"],"sourcesContent":["/**\n * PNG 渲染器\n *\n * 将 boolean[][] 矩阵编码为 PNG 格式的 Uint8Array。\n * 纯算法实现，零运行时依赖，可在任意 JS 环境使用。\n *\n * 使用 uncompressed DEFLATE blocks 以避免引入压缩算法，\n * 输出文件略大（~1.5x），但生成速度极快，对 QR 码小图完全可接受。\n */\n\nexport interface PngOptions {\n  /** 每个模块的像素大小，默认 4 */\n  moduleSize?: number\n  /** 静默区模块数，默认 4 */\n  quietZone?: number\n  /** 前景色 RGB，默认 [0, 0, 0]（黑色） */\n  foreground?: [number, number, number]\n  /** 背景色 RGB，默认 [255, 255, 255]（白色） */\n  background?: [number, number, number]\n  /** 是否透明背景（忽略 background），默认 false */\n  transparent?: boolean\n}\n\n// ===== CRC32 =====\n\nconst CRC_TABLE = new Uint32Array(256)\nfor (let n = 0; n < 256; n++) {\n  let c = n\n  for (let k = 0; k < 8; k++) {\n    c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1\n  }\n  CRC_TABLE[n] = c\n}\n\nfunction crc32(data: Uint8Array, start: number, length: number): number {\n  let crc = 0xffffffff\n  for (let i = start; i < start + length; i++) {\n    crc = CRC_TABLE[(crc ^ data[i]) & 0xff] ^ (crc >>> 8)\n  }\n  return (crc ^ 0xffffffff) >>> 0\n}\n\n// ===== Adler32 =====\n\nfunction adler32(data: Uint8Array): number {\n  let a = 1\n  let b = 0\n  for (let i = 0; i < data.length; i++) {\n    a = (a + data[i]) % 65521\n    b = (b + a) % 65521\n  }\n  return ((b << 16) | a) >>> 0\n}\n\n// ===== Helper: write big-endian uint32 =====\n\nfunction writeU32BE(buf: Uint8Array, offset: number, value: number): void {\n  buf[offset] = (value >>> 24) & 0xff\n  buf[offset + 1] = (value >>> 16) & 0xff\n  buf[offset + 2] = (value >>> 8) & 0xff\n  buf[offset + 3] = value & 0xff\n}\n\nfunction writeU16LE(buf: Uint8Array, offset: number, value: number): void {\n  buf[offset] = value & 0xff\n  buf[offset + 1] = (value >>> 8) & 0xff\n}\n\n// ===== PNG Chunk =====\n\nfunction createChunk(type: string, data: Uint8Array): Uint8Array {\n  const chunk = new Uint8Array(4 + 4 + data.length + 4)\n  // Length\n  writeU32BE(chunk, 0, data.length)\n  // Type\n  for (let i = 0; i < 4; i++) chunk[4 + i] = type.charCodeAt(i)\n  // Data\n  chunk.set(data, 8)\n  // CRC (over type + data)\n  const crc = crc32(chunk, 4, 4 + data.length)\n  writeU32BE(chunk, 8 + data.length, crc)\n  return chunk\n}\n\n// ===== Uncompressed DEFLATE =====\n\n/**\n * Wrap raw data in uncompressed DEFLATE format (zlib stream).\n * Max block size = 65535 bytes, split into multiple stored blocks if needed.\n */\nfunction deflateUncompressed(raw: Uint8Array): Uint8Array {\n  const MAX_BLOCK = 65535\n\n  // Calculate number of blocks\n  const numBlocks = Math.ceil(raw.length / MAX_BLOCK) || 1\n\n  // zlib header (2 bytes) + blocks (5 bytes header each + data) + adler32 (4 bytes)\n  const totalBlockHeaders = numBlocks * 5\n  const outputSize = 2 + totalBlockHeaders + raw.length + 4\n  const out = new Uint8Array(outputSize)\n  let pos = 0\n\n  // Zlib header: CM=8 (deflate), CINFO=7 (32K window), FCHECK\n  out[pos++] = 0x78\n  out[pos++] = 0x01 // minimal compression\n\n  let remaining = raw.length\n  let offset = 0\n\n  while (remaining > 0 || offset === 0) {\n    const blockSize = Math.min(remaining, MAX_BLOCK)\n    const isLast = remaining <= MAX_BLOCK\n\n    // BFINAL (1 if last) | BTYPE=00 (stored)\n    out[pos++] = isLast ? 0x01 : 0x00\n    // LEN (little-endian 16-bit)\n    writeU16LE(out, pos, blockSize)\n    pos += 2\n    // NLEN (one's complement of LEN)\n    writeU16LE(out, pos, blockSize ^ 0xffff)\n    pos += 2\n    // Raw data\n    out.set(raw.subarray(offset, offset + blockSize), pos)\n    pos += blockSize\n\n    offset += blockSize\n    remaining -= blockSize\n\n    if (remaining <= 0) break\n  }\n\n  // Adler-32 checksum (big-endian)\n  const checksum = adler32(raw)\n  writeU32BE(out, pos, checksum)\n\n  return out\n}\n\n// ===== Main API =====\n\n/** 将 QR 码矩阵编码为 PNG 格式的 Uint8Array */\nexport function toPng(modules: boolean[][], options?: PngOptions): Uint8Array {\n  const {\n    moduleSize = 4,\n    quietZone = 4,\n    foreground = [0, 0, 0],\n    background = [255, 255, 255],\n    transparent = false\n  } = options ?? {}\n\n  const count = modules.length\n  const width = (count + quietZone * 2) * moduleSize\n  const height = width\n\n  if (transparent) {\n    // RGBA 模式：背景像素 alpha=0，前景 alpha=255\n    const rowBytes = 1 + width * 4\n    const rawData = new Uint8Array(rowBytes * height)\n\n    for (let py = 0; py < height; py++) {\n      const rowOffset = py * rowBytes\n      rawData[rowOffset] = 0 // Filter: None\n\n      for (let px = 0; px < width; px++) {\n        const moduleRow = Math.floor(py / moduleSize) - quietZone\n        const moduleCol = Math.floor(px / moduleSize) - quietZone\n\n        const isDark =\n          moduleRow >= 0 &&\n          moduleRow < count &&\n          moduleCol >= 0 &&\n          moduleCol < count &&\n          modules[moduleRow][moduleCol]\n\n        const idx = rowOffset + 1 + px * 4\n        if (isDark) {\n          rawData[idx] = foreground[0]\n          rawData[idx + 1] = foreground[1]\n          rawData[idx + 2] = foreground[2]\n          rawData[idx + 3] = 255\n        } else {\n          rawData[idx] = 0\n          rawData[idx + 1] = 0\n          rawData[idx + 2] = 0\n          rawData[idx + 3] = 0\n        }\n      }\n    }\n\n    return _buildPng(deflateUncompressed(rawData), width, height, 6 /* RGBA */)\n  }\n\n  // Build raw pixel data (RGB, no alpha for smaller size)\n  // Each row: 1 filter byte + width * 3 RGB bytes\n  const rowBytes = 1 + width * 3\n  const rawData = new Uint8Array(rowBytes * height)\n\n  for (let py = 0; py < height; py++) {\n    const rowOffset = py * rowBytes\n    rawData[rowOffset] = 0 // Filter: None\n\n    for (let px = 0; px < width; px++) {\n      const moduleRow = Math.floor(py / moduleSize) - quietZone\n      const moduleCol = Math.floor(px / moduleSize) - quietZone\n\n      const isDark =\n        moduleRow >= 0 &&\n        moduleRow < count &&\n        moduleCol >= 0 &&\n        moduleCol < count &&\n        modules[moduleRow][moduleCol]\n\n      const color = isDark ? foreground : background\n      const idx = rowOffset + 1 + px * 3\n      rawData[idx] = color[0]\n      rawData[idx + 1] = color[1]\n      rawData[idx + 2] = color[2]\n    }\n  }\n\n  // Compress with uncompressed DEFLATE\n  const compressed = deflateUncompressed(rawData)\n\n  return _buildPng(compressed, width, height, 2 /* RGB */)\n}\n\nfunction _buildPng(\n  compressed: Uint8Array,\n  width: number,\n  height: number,\n  colorType: number\n): Uint8Array {\n  const PNG_SIGNATURE = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10])\n\n  // IHDR\n  const ihdrData = new Uint8Array(13)\n  writeU32BE(ihdrData, 0, width)\n  writeU32BE(ihdrData, 4, height)\n  ihdrData[8] = 8 // bit depth\n  ihdrData[9] = colorType // color type (2=RGB, 6=RGBA)\n  ihdrData[10] = 0 // compression\n  ihdrData[11] = 0 // filter\n  ihdrData[12] = 0 // interlace\n  const ihdrChunk = createChunk('IHDR', ihdrData)\n\n  // IDAT\n  const idatChunk = createChunk('IDAT', compressed)\n\n  // IEND\n  const iendChunk = createChunk('IEND', new Uint8Array(0))\n\n  // Concatenate\n  const png = new Uint8Array(\n    PNG_SIGNATURE.length + ihdrChunk.length + idatChunk.length + iendChunk.length\n  )\n  let offset = 0\n  png.set(PNG_SIGNATURE, offset)\n  offset += PNG_SIGNATURE.length\n  png.set(ihdrChunk, offset)\n  offset += ihdrChunk.length\n  png.set(idatChunk, offset)\n  offset += idatChunk.length\n  png.set(iendChunk, offset)\n\n  return png\n}\n\n/** 将 QR 码矩阵编码为 PNG data URL */\nexport function toPngDataURL(modules: boolean[][], options?: PngOptions): string {\n  const pngBytes = toPng(modules, options)\n  const base64 = uint8ArrayToBase64(pngBytes)\n  return `data:image/png;base64,${base64}`\n}\n\n/** 跨平台 Uint8Array → base64 */\nfunction uint8ArrayToBase64(bytes: Uint8Array): string {\n  // Node.js Buffer\n  if (typeof Buffer !== 'undefined') {\n    return Buffer.from(bytes).toString('base64')\n  }\n  // Browser\n  let binary = ''\n  for (let i = 0; i < bytes.length; i++) {\n    binary += String.fromCharCode(bytes[i])\n  }\n  return btoa(binary)\n}\n"],"mappings":"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"}