ES6 module for fast PNG quantization with WebGL
npm install webgl-png-compress
This module exposes three functions:
function compress (imageData, width, height, gl, n, iterations)
Use this generate a palette through iterative k-means.
imageData
should be in raw format as extracted from a 2D canvas.
const context = imageCanvas.getContext('2d')
context.drawImage(this.image, 0, 0)
const imageData = context.getImageData(0, 0, this.image.width, this.image.height)
n
cluster centers are placed in the image at random and then the designated number of iterations of k-means are performed
to converge to an optimized palette.
Returns the generated palette.
function condensePalette (palette, eps=0)
Use this to remove degenerate entries from the palette based the tolerence specified as eps
.
Returns the reduced palette.
function writePNG (imageData, palette, width, height)
Creates a buffer containing a quantized PNG with a PLTE
block from the original imageData
with
Floyd-Steinberg dithering applied.
Returns the buffer.
PNGs are capable of displaying palettized images by declaring a PLTE
chunk. See the specification for more details.
Once a palette chunk is declared, the image data can be made up of 8-bit indices that reference colors in the palette, as opposed to each pixel declared independently as a 24-bit color. Palette based compression will always reduced the image size by ~30%
when compared to uncompressed PNG images.
However, the real gain from this type of compression comes from the fact the PNG data chunk is compressed with Deflate (a LZ77 based compression).
Replacing colors with indices makes the data much more compressible, particularly in vector images that may have long linear stretches of repeated colors.
This project uses k-means to generate an optimized palette from a source image, and that palette (or any other palette) can be used to write out a new, compressed image.
You can compress any image loadable in the browser (including JPG), but for certain types of images (particularly photographs), the PNG compression may not perform as well as the compression that has likely already been applied to the JPG.
Each pixel is treated as a vertex with a shader. The shader is used to map the pixel to the closest cluster center. Rather than iteratively comparing each pixel against the cluster centers, it happens in parallel for all pixels on your GPU.
It is recommended to downscale large images for palette generation to avoid running out of GPU memory.