thumbnail/imagecrush.go

109 lines
2.1 KiB
Go

//+build !disableimagecrush
package thumbnail
import (
"bytes"
"fmt"
"image"
"image/color"
"image/png"
"code.ivysaur.me/imagequant"
)
func goImageToRgba32(im image.Image) []byte {
w := im.Bounds().Max.X
h := im.Bounds().Max.Y
ret := make([]byte, w*h*4)
p := 0
for y := 0; y < h; y += 1 {
for x := 0; x < w; x += 1 {
r16, g16, b16, a16 := im.At(x, y).RGBA() // Each value ranges within [0, 0xffff]
ret[p+0] = uint8(r16 >> 8)
ret[p+1] = uint8(g16 >> 8)
ret[p+2] = uint8(b16 >> 8)
ret[p+3] = uint8(a16 >> 8)
p += 4
}
}
return ret
}
func rgb8PaletteToGoImage(w, h int, rgb8data []byte, pal color.Palette) image.Image {
rect := image.Rectangle{
Max: image.Point{
X: w,
Y: h,
},
}
ret := image.NewPaletted(rect, pal)
for y := 0; y < h; y += 1 {
for x := 0; x < w; x += 1 {
ret.SetColorIndex(x, y, rgb8data[y*h+x])
}
}
return ret
}
func crushFast(img image.Image) ([]byte, error) {
return crush(img, imagequant.SPEED_FASTEST)
}
func crush(img image.Image, speed int) ([]byte, error) {
width := img.Bounds().Max.X
height := img.Bounds().Max.Y
attr, err := imagequant.NewAttributes()
if err != nil {
return nil, fmt.Errorf("NewAttributes: %s", err.Error())
}
defer attr.Release()
err = attr.SetSpeed(speed)
if err != nil {
return nil, fmt.Errorf("SetSpeed: %s", err.Error())
}
rgba32data := goImageToRgba32(img)
iqm, err := imagequant.NewImage(attr, string(rgba32data), width, height, 0)
if err != nil {
return nil, fmt.Errorf("NewImage: %s", err.Error())
}
defer iqm.Release()
res, err := iqm.Quantize(attr)
if err != nil {
return nil, fmt.Errorf("Quantize: %s", err.Error())
}
defer res.Release()
rgb8data, err := res.WriteRemappedImage()
if err != nil {
return nil, fmt.Errorf("WriteRemappedImage: %s", err.Error())
}
im2 := rgb8PaletteToGoImage(res.GetImageWidth(), res.GetImageHeight(), rgb8data, res.GetPalette())
buff := bytes.Buffer{}
penc := png.Encoder{
CompressionLevel: png.BestCompression,
}
err = penc.Encode(&buff, im2)
if err != nil {
return nil, fmt.Errorf("png.Encode: %s", err.Error())
}
return buff.Bytes(), nil
}