npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

etcpack-wasm

v1.0.6

Published

WebAssembly port of ETCPACK - ETC/EAC texture compression for the web

Downloads

70

Readme

ETCPACK WASM API 文档

概述

ETCPACK WASM 提供了完整的 ETC/EAC 纹理压缩功能,支持在浏览器中进行高质量的纹理压缩。

安装

# 构建 WASM 模块
cd wasm
./build.sh prod

快速开始

import { createETCPACK, TextureFormat } from './etcpack-api.js';

// 初始化
const encoder = await createETCPACK();

// 压缩 RGB 图像
const compressed = encoder.compressRGB({
  data: rgbData,        // Uint8Array
  width: 512,
  height: 512,
  format: TextureFormat.ETC2_RGB
});

console.log(`压缩比: ${compressed.compressionRatio.toFixed(2)}:1`);

TypeScript 类型

TextureFormat

enum TextureFormat {
  ETC1_RGB = 0,           // ETC1 RGB (向后兼容)
  ETC2_RGB = 1,           // ETC2 RGB
  ETC2_RGBA1 = 2,         // ETC2 RGB + 1-bit Alpha
  ETC2_RGBA = 3,          // ETC2 RGBA (with EAC)
  EAC_R11 = 4,            // EAC R11 unsigned
  EAC_R11_SIGNED = 5,     // EAC R11 signed
  EAC_RG11 = 6,           // EAC RG11 unsigned
  EAC_RG11_SIGNED = 7     // EAC RG11 signed
}

CompressOptions

interface CompressOptions {
  data: Uint8Array | Uint8ClampedArray;  // 图像数据
  width: number;                          // 宽度(必须是 4 的倍数)
  height: number;                         // 高度(必须是 4 的倍数)
  format?: TextureFormat;                 // 格式(可选)
}

CompressResult

interface CompressResult {
  data: Uint8Array;           // 压缩后的数据
  width: number;              // 图像宽度
  height: number;             // 图像高度
  format: TextureFormat;      // 压缩格式
  compressedSize: number;     // 压缩后大小(字节)
  originalSize: number;       // 原始大小(字节)
  compressionRatio: number;   // 压缩比
}

DecompressOptions

interface DecompressOptions {
  data: Uint8Array;          // 压缩数据
  width: number;             // 宽度
  height: number;            // 高度
  format: TextureFormat;     // 压缩格式
}

DecompressResult

interface DecompressResult {
  data: Uint8Array;          // 解压后的数据
  width: number;             // 图像宽度
  height: number;            // 图像高度
  channels: number;          // 通道数 (3=RGB, 4=RGBA, 1=R, 2=RG)
}

API 方法

ETCPACKEncoder

compressRGB(options: CompressOptions): CompressResult

压缩 RGB 图像为 ETC1/ETC2 格式。

参数

  • data: Uint8Array - RGB 图像数据
  • width: number - 图像宽度(必须是 4 的倍数)
  • height: number - 图像高度(必须是 4 的倍数)
  • format: TextureFormat - 压缩格式(默认 ETC2_RGB)

返回: CompressResult

示例

const result = encoder.compressRGB({
  data: rgbData,
  width: 512,
  height: 512,
  format: TextureFormat.ETC2_RGB
});

decompressRGB(options: DecompressOptions): DecompressResult

解压 RGB 图像。

参数

  • data: Uint8Array - 压缩数据
  • width: number - 图像宽度
  • height: number - 图像高度
  • format: TextureFormat - 压缩格式

返回: DecompressResult(channels = 3)


compressRGBA(options: CompressOptions): CompressResult

压缩 RGBA 图像为 ETC2 + EAC Alpha 格式(16 字节/块)。

参数

  • data: Uint8Array - RGBA 图像数据
  • width: number - 图像宽度
  • height: number - 图像高度

返回: CompressResult

示例

const result = encoder.compressRGBA({
  data: rgbaData,
  width: 256,
  height: 256
});

decompressRGBA(options: DecompressOptions): DecompressResult

解压 RGBA 图像。

返回: DecompressResult(channels = 4)


compressRGBA1(options: CompressOptions): CompressResult

压缩 RGBA 图像为 ETC2 + 1-bit Alpha(punchthrough)格式(8 字节/块)。

适用于二值 Alpha(完全透明或完全不透明)。

示例

const result = encoder.compressRGBA1({
  data: rgbaData,
  width: 512,
  height: 512
});

decompressRGBA1(options: DecompressOptions): DecompressResult

解压 RGBA1 图像。


compressR11(options: CompressOptions & { signed?: boolean }): CompressResult

