{
  "lead": "JPEG 能把照片压到原来的十分之一甚至更小，靠的是「丢掉人眼不太在意的信息」：人眼对亮度比对颜色敏感，对平缓的变化比对细碎的纹理敏感。编码器先把颜色分离出来并降低分辨率，再把每个 8×8 的小块换成「频率」表示，粗略地保存高频成分，最后用 Huffman 编码把大量的 0 压缩掉。这个页面的编码器完全手写，生成的文件由浏览器自己的解码器显示。",
  "formulas": [
    [
      "F(u,v) = ¼ C(u)C(v) Σ<sub>x,y</sub> f(x,y) cos((2x+1)uπ/16) cos((2y+1)vπ/16)",
      "二维离散余弦变换：把 8×8 像素换成 64 个频率分量"
    ],
    [
      "q(u,v) = round(F(u,v) / Q(u,v))",
      "量化：除以量化表再取整，JPEG 唯一丢失信息的一步"
    ]
  ],
  "terms": [
    [
      "① 颜色空间与色度采样",
      "RGB 先换成 YCbCr：Y 是亮度，Cb、Cr 是蓝、红色差。4:2:0 模式把两个色差通道的分辨率各减半，数据量直接少一半，照片里几乎看不出区别；但蓝底上的红字、彩色噪点这种颜色剧烈变化的地方，颜色会「渗」出去，放大图里看得很清楚。"
    ],
    [
      "② DCT",
      "每个 8×8 块做一次离散余弦变换，得到 64 个系数：左上角是平均亮度（直流分量 DC），越往右下频率越高。平滑的区域能量几乎全集中在左上角的几个系数里。"
    ],
    [
      "③ 量化",
      "每个系数除以量化表里对应的数再四舍五入。量化表是 JPEG 标准附录 K 给出的，高频位置的数更大，所以高频细节被粗略保存甚至直接变成 0；「质量」按 libjpeg 的公式整体缩放这张表。质量越低，0 越多，文件越小，块状和振铃（边缘附近的波纹）越明显。"
    ],
    [
      "④ 熵编码",
      "系数按 Z 字形从低频到高频排列，让 0 集中到后面。DC 只记录与上一块的差值；交流系数（AC）记成「前面有几个 0 + 这个数有几位」再附上数值，最后一串 0 用一个「块结束」符号代替，这些符号再用标准 Huffman 表编码。压缩数据里出现的 0xFF 字节后面要补一个 0x00，以免被误认成文件标记。"
    ],
    [
      "文件格式",
      "输出是标准的 JFIF 文件：SOI、APP0（JFIF 标识）、DQT（量化表）、SOF0（尺寸和采样方式）、DHT（Huffman 表）、SOS（扫描开始）、压缩数据、EOI。所以任何看图软件都能打开「下载」得到的文件。"
    ]
  ],
  "flow": [
    [
      "js",
      "拖动滑块时，JS 调用 <code>jpeg_encode(像素, 宽, 高, 质量, 是否 4:2:0)</code>。"
    ],
    [
      "rust",
      "Rust 完成颜色转换、色度采样、逐块 DCT、量化、Huffman 编码，返回完整的 .jpg 文件字节。点击图片时 <code>jpeg_inspect</code> 返回那个 8×8 块在每一步的数值。"
    ],
    [
      "js",
      "JS 把字节包装成 Blob 交给浏览器解码显示，再把解码结果与原图逐像素比较算出 PSNR。解码不经过我们的代码，所以能看的图片本身就证明编码是正确的。"
    ]
  ],
  "perf": [
    "实测（桌面 Chromium）：880×560 的示例图编码约 17~30ms（4:4:4 比 4:2:0 多处理一倍的色度数据）。",
    "PSNR（峰值信噪比）越高越接近原图，30 dB 以上一般就不容易看出差别。示例图在 4:2:0 下 PSNR 偏低，主要是左下角那块彩色随机噪点：色度减半后它的颜色误差很大；换成 4:4:4，质量 90 时 PSNR 从 21.6 dB 升到 37.5 dB。",
    "验证：生成的文件用 Pillow（libjpeg）解码，在同样的质量和采样方式下，文件大小和 PSNR 与 libjpeg 自己编码的结果几乎相同（例如质量 50、4:4:4：11.0 KB / 29.33 dB 对 11.1 KB / 29.32 dB）。"
  ],
  "src": [
    [
      "crates/media/jpeg/src/encoder.rs",
      ""
    ],
    [
      "crates/media/jpeg/src/huffman.rs",
      ""
    ],
    [
      "crates/media/jpeg/src/tables.rs",
      ""
    ],
    [
      "www/jpeg/index.js",
      ""
    ]
  ]
}