压缩单通道 11-bit 数据(EAC R11)。

参数

  • data: Uint8Array - 单通道数据(16-bit)
  • width: number - 宽度
  • height: number - 高度
  • signed: boolean - 是否有符号(默认 false)

用途

  • 高度图 / 位移贴图
  • 法线贴图(单通道)
  • 灰度图

示例

const result = encoder.compressR11({
  data: heightmapData,
  width: 1024,
  height: 1024,
  signed: false
});

decompressR11(options: DecompressOptions & { signed?: boolean }): DecompressResult

解压 R11 图像。

返回: DecompressResult(channels = 1,16-bit 输出)


compressRG11(options: CompressOptions & { signed?: boolean }): CompressResult

压缩双通道 11-bit 数据(EAC RG11)。

用途

  • 法线贴图(XY 通道)
  • 双通道数据

示例

const result = encoder.compressRG11({
  data: normalMapData,
  width: 512,
  height: 512,
  signed: true  // 法线通常使用有符号
});

getCompressedSize(width: number, height: number, format: TextureFormat): number

计算压缩后的数据大小(字节)。

示例

const size = encoder.getCompressedSize(512, 512, TextureFormat.ETC2_RGB);
console.log(`预期大小: ${size} 字节`); // 32768 字节

validateDimensions(width: number, height: number): boolean

验证图像尺寸是否有效(必须是 4 的倍数)。

抛出: Error - 如果尺寸无效

示例

try {
  encoder.validateDimensions(513, 512);
} catch (e) {
  console.error(e.message); // "Image dimensions must be multiples of 4..."
}

使用场景

1. WebGL 纹理压缩

async function uploadCompressedTexture(
  gl: WebGLRenderingContext,
  imageData: Uint8Array,
  width: number,
  height: number
) {
  const encoder = await createETCPACK();

  const compressed = encoder.compressRGB({
    data: imageData,
    width,
    height,
    format: TextureFormat.ETC2_RGB
  });

  const texture = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, texture);

  const ext = gl.getExtension('WEBGL_compressed_texture_etc');
  gl.compressedTexImage2D(
    gl.TEXTURE_2D, 0,
    ext.COMPRESSED_RGB8_ETC2,
    width, height, 0,
    compressed.data
  );

  return texture;
}

2. Canvas 图像压缩

async function compressCanvas(canvas: HTMLCanvasElement) {
  const encoder = await createETCPACK();
  const ctx = canvas.getContext('2d')!;
  const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

  return encoder.compressRGBA({
    data: imageData.data,
    width: canvas.width,
    height: canvas.height
  });
}

3. 批量纹理处理

async function compressBatch(textures: Array<{name: string, data: Uint8Array, width: number, height: number}>) {
  const encoder = await createETCPACK();

  const results = textures.map(tex => ({
    name: tex.name,
    ...encoder.compressRGB({
      data: tex.data,
      width: tex.width,
      height: tex.height
    })
  }));

  return results;
}

性能参考

| 格式 | 压缩比 | 质量 | 速度 | 用途 | |-----|--------|------|------|------| | ETC1 RGB | 6:1 | 中 | 快 | 旧设备兼容 | | ETC2 RGB | 6:1 | 高 | 快 | 不透明纹理 | | ETC2 RGBA1 | 6:1 | 高 | 快 | Cutout 透明 | | ETC2 RGBA | 4:1 | 高 | 中 | 完整 Alpha | | EAC R11 | 2:1 | 极高 | 中 | 高度图 | | EAC RG11 | 1:1 | 极高 | 中 | 法线贴图 |

注意事项

  1. 尺寸要求:图像宽高必须是 4 的倍数
  2. 内存使用:压缩/解压过程会临时分配内存
  3. 浏览器兼容:需要 WebAssembly 支持(现代浏览器)
  4. 纹理格式支持:使用前检查 WEBGL_compressed_texture_etc 扩展

错误处理

try {
  const result = encoder.compressRGB({
    data: imageData,
    width: 513,  // 不是 4 的倍数
    height: 512
  });
} catch (error) {
  if (error.message.includes('multiples of 4')) {
    console.error('图像尺寸必须是 4 的倍数');
  }
}

文件大小对比

| 分辨率 | RGB 原始 | ETC2 压缩 | 节省 | |--------|----------|-----------|------| | 512×512 | 768 KB | 128 KB | 83% | | 1024×1024 | 3 MB | 512 KB | 83% | | 2048×2048 | 12 MB | 2 MB | 83% | | 4096×4096 | 48 MB | 8 MB | 83% |

更多资源